finapps 1.0.8 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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