savon 2.11.1 → 2.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +132 -73
- data/README.md +35 -20
- data/lib/savon/block_interface.rb +1 -0
- data/lib/savon/builder.rb +126 -30
- data/lib/savon/client.rb +2 -2
- data/lib/savon/header.rb +2 -6
- data/lib/savon/http_error.rb +4 -4
- data/lib/savon/log_message.rb +2 -1
- data/lib/savon/message.rb +1 -0
- data/lib/savon/mock/expectation.rb +1 -0
- data/lib/savon/mock/spec_helper.rb +1 -0
- data/lib/savon/mock.rb +1 -0
- data/lib/savon/model.rb +4 -3
- data/lib/savon/operation.rb +22 -19
- data/lib/savon/options.rb +98 -19
- data/lib/savon/qualified_message.rb +29 -27
- data/lib/savon/request.rb +22 -6
- data/lib/savon/request_logger.rb +8 -2
- data/lib/savon/response.rb +58 -10
- data/lib/savon/soap_fault.rb +3 -4
- data/lib/savon/string_utils.rb +17 -0
- data/lib/savon/version.rb +2 -1
- data/lib/savon.rb +2 -0
- metadata +80 -100
- data/.gitignore +0 -14
- data/.travis.yml +0 -15
- data/.yardopts +0 -6
- data/CONTRIBUTING.md +0 -46
- data/Gemfile +0 -18
- data/donate.png +0 -0
- data/lib/savon/core_ext/string.rb +0 -29
- data/savon.gemspec +0 -46
- data/spec/fixtures/gzip/message.gz +0 -0
- data/spec/fixtures/response/another_soap_fault.xml +0 -14
- data/spec/fixtures/response/authentication.xml +0 -14
- data/spec/fixtures/response/f5.xml +0 -39
- data/spec/fixtures/response/header.xml +0 -13
- data/spec/fixtures/response/list.xml +0 -18
- data/spec/fixtures/response/multi_ref.xml +0 -39
- data/spec/fixtures/response/soap_fault.xml +0 -8
- data/spec/fixtures/response/soap_fault12.xml +0 -18
- data/spec/fixtures/response/soap_fault_funky.xml +0 -8
- data/spec/fixtures/response/taxcloud.xml +0 -1
- data/spec/fixtures/ssl/client_cert.pem +0 -16
- data/spec/fixtures/ssl/client_encrypted_key.pem +0 -30
- data/spec/fixtures/ssl/client_encrypted_key_cert.pem +0 -24
- data/spec/fixtures/ssl/client_key.pem +0 -15
- data/spec/fixtures/wsdl/authentication.xml +0 -63
- data/spec/fixtures/wsdl/betfair.xml +0 -2981
- data/spec/fixtures/wsdl/edialog.xml +0 -15416
- data/spec/fixtures/wsdl/interhome.xml +0 -2137
- data/spec/fixtures/wsdl/lower_camel.xml +0 -52
- data/spec/fixtures/wsdl/multiple_namespaces.xml +0 -92
- data/spec/fixtures/wsdl/multiple_types.xml +0 -60
- data/spec/fixtures/wsdl/no_message_tag.xml +0 -1267
- data/spec/fixtures/wsdl/taxcloud.xml +0 -934
- data/spec/fixtures/wsdl/team_software.xml +0 -1
- data/spec/fixtures/wsdl/vies.xml +0 -176
- data/spec/fixtures/wsdl/wasmuth.xml +0 -153
- data/spec/integration/centra_spec.rb +0 -66
- data/spec/integration/email_example_spec.rb +0 -32
- data/spec/integration/random_quote_spec.rb +0 -23
- data/spec/integration/ratp_example_spec.rb +0 -28
- data/spec/integration/stockquote_example_spec.rb +0 -28
- data/spec/integration/support/application.rb +0 -82
- data/spec/integration/support/server.rb +0 -84
- data/spec/integration/temperature_example_spec.rb +0 -46
- data/spec/integration/zipcode_example_spec.rb +0 -42
- data/spec/savon/builder_spec.rb +0 -137
- data/spec/savon/client_spec.rb +0 -271
- data/spec/savon/core_ext/string_spec.rb +0 -37
- data/spec/savon/features/message_tag_spec.rb +0 -61
- data/spec/savon/http_error_spec.rb +0 -49
- data/spec/savon/log_message_spec.rb +0 -44
- data/spec/savon/message_spec.rb +0 -70
- data/spec/savon/mock_spec.rb +0 -174
- data/spec/savon/model_spec.rb +0 -182
- data/spec/savon/observers_spec.rb +0 -92
- data/spec/savon/operation_spec.rb +0 -230
- data/spec/savon/options_spec.rb +0 -1064
- data/spec/savon/qualified_message_spec.rb +0 -20
- data/spec/savon/request_logger_spec.rb +0 -37
- data/spec/savon/request_spec.rb +0 -496
- data/spec/savon/response_spec.rb +0 -270
- data/spec/savon/soap_fault_spec.rb +0 -131
- data/spec/spec_helper.rb +0 -30
- data/spec/support/adapters.rb +0 -48
- data/spec/support/endpoint.rb +0 -25
- data/spec/support/fixture.rb +0 -39
- data/spec/support/integration.rb +0 -9
- data/spec/support/stdout.rb +0 -25
data/lib/savon/options.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "logger"
|
2
3
|
require "httpi"
|
3
4
|
|
@@ -73,24 +74,26 @@ module Savon
|
|
73
74
|
@option_type = :global
|
74
75
|
|
75
76
|
defaults = {
|
76
|
-
:encoding
|
77
|
-
:soap_version
|
78
|
-
:namespaces
|
79
|
-
:logger
|
80
|
-
:log
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
84
|
-
:
|
85
|
-
:
|
86
|
-
:
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
:
|
92
|
-
:
|
93
|
-
:
|
77
|
+
:encoding => "UTF-8",
|
78
|
+
:soap_version => 1,
|
79
|
+
:namespaces => {},
|
80
|
+
:logger => Logger.new($stdout),
|
81
|
+
:log => false,
|
82
|
+
:log_headers => true,
|
83
|
+
:filters => [],
|
84
|
+
:pretty_print_xml => false,
|
85
|
+
:raise_errors => true,
|
86
|
+
:strip_namespaces => true,
|
87
|
+
:delete_namespace_attributes => false,
|
88
|
+
:convert_response_tags_to => lambda { |tag| StringUtils.snakecase(tag).to_sym},
|
89
|
+
:convert_attributes_to => lambda { |k,v| [k,v] },
|
90
|
+
:multipart => false,
|
91
|
+
:adapter => nil,
|
92
|
+
:use_wsa_headers => false,
|
93
|
+
:no_message_tag => false,
|
94
|
+
:follow_redirects => false,
|
95
|
+
:unwrap => false,
|
96
|
+
:host => nil
|
94
97
|
}
|
95
98
|
|
96
99
|
options = defaults.merge(options)
|
@@ -136,7 +139,7 @@ module Savon
|
|
136
139
|
|
137
140
|
# Proxy server to use for all requests.
|
138
141
|
def proxy(proxy)
|
139
|
-
@options[:proxy] = proxy
|
142
|
+
@options[:proxy] = proxy unless proxy.nil?
|
140
143
|
end
|
141
144
|
|
142
145
|
# A Hash of HTTP headers.
|
@@ -154,6 +157,11 @@ module Savon
|
|
154
157
|
@options[:read_timeout] = read_timeout
|
155
158
|
end
|
156
159
|
|
160
|
+
# Write timeout in seconds.
|
161
|
+
def write_timeout(write_timeout)
|
162
|
+
@options[:write_timeout] = write_timeout
|
163
|
+
end
|
164
|
+
|
157
165
|
# The encoding to use. Defaults to "UTF-8".
|
158
166
|
def encoding(encoding)
|
159
167
|
@options[:encoding] = encoding
|
@@ -212,6 +220,11 @@ module Savon
|
|
212
220
|
@options[:logger].level = levels[level]
|
213
221
|
end
|
214
222
|
|
223
|
+
# To log headers or not.
|
224
|
+
def log_headers(log_headers)
|
225
|
+
@options[:log_headers] = log_headers
|
226
|
+
end
|
227
|
+
|
215
228
|
# A list of XML tags to filter from logged SOAP messages.
|
216
229
|
def filters(*filters)
|
217
230
|
@options[:filters] = filters.flatten
|
@@ -227,6 +240,16 @@ module Savon
|
|
227
240
|
@options[:ssl_version] = version
|
228
241
|
end
|
229
242
|
|
243
|
+
# Specifies the SSL version to use.
|
244
|
+
def ssl_min_version(version)
|
245
|
+
@options[:ssl_min_version] = version
|
246
|
+
end
|
247
|
+
|
248
|
+
# Specifies the SSL version to use.
|
249
|
+
def ssl_max_version(version)
|
250
|
+
@options[:ssl_max_version] = version
|
251
|
+
end
|
252
|
+
|
230
253
|
# Whether and how to to verify the connection.
|
231
254
|
def ssl_verify_mode(verify_mode)
|
232
255
|
@options[:ssl_verify_mode] = verify_mode
|
@@ -267,6 +290,19 @@ module Savon
|
|
267
290
|
@options[:ssl_ca_cert] = cert
|
268
291
|
end
|
269
292
|
|
293
|
+
def ssl_ciphers(ciphers)
|
294
|
+
@options[:ssl_ciphers] = ciphers
|
295
|
+
end
|
296
|
+
|
297
|
+
# Sets the ca cert path.
|
298
|
+
def ssl_ca_cert_path(path)
|
299
|
+
@options[:ssl_ca_cert_path] = path
|
300
|
+
end
|
301
|
+
|
302
|
+
# Sets the ssl cert store.
|
303
|
+
def ssl_cert_store(store)
|
304
|
+
@options[:ssl_cert_store] = store
|
305
|
+
end
|
270
306
|
|
271
307
|
# HTTP basic auth credentials.
|
272
308
|
def basic_auth(*credentials)
|
@@ -288,6 +324,11 @@ module Savon
|
|
288
324
|
@options[:strip_namespaces] = strip_namespaces
|
289
325
|
end
|
290
326
|
|
327
|
+
# Instruct Nori whether to delete namespace attributes from XML nodes.
|
328
|
+
def delete_namespace_attributes(delete_namespace_attributes)
|
329
|
+
@options[:delete_namespace_attributes] = delete_namespace_attributes
|
330
|
+
end
|
331
|
+
|
291
332
|
# Tell Gyoku how to convert Hash key Symbols to XML tags.
|
292
333
|
# Accepts one of :lower_camelcase, :camelcase, :upcase, or :none.
|
293
334
|
def convert_request_keys_to(converter)
|
@@ -377,6 +418,40 @@ module Savon
|
|
377
418
|
@options[:attributes] = attributes
|
378
419
|
end
|
379
420
|
|
421
|
+
# Attachments for the SOAP message (https://www.w3.org/TR/SOAP-attachments)
|
422
|
+
#
|
423
|
+
# should pass an Array or a Hash; items should be path strings or
|
424
|
+
# { filename: 'file.name', content: 'content' } objects
|
425
|
+
# The Content-ID in multipart message sections will be the filename or the key if Hash is given
|
426
|
+
#
|
427
|
+
# usage examples:
|
428
|
+
#
|
429
|
+
# response = client.call :operation1 do
|
430
|
+
# message param1: 'value'
|
431
|
+
# attachments [
|
432
|
+
# { filename: 'x1.xml', content: '<xml>abc</xml>'},
|
433
|
+
# { filename: 'x2.xml', content: '<xml>abc</xml>'}
|
434
|
+
# ]
|
435
|
+
# end
|
436
|
+
# # Content-ID will be x1.xml and x2.xml
|
437
|
+
#
|
438
|
+
# response = client.call :operation1 do
|
439
|
+
# message param1: 'value'
|
440
|
+
# attachments 'x1.xml' => '/tmp/1281ab7d7d.xml', 'x2.xml' => '/tmp/4c5v8e833a.xml'
|
441
|
+
# end
|
442
|
+
# # Content-ID will be x1.xml and x2.xml
|
443
|
+
#
|
444
|
+
# response = client.call :operation1 do
|
445
|
+
# message param1: 'value'
|
446
|
+
# attachments [ '/tmp/1281ab7d7d.xml', '/tmp/4c5v8e833a.xml']
|
447
|
+
# end
|
448
|
+
# # Content-ID will be 1281ab7d7d.xml and 4c5v8e833a.xml
|
449
|
+
#
|
450
|
+
# The Content-ID is important if you want to refer to the attachments from the SOAP request
|
451
|
+
def attachments(attachments)
|
452
|
+
@options[:attachments] = attachments
|
453
|
+
end
|
454
|
+
|
380
455
|
# Value of the SOAPAction HTTP header.
|
381
456
|
def soap_action(soap_action)
|
382
457
|
@options[:soap_action] = soap_action
|
@@ -406,5 +481,9 @@ module Savon
|
|
406
481
|
def multipart(multipart)
|
407
482
|
@options[:multipart] = multipart
|
408
483
|
end
|
484
|
+
|
485
|
+
def headers(headers)
|
486
|
+
@options[:headers] = headers
|
487
|
+
end
|
409
488
|
end
|
410
489
|
end
|
@@ -1,50 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "gyoku"
|
2
3
|
|
3
4
|
module Savon
|
4
5
|
class QualifiedMessage
|
5
|
-
|
6
6
|
def initialize(types, used_namespaces, key_converter)
|
7
|
-
@types
|
7
|
+
@types = types
|
8
8
|
@used_namespaces = used_namespaces
|
9
|
-
@key_converter
|
9
|
+
@key_converter = key_converter
|
10
10
|
end
|
11
11
|
|
12
12
|
def to_hash(hash, path)
|
13
13
|
return hash unless hash
|
14
|
-
return hash.map { |value| to_hash(value, path) } if hash.
|
15
|
-
return hash.to_s unless hash.
|
16
|
-
|
17
|
-
hash.
|
18
|
-
|
19
|
-
|
20
|
-
newhash
|
14
|
+
return hash.map { |value| to_hash(value, path) } if hash.is_a?(Array)
|
15
|
+
return hash.to_s unless hash.is_a?(Hash)
|
16
|
+
|
17
|
+
hash.each_with_object({}) do |(key, value), newhash|
|
18
|
+
case key
|
19
|
+
when :order!
|
20
|
+
newhash[key] = add_namespaces_to_values(value, path)
|
21
|
+
when :attributes!, :content!
|
22
|
+
newhash[key] = to_hash(value, path)
|
21
23
|
else
|
22
|
-
|
23
|
-
|
24
|
-
newpath = path + [translated_key]
|
25
|
-
|
26
|
-
if @used_namespaces[newpath]
|
27
|
-
newhash.merge(
|
28
|
-
"#{@used_namespaces[newpath]}:#{translated_key}" =>
|
29
|
-
to_hash(value, @types[newpath] ? [@types[newpath]] : newpath)
|
30
|
-
)
|
24
|
+
if key.to_s =~ /!$/
|
25
|
+
newhash[key] = value
|
31
26
|
else
|
32
|
-
|
27
|
+
translated_key = translate_tag(key)
|
28
|
+
newkey = add_namespaces_to_values(key, path).first
|
29
|
+
newpath = path + [translated_key]
|
30
|
+
newhash[newkey] = to_hash(value, @types[newpath] ? [@types[newpath]] : newpath)
|
33
31
|
end
|
34
32
|
end
|
33
|
+
newhash
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
37
|
private
|
39
38
|
|
40
|
-
def
|
41
|
-
|
42
|
-
camelcased_value = Gyoku.xml_tag(value, :key_converter => @key_converter)
|
43
|
-
namespace_path = path + [camelcased_value.to_s]
|
44
|
-
namespace = @used_namespaces[namespace_path]
|
45
|
-
"#{namespace.blank? ? '' : namespace + ":"}#{camelcased_value}"
|
46
|
-
}
|
39
|
+
def translate_tag(key)
|
40
|
+
Gyoku.xml_tag(key, :key_converter => @key_converter).to_s
|
47
41
|
end
|
48
42
|
|
43
|
+
def add_namespaces_to_values(values, path)
|
44
|
+
Array(values).collect do |value|
|
45
|
+
translated_value = translate_tag(value)
|
46
|
+
namespace_path = path + [translated_value]
|
47
|
+
namespace = @used_namespaces[namespace_path] || ''
|
48
|
+
namespace.empty? ? value : "#{namespace}:#{translated_value}"
|
49
|
+
end
|
50
|
+
end
|
49
51
|
end
|
50
52
|
end
|
data/lib/savon/request.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "httpi"
|
2
3
|
|
3
4
|
module Savon
|
@@ -21,18 +22,25 @@ module Savon
|
|
21
22
|
def configure_timeouts
|
22
23
|
@http_request.open_timeout = @globals[:open_timeout] if @globals.include? :open_timeout
|
23
24
|
@http_request.read_timeout = @globals[:read_timeout] if @globals.include? :read_timeout
|
25
|
+
@http_request.write_timeout = @globals[:write_timeout] if @globals.include? :write_timeout
|
24
26
|
end
|
25
27
|
|
26
28
|
def configure_ssl
|
27
29
|
@http_request.auth.ssl.ssl_version = @globals[:ssl_version] if @globals.include? :ssl_version
|
30
|
+
@http_request.auth.ssl.min_version = @globals[:ssl_min_version] if @globals.include? :ssl_min_version
|
31
|
+
@http_request.auth.ssl.max_version = @globals[:ssl_max_version] if @globals.include? :ssl_max_version
|
32
|
+
|
28
33
|
@http_request.auth.ssl.verify_mode = @globals[:ssl_verify_mode] if @globals.include? :ssl_verify_mode
|
34
|
+
@http_request.auth.ssl.ciphers = @globals[:ssl_ciphers] if @globals.include? :ssl_ciphers
|
29
35
|
|
30
36
|
@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_key = @globals[:ssl_cert_key]
|
37
|
+
@http_request.auth.ssl.cert_key = @globals[:ssl_cert_key] if @globals.include? :ssl_cert_key
|
32
38
|
@http_request.auth.ssl.cert_file = @globals[:ssl_cert_file] if @globals.include? :ssl_cert_file
|
33
|
-
@http_request.auth.ssl.cert = @globals[:ssl_cert]
|
39
|
+
@http_request.auth.ssl.cert = @globals[:ssl_cert] if @globals.include? :ssl_cert
|
34
40
|
@http_request.auth.ssl.ca_cert_file = @globals[:ssl_ca_cert_file] if @globals.include? :ssl_ca_cert_file
|
35
|
-
@http_request.auth.ssl.
|
41
|
+
@http_request.auth.ssl.ca_cert_path = @globals[:ssl_ca_cert_path] if @globals.include? :ssl_ca_cert_path
|
42
|
+
@http_request.auth.ssl.ca_cert = @globals[:ssl_ca_cert] if @globals.include? :ssl_ca_cert
|
43
|
+
@http_request.auth.ssl.cert_store = @globals[:ssl_cert_store] if @globals.include? :ssl_cert_store
|
36
44
|
|
37
45
|
@http_request.auth.ssl.cert_key_password = @globals[:ssl_cert_key_password] if @globals.include? :ssl_cert_key_password
|
38
46
|
end
|
@@ -55,12 +63,19 @@ module Savon
|
|
55
63
|
def build
|
56
64
|
configure_proxy
|
57
65
|
configure_timeouts
|
66
|
+
configure_headers
|
58
67
|
configure_ssl
|
59
68
|
configure_auth
|
60
69
|
configure_redirect_handling
|
61
70
|
|
62
71
|
@http_request
|
63
72
|
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def configure_headers
|
77
|
+
@http_request.headers = @globals[:headers] if @globals.include? :headers
|
78
|
+
end
|
64
79
|
end
|
65
80
|
|
66
81
|
class SOAPRequest < HTTPRequest
|
@@ -72,9 +87,9 @@ module Savon
|
|
72
87
|
|
73
88
|
def build(options = {})
|
74
89
|
configure_proxy
|
75
|
-
configure_cookies options[:cookies]
|
76
90
|
configure_timeouts
|
77
|
-
configure_headers options[:soap_action]
|
91
|
+
configure_headers options[:soap_action], options[:headers]
|
92
|
+
configure_cookies options[:cookies]
|
78
93
|
configure_ssl
|
79
94
|
configure_auth
|
80
95
|
configure_redirect_handling
|
@@ -88,8 +103,9 @@ module Savon
|
|
88
103
|
@http_request.set_cookies(cookies) if cookies
|
89
104
|
end
|
90
105
|
|
91
|
-
def configure_headers(soap_action)
|
106
|
+
def configure_headers(soap_action, headers)
|
92
107
|
@http_request.headers = @globals[:headers] if @globals.include? :headers
|
108
|
+
@http_request.headers.merge!(headers) if headers
|
93
109
|
@http_request.headers["SOAPAction"] ||= %{"#{soap_action}"} if soap_action
|
94
110
|
@http_request.headers["Content-Type"] ||= CONTENT_TYPE[@globals[:soap_version]] % @globals[:encoding]
|
95
111
|
end
|
data/lib/savon/request_logger.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "savon/log_message"
|
2
3
|
|
3
4
|
module Savon
|
@@ -23,21 +24,26 @@ module Savon
|
|
23
24
|
@globals[:log]
|
24
25
|
end
|
25
26
|
|
27
|
+
def log_headers?
|
28
|
+
@globals[:log_headers]
|
29
|
+
end
|
30
|
+
|
26
31
|
private
|
27
32
|
|
28
33
|
def log_request(request)
|
29
34
|
logger.info { "SOAP request: #{request.url}" }
|
30
|
-
logger.info { headers_to_log(request.headers) }
|
35
|
+
logger.info { headers_to_log(request.headers) } if log_headers?
|
31
36
|
logger.debug { body_to_log(request.body) }
|
32
37
|
end
|
33
38
|
|
34
39
|
def log_response(response)
|
35
40
|
logger.info { "SOAP response (status #{response.code})" }
|
41
|
+
logger.debug { headers_to_log(response.headers) } if log_headers?
|
36
42
|
logger.debug { body_to_log(response.body) }
|
37
43
|
end
|
38
44
|
|
39
45
|
def headers_to_log(headers)
|
40
|
-
headers.map { |key, value| "#{key}: #{value}" }.join("
|
46
|
+
headers.map { |key, value| "#{key}: #{value}" }.join("\n")
|
41
47
|
end
|
42
48
|
|
43
49
|
def body_to_log(body)
|
data/lib/savon/response.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "nori"
|
2
3
|
require "savon/soap_fault"
|
3
4
|
require "savon/http_error"
|
4
5
|
|
5
6
|
module Savon
|
6
7
|
class Response
|
8
|
+
CRLF = /\r\n/
|
9
|
+
WSP = /[#{%Q|\x9\x20|}]/
|
7
10
|
|
8
11
|
def initialize(http, globals, locals)
|
9
12
|
@http = http
|
10
13
|
@globals = globals
|
11
14
|
@locals = locals
|
15
|
+
@attachments = []
|
16
|
+
@xml = ''
|
17
|
+
@has_parsed_body = false
|
12
18
|
|
13
19
|
build_soap_and_http_errors!
|
14
20
|
raise_soap_and_http_errors! if @globals[:raise_errors]
|
@@ -48,12 +54,17 @@ module Savon
|
|
48
54
|
result.kind_of?(Array) ? result.compact : [result].compact
|
49
55
|
end
|
50
56
|
|
51
|
-
def
|
52
|
-
@
|
57
|
+
def full_hash
|
58
|
+
@full_hash ||= nori.parse(xml)
|
53
59
|
end
|
54
60
|
|
55
61
|
def xml
|
56
|
-
|
62
|
+
if multipart?
|
63
|
+
parse_body unless @has_parsed_body
|
64
|
+
@xml
|
65
|
+
else
|
66
|
+
@http.body
|
67
|
+
end
|
57
68
|
end
|
58
69
|
|
59
70
|
alias_method :to_xml, :xml
|
@@ -68,14 +79,50 @@ module Savon
|
|
68
79
|
end
|
69
80
|
|
70
81
|
def find(*path)
|
71
|
-
envelope = nori.find(
|
72
|
-
raise_invalid_response_error! unless envelope
|
82
|
+
envelope = nori.find(full_hash, 'Envelope')
|
83
|
+
raise_invalid_response_error! unless envelope.is_a?(Hash)
|
73
84
|
|
74
85
|
nori.find(envelope, *path)
|
75
86
|
end
|
76
87
|
|
88
|
+
def attachments
|
89
|
+
if multipart?
|
90
|
+
parse_body unless @has_parsed_body
|
91
|
+
@attachments
|
92
|
+
else
|
93
|
+
[]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def multipart?
|
98
|
+
!(http.headers['content-type'] =~ /^multipart/im).nil?
|
99
|
+
end
|
100
|
+
|
77
101
|
private
|
78
102
|
|
103
|
+
def boundary
|
104
|
+
return unless multipart?
|
105
|
+
Mail::Field.new('content-type', http.headers['content-type']).parameters['boundary']
|
106
|
+
end
|
107
|
+
|
108
|
+
def parse_body
|
109
|
+
http.body.force_encoding Encoding::ASCII_8BIT
|
110
|
+
parts = http.body.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
|
111
|
+
parts[1..-1].to_a.each_with_index do |part, index|
|
112
|
+
header_part, body_part = part.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
|
113
|
+
section = Mail::Part.new(
|
114
|
+
body: body_part
|
115
|
+
)
|
116
|
+
section.header = header_part
|
117
|
+
if index == 0
|
118
|
+
@xml = section.body.to_s
|
119
|
+
else
|
120
|
+
@attachments << section
|
121
|
+
end
|
122
|
+
end
|
123
|
+
@has_parsed_body = true
|
124
|
+
end
|
125
|
+
|
79
126
|
def build_soap_and_http_errors!
|
80
127
|
@soap_fault = SOAPFault.new(@http, nori, xml) if soap_fault?
|
81
128
|
@http_error = HTTPError.new(@http) if http_error?
|
@@ -98,11 +145,12 @@ module Savon
|
|
98
145
|
return @nori if @nori
|
99
146
|
|
100
147
|
nori_options = {
|
101
|
-
:
|
102
|
-
:
|
103
|
-
:
|
104
|
-
:
|
105
|
-
:
|
148
|
+
:delete_namespace_attributes => @globals[:delete_namespace_attributes],
|
149
|
+
:strip_namespaces => @globals[:strip_namespaces],
|
150
|
+
:convert_tags_to => @globals[:convert_response_tags_to],
|
151
|
+
:convert_attributes_to => @globals[:convert_attributes_to],
|
152
|
+
:advanced_typecasting => @locals[:advanced_typecasting],
|
153
|
+
:parser => @locals[:response_parser]
|
106
154
|
}
|
107
155
|
|
108
156
|
non_nil_nori_options = nori_options.reject { |_, value| value.nil? }
|
data/lib/savon/soap_fault.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
3
2
|
module Savon
|
4
3
|
class SOAPFault < Error
|
5
4
|
|
6
5
|
def self.present?(http, xml = nil)
|
7
6
|
xml ||= http.body
|
8
7
|
fault_node = xml.include?("Fault>")
|
9
|
-
soap1_fault = xml.
|
8
|
+
soap1_fault = xml.match(/faultcode\/?\>/) && xml.match(/faultstring\/?\>/)
|
10
9
|
soap2_fault = xml.include?("Code>") && xml.include?("Reason>")
|
11
10
|
|
12
11
|
fault_node && (soap1_fault || soap2_fault)
|
@@ -27,7 +26,7 @@ module Savon
|
|
27
26
|
|
28
27
|
def to_hash
|
29
28
|
parsed = nori.parse(xml || http.body)
|
30
|
-
nori.find(parsed, 'Envelope', 'Body')
|
29
|
+
nori.find(parsed, 'Envelope', 'Body') || {}
|
31
30
|
end
|
32
31
|
|
33
32
|
private
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Savon
|
4
|
+
module StringUtils
|
5
|
+
def self.snakecase(inputstring)
|
6
|
+
str = inputstring.dup
|
7
|
+
str.gsub! /::/, '/'
|
8
|
+
str.gsub! /([A-Z]+)([A-Z][a-z])/, '\1_\2'
|
9
|
+
str.gsub! /([a-z\d])([A-Z])/, '\1_\2'
|
10
|
+
str.tr! ".", "_"
|
11
|
+
str.tr! "-", "_"
|
12
|
+
str.downcase!
|
13
|
+
str
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
data/lib/savon/version.rb
CHANGED