gocardless_pro 2.24.0 → 2.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +23 -4
  3. data/lib/gocardless_pro/api_service.rb +4 -0
  4. data/lib/gocardless_pro/client.rb +46 -1
  5. data/lib/gocardless_pro/error/authentication_error.rb +4 -0
  6. data/lib/gocardless_pro/error/permission_error.rb +4 -0
  7. data/lib/gocardless_pro/error/rate_limit_error.rb +4 -0
  8. data/lib/gocardless_pro/middlewares/raise_gocardless_errors.rb +12 -1
  9. data/lib/gocardless_pro/resources/bank_authorisation.rb +81 -0
  10. data/lib/gocardless_pro/resources/billing_request.rb +108 -0
  11. data/lib/gocardless_pro/resources/billing_request_flow.rb +72 -0
  12. data/lib/gocardless_pro/resources/billing_request_template.rb +68 -0
  13. data/lib/gocardless_pro/resources/block.rb +66 -0
  14. data/lib/gocardless_pro/resources/creditor.rb +2 -3
  15. data/lib/gocardless_pro/resources/event.rb +20 -0
  16. data/lib/gocardless_pro/resources/institution.rb +47 -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/redirect_flow.rb +2 -0
  20. data/lib/gocardless_pro/resources/scenario_simulator.rb +42 -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/blocks_service.rb +223 -0
  27. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +1 -5
  28. data/lib/gocardless_pro/services/creditors_service.rb +1 -3
  29. data/lib/gocardless_pro/services/currency_exchange_rates_service.rb +1 -1
  30. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +1 -5
  31. data/lib/gocardless_pro/services/customers_service.rb +1 -3
  32. data/lib/gocardless_pro/services/events_service.rb +1 -1
  33. data/lib/gocardless_pro/services/instalment_schedules_service.rb +1 -7
  34. data/lib/gocardless_pro/services/institutions_service.rb +56 -0
  35. data/lib/gocardless_pro/services/mandate_import_entries_service.rb +1 -1
  36. data/lib/gocardless_pro/services/mandate_imports_service.rb +0 -6
  37. data/lib/gocardless_pro/services/mandates_service.rb +1 -7
  38. data/lib/gocardless_pro/services/payer_authorisations_service.rb +202 -0
  39. data/lib/gocardless_pro/services/payments_service.rb +1 -7
  40. data/lib/gocardless_pro/services/payout_items_service.rb +1 -1
  41. data/lib/gocardless_pro/services/payouts_service.rb +1 -1
  42. data/lib/gocardless_pro/services/redirect_flows_service.rb +0 -4
  43. data/lib/gocardless_pro/services/refunds_service.rb +1 -3
  44. data/lib/gocardless_pro/services/scenario_simulators_service.rb +170 -0
  45. data/lib/gocardless_pro/services/subscriptions_service.rb +10 -13
  46. data/lib/gocardless_pro/services/tax_rates_service.rb +1 -1
  47. data/lib/gocardless_pro/services/webhooks_service.rb +111 -0
  48. data/lib/gocardless_pro/version.rb +1 -1
  49. data/lib/gocardless_pro.rb +30 -0
  50. data/spec/api_service_spec.rb +12 -1
  51. data/spec/middlewares/raise_gocardless_errors_spec.rb +30 -0
  52. data/spec/resources/bank_authorisation_spec.rb +259 -0
  53. data/spec/resources/billing_request_flow_spec.rb +219 -0
  54. data/spec/resources/billing_request_spec.rb +782 -0
  55. data/spec/resources/billing_request_template_spec.rb +502 -0
  56. data/spec/resources/block_spec.rb +560 -0
  57. data/spec/resources/institution_spec.rb +108 -0
  58. data/spec/resources/payer_authorisation_spec.rb +418 -0
  59. data/spec/resources/redirect_flow_spec.rb +9 -0
  60. data/spec/resources/scenario_simulator_spec.rb +63 -0
  61. data/spec/resources/webhook_spec.rb +323 -0
  62. data/spec/services/bank_authorisations_service_spec.rb +353 -0
  63. data/spec/services/billing_request_flows_service_spec.rb +253 -0
  64. data/spec/services/billing_request_templates_service_spec.rb +789 -0
  65. data/spec/services/billing_requests_service_spec.rb +1082 -0
  66. data/spec/services/blocks_service_spec.rb +823 -0
  67. data/spec/services/creditor_bank_accounts_service_spec.rb +0 -13
  68. data/spec/services/creditors_service_spec.rb +0 -13
  69. data/spec/services/customer_bank_accounts_service_spec.rb +0 -13
  70. data/spec/services/customers_service_spec.rb +0 -13
  71. data/spec/services/instalment_schedules_service_spec.rb +0 -26
  72. data/spec/services/institutions_service_spec.rb +232 -0
  73. data/spec/services/mandate_imports_service_spec.rb +0 -13
  74. data/spec/services/mandates_service_spec.rb +0 -13
  75. data/spec/services/payer_authorisations_service_spec.rb +559 -0
  76. data/spec/services/payments_service_spec.rb +0 -13
  77. data/spec/services/redirect_flows_service_spec.rb +9 -13
  78. data/spec/services/refunds_service_spec.rb +0 -13
  79. data/spec/services/scenario_simulators_service_spec.rb +74 -0
  80. data/spec/services/subscriptions_service_spec.rb +0 -13
  81. data/spec/services/webhooks_service_spec.rb +545 -0
  82. metadata +64 -7
@@ -0,0 +1,323 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Resources::Webhook 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
+ before do
17
+ stub_request(:get, %r{.*api.gocardless.com/webhooks}).to_return(
18
+ body: {
19
+ 'webhooks' => [{
20
+
21
+ 'created_at' => 'created_at-input',
22
+ 'id' => 'id-input',
23
+ 'is_test' => 'is_test-input',
24
+ 'request_body' => 'request_body-input',
25
+ 'request_headers' => 'request_headers-input',
26
+ 'response_body' => 'response_body-input',
27
+ 'response_body_truncated' => 'response_body_truncated-input',
28
+ 'response_code' => 'response_code-input',
29
+ 'response_headers' => 'response_headers-input',
30
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
31
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
32
+ 'successful' => 'successful-input',
33
+ 'url' => 'url-input',
34
+ }],
35
+ meta: {
36
+ cursors: {
37
+ before: nil,
38
+ after: 'ABC123',
39
+ },
40
+ },
41
+ }.to_json,
42
+ headers: response_headers
43
+ )
44
+ end
45
+
46
+ it 'wraps each item in the resource class' do
47
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::Webhook)
48
+
49
+ expect(get_list_response.records.first.created_at).to eq('created_at-input')
50
+
51
+ expect(get_list_response.records.first.id).to eq('id-input')
52
+
53
+ expect(get_list_response.records.first.is_test).to eq('is_test-input')
54
+
55
+ expect(get_list_response.records.first.request_body).to eq('request_body-input')
56
+
57
+ expect(get_list_response.records.first.request_headers).to eq('request_headers-input')
58
+
59
+ expect(get_list_response.records.first.response_body).to eq('response_body-input')
60
+
61
+ expect(get_list_response.records.first.response_body_truncated).to eq('response_body_truncated-input')
62
+
63
+ expect(get_list_response.records.first.response_code).to eq('response_code-input')
64
+
65
+ expect(get_list_response.records.first.response_headers).to eq('response_headers-input')
66
+
67
+ expect(get_list_response.records.first.response_headers_content_truncated).to eq('response_headers_content_truncated-input')
68
+
69
+ expect(get_list_response.records.first.response_headers_count_truncated).to eq('response_headers_count_truncated-input')
70
+
71
+ expect(get_list_response.records.first.successful).to eq('successful-input')
72
+
73
+ expect(get_list_response.records.first.url).to eq('url-input')
74
+ end
75
+
76
+ it 'exposes the cursors for before and after' do
77
+ expect(get_list_response.before).to eq(nil)
78
+ expect(get_list_response.after).to eq('ABC123')
79
+ end
80
+
81
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
82
+ end
83
+ end
84
+
85
+ describe '#all' do
86
+ let!(:first_response_stub) do
87
+ stub_request(:get, %r{.*api.gocardless.com/webhooks$}).to_return(
88
+ body: {
89
+ 'webhooks' => [{
90
+
91
+ 'created_at' => 'created_at-input',
92
+ 'id' => 'id-input',
93
+ 'is_test' => 'is_test-input',
94
+ 'request_body' => 'request_body-input',
95
+ 'request_headers' => 'request_headers-input',
96
+ 'response_body' => 'response_body-input',
97
+ 'response_body_truncated' => 'response_body_truncated-input',
98
+ 'response_code' => 'response_code-input',
99
+ 'response_headers' => 'response_headers-input',
100
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
101
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
102
+ 'successful' => 'successful-input',
103
+ 'url' => 'url-input',
104
+ }],
105
+ meta: {
106
+ cursors: { after: 'AB345' },
107
+ limit: 1,
108
+ },
109
+ }.to_json,
110
+ headers: response_headers
111
+ )
112
+ end
113
+
114
+ let!(:second_response_stub) do
115
+ stub_request(:get, %r{.*api.gocardless.com/webhooks\?after=AB345}).to_return(
116
+ body: {
117
+ 'webhooks' => [{
118
+
119
+ 'created_at' => 'created_at-input',
120
+ 'id' => 'id-input',
121
+ 'is_test' => 'is_test-input',
122
+ 'request_body' => 'request_body-input',
123
+ 'request_headers' => 'request_headers-input',
124
+ 'response_body' => 'response_body-input',
125
+ 'response_body_truncated' => 'response_body_truncated-input',
126
+ 'response_code' => 'response_code-input',
127
+ 'response_headers' => 'response_headers-input',
128
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
129
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
130
+ 'successful' => 'successful-input',
131
+ 'url' => 'url-input',
132
+ }],
133
+ meta: {
134
+ limit: 2,
135
+ cursors: {},
136
+ },
137
+ }.to_json,
138
+ headers: response_headers
139
+ )
140
+ end
141
+
142
+ it 'automatically makes the extra requests' do
143
+ expect(client.webhooks.all.to_a.length).to eq(2)
144
+ expect(first_response_stub).to have_been_requested
145
+ expect(second_response_stub).to have_been_requested
146
+ end
147
+ end
148
+
149
+ describe '#get' do
150
+ let(:id) { 'ID123' }
151
+
152
+ subject(:get_response) { client.webhooks.get(id) }
153
+
154
+ context 'passing in a custom header' do
155
+ let!(:stub) do
156
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
157
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
158
+ with(headers: { 'Foo' => 'Bar' }).
159
+ to_return(
160
+ body: {
161
+ 'webhooks' => {
162
+
163
+ 'created_at' => 'created_at-input',
164
+ 'id' => 'id-input',
165
+ 'is_test' => 'is_test-input',
166
+ 'request_body' => 'request_body-input',
167
+ 'request_headers' => 'request_headers-input',
168
+ 'response_body' => 'response_body-input',
169
+ 'response_body_truncated' => 'response_body_truncated-input',
170
+ 'response_code' => 'response_code-input',
171
+ 'response_headers' => 'response_headers-input',
172
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
173
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
174
+ 'successful' => 'successful-input',
175
+ 'url' => 'url-input',
176
+ },
177
+ }.to_json,
178
+ headers: response_headers
179
+ )
180
+ end
181
+
182
+ subject(:get_response) do
183
+ client.webhooks.get(id, headers: {
184
+ 'Foo' => 'Bar',
185
+ })
186
+ end
187
+
188
+ it 'includes the header' do
189
+ get_response
190
+ expect(stub).to have_been_requested
191
+ end
192
+ end
193
+
194
+ context 'when there is a webhook to return' do
195
+ before do
196
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
197
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
198
+ body: {
199
+ 'webhooks' => {
200
+
201
+ 'created_at' => 'created_at-input',
202
+ 'id' => 'id-input',
203
+ 'is_test' => 'is_test-input',
204
+ 'request_body' => 'request_body-input',
205
+ 'request_headers' => 'request_headers-input',
206
+ 'response_body' => 'response_body-input',
207
+ 'response_body_truncated' => 'response_body_truncated-input',
208
+ 'response_code' => 'response_code-input',
209
+ 'response_headers' => 'response_headers-input',
210
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
211
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
212
+ 'successful' => 'successful-input',
213
+ 'url' => 'url-input',
214
+ },
215
+ }.to_json,
216
+ headers: response_headers
217
+ )
218
+ end
219
+
220
+ it 'wraps the response in a resource' do
221
+ expect(get_response).to be_a(GoCardlessPro::Resources::Webhook)
222
+ end
223
+ end
224
+
225
+ context 'when nothing is returned' do
226
+ before do
227
+ stub_url = '/webhooks/:identity'.gsub(':identity', id)
228
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
229
+ body: '',
230
+ headers: response_headers
231
+ )
232
+ end
233
+
234
+ it 'returns nil' do
235
+ expect(get_response).to be_nil
236
+ end
237
+ end
238
+
239
+ context "when an ID is specified which can't be included in a valid URI" do
240
+ let(:id) { '`' }
241
+
242
+ it "doesn't raise an error" do
243
+ expect { get_response }.to_not raise_error(/bad URI/)
244
+ end
245
+ end
246
+ end
247
+
248
+ describe '#retry' do
249
+ subject(:post_response) { client.webhooks.retry(resource_id) }
250
+
251
+ let(:resource_id) { 'ABC123' }
252
+
253
+ let!(:stub) do
254
+ # /webhooks/%v/actions/retry
255
+ stub_url = '/webhooks/:identity/actions/retry'.gsub(':identity', resource_id)
256
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
257
+ body: {
258
+ 'webhooks' => {
259
+
260
+ 'created_at' => 'created_at-input',
261
+ 'id' => 'id-input',
262
+ 'is_test' => 'is_test-input',
263
+ 'request_body' => 'request_body-input',
264
+ 'request_headers' => 'request_headers-input',
265
+ 'response_body' => 'response_body-input',
266
+ 'response_body_truncated' => 'response_body_truncated-input',
267
+ 'response_code' => 'response_code-input',
268
+ 'response_headers' => 'response_headers-input',
269
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
270
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
271
+ 'successful' => 'successful-input',
272
+ 'url' => 'url-input',
273
+ },
274
+ }.to_json,
275
+ headers: response_headers
276
+ )
277
+ end
278
+
279
+ it 'wraps the response and calls the right endpoint' do
280
+ expect(post_response).to be_a(GoCardlessPro::Resources::Webhook)
281
+
282
+ expect(stub).to have_been_requested
283
+ end
284
+
285
+ context 'when the request needs a body and custom header' do
286
+ let(:body) { { foo: 'bar' } }
287
+ let(:headers) { { 'Foo' => 'Bar' } }
288
+ subject(:post_response) { client.webhooks.retry(resource_id, body, headers) }
289
+
290
+ let(:resource_id) { 'ABC123' }
291
+
292
+ let!(:stub) do
293
+ # /webhooks/%v/actions/retry
294
+ stub_url = '/webhooks/:identity/actions/retry'.gsub(':identity', resource_id)
295
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
296
+ with(
297
+ body: { foo: 'bar' },
298
+ headers: { 'Foo' => 'Bar' }
299
+ ).to_return(
300
+ body: {
301
+ 'webhooks' => {
302
+
303
+ 'created_at' => 'created_at-input',
304
+ 'id' => 'id-input',
305
+ 'is_test' => 'is_test-input',
306
+ 'request_body' => 'request_body-input',
307
+ 'request_headers' => 'request_headers-input',
308
+ 'response_body' => 'response_body-input',
309
+ 'response_body_truncated' => 'response_body_truncated-input',
310
+ 'response_code' => 'response_code-input',
311
+ 'response_headers' => 'response_headers-input',
312
+ 'response_headers_content_truncated' => 'response_headers_content_truncated-input',
313
+ 'response_headers_count_truncated' => 'response_headers_count_truncated-input',
314
+ 'successful' => 'successful-input',
315
+ 'url' => 'url-input',
316
+ },
317
+ }.to_json,
318
+ headers: response_headers
319
+ )
320
+ end
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,353 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::BankAuthorisationsService 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 '#get' do
13
+ let(:id) { 'ID123' }
14
+
15
+ subject(:get_response) { client.bank_authorisations.get(id) }
16
+
17
+ context 'passing in a custom header' do
18
+ let!(:stub) do
19
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
20
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
21
+ with(headers: { 'Foo' => 'Bar' }).
22
+ to_return(
23
+ body: {
24
+ 'bank_authorisations' => {
25
+
26
+ 'authorisation_type' => 'authorisation_type-input',
27
+ 'authorised_at' => 'authorised_at-input',
28
+ 'created_at' => 'created_at-input',
29
+ 'expires_at' => 'expires_at-input',
30
+ 'id' => 'id-input',
31
+ 'last_visited_at' => 'last_visited_at-input',
32
+ 'links' => 'links-input',
33
+ 'redirect_uri' => 'redirect_uri-input',
34
+ 'url' => 'url-input',
35
+ },
36
+ }.to_json,
37
+ headers: response_headers
38
+ )
39
+ end
40
+
41
+ subject(:get_response) do
42
+ client.bank_authorisations.get(id, headers: {
43
+ 'Foo' => 'Bar',
44
+ })
45
+ end
46
+
47
+ it 'includes the header' do
48
+ get_response
49
+ expect(stub).to have_been_requested
50
+ end
51
+ end
52
+
53
+ context 'when there is a bank_authorisation to return' do
54
+ before do
55
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
56
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
57
+ body: {
58
+ 'bank_authorisations' => {
59
+
60
+ 'authorisation_type' => 'authorisation_type-input',
61
+ 'authorised_at' => 'authorised_at-input',
62
+ 'created_at' => 'created_at-input',
63
+ 'expires_at' => 'expires_at-input',
64
+ 'id' => 'id-input',
65
+ 'last_visited_at' => 'last_visited_at-input',
66
+ 'links' => 'links-input',
67
+ 'redirect_uri' => 'redirect_uri-input',
68
+ 'url' => 'url-input',
69
+ },
70
+ }.to_json,
71
+ headers: response_headers
72
+ )
73
+ end
74
+
75
+ it 'wraps the response in a resource' do
76
+ expect(get_response).to be_a(GoCardlessPro::Resources::BankAuthorisation)
77
+ end
78
+ end
79
+
80
+ context 'when nothing is returned' do
81
+ before do
82
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
83
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
84
+ body: '',
85
+ headers: response_headers
86
+ )
87
+ end
88
+
89
+ it 'returns nil' do
90
+ expect(get_response).to be_nil
91
+ end
92
+ end
93
+
94
+ context "when an ID is specified which can't be included in a valid URI" do
95
+ let(:id) { '`' }
96
+
97
+ it "doesn't raise an error" do
98
+ expect { get_response }.to_not raise_error(/bad URI/)
99
+ end
100
+ end
101
+
102
+ describe 'retry behaviour' do
103
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
104
+
105
+ it 'retries timeouts' do
106
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
107
+
108
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
109
+ to_timeout.then.to_return(status: 200, headers: response_headers)
110
+
111
+ get_response
112
+ expect(stub).to have_been_requested.twice
113
+ end
114
+
115
+ it 'retries 5XX errors, other than 500s' do
116
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
117
+
118
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
119
+ to_return(status: 502,
120
+ headers: { 'Content-Type' => 'text/html' },
121
+ body: '<html><body>Response from Cloudflare</body></html>').
122
+ then.to_return(status: 200, headers: response_headers)
123
+
124
+ get_response
125
+ expect(stub).to have_been_requested.twice
126
+ end
127
+
128
+ it 'retries 500 errors returned by the API' do
129
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
130
+
131
+ gocardless_error = {
132
+ 'error' => {
133
+ 'message' => 'Internal server error',
134
+ 'documentation_url' => 'https://developer.gocardless.com/#gocardless',
135
+ 'errors' => [{
136
+ 'message' => 'Internal server error',
137
+ 'reason' => 'internal_server_error',
138
+ }],
139
+ 'type' => 'gocardless',
140
+ 'code' => 500,
141
+ 'request_id' => 'dummy_request_id',
142
+ 'id' => 'dummy_exception_id',
143
+ },
144
+ }
145
+
146
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
147
+ to_return(status: 500,
148
+ headers: response_headers,
149
+ body: gocardless_error.to_json).
150
+ then.to_return(status: 200, headers: response_headers)
151
+
152
+ get_response
153
+ expect(stub).to have_been_requested.twice
154
+ end
155
+ end
156
+ end
157
+
158
+ describe '#create' do
159
+ subject(:post_create_response) { client.bank_authorisations.create(params: new_resource) }
160
+ context 'with a valid request' do
161
+ let(:new_resource) do
162
+ {
163
+
164
+ 'authorisation_type' => 'authorisation_type-input',
165
+ 'authorised_at' => 'authorised_at-input',
166
+ 'created_at' => 'created_at-input',
167
+ 'expires_at' => 'expires_at-input',
168
+ 'id' => 'id-input',
169
+ 'last_visited_at' => 'last_visited_at-input',
170
+ 'links' => 'links-input',
171
+ 'redirect_uri' => 'redirect_uri-input',
172
+ 'url' => 'url-input',
173
+ }
174
+ end
175
+
176
+ before do
177
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).
178
+ with(
179
+ body: {
180
+ 'bank_authorisations' => {
181
+
182
+ 'authorisation_type' => 'authorisation_type-input',
183
+ 'authorised_at' => 'authorised_at-input',
184
+ 'created_at' => 'created_at-input',
185
+ 'expires_at' => 'expires_at-input',
186
+ 'id' => 'id-input',
187
+ 'last_visited_at' => 'last_visited_at-input',
188
+ 'links' => 'links-input',
189
+ 'redirect_uri' => 'redirect_uri-input',
190
+ 'url' => 'url-input',
191
+ },
192
+ }
193
+ ).
194
+ to_return(
195
+ body: {
196
+ 'bank_authorisations' =>
197
+
198
+ {
199
+
200
+ 'authorisation_type' => 'authorisation_type-input',
201
+ 'authorised_at' => 'authorised_at-input',
202
+ 'created_at' => 'created_at-input',
203
+ 'expires_at' => 'expires_at-input',
204
+ 'id' => 'id-input',
205
+ 'last_visited_at' => 'last_visited_at-input',
206
+ 'links' => 'links-input',
207
+ 'redirect_uri' => 'redirect_uri-input',
208
+ 'url' => 'url-input',
209
+ },
210
+
211
+ }.to_json,
212
+ headers: response_headers
213
+ )
214
+ end
215
+
216
+ it 'creates and returns the resource' do
217
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::BankAuthorisation)
218
+ end
219
+
220
+ describe 'retry behaviour' do
221
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
222
+
223
+ it 'retries timeouts' do
224
+ stub = stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).
225
+ to_timeout.then.to_return(status: 200, headers: response_headers)
226
+
227
+ post_create_response
228
+ expect(stub).to have_been_requested.twice
229
+ end
230
+
231
+ it 'retries 5XX errors' do
232
+ stub = stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).
233
+ to_return(status: 502,
234
+ headers: { 'Content-Type' => 'text/html' },
235
+ body: '<html><body>Response from Cloudflare</body></html>').
236
+ then.to_return(status: 200, headers: response_headers)
237
+
238
+ post_create_response
239
+ expect(stub).to have_been_requested.twice
240
+ end
241
+ end
242
+ end
243
+
244
+ context 'with a request that returns a validation error' do
245
+ let(:new_resource) { {} }
246
+
247
+ before do
248
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).to_return(
249
+ body: {
250
+ error: {
251
+ type: 'validation_failed',
252
+ code: 422,
253
+ errors: [
254
+ { message: 'test error message', field: 'test_field' },
255
+ ],
256
+ },
257
+ }.to_json,
258
+ headers: response_headers,
259
+ status: 422
260
+ )
261
+ end
262
+
263
+ it 'throws the correct error' do
264
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
265
+ end
266
+ end
267
+
268
+ context 'with a request that returns an idempotent creation conflict error' do
269
+ let(:id) { 'ID123' }
270
+
271
+ let(:new_resource) do
272
+ {
273
+
274
+ 'authorisation_type' => 'authorisation_type-input',
275
+ 'authorised_at' => 'authorised_at-input',
276
+ 'created_at' => 'created_at-input',
277
+ 'expires_at' => 'expires_at-input',
278
+ 'id' => 'id-input',
279
+ 'last_visited_at' => 'last_visited_at-input',
280
+ 'links' => 'links-input',
281
+ 'redirect_uri' => 'redirect_uri-input',
282
+ 'url' => 'url-input',
283
+ }
284
+ end
285
+
286
+ let!(:post_stub) do
287
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).to_return(
288
+ body: {
289
+ error: {
290
+ type: 'invalid_state',
291
+ code: 409,
292
+ errors: [
293
+ {
294
+ message: 'A resource has already been created with this idempotency key',
295
+ reason: 'idempotent_creation_conflict',
296
+ links: {
297
+ conflicting_resource_id: id,
298
+ },
299
+ },
300
+ ],
301
+ },
302
+ }.to_json,
303
+ headers: response_headers,
304
+ status: 409
305
+ )
306
+ end
307
+
308
+ let!(:get_stub) do
309
+ stub_url = "/bank_authorisations/#{id}"
310
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
311
+ to_return(
312
+ body: {
313
+ 'bank_authorisations' => {
314
+
315
+ 'authorisation_type' => 'authorisation_type-input',
316
+ 'authorised_at' => 'authorised_at-input',
317
+ 'created_at' => 'created_at-input',
318
+ 'expires_at' => 'expires_at-input',
319
+ 'id' => 'id-input',
320
+ 'last_visited_at' => 'last_visited_at-input',
321
+ 'links' => 'links-input',
322
+ 'redirect_uri' => 'redirect_uri-input',
323
+ 'url' => 'url-input',
324
+ },
325
+ }.to_json,
326
+ headers: response_headers
327
+ )
328
+ end
329
+
330
+ context 'with default behaviour' do
331
+ it 'fetches the already-created resource' do
332
+ post_create_response
333
+ expect(post_stub).to have_been_requested
334
+ expect(get_stub).to have_been_requested
335
+ end
336
+ end
337
+
338
+ context 'with on_idempotency_conflict: :raise' do
339
+ let(:client) do
340
+ GoCardlessPro::Client.new(
341
+ access_token: 'SECRET_TOKEN',
342
+ on_idempotency_conflict: :raise
343
+ )
344
+ end
345
+
346
+ it 'raises an IdempotencyConflict error' do
347
+ expect { post_create_response }.
348
+ to raise_error(GoCardlessPro::IdempotencyConflict)
349
+ end
350
+ end
351
+ end
352
+ end
353
+ end