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
@@ -1,82 +0,0 @@
|
|
1
|
-
require "rack/builder"
|
2
|
-
require "json"
|
3
|
-
|
4
|
-
class IntegrationServer
|
5
|
-
|
6
|
-
def self.respond_with(options = {})
|
7
|
-
code = options.fetch(:code, 200)
|
8
|
-
body = options.fetch(:body, "")
|
9
|
-
headers = { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s }
|
10
|
-
|
11
|
-
[code, headers, [body]]
|
12
|
-
end
|
13
|
-
|
14
|
-
Application = Rack::Builder.new do
|
15
|
-
|
16
|
-
map "/" do
|
17
|
-
run lambda { |env|
|
18
|
-
IntegrationServer.respond_with :body => env["REQUEST_METHOD"].downcase
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
|
-
map "/repeat" do
|
23
|
-
run lambda { |env|
|
24
|
-
# stupid way of extracting the value from a query string (e.g. "code=500") [dh, 2012-12-08]
|
25
|
-
IntegrationServer.respond_with :body => env["rack.input"].read
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
29
|
-
map "/404" do
|
30
|
-
run lambda { |env|
|
31
|
-
IntegrationServer.respond_with :code => 404, :body => env["rack.input"].read
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
map "/timeout" do
|
36
|
-
run lambda { |env|
|
37
|
-
sleep 2
|
38
|
-
IntegrationServer.respond_with :body => "timeout"
|
39
|
-
}
|
40
|
-
end
|
41
|
-
|
42
|
-
map "/inspect_request" do
|
43
|
-
run lambda { |env|
|
44
|
-
body = {
|
45
|
-
:soap_action => env["HTTP_SOAPACTION"],
|
46
|
-
:cookie => env["HTTP_COOKIE"],
|
47
|
-
:x_token => env["HTTP_X_TOKEN"],
|
48
|
-
:content_type => env["CONTENT_TYPE"]
|
49
|
-
}
|
50
|
-
|
51
|
-
IntegrationServer.respond_with :body => JSON.dump(body)
|
52
|
-
}
|
53
|
-
end
|
54
|
-
|
55
|
-
map "/basic_auth" do
|
56
|
-
use Rack::Auth::Basic, "basic-realm" do |username, password|
|
57
|
-
username == "admin" && password == "secret"
|
58
|
-
end
|
59
|
-
|
60
|
-
run lambda { |env|
|
61
|
-
IntegrationServer.respond_with :body => "basic-auth"
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
map "/digest_auth" do
|
66
|
-
unprotected_app = lambda { |env|
|
67
|
-
IntegrationServer.respond_with :body => "digest-auth"
|
68
|
-
}
|
69
|
-
|
70
|
-
realm = 'digest-realm'
|
71
|
-
app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
|
72
|
-
username == 'admin' ? Digest::MD5.hexdigest("admin:#{realm}:secret") : nil
|
73
|
-
end
|
74
|
-
app.realm = realm
|
75
|
-
app.opaque = 'this-should-be-secret'
|
76
|
-
app.passwords_hashed = true
|
77
|
-
|
78
|
-
run app
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
require "puma"
|
2
|
-
require "puma/minissl"
|
3
|
-
|
4
|
-
require "integration/support/application"
|
5
|
-
|
6
|
-
class IntegrationServer
|
7
|
-
|
8
|
-
def self.run(options = {})
|
9
|
-
server = new(options)
|
10
|
-
server.run
|
11
|
-
server
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.ssl_ca_file; integration_fixture("ca_all.pem") end
|
15
|
-
def self.ssl_key_file; integration_fixture("server.key") end
|
16
|
-
def self.ssl_cert_file; integration_fixture("server.cert") end
|
17
|
-
|
18
|
-
def self.integration_fixture(file)
|
19
|
-
file = File.expand_path("../../fixtures/#{file}", __FILE__)
|
20
|
-
raise "No such file '#{file}'" unless File.exist? file
|
21
|
-
file
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize(options = {})
|
25
|
-
@app = Application
|
26
|
-
@host = options.fetch(:host, "localhost")
|
27
|
-
@port = options.fetch(:port, 17172)
|
28
|
-
@ssl = options.fetch(:ssl, false)
|
29
|
-
|
30
|
-
@server = Puma::Server.new(app, events)
|
31
|
-
|
32
|
-
if ssl?
|
33
|
-
add_ssl_listener
|
34
|
-
else
|
35
|
-
add_tcp_listener
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
attr_reader :app, :host, :port, :server
|
40
|
-
|
41
|
-
def url(path = "")
|
42
|
-
protocol = ssl? ? "https" : "http"
|
43
|
-
File.join "#{protocol}://#{host}:#{port}/", path.to_s
|
44
|
-
end
|
45
|
-
|
46
|
-
def ssl?
|
47
|
-
@ssl
|
48
|
-
end
|
49
|
-
|
50
|
-
def run
|
51
|
-
server.run
|
52
|
-
end
|
53
|
-
|
54
|
-
def stop
|
55
|
-
server.stop(true)
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def events
|
61
|
-
Puma::Events.new($stdout, $stderr)
|
62
|
-
end
|
63
|
-
|
64
|
-
def add_tcp_listener
|
65
|
-
server.add_tcp_listener(host, port)
|
66
|
-
rescue Errno::EADDRINUSE
|
67
|
-
raise "Panther is already running at #{url}"
|
68
|
-
end
|
69
|
-
|
70
|
-
def add_ssl_listener
|
71
|
-
server.add_ssl_listener(host, port, ssl_context)
|
72
|
-
end
|
73
|
-
|
74
|
-
def ssl_context
|
75
|
-
context = Puma::MiniSSL::Context.new
|
76
|
-
|
77
|
-
context.key = IntegrationServer.ssl_key_file
|
78
|
-
context.cert = IntegrationServer.ssl_cert_file
|
79
|
-
context.verify_mode = Puma::MiniSSL::VERIFY_PEER
|
80
|
-
|
81
|
-
context
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe "Temperature example" do
|
4
|
-
|
5
|
-
it "converts 30 degrees celsius to 86 degrees fahrenheit" do
|
6
|
-
client = Savon.client do
|
7
|
-
# The WSDL document provided by the service.
|
8
|
-
wsdl "http://www.webservicex.net/ConvertTemperature.asmx?WSDL"
|
9
|
-
|
10
|
-
# Needed because (up until now), Savon doesn't match XS types to Hash keys,
|
11
|
-
# but defaults to convert Hash message Symbols (like :from_unit) to lowerCamelCase.
|
12
|
-
# The service expects these to be CamelCase instead. Look at Savon's log output
|
13
|
-
# and compare it with an example request generated by soapUI.
|
14
|
-
convert_request_keys_to :camelcase
|
15
|
-
|
16
|
-
# Lower timeouts so these specs don't take forever when the service is not available.
|
17
|
-
open_timeout 10
|
18
|
-
read_timeout 10
|
19
|
-
|
20
|
-
# Disable logging for cleaner spec output.
|
21
|
-
log false
|
22
|
-
end
|
23
|
-
|
24
|
-
response = call_and_fail_gracefully(client, :convert_temp) do
|
25
|
-
# For the corrent values to pass for :from_unit and :to_unit, I searched the WSDL for
|
26
|
-
# the "FromUnit" type which is a "TemperatureUnit" enumeration that looks like this:
|
27
|
-
#
|
28
|
-
# <s:simpleType name="TemperatureUnit">
|
29
|
-
# <s:restriction base="s:string">
|
30
|
-
# <s:enumeration value="degreeCelsius"/>
|
31
|
-
# <s:enumeration value="degreeFahrenheit"/>
|
32
|
-
# <s:enumeration value="degreeRankine"/>
|
33
|
-
# <s:enumeration value="degreeReaumur"/>
|
34
|
-
# <s:enumeration value="kelvin"/>
|
35
|
-
# </s:restriction>
|
36
|
-
# </s:simpleType>
|
37
|
-
#
|
38
|
-
# Support for XS schema types needs to be improved.
|
39
|
-
message(:temperature => 30, :from_unit => "degreeCelsius", :to_unit => "degreeFahrenheit")
|
40
|
-
end
|
41
|
-
|
42
|
-
fahrenheit = response.body[:convert_temp_response][:convert_temp_result]
|
43
|
-
expect(fahrenheit).to eq("86")
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe "ZIP code example" do
|
4
|
-
|
5
|
-
it "supports threads making requests simultaneously" do
|
6
|
-
client = Savon.client(
|
7
|
-
# The WSDL document provided by the service.
|
8
|
-
:wsdl => "http://www.thomas-bayer.com/axis2/services/BLZService?wsdl",
|
9
|
-
|
10
|
-
# Lower timeouts so these specs don't take forever when the service is not available.
|
11
|
-
:open_timeout => 10,
|
12
|
-
:read_timeout => 10,
|
13
|
-
|
14
|
-
# Disable logging for cleaner spec output.
|
15
|
-
:log => false
|
16
|
-
)
|
17
|
-
|
18
|
-
mutex = Mutex.new
|
19
|
-
|
20
|
-
request_data = [70070010, 24050110, 20050550]
|
21
|
-
threads_waiting = request_data.size
|
22
|
-
|
23
|
-
threads = request_data.map do |blz|
|
24
|
-
thread = Thread.new do
|
25
|
-
response = call_and_fail_gracefully client, :get_bank, :message => { :blz => blz }
|
26
|
-
Thread.current[:value] = response.body[:get_bank_response][:details]
|
27
|
-
mutex.synchronize { threads_waiting -= 1 }
|
28
|
-
end
|
29
|
-
|
30
|
-
thread.abort_on_exception = true
|
31
|
-
thread
|
32
|
-
end
|
33
|
-
|
34
|
-
sleep(1) until threads_waiting == 0
|
35
|
-
|
36
|
-
threads.each(&:kill)
|
37
|
-
values = threads.map { |thr| thr[:value] }.compact
|
38
|
-
|
39
|
-
expect(values.uniq.size).to eq(values.size)
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
data/spec/savon/builder_spec.rb
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Savon::Builder do
|
4
|
-
|
5
|
-
subject(:builder) { Savon::Builder.new(:authenticate, wsdl, globals, locals) }
|
6
|
-
|
7
|
-
let(:globals) { Savon::GlobalOptions.new }
|
8
|
-
let(:locals) { Savon::LocalOptions.new }
|
9
|
-
let(:wsdl) { Wasabi::Document.new Fixture.wsdl(:authentication) }
|
10
|
-
let(:no_wsdl) { Wasabi::Document.new }
|
11
|
-
|
12
|
-
describe "#pretty" do
|
13
|
-
it "returns the pretty printed request" do
|
14
|
-
expect(builder.pretty).to include("<env:Body>\n <tns:authenticate/>")
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe "#to_s" do
|
19
|
-
it "includes the global :env_namespace if it's available" do
|
20
|
-
globals[:env_namespace] = :soapenv
|
21
|
-
expect(builder.to_s).to include("<soapenv:Envelope")
|
22
|
-
end
|
23
|
-
|
24
|
-
it "defaults to include the default envelope namespace of :env" do
|
25
|
-
expect(builder.to_s).to include("<env:Envelope")
|
26
|
-
end
|
27
|
-
|
28
|
-
it "includes the target namespace from the WSDL" do
|
29
|
-
expect(builder.to_s).to include('xmlns:tns="http://v1_0.ws.auth.order.example.com/"')
|
30
|
-
end
|
31
|
-
|
32
|
-
it "includes the target namespace from the global :namespace if it's available" do
|
33
|
-
globals[:namespace] = "http://v1.example.com"
|
34
|
-
expect(builder.to_s).to include('xmlns:tns="http://v1.example.com"')
|
35
|
-
end
|
36
|
-
|
37
|
-
it "includes the local :message_tag if available" do
|
38
|
-
locals[:message_tag] = "doAuthenticate"
|
39
|
-
expect(builder.to_s).to include("<tns:doAuthenticate>")
|
40
|
-
end
|
41
|
-
|
42
|
-
it "includes the message tag from the WSDL if its available" do
|
43
|
-
expect(builder.to_s).to include("<tns:authenticate>")
|
44
|
-
end
|
45
|
-
|
46
|
-
it "includes a message tag created by Gyoku if both option and WSDL are missing" do
|
47
|
-
globals[:namespace] = "http://v1.example.com"
|
48
|
-
|
49
|
-
locals = Savon::LocalOptions.new
|
50
|
-
builder = Savon::Builder.new(:authenticate, no_wsdl, globals, locals)
|
51
|
-
|
52
|
-
expect(builder.to_s).to include("<wsdl:authenticate>")
|
53
|
-
end
|
54
|
-
|
55
|
-
it "uses the global :namespace_identifier option if it's available" do
|
56
|
-
globals[:namespace_identifier] = :v1
|
57
|
-
expect(builder.to_s).to include("<v1:authenticate>")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "uses the WSDL's namespace_identifier if the global option was not specified" do
|
61
|
-
expect(builder.to_s).to include("<tns:authenticate>")
|
62
|
-
end
|
63
|
-
|
64
|
-
it "uses the default :wsdl identifier if both option and WSDL were not specified" do
|
65
|
-
globals[:namespace] = "http://v1.example.com"
|
66
|
-
|
67
|
-
builder = Savon::Builder.new(:authenticate, no_wsdl, globals, locals)
|
68
|
-
expect(builder.to_s).to include("<wsdl:authenticate>")
|
69
|
-
end
|
70
|
-
|
71
|
-
it "uses the global :element_form_default option if it's available " do
|
72
|
-
globals[:element_form_default] = :qualified
|
73
|
-
locals[:message] = { :username => "luke", :password => "secret" }
|
74
|
-
|
75
|
-
expect(builder.to_s).to include("<tns:username>luke</tns:username>")
|
76
|
-
end
|
77
|
-
|
78
|
-
it "uses the WSDL's element_form_default value if the global option was set specified" do
|
79
|
-
locals[:message] = { :username => "luke", :password => "secret" }
|
80
|
-
wsdl.element_form_default = :qualified
|
81
|
-
|
82
|
-
expect(builder.to_s).to include("<tns:username>luke</tns:username>")
|
83
|
-
end
|
84
|
-
|
85
|
-
describe "#wsse_signature" do
|
86
|
-
fixture_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'ssl')
|
87
|
-
|
88
|
-
let(:cert) { File.join(fixture_dir, 'client_cert.pem') }
|
89
|
-
let(:private_key) { File.join(fixture_dir, 'client_key.pem') }
|
90
|
-
let(:signature) do
|
91
|
-
Akami::WSSE::Signature.new(
|
92
|
-
Akami::WSSE::Certs.new(
|
93
|
-
:cert_file => cert,
|
94
|
-
:private_key_file => private_key
|
95
|
-
)
|
96
|
-
)
|
97
|
-
end
|
98
|
-
let(:globals) { Savon::GlobalOptions.new(wsse_signature: signature) }
|
99
|
-
|
100
|
-
subject(:signed_message_nn) {Nokogiri::XML(builder.to_s).remove_namespaces!}
|
101
|
-
subject(:signed_message) {Nokogiri::XML(builder.to_s)}
|
102
|
-
|
103
|
-
it "should contain a header" do
|
104
|
-
expect(signed_message_nn.xpath('/Envelope/Header').size).to eq(1)
|
105
|
-
end
|
106
|
-
|
107
|
-
it "should contain a wsse:Security" do
|
108
|
-
expect(signed_message_nn.xpath('/Envelope/Header/Security').size).to eq(1)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "should have a Body[@wsu:Id]" do
|
112
|
-
#must investigate: acts funny in mri ruby
|
113
|
-
#expect(signed_message.xpath('//soapenv:Body', soapenv: "http://schemas.xmlsoap.org/soap/envelope/").attribute('ws:Id').value).to include('Body-')
|
114
|
-
expect(signed_message_nn.xpath('//Body').attr('Id').value).to include('Body-')
|
115
|
-
end
|
116
|
-
|
117
|
-
it "signature should be valid" do
|
118
|
-
certs = Akami::WSSE::Certs.new(:cert_file => cert, :private_key_file => private_key)
|
119
|
-
signature_value = signed_message_nn.xpath('//SignatureValue').text
|
120
|
-
signed_info_fragment = signed_message.xpath('//default:SignedInfo', default: "http://www.w3.org/2000/09/xmldsig#").to_xml
|
121
|
-
data = Nokogiri::XML(signed_info_fragment){|config| config.options = Nokogiri::XML::ParseOptions::NOBLANKS}
|
122
|
-
data.root.default_namespace='http://www.w3.org/2000/09/xmldsig#'
|
123
|
-
|
124
|
-
signed_info = data.canonicalize Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
125
|
-
|
126
|
-
signature = certs.private_key.sign(OpenSSL::Digest::SHA1.new, signed_info)
|
127
|
-
expect(Base64.encode64(signature).gsub("\n", '')).to eq(signature_value)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
describe '#body_attributes' do
|
133
|
-
it 'should not be nil' do
|
134
|
-
expect(builder.body_attributes).to eq({})
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
data/spec/savon/client_spec.rb
DELETED
@@ -1,271 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "integration/support/server"
|
3
|
-
|
4
|
-
describe Savon::Client do
|
5
|
-
|
6
|
-
before :all do
|
7
|
-
@server = IntegrationServer.run
|
8
|
-
end
|
9
|
-
|
10
|
-
after :all do
|
11
|
-
@server.stop
|
12
|
-
end
|
13
|
-
|
14
|
-
describe ".new" do
|
15
|
-
it "supports a block without arguments to create a client with global options" do
|
16
|
-
client = Savon.client do
|
17
|
-
wsdl Fixture.wsdl(:authentication)
|
18
|
-
end
|
19
|
-
|
20
|
-
expect(client.globals[:wsdl]).to eq(Fixture.wsdl(:authentication))
|
21
|
-
end
|
22
|
-
|
23
|
-
it "supports a block with one argument to create a client with global options" do
|
24
|
-
client = Savon.client do |globals|
|
25
|
-
globals.wsdl Fixture.wsdl(:authentication)
|
26
|
-
end
|
27
|
-
|
28
|
-
expect(client.globals[:wsdl]).to eq(Fixture.wsdl(:authentication))
|
29
|
-
end
|
30
|
-
|
31
|
-
it "builds an HTTPI request for Wasabi" do
|
32
|
-
http_request = mock
|
33
|
-
wsdl_request = mock(:build => http_request)
|
34
|
-
Savon::WSDLRequest.expects(:new).with(instance_of(Savon::GlobalOptions)).returns(wsdl_request)
|
35
|
-
|
36
|
-
Wasabi::Document.any_instance.expects(:request=).with(http_request)
|
37
|
-
Savon.client(:wsdl => "http://example.com")
|
38
|
-
end
|
39
|
-
|
40
|
-
it "raises if initialized with anything other than a Hash" do
|
41
|
-
expect { Savon.client("http://example.com") }.
|
42
|
-
to raise_error(Savon::InitializationError, /Some code tries to initialize Savon with the "http:\/\/example\.com" \(String\)/)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "raises if not initialized with either a :wsdl or both :endpoint and :namespace options" do
|
46
|
-
expect { Savon.client(:endpoint => "http://example.com") }.
|
47
|
-
to raise_error(Savon::InitializationError, /Expected either a WSDL document or the SOAP endpoint and target namespace options/)
|
48
|
-
end
|
49
|
-
|
50
|
-
it "raises a when given an unknown option via the Hash syntax" do
|
51
|
-
expect { Savon.client(:invalid_global_option => true) }.
|
52
|
-
to raise_error(Savon::UnknownOptionError, "Unknown global option: :invalid_global_option")
|
53
|
-
end
|
54
|
-
|
55
|
-
it "raises a when given an unknown option via the block syntax" do
|
56
|
-
expect { Savon.client { another_invalid_global_option true } }.
|
57
|
-
to raise_error(Savon::UnknownOptionError, "Unknown global option: :another_invalid_global_option")
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
describe "#globals" do
|
62
|
-
it "returns the current set of global options" do
|
63
|
-
expect(new_client.globals).to be_an_instance_of(Savon::GlobalOptions)
|
64
|
-
end
|
65
|
-
|
66
|
-
fit "defaults :log to false" do
|
67
|
-
client = Savon.client(:wsdl => Fixture.wsdl(:authentication))
|
68
|
-
expect(client.globals[:log]).to be_falsey
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe "#service_name" do
|
73
|
-
it "returns the name of the service" do
|
74
|
-
expect(new_client.service_name).to eq('AuthenticationWebServiceImplService')
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
describe "#operations" do
|
79
|
-
it "returns all operation names" do
|
80
|
-
operations = new_client.operations
|
81
|
-
expect(operations).to eq([:authenticate])
|
82
|
-
end
|
83
|
-
|
84
|
-
it "raises when there is no WSDL document" do
|
85
|
-
expect { new_client_without_wsdl.operations }.to raise_error("Unable to inspect the service without a WSDL document.")
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe "#operation" do
|
90
|
-
it "returns a new SOAP operation" do
|
91
|
-
operation = new_client.operation(:authenticate)
|
92
|
-
expect(operation).to be_a(Savon::Operation)
|
93
|
-
end
|
94
|
-
|
95
|
-
it "raises if there's no such SOAP operation" do
|
96
|
-
expect { new_client.operation(:does_not_exist) }.
|
97
|
-
to raise_error(Savon::UnknownOperationError)
|
98
|
-
end
|
99
|
-
|
100
|
-
it "does not raise when there is no WSDL document" do
|
101
|
-
new_client_without_wsdl.operation(:does_not_exist)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
describe "#call" do
|
106
|
-
it "calls a new SOAP operation" do
|
107
|
-
locals = { :message => { :symbol => "AAPL" } }
|
108
|
-
soap_response = new_soap_response
|
109
|
-
|
110
|
-
wsdl = Wasabi::Document.new('http://example.com')
|
111
|
-
operation = Savon::Operation.new(:authenticate, wsdl, Savon::GlobalOptions.new)
|
112
|
-
operation.expects(:call).with(locals).returns(soap_response)
|
113
|
-
|
114
|
-
Savon::Operation.expects(:create).with(
|
115
|
-
:authenticate,
|
116
|
-
instance_of(Wasabi::Document),
|
117
|
-
instance_of(Savon::GlobalOptions)
|
118
|
-
).returns(operation)
|
119
|
-
|
120
|
-
response = new_client.call(:authenticate, locals)
|
121
|
-
expect(response).to eq(soap_response)
|
122
|
-
end
|
123
|
-
|
124
|
-
it "supports a block without arguments to call an operation with local options" do
|
125
|
-
client = new_client(:endpoint => @server.url(:repeat))
|
126
|
-
|
127
|
-
response = client.call(:authenticate) do
|
128
|
-
message(:symbol => "AAPL" )
|
129
|
-
end
|
130
|
-
|
131
|
-
expect(response.http.body).to include("<symbol>AAPL</symbol>")
|
132
|
-
end
|
133
|
-
|
134
|
-
it "supports a block with one argument to call an operation with local options" do
|
135
|
-
client = new_client(:endpoint => @server.url(:repeat))
|
136
|
-
|
137
|
-
# supports instance variables!
|
138
|
-
@instance_variable = { :symbol => "AAPL" }
|
139
|
-
|
140
|
-
response = client.call(:authenticate) do |locals|
|
141
|
-
locals.message(@instance_variable)
|
142
|
-
end
|
143
|
-
|
144
|
-
expect(response.http.body).to include("<symbol>AAPL</symbol>")
|
145
|
-
end
|
146
|
-
|
147
|
-
it "accepts arguments for the message tag" do
|
148
|
-
client = new_client(:endpoint => @server.url(:repeat))
|
149
|
-
response = client.call(:authenticate, :attributes => { "ID" => "ABC321"})
|
150
|
-
|
151
|
-
expect(response.http.body).to include('<tns:authenticate ID="ABC321">')
|
152
|
-
end
|
153
|
-
|
154
|
-
it "raises when the operation name is not a symbol" do
|
155
|
-
expect { new_client.call("not a symbol") }.to raise_error(
|
156
|
-
ArgumentError,
|
157
|
-
"Expected the first parameter (the name of the operation to call) to be a symbol\n" \
|
158
|
-
"Actual: \"not a symbol\" (String)"
|
159
|
-
)
|
160
|
-
end
|
161
|
-
|
162
|
-
it "raises a when given an unknown option via the Hash syntax" do
|
163
|
-
expect { new_client.call(:authenticate, :invalid_local_option => true) }.
|
164
|
-
to raise_error(Savon::UnknownOptionError, "Unknown local option: :invalid_local_option")
|
165
|
-
end
|
166
|
-
|
167
|
-
it "raises a when given an unknown option via the block syntax" do
|
168
|
-
expect { new_client.call(:authenticate) { another_invalid_local_option true } }.
|
169
|
-
to raise_error(Savon::UnknownOptionError, "Unknown local option: :another_invalid_local_option")
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
describe "#build_request" do
|
174
|
-
it "returns the request without making an actual call" do
|
175
|
-
expected_request = mock('request')
|
176
|
-
wsdl = Wasabi::Document.new('http://example.com')
|
177
|
-
|
178
|
-
operation = Savon::Operation.new(
|
179
|
-
:authenticate,
|
180
|
-
wsdl,
|
181
|
-
Savon::GlobalOptions.new
|
182
|
-
)
|
183
|
-
operation.expects(:request).returns(expected_request)
|
184
|
-
|
185
|
-
Savon::Operation.expects(:create).with(
|
186
|
-
:authenticate,
|
187
|
-
instance_of(Wasabi::Document),
|
188
|
-
instance_of(Savon::GlobalOptions)
|
189
|
-
).returns(operation)
|
190
|
-
|
191
|
-
operation.expects(:call).never
|
192
|
-
|
193
|
-
client = new_client(:endpoint => @server.url(:repeat))
|
194
|
-
request = client.build_request(:authenticate) do
|
195
|
-
message(:symbol => "AAPL" )
|
196
|
-
end
|
197
|
-
|
198
|
-
expect(request).to eq expected_request
|
199
|
-
end
|
200
|
-
|
201
|
-
it "accepts a block without arguments" do
|
202
|
-
client = new_client(:endpoint => @server.url(:repeat))
|
203
|
-
request = client.build_request(:authenticate) do
|
204
|
-
message(:symbol => "AAPL" )
|
205
|
-
end
|
206
|
-
|
207
|
-
expect(request.body).
|
208
|
-
to include('<tns:authenticate><symbol>AAPL</symbol></tns:authenticate>')
|
209
|
-
end
|
210
|
-
|
211
|
-
it "accepts a block with one argument" do
|
212
|
-
client = new_client(:endpoint => @server.url(:repeat))
|
213
|
-
|
214
|
-
# supports instance variables!
|
215
|
-
@instance_variable = { :symbol => "AAPL" }
|
216
|
-
|
217
|
-
request = client.build_request(:authenticate) do |locals|
|
218
|
-
locals.message(@instance_variable)
|
219
|
-
end
|
220
|
-
|
221
|
-
expect(request.body).
|
222
|
-
to include("<tns:authenticate><symbol>AAPL</symbol></tns:authenticate>")
|
223
|
-
end
|
224
|
-
|
225
|
-
it "accepts argument for the message tag" do
|
226
|
-
client = new_client(:endpoint => @server.url(:repeat))
|
227
|
-
request = client.build_request(:authenticate, :attributes => { "ID" => "ABC321" })
|
228
|
-
|
229
|
-
expect(request.body).
|
230
|
-
to include("<tns:authenticate ID=\"ABC321\"></tns:authenticate>")
|
231
|
-
end
|
232
|
-
|
233
|
-
it "raises when the operation name is not a symbol" do
|
234
|
-
expect { new_client.build_request("not a symbol") }.to raise_error
|
235
|
-
end
|
236
|
-
|
237
|
-
it "raises when given an unknown option via the Hash syntax" do
|
238
|
-
expect { new_client.build_request(:authenticate, :invalid_local_option => true) }.to raise_error
|
239
|
-
end
|
240
|
-
|
241
|
-
it "raises when given an unknown option via the block syntax" do
|
242
|
-
expect { new_client.build_request(:authenticate) { another_invalid_local_option true } }.to raise_error
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
def new_http_response(options = {})
|
247
|
-
defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
|
248
|
-
response = defaults.merge options
|
249
|
-
|
250
|
-
HTTPI::Response.new response[:code], response[:headers], response[:body]
|
251
|
-
end
|
252
|
-
|
253
|
-
def new_soap_response(options = {})
|
254
|
-
http = new_http_response(options)
|
255
|
-
globals = Savon::GlobalOptions.new
|
256
|
-
locals = Savon::LocalOptions.new
|
257
|
-
|
258
|
-
Savon::Response.new(http, globals, locals)
|
259
|
-
end
|
260
|
-
|
261
|
-
def new_client(globals = {})
|
262
|
-
globals = { :wsdl => Fixture.wsdl(:authentication), :log => false }.merge(globals)
|
263
|
-
Savon.client(globals)
|
264
|
-
end
|
265
|
-
|
266
|
-
def new_client_without_wsdl(globals = {})
|
267
|
-
globals = { :endpoint => "http://example.co", :namespace => "http://v1.example.com", :log => false }.merge(globals)
|
268
|
-
Savon.client(globals)
|
269
|
-
end
|
270
|
-
|
271
|
-
end
|