savon 2.16.0 → 2.17.0
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 +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +21 -0
- data/Rakefile +6 -2
- data/lib/savon/block_interface.rb +3 -4
- data/lib/savon/builder.rb +19 -17
- data/lib/savon/client.rb +44 -13
- data/lib/savon/header.rb +12 -12
- data/lib/savon/http_error.rb +1 -3
- data/lib/savon/log_message.rb +2 -3
- data/lib/savon/message.rb +6 -7
- data/lib/savon/mock/expectation.rb +37 -23
- data/lib/savon/mock/spec_helper.rb +7 -11
- data/lib/savon/mock.rb +1 -0
- data/lib/savon/model.rb +12 -17
- data/lib/savon/operation.rb +93 -56
- data/lib/savon/options.rb +253 -153
- data/lib/savon/qualified_message.rb +2 -1
- data/lib/savon/response.rb +25 -23
- data/lib/savon/soap_fault.rb +2 -3
- data/lib/savon/string_utils.rb +3 -4
- data/lib/savon/transport/faraday.rb +70 -0
- data/lib/savon/transport/httpi.rb +136 -0
- data/lib/savon/transport/logging.rb +60 -0
- data/lib/savon/transport/response.rb +44 -0
- data/lib/savon/version.rb +2 -1
- data/lib/savon.rb +2 -3
- metadata +78 -73
- data/lib/savon/request.rb +0 -113
- data/lib/savon/request_logger.rb +0 -54
data/lib/savon/options.rb
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require "logger"
|
|
3
4
|
require "httpi"
|
|
4
5
|
|
|
5
6
|
module Savon
|
|
7
|
+
# Base class for GlobalOptions and LocalOptions.
|
|
8
|
+
# Stores options in a hash, dispatches setter calls by method name,
|
|
9
|
+
# raises UnknownOptionError for anything not defined on the subclass.
|
|
6
10
|
class Options
|
|
7
|
-
|
|
8
11
|
def initialize(options = {})
|
|
9
12
|
@options = {}
|
|
10
13
|
assign options
|
|
@@ -18,7 +21,7 @@ module Savon
|
|
|
18
21
|
|
|
19
22
|
def []=(option, value)
|
|
20
23
|
value = [value].flatten
|
|
21
|
-
|
|
24
|
+
send(option, *value)
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
def include?(option)
|
|
@@ -29,7 +32,7 @@ module Savon
|
|
|
29
32
|
|
|
30
33
|
def assign(options)
|
|
31
34
|
options.each do |option, value|
|
|
32
|
-
|
|
35
|
+
send(option, value)
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
|
|
@@ -38,6 +41,8 @@ module Savon
|
|
|
38
41
|
end
|
|
39
42
|
end
|
|
40
43
|
|
|
44
|
+
# Options available in both GlobalOptions and LocalOptions.
|
|
45
|
+
# Currently covers WSSE authentication and timestamp headers.
|
|
41
46
|
module SharedOptions
|
|
42
47
|
# WSSE auth credentials for Akami.
|
|
43
48
|
# Local will override the global wsse_auth value, e.g.
|
|
@@ -46,11 +51,12 @@ module Savon
|
|
|
46
51
|
# global == [user, pass] && local == nil => [user, pass]
|
|
47
52
|
def wsse_auth(*credentials)
|
|
48
53
|
credentials.flatten!
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
@options[:wsse_auth] =
|
|
55
|
+
if credentials.size == 1
|
|
56
|
+
credentials.first
|
|
57
|
+
else
|
|
58
|
+
credentials
|
|
59
|
+
end
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
# Instruct Akami to enable wsu:Timestamp headers.
|
|
@@ -67,33 +73,229 @@ module Savon
|
|
|
67
73
|
end
|
|
68
74
|
end
|
|
69
75
|
|
|
76
|
+
# HTTPI-specific transport options included in GlobalOptions.
|
|
77
|
+
#
|
|
78
|
+
# Every option in this module is handled by HTTPI and has no effect when
|
|
79
|
+
# transport: :faraday is set. Faraday callers configure these concerns
|
|
80
|
+
# directly on the Faraday::Connection returned by client.faraday.
|
|
81
|
+
module HTTPITransportOptions
|
|
82
|
+
# Maps each httpi-only option to the Faraday equivalent so the
|
|
83
|
+
# error raised at init tells the caller exactly what to do instead.
|
|
84
|
+
# Options with a :default entry are only flagged when the caller
|
|
85
|
+
# sets a value that differs from the GlobalOptions default.
|
|
86
|
+
FARADAY_INCOMPATIBLE_GLOBALS = {
|
|
87
|
+
proxy: { hint: "client.faraday.proxy = url" },
|
|
88
|
+
open_timeout: { hint: "client.faraday.options.timeout = N" },
|
|
89
|
+
read_timeout: { hint: "client.faraday.options.timeout = N" },
|
|
90
|
+
write_timeout: { hint: "client.faraday.options.write_timeout = N" },
|
|
91
|
+
ssl_version: { hint: "client.faraday.ssl.version = version" },
|
|
92
|
+
ssl_min_version: { hint: "client.faraday.ssl.min_version = version" },
|
|
93
|
+
ssl_max_version: { hint: "client.faraday.ssl.max_version = version" },
|
|
94
|
+
ssl_verify_mode: { hint: "client.faraday.ssl.verify = true/false" },
|
|
95
|
+
ssl_cert_key_file: { hint: "client.faraday.ssl.client_key_file = path" },
|
|
96
|
+
ssl_cert_key: { hint: "client.faraday.ssl.client_key = key" },
|
|
97
|
+
ssl_cert_key_password: { hint: "configure ssl context on client.faraday.ssl" },
|
|
98
|
+
ssl_cert_file: { hint: "client.faraday.ssl.client_cert_file = path" },
|
|
99
|
+
ssl_cert: { hint: "client.faraday.ssl.client_cert = cert" },
|
|
100
|
+
ssl_ca_cert_file: { hint: "client.faraday.ssl.ca_file = path" },
|
|
101
|
+
ssl_ca_cert: { hint: "client.faraday.ssl.ca_cert = cert" },
|
|
102
|
+
ssl_ciphers: { hint: "client.faraday.ssl.ciphers = ciphers" },
|
|
103
|
+
ssl_ca_cert_path: { hint: "client.faraday.ssl.ca_path = path" },
|
|
104
|
+
ssl_cert_store: { hint: "client.faraday.ssl.cert_store = store" },
|
|
105
|
+
basic_auth: { hint: "client.faraday.request :basic_auth, user, pass" },
|
|
106
|
+
digest_auth: { hint: "client.faraday.request :authorization, :Digest, credentials" },
|
|
107
|
+
ntlm: { hint: "client.faraday.request :ntlm, user, pass" },
|
|
108
|
+
follow_redirects: { hint: "client.faraday.use :follow_redirects", default: false },
|
|
109
|
+
adapter: { hint: "client.faraday.adapter :net_http", default: nil }
|
|
110
|
+
}.freeze
|
|
111
|
+
|
|
112
|
+
# Validates that the chosen transport is compatible with the options set.
|
|
113
|
+
# Must be called after all options (including any block-form options) are set.
|
|
114
|
+
# Collects every conflict and raises a single InitializationError listing all
|
|
115
|
+
# problems and solutions at once.
|
|
116
|
+
def validate_transport!
|
|
117
|
+
return unless self[:transport] == :faraday
|
|
118
|
+
|
|
119
|
+
unless faraday_loaded?
|
|
120
|
+
raise InitializationError,
|
|
121
|
+
"transport: :faraday requires the faraday gem.\n" \
|
|
122
|
+
"Add to your Gemfile: gem 'faraday'"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
violations = FARADAY_INCOMPATIBLE_GLOBALS.filter_map { |option, config|
|
|
126
|
+
next unless include?(option)
|
|
127
|
+
next if config.key?(:default) && self[option] == config[:default]
|
|
128
|
+
|
|
129
|
+
" #{option} - Use: #{config[:hint]}"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return if violations.empty?
|
|
133
|
+
|
|
134
|
+
raise InitializationError,
|
|
135
|
+
"The following options are not supported with transport: :faraday:\n#{violations.join("\n")}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Proxy server to use for all requests.
|
|
139
|
+
def proxy(proxy)
|
|
140
|
+
@options[:proxy] = proxy unless proxy.nil?
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Open timeout in seconds.
|
|
144
|
+
def open_timeout(open_timeout)
|
|
145
|
+
@options[:open_timeout] = open_timeout
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Read timeout in seconds.
|
|
149
|
+
def read_timeout(read_timeout)
|
|
150
|
+
@options[:read_timeout] = read_timeout
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Write timeout in seconds.
|
|
154
|
+
def write_timeout(write_timeout)
|
|
155
|
+
@options[:write_timeout] = write_timeout
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Specifies the SSL version to use.
|
|
159
|
+
def ssl_version(version)
|
|
160
|
+
@options[:ssl_version] = version
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Specifies the minimum SSL version to use.
|
|
164
|
+
def ssl_min_version(version)
|
|
165
|
+
@options[:ssl_min_version] = version
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Specifies the maximum SSL version to use.
|
|
169
|
+
def ssl_max_version(version)
|
|
170
|
+
@options[:ssl_max_version] = version
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Whether and how to verify the SSL connection.
|
|
174
|
+
def ssl_verify_mode(verify_mode)
|
|
175
|
+
@options[:ssl_verify_mode] = verify_mode
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Sets the cert key file to use.
|
|
179
|
+
def ssl_cert_key_file(file)
|
|
180
|
+
@options[:ssl_cert_key_file] = file
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Sets the cert key to use.
|
|
184
|
+
def ssl_cert_key(key)
|
|
185
|
+
@options[:ssl_cert_key] = key
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Sets the cert key password to use.
|
|
189
|
+
def ssl_cert_key_password(password)
|
|
190
|
+
@options[:ssl_cert_key_password] = password
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Sets the cert file to use.
|
|
194
|
+
def ssl_cert_file(file)
|
|
195
|
+
@options[:ssl_cert_file] = file
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Sets the cert to use.
|
|
199
|
+
def ssl_cert(cert)
|
|
200
|
+
@options[:ssl_cert] = cert
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Sets the CA cert file to use.
|
|
204
|
+
def ssl_ca_cert_file(file)
|
|
205
|
+
@options[:ssl_ca_cert_file] = file
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Sets the CA cert to use.
|
|
209
|
+
def ssl_ca_cert(cert)
|
|
210
|
+
@options[:ssl_ca_cert] = cert
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Sets the SSL ciphers to use.
|
|
214
|
+
def ssl_ciphers(ciphers)
|
|
215
|
+
@options[:ssl_ciphers] = ciphers
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Sets the CA cert path.
|
|
219
|
+
def ssl_ca_cert_path(path)
|
|
220
|
+
@options[:ssl_ca_cert_path] = path
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Sets the SSL cert store.
|
|
224
|
+
def ssl_cert_store(store)
|
|
225
|
+
@options[:ssl_cert_store] = store
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# HTTP basic auth credentials.
|
|
229
|
+
def basic_auth(*credentials)
|
|
230
|
+
@options[:basic_auth] = credentials.flatten
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# HTTP digest auth credentials.
|
|
234
|
+
def digest_auth(*credentials)
|
|
235
|
+
@options[:digest_auth] = credentials.flatten
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# NTLM auth credentials.
|
|
239
|
+
def ntlm(*credentials)
|
|
240
|
+
@options[:ntlm] = credentials.flatten
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Instruct requests to follow HTTP redirects.
|
|
244
|
+
def follow_redirects(follow_redirects)
|
|
245
|
+
@options[:follow_redirects] = follow_redirects
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Instruct Savon which HTTPI adapter to use instead of the default.
|
|
249
|
+
def adapter(adapter)
|
|
250
|
+
@options[:adapter] = adapter
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
private
|
|
254
|
+
|
|
255
|
+
# Attempts to load faraday. Returns true if available, false on LoadError.
|
|
256
|
+
def faraday_loaded?
|
|
257
|
+
require "faraday"
|
|
258
|
+
true
|
|
259
|
+
rescue LoadError
|
|
260
|
+
false
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Client-level options applied to every request made by a Savon::Client instance.
|
|
265
|
+
# Covers service location, SOAP configuration, logging, response parsing,
|
|
266
|
+
# and transport selection. HTTPI-specific options (proxy, timeouts, SSL, auth)
|
|
267
|
+
# come from HTTPITransportOptions.
|
|
70
268
|
class GlobalOptions < Options
|
|
71
269
|
include SharedOptions
|
|
270
|
+
include HTTPITransportOptions
|
|
72
271
|
|
|
73
272
|
def initialize(options = {})
|
|
74
273
|
@option_type = :global
|
|
75
274
|
|
|
76
275
|
defaults = {
|
|
77
|
-
:
|
|
78
|
-
:
|
|
79
|
-
:
|
|
80
|
-
:
|
|
81
|
-
:
|
|
82
|
-
:
|
|
83
|
-
:
|
|
84
|
-
:
|
|
85
|
-
:
|
|
86
|
-
:
|
|
87
|
-
:
|
|
88
|
-
:
|
|
89
|
-
:
|
|
90
|
-
:
|
|
91
|
-
:
|
|
92
|
-
:
|
|
93
|
-
:
|
|
94
|
-
:
|
|
95
|
-
:
|
|
96
|
-
|
|
276
|
+
encoding: "UTF-8",
|
|
277
|
+
soap_version: 1,
|
|
278
|
+
namespaces: {},
|
|
279
|
+
logger: Logger.new($stdout),
|
|
280
|
+
log: false,
|
|
281
|
+
log_headers: true,
|
|
282
|
+
filters: [],
|
|
283
|
+
pretty_print_xml: false,
|
|
284
|
+
raise_errors: true,
|
|
285
|
+
strip_namespaces: true,
|
|
286
|
+
delete_namespace_attributes: false,
|
|
287
|
+
convert_response_tags_to: ->(tag) { StringUtils.snakecase(tag).to_sym },
|
|
288
|
+
convert_attributes_to: ->(k, v) { [k, v] },
|
|
289
|
+
multipart: false,
|
|
290
|
+
use_wsa_headers: false,
|
|
291
|
+
no_message_tag: false,
|
|
292
|
+
unwrap: false,
|
|
293
|
+
host: nil,
|
|
294
|
+
transport: :httpi,
|
|
295
|
+
|
|
296
|
+
# httpi transport defaults
|
|
297
|
+
adapter: nil,
|
|
298
|
+
follow_redirects: false
|
|
97
299
|
}
|
|
98
300
|
|
|
99
301
|
options = defaults.merge(options)
|
|
@@ -112,7 +314,7 @@ module Savon
|
|
|
112
314
|
@options[:wsdl] = wsdl_address
|
|
113
315
|
end
|
|
114
316
|
|
|
115
|
-
#
|
|
317
|
+
# Set a different host for actions in the WSDL.
|
|
116
318
|
def host(host)
|
|
117
319
|
@options[:host] = host
|
|
118
320
|
end
|
|
@@ -127,7 +329,7 @@ module Savon
|
|
|
127
329
|
@options[:namespace] = namespace
|
|
128
330
|
end
|
|
129
331
|
|
|
130
|
-
# The namespace
|
|
332
|
+
# The namespace identifier.
|
|
131
333
|
def namespace_identifier(identifier)
|
|
132
334
|
@options[:namespace_identifier] = identifier
|
|
133
335
|
end
|
|
@@ -137,31 +339,11 @@ module Savon
|
|
|
137
339
|
@options[:namespaces] = namespaces
|
|
138
340
|
end
|
|
139
341
|
|
|
140
|
-
#
|
|
141
|
-
def proxy(proxy)
|
|
142
|
-
@options[:proxy] = proxy unless proxy.nil?
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# A Hash of HTTP headers.
|
|
342
|
+
# A Hash of HTTP headers sent with every request.
|
|
146
343
|
def headers(headers)
|
|
147
344
|
@options[:headers] = headers
|
|
148
345
|
end
|
|
149
346
|
|
|
150
|
-
# Open timeout in seconds.
|
|
151
|
-
def open_timeout(open_timeout)
|
|
152
|
-
@options[:open_timeout] = open_timeout
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# Read timeout in seconds.
|
|
156
|
-
def read_timeout(read_timeout)
|
|
157
|
-
@options[:read_timeout] = read_timeout
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# Write timeout in seconds.
|
|
161
|
-
def write_timeout(write_timeout)
|
|
162
|
-
@options[:write_timeout] = write_timeout
|
|
163
|
-
end
|
|
164
|
-
|
|
165
347
|
# The encoding to use. Defaults to "UTF-8".
|
|
166
348
|
def encoding(encoding)
|
|
167
349
|
@options[:encoding] = encoding
|
|
@@ -210,7 +392,7 @@ module Savon
|
|
|
210
392
|
|
|
211
393
|
# Changes the Logger's log level.
|
|
212
394
|
def log_level(level)
|
|
213
|
-
levels = { :
|
|
395
|
+
levels = { debug: 0, info: 1, warn: 2, error: 3, fatal: 4 }
|
|
214
396
|
|
|
215
397
|
unless levels.include? level
|
|
216
398
|
raise ArgumentError, "Invalid log level: #{level.inspect}\n" \
|
|
@@ -220,7 +402,7 @@ module Savon
|
|
|
220
402
|
@options[:logger].level = levels[level]
|
|
221
403
|
end
|
|
222
404
|
|
|
223
|
-
#
|
|
405
|
+
# Whether to log headers.
|
|
224
406
|
def log_headers(log_headers)
|
|
225
407
|
@options[:log_headers] = log_headers
|
|
226
408
|
end
|
|
@@ -235,90 +417,6 @@ module Savon
|
|
|
235
417
|
@options[:pretty_print_xml] = pretty_print_xml
|
|
236
418
|
end
|
|
237
419
|
|
|
238
|
-
# Specifies the SSL version to use.
|
|
239
|
-
def ssl_version(version)
|
|
240
|
-
@options[:ssl_version] = version
|
|
241
|
-
end
|
|
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
|
-
|
|
253
|
-
# Whether and how to to verify the connection.
|
|
254
|
-
def ssl_verify_mode(verify_mode)
|
|
255
|
-
@options[:ssl_verify_mode] = verify_mode
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
# Sets the cert key file to use.
|
|
259
|
-
def ssl_cert_key_file(file)
|
|
260
|
-
@options[:ssl_cert_key_file] = file
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
# Sets the cert key to use.
|
|
264
|
-
def ssl_cert_key(key)
|
|
265
|
-
@options[:ssl_cert_key] = key
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
# Sets the cert key password to use.
|
|
269
|
-
def ssl_cert_key_password(password)
|
|
270
|
-
@options[:ssl_cert_key_password] = password
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
# Sets the cert file to use.
|
|
274
|
-
def ssl_cert_file(file)
|
|
275
|
-
@options[:ssl_cert_file] = file
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
# Sets the cert to use.
|
|
279
|
-
def ssl_cert(cert)
|
|
280
|
-
@options[:ssl_cert] = cert
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
# Sets the ca cert file to use.
|
|
284
|
-
def ssl_ca_cert_file(file)
|
|
285
|
-
@options[:ssl_ca_cert_file] = file
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
# Sets the ca cert to use.
|
|
289
|
-
def ssl_ca_cert(cert)
|
|
290
|
-
@options[:ssl_ca_cert] = cert
|
|
291
|
-
end
|
|
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
|
|
306
|
-
|
|
307
|
-
# HTTP basic auth credentials.
|
|
308
|
-
def basic_auth(*credentials)
|
|
309
|
-
@options[:basic_auth] = credentials.flatten
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
# HTTP digest auth credentials.
|
|
313
|
-
def digest_auth(*credentials)
|
|
314
|
-
@options[:digest_auth] = credentials.flatten
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
# NTLM auth credentials.
|
|
318
|
-
def ntlm(*credentials)
|
|
319
|
-
@options[:ntlm] = credentials.flatten
|
|
320
|
-
end
|
|
321
|
-
|
|
322
420
|
# Instruct Nori whether to strip namespaces from XML nodes.
|
|
323
421
|
def strip_namespaces(strip_namespaces)
|
|
324
422
|
@options[:strip_namespaces] = strip_namespaces
|
|
@@ -335,22 +433,22 @@ module Savon
|
|
|
335
433
|
@options[:convert_request_keys_to] = converter
|
|
336
434
|
end
|
|
337
435
|
|
|
338
|
-
# Tell Gyoku to unwrap Array of Hashes
|
|
339
|
-
# Accepts a boolean,
|
|
436
|
+
# Tell Gyoku to unwrap Array of Hashes.
|
|
437
|
+
# Accepts a boolean, defaults to false.
|
|
340
438
|
def unwrap(unwrap)
|
|
341
439
|
@options[:unwrap] = unwrap
|
|
342
440
|
end
|
|
343
441
|
|
|
344
442
|
# Tell Nori how to convert XML tags from the SOAP response into Hash keys.
|
|
345
443
|
# Accepts a lambda or a block which receives an XML tag and returns a Hash key.
|
|
346
|
-
# Defaults to
|
|
444
|
+
# Defaults to converting tags to snakecase Symbols.
|
|
347
445
|
def convert_response_tags_to(converter = nil, &block)
|
|
348
446
|
@options[:convert_response_tags_to] = block || converter
|
|
349
447
|
end
|
|
350
448
|
|
|
351
449
|
# Tell Nori how to convert XML attributes on tags from the SOAP response into Hash keys.
|
|
352
450
|
# Accepts a lambda or a block which receives an XML tag and returns a Hash key.
|
|
353
|
-
# Defaults to doing nothing
|
|
451
|
+
# Defaults to doing nothing.
|
|
354
452
|
def convert_attributes_to(converter = nil, &block)
|
|
355
453
|
@options[:convert_attributes_to] = block || converter
|
|
356
454
|
end
|
|
@@ -360,26 +458,27 @@ module Savon
|
|
|
360
458
|
@options[:multipart] = multipart
|
|
361
459
|
end
|
|
362
460
|
|
|
363
|
-
# Instruct Savon what HTTPI adapter it should use instead of default
|
|
364
|
-
def adapter(adapter)
|
|
365
|
-
@options[:adapter] = adapter
|
|
366
|
-
end
|
|
367
|
-
|
|
368
461
|
# Enable inclusion of WS-Addressing headers.
|
|
369
462
|
def use_wsa_headers(use)
|
|
370
463
|
@options[:use_wsa_headers] = use
|
|
371
464
|
end
|
|
372
465
|
|
|
466
|
+
# Suppress the message tag wrapper around the SOAP body.
|
|
373
467
|
def no_message_tag(bool)
|
|
374
468
|
@options[:no_message_tag] = bool
|
|
375
469
|
end
|
|
376
470
|
|
|
377
|
-
#
|
|
378
|
-
|
|
379
|
-
|
|
471
|
+
# HTTP transport to use. Accepts :httpi (default) or :faraday.
|
|
472
|
+
# When set to :faraday, configure transport concerns directly on the
|
|
473
|
+
# Faraday::Connection returned by client.faraday instead of using
|
|
474
|
+
# the HTTPITransportOptions.
|
|
475
|
+
def transport(transport)
|
|
476
|
+
@options[:transport] = transport
|
|
380
477
|
end
|
|
381
478
|
end
|
|
382
479
|
|
|
480
|
+
# Per-request options passed to client.call.
|
|
481
|
+
# Overrides or extends the matching GlobalOptions for a single SOAP operation.
|
|
383
482
|
class LocalOptions < Options
|
|
384
483
|
include SharedOptions
|
|
385
484
|
|
|
@@ -387,9 +486,9 @@ module Savon
|
|
|
387
486
|
@option_type = :local
|
|
388
487
|
|
|
389
488
|
defaults = {
|
|
390
|
-
:
|
|
391
|
-
:
|
|
392
|
-
:
|
|
489
|
+
advanced_typecasting: true,
|
|
490
|
+
response_parser: :nokogiri,
|
|
491
|
+
multipart: false
|
|
393
492
|
}
|
|
394
493
|
|
|
395
494
|
super defaults.merge(options)
|
|
@@ -397,7 +496,7 @@ module Savon
|
|
|
397
496
|
|
|
398
497
|
# The local SOAP header. Expected to be a Hash or respond to #to_s.
|
|
399
498
|
# Will be merged with the global SOAP header if both are Hashes.
|
|
400
|
-
# Otherwise the local option will be
|
|
499
|
+
# Otherwise the local option will be preferred.
|
|
401
500
|
def soap_header(header)
|
|
402
501
|
@options[:soap_header] = header
|
|
403
502
|
end
|
|
@@ -482,6 +581,7 @@ module Savon
|
|
|
482
581
|
@options[:multipart] = multipart
|
|
483
582
|
end
|
|
484
583
|
|
|
584
|
+
# Per-request HTTP headers. Merged with global headers for each request.
|
|
485
585
|
def headers(headers)
|
|
486
586
|
@options[:headers] = headers
|
|
487
587
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require "gyoku"
|
|
3
4
|
|
|
4
5
|
module Savon
|
|
@@ -37,7 +38,7 @@ module Savon
|
|
|
37
38
|
private
|
|
38
39
|
|
|
39
40
|
def translate_tag(key)
|
|
40
|
-
Gyoku.xml_tag(key, :
|
|
41
|
+
Gyoku.xml_tag(key, key_converter: @key_converter).to_s
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
def add_namespaces_to_values(values, path)
|
data/lib/savon/response.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require "nori"
|
|
3
4
|
require "savon/soap_fault"
|
|
4
5
|
require "savon/http_error"
|
|
@@ -6,14 +7,14 @@ require "savon/http_error"
|
|
|
6
7
|
module Savon
|
|
7
8
|
class Response
|
|
8
9
|
CRLF = /\r\n/
|
|
9
|
-
WSP = /[
|
|
10
|
+
WSP = /[\t ]/
|
|
10
11
|
|
|
11
12
|
def initialize(http, globals, locals)
|
|
12
|
-
@http
|
|
13
|
-
@globals
|
|
14
|
-
@locals
|
|
15
|
-
@attachments
|
|
16
|
-
@xml
|
|
13
|
+
@http = http
|
|
14
|
+
@globals = globals
|
|
15
|
+
@locals = locals
|
|
16
|
+
@attachments = []
|
|
17
|
+
@xml = ''
|
|
17
18
|
@has_parsed_body = false
|
|
18
19
|
|
|
19
20
|
build_soap_and_http_errors!
|
|
@@ -25,7 +26,7 @@ module Savon
|
|
|
25
26
|
def success?
|
|
26
27
|
!soap_fault? && !http_error?
|
|
27
28
|
end
|
|
28
|
-
|
|
29
|
+
alias successful? success?
|
|
29
30
|
|
|
30
31
|
def soap_fault?
|
|
31
32
|
SOAPFault.present?(@http, xml)
|
|
@@ -43,15 +44,16 @@ module Savon
|
|
|
43
44
|
find('Body')
|
|
44
45
|
end
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
alias to_hash body
|
|
47
48
|
|
|
48
49
|
def to_array(*path)
|
|
49
|
-
result = path.inject
|
|
50
|
+
result = path.inject(body) { |memo, key|
|
|
50
51
|
return [] if memo[key].nil?
|
|
52
|
+
|
|
51
53
|
memo[key]
|
|
52
|
-
|
|
54
|
+
}
|
|
53
55
|
|
|
54
|
-
result.
|
|
56
|
+
result.is_a?(Array) ? result.compact : [result].compact
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
def hash
|
|
@@ -72,8 +74,8 @@ module Savon
|
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
alias to_xml xml
|
|
78
|
+
alias to_s xml
|
|
77
79
|
|
|
78
80
|
def doc
|
|
79
81
|
@doc ||= Nokogiri.XML(xml)
|
|
@@ -107,19 +109,20 @@ module Savon
|
|
|
107
109
|
|
|
108
110
|
def boundary
|
|
109
111
|
return unless multipart?
|
|
112
|
+
|
|
110
113
|
Mail::Field.new('content-type', http.headers['content-type']).parameters['boundary']
|
|
111
114
|
end
|
|
112
115
|
|
|
113
116
|
def parse_body
|
|
114
117
|
http.body.force_encoding Encoding::ASCII_8BIT
|
|
115
118
|
parts = http.body.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
|
|
116
|
-
parts[1
|
|
119
|
+
parts[1..].to_a.each_with_index do |part, index|
|
|
117
120
|
header_part, body_part = part.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
|
|
118
121
|
section = Mail::Part.new(
|
|
119
122
|
body: body_part
|
|
120
123
|
)
|
|
121
124
|
section.header = header_part
|
|
122
|
-
if index
|
|
125
|
+
if index.zero?
|
|
123
126
|
@xml = section.body.to_s
|
|
124
127
|
else
|
|
125
128
|
@attachments << section
|
|
@@ -139,7 +142,7 @@ module Savon
|
|
|
139
142
|
end
|
|
140
143
|
|
|
141
144
|
def raise_invalid_response_error!
|
|
142
|
-
raise InvalidResponseError, "Unable to parse response body:\n
|
|
145
|
+
raise InvalidResponseError, "Unable to parse response body:\n#{xml.inspect}"
|
|
143
146
|
end
|
|
144
147
|
|
|
145
148
|
def xml_namespaces
|
|
@@ -150,17 +153,16 @@ module Savon
|
|
|
150
153
|
return @nori if @nori
|
|
151
154
|
|
|
152
155
|
nori_options = {
|
|
153
|
-
:
|
|
154
|
-
:
|
|
155
|
-
:
|
|
156
|
-
:
|
|
157
|
-
:
|
|
158
|
-
:
|
|
156
|
+
delete_namespace_attributes: @globals[:delete_namespace_attributes],
|
|
157
|
+
strip_namespaces: @globals[:strip_namespaces],
|
|
158
|
+
convert_tags_to: @globals[:convert_response_tags_to],
|
|
159
|
+
convert_attributes_to: @globals[:convert_attributes_to],
|
|
160
|
+
advanced_typecasting: @locals[:advanced_typecasting],
|
|
161
|
+
parser: @locals[:response_parser]
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
non_nil_nori_options = nori_options.reject { |_, value| value.nil? }
|
|
162
165
|
@nori = Nori.new(non_nil_nori_options)
|
|
163
166
|
end
|
|
164
|
-
|
|
165
167
|
end
|
|
166
168
|
end
|