finapps_core 5.0.5 → 5.0.10

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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +9 -8
  3. data/.rubocop.yml +131 -74
  4. data/.tmuxinator.yml +20 -0
  5. data/.travis.yml +5 -6
  6. data/RELEASES.md +8 -0
  7. data/finapps_core.gemspec +10 -10
  8. data/lib/finapps_core.rb +1 -0
  9. data/lib/finapps_core/middleware/middleware.rb +7 -3
  10. data/lib/finapps_core/middleware/request/accept_json.rb +2 -1
  11. data/lib/finapps_core/middleware/request/user_agent.rb +1 -1
  12. data/lib/finapps_core/middleware/request/x_consumer_id.rb +20 -0
  13. data/lib/finapps_core/middleware/response/raise_error.rb +36 -7
  14. data/lib/finapps_core/rest/base_client.rb +18 -30
  15. data/lib/finapps_core/rest/configuration.rb +17 -4
  16. data/lib/finapps_core/rest/connection.rb +31 -21
  17. data/lib/finapps_core/rest/defaults.rb +1 -1
  18. data/lib/finapps_core/utils/validatable.rb +1 -1
  19. data/lib/finapps_core/version.rb +1 -1
  20. data/spec/core_extensions/object/is_integer_spec.rb +6 -7
  21. data/spec/middleware/request/accept_json_spec.rb +7 -3
  22. data/spec/middleware/request/no_encoding_basic_authentication_spec.rb +15 -6
  23. data/spec/middleware/request/request_id_spec.rb +4 -4
  24. data/spec/middleware/request/tenant_authentication_spec.rb +21 -14
  25. data/spec/middleware/request/user_agent_spec.rb +8 -3
  26. data/spec/middleware/request/x_consumer_id_spec.rb +16 -0
  27. data/spec/middleware/response/raise_error_spec.rb +47 -15
  28. data/spec/rest/base_client_spec.rb +87 -43
  29. data/spec/rest/configuration_spec.rb +25 -18
  30. data/spec/rest/credentials_spec.rb +4 -4
  31. data/spec/rest/defaults_spec.rb +1 -1
  32. data/spec/rest/resources_spec.rb +10 -20
  33. data/spec/spec_helper.rb +3 -3
  34. data/spec/utils/validatable_spec.rb +9 -8
  35. metadata +69 -64
@@ -31,7 +31,8 @@ module FinAppsCore
31
31
  end
32
32
 
33
33
  # Performs HTTP GET, POST, UPDATE and DELETE requests.
34
- # You shouldn't need to use this method directly, but it can be useful for debugging.
34
+ # You shouldn't need to use this method directly,
35
+ # but it can be useful for debugging.
35
36
  # Returns a hash obtained from parsing the JSON object in the response body.
36
37
  #
37
38
  # @param [String] path
@@ -42,7 +43,7 @@ module FinAppsCore
42
43
  not_blank(path, :path)
43
44
  not_blank(method, :method)
44
45
 
45
- response, error_messages = execute_request(path, method, params)
46
+ response, error_messages = execute_request(method, path, params)
46
47
  result = block_given? ? yield(response) : response_body(response)
47
48
 
48
49
  [result, error_messages]
@@ -76,26 +77,19 @@ module FinAppsCore
76
77
  end
77
78
  end
78
79
 
79
- def execute_request(path, method, params)
80
- response = nil
81
- errors = []
82
-
83
- begin
84
- response = execute_method path, method, params
85
- rescue FinAppsCore::InvalidArgumentsError,
86
- FinAppsCore::MissingArgumentsError,
87
- Faraday::Error::ConnectionFailed => e
88
- handle_error e
89
- rescue Faraday::Error::ClientError => e
90
- errors = handle_client_error(e)
91
- end
92
-
93
- [response, errors]
80
+ def execute_request(method, path, params)
81
+ [send(method, path, params), []]
82
+ rescue FinAppsCore::InvalidArgumentsError,
83
+ FinAppsCore::MissingArgumentsError,
84
+ Faraday::ConnectionFailed => e
85
+ [nil, handle_error(e)]
86
+ rescue Faraday::ClientError => e
87
+ [nil, handle_client_error(e)]
94
88
  end
95
89
 
96
90
  def handle_error(error)
97
91
  logger.fatal "#{self.class}##{__method__} => #{error}"
98
- raise error
92
+ fail error
99
93
  end
100
94
 
101
95
  def handle_client_error(error)
@@ -103,19 +97,13 @@ module FinAppsCore
103
97
  error.response && error.response[:error_messages] ? error.response[:error_messages] : [error.message]
104
98
  end
105
99
 
106
- def execute_method(path, method, params)
107
- case method
108
- when :get
109
- get(path)
110
- when :post
111
- post(path, params)
112
- when :put
113
- put(path, params)
114
- when :delete
115
- delete(path, params)
116
- else
117
- raise FinAppsCore::UnsupportedHttpMethodError, "Method not supported: #{method}."
100
+ def execute_method(method, path, params)
101
+ unless %i(get post put delete).include?(method)
102
+ fail FinAppsCore::UnsupportedHttpMethodError,
103
+ "Method not supported: #{method}."
118
104
  end
105
+
106
+ send(method, path, params)
119
107
  end
120
108
  end
121
109
  end
@@ -8,14 +8,13 @@ module FinAppsCore
8
8
 
9
9
  attr_accessor :tenant_token, :user_identifier, :user_token,
10
10
  :host, :proxy, :timeout, :retry_limit, :rashify,
11
- :log_level, :request_id
11
+ :log_level, :request_id, :consumer_id
12
12
 
13
13
  def initialize(options = {})
14
14
  FinAppsCore::REST::Defaults::DEFAULTS.merge(remove_empty_options(options))
15
15
  .each {|key, value| public_send("#{key}=", value) }
16
-
17
- raise FinAppsCore::InvalidArgumentsError, "Invalid argument. {host: #{host}}" unless valid_host?
18
- raise FinAppsCore::InvalidArgumentsError, "Invalid argument. {timeout: #{timeout}}" unless timeout.integer?
16
+ fail_invalid_host
17
+ fail_invalid_timeout
19
18
  end
20
19
 
21
20
  def valid_user_credentials?
@@ -24,6 +23,20 @@ module FinAppsCore
24
23
 
25
24
  private
26
25
 
26
+ def fail_invalid_host
27
+ return if valid_host?
28
+
29
+ fail FinAppsCore::InvalidArgumentsError,
30
+ "Invalid argument. {host: #{host}}"
31
+ end
32
+
33
+ def fail_invalid_timeout
34
+ return if timeout.integer?
35
+
36
+ fail FinAppsCore::InvalidArgumentsError,
37
+ "Invalid argument. {timeout: #{timeout}}"
38
+ end
39
+
27
40
  def valid_host?
28
41
  host.start_with?('http://', 'https://')
29
42
  end
@@ -8,39 +8,49 @@ module FinAppsCore
8
8
  options = connection_options config
9
9
 
10
10
  Faraday.new(options) do |conn|
11
- conn.request :accept_json
12
- conn.request :user_agent
13
- if config.valid_user_credentials?
14
- conn.request :no_encoding_basic_authentication, config.user_token
15
- else
16
- conn.request :tenant_authentication, config.tenant_token
17
- end
18
- conn.request :json
19
- conn.request :retry
20
- conn.request :multipart
21
- conn.request :url_encoded
22
- conn.request :request_id, config.request_id if config.request_id
23
-
24
11
  conn.use FinAppsCore::Middleware::RaiseError
25
- conn.response :json,
26
- content_type: /\bjson$/,
27
- parser_options: { symbolize_names: true }
28
- conn.response :logger, logger, bodies: true
12
+ init_connection_request conn, config
13
+ init_connection_response conn, logger
14
+ init_connection_auth conn, config
29
15
 
30
16
  # Adapter (ensure that the adapter is always last.)
31
17
  conn.adapter Faraday.default_adapter
32
18
  end
33
19
  end
34
- module_function :faraday
20
+
21
+ def init_connection_response(conn, logger)
22
+ conn.response :logger, logger, bodies: true
23
+ conn.response :json,
24
+ content_type: /\bjson$/,
25
+ parser_options: {symbolize_names: true}
26
+ end
27
+
28
+ def init_connection_request(conn, config)
29
+ conn.request :accept_json
30
+ conn.request :user_agent
31
+ conn.request :x_consumer_id, config.consumer_id if config.consumer_id
32
+ conn.request :json
33
+ conn.request :retry
34
+ conn.request :multipart
35
+ conn.request :url_encoded
36
+ conn.request :request_id, config.request_id if config.request_id
37
+ end
38
+
39
+ def init_connection_auth(conn, config)
40
+ if config.valid_user_credentials?
41
+ conn.request :no_encoding_basic_authentication, config.user_token
42
+ else
43
+ conn.request :tenant_authentication, config.tenant_token
44
+ end
45
+ end
35
46
 
36
47
  def connection_options(config)
37
48
  {
38
49
  url: "#{config.host}/v#{Defaults::API_VERSION}/",
39
- request: { open_timeout: config.timeout,
40
- timeout: config.timeout }
50
+ request: {open_timeout: config.timeout,
51
+ timeout: config.timeout}
41
52
  }
42
53
  end
43
- module_function :connection_options
44
54
  end
45
55
  end
46
56
  end
@@ -12,7 +12,7 @@ module FinAppsCore
12
12
  host: 'https://api.finclear.io',
13
13
  timeout: 30,
14
14
  proxy: nil,
15
- log_level: Logger::INFO
15
+ log_level: Logger::UNKNOWN
16
16
  }.freeze
17
17
  end
18
18
  end
@@ -8,7 +8,7 @@ module FinAppsCore
8
8
  # Adds validation capabilities when included into other classes
9
9
  module Validatable
10
10
  def not_blank(value, name = nil)
11
- raise FinAppsCore::MissingArgumentsError, name.nil? ? nil : ": #{name}" if nil_or_empty?(value)
11
+ fail FinAppsCore::MissingArgumentsError, name.nil? ? nil : ": #{name}" if nil_or_empty?(value)
12
12
  end
13
13
 
14
14
  def nil_or_empty?(value)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FinAppsCore
4
- VERSION = '5.0.5'
4
+ VERSION = '5.0.10'
5
5
  end
@@ -2,16 +2,15 @@
2
2
 
3
3
  RSpec.describe ObjectExtensions do
4
4
  context 'when refining Object' do
5
- using ObjectExtensions
5
+ using described_class
6
6
 
7
7
  describe '#integer?' do
8
- context 'for integers' do
9
- subject { rand(1..10) }
10
- it { expect(subject.integer?).to eq(true) }
8
+ context 'with integers' do
9
+ it { expect(rand(1..10).integer?).to eq(true) }
11
10
  end
12
- context 'for non integers' do
13
- subject { rand }
14
- it { expect(subject.integer?).to eq(false) }
11
+
12
+ context 'with non integers' do
13
+ it { expect(rand.integer?).to eq(false) }
15
14
  end
16
15
  end
17
16
  end
@@ -2,12 +2,16 @@
2
2
 
3
3
  RSpec.describe FinAppsCore::Middleware::AcceptJson do
4
4
  let(:fake_app) { proc {|env| env } }
5
+
5
6
  describe '#call' do
6
- subject { FinAppsCore::Middleware::AcceptJson.new(fake_app) }
7
- env = { request_headers: {} }
7
+ subject(:accept_json) { described_class.new(fake_app) }
8
+
9
+ env = {request_headers: {}}
8
10
 
9
11
  it('generates a UserAgent header') do
10
- expect(subject.call(env)[:request_headers][FinAppsCore::Middleware::AcceptJson::KEY]).to eq('application/json')
12
+ header_key = FinAppsCore::Middleware::AcceptJson::KEY
13
+ expect(accept_json.call(env)[:request_headers][header_key])
14
+ .to eq('application/json')
11
15
  end
12
16
  end
13
17
  end
@@ -7,22 +7,31 @@ RSpec.describe FinAppsCore::Middleware::NoEncodingBasicAuthentication do
7
7
  app = proc {|env| env }
8
8
 
9
9
  context 'when credentials were provided' do
10
- let(:middleware) { FinAppsCore::Middleware::NoEncodingBasicAuthentication.new(app, :token) }
10
+ let(:middleware) { described_class.new(app, :token) }
11
11
  let(:expected_header_value) { 'Bearer token' }
12
12
 
13
13
  context 'when header was not previously set' do
14
- let(:request_env) { { request_headers: {} } }
15
14
  subject(:result) { middleware.call(request_env) }
16
15
 
17
- it('generates a header') { expect(result[:request_headers][key]).to eq(expected_header_value) }
16
+ let(:request_env) { {request_headers: {}} }
17
+
18
+ it('generates a header') {
19
+ expect(result[:request_headers][key]).to eq(expected_header_value)
20
+ }
18
21
  end
19
22
 
20
23
  context 'when header was previously set' do
21
- let(:request_env) { { request_headers: { key => 'foo' } } }
22
24
  subject(:result) { middleware.call(request_env) }
23
25
 
24
- it('does not override existing header') { expect(result[:request_headers][key]).to eq('foo') }
25
- it('does not generate a header') { expect(result[:request_headers][key]).to_not eq(expected_header_value) }
26
+ let(:request_env) { {request_headers: {key => 'foo'}} }
27
+
28
+ it('does not override existing header') {
29
+ expect(result[:request_headers][key]).to eq('foo')
30
+ }
31
+
32
+ it('does not generate a header') {
33
+ expect(result[:request_headers][key]).not_to eq(expected_header_value)
34
+ }
26
35
  end
27
36
  end
28
37
  end
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe FinAppsCore::Middleware::RequestId do
4
- let(:key) { FinAppsCore::Middleware::RequestId::KEY }
5
4
  let(:id) { 'request_id' }
6
5
  let(:fake_app) { proc {|env| env } }
7
- let(:env) { { request_headers: {} } }
6
+ let(:env) { {request_headers: {}} }
8
7
 
9
8
  describe '#call' do
10
- subject { FinAppsCore::Middleware::RequestId.new(fake_app, id) }
9
+ subject(:request_id) { described_class.new(fake_app, id) }
11
10
 
12
11
  it('generates a X-Request-Id header') do
13
- expect(subject.call(env)[:request_headers][key]).to eq(id)
12
+ key = FinAppsCore::Middleware::RequestId::KEY
13
+ expect(request_id.call(env)[:request_headers][key]).to eq(id)
14
14
  end
15
15
  end
16
16
  end
@@ -1,30 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe FinAppsCore::Middleware::TenantAuthentication do
4
- let(:valid_tenant_options) { VALID_CREDENTIALS }
5
- let(:key) { FinAppsCore::Middleware::TenantAuthentication::KEY }
6
-
7
4
  describe '#call' do
8
- fake_app = proc {|env| env }
5
+ subject(:actual_header) do
6
+ middleware.call(request_env)[:request_headers][key]
7
+ end
9
8
 
10
9
  context 'when company credentials were provided' do
11
- let(:middleware) { FinAppsCore::Middleware::TenantAuthentication.new(fake_app, VALID_CREDENTIALS[:token]) }
12
- let(:expected_header) { valid_tenant_options[:token] }
10
+ let(:key) { FinAppsCore::Middleware::TenantAuthentication::KEY }
11
+ let(:middleware) do
12
+ fake_app = proc {|env| env }
13
+ described_class.new(fake_app, VALID_CREDENTIALS[:token])
14
+ end
15
+ let(:expected_header) { VALID_CREDENTIALS[:token] }
13
16
 
14
17
  context 'when header was not previously set' do
15
- let(:request_env) { { request_headers: {} } }
16
- subject(:actual_header) { middleware.call(request_env)[:request_headers][key] }
18
+ let(:request_env) { {request_headers: {}} }
17
19
 
18
- it('generates a Tenant Authentication header') { expect(actual_header).to eq(expected_header) }
20
+ it('generates a Tenant Authentication header') {
21
+ expect(actual_header).to eq(expected_header)
22
+ }
19
23
  end
20
24
 
21
25
  context 'when header was previously set' do
22
- let(:existing_header) { { FinAppsCore::Middleware::TenantAuthentication::KEY => 'foo' } }
23
- let(:request_env) { { request_headers: existing_header } }
24
- subject(:actual_header) { middleware.call(request_env)[:request_headers][key] }
26
+ let(:request_env) { {request_headers: {key => 'foo'}} }
27
+
28
+ it('does not override existing Tenant Authentication header') {
29
+ expect(actual_header).to eq('foo')
30
+ }
25
31
 
26
- it('does not override existing Tenant Authentication header') { expect(actual_header).to eq('foo') }
27
- it('does not generate a Tenant Authentication header') { expect(actual_header).to_not eq(expected_header) }
32
+ it('does not generate a Tenant Authentication header') {
33
+ expect(actual_header).not_to eq(expected_header)
34
+ }
28
35
  end
29
36
  end
30
37
  end
@@ -2,12 +2,17 @@
2
2
 
3
3
  RSpec.describe FinAppsCore::Middleware::UserAgent do
4
4
  let(:fake_app) { proc {|env| env } }
5
+
5
6
  describe '#call' do
6
- subject { FinAppsCore::Middleware::UserAgent.new(fake_app) }
7
- env = { request_headers: {} }
7
+ subject(:user_agent) { described_class.new(fake_app) }
8
+
9
+ let(:key) { FinAppsCore::Middleware::UserAgent::KEY }
10
+
11
+ env = {request_headers: {}}
8
12
 
9
13
  it('generates a UserAgent header') do
10
- expect(subject.call(env)[:request_headers][FinAppsCore::Middleware::UserAgent::KEY]).to start_with('finapps-ruby')
14
+ expect(user_agent.call(env)[:request_headers][key])
15
+ .to start_with('finapps-ruby')
11
16
  end
12
17
  end
13
18
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe FinAppsCore::Middleware::XConsumerId do
4
+ let(:id) { 'valid_consumer_id' }
5
+ let(:fake_app) { proc {|env| env } }
6
+ let(:env) { {request_headers: {}} }
7
+
8
+ describe '#call' do
9
+ subject(:x_consumer_id) { described_class.new(fake_app, id) }
10
+
11
+ it('generates an X-Consumer-ID header') do
12
+ key = FinAppsCore::Middleware::XConsumerId::KEY
13
+ expect(x_consumer_id.call(env)[:request_headers][key]).to eq(id)
14
+ end
15
+ end
16
+ end
@@ -2,39 +2,71 @@
2
2
 
3
3
  RSpec.describe FinAppsCore::Middleware::RaiseError do
4
4
  let(:fake_app) { proc {|env| env } }
5
- Env = Struct.new(:status, :response_headers, :body)
5
+
6
+ before do
7
+ stub_const('Env', Struct.new(:status, :response_headers, :body))
8
+ end
6
9
 
7
10
  describe '#on_complete' do
8
- subject { FinAppsCore::Middleware::RaiseError.new(fake_app) }
11
+ subject(:error_raiser) { described_class.new(fake_app) }
9
12
 
10
- context 'for successful requests' do
13
+ context 'with successful requests' do
11
14
  let(:env) { Env.new(200) }
12
- it { expect { subject.on_complete(env) }.not_to raise_error }
15
+
16
+ it { expect { error_raiser.on_complete(env) }.not_to raise_error }
13
17
  end
14
- context 'for invalid session errors' do
15
- let(:env) { Env.new(401, {}, '{"messages":["Invalid User Identifier or Credentials"]}') }
18
+
19
+ context 'with invalid session errors' do
20
+ let(:env) do
21
+ body = '{"messages":["Invalid User Identifier or Credentials"]}'
22
+ Env.new(401, {}, body)
23
+ end
24
+
16
25
  error_message = 'API Invalid Session'
17
- it { expect { subject.on_complete(env) }.to raise_error(FinAppsCore::ApiUnauthenticatedError, error_message) }
26
+ it {
27
+ expect { error_raiser.on_complete(env) }
28
+ .to raise_error(FinAppsCore::ApiUnauthenticatedError, error_message)
29
+ }
18
30
  end
19
- context 'for client errors' do
31
+
32
+ context 'with client errors' do
20
33
  let(:env) { Env.new(404, {}, '{"messages":["Resource Not Found"]}') }
34
+
21
35
  error_message = 'the server responded with status 404'
22
- it { expect { subject.on_complete(env) }.to raise_error(Faraday::Error::ClientError, error_message) }
36
+ it {
37
+ expect { error_raiser.on_complete(env) }
38
+ .to raise_error(Faraday::ClientError, error_message)
39
+ }
23
40
  end
24
- context 'for connection failed error' do
41
+
42
+ context 'with connection failed error' do
25
43
  let(:env) { Env.new(407) }
44
+
26
45
  error_message = 'Connection Failed'
27
- it { expect { subject.on_complete(env) }.to raise_error(FinAppsCore::ConnectionFailedError, error_message) }
46
+ it {
47
+ expect { error_raiser.on_complete(env) }
48
+ .to raise_error(FinAppsCore::ConnectionFailedError, error_message)
49
+ }
28
50
  end
29
- context 'for session timeout error' do
51
+
52
+ context 'with session timeout error' do
30
53
  let(:env) { Env.new(419) }
54
+
31
55
  error_message = 'API Session Timed out'
32
- it { expect { subject.on_complete(env) }.to raise_error(FinAppsCore::ApiSessionTimeoutError, error_message) }
56
+ it {
57
+ expect { error_raiser.on_complete(env) }
58
+ .to raise_error(FinAppsCore::ApiSessionTimeoutError, error_message)
59
+ }
33
60
  end
34
- context 'for user lockout error' do
61
+
62
+ context 'with user lockout error' do
35
63
  let(:env) { Env.new(403, {}, '{"messages":["Account is locked"]}') }
64
+
36
65
  error_message = 'User is Locked'
37
- it { expect { subject.on_complete(env) }.to raise_error(FinAppsCore::UserLockoutError, error_message) }
66
+ it {
67
+ expect { error_raiser.on_complete(env) }
68
+ .to raise_error(FinAppsCore::UserLockoutError, error_message)
69
+ }
38
70
  end
39
71
  end
40
72
  end