gocardless_pro 2.23.0 → 2.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +23 -4
  3. data/lib/gocardless_pro.rb +27 -0
  4. data/lib/gocardless_pro/api_service.rb +4 -0
  5. data/lib/gocardless_pro/client.rb +41 -1
  6. data/lib/gocardless_pro/error/authentication_error.rb +4 -0
  7. data/lib/gocardless_pro/error/permission_error.rb +4 -0
  8. data/lib/gocardless_pro/error/rate_limit_error.rb +4 -0
  9. data/lib/gocardless_pro/middlewares/raise_gocardless_errors.rb +12 -1
  10. data/lib/gocardless_pro/resources/bank_authorisation.rb +85 -0
  11. data/lib/gocardless_pro/resources/billing_request.rb +108 -0
  12. data/lib/gocardless_pro/resources/billing_request_flow.rb +72 -0
  13. data/lib/gocardless_pro/resources/billing_request_template.rb +68 -0
  14. data/lib/gocardless_pro/resources/creditor.rb +2 -3
  15. data/lib/gocardless_pro/resources/event.rb +12 -0
  16. data/lib/gocardless_pro/resources/institution.rb +45 -0
  17. data/lib/gocardless_pro/resources/payer_authorisation.rb +131 -0
  18. data/lib/gocardless_pro/resources/payout_item.rb +4 -0
  19. data/lib/gocardless_pro/resources/scenario_simulator.rb +42 -0
  20. data/lib/gocardless_pro/resources/tax_rate.rb +3 -0
  21. data/lib/gocardless_pro/resources/webhook.rb +62 -0
  22. data/lib/gocardless_pro/services/bank_authorisations_service.rb +80 -0
  23. data/lib/gocardless_pro/services/billing_request_flows_service.rb +70 -0
  24. data/lib/gocardless_pro/services/billing_request_templates_service.rb +131 -0
  25. data/lib/gocardless_pro/services/billing_requests_service.rb +352 -0
  26. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +0 -4
  27. data/lib/gocardless_pro/services/creditors_service.rb +0 -2
  28. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +0 -4
  29. data/lib/gocardless_pro/services/customers_service.rb +0 -2
  30. data/lib/gocardless_pro/services/instalment_schedules_service.rb +0 -6
  31. data/lib/gocardless_pro/services/institutions_service.rb +56 -0
  32. data/lib/gocardless_pro/services/mandate_imports_service.rb +0 -6
  33. data/lib/gocardless_pro/services/mandates_service.rb +0 -6
  34. data/lib/gocardless_pro/services/payer_authorisations_service.rb +202 -0
  35. data/lib/gocardless_pro/services/payments_service.rb +0 -6
  36. data/lib/gocardless_pro/services/redirect_flows_service.rb +0 -4
  37. data/lib/gocardless_pro/services/refunds_service.rb +0 -2
  38. data/lib/gocardless_pro/services/scenario_simulators_service.rb +170 -0
  39. data/lib/gocardless_pro/services/subscriptions_service.rb +10 -13
  40. data/lib/gocardless_pro/services/webhooks_service.rb +111 -0
  41. data/lib/gocardless_pro/version.rb +1 -1
  42. data/spec/api_service_spec.rb +12 -1
  43. data/spec/middlewares/raise_gocardless_errors_spec.rb +30 -0
  44. data/spec/resources/bank_authorisation_spec.rb +259 -0
  45. data/spec/resources/billing_request_flow_spec.rb +219 -0
  46. data/spec/resources/billing_request_spec.rb +782 -0
  47. data/spec/resources/billing_request_template_spec.rb +502 -0
  48. data/spec/resources/institution_spec.rb +103 -0
  49. data/spec/resources/payer_authorisation_spec.rb +418 -0
  50. data/spec/resources/scenario_simulator_spec.rb +63 -0
  51. data/spec/resources/webhook_spec.rb +323 -0
  52. data/spec/services/bank_authorisations_service_spec.rb +353 -0
  53. data/spec/services/billing_request_flows_service_spec.rb +253 -0
  54. data/spec/services/billing_request_templates_service_spec.rb +789 -0
  55. data/spec/services/billing_requests_service_spec.rb +1082 -0
  56. data/spec/services/creditor_bank_accounts_service_spec.rb +0 -13
  57. data/spec/services/creditors_service_spec.rb +0 -13
  58. data/spec/services/customer_bank_accounts_service_spec.rb +0 -13
  59. data/spec/services/customers_service_spec.rb +0 -13
  60. data/spec/services/instalment_schedules_service_spec.rb +0 -26
  61. data/spec/services/institutions_service_spec.rb +223 -0
  62. data/spec/services/mandate_imports_service_spec.rb +0 -13
  63. data/spec/services/mandates_service_spec.rb +0 -13
  64. data/spec/services/payer_authorisations_service_spec.rb +559 -0
  65. data/spec/services/payments_service_spec.rb +0 -13
  66. data/spec/services/redirect_flows_service_spec.rb +0 -13
  67. data/spec/services/refunds_service_spec.rb +0 -13
  68. data/spec/services/scenario_simulators_service_spec.rb +74 -0
  69. data/spec/services/subscriptions_service_spec.rb +0 -13
  70. data/spec/services/webhooks_service_spec.rb +545 -0
  71. metadata +54 -3
@@ -222,19 +222,6 @@ describe GoCardlessPro::Services::PaymentsService do
222
222
  to raise_error(GoCardlessPro::IdempotencyConflict)
223
223
  end
224
224
  end
225
-
226
- context 'with on_idempotency_conflict: :unknown' do
227
- let(:client) do
228
- GoCardlessPro::Client.new(
229
- access_token: 'SECRET_TOKEN',
230
- on_idempotency_conflict: :unknown
231
- )
232
- end
233
-
234
- it 'raises an ArgumentError' do
235
- expect { post_create_response }.to raise_error(ArgumentError)
236
- end
237
- end
238
225
  end
239
226
  end
240
227
 
@@ -207,19 +207,6 @@ describe GoCardlessPro::Services::RedirectFlowsService do
207
207
  to raise_error(GoCardlessPro::IdempotencyConflict)
208
208
  end
209
209
  end
210
-
211
- context 'with on_idempotency_conflict: :unknown' do
212
- let(:client) do
213
- GoCardlessPro::Client.new(
214
- access_token: 'SECRET_TOKEN',
215
- on_idempotency_conflict: :unknown
216
- )
217
- end
218
-
219
- it 'raises an ArgumentError' do
220
- expect { post_create_response }.to raise_error(ArgumentError)
221
- end
222
- end
223
210
  end
224
211
  end
225
212
 
@@ -202,19 +202,6 @@ describe GoCardlessPro::Services::RefundsService do
202
202
  to raise_error(GoCardlessPro::IdempotencyConflict)
203
203
  end
204
204
  end
205
-
206
- context 'with on_idempotency_conflict: :unknown' do
207
- let(:client) do
208
- GoCardlessPro::Client.new(
209
- access_token: 'SECRET_TOKEN',
210
- on_idempotency_conflict: :unknown
211
- )
212
- end
213
-
214
- it 'raises an ArgumentError' do
215
- expect { post_create_response }.to raise_error(ArgumentError)
216
- end
217
- end
218
205
  end
219
206
  end
220
207
 
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::ScenarioSimulatorsService do
4
+ let(:client) do
5
+ GoCardlessPro::Client.new(
6
+ access_token: 'SECRET_TOKEN'
7
+ )
8
+ end
9
+
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
12
+ describe '#run' do
13
+ subject(:post_response) { client.scenario_simulators.run(resource_id) }
14
+
15
+ let(:resource_id) { 'ABC123' }
16
+
17
+ let!(:stub) do
18
+ # /scenario_simulators/%v/actions/run
19
+ stub_url = '/scenario_simulators/:identity/actions/run'.gsub(':identity', resource_id)
20
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
21
+ body: {
22
+ 'scenario_simulators' => {
23
+
24
+ 'id' => 'id-input',
25
+ },
26
+ }.to_json,
27
+ headers: response_headers
28
+ )
29
+ end
30
+
31
+ it 'wraps the response and calls the right endpoint' do
32
+ expect(post_response).to be_a(GoCardlessPro::Resources::ScenarioSimulator)
33
+
34
+ expect(stub).to have_been_requested
35
+ end
36
+
37
+ describe 'retry behaviour' do
38
+ it "doesn't retry errors" do
39
+ stub_url = '/scenario_simulators/:identity/actions/run'.gsub(':identity', resource_id)
40
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
41
+ to_timeout
42
+
43
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
44
+ expect(stub).to have_been_requested
45
+ end
46
+ end
47
+
48
+ context 'when the request needs a body and custom header' do
49
+ let(:body) { { foo: 'bar' } }
50
+ let(:headers) { { 'Foo' => 'Bar' } }
51
+ subject(:post_response) { client.scenario_simulators.run(resource_id, body, headers) }
52
+
53
+ let(:resource_id) { 'ABC123' }
54
+
55
+ let!(:stub) do
56
+ # /scenario_simulators/%v/actions/run
57
+ stub_url = '/scenario_simulators/:identity/actions/run'.gsub(':identity', resource_id)
58
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
59
+ with(
60
+ body: { foo: 'bar' },
61
+ headers: { 'Foo' => 'Bar' }
62
+ ).to_return(
63
+ body: {
64
+ 'scenario_simulators' => {
65
+
66
+ 'id' => 'id-input',
67
+ },
68
+ }.to_json,
69
+ headers: response_headers
70
+ )
71
+ end
72
+ end
73
+ end
74
+ end
@@ -257,19 +257,6 @@ describe GoCardlessPro::Services::SubscriptionsService do
257
257
  to raise_error(GoCardlessPro::IdempotencyConflict)
258
258
  end
259
259
  end
260
-
261
- context 'with on_idempotency_conflict: :unknown' do
262
- let(:client) do
263
- GoCardlessPro::Client.new(
264
- access_token: 'SECRET_TOKEN',
265
- on_idempotency_conflict: :unknown
266
- )
267
- end
268
-
269
- it 'raises an ArgumentError' do
270
- expect { post_create_response }.to raise_error(ArgumentError)
271
- end
272
- end
273
260
  end
274
261
  end
275
262
 
@@ -0,0 +1,545 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::WebhooksService do
4
+ let(:client) do
5
+ GoCardlessPro::Client.new(
6
+ access_token: 'SECRET_TOKEN'
7
+ )
8
+ end
9
+
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
12
+ describe '#list' do
13
+ describe 'with no filters' do
14
+ subject(:get_list_response) { client.webhooks.list }
15
+
16
+ let(:body) do
17
+ {
18
+ 'webhooks' => [{
19
+
20
+ 'created_at' => 'created_at-input',
21
+ 'id' => 'id-input',
22
+ 'is_test' => 'is_test-input',
23
+ 'request_body' => 'request_body-input',
24
+ 'request_headers' => 'request_headers-input',
25
+ 'response_body' => 'response_body-input',
26
+ 'response_body_truncated' => 'response_body_truncated-input',
27
+ 'response_code' => 'response_code-input',
28
+ 'response_headers' => 'response_headers-input',
29
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
30
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
31
+ 'successful' => 'successful-input',
32
+ 'url' => 'url-input',
33
+ }],
34
+ meta: {
35
+ cursors: {
36
+ before: nil,
37
+ after: 'ABC123',
38
+ },
39
+ },
40
+ }.to_json
41
+ end
42
+
43
+ before do
44
+ stub_request(:get, %r{.*api.gocardless.com/webhooks}).to_return(
45
+ body: body,
46
+ headers: response_headers
47
+ )
48
+ end
49
+
50
+ it 'wraps each item in the resource class' do
51
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::Webhook)
52
+
53
+ expect(get_list_response.records.first.created_at).to eq('created_at-input')
54
+
55
+ expect(get_list_response.records.first.id).to eq('id-input')
56
+
57
+ expect(get_list_response.records.first.is_test).to eq('is_test-input')
58
+
59
+ expect(get_list_response.records.first.request_body).to eq('request_body-input')
60
+
61
+ expect(get_list_response.records.first.request_headers).to eq('request_headers-input')
62
+
63
+ expect(get_list_response.records.first.response_body).to eq('response_body-input')
64
+
65
+ expect(get_list_response.records.first.response_body_truncated).to eq('response_body_truncated-input')
66
+
67
+ expect(get_list_response.records.first.response_code).to eq('response_code-input')
68
+
69
+ expect(get_list_response.records.first.response_headers).to eq('response_headers-input')
70
+
71
+ expect(get_list_response.records.first.response_headers_content_truncated).to eq('response_headers_content_truncated-input')
72
+
73
+ expect(get_list_response.records.first.response_headers_count_truncated).to eq('response_headers_count_truncated-input')
74
+
75
+ expect(get_list_response.records.first.successful).to eq('successful-input')
76
+
77
+ expect(get_list_response.records.first.url).to eq('url-input')
78
+ end
79
+
80
+ it 'exposes the cursors for before and after' do
81
+ expect(get_list_response.before).to eq(nil)
82
+ expect(get_list_response.after).to eq('ABC123')
83
+ end
84
+
85
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
86
+
87
+ describe 'retry behaviour' do
88
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
89
+
90
+ it 'retries timeouts' do
91
+ stub = stub_request(:get, %r{.*api.gocardless.com/webhooks}).
92
+ to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
93
+
94
+ get_list_response
95
+ expect(stub).to have_been_requested.twice
96
+ end
97
+
98
+ it 'retries 5XX errors' do
99
+ stub = stub_request(:get, %r{.*api.gocardless.com/webhooks}).
100
+ to_return(status: 502,
101
+ headers: { 'Content-Type' => 'text/html' },
102
+ body: '<html><body>Response from Cloudflare</body></html>').
103
+ then.to_return(status: 200, headers: response_headers, body: body)
104
+
105
+ get_list_response
106
+ expect(stub).to have_been_requested.twice
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#all' do
113
+ let!(:first_response_stub) do
114
+ stub_request(:get, %r{.*api.gocardless.com/webhooks$}).to_return(
115
+ body: {
116
+ 'webhooks' => [{
117
+
118
+ 'created_at' => 'created_at-input',
119
+ 'id' => 'id-input',
120
+ 'is_test' => 'is_test-input',
121
+ 'request_body' => 'request_body-input',
122
+ 'request_headers' => 'request_headers-input',
123
+ 'response_body' => 'response_body-input',
124
+ 'response_body_truncated' => 'response_body_truncated-input',
125
+ 'response_code' => 'response_code-input',
126
+ 'response_headers' => 'response_headers-input',
127
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
128
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
129
+ 'successful' => 'successful-input',
130
+ 'url' => 'url-input',
131
+ }],
132
+ meta: {
133
+ cursors: { after: 'AB345' },
134
+ limit: 1,
135
+ },
136
+ }.to_json,
137
+ headers: response_headers
138
+ )
139
+ end
140
+
141
+ let!(:second_response_stub) do
142
+ stub_request(:get, %r{.*api.gocardless.com/webhooks\?after=AB345}).to_return(
143
+ body: {
144
+ 'webhooks' => [{
145
+
146
+ 'created_at' => 'created_at-input',
147
+ 'id' => 'id-input',
148
+ 'is_test' => 'is_test-input',
149
+ 'request_body' => 'request_body-input',
150
+ 'request_headers' => 'request_headers-input',
151
+ 'response_body' => 'response_body-input',
152
+ 'response_body_truncated' => 'response_body_truncated-input',
153
+ 'response_code' => 'response_code-input',
154
+ 'response_headers' => 'response_headers-input',
155
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
156
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
157
+ 'successful' => 'successful-input',
158
+ 'url' => 'url-input',
159
+ }],
160
+ meta: {
161
+ limit: 2,
162
+ cursors: {},
163
+ },
164
+ }.to_json,
165
+ headers: response_headers
166
+ )
167
+ end
168
+
169
+ it 'automatically makes the extra requests' do
170
+ expect(client.webhooks.all.to_a.length).to eq(2)
171
+ expect(first_response_stub).to have_been_requested
172
+ expect(second_response_stub).to have_been_requested
173
+ end
174
+
175
+ describe 'retry behaviour' do
176
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
177
+
178
+ it 'retries timeouts' do
179
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/webhooks$}).to_return(
180
+ body: {
181
+ 'webhooks' => [{
182
+
183
+ 'created_at' => 'created_at-input',
184
+ 'id' => 'id-input',
185
+ 'is_test' => 'is_test-input',
186
+ 'request_body' => 'request_body-input',
187
+ 'request_headers' => 'request_headers-input',
188
+ 'response_body' => 'response_body-input',
189
+ 'response_body_truncated' => 'response_body_truncated-input',
190
+ 'response_code' => 'response_code-input',
191
+ 'response_headers' => 'response_headers-input',
192
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
193
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
194
+ 'successful' => 'successful-input',
195
+ 'url' => 'url-input',
196
+ }],
197
+ meta: {
198
+ cursors: { after: 'AB345' },
199
+ limit: 1,
200
+ },
201
+ }.to_json,
202
+ headers: response_headers
203
+ )
204
+
205
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/webhooks\?after=AB345}).
206
+ to_timeout.then.
207
+ to_return(
208
+ body: {
209
+ 'webhooks' => [{
210
+
211
+ 'created_at' => 'created_at-input',
212
+ 'id' => 'id-input',
213
+ 'is_test' => 'is_test-input',
214
+ 'request_body' => 'request_body-input',
215
+ 'request_headers' => 'request_headers-input',
216
+ 'response_body' => 'response_body-input',
217
+ 'response_body_truncated' => 'response_body_truncated-input',
218
+ 'response_code' => 'response_code-input',
219
+ 'response_headers' => 'response_headers-input',
220
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
221
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
222
+ 'successful' => 'successful-input',
223
+ 'url' => 'url-input',
224
+ }],
225
+ meta: {
226
+ limit: 2,
227
+ cursors: {},
228
+ },
229
+ }.to_json,
230
+ headers: response_headers
231
+ )
232
+
233
+ client.webhooks.all.to_a
234
+
235
+ expect(first_response_stub).to have_been_requested
236
+ expect(second_response_stub).to have_been_requested.twice
237
+ end
238
+
239
+ it 'retries 5XX errors' do
240
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/webhooks$}).to_return(
241
+ body: {
242
+ 'webhooks' => [{
243
+
244
+ 'created_at' => 'created_at-input',
245
+ 'id' => 'id-input',
246
+ 'is_test' => 'is_test-input',
247
+ 'request_body' => 'request_body-input',
248
+ 'request_headers' => 'request_headers-input',
249
+ 'response_body' => 'response_body-input',
250
+ 'response_body_truncated' => 'response_body_truncated-input',
251
+ 'response_code' => 'response_code-input',
252
+ 'response_headers' => 'response_headers-input',
253
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
254
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
255
+ 'successful' => 'successful-input',
256
+ 'url' => 'url-input',
257
+ }],
258
+ meta: {
259
+ cursors: { after: 'AB345' },
260
+ limit: 1,
261
+ },
262
+ }.to_json,
263
+ headers: response_headers
264
+ )
265
+
266
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/webhooks\?after=AB345}).
267
+ to_return(
268
+ status: 502,
269
+ body: '<html><body>Response from Cloudflare</body></html>',
270
+ headers: { 'Content-Type' => 'text/html' }
271
+ ).then.to_return(
272
+ body: {
273
+ 'webhooks' => [{
274
+
275
+ 'created_at' => 'created_at-input',
276
+ 'id' => 'id-input',
277
+ 'is_test' => 'is_test-input',
278
+ 'request_body' => 'request_body-input',
279
+ 'request_headers' => 'request_headers-input',
280
+ 'response_body' => 'response_body-input',
281
+ 'response_body_truncated' => 'response_body_truncated-input',
282
+ 'response_code' => 'response_code-input',
283
+ 'response_headers' => 'response_headers-input',
284
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
285
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
286
+ 'successful' => 'successful-input',
287
+ 'url' => 'url-input',
288
+ }],
289
+ meta: {
290
+ limit: 2,
291
+ cursors: {},
292
+ },
293
+ }.to_json,
294
+ headers: response_headers
295
+ )
296
+
297
+ client.webhooks.all.to_a
298
+
299
+ expect(first_response_stub).to have_been_requested
300
+ expect(second_response_stub).to have_been_requested.twice
301
+ end
302
+ end
303
+ end
304
+
305
+ describe '#get' do
306
+ let(:id) { 'ID123' }
307
+
308
+ subject(:get_response) { client.webhooks.get(id) }
309
+
310
+ context 'passing in a custom header' do
311
+ let!(:stub) do
312
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
313
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
314
+ with(headers: { 'Foo' => 'Bar' }).
315
+ to_return(
316
+ body: {
317
+ 'webhooks' => {
318
+
319
+ 'created_at' => 'created_at-input',
320
+ 'id' => 'id-input',
321
+ 'is_test' => 'is_test-input',
322
+ 'request_body' => 'request_body-input',
323
+ 'request_headers' => 'request_headers-input',
324
+ 'response_body' => 'response_body-input',
325
+ 'response_body_truncated' => 'response_body_truncated-input',
326
+ 'response_code' => 'response_code-input',
327
+ 'response_headers' => 'response_headers-input',
328
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
329
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
330
+ 'successful' => 'successful-input',
331
+ 'url' => 'url-input',
332
+ },
333
+ }.to_json,
334
+ headers: response_headers
335
+ )
336
+ end
337
+
338
+ subject(:get_response) do
339
+ client.webhooks.get(id, headers: {
340
+ 'Foo' => 'Bar',
341
+ })
342
+ end
343
+
344
+ it 'includes the header' do
345
+ get_response
346
+ expect(stub).to have_been_requested
347
+ end
348
+ end
349
+
350
+ context 'when there is a webhook to return' do
351
+ before do
352
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
353
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
354
+ body: {
355
+ 'webhooks' => {
356
+
357
+ 'created_at' => 'created_at-input',
358
+ 'id' => 'id-input',
359
+ 'is_test' => 'is_test-input',
360
+ 'request_body' => 'request_body-input',
361
+ 'request_headers' => 'request_headers-input',
362
+ 'response_body' => 'response_body-input',
363
+ 'response_body_truncated' => 'response_body_truncated-input',
364
+ 'response_code' => 'response_code-input',
365
+ 'response_headers' => 'response_headers-input',
366
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
367
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
368
+ 'successful' => 'successful-input',
369
+ 'url' => 'url-input',
370
+ },
371
+ }.to_json,
372
+ headers: response_headers
373
+ )
374
+ end
375
+
376
+ it 'wraps the response in a resource' do
377
+ expect(get_response).to be_a(GoCardlessPro::Resources::Webhook)
378
+ end
379
+ end
380
+
381
+ context 'when nothing is returned' do
382
+ before do
383
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
384
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
385
+ body: '',
386
+ headers: response_headers
387
+ )
388
+ end
389
+
390
+ it 'returns nil' do
391
+ expect(get_response).to be_nil
392
+ end
393
+ end
394
+
395
+ context "when an ID is specified which can't be included in a valid URI" do
396
+ let(:id) { '`' }
397
+
398
+ it "doesn't raise an error" do
399
+ expect { get_response }.to_not raise_error(/bad URI/)
400
+ end
401
+ end
402
+
403
+ describe 'retry behaviour' do
404
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
405
+
406
+ it 'retries timeouts' do
407
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
408
+
409
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
410
+ to_timeout.then.to_return(status: 200, headers: response_headers)
411
+
412
+ get_response
413
+ expect(stub).to have_been_requested.twice
414
+ end
415
+
416
+ it 'retries 5XX errors, other than 500s' do
417
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
418
+
419
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
420
+ to_return(status: 502,
421
+ headers: { 'Content-Type' => 'text/html' },
422
+ body: '<html><body>Response from Cloudflare</body></html>').
423
+ then.to_return(status: 200, headers: response_headers)
424
+
425
+ get_response
426
+ expect(stub).to have_been_requested.twice
427
+ end
428
+
429
+ it 'retries 500 errors returned by the API' do
430
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
431
+
432
+ gocardless_error = {
433
+ 'error' => {
434
+ 'message' => 'Internal server error',
435
+ 'documentation_url' => 'https://developer.gocardless.com/#gocardless',
436
+ 'errors' => [{
437
+ 'message' => 'Internal server error',
438
+ 'reason' => 'internal_server_error',
439
+ }],
440
+ 'type' => 'gocardless',
441
+ 'code' => 500,
442
+ 'request_id' => 'dummy_request_id',
443
+ 'id' => 'dummy_exception_id',
444
+ },
445
+ }
446
+
447
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
448
+ to_return(status: 500,
449
+ headers: response_headers,
450
+ body: gocardless_error.to_json).
451
+ then.to_return(status: 200, headers: response_headers)
452
+
453
+ get_response
454
+ expect(stub).to have_been_requested.twice
455
+ end
456
+ end
457
+ end
458
+
459
+ describe '#retry' do
460
+ subject(:post_response) { client.webhooks.retry(resource_id) }
461
+
462
+ let(:resource_id) { 'ABC123' }
463
+
464
+ let!(:stub) do
465
+ # /webhooks/%v/actions/retry
466
+ stub_url = '/webhooks/:identity/actions/retry'.gsub(':identity', resource_id)
467
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
468
+ body: {
469
+ 'webhooks' => {
470
+
471
+ 'created_at' => 'created_at-input',
472
+ 'id' => 'id-input',
473
+ 'is_test' => 'is_test-input',
474
+ 'request_body' => 'request_body-input',
475
+ 'request_headers' => 'request_headers-input',
476
+ 'response_body' => 'response_body-input',
477
+ 'response_body_truncated' => 'response_body_truncated-input',
478
+ 'response_code' => 'response_code-input',
479
+ 'response_headers' => 'response_headers-input',
480
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
481
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
482
+ 'successful' => 'successful-input',
483
+ 'url' => 'url-input',
484
+ },
485
+ }.to_json,
486
+ headers: response_headers
487
+ )
488
+ end
489
+
490
+ it 'wraps the response and calls the right endpoint' do
491
+ expect(post_response).to be_a(GoCardlessPro::Resources::Webhook)
492
+
493
+ expect(stub).to have_been_requested
494
+ end
495
+
496
+ describe 'retry behaviour' do
497
+ it "doesn't retry errors" do
498
+ stub_url = '/webhooks/:identity/actions/retry'.gsub(':identity', resource_id)
499
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
500
+ to_timeout
501
+
502
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
503
+ expect(stub).to have_been_requested
504
+ end
505
+ end
506
+
507
+ context 'when the request needs a body and custom header' do
508
+ let(:body) { { foo: 'bar' } }
509
+ let(:headers) { { 'Foo' => 'Bar' } }
510
+ subject(:post_response) { client.webhooks.retry(resource_id, body, headers) }
511
+
512
+ let(:resource_id) { 'ABC123' }
513
+
514
+ let!(:stub) do
515
+ # /webhooks/%v/actions/retry
516
+ stub_url = '/webhooks/:identity/actions/retry'.gsub(':identity', resource_id)
517
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
518
+ with(
519
+ body: { foo: 'bar' },
520
+ headers: { 'Foo' => 'Bar' }
521
+ ).to_return(
522
+ body: {
523
+ 'webhooks' => {
524
+
525
+ 'created_at' => 'created_at-input',
526
+ 'id' => 'id-input',
527
+ 'is_test' => 'is_test-input',
528
+ 'request_body' => 'request_body-input',
529
+ 'request_headers' => 'request_headers-input',
530
+ 'response_body' => 'response_body-input',
531
+ 'response_body_truncated' => 'response_body_truncated-input',
532
+ 'response_code' => 'response_code-input',
533
+ 'response_headers' => 'response_headers-input',
534
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
535
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
536
+ 'successful' => 'successful-input',
537
+ 'url' => 'url-input',
538
+ },
539
+ }.to_json,
540
+ headers: response_headers
541
+ )
542
+ end
543
+ end
544
+ end
545
+ end