peddler 1.6.0 → 1.6.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 +4 -4
- data/lib/mws/merchant_fulfillment/client.rb +2 -0
- data/lib/peddler/client.rb +14 -11
- data/lib/peddler/errors/error.rb +2 -2
- data/lib/peddler/errors/handler.rb +21 -9
- data/lib/peddler/errors/parser.rb +1 -3
- data/lib/peddler/flat_file_parser.rb +10 -19
- data/lib/peddler/headers.rb +2 -0
- data/lib/peddler/vcr_matcher.rb +4 -4
- data/lib/peddler/version.rb +2 -1
- data/test/helper.rb +1 -0
- data/test/integration/test_feeds.rb +10 -8
- data/test/integration/test_reports.rb +1 -7
- data/test/integration_helper.rb +4 -6
- data/test/unit/mws/test_feeds_client.rb +0 -1
- data/test/unit/mws/test_fulfillment_inbound_shipment_client.rb +1 -1
- data/test/unit/mws/test_merchant_fulfillment_client.rb +32 -8
- data/test/unit/peddler/test_client.rb +38 -8
- data/test/unit/peddler/test_flat_file_parser.rb +1 -1
- data/test/unit/peddler/test_headers.rb +5 -0
- data/test/unit/peddler/test_operation.rb +2 -2
- data/test/unit/peddler/test_structured_list.rb +2 -2
- data/test/unit/peddler/test_xml_response_parser.rb +1 -1
- data/test/vcr_cassettes/Feeds.yml +5873 -5454
- data/test/vcr_cassettes/Reports.yml +2785 -4396
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d83b099126be1823bbd9bd9c45ea1d7ec3455f9
|
4
|
+
data.tar.gz: 74b18adc957648244ac9b7bf04d60fe3006c4fc2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f977ea894c96c6454bfd442bd8f5e866aaf56029c23649d7d133fbca85b4544a894d99e254847a77afaa36460dfb1caf39f0eefb272982739b0c375cf340ba0
|
7
|
+
data.tar.gz: 3b7f7bdf62a91254838b412ba634eca896f8a4cb14683ecb92b7e1621c6a7b72d92874e1a1f564abb0529e390ae85c7cf9cf9fb2d399d9ef695d0f888c6358ac
|
@@ -22,6 +22,7 @@ module MWS
|
|
22
22
|
def get_eligible_shipping_services(shipment_request_details)
|
23
23
|
operation('GetEligibleShippingServices')
|
24
24
|
.add('ShipmentRequestDetails' => shipment_request_details)
|
25
|
+
.structure!('ItemList', 'Item')
|
25
26
|
|
26
27
|
run
|
27
28
|
end
|
@@ -42,6 +43,7 @@ module MWS
|
|
42
43
|
'ShippingServiceId' => shipping_service_id
|
43
44
|
)
|
44
45
|
.add(opts)
|
46
|
+
.structure!('ItemList', 'Item')
|
45
47
|
|
46
48
|
run
|
47
49
|
end
|
data/lib/peddler/client.rb
CHANGED
@@ -129,13 +129,7 @@ module Peddler
|
|
129
129
|
|
130
130
|
# @!parse attr_writer :body
|
131
131
|
def body=(str)
|
132
|
-
|
133
|
-
headers['Content-Type'] = content_type(str)
|
134
|
-
else
|
135
|
-
headers.delete('Content-Type')
|
136
|
-
end
|
137
|
-
|
138
|
-
@body = str
|
132
|
+
str ? add_content(str) : clear_content!
|
139
133
|
end
|
140
134
|
|
141
135
|
# @api private
|
@@ -169,6 +163,7 @@ module Peddler
|
|
169
163
|
opts = build_options
|
170
164
|
opts.store(:response_block, Proc.new) if block_given?
|
171
165
|
res = post(opts)
|
166
|
+
self.body = nil if res.status == 200
|
172
167
|
|
173
168
|
parser.new(res, encoding)
|
174
169
|
rescue Excon::Error => e
|
@@ -181,11 +176,19 @@ module Peddler
|
|
181
176
|
Marketplace.new(primary_marketplace_id)
|
182
177
|
end
|
183
178
|
|
184
|
-
def
|
185
|
-
|
186
|
-
|
179
|
+
def clear_content!
|
180
|
+
headers.delete('Content-Type')
|
181
|
+
@body = nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def add_content(content)
|
185
|
+
if content.start_with?('<?xml')
|
186
|
+
headers['Content-Type'] = 'text/xml'
|
187
|
+
@body = content
|
187
188
|
else
|
188
|
-
|
189
|
+
headers['Content-Type'] =
|
190
|
+
"text/tab-separated-values; charset=#{encoding}"
|
191
|
+
@body = content.encode(encoding)
|
189
192
|
end
|
190
193
|
end
|
191
194
|
|
data/lib/peddler/errors/error.rb
CHANGED
@@ -2,7 +2,7 @@ module Peddler
|
|
2
2
|
# @api private
|
3
3
|
module Errors
|
4
4
|
# Known codes
|
5
|
-
CODES = %w
|
5
|
+
CODES = %w[
|
6
6
|
AccessDenied
|
7
7
|
InvalidMarketplace
|
8
8
|
InvalidParameterValue
|
@@ -10,7 +10,7 @@ module Peddler
|
|
10
10
|
MalformedInput
|
11
11
|
QuotaExceeded
|
12
12
|
RequestThrottled
|
13
|
-
|
13
|
+
].freeze
|
14
14
|
|
15
15
|
# @api private
|
16
16
|
class Error < StandardError
|
@@ -5,6 +5,9 @@ module Peddler
|
|
5
5
|
module Errors
|
6
6
|
# @api private
|
7
7
|
class Handler
|
8
|
+
DIGIT_AS_FIRST_CHAR = /^\d/
|
9
|
+
private_constant :DIGIT_AS_FIRST_CHAR
|
10
|
+
|
8
11
|
def self.call(exception)
|
9
12
|
new(exception).handle
|
10
13
|
end
|
@@ -20,26 +23,35 @@ module Peddler
|
|
20
23
|
end
|
21
24
|
|
22
25
|
def handle
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
rescue NameError
|
29
|
-
raise exception
|
26
|
+
raise exception unless http_status_error?
|
27
|
+
raise exception if bad_name_for_error_class?
|
28
|
+
|
29
|
+
raise error_class.new(error_message, exception)
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
34
|
def error_class
|
35
|
-
Errors.const_get(
|
35
|
+
Errors.const_get(error_name)
|
36
36
|
rescue NameError
|
37
|
-
Builder.build(
|
37
|
+
Builder.build(error_name)
|
38
38
|
end
|
39
39
|
|
40
40
|
def http_status_error?
|
41
41
|
exception.is_a?(::Excon::Error::HTTPStatus)
|
42
42
|
end
|
43
|
+
|
44
|
+
def bad_name_for_error_class?
|
45
|
+
error_name =~ DIGIT_AS_FIRST_CHAR
|
46
|
+
end
|
47
|
+
|
48
|
+
def error_name
|
49
|
+
exception.response.code
|
50
|
+
end
|
51
|
+
|
52
|
+
def error_message
|
53
|
+
exception.response.message
|
54
|
+
end
|
43
55
|
end
|
44
56
|
end
|
45
57
|
end
|
@@ -11,20 +11,20 @@ module Peddler
|
|
11
11
|
# http://stackoverflow.com/questions/8073920/importing-csv-quoting-error-is-driving-me-nuts
|
12
12
|
OPTIONS = { col_sep: "\t", quote_char: "\x00", headers: true }.freeze
|
13
13
|
|
14
|
-
attr_reader :content, :summary
|
14
|
+
attr_reader :content, :summary
|
15
15
|
|
16
16
|
def initialize(res, encoding)
|
17
17
|
super(res)
|
18
|
-
|
19
|
-
|
18
|
+
scrub_body!(encoding)
|
19
|
+
extract_content_and_summary
|
20
20
|
end
|
21
21
|
|
22
22
|
def parse(&blk)
|
23
|
-
CSV.parse(
|
23
|
+
CSV.parse(content, OPTIONS, &blk) if content
|
24
24
|
end
|
25
25
|
|
26
26
|
def records_count
|
27
|
-
summarize if summary
|
27
|
+
summarize if summary
|
28
28
|
end
|
29
29
|
|
30
30
|
def valid?
|
@@ -33,22 +33,13 @@ module Peddler
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
-
def
|
37
|
-
|
38
|
-
@summary, @content = body.split("\n\n")
|
39
|
-
else
|
40
|
-
@content = body.dup
|
41
|
-
end
|
36
|
+
def scrub_body!(encoding)
|
37
|
+
body.force_encoding(encoding) unless body.encoding == 'UTF-8'
|
42
38
|
end
|
43
39
|
|
44
|
-
def
|
45
|
-
content
|
46
|
-
|
47
|
-
.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
|
48
|
-
end
|
49
|
-
|
50
|
-
def summary?
|
51
|
-
body.include?("\n\n")
|
40
|
+
def extract_content_and_summary
|
41
|
+
@content = body.encode('UTF-8', invalid: :replace, undef: :replace)
|
42
|
+
@summary, @content = @content.split("\n\n") if @content.include?("\n\n")
|
52
43
|
end
|
53
44
|
|
54
45
|
def summarize
|
data/lib/peddler/headers.rb
CHANGED
data/lib/peddler/vcr_matcher.rb
CHANGED
@@ -2,13 +2,13 @@ module Peddler
|
|
2
2
|
# A custom matcher that can be used to record MWS interactions when
|
3
3
|
# integration-testing
|
4
4
|
class VCRMatcher
|
5
|
-
TRANSIENT_PARAMS = %w
|
5
|
+
TRANSIENT_PARAMS = %w[
|
6
6
|
Signature Timestamp StartDate CreatedAfter QueryStartDateTime
|
7
|
-
|
7
|
+
].freeze
|
8
8
|
|
9
|
-
SELLER_PARAMS = %w
|
9
|
+
SELLER_PARAMS = %w[
|
10
10
|
AWSAccessKeyId SellerId
|
11
|
-
|
11
|
+
].freeze
|
12
12
|
|
13
13
|
class << self
|
14
14
|
def call(*requests)
|
data/lib/peddler/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -17,19 +17,21 @@ class TestFeeds < IntegrationTest
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_submits_feeds
|
20
|
-
|
21
|
-
|
20
|
+
feed_content = "sku\tprice\tquantity\nwidget\t\t0\n"
|
21
|
+
feed_type = '_POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA_'
|
22
22
|
|
23
23
|
clients.each do |client|
|
24
|
-
res = client.submit_feed(
|
25
|
-
|
26
|
-
|
24
|
+
res = client.submit_feed(feed_content, feed_type)
|
25
|
+
feed_submission_id = res.dig('FeedSubmissionInfo', 'FeedSubmissionId')
|
26
|
+
assert feed_submission_id
|
27
|
+
|
28
|
+
res = client.get_feed_submission_result(feed_submission_id)
|
29
|
+
assert res.records_count
|
27
30
|
|
28
31
|
# Clean up
|
29
|
-
client.body = nil
|
30
32
|
client.cancel_feed_submissions(
|
31
|
-
feed_submission_id:
|
32
|
-
feed_type_list:
|
33
|
+
feed_submission_id: feed_submission_id,
|
34
|
+
feed_type_list: feed_type
|
33
35
|
)
|
34
36
|
end
|
35
37
|
end
|
@@ -37,18 +37,12 @@ class TestReports < IntegrationTest
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def test_gets_report_list
|
41
|
-
clients.each do |client|
|
42
|
-
res = client.get_report_list
|
43
|
-
refute_empty res.parse
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
40
|
def test_gets_report
|
48
41
|
clients.each do |client|
|
49
42
|
res = client.get_report_list(max_count: 1)
|
50
43
|
id = res.parse['ReportInfo']['ReportId']
|
51
44
|
res = client.get_report(id)
|
45
|
+
assert res.valid?
|
52
46
|
refute_empty res.parse || res.records_count
|
53
47
|
end
|
54
48
|
end
|
data/test/integration_helper.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'recorder'
|
3
3
|
|
4
|
-
%w
|
4
|
+
%w[mws.yml mws.yml.example].each do |path|
|
5
5
|
file = File.expand_path("../#{path}", __FILE__)
|
6
6
|
if File.exist?(file)
|
7
7
|
$mws = YAML.load_file(file)
|
@@ -16,9 +16,7 @@ class IntegrationTest < MiniTest::Test
|
|
16
16
|
|
17
17
|
def clients
|
18
18
|
api = @api || test_name
|
19
|
-
$mws.map
|
20
|
-
MWS.const_get("#{api}::Client").new(record)
|
21
|
-
end
|
19
|
+
$mws.map { |record| MWS.const_get("#{api}::Client").new(record) }.shuffle
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
@@ -26,9 +24,9 @@ end
|
|
26
24
|
|
27
25
|
VCR.configure do |c|
|
28
26
|
c.before_record do |interaction|
|
29
|
-
%w
|
27
|
+
%w[
|
30
28
|
BuyerName BuyerEmail Name AddressLine1 PostalCode Phone Amount
|
31
|
-
|
29
|
+
].each do |key|
|
32
30
|
interaction.response.body.gsub!(/<#{key}>[^<]+</, "<#{key}>FILTERED<")
|
33
31
|
end
|
34
32
|
end
|
@@ -9,14 +9,26 @@ class TestMWSMerchantFulfillmentClient < MiniTest::Test
|
|
9
9
|
def test_gets_eligible_shipping_services
|
10
10
|
operation = {
|
11
11
|
'Action' => 'GetEligibleShippingServices',
|
12
|
-
'ShipmentRequestDetails.
|
13
|
-
'ShipmentRequestDetails.
|
12
|
+
'ShipmentRequestDetails.AmazonOrderId' => '123',
|
13
|
+
'ShipmentRequestDetails.Weight.Value' => '10',
|
14
|
+
'ShipmentRequestDetails.Weight.Unit' => 'ounces',
|
15
|
+
'ShipmentRequestDetails.ItemList.Item.1.OrderItemId' => '123',
|
16
|
+
'ShipmentRequestDetails.ItemList.Item.1.Quantity' => '1'
|
14
17
|
}
|
15
18
|
|
16
19
|
@client.stub(:run, nil) do
|
17
20
|
shipment_request_details = {
|
18
|
-
|
19
|
-
|
21
|
+
amazon_order_id: '123',
|
22
|
+
weight: {
|
23
|
+
value: '10',
|
24
|
+
unit: 'ounces'
|
25
|
+
},
|
26
|
+
item_list: [
|
27
|
+
{
|
28
|
+
order_item_id: '123',
|
29
|
+
quantity: '1'
|
30
|
+
}
|
31
|
+
]
|
20
32
|
}
|
21
33
|
@client.get_eligible_shipping_services(shipment_request_details)
|
22
34
|
end
|
@@ -27,15 +39,27 @@ class TestMWSMerchantFulfillmentClient < MiniTest::Test
|
|
27
39
|
def test_creates_shipment
|
28
40
|
operation = {
|
29
41
|
'Action' => 'CreateShipment',
|
30
|
-
'ShipmentRequestDetails.
|
31
|
-
'ShipmentRequestDetails.
|
42
|
+
'ShipmentRequestDetails.AmazonOrderId' => '123',
|
43
|
+
'ShipmentRequestDetails.Weight.Value' => '10',
|
44
|
+
'ShipmentRequestDetails.Weight.Unit' => 'ounces',
|
45
|
+
'ShipmentRequestDetails.ItemList.Item.1.OrderItemId' => '123',
|
46
|
+
'ShipmentRequestDetails.ItemList.Item.1.Quantity' => '1',
|
32
47
|
'ShippingServiceId' => 'FOO'
|
33
48
|
}
|
34
49
|
|
35
50
|
@client.stub(:run, nil) do
|
36
51
|
shipment_request_details = {
|
37
|
-
|
38
|
-
|
52
|
+
amazon_order_id: '123',
|
53
|
+
weight: {
|
54
|
+
value: '10',
|
55
|
+
unit: 'ounces'
|
56
|
+
},
|
57
|
+
item_list: [
|
58
|
+
{
|
59
|
+
order_item_id: '123',
|
60
|
+
quantity: '1'
|
61
|
+
}
|
62
|
+
]
|
39
63
|
}
|
40
64
|
@client.create_shipment(shipment_request_details, 'FOO')
|
41
65
|
end
|
@@ -3,9 +3,9 @@ require 'null_client'
|
|
3
3
|
|
4
4
|
class TestPeddlerClient < MiniTest::Test
|
5
5
|
def setup
|
6
|
-
@
|
6
|
+
@response_body = 'foo'
|
7
7
|
Excon.defaults[:mock] = true
|
8
|
-
Excon.stub({}, body: @
|
8
|
+
Excon.stub({}, body: @response_body, status: 200)
|
9
9
|
|
10
10
|
@klass = Class.new(Null::Client)
|
11
11
|
@client = @klass.new
|
@@ -71,14 +71,14 @@ class TestPeddlerClient < MiniTest::Test
|
|
71
71
|
assert_equal '123', client.aws_access_key_id
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
74
|
+
def test_sets_content_type_header_for_latin_flat_file
|
75
75
|
@client.body = 'foo'
|
76
76
|
content_type = @client.headers.fetch('Content-Type')
|
77
77
|
|
78
78
|
assert_equal 'text/tab-separated-values; charset=CP1252', content_type
|
79
79
|
end
|
80
80
|
|
81
|
-
def
|
81
|
+
def test_sets_content_type_header_for_chinese_flat_file
|
82
82
|
@client.primary_marketplace_id = 'AAHKV2X7AFYLW'
|
83
83
|
@client.body = 'foo'
|
84
84
|
content_type = @client.headers.fetch('Content-Type')
|
@@ -86,7 +86,7 @@ class TestPeddlerClient < MiniTest::Test
|
|
86
86
|
assert_equal 'text/tab-separated-values; charset=UTF-16', content_type
|
87
87
|
end
|
88
88
|
|
89
|
-
def
|
89
|
+
def test_sets_content_type_header_for_japanese_flat_file
|
90
90
|
@client.primary_marketplace_id = 'A1VC38T7YXB528'
|
91
91
|
@client.body = 'foo'
|
92
92
|
content_type = @client.headers.fetch('Content-Type')
|
@@ -94,16 +94,46 @@ class TestPeddlerClient < MiniTest::Test
|
|
94
94
|
assert_equal 'text/tab-separated-values; charset=Windows-31J', content_type
|
95
95
|
end
|
96
96
|
|
97
|
-
def
|
97
|
+
def test_sets_content_type_header_for_xml
|
98
98
|
@client.body = '<?xml version="1.0"?><Foo></Foo>'
|
99
99
|
content_type = @client.headers.fetch('Content-Type')
|
100
100
|
|
101
101
|
assert_equal 'text/xml', content_type
|
102
102
|
end
|
103
103
|
|
104
|
+
def test_encodes_body_for_latin_flat_file
|
105
|
+
@client.body = 'foo'
|
106
|
+
assert_equal 'Windows-1252', @client.body.encoding.to_s
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_encodes_body_for_chinese_flat_file
|
110
|
+
@client.primary_marketplace_id = 'AAHKV2X7AFYLW'
|
111
|
+
@client.body = 'foo'
|
112
|
+
assert_equal 'UTF-16', @client.body.encoding.to_s
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_encodes_body_for_japanese_flat_file
|
116
|
+
@client.primary_marketplace_id = 'A1VC38T7YXB528'
|
117
|
+
@client.body = 'foo'
|
118
|
+
assert_equal 'Windows-31J', @client.body.encoding.to_s
|
119
|
+
end
|
120
|
+
|
104
121
|
def test_runs_a_request
|
105
122
|
res = @client.run
|
106
|
-
assert_equal @
|
123
|
+
assert_equal @response_body, res.body
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_clears_body_when_run_succeeds
|
127
|
+
@client.body = 'foo'
|
128
|
+
@client.run
|
129
|
+
assert_nil @client.body
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_does_not_clear_body_when_run_fails
|
133
|
+
Excon.stub({}, status: 503)
|
134
|
+
@client.body = 'foo'
|
135
|
+
assert_raises { @client.run }
|
136
|
+
refute_nil @client.body
|
107
137
|
end
|
108
138
|
|
109
139
|
def test_streams_response
|
@@ -111,7 +141,7 @@ class TestPeddlerClient < MiniTest::Test
|
|
111
141
|
streamer = ->(chunk, _, _) { chunks << chunk }
|
112
142
|
@client.run(&streamer)
|
113
143
|
|
114
|
-
assert_equal @
|
144
|
+
assert_equal @response_body, chunks
|
115
145
|
end
|
116
146
|
|
117
147
|
class Instrumentor
|
@@ -70,7 +70,7 @@ class TestPeddlerFlatFileParser < MiniTest::Test
|
|
70
70
|
body = "Foo\n\xFF\n"
|
71
71
|
body.force_encoding('ASCII-8BIT')
|
72
72
|
parser = Peddler::FlatFileParser.new(build_mock_response(body), 'ASCII-8BIT')
|
73
|
-
assert_equal '
|
73
|
+
assert_equal '�', parser.parse['Foo'][0]
|
74
74
|
end
|
75
75
|
|
76
76
|
private
|
@@ -15,10 +15,10 @@ class TestPeddlerOperation < MiniTest::Test
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_converts_nested_key_to_structured_list
|
18
|
-
@operation.store('Foo.Status', [1])
|
18
|
+
@operation.store('Foo.Status', [{ 'Baz' => 1 }])
|
19
19
|
@operation.structure!('Status', 'Bar')
|
20
20
|
refute @operation.key?('FooStatus')
|
21
|
-
assert_equal 1, @operation['Foo.Status.Bar.1']
|
21
|
+
assert_equal 1, @operation['Foo.Status.Bar.1.Baz']
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_store_camelizes_symbol_key
|
@@ -13,12 +13,12 @@ class TestPeddlerStructuredList < MiniTest::Test
|
|
13
13
|
|
14
14
|
def test_builds_a_structured_list_for_an_array_of_values
|
15
15
|
exp = { 'OrderStatus.Status.1' => 'foo', 'OrderStatus.Status.2' => 'bar' }
|
16
|
-
assert_equal exp, @list.build(%w
|
16
|
+
assert_equal exp, @list.build(%w[foo bar])
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_flattens_nested_arrays_of_values
|
20
20
|
exp = { 'OrderStatus.Status.1' => 'foo', 'OrderStatus.Status.2' => 'bar' }
|
21
|
-
assert_equal exp, @list.build([%w
|
21
|
+
assert_equal exp, @list.build([%w[foo bar]])
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_handles_single_key
|
@@ -31,7 +31,7 @@ class TestPeddlerXMLResponseParser < MiniTest::Test
|
|
31
31
|
def response(body)
|
32
32
|
OpenStruct.new(
|
33
33
|
body: body,
|
34
|
-
headers: { 'Content-Type' => 'text/xml', 'Content-Length' =>
|
34
|
+
headers: { 'Content-Type' => 'text/xml', 'Content-Length' => body.size }
|
35
35
|
)
|
36
36
|
end
|
37
37
|
end
|