plaid 0.1.6 → 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 +4 -4
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +6 -0
- data/lib/plaid.rb +33 -10
- data/lib/plaid/auth.rb +12 -0
- data/lib/plaid/category/category.rb +36 -0
- data/lib/plaid/config.rb +3 -3
- data/lib/plaid/institution/institution.rb +36 -0
- data/lib/plaid/user/account/account.rb +23 -0
- data/lib/plaid/user/transaction/transaction.rb +19 -0
- data/lib/plaid/user/user.rb +54 -0
- data/lib/plaid/util.rb +67 -0
- data/lib/plaid/version.rb +3 -0
- data/plaid.gemspec +23 -0
- data/spec/plaid_spec.rb +252 -0
- data/spec/spec_helper.rb +4 -0
- metadata +40 -38
- data/lib/plaid/call.rb +0 -83
- data/lib/plaid/customer.rb +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 177a3e175a0defda0b9303805fe7f1f841d161d6
|
4
|
+
data.tar.gz: addfdce9ab4abe67eb4fa0478a83e56e5e09172e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 578a30e11a1c222c77a8c8cc6d5877400cd77d25cca5d898ade5e2f2fc5092d2fb248c874676999ab134911804724c7a23e3ef96af53b755056427c71cabe92c
|
7
|
+
data.tar.gz: e0e2213f052cb3641f715603ee0991cb24ee1edae76c5d913ecc3aabcddaf6bd7be5b10cd4841c4aeceff5209b6477a48080529ab12faaa25b35a2b9f525d674
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Justin Crites
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Plaid
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'plaid'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install plaid
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/plaid/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/plaid.rb
CHANGED
@@ -1,20 +1,43 @@
|
|
1
|
+
require 'plaid/version'
|
1
2
|
require 'plaid/config'
|
2
|
-
require 'plaid/
|
3
|
-
|
4
|
-
require '
|
3
|
+
require 'plaid/util'
|
4
|
+
|
5
|
+
require 'plaid/auth'
|
6
|
+
require 'plaid/user/user'
|
7
|
+
require 'plaid/institution/institution'
|
8
|
+
require 'plaid/category/category'
|
9
|
+
|
5
10
|
module Plaid
|
11
|
+
# Define an instance of the gem thus responding with one customer at a time
|
6
12
|
class << self
|
13
|
+
# Include the SDK methods
|
14
|
+
|
15
|
+
# Configures the gem with the public, private, and environment vars
|
7
16
|
include Plaid::Configure
|
8
17
|
|
9
|
-
#
|
10
|
-
|
11
|
-
|
18
|
+
# Include the utility classes used throughout the gem
|
19
|
+
include Plaid::Util
|
20
|
+
|
21
|
+
# Includes the method to authenticate the user. Defined in auth.rb
|
22
|
+
include Plaid::Auth
|
23
|
+
|
24
|
+
# Builds the user object and returns on successful authentication
|
25
|
+
def user(res)
|
26
|
+
@user = Plaid::User.new
|
27
|
+
@user.new(res)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Builds an institution object and returns when the institution details exist
|
31
|
+
def institution(res)
|
32
|
+
@institution = Plaid::Institution.new
|
33
|
+
@institution.new(res)
|
12
34
|
end
|
13
35
|
|
14
|
-
#
|
15
|
-
def
|
16
|
-
@
|
36
|
+
# Builds an institution object and returns when the category details exist
|
37
|
+
def category(res)
|
38
|
+
@category = Plaid::Category.new
|
39
|
+
@category.new(res)
|
17
40
|
end
|
18
41
|
|
19
42
|
end
|
20
|
-
end
|
43
|
+
end
|
data/lib/plaid/auth.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'plaid/util'
|
2
|
+
module Plaid
|
3
|
+
class Category
|
4
|
+
include Plaid::Util
|
5
|
+
|
6
|
+
attr_accessor(:type, :hierarchy, :id)
|
7
|
+
|
8
|
+
# Returns an instantiated category object, or an array of all categories
|
9
|
+
def new(id=nil)
|
10
|
+
res = get('categories',id)
|
11
|
+
id.nil? ? cat = instantiate_all_categories(res) : cat = instantiate_one_category(res)
|
12
|
+
cat
|
13
|
+
end
|
14
|
+
|
15
|
+
def instantiate_all_categories(res)
|
16
|
+
cat_array = []
|
17
|
+
res['category'].each do |cat|
|
18
|
+
@category = Category.new
|
19
|
+
cat_array << @category.build_category(cat)
|
20
|
+
end
|
21
|
+
cat_array
|
22
|
+
end
|
23
|
+
|
24
|
+
def instantiate_one_category(res)
|
25
|
+
@category = Category.new
|
26
|
+
@category.build_category(res)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def build_category(cat)
|
32
|
+
self.type = cat['type'], self.hierarchy = cat['hierarchy'], self.id = cat['id']
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/plaid/config.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Plaid
|
2
2
|
module Configure
|
3
|
-
attr_writer :customer_id, :secret
|
3
|
+
attr_writer :customer_id, :secret, :environment_location
|
4
4
|
|
5
|
-
KEYS = [:customer_id, :secret]
|
5
|
+
KEYS = [:customer_id, :secret, :environment_location]
|
6
6
|
|
7
7
|
def config
|
8
8
|
yield self
|
@@ -10,4 +10,4 @@ module Plaid
|
|
10
10
|
end
|
11
11
|
|
12
12
|
end
|
13
|
-
end
|
13
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'plaid/util'
|
2
|
+
module Plaid
|
3
|
+
class Institution
|
4
|
+
include Plaid::Util
|
5
|
+
|
6
|
+
attr_accessor(:id, :name, :type, :has_mfa, :mfa)
|
7
|
+
|
8
|
+
# Returns an instantiated category object, or an array of all categories
|
9
|
+
def new(id=nil)
|
10
|
+
res = get('institutions',id)
|
11
|
+
id.nil? ? cat = instantiate_all_institutions(res) : cat = instantiate_one_institution(res)
|
12
|
+
cat
|
13
|
+
end
|
14
|
+
|
15
|
+
def instantiate_all_institutions(res)
|
16
|
+
inst_array = []
|
17
|
+
res['institution'].each do |inst|
|
18
|
+
@institution = Institution.new
|
19
|
+
inst_array << @institution.build_institution(inst)
|
20
|
+
end
|
21
|
+
inst_array
|
22
|
+
end
|
23
|
+
|
24
|
+
def instantiate_one_institution(res)
|
25
|
+
@category = Institution.new
|
26
|
+
@category.build_institution(res)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def build_institution(cat)
|
32
|
+
self.id = cat['id'], self.name = cat['name'], self.type = cat['type'], self.has_mfa = cat['has_mfa'], self.mfa = cat['mfa']
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'plaid/util'
|
2
|
+
module Plaid
|
3
|
+
class Account
|
4
|
+
include Plaid::Util
|
5
|
+
# Define vars for user accounts
|
6
|
+
attr_accessor(:available_balance, :current_balance, :institution_type, :meta, :transactions, :numbers, :name)
|
7
|
+
|
8
|
+
# Instantiate a new account with the results of the successful API call
|
9
|
+
# Build an array of nested transactions, and return self if successful
|
10
|
+
def new(res)
|
11
|
+
begin
|
12
|
+
self.name = res['name'], self.available_balance = res['balance']['available'], self.current_balance = res['balance']['current'], self.institution_type = res['institution_type']
|
13
|
+
self.meta = res['meta'] if res['meta']
|
14
|
+
res['numbers'] ? self.numbers = res['numbers'] : self.numbers = 'Upgrade user to access routing information for this account'
|
15
|
+
rescue => e
|
16
|
+
error_handler(e)
|
17
|
+
else
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'plaid/util'
|
2
|
+
module Plaid
|
3
|
+
class Transaction
|
4
|
+
include Plaid::Util
|
5
|
+
# Define vars for user accounts
|
6
|
+
attr_accessor(:id, :account, :amount, :name, :meta, :location, :pending, :score, :type, :category, :category_id)
|
7
|
+
|
8
|
+
# Instantiate a new account with the results of the successful API call
|
9
|
+
# Build an array of nested transactions, and return self if successful
|
10
|
+
def new(res)
|
11
|
+
begin
|
12
|
+
self.id = res['_id'], self.account = res['_account'], self.amount = res['amount'], self.name = res['name'], self.meta = res['meta'], self.location = res['location'], self.pending = res['pending'], self.score = res['score'], self.type = res['type'], self.category = res['category'], self.category_id = res['category_id']
|
13
|
+
rescue => e
|
14
|
+
error_handler(e)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'account/account'
|
2
|
+
require_relative 'transaction/transaction'
|
3
|
+
require 'plaid/util'
|
4
|
+
module Plaid
|
5
|
+
class User
|
6
|
+
include Plaid::Util
|
7
|
+
|
8
|
+
# Define user vars
|
9
|
+
attr_accessor(:accounts, :transactions, :access_token)
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
self.accounts = []
|
13
|
+
self.transactions = []
|
14
|
+
self.access_token = ''
|
15
|
+
end
|
16
|
+
|
17
|
+
# Instantiate a new user with the results of the successful API call
|
18
|
+
# Build an array of nested accounts, and return self if successful
|
19
|
+
def new(res)
|
20
|
+
begin
|
21
|
+
self.access_token = res['access_token']
|
22
|
+
if res['msg'].nil?
|
23
|
+
res['accounts'].each do |account|
|
24
|
+
self.accounts << new_account(account)
|
25
|
+
end if res['accounts']
|
26
|
+
res['transactions'].each do |transaction|
|
27
|
+
self.transactions << new_transaction(transaction)
|
28
|
+
end if res['transactions']
|
29
|
+
else
|
30
|
+
self.accounts = res.msg, self.transactions = res.msg
|
31
|
+
end
|
32
|
+
rescue => e
|
33
|
+
error_handler(e)
|
34
|
+
else
|
35
|
+
self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
# Instantiate and build a new account object, return this to the accounts array
|
42
|
+
def new_account(account)
|
43
|
+
@account = Account.new
|
44
|
+
@account.new(account)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Instantiate and build a new account object, return this to the accounts array
|
48
|
+
def new_transaction(transaction)
|
49
|
+
@transaction = Transaction.new
|
50
|
+
@transaction.new(transaction)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/lib/plaid/util.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
module Plaid
|
5
|
+
module Util
|
6
|
+
|
7
|
+
def post(path,username,password,type,options={})
|
8
|
+
uri = build_uri(path)
|
9
|
+
res = Net::HTTP.post_form(uri, {client_id: self.instance_variable_get(:'@customer_id') ,secret: self.instance_variable_get(:'@secret'), username: username, password: password, type: type})
|
10
|
+
parse_response(res)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(path,id=nil)
|
14
|
+
uri = build_uri(path,id)
|
15
|
+
res = Net::HTTP.get(uri)
|
16
|
+
parse_response(res)
|
17
|
+
end
|
18
|
+
|
19
|
+
def error_handler(err,res=nil)
|
20
|
+
case err
|
21
|
+
when 'Bad Request'
|
22
|
+
raise 'The request was malformed. Did you check the API docs?'
|
23
|
+
when 'Unauthorized'
|
24
|
+
raise 'Access denied: Try using the correct credentials.'
|
25
|
+
when 'Request Failed'
|
26
|
+
raise 'Request Failed'
|
27
|
+
when 'Not Found'
|
28
|
+
raise 'Not Found'
|
29
|
+
else
|
30
|
+
raise err
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def build_uri(path,id=nil)
|
37
|
+
id ? URI.parse(self.instance_variable_get(:'@environment_location') + path + id) :
|
38
|
+
URI.parse(self.instance_variable_get(:'@environment_location') + path)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def parse_response(res)
|
44
|
+
body = JSON.parse(res.body)
|
45
|
+
case res.code
|
46
|
+
when '200'
|
47
|
+
return body
|
48
|
+
when '201'
|
49
|
+
return { msg: 'Requires further authentication', body: body}
|
50
|
+
when '400'
|
51
|
+
error_handler('Bad Request',res)
|
52
|
+
when '401'
|
53
|
+
error_handler('Unauthorized',res)
|
54
|
+
when '402'
|
55
|
+
if body['code'] == 1205
|
56
|
+
return { msg: 'User account is locked', body: body }
|
57
|
+
else
|
58
|
+
error_handler('Request Failed',res)
|
59
|
+
end
|
60
|
+
when '404'
|
61
|
+
error_handler('Not Found',res)
|
62
|
+
else
|
63
|
+
error_handler('Server Error',res)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/plaid.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'plaid/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'plaid'
|
7
|
+
spec.version = Plaid::VERSION
|
8
|
+
spec.authors = ['Justin Crites']
|
9
|
+
spec.email = ['crites.justin@gmail.com']
|
10
|
+
spec.summary = 'Ruby bindings for Plaid'
|
11
|
+
spec.description = 'Ruby gem wrapper for the Plaid API. Read more at the homepage, the wiki, or the plaid documentation.'
|
12
|
+
spec.homepage = 'https://github.com/plaid/plaid-ruby'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
21
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
22
|
+
spec.add_development_dependency 'rspec', '~>3.1'
|
23
|
+
end
|
data/spec/plaid_spec.rb
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
########## Plaid specs ##########
|
3
|
+
describe Plaid do
|
4
|
+
# Configuration specs - used in gem configuration
|
5
|
+
describe '.config' do
|
6
|
+
context 'has valid dev keys' do
|
7
|
+
Plaid.config do |p|
|
8
|
+
p.customer_id = 'test_id'
|
9
|
+
p.secret = 'test_secret'
|
10
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
11
|
+
end
|
12
|
+
res = Plaid.auth('connect','plaid_test','plaid_good','wells')
|
13
|
+
it { expect(res).to be instance_of Plaid::User }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'has valid production keys' do
|
17
|
+
Plaid.config do |p|
|
18
|
+
p.customer_id = 'test_id'
|
19
|
+
p.secret = 'test_secret'
|
20
|
+
p.environment_location = 'https://api.plaid.com/'
|
21
|
+
end
|
22
|
+
#TODO: Test production level credentials
|
23
|
+
res = Plaid.auth('connect','plaid_test','plaid_good','wells')
|
24
|
+
it { expect(res).to raise_error }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'has invalid dev keys' do
|
28
|
+
Plaid.config do |p|
|
29
|
+
p.customer_id = 'test_id'
|
30
|
+
p.secret = 'test_bad'
|
31
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
32
|
+
end
|
33
|
+
it { expect(Plaid.auth('connect','plaid_test','plaid_good','wells')).to raise_error }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'has invalid production keys' do
|
37
|
+
Plaid.config do |p|
|
38
|
+
p.customer_id = 'test_id'
|
39
|
+
p.secret = 'test_bad'
|
40
|
+
p.environment_location = 'https://api.plaid.com/'
|
41
|
+
end
|
42
|
+
it { expect(Plaid.auth('connect','plaid_test','plaid_good','wells')).to raise_error }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Authentication flow specs - returns Plaid::User
|
47
|
+
# TODO: Abstract the config from each section with the result in passing tests
|
48
|
+
describe '.auth' do
|
49
|
+
|
50
|
+
context 'has correct credentials for single factor auth, authenticates to the connect path' do
|
51
|
+
Plaid.config do |p|
|
52
|
+
p.customer_id = 'test_id'
|
53
|
+
p.secret = 'test_secret'
|
54
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
55
|
+
end
|
56
|
+
user = Plaid.auth('connect','plaid_test','plaid_good','wells')
|
57
|
+
it { expect(user.accounts.blank?).to be_false }
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'has correct credentials for single factor auth, authenticates to the auth path' do
|
61
|
+
Plaid.config do |p|
|
62
|
+
p.customer_id = 'test_id'
|
63
|
+
p.secret = 'test_secret'
|
64
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
65
|
+
end
|
66
|
+
user = Plaid.auth('auth','plaid_test','plaid_good','wells')
|
67
|
+
it { expect(user.accounts[0].numbers.nil?).to be_false }
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'has correct username, but incorrect password for single factor auth under auth path' do
|
71
|
+
Plaid.config do |p|
|
72
|
+
p.customer_id = 'test_id'
|
73
|
+
p.secret = 'test_secret'
|
74
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
75
|
+
end
|
76
|
+
it { expect(Plaid.auth('auth','plaid_test','plaid_bad','wells')).to raise_error }
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'has incorrect username under auth path' do
|
80
|
+
Plaid.config do |p|
|
81
|
+
p.customer_id = 'test_id'
|
82
|
+
p.secret = 'test_secret'
|
83
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
84
|
+
end
|
85
|
+
it { expect(Plaid.auth('auth','plaid_bad','plaid_bad','wells')).to raise_error }
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'has correct username, but incorrect password for single factor auth under connect path' do
|
89
|
+
Plaid.config do |p|
|
90
|
+
p.customer_id = 'test_id'
|
91
|
+
p.secret = 'test_secret'
|
92
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
93
|
+
end
|
94
|
+
it { expect(Plaid.auth('connect','plaid_test','plaid_bad','wells')).to raise_error }
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'has incorrect username under connect path' do
|
98
|
+
Plaid.config do |p|
|
99
|
+
p.customer_id = 'test_id'
|
100
|
+
p.secret = 'test_secret'
|
101
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
102
|
+
end
|
103
|
+
it { expect(Plaid.auth('connect','plaid_bad','plaid_bad','wells')).to raise_error }
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'has to enter MFA credentials' do
|
107
|
+
Plaid.config do |p|
|
108
|
+
p.customer_id = 'test_id'
|
109
|
+
p.secret = 'test_secret'
|
110
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
111
|
+
end
|
112
|
+
user = Plaid.auth('connect','plaid_selections', 'plaid_good','wells')
|
113
|
+
it { expect(user.accounts).to eq 'Requires further authentication' }
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'enters correct information with locked account' do
|
117
|
+
Plaid.config do |p|
|
118
|
+
p.customer_id = 'test_id'
|
119
|
+
p.secret = 'test_secret'
|
120
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
121
|
+
end
|
122
|
+
user = Plaid.auth('connect','plaid_selections', 'plaid_locked','wells')
|
123
|
+
it { expect(user.accounts).to eq 'User account is locked' }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
=begin
|
128
|
+
TODO: Finish up these tests
|
129
|
+
# Institution specs
|
130
|
+
describe Plaid::Institution do
|
131
|
+
|
132
|
+
context 'when institution is found' do
|
133
|
+
Plaid.config do |p|
|
134
|
+
p.customer_id = 'test_id'
|
135
|
+
p.secret = 'test_secret'
|
136
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
137
|
+
end
|
138
|
+
institution = Plaid.institution('amex')
|
139
|
+
it { expect(institution).to be instance_of Plaid::Institution }
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'when institution is not found' do
|
143
|
+
Plaid.config do |p|
|
144
|
+
p.customer_id = 'test_id'
|
145
|
+
p.secret = 'test_secret'
|
146
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
147
|
+
end
|
148
|
+
it { expect(Plaid.institution('dumb_bank')).to raise_error }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Category specs
|
153
|
+
describe Plaid::Category do
|
154
|
+
|
155
|
+
context 'when category is found' do
|
156
|
+
Plaid.config do |p|
|
157
|
+
p.customer_id = 'test_id'
|
158
|
+
p.secret = 'test_secret'
|
159
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
160
|
+
end
|
161
|
+
category = Plaid.category('17001013')
|
162
|
+
it { expect(category).to be instance_of Plaid::Category }
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'when category is not found' do
|
166
|
+
Plaid.config do |p|
|
167
|
+
p.customer_id = 'test_id'
|
168
|
+
p.secret = 'test_secret'
|
169
|
+
p.environment_location = 'https://tartan.plaid.com/'
|
170
|
+
end
|
171
|
+
it { expect(Plaid.category('dumb_cat')).to raise_error }
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
########## Plaid instantiated user specs ##########
|
178
|
+
|
179
|
+
describe Plaid::User do
|
180
|
+
subject(:success_user) { Plaid.auth('connect','plaid_test','plaid_good','wells') }
|
181
|
+
subject(:mfa_user) { Plaid.auth('connect','plaid_selections', 'plaid_good','wells') }
|
182
|
+
|
183
|
+
# MFA specs - after user is instantiated,
|
184
|
+
describe '#mfa_authentication' do
|
185
|
+
context 'has to enter another round of MFA credentials' do
|
186
|
+
res = user.mfa_authenticaiton('again')
|
187
|
+
expect(res).to eq 'Another round requested'
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'enters correct credentials for MFA auth and authenticates' do
|
191
|
+
user.mfa_authentication('tomato')
|
192
|
+
expect(user.accounts).to be_truthy
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'enters incorrect credentials for MFA auth' do
|
196
|
+
res = user.mfa_authentication('bad')
|
197
|
+
expect(res).to eq 'Incorrect answer to MFA'
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Upgrade specs - either pass or fails
|
202
|
+
describe '#upgrade' do
|
203
|
+
context 'auth upgrade is successful' do
|
204
|
+
user.upgrade('auth')
|
205
|
+
expect(user.auth).to be true
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'auth upgrade failed' do
|
209
|
+
user.upgrade('auth')
|
210
|
+
expect(user.auth).to be false
|
211
|
+
end
|
212
|
+
|
213
|
+
context 'connect upgrade is successful' do
|
214
|
+
user.upgrade('connect')
|
215
|
+
expect(user.auth).to be true
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'connect upgrade failed' do
|
219
|
+
user.upgrade('connect')
|
220
|
+
expect(user.auth).to be false
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Auth specs
|
225
|
+
describe '#auth' do
|
226
|
+
context 'has access and returns accounts' do
|
227
|
+
auth = user.auth
|
228
|
+
expect(auth).to be true
|
229
|
+
expect(user.accounts).to be_truthy
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'does not have access to auth' do
|
233
|
+
auth = user.auth
|
234
|
+
expect(auth).to be false
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Connect specs
|
239
|
+
describe '#connect' do
|
240
|
+
context 'has access and returns accounts' do
|
241
|
+
expect(user.connect).to be true
|
242
|
+
expect(user.transactions).to be_truthy
|
243
|
+
end
|
244
|
+
|
245
|
+
context 'does not have access to auth' do
|
246
|
+
expect(user.connect).to be false
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
=end
|
251
|
+
|
252
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,83 +1,83 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plaid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Crites
|
8
|
-
- Gamble McAdam
|
9
|
-
- Rahul Ramakrishnan
|
10
8
|
autorequire:
|
11
9
|
bindir: bin
|
12
10
|
cert_chain: []
|
13
|
-
date:
|
11
|
+
date: 2015-01-22 00:00:00.000000000 Z
|
14
12
|
dependencies:
|
15
13
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
14
|
+
name: bundler
|
17
15
|
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- - "
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
22
|
-
type: :
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
requirements:
|
26
|
-
- - ">="
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
version: '0'
|
29
|
-
- !ruby/object:Gem::Dependency
|
30
|
-
name: json
|
31
|
-
requirement: !ruby/object:Gem::Requirement
|
32
|
-
requirements:
|
33
|
-
- - ">="
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version: '0'
|
36
|
-
type: :runtime
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
37
21
|
prerelease: false
|
38
22
|
version_requirements: !ruby/object:Gem::Requirement
|
39
23
|
requirements:
|
40
|
-
- - "
|
24
|
+
- - "~>"
|
41
25
|
- !ruby/object:Gem::Version
|
42
|
-
version: '
|
26
|
+
version: '1.7'
|
43
27
|
- !ruby/object:Gem::Dependency
|
44
28
|
name: rake
|
45
29
|
requirement: !ruby/object:Gem::Requirement
|
46
30
|
requirements:
|
47
|
-
- - "
|
31
|
+
- - "~>"
|
48
32
|
- !ruby/object:Gem::Version
|
49
|
-
version: '0'
|
33
|
+
version: '10.0'
|
50
34
|
type: :development
|
51
35
|
prerelease: false
|
52
36
|
version_requirements: !ruby/object:Gem::Requirement
|
53
37
|
requirements:
|
54
|
-
- - "
|
38
|
+
- - "~>"
|
55
39
|
- !ruby/object:Gem::Version
|
56
|
-
version: '0'
|
40
|
+
version: '10.0'
|
57
41
|
- !ruby/object:Gem::Dependency
|
58
42
|
name: rspec
|
59
43
|
requirement: !ruby/object:Gem::Requirement
|
60
44
|
requirements:
|
61
|
-
- - "
|
45
|
+
- - "~>"
|
62
46
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
47
|
+
version: '3.1'
|
64
48
|
type: :development
|
65
49
|
prerelease: false
|
66
50
|
version_requirements: !ruby/object:Gem::Requirement
|
67
51
|
requirements:
|
68
|
-
- - "
|
52
|
+
- - "~>"
|
69
53
|
- !ruby/object:Gem::Version
|
70
|
-
version: '
|
71
|
-
description: Ruby
|
72
|
-
|
54
|
+
version: '3.1'
|
55
|
+
description: Ruby gem wrapper for the Plaid API. Read more at the homepage, the wiki,
|
56
|
+
or the plaid documentation.
|
57
|
+
email:
|
58
|
+
- crites.justin@gmail.com
|
73
59
|
executables: []
|
74
60
|
extensions: []
|
75
61
|
extra_rdoc_files: []
|
76
62
|
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
77
68
|
- lib/plaid.rb
|
78
|
-
- lib/plaid/
|
69
|
+
- lib/plaid/auth.rb
|
70
|
+
- lib/plaid/category/category.rb
|
79
71
|
- lib/plaid/config.rb
|
80
|
-
- lib/plaid/
|
72
|
+
- lib/plaid/institution/institution.rb
|
73
|
+
- lib/plaid/user/account/account.rb
|
74
|
+
- lib/plaid/user/transaction/transaction.rb
|
75
|
+
- lib/plaid/user/user.rb
|
76
|
+
- lib/plaid/util.rb
|
77
|
+
- lib/plaid/version.rb
|
78
|
+
- plaid.gemspec
|
79
|
+
- spec/plaid_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
81
|
homepage: https://github.com/plaid/plaid-ruby
|
82
82
|
licenses:
|
83
83
|
- MIT
|
@@ -101,5 +101,7 @@ rubyforge_project:
|
|
101
101
|
rubygems_version: 2.2.2
|
102
102
|
signing_key:
|
103
103
|
specification_version: 4
|
104
|
-
summary:
|
105
|
-
test_files:
|
104
|
+
summary: Ruby bindings for Plaid
|
105
|
+
test_files:
|
106
|
+
- spec/plaid_spec.rb
|
107
|
+
- spec/spec_helper.rb
|
data/lib/plaid/call.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
module Plaid
|
2
|
-
class Call
|
3
|
-
|
4
|
-
BASE_URL = 'https://tartan.plaid.com/'
|
5
|
-
|
6
|
-
# This initializes our instance variables, and sets up a new Customer class.
|
7
|
-
def initialize
|
8
|
-
Plaid::Configure::KEYS.each do |key|
|
9
|
-
instance_variable_set(:"@#{key}", Plaid.instance_variable_get(:"@#{key}"))
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
# This is a specific route for auth,
|
14
|
-
# it returns specific acct info
|
15
|
-
def add_account_auth(type, username, password, email)
|
16
|
-
parse_auth_response(post('/auth', type, username, password, email))
|
17
|
-
end
|
18
|
-
|
19
|
-
# This is a specific route for connect,
|
20
|
-
# it returns transaction information
|
21
|
-
def add_account_connect(type,username,password,email,options={})
|
22
|
-
parse_connect_response(post('/connect',type,username,password,email,options))
|
23
|
-
end
|
24
|
-
|
25
|
-
def get_place(id)
|
26
|
-
parse_place(get('entities/',id))
|
27
|
-
end
|
28
|
-
|
29
|
-
def get_institutions
|
30
|
-
JSON.parse(get('/institutions'))
|
31
|
-
end
|
32
|
-
|
33
|
-
protected
|
34
|
-
|
35
|
-
# Specific parser for auth response
|
36
|
-
def parse_auth_response(response)
|
37
|
-
parsed = JSON.parse(response)
|
38
|
-
case response.code
|
39
|
-
when 200
|
40
|
-
{code: response.code, access_token: parsed['access_token'], accounts: parsed['accounts']}
|
41
|
-
when 201
|
42
|
-
{code: response.code, type: parsed['type'], access_token: parsed['access_token'], mfa: parsed['mfa']}
|
43
|
-
else
|
44
|
-
{code: response.code, message: parsed}
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def parse_connect_response(response)
|
49
|
-
parsed = JSON.parse(response)
|
50
|
-
case response.code
|
51
|
-
when 200
|
52
|
-
{code: response.code, access_token: parsed['access_token'], accounts: parsed['accounts'], transactions: parsed['transactions']}
|
53
|
-
when 201
|
54
|
-
{code: response.code, type: parsed['type'], access_token: parsed['access_token'], mfa: parsed['mfa']}
|
55
|
-
else
|
56
|
-
{code: response.code, message: parsed}
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def parse_place(response)
|
61
|
-
parsed = JSON.parse(response)
|
62
|
-
{code: response.code, category: parsed['category'], name: parsed['name'], id: parsed['_id'], phone: parsed['meta']['contact']['telephone'], location: parsed['meta']['location']}
|
63
|
-
end
|
64
|
-
|
65
|
-
def parse_institutions(response)
|
66
|
-
parsed = JSON.parse(response)
|
67
|
-
{code: response.code, institutions: parsed}
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def post(path,type,username,password,email,options={})
|
73
|
-
url = BASE_URL + path
|
74
|
-
RestClient.post url, client_id: self.instance_variable_get(:'@customer_id') ,secret: self.instance_variable_get(:'@secret'), type: type ,credentials: {username: username, password: password} ,email: email, options: options
|
75
|
-
end
|
76
|
-
|
77
|
-
def get(path,id = nil)
|
78
|
-
url = BASE_URL + path + id.to_s
|
79
|
-
RestClient.get(url)
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
end
|
data/lib/plaid/customer.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
module Plaid
|
2
|
-
# This is used when a customer needs to be defined by the plaid access token.
|
3
|
-
# Abstracting as a class makes it easier since we wont have to redefine the access_token over and over.
|
4
|
-
class Customer
|
5
|
-
|
6
|
-
BASE_URL = 'https://tartan.plaid.com'
|
7
|
-
|
8
|
-
# This initializes our instance variables, and sets up a new Customer class.
|
9
|
-
def initialize
|
10
|
-
Plaid::Configure::KEYS.each do |key|
|
11
|
-
instance_variable_set(:"@#{key}", Plaid.instance_variable_get(:"@#{key}"))
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def mfa_auth_step(access_token, code, type)
|
16
|
-
parse_response(post('/auth/step', access_token, mfa: code, type: type), 0)
|
17
|
-
end
|
18
|
-
|
19
|
-
def mfa_connect_step(access_token,code)
|
20
|
-
parse_response(post('/connect/step', access_token, mfa: code),1)
|
21
|
-
end
|
22
|
-
|
23
|
-
def get_transactions(access_token)
|
24
|
-
parse_response(get('/connect', access_token),2)
|
25
|
-
end
|
26
|
-
|
27
|
-
def delete_account(access_token)
|
28
|
-
parse_response(delete('/connect', access_token),3)
|
29
|
-
end
|
30
|
-
|
31
|
-
protected
|
32
|
-
|
33
|
-
def parse_response(response,method)
|
34
|
-
parsed = JSON.parse(response)
|
35
|
-
if response.code == '200'
|
36
|
-
case method
|
37
|
-
when 0
|
38
|
-
{code: response.code, access_token: parsed['access_token'], accounts: parsed['accounts']}
|
39
|
-
when 1
|
40
|
-
{code: response.code, access_token: parsed['access_token'], accounts: parsed['accounts'], transactions: parsed['transactions']}
|
41
|
-
when 2
|
42
|
-
{code: response.code, transactions: parsed['transactions']}
|
43
|
-
else
|
44
|
-
{code: response.code, message: parsed}
|
45
|
-
end
|
46
|
-
else
|
47
|
-
{code: response.code, message: parsed}
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def get(path,access_token)
|
54
|
-
url = BASE_URL + path
|
55
|
-
RestClient.get(url, params: {client_id: self.instance_variable_get(:'@customer_id'), secret: self.instance_variable_get(:'@secret'), access_token: access_token})
|
56
|
-
end
|
57
|
-
|
58
|
-
def post(path,access_token,options={})
|
59
|
-
url = BASE_URL + path
|
60
|
-
RestClient.post url, client_id: self.instance_variable_get(:'@customer_id'), secret: self.instance_variable_get(:'@secret'), access_token: access_token, mfa: options[:mfa], type: options[:type]
|
61
|
-
end
|
62
|
-
|
63
|
-
def delete(path,access_token)
|
64
|
-
url = BASE_URL + path
|
65
|
-
RestClient.delete(url, params: {client_id: self.instance_variable_get(:'@customer_id'), secret: self.instance_variable_get(:'@secret'), access_token: access_token})
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|