gocardless_pro 2.24.0 → 2.29.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.
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
@@ -34,8 +34,6 @@ module GoCardlessPro
34
34
  raise IdempotencyConflict, e.error
35
35
  when :fetch
36
36
  return get(e.conflicting_resource_id)
37
- else
38
- raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
39
37
  end
40
38
  end
41
39
 
@@ -65,7 +63,7 @@ module GoCardlessPro
65
63
  )
66
64
  end
67
65
 
68
- # Get a lazily enumerated list of all the items returned. This is simmilar to the `list` method but will paginate for you automatically.
66
+ # Get a lazily enumerated list of all the items returned. This is similar to the `list` method but will paginate for you automatically.
69
67
  #
70
68
  # @param options [Hash] parameters as a hash. If the request is a GET, these will be converted to query parameters.
71
69
  # Otherwise they will be the body of the request.
@@ -140,9 +138,10 @@ module GoCardlessPro
140
138
  # Pause a subscription object.
141
139
  # No payments will be created until it is resumed.
142
140
  #
143
- # This can only be used when a subscription collecting a fixed number of
144
- # payments (created using `count`)
145
- # or when they continue forever (created without `count` or `end_date`)
141
+ # This can only be used when a subscription is collecting a fixed number of
142
+ # payments (created using `count`),
143
+ # when they continue forever (created without `count` or `end_date`) or
144
+ # the subscription is already paused for a number of cycles.
146
145
  #
147
146
  # When `pause_cycles` is omitted the subscription is paused until the [resume
148
147
  # endpoint](#subscriptions-resume-a-subscription) is called.
@@ -166,7 +165,11 @@ module GoCardlessPro
166
165
  # - `validation_failed` if invalid data is provided when attempting to pause a
167
166
  # subscription.
168
167
  #
169
- # - `subscription_not_active` if the subscription is no longer active.
168
+ # - `subscription_paused_cannot_update_cycles` if the subscription is already
169
+ # paused for a number of cycles and the request provides a value for
170
+ # `pause_cycle`.
171
+ #
172
+ # - `subscription_cannot_be_paused` if the subscription cannot be paused.
170
173
  #
171
174
  # - `subscription_already_ended` if the subscription has taken all payments.
172
175
  #
@@ -198,8 +201,6 @@ module GoCardlessPro
198
201
  raise IdempotencyConflict, e.error
199
202
  when :fetch
200
203
  return get(e.conflicting_resource_id)
201
- else
202
- raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
203
204
  end
204
205
  end
205
206
 
@@ -253,8 +254,6 @@ module GoCardlessPro
253
254
  raise IdempotencyConflict, e.error
254
255
  when :fetch
255
256
  return get(e.conflicting_resource_id)
256
- else
257
- raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
258
257
  end
259
258
  end
260
259
 
@@ -297,8 +296,6 @@ module GoCardlessPro
297
296
  raise IdempotencyConflict, e.error
298
297
  when :fetch
299
298
  return get(e.conflicting_resource_id)
300
- else
301
- raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
302
299
  end
303
300
  end
304
301
 
@@ -28,7 +28,7 @@ module GoCardlessPro
28
28
  )
29
29
  end
30
30
 
31
- # Get a lazily enumerated list of all the items returned. This is simmilar to the `list` method but will paginate for you automatically.
31
+ # Get a lazily enumerated list of all the items returned. This is similar to the `list` method but will paginate for you automatically.
32
32
  #
33
33
  # @param options [Hash] parameters as a hash. If the request is a GET, these will be converted to query parameters.
34
34
  # Otherwise they will be the body of the request.
@@ -0,0 +1,111 @@
1
+ require_relative './base_service'
2
+
3
+ # encoding: utf-8
4
+ #
5
+ # This client is automatically generated from a template and JSON schema definition.
6
+ # See https://github.com/gocardless/gocardless-pro-ruby#contributing before editing.
7
+ #
8
+
9
+ module GoCardlessPro
10
+ module Services
11
+ # Service for making requests to the Webhook endpoints
12
+ class WebhooksService < BaseService
13
+ # Returns a [cursor-paginated](#api-usage-cursor-pagination) list of your
14
+ # webhooks.
15
+ # Example URL: /webhooks
16
+ # @param options [Hash] parameters as a hash, under a params key.
17
+ def list(options = {})
18
+ path = '/webhooks'
19
+
20
+ options[:retry_failures] = true
21
+
22
+ response = make_request(:get, path, options)
23
+
24
+ ListResponse.new(
25
+ response: response,
26
+ unenveloped_body: unenvelope_body(response.body),
27
+ resource_class: Resources::Webhook
28
+ )
29
+ end
30
+
31
+ # Get a lazily enumerated list of all the items returned. This is similar to the `list` method but will paginate for you automatically.
32
+ #
33
+ # @param options [Hash] parameters as a hash. If the request is a GET, these will be converted to query parameters.
34
+ # Otherwise they will be the body of the request.
35
+ def all(options = {})
36
+ Paginator.new(
37
+ service: self,
38
+ options: options
39
+ ).enumerator
40
+ end
41
+
42
+ # Retrieves the details of an existing webhook.
43
+ # Example URL: /webhooks/:identity
44
+ #
45
+ # @param identity # Unique identifier, beginning with "WB".
46
+ # @param options [Hash] parameters as a hash, under a params key.
47
+ def get(identity, options = {})
48
+ path = sub_url('/webhooks/:identity', 'identity' => identity)
49
+
50
+ options[:retry_failures] = true
51
+
52
+ response = make_request(:get, path, options)
53
+
54
+ return if response.body.nil?
55
+
56
+ Resources::Webhook.new(unenvelope_body(response.body), response)
57
+ end
58
+
59
+ # Requests for a previous webhook to be sent again
60
+ # Example URL: /webhooks/:identity/actions/retry
61
+ #
62
+ # @param identity # Unique identifier, beginning with "WB".
63
+ # @param options [Hash] parameters as a hash, under a params key.
64
+ def retry(identity, options = {})
65
+ path = sub_url('/webhooks/:identity/actions/retry', 'identity' => identity)
66
+
67
+ params = options.delete(:params) || {}
68
+ options[:params] = {}
69
+ options[:params]['data'] = params
70
+
71
+ options[:retry_failures] = false
72
+
73
+ begin
74
+ response = make_request(:post, path, options)
75
+
76
+ # Response doesn't raise any errors until #body is called
77
+ response.tap(&:body)
78
+ rescue InvalidStateError => e
79
+ if e.idempotent_creation_conflict?
80
+ case @api_service.on_idempotency_conflict
81
+ when :raise
82
+ raise IdempotencyConflict, e.error
83
+ when :fetch
84
+ return get(e.conflicting_resource_id)
85
+ end
86
+ end
87
+
88
+ raise e
89
+ end
90
+
91
+ return if response.body.nil?
92
+
93
+ Resources::Webhook.new(unenvelope_body(response.body), response)
94
+ end
95
+
96
+ private
97
+
98
+ # Unenvelope the response of the body using the service's `envelope_key`
99
+ #
100
+ # @param body [Hash]
101
+ def unenvelope_body(body)
102
+ body[envelope_key] || body['data']
103
+ end
104
+
105
+ # return the key which API responses will envelope data under
106
+ def envelope_key
107
+ 'webhooks'
108
+ end
109
+ end
110
+ end
111
+ end
@@ -4,5 +4,5 @@ end
4
4
 
5
5
  module GoCardlessPro
6
6
  # Current version of the GC gem
7
- VERSION = '2.24.0'.freeze
7
+ VERSION = '2.29.0'.freeze
8
8
  end
@@ -32,15 +32,33 @@ require_relative 'gocardless_pro/error/gocardless_error'
32
32
  require_relative 'gocardless_pro/error/invalid_api_usage_error'
33
33
  require_relative 'gocardless_pro/error/invalid_state_error'
34
34
  require_relative 'gocardless_pro/error/api_error'
35
+ require_relative 'gocardless_pro/error/permission_error'
36
+ require_relative 'gocardless_pro/error/authentication_error'
37
+ require_relative 'gocardless_pro/error/rate_limit_error'
35
38
  require_relative 'gocardless_pro/paginator'
36
39
  require_relative 'gocardless_pro/request'
37
40
  require_relative 'gocardless_pro/response'
38
41
  require_relative 'gocardless_pro/api_response'
39
42
  require_relative 'gocardless_pro/webhook'
40
43
 
44
+ require_relative 'gocardless_pro/resources/bank_authorisation'
45
+ require_relative 'gocardless_pro/services/bank_authorisations_service'
46
+
41
47
  require_relative 'gocardless_pro/resources/bank_details_lookup'
42
48
  require_relative 'gocardless_pro/services/bank_details_lookups_service'
43
49
 
50
+ require_relative 'gocardless_pro/resources/billing_request'
51
+ require_relative 'gocardless_pro/services/billing_requests_service'
52
+
53
+ require_relative 'gocardless_pro/resources/billing_request_flow'
54
+ require_relative 'gocardless_pro/services/billing_request_flows_service'
55
+
56
+ require_relative 'gocardless_pro/resources/billing_request_template'
57
+ require_relative 'gocardless_pro/services/billing_request_templates_service'
58
+
59
+ require_relative 'gocardless_pro/resources/block'
60
+ require_relative 'gocardless_pro/services/blocks_service'
61
+
44
62
  require_relative 'gocardless_pro/resources/creditor'
45
63
  require_relative 'gocardless_pro/services/creditors_service'
46
64
 
@@ -65,6 +83,9 @@ require_relative 'gocardless_pro/services/events_service'
65
83
  require_relative 'gocardless_pro/resources/instalment_schedule'
66
84
  require_relative 'gocardless_pro/services/instalment_schedules_service'
67
85
 
86
+ require_relative 'gocardless_pro/resources/institution'
87
+ require_relative 'gocardless_pro/services/institutions_service'
88
+
68
89
  require_relative 'gocardless_pro/resources/mandate'
69
90
  require_relative 'gocardless_pro/services/mandates_service'
70
91
 
@@ -77,6 +98,9 @@ require_relative 'gocardless_pro/services/mandate_import_entries_service'
77
98
  require_relative 'gocardless_pro/resources/mandate_pdf'
78
99
  require_relative 'gocardless_pro/services/mandate_pdfs_service'
79
100
 
101
+ require_relative 'gocardless_pro/resources/payer_authorisation'
102
+ require_relative 'gocardless_pro/services/payer_authorisations_service'
103
+
80
104
  require_relative 'gocardless_pro/resources/payment'
81
105
  require_relative 'gocardless_pro/services/payments_service'
82
106
 
@@ -92,10 +116,16 @@ require_relative 'gocardless_pro/services/redirect_flows_service'
92
116
  require_relative 'gocardless_pro/resources/refund'
93
117
  require_relative 'gocardless_pro/services/refunds_service'
94
118
 
119
+ require_relative 'gocardless_pro/resources/scenario_simulator'
120
+ require_relative 'gocardless_pro/services/scenario_simulators_service'
121
+
95
122
  require_relative 'gocardless_pro/resources/subscription'
96
123
  require_relative 'gocardless_pro/services/subscriptions_service'
97
124
 
98
125
  require_relative 'gocardless_pro/resources/tax_rate'
99
126
  require_relative 'gocardless_pro/services/tax_rates_service'
100
127
 
128
+ require_relative 'gocardless_pro/resources/webhook'
129
+ require_relative 'gocardless_pro/services/webhooks_service'
130
+
101
131
  require_relative 'gocardless_pro/client.rb'
@@ -1,8 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe GoCardlessPro::ApiService do
4
- subject(:service) { described_class.new('https://api.example.com', 'secret_token') }
4
+ subject(:service) do
5
+ described_class.new('https://api.example.com', 'secret_token', options)
6
+ end
5
7
 
8
+ let(:options) { {} }
6
9
  let(:default_response) do
7
10
  {
8
11
  status: 200,
@@ -174,4 +177,12 @@ describe GoCardlessPro::ApiService do
174
177
  expect(stub).to have_been_requested
175
178
  end
176
179
  end
180
+
181
+ describe 'when passing an invalid :on_idempotency_conflict' do
182
+ let(:options) { { on_idempotency_conflict: :junk } }
183
+
184
+ it 'raises an error' do
185
+ expect { service }.to raise_error(ArgumentError)
186
+ end
187
+ end
177
188
  end
@@ -74,6 +74,36 @@ describe GoCardlessPro::Middlewares::RaiseGoCardlessErrors do
74
74
  end
75
75
  end
76
76
 
77
+ context 'for a Permission error' do
78
+ let(:status) { 403 }
79
+ let(:body) { { error: { type: 'invalid_api_usage' } }.to_json }
80
+
81
+ it 'raises a GoCardlessError' do
82
+ expect { connection.post('https://api.gocardless.com/widgets') }.
83
+ to raise_error(GoCardlessPro::PermissionError)
84
+ end
85
+ end
86
+
87
+ context 'for a RateLimit error' do
88
+ let(:status) { 429 }
89
+ let(:body) { { error: { type: 'invalid_api_usage' } }.to_json }
90
+
91
+ it 'raises a GoCardlessError' do
92
+ expect { connection.post('https://api.gocardless.com/widgets') }.
93
+ to raise_error(GoCardlessPro::RateLimitError)
94
+ end
95
+ end
96
+
97
+ context 'for a Authentication error' do
98
+ let(:status) { 401 }
99
+ let(:body) { { error: { type: 'invalid_api_usage' } }.to_json }
100
+
101
+ it 'raises a GoCardlessError' do
102
+ expect { connection.post('https://api.gocardless.com/widgets') }.
103
+ to raise_error(GoCardlessPro::AuthenticationError)
104
+ end
105
+ end
106
+
77
107
  context 'for an invalid API usage error' do
78
108
  let(:status) { 400 }
79
109
  let(:body) { { error: { type: 'invalid_api_usage' } }.to_json }
@@ -0,0 +1,259 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Resources::BankAuthorisation 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
+ end
102
+
103
+ describe '#create' do
104
+ subject(:post_create_response) { client.bank_authorisations.create(params: new_resource) }
105
+ context 'with a valid request' do
106
+ let(:new_resource) do
107
+ {
108
+
109
+ 'authorisation_type' => 'authorisation_type-input',
110
+ 'authorised_at' => 'authorised_at-input',
111
+ 'created_at' => 'created_at-input',
112
+ 'expires_at' => 'expires_at-input',
113
+ 'id' => 'id-input',
114
+ 'last_visited_at' => 'last_visited_at-input',
115
+ 'links' => 'links-input',
116
+ 'redirect_uri' => 'redirect_uri-input',
117
+ 'url' => 'url-input',
118
+ }
119
+ end
120
+
121
+ before do
122
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).
123
+ with(
124
+ body: {
125
+ 'bank_authorisations' => {
126
+
127
+ 'authorisation_type' => 'authorisation_type-input',
128
+ 'authorised_at' => 'authorised_at-input',
129
+ 'created_at' => 'created_at-input',
130
+ 'expires_at' => 'expires_at-input',
131
+ 'id' => 'id-input',
132
+ 'last_visited_at' => 'last_visited_at-input',
133
+ 'links' => 'links-input',
134
+ 'redirect_uri' => 'redirect_uri-input',
135
+ 'url' => 'url-input',
136
+ },
137
+ }
138
+ ).
139
+ to_return(
140
+ body: {
141
+ 'bank_authorisations' =>
142
+
143
+ {
144
+
145
+ 'authorisation_type' => 'authorisation_type-input',
146
+ 'authorised_at' => 'authorised_at-input',
147
+ 'created_at' => 'created_at-input',
148
+ 'expires_at' => 'expires_at-input',
149
+ 'id' => 'id-input',
150
+ 'last_visited_at' => 'last_visited_at-input',
151
+ 'links' => 'links-input',
152
+ 'redirect_uri' => 'redirect_uri-input',
153
+ 'url' => 'url-input',
154
+ },
155
+
156
+ }.to_json,
157
+ headers: response_headers
158
+ )
159
+ end
160
+
161
+ it 'creates and returns the resource' do
162
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::BankAuthorisation)
163
+ end
164
+ end
165
+
166
+ context 'with a request that returns a validation error' do
167
+ let(:new_resource) { {} }
168
+
169
+ before do
170
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).to_return(
171
+ body: {
172
+ error: {
173
+ type: 'validation_failed',
174
+ code: 422,
175
+ errors: [
176
+ { message: 'test error message', field: 'test_field' },
177
+ ],
178
+ },
179
+ }.to_json,
180
+ headers: response_headers,
181
+ status: 422
182
+ )
183
+ end
184
+
185
+ it 'throws the correct error' do
186
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
187
+ end
188
+ end
189
+
190
+ context 'with a request that returns an idempotent creation conflict error' do
191
+ let(:id) { 'ID123' }
192
+
193
+ let(:new_resource) do
194
+ {
195
+
196
+ 'authorisation_type' => 'authorisation_type-input',
197
+ 'authorised_at' => 'authorised_at-input',
198
+ 'created_at' => 'created_at-input',
199
+ 'expires_at' => 'expires_at-input',
200
+ 'id' => 'id-input',
201
+ 'last_visited_at' => 'last_visited_at-input',
202
+ 'links' => 'links-input',
203
+ 'redirect_uri' => 'redirect_uri-input',
204
+ 'url' => 'url-input',
205
+ }
206
+ end
207
+
208
+ let!(:post_stub) do
209
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).to_return(
210
+ body: {
211
+ error: {
212
+ type: 'invalid_state',
213
+ code: 409,
214
+ errors: [
215
+ {
216
+ message: 'A resource has already been created with this idempotency key',
217
+ reason: 'idempotent_creation_conflict',
218
+ links: {
219
+ conflicting_resource_id: id,
220
+ },
221
+ },
222
+ ],
223
+ },
224
+ }.to_json,
225
+ headers: response_headers,
226
+ status: 409
227
+ )
228
+ end
229
+
230
+ let!(:get_stub) do
231
+ stub_url = "/bank_authorisations/#{id}"
232
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
233
+ to_return(
234
+ body: {
235
+ 'bank_authorisations' => {
236
+
237
+ 'authorisation_type' => 'authorisation_type-input',
238
+ 'authorised_at' => 'authorised_at-input',
239
+ 'created_at' => 'created_at-input',
240
+ 'expires_at' => 'expires_at-input',
241
+ 'id' => 'id-input',
242
+ 'last_visited_at' => 'last_visited_at-input',
243
+ 'links' => 'links-input',
244
+ 'redirect_uri' => 'redirect_uri-input',
245
+ 'url' => 'url-input',
246
+ },
247
+ }.to_json,
248
+ headers: response_headers
249
+ )
250
+ end
251
+
252
+ it 'fetches the already-created resource' do
253
+ post_create_response
254
+ expect(post_stub).to have_been_requested
255
+ expect(get_stub).to have_been_requested
256
+ end
257
+ end
258
+ end
259
+ end