diff --git a/lib/gocardless_pro.rb b/lib/gocardless_pro.rb index bab1ca2..9c60346 100644 --- a/lib/gocardless_pro.rb +++ b/lib/gocardless_pro.rb @@ -78,6 +78,9 @@ module GoCardlessPro require_relative 'gocardless_pro/resources/event' require_relative 'gocardless_pro/services/events_service' +require_relative 'gocardless_pro/resources/export' +require_relative 'gocardless_pro/services/exports_service' + require_relative 'gocardless_pro/resources/instalment_schedule' require_relative 'gocardless_pro/services/instalment_schedules_service' diff --git a/lib/gocardless_pro/client.rb b/lib/gocardless_pro/client.rb index f50b0ae..ddc6412 100644 --- a/lib/gocardless_pro/client.rb +++ b/lib/gocardless_pro/client.rb @@ -68,6 +68,11 @@ def events @events ||= Services::EventsService.new(@api_service) end + # Access to the service for export to make API calls + def exports + @exports ||= Services::ExportsService.new(@api_service) + end + # Access to the service for instalment_schedule to make API calls def instalment_schedules @instalment_schedules ||= Services::InstalmentSchedulesService.new(@api_service) @@ -228,7 +233,7 @@ def default_options 'User-Agent' => "#{user_agent}", 'Content-Type' => 'application/json', 'GoCardless-Client-Library' => 'gocardless-pro-ruby', - 'GoCardless-Client-Version' => '2.56.0', + 'GoCardless-Client-Version' => '2.57.0', }, } end diff --git a/lib/gocardless_pro/resources/export.rb b/lib/gocardless_pro/resources/export.rb new file mode 100644 index 0000000..d1f59ea --- /dev/null +++ b/lib/gocardless_pro/resources/export.rb @@ -0,0 +1,44 @@ +# +# This client is automatically generated from a template and JSON schema definition. +# See https://github.com/gocardless/gocardless-pro-ruby#contributing before editing. +# + +require 'uri' + +module GoCardlessPro + # A module containing classes for each of the resources in the GC Api + module Resources + # Represents an instance of a export resource returned from the API + + # File-based exports of data + class Export + attr_reader :created_at + attr_reader :currency + attr_reader :download_url + attr_reader :export_type + attr_reader :id + + # Initialize a export resource instance + # @param object [Hash] an object returned from the API + def initialize(object, response = nil) + @object = object + + @created_at = object['created_at'] + @currency = object['currency'] + @download_url = object['download_url'] + @export_type = object['export_type'] + @id = object['id'] + @response = response + end + + def api_response + ApiResponse.new(@response) + end + + # Provides the export resource as a hash of all its readable attributes + def to_h + @object + end + end + end +end diff --git a/lib/gocardless_pro/resources/subscription.rb b/lib/gocardless_pro/resources/subscription.rb index 9baf108..9a9badc 100644 --- a/lib/gocardless_pro/resources/subscription.rb +++ b/lib/gocardless_pro/resources/subscription.rb @@ -82,6 +82,7 @@ class Subscription attr_reader :metadata attr_reader :month attr_reader :name + attr_reader :parent_plan_paused attr_reader :payment_reference attr_reader :retry_if_possible attr_reader :start_date @@ -108,6 +109,7 @@ def initialize(object, response = nil) @metadata = object['metadata'] @month = object['month'] @name = object['name'] + @parent_plan_paused = object['parent_plan_paused'] @payment_reference = object['payment_reference'] @retry_if_possible = object['retry_if_possible'] @start_date = object['start_date'] diff --git a/lib/gocardless_pro/services/exports_service.rb b/lib/gocardless_pro/services/exports_service.rb new file mode 100644 index 0000000..c958497 --- /dev/null +++ b/lib/gocardless_pro/services/exports_service.rb @@ -0,0 +1,75 @@ +require_relative './base_service' + +# encoding: utf-8 +# +# This client is automatically generated from a template and JSON schema definition. +# See https://github.com/gocardless/gocardless-pro-ruby#contributing before editing. +# + +module GoCardlessPro + module Services + # Service for making requests to the Export endpoints + class ExportsService < BaseService + # Returns a single export. + # Example URL: /exports/:identity + # + # @param identity # Unique identifier, beginning with "EX". + # @param options [Hash] parameters as a hash, under a params key. + def get(identity, options = {}) + path = sub_url('/exports/:identity', { + 'identity' => identity, + }) + + options[:retry_failures] = true + + response = make_request(:get, path, options) + + return if response.body.nil? + + Resources::Export.new(unenvelope_body(response.body), response) + end + + # Returns a list of exports which are available for download. + # Example URL: /exports + # @param options [Hash] parameters as a hash, under a params key. + def list(options = {}) + path = '/exports' + + options[:retry_failures] = true + + response = make_request(:get, path, options) + + ListResponse.new( + response: response, + unenveloped_body: unenvelope_body(response.body), + resource_class: Resources::Export + ) + end + + # Get a lazily enumerated list of all the items returned. This is similar to the `list` method but will paginate for you automatically. + # + # @param options [Hash] parameters as a hash. If the request is a GET, these will be converted to query parameters. + # Otherwise they will be the body of the request. + def all(options = {}) + Paginator.new( + service: self, + options: options + ).enumerator + end + + private + + # Unenvelope the response of the body using the service's `envelope_key` + # + # @param body [Hash] + def unenvelope_body(body) + body[envelope_key] || body['data'] + end + + # return the key which API responses will envelope data under + def envelope_key + 'exports' + end + end + end +end diff --git a/lib/gocardless_pro/services/logos_service.rb b/lib/gocardless_pro/services/logos_service.rb index 09addf7..eb16415 100644 --- a/lib/gocardless_pro/services/logos_service.rb +++ b/lib/gocardless_pro/services/logos_service.rb @@ -12,6 +12,11 @@ module Services class LogosService < BaseService # Creates a new logo associated with a creditor. If a creditor already has a # logo, this will update the existing logo linked to the creditor. + # + # We support JPG and PNG formats. Your logo will be scaled to a maximum of 300px + # by 40px. For more guidance on how to upload logos that will look + # great across your customer payment page and notification emails see + # [here](https://developer.gocardless.com/gc-embed/setting-up-branding#tips_for_uploading_your_logo). # Example URL: /branding/logos # @param options [Hash] parameters as a hash, under a params key. def create_for_creditor(options = {}) diff --git a/lib/gocardless_pro/version.rb b/lib/gocardless_pro/version.rb index 01b5abe..22c003b 100644 --- a/lib/gocardless_pro/version.rb +++ b/lib/gocardless_pro/version.rb @@ -3,5 +3,5 @@ module GoCardlessPro module GoCardlessPro # Current version of the GC gem - VERSION = '2.56.0' + VERSION = '2.57.0' end diff --git a/spec/resources/export_spec.rb b/spec/resources/export_spec.rb new file mode 100644 index 0000000..701e942 --- /dev/null +++ b/spec/resources/export_spec.rb @@ -0,0 +1,191 @@ +require 'spec_helper' + +describe GoCardlessPro::Resources::Export do + let(:client) do + GoCardlessPro::Client.new( + access_token: 'SECRET_TOKEN' + ) + end + + let(:response_headers) { { 'Content-Type' => 'application/json' } } + + describe '#get' do + let(:id) { 'ID123' } + + subject(:get_response) { client.exports.get(id) } + + context 'passing in a custom header' do + let!(:stub) do + stub_url = '/exports/:identity'.gsub(':identity', id) + stub_request(:get, /.*api.gocardless.com#{stub_url}/). + with(headers: { 'Foo' => 'Bar' }). + to_return( + body: { + 'exports' => { + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }, + }.to_json, + headers: response_headers + ) + end + + subject(:get_response) do + client.exports.get(id, headers: { + 'Foo' => 'Bar', + }) + end + + it 'includes the header' do + get_response + expect(stub).to have_been_requested + end + end + + context 'when there is a export to return' do + before do + stub_url = '/exports/:identity'.gsub(':identity', id) + stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return( + body: { + 'exports' => { + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }, + }.to_json, + headers: response_headers + ) + end + + it 'wraps the response in a resource' do + expect(get_response).to be_a(GoCardlessPro::Resources::Export) + end + end + + context 'when nothing is returned' do + before do + stub_url = '/exports/:identity'.gsub(':identity', id) + stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return( + body: '', + headers: response_headers + ) + end + + it 'returns nil' do + expect(get_response).to be_nil + end + end + + context "when an ID is specified which can't be included in a valid URI" do + let(:id) { '`' } + + it "doesn't raise an error" do + expect { get_response }.to_not raise_error(/bad URI/) + end + end + end + + describe '#list' do + describe 'with no filters' do + subject(:get_list_response) { client.exports.list } + + before do + stub_request(:get, %r{.*api.gocardless.com/exports}).to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + cursors: { + before: nil, + after: 'ABC123', + }, + }, + }.to_json, + headers: response_headers + ) + end + + it 'wraps each item in the resource class' do + expect(get_list_response.records.map { |x| x.class }.uniq.first).to eq(GoCardlessPro::Resources::Export) + + expect(get_list_response.records.first.created_at).to eq('created_at-input') + + expect(get_list_response.records.first.currency).to eq('currency-input') + + expect(get_list_response.records.first.download_url).to eq('download_url-input') + + expect(get_list_response.records.first.export_type).to eq('export_type-input') + + expect(get_list_response.records.first.id).to eq('id-input') + end + + it 'exposes the cursors for before and after' do + expect(get_list_response.before).to eq(nil) + expect(get_list_response.after).to eq('ABC123') + end + + specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') } + end + end + + describe '#all' do + let!(:first_response_stub) do + stub_request(:get, %r{.*api.gocardless.com/exports$}).to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + cursors: { after: 'AB345' }, + limit: 1, + }, + }.to_json, + headers: response_headers + ) + end + + let!(:second_response_stub) do + stub_request(:get, %r{.*api.gocardless.com/exports\?after=AB345}).to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + limit: 2, + cursors: {}, + }, + }.to_json, + headers: response_headers + ) + end + + it 'automatically makes the extra requests' do + expect(client.exports.all.to_a.length).to eq(2) + expect(first_response_stub).to have_been_requested + expect(second_response_stub).to have_been_requested + end + end +end diff --git a/spec/resources/subscription_spec.rb b/spec/resources/subscription_spec.rb index 0cb9916..afeca8d 100644 --- a/spec/resources/subscription_spec.rb +++ b/spec/resources/subscription_spec.rb @@ -30,6 +30,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -59,6 +60,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -88,6 +90,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -150,6 +153,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -202,6 +206,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -245,6 +250,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -293,6 +299,8 @@ expect(get_list_response.records.first.name).to eq('name-input') + expect(get_list_response.records.first.parent_plan_paused).to eq('parent_plan_paused-input') + expect(get_list_response.records.first.payment_reference).to eq('payment_reference-input') expect(get_list_response.records.first.retry_if_possible).to eq('retry_if_possible-input') @@ -334,6 +342,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -369,6 +378,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -420,6 +430,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -465,6 +476,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -532,6 +544,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -577,6 +590,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -628,6 +642,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -668,6 +683,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -719,6 +735,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -759,6 +776,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -810,6 +828,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', diff --git a/spec/services/exports_service_spec.rb b/spec/services/exports_service_spec.rb new file mode 100644 index 0000000..081b4aa --- /dev/null +++ b/spec/services/exports_service_spec.rb @@ -0,0 +1,370 @@ +require 'spec_helper' + +describe GoCardlessPro::Services::ExportsService do + let(:client) do + GoCardlessPro::Client.new( + access_token: 'SECRET_TOKEN' + ) + end + + let(:response_headers) { { 'Content-Type' => 'application/json' } } + + describe '#get' do + let(:id) { 'ID123' } + + subject(:get_response) { client.exports.get(id) } + + context 'passing in a custom header' do + let!(:stub) do + stub_url = '/exports/:identity'.gsub(':identity', id) + stub_request(:get, /.*api.gocardless.com#{stub_url}/). + with(headers: { 'Foo' => 'Bar' }). + to_return( + body: { + 'exports' => { + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }, + }.to_json, + headers: response_headers + ) + end + + subject(:get_response) do + client.exports.get(id, headers: { + 'Foo' => 'Bar', + }) + end + + it 'includes the header' do + get_response + expect(stub).to have_been_requested + end + end + + context 'when there is a export to return' do + before do + stub_url = '/exports/:identity'.gsub(':identity', id) + stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return( + body: { + 'exports' => { + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }, + }.to_json, + headers: response_headers + ) + end + + it 'wraps the response in a resource' do + expect(get_response).to be_a(GoCardlessPro::Resources::Export) + end + end + + context 'when nothing is returned' do + before do + stub_url = '/exports/:identity'.gsub(':identity', id) + stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return( + body: '', + headers: response_headers + ) + end + + it 'returns nil' do + expect(get_response).to be_nil + end + end + + context "when an ID is specified which can't be included in a valid URI" do + let(:id) { '`' } + + it "doesn't raise an error" do + expect { get_response }.to_not raise_error(/bad URI/) + end + end + + describe 'retry behaviour' do + before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) } + + it 'retries timeouts' do + stub_url = '/exports/:identity'.gsub(':identity', id) + + stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/). + to_timeout.then.to_return({ status: 200, headers: response_headers }) + + get_response + expect(stub).to have_been_requested.twice + end + + it 'retries 5XX errors, other than 500s' do + stub_url = '/exports/:identity'.gsub(':identity', id) + + stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/). + to_return({ status: 502, + headers: { 'Content-Type' => 'text/html' }, + body: 'Response from Cloudflare' }). + then.to_return({ status: 200, headers: response_headers }) + + get_response + expect(stub).to have_been_requested.twice + end + + it 'retries 500 errors returned by the API' do + stub_url = '/exports/:identity'.gsub(':identity', id) + + gocardless_error = { + 'error' => { + 'message' => 'Internal server error', + 'documentation_url' => 'https://developer.gocardless.com/#gocardless', + 'errors' => [{ + 'message' => 'Internal server error', + 'reason' => 'internal_server_error', + }], + 'type' => 'gocardless', + 'code' => 500, + 'request_id' => 'dummy_request_id', + 'id' => 'dummy_exception_id', + }, + } + + stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/). + to_return({ status: 500, + headers: response_headers, + body: gocardless_error.to_json }). + then.to_return({ status: 200, headers: response_headers }) + + get_response + expect(stub).to have_been_requested.twice + end + end + end + + describe '#list' do + describe 'with no filters' do + subject(:get_list_response) { client.exports.list } + + let(:body) do + { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + cursors: { + before: nil, + after: 'ABC123', + }, + }, + }.to_json + end + + before do + stub_request(:get, %r{.*api.gocardless.com/exports}).to_return( + body: body, + headers: response_headers + ) + end + + it 'wraps each item in the resource class' do + expect(get_list_response.records.map { |x| x.class }.uniq.first).to eq(GoCardlessPro::Resources::Export) + + expect(get_list_response.records.first.created_at).to eq('created_at-input') + + expect(get_list_response.records.first.currency).to eq('currency-input') + + expect(get_list_response.records.first.download_url).to eq('download_url-input') + + expect(get_list_response.records.first.export_type).to eq('export_type-input') + + expect(get_list_response.records.first.id).to eq('id-input') + end + + it 'exposes the cursors for before and after' do + expect(get_list_response.before).to eq(nil) + expect(get_list_response.after).to eq('ABC123') + end + + specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') } + + describe 'retry behaviour' do + before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) } + + it 'retries timeouts' do + stub = stub_request(:get, %r{.*api.gocardless.com/exports}). + to_timeout.then.to_return({ status: 200, headers: response_headers, body: body }) + + get_list_response + expect(stub).to have_been_requested.twice + end + + it 'retries 5XX errors' do + stub = stub_request(:get, %r{.*api.gocardless.com/exports}). + to_return({ status: 502, + headers: { 'Content-Type' => 'text/html' }, + body: 'Response from Cloudflare' }). + then.to_return({ status: 200, headers: response_headers, body: body }) + + get_list_response + expect(stub).to have_been_requested.twice + end + end + end + end + + describe '#all' do + let!(:first_response_stub) do + stub_request(:get, %r{.*api.gocardless.com/exports$}).to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + cursors: { after: 'AB345' }, + limit: 1, + }, + }.to_json, + headers: response_headers + ) + end + + let!(:second_response_stub) do + stub_request(:get, %r{.*api.gocardless.com/exports\?after=AB345}).to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + limit: 2, + cursors: {}, + }, + }.to_json, + headers: response_headers + ) + end + + it 'automatically makes the extra requests' do + expect(client.exports.all.to_a.length).to eq(2) + expect(first_response_stub).to have_been_requested + expect(second_response_stub).to have_been_requested + end + + describe 'retry behaviour' do + before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) } + + it 'retries timeouts' do + first_response_stub = stub_request(:get, %r{.*api.gocardless.com/exports$}).to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + cursors: { after: 'AB345' }, + limit: 1, + }, + }.to_json, + headers: response_headers + ) + + second_response_stub = stub_request(:get, %r{.*api.gocardless.com/exports\?after=AB345}). + to_timeout.then. + to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + limit: 2, + cursors: {}, + }, + }.to_json, + headers: response_headers + ) + + client.exports.all.to_a + + expect(first_response_stub).to have_been_requested + expect(second_response_stub).to have_been_requested.twice + end + + it 'retries 5XX errors' do + first_response_stub = stub_request(:get, %r{.*api.gocardless.com/exports$}).to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + cursors: { after: 'AB345' }, + limit: 1, + }, + }.to_json, + headers: response_headers + ) + + second_response_stub = stub_request(:get, %r{.*api.gocardless.com/exports\?after=AB345}). + to_return( + status: 502, + body: 'Response from Cloudflare', + headers: { 'Content-Type' => 'text/html' } + ).then.to_return( + body: { + 'exports' => [{ + + 'created_at' => 'created_at-input', + 'currency' => 'currency-input', + 'download_url' => 'download_url-input', + 'export_type' => 'export_type-input', + 'id' => 'id-input', + }], + meta: { + limit: 2, + cursors: {}, + }, + }.to_json, + headers: response_headers + ) + + client.exports.all.to_a + + expect(first_response_stub).to have_been_requested + expect(second_response_stub).to have_been_requested.twice + end + end + end +end diff --git a/spec/services/subscriptions_service_spec.rb b/spec/services/subscriptions_service_spec.rb index 9bba669..060f553 100644 --- a/spec/services/subscriptions_service_spec.rb +++ b/spec/services/subscriptions_service_spec.rb @@ -30,6 +30,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -59,6 +60,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -88,6 +90,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -173,6 +176,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -225,6 +229,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -283,6 +288,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -336,6 +342,8 @@ expect(get_list_response.records.first.name).to eq('name-input') + expect(get_list_response.records.first.parent_plan_paused).to eq('parent_plan_paused-input') + expect(get_list_response.records.first.payment_reference).to eq('payment_reference-input') expect(get_list_response.records.first.retry_if_possible).to eq('retry_if_possible-input') @@ -400,6 +408,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -435,6 +444,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -479,6 +489,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -514,6 +525,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -554,6 +566,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -592,6 +605,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -643,6 +657,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -688,6 +703,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -810,6 +826,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -880,6 +897,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -942,6 +960,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -982,6 +1001,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -1044,6 +1064,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -1084,6 +1105,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input', @@ -1146,6 +1168,7 @@ 'metadata' => 'metadata-input', 'month' => 'month-input', 'name' => 'name-input', + 'parent_plan_paused' => 'parent_plan_paused-input', 'payment_reference' => 'payment_reference-input', 'retry_if_possible' => 'retry_if_possible-input', 'start_date' => 'start_date-input',