savon 2.12.0 → 2.13.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/.gitignore +2 -0
- data/CHANGELOG.md +98 -76
- data/CONTRIBUTING.md +15 -19
- data/Gemfile +2 -7
- data/README.md +15 -19
- data/RELEASING.md +10 -0
- data/lib/savon/block_interface.rb +1 -0
- data/lib/savon/builder.rb +98 -29
- data/lib/savon/client.rb +1 -0
- data/lib/savon/core_ext/string.rb +1 -0
- data/lib/savon/header.rb +2 -6
- data/lib/savon/http_error.rb +4 -4
- data/lib/savon/log_message.rb +1 -0
- 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 +1 -0
- data/lib/savon/operation.rb +20 -18
- data/lib/savon/options.rb +56 -0
- data/lib/savon/qualified_message.rb +4 -3
- data/lib/savon/request.rb +5 -0
- data/lib/savon/request_logger.rb +8 -2
- data/lib/savon/response.rb +48 -1
- data/lib/savon/soap_fault.rb +2 -1
- data/lib/savon/version.rb +2 -1
- data/lib/savon.rb +1 -0
- data/savon.gemspec +9 -8
- data/spec/fixtures/response/empty_soap_fault.xml +13 -0
- data/spec/fixtures/wsdl/elements_in_types.xml +43 -0
- data/spec/integration/support/application.rb +33 -1
- data/spec/integration/support/server.rb +1 -0
- data/spec/integration/zipcode_example_spec.rb +5 -8
- data/spec/savon/builder_spec.rb +2 -1
- data/spec/savon/client_spec.rb +5 -4
- data/spec/savon/core_ext/string_spec.rb +2 -1
- data/spec/savon/features/message_tag_spec.rb +2 -1
- data/spec/savon/http_error_spec.rb +9 -1
- data/spec/savon/log_message_spec.rb +2 -1
- data/spec/savon/message_spec.rb +2 -11
- data/spec/savon/mock_spec.rb +2 -1
- data/spec/savon/model_spec.rb +2 -1
- data/spec/savon/multipart_request_spec.rb +46 -0
- data/spec/savon/observers_spec.rb +2 -1
- data/spec/savon/operation_spec.rb +20 -43
- data/spec/savon/options_spec.rb +51 -1
- data/spec/savon/qualified_message_spec.rb +2 -1
- data/spec/savon/request_logger_spec.rb +2 -1
- data/spec/savon/request_spec.rb +47 -6
- data/spec/savon/response_spec.rb +2 -1
- data/spec/savon/soap_fault_spec.rb +12 -1
- data/spec/savon/softlayer_spec.rb +17 -2
- data/spec/spec_helper.rb +5 -4
- data/spec/support/adapters.rb +1 -0
- data/spec/support/endpoint.rb +1 -0
- data/spec/support/fixture.rb +1 -0
- data/spec/support/integration.rb +1 -0
- data/spec/support/stdout.rb +1 -0
- metadata +57 -34
- data/.travis.yml +0 -18
- data/donate.png +0 -0
- data/spec/integration/centra_spec.rb +0 -67
- 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 -34
- data/spec/integration/temperature_example_spec.rb +0 -46
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]
|
@@ -53,7 +59,12 @@ module Savon
|
|
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
|
@@ -74,8 +85,44 @@ module Savon
|
|
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?
|
data/lib/savon/soap_fault.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Savon
|
2
3
|
class SOAPFault < Error
|
3
4
|
|
4
5
|
def self.present?(http, xml = nil)
|
5
6
|
xml ||= http.body
|
6
7
|
fault_node = xml.include?("Fault>")
|
7
|
-
soap1_fault = xml.
|
8
|
+
soap1_fault = xml.match(/faultcode\/?\>/) && xml.match(/faultstring\/?\>/)
|
8
9
|
soap2_fault = xml.include?("Code>") && xml.include?("Reason>")
|
9
10
|
|
10
11
|
fault_node && (soap1_fault || soap2_fault)
|
data/lib/savon/version.rb
CHANGED
data/lib/savon.rb
CHANGED
data/savon.gemspec
CHANGED
@@ -12,29 +12,30 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.homepage = "http://savonrb.com"
|
13
13
|
s.summary = "Heavy metal SOAP client"
|
14
14
|
s.description = s.summary
|
15
|
-
s.required_ruby_version = '>=
|
15
|
+
s.required_ruby_version = '>= 2.7.0'
|
16
16
|
|
17
|
-
s.rubyforge_project = s.name
|
18
17
|
s.license = 'MIT'
|
19
18
|
|
20
19
|
s.add_dependency "nori", "~> 2.4"
|
21
|
-
s.add_dependency "httpi", "
|
20
|
+
s.add_dependency "httpi", ">= 2.4.5"
|
22
21
|
s.add_dependency "wasabi", "~> 3.4"
|
23
22
|
s.add_dependency "akami", "~> 1.2"
|
24
23
|
s.add_dependency "gyoku", "~> 1.2"
|
25
24
|
s.add_dependency "builder", ">= 2.1.2"
|
26
25
|
s.add_dependency "nokogiri", ">= 1.8.1"
|
26
|
+
s.add_dependency "mail", "~> 2.5"
|
27
27
|
|
28
28
|
s.add_development_dependency "rack"
|
29
|
-
s.add_development_dependency "puma", "
|
29
|
+
s.add_development_dependency "puma", ">= 4.3.8"
|
30
30
|
|
31
|
-
s.add_development_dependency "
|
32
|
-
s.add_development_dependency "
|
31
|
+
s.add_development_dependency "byebug"
|
32
|
+
s.add_development_dependency "rake", ">= 12.3.3"
|
33
|
+
s.add_development_dependency "rspec", "~> 3.9"
|
33
34
|
s.add_development_dependency "mocha", "~> 0.14"
|
34
|
-
s.add_development_dependency "json", "
|
35
|
+
s.add_development_dependency "json", ">= 2.3.0"
|
35
36
|
|
36
37
|
ignores = File.readlines(".gitignore").grep(/\S+/).map(&:chomp)
|
37
|
-
dotfiles = %w[.gitignore .
|
38
|
+
dotfiles = %w[.gitignore .yardopts]
|
38
39
|
|
39
40
|
all_files_without_ignores = Dir["**/*"].reject { |f|
|
40
41
|
File.directory?(f) || ignores.any? { |i| File.fnmatch(i, f) }
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
|
2
|
+
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
|
3
|
+
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
|
4
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
5
|
+
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
6
|
+
<SOAP-ENV:Body>
|
7
|
+
<SOAP-ENV:Fault>
|
8
|
+
<faultcode/>
|
9
|
+
<faultstring/>
|
10
|
+
<detail><soapVal><ERRNO xsi:type="xsd:string">80:1289245853:55</ERRNO></soapVal></detail>
|
11
|
+
</SOAP-ENV:Fault>
|
12
|
+
</SOAP-ENV:Body>
|
13
|
+
</SOAP-ENV:Envelope>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<wsdl:definitions xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="www.example.com/XML" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s1="http://microsoft.com/wsdl/types/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="www.example.com/XML" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
|
3
|
+
<wsdl:types>
|
4
|
+
<s:schema elementFormDefault="qualified" targetNamespace="www.example.com/XML">
|
5
|
+
<s:complexType name="Transaction" abstract="true">
|
6
|
+
<s:sequence>
|
7
|
+
<s:element minOccurs="0" maxOccurs="1" name="Qualified" type="s:string" />
|
8
|
+
</s:sequence>
|
9
|
+
</s:complexType>
|
10
|
+
<s:complexType name="TopLevelTransaction">
|
11
|
+
<s:complexContent mixed="false">
|
12
|
+
<s:extension base="tns:Transaction">
|
13
|
+
</s:extension>
|
14
|
+
</s:complexContent>
|
15
|
+
</s:complexType>
|
16
|
+
<s:element name="TopLevelTransaction">
|
17
|
+
<s:complexType>
|
18
|
+
<s:sequence>
|
19
|
+
<s:element minOccurs="0" maxOccurs="1" name="TopLevelTransaction" type="tns:TopLevelTransaction" />
|
20
|
+
</s:sequence>
|
21
|
+
</s:complexType>
|
22
|
+
</s:element>
|
23
|
+
</s:schema>
|
24
|
+
</wsdl:types>
|
25
|
+
<wsdl:message name="TopLevelTransactionSoapIn">
|
26
|
+
<wsdl:part name="parameters" element="tns:TopLevelTransaction" />
|
27
|
+
</wsdl:message>
|
28
|
+
<wsdl:portType name="XMLTESoap">
|
29
|
+
<wsdl:operation name="TopLevelTransaction">
|
30
|
+
<wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">TopLevelTransaction.</wsdl:documentation>
|
31
|
+
<wsdl:input message="tns:TopLevelTransactionSoapIn" />
|
32
|
+
</wsdl:operation>
|
33
|
+
</wsdl:portType>
|
34
|
+
<wsdl:binding name="XMLTESoap12" type="tns:XMLTESoap">
|
35
|
+
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
|
36
|
+
<wsdl:operation name="TopLevelTransaction">
|
37
|
+
<soap12:operation soapAction="www.example.com/XML/TopLevelTransaction" style="document" />
|
38
|
+
<wsdl:input>
|
39
|
+
<soap12:body use="literal" />
|
40
|
+
</wsdl:input>
|
41
|
+
</wsdl:operation>
|
42
|
+
</wsdl:binding>
|
43
|
+
</wsdl:definitions>
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "rack"
|
2
3
|
require "json"
|
3
4
|
|
@@ -6,7 +7,7 @@ class IntegrationServer
|
|
6
7
|
def self.respond_with(options = {})
|
7
8
|
code = options.fetch(:code, 200)
|
8
9
|
body = options.fetch(:body, "")
|
9
|
-
headers = { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s }
|
10
|
+
headers = { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s }.merge options.fetch(:headers, {})
|
10
11
|
|
11
12
|
[code, headers, [body]]
|
12
13
|
end
|
@@ -78,5 +79,36 @@ class IntegrationServer
|
|
78
79
|
run app
|
79
80
|
end
|
80
81
|
|
82
|
+
map "/multipart" do
|
83
|
+
run lambda { |env|
|
84
|
+
boundary = 'mimepart_boundary'
|
85
|
+
message = Mail.new
|
86
|
+
xml_part = Mail::Part.new do
|
87
|
+
content_type 'text/xml'
|
88
|
+
body %{<?xml version='1.0' encoding='UTF-8'?>
|
89
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
90
|
+
<soapenv:Header>response header</soapenv:Header>
|
91
|
+
<soapenv:Body>response body</soapenv:Body>
|
92
|
+
</soapenv:Envelope>}
|
93
|
+
# in Content-Type the start parameter is recommended (RFC 2387)
|
94
|
+
content_id '<soap-request-body@soap>'
|
95
|
+
end
|
96
|
+
message.add_part xml_part
|
97
|
+
|
98
|
+
message.add_file File.expand_path("../../../fixtures/gzip/message.gz", __FILE__)
|
99
|
+
message.parts.last.content_location = 'message.gz'
|
100
|
+
message.parts.last.content_id = 'attachment1'
|
101
|
+
|
102
|
+
message.ready_to_send!
|
103
|
+
message.body.set_sort_order [ "text/xml" ]
|
104
|
+
message.body.encoded(message.content_transfer_encoding)
|
105
|
+
|
106
|
+
IntegrationServer.respond_with({
|
107
|
+
headers: { "Content-Type" => "multipart/related; boundary=\"#{message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\"" },
|
108
|
+
body: message.body.encoded(message.content_transfer_encoding)
|
109
|
+
})
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
81
113
|
end
|
82
114
|
end
|
@@ -1,18 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
describe "ZIP code example" do
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
4
3
|
|
4
|
+
RSpec.describe "ZIP code example" do
|
5
5
|
it "supports threads making requests simultaneously" do
|
6
6
|
client = Savon.client(
|
7
|
-
# The WSDL document provided by the service.
|
8
7
|
:wsdl => "http://www.thomas-bayer.com/axis2/services/BLZService?wsdl",
|
9
8
|
|
10
9
|
# Lower timeouts so these specs don't take forever when the service is not available.
|
11
10
|
:open_timeout => 10,
|
12
11
|
:read_timeout => 10,
|
13
12
|
|
14
|
-
# Disable logging for cleaner spec output.
|
15
|
-
:log => false
|
13
|
+
:log => false # Disable logging for cleaner spec output.
|
16
14
|
)
|
17
15
|
|
18
16
|
mutex = Mutex.new
|
@@ -22,7 +20,7 @@ describe "ZIP code example" do
|
|
22
20
|
|
23
21
|
threads = request_data.map do |blz|
|
24
22
|
thread = Thread.new do
|
25
|
-
response = call_and_fail_gracefully
|
23
|
+
response = call_and_fail_gracefully(client, :get_bank, :message => { :blz => blz })
|
26
24
|
Thread.current[:value] = response.body[:get_bank_response][:details]
|
27
25
|
mutex.synchronize { threads_waiting -= 1 }
|
28
26
|
end
|
@@ -38,5 +36,4 @@ describe "ZIP code example" do
|
|
38
36
|
|
39
37
|
expect(values.uniq.size).to eq(values.size)
|
40
38
|
end
|
41
|
-
|
42
39
|
end
|
data/spec/savon/builder_spec.rb
CHANGED
data/spec/savon/client_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "spec_helper"
|
2
3
|
require "integration/support/server"
|
3
4
|
|
4
|
-
describe Savon::Client do
|
5
|
+
RSpec.describe Savon::Client do
|
5
6
|
|
6
7
|
before :all do
|
7
8
|
@server = IntegrationServer.run
|
@@ -231,15 +232,15 @@ describe Savon::Client do
|
|
231
232
|
end
|
232
233
|
|
233
234
|
it "raises when the operation name is not a symbol" do
|
234
|
-
expect { new_client.build_request("not a symbol") }.to raise_error
|
235
|
+
expect { new_client.build_request("not a symbol") }.to raise_error ArgumentError
|
235
236
|
end
|
236
237
|
|
237
238
|
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
|
+
expect { new_client.build_request(:authenticate, :invalid_local_option => true) }.to raise_error Savon::UnknownOptionError
|
239
240
|
end
|
240
241
|
|
241
242
|
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
|
+
expect { new_client.build_request(:authenticate) { another_invalid_local_option true } }.to raise_error Savon::UnknownOptionError
|
243
244
|
end
|
244
245
|
end
|
245
246
|
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "spec_helper"
|
2
3
|
|
3
|
-
describe Savon::HTTPError do
|
4
|
+
RSpec.describe Savon::HTTPError do
|
4
5
|
let(:http_error) { Savon::HTTPError.new new_response(:code => 404, :body => "Not Found") }
|
6
|
+
let(:http_error_with_empty_body) { Savon::HTTPError.new new_response(:code => 404, :body => "") }
|
5
7
|
let(:no_error) { Savon::HTTPError.new new_response }
|
6
8
|
|
7
9
|
it "inherits from Savon::Error" do
|
@@ -30,6 +32,12 @@ describe Savon::HTTPError do
|
|
30
32
|
it "returns the HTTP error message" do
|
31
33
|
expect(http_error.send method).to eq("HTTP error (404): Not Found")
|
32
34
|
end
|
35
|
+
|
36
|
+
context "when the body is empty" do
|
37
|
+
it "returns the HTTP error without the body message" do
|
38
|
+
expect(http_error_with_empty_body.send method).to eq("HTTP error (404)")
|
39
|
+
end
|
40
|
+
end
|
33
41
|
end
|
34
42
|
end
|
35
43
|
|
data/spec/savon/message_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "spec_helper"
|
2
3
|
require "integration/support/server"
|
3
4
|
|
4
|
-
describe Savon::Message do
|
5
|
+
RSpec.describe Savon::Message do
|
5
6
|
|
6
7
|
before do
|
7
8
|
@server = IntegrationServer.run
|
@@ -55,16 +56,6 @@ describe Savon::Message do
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
58
|
-
|
59
|
-
context 'wsa:MessageID' do
|
60
|
-
let(:message_id_tag) {
|
61
|
-
'<wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">'
|
62
|
-
}
|
63
|
-
it 'should include xmlns:wsa attribute' do
|
64
|
-
response = client.call(:something, message: {})
|
65
|
-
expect(response.xml).to include(message_id_tag)
|
66
|
-
end
|
67
|
-
end
|
68
59
|
end
|
69
60
|
|
70
61
|
end
|
data/spec/savon/mock_spec.rb
CHANGED
data/spec/savon/model_spec.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Savon::Builder do
|
4
|
+
|
5
|
+
let(:globals) { Savon::GlobalOptions.new({ :endpoint => "http://example.co", :namespace => "http://v1.example.com" }) }
|
6
|
+
let(:no_wsdl) { Wasabi::Document.new }
|
7
|
+
|
8
|
+
it "building multipart request from inline content" do
|
9
|
+
locals = {
|
10
|
+
attachments: [
|
11
|
+
{ filename: 'x1.xml', content: '<xml>abc1</xml>'},
|
12
|
+
{ filename: 'x2.xml', content: '<xml>abc2</xml>'},
|
13
|
+
]
|
14
|
+
}
|
15
|
+
builder = Savon::Builder.new(:operation1, no_wsdl, globals, Savon::LocalOptions.new(locals))
|
16
|
+
request_body = builder.to_s
|
17
|
+
|
18
|
+
expect(request_body).to include('Content-Type')
|
19
|
+
expect(request_body).to match(/<[a-z]+:operation1>/)
|
20
|
+
|
21
|
+
locals[:attachments].each do |attachment|
|
22
|
+
expect(request_body).to match(/^Content-Location: #{attachment[:filename]}\s$/)
|
23
|
+
expect(request_body).to include(Base64.encode64(attachment[:content]).strip)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
it "building multipart request from file" do
|
29
|
+
locals = {
|
30
|
+
attachments: {
|
31
|
+
'file.gz' => File.expand_path("../../fixtures/gzip/message.gz", __FILE__)
|
32
|
+
}
|
33
|
+
}
|
34
|
+
builder = Savon::Builder.new(:operation1, no_wsdl, globals, Savon::LocalOptions.new(locals))
|
35
|
+
request_body = builder.to_s
|
36
|
+
|
37
|
+
expect(request_body).to include('Content-Type')
|
38
|
+
expect(request_body).to match(/<[a-z]+:operation1>/)
|
39
|
+
|
40
|
+
locals[:attachments].each do |id, file|
|
41
|
+
expect(request_body).to match(/^Content-Location: #{id}\s$/)
|
42
|
+
expect(request_body.gsub("\r", "")).to include(Base64.encode64(File.read(file)).strip)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "spec_helper"
|
2
3
|
require "integration/support/server"
|
3
4
|
require "json"
|
4
5
|
require "ostruct"
|
5
6
|
|
6
|
-
describe Savon::Operation do
|
7
|
+
RSpec.describe Savon::Operation do
|
7
8
|
|
8
9
|
let(:globals) { Savon::GlobalOptions.new(:endpoint => @server.url(:repeat), :log => false) }
|
9
10
|
let(:wsdl) { Wasabi::Document.new Fixture.wsdl(:taxcloud) }
|
@@ -108,7 +109,7 @@ describe Savon::Operation do
|
|
108
109
|
it "sets the Content-Length header" do
|
109
110
|
# XXX: probably the worst spec ever written. refactor! [dh, 2013-01-05]
|
110
111
|
http_request = HTTPI::Request.new
|
111
|
-
http_request.headers.expects(:[]=).with("Content-Length", "
|
112
|
+
http_request.headers.expects(:[]=).with("Content-Length", "723")
|
112
113
|
Savon::SOAPRequest.any_instance.expects(:build).returns(http_request)
|
113
114
|
|
114
115
|
new_operation(:verify_address, wsdl, globals).call
|
@@ -165,40 +166,28 @@ describe Savon::Operation do
|
|
165
166
|
expect(actual_soap_action).to eq(%("authenticate"))
|
166
167
|
end
|
167
168
|
|
168
|
-
it "
|
169
|
-
globals.multipart
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
169
|
+
it "handle multipart response" do
|
170
|
+
globals.endpoint @server.url(:multipart)
|
171
|
+
operation = new_operation(:example, no_wsdl, globals)
|
172
|
+
response = operation.call do
|
173
|
+
attachments [
|
174
|
+
{ filename: 'x1.xml', content: '<xml>abc</xml>'},
|
175
|
+
{ filename: 'x2.xml', content: '<xml>cde</xml>'},
|
176
|
+
]
|
176
177
|
end
|
177
|
-
end
|
178
|
-
|
179
|
-
it "returns a Savon::Multipart::Response if available and requested locally" do
|
180
|
-
with_multipart_mocked do
|
181
|
-
operation = new_operation(:authenticate, no_wsdl, globals)
|
182
|
-
response = operation.call(:multipart => true)
|
183
178
|
|
184
|
-
|
185
|
-
|
179
|
+
expect(response.multipart?).to be true
|
180
|
+
expect(response.header).to eq 'response header'
|
181
|
+
expect(response.body).to eq 'response body'
|
182
|
+
expect(response.attachments.first.content_id).to eq 'attachment1'
|
186
183
|
end
|
187
184
|
|
188
|
-
it "
|
189
|
-
globals
|
190
|
-
|
191
|
-
operation = new_operation(:authenticate, no_wsdl, globals)
|
185
|
+
it "simple request is not multipart" do
|
186
|
+
operation = new_operation(:example, no_wsdl, globals)
|
187
|
+
response = operation.call
|
192
188
|
|
193
|
-
expect
|
194
|
-
|
195
|
-
end
|
196
|
-
|
197
|
-
it "raises if savon-multipart is not available and it was requested locally" do
|
198
|
-
operation = new_operation(:authenticate, no_wsdl, globals)
|
199
|
-
|
200
|
-
expect { operation.call(:multipart => true) }.
|
201
|
-
to raise_error RuntimeError, /Unable to find Savon::Multipart/
|
189
|
+
expect(response.multipart?).to be false
|
190
|
+
expect(response.attachments).to be_empty
|
202
191
|
end
|
203
192
|
end
|
204
193
|
|
@@ -211,18 +200,6 @@ describe Savon::Operation do
|
|
211
200
|
end
|
212
201
|
end
|
213
202
|
|
214
|
-
def with_multipart_mocked
|
215
|
-
multipart_response = Class.new { def initialize(*args); end }
|
216
|
-
multipart_mock = Module.new
|
217
|
-
multipart_mock.const_set('Response', multipart_response)
|
218
|
-
|
219
|
-
Savon.const_set('Multipart', multipart_mock)
|
220
|
-
|
221
|
-
yield
|
222
|
-
ensure
|
223
|
-
Savon.send(:remove_const, :Multipart) if Savon.const_defined? :Multipart
|
224
|
-
end
|
225
|
-
|
226
203
|
def inspect_request(response)
|
227
204
|
hash = JSON.parse(response.http.body)
|
228
205
|
OpenStruct.new(hash)
|