johnreitano-savon 0.7.2.1 → 0.7.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +36 -4
- data/README.rdoc +78 -0
- data/Rakefile +15 -9
- data/lib/savon.rb +18 -20
- data/lib/savon/client.rb +60 -32
- data/lib/savon/core_ext.rb +8 -3
- data/lib/savon/core_ext/array.rb +31 -0
- data/lib/savon/core_ext/datetime.rb +1 -1
- data/lib/savon/core_ext/hash.rb +74 -50
- data/lib/savon/core_ext/net_http.rb +1 -2
- data/lib/savon/core_ext/object.rb +1 -3
- data/lib/savon/core_ext/string.rb +23 -4
- data/lib/savon/core_ext/uri.rb +3 -3
- data/lib/savon/logger.rb +56 -0
- data/lib/savon/request.rb +61 -60
- data/lib/savon/response.rb +68 -12
- data/lib/savon/soap.rb +180 -54
- data/lib/savon/wsdl.rb +77 -62
- data/lib/savon/wsdl_stream.rb +85 -0
- data/lib/savon/wsse.rb +45 -18
- data/spec/basic_spec_helper.rb +0 -1
- data/spec/endpoint_helper.rb +1 -0
- data/spec/fixtures/wsdl/xml/geotrust.xml +156 -0
- data/spec/http_stubs.rb +3 -0
- data/spec/savon/client_spec.rb +6 -2
- data/spec/savon/core_ext/array_spec.rb +19 -0
- data/spec/savon/core_ext/datetime_spec.rb +1 -1
- data/spec/savon/core_ext/hash_spec.rb +104 -64
- data/spec/savon/core_ext/object_spec.rb +1 -1
- data/spec/savon/core_ext/string_spec.rb +21 -2
- data/spec/savon/core_ext/uri_spec.rb +4 -0
- data/spec/savon/request_spec.rb +12 -8
- data/spec/savon/soap_spec.rb +141 -111
- data/spec/savon/wsdl_spec.rb +18 -0
- metadata +39 -19
- data/README.textile +0 -75
- data/spec/savon/savon_spec.rb +0 -23
@@ -6,8 +6,7 @@ module Net
|
|
6
6
|
@address, @port = address, port
|
7
7
|
end
|
8
8
|
|
9
|
-
# Convenience method for setting SSL client authentication
|
10
|
-
# through a Hash of +options+.
|
9
|
+
# Convenience method for setting SSL client authentication through a Hash of +options+.
|
11
10
|
def ssl_client_auth(options)
|
12
11
|
self.use_ssl = true
|
13
12
|
self.cert = options[:cert] if options[:cert]
|
@@ -1,8 +1,6 @@
|
|
1
1
|
class Object
|
2
2
|
|
3
|
-
# Returns +true+ if the Object is
|
4
|
-
# For example, "", false, nil, [], and {} are blank.
|
5
|
-
# Implementation from ActiveSupport.
|
3
|
+
# Returns +true+ if the Object is nil, false or empty. Implementation from ActiveSupport.
|
6
4
|
def blank?
|
7
5
|
respond_to?(:empty?) ? empty? : !self
|
8
6
|
end unless defined? blank?
|
@@ -26,22 +26,41 @@ class String
|
|
26
26
|
str
|
27
27
|
end
|
28
28
|
|
29
|
+
# Returns whether the String starts with a given +prefix+.
|
30
|
+
def starts_with?(prefix)
|
31
|
+
prefix = prefix.to_s
|
32
|
+
self[0, prefix.length] == prefix
|
33
|
+
end unless defined? starts_with?
|
34
|
+
|
35
|
+
# Returns whether the String ends with a given +suffix+.
|
36
|
+
def ends_with?(suffix)
|
37
|
+
suffix = suffix.to_s
|
38
|
+
self[-suffix.length, suffix.length] == suffix
|
39
|
+
end unless defined? ends_with?
|
40
|
+
|
29
41
|
# Returns the String without namespace.
|
30
42
|
def strip_namespace
|
31
|
-
|
43
|
+
split(":").last
|
32
44
|
end
|
33
45
|
|
34
|
-
# Translates SOAP response values to
|
46
|
+
# Translates SOAP response values to Ruby Objects.
|
35
47
|
def map_soap_response
|
36
|
-
return DateTime.parse(
|
48
|
+
return DateTime.parse(self) if Savon::SOAP::DateTimeRegexp === self
|
37
49
|
return true if self.strip.downcase == "true"
|
38
50
|
return false if self.strip.downcase == "false"
|
39
51
|
self
|
40
52
|
end
|
41
53
|
|
42
54
|
# Returns the String as a SOAP request compliant value.
|
55
|
+
# Escapes special characters for XML.
|
43
56
|
def to_soap_value
|
44
|
-
|
57
|
+
str = dup
|
58
|
+
str.gsub! "&", "&"
|
59
|
+
str.gsub! '"', """
|
60
|
+
str.gsub! "'", "'"
|
61
|
+
str.gsub! "<", "<"
|
62
|
+
str.gsub! ">", ">"
|
63
|
+
str
|
45
64
|
end
|
46
65
|
|
47
66
|
end
|
data/lib/savon/core_ext/uri.rb
CHANGED
data/lib/savon/logger.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Savon
|
2
|
+
|
3
|
+
# = Savon::Logger
|
4
|
+
#
|
5
|
+
# Savon::Logger can be mixed into classes to provide logging behavior.
|
6
|
+
#
|
7
|
+
# By default, the Logger mixin uses {Ruby's Logger}[http://ruby-doc.org/stdlib/libdoc/logger/rdoc/]
|
8
|
+
# from the standard library, a log level of :debug and is pointing to STDOUT.
|
9
|
+
module Logger
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
# Sets whether to log.
|
14
|
+
def log=(log)
|
15
|
+
@log = log
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns whether to log. Defaults to +true+.
|
19
|
+
def log?
|
20
|
+
@log != false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sets the logger.
|
24
|
+
def logger=(logger)
|
25
|
+
@logger = logger
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the logger. Defaults to an instance of +Logger+ writing to STDOUT.
|
29
|
+
def logger
|
30
|
+
@logger ||= ::Logger.new STDOUT
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets the log level.
|
34
|
+
def log_level=(log_level)
|
35
|
+
@log_level = log_level
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the log level. Defaults to +debug+.
|
39
|
+
def log_level
|
40
|
+
@log_level ||= :debug
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# Extends the class including this module with its ClassMethods.
|
46
|
+
def self.included(base)
|
47
|
+
base.extend ClassMethods
|
48
|
+
end
|
49
|
+
|
50
|
+
# Logs a given +message+.
|
51
|
+
def log(message)
|
52
|
+
self.class.logger.send self.class.log_level, message if self.class.log?
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
data/lib/savon/request.rb
CHANGED
@@ -1,51 +1,50 @@
|
|
1
1
|
module Savon
|
2
2
|
|
3
|
-
#
|
3
|
+
# = Savon::Request
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Savon::Request handles both WSDL and SOAP requests.
|
6
|
+
#
|
7
|
+
# == The Net::HTTP object
|
8
|
+
#
|
9
|
+
# You can access the Net::HTTP object used for both WSDL and SOAP requests via:
|
10
|
+
#
|
11
|
+
# client.request.http
|
12
|
+
#
|
13
|
+
# Here's an example of how to set open and read timeouts on the Net::HTTP object.
|
14
|
+
#
|
15
|
+
# client.request.http.open_timeout = 30
|
16
|
+
# client.request.http.read_timeout = 30
|
17
|
+
#
|
18
|
+
# Please refer to the {Net::HTTP documentation}[http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/]
|
19
|
+
# for more information.
|
20
|
+
#
|
21
|
+
# == HTTP basic authentication
|
22
|
+
#
|
23
|
+
# Setting credentials for HTTP basic authentication:
|
24
|
+
#
|
25
|
+
# client.request.basic_auth "username", "password"
|
26
|
+
#
|
27
|
+
# == SSL client authentication
|
28
|
+
#
|
29
|
+
# You can use the methods provided by Net::HTTP to set SSL client authentication or use a shortcut:
|
30
|
+
#
|
31
|
+
# client.request.http.ssl_client_auth(
|
32
|
+
# :cert => OpenSSL::X509::Certificate.new(File.read("client_cert.pem")),
|
33
|
+
# :key => OpenSSL::PKey::RSA.new(File.read("client_key.pem"), "password if one exists"),
|
34
|
+
# :ca_file => "cacert.pem",
|
35
|
+
# :verify_mode => OpenSSL::SSL::VERIFY_PEER
|
36
|
+
# )
|
37
|
+
#
|
38
|
+
# == HTTP headers
|
39
|
+
#
|
40
|
+
# There's an accessor for the Hash of HTTP headers sent with any SOAP call:
|
41
|
+
#
|
42
|
+
# client.request.headers["custom"] = "header"
|
6
43
|
class Request
|
44
|
+
include Logger
|
7
45
|
|
8
46
|
# Content-Types by SOAP version.
|
9
|
-
ContentType = { 1 => "text/xml", 2 => "application/soap+xml" }
|
10
|
-
|
11
|
-
# Whether to log HTTP requests.
|
12
|
-
@@log = true
|
13
|
-
|
14
|
-
# The default logger.
|
15
|
-
@@logger = Logger.new STDOUT
|
16
|
-
|
17
|
-
# The default log level.
|
18
|
-
@@log_level = :debug
|
19
|
-
|
20
|
-
# Sets whether to log HTTP requests.
|
21
|
-
def self.log=(log)
|
22
|
-
@@log = log
|
23
|
-
end
|
24
|
-
|
25
|
-
# Returns whether to log HTTP requests.
|
26
|
-
def self.log?
|
27
|
-
@@log
|
28
|
-
end
|
29
|
-
|
30
|
-
# Sets the logger.
|
31
|
-
def self.logger=(logger)
|
32
|
-
@@logger = logger
|
33
|
-
end
|
34
|
-
|
35
|
-
# Returns the logger.
|
36
|
-
def self.logger
|
37
|
-
@@logger
|
38
|
-
end
|
39
|
-
|
40
|
-
# Sets the log level.
|
41
|
-
def self.log_level=(log_level)
|
42
|
-
@@log_level = log_level
|
43
|
-
end
|
44
|
-
|
45
|
-
# Returns the log level.
|
46
|
-
def self.log_level
|
47
|
-
@@log_level
|
48
|
-
end
|
47
|
+
ContentType = { 1 => "text/xml;charset=UTF-8", 2 => "application/soap+xml;charset=UTF-8" }
|
49
48
|
|
50
49
|
# Expects a SOAP +endpoint+ String. Also accepts an optional Hash
|
51
50
|
# of +options+ for specifying a proxy server.
|
@@ -75,6 +74,12 @@ module Savon
|
|
75
74
|
@basic_auth = [username, password]
|
76
75
|
end
|
77
76
|
|
77
|
+
# Sets the +username+ and +password+ for NTLM authentication
|
78
|
+
# if +username+ does not work try 'domain\username'
|
79
|
+
def ntlm_auth(username, password)
|
80
|
+
@ntlm_auth = [username, password]
|
81
|
+
end
|
82
|
+
|
78
83
|
# Retrieves WSDL document and returns the Net::HTTP response.
|
79
84
|
def wsdl
|
80
85
|
log "Retrieving WSDL from: #{@endpoint}"
|
@@ -83,8 +88,7 @@ module Savon
|
|
83
88
|
http.start { |h| h.request request(:wsdl) }
|
84
89
|
end
|
85
90
|
|
86
|
-
# Executes a SOAP request using a given Savon::SOAP instance and
|
87
|
-
# returns the Net::HTTP response.
|
91
|
+
# Executes a SOAP request using a given Savon::SOAP instance and returns the Net::HTTP response.
|
88
92
|
def soap(soap)
|
89
93
|
@soap = soap
|
90
94
|
http.endpoint @soap.endpoint.host, @soap.endpoint.port
|
@@ -108,7 +112,7 @@ module Savon
|
|
108
112
|
# Logs the SOAP request.
|
109
113
|
def log_request
|
110
114
|
log "SOAP request: #{@soap.endpoint}"
|
111
|
-
log
|
115
|
+
log soap_headers.merge(headers).map { |key, value| "#{key}: #{value}" }.join(", ")
|
112
116
|
log @soap.to_xml
|
113
117
|
end
|
114
118
|
|
@@ -118,14 +122,21 @@ module Savon
|
|
118
122
|
log @response.body
|
119
123
|
end
|
120
124
|
|
121
|
-
# Returns a Net::HTTP request for a given +type+. Yields the request
|
122
|
-
# to an optional block.
|
125
|
+
# Returns a Net::HTTP request for a given +type+. Yields the request to an optional block.
|
123
126
|
def request(type)
|
124
127
|
request = case type
|
125
|
-
when :wsdl then Net::HTTP::Get.new @endpoint.
|
126
|
-
when :soap then Net::HTTP::Post.new @soap.endpoint.
|
128
|
+
when :wsdl then Net::HTTP::Get.new @endpoint.request_uri, headers
|
129
|
+
when :soap then Net::HTTP::Post.new @soap.endpoint.request_uri, soap_headers.merge(headers)
|
130
|
+
end
|
131
|
+
|
132
|
+
case
|
133
|
+
when @basic_auth
|
134
|
+
request.basic_auth(*@basic_auth)
|
135
|
+
when @ntlm_auth
|
136
|
+
log "Attempting NTLM Authentication"
|
137
|
+
request.ntlm_auth(*@ntlm_auth)
|
127
138
|
end
|
128
|
-
request.basic_auth
|
139
|
+
request.basic_auth(*@basic_auth) if @basic_auth
|
129
140
|
yield request if block_given?
|
130
141
|
request
|
131
142
|
end
|
@@ -135,15 +146,5 @@ module Savon
|
|
135
146
|
{ "Content-Type" => ContentType[@soap.version], "SOAPAction" => @soap.action }
|
136
147
|
end
|
137
148
|
|
138
|
-
# Logs a given +message+.
|
139
|
-
def log(message)
|
140
|
-
self.class.logger.send self.class.log_level, message if log?
|
141
|
-
end
|
142
|
-
|
143
|
-
# Returns whether to log.
|
144
|
-
def log?
|
145
|
-
self.class.log? && self.class.logger.respond_to?(self.class.log_level)
|
146
|
-
end
|
147
|
-
|
148
149
|
end
|
149
150
|
end
|
data/lib/savon/response.rb
CHANGED
@@ -1,10 +1,66 @@
|
|
1
1
|
module Savon
|
2
2
|
|
3
|
-
#
|
3
|
+
# = Savon::Response
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Savon::Response represents both HTTP and SOAP response.
|
6
|
+
#
|
7
|
+
# == SOAP fault
|
8
|
+
#
|
9
|
+
# Assuming the default behavior of raising errors is disabled, you can ask the response object
|
10
|
+
# if there was a SOAP fault or an HTTP error and get the SOAP fault or HTTP error message.
|
11
|
+
#
|
12
|
+
# response.soap_fault?
|
13
|
+
# # => true
|
14
|
+
#
|
15
|
+
# response.soap_fault
|
16
|
+
# # => "(soap:Server) Fault occurred while processing."
|
17
|
+
#
|
18
|
+
# response.http_error?
|
19
|
+
# # => true
|
20
|
+
#
|
21
|
+
# response.http_error
|
22
|
+
# # => "Not found (404)"
|
23
|
+
#
|
24
|
+
# == Response as XML
|
25
|
+
#
|
26
|
+
# To get the raw SOAP response XML, you can call to_xml or to_s on the response object.
|
27
|
+
#
|
28
|
+
# response.to_xml
|
29
|
+
# => "<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
30
|
+
# => "..."
|
31
|
+
# => "</soap:Envelope>"
|
32
|
+
#
|
33
|
+
# == Response as a Hash
|
34
|
+
#
|
35
|
+
# You can also let Savon translate the SOAP response body to a Hash.
|
36
|
+
#
|
37
|
+
# response.to_hash
|
38
|
+
# => { :findUserByIdResponse => {
|
39
|
+
# => :id => "123",
|
40
|
+
# => :username => "eve"
|
41
|
+
# => :active => true
|
42
|
+
# => }
|
43
|
+
#
|
44
|
+
# When translating the SOAP response to a Hash, some XML tags and values are converted to more
|
45
|
+
# convenient Ruby objects. Translation is done through John Nunemaker's {Crack}[http://github.com/jnunemaker/crack]
|
46
|
+
# library along with some custom mapping.
|
47
|
+
#
|
48
|
+
# * XML tags (Hash keys) are converted to snake_case Symbols and namespaces are stripped off
|
49
|
+
# * SOAP xs:nil values are converted to nil objects
|
50
|
+
# * XML values specified in xs:DateTime format are converted to DateTime objects
|
51
|
+
# * XML values of "true" and "false" are converted to TrueClass and FalseClass
|
52
|
+
#
|
53
|
+
# == Net::HTTP response
|
54
|
+
#
|
55
|
+
# If for some reason you need to access the Net::HTTP response object ... you can.
|
56
|
+
#
|
57
|
+
# bc. response.http
|
58
|
+
# => #<Net::HTTPOK:0x7f749a1aa4a8>
|
6
59
|
class Response
|
7
60
|
|
61
|
+
# The maximum HTTP response code considered to be OK.
|
62
|
+
MaxNonErrorResponseCode = 299
|
63
|
+
|
8
64
|
# The global setting of whether to raise errors.
|
9
65
|
@@raise_errors = true
|
10
66
|
|
@@ -28,7 +84,7 @@ module Savon
|
|
28
84
|
|
29
85
|
# Returns whether there was a SOAP fault.
|
30
86
|
def soap_fault?
|
31
|
-
|
87
|
+
!@soap_fault.blank?
|
32
88
|
end
|
33
89
|
|
34
90
|
# Returns the SOAP fault message.
|
@@ -36,7 +92,7 @@ module Savon
|
|
36
92
|
|
37
93
|
# Returns whether there was an HTTP error.
|
38
94
|
def http_error?
|
39
|
-
|
95
|
+
!@http_error.blank?
|
40
96
|
end
|
41
97
|
|
42
98
|
# Returns the HTTP error message.
|
@@ -54,13 +110,13 @@ module Savon
|
|
54
110
|
|
55
111
|
# Returns the HTTP response object.
|
56
112
|
attr_reader :http
|
57
|
-
|
113
|
+
|
58
114
|
alias :to_s :to_xml
|
59
115
|
|
60
116
|
private
|
61
117
|
|
62
|
-
# Handles SOAP faults. Raises a Savon::SOAPFault unless the default
|
63
|
-
#
|
118
|
+
# Handles SOAP faults. Raises a Savon::SOAPFault unless the default behavior of raising errors
|
119
|
+
# was turned off.
|
64
120
|
def handle_soap_fault
|
65
121
|
if soap_fault_message
|
66
122
|
@soap_fault = soap_fault_message
|
@@ -73,8 +129,8 @@ module Savon
|
|
73
129
|
@soap_fault_message ||= soap_fault_message_by_version to_hash[:fault]
|
74
130
|
end
|
75
131
|
|
76
|
-
# Expects a Hash that might contain information about a SOAP fault.
|
77
|
-
#
|
132
|
+
# Expects a Hash that might contain information about a SOAP fault. Returns the SOAP fault
|
133
|
+
# message in case one was found.
|
78
134
|
def soap_fault_message_by_version(soap_fault)
|
79
135
|
return unless soap_fault
|
80
136
|
|
@@ -85,10 +141,10 @@ module Savon
|
|
85
141
|
end
|
86
142
|
end
|
87
143
|
|
88
|
-
# Handles HTTP errors. Raises a Savon::HTTPError unless the default
|
89
|
-
#
|
144
|
+
# Handles HTTP errors. Raises a Savon::HTTPError unless the default behavior of raising errors
|
145
|
+
# was turned off.
|
90
146
|
def handle_http_error
|
91
|
-
if @http.code.to_i
|
147
|
+
if @http.code.to_i > MaxNonErrorResponseCode
|
92
148
|
@http_error = "#{@http.message} (#{@http.code})"
|
93
149
|
@http_error << ": #{@http.body}" unless @http.body.empty?
|
94
150
|
raise Savon::HTTPError, http_error if self.class.raise_errors?
|
data/lib/savon/soap.rb
CHANGED
@@ -1,12 +1,125 @@
|
|
1
1
|
module Savon
|
2
2
|
|
3
|
-
#
|
3
|
+
# = Savon::SOAP
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Savon::SOAP represents the SOAP request. Pass a block to your SOAP call and the SOAP object is
|
6
|
+
# passed to it as the first argument. The object allows setting the SOAP version, header, body
|
7
|
+
# and namespaces per request.
|
8
|
+
#
|
9
|
+
# == Body
|
10
|
+
#
|
11
|
+
# The body method lets you specify parameters to be received by the SOAP action.
|
12
|
+
#
|
13
|
+
# You can either pass in a hash (which will be translated to XML via Hash.to_soap_xml):
|
14
|
+
#
|
15
|
+
# response = client.get_user_by_id do |soap|
|
16
|
+
# soap.body = { :id => 123 }
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Or a string containing the raw XML:
|
20
|
+
#
|
21
|
+
# response = client.get_user_by_id do |soap|
|
22
|
+
# soap.body = "<id>123</id>"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Request output:
|
26
|
+
#
|
27
|
+
# <env:Envelope
|
28
|
+
# xmlns:wsdl="http://example.com/user/1.0/UserService"
|
29
|
+
# xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
30
|
+
# <env:Body>
|
31
|
+
# <wsdl:getUserById><id>123</id></wsdl:getUserById>
|
32
|
+
# </env:Body>
|
33
|
+
# </env:Envelope>
|
34
|
+
#
|
35
|
+
# Please look at the documentation of Hash.to_soap_xml for some more information.
|
36
|
+
#
|
37
|
+
# == Version
|
38
|
+
#
|
39
|
+
# Savon defaults to SOAP 1.1. In case your service uses SOAP 1.2, you can use the version method
|
40
|
+
# to change the default per request.
|
41
|
+
#
|
42
|
+
# response = client.get_all_users do |soap|
|
43
|
+
# soap.version = 2
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# You can also change the default to SOAP 1.2 for all request:
|
47
|
+
#
|
48
|
+
# Savon::SOAP.version = 2
|
49
|
+
#
|
50
|
+
# == Header
|
51
|
+
#
|
52
|
+
# If you need to add custom XML into the SOAP header, you can use the header method.
|
53
|
+
#
|
54
|
+
# The value is expected to be a hash (which will be translated to XML via Hash.to_soap_xml):
|
55
|
+
#
|
56
|
+
# response = client.get_all_users do |soap|
|
57
|
+
# soap.header["specialApiKey"] = "secret"
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# Or a string containing the raw XML:
|
61
|
+
#
|
62
|
+
# response = client.get_all_users do |soap|
|
63
|
+
# soap.header = "<specialApiKey>secret</specialApiKey>"
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# Request output:
|
67
|
+
#
|
68
|
+
# <env:Envelope
|
69
|
+
# xmlns:wsdl="http://example.com/user/1.0/UserService"
|
70
|
+
# xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
71
|
+
# <env:Header>
|
72
|
+
# <specialApiKey>secret</specialApiKey>
|
73
|
+
# </env:Header>
|
74
|
+
# <env:Body>
|
75
|
+
# <wsdl:getAllUsers></wsdl:getAllUsers>
|
76
|
+
# </env:Body>
|
77
|
+
# </env:Envelope>
|
78
|
+
#
|
79
|
+
# == Namespaces
|
80
|
+
#
|
81
|
+
# The namespaces method contains a hash of attributes for the SOAP envelope. You can overwrite it
|
82
|
+
# or add additional attributes.
|
83
|
+
#
|
84
|
+
# response = client.get_all_users do |soap|
|
85
|
+
# soap.namespaces["xmlns:domains"] = "http://domains.example.com"
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# Request output:
|
89
|
+
#
|
90
|
+
# <env:Envelope
|
91
|
+
# xmlns:wsdl="http://example.com/user/1.0/UserService"
|
92
|
+
# xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
|
93
|
+
# xmlns:domains="http://domains.example.com">
|
94
|
+
# <env:Body>
|
95
|
+
# <wsdl:getAllUsers></wsdl:getAllUsers>
|
96
|
+
# </env:Body>
|
97
|
+
# </env:Envelope>
|
98
|
+
#
|
99
|
+
# == Input
|
100
|
+
#
|
101
|
+
# You can change the name of the SOAP input tag in case you need to.
|
102
|
+
#
|
103
|
+
# response = client.get_all_users do |soap|
|
104
|
+
# soap.input = "GetAllUsersRequest"
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# Request output:
|
108
|
+
#
|
109
|
+
# <env:Envelope
|
110
|
+
# xmlns:wsdl="http://example.com/user/1.0/UserService"
|
111
|
+
# xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
112
|
+
# <env:Body>
|
113
|
+
# <wsdl:GetAllUsersRequest></wsdl:GetAllUsersRequest>
|
114
|
+
# </env:Body>
|
115
|
+
# </env:Envelope>
|
6
116
|
class SOAP
|
7
117
|
|
118
|
+
# Supported SOAP versions.
|
119
|
+
Versions = [1, 2]
|
120
|
+
|
8
121
|
# SOAP namespaces by SOAP version.
|
9
|
-
|
122
|
+
Namespace = {
|
10
123
|
1 => "http://schemas.xmlsoap.org/soap/envelope/",
|
11
124
|
2 => "http://www.w3.org/2003/05/soap-envelope"
|
12
125
|
}
|
@@ -14,6 +127,12 @@ module Savon
|
|
14
127
|
# Content-Types by SOAP version.
|
15
128
|
ContentType = { 1 => "text/xml", 2 => "application/soap+xml" }
|
16
129
|
|
130
|
+
# SOAP xs:dateTime format.
|
131
|
+
DateTimeFormat = "%Y-%m-%dT%H:%M:%SZ"
|
132
|
+
|
133
|
+
# SOAP xs:dateTime Regexp.
|
134
|
+
DateTimeRegexp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
|
135
|
+
|
17
136
|
# The global SOAP version.
|
18
137
|
@@version = 1
|
19
138
|
|
@@ -24,11 +143,11 @@ module Savon
|
|
24
143
|
|
25
144
|
# Sets the global SOAP version.
|
26
145
|
def self.version=(version)
|
27
|
-
@@version = version if
|
146
|
+
@@version = version if Versions.include? version
|
28
147
|
end
|
29
148
|
|
30
|
-
# Sets the global SOAP header. Expected to be a Hash that can be translated
|
31
|
-
#
|
149
|
+
# Sets the global SOAP header. Expected to be a Hash that can be translated to XML via
|
150
|
+
# Hash.to_soap_xml or any other Object responding to to_s.
|
32
151
|
def self.header=(header)
|
33
152
|
@@header = header
|
34
153
|
end
|
@@ -38,20 +157,22 @@ module Savon
|
|
38
157
|
@@header ||= {}
|
39
158
|
end
|
40
159
|
|
41
|
-
# Sets the global namespaces. Expected to be a Hash containing the
|
42
|
-
#
|
160
|
+
# Sets the global namespaces. Expected to be a Hash containing the namespaces (keys) and the
|
161
|
+
# corresponding URI's (values).
|
43
162
|
def self.namespaces=(namespaces)
|
44
163
|
@@namespaces = namespaces if namespaces.kind_of? Hash
|
45
164
|
end
|
46
165
|
|
47
|
-
# Returns the global namespaces. A Hash containing the namespaces (keys)
|
48
|
-
#
|
166
|
+
# Returns the global namespaces. A Hash containing the namespaces (keys) and the corresponding
|
167
|
+
# URI's (values).
|
49
168
|
def self.namespaces
|
50
169
|
@@namespaces ||= {}
|
51
170
|
end
|
52
171
|
|
53
|
-
# Initialzes the SOAP object.
|
54
|
-
def initialize
|
172
|
+
# Initialzes the SOAP object. Expects a SOAP +operation+ Hash along with an +endpoint+.
|
173
|
+
def initialize(action, input, endpoint)
|
174
|
+
@action, @input = action, input
|
175
|
+
@endpoint = endpoint.kind_of?(URI) ? endpoint : URI(endpoint)
|
55
176
|
@builder = Builder::XmlMarkup.new
|
56
177
|
end
|
57
178
|
|
@@ -77,8 +198,8 @@ module Savon
|
|
77
198
|
# Accessor for the SOAP endpoint.
|
78
199
|
attr_accessor :endpoint
|
79
200
|
|
80
|
-
# Sets the SOAP header. Expected to be a Hash that can be translated
|
81
|
-
#
|
201
|
+
# Sets the SOAP header. Expected to be a Hash that can be translated to XML via Hash.to_soap_xml
|
202
|
+
# or any other Object responding to to_s.
|
82
203
|
attr_writer :header
|
83
204
|
|
84
205
|
# Returns the SOAP header. Defaults to an empty Hash.
|
@@ -86,28 +207,32 @@ module Savon
|
|
86
207
|
@header ||= {}
|
87
208
|
end
|
88
209
|
|
89
|
-
#
|
90
|
-
#
|
91
|
-
|
210
|
+
# Accessor for the SOAP body. Expected to be a Hash that can be translated to XML via Hash.to_soap_xml
|
211
|
+
# or any other Object responding to to_s.
|
212
|
+
attr_accessor :body
|
213
|
+
|
214
|
+
# Accessor for overwriting the default SOAP request. Let's you specify completely custom XML.
|
215
|
+
attr_accessor :xml
|
92
216
|
|
93
|
-
# Sets the namespaces. Expected to be a Hash containing the namespaces
|
94
|
-
#
|
217
|
+
# Sets the namespaces. Expected to be a Hash containing the namespaces (keys) and the
|
218
|
+
# corresponding URI's (values).
|
95
219
|
attr_writer :namespaces
|
96
220
|
|
97
|
-
# Returns the namespaces. A Hash containing the namespaces (keys)
|
98
|
-
# and the
|
221
|
+
# Returns the namespaces. A Hash containing the namespaces (keys) and the corresponding URI's
|
222
|
+
# (values). Defaults to a Hash containing an +xmlns:env+ key and the namespace for the current
|
223
|
+
# SOAP version.
|
99
224
|
def namespaces
|
100
|
-
@namespaces ||= { "xmlns:env" =>
|
225
|
+
@namespaces ||= { "xmlns:env" => Namespace[version] }
|
101
226
|
end
|
102
227
|
|
103
|
-
# Convenience method for setting the
|
228
|
+
# Convenience method for setting the +xmlns:wsdl+ namespace.
|
104
229
|
def namespace=(namespace)
|
105
230
|
namespaces["xmlns:wsdl"] = namespace
|
106
231
|
end
|
107
232
|
|
108
233
|
# Sets the SOAP version.
|
109
234
|
def version=(version)
|
110
|
-
@version = version if
|
235
|
+
@version = version if Versions.include? version
|
111
236
|
end
|
112
237
|
|
113
238
|
# Returns the SOAP version. Defaults to the global default.
|
@@ -117,60 +242,61 @@ module Savon
|
|
117
242
|
|
118
243
|
# Returns the SOAP envelope XML.
|
119
244
|
def to_xml
|
120
|
-
unless @
|
121
|
-
@
|
122
|
-
|
245
|
+
unless @xml
|
246
|
+
@builder.instruct!
|
247
|
+
@xml = @builder.env :Envelope, merged_namespaces do |xml|
|
248
|
+
xml.env(:Header) { xml << merged_header } unless merged_header.empty?
|
123
249
|
xml_body xml
|
124
250
|
end
|
125
251
|
end
|
126
|
-
@
|
252
|
+
@xml
|
127
253
|
end
|
128
254
|
|
129
255
|
private
|
130
256
|
|
131
|
-
# Adds a SOAP XML header to a given +xml+ Object.
|
132
|
-
def xml_header(xml)
|
133
|
-
xml.env(:Header) do
|
134
|
-
xml << all_header + wsse_header
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
257
|
# Returns a String containing the global and per request header.
|
139
|
-
def
|
258
|
+
def merged_header
|
140
259
|
if self.class.header.kind_of?(Hash) && header.kind_of?(Hash)
|
141
|
-
self.class.header.merge(header).to_soap_xml
|
260
|
+
merged_header = self.class.header.merge(header).to_soap_xml
|
142
261
|
else
|
143
|
-
self.class.header.
|
262
|
+
global_header = self.class.header.to_soap_xml rescue self.class.header.to_s
|
263
|
+
request_header = header.to_soap_xml rescue header.to_s
|
264
|
+
merged_header = global_header + request_header
|
144
265
|
end
|
266
|
+
merged_header + wsse_header
|
267
|
+
end
|
268
|
+
|
269
|
+
# Returns the WSSE header or an empty String in case WSSE was not set.
|
270
|
+
def wsse_header
|
271
|
+
@wsse.respond_to?(:header) ? @wsse.header : ""
|
145
272
|
end
|
146
273
|
|
147
274
|
# Adds a SOAP XML body to a given +xml+ Object.
|
148
275
|
def xml_body(xml)
|
149
276
|
xml.env(:Body) do
|
150
|
-
xml.tag!(:wsdl, *input_array)
|
151
|
-
xml << (@body.to_soap_xml rescue @body.to_s)
|
152
|
-
end
|
277
|
+
xml.tag!(:wsdl, *input_array) { xml << (@body.to_soap_xml rescue @body.to_s) }
|
153
278
|
end
|
154
279
|
end
|
155
280
|
|
156
281
|
# Returns a Hash containing the global and per request namespaces.
|
157
|
-
def
|
282
|
+
def merged_namespaces
|
158
283
|
self.class.namespaces.merge namespaces
|
159
284
|
end
|
160
285
|
|
161
|
-
# Returns an Array of SOAP input names to append to the
|
162
|
-
#
|
163
|
-
#
|
286
|
+
# Returns an Array of SOAP input names to append to the wsdl namespace. Defaults to use the
|
287
|
+
# name of the SOAP action. May return an empty Array in case the specified SOAP input seems
|
288
|
+
# to be invalid.
|
164
289
|
def input_array
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
290
|
+
if input.kind_of?(Array) && !input.blank?
|
291
|
+
[input[0].to_sym, input[1]]
|
292
|
+
elsif !input.blank?
|
293
|
+
[input.to_sym]
|
294
|
+
elsif !action.blank?
|
295
|
+
[action.to_sym]
|
296
|
+
else
|
297
|
+
[]
|
298
|
+
end
|
173
299
|
end
|
174
300
|
|
175
301
|
end
|
176
|
-
end
|
302
|
+
end
|