savon 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://secure.travis-ci.org/savonrb/savon.png)](http://travis-ci.org/savonrb/savon)
|
8
|
+
[![Build Status](https://secure.travis-ci.org/savonrb/savon.png?branch=version2)](http://travis-ci.org/savonrb/savon)
|
9
9
|
[![Gem Version](https://badge.fury.io/rb/savon.png)](http://badge.fury.io/rb/savon)
|
10
10
|
[![Code Climate](https://codeclimate.com/github/savonrb/savon.png)](https://codeclimate.com/github/savonrb/savon)
|
11
|
+
[![Coverage Status](https://coveralls.io/repos/savonrb/savon/badge.png?branch=version2)](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
|
+
[![donate](donate.png)](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
|