fortnox-api 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|