finapps 1.0.8 → 2.0.2

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rspec +1 -2
  4. data/.rubocop.yml +102 -0
  5. data/.ruby-gemset +1 -1
  6. data/.ruby-version +1 -1
  7. data/Rakefile +1 -1
  8. data/finapps.gemspec +10 -10
  9. data/lib/finapps/core_extensions/hash/compact.rb +22 -0
  10. data/lib/finapps/core_extensions/integerable.rb +14 -0
  11. data/lib/finapps/core_extensions/object/blank.rb +145 -0
  12. data/lib/finapps/error.rb +7 -0
  13. data/lib/finapps/hash_constructable.rb +9 -0
  14. data/lib/finapps/middleware/raise_error.rb +46 -0
  15. data/lib/finapps/middleware/tenant_authentication.rb +19 -0
  16. data/lib/finapps/rest/base_client.rb +96 -0
  17. data/lib/finapps/rest/client.rb +11 -199
  18. data/lib/finapps/rest/configuration.rb +55 -0
  19. data/lib/finapps/rest/connection.rb +14 -33
  20. data/lib/finapps/rest/defaults.rb +55 -64
  21. data/lib/finapps/rest/resource.rb +3 -6
  22. data/lib/finapps/rest/resources.rb +11 -6
  23. data/lib/finapps/rest/users.rb +17 -57
  24. data/lib/finapps/utils/loggeable.rb +13 -0
  25. data/lib/finapps/version.rb +1 -1
  26. data/lib/finapps.rb +11 -23
  27. data/lib/tasks/releaser.rake +2 -2
  28. data/spec/middleware/tenant_authentication_spec.rb +29 -0
  29. data/spec/rest/base_client_spec.rb +89 -0
  30. data/spec/rest/client_spec.rb +16 -102
  31. data/spec/rest/configuration_spec.rb +75 -0
  32. data/spec/spec_helper.rb +10 -7
  33. data/spec/support/fake_api.rb +9 -2
  34. data/spec/support/fixtures/error.json +5 -0
  35. data/spec/support/fixtures/relevance_ruleset_names.json +47 -0
  36. metadata +49 -57
  37. data/lib/finapps/middleware/api_token.rb +0 -26
  38. data/lib/finapps/middleware/raise_http_exceptions.rb +0 -92
  39. data/lib/finapps/middleware/response_logger.rb +0 -37
  40. data/lib/finapps/rest/alert.rb +0 -62
  41. data/lib/finapps/rest/alert_definition.rb +0 -40
  42. data/lib/finapps/rest/alert_preferences.rb +0 -40
  43. data/lib/finapps/rest/alert_settings.rb +0 -40
  44. data/lib/finapps/rest/budget_calculation.rb +0 -45
  45. data/lib/finapps/rest/budget_models.rb +0 -42
  46. data/lib/finapps/rest/budgets.rb +0 -103
  47. data/lib/finapps/rest/cashflows.rb +0 -87
  48. data/lib/finapps/rest/categories.rb +0 -21
  49. data/lib/finapps/rest/errors.rb +0 -155
  50. data/lib/finapps/rest/institutions.rb +0 -47
  51. data/lib/finapps/rest/relevance/rulesets.rb +0 -64
  52. data/lib/finapps/rest/transactions.rb +0 -45
  53. data/lib/finapps/rest/user_institutions.rb +0 -138
  54. data/lib/finapps/utils/logging.rb +0 -95
  55. data/lib/finapps/utils/utils.rb +0 -57
  56. data/spec/middleware/api_token_spec.rb +0 -32
  57. data/spec/rest/connection_spec.rb +0 -40
  58. data/spec/rest/users_spec.rb +0 -24
  59. data/spec/utils/logging_spec.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 190f48c84e9cbfcdc797d8c22188152e93fd465c
4
- data.tar.gz: db5d6511e35f37300cf54a53528c12e18a276f8a
3
+ metadata.gz: a0abfe9b391e46bb0657c3b893f6f26117a20f42
4
+ data.tar.gz: 5e3764decd46daa480ce291872a10ebf7f7c8871
5
5
  SHA512:
6
- metadata.gz: 2c6c1292afb1b78387f6e3b45bdcf7e87615d95dedd23e9ecc3a383a512cc35baee77db9b411200372a61663325ffd663a90fcf194a65d21f9823805fe94667b
7
- data.tar.gz: 980bd68d963e142304f09af751d5bbc46065ef8e568199f4f019770b8f13a5916a1061cd5e93bc859210d219bb547b76f34702b1b0723454088e921a4219266d
6
+ metadata.gz: 6378daa1c4cd58c8017b1efd59dd6048fb4baf93ccfbacac82862ab2fcd6880e9418054a4887263ab9106640c091524483acbd2ab5e3cc20e92d761c6ebf6fd4
7
+ data.tar.gz: 8d5e4a4c45df74feab9d3dbbeeea42c32f43a40feccaf5290dd891373330a66cb9806c067c6cc7e1b09fa114b0b5ef3e1c6cd235782a0975196d6214e540ec7e
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /spec/examples.txt
2
+ coverage/
2
3
  *.gem
3
4
  .config
4
5
  Gemfile.lock
@@ -27,4 +28,3 @@ mkmf.log
27
28
  .secret
28
29
  .idea/
29
30
  /spec/tmp/*
30
-
data/.rspec CHANGED
@@ -1,5 +1,4 @@
1
- --drb
2
- --format Fuubar
3
1
  --color
4
2
  --warnings
5
3
  --require spec_helper
4
+ --fail-fast
data/.rubocop.yml ADDED
@@ -0,0 +1,102 @@
1
+ AllCops:
2
+ Exclude:
3
+ - "bin/**/*"
4
+
5
+ Rails:
6
+ Enabled: true
7
+
8
+ ##################### Metrics ##################################
9
+
10
+ Metrics/AbcSize:
11
+ # The ABC size is a calculated magnitude, so this number can be a Fixnum or
12
+ # a Float.
13
+ Max: 25
14
+
15
+ # Commonly used screens these days easily fit more than 80 characters.
16
+ Metrics/LineLength:
17
+ Max: 120
18
+
19
+ # Too short methods lead to extraction of single-use methods, which can make
20
+ # the code easier to read (by naming things), but can also clutter the class
21
+ Metrics/MethodLength:
22
+ Max: 20
23
+
24
+ # No space makes the method definition shorter and differentiates
25
+ # from a regular assignment.
26
+ Style/SpaceAroundEqualsInParameterDefault:
27
+ EnforcedStyle: no_space
28
+
29
+ # We do not need to support Ruby 1.9, so this is good to use.
30
+ Style/SymbolArray:
31
+ Enabled: true
32
+
33
+ # Most readable form.
34
+ Style/AlignHash:
35
+ EnforcedHashRocketStyle: table
36
+ EnforcedColonStyle: table
37
+
38
+ # Mixing the styles looks just silly.
39
+ Style/HashSyntax:
40
+ EnforcedStyle: ruby19_no_mixed_keys
41
+
42
+ # String#% is by far the least verbose and only object oriented variant.
43
+ Style/FormatString:
44
+ EnforcedStyle: percent
45
+
46
+ Style/CollectionMethods:
47
+ Enabled: true
48
+ PreferredMethods:
49
+ # inject seems more common in the community.
50
+ reduce: "inject"
51
+
52
+ # Either allow this style or don't. Marking it as safe with parenthesis
53
+ # is silly. Let's try to live without them for now.
54
+ Style/ParenthesesAroundCondition:
55
+ AllowSafeAssignment: false
56
+ Lint/AssignmentInCondition:
57
+ AllowSafeAssignment: false
58
+
59
+ # A specialized exception class will take one or more arguments and construct the message from it.
60
+ # So both variants make sense.
61
+ Style/RaiseArgs:
62
+ Enabled: false
63
+
64
+ # Fail is an alias of raise. Avoid aliases, it's more cognitive load for no gain.
65
+ # The argument that fail should be used to abort the program is wrong too,
66
+ # there's Kernel#abort for that.
67
+ Style/SignalException:
68
+ EnforcedStyle: only_raise
69
+
70
+ # Suppressing exceptions can be perfectly fine, and be it to avoid to
71
+ # explicitly type nil into the rescue since that's what you want to return,
72
+ # or suppressing LoadError for optional dependencies
73
+ Lint/HandleExceptions:
74
+ Enabled: false
75
+
76
+ Style/SpaceInsideBlockBraces:
77
+ # The space here provides no real gain in readability while consuming
78
+ # horizontal space that could be used for a better parameter name.
79
+ # Also {| differentiates better from a hash than { | does.
80
+ SpaceBeforeBlockParameters: false
81
+
82
+ # No trailing space differentiates better from the block:
83
+ # foo} means hash, foo } means block.
84
+ Style/SpaceInsideHashLiteralBraces:
85
+ EnforcedStyle: no_space
86
+
87
+ # Enforcing -> would be nice, but not at the cost of enforcing lambda { } for
88
+ # multiline lambdas.
89
+ Style/Lambda:
90
+ Enabled: false
91
+
92
+ # Enforcing the names of variables? To single letter ones? Just no.
93
+ Style/SingleLineBlockParams:
94
+ Enabled: false
95
+
96
+ # This is just silly. Calling the argument `other` in all cases makes no sense.
97
+ Style/OpMethod:
98
+ Enabled: false
99
+
100
+ # Reset some HoundCI changes back to Rubocop defaults
101
+ Style/DotPosition:
102
+ EnforcedStyle: leading
data/.ruby-gemset CHANGED
@@ -1 +1 @@
1
- finapps
1
+ ruby-client-v2
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.2.2
1
+ ruby-2.3.1
data/Rakefile CHANGED
@@ -1 +1 @@
1
- import './lib/tasks/releaser.rake'
1
+ import './lib/tasks/releaser.rake'
data/finapps.gemspec CHANGED
@@ -10,27 +10,27 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ['Erich Quintero']
11
11
  spec.email = ['erich@financialapps.com']
12
12
 
13
- spec.summary = %q{FinApps REST API ruby client.}
14
- spec.description = %q{A simple library for communicating with the FinApps REST API.}
15
- spec.homepage = 'http://github.com/finapps/finapps-ruby'
13
+ spec.summary = 'FinApps REST API ruby client.'
14
+ spec.description = 'A simple library for communicating with the FinApps REST API.'
15
+ spec.homepage = 'https://github.com/finapps/ruby-client'
16
16
  spec.license = 'MIT'
17
17
 
18
18
  spec.files = `git ls-files -z`.split("\x0")
19
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f) }
20
20
  spec.test_files = Dir['spec/**/*.rb']
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9.2'
24
- spec.add_runtime_dependency 'faraday_middleware', '~> 0.10', '>= 0.10.0'
25
- spec.add_runtime_dependency 'typhoeus', '~> 1.0', '>= 1.0.1'
23
+ spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9.2'
24
+ spec.add_runtime_dependency 'faraday_middleware', '~> 0.10', '>= 0.10.0'
25
+ spec.add_runtime_dependency 'typhoeus', '~> 1.0', '>= 1.0.2'
26
26
  spec.add_runtime_dependency 'rash', '~> 0.4', '>= 0.4.0'
27
27
 
28
28
  spec.add_development_dependency 'bundler', '~> 1.11', '>= 1.11.2'
29
- spec.add_development_dependency 'rake', '~> 11.1', '>= 11.1.2'
30
- spec.add_development_dependency 'fuubar', '~> 2.0', '>= 2.0.0'
29
+ spec.add_development_dependency 'rake', '~> 11.2', '>= 11.2.2'
31
30
  spec.add_development_dependency 'rspec', '~> 3.4', '>= 3.4.0'
32
- spec.add_development_dependency 'webmock', '~> 1.24', '>= 1.24.2'
31
+ spec.add_development_dependency 'webmock', '~> 2.1', '>= 2.1.0'
33
32
  spec.add_development_dependency 'sinatra', '~> 1.4', '>= 1.4.7'
33
+ spec.add_development_dependency 'simplecov', '~> 0.11', '>= 0.11.2'
34
34
 
35
35
  spec.extra_rdoc_files = %w(README.md LICENSE.txt)
36
36
  spec.rdoc_options = %w(--line-numbers --inline-source --title finapps-ruby --main README.md)
@@ -0,0 +1,22 @@
1
+ # from rails/activesupport/lib/active_support/core_ext/hash/compact.rb
2
+
3
+ class Hash # :nodoc:
4
+ # Returns a hash with non +nil+ values.
5
+ #
6
+ # hash = { a: true, b: false, c: nil}
7
+ # hash.compact # => { a: true, b: false}
8
+ # hash # => { a: true, b: false, c: nil}
9
+ # { c: nil }.compact # => {}
10
+ def compact
11
+ select {|_, value| !value.nil? }
12
+ end
13
+
14
+ # Replaces current hash with non +nil+ values.
15
+ #
16
+ # hash = { a: true, b: false, c: nil}
17
+ # hash.compact! # => { a: true, b: false}
18
+ # hash # => { a: true, b: false}
19
+ def compact!
20
+ reject! {|_, value| value.nil? }
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module CoreExtensions
2
+ # adds an integer? method to any object when used in a class
3
+ module Integerable
4
+ refine Object do
5
+ def integer?
6
+ Integer(self)
7
+ rescue
8
+ false
9
+ else
10
+ true
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,145 @@
1
+ # from: rails/activesupport/lib/active_support/core_ext/object/blank.rb
2
+
3
+ class Object # :nodoc:
4
+ # An object is blank if it's false, empty, or a whitespace string.
5
+ # For example, +false+, '', ' ', +nil+, [], and {} are all blank.
6
+ #
7
+ # This simplifies
8
+ #
9
+ # !address || address.empty?
10
+ #
11
+ # to
12
+ #
13
+ # address.blank?
14
+ #
15
+ # @return [true, false]
16
+ def blank?
17
+ respond_to?(:empty?) ? empty? : !self
18
+ end
19
+
20
+ # An object is present if it's not blank.
21
+ #
22
+ # @return [true, false]
23
+ def present?
24
+ !blank?
25
+ end
26
+
27
+ # Returns the receiver if it's present otherwise returns +nil+.
28
+ # <tt>object.presence</tt> is equivalent to
29
+ #
30
+ # object.present? ? object : nil
31
+ #
32
+ # For example, something like
33
+ #
34
+ # state = params[:state] if params[:state].present?
35
+ # country = params[:country] if params[:country].present?
36
+ # region = state || country || 'US'
37
+ #
38
+ # becomes
39
+ #
40
+ # region = params[:state].presence || params[:country].presence || 'US'
41
+ #
42
+ # @return [Object]
43
+ def presence
44
+ self if present?
45
+ end
46
+ end
47
+
48
+ class NilClass # :nodoc:
49
+ # +nil+ is blank:
50
+ #
51
+ # nil.blank? # => true
52
+ #
53
+ # @return [true]
54
+ def blank?
55
+ true
56
+ end
57
+ end
58
+
59
+ class FalseClass # :nodoc:
60
+ # +false+ is blank:
61
+ #
62
+ # false.blank? # => true
63
+ #
64
+ # @return [true]
65
+ def blank?
66
+ true
67
+ end
68
+ end
69
+
70
+ class TrueClass # :nodoc:
71
+ # +true+ is not blank:
72
+ #
73
+ # true.blank? # => false
74
+ #
75
+ # @return [false]
76
+ def blank?
77
+ false
78
+ end
79
+ end
80
+
81
+ class Array # :nodoc:
82
+ # An array is blank if it's empty:
83
+ #
84
+ # [].blank? # => true
85
+ # [1,2,3].blank? # => false
86
+ #
87
+ # @return [true, false]
88
+ alias blank? empty?
89
+ end
90
+
91
+ class Hash # :nodoc:
92
+ # A hash is blank if it's empty:
93
+ #
94
+ # {}.blank? # => true
95
+ # { key: 'value' }.blank? # => false
96
+ #
97
+ # @return [true, false]
98
+ alias blank? empty?
99
+ end
100
+
101
+ class String # :nodoc:
102
+ BLANK_RE = /\A[[:space:]]*\z/
103
+
104
+ # A string is blank if it's empty or contains whitespaces only:
105
+ #
106
+ # ''.blank? # => true
107
+ # ' '.blank? # => true
108
+ # "\t\n\r".blank? # => true
109
+ # ' blah '.blank? # => false
110
+ #
111
+ # Unicode whitespace is supported:
112
+ #
113
+ # "\u00a0".blank? # => true
114
+ #
115
+ # @return [true, false]
116
+ def blank?
117
+ # The regexp that matches blank strings is expensive. For the case of empty
118
+ # strings we can speed up this method (~3.5x) with an empty? call. The
119
+ # penalty for the rest of strings is marginal.
120
+ empty? || self =~ BLANK_RE
121
+ end
122
+ end
123
+
124
+ class Numeric #:nodoc:
125
+ # No number is blank:
126
+ #
127
+ # 1.blank? # => false
128
+ # 0.blank? # => false
129
+ #
130
+ # @return [false]
131
+ def blank?
132
+ false
133
+ end
134
+ end
135
+
136
+ class Time #:nodoc:
137
+ # No Time is blank:
138
+ #
139
+ # Time.now.blank? # => false
140
+ #
141
+ # @return [false]
142
+ def blank?
143
+ false
144
+ end
145
+ end
@@ -0,0 +1,7 @@
1
+ module FinApps # :nodoc:
2
+ class Error < StandardError; end
3
+ class InvalidArgumentsError < Error; end
4
+ class MissingArgumentsError < Error; end
5
+
6
+ %i(InvalidArgumentsError MissingArgumentsError).each {|const| Error.const_set(const, FinApps.const_get(const)) }
7
+ end
@@ -0,0 +1,9 @@
1
+ module FinApps
2
+ # when included into any object, allows to initialize public attributes from a hash
3
+ module HashConstructable
4
+ def initialize(options_hash={}, defaults=nil)
5
+ merged_hash = defaults.nil? ? options_hash : defaults.merge(options_hash.compact)
6
+ merged_hash.each {|k, v| public_send("#{k}=", v) }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,46 @@
1
+ module FinApps
2
+ module Middleware
3
+ class RaiseError < Faraday::Response::Middleware # :nodoc:
4
+ include FinApps::Utils::Loggeable
5
+
6
+ CLIENT_ERROR_STATUSES = 400...600
7
+
8
+ def on_complete(env)
9
+ case env[:status]
10
+ when 407
11
+ # mimic the behavior that we get with proxy requests with HTTPS
12
+ raise Faraday::Error::ConnectionFailed, '407 "Proxy Authentication Required"'
13
+ when CLIENT_ERROR_STATUSES
14
+ raise Faraday::Error::ClientError, response_values(env)
15
+ else
16
+ # 200..206 Success codes
17
+ # all good!
18
+ logger.debug "##{__method__} => Status code: [#{env[:status]}]"
19
+ end
20
+ end
21
+
22
+ def response_values(env)
23
+ {
24
+ status: env.status,
25
+ headers: env.response_headers,
26
+ body: env.body,
27
+ error_messages: error_messages(env.body)
28
+ }
29
+ end
30
+
31
+ private
32
+
33
+ def error_messages(body)
34
+ return nil unless body.present?
35
+ body = parse_string(body) if body.is_a?(String)
36
+ body.is_a?(Hash) ? body['messages'].presence : nil
37
+ end
38
+
39
+ def parse_string(body)
40
+ ::JSON.parse(body)
41
+ rescue ::JSON::ParserError
42
+ logger.error "##{__method__} => Unable to parse JSON response."
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ module FinApps
2
+ module Middleware
3
+ # Adds a custom header for tenant level authorization.
4
+ # If the value for this header already exists, it is not overriden.
5
+ class TenantAuthentication < Faraday::Middleware
6
+ KEY = 'X-FinApps-Token'.freeze unless defined? KEY
7
+
8
+ def initialize(app, options={})
9
+ super(app)
10
+ @header_value = "#{options[:identifier].strip}=#{options[:token].strip}"
11
+ end
12
+
13
+ def call(env)
14
+ env[:request_headers][KEY] ||= @header_value
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,96 @@
1
+ module FinApps
2
+ module REST
3
+ class BaseClient # :nodoc:
4
+ include ::FinApps::Utils::Loggeable
5
+
6
+ attr_reader :config
7
+
8
+ def initialize(options={}, logger=nil)
9
+ @config = FinApps::REST::Configuration.new options
10
+ @logger = logger
11
+ end
12
+
13
+ # Returns an initialized Faraday connection object.
14
+ #
15
+ # @return Faraday::Connection.
16
+ def connection
17
+ @connection ||= FinApps::REST::Connection.faraday(config, logger)
18
+ end
19
+
20
+ # Performs HTTP GET, POST, UPDATE and DELETE requests.
21
+ # You shouldn't need to use this method directly, but it can be useful for debugging.
22
+ # Returns a hash obtained from parsing the JSON object in the response body.
23
+ #
24
+ # @param [String] path
25
+ # @param [String] method
26
+ # @param [Hash] params
27
+ # @return [Hash,Array<String>]
28
+ def send_request(path, method, params={})
29
+ raise FinApps::MissingArgumentsError.new 'Missing argument: path.' if path.blank?
30
+ raise FinApps::MissingArgumentsError.new 'Missing argument: method.' if method.blank?
31
+
32
+ response, error_messages = execute_request(method, params, path)
33
+ result = if response.present?
34
+ block_given? ? yield(response) : response.body
35
+ else
36
+ logger.error "##{__method__} => Null response found. Unable to process it."
37
+ nil
38
+ end
39
+
40
+ [result, error_messages]
41
+ end
42
+
43
+ private
44
+
45
+ def execute_request(method, params, path)
46
+ error_messages = []
47
+
48
+ begin
49
+ response = execute_method method, params, path
50
+ rescue FinApps::InvalidArgumentsError,
51
+ FinApps::MissingArgumentsError,
52
+ Faraday::Error::ConnectionFailed => error
53
+ logger.fatal "##{__method__} => #{error}"
54
+ raise error
55
+ rescue Faraday::Error::ClientError => error
56
+ error_messages = error.response[:error_messages].blank? ? [error.message] : error.response[:error_messages]
57
+ logger.error "##{__method__} => Faraday::Error::ClientError, #{error}"
58
+ rescue StandardError => error
59
+ error_messages << 'Unexpected error.'
60
+ logger.fatal "##{__method__} => StandardError, #{error}"
61
+ end
62
+
63
+ [response, error_messages]
64
+ end
65
+
66
+ def execute_method(method, params, path)
67
+ case method
68
+ when :get
69
+ get(path)
70
+ when :post
71
+ post(path, params)
72
+ when :put
73
+ put(path, params)
74
+ when :delete
75
+ delete(path, params)
76
+ else
77
+ raise FinApps::InvalidArgumentsError.new "Method not supported: #{method}."
78
+ end
79
+ end
80
+
81
+ # Defines methods to perform HTTP GET, POST, PUT and DELETE requests.
82
+ # Returns a hash obtained from parsing the JSON object in the response body.
83
+ #
84
+ def method_missing(method_id, *arguments, &block)
85
+ if %i(get post put delete).include? method_id
86
+ connection.send(method_id) do |req|
87
+ req.url arguments.first
88
+ req.body = arguments[1] unless method_id == :get
89
+ end
90
+ else
91
+ super
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end