quovo 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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +12 -0
  4. data/.rubocop.yml +12 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE +21 -0
  8. data/README.md +176 -0
  9. data/Rakefile +12 -0
  10. data/bin/console +53 -0
  11. data/lib/quovo.rb +81 -0
  12. data/lib/quovo/api.rb +31 -0
  13. data/lib/quovo/api/accounts.rb +65 -0
  14. data/lib/quovo/api/base.rb +27 -0
  15. data/lib/quovo/api/brokerages.rb +21 -0
  16. data/lib/quovo/api/challenges.rb +28 -0
  17. data/lib/quovo/api/history.rb +36 -0
  18. data/lib/quovo/api/portfolios.rb +47 -0
  19. data/lib/quovo/api/positions.rb +30 -0
  20. data/lib/quovo/api/users.rb +44 -0
  21. data/lib/quovo/config.rb +61 -0
  22. data/lib/quovo/errors.rb +7 -0
  23. data/lib/quovo/fake.rb +29 -0
  24. data/lib/quovo/hook.rb +23 -0
  25. data/lib/quovo/models/account.rb +34 -0
  26. data/lib/quovo/models/base.rb +38 -0
  27. data/lib/quovo/models/brokerage.rb +15 -0
  28. data/lib/quovo/models/challenge.rb +34 -0
  29. data/lib/quovo/models/choice.rb +10 -0
  30. data/lib/quovo/models/image.rb +10 -0
  31. data/lib/quovo/models/portfolio.rb +50 -0
  32. data/lib/quovo/models/position.rb +36 -0
  33. data/lib/quovo/models/progress.rb +11 -0
  34. data/lib/quovo/models/sync.rb +20 -0
  35. data/lib/quovo/models/transaction.rb +37 -0
  36. data/lib/quovo/models/user.rb +14 -0
  37. data/lib/quovo/refinements/cast.rb +22 -0
  38. data/lib/quovo/refinements/compact.rb +11 -0
  39. data/lib/quovo/refinements/permit.rb +11 -0
  40. data/lib/quovo/refinements/require.rb +20 -0
  41. data/lib/quovo/refinements/sensitive.rb +38 -0
  42. data/lib/quovo/refinements/to_time.rb +18 -0
  43. data/lib/quovo/request.rb +114 -0
  44. data/lib/quovo/scope.rb +19 -0
  45. data/lib/quovo/token.rb +70 -0
  46. data/lib/quovo/version.rb +3 -0
  47. data/quovo.gemspec +19 -0
  48. metadata +93 -0
@@ -0,0 +1,27 @@
1
+ module Quovo
2
+ module Api
3
+ class Base
4
+ include Quovo::Models
5
+ include Quovo::Request
6
+
7
+ def initialize(token = Quovo::Token.new)
8
+ @token = token
9
+ end
10
+
11
+ def api(method, path, params = {})
12
+ format = case method
13
+ when :delete
14
+ :plain
15
+ else
16
+ :json
17
+ end
18
+
19
+ request(method, path, params, format) do |req|
20
+ req['Authorization'] = "Bearer #{token.get}"
21
+ end || {}
22
+ end
23
+
24
+ attr_reader :token
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module Quovo
2
+ module Api
3
+ class Brokerages < Base
4
+ using Quovo::Refinements::Cast
5
+ using Quovo::Refinements::Require
6
+
7
+ def all
8
+ api(:get, '/brokerages')
9
+ .fetch('brokerages')
10
+ .cast(Brokerage)
11
+ end
12
+
13
+ def find(id)
14
+ id.require!(as: :id)
15
+ api(:get, "/brokerages/#{id}")
16
+ .fetch('brokerage')
17
+ .cast(Brokerage)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module Quovo
2
+ module Api
3
+ class Challenges < Base
4
+ using Quovo::Refinements::Cast
5
+ using Quovo::Refinements::Require
6
+
7
+ def for_account(id)
8
+ id.require!(as: :id)
9
+ api(:get, "/accounts/#{id}/challenges")
10
+ .fetch('challenges')
11
+ .cast(Challenge)
12
+ end
13
+
14
+ def answers!(account_id, answers)
15
+ account_id.require!(as: 'account_id')
16
+ answers.require!(as: 'answers')
17
+ answers.each do |answer|
18
+ answer.require!(:answer, :question)
19
+ end
20
+
21
+ params = { questions: answers.to_json }
22
+ api(:put, "/accounts/#{account_id}/challenges", params)
23
+ .fetch('challenges')
24
+ .cast(Challenge)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ module Quovo
2
+ module Api
3
+ class History < Base
4
+ using Quovo::Refinements::Cast
5
+ using Quovo::Refinements::Require
6
+ using Quovo::Refinements::Permit
7
+
8
+ def for_user(id, params = {})
9
+ id.require!(as: :id)
10
+ params.permit!(:start_date, :end_date, :start_id, :end_id)
11
+ api(:get, "/users/#{id}/history", params)
12
+ .fetch('history')
13
+ .cast(Transaction)
14
+ .sort_by(&:sort_key)
15
+ end
16
+
17
+ def for_account(id, params = {})
18
+ id.require!(as: :id)
19
+ params.permit!(:start_date, :end_date, :start_id, :end_id)
20
+ api(:get, "/accounts/#{id}/history", params)
21
+ .fetch('history')
22
+ .cast(Transaction)
23
+ .sort_by(&:sort_key)
24
+ end
25
+
26
+ def for_portfolio(id, params = {})
27
+ id.require!(as: :id)
28
+ params.permit!(:start_date, :end_date, :start_id, :end_id)
29
+ api(:get, "/portfolios/#{id}/history", params)
30
+ .fetch('history')
31
+ .cast(Transaction)
32
+ .sort_by(&:sort_key)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,47 @@
1
+ module Quovo
2
+ module Api
3
+ class Portfolios < Base
4
+ using Quovo::Refinements::Cast
5
+ using Quovo::Refinements::Compact
6
+ using Quovo::Refinements::Permit
7
+ using Quovo::Refinements::Require
8
+
9
+ def all
10
+ api(:get, '/portfolios')
11
+ .fetch('portfolios')
12
+ .cast(Portfolio)
13
+ end
14
+
15
+ def find(id)
16
+ id.require!(as: :id)
17
+ api(:get, "/portfolios/#{id}")
18
+ .fetch('portfolio')
19
+ .cast(Portfolio)
20
+ end
21
+
22
+ def update(id, params)
23
+ id.require!(as: :id)
24
+ params
25
+ .permit!(:nickname, :portfolio_type, :is_inactive)
26
+ .compact!
27
+ api(:put, "/portfolios/#{id}", params)
28
+ .fetch('portfolio')
29
+ .cast(Portfolio)
30
+ end
31
+
32
+ def for_user(id)
33
+ id.require!(as: :id)
34
+ api(:get, "/users/#{id}/portfolios")
35
+ .fetch('portfolios')
36
+ .cast(Portfolio)
37
+ end
38
+
39
+ def for_account(id)
40
+ id.require!(as: :id)
41
+ api(:get, "/accounts/#{id}/portfolios")
42
+ .fetch('portfolios')
43
+ .cast(Portfolio)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ module Quovo
2
+ module Api
3
+ class Positions < Base
4
+ using Quovo::Refinements::Cast
5
+ using Quovo::Refinements::Permit
6
+ using Quovo::Refinements::Require
7
+
8
+ def for_user(id)
9
+ id.require!(as: :id)
10
+ api(:get, "/users/#{id}/positions")
11
+ .fetch('positions')
12
+ .cast(Position)
13
+ end
14
+
15
+ def for_account(id)
16
+ id.require!(as: :id)
17
+ api(:get, "/accounts/#{id}/positions")
18
+ .fetch('positions')
19
+ .cast(Position)
20
+ end
21
+
22
+ def for_portfolio(id)
23
+ id.require!(as: :id)
24
+ api(:get, "/portfolios/#{id}/positions")
25
+ .fetch('positions')
26
+ .cast(Position)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,44 @@
1
+ module Quovo
2
+ module Api
3
+ class Users < Base
4
+ using Quovo::Refinements::Cast
5
+ using Quovo::Refinements::Require
6
+ using Quovo::Refinements::Permit
7
+
8
+ def all
9
+ api(:get, '/users')
10
+ .fetch('users')
11
+ .cast(User)
12
+ end
13
+
14
+ def find(id)
15
+ id.require!(as: :id)
16
+ api(:get, "/users/#{id}")
17
+ .fetch('user')
18
+ .cast(User)
19
+ end
20
+
21
+ def create(params)
22
+ params
23
+ .permit!(:username, :name, :email, :phone)
24
+ .require!(:username)
25
+ api(:post, '/users', params)
26
+ .fetch('user')
27
+ .cast(User)
28
+ end
29
+
30
+ def update(id, params)
31
+ id.require!(as: :id)
32
+ params.permit!(:name, :email, :phone)
33
+ api(:put, "/users/#{id}", params)
34
+ .fetch('user')
35
+ .cast(User)
36
+ end
37
+
38
+ def delete(id)
39
+ id.require!(as: :id)
40
+ api(:delete, "/users/#{id}")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,61 @@
1
+ module Quovo
2
+ class Config
3
+ attr_accessor :username
4
+ attr_accessor :password
5
+ attr_accessor :token_ttl # seconds
6
+ attr_accessor :request_timeout # seconds
7
+ attr_accessor :token_prefix
8
+ attr_accessor :token_storage
9
+ attr_accessor :debug
10
+ attr_accessor :strip_sensitive_params
11
+
12
+ DEFAULT_ENDPOINT = 'https://api.quovo.com/v2'.freeze
13
+
14
+ def initialize
15
+ @username = nil
16
+ @password = nil
17
+ @token_ttl = 60 * 60
18
+ @request_timeout = 60
19
+ @token_prefix = 'QUOVO-ACCESS-TOKEN'
20
+ @token_storage = default_memory_storage
21
+ @debug = false
22
+ @strip_sensitive_params = true
23
+ end
24
+
25
+ def endpoint
26
+ DEFAULT_ENDPOINT
27
+ end
28
+
29
+ def [](option)
30
+ send(option)
31
+ end
32
+
33
+ def self.configurator
34
+ @conf_mod ||= Module.new do
35
+ def configure
36
+ yield(config)
37
+ end
38
+
39
+ def config
40
+ @config ||= Config.new
41
+ end
42
+ end
43
+ end
44
+
45
+ def default_memory_storage
46
+ Object.new.tap do |o|
47
+ def o.storage
48
+ @storage ||= {}
49
+ end
50
+
51
+ def o.read(key)
52
+ storage[key]
53
+ end
54
+
55
+ def o.write(key, value)
56
+ storage[key] = value
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,7 @@
1
+ module Quovo
2
+ QuovoError = Class.new(StandardError)
3
+ ParamsError = Class.new(QuovoError)
4
+ HttpError = Class.new(QuovoError)
5
+ ForbiddenError = Class.new(HttpError)
6
+ NotFoundError = Class.new(HttpError)
7
+ end
@@ -0,0 +1,29 @@
1
+ module Quovo
2
+ module Fake
3
+ def fake?
4
+ @fake
5
+ end
6
+
7
+ # format of fake calls
8
+ # [
9
+ # [:get, "/accounts/id", {}, { id: 123, nickname: '123' ... }]
10
+ # ]
11
+ def fake!(fake_calls = [])
12
+ @fake_calls = fake_calls
13
+ @fake = true
14
+ Quovo.config.token_storage = Object.new.tap do |o|
15
+ def o.read(_)
16
+ ['FAKE-TOKEN', (Time.now.utc + 1_000).iso8601].join('|')
17
+ end
18
+ end
19
+ end
20
+
21
+ def real!
22
+ @fake = false
23
+ end
24
+
25
+ def fake_calls
26
+ @fake_calls
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module Quovo
2
+ module Hook
3
+ def hook(&callback)
4
+ hooks << callback
5
+ end
6
+
7
+ def hooks
8
+ @hooks ||= []
9
+ end
10
+
11
+ def clear_hooks!
12
+ @hooks = []
13
+ end
14
+
15
+ def run_hooks!(*args)
16
+ log_params = (args << Quovo.current_scope)
17
+ hooks.each do |hook|
18
+ hook.call(*log_params)
19
+ end
20
+ :ok
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module Quovo
2
+ module Models
3
+ class Account < Base
4
+ using Quovo::Refinements::ToTime
5
+
6
+ fields %i(
7
+ id
8
+ nickname
9
+ is_inactive
10
+ brokerage
11
+ brokerage_name
12
+ user
13
+ username
14
+ status
15
+ value
16
+ config_instructions
17
+ failures
18
+ update_count
19
+ opened
20
+ updated
21
+ )
22
+
23
+ undef :opened
24
+ def opened
25
+ @opened.to_time
26
+ end
27
+
28
+ undef :updated
29
+ def updated
30
+ @updated.to_time
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ module Quovo
2
+ module Models
3
+ class Base
4
+ extend ::Forwardable
5
+ def_delegators :to_h, :fetch, :to_json
6
+ def self.fields(fields = nil)
7
+ if fields
8
+ @fields = fields.map(&:to_sym)
9
+ @fields.each do |field|
10
+ attr_reader field
11
+ end
12
+ else
13
+ @fields || []
14
+ end
15
+ end
16
+
17
+ def initialize(props)
18
+ props.each do |field, value|
19
+ instance_variable_set("@#{field}", value)
20
+ end
21
+ end
22
+
23
+ def [](field)
24
+ send(field)
25
+ end
26
+
27
+ def to_hash
28
+ self.class.fields.map do |field|
29
+ [field, self[field]]
30
+ end.to_h
31
+ end
32
+
33
+ def to_h
34
+ to_hash
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ module Quovo
2
+ module Models
3
+ class Brokerage < Base
4
+ fields %i(
5
+ id
6
+ is_test
7
+ name
8
+ notes
9
+ password
10
+ username
11
+ website
12
+ )
13
+ end
14
+ end
15
+ end