quovo 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a3f2ab2081c5d4a1ba365f371b1c8784ef51a214
4
+ data.tar.gz: fb22a12ed8ccad14dc2f1bcda6e4f3dc2600bc1b
5
+ SHA512:
6
+ metadata.gz: 91c9ee1b9a94cc1d3da4a1e159e8812c829763ce322ce3514a883dc4decb783d79d58b36f08ccd0b29f2ae854a1348e8c4c42c83be3445b6f8fbf63a8f68124a
7
+ data.tar.gz: 6ed3bb5cbb6f4d51d35f522e1101150d160f6cf0fff06406174ffc17432acac2a1ec6cba7ef9bf38ecaa5465f661e595a06ec8a483f113c9167ada6f9f49c52e
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ .storage
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ coverage
12
+ .DS_Store
@@ -0,0 +1,12 @@
1
+ Style/Documentation:
2
+ Enabled: false
3
+ Metrics/MethodLength:
4
+ Enabled: false
5
+ Metrics/ParameterLists:
6
+ Enabled: false
7
+ Metrics/LineLength:
8
+ Enabled: false
9
+ Metrics/AbcSize:
10
+ Exclude:
11
+ - 'bin/console'
12
+ - 'tests/**'
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ cache: bundler
3
+ language: ruby
4
+ rvm:
5
+ - 2.2
6
+ before_install: gem install bundler
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'bundler'
6
+ gem 'rake'
7
+ gem 'minitest', require: false
8
+ gem 'coveralls', require: false
9
+ gem 'rubocop', require: false
10
+ gem 'pry', require: false
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.
@@ -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
+ [![Build Status](https://travis-ci.org/CanopyFA/quovo-ruby.svg?branch=master)](https://travis-ci.org/CanopyFA/quovo-ruby) [![Coverage Status](https://coveralls.io/repos/github/CanopyFA/quovo-ruby/badge.svg?branch=master)](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).
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'rubocop/rake_task'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'tests'
7
+ t.pattern = 'tests/**/*_test.rb'
8
+ end
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: [:rubocop, :test]
@@ -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
@@ -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
@@ -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