class Savon::Builder

Constants

SCHEMA_TYPES
SOAP_NAMESPACE
WSA_NAMESPACE

Attributes

multipart[R]

Public Class Methods

new(operation_name, wsdl, globals, locals) click to toggle source
# File lib/savon/builder.rb, line 24
def initialize(operation_name, wsdl, globals, locals)
  @operation_name = operation_name

  @wsdl      = wsdl
  @globals   = globals
  @locals    = locals
  @signature = @locals[:wsse_signature] || @globals[:wsse_signature]

  @types = convert_type_definitions_to_hash
  @used_namespaces = convert_type_namespaces_to_hash
end

Public Instance Methods

body_attributes() click to toggle source
# File lib/savon/builder.rb, line 68
def body_attributes
  @body_attributes ||= @signature.nil? ? {} : @signature.body_attributes
end
build_document() click to toggle source
# File lib/savon/builder.rb, line 40
def build_document
  xml_result = build_xml

  # if we have a signature sign the document
  if @signature
    @signature.document = xml_result

    2.times do
      @header = nil
      @signature.document = build_xml
    end

    xml_result = @signature.document
  end

  # if there are attachments for the request, we should build a multipart message according to
  # https://www.w3.org/TR/SOAP-attachments
  if @locals[:attachments]
    build_multipart_message(xml_result)
  else
    xml_result
  end
end
header_attributes() click to toggle source
# File lib/savon/builder.rb, line 64
def header_attributes
  @globals[:use_wsa_headers] ? { 'xmlns:wsa' => WSA_NAMESPACE } : {}
end
pretty() click to toggle source
# File lib/savon/builder.rb, line 36
def pretty
  Nokogiri.XML(to_s).to_xml(:indent => 2)
end
to_s() click to toggle source
# File lib/savon/builder.rb, line 72
def to_s
  return @locals[:xml] if @locals.include? :xml
  build_document
end

Private Instance Methods

add_attachment_to_multipart_message(multipart_message, attachment, identifier) click to toggle source
# File lib/savon/builder.rb, line 296
def add_attachment_to_multipart_message(multipart_message, attachment, identifier)
  multipart_message.add_file attachment.clone
  multipart_message.parts.last.content_id = multipart_message.parts.last.content_location = identifier.to_s
end
add_attachments_to_multipart_message(multipart_message) click to toggle source
# File lib/savon/builder.rb, line 281
def add_attachments_to_multipart_message(multipart_message)
  if @locals[:attachments].is_a? Hash
    # hash example: { 'att1' => '/path/to/att1', 'att2' => '/path/to/att2' }
    @locals[:attachments].each do |identifier, attachment|
      add_attachment_to_multipart_message(multipart_message, attachment, identifier)
    end
  elsif @locals[:attachments].is_a? Array
    # array example: [ '/path/to/att1', '/path/to/att2' ]
    # array example: [ { filename: 'att1.xml', content: '<x/>' }, { filename: 'att2.xml', content: '<y/>' } ]
    @locals[:attachments].each do |attachment|
      add_attachment_to_multipart_message(multipart_message, attachment, attachment.is_a?(String) ? File.basename(attachment) : attachment[:filename])
    end
  end
end
body_message() click to toggle source
# File lib/savon/builder.rb, line 189
def body_message
  if @wsdl.document? and @wsdl.soap_input(@operation_name.to_sym).is_a?(Hash)
    serialized_messages
  else
    message.to_s
  end
end
build_multipart_message(message_xml) click to toggle source
# File lib/savon/builder.rb, line 249
def build_multipart_message(message_xml)
  multipart_message = init_multipart_message(message_xml)
  add_attachments_to_multipart_message(multipart_message)

  multipart_message.ready_to_send!

  # the mail.body.encoded algorithm reorders the parts, default order is [ "text/plain", "text/enriched", "text/html" ]
  # should redefine the sort order, because the soap request xml should be the first
  multipart_message.body.set_sort_order [ "text/xml" ]

  multipart_message.body.encoded(multipart_message.content_transfer_encoding)
end
build_xml() click to toggle source
# File lib/savon/builder.rb, line 236
def build_xml
  tag(builder, :Envelope, namespaces_with_globals) do |xml|
    tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
    tag(xml, :Body, body_attributes) do
      if @globals[:no_message_tag]
        xml << message.to_s
      else
        xml.tag!(*namespaced_message_tag) { xml << body_message }
      end
    end
  end
end
builder() click to toggle source
# File lib/savon/builder.rb, line 222
def builder
  builder = ::Builder::XmlMarkup.new
  builder.instruct!(:xml, :encoding => @globals[:encoding])
  builder
end
convert_type_definitions_to_hash() click to toggle source
# File lib/savon/builder.rb, line 79
def convert_type_definitions_to_hash
  @wsdl.type_definitions.inject({}) do |memo, (path, type)|
    memo[path] = type
    memo
  end
end
convert_type_namespaces_to_hash() click to toggle source
# File lib/savon/builder.rb, line 86
def convert_type_namespaces_to_hash
  @wsdl.type_namespaces.inject({}) do |memo, (path, uri)|
    key, value = use_namespace(path, uri)
    memo[key] = value
    memo
  end
end
env_namespace() click to toggle source
# File lib/savon/builder.rb, line 138
def env_namespace
  @env_namespace ||= @globals[:env_namespace] || :env
end
header() click to toggle source
# File lib/savon/builder.rb, line 142
def header
  @header ||= Header.new(@globals, @locals)
end
init_multipart_message(message_xml) click to toggle source
# File lib/savon/builder.rb, line 262
def init_multipart_message(message_xml)
  multipart_message = Mail.new
  xml_part = Mail::Part.new do
    content_type 'text/xml'
    body message_xml
    # in Content-Type the start parameter is recommended (RFC 2387)
    content_id '<soap-request-body@soap>'
  end
  multipart_message.add_part xml_part

  #request.headers["Content-Type"] = "multipart/related; boundary=\"#{multipart_message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\""
  @multipart = {
    multipart_boundary: multipart_message.body.boundary,
    start: xml_part.content_id,
  }

  multipart_message
end
message() click to toggle source
# File lib/savon/builder.rb, line 197
def message
  element_form_default = @globals[:element_form_default] || @wsdl.element_form_default
  # TODO: clean this up! [dh, 2012-12-17]
  Message.new(message_tag, namespace_identifier, @types, @used_namespaces, @locals[:message],
              element_form_default, @globals[:convert_request_keys_to], @globals[:unwrap])
end
message_attributes() click to toggle source
# File lib/savon/builder.rb, line 185
def message_attributes
  @locals[:attributes] || {}
end
message_tag() click to toggle source
# File lib/savon/builder.rb, line 174
def message_tag
  wsdl_tag_name = @wsdl.document? && @wsdl.soap_input(@operation_name.to_sym)

  message_tag = wsdl_tag_name.keys.first if wsdl_tag_name.is_a?(Hash)
  message_tag ||= @locals[:message_tag]
  message_tag ||= wsdl_tag_name
  message_tag ||= Gyoku.xml_tag(@operation_name, :key_converter => @globals[:convert_request_keys_to])

  message_tag.to_sym
end
namespace_by_uri(uri) click to toggle source
# File lib/savon/builder.rb, line 215
def namespace_by_uri(uri)
  namespaces.each do |candidate_identifier, candidate_uri|
    return candidate_identifier.gsub(/^xmlns:/, '') if candidate_uri == uri
  end
  nil
end
namespace_identifier() click to toggle source
# File lib/savon/builder.rb, line 204
def namespace_identifier
  return @globals[:namespace_identifier] if @globals.include? :namespace_identifier
  return @namespace_identifier if @namespace_identifier

  operation = @wsdl.operations[@operation_name] if @wsdl.document?
  namespace_identifier = operation[:namespace_identifier] if operation
  namespace_identifier ||= "wsdl"

  @namespace_identifier = namespace_identifier.to_sym
end
namespaced_message_tag() click to toggle source
# File lib/savon/builder.rb, line 146
def namespaced_message_tag
  tag_name = message_tag
  return [tag_name] if @wsdl.document? and @wsdl.soap_input(@operation_name.to_sym).is_a?(Hash)
  if namespace_identifier == nil
    [tag_name, message_attributes]
  elsif @used_namespaces[[tag_name.to_s]]
    [@used_namespaces[[tag_name.to_s]], tag_name, message_attributes]
  else
    [namespace_identifier, tag_name, message_attributes]
  end
end
namespaces() click to toggle source
# File lib/savon/builder.rb, line 110
def namespaces
  @namespaces ||= begin
    namespaces = SCHEMA_TYPES.dup

    # check namespace_identifier
    namespaces["xmlns#{namespace_identifier.nil? ? '' : ":#{namespace_identifier}"}"] =
      @globals[:namespace] || @wsdl.namespace

    # check env_namespace
    namespaces["xmlns#{env_namespace && env_namespace != "" ? ":#{env_namespace}" : ''}"] =
      SOAP_NAMESPACE[@globals[:soap_version]]

    if @wsdl&.document
      @wsdl.parser.namespaces.each do |identifier, path|
        next if identifier == 'xmlns' # Do not include xmlns namespace as this causes issues for some servers (https://github.com/savonrb/savon/issues/986)

        prefixed_identifier = "xmlns:#{identifier}"

        next if namespaces.key?(prefixed_identifier)

        namespaces[prefixed_identifier] = path
      end
    end

    namespaces
  end
end
namespaces_with_globals() click to toggle source
# File lib/savon/builder.rb, line 106
def namespaces_with_globals
  namespaces.merge @globals[:namespaces]
end
serialized_message_tag() click to toggle source
# File lib/savon/builder.rb, line 158
def serialized_message_tag
  [:wsdl, @wsdl.soap_input(@operation_name.to_sym).keys.first, {}]
end
serialized_messages() click to toggle source
# File lib/savon/builder.rb, line 162
def serialized_messages
  messages = ""
  message_tag = serialized_message_tag[1]
  @wsdl.soap_input(@operation_name.to_sym)[message_tag].each_pair do |message, type|
    break if @locals[:message].nil?
    message_locals = @locals[:message][StringUtils.snakecase(message).to_sym]
    message_content = Message.new(message_tag, namespace_identifier, @types, @used_namespaces, message_locals, :unqualified, @globals[:convert_request_keys_to], @globals[:unwrap]).to_s
    messages += "<#{message} xsi:type=\"#{type.join(':')}\">#{message_content}</#{message}>"
  end
  messages
end
tag(xml, name, namespaces = {}, &block) click to toggle source
# File lib/savon/builder.rb, line 228
def tag(xml, name, namespaces = {}, &block)
  if env_namespace && env_namespace != ""
    xml.tag! env_namespace, name, namespaces, &block
  else
    xml.tag! name, namespaces, &block
  end
end
use_namespace(path, uri) click to toggle source
# File lib/savon/builder.rb, line 94
def use_namespace(path, uri)
  @internal_namespace_count ||= 0

  unless identifier = namespace_by_uri(uri)
    identifier = "ins#{@internal_namespace_count}"
    namespaces["xmlns:#{identifier}"] = uri
    @internal_namespace_count += 1
  end

  [path, identifier]
end