dwolla_v2 0.4.0 → 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/README.md +170 -57
- data/dwolla_v2.gemspec +1 -0
- data/lib/dwolla_v2.rb +6 -2
- data/lib/dwolla_v2/auth.rb +3 -4
- data/lib/dwolla_v2/client.rb +10 -2
- data/lib/dwolla_v2/error.rb +60 -15
- data/lib/dwolla_v2/middleware/{parse_iso8601_response_body.rb → deep_parse_iso8601_response_body.rb} +1 -1
- data/lib/dwolla_v2/middleware/{symbolize_response_body.rb → deep_super_hasherize_response_body.rb} +2 -2
- data/lib/dwolla_v2/middleware/handle_errors.rb +1 -1
- data/lib/dwolla_v2/response.rb +38 -12
- data/lib/dwolla_v2/super_hash.rb +12 -0
- data/lib/dwolla_v2/token.rb +8 -2
- data/lib/dwolla_v2/util.rb +12 -3
- data/lib/dwolla_v2/version.rb +1 -1
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2db53d14d332c6a4e13f74f7a9986e361b8509e7
|
4
|
+
data.tar.gz: 2c61640b59008a32254807efcc050f6d4256b277
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47f74c7ce9469f1b9f46422ac6339c3091784da493892e2e2a68ea9a46c6cb25082513eb7455d0d404c22934d62c8c5b967929394c1588762d23fabf3eb6007b
|
7
|
+
data.tar.gz: e0117f0296365e9abec6b5caaa1ceaa3f716fb5e75c6824c35b49f851490abe48dc14fa5aa362e2490af89cbf7ca90d1c267db0d83e552859c3237ea01f851ce
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ Dwolla V2 Ruby client. For the V1 Ruby client see [Dwolla/dwolla-ruby](https://g
|
|
11
11
|
Add this line to your application's Gemfile:
|
12
12
|
|
13
13
|
```ruby
|
14
|
-
gem 'dwolla_v2', '~> 0
|
14
|
+
gem 'dwolla_v2', '~> 1.0'
|
15
15
|
```
|
16
16
|
|
17
17
|
And then execute:
|
@@ -22,41 +22,122 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
$ gem install dwolla_v2
|
24
24
|
|
25
|
-
##
|
25
|
+
## `DwollaV2::Client`
|
26
26
|
|
27
|
-
|
27
|
+
### Basic usage
|
28
28
|
|
29
29
|
```ruby
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
# config/initializers/dwolla.rb
|
31
|
+
$dwolla = DwollaV2::Client.new(id: ENV["DWOLLA_ID"], secret: ENV["DWOLLA_SECRET"])
|
32
|
+
```
|
33
|
+
|
34
|
+
### Using the sandbox environment (optional)
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# config/initializers/dwolla.rb
|
38
|
+
$dwolla = DwollaV2::Client.new(id: ENV["DWOLLA_ID"], secret: ENV["DWOLLA_SECRET"]) do |config|
|
39
|
+
config.environment = :sandbox
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
`environment=` defaults to `:production` and accepts either a String or a Symbol.
|
44
|
+
|
45
|
+
### Configure an `on_grant` callback (optional)
|
46
|
+
|
47
|
+
An `on_grant` callback is useful for storing new tokens when they are granted. For example, you
|
48
|
+
may have a `YourTokenData` ActiveRecord model that you use to store your tokens. The `on_grant`
|
49
|
+
callback is called with the `DwollaV2::Token` that was just granted by the server. You can pass
|
50
|
+
this to the `create!` method of an ActiveRecord model to create a record using the token's data.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
# config/initializers/dwolla.rb
|
54
|
+
$dwolla = DwollaV2::Client.new(id: ENV["DWOLLA_ID"], secret: ENV["DWOLLA_SECRET"]) do |config|
|
55
|
+
config.on_grant {|t| YourTokenData.create!(t) }
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
It is highly recommended that you encrypt any token data you store using
|
60
|
+
[attr_encrypted][attr_encrypted] or [vault-rails][vault-rails] (if you use [Vault][vault]). These
|
61
|
+
are both configured in your ActiveRecord models.
|
62
|
+
|
63
|
+
[attr_encrypted]: https://github.com/attr-encrypted/attr_encrypted
|
64
|
+
[vault-rails]: https://github.com/hashicorp/vault-rails
|
65
|
+
[vault]: https://www.vaultproject.io/
|
66
|
+
|
67
|
+
### Configure Faraday (optional)
|
68
|
+
|
69
|
+
DwollaV2 uses [Faraday][faraday] to make HTTP requests. You can configure your own
|
70
|
+
[Faraday middleware][faraday-middleware] and adapter when configuring your client. Remember to
|
71
|
+
always include an adapter last, even if you want to use the default adapter.
|
72
|
+
|
73
|
+
[faraday]: https://github.com/lostisland/faraday
|
74
|
+
[faraday-middleware]: https://github.com/lostisland/faraday_middleware
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
# config/initializers/dwolla.rb
|
78
|
+
$dwolla = DwollaV2::Client.new(id: ENV["DWOLLA_ID"], secret: ENV["DWOLLA_SECRET"]) do |config|
|
79
|
+
config.faraday do |faraday|
|
36
80
|
faraday.response :logger
|
37
81
|
faraday.adapter Faraday.default_adapter
|
38
82
|
end
|
39
83
|
end
|
40
84
|
```
|
41
85
|
|
42
|
-
|
86
|
+
## `DwollaV2::Token`
|
43
87
|
|
44
|
-
|
88
|
+
Tokens can be used to make requests to the Dwolla V2 API. There are two types of tokens:
|
89
|
+
|
90
|
+
### Application tokens
|
91
|
+
|
92
|
+
Application tokens are used to access the API on behalf of a consumer application. API resources that
|
93
|
+
belong to an application include: `webhook-subscriptions`, `events`, and `webhooks`. Application
|
94
|
+
tokens can be created using the [`client_credentials`][client_credentials] OAuth grant type:
|
95
|
+
|
96
|
+
[client_credentials]: https://tools.ietf.org/html/rfc6749#section-4.4
|
45
97
|
|
46
98
|
```ruby
|
47
|
-
$dwolla.auths.client
|
99
|
+
application_token = $dwolla.auths.client
|
100
|
+
# => #<DwollaV2::Token client=#<DwollaV2::Client id="..." secret="..." environment=:sandbox> access_token="..." expires_in=3600 scope="...">
|
48
101
|
```
|
49
102
|
|
50
|
-
|
103
|
+
*Application tokens do not include a `refresh_token`. When an application token expires, generate
|
104
|
+
a new one using `$dwolla.auths.client`.*
|
105
|
+
|
106
|
+
### Account tokens
|
107
|
+
|
108
|
+
Account tokens are used to access the API on behalf of a Dwolla account. API resources that belong
|
109
|
+
to an account include `customers`, `funding-sources`, `documents`, `mass-payments`, `mass-payment-items`,
|
110
|
+
`transfers`, and `on-demand-authorizations`.
|
111
|
+
|
112
|
+
There are two ways to get an account token. One is by generating a token at
|
113
|
+
https://uat.dwolla.com/applications (sandbox) or https://www.dwolla.com/applications (production).
|
114
|
+
|
115
|
+
You can instantiate a generated token by doing the following:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
account_token = $dwolla.tokens.new access_token: "...", refresh_token: "..."
|
119
|
+
# => #<DwollaV2::Token client=#<DwollaV2::Client id="..." secret="..." environment=:sandbox> access_token="..." refresh_token="...">
|
120
|
+
```
|
121
|
+
|
122
|
+
The other way to get an account token is using the [`authorization_code`][authorization_code]
|
123
|
+
OAuth grant type. This flow works by redirecting a user to dwolla.com in order to get authorization
|
124
|
+
and sending them back to your website with an authorization code which can be exchanged for a token.
|
125
|
+
For example:
|
126
|
+
|
127
|
+
[authorization_code]: https://tools.ietf.org/html/rfc6749#section-4.1
|
51
128
|
|
52
129
|
```ruby
|
53
130
|
class YourAuthController < ApplicationController
|
131
|
+
# redirect the user to dwolla.com for authorization
|
54
132
|
def authorize
|
55
133
|
redirect_to auth.url
|
56
134
|
end
|
57
135
|
|
136
|
+
# https://yoursite.com/callback
|
58
137
|
def callback
|
59
|
-
|
138
|
+
# exchange the code for a token
|
139
|
+
token = auth.callback(params)
|
140
|
+
# => #<DwollaV2::Token client=#<DwollaV2::Client id="..." secret="..." environment=:sandbox> access_token="..." refresh_token="..." expires_in=3600 scope="ManageCustomers|Funding" account_id="...">
|
60
141
|
session[:account_id] = token.account_id
|
61
142
|
end
|
62
143
|
|
@@ -70,31 +151,51 @@ class YourAuthController < ApplicationController
|
|
70
151
|
end
|
71
152
|
```
|
72
153
|
|
73
|
-
|
154
|
+
### Refreshing tokens
|
155
|
+
|
156
|
+
Tokens with `refresh_token`s can be refreshed using `$dwolla.auths.refresh`, which takes a
|
157
|
+
`DwollaV2::Token` as its first argument and returns a new token.
|
74
158
|
|
75
159
|
```ruby
|
76
|
-
|
160
|
+
refreshed_token = $dwolla.auths.refresh(expired_token)
|
161
|
+
# => #<DwollaV2::Token client=#<DwollaV2::Client id="..." secret="..." environment=:sandbox> access_token="..." refresh_token="..." expires_in=3600 scope="ManageCustomers|Funding" account_id="...">
|
77
162
|
```
|
78
163
|
|
79
|
-
|
164
|
+
### Initializing tokens:
|
165
|
+
|
166
|
+
`DwollaV2::Token`s can be initialized with the following attributes:
|
80
167
|
|
81
168
|
```ruby
|
82
|
-
|
83
|
-
|
169
|
+
$dwolla.tokens.new access_token: "...",
|
170
|
+
refresh_token: "...",
|
171
|
+
expires_in: 123,
|
172
|
+
scope: "...",
|
173
|
+
account_id: "..."
|
174
|
+
#<DwollaV2::Token client=#<DwollaV2::Client id="..." secret="..." environment=:sandbox> access_token="..." refresh_token="..." expires_in=123 scope="..." account_id="...">
|
84
175
|
```
|
85
176
|
|
86
|
-
|
177
|
+
## Requests
|
178
|
+
|
179
|
+
`DwollaV2::Token`s can make requests using the `#get`, `#post`, `#put`, and `#delete` methods.
|
87
180
|
|
88
181
|
```ruby
|
89
|
-
|
90
|
-
token.
|
91
|
-
|
92
|
-
|
93
|
-
token.
|
94
|
-
|
182
|
+
# GET api.dwolla.com/resource?foo=bar
|
183
|
+
token.get "resource", foo: "bar"
|
184
|
+
|
185
|
+
# POST api.dwolla.com/resource {"foo":"bar"}
|
186
|
+
token.post "resource", foo: "bar"
|
187
|
+
|
188
|
+
# POST api.dwolla.com/resource multipart/form-data foo=...
|
189
|
+
token.post "resource", foo: Faraday::UploadIO.new("/path/to/bar.png", "image/png")
|
190
|
+
|
191
|
+
# PUT api.dwolla.com/resource {"foo":"bar"}
|
192
|
+
token.put "resource", foo: "bar"
|
193
|
+
|
194
|
+
# DELETE api.dwolla.com/resource
|
195
|
+
token.delete "resource"
|
95
196
|
```
|
96
197
|
|
97
|
-
|
198
|
+
Requests can also be made in parallel:
|
98
199
|
|
99
200
|
```ruby
|
100
201
|
foo, bar = nil
|
@@ -106,50 +207,52 @@ puts foo # only ready after `in_parallel` block has executed
|
|
106
207
|
puts bar # only ready after `in_parallel` block has executed
|
107
208
|
```
|
108
209
|
|
109
|
-
|
210
|
+
## Responses
|
110
211
|
|
111
212
|
Requests return a `DwollaV2::Response`.
|
112
213
|
|
113
|
-
**Response status**:
|
114
|
-
|
115
214
|
```ruby
|
116
|
-
res = token.
|
117
|
-
|
118
|
-
```
|
215
|
+
res = token.get "/"
|
216
|
+
# => #<DwollaV2::Response status=200 headers={"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:30:23 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; charset=UTF-8", "content-length"=>"150", "connection"=>"close", "set-cookie"=>"__cfduid=d9dcd0f586c166d36cbd45b992bdaa11b1459179023; expires=Tue, 28-Mar-17 15:30:23 GMT; path=/; domain=.dwolla.com; HttpOnly", "x-request-id"=>"69a4e612-5dae-4c52-a6a0-2f921e34a88a", "cf-ray"=>"28ac1f81875941e3-MSP"} {"_links"=>{"events"=>{"href"=>"https://api-uat.dwolla.com/events"}, "webhook-subscriptions"=>{"href"=>"https://api-uat.dwolla.com/webhook-subscriptions"}}}>
|
119
217
|
|
120
|
-
|
218
|
+
res.status
|
219
|
+
# => 200
|
121
220
|
|
122
|
-
|
123
|
-
|
124
|
-
|
221
|
+
res.headers
|
222
|
+
# => {"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:30:23 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; charset=UTF-8", "content-length"=>"150", "connection"=>"close", "set-cookie"=>"__cfduid=d9dcd0f586c166d36cbd45b992bdaa11b1459179023; expires=Tue, 28-Mar-17 15:30:23 GMT; path=/; domain=.dwolla.com; HttpOnly", "x-request-id"=>"69a4e612-5dae-4c52-a6a0-2f921e34a88a", "cf-ray"=>"28ac1f81875941e3-MSP"}
|
223
|
+
|
224
|
+
res._links.events.href
|
225
|
+
# => "https://api-uat.dwolla.com/events"
|
125
226
|
```
|
126
227
|
|
127
|
-
|
228
|
+
## Errors
|
128
229
|
|
129
|
-
|
130
|
-
|
230
|
+
If the server returns an error, a `DwollaV2::Error` (or one of its subclasses) will be raised.
|
231
|
+
`DwollaV2::Error`s are similar to `DwollaV2::Response`s.
|
131
232
|
|
132
233
|
```ruby
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
234
|
+
begin
|
235
|
+
token.get "/not-found"
|
236
|
+
rescue DwollaV2::NotFoundError => e
|
237
|
+
e
|
238
|
+
# => #<DwollaV2::NotFoundError status=404 headers={"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:35:32 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; profile=\"http://nocarrier.co.uk/profiles/vnd.error/\"; charset=UTF-8", "content-length"=>"69", "connection"=>"close", "set-cookie"=>"__cfduid=da1478bfdf3e56275cd8a6a741866ccce1459179332; expires=Tue, 28-Mar-17 15:35:32 GMT; path=/; domain=.dwolla.com; HttpOnly", "access-control-allow-origin"=>"*", "x-request-id"=>"667fca74-b53d-43db-bddd-50426a011881", "cf-ray"=>"28ac270abca64207-MSP"} {"code"=>"NotFound", "message"=>"The requested resource was not found."}>
|
239
|
+
|
240
|
+
e.status
|
241
|
+
# => 404
|
242
|
+
|
243
|
+
e.headers
|
244
|
+
# => {"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:35:32 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; profile=\"http://nocarrier.co.uk/profiles/vnd.error/\"; charset=UTF-8", "content-length"=>"69", "connection"=>"close", "set-cookie"=>"__cfduid=da1478bfdf3e56275cd8a6a741866ccce1459179332; expires=Tue, 28-Mar-17 15:35:32 GMT; path=/; domain=.dwolla.com; HttpOnly", "access-control-allow-origin"=>"*", "x-request-id"=>"667fca74-b53d-43db-bddd-50426a011881", "cf-ray"=>"28ac270abca64207-MSP"}
|
245
|
+
|
246
|
+
e.code
|
247
|
+
# => "NotFound"
|
248
|
+
rescue DwollaV2::Error => e
|
249
|
+
# ...
|
250
|
+
end
|
142
251
|
```
|
143
252
|
|
144
|
-
|
145
|
-
|
146
|
-
All errors inherit from `DwollaV2::Error`
|
253
|
+
### `DwollaV2::Error` subclasses:
|
147
254
|
|
148
|
-
|
149
|
-
|
150
|
-
Dwolla API errors have `code`, `message`, `_links`, and `_embedded` attributes.
|
151
|
-
|
152
|
-
**Error list:**
|
255
|
+
*See https://docsv2.dwolla.com/#errors for more info.*
|
153
256
|
|
154
257
|
- `DwollaV2::AccessDeniedError`
|
155
258
|
- `DwollaV2::InvalidCredentialsError`
|
@@ -176,6 +279,15 @@ Dwolla API errors have `code`, `message`, `_links`, and `_embedded` attributes.
|
|
176
279
|
- `DwollaV2::MethodNotAllowedError`
|
177
280
|
- `DwollaV2::ValidationError`
|
178
281
|
|
282
|
+
## Sample code
|
283
|
+
|
284
|
+
The following gist contains some sample bootstrapping code:
|
285
|
+
|
286
|
+
https://gist.github.com/sausman/df58a196b3bc0381b0e8
|
287
|
+
|
288
|
+
If you have any questions regarding your specific implementation we'll do our best to help
|
289
|
+
at https://discuss.dwolla.com/.
|
290
|
+
|
179
291
|
## Development
|
180
292
|
|
181
293
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -192,6 +304,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
192
304
|
|
193
305
|
## Changelog
|
194
306
|
|
307
|
+
- **1.0.0** - Refactor `Error` class to be more like response, add ability to access keys using methods.
|
195
308
|
- **0.4.0** - Refactor and document how `DwollaV2::Response` works
|
196
309
|
- **0.3.1** - better `DwollaV2::Error` error messages
|
197
310
|
- **0.3.0** - ISO8601 values in response body are converted to `Time` objects
|
data/dwolla_v2.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency "rspec", "~> 3.0"
|
25
25
|
spec.add_development_dependency "webmock", "~> 1.22"
|
26
26
|
|
27
|
+
spec.add_dependency "hashie", "~> 3.4"
|
27
28
|
spec.add_dependency "faraday", "~> 0.9"
|
28
29
|
spec.add_dependency "faraday_middleware", "~> 0.10"
|
29
30
|
end
|
data/lib/dwolla_v2.rb
CHANGED
@@ -2,6 +2,9 @@ require "base64"
|
|
2
2
|
require "uri"
|
3
3
|
require "json"
|
4
4
|
require "forwardable"
|
5
|
+
require "ostruct"
|
6
|
+
|
7
|
+
require "hashie"
|
5
8
|
|
6
9
|
require "faraday"
|
7
10
|
require "faraday_middleware"
|
@@ -14,9 +17,10 @@ require "dwolla_v2/token"
|
|
14
17
|
require "dwolla_v2/response"
|
15
18
|
require "dwolla_v2/error"
|
16
19
|
require "dwolla_v2/util"
|
20
|
+
require "dwolla_v2/super_hash"
|
17
21
|
|
18
|
-
require "dwolla_v2/middleware/
|
19
|
-
require "dwolla_v2/middleware/
|
22
|
+
require "dwolla_v2/middleware/deep_parse_iso8601_response_body"
|
23
|
+
require "dwolla_v2/middleware/deep_super_hasherize_response_body"
|
20
24
|
require "dwolla_v2/middleware/handle_errors"
|
21
25
|
|
22
26
|
# OAuth errors https://tools.ietf.org/html/rfc6749
|
data/lib/dwolla_v2/auth.rb
CHANGED
@@ -46,11 +46,10 @@ module DwollaV2
|
|
46
46
|
|
47
47
|
def self.request_token client, params
|
48
48
|
res = client.conn.post client.token_url, params
|
49
|
-
|
50
|
-
|
51
|
-
Error.raise! res_body
|
49
|
+
if !res.body.is_a?(Hash) || res.body.has_key?(:error)
|
50
|
+
Error.raise! res
|
52
51
|
else
|
53
|
-
token = Token.new client,
|
52
|
+
token = Token.new client, res.body
|
54
53
|
client.on_grant.call token unless client.on_grant.nil?
|
55
54
|
token
|
56
55
|
end
|
data/lib/dwolla_v2/client.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module DwollaV2
|
2
2
|
class Client
|
3
3
|
ENVIRONMENTS = {
|
4
|
-
:
|
4
|
+
:production => {
|
5
5
|
:auth_url => "https://www.dwolla.com/oauth/v2/authenticate",
|
6
6
|
:token_url => "https://www.dwolla.com/oauth/v2/token",
|
7
7
|
:api_url => "https://api.dwolla.com"
|
@@ -28,13 +28,14 @@ module DwollaV2
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def environment= env
|
31
|
+
env = :"#{env}"
|
31
32
|
raise ArgumentError.new "invalid environment" unless ENVIRONMENTS.has_key? env
|
32
33
|
@environment = env
|
33
34
|
end
|
34
35
|
|
35
36
|
def environment env = nil
|
36
37
|
self.environment = env unless env.nil?
|
37
|
-
@environment || :
|
38
|
+
@environment || :production
|
38
39
|
end
|
39
40
|
|
40
41
|
def on_grant &callback
|
@@ -51,6 +52,9 @@ module DwollaV2
|
|
51
52
|
@conn ||= Faraday.new do |f|
|
52
53
|
f.request :basic_auth, id, secret
|
53
54
|
f.request :url_encoded
|
55
|
+
f.use HandleErrors
|
56
|
+
f.use DeepSuperHasherizeResponseBody
|
57
|
+
f.use DeepParseIso8601ResponseBody
|
54
58
|
f.response :json, :content_type => /\bjson$/
|
55
59
|
faraday.call(f) if faraday
|
56
60
|
f.adapter Faraday.default_adapter unless faraday
|
@@ -68,5 +72,9 @@ module DwollaV2
|
|
68
72
|
def api_url
|
69
73
|
ENVIRONMENTS[environment][:api_url]
|
70
74
|
end
|
75
|
+
|
76
|
+
def inspect
|
77
|
+
Util.pretty_inspect self.class.name, id: id, secret: secret, environment: environment
|
78
|
+
end
|
71
79
|
end
|
72
80
|
end
|
data/lib/dwolla_v2/error.rb
CHANGED
@@ -1,38 +1,83 @@
|
|
1
1
|
module DwollaV2
|
2
2
|
class Error < StandardError
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
delegate [:status] => :@response
|
6
|
+
delegate [:to_s, :to_json] => :response_body
|
7
|
+
|
8
|
+
def self.raise! response
|
9
|
+
unless response.respond_to? :body
|
10
|
+
response = turn_into_response(response)
|
11
|
+
end
|
12
|
+
if response.body.is_a?(Hash) && klass = error_class(response.body[:error] || response.body[:code])
|
13
|
+
raise klass, response, caller
|
7
14
|
else
|
8
|
-
raise self
|
15
|
+
raise self, response, caller
|
9
16
|
end
|
10
17
|
end
|
11
18
|
|
12
|
-
def initialize
|
13
|
-
@
|
19
|
+
def initialize response
|
20
|
+
@response = response
|
14
21
|
end
|
15
22
|
|
16
|
-
def
|
17
|
-
@
|
23
|
+
def headers
|
24
|
+
if @response.respond_to? :response_headers
|
25
|
+
@response.response_headers
|
26
|
+
elsif @response.respond_to? :headers
|
27
|
+
@response.headers
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def respond_to? method, include_private = false
|
32
|
+
super || response_body.respond_to?(method)
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_a? klass
|
36
|
+
super || response_body.is_a?(klass)
|
37
|
+
end
|
38
|
+
|
39
|
+
def kind_of? klass
|
40
|
+
super || response_body.kind_of?(klass)
|
18
41
|
end
|
19
42
|
|
20
|
-
def
|
21
|
-
|
43
|
+
def == other
|
44
|
+
super || response_body == other
|
22
45
|
end
|
23
46
|
|
24
|
-
def method_missing method
|
25
|
-
|
47
|
+
def method_missing method, *args, &block
|
48
|
+
if response_body.respond_to? method
|
49
|
+
response_body.public_send method, *args, &block
|
50
|
+
else
|
51
|
+
super
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def message
|
56
|
+
to_s
|
26
57
|
end
|
27
58
|
|
28
|
-
def
|
29
|
-
|
59
|
+
def to_str
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
Util.pretty_inspect self.class.name, { status: status, headers: headers }, response_body
|
30
65
|
end
|
31
66
|
|
32
67
|
private
|
33
68
|
|
69
|
+
def self.turn_into_response response
|
70
|
+
OpenStruct.new status: nil,
|
71
|
+
headers: nil,
|
72
|
+
body: Util.deep_super_hasherize(Util.deep_parse_iso8601_values response)
|
73
|
+
end
|
74
|
+
|
34
75
|
def self.error_class error_code
|
35
76
|
DwollaV2.const_get "#{Util.classify(error_code).chomp("Error")}Error" rescue false
|
36
77
|
end
|
78
|
+
|
79
|
+
def response_body
|
80
|
+
@response.body
|
81
|
+
end
|
37
82
|
end
|
38
83
|
end
|
data/lib/dwolla_v2/middleware/{symbolize_response_body.rb → deep_super_hasherize_response_body.rb}
RENAMED
@@ -1,12 +1,12 @@
|
|
1
1
|
module DwollaV2
|
2
|
-
class
|
2
|
+
class DeepSuperHasherizeResponseBody
|
3
3
|
def initialize app
|
4
4
|
@app = app
|
5
5
|
end
|
6
6
|
|
7
7
|
def call request_env
|
8
8
|
@app.call(request_env).on_complete do |response_env|
|
9
|
-
response_env.body = Util.
|
9
|
+
response_env.body = Util.deep_super_hasherize(response_env.body)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
data/lib/dwolla_v2/response.rb
CHANGED
@@ -2,23 +2,49 @@ module DwollaV2
|
|
2
2
|
class Response
|
3
3
|
extend Forwardable
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
[:all?, :any?, :chunk, :chunk_while, :collect, :collect_concat, :count, :cycle,
|
8
|
-
:detect, :drop, :drop_while, :each_cons, :each_entry, :each_slice, :each_with_index,
|
9
|
-
:each_with_object, :entries, :find, :find_all, :find_index, :first, :flat_map, :grep,
|
10
|
-
:grep_v, :group_by, :include?, :inject, :lazy, :map, :max, :max_by, :member?, :min,
|
11
|
-
:min_by, :minmax, :minmax_by, :none?, :one?, :partition, :reduce, :reject,
|
12
|
-
:reverse_each, :select, :slice_after, :slice_before, :slice_when, :sort, :sort_by,
|
13
|
-
:take, :take_while, :to_a, :to_h, :zip]
|
14
|
-
|
15
|
-
delegate [:status, :headers, :body] => :@response
|
16
|
-
delegate [*ENUMERABLE, :==, :[]] => :response_body
|
5
|
+
delegate [:status] => :@response
|
6
|
+
delegate [:to_s, :to_json] => :response_body
|
17
7
|
|
18
8
|
def initialize response
|
19
9
|
@response = response
|
20
10
|
end
|
21
11
|
|
12
|
+
def headers
|
13
|
+
if @response.respond_to? :response_headers
|
14
|
+
@response.response_headers
|
15
|
+
elsif @response.respond_to? :headers
|
16
|
+
@response.headers
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to? method, include_private = false
|
21
|
+
super || response_body.respond_to?(method)
|
22
|
+
end
|
23
|
+
|
24
|
+
def is_a? klass
|
25
|
+
super || response_body.is_a?(klass)
|
26
|
+
end
|
27
|
+
|
28
|
+
def kind_of? klass
|
29
|
+
super || response_body.kind_of?(klass)
|
30
|
+
end
|
31
|
+
|
32
|
+
def == other
|
33
|
+
super || response_body == other
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing method, *args, &block
|
37
|
+
if response_body.respond_to? method
|
38
|
+
response_body.public_send method, *args, &block
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
Util.pretty_inspect self.class.name, { status: status, headers: headers }, response_body
|
46
|
+
end
|
47
|
+
|
22
48
|
private
|
23
49
|
|
24
50
|
def response_body
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module DwollaV2
|
2
|
+
class SuperHash < Hash
|
3
|
+
include Hashie::Extensions::KeyConversion
|
4
|
+
include Hashie::Extensions::MethodAccess
|
5
|
+
include Hashie::Extensions::IndifferentAccess
|
6
|
+
include Hashie::Extensions::DeepFetch
|
7
|
+
|
8
|
+
def == other
|
9
|
+
super(other) || super(self.class[other])
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/dwolla_v2/token.rb
CHANGED
@@ -43,6 +43,12 @@ module DwollaV2
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
def inspect
|
47
|
+
Util.pretty_inspect self.class.name,
|
48
|
+
client: client, access_token: access_token, refresh_token: refresh_token,
|
49
|
+
expires_in: expires_in, scope: scope, app_id: app_id, account_id: account_id
|
50
|
+
end
|
51
|
+
|
46
52
|
private
|
47
53
|
|
48
54
|
def conn
|
@@ -52,8 +58,8 @@ module DwollaV2
|
|
52
58
|
f.request :multipart
|
53
59
|
f.request :json
|
54
60
|
f.use HandleErrors
|
55
|
-
f.use
|
56
|
-
f.use
|
61
|
+
f.use DeepSuperHasherizeResponseBody
|
62
|
+
f.use DeepParseIso8601ResponseBody
|
57
63
|
f.response :json, :content_type => /\bjson$/
|
58
64
|
client.faraday.call(f) if client.faraday
|
59
65
|
f.adapter Faraday.default_adapter unless client.faraday
|
data/lib/dwolla_v2/util.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module DwollaV2
|
2
2
|
module Util
|
3
|
-
def self.
|
3
|
+
def self.deep_super_hasherize obj
|
4
4
|
if obj.is_a? Hash
|
5
|
-
|
5
|
+
SuperHash[obj.map{|k,v| [k, deep_super_hasherize(v)] }]
|
6
6
|
elsif obj.is_a? Array
|
7
|
-
obj.map {|i|
|
7
|
+
obj.map {|i| deep_super_hasherize(i) }
|
8
8
|
else
|
9
9
|
obj
|
10
10
|
end
|
@@ -27,5 +27,14 @@ module DwollaV2
|
|
27
27
|
i.sub(/^(.)/) { $1.capitalize }
|
28
28
|
end.join
|
29
29
|
end
|
30
|
+
|
31
|
+
def self.pretty_inspect klass_name, attrs, append = nil
|
32
|
+
[
|
33
|
+
"#<#{klass_name}",
|
34
|
+
attrs.map {|k,v| " #{k}=#{v.inspect}" unless v.nil? },
|
35
|
+
(" #{append.is_a?(String) ? append.inspect : append}" unless append.nil?),
|
36
|
+
">"
|
37
|
+
].flatten.join
|
38
|
+
end
|
30
39
|
end
|
31
40
|
end
|
data/lib/dwolla_v2/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dwolla_v2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Ausman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.22'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hashie
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.4'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.4'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: faraday
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,11 +153,12 @@ files:
|
|
139
153
|
- lib/dwolla_v2/errors/unsupported_grant_type_error.rb
|
140
154
|
- lib/dwolla_v2/errors/unsupported_response_type_error.rb
|
141
155
|
- lib/dwolla_v2/errors/validation_error.rb
|
156
|
+
- lib/dwolla_v2/middleware/deep_parse_iso8601_response_body.rb
|
157
|
+
- lib/dwolla_v2/middleware/deep_super_hasherize_response_body.rb
|
142
158
|
- lib/dwolla_v2/middleware/handle_errors.rb
|
143
|
-
- lib/dwolla_v2/middleware/parse_iso8601_response_body.rb
|
144
|
-
- lib/dwolla_v2/middleware/symbolize_response_body.rb
|
145
159
|
- lib/dwolla_v2/portal.rb
|
146
160
|
- lib/dwolla_v2/response.rb
|
161
|
+
- lib/dwolla_v2/super_hash.rb
|
147
162
|
- lib/dwolla_v2/token.rb
|
148
163
|
- lib/dwolla_v2/util.rb
|
149
164
|
- lib/dwolla_v2/version.rb
|