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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a3f2ab2081c5d4a1ba365f371b1c8784ef51a214
|
4
|
+
data.tar.gz: fb22a12ed8ccad14dc2f1bcda6e4f3dc2600bc1b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 91c9ee1b9a94cc1d3da4a1e159e8812c829763ce322ce3514a883dc4decb783d79d58b36f08ccd0b29f2ae854a1348e8c4c42c83be3445b6f8fbf63a8f68124a
|
7
|
+
data.tar.gz: 6ed3bb5cbb6f4d51d35f522e1101150d160f6cf0fff06406174ffc17432acac2a1ec6cba7ef9bf38ecaa5465f661e595a06ec8a483f113c9167ada6f9f49c52e
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2016 Canopy
|
2
|
+
|
3
|
+
The MIT License (MIT)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
# Quovo
|
2
|
+
|
3
|
+
Quovo RESTful API ruby client.
|
4
|
+
|
5
|
+
Read more about Quovo [here](https://new.quovo.com/api/docs/).
|
6
|
+
|
7
|
+
[](https://travis-ci.org/CanopyFA/quovo-ruby) [](https://coveralls.io/github/CanopyFA/quovo-ruby?branch=master)
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# Gemfile
|
13
|
+
gem 'quovo'
|
14
|
+
|
15
|
+
# run `bundle` in shell
|
16
|
+
```
|
17
|
+
|
18
|
+
## Configuration
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
Quovo.configurate do |config|
|
22
|
+
config.username = 'username'
|
23
|
+
config.password = 'password'
|
24
|
+
config.request_timeout = 30.seconds # by default 60 seconds
|
25
|
+
config.token_ttl = 2.hour # by default 1 hour
|
26
|
+
config.token_prefix = 'APP-NAME' # add custom prefix for token (helps to manage token list)
|
27
|
+
config.debug = true # if you want to see detailed logs
|
28
|
+
config.strip_sensitive_params = true # show [FILTERED] in logs for sensitive data
|
29
|
+
end
|
30
|
+
```
|
31
|
+
If you use Rails put this configuration into `config/initializers/quovo.rb`.
|
32
|
+
|
33
|
+
**Note:** By default client uses in-memory storage for access tokens. If you want to use persistent storage (redis, file)
|
34
|
+
you need to set storage object in configuration:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
Quovo.configurate do |config|
|
38
|
+
# ...
|
39
|
+
config.token_storage = Object.new.tap do |o|
|
40
|
+
def o.read(key)
|
41
|
+
# read value by key from storage
|
42
|
+
end
|
43
|
+
|
44
|
+
def o.write(key, value)
|
45
|
+
# write value by key to storage
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
You can also use `Rails.cache` as storage:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
Quovo.configurate do |config|
|
55
|
+
# ...
|
56
|
+
config.token_storage = Rails.cache
|
57
|
+
end
|
58
|
+
```
|
59
|
+
## Scopes and Hooks
|
60
|
+
|
61
|
+
You can scope quovo actions with user-defined hash that
|
62
|
+
could be used in hooks. Useful for action logging.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
Quovo.scope(user: user) do
|
66
|
+
Quovo.accounts.all
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Hook is a registered callback that invokes when web request happens.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
Quovo.hook do |path, method, params, status_code, response, elapsed_time, scope|
|
74
|
+
# path, method, params, status_code, response - attributes of web request
|
75
|
+
# elapsed_time - time in seconds of web request
|
76
|
+
# scope - user-defined hash, see docs about scopes
|
77
|
+
|
78
|
+
# log quovo action in database or file
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
## Quovo Api bindings
|
83
|
+
|
84
|
+
### Brokerages
|
85
|
+
```ruby
|
86
|
+
# Provides information on Quovo's supported brokerages
|
87
|
+
client.brokerages.all
|
88
|
+
# Information about single brokerage
|
89
|
+
client.brokerages.find(brokerage_id)
|
90
|
+
```
|
91
|
+
|
92
|
+
### Users
|
93
|
+
```ruby
|
94
|
+
client.users.all
|
95
|
+
client.users.find(user_id)
|
96
|
+
# Creates new user
|
97
|
+
# additional parameters
|
98
|
+
# name: - client's name
|
99
|
+
# email: - client's email. Cannot match another user's email
|
100
|
+
# phone: - client's phone number
|
101
|
+
client.users.create(username)
|
102
|
+
# Updates user information
|
103
|
+
# additional parameters
|
104
|
+
# name: - client's name
|
105
|
+
# email: - client's email. Cannot match another user's email
|
106
|
+
# phone: - client's phone number
|
107
|
+
client.users.update(user_id)
|
108
|
+
client.users.delete(user_id)
|
109
|
+
```
|
110
|
+
|
111
|
+
### Accounts
|
112
|
+
```ruby
|
113
|
+
client.accounts.all
|
114
|
+
client.accounts.find(id)
|
115
|
+
client.accounts.create(user_id, brokerage_id, username, password)
|
116
|
+
client.accounts.update(account_id, brokerage_id, username, password)
|
117
|
+
client.accounts.delete(account_id)
|
118
|
+
client.accounts.for_user(user_id)
|
119
|
+
# Init new sync
|
120
|
+
client.accounts.sync!(account_id)
|
121
|
+
# Get sync status
|
122
|
+
client.accounts.sync(account_id)
|
123
|
+
```
|
124
|
+
|
125
|
+
### Challenges
|
126
|
+
```ruby
|
127
|
+
client.challenges.for_account(account_id)
|
128
|
+
# for text, image questions
|
129
|
+
client.challenges.answers!(account_id, [{question: 'question text', answer: 'answer text'}])
|
130
|
+
# for choice questions
|
131
|
+
client.challenges.answers!(account_id, [{question: 'question text', answer: 0}])
|
132
|
+
```
|
133
|
+
|
134
|
+
### History
|
135
|
+
```ruby
|
136
|
+
client.history.all
|
137
|
+
# additional parameters
|
138
|
+
# start: - pointer to next set of items
|
139
|
+
# count: - max number of results to return
|
140
|
+
# start_date: - filters out history before this date
|
141
|
+
# end_date: - filters out history after this date
|
142
|
+
# start_id: - filters out history before this id
|
143
|
+
# end_id: - filters out history after this id
|
144
|
+
client.history.for_user(user_id)
|
145
|
+
client.history.for_account(account_id)
|
146
|
+
client.history.for_portfolio(portfolio_id)
|
147
|
+
```
|
148
|
+
|
149
|
+
### Portfolios
|
150
|
+
```ruby
|
151
|
+
client.portfolios.all
|
152
|
+
client.portfolios.find(portfolio_id)
|
153
|
+
client.portfolios.update(portfolio_id, nickname, portfolio_type, is_inactive)
|
154
|
+
client.portfolios.for_user(user_id)
|
155
|
+
client.portfolios.for_account(account_id)
|
156
|
+
```
|
157
|
+
|
158
|
+
### Positions
|
159
|
+
```ruby
|
160
|
+
# Load all positions
|
161
|
+
# additional parameters
|
162
|
+
# start: - pointer to next set of items
|
163
|
+
# count: - max number of results to return
|
164
|
+
client.positions.all
|
165
|
+
client.positions.for_user(user_id)
|
166
|
+
client.positions.for_account(account_id)
|
167
|
+
client.positions.for_portfolio(portfolio_id)
|
168
|
+
```
|
169
|
+
|
170
|
+
## Contributors
|
171
|
+
* Canopy Financials [https://www.canopyfa.com](https://www.canopyfa.com)
|
172
|
+
* Castle Digital Partners [https://castle.co](https://castle.co)
|
173
|
+
|
174
|
+
## License
|
175
|
+
|
176
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'quovo'
|
5
|
+
require 'pry'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
def reconfigure!
|
9
|
+
Quovo.configure do |config|
|
10
|
+
config.username = ARGV[0]
|
11
|
+
config.password = ARGV[1]
|
12
|
+
config.debug = true
|
13
|
+
config.token_storage = Object.new.tap do |o|
|
14
|
+
def o.path
|
15
|
+
'../.storage'
|
16
|
+
end
|
17
|
+
|
18
|
+
def o.storage
|
19
|
+
@storage ||= begin
|
20
|
+
data = File.exist?(path) ? File.read(path) : nil
|
21
|
+
data ? JSON.parse(data) : {}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def o.read(key)
|
26
|
+
storage[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
def o.write(key, value)
|
30
|
+
storage[key] = value
|
31
|
+
File.write(path, storage.to_json)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def reload!
|
38
|
+
Quovo.instance_variables.each do |var|
|
39
|
+
Quovo.instance_variable_set(var, nil)
|
40
|
+
end
|
41
|
+
Quovo.clear_hooks!
|
42
|
+
original_verbose = $VERBOSE
|
43
|
+
$VERBOSE = nil
|
44
|
+
$LOADED_FEATURES
|
45
|
+
.select { |file| file =~ %r{\/quovo\/} }
|
46
|
+
.each { |file| load(file) }
|
47
|
+
$VERBOSE = original_verbose
|
48
|
+
reconfigure!
|
49
|
+
'Reloaded!'
|
50
|
+
end
|
51
|
+
|
52
|
+
reconfigure!
|
53
|
+
Pry.start
|
data/lib/quovo.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'uri'
|
3
|
+
require 'time'
|
4
|
+
require 'base64'
|
5
|
+
require 'forwardable'
|
6
|
+
require 'net/http'
|
7
|
+
require 'openssl'
|
8
|
+
|
9
|
+
require 'quovo/refinements/require'
|
10
|
+
require 'quovo/refinements/to_time'
|
11
|
+
require 'quovo/refinements/cast'
|
12
|
+
require 'quovo/refinements/compact'
|
13
|
+
require 'quovo/refinements/permit'
|
14
|
+
require 'quovo/refinements/sensitive'
|
15
|
+
|
16
|
+
require 'quovo/errors'
|
17
|
+
|
18
|
+
require 'quovo/scope'
|
19
|
+
require 'quovo/hook'
|
20
|
+
require 'quovo/version'
|
21
|
+
require 'quovo/config'
|
22
|
+
require 'quovo/request'
|
23
|
+
require 'quovo/token'
|
24
|
+
require 'quovo/fake'
|
25
|
+
|
26
|
+
require 'quovo/models/base'
|
27
|
+
require 'quovo/models/account'
|
28
|
+
require 'quovo/models/brokerage'
|
29
|
+
require 'quovo/models/choice'
|
30
|
+
require 'quovo/models/image'
|
31
|
+
require 'quovo/models/challenge'
|
32
|
+
require 'quovo/models/progress'
|
33
|
+
require 'quovo/models/sync'
|
34
|
+
require 'quovo/models/portfolio'
|
35
|
+
require 'quovo/models/user'
|
36
|
+
require 'quovo/models/position'
|
37
|
+
require 'quovo/models/transaction'
|
38
|
+
|
39
|
+
require 'quovo/api/base'
|
40
|
+
require 'quovo/api/brokerages'
|
41
|
+
require 'quovo/api/accounts'
|
42
|
+
require 'quovo/api/challenges'
|
43
|
+
require 'quovo/api/portfolios'
|
44
|
+
require 'quovo/api/users'
|
45
|
+
require 'quovo/api/positions'
|
46
|
+
require 'quovo/api/history'
|
47
|
+
require 'quovo/api'
|
48
|
+
|
49
|
+
module Quovo
|
50
|
+
extend Quovo::Config.configurator
|
51
|
+
extend Quovo::Scope
|
52
|
+
extend Quovo::Hook
|
53
|
+
extend Quovo::Api
|
54
|
+
extend Quovo::Fake
|
55
|
+
|
56
|
+
def self.inspect
|
57
|
+
config.inspect
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.enable_logging
|
61
|
+
Quovo.hook do |path, method, params, status_code, response, elapsed_time, scope|
|
62
|
+
if Quovo.config.debug
|
63
|
+
log = [
|
64
|
+
'',
|
65
|
+
'Quovo Action:',
|
66
|
+
"path: #{path}",
|
67
|
+
"method: #{method}",
|
68
|
+
"params: #{params.inspect}",
|
69
|
+
"status_code: #{status_code}",
|
70
|
+
"response: #{response.inspect}",
|
71
|
+
"elapsed_time: #{elapsed_time}s",
|
72
|
+
"scope: #{scope.inspect}",
|
73
|
+
''
|
74
|
+
]
|
75
|
+
puts log.join("\n ")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
enable_logging
|
81
|
+
end
|
data/lib/quovo/api.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Quovo
|
2
|
+
module Api
|
3
|
+
def brokerages
|
4
|
+
@brokerages ||= Quovo::Api::Brokerages.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def accounts
|
8
|
+
@accounts ||= Quovo::Api::Accounts.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def challenges
|
12
|
+
@challenges ||= Quovo::Api::Challenges.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def portfolios
|
16
|
+
@portfolios ||= Quovo::Api::Portfolios.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def users
|
20
|
+
@users ||= Quovo::Api::Users.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def positions
|
24
|
+
@positions ||= Quovo::Api::Positions.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def history
|
28
|
+
@history ||= Quovo::Api::History.new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Quovo
|
2
|
+
module Api
|
3
|
+
class Accounts < Base
|
4
|
+
using Quovo::Refinements::Cast
|
5
|
+
using Quovo::Refinements::Require
|
6
|
+
using Quovo::Refinements::Permit
|
7
|
+
|
8
|
+
def all
|
9
|
+
api(:get, '/accounts')
|
10
|
+
.fetch('accounts')
|
11
|
+
.cast(Account)
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(id)
|
15
|
+
id.require!(as: :id)
|
16
|
+
api(:get, "/accounts/#{id}")
|
17
|
+
.fetch('account')
|
18
|
+
.cast(Account)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(params)
|
22
|
+
params.require!(:user, :brokerage, :username, :password)
|
23
|
+
api(:post, '/accounts', params)
|
24
|
+
.fetch('account')
|
25
|
+
.cast(Account)
|
26
|
+
end
|
27
|
+
|
28
|
+
def update(id, params)
|
29
|
+
id.require!(as: :id)
|
30
|
+
params
|
31
|
+
.permit!(:brokerage, :username, :password)
|
32
|
+
params.require!(:username, :password) if params[:username] || params[:password]
|
33
|
+
api(:put, "/accounts/#{id}", params)
|
34
|
+
.fetch('account')
|
35
|
+
.cast(Account)
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete(id)
|
39
|
+
id.require!(as: :id)
|
40
|
+
api(:delete, "/accounts/#{id}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def for_user(id)
|
44
|
+
id.require!(as: :id)
|
45
|
+
api(:get, "/users/#{id}/accounts")
|
46
|
+
.fetch('accounts')
|
47
|
+
.cast(Account)
|
48
|
+
end
|
49
|
+
|
50
|
+
def sync!(id)
|
51
|
+
id.require!(as: :id)
|
52
|
+
api(:post, "/accounts/#{id}/sync")
|
53
|
+
.fetch('sync')
|
54
|
+
.cast(Sync)
|
55
|
+
end
|
56
|
+
|
57
|
+
def sync(id)
|
58
|
+
id.require!(as: :id)
|
59
|
+
api(:get, "/accounts/#{id}/sync")
|
60
|
+
.fetch('sync')
|
61
|
+
.cast(Sync)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|