optimum 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3534d5d95a086cfb762786c9f275b468c819f490c33d04a1b4d5479eaa2d7600
4
+ data.tar.gz: aff2d7e31171732f026966d41b6e65466f73ee3c3e5aa9b81e33dfabc5e5cae0
5
+ SHA512:
6
+ metadata.gz: ff410a48fc4f8fc417ffbddd931603901566cb13bed126e33f2420f178920105f43358c8c640c7dd3f3159f4e66ef1ad0519105f4c9825c8308ecbdf5885e6f0
7
+ data.tar.gz: 4443d5f791fb50599012110c9aa7d5a4faf4898073838dbf815be8485ecce1c35a799f4c29dc15378ad4aebbd3e389b50e57c632ab976bc947e543a19203b182
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ .env
10
+ .DS_Store
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
4
+ --order random
@@ -0,0 +1,31 @@
1
+ require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.5.1
5
+
6
+ # Don't force top level comments in every class
7
+ Style/Documentation:
8
+ Enabled: false
9
+
10
+ # A good line length is 100 chars
11
+ Layout/LineLength:
12
+ Max: 100
13
+ AllowURI: true
14
+
15
+ Metrics/BlockLength:
16
+ Enabled: false
17
+
18
+ Metrics/ClassLength:
19
+ Max: 300
20
+
21
+ Metrics/MethodLength:
22
+ Max: 20
23
+
24
+ Metrics/AbcSize:
25
+ Max: 30
26
+
27
+ RSpec/ExampleLength:
28
+ Enabled: false
29
+
30
+ RSpec/MultipleExpectations:
31
+ Max: 10
@@ -0,0 +1 @@
1
+ 2.6.6
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.3
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in optimum.gemspec
6
+ gemspec
@@ -0,0 +1,105 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ optimum (1.0.0)
5
+ addressable
6
+ faraday
7
+ faraday_middleware
8
+ multi_json
9
+ rainbow
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ addressable (2.7.0)
15
+ public_suffix (>= 2.0.2, < 5.0)
16
+ ast (2.4.0)
17
+ byebug (11.1.0)
18
+ coderay (1.1.2)
19
+ concurrent-ruby (1.1.5)
20
+ crack (0.4.3)
21
+ safe_yaml (~> 1.0.0)
22
+ diff-lcs (1.3)
23
+ docile (1.3.2)
24
+ dotenv (2.7.5)
25
+ faker (2.10.1)
26
+ i18n (>= 1.6, < 2)
27
+ faraday (1.0.1)
28
+ multipart-post (>= 1.2, < 3)
29
+ faraday_middleware (1.0.0)
30
+ faraday (~> 1.0)
31
+ hashdiff (1.0.0)
32
+ i18n (1.8.2)
33
+ concurrent-ruby (~> 1.0)
34
+ jaro_winkler (1.5.4)
35
+ json (2.3.0)
36
+ method_source (0.9.2)
37
+ multi_json (1.15.0)
38
+ multipart-post (2.1.1)
39
+ parallel (1.19.1)
40
+ parser (2.7.0.2)
41
+ ast (~> 2.4.0)
42
+ pry (0.12.2)
43
+ coderay (~> 1.1.0)
44
+ method_source (~> 0.9.0)
45
+ pry-byebug (3.7.0)
46
+ byebug (~> 11.0)
47
+ pry (~> 0.10)
48
+ public_suffix (4.0.3)
49
+ rainbow (3.0.0)
50
+ rake (10.5.0)
51
+ rspec (3.9.0)
52
+ rspec-core (~> 3.9.0)
53
+ rspec-expectations (~> 3.9.0)
54
+ rspec-mocks (~> 3.9.0)
55
+ rspec-core (3.9.1)
56
+ rspec-support (~> 3.9.1)
57
+ rspec-expectations (3.9.0)
58
+ diff-lcs (>= 1.2.0, < 2.0)
59
+ rspec-support (~> 3.9.0)
60
+ rspec-mocks (3.9.1)
61
+ diff-lcs (>= 1.2.0, < 2.0)
62
+ rspec-support (~> 3.9.0)
63
+ rspec-support (3.9.2)
64
+ rubocop (0.79.0)
65
+ jaro_winkler (~> 1.5.1)
66
+ parallel (~> 1.10)
67
+ parser (>= 2.7.0.1)
68
+ rainbow (>= 2.2.2, < 4.0)
69
+ ruby-progressbar (~> 1.7)
70
+ unicode-display_width (>= 1.4.0, < 1.7)
71
+ rubocop-rspec (1.37.1)
72
+ rubocop (>= 0.68.1)
73
+ ruby-progressbar (1.10.1)
74
+ safe_yaml (1.0.5)
75
+ simplecov (0.17.1)
76
+ docile (~> 1.1)
77
+ json (>= 1.8, < 3)
78
+ simplecov-html (~> 0.10.0)
79
+ simplecov-html (0.10.2)
80
+ unicode-display_width (1.6.1)
81
+ vcr (5.0.0)
82
+ webmock (3.8.0)
83
+ addressable (>= 2.3.6)
84
+ crack (>= 0.3.2)
85
+ hashdiff (>= 0.4.0, < 2.0.0)
86
+
87
+ PLATFORMS
88
+ ruby
89
+
90
+ DEPENDENCIES
91
+ bundler
92
+ dotenv
93
+ faker
94
+ optimum!
95
+ pry-byebug
96
+ rake (~> 10.0)
97
+ rspec (~> 3.0)
98
+ rubocop
99
+ rubocop-rspec
100
+ simplecov
101
+ vcr
102
+ webmock
103
+
104
+ BUNDLED WITH
105
+ 2.1.4
@@ -0,0 +1,74 @@
1
+ # optimum API wrapper
2
+
3
+ This is a simple gem to interact with [optimum's](https://www.optimumfinance.co.uk/) Instant Decision API.
4
+
5
+ The API documentation can be found here: https://optimum-core-staging.herokuapp.com/api/v1/instant-decision/docs
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'optimum'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install optimum
22
+
23
+ ## Usage
24
+
25
+ You need to have an introducerId to use the API. Every request will be authenticated with that id in the params.
26
+
27
+ ### Configuration
28
+
29
+ You can configure Optimum by providing a configuration block, before you do any call:
30
+
31
+ ```ruby
32
+ Optimum.configure do |config|
33
+ config.introducer_id = YOUR_ID_HERE
34
+ # config.debug = true # This is false by default, keep in mind it will ve VERY verbose
35
+ end
36
+ ```
37
+
38
+ ### Quote
39
+
40
+ All the available methods are encapsulated in the `Decision` class. The available methods are:
41
+
42
+ ```ruby
43
+ Optimum::Decision.create(PARAMS)
44
+ # => This will return a decision, along with opportunityId, which will be used in subsequent calls.
45
+
46
+ Optimum::Decision.update(PARAMS)
47
+ ```
48
+
49
+ Every call will return a `Optimum::Response` instance, that you can use to get all the information
50
+ you need:
51
+
52
+ ```ruby
53
+ response = Optimum::Decision.update(PARAMS)
54
+
55
+ response.status #=> 200
56
+ response.success? #=> true
57
+ response.errors #=> []
58
+ response.data #=> { ... }
59
+ ```
60
+
61
+ ## Development
62
+
63
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run
64
+ the tests. You can also run `bin/console` for an interactive prompt that will allow you to
65
+ experiment.
66
+
67
+ Please refer to optimum's documentation on how to test different states (we do have recorded VCR
68
+ responses, but if something changes on their side we need to update). The documentation is here:
69
+ https://optimum-core-staging.herokuapp.com/api/v1/instant-decision/docs
70
+
71
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new
72
+ version, update the version number in `version.rb`, and then run `bundle exec rake release`, which
73
+ will create a git tag for the version, push git commits and tags, and push the `.gem` file
74
+ to [rubygems.org](https://rubygems.org).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'optimum'
6
+ require 'date'
7
+
8
+ require 'dotenv'
9
+ Dotenv.load
10
+
11
+ Optimum.configure do |config|
12
+ config.introducer_id = ENV['OPTIMUM_INTRODUCER_ID']
13
+ end
14
+
15
+ send(:include, Optimum)
16
+
17
+ require 'pry'
18
+ Pry.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optimum/version'
4
+ require 'optimum/path_sanitizer'
5
+ require 'optimum/configuration'
6
+ require 'optimum/connection'
7
+ require 'optimum/decision'
8
+
9
+ module Optimum
10
+ module_function
11
+
12
+ def configuration
13
+ @configuration ||= Configuration.new
14
+ end
15
+
16
+ def connection
17
+ @connection ||= Connection.new
18
+ end
19
+
20
+ def configure
21
+ yield(configuration)
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Optimum
4
+ class Configuration
5
+ attr_accessor :env, :introducer_id
6
+ attr_writer :debug
7
+
8
+ API_DOMAINS = {
9
+ production: 'https://www.optimum.localhost',
10
+ development: 'https://optimum-core-staging.herokuapp.com'
11
+ }.freeze
12
+
13
+ def initialize
14
+ @introducer_id = ''
15
+ @webhook_signature = ''
16
+ @env = defined?(Rails) ? Rails.env : :development
17
+ @debug = false
18
+ end
19
+
20
+ def debug?
21
+ @debug
22
+ end
23
+
24
+ def api_domain
25
+ API_DOMAINS[@env.to_sym] || API_DOMAINS[:development]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday_middleware'
5
+ require 'rainbow'
6
+ require 'addressable'
7
+
8
+ require 'optimum/response'
9
+
10
+ module Optimum
11
+ class Connection
12
+ BASE_PATH = '/api/v1/instant-decision'
13
+
14
+ def get(path, params = {})
15
+ log "GET #{path} with #{params}"
16
+
17
+ http_response = adapter.get(PathSanitizer.sanitize(path)) do |req|
18
+ req.body = params.to_json
19
+ end
20
+
21
+ log "Response: #{http_response.body}"
22
+
23
+ Response.new(http_response)
24
+ end
25
+
26
+ def post(path, params = {})
27
+ log "POST #{path} with #{params}"
28
+
29
+ http_response = adapter.post(PathSanitizer.sanitize(path), params.to_json)
30
+
31
+ log "Response: #{http_response.body}"
32
+
33
+ Response.new(http_response)
34
+ end
35
+
36
+ def put(path, params = {})
37
+ log "PUT #{path} with #{params}"
38
+
39
+ http_response = adapter.put(PathSanitizer.sanitize(path), params.to_json)
40
+
41
+ log "Response: #{http_response.body}"
42
+
43
+ Response.new(http_response)
44
+ end
45
+
46
+ private
47
+
48
+ def log(text)
49
+ return unless Optimum.configuration.debug?
50
+
51
+ puts Rainbow("[Optimum] #{text}").magenta.bright
52
+ end
53
+
54
+ def base_url
55
+ Addressable::URI.join(Optimum.configuration.api_domain, BASE_PATH).to_s
56
+ end
57
+
58
+ def adapter
59
+ Faraday.new(url: base_url) do |conn|
60
+ conn.headers['Content-Type'] = 'application/json'
61
+ conn.headers['User-Agent'] = "ruby-optimum-#{VERSION}"
62
+ conn.response :json, parser_options: { symbolize_names: true }, content_type: /\bjson$/
63
+ conn.response :logger if Optimum.configuration.debug?
64
+ conn.adapter Faraday.default_adapter
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faker'
4
+
5
+ module Optimum
6
+ # This class can be used to generate fake customers for test purposes
7
+ class CustomerGenerator
8
+ attr_accessor :id, :first_name, :last_name, :email, :phone, :address, :date_of_birth, :city
9
+ attr_accessor :postcode
10
+
11
+ DEFAULTS = {
12
+ id: SecureRandom.hex(4),
13
+ first_name: Faker::Name.first_name,
14
+ last_name: Faker::Name.last_name,
15
+ email: Faker::Internet.email,
16
+ phone: Faker::PhoneNumber.phone_number,
17
+ address: Faker::Address.street_address,
18
+ date_of_birth: Faker::Date.birthday(min_age: 23, max_age: 65),
19
+ city: Faker::Address.city,
20
+ postcode: Faker::Address.postcode
21
+ }.freeze
22
+
23
+ def self.generate(params = {})
24
+ new(params).generate
25
+ end
26
+
27
+ def initialize(params = {})
28
+ Faker::Config.locale = 'en-GB'
29
+
30
+ DEFAULTS.each do |key, default_value|
31
+ send("#{key}=", params[key] || default_value)
32
+ end
33
+ end
34
+
35
+ def generate
36
+ {
37
+ data: {
38
+ application: {
39
+ company: company_information,
40
+ people: [user_information],
41
+ requested_products: {
42
+ credit_facility: {
43
+ approval: {
44
+ amount: 123_123,
45
+ duration: 12,
46
+ detailed_purpose: 'business loan'
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ end
54
+
55
+ def company_information
56
+ {
57
+ last_12_months_turnover: {
58
+ amount: 0.0
59
+ },
60
+ registered_company_name: 'HOKO LTD',
61
+ industry: 'B | Mining and Quarrying',
62
+ company_number: '09525857',
63
+ type: 'limited_liability_company',
64
+ trading_from_date: '2015-05-22',
65
+ registered_address: {
66
+ postcode: 'W1T 3NF',
67
+ street_line_1: 'Berners House',
68
+ street_line_2: '47-48 Berners Street',
69
+ town: 'London',
70
+ country: 'GB'
71
+ },
72
+ vat_status: {
73
+ is_vat_registered: true
74
+ }
75
+ }
76
+ end
77
+
78
+ def user_information
79
+ {
80
+ uid: SecureRandom.uuid,
81
+ first_name: first_name,
82
+ last_name: last_name,
83
+ date_of_birth: date_of_birth,
84
+ roles: %w[applicant shareholder guarantor director],
85
+ phones: phones,
86
+ emails: emails,
87
+ residential_addresses: residential_addresses,
88
+ privacy_policy: {
89
+ agreed: true,
90
+ datetime: DateTime.now
91
+ }
92
+ }
93
+ end
94
+
95
+ def phones
96
+ [
97
+ {
98
+ uid: SecureRandom.uuid,
99
+ number: phone,
100
+ type: 'primary'
101
+ }
102
+ ]
103
+ end
104
+
105
+ def emails
106
+ [
107
+ {
108
+ uid: SecureRandom.uuid,
109
+ email: email,
110
+ type: 'primary'
111
+ }
112
+ ]
113
+ end
114
+
115
+ def residential_addresses
116
+ [
117
+ {
118
+ uid: SecureRandom.uuid,
119
+ town: city,
120
+ street_line_1: address,
121
+ street_line_2: '',
122
+ country: 'GB',
123
+ postcode: postcode,
124
+ house_number: '10',
125
+ residential_status: 'owner_no_mortgage'
126
+ }
127
+ ]
128
+ end
129
+ end
130
+ end