savon_with_adapter 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +11 -0
  4. data/.yardopts +6 -0
  5. data/CHANGELOG.md +1042 -0
  6. data/CONTRIBUTING.md +46 -0
  7. data/Gemfile +18 -0
  8. data/LICENSE +20 -0
  9. data/README.md +81 -0
  10. data/Rakefile +14 -0
  11. data/donate.png +0 -0
  12. data/lib/savon.rb +27 -0
  13. data/lib/savon/block_interface.rb +26 -0
  14. data/lib/savon/builder.rb +166 -0
  15. data/lib/savon/client.rb +89 -0
  16. data/lib/savon/core_ext/string.rb +29 -0
  17. data/lib/savon/header.rb +70 -0
  18. data/lib/savon/http_error.rb +27 -0
  19. data/lib/savon/log_message.rb +48 -0
  20. data/lib/savon/message.rb +35 -0
  21. data/lib/savon/mock.rb +5 -0
  22. data/lib/savon/mock/expectation.rb +71 -0
  23. data/lib/savon/mock/spec_helper.rb +62 -0
  24. data/lib/savon/model.rb +80 -0
  25. data/lib/savon/operation.rb +127 -0
  26. data/lib/savon/options.rb +336 -0
  27. data/lib/savon/qualified_message.rb +49 -0
  28. data/lib/savon/request.rb +89 -0
  29. data/lib/savon/request_logger.rb +48 -0
  30. data/lib/savon/response.rb +112 -0
  31. data/lib/savon/soap_fault.rb +48 -0
  32. data/lib/savon/version.rb +3 -0
  33. data/savon.gemspec +52 -0
  34. data/spec/fixtures/gzip/message.gz +0 -0
  35. data/spec/fixtures/response/another_soap_fault.xml +14 -0
  36. data/spec/fixtures/response/authentication.xml +14 -0
  37. data/spec/fixtures/response/header.xml +13 -0
  38. data/spec/fixtures/response/list.xml +18 -0
  39. data/spec/fixtures/response/multi_ref.xml +39 -0
  40. data/spec/fixtures/response/soap_fault.xml +8 -0
  41. data/spec/fixtures/response/soap_fault12.xml +18 -0
  42. data/spec/fixtures/response/taxcloud.xml +1 -0
  43. data/spec/fixtures/ssl/client_cert.pem +16 -0
  44. data/spec/fixtures/ssl/client_encrypted_key.pem +30 -0
  45. data/spec/fixtures/ssl/client_encrypted_key_cert.pem +24 -0
  46. data/spec/fixtures/ssl/client_key.pem +15 -0
  47. data/spec/fixtures/wsdl/authentication.xml +63 -0
  48. data/spec/fixtures/wsdl/betfair.xml +2981 -0
  49. data/spec/fixtures/wsdl/edialog.xml +15416 -0
  50. data/spec/fixtures/wsdl/interhome.xml +2137 -0
  51. data/spec/fixtures/wsdl/lower_camel.xml +52 -0
  52. data/spec/fixtures/wsdl/multiple_namespaces.xml +92 -0
  53. data/spec/fixtures/wsdl/multiple_types.xml +60 -0
  54. data/spec/fixtures/wsdl/taxcloud.xml +934 -0
  55. data/spec/fixtures/wsdl/team_software.xml +1 -0
  56. data/spec/fixtures/wsdl/vies.xml +176 -0
  57. data/spec/fixtures/wsdl/wasmuth.xml +153 -0
  58. data/spec/integration/centra_spec.rb +72 -0
  59. data/spec/integration/email_example_spec.rb +32 -0
  60. data/spec/integration/random_quote_spec.rb +23 -0
  61. data/spec/integration/ratp_example_spec.rb +28 -0
  62. data/spec/integration/stockquote_example_spec.rb +28 -0
  63. data/spec/integration/support/application.rb +82 -0
  64. data/spec/integration/support/server.rb +84 -0
  65. data/spec/integration/temperature_example_spec.rb +46 -0
  66. data/spec/integration/zipcode_example_spec.rb +42 -0
  67. data/spec/savon/builder_spec.rb +86 -0
  68. data/spec/savon/client_spec.rb +198 -0
  69. data/spec/savon/core_ext/string_spec.rb +37 -0
  70. data/spec/savon/features/message_tag_spec.rb +61 -0
  71. data/spec/savon/http_error_spec.rb +49 -0
  72. data/spec/savon/log_message_spec.rb +33 -0
  73. data/spec/savon/message_spec.rb +40 -0
  74. data/spec/savon/mock_spec.rb +157 -0
  75. data/spec/savon/model_spec.rb +154 -0
  76. data/spec/savon/observers_spec.rb +92 -0
  77. data/spec/savon/operation_spec.rb +211 -0
  78. data/spec/savon/options_spec.rb +772 -0
  79. data/spec/savon/request_spec.rb +493 -0
  80. data/spec/savon/response_spec.rb +258 -0
  81. data/spec/savon/soap_fault_spec.rb +126 -0
  82. data/spec/spec_helper.rb +30 -0
  83. data/spec/support/endpoint.rb +25 -0
  84. data/spec/support/fixture.rb +39 -0
  85. data/spec/support/integration.rb +9 -0
  86. data/spec/support/stdout.rb +25 -0
  87. metadata +310 -0
@@ -0,0 +1,336 @@
1
+ require "logger"
2
+ require "httpi"
3
+
4
+ module Savon
5
+ class Options
6
+
7
+ def initialize(options = {})
8
+ @options = {}
9
+ assign options
10
+ end
11
+
12
+ attr_reader :option_type
13
+
14
+ def [](option)
15
+ @options[option]
16
+ end
17
+
18
+ def []=(option, value)
19
+ value = [value].flatten
20
+ self.send(option, *value)
21
+ end
22
+
23
+ def include?(option)
24
+ @options.key? option
25
+ end
26
+
27
+ private
28
+
29
+ def assign(options)
30
+ options.each do |option, value|
31
+ self.send(option, value)
32
+ end
33
+ end
34
+
35
+ def method_missing(option, _)
36
+ raise UnknownOptionError, "Unknown #{option_type} option: #{option.inspect}"
37
+ end
38
+
39
+ end
40
+
41
+ class GlobalOptions < Options
42
+
43
+ def initialize(options = {})
44
+ @option_type = :global
45
+
46
+ defaults = {
47
+ :encoding => "UTF-8",
48
+ :soap_version => 1,
49
+ :namespaces => {},
50
+ :logger => Logger.new($stdout),
51
+ :log => false,
52
+ :filters => [],
53
+ :pretty_print_xml => false,
54
+ :raise_errors => true,
55
+ :strip_namespaces => true,
56
+ :convert_response_tags_to => lambda { |tag| tag.snakecase.to_sym},
57
+ :multipart => false,
58
+ :adapter => nil,
59
+ }
60
+
61
+ options = defaults.merge(options)
62
+
63
+ # this option is a shortcut on the logger which needs to be set
64
+ # before it can be modified to set the option.
65
+ delayed_level = options.delete(:log_level)
66
+
67
+ super(options)
68
+
69
+ log_level(delayed_level) unless delayed_level.nil?
70
+ end
71
+
72
+ # Location of the local or remote WSDL document.
73
+ def wsdl(wsdl_address)
74
+ @options[:wsdl] = wsdl_address
75
+ end
76
+
77
+ # SOAP endpoint.
78
+ def endpoint(endpoint)
79
+ @options[:endpoint] = endpoint
80
+ end
81
+
82
+ # Target namespace.
83
+ def namespace(namespace)
84
+ @options[:namespace] = namespace
85
+ end
86
+
87
+ # The namespace identifer.
88
+ def namespace_identifier(identifier)
89
+ @options[:namespace_identifier] = identifier
90
+ end
91
+
92
+ # Namespaces for the SOAP envelope.
93
+ def namespaces(namespaces)
94
+ @options[:namespaces] = namespaces
95
+ end
96
+
97
+ # Proxy server to use for all requests.
98
+ def proxy(proxy)
99
+ @options[:proxy] = proxy
100
+ end
101
+
102
+ # A Hash of HTTP headers.
103
+ def headers(headers)
104
+ @options[:headers] = headers
105
+ end
106
+
107
+ # Open timeout in seconds.
108
+ def open_timeout(open_timeout)
109
+ @options[:open_timeout] = open_timeout
110
+ end
111
+
112
+ # Read timeout in seconds.
113
+ def read_timeout(read_timeout)
114
+ @options[:read_timeout] = read_timeout
115
+ end
116
+
117
+ # The encoding to use. Defaults to "UTF-8".
118
+ def encoding(encoding)
119
+ @options[:encoding] = encoding
120
+ end
121
+
122
+ # The global SOAP header. Expected to be a Hash or responding to #to_s.
123
+ def soap_header(header)
124
+ @options[:soap_header] = header
125
+ end
126
+
127
+ # Sets whether elements should be :qualified or unqualified.
128
+ # If you need to use this option, please open an issue and make
129
+ # sure to add your WSDL document for debugging.
130
+ def element_form_default(element_form_default)
131
+ @options[:element_form_default] = element_form_default
132
+ end
133
+
134
+ # Can be used to change the SOAP envelope namespace identifier.
135
+ # If you need to use this option, please open an issue and make
136
+ # sure to add your WSDL document for debugging.
137
+ def env_namespace(env_namespace)
138
+ @options[:env_namespace] = env_namespace
139
+ end
140
+
141
+ # Changes the SOAP version to 1 or 2.
142
+ def soap_version(soap_version)
143
+ @options[:soap_version] = soap_version
144
+ end
145
+
146
+ # Whether or not to raise SOAP fault and HTTP errors.
147
+ def raise_errors(raise_errors)
148
+ @options[:raise_errors] = raise_errors
149
+ end
150
+
151
+ # Whether or not to log.
152
+ def log(log)
153
+ HTTPI.log = log
154
+ @options[:log] = log
155
+ end
156
+
157
+ # The logger to use. Defaults to a Savon::Logger instance.
158
+ def logger(logger)
159
+ @options[:logger] = logger
160
+ end
161
+
162
+ # Changes the Logger's log level.
163
+ def log_level(level)
164
+ levels = { :debug => 0, :info => 1, :warn => 2, :error => 3, :fatal => 4 }
165
+
166
+ unless levels.include? level
167
+ raise ArgumentError, "Invalid log level: #{level.inspect}\n" \
168
+ "Expected one of: #{levels.keys.inspect}"
169
+ end
170
+
171
+ @options[:logger].level = levels[level]
172
+ end
173
+
174
+ # A list of XML tags to filter from logged SOAP messages.
175
+ def filters(*filters)
176
+ @options[:filters] = filters.flatten
177
+ end
178
+
179
+ # Whether to pretty print request and response XML log messages.
180
+ def pretty_print_xml(pretty_print_xml)
181
+ @options[:pretty_print_xml] = pretty_print_xml
182
+ end
183
+
184
+ # Specifies the SSL version to use.
185
+ def ssl_version(version)
186
+ @options[:ssl_version] = version
187
+ end
188
+
189
+ # Whether and how to to verify the connection.
190
+ def ssl_verify_mode(verify_mode)
191
+ @options[:ssl_verify_mode] = verify_mode
192
+ end
193
+
194
+ # Sets the cert key file to use.
195
+ def ssl_cert_key_file(file)
196
+ @options[:ssl_cert_key_file] = file
197
+ end
198
+
199
+ # Sets the cert key password to use.
200
+ def ssl_cert_key_password(password)
201
+ @options[:ssl_cert_key_password] = password
202
+ end
203
+
204
+ # Sets the cert file to use.
205
+ def ssl_cert_file(file)
206
+ @options[:ssl_cert_file] = file
207
+ end
208
+
209
+ # Sets the ca cert file to use.
210
+ def ssl_ca_cert_file(file)
211
+ @options[:ssl_ca_cert_file] = file
212
+ end
213
+
214
+ # HTTP basic auth credentials.
215
+ def basic_auth(*credentials)
216
+ @options[:basic_auth] = credentials.flatten
217
+ end
218
+
219
+ # HTTP digest auth credentials.
220
+ def digest_auth(*credentials)
221
+ @options[:digest_auth] = credentials.flatten
222
+ end
223
+
224
+ # NTLM auth credentials.
225
+ def ntlm(*credentials)
226
+ @options[:ntlm] = credentials.flatten
227
+ end
228
+
229
+ # WSSE auth credentials for Akami.
230
+ def wsse_auth(*credentials)
231
+ @options[:wsse_auth] = credentials.flatten
232
+ end
233
+
234
+ # Instruct Akami to enable wsu:Timestamp headers.
235
+ def wsse_timestamp(*timestamp)
236
+ @options[:wsse_timestamp] = timestamp.flatten
237
+ end
238
+
239
+ # Instruct Nori whether to strip namespaces from XML nodes.
240
+ def strip_namespaces(strip_namespaces)
241
+ @options[:strip_namespaces] = strip_namespaces
242
+ end
243
+
244
+ # Tell Gyoku how to convert Hash key Symbols to XML tags.
245
+ # Accepts one of :lower_camelcase, :camelcase, :upcase, or :none.
246
+ def convert_request_keys_to(converter)
247
+ @options[:convert_request_keys_to] = converter
248
+ end
249
+
250
+ # Tell Nori how to convert XML tags from the SOAP response into Hash keys.
251
+ # Accepts a lambda or a block which receives an XML tag and returns a Hash key.
252
+ # Defaults to convert tags to snakecase Symbols.
253
+ def convert_response_tags_to(converter = nil, &block)
254
+ @options[:convert_response_tags_to] = block || converter
255
+ end
256
+
257
+ # Instruct Savon to create a multipart response if available.
258
+ def multipart(multipart)
259
+ @options[:multipart] = multipart
260
+ end
261
+
262
+ # Instruct Savon what HTTPI adapter it should use instead of default
263
+ def adapter(adapter)
264
+ @options[:adapter] = adapter
265
+ end
266
+ end
267
+
268
+ class LocalOptions < Options
269
+
270
+ def initialize(options = {})
271
+ @option_type = :local
272
+
273
+ defaults = {
274
+ :advanced_typecasting => true,
275
+ :response_parser => :nokogiri,
276
+ :multipart => false
277
+ }
278
+
279
+ super defaults.merge(options)
280
+ end
281
+
282
+ # The local SOAP header. Expected to be a Hash or respond to #to_s.
283
+ # Will be merged with the global SOAP header if both are Hashes.
284
+ # Otherwise the local option will be prefered.
285
+ def soap_header(header)
286
+ @options[:soap_header] = header
287
+ end
288
+
289
+ # The SOAP message to send. Expected to be a Hash or a String.
290
+ def message(message)
291
+ @options[:message] = message
292
+ end
293
+
294
+ # SOAP message tag (formerly known as SOAP input tag). If it's not set, Savon retrieves the name from
295
+ # the WSDL document (if available). Otherwise, Gyoku converts the operation name into an XML element.
296
+ def message_tag(message_tag)
297
+ @options[:message_tag] = message_tag
298
+ end
299
+
300
+ # Attributes for the SOAP message tag.
301
+ def attributes(attributes)
302
+ @options[:attributes] = attributes
303
+ end
304
+
305
+ # Value of the SOAPAction HTTP header.
306
+ def soap_action(soap_action)
307
+ @options[:soap_action] = soap_action
308
+ end
309
+
310
+ # Cookies to be used for the next request.
311
+ def cookies(cookies)
312
+ @options[:cookies] = cookies
313
+ end
314
+
315
+ # The SOAP request XML to send. Expected to be a String.
316
+ def xml(xml)
317
+ @options[:xml] = xml
318
+ end
319
+
320
+ # Instruct Nori to use advanced typecasting.
321
+ def advanced_typecasting(advanced)
322
+ @options[:advanced_typecasting] = advanced
323
+ end
324
+
325
+ # Instruct Nori to use :rexml or :nokogiri to parse the response.
326
+ def response_parser(parser)
327
+ @options[:response_parser] = parser
328
+ end
329
+
330
+ # Instruct Savon to create a multipart response if available.
331
+ def multipart(multipart)
332
+ @options[:multipart] = multipart
333
+ end
334
+
335
+ end
336
+ end
@@ -0,0 +1,49 @@
1
+ require "gyoku"
2
+
3
+ module Savon
4
+ class QualifiedMessage
5
+
6
+ def initialize(types, used_namespaces, key_converter)
7
+ @types = types
8
+ @used_namespaces = used_namespaces
9
+ @key_converter = key_converter
10
+ end
11
+
12
+ def to_hash(hash, path)
13
+ return unless hash
14
+ return hash.map { |value| to_hash(value, path) } if hash.kind_of?(Array)
15
+ return hash.to_s unless hash.kind_of? Hash
16
+
17
+ hash.inject({}) do |newhash, (key, value)|
18
+ if key == :order!
19
+ add_namespaces_to_values(value, path)
20
+ newhash.merge(key => value)
21
+ else
22
+ translated_key = Gyoku.xml_tag(key, :key_converter => @key_converter).to_s
23
+ newpath = path + [translated_key]
24
+
25
+ if @used_namespaces[newpath]
26
+ newhash.merge(
27
+ "#{@used_namespaces[newpath]}:#{translated_key}" =>
28
+ to_hash(value, @types[newpath] ? [@types[newpath]] : newpath)
29
+ )
30
+ else
31
+ newhash.merge(translated_key => value)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def add_namespaces_to_values(values, path)
40
+ values.collect! { |value|
41
+ camelcased_value = Gyoku.xml_tag(value, :key_converter => @key_converter)
42
+ namespace_path = path + [camelcased_value.to_s]
43
+ namespace = @used_namespaces[namespace_path]
44
+ "#{namespace.blank? ? '' : namespace + ":"}#{camelcased_value}"
45
+ }
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,89 @@
1
+ require "httpi"
2
+
3
+ module Savon
4
+ class HTTPRequest
5
+
6
+ def initialize(globals, http_request = nil)
7
+ @globals = globals
8
+ @http_request = http_request || HTTPI::Request.new
9
+ end
10
+
11
+ def build
12
+ @http_request
13
+ end
14
+
15
+ private
16
+
17
+ def configure_proxy
18
+ @http_request.proxy = @globals[:proxy] if @globals.include? :proxy
19
+ end
20
+
21
+ def configure_timeouts
22
+ @http_request.open_timeout = @globals[:open_timeout] if @globals.include? :open_timeout
23
+ @http_request.read_timeout = @globals[:read_timeout] if @globals.include? :read_timeout
24
+ end
25
+
26
+ def configure_ssl
27
+ @http_request.auth.ssl.ssl_version = @globals[:ssl_version] if @globals.include? :ssl_version
28
+ @http_request.auth.ssl.verify_mode = @globals[:ssl_verify_mode] if @globals.include? :ssl_verify_mode
29
+
30
+ @http_request.auth.ssl.cert_key_file = @globals[:ssl_cert_key_file] if @globals.include? :ssl_cert_key_file
31
+ @http_request.auth.ssl.cert_file = @globals[:ssl_cert_file] if @globals.include? :ssl_cert_file
32
+ @http_request.auth.ssl.ca_cert_file = @globals[:ssl_ca_cert_file] if @globals.include? :ssl_ca_cert_file
33
+
34
+ @http_request.auth.ssl.cert_key_password = @globals[:ssl_cert_key_password] if @globals.include? :ssl_cert_key_password
35
+ end
36
+
37
+ def configure_auth
38
+ @http_request.auth.basic(*@globals[:basic_auth]) if @globals.include? :basic_auth
39
+ @http_request.auth.digest(*@globals[:digest_auth]) if @globals.include? :digest_auth
40
+ @http_request.auth.ntlm(*@globals[:ntlm]) if @globals.include? :ntlm
41
+ end
42
+
43
+ end
44
+
45
+ class WSDLRequest < HTTPRequest
46
+
47
+ def build
48
+ configure_proxy
49
+ configure_timeouts
50
+ configure_ssl
51
+ configure_auth
52
+
53
+ @http_request
54
+ end
55
+
56
+ end
57
+
58
+ class SOAPRequest < HTTPRequest
59
+
60
+ CONTENT_TYPE = {
61
+ 1 => "text/xml;charset=%s",
62
+ 2 => "application/soap+xml;charset=%s"
63
+ }
64
+
65
+ def build(options = {})
66
+ configure_proxy
67
+ configure_cookies options[:cookies]
68
+ configure_timeouts
69
+ configure_headers options[:soap_action]
70
+ configure_ssl
71
+ configure_auth
72
+
73
+ @http_request
74
+ end
75
+
76
+ private
77
+
78
+ def configure_cookies(cookies)
79
+ @http_request.set_cookies(cookies) if cookies
80
+ end
81
+
82
+ def configure_headers(soap_action)
83
+ @http_request.headers = @globals[:headers] if @globals.include? :headers
84
+ @http_request.headers["SOAPAction"] ||= %{"#{soap_action}"} if soap_action
85
+ @http_request.headers["Content-Type"] ||= CONTENT_TYPE[@globals[:soap_version]] % @globals[:encoding]
86
+ end
87
+
88
+ end
89
+ end