fakturoid 0.4.0 → 1.0.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/.rubocop.yml +10 -11
- data/.ruby-version +1 -1
- data/CHANGELOG.md +15 -0
- data/README.md +597 -107
- 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 -10
- 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 +48 -51
- 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/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,12 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|