taxjar-ruby 2.5.0 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,8 +28,15 @@ module Taxjar
28
28
  def perform
29
29
  options_key = [:get, :delete].include?(@request_method) ? :params : :json
30
30
  response = build_http_client.request(request_method, uri.to_s, options_key => @options)
31
- response_body = symbolize_keys!(response.parse)
32
- fail_or_return_response_body(response.code, response_body)
31
+ response_body =
32
+ begin
33
+ symbolize_keys!(response.parse(:json))
34
+ rescue JSON::ParserError
35
+ nil
36
+ end
37
+ fail_or_return_response_body(response, response_body)
38
+ rescue HTTP::Error => e
39
+ raise Taxjar::Error, e
33
40
  end
34
41
 
35
42
  private
@@ -67,16 +74,15 @@ module Taxjar
67
74
  object
68
75
  end
69
76
 
70
- def fail_or_return_response_body(code, body)
71
- e = extract_error(code, body)
72
- fail(e) if e
73
- body[object_key.to_sym]
74
- end
75
-
76
- def extract_error(code, body)
77
- klass = Taxjar::Error::ERRORS[code]
78
- if !klass.nil?
79
- klass.from_response(body)
77
+ def fail_or_return_response_body(response, body)
78
+ if body.nil?
79
+ fail(Taxjar::Error.for_json_parse_error(response.code))
80
+ elsif response.status.success?
81
+ body[object_key.to_sym]
82
+ elsif !(klass = Taxjar::Error::ERRORS[response.code]).nil?
83
+ fail(klass.from_response(body))
84
+ else
85
+ fail(Taxjar::Error.from_response_code(response.code))
80
86
  end
81
87
  end
82
88
  end
data/lib/taxjar/client.rb CHANGED
@@ -38,7 +38,14 @@ module Taxjar
38
38
  end
39
39
 
40
40
  def user_agent
41
- "TaxjarRubyGem/#{Taxjar::Version}"
41
+ def platform
42
+ (`uname -a` || '').strip
43
+ rescue Errno::ENOENT, Errno::ENOMEM
44
+ ''
45
+ end
46
+ ruby_version = "ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
47
+ openSSL_version = OpenSSL::OPENSSL_LIBRARY_VERSION
48
+ "TaxJar/Ruby (#{platform}; #{ruby_version}; #{openSSL_version}) taxjar-ruby/#{Taxjar::Version}"
42
49
  end
43
50
  end
44
51
  end
data/lib/taxjar/error.rb CHANGED
@@ -61,6 +61,15 @@ module Taxjar
61
61
  new(message, code)
62
62
  end
63
63
 
64
+ def from_response_code(code)
65
+ message = HTTP::Response::Status::REASONS[code] || "Unknown Error"
66
+ new(message, code)
67
+ end
68
+
69
+ def for_json_parse_error(code)
70
+ ServerError.new("Couldn't parse response as JSON.", code)
71
+ end
72
+
64
73
  end
65
74
 
66
75
  def initialize(message = '', code = nil)
data/lib/taxjar/order.rb CHANGED
@@ -8,6 +8,7 @@ module Taxjar
8
8
  attribute :user_id, :integer
9
9
  attribute :transaction_date, :string
10
10
  attribute :provider, :string
11
+ attribute :exemption_type, :string
11
12
  attribute :from_country, :string
12
13
  attribute :from_zip, :string
13
14
  attribute :from_state, :string
data/lib/taxjar/refund.rb CHANGED
@@ -9,6 +9,7 @@ module Taxjar
9
9
  attribute :transaction_date, :string
10
10
  attribute :transaction_reference_id, :string
11
11
  attribute :provider, :string
12
+ attribute :exemption_type, :string
12
13
  attribute :from_country, :string
13
14
  attribute :from_zip, :string
14
15
  attribute :from_state, :string
data/lib/taxjar/tax.rb CHANGED
@@ -12,6 +12,7 @@ module Taxjar
12
12
  attribute :has_nexus, :boolean
13
13
  attribute :freight_taxable, :boolean
14
14
  attribute :tax_source, :string
15
+ attribute :exemption_type, :string
15
16
 
16
17
  object_attr_reader Taxjar::Jurisdictions, :jurisdictions
17
18
  object_attr_reader Taxjar::Breakdown, :breakdown
@@ -2,15 +2,15 @@ module Taxjar
2
2
  module Version
3
3
  module_function
4
4
  def major
5
- 2
5
+ 3
6
6
  end
7
7
 
8
8
  def minor
9
- 5
9
+ 0
10
10
  end
11
11
 
12
12
  def patch
13
- 0
13
+ 2
14
14
  end
15
15
 
16
16
  def pre
@@ -4,6 +4,7 @@
4
4
  "user_id": 10649,
5
5
  "transaction_date": "2015-05-14T00:00:00Z",
6
6
  "provider": "api",
7
+ "exemption_type": "non_exempt",
7
8
  "from_country": "US",
8
9
  "from_zip": "93107",
9
10
  "from_state": "CA",
@@ -5,6 +5,7 @@
5
5
  "transaction_date": "2015-05-14T00:00:00Z",
6
6
  "transaction_reference_id": "123",
7
7
  "provider": "api",
8
+ "exemption_type": "non_exempt",
8
9
  "from_country": "US",
9
10
  "from_zip": "93107",
10
11
  "from_state": "CA",
@@ -8,6 +8,7 @@
8
8
  "has_nexus": true,
9
9
  "freight_taxable": true,
10
10
  "tax_source": "destination",
11
+ "exemption_type": "non_exempt",
11
12
  "jurisdictions": {
12
13
  "country": "US",
13
14
  "state": "NJ",
@@ -8,6 +8,7 @@
8
8
  "has_nexus": true,
9
9
  "freight_taxable": true,
10
10
  "tax_source": "destination",
11
+ "exemption_type": "non_exempt",
11
12
  "jurisdictions": {
12
13
  "country": "CA",
13
14
  "state": "ON"
@@ -8,6 +8,7 @@
8
8
  "has_nexus": true,
9
9
  "freight_taxable": true,
10
10
  "tax_source": "destination",
11
+ "exemption_type": "non_exempt",
11
12
  "jurisdictions": {
12
13
  "country": "FI"
13
14
  },
@@ -168,6 +168,7 @@ describe Taxjar::API do
168
168
  :to_zip => '07446',
169
169
  :amount => 16.50,
170
170
  :shipping => 1.5,
171
+ :exemption_type => 'non_exempt',
171
172
  :line_items => [{:line_item => {:id => '1',
172
173
  :quantity => 1,
173
174
  :unit_price => 15.0,
@@ -191,6 +192,7 @@ describe Taxjar::API do
191
192
  expect(tax.has_nexus).to eq(true)
192
193
  expect(tax.freight_taxable).to eq(true)
193
194
  expect(tax.tax_source).to eq('destination')
195
+ expect(tax.exemption_type).to eq('non_exempt')
194
196
  end
195
197
 
196
198
  it 'allows access to jurisdictions' do
@@ -275,6 +277,7 @@ describe Taxjar::API do
275
277
  expect(tax.has_nexus).to eq(true)
276
278
  expect(tax.freight_taxable).to eq(true)
277
279
  expect(tax.tax_source).to eq('destination')
280
+ expect(tax.exemption_type).to eq('non_exempt')
278
281
  end
279
282
 
280
283
  it 'allows access to jurisdictions' do
@@ -330,6 +333,7 @@ describe Taxjar::API do
330
333
  expect(tax.has_nexus).to eq(true)
331
334
  expect(tax.freight_taxable).to eq(true)
332
335
  expect(tax.tax_source).to eq('destination')
336
+ expect(tax.exemption_type).to eq('non_exempt')
333
337
  end
334
338
 
335
339
  it 'allows access to jurisdictions' do
@@ -71,6 +71,7 @@ describe Taxjar::API::Order do
71
71
  expect(order.user_id).to eq(10649)
72
72
  expect(order.transaction_date).to eq('2015-05-14T00:00:00Z')
73
73
  expect(order.provider).to eq('api')
74
+ expect(order.exemption_type).to eq('non_exempt')
74
75
  expect(order.from_country).to eq('US')
75
76
  expect(order.from_zip).to eq('93107')
76
77
  expect(order.from_state).to eq('CA')
@@ -107,6 +108,7 @@ describe Taxjar::API::Order do
107
108
  @order = {:transaction_id => '123',
108
109
  :transaction_date => '2015/05/14',
109
110
  :provider => 'api',
111
+ :exemption_type => 'non_exempt',
110
112
  :to_country => 'US',
111
113
  :to_zip => '90002',
112
114
  :to_city => 'Los Angeles',
@@ -137,6 +139,7 @@ describe Taxjar::API::Order do
137
139
  expect(order.user_id).to eq(10649)
138
140
  expect(order.transaction_date).to eq("2015-05-14T00:00:00Z")
139
141
  expect(order.provider).to eq('api')
142
+ expect(order.exemption_type).to eq('non_exempt')
140
143
  expect(order.from_country).to eq('US')
141
144
  expect(order.from_zip).to eq('93107')
142
145
  expect(order.from_state).to eq('CA')
@@ -174,6 +177,7 @@ describe Taxjar::API::Order do
174
177
  @order = {:transaction_id => '123',
175
178
  :amount => 17.95,
176
179
  :shipping => 2.0,
180
+ :exemption_type => 'non_exempt',
177
181
  :line_items => [{:id => 1,
178
182
  :quantity => 1,
179
183
  :product_identifier => '12-34243-0',
@@ -197,6 +201,7 @@ describe Taxjar::API::Order do
197
201
  expect(order.user_id).to eq(10649)
198
202
  expect(order.transaction_date).to eq("2015-05-14T00:00:00Z")
199
203
  expect(order.provider).to eq('api')
204
+ expect(order.exemption_type).to eq('non_exempt')
200
205
  expect(order.from_country).to eq('US')
201
206
  expect(order.from_zip).to eq('93107')
202
207
  expect(order.from_state).to eq('CA')
@@ -244,6 +249,7 @@ describe Taxjar::API::Order do
244
249
  expect(order.user_id).to eq(10649)
245
250
  expect(order.transaction_date).to eq("2015-05-14T00:00:00Z")
246
251
  expect(order.provider).to eq('api')
252
+ expect(order.exemption_type).to eq('non_exempt')
247
253
  expect(order.from_country).to eq('US')
248
254
  expect(order.from_zip).to eq('93107')
249
255
  expect(order.from_state).to eq('CA')
@@ -72,6 +72,7 @@ describe Taxjar::API::Refund do
72
72
  expect(refund.transaction_date).to eq("2015-05-14T00:00:00Z")
73
73
  expect(refund.transaction_reference_id).to eq("123")
74
74
  expect(refund.provider).to eql('api')
75
+ expect(refund.exemption_type).to eq('non_exempt')
75
76
  expect(refund.from_country).to eq('US')
76
77
  expect(refund.from_zip).to eq('93107')
77
78
  expect(refund.from_state).to eq('CA')
@@ -109,6 +110,7 @@ describe Taxjar::API::Refund do
109
110
  :transaction_date => '2015/05/14',
110
111
  :transaction_reference_id => '123',
111
112
  :provider => 'api',
113
+ :exemption_type => 'non_exempt',
112
114
  :to_country => 'US',
113
115
  :to_zip => '90002',
114
116
  :to_state => 'CA',
@@ -140,6 +142,7 @@ describe Taxjar::API::Refund do
140
142
  expect(refund.transaction_date).to eq("2015-05-14T00:00:00Z")
141
143
  expect(refund.transaction_reference_id).to eq("123")
142
144
  expect(refund.provider).to eq('api')
145
+ expect(refund.exemption_type).to eq('non_exempt')
143
146
  expect(refund.from_country).to eq('US')
144
147
  expect(refund.from_zip).to eq('93107')
145
148
  expect(refund.from_state).to eq('CA')
@@ -178,6 +181,7 @@ describe Taxjar::API::Refund do
178
181
  :amount => 17.95,
179
182
  :shipping => 2.0,
180
183
  :sales_tax => 0.95,
184
+ :exemption_type => 'non_exempt',
181
185
  :line_items => [{:quantity => 1,
182
186
  :product_identifier => '12-34243-9',
183
187
  :description => 'Heavy Widget',
@@ -201,6 +205,7 @@ describe Taxjar::API::Refund do
201
205
  expect(refund.transaction_date).to eq("2015-05-14T00:00:00Z")
202
206
  expect(refund.transaction_reference_id).to eq("123")
203
207
  expect(refund.provider).to eq('api')
208
+ expect(refund.exemption_type).to eq('non_exempt')
204
209
  expect(refund.from_country).to eq('US')
205
210
  expect(refund.from_zip).to eq('93107')
206
211
  expect(refund.from_state).to eq('CA')
@@ -249,6 +254,7 @@ describe Taxjar::API::Refund do
249
254
  expect(refund.transaction_date).to eq("2015-05-14T00:00:00Z")
250
255
  expect(refund.transaction_reference_id).to eq("123")
251
256
  expect(refund.provider).to eq('api')
257
+ expect(refund.exemption_type).to eq('non_exempt')
252
258
  expect(refund.from_country).to eq('US')
253
259
  expect(refund.from_zip).to eq('93107')
254
260
  expect(refund.from_state).to eq('CA')
@@ -41,7 +41,7 @@ describe Taxjar::API::Request do
41
41
  it 'should return headers' do
42
42
  expect(subject).to respond_to(:headers)
43
43
  expect(subject.headers).to be_instance_of(Hash)
44
- expect(subject.headers[:user_agent]).to match('TaxjarRubyGem')
44
+ expect(subject.headers[:user_agent]).to match(/^TaxJar\/Ruby \(.+\) taxjar-ruby\/\d+\.\d+\.\d+$/)
45
45
  expect(subject.headers[:authorization]).to eq('Bearer AK')
46
46
  end
47
47
 
@@ -52,7 +52,7 @@ describe Taxjar::API::Request do
52
52
  subject = Taxjar::API::Request.new(client, :get, '/api_path', 'object')
53
53
  expect(subject).to respond_to(:headers)
54
54
  expect(subject.headers).to be_instance_of(Hash)
55
- expect(subject.headers[:user_agent]).to match('TaxjarRubyGem')
55
+ expect(subject.headers[:user_agent]).to match(/^TaxJar\/Ruby \(.+\) taxjar-ruby\/\d+\.\d+\.\d+$/)
56
56
  expect(subject.headers[:authorization]).to eq('Bearer AK')
57
57
  expect(subject.headers['X-TJ-Expected-Response']).to eq(422)
58
58
  end
@@ -124,8 +124,7 @@ describe Taxjar::API::Request do
124
124
  it "runs through the proxy" do
125
125
  stub_request(:get, "https://api.taxjar.com/api_path").
126
126
  with(:headers => {'Authorization'=>'Bearer AK', 'Connection'=>'close',
127
- 'Host'=>'api.taxjar.com',
128
- 'User-Agent'=>"TaxjarRubyGem/#{Taxjar::Version.to_s}"}).
127
+ 'Host'=>'api.taxjar.com'}).
129
128
  to_return(:status => 200, :body => '{"object": {"id": "3"}}',
130
129
  :headers => {content_type: 'application/json; charset=UTF-8'})
131
130
 
@@ -138,8 +137,7 @@ describe Taxjar::API::Request do
138
137
  it 'should return a body if no errors' do
139
138
  stub_request(:get, "https://api.taxjar.com/api_path").
140
139
  with(:headers => {'Authorization'=>'Bearer AK', 'Connection'=>'close',
141
- 'Host'=>'api.taxjar.com',
142
- 'User-Agent'=>"TaxjarRubyGem/#{Taxjar::Version.to_s}"}).
140
+ 'Host'=>'api.taxjar.com'}).
143
141
  to_return(:status => 200, :body => '{"object": {"id": "3"}}',
144
142
  :headers => {content_type: 'application/json; charset=UTF-8'})
145
143
 
@@ -159,8 +157,7 @@ describe Taxjar::API::Request do
159
157
  with(:body => "{\"city\":\"New York\"}",
160
158
  :headers => {'Authorization'=>'Bearer AK', 'Connection'=>'close',
161
159
  'Content-Type'=>'application/json; charset=UTF-8',
162
- 'Host'=>'api.taxjar.com',
163
- 'User-Agent'=>"TaxjarRubyGem/#{Taxjar::Version.to_s}"}).
160
+ 'Host'=>'api.taxjar.com'}).
164
161
  to_return(:status => 200, :body => '{"object": {"id": "3"}}',
165
162
  :headers => {content_type: 'application/json; charset=UTF-8'})
166
163
 
@@ -168,13 +165,40 @@ describe Taxjar::API::Request do
168
165
  end
169
166
  end
170
167
 
168
+ it 'handles unexpected Content-Type responses' do
169
+ stub_request(:get, "https://api.taxjar.com/api_path").
170
+ with(:headers => {'Authorization'=>'Bearer AK', 'Connection'=>'close',
171
+ 'Host'=>'api.taxjar.com'}).
172
+ to_return(:status => 200, :body => 'Something unexpected',
173
+ :headers => {content_type: 'text/html; charset=UTF-8'})
174
+
175
+ expect{subject.perform}.to raise_error(Taxjar::Error::ServerError)
176
+ end
177
+
178
+ [
179
+ HTTP::Error,
180
+ HTTP::ConnectionError,
181
+ HTTP::RequestError,
182
+ HTTP::ResponseError,
183
+ HTTP::StateError,
184
+ HTTP::TimeoutError,
185
+ HTTP::HeaderError
186
+ ].each do |http_error_class|
187
+ context "#{http_error_class}" do
188
+ it "is classified as a Taxjar::Error" do
189
+ stub_request(:get, "https://api.taxjar.com/api_path").to_raise(http_error_class)
190
+
191
+ expect{subject.perform}.to raise_error(Taxjar::Error)
192
+ end
193
+ end
194
+ end
195
+
171
196
  Taxjar::Error::ERRORS.each do |status, exception|
172
197
  context "when HTTP status is #{status}" do
173
198
  it "raises #{exception}" do
174
199
  stub_request(:get, "https://api.taxjar.com/api_path").
175
200
  with(:headers => {'Authorization'=>'Bearer AK', 'Connection'=>'close',
176
- 'Host'=>'api.taxjar.com',
177
- 'User-Agent'=>"TaxjarRubyGem/#{Taxjar::Version.to_s}"}).
201
+ 'Host'=>'api.taxjar.com'}).
178
202
  to_return(:status => status,
179
203
  :body => '{"error": "Not Acceptable",
180
204
  "detail": "error explanation",
@@ -185,5 +209,41 @@ describe Taxjar::API::Request do
185
209
  end
186
210
  end
187
211
  end
212
+
213
+ context "when HTTP status is 502" do
214
+ it "raises Taxjar::Error" do
215
+ stub_request(:get, "https://api.taxjar.com/api_path").
216
+ with(:headers => {'Authorization'=>'Bearer AK', 'Connection'=>'close',
217
+ 'Host'=>'api.taxjar.com'}).
218
+ to_return(:status => 502,
219
+ :body => '{}',
220
+ :headers => {content_type: 'application/json; charset=UTF-8'})
221
+
222
+ expect{subject.perform}.to raise_error(
223
+ an_instance_of(Taxjar::Error).and having_attributes({
224
+ "message" => "Bad Gateway",
225
+ "code" => 502
226
+ })
227
+ )
228
+ end
229
+ end
230
+
231
+ context "when HTTP status is 5xx" do
232
+ it "raises Taxjar::Error" do
233
+ stub_request(:get, "https://api.taxjar.com/api_path").
234
+ with(:headers => {'Authorization'=>'Bearer AK', 'Connection'=>'close',
235
+ 'Host'=>'api.taxjar.com'}).
236
+ to_return(:status => 509,
237
+ :body => '{}',
238
+ :headers => {content_type: 'application/json; charset=UTF-8'})
239
+
240
+ expect{subject.perform}.to raise_error(
241
+ an_instance_of(Taxjar::Error).and having_attributes({
242
+ "message" => "Unknown Error",
243
+ "code" => 509
244
+ })
245
+ )
246
+ end
247
+ end
188
248
  end
189
249
  end
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  describe Taxjar::Client do
4
- describe '#api_key?' do
4
+ describe '#api_key?' do
5
5
  it 'returns true if api_key is present' do
6
6
  client = Taxjar::Client.new(api_key: 'AK')
7
7
  expect(client.api_key?).to be true
@@ -25,14 +25,14 @@ describe Taxjar::Client do
25
25
  client.set_api_config('api_url', 'https://api.sandbox.taxjar.com')
26
26
  expect(client.api_url).to eq('https://api.sandbox.taxjar.com')
27
27
  end
28
-
28
+
29
29
  it 'sets new custom headers' do
30
30
  client = Taxjar::Client.new(api_key: 'AK')
31
31
  client.set_api_config('headers', { 'X-TJ-Expected-Response' => 422 })
32
32
  expect(client.headers).to eq({ 'X-TJ-Expected-Response' => 422 })
33
33
  end
34
34
  end
35
-
35
+
36
36
  describe "#get_api_config" do
37
37
  it 'gets a config value' do
38
38
  client = Taxjar::Client.new(api_key: 'AK')
@@ -44,7 +44,7 @@ describe Taxjar::Client do
44
44
  describe '#user_agent' do
45
45
  it 'returns string with version' do
46
46
  client = Taxjar::Client.new(api_key: 'AK')
47
- expect(client.user_agent).to eq("TaxjarRubyGem/#{Taxjar::Version}")
47
+ expect(client.user_agent).to match(/^TaxJar\/Ruby \(.+\) taxjar-ruby\/\d+\.\d+\.\d+$/)
48
48
  end
49
49
  end
50
50
  end