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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +12 -0
- data/.rubocop.yml +12 -0
- data/.travis.yml +6 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +176 -0
- data/Rakefile +12 -0
- data/bin/console +53 -0
- data/lib/quovo.rb +81 -0
- data/lib/quovo/api.rb +31 -0
- data/lib/quovo/api/accounts.rb +65 -0
- data/lib/quovo/api/base.rb +27 -0
- data/lib/quovo/api/brokerages.rb +21 -0
- data/lib/quovo/api/challenges.rb +28 -0
- data/lib/quovo/api/history.rb +36 -0
- data/lib/quovo/api/portfolios.rb +47 -0
- data/lib/quovo/api/positions.rb +30 -0
- data/lib/quovo/api/users.rb +44 -0
- data/lib/quovo/config.rb +61 -0
- data/lib/quovo/errors.rb +7 -0
- data/lib/quovo/fake.rb +29 -0
- data/lib/quovo/hook.rb +23 -0
- data/lib/quovo/models/account.rb +34 -0
- data/lib/quovo/models/base.rb +38 -0
- data/lib/quovo/models/brokerage.rb +15 -0
- data/lib/quovo/models/challenge.rb +34 -0
- data/lib/quovo/models/choice.rb +10 -0
- data/lib/quovo/models/image.rb +10 -0
- data/lib/quovo/models/portfolio.rb +50 -0
- data/lib/quovo/models/position.rb +36 -0
- data/lib/quovo/models/progress.rb +11 -0
- data/lib/quovo/models/sync.rb +20 -0
- data/lib/quovo/models/transaction.rb +37 -0
- data/lib/quovo/models/user.rb +14 -0
- data/lib/quovo/refinements/cast.rb +22 -0
- data/lib/quovo/refinements/compact.rb +11 -0
- data/lib/quovo/refinements/permit.rb +11 -0
- data/lib/quovo/refinements/require.rb +20 -0
- data/lib/quovo/refinements/sensitive.rb +38 -0
- data/lib/quovo/refinements/to_time.rb +18 -0
- data/lib/quovo/request.rb +114 -0
- data/lib/quovo/scope.rb +19 -0
- data/lib/quovo/token.rb +70 -0
- data/lib/quovo/version.rb +3 -0
- data/quovo.gemspec +19 -0
- 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
|
data/lib/quovo/config.rb
ADDED
@@ -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
|
data/lib/quovo/errors.rb
ADDED
data/lib/quovo/fake.rb
ADDED
@@ -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
|
data/lib/quovo/hook.rb
ADDED
@@ -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
|