savon 0.7.5 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/CHANGELOG +11 -0
  2. data/README.rdoc +5 -3
  3. data/Rakefile +2 -0
  4. data/lib/savon.rb +1 -0
  5. data/lib/savon/client.rb +54 -13
  6. data/lib/savon/core_ext.rb +0 -0
  7. data/lib/savon/core_ext/array.rb +20 -6
  8. data/lib/savon/core_ext/datetime.rb +0 -0
  9. data/lib/savon/core_ext/hash.rb +36 -15
  10. data/lib/savon/core_ext/net_http.rb +1 -2
  11. data/lib/savon/core_ext/object.rb +1 -3
  12. data/lib/savon/core_ext/string.rb +9 -2
  13. data/lib/savon/core_ext/symbol.rb +0 -0
  14. data/lib/savon/core_ext/uri.rb +1 -1
  15. data/lib/savon/logger.rb +56 -0
  16. data/lib/savon/request.rb +42 -50
  17. data/lib/savon/response.rb +62 -9
  18. data/lib/savon/soap.rb +157 -42
  19. data/lib/savon/wsdl.rb +71 -6
  20. data/lib/savon/wsdl_stream.rb +2 -2
  21. data/lib/savon/wsse.rb +36 -5
  22. data/spec/basic_spec_helper.rb +0 -0
  23. data/spec/endpoint_helper.rb +0 -0
  24. data/spec/fixtures/response/response_fixture.rb +0 -0
  25. data/spec/fixtures/response/xml/authentication.xml +0 -0
  26. data/spec/fixtures/response/xml/multi_ref.xml +0 -0
  27. data/spec/fixtures/response/xml/soap_fault.xml +0 -0
  28. data/spec/fixtures/response/xml/soap_fault12.xml +0 -0
  29. data/spec/fixtures/wsdl/wsdl_fixture.rb +0 -0
  30. data/spec/fixtures/wsdl/xml/authentication.xml +0 -0
  31. data/spec/fixtures/wsdl/xml/geotrust.xml +0 -0
  32. data/spec/fixtures/wsdl/xml/namespaced_actions.xml +0 -0
  33. data/spec/fixtures/wsdl/xml/no_namespace.xml +0 -0
  34. data/spec/http_stubs.rb +0 -0
  35. data/spec/integration/http_basic_auth_spec.rb +0 -0
  36. data/spec/integration/server.rb +0 -0
  37. data/spec/savon/client_spec.rb +5 -1
  38. data/spec/savon/core_ext/array_spec.rb +0 -0
  39. data/spec/savon/core_ext/datetime_spec.rb +0 -0
  40. data/spec/savon/core_ext/hash_spec.rb +10 -1
  41. data/spec/savon/core_ext/net_http_spec.rb +0 -0
  42. data/spec/savon/core_ext/object_spec.rb +0 -0
  43. data/spec/savon/core_ext/string_spec.rb +6 -2
  44. data/spec/savon/core_ext/symbol_spec.rb +0 -0
  45. data/spec/savon/core_ext/uri_spec.rb +4 -0
  46. data/spec/savon/request_spec.rb +5 -4
  47. data/spec/savon/response_spec.rb +0 -0
  48. data/spec/savon/soap_spec.rb +124 -130
  49. data/spec/savon/wsdl_spec.rb +0 -0
  50. data/spec/savon/wsse_spec.rb +0 -0
  51. data/spec/spec_helper.rb +0 -0
  52. metadata +55 -37
  53. data/readme/client.rdoc +0 -18
  54. data/readme/errors.rdoc +0 -11
  55. data/readme/logging.rdoc +0 -11
  56. data/readme/participate.rdoc +0 -21
  57. data/readme/request.rdoc +0 -37
  58. data/readme/response.rdoc +0 -46
  59. data/readme/soap.rdoc +0 -71
  60. data/readme/value_mapping.rdoc +0 -49
  61. data/readme/wsdl.rdoc +0 -39
  62. data/readme/wsse.rdoc +0 -28
@@ -1,8 +1,61 @@
1
1
  module Savon
2
2
 
3
- # == Savon::Response
3
+ # = Savon::Response
4
4
  #
5
- # Represents the HTTP and SOAP response.
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
 
8
61
  # The maximum HTTP response code considered to be OK.
@@ -57,13 +110,13 @@ module Savon
57
110
 
58
111
  # Returns the HTTP response object.
59
112
  attr_reader :http
60
-
113
+
61
114
  alias :to_s :to_xml
62
115
 
63
116
  private
64
117
 
65
- # Handles SOAP faults. Raises a Savon::SOAPFault unless the default
66
- # behavior of raising errors was turned off.
118
+ # Handles SOAP faults. Raises a Savon::SOAPFault unless the default behavior of raising errors
119
+ # was turned off.
67
120
  def handle_soap_fault
68
121
  if soap_fault_message
69
122
  @soap_fault = soap_fault_message
@@ -76,8 +129,8 @@ module Savon
76
129
  @soap_fault_message ||= soap_fault_message_by_version to_hash[:fault]
77
130
  end
78
131
 
79
- # Expects a Hash that might contain information about a SOAP fault.
80
- # Returns the SOAP fault message in case one was found.
132
+ # Expects a Hash that might contain information about a SOAP fault. Returns the SOAP fault
133
+ # message in case one was found.
81
134
  def soap_fault_message_by_version(soap_fault)
82
135
  return unless soap_fault
83
136
 
@@ -88,8 +141,8 @@ module Savon
88
141
  end
89
142
  end
90
143
 
91
- # Handles HTTP errors. Raises a Savon::HTTPError unless the default
92
- # behavior of raising errors was turned off.
144
+ # Handles HTTP errors. Raises a Savon::HTTPError unless the default behavior of raising errors
145
+ # was turned off.
93
146
  def handle_http_error
94
147
  if @http.code.to_i > MaxNonErrorResponseCode
95
148
  @http_error = "#{@http.message} (#{@http.code})"
@@ -1,10 +1,123 @@
1
1
  module Savon
2
2
 
3
- # == Savon::SOAP
3
+ # = Savon::SOAP
4
4
  #
5
- # Represents the SOAP parameters and envelope.
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/",
@@ -14,9 +127,6 @@ module Savon
14
127
  # Content-Types by SOAP version.
15
128
  ContentType = { 1 => "text/xml", 2 => "application/soap+xml" }
16
129
 
17
- # Supported SOAP versions.
18
- Versions = [1, 2]
19
-
20
130
  # SOAP xs:dateTime format.
21
131
  DateTimeFormat = "%Y-%m-%dT%H:%M:%SZ"
22
132
 
@@ -36,8 +146,8 @@ module Savon
36
146
  @@version = version if Versions.include? version
37
147
  end
38
148
 
39
- # Sets the global SOAP header. Expected to be a Hash that can be translated
40
- # to XML via Hash.to_soap_xml or any other Object responding to to_s.
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.
41
151
  def self.header=(header)
42
152
  @@header = header
43
153
  end
@@ -47,21 +157,21 @@ module Savon
47
157
  @@header ||= {}
48
158
  end
49
159
 
50
- # Sets the global namespaces. Expected to be a Hash containing the
51
- # namespaces (keys) and the corresponding URI's (values).
160
+ # Sets the global namespaces. Expected to be a Hash containing the namespaces (keys) and the
161
+ # corresponding URI's (values).
52
162
  def self.namespaces=(namespaces)
53
163
  @@namespaces = namespaces if namespaces.kind_of? Hash
54
164
  end
55
165
 
56
- # Returns the global namespaces. A Hash containing the namespaces (keys)
57
- # and the corresponding URI's (values).
166
+ # Returns the global namespaces. A Hash containing the namespaces (keys) and the corresponding
167
+ # URI's (values).
58
168
  def self.namespaces
59
169
  @@namespaces ||= {}
60
170
  end
61
171
 
62
- # Initialzes the SOAP object.
63
- def initialize(operation, endpoint)
64
- @action, @input = operation[:action], operation[:input]
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
65
175
  @endpoint = endpoint.kind_of?(URI) ? endpoint : URI(endpoint)
66
176
  @builder = Builder::XmlMarkup.new
67
177
  end
@@ -88,8 +198,8 @@ module Savon
88
198
  # Accessor for the SOAP endpoint.
89
199
  attr_accessor :endpoint
90
200
 
91
- # Sets the SOAP header. Expected to be a Hash that can be translated
92
- # to XML via Hash.to_soap_xml or any other Object responding to to_s.
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.
93
203
  attr_writer :header
94
204
 
95
205
  # Returns the SOAP header. Defaults to an empty Hash.
@@ -97,21 +207,25 @@ module Savon
97
207
  @header ||= {}
98
208
  end
99
209
 
100
- # Sets the SOAP body. Expected to be a Hash that can be translated to
101
- # XML via Hash.to_soap_xml or any other Object responding to to_s.
102
- attr_writer :body
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
103
213
 
104
- # Sets the namespaces. Expected to be a Hash containing the namespaces
105
- # (keys) and the corresponding URI's (values).
214
+ # Accessor for overwriting the default SOAP request. Let's you specify completely custom XML.
215
+ attr_accessor :xml
216
+
217
+ # Sets the namespaces. Expected to be a Hash containing the namespaces (keys) and the
218
+ # corresponding URI's (values).
106
219
  attr_writer :namespaces
107
220
 
108
- # Returns the namespaces. A Hash containing the namespaces (keys)
109
- # and the corresponding URI's (values).
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.
110
224
  def namespaces
111
225
  @namespaces ||= { "xmlns:env" => Namespace[version] }
112
226
  end
113
227
 
114
- # Convenience method for setting the "xmlns:wsdl" namespace.
228
+ # Convenience method for setting the +xmlns:wsdl+ namespace.
115
229
  def namespace=(namespace)
116
230
  namespaces["xmlns:wsdl"] = namespace
117
231
  end
@@ -128,25 +242,28 @@ module Savon
128
242
 
129
243
  # Returns the SOAP envelope XML.
130
244
  def to_xml
131
- unless @xml_body
132
- @xml_body = @builder.env :Envelope, all_namespaces do |xml|
133
- xml.env(:Header) { xml << all_header } unless all_header.empty?
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?
134
249
  xml_body xml
135
250
  end
136
251
  end
137
- @xml_body
252
+ @xml
138
253
  end
139
254
 
140
255
  private
141
256
 
142
257
  # Returns a String containing the global and per request header.
143
- def all_header
258
+ def merged_header
144
259
  if self.class.header.kind_of?(Hash) && header.kind_of?(Hash)
145
- custom_header = self.class.header.merge(header).to_soap_xml
260
+ merged_header = self.class.header.merge(header).to_soap_xml
146
261
  else
147
- custom_header = self.class.header.to_s + header.to_s
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
148
265
  end
149
- custom_header + wsse_header
266
+ merged_header + wsse_header
150
267
  end
151
268
 
152
269
  # Returns the WSSE header or an empty String in case WSSE was not set.
@@ -157,26 +274,24 @@ module Savon
157
274
  # Adds a SOAP XML body to a given +xml+ Object.
158
275
  def xml_body(xml)
159
276
  xml.env(:Body) do
160
- xml.tag!(:wsdl, *input_array) do
161
- xml << (@body.to_soap_xml rescue @body.to_s)
162
- end
277
+ xml.tag!(:wsdl, *input_array) { xml << (@body.to_soap_xml rescue @body.to_s) }
163
278
  end
164
279
  end
165
280
 
166
281
  # Returns a Hash containing the global and per request namespaces.
167
- def all_namespaces
282
+ def merged_namespaces
168
283
  self.class.namespaces.merge namespaces
169
284
  end
170
285
 
171
- # Returns an Array of SOAP input names to append to the :wsdl namespace.
172
- # Defaults to use the name of the SOAP action and may be an empty Array
173
- # in case the specified SOAP input seems invalid.
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.
174
289
  def input_array
175
- if !input.blank? && input.kind_of?(Array)
290
+ if input.kind_of?(Array) && !input.blank?
176
291
  [input[0].to_sym, input[1]]
177
292
  elsif !input.blank?
178
293
  [input.to_sym]
179
- elsif action.blank?
294
+ elsif !action.blank?
180
295
  [action.to_sym]
181
296
  else
182
297
  []
@@ -184,4 +299,4 @@ module Savon
184
299
  end
185
300
 
186
301
  end
187
- end
302
+ end
@@ -1,8 +1,74 @@
1
1
  module Savon
2
2
 
3
- # Savon::WSDL
3
+ # = Savon::WSDL
4
4
  #
5
- # Represents the WSDL document.
5
+ # Savon::WSDL represents the WSDL of your service, including information like the namespace URI,
6
+ # the SOAP endpoint and available SOAP actions.
7
+ #
8
+ # == The WSDL document
9
+ #
10
+ # Retrieve the raw WSDL document:
11
+ #
12
+ # client.wsdl.to_s
13
+ #
14
+ # == Available SOAP actions
15
+ #
16
+ # Get an array of available SOAP actions:
17
+ #
18
+ # client.wsdl.soap_actions
19
+ # # => [:get_all_users, :get_user_by_id]
20
+ #
21
+ # == Namespace URI
22
+ #
23
+ # Get the namespace URI:
24
+ #
25
+ # client.wsdl.namespace_uri
26
+ # # => "http://ws.userservice.example.com"
27
+ #
28
+ # == SOAP endpoint
29
+ #
30
+ # Get the SOAP endpoint:
31
+ #
32
+ # client.wsdl.soap_endpoint
33
+ # # => "http://example.com"
34
+ #
35
+ # == Disable Savon::WSDL
36
+ #
37
+ # Especially with large services (i.e. Ebay), getting and parsing the WSDL document can really
38
+ # slow down your request. The WSDL is great for exploring a service, but it's recommended to
39
+ # disable it for production.
40
+ #
41
+ # When disabling the WSDL, you need to pay attention to certain differences:
42
+ #
43
+ # 1. You instantiate Savon::Client with the actual SOAP endpoint instead of pointing it to the
44
+ # WSDL of your service.
45
+ # 2. You also need to manually specify the SOAP.namespace.
46
+ # 3. Append an exclamation mark (!) to your SOAP call:
47
+ #
48
+ # client = Savon::Client.new "http://example.com"
49
+ #
50
+ # client.get_user_by_id! do |soap|
51
+ # soap.namespace = "http://example.com/UserService"
52
+ # soap.body = { :id => 666 }
53
+ # end
54
+ #
55
+ # Without the WSDL, Savon also has to guess the name of the SOAP action and input tag. It takes
56
+ # the name of the method called on its client instance, converts it from snake_case to lowerCamelCase
57
+ # and uses the result.
58
+ #
59
+ # The example above expects a SOAP action with an original name of "getUserById". If you service
60
+ # uses UpperCamelCase method names, you can just use the original name:
61
+ #
62
+ # client.GetAllUsers!
63
+ #
64
+ # For special cases, you could also specify the SOAP.action and SOAP.input inside the block:
65
+ #
66
+ # client.get_user_by_id! do |soap|
67
+ # soap.namespace = "http://example.com/UserService"
68
+ # soap.action = "GetUserById"
69
+ # soap.input = "GetUserByIdRequest"
70
+ # soap.body = { :id => 123 }
71
+ # end
6
72
  class WSDL
7
73
 
8
74
  # Initializer, expects a Savon::Request.
@@ -45,11 +111,10 @@ module Savon
45
111
  super
46
112
  end
47
113
 
48
- # Returns a SOAP operation Hash containing the SOAP action and input
49
- # for a given +soap_call+.
114
+ # Returns an Array containg the SOAP action and input for a given +soap_call+.
50
115
  def operation_from(soap_action)
51
- return operations[soap_action] if enabled?
52
- { :action => soap_action.to_soap_key, :input => soap_action.to_soap_key }
116
+ return [soap_action.to_soap_key, soap_action.to_soap_key] unless enabled?
117
+ [operations[soap_action][:action], operations[soap_action][:input]]
53
118
  end
54
119
 
55
120
  # Returns the raw WSDL document.