finapps_core 5.0.4 → 5.0.9

Sign up to get free protection for your applications and to get access to all the features.
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 +7 -7
  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 +19 -31
  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 +61 -56
@@ -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]
@@ -66,7 +67,7 @@ module FinAppsCore
66
67
  private
67
68
 
68
69
  def response_body(response)
69
- response.body if response.respond_to?(:body)
70
+ response.body if response.respond_to?(:body) && !response.body.to_s.empty?
70
71
  end
71
72
 
72
73
  def send_to_connection(method_id, arguments)
@@ -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::Error::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.4'
4
+ VERSION = '5.0.9'
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