fortnox-api 0.4.0 → 0.5.0
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/README.md +31 -29
- data/bin/console +8 -0
- data/fortnox-api.gemspec +1 -3
- data/lib/fortnox/api.rb +49 -14
- data/lib/fortnox/api/circular_queue.rb +33 -0
- data/lib/fortnox/api/mappers.rb +1 -1
- data/lib/fortnox/api/mappers/base.rb +3 -0
- data/lib/fortnox/api/models/article.rb +25 -25
- data/lib/fortnox/api/models/customer.rb +47 -47
- data/lib/fortnox/api/models/document_base.rb +4 -4
- data/lib/fortnox/api/repositories/article.rb +3 -4
- data/lib/fortnox/api/repositories/base.rb +77 -5
- data/lib/fortnox/api/repositories/base/savers.rb +2 -2
- data/lib/fortnox/api/repositories/customer.rb +3 -4
- data/lib/fortnox/api/repositories/invoice.rb +3 -4
- data/lib/fortnox/api/repositories/order.rb +3 -4
- data/lib/fortnox/api/repositories/project.rb +3 -3
- data/lib/fortnox/api/types/document_row.rb +3 -3
- data/lib/fortnox/api/types/model.rb +2 -6
- data/lib/fortnox/api/version.rb +1 -1
- data/spec/fortnox/api/mappers/base_spec.rb +6 -0
- data/spec/fortnox/api/repositories/article_spec.rb +4 -0
- data/spec/fortnox/api/repositories/base_spec.rb +348 -0
- data/spec/fortnox/api/repositories/customer_spec.rb +4 -0
- data/spec/fortnox/api/repositories/invoice_spec.rb +4 -0
- data/spec/fortnox/api/repositories/order_spec.rb +4 -0
- data/spec/fortnox/api/repositories/project_spec.rb +4 -0
- data/spec/fortnox/api/types/examples/document_row.rb +2 -2
- data/spec/fortnox/api/types/house_work_types_spec.rb +4 -0
- data/spec/fortnox/api_spec.rb +161 -31
- data/spec/spec_helper.rb +7 -2
- data/spec/support/helpers/configuration_helper.rb +10 -0
- metadata +10 -27
- data/lib/fortnox/api/base.rb +0 -47
- data/lib/fortnox/api/class_methods.rb +0 -30
- data/lib/fortnox/api/environment_validation.rb +0 -78
- data/spec/fortnox/api/base_spec.rb +0 -167
- data/spec/support/helpers/environment_helper.rb +0 -7
@@ -44,13 +44,13 @@ module Fortnox
|
|
44
44
|
base.attribute :contribution_value, Types::Nullable::Float.with( read_only: true )
|
45
45
|
|
46
46
|
# Country Country for the document address.
|
47
|
-
base.attribute :country,
|
47
|
+
base.attribute :country, Types::CountryCode
|
48
48
|
|
49
49
|
# CostCenter Code of the cost center.
|
50
50
|
base.attribute :cost_center, Types::Nullable::String
|
51
51
|
|
52
52
|
# Currency Code of the currency.
|
53
|
-
base.attribute :currency,
|
53
|
+
base.attribute :currency, Types::Currency
|
54
54
|
|
55
55
|
# CurrencyRate Currency rate used for the document
|
56
56
|
base.attribute :currency_rate, Types::Nullable::Float
|
@@ -74,7 +74,7 @@ module Fortnox
|
|
74
74
|
base.attribute :delivery_city, Types::Sized::String[ 1024 ]
|
75
75
|
|
76
76
|
# DeliveryCountry Country for the document delivery address.
|
77
|
-
base.attribute :delivery_country,
|
77
|
+
base.attribute :delivery_country, Types::CountryCode
|
78
78
|
|
79
79
|
# DeliveryDate Date of delivery.
|
80
80
|
base.attribute :delivery_date, Types::Nullable::Date
|
@@ -98,7 +98,7 @@ module Fortnox
|
|
98
98
|
base.attribute :external_invoice_reference2, Types::Sized::String[ 80 ]
|
99
99
|
|
100
100
|
# Freight Freight cost of the document. 12 digits (incl. decimals)
|
101
|
-
base.attribute :freight, Types::Sized::Float[ 0.0, 99_999_999_999.
|
101
|
+
base.attribute :freight, Types::Sized::Float[ 0.0, 99_999_999_999.9 ]
|
102
102
|
|
103
103
|
# FreightVAT VAT of the freight cost.
|
104
104
|
base.attribute :freight_vat, Types::Nullable::Float.with( read_only: true )
|
@@ -5,10 +5,9 @@ require "fortnox/api/mappers/article"
|
|
5
5
|
module Fortnox
|
6
6
|
module API
|
7
7
|
module Repository
|
8
|
-
class Article <
|
9
|
-
|
10
|
-
|
11
|
-
MAPPER = Fortnox::API::Mapper::Article
|
8
|
+
class Article < Base
|
9
|
+
MODEL = Model::Article
|
10
|
+
MAPPER = Mapper::Article
|
12
11
|
URI = '/articles/'.freeze
|
13
12
|
end
|
14
13
|
end
|
@@ -1,24 +1,76 @@
|
|
1
|
-
require
|
1
|
+
require 'httparty'
|
2
|
+
require 'fortnox/api/request_handling'
|
2
3
|
require "fortnox/api/repositories/base/loaders"
|
3
4
|
require "fortnox/api/repositories/base/savers"
|
4
5
|
|
5
6
|
module Fortnox
|
6
7
|
module API
|
7
8
|
module Repository
|
8
|
-
class Base
|
9
|
-
|
9
|
+
class Base
|
10
|
+
include HTTParty
|
11
|
+
include Fortnox::API::RequestHandling
|
10
12
|
include Loaders
|
11
13
|
include Savers
|
12
14
|
|
15
|
+
HTTParty::Parser::SupportedFormats[ "text/html" ] = :json
|
16
|
+
|
17
|
+
DEFAULT_HEADERS = {
|
18
|
+
'Content-Type' => 'application/json',
|
19
|
+
'Accept' => 'application/json',
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
HTTP_METHODS = [ :get, :put, :post, :delete ].freeze
|
23
|
+
|
24
|
+
attr_accessor :headers
|
13
25
|
attr_reader :mapper, :keys_filtered_on_save
|
14
26
|
|
15
|
-
def
|
16
|
-
|
27
|
+
def self.set_headers( headers = {} )
|
28
|
+
self.headers.merge!( headers )
|
29
|
+
end
|
30
|
+
|
31
|
+
HTTP_METHODS.each do |method|
|
32
|
+
define_method method do |*args|
|
33
|
+
self.headers['Access-Token'] = next_access_token
|
34
|
+
execute do |remote|
|
35
|
+
remote.send( method, *args )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize( keys_filtered_on_save: [ :url ], token_store: :default )
|
41
|
+
self.class.base_uri( get_base_url )
|
42
|
+
|
43
|
+
self.headers = DEFAULT_HEADERS.merge({
|
44
|
+
'Client-Secret' => get_client_secret,
|
45
|
+
})
|
17
46
|
|
18
47
|
@keys_filtered_on_save = keys_filtered_on_save
|
48
|
+
@token_store = token_store
|
19
49
|
@mapper = Registry[ Mapper::Base.canonical_name_sym( self.class::MODEL )].new
|
20
50
|
end
|
21
51
|
|
52
|
+
def next_access_token
|
53
|
+
@access_tokens ||= CircularQueue.new( *get_access_tokens )
|
54
|
+
@access_tokens.next
|
55
|
+
end
|
56
|
+
|
57
|
+
def check_access_tokens!( tokens )
|
58
|
+
if tokens == nil or tokens.empty?
|
59
|
+
fail MissingConfiguration, "You have not provided any access tokens in token store #{ @token_store.inspect }."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_access_tokens
|
64
|
+
begin
|
65
|
+
tokens = config.token_store.fetch( @token_store )
|
66
|
+
rescue KeyError
|
67
|
+
token_store_not_found!( @token_store.inspect )
|
68
|
+
end
|
69
|
+
|
70
|
+
check_access_tokens!( tokens )
|
71
|
+
tokens
|
72
|
+
end
|
73
|
+
|
22
74
|
private
|
23
75
|
|
24
76
|
def instantiate( hash )
|
@@ -27,6 +79,26 @@ module Fortnox
|
|
27
79
|
self.class::MODEL.new( hash )
|
28
80
|
end
|
29
81
|
|
82
|
+
def get_base_url
|
83
|
+
base_url = config.base_url
|
84
|
+
fail MissingConfiguration, 'You have to provide a base url.' unless base_url
|
85
|
+
base_url
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_client_secret
|
89
|
+
client_secret = config.client_secret
|
90
|
+
fail MissingConfiguration, 'You have to provide your client secret.' unless client_secret
|
91
|
+
client_secret
|
92
|
+
end
|
93
|
+
|
94
|
+
def config
|
95
|
+
Fortnox::API.config
|
96
|
+
end
|
97
|
+
|
98
|
+
def token_store_not_found!( store_name )
|
99
|
+
fail MissingConfiguration,
|
100
|
+
"There is no token store named #{ store_name }."
|
101
|
+
end
|
30
102
|
end
|
31
103
|
end
|
32
104
|
end
|
@@ -19,13 +19,13 @@ module Fortnox
|
|
19
19
|
|
20
20
|
def save_new( entity )
|
21
21
|
execute_save( entity ) do |body|
|
22
|
-
post( self.class::URI, { body: body })
|
22
|
+
post( self.class::URI, { body: body } )
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
def update_existing( entity )
|
27
27
|
execute_save( entity ) do |body|
|
28
|
-
put( get_update_url_for( entity ), { body: body })
|
28
|
+
put( get_update_url_for( entity ), { body: body } )
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -5,10 +5,9 @@ require "fortnox/api/mappers/customer"
|
|
5
5
|
module Fortnox
|
6
6
|
module API
|
7
7
|
module Repository
|
8
|
-
class Customer <
|
9
|
-
|
10
|
-
|
11
|
-
MAPPER = Fortnox::API::Mapper::Customer
|
8
|
+
class Customer < Base
|
9
|
+
MODEL = Model::Customer
|
10
|
+
MAPPER = Mapper::Customer
|
12
11
|
URI = '/customers/'.freeze
|
13
12
|
end
|
14
13
|
end
|
@@ -5,10 +5,9 @@ require "fortnox/api/mappers/invoice"
|
|
5
5
|
module Fortnox
|
6
6
|
module API
|
7
7
|
module Repository
|
8
|
-
class Invoice <
|
9
|
-
|
10
|
-
|
11
|
-
MAPPER = Fortnox::API::Mapper::Invoice
|
8
|
+
class Invoice < Base
|
9
|
+
MODEL = Model::Invoice
|
10
|
+
MAPPER = Mapper::Invoice
|
12
11
|
URI = '/invoices/'.freeze
|
13
12
|
end
|
14
13
|
end
|
@@ -5,10 +5,9 @@ require "fortnox/api/mappers/order"
|
|
5
5
|
module Fortnox
|
6
6
|
module API
|
7
7
|
module Repository
|
8
|
-
class Order <
|
9
|
-
|
10
|
-
|
11
|
-
MAPPER = Fortnox::API::Mapper::Order
|
8
|
+
class Order < Base
|
9
|
+
MODEL = Model::Order
|
10
|
+
MAPPER = Mapper::Order
|
12
11
|
URI = '/orders/'.freeze
|
13
12
|
end
|
14
13
|
end
|
@@ -5,9 +5,9 @@ require "fortnox/api/mappers/project"
|
|
5
5
|
module Fortnox
|
6
6
|
module API
|
7
7
|
module Repository
|
8
|
-
class Project <
|
9
|
-
MODEL =
|
10
|
-
MAPPER =
|
8
|
+
class Project < Base
|
9
|
+
MODEL = Model::Project
|
10
|
+
MAPPER = Mapper::Project
|
11
11
|
URI = '/projects/'.freeze
|
12
12
|
end
|
13
13
|
end
|
@@ -20,7 +20,7 @@ module Fortnox
|
|
20
20
|
attribute :cost_center, Types::Nullable::String
|
21
21
|
|
22
22
|
#DeliveredQuantity Delivered quantity. 14 digits
|
23
|
-
attribute :delivered_quantity, Types::Sized::Float[ 0.0, 9_999_999_999_999.
|
23
|
+
attribute :delivered_quantity, Types::Sized::Float[ 0.0, 9_999_999_999_999.9 ]
|
24
24
|
|
25
25
|
#Description Description Row description. 50 characters
|
26
26
|
attribute :description, Types::Sized::String[ 50 ]
|
@@ -29,7 +29,7 @@ module Fortnox
|
|
29
29
|
# TODO(hannes): Verify that we can send in more than 5 digits through
|
30
30
|
# the actual API for DiscountType PERCENT. This cannot be done until
|
31
31
|
# we fix issue #62...
|
32
|
-
attribute :discount, Types::Sized::Float[ 0.0, 99_999_999_999.
|
32
|
+
attribute :discount, Types::Sized::Float[ 0.0, 99_999_999_999.9 ]
|
33
33
|
|
34
34
|
#DiscountType The type of discount used for the row.
|
35
35
|
attribute :discount_type, Types::DiscountType
|
@@ -44,7 +44,7 @@ module Fortnox
|
|
44
44
|
attribute :house_work_type, Types::HouseWorkType
|
45
45
|
|
46
46
|
#Price Price per unit. 12 digits
|
47
|
-
attribute :price, Types::Sized::Float[ 0.0, 99_999_999_999.
|
47
|
+
attribute :price, Types::Sized::Float[ 0.0, 99_999_999_999.9 ]
|
48
48
|
|
49
49
|
#Project Code of the project for the row.
|
50
50
|
attribute :project, Types::Nullable::String
|
@@ -24,14 +24,10 @@ module Fortnox
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def first_missing_required_key( attributes )
|
27
|
-
|
28
|
-
missing_required = all_missing_keys.select do |name|
|
27
|
+
missing_keys( attributes ).find do |name|
|
29
28
|
attribute = self.class.schema[ name ]
|
30
|
-
|
31
|
-
attribute.options[:required]
|
29
|
+
attribute.respond_to?(:options) && attribute.options[:required]
|
32
30
|
end
|
33
|
-
|
34
|
-
missing_required.first
|
35
31
|
end
|
36
32
|
end
|
37
33
|
|
data/lib/fortnox/api/version.rb
CHANGED
@@ -49,6 +49,12 @@ describe Fortnox::API::Mapper::Base do
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
describe 'array with very large int (Bigint if Ruby <2.4)' do
|
53
|
+
include_examples 'identity mapper', :array do
|
54
|
+
let( :value ){ [(100**10)] }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
52
58
|
describe 'advanced array' do
|
53
59
|
include_examples 'simple mapper', :array, [ "2016-01-01", "2016-01-02" ] do
|
54
60
|
let( :value ){ [ Date.new(2016, 1, 1), Date.new(2016, 1, 2) ] }
|
@@ -9,6 +9,10 @@ require 'fortnox/api/repositories/examples/save_with_specially_named_attribute'
|
|
9
9
|
require 'fortnox/api/repositories/examples/search'
|
10
10
|
|
11
11
|
describe Fortnox::API::Repository::Article, order: :defined, integration: true do
|
12
|
+
include Helpers::Configuration
|
13
|
+
|
14
|
+
before{ set_api_test_configuration }
|
15
|
+
|
12
16
|
subject(:repository){ described_class.new }
|
13
17
|
|
14
18
|
include_examples '.save',
|
@@ -0,0 +1,348 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fortnox/api'
|
3
|
+
|
4
|
+
describe Fortnox::API::Repository::Base do
|
5
|
+
using_test_class do
|
6
|
+
module Model
|
7
|
+
class Test
|
8
|
+
end
|
9
|
+
end
|
10
|
+
module Repository
|
11
|
+
class Test < Fortnox::API::Repository::Base
|
12
|
+
MODEL = Model::Test
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'dry/container/stub'
|
17
|
+
Fortnox::API::Registry.enable_stubs!
|
18
|
+
Fortnox::API::Registry.stub( :test, Model::Test )
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:access_token){ '3f08d038-f380-4893-94a0-a08f6e60e67a' }
|
22
|
+
let(:access_token2){ '89feajou-sif8-8f8u-29ja-xdfniokeniod' }
|
23
|
+
let(:client_secret){ 'P5K5vE3Kun' }
|
24
|
+
let(:repository){ Repository::Test.new }
|
25
|
+
let(:application_json){}
|
26
|
+
let(:headers) do
|
27
|
+
{
|
28
|
+
'Access-Token' => access_token,
|
29
|
+
'Client-Secret' => client_secret,
|
30
|
+
'Content-Type' => 'application/json',
|
31
|
+
'Accept' => 'application/json',
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'creation' do
|
36
|
+
shared_examples_for 'missing configuration' do
|
37
|
+
subject{ ->{ repository } }
|
38
|
+
|
39
|
+
let(:error){ Fortnox::API::MissingConfiguration }
|
40
|
+
|
41
|
+
it{ is_expected.to raise_error( error, /#{message}/ ) }
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'without base url' do
|
45
|
+
include_examples 'missing configuration' do
|
46
|
+
before{ Fortnox::API.configure{ |conf| conf.base_url = nil } }
|
47
|
+
let(:message){ 'have to provide a base url' }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'without client secret' do
|
52
|
+
include_examples 'missing configuration' do
|
53
|
+
before{ Fortnox::API.configure{ |conf| conf.client_secret = nil } }
|
54
|
+
let(:message){ 'have to provide your client secret' }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#next_access_token' do
|
60
|
+
before{ Fortnox::API.configure{ |conf| conf.client_secret = client_secret } }
|
61
|
+
|
62
|
+
context 'with default token store' do
|
63
|
+
context 'with one access token' do
|
64
|
+
subject{ repository.next_access_token }
|
65
|
+
|
66
|
+
before{ Fortnox::API.configure{ |conf| conf.access_token = access_token } }
|
67
|
+
it{ is_expected.to eql( access_token ) }
|
68
|
+
|
69
|
+
describe 'next request' do
|
70
|
+
before{ repository.next_access_token }
|
71
|
+
|
72
|
+
it 'still uses the same token' do
|
73
|
+
is_expected.to eql( access_token )
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'with multiple access tokens' do
|
79
|
+
subject{ access_tokens }
|
80
|
+
|
81
|
+
before{ Fortnox::API.configure{ |conf| conf.access_tokens = access_tokens } }
|
82
|
+
let( :access_tokens ){ [access_token, access_token2] }
|
83
|
+
|
84
|
+
it{ is_expected.to include( repository.next_access_token ) }
|
85
|
+
|
86
|
+
it 'changes token on next request' do
|
87
|
+
token1 = repository.next_access_token
|
88
|
+
token2 = repository.next_access_token
|
89
|
+
|
90
|
+
expect( token1 ).not_to eql( token2 )
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'circulates tokens' do
|
94
|
+
token1 = repository.next_access_token
|
95
|
+
repository.next_access_token
|
96
|
+
token3 = repository.next_access_token
|
97
|
+
|
98
|
+
expect( token1 ).to eql( token3 )
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'with multiple stores' do
|
104
|
+
subject{ repository.next_access_token }
|
105
|
+
|
106
|
+
before do
|
107
|
+
Fortnox::API.configure do |config|
|
108
|
+
config.access_tokens = { store1: access_token, store2: access_token2 }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'first token store' do
|
113
|
+
let( :repository ){ Repository::Test.new( token_store: :store1) }
|
114
|
+
|
115
|
+
it{ is_expected.to eql access_token }
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'second token store' do
|
119
|
+
let( :repository ){ Repository::Test.new( token_store: :store2 ) }
|
120
|
+
|
121
|
+
it{ is_expected.to eql access_token2 }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '#get_access_tokens' do
|
127
|
+
subject( :get_access_tokens ){ repository.get_access_tokens }
|
128
|
+
|
129
|
+
before{ Fortnox::API.configure{ |conf| conf.client_secret = client_secret } }
|
130
|
+
|
131
|
+
let( :token_store_not_present ){ "no token store named #{ token_store.inspect }." }
|
132
|
+
let( :error ){ Fortnox::API::MissingConfiguration }
|
133
|
+
|
134
|
+
context 'with non existing token store' do
|
135
|
+
subject{ ->{ get_access_tokens } }
|
136
|
+
|
137
|
+
before do
|
138
|
+
Fortnox::API.configure{ |conf| conf.access_tokens = { some_store: [access_token] } }
|
139
|
+
end
|
140
|
+
|
141
|
+
let( :repository ){ Repository::Test.new( token_store: token_store ) }
|
142
|
+
let( :token_store ){ :non_existing_store }
|
143
|
+
|
144
|
+
it{ is_expected.to raise_error( error, /#{token_store_not_present}/ ) }
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'with no tokens set' do
|
148
|
+
subject{ ->{ get_access_tokens } }
|
149
|
+
|
150
|
+
before{ Fortnox::API.configure{ |conf| conf.access_tokens = {} } }
|
151
|
+
let( :token_store ){ :default }
|
152
|
+
|
153
|
+
it{ is_expected.to raise_error( error, /#{token_store_not_present}/) }
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'with one access token in token store' do
|
157
|
+
before{ Fortnox::API.configure{ |conf| conf.access_token = access_token } }
|
158
|
+
let( :token_store ){ :default }
|
159
|
+
|
160
|
+
it{ is_expected.to eql( access_token ) }
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'with multiple access tokens' do
|
164
|
+
before do
|
165
|
+
Fortnox::API.configure{ |conf| conf.access_tokens = [access_token, access_token2] }
|
166
|
+
end
|
167
|
+
let( :token_store ){ :default }
|
168
|
+
|
169
|
+
it{ is_expected.to eql( [access_token, access_token2] ) }
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'with multiple token stores' do
|
173
|
+
before do
|
174
|
+
Fortnox::API.configure do |conf|
|
175
|
+
conf.access_tokens = { store_a: store_a_tokens, store_b: store_b_token }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
let( :store_a_tokens ){ ['token_a1', 'token_a2'] }
|
179
|
+
let( :store_b_token ){ 'token_b1' }
|
180
|
+
|
181
|
+
context 'with valid store name' do
|
182
|
+
let( :repository ){ Repository::Test.new( token_store: :store_a ) }
|
183
|
+
|
184
|
+
it{ is_expected.to eql( store_a_tokens ) }
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'with non collection' do
|
188
|
+
let( :repository ){ Repository::Test.new( token_store: :store_b ) }
|
189
|
+
|
190
|
+
it{ is_expected.to eql( store_b_token ) }
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'with invalid store name' do
|
194
|
+
subject{ ->{ get_access_tokens } }
|
195
|
+
|
196
|
+
let( :repository ){ Repository::Test.new( token_store: :nonsence_store ) }
|
197
|
+
|
198
|
+
it{ is_expected.to raise_error( error ) }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#check_access_tokens!' do
|
204
|
+
subject{ ->{ repository.check_access_tokens!(tokens) } }
|
205
|
+
|
206
|
+
before{ Fortnox::API.configure{ |conf| conf.client_secret = client_secret } }
|
207
|
+
let( :error ){ Fortnox::API::MissingConfiguration }
|
208
|
+
let( :message ){ "not provided any access tokens in token store #{ token_store.inspect }" }
|
209
|
+
let( :token_store ){ :default }
|
210
|
+
|
211
|
+
context 'with nil' do
|
212
|
+
let( :tokens ){ nil }
|
213
|
+
|
214
|
+
it{ is_expected.to raise_error( error, /#{message}/ ) }
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'with empty array' do
|
218
|
+
let( :tokens ){ [] }
|
219
|
+
|
220
|
+
it{ is_expected.to raise_error( error, /#{message}/ ) }
|
221
|
+
end
|
222
|
+
|
223
|
+
context 'with an empty, non default, token store' do
|
224
|
+
before{ Fortnox::API.configure{ |conf| conf.access_tokens = { token_store => tokens } } }
|
225
|
+
let( :repository ){ Repository::Test.new( token_store: token_store ) }
|
226
|
+
let( :tokens ){ [] }
|
227
|
+
let( :token_store ){ :store1 }
|
228
|
+
|
229
|
+
it{ is_expected.to raise_error( error, /#{message}/ ) }
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'with valid tokens' do
|
233
|
+
let( :tokens ){ ['12345', 'abcde'] }
|
234
|
+
|
235
|
+
it{ is_expected.not_to raise_error }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context 'when making a request including the proper headers' do
|
240
|
+
before do
|
241
|
+
Fortnox::API.configure do |conf|
|
242
|
+
conf.client_secret = client_secret
|
243
|
+
conf.access_token = access_token
|
244
|
+
end
|
245
|
+
|
246
|
+
stub_request(
|
247
|
+
:get,
|
248
|
+
'https://api.fortnox.se/3/test',
|
249
|
+
).with(
|
250
|
+
headers: headers
|
251
|
+
).to_return(
|
252
|
+
status: 200
|
253
|
+
)
|
254
|
+
end
|
255
|
+
|
256
|
+
subject{ repository.get( '/test', { body: '' } ) }
|
257
|
+
|
258
|
+
it{ is_expected.to be_nil }
|
259
|
+
end
|
260
|
+
|
261
|
+
describe 'making requests with multiple access tokens' do
|
262
|
+
before do
|
263
|
+
Fortnox::API.configure do |conf|
|
264
|
+
conf.client_secret = client_secret
|
265
|
+
conf.access_tokens = [access_token, access_token2]
|
266
|
+
end
|
267
|
+
|
268
|
+
stub_request(
|
269
|
+
:get,
|
270
|
+
'https://api.fortnox.se/3/test',
|
271
|
+
).with(
|
272
|
+
headers: {
|
273
|
+
'Access-Token' => access_token,
|
274
|
+
'Client-Secret' => client_secret,
|
275
|
+
'Content-Type' => 'application/json',
|
276
|
+
'Accept' => 'application/json',
|
277
|
+
}
|
278
|
+
).to_return(
|
279
|
+
status: 200,
|
280
|
+
body: '1'
|
281
|
+
)
|
282
|
+
|
283
|
+
stub_request(
|
284
|
+
:get,
|
285
|
+
'https://api.fortnox.se/3/test',
|
286
|
+
).with(
|
287
|
+
headers: {
|
288
|
+
'Access-Token' => access_token2,
|
289
|
+
'Client-Secret' => client_secret,
|
290
|
+
'Content-Type' => 'application/json',
|
291
|
+
'Accept' => 'application/json',
|
292
|
+
}
|
293
|
+
).to_return(
|
294
|
+
status: 200,
|
295
|
+
body: '2'
|
296
|
+
)
|
297
|
+
end
|
298
|
+
|
299
|
+
context 'with subsequent requests on same object' do
|
300
|
+
let!(:response1){ repository.get( '/test', { body: '' } ) }
|
301
|
+
let!(:response2){ repository.get( '/test', { body: '' } ) }
|
302
|
+
let!(:response3){ repository.get( '/test', { body: '' } ) }
|
303
|
+
|
304
|
+
# rubocop:disable RSpec/MultipleExpectations
|
305
|
+
# All these checks must be run in same it-statement because
|
306
|
+
# of the random starting index.
|
307
|
+
it 'works' do
|
308
|
+
expect(response1).not_to eq( response2 )
|
309
|
+
expect(response1).to eq( response3 )
|
310
|
+
expect(response3).not_to eq( response2 )
|
311
|
+
end
|
312
|
+
# rubocop:enable RSpec/MultipleExpectations
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
context 'when raising error from remote server' do
|
317
|
+
before do
|
318
|
+
Fortnox::API.configure do |conf|
|
319
|
+
conf.client_secret = client_secret
|
320
|
+
conf.access_tokens = [access_token, access_token2]
|
321
|
+
end
|
322
|
+
|
323
|
+
stub_request(
|
324
|
+
:post,
|
325
|
+
'https://api.fortnox.se/3/test',
|
326
|
+
).to_return(
|
327
|
+
status: 500,
|
328
|
+
body: { 'ErrorInformation' => { 'error' => 1, 'message' => 'Räkenskapsår finns inte upplagt. För att kunna skapa en faktura krävs det att det finns ett räkenskapsår' } }.to_json,
|
329
|
+
headers: { 'Content-Type' => 'application/json' },
|
330
|
+
)
|
331
|
+
end
|
332
|
+
|
333
|
+
subject{ ->{ repository.post( '/test', { body: '' } ) } }
|
334
|
+
|
335
|
+
it{ is_expected.to raise_error( Fortnox::API::RemoteServerError ) }
|
336
|
+
it{ is_expected.to raise_error( 'Räkenskapsår finns inte upplagt. För att kunna skapa en faktura krävs det att det finns ett räkenskapsår' ) }
|
337
|
+
|
338
|
+
context 'with debugging enabled' do
|
339
|
+
around do |example|
|
340
|
+
Fortnox::API.config.debugging = true
|
341
|
+
example.run
|
342
|
+
Fortnox::API.config.debugging = false
|
343
|
+
end
|
344
|
+
|
345
|
+
it{ is_expected.to raise_error( /\<HTTParty\:\:Request\:0x/ ) }
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|