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