savon 2.2.0 → 2.3.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 +7 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +50 -2
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +4 -2
- data/README.md +18 -9
- data/donate.png +0 -0
- data/lib/savon/builder.rb +5 -4
- data/lib/savon/core_ext/string.rb +0 -1
- data/lib/savon/header.rb +45 -17
- data/lib/savon/message.rb +1 -3
- data/lib/savon/mock/expectation.rb +1 -0
- data/lib/savon/operation.rb +25 -36
- data/lib/savon/options.rb +27 -3
- data/lib/savon/qualified_message.rb +14 -10
- data/lib/savon/request.rb +1 -0
- data/lib/savon/request_logger.rb +48 -0
- data/lib/savon/response.rb +28 -13
- data/lib/savon/version.rb +1 -1
- data/savon.gemspec +9 -8
- data/spec/fixtures/wsdl/vies.xml +176 -0
- data/spec/savon/builder_spec.rb +0 -1
- data/spec/savon/features/message_tag_spec.rb +5 -0
- data/spec/savon/message_spec.rb +40 -0
- data/spec/savon/mock_spec.rb +14 -0
- data/spec/savon/operation_spec.rb +50 -3
- data/spec/savon/options_spec.rb +91 -6
- data/spec/savon/request_spec.rb +28 -0
- data/spec/savon/response_spec.rb +75 -1
- data/spec/spec_helper.rb +5 -2
- data/tags +299 -0
- metadata +40 -62
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cae0817f78856aab3d2755e35f8d8eae26f13ab1
|
4
|
+
data.tar.gz: f072a4794247d9bbd34e1c20884c398524b34ef3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 60c8697a66a84e2405318c27900207f14734231baa672ca609e77c8425e9ed6e6a897df1e1e62759775acfb14d66194bb08c32210552b0d19cb570c9ff927d70
|
7
|
+
data.tar.gz: 1467af7b259d9a6d67b724eabe9cd9905b37580b923c552ce0b7a76251e6a3e960379dab11ba8e3f2555502aebf56b7f8728a1f331c51975b5a905212da1ed1c
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,51 @@
|
|
1
|
+
### 2.3.0 (2013-07-27)
|
2
|
+
|
3
|
+
Combined release ticket: [#481](https://github.com/savonrb/savon/issues/481)
|
4
|
+
|
5
|
+
* Feature: [#405](https://github.com/savonrb/savon/issues/405) Improved NTLM support based on HTTPI v2.1.0.
|
6
|
+
|
7
|
+
* Feature: [#424](https://github.com/savonrb/savon/issues/424) Adds support for multipart responses
|
8
|
+
through the updated [savon-multipart](https://github.com/savonrb/savon-multipart) gem. You can now
|
9
|
+
specify `multipart: true` either as a global or local option. Please make sure you have the
|
10
|
+
updated `savon-multipart` gem installed and loaded, as it is not a direct dependency of Savon.
|
11
|
+
|
12
|
+
``` ruby
|
13
|
+
require 'savon'
|
14
|
+
require 'savon-multipart'
|
15
|
+
|
16
|
+
# expect multipart responses for every operation
|
17
|
+
client = Savon.client(wsdl: wsdl, multipart: true)
|
18
|
+
|
19
|
+
# only expect a multipart response for this operation
|
20
|
+
client.call(:my_operation, multipart: true)
|
21
|
+
```
|
22
|
+
|
23
|
+
* Feature: [#470](https://github.com/savonrb/savon/issues/470) Added a local `:soap_header` option
|
24
|
+
to allow setting the SOAP header per request.
|
25
|
+
|
26
|
+
* Feature: [#402](https://github.com/savonrb/savon/issues/402) Makes it possible to create mocks
|
27
|
+
that don't care about the message sent by using `:any` for the `:message` option.
|
28
|
+
|
29
|
+
``` ruby
|
30
|
+
savon.expects(:authenticate).with(message: :any)
|
31
|
+
```
|
32
|
+
|
33
|
+
* Fix: [#450](https://github.com/savonrb/savon/pull/450) Added `Savon::Response#soap_fault`
|
34
|
+
and `Savon::Response#http_error` which were present in version 1.
|
35
|
+
|
36
|
+
* Fix: [#474](https://github.com/savonrb/savon/issues/474) Changed `Savon::Response#header` and
|
37
|
+
`Savon::Response#body` to respect the global `:convert_response_tags_to` and `:strip_namespaces`
|
38
|
+
options and return the expected result instead of raising a `Savon::InvalidResponseError`.
|
39
|
+
|
40
|
+
* Fix: [#461](https://github.com/savonrb/savon/issues/461) Fixed two problems related to namespace
|
41
|
+
qualified messages and the element `:order!`.
|
42
|
+
|
43
|
+
* Fix: [#476](https://github.com/savonrb/savon/issues/476) fixes a problem where the namespace
|
44
|
+
for the message tag was not correctly determined from the WSDL.
|
45
|
+
|
46
|
+
* Fix: [#468](https://github.com/savonrb/savon/issues/468) Changed the dependency on Nokogiri
|
47
|
+
to < 1.6, because Nokogiri 1.6 dropped support for Ruby 1.8.
|
48
|
+
|
1
49
|
### 2.2.0 (2013-04-21)
|
2
50
|
|
3
51
|
* Feature: [#416](https://github.com/savonrb/savon/pull/416) The global `namespace_identifier`
|
@@ -10,14 +58,14 @@
|
|
10
58
|
This is because regardless of whether you're using the Hash or block syntax to pass global
|
11
59
|
or local options, both are just method calls on some options object.
|
12
60
|
|
13
|
-
```
|
61
|
+
``` ruby
|
14
62
|
NoMethodError: undefined method 'wsdk' for #<Savon::GlobalOptions:0x007fed95a55228>
|
15
63
|
```
|
16
64
|
|
17
65
|
As of this change, Savon now catches those errors and raise a `Savon::UnknownOptionError`
|
18
66
|
with a slightly more helpful error message instead.
|
19
67
|
|
20
|
-
```
|
68
|
+
``` ruby
|
21
69
|
Savon::UnknownOptionError:
|
22
70
|
Unknown global option: :wsdk
|
23
71
|
```
|
data/CONTRIBUTING.md
CHANGED
@@ -14,7 +14,7 @@ problems and make sure they don't come back.
|
|
14
14
|
|
15
15
|
So if you can reproduce your problem in a spec, that would be awesome! If you can't, please
|
16
16
|
let us know how we could make this easier for you. Also, provide code and the WSDL of the
|
17
|
-
service
|
17
|
+
service you're working with so others can try to come up with a spec for your problem.
|
18
18
|
|
19
19
|
After we have a failing spec, it obviously needs to be fixed. Make sure your new spec is the
|
20
20
|
only failing one under the `spec` directory. Travis only runs the "unit tests" at `spec/savon`,
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -5,14 +5,15 @@ Heavy metal SOAP client
|
|
5
5
|
[Documentation](http://savonrb.com) | [RDoc](http://rubydoc.info/gems/savon) |
|
6
6
|
[Mailing list](https://groups.google.com/forum/#!forum/savonrb) | [Twitter](http://twitter.com/savonrb)
|
7
7
|
|
8
|
-
[](http://travis-ci.org/savonrb/savon)
|
8
|
+
[](http://travis-ci.org/savonrb/savon)
|
9
9
|
[](http://badge.fury.io/rb/savon)
|
10
10
|
[](https://codeclimate.com/github/savonrb/savon)
|
11
|
+
[](https://coveralls.io/r/savonrb/savon)
|
11
12
|
|
12
13
|
|
13
|
-
##
|
14
|
+
## Version 2
|
14
15
|
|
15
|
-
Savon is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
|
16
|
+
Savon version 2 is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
|
16
17
|
|
17
18
|
```
|
18
19
|
$ gem install savon
|
@@ -21,7 +22,7 @@ $ gem install savon
|
|
21
22
|
or add it to your Gemfile like this:
|
22
23
|
|
23
24
|
```
|
24
|
-
gem 'savon', '~> 2.
|
25
|
+
gem 'savon', '~> 2.2.0'
|
25
26
|
```
|
26
27
|
|
27
28
|
|
@@ -43,15 +44,23 @@ response.body
|
|
43
44
|
# => { find_user_response: { id: 42, name: 'Hoff' } }
|
44
45
|
```
|
45
46
|
|
46
|
-
For more examples, you should check out the
|
47
|
+
For more examples, you should check out the
|
48
|
+
[integration tests](https://github.com/savonrb/savon/tree/version2/spec/integration).
|
47
49
|
|
48
50
|
|
49
|
-
##
|
51
|
+
## Give back
|
52
|
+
|
53
|
+
If you're using Savon and you or your company is making money from it, then please consider
|
54
|
+
donating via [Gittip](https://www.gittip.com/rubiii/) so that I can continue to improve it.
|
55
|
+
|
56
|
+
[](https://www.gittip.com/rubiii/)
|
50
57
|
|
51
|
-
Please make sure to read the documentation for your version:
|
52
58
|
|
53
|
-
|
54
|
-
|
59
|
+
## Documentation
|
60
|
+
|
61
|
+
Please make sure to [read the documentation](http://savonrb.com/version2/).
|
55
62
|
|
56
63
|
And if you find any problems with it or if you think something's missing,
|
57
64
|
feel free to [help out and improve the documentation](https://github.com/savonrb/savonrb.com).
|
65
|
+
|
66
|
+
Donate icon from the [Noun Project](http://thenounproject.com/noun/donate/#icon-No285).
|
data/donate.png
ADDED
Binary file
|
data/lib/savon/builder.rb
CHANGED
@@ -101,12 +101,13 @@ module Savon
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def namespaced_message_tag
|
104
|
+
tag_name = message_tag
|
104
105
|
if namespace_identifier == nil
|
105
|
-
[
|
106
|
-
elsif @used_namespaces[[
|
107
|
-
[@used_namespaces[[
|
106
|
+
[tag_name, message_attributes]
|
107
|
+
elsif @used_namespaces[[tag_name.to_s]]
|
108
|
+
[@used_namespaces[[tag_name.to_s]], tag_name, message_attributes]
|
108
109
|
else
|
109
|
-
[namespace_identifier,
|
110
|
+
[namespace_identifier, tag_name, message_attributes]
|
110
111
|
end
|
111
112
|
end
|
112
113
|
|
data/lib/savon/header.rb
CHANGED
@@ -5,37 +5,65 @@ module Savon
|
|
5
5
|
class Header
|
6
6
|
|
7
7
|
def initialize(globals, locals)
|
8
|
-
@
|
9
|
-
|
10
|
-
@
|
8
|
+
@gyoku_options = { :key_converter => globals[:convert_request_keys_to] }
|
9
|
+
|
10
|
+
@wsse_auth = globals[:wsse_auth]
|
11
|
+
@wsse_timestamp = globals[:wsse_timestamp]
|
12
|
+
|
13
|
+
@global_header = globals[:soap_header]
|
14
|
+
@local_header = locals[:soap_header]
|
15
|
+
|
16
|
+
@header = build
|
11
17
|
end
|
12
18
|
|
19
|
+
attr_reader :local_header, :global_header, :gyoku_options,
|
20
|
+
:wsse_auth, :wsse_timestamp
|
21
|
+
|
13
22
|
def empty?
|
14
|
-
|
23
|
+
@header.empty?
|
15
24
|
end
|
16
25
|
|
17
26
|
def to_s
|
18
|
-
|
19
|
-
|
20
|
-
gyoku_options = { :key_converter => @globals[:convert_request_keys_to] }
|
21
|
-
@header = (Hash === header ? Gyoku.xml(header, gyoku_options) : header) + wsse_header
|
27
|
+
@header
|
22
28
|
end
|
23
29
|
|
24
30
|
private
|
25
31
|
|
26
|
-
def
|
27
|
-
|
28
|
-
wsse.credentials(*@globals[:wsse_auth]) if @globals.include? :wsse_auth
|
29
|
-
wsse.timestamp = @globals[:wsse_timestamp] if @globals.include? :wsse_timestamp
|
30
|
-
wsse
|
32
|
+
def build
|
33
|
+
build_header + build_wsse_header
|
31
34
|
end
|
32
35
|
|
33
|
-
def
|
34
|
-
|
36
|
+
def build_header
|
37
|
+
header =
|
38
|
+
if global_header.kind_of?(Hash) && local_header.kind_of?(Hash)
|
39
|
+
global_header.merge(local_header)
|
40
|
+
elsif local_header
|
41
|
+
local_header
|
42
|
+
else
|
43
|
+
global_header
|
44
|
+
end
|
45
|
+
|
46
|
+
convert_to_xml(header)
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_wsse_header
|
50
|
+
wsse_header = akami
|
51
|
+
wsse_header.respond_to?(:to_xml) ? wsse_header.to_xml : ""
|
35
52
|
end
|
36
53
|
|
37
|
-
def
|
38
|
-
|
54
|
+
def convert_to_xml(hash_or_string)
|
55
|
+
if hash_or_string.kind_of? Hash
|
56
|
+
Gyoku.xml(hash_or_string, gyoku_options)
|
57
|
+
else
|
58
|
+
hash_or_string.to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def akami
|
63
|
+
wsse = Akami.wsse
|
64
|
+
wsse.credentials(*wsse_auth) if wsse_auth
|
65
|
+
wsse.timestamp = wsse_timestamp if wsse_timestamp
|
66
|
+
wsse
|
39
67
|
end
|
40
68
|
|
41
69
|
end
|
data/lib/savon/message.rb
CHANGED
@@ -20,9 +20,7 @@ module Savon
|
|
20
20
|
|
21
21
|
if @element_form_default == :qualified
|
22
22
|
translated_operation_name = Gyoku.xml_tag(@operation_name, :key_converter => @key_converter).to_s
|
23
|
-
|
24
|
-
# the third argument is therefore always `nil`. [dh, 2013-03-09]
|
25
|
-
@message = QualifiedMessage.new(@types, @used_namespaces, @request_key_converter).to_hash(@message, [translated_operation_name])
|
23
|
+
@message = QualifiedMessage.new(@types, @used_namespaces, @key_converter).to_hash(@message, [translated_operation_name])
|
26
24
|
end
|
27
25
|
|
28
26
|
gyoku_options = {
|
@@ -54,6 +54,7 @@ module Savon
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def verify_message!
|
57
|
+
return if @expected[:message] == :any
|
57
58
|
unless @expected[:message] == @actual[:message]
|
58
59
|
expected_message = " with this message: #{@expected[:message].inspect}" if @expected[:message]
|
59
60
|
expected_message ||= " with no message."
|
data/lib/savon/operation.rb
CHANGED
@@ -3,7 +3,7 @@ require "savon/block_interface"
|
|
3
3
|
require "savon/request"
|
4
4
|
require "savon/builder"
|
5
5
|
require "savon/response"
|
6
|
-
require "savon/
|
6
|
+
require "savon/request_logger"
|
7
7
|
|
8
8
|
module Savon
|
9
9
|
class Operation
|
@@ -35,6 +35,8 @@ module Savon
|
|
35
35
|
@name = name
|
36
36
|
@wsdl = wsdl
|
37
37
|
@globals = globals
|
38
|
+
|
39
|
+
@logger = RequestLogger.new(globals)
|
38
40
|
end
|
39
41
|
|
40
42
|
def build(locals = {}, &block)
|
@@ -46,15 +48,33 @@ module Savon
|
|
46
48
|
builder = build(locals, &block)
|
47
49
|
|
48
50
|
response = Savon.notify_observers(@name, builder, @globals, @locals)
|
49
|
-
response ||=
|
51
|
+
response ||= call_with_logging build_request(builder)
|
50
52
|
|
51
53
|
raise_expected_httpi_response! unless response.kind_of?(HTTPI::Response)
|
52
54
|
|
53
|
-
|
55
|
+
create_response(response)
|
54
56
|
end
|
55
57
|
|
56
58
|
private
|
57
59
|
|
60
|
+
def create_response(response)
|
61
|
+
if multipart_supported?
|
62
|
+
Multipart::Response.new(response, @globals, @locals)
|
63
|
+
else
|
64
|
+
Response.new(response, @globals, @locals)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def multipart_supported?
|
69
|
+
return false unless @globals[:multipart] || @locals[:multipart]
|
70
|
+
|
71
|
+
if Savon.const_defined? :Multipart
|
72
|
+
true
|
73
|
+
else
|
74
|
+
raise 'Unable to find Savon::Multipart. Make sure the savon-multipart gem is installed and loaded.'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
58
78
|
def set_locals(locals, block)
|
59
79
|
locals = LocalOptions.new(locals)
|
60
80
|
BlockInterface.new(locals).evaluate(block) if block
|
@@ -62,12 +82,8 @@ module Savon
|
|
62
82
|
@locals = locals
|
63
83
|
end
|
64
84
|
|
65
|
-
def
|
66
|
-
|
67
|
-
response = HTTPI.post(request)
|
68
|
-
log_response(response) if log?
|
69
|
-
|
70
|
-
response
|
85
|
+
def call_with_logging(request)
|
86
|
+
@logger.log(request) { HTTPI.post(request) }
|
71
87
|
end
|
72
88
|
|
73
89
|
def build_request(builder)
|
@@ -102,33 +118,6 @@ module Savon
|
|
102
118
|
@globals[:endpoint] || @wsdl.endpoint
|
103
119
|
end
|
104
120
|
|
105
|
-
def log_request(request)
|
106
|
-
logger.info "SOAP request: #{request.url}"
|
107
|
-
logger.info headers_to_log(request.headers)
|
108
|
-
logger.debug body_to_log(request.body)
|
109
|
-
end
|
110
|
-
|
111
|
-
def log_response(response)
|
112
|
-
logger.info "SOAP response (status #{response.code})"
|
113
|
-
logger.debug body_to_log(response.body)
|
114
|
-
end
|
115
|
-
|
116
|
-
def headers_to_log(headers)
|
117
|
-
headers.map { |key, value| "#{key}: #{value}" }.join(", ")
|
118
|
-
end
|
119
|
-
|
120
|
-
def body_to_log(body)
|
121
|
-
LogMessage.new(body, @globals[:filters], @globals[:pretty_print_xml]).to_s
|
122
|
-
end
|
123
|
-
|
124
|
-
def logger
|
125
|
-
@globals[:logger]
|
126
|
-
end
|
127
|
-
|
128
|
-
def log?
|
129
|
-
@globals[:log]
|
130
|
-
end
|
131
|
-
|
132
121
|
def raise_expected_httpi_response!
|
133
122
|
raise Error, "Observers need to return an HTTPI::Response to mock " \
|
134
123
|
"the request or nil to execute the request."
|
data/lib/savon/options.rb
CHANGED
@@ -53,7 +53,8 @@ module Savon
|
|
53
53
|
:pretty_print_xml => false,
|
54
54
|
:raise_errors => true,
|
55
55
|
:strip_namespaces => true,
|
56
|
-
:convert_response_tags_to => lambda { |tag| tag.snakecase.to_sym
|
56
|
+
:convert_response_tags_to => lambda { |tag| tag.snakecase.to_sym},
|
57
|
+
:multipart => false,
|
57
58
|
}
|
58
59
|
|
59
60
|
options = defaults.merge(options)
|
@@ -117,7 +118,7 @@ module Savon
|
|
117
118
|
@options[:encoding] = encoding
|
118
119
|
end
|
119
120
|
|
120
|
-
# The global SOAP header. Expected to be a Hash.
|
121
|
+
# The global SOAP header. Expected to be a Hash or responding to #to_s.
|
121
122
|
def soap_header(header)
|
122
123
|
@options[:soap_header] = header
|
123
124
|
end
|
@@ -219,6 +220,11 @@ module Savon
|
|
219
220
|
@options[:digest_auth] = credentials.flatten
|
220
221
|
end
|
221
222
|
|
223
|
+
# NTLM auth credentials.
|
224
|
+
def ntlm(*credentials)
|
225
|
+
@options[:ntlm] = credentials.flatten
|
226
|
+
end
|
227
|
+
|
222
228
|
# WSSE auth credentials for Akami.
|
223
229
|
def wsse_auth(*credentials)
|
224
230
|
@options[:wsse_auth] = credentials.flatten
|
@@ -246,6 +252,11 @@ module Savon
|
|
246
252
|
def convert_response_tags_to(converter = nil, &block)
|
247
253
|
@options[:convert_response_tags_to] = block || converter
|
248
254
|
end
|
255
|
+
|
256
|
+
# Instruct Savon to create a multipart response if available.
|
257
|
+
def multipart(multipart)
|
258
|
+
@options[:multipart] = multipart
|
259
|
+
end
|
249
260
|
end
|
250
261
|
|
251
262
|
class LocalOptions < Options
|
@@ -255,12 +266,20 @@ module Savon
|
|
255
266
|
|
256
267
|
defaults = {
|
257
268
|
:advanced_typecasting => true,
|
258
|
-
:response_parser => :nokogiri
|
269
|
+
:response_parser => :nokogiri,
|
270
|
+
:multipart => false
|
259
271
|
}
|
260
272
|
|
261
273
|
super defaults.merge(options)
|
262
274
|
end
|
263
275
|
|
276
|
+
# The local SOAP header. Expected to be a Hash or respond to #to_s.
|
277
|
+
# Will be merged with the global SOAP header if both are Hashes.
|
278
|
+
# Otherwise the local option will be prefered.
|
279
|
+
def soap_header(header)
|
280
|
+
@options[:soap_header] = header
|
281
|
+
end
|
282
|
+
|
264
283
|
# The SOAP message to send. Expected to be a Hash or a String.
|
265
284
|
def message(message)
|
266
285
|
@options[:message] = message
|
@@ -302,5 +321,10 @@ module Savon
|
|
302
321
|
@options[:response_parser] = parser
|
303
322
|
end
|
304
323
|
|
324
|
+
# Instruct Savon to create a multipart response if available.
|
325
|
+
def multipart(multipart)
|
326
|
+
@options[:multipart] = multipart
|
327
|
+
end
|
328
|
+
|
305
329
|
end
|
306
330
|
end
|