diff --git a/lib/gocardless_pro.rb b/lib/gocardless_pro.rb index ba22201..bab1ca2 100644 --- a/lib/gocardless_pro.rb +++ b/lib/gocardless_pro.rb @@ -84,6 +84,9 @@ module GoCardlessPro require_relative 'gocardless_pro/resources/institution' require_relative 'gocardless_pro/services/institutions_service' +require_relative 'gocardless_pro/resources/logo' +require_relative 'gocardless_pro/services/logos_service' + require_relative 'gocardless_pro/resources/mandate' require_relative 'gocardless_pro/services/mandates_service' @@ -102,6 +105,9 @@ module GoCardlessPro require_relative 'gocardless_pro/resources/payer_authorisation' require_relative 'gocardless_pro/services/payer_authorisations_service' +require_relative 'gocardless_pro/resources/payer_theme' +require_relative 'gocardless_pro/services/payer_themes_service' + require_relative 'gocardless_pro/resources/payment' require_relative 'gocardless_pro/services/payments_service' diff --git a/lib/gocardless_pro/client.rb b/lib/gocardless_pro/client.rb index 4e8d8eb..f50b0ae 100644 --- a/lib/gocardless_pro/client.rb +++ b/lib/gocardless_pro/client.rb @@ -78,6 +78,11 @@ def institutions @institutions ||= Services::InstitutionsService.new(@api_service) end + # Access to the service for logo to make API calls + def logos + @logos ||= Services::LogosService.new(@api_service) + end + # Access to the service for mandate to make API calls def mandates @mandates ||= Services::MandatesService.new(@api_service) @@ -108,6 +113,11 @@ def payer_authorisations @payer_authorisations ||= Services::PayerAuthorisationsService.new(@api_service) end + # Access to the service for payer_theme to make API calls + def payer_themes + @payer_themes ||= Services::PayerThemesService.new(@api_service) + end + # Access to the service for payment to make API calls def payments @payments ||= Services::PaymentsService.new(@api_service) @@ -218,7 +228,7 @@ def default_options 'User-Agent' => "#{user_agent}", 'Content-Type' => 'application/json', 'GoCardless-Client-Library' => 'gocardless-pro-ruby', - 'GoCardless-Client-Version' => '2.55.0', + 'GoCardless-Client-Version' => '2.56.0', }, } end diff --git a/lib/gocardless_pro/resources/logo.rb b/lib/gocardless_pro/resources/logo.rb new file mode 100644 index 0000000..e171847 --- /dev/null +++ b/lib/gocardless_pro/resources/logo.rb @@ -0,0 +1,38 @@ +# +# 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 logo resource returned from the API + + # Logos are image uploads that, when associated with a creditor, are shown + # on the [billing request flow](#billing-requests-billing-request-flows) + # payment pages. + class Logo + attr_reader :id + + # Initialize a logo resource instance + # @param object [Hash] an object returned from the API + def initialize(object, response = nil) + @object = object + + @id = object['id'] + @response = response + end + + def api_response + ApiResponse.new(@response) + end + + # Provides the logo resource as a hash of all its readable attributes + def to_h + @object + end + end + end +end diff --git a/lib/gocardless_pro/resources/payer_theme.rb b/lib/gocardless_pro/resources/payer_theme.rb new file mode 100644 index 0000000..95e8ce2 --- /dev/null +++ b/lib/gocardless_pro/resources/payer_theme.rb @@ -0,0 +1,36 @@ +# +# 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 payer_theme resource returned from the API + + # Custom colour themes for payment pages and customer notifications. + class PayerTheme + attr_reader :id + + # Initialize a payer_theme resource instance + # @param object [Hash] an object returned from the API + def initialize(object, response = nil) + @object = object + + @id = object['id'] + @response = response + end + + def api_response + ApiResponse.new(@response) + end + + # Provides the payer_theme resource as a hash of all its readable attributes + def to_h + @object + end + end + end +end diff --git a/lib/gocardless_pro/services/logos_service.rb b/lib/gocardless_pro/services/logos_service.rb new file mode 100644 index 0000000..09addf7 --- /dev/null +++ b/lib/gocardless_pro/services/logos_service.rb @@ -0,0 +1,48 @@ +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 Logo endpoints + 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. + # Example URL: /branding/logos + # @param options [Hash] parameters as a hash, under a params key. + def create_for_creditor(options = {}) + path = '/branding/logos' + + params = options.delete(:params) || {} + options[:params] = {} + options[:params][envelope_key] = params + + options[:retry_failures] = true + + response = make_request(:post, path, options) + + return if response.body.nil? + + Resources::Logo.new(unenvelope_body(response.body), response) + 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 + 'logos' + end + end + end +end diff --git a/lib/gocardless_pro/services/payer_themes_service.rb b/lib/gocardless_pro/services/payer_themes_service.rb new file mode 100644 index 0000000..290d845 --- /dev/null +++ b/lib/gocardless_pro/services/payer_themes_service.rb @@ -0,0 +1,49 @@ +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 PayerTheme endpoints + class PayerThemesService < BaseService + # Creates a new payer theme associated with a creditor. If a creditor already + # has payer themes, this will update the existing payer theme linked to the + # creditor. + # Example URL: /branding/payer_themes + # @param options [Hash] parameters as a hash, under a params key. + def create_for_creditor(options = {}) + path = '/branding/payer_themes' + + params = options.delete(:params) || {} + options[:params] = {} + options[:params][envelope_key] = params + + options[:retry_failures] = true + + response = make_request(:post, path, options) + + return if response.body.nil? + + Resources::PayerTheme.new(unenvelope_body(response.body), response) + 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 + 'payer_themes' + end + end + end +end diff --git a/lib/gocardless_pro/version.rb b/lib/gocardless_pro/version.rb index aa1d9fb..01b5abe 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.55.0' + VERSION = '2.56.0' end diff --git a/spec/resources/logo_spec.rb b/spec/resources/logo_spec.rb new file mode 100644 index 0000000..4e20624 --- /dev/null +++ b/spec/resources/logo_spec.rb @@ -0,0 +1,113 @@ +require 'spec_helper' + +describe GoCardlessPro::Resources::Logo do + let(:client) do + GoCardlessPro::Client.new( + access_token: 'SECRET_TOKEN' + ) + end + + let(:response_headers) { { 'Content-Type' => 'application/json' } } + + describe '#create' do + subject(:post_create_response) { client.logos.create_for_creditor(params: new_resource) } + context 'with a valid request' do + let(:new_resource) do + { + + 'id' => 'id-input', + } + end + + before do + stub_request(:post, %r{.*api.gocardless.com/branding/logos}). + with( + body: { + 'logos' => { + + 'id' => 'id-input', + }, + } + ). + to_return( + body: { + 'logos' => + + { + + 'id' => 'id-input', + }, + + }.to_json, + headers: response_headers + ) + end + + it 'creates and returns the resource' do + expect(post_create_response).to be_a(GoCardlessPro::Resources::Logo) + end + end + + context 'with a request that returns a validation error' do + let(:new_resource) { {} } + + before do + stub_request(:post, %r{.*api.gocardless.com/branding/logos}).to_return( + body: { + error: { + type: 'validation_failed', + code: 422, + errors: [ + { message: 'test error message', field: 'test_field' }, + ], + }, + }.to_json, + headers: response_headers, + status: 422 + ) + end + + it 'throws the correct error' do + expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError) + end + end + + context 'with a request that returns an idempotent creation conflict error' do + let(:id) { 'ID123' } + + let(:new_resource) do + { + + 'id' => 'id-input', + } + end + + let!(:post_stub) do + stub_request(:post, %r{.*api.gocardless.com/branding/logos}).to_return( + body: { + error: { + type: 'invalid_state', + code: 409, + errors: [ + { + message: 'A resource has already been created with this idempotency key', + reason: 'idempotent_creation_conflict', + links: { + conflicting_resource_id: id, + }, + }, + ], + }, + }.to_json, + headers: response_headers, + status: 409 + ) + end + + it 'raises an InvalidStateError' do + expect { post_create_response }.to raise_error(GoCardlessPro::InvalidStateError) + expect(post_stub).to have_been_requested + end + end + end +end diff --git a/spec/resources/payer_theme_spec.rb b/spec/resources/payer_theme_spec.rb new file mode 100644 index 0000000..f5d0381 --- /dev/null +++ b/spec/resources/payer_theme_spec.rb @@ -0,0 +1,113 @@ +require 'spec_helper' + +describe GoCardlessPro::Resources::PayerTheme do + let(:client) do + GoCardlessPro::Client.new( + access_token: 'SECRET_TOKEN' + ) + end + + let(:response_headers) { { 'Content-Type' => 'application/json' } } + + describe '#create' do + subject(:post_create_response) { client.payer_themes.create_for_creditor(params: new_resource) } + context 'with a valid request' do + let(:new_resource) do + { + + 'id' => 'id-input', + } + end + + before do + stub_request(:post, %r{.*api.gocardless.com/branding/payer_themes}). + with( + body: { + 'payer_themes' => { + + 'id' => 'id-input', + }, + } + ). + to_return( + body: { + 'payer_themes' => + + { + + 'id' => 'id-input', + }, + + }.to_json, + headers: response_headers + ) + end + + it 'creates and returns the resource' do + expect(post_create_response).to be_a(GoCardlessPro::Resources::PayerTheme) + end + end + + context 'with a request that returns a validation error' do + let(:new_resource) { {} } + + before do + stub_request(:post, %r{.*api.gocardless.com/branding/payer_themes}).to_return( + body: { + error: { + type: 'validation_failed', + code: 422, + errors: [ + { message: 'test error message', field: 'test_field' }, + ], + }, + }.to_json, + headers: response_headers, + status: 422 + ) + end + + it 'throws the correct error' do + expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError) + end + end + + context 'with a request that returns an idempotent creation conflict error' do + let(:id) { 'ID123' } + + let(:new_resource) do + { + + 'id' => 'id-input', + } + end + + let!(:post_stub) do + stub_request(:post, %r{.*api.gocardless.com/branding/payer_themes}).to_return( + body: { + error: { + type: 'invalid_state', + code: 409, + errors: [ + { + message: 'A resource has already been created with this idempotency key', + reason: 'idempotent_creation_conflict', + links: { + conflicting_resource_id: id, + }, + }, + ], + }, + }.to_json, + headers: response_headers, + status: 409 + ) + end + + it 'raises an InvalidStateError' do + expect { post_create_response }.to raise_error(GoCardlessPro::InvalidStateError) + expect(post_stub).to have_been_requested + end + end + end +end diff --git a/spec/services/logos_service_spec.rb b/spec/services/logos_service_spec.rb new file mode 100644 index 0000000..e8dcded --- /dev/null +++ b/spec/services/logos_service_spec.rb @@ -0,0 +1,136 @@ +require 'spec_helper' + +describe GoCardlessPro::Services::LogosService do + let(:client) do + GoCardlessPro::Client.new( + access_token: 'SECRET_TOKEN' + ) + end + + let(:response_headers) { { 'Content-Type' => 'application/json' } } + + describe '#create' do + subject(:post_create_response) { client.logos.create_for_creditor(params: new_resource) } + context 'with a valid request' do + let(:new_resource) do + { + + 'id' => 'id-input', + } + end + + before do + stub_request(:post, %r{.*api.gocardless.com/branding/logos}). + with( + body: { + 'logos' => { + + 'id' => 'id-input', + }, + } + ). + to_return( + body: { + 'logos' => + + { + + 'id' => 'id-input', + }, + + }.to_json, + headers: response_headers + ) + end + + it 'creates and returns the resource' do + expect(post_create_response).to be_a(GoCardlessPro::Resources::Logo) + end + + describe 'retry behaviour' do + before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) } + + it 'retries timeouts' do + stub = stub_request(:post, %r{.*api.gocardless.com/branding/logos}). + to_timeout.then.to_return({ status: 200, headers: response_headers }) + + post_create_response + expect(stub).to have_been_requested.twice + end + + it 'retries 5XX errors' do + stub = stub_request(:post, %r{.*api.gocardless.com/branding/logos}). + to_return({ status: 502, + headers: { 'Content-Type' => 'text/html' }, + body: 'Response from Cloudflare' }). + then.to_return({ status: 200, headers: response_headers }) + + post_create_response + expect(stub).to have_been_requested.twice + end + end + end + + context 'with a request that returns a validation error' do + let(:new_resource) { {} } + + before do + stub_request(:post, %r{.*api.gocardless.com/branding/logos}).to_return( + body: { + error: { + type: 'validation_failed', + code: 422, + errors: [ + { message: 'test error message', field: 'test_field' }, + ], + }, + }.to_json, + headers: response_headers, + status: 422 + ) + end + + it 'throws the correct error' do + expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError) + end + end + + context 'with a request that returns an idempotent creation conflict error' do + let(:id) { 'ID123' } + + let(:new_resource) do + { + + 'id' => 'id-input', + } + end + + let!(:post_stub) do + stub_request(:post, %r{.*api.gocardless.com/branding/logos}).to_return( + body: { + error: { + type: 'invalid_state', + code: 409, + errors: [ + { + message: 'A resource has already been created with this idempotency key', + reason: 'idempotent_creation_conflict', + links: { + conflicting_resource_id: id, + }, + }, + ], + }, + }.to_json, + headers: response_headers, + status: 409 + ) + end + + it 'raises an InvalidStateError' do + expect { post_create_response }.to raise_error(GoCardlessPro::InvalidStateError) + expect(post_stub).to have_been_requested + end + end + end +end diff --git a/spec/services/payer_themes_service_spec.rb b/spec/services/payer_themes_service_spec.rb new file mode 100644 index 0000000..905a8f2 --- /dev/null +++ b/spec/services/payer_themes_service_spec.rb @@ -0,0 +1,136 @@ +require 'spec_helper' + +describe GoCardlessPro::Services::PayerThemesService do + let(:client) do + GoCardlessPro::Client.new( + access_token: 'SECRET_TOKEN' + ) + end + + let(:response_headers) { { 'Content-Type' => 'application/json' } } + + describe '#create' do + subject(:post_create_response) { client.payer_themes.create_for_creditor(params: new_resource) } + context 'with a valid request' do + let(:new_resource) do + { + + 'id' => 'id-input', + } + end + + before do + stub_request(:post, %r{.*api.gocardless.com/branding/payer_themes}). + with( + body: { + 'payer_themes' => { + + 'id' => 'id-input', + }, + } + ). + to_return( + body: { + 'payer_themes' => + + { + + 'id' => 'id-input', + }, + + }.to_json, + headers: response_headers + ) + end + + it 'creates and returns the resource' do + expect(post_create_response).to be_a(GoCardlessPro::Resources::PayerTheme) + end + + describe 'retry behaviour' do + before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) } + + it 'retries timeouts' do + stub = stub_request(:post, %r{.*api.gocardless.com/branding/payer_themes}). + to_timeout.then.to_return({ status: 200, headers: response_headers }) + + post_create_response + expect(stub).to have_been_requested.twice + end + + it 'retries 5XX errors' do + stub = stub_request(:post, %r{.*api.gocardless.com/branding/payer_themes}). + to_return({ status: 502, + headers: { 'Content-Type' => 'text/html' }, + body: 'Response from Cloudflare' }). + then.to_return({ status: 200, headers: response_headers }) + + post_create_response + expect(stub).to have_been_requested.twice + end + end + end + + context 'with a request that returns a validation error' do + let(:new_resource) { {} } + + before do + stub_request(:post, %r{.*api.gocardless.com/branding/payer_themes}).to_return( + body: { + error: { + type: 'validation_failed', + code: 422, + errors: [ + { message: 'test error message', field: 'test_field' }, + ], + }, + }.to_json, + headers: response_headers, + status: 422 + ) + end + + it 'throws the correct error' do + expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError) + end + end + + context 'with a request that returns an idempotent creation conflict error' do + let(:id) { 'ID123' } + + let(:new_resource) do + { + + 'id' => 'id-input', + } + end + + let!(:post_stub) do + stub_request(:post, %r{.*api.gocardless.com/branding/payer_themes}).to_return( + body: { + error: { + type: 'invalid_state', + code: 409, + errors: [ + { + message: 'A resource has already been created with this idempotency key', + reason: 'idempotent_creation_conflict', + links: { + conflicting_resource_id: id, + }, + }, + ], + }, + }.to_json, + headers: response_headers, + status: 409 + ) + end + + it 'raises an InvalidStateError' do + expect { post_create_response }.to raise_error(GoCardlessPro::InvalidStateError) + expect(post_stub).to have_been_requested + end + end + end +end