fakturoid 0.5.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -10
- data/.ruby-version +1 -1
- data/CHANGELOG.md +11 -0
- data/README.md +501 -145
- data/Rakefile +3 -1
- data/fakturoid.gemspec +36 -25
- data/lib/fakturoid/api/account.rb +13 -0
- data/lib/fakturoid/api/bank_account.rb +13 -0
- data/lib/fakturoid/api/base.rb +18 -0
- data/lib/fakturoid/api/event.rb +23 -0
- data/lib/fakturoid/api/expense.rb +55 -0
- data/lib/fakturoid/api/expense_payment.rb +20 -0
- data/lib/fakturoid/api/generator.rb +36 -0
- data/lib/fakturoid/api/inbox_file.rb +34 -0
- data/lib/fakturoid/api/inventory_item.rb +66 -0
- data/lib/fakturoid/api/inventory_move.rb +40 -0
- data/lib/fakturoid/api/invoice.rb +62 -0
- data/lib/fakturoid/api/invoice_message.rb +14 -0
- data/lib/fakturoid/api/invoice_payment.rb +26 -0
- data/lib/fakturoid/api/number_format.rb +13 -0
- data/lib/fakturoid/api/recurring_generator.rb +36 -0
- data/lib/fakturoid/api/subject.rb +42 -0
- data/lib/fakturoid/api/todo.rb +20 -0
- data/lib/fakturoid/api/user.rb +17 -0
- data/lib/fakturoid/api.rb +84 -9
- data/lib/fakturoid/client.rb +46 -12
- data/lib/fakturoid/config.rb +69 -12
- data/lib/fakturoid/oauth/access_token_service.rb +46 -0
- data/lib/fakturoid/oauth/credentials.rb +63 -0
- data/lib/fakturoid/oauth/flow/authorization_code.rb +53 -0
- data/lib/fakturoid/oauth/flow/base.rb +42 -0
- data/lib/fakturoid/oauth/flow/client_credentials.rb +27 -0
- data/lib/fakturoid/oauth/flow.rb +5 -0
- data/lib/fakturoid/oauth/request/api.rb +11 -0
- data/lib/fakturoid/oauth/request/base.rb +60 -0
- data/lib/fakturoid/oauth/request/oauth.rb +24 -0
- data/lib/fakturoid/oauth/request.rb +5 -0
- data/lib/fakturoid/oauth/token_response.rb +56 -0
- data/lib/fakturoid/oauth.rb +39 -0
- data/lib/fakturoid/response.rb +8 -20
- data/lib/fakturoid/utils.rb +27 -0
- data/lib/fakturoid/version.rb +1 -1
- data/lib/fakturoid.rb +22 -22
- metadata +47 -53
- data/.github/workflows/rubocop.yml +0 -20
- data/.github/workflows/tests.yml +0 -27
- data/.gitignore +0 -7
- data/Gemfile +0 -6
- data/lib/fakturoid/api/arguments.rb +0 -21
- data/lib/fakturoid/api/http_methods.rb +0 -23
- data/lib/fakturoid/client/account.rb +0 -11
- data/lib/fakturoid/client/bank_account.rb +0 -11
- data/lib/fakturoid/client/event.rb +0 -19
- data/lib/fakturoid/client/expense.rb +0 -49
- data/lib/fakturoid/client/generator.rb +0 -44
- data/lib/fakturoid/client/inventory_items.rb +0 -59
- data/lib/fakturoid/client/inventory_moves.rb +0 -36
- data/lib/fakturoid/client/invoice.rb +0 -73
- data/lib/fakturoid/client/number_format.rb +0 -11
- data/lib/fakturoid/client/subject.rb +0 -41
- data/lib/fakturoid/client/todo.rb +0 -18
- data/lib/fakturoid/client/user.rb +0 -20
- data/lib/fakturoid/connection.rb +0 -30
- data/lib/fakturoid/request.rb +0 -31
- data/test/api_test.rb +0 -24
- data/test/config_test.rb +0 -40
- data/test/fixtures/blocked_account.json +0 -8
- data/test/fixtures/invoice.json +0 -81
- data/test/fixtures/invoice.pdf +0 -0
- data/test/fixtures/invoice_error.json +0 -7
- data/test/fixtures/subjects.json +0 -52
- data/test/request_test.rb +0 -20
- data/test/response_test.rb +0 -189
- data/test/test_helper.rb +0 -19
data/lib/fakturoid/api.rb
CHANGED
@@ -1,19 +1,94 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative "api/base"
|
4
|
+
|
5
|
+
# Sorted alphabetically
|
6
|
+
require_relative "api/account"
|
7
|
+
require_relative "api/bank_account"
|
8
|
+
require_relative "api/event"
|
9
|
+
require_relative "api/expense"
|
10
|
+
require_relative "api/expense_payment"
|
11
|
+
require_relative "api/generator"
|
12
|
+
require_relative "api/inbox_file"
|
13
|
+
require_relative "api/inventory_item"
|
14
|
+
require_relative "api/inventory_move"
|
15
|
+
require_relative "api/invoice"
|
16
|
+
require_relative "api/invoice_message"
|
17
|
+
require_relative "api/invoice_payment"
|
18
|
+
require_relative "api/number_format"
|
19
|
+
require_relative "api/recurring_generator"
|
20
|
+
require_relative "api/subject"
|
21
|
+
require_relative "api/todo"
|
22
|
+
require_relative "api/user"
|
5
23
|
|
6
24
|
module Fakturoid
|
7
|
-
|
8
|
-
|
9
|
-
|
25
|
+
module Api
|
26
|
+
def account
|
27
|
+
@account ||= Account.new(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def bank_accounts
|
31
|
+
@bank_accounts ||= BankAccount.new(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def events
|
35
|
+
@events ||= Event.new(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def expenses
|
39
|
+
@expenses ||= Expense.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def expense_payments
|
43
|
+
@expense_payments ||= ExpensePayment.new(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def generators
|
47
|
+
@generators ||= Generator.new(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def inbox_files
|
51
|
+
@inbox_files ||= InboxFile.new(self)
|
52
|
+
end
|
53
|
+
|
54
|
+
def inventory_items
|
55
|
+
@inventory_items ||= InventoryItem.new(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
def inventory_moves
|
59
|
+
@inventory_moves ||= InventoryMove.new(self)
|
60
|
+
end
|
61
|
+
|
62
|
+
def invoices
|
63
|
+
@invoices ||= Invoice.new(self)
|
64
|
+
end
|
65
|
+
|
66
|
+
def invoice_messages
|
67
|
+
@invoice_messages ||= InvoiceMessage.new(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
def invoice_payments
|
71
|
+
@invoice_payments ||= InvoicePayment.new(self)
|
72
|
+
end
|
73
|
+
|
74
|
+
def number_formats
|
75
|
+
@number_formats ||= NumberFormat.new(self)
|
76
|
+
end
|
77
|
+
|
78
|
+
def recurring_generators
|
79
|
+
@recurring_generators ||= RecurringGenerator.new(self)
|
80
|
+
end
|
81
|
+
|
82
|
+
def subjects
|
83
|
+
@subjects ||= Subject.new(self)
|
84
|
+
end
|
10
85
|
|
11
|
-
def
|
12
|
-
@
|
86
|
+
def todos
|
87
|
+
@todos ||= Todo.new(self)
|
13
88
|
end
|
14
89
|
|
15
|
-
def
|
16
|
-
@
|
90
|
+
def users
|
91
|
+
@users ||= User.new(self)
|
17
92
|
end
|
18
93
|
end
|
19
94
|
end
|
data/lib/fakturoid/client.rb
CHANGED
@@ -1,14 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
module Fakturoid
|
4
|
+
class Client
|
5
|
+
extend Forwardable
|
6
|
+
include Api
|
7
|
+
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
# Authorization methods
|
11
|
+
def_delegators :@oauth, :authorization_uri, :authorize, :revoke_access, :perform_request
|
12
|
+
|
13
|
+
def self.configure(&block)
|
14
|
+
@config ||= Fakturoid::Config.new(&block) # rubocop:disable Naming/MemoizedInstanceVariableName
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.config
|
18
|
+
@config
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(config = {})
|
22
|
+
raise ConfigurationError, "Configuration is missing" if self.class.config.nil?
|
23
|
+
|
24
|
+
@config = self.class.config.duplicate(config)
|
25
|
+
@oauth = Oauth.new(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def account=(account)
|
29
|
+
config.account = account
|
30
|
+
end
|
31
|
+
|
32
|
+
def credentials
|
33
|
+
config.credentials
|
34
|
+
end
|
35
|
+
|
36
|
+
def credentials=(values)
|
37
|
+
config.credentials = values
|
38
|
+
end
|
39
|
+
|
40
|
+
def credentials_updated_callback(&block)
|
41
|
+
config.credentials_updated_callback = block
|
42
|
+
end
|
43
|
+
|
44
|
+
def call_credentials_updated_callback
|
45
|
+
config.credentials_updated_callback&.call(config.credentials)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/fakturoid/config.rb
CHANGED
@@ -2,39 +2,96 @@
|
|
2
2
|
|
3
3
|
module Fakturoid
|
4
4
|
class Config
|
5
|
-
attr_accessor :email, :
|
5
|
+
attr_accessor :email, :account, :client_id, :client_secret, :oauth_flow, :redirect_uri, :credentials_updated_callback
|
6
6
|
attr_writer :user_agent
|
7
7
|
|
8
|
-
|
8
|
+
SUPPORTED_FLOWS = %w[authorization_code client_credentials].freeze
|
9
|
+
API_ENDPOINT = "https://app.fakturoid.cz/api/v3"
|
10
|
+
# API_ENDPOINT = "http://app.fakturoid.localhost/api/v3" # For development purposes
|
11
|
+
OAUTH_ENDPOINT = "#{API_ENDPOINT}/oauth".freeze
|
9
12
|
|
10
|
-
def initialize
|
13
|
+
def initialize
|
11
14
|
yield self
|
15
|
+
|
16
|
+
validate_configuration
|
17
|
+
end
|
18
|
+
|
19
|
+
def credentials
|
20
|
+
@credentials ||= Oauth::Credentials.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def credentials=(values)
|
24
|
+
@credentials = values.is_a?(Hash) ? Oauth::Credentials.new(values) : values
|
12
25
|
end
|
13
26
|
|
14
27
|
def user_agent
|
15
|
-
if
|
28
|
+
if Utils.empty?(@user_agent)
|
16
29
|
"Fakturoid ruby gem (#{email})"
|
17
30
|
else
|
18
31
|
@user_agent
|
19
32
|
end
|
20
33
|
end
|
21
34
|
|
22
|
-
def
|
23
|
-
"
|
35
|
+
def api_endpoint
|
36
|
+
raise ConfigurationError, "Account slug is required" if Utils.empty?(account)
|
37
|
+
|
38
|
+
"#{API_ENDPOINT}/accounts/#{account}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def api_endpoint_without_account
|
42
|
+
API_ENDPOINT
|
43
|
+
end
|
44
|
+
|
45
|
+
def oauth_endpoint
|
46
|
+
OAUTH_ENDPOINT
|
24
47
|
end
|
25
48
|
|
26
|
-
def
|
27
|
-
|
49
|
+
def authorization_uri(state: nil)
|
50
|
+
params = {
|
51
|
+
client_id: client_id,
|
52
|
+
redirect_uri: redirect_uri,
|
53
|
+
response_type: "code"
|
54
|
+
}
|
55
|
+
params[:state] = state unless Utils.empty?(state)
|
56
|
+
|
57
|
+
connection = Faraday::Connection.new(oauth_endpoint)
|
58
|
+
connection.build_url(nil, params)
|
28
59
|
end
|
29
60
|
|
30
|
-
def
|
31
|
-
|
61
|
+
def access_token_auth_header
|
62
|
+
"#{credentials.token_type} #{credentials.access_token}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def authorization_code_flow?
|
66
|
+
oauth_flow == "authorization_code"
|
67
|
+
end
|
68
|
+
|
69
|
+
def client_credentials_flow?
|
70
|
+
oauth_flow == "client_credentials"
|
71
|
+
end
|
72
|
+
|
73
|
+
# We can create multiple instances of the client, make sure we isolate the config for each
|
74
|
+
# as it contains credentials which must not be shared.
|
75
|
+
def duplicate(new_config)
|
76
|
+
self.class.new do |config|
|
77
|
+
config.email = email
|
78
|
+
config.account = new_config[:account] || account
|
79
|
+
config.user_agent = user_agent
|
80
|
+
config.client_id = client_id
|
81
|
+
config.client_secret = client_secret
|
82
|
+
config.oauth_flow = oauth_flow # 'client_credentials', 'authorization_code'
|
83
|
+
# only authorization_code
|
84
|
+
config.redirect_uri = redirect_uri
|
85
|
+
end
|
32
86
|
end
|
33
87
|
|
34
88
|
private
|
35
89
|
|
36
|
-
def
|
37
|
-
|
90
|
+
def validate_configuration
|
91
|
+
raise ConfigurationError, "Missing or unsupported OAuth flow, supported flows are - `authorization_code`, `client_credentials`" unless SUPPORTED_FLOWS.include?(oauth_flow)
|
92
|
+
raise ConfigurationError, "`email` or `user` agent is required" if Utils.empty?(email) && Utils.empty?(user_agent)
|
93
|
+
raise ConfigurationError, "Client credentials are required" if Utils.empty?(client_id) || Utils.empty?(client_secret)
|
94
|
+
raise ConfigurationError, "`redirect_uri` is required for Authorization Code Flow" if authorization_code_flow? && Utils.empty?(redirect_uri)
|
38
95
|
end
|
39
96
|
end
|
40
97
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fakturoid
|
4
|
+
class Oauth
|
5
|
+
class AccessTokenService
|
6
|
+
attr_reader :oauth, :client
|
7
|
+
|
8
|
+
def initialize(oauth)
|
9
|
+
@oauth = oauth
|
10
|
+
@client = oauth.client
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform_request(method, path, params)
|
14
|
+
check_access_token
|
15
|
+
fetch_access_token if client.config.credentials.access_token_expired?
|
16
|
+
|
17
|
+
retried = false
|
18
|
+
|
19
|
+
begin
|
20
|
+
Request::Api.new(method, path, client).call(params)
|
21
|
+
rescue AuthenticationError
|
22
|
+
raise if retried
|
23
|
+
retried = true
|
24
|
+
fetch_access_token
|
25
|
+
|
26
|
+
retry
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def check_access_token
|
33
|
+
raise ArgumentError, "OAuth access was not authorized by user" unless oauth.authorized?
|
34
|
+
return unless Utils.empty?(client.config.credentials.access_token)
|
35
|
+
|
36
|
+
fetch_access_token
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_access_token
|
40
|
+
oauth.fetch_access_token.tap do
|
41
|
+
client.call_credentials_updated_callback
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fakturoid
|
4
|
+
class Oauth
|
5
|
+
class Credentials
|
6
|
+
EXPIRY_BUFFER_IN_SECONDS = 10
|
7
|
+
MAX_EXPIRY_IN_SECONDS = 2 * 3600 # 2 hours
|
8
|
+
|
9
|
+
attr_accessor :access_token, :refresh_token, :token_type
|
10
|
+
attr_reader :expires_at
|
11
|
+
|
12
|
+
def initialize(values = {})
|
13
|
+
update(values)
|
14
|
+
end
|
15
|
+
|
16
|
+
def update(values)
|
17
|
+
values = values.transform_keys(&:to_sym)
|
18
|
+
|
19
|
+
self.access_token = values[:access_token]
|
20
|
+
self.refresh_token = values[:refresh_token] unless Utils.empty?(values[:refresh_token])
|
21
|
+
self.expires_at = values[:expires_at] || values[:expires_in]
|
22
|
+
self.token_type ||= values[:token_type]
|
23
|
+
end
|
24
|
+
|
25
|
+
def expires_at=(value)
|
26
|
+
@expires_at = parse_expires_at(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def expires_in=(value)
|
30
|
+
self.expires_at = value
|
31
|
+
end
|
32
|
+
|
33
|
+
def access_token_expired?
|
34
|
+
Time.now > (expires_at - EXPIRY_BUFFER_IN_SECONDS)
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_json
|
38
|
+
{
|
39
|
+
access_token: access_token,
|
40
|
+
refresh_token: refresh_token,
|
41
|
+
expires_at: expires_at.to_datetime, # `DateTime` serializes into is8601, `Time` doesn't, so it can be saved as JSON safely.
|
42
|
+
token_type: token_type
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def parse_expires_at(value)
|
49
|
+
case value
|
50
|
+
when DateTime
|
51
|
+
value.to_time
|
52
|
+
when String
|
53
|
+
Time.parse(value)
|
54
|
+
when Integer # `value` in seconds
|
55
|
+
raise ArgumentError, "`expires_at` cannot be unix timestamp (was #{value.inspect})" if value > MAX_EXPIRY_IN_SECONDS
|
56
|
+
Time.now + value
|
57
|
+
else
|
58
|
+
value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fakturoid
|
4
|
+
class Oauth
|
5
|
+
module Flow
|
6
|
+
class AuthorizationCode
|
7
|
+
include Base
|
8
|
+
|
9
|
+
GRANT_TYPE = "authorization_code"
|
10
|
+
|
11
|
+
def authorization_uri(state: nil)
|
12
|
+
client.config.authorization_uri(state: state)
|
13
|
+
end
|
14
|
+
|
15
|
+
def authorize(code:)
|
16
|
+
payload = {
|
17
|
+
grant_type: GRANT_TYPE,
|
18
|
+
redirect_uri: client.config.redirect_uri,
|
19
|
+
code: code
|
20
|
+
}
|
21
|
+
|
22
|
+
response = perform_request(HTTP_POST, "token.json", payload: payload)
|
23
|
+
client.config.credentials.update(response.body)
|
24
|
+
client.call_credentials_updated_callback
|
25
|
+
response
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_access_token
|
29
|
+
payload = {
|
30
|
+
grant_type: "refresh_token",
|
31
|
+
refresh_token: client.config.credentials.refresh_token
|
32
|
+
}
|
33
|
+
|
34
|
+
response = perform_request(HTTP_POST, "token.json", payload: payload)
|
35
|
+
client.config.credentials.update(response.body)
|
36
|
+
response
|
37
|
+
end
|
38
|
+
|
39
|
+
def revoke_access
|
40
|
+
payload = {
|
41
|
+
token: client.config.credentials.refresh_token
|
42
|
+
}
|
43
|
+
|
44
|
+
perform_request(HTTP_POST, "revoke.json", payload: payload)
|
45
|
+
end
|
46
|
+
|
47
|
+
def authorized?
|
48
|
+
!Utils.empty?(client.config.credentials.refresh_token)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fakturoid
|
4
|
+
class Oauth
|
5
|
+
module Flow
|
6
|
+
module Base
|
7
|
+
attr_reader :client
|
8
|
+
|
9
|
+
def initialize(client)
|
10
|
+
@client = client
|
11
|
+
end
|
12
|
+
|
13
|
+
def authorization_uri(state: nil)
|
14
|
+
raise NotImplementedError, "Authorization path is not supported"
|
15
|
+
end
|
16
|
+
|
17
|
+
def authorize(code:)
|
18
|
+
raise NotImplementedError, "Authorize is not supported"
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch_access_token
|
22
|
+
raise NotImplementedError, "Fetch access token is not supported"
|
23
|
+
end
|
24
|
+
|
25
|
+
def revoke_access
|
26
|
+
raise NotImplementedError, "Revoke access is not supported"
|
27
|
+
end
|
28
|
+
|
29
|
+
def authorized?
|
30
|
+
raise NotImplementedError, "Authorized is not supported"
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def perform_request(method, path, params)
|
36
|
+
raw_response = Request::Oauth.new(method, path, client).call(params)
|
37
|
+
TokenResponse.new(raw_response)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fakturoid
|
4
|
+
class Oauth
|
5
|
+
module Flow
|
6
|
+
class ClientCredentials
|
7
|
+
include Base
|
8
|
+
|
9
|
+
GRANT_TYPE = "client_credentials"
|
10
|
+
|
11
|
+
def fetch_access_token
|
12
|
+
payload = {
|
13
|
+
grant_type: GRANT_TYPE
|
14
|
+
}
|
15
|
+
|
16
|
+
response = perform_request(HTTP_POST, "token.json", payload: payload)
|
17
|
+
client.config.credentials.update(response.body)
|
18
|
+
response
|
19
|
+
end
|
20
|
+
|
21
|
+
def authorized?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fakturoid
|
4
|
+
class Oauth
|
5
|
+
module Request
|
6
|
+
module Base
|
7
|
+
attr_reader :method, :path, :client
|
8
|
+
|
9
|
+
HTTP_METHODS = [:get, :post, :patch, :delete].freeze
|
10
|
+
REQUEST_TIMEOUT = 10
|
11
|
+
|
12
|
+
def initialize(method, path, client)
|
13
|
+
@method = method
|
14
|
+
@path = path
|
15
|
+
@client = client
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(params = {})
|
19
|
+
raise ArgumentError, "Unknown http method: #{method}" unless HTTP_METHODS.include?(method.to_sym)
|
20
|
+
|
21
|
+
request_params = params[:request_params] || {}
|
22
|
+
|
23
|
+
http_connection = connection(params)
|
24
|
+
|
25
|
+
http_connection.send(method) do |req|
|
26
|
+
req.url path, request_params
|
27
|
+
req.body = MultiJson.dump(params[:payload]) if params.key?(:payload)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def default_options(options = {})
|
34
|
+
{
|
35
|
+
headers: {
|
36
|
+
content_type: "application/json",
|
37
|
+
accept: "application/json",
|
38
|
+
user_agent: client.config.user_agent
|
39
|
+
},
|
40
|
+
url: options[:url] || endpoint,
|
41
|
+
request: {
|
42
|
+
timeout: REQUEST_TIMEOUT
|
43
|
+
}
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def endpoint
|
48
|
+
client.config.api_endpoint
|
49
|
+
end
|
50
|
+
|
51
|
+
def connection(options = {})
|
52
|
+
Faraday.new default_options(options) do |conn|
|
53
|
+
conn.headers[:authorization] = client.config.access_token_auth_header
|
54
|
+
conn.adapter Faraday.default_adapter
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fakturoid
|
4
|
+
class Oauth
|
5
|
+
module Request
|
6
|
+
class Oauth
|
7
|
+
include Base
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def connection(options = {})
|
12
|
+
Faraday.new default_options(options) do |conn|
|
13
|
+
conn.set_basic_auth(client.config.client_id, client.config.client_secret)
|
14
|
+
conn.adapter Faraday.default_adapter
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def endpoint
|
19
|
+
client.config.oauth_endpoint
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fakturoid
|
4
|
+
class Oauth
|
5
|
+
class TokenResponse
|
6
|
+
attr_reader :client, :response, :body
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
@response = response
|
10
|
+
@body = MultiJson.load(response.env.body) unless Utils.empty?(response.env.body)
|
11
|
+
|
12
|
+
handle_response
|
13
|
+
end
|
14
|
+
|
15
|
+
def status_code
|
16
|
+
response.env["status"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def refresh_token
|
20
|
+
body["refresh_token"]
|
21
|
+
end
|
22
|
+
|
23
|
+
def access_token
|
24
|
+
body["access_token"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def expires_in
|
28
|
+
body["expires_in"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def token_type
|
32
|
+
body["token_type"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
"#<#{self.class.name}:#{object_id} @body=\"#{body}\" @status_code=\"#{status_code}\">"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def handle_response
|
42
|
+
case status_code
|
43
|
+
when 400 then raise error(OauthError, "OAuth request failed")
|
44
|
+
when 401 then raise error(AuthenticationError, "OAuth authentication failed")
|
45
|
+
else
|
46
|
+
raise error(ServerError, "Server error") if status_code >= 500
|
47
|
+
raise error(ClientError, "Client error") if status_code >= 400
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def error(klass, message = nil)
|
52
|
+
klass.new message, status_code, body
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|