taxjar-ruby 2.5.0 → 3.0.2

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.
@@ -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