plaider 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 784fe8b77ed4336121bfe0ca3627f7f1f10a64ca
4
+ data.tar.gz: 1e0ed9af526c2ac579e5524cc50f1280d0e5b2c6
5
+ SHA512:
6
+ metadata.gz: e8bd65c4519c147fa830b6f2aaa20f55e698f50d0875ff299dadec7eee5d13c76124e4216b4163fbf26ddc3ab49ad748e6902678538c83f0338afdb04f9b62b9
7
+ data.tar.gz: b6253345ea5d2ec1b3604ed65bcc5a27d250d6749ce114735ee78c69671f7a9a58b763976586e0fda3f9c02f42e524e263f7633edcc8c40ba0724eec52f82e68
@@ -0,0 +1,9 @@
1
+ /.bundle
2
+ /log/*.log
3
+ /tmp
4
+ /.idea
5
+ public/assets
6
+ .DS_Store
7
+ */.DS_Store
8
+ /coverage
9
+ /pkg
@@ -0,0 +1 @@
1
+ plaid
@@ -0,0 +1 @@
1
+ ruby-2.1.2
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ bundler_args: "--without development"
3
+ before_install:
4
+ - gem install bundler
5
+ rvm:
6
+ - 1.9.3
7
+ - 2.0.0
8
+ - 2.1.2
9
+ script:
10
+ - gem build plaider.gemspec
11
+ - gem install plaider-*
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :test do
4
+ gem 'rake'
5
+ gem 'minitest'
6
+ gem 'test-unit'
7
+ gem 'simplecov', require: false
8
+ gem 'coveralls', require: false
9
+ gem 'webmock'
10
+ end
11
+
12
+ gemspec
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ plaider (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.3.6)
10
+ coveralls (0.7.1)
11
+ multi_json (~> 1.3)
12
+ rest-client
13
+ simplecov (>= 0.7)
14
+ term-ansicolor
15
+ thor
16
+ crack (0.4.2)
17
+ safe_yaml (~> 1.0.0)
18
+ docile (1.1.5)
19
+ json (1.8.1)
20
+ mime-types (2.4.3)
21
+ minitest (5.4.2)
22
+ multi_json (1.10.1)
23
+ netrc (0.8.0)
24
+ power_assert (0.1.4)
25
+ rake (10.3.2)
26
+ rest-client (1.7.2)
27
+ mime-types (>= 1.16, < 3.0)
28
+ netrc (~> 0.7)
29
+ safe_yaml (1.0.4)
30
+ simplecov (0.9.1)
31
+ docile (~> 1.1.0)
32
+ multi_json (~> 1.0)
33
+ simplecov-html (~> 0.8.0)
34
+ simplecov-html (0.8.0)
35
+ term-ansicolor (1.3.0)
36
+ tins (~> 1.0)
37
+ test-unit (3.0.3)
38
+ power_assert
39
+ thor (0.19.1)
40
+ tins (1.3.3)
41
+ webmock (1.20.0)
42
+ addressable (>= 2.3.6)
43
+ crack (>= 0.3.2)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ bundler (~> 1.6)
50
+ coveralls
51
+ json (~> 1.8)
52
+ minitest
53
+ plaider!
54
+ rake
55
+ simplecov
56
+ test-unit
57
+ webmock
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Gene Drabkin
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.
@@ -0,0 +1,91 @@
1
+ # Plaider
2
+ [![Build Status](https://travis-ci.org/cloocher/plaider.png)](https://travis-ci.org/cloocher/plaider)
3
+ [![Coverage Status](https://coveralls.io/repos/cloocher/plaider/badge.png?branch=master)](https://coveralls.io/r/cloocher/plaider)
4
+ [![Gem Version](https://badge.fury.io/rb/plaider.png)](http://badge.fury.io/rb/plaider)
5
+
6
+ Plaid API client
7
+
8
+ ## Installation
9
+
10
+ Plaider is available through [Rubygems](http://rubygems.org/gems/plaider) and can be installed via:
11
+
12
+ ```
13
+ $ gem install plaider
14
+ ```
15
+
16
+ or add it to your Gemfile like this:
17
+
18
+ ```
19
+ gem 'plaider'
20
+ ```
21
+
22
+ ## Start Guide
23
+
24
+ Register for [Plaid](https://plaid.com/account/signup).
25
+
26
+ ## Usage
27
+
28
+ ```ruby
29
+ require 'plaider'
30
+
31
+ # Plaider global configuration
32
+ Plaider.configure do |config|
33
+ config.client_id = 'client id'
34
+ config.secret = 'secret'
35
+ end
36
+
37
+ # alternatively, specify configuration options when instantiating an Aggcat::Client
38
+ client = Plaider::Client.new(
39
+ client_id: 'client id',
40
+ secret: 'secret',
41
+ access_token: 'scope for all requests'
42
+ )
43
+
44
+ # create an scoped client by customer_id
45
+ client = Aggcat.scope(customer_id)
46
+
47
+ # get all supported financial institutions
48
+ client.institutions
49
+
50
+ # get details for Chase
51
+ client.institution('5301a99504977c52b60000d0')
52
+
53
+ # add new financial account to aggregate from Chase
54
+ response = client.add_user('chase', username, password, email)
55
+
56
+ # in case MFA is required
57
+ questions = response[:mfa]
58
+ answer = 'answer'
59
+ client.user_confirmation(answer)
60
+
61
+ # get already aggregated financial accounts and transactions
62
+ client.transactions
63
+
64
+ # get all aggregated account balances
65
+ client.balance
66
+
67
+ # get account transactions
68
+ start_date = Date.today - 30
69
+ end_date = Date.today # optional
70
+ pending = true # include pending transactions
71
+ client.transactions(account_id, start_date, end_date, pending)
72
+
73
+ # update user credentials
74
+ client.update_user(new_username, new_password)
75
+
76
+ # you can set scope inline for any request
77
+ Aggcat.scope(access_request).transactions
78
+
79
+ # delete user
80
+ client.delete_user
81
+ ```
82
+
83
+ ## Requirements
84
+
85
+ * Ruby 1.9.3 or higher
86
+
87
+ ## Copyright
88
+ Copyright (c) 2014 Gene Drabkin.
89
+ See [LICENSE][] for details.
90
+
91
+ [license]: LICENSE.md
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'lib' << 'test'
6
+ t.pattern = 'test/*/*_test.rb'
7
+ t.verbose = true
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,29 @@
1
+ require 'plaider/version'
2
+ require 'plaider/configurable'
3
+ require 'plaider/client'
4
+
5
+ module Plaider
6
+ class << self
7
+ include Plaider::Configurable
8
+
9
+ def scope(access_token = nil)
10
+ if !defined?(@access_token) || @access_token != access_token
11
+ @access_token = access_token
12
+ @client = Plaider::Client.new(options.merge({access_token: access_token}))
13
+ end
14
+ @client
15
+ end
16
+
17
+ def client
18
+ @client ||= Plaider::Client.new(options)
19
+ end
20
+
21
+ private
22
+
23
+ def method_missing(method_name, *args, &block)
24
+ return super unless client.respond_to?(method_name)
25
+ client.send(method_name, *args, &block)
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,146 @@
1
+ require 'net/https'
2
+ require 'json'
3
+
4
+ module Plaider
5
+ class Client
6
+
7
+ BASE_URL = 'https://tartan.plaid.com'
8
+
9
+ DATE_FORMAT = '%Y-%m-%d'
10
+
11
+ OPEN_TIMEOUT = 15
12
+ READ_TIMEOUT = 120
13
+
14
+ def initialize(options={})
15
+ options[:open_timeout] ||= OPEN_TIMEOUT
16
+ options[:read_timeout] ||= READ_TIMEOUT
17
+ options[:verbose] ||= false
18
+ Plaider::Configurable::KEYS.each do |key|
19
+ instance_variable_set(:"@#{key}", !options[key].nil? ? options[key] : Plaider.instance_variable_get(:"@#{key}"))
20
+ end
21
+ end
22
+
23
+ def institutions
24
+ get('/institutions')
25
+ end
26
+
27
+ def institution(institution_id)
28
+ validate(institution_id: institution_id)
29
+ get("/institutions/#{institution_id}")
30
+ end
31
+
32
+ def categories
33
+ get('/categories')
34
+ end
35
+
36
+ def category(category_id)
37
+ validate(category_id: category_id)
38
+ get("/categories/#{category_id}")
39
+ end
40
+
41
+ def entity(entity_id)
42
+ validate(entity_id: entity_id)
43
+ get("/entities/#{entity_id}")
44
+ end
45
+
46
+ def balance
47
+ post('/balance')
48
+ end
49
+
50
+ def add_user(institution_type, username, password, email)
51
+ validate(institution_type: institution_type, username: username, password: password, email: email)
52
+ response = post('/connect', {type: institution_type, username: username, password: password, email: email})
53
+ status_code = response[:status_code].to_i
54
+ @access_token = response[:result][:access_token] if [200, 201].include?(status_code)
55
+ response
56
+ end
57
+
58
+ def user_confirmation(mfa)
59
+ validate(mfa: mfa)
60
+ post('/connect/step', {mfa: mfa})
61
+ end
62
+
63
+ def transactions(account_id = nil, start_date = nil, end_date = nil, pending = false)
64
+ params = {}
65
+ params[:account_id] = account_id if account_id
66
+ params[:gte] = format_date(start_date)
67
+ params[:lte] = format_date(end_date)
68
+ params[:pending] = account_id if pending
69
+ post('/connect/get', params)
70
+ end
71
+
72
+ def update_user(username, password)
73
+ validate(username: username, password: password)
74
+ patch('/connect', {username: username, password: password})
75
+ end
76
+
77
+ def delete_user
78
+ response = delete('/connect')
79
+ @access_token = nil
80
+ response
81
+ end
82
+
83
+ protected
84
+
85
+ def get(path)
86
+ process(Net::HTTP::Get.new(path))
87
+ end
88
+
89
+ def post(path, params = {})
90
+ request = Net::HTTP::Post.new(path)
91
+ params.merge!(credentials)
92
+ params.merge!(access_token: @access_token)
93
+ request.set_form_data(params)
94
+ process(request)
95
+ end
96
+
97
+ def patch(path, params = {})
98
+ request = Net::HTTP::Patch.new(path)
99
+ params.merge!(credentials)
100
+ params.merge!(access_token: @access_token)
101
+ request.set_form_data(params)
102
+ process(request)
103
+ end
104
+
105
+ def delete(path)
106
+ request = Net::HTTP::Delete.new(path)
107
+ request.set_form_data(credentials.merge(access_token: @access_token))
108
+ puts request.inspect
109
+ process(request)
110
+ end
111
+
112
+ private
113
+
114
+ def validate(args)
115
+ args.each do |name, value|
116
+ if value.nil? || value.to_s.empty?
117
+ raise ArgumentError.new("#{name} is required")
118
+ end
119
+ end
120
+ end
121
+
122
+ def credentials
123
+ @credentials ||= {client_id: @client_id, secret: @secret}
124
+ end
125
+
126
+ def http
127
+ unless defined?(@http)
128
+ uri = URI.parse(BASE_URL)
129
+ @http = Net::HTTP.new(uri.host, uri.port)
130
+ @http.use_ssl = true
131
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
132
+ end
133
+ @http
134
+ end
135
+
136
+ def process(request)
137
+ response = http.request(request)
138
+ {status_code: response.code, result: JSON.parse(response.body, {symbolize_names: true})}
139
+ end
140
+
141
+ def format_date(date)
142
+ !!date ? date.strftime(DATE_FORMAT) : date
143
+ end
144
+ end
145
+
146
+ end
@@ -0,0 +1,20 @@
1
+ module Plaider
2
+ module Configurable
3
+
4
+ KEYS = [:client_id, :secret, :access_token, :open_timeout, :read_timeout]
5
+
6
+ attr_writer *KEYS
7
+
8
+ def configure
9
+ yield self
10
+ self
11
+ end
12
+
13
+ private
14
+
15
+ def options
16
+ Plaider::Configurable::KEYS.inject({}) { |hash, key| hash[key] = instance_variable_get(:"@#{key}"); hash }
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Plaider
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'plaider/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'plaider'
8
+ spec.version = Plaider::VERSION
9
+ spec.authors = ['Gene Drabkin']
10
+ spec.email = ['gene.drabkin@gmail.com']
11
+ spec.description = %q{Wraps Plaid API in a simple client}
12
+ spec.summary = %q{Ruby client for Plaid API}
13
+ spec.homepage = 'https://github.com/cloocher/plaider'
14
+ spec.license = 'MIT'
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.required_ruby_version = '>= 1.9.3'
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.6'
23
+ spec.add_development_dependency 'json', '~> 1.8'
24
+ end