dwolla_v2 0.4.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|