diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fe632a..c87d606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ ## [Unreleased] +## [1.3.0] - 2024-10-30 + +- Add support for general configuration and use Incognia::Api as a static class + +## [1.2.0] - 2024-08-26 + +- Removes the requirement to send installation id to register signup, login and payment + ## [1.1.0] - 2024-07-24 - Add support to passing request_token and occurred_at to #register_feedback diff --git a/Gemfile.lock b/Gemfile.lock index 2b3346c..5304589 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - incognia_api (1.1.0) + incognia_api (1.3.0) faraday (~> 1.10) faraday_middleware (~> 1.2) diff --git a/README.md b/README.md index 2ccd731..0fc4420 100644 --- a/README.md +++ b/README.md @@ -35,28 +35,30 @@ Or install it yourself as: ### Configuration -Before using the API client, you must initialize it using credentials obtained -from the [Incognia dashboard](): +Before using the API client, you must configure it using credentials obtained +from the [Incognia dashboard](https://dash.incognia.com/): ```ruby -api = Incognia::Api.new(client_id: "your-client-id", client_secret: -"your-client-secret") +Incognia.configure(client_id: ENV['INCOGNIA_CLIENT_ID'], client_secret: ENV['INCOGNIA_CLIENT_SECRET']) +# Incognia.configure(client_id: "your-client-id", client_secret: "your-client-secret") ``` -For sandbox credentials, refer to the [API testing guide](). +For sandbox credentials, refer to the [API testing guide](https://developer.incognia.com/). + +:bulb: For Rails applications it's recommended to create an initializer file, for example `config/initializers/incognia.rb`. ### Registering a Signup -This method registers a new signup for the given installation and address, returning a signup assessment, containing the risk assessment and supporting evidence: +This method registers a new signup for the given request token and address, returning a signup assessment, containing the risk assessment and supporting evidence: ```ruby address = Incognia::Address.new(line: "West 34th Street, New York City, NY 10001") -installation_id = "WlMksW+jh5GPhqWBorsV8yDihoSHHpmt+DpjJ7eYxpHhuO/5tuHTuA..." +request_token = "WlMksW+jh5GPhqWBorsV8yDihoSHHpmt+DpjJ7eYxpHhuO/5tuHTuA..." -assessment = api.register_signup( - installation_id: installation_id, +assessment = Incognia::Api.register_signup( + request_token: request_token, address: address ) @@ -68,11 +70,11 @@ It also supports optional parameters, for example: ```ruby address = Incognia::Address.new(line: "West 34th Street, New York City, NY 10001") -installation_id = "WlMksW+jh5GPhqWBorsV8yDihoSHHpmt+DpjJ7eYxpHhuO/5tuHTuA..." +request_token = "WlMksW+jh5GPhqWBorsV8yDihoSHHpmt+DpjJ7eYxpHhuO/5tuHTuA..." external_id = "7b02736a-7718-4b83-8982-f68fb6f501fa" -assessment = api.register_signup( - installation_id: installation_id, +assessment = Incognia::Api.register_signup( + request_token: request_token, address: address, external_id: external_id ) @@ -82,14 +84,14 @@ assessment = api.register_signup( ### Registering a Login -This method registers a new login for the given installation and account, returning a login assessment, containing the risk assessment and supporting evidence: +This method registers a new login for the given request token and account, returning a login assessment, containing the risk assessment and supporting evidence: ```ruby -installation_id = "WlMksW+jh5GPhqWBorsV8yDihoSHHpmt+DpjJ7eYxpHhuO/5tuHTuA..." +request_token = "WlMksW+jh5GPhqWBorsV8yDihoSHHpmt+DpjJ7eYxpHhuO/5tuHTuA..." account_id = 'account-identifier-123' -assessment = api.register_login( - installation_id: installation_id, +assessment = Incognia::Api.register_login( + request_token: request_token, account_id: account_id, ) @@ -100,12 +102,12 @@ assessment = api.register_login( It also supports optional parameters, for example: ```ruby -installation_id = "WlMksW+jh5GPhqWBorsV8yDihoSHHpmt+DpjJ7eYxpHhuO/5tuHTuA..." +request_token = "WlMksW+jh5GPhqWBorsV8yDihoSHHpmt+DpjJ7eYxpHhuO/5tuHTuA..." account_id = 'account-identifier-123' external_id = 'some-external-identifier' -assessment = api.register_login( - installation_id: installation_id, +assessment = Incognia::Api.register_login( + request_token: request_token, account_id: account_id, external_id: external_id, eval: false # can be used to register a new login without evaluating it @@ -116,12 +118,12 @@ assessment = api.register_login( ### Registering Payment -This method registers a new payment for the given installation and account, returning a `hash`, +This method registers a new payment for the given request token and account, returning a `hash`, containing the risk assessment and supporting evidence. ```ruby -assessment = api.register_payment( - installation_id: 'installation-id', +assessment = Incognia::Api.register_payment( + request_token: 'request-token', account_id: 'account-id' ) @@ -180,8 +182,8 @@ payment_methods = [ } ] -assessment = api.register_payment( - installation_id: 'installation-id', +assessment = Incognia::Api.register_payment( + request_token: 'request-token', account_id: 'account-id', external_id: 'external-id', addresses: addresses, @@ -202,14 +204,14 @@ The `expires_at` argument should be a _Time_, _DateTime_ or an date in **RFC 333 ```ruby -installation_id = 'installation-id' +request_token = 'request-token' account_id = 'account-id' occurred_at = DateTime.parse('2024-07-22T15:20:00Z') -success = api.register_feedback( +success = Incognia::Api.register_feedback( event: Incognia::Constants::FeedbackEvent::ACCOUNT_TAKEOVER, occurred_at: occurred_at, - installation_id: installation_id, + request_token: request_token, account_id: account_id ) @@ -219,10 +221,10 @@ success = api.register_feedback( For custom fraud, set the value of `event` with the corresponding code: ```ruby -success = api.register_feedback( +success = Incognia::Api.register_feedback( event: 'custom_fraud_name', occurred_at: occurred_at, - installation_id: installation_id, + request_token: request_token, account_id: account_id ) diff --git a/lib/incognia_api.rb b/lib/incognia_api.rb index 0ffe03c..72c0c59 100644 --- a/lib/incognia_api.rb +++ b/lib/incognia_api.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require_relative "incognia_api/configuration" require_relative "incognia_api/version" require_relative "incognia_api/client" require_relative "incognia_api/util" @@ -15,6 +16,14 @@ require_relative "incognia_api/constants/feedback_event" module Incognia + def self.configure(**args) + config.configure(**args) + end + + def self.config + Configuration.instance + end + class APIError < StandardError attr_reader :message, :errors, :status diff --git a/lib/incognia_api/api.rb b/lib/incognia_api/api.rb index 0aea807..788c8bf 100644 --- a/lib/incognia_api/api.rb +++ b/lib/incognia_api/api.rb @@ -5,79 +5,82 @@ module Incognia class Api - # business layer: uses the Client to build domain objects - # raises missing parameters errors - attr_accessor :connection - - def initialize(client_id:, client_secret:) - @connection = Client.new(client_id: client_id, - client_secret: client_secret, - host: "https://api.incognia.com/api") - end - - def register_signup(installation_id:, address: nil, **opts) - params = { installation_id: installation_id } - params.merge!(opts) - params.merge!(address&.to_hash) if address - - response = connection.request( - :post, - 'v2/onboarding/signups', - params - ) - - SignupAssessment.from_hash(response.body) if response.success? - end - - def register_login(installation_id:, account_id:, **opts) - params = { - type: :login, - installation_id: installation_id, - account_id: account_id, - } - params.merge!(opts) - - response = connection.request( - :post, - 'v2/authentication/transactions', - params - ) - - LoginAssessment.from_hash(response.body) if response.success? - end + class << self + # business layer: uses the Client.instance to build domain objects + # raises missing parameters errors + + def register_signup(request_token: nil, address: nil, **opts) + params = { request_token: request_token }.compact + params.merge!(opts) + params.merge!(address&.to_hash) if address + + response = connection.request( + :post, + 'v2/onboarding/signups', + params + ) + + SignupAssessment.from_hash(response.body) if response.success? + end - def register_feedback(event:, occurred_at: nil, expires_at: nil, timestamp: nil, **ids) - if !timestamp.nil? - warn("Deprecation warning: use occurred_at instead of timestamp") + def register_login(account_id:, request_token: nil, **opts) + params = { + type: :login, + account_id: account_id, + request_token: request_token + }.compact + params.merge!(opts) + + response = connection.request( + :post, + 'v2/authentication/transactions', + params + ) + + LoginAssessment.from_hash(response.body) if response.success? end - timestamp = timestamp.strftime('%s%L') if timestamp.respond_to? :strftime - occurred_at = occurred_at.to_datetime.rfc3339 if occurred_at.respond_to? :to_datetime - expires_at = expires_at.to_datetime.rfc3339 if expires_at.respond_to? :to_datetime + def register_feedback(event:, occurred_at: nil, expires_at: nil, timestamp: nil, **ids) + if !timestamp.nil? + warn("Deprecation warning: use occurred_at instead of timestamp") + end - params = { event: event, timestamp: timestamp&.to_i, occurred_at: occurred_at, expires_at: expires_at }.compact - params.merge!(ids) + timestamp = timestamp.strftime('%s%L') if timestamp.respond_to? :strftime + occurred_at = occurred_at.to_datetime.rfc3339 if occurred_at.respond_to? :to_datetime + expires_at = expires_at.to_datetime.rfc3339 if expires_at.respond_to? :to_datetime - response = connection.request( - :post, - '/api/v2/feedbacks', - params - ) + params = { event: event, timestamp: timestamp&.to_i, occurred_at: occurred_at, expires_at: expires_at }.compact + params.merge!(ids) - response.success? - end + response = connection.request( + :post, + '/api/v2/feedbacks', + params + ) - def register_payment(installation_id:, account_id:, **opts) - params = { installation_id: installation_id, account_id: account_id, type: :payment } - params.merge!(opts) + response.success? + end - response = connection.request( - :post, - 'v2/authentication/transactions', - params - ) + def register_payment(account_id:, request_token: nil, **opts) + params = { + type: :payment, + account_id: account_id, + request_token: request_token + }.compact + params.merge!(opts) + + response = connection.request( + :post, + 'v2/authentication/transactions', + params + ) + + PaymentAssessment.from_hash(response.body) if response.success? + end - PaymentAssessment.from_hash(response.body) if response.success? + def connection + Client.instance + end end end end diff --git a/lib/incognia_api/client.rb b/lib/incognia_api/client.rb index f03e3eb..a5b8ae1 100644 --- a/lib/incognia_api/client.rb +++ b/lib/incognia_api/client.rb @@ -1,32 +1,14 @@ require "time" +require "singleton" module Incognia class Client + include Singleton # TODO: # (ok) http/adapter specific code # (ok) raises network/authentication errors # (ok) handles token refreshing ok # future: handles retrying - attr_reader :connection - - def initialize(client_id:, client_secret:, host:) - @client_id = client_id - @client_secret = client_secret - @host = host - - headers = { 'User-Agent' => "incognia-ruby/#{Incognia::VERSION} " \ - "({#{RbConfig::CONFIG['host']}}) " \ - "{#{RbConfig::CONFIG['arch']}} " \ - "Ruby/#{RbConfig::CONFIG['ruby_version']}" } - - @connection = Faraday.new(host, headers: headers) do |faraday| - faraday.request :json - faraday.response :json, content_type: /\bjson$/ - faraday.response :raise_error - - faraday.adapter Faraday.default_adapter - end - end def request(method, endpoint = nil, data = nil, headers = {}) json_data = JSON.generate(data) if data @@ -48,12 +30,29 @@ def credentials @credentials end + def connection + return @connection if @connection + + headers = { 'User-Agent' => "incognia-ruby/#{Incognia::VERSION} " \ + "({#{RbConfig::CONFIG['host']}}) " \ + "{#{RbConfig::CONFIG['arch']}} " \ + "Ruby/#{RbConfig::CONFIG['ruby_version']}" } + + @connection = Faraday.new(Incognia.config.host, headers: headers) do |faraday| + faraday.request :json + faraday.response :json, content_type: /\bjson$/ + faraday.response :raise_error + + faraday.adapter Faraday.default_adapter + end + end + protected def request_credentials basic_auth = Faraday::Request .lookup_middleware(:basic_auth) - .header(@client_id, @client_secret) + .header(Incognia.config.client_id, Incognia.config.client_secret) response = connection.send(:post, 'v2/token') do |r| r.headers[Faraday::Request::Authorization::KEY] = basic_auth diff --git a/lib/incognia_api/configuration.rb b/lib/incognia_api/configuration.rb new file mode 100644 index 0000000..42ff154 --- /dev/null +++ b/lib/incognia_api/configuration.rb @@ -0,0 +1,17 @@ +require 'singleton' + +module Incognia + class Configuration + include Singleton + + attr_accessor :client_id, :client_secret, :host + + def configure(client_id:, client_secret:, host: nil) + @client_id = client_id + @client_secret = client_secret + @host = host || 'https://api.incognia.com/api' + + self + end + end +end diff --git a/lib/incognia_api/version.rb b/lib/incognia_api/version.rb index 93de543..10a0763 100644 --- a/lib/incognia_api/version.rb +++ b/lib/incognia_api/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Incognia - VERSION = "1.1.0" + VERSION = "1.3.0" end diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 0da4e9f..b80641b 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -5,15 +5,17 @@ module Incognia let(:token_fixture) { File.new("spec/fixtures/token.json").read } let(:test_endpoint) { "https://api.incognia.com/api/v2/endpoint" } - subject do - described_class.new( + subject(:instance) { described_class.instance } + + before do + Incognia.configure( client_id: 'client_id', client_secret: 'client_secret', host: 'https://api.incognia.com/api' ) end - context "#request" do + describe "#request" do it "makes an HTTP request" do stub_token_request stub = stub_request(:post, test_endpoint). @@ -24,7 +26,7 @@ module Incognia headers: { 'Content-Type' => 'application/json' } ) - subject.request(:post, 'v2/endpoint', { foo: :bar }) + instance.request(:post, 'v2/endpoint', { foo: :bar }) expect(stub).to have_been_made.once end @@ -42,7 +44,7 @@ module Incognia headers: { 'Content-Type' => 'application/json' } ) - subject.request( + instance.request( :post, "v2/endpoint", { foo: :bar } @@ -68,7 +70,7 @@ module Incognia headers: { 'Content-Type' => 'application/json' } ) - subject.request( + instance.request( :post, "v2/endpoint", { foo: :bar } @@ -87,7 +89,7 @@ module Incognia headers: { 'Content-Type' => 'application/json' } ) - subject.request( + instance.request( :post, 'v2/endpoint', { foo: :bar }, @@ -108,8 +110,8 @@ module Incognia headers: { 'Content-Type' => 'application/json' } ) - expect(subject.request(:post, 'v2/endpoint')).to be_success - expect(subject.request(:post, 'v2/endpoint').body['foo']).to eq('bar') + expect(instance.request(:post, 'v2/endpoint')).to be_success + expect(instance.request(:post, 'v2/endpoint').body['foo']).to eq('bar') end context "when receives errors" do @@ -118,7 +120,7 @@ module Incognia stub_signup_request_400(error: :example) expect { - subject.request(:post, "v2/onboarding/signups") + instance.request(:post, "v2/onboarding/signups") }.to raise_exception APIError, /server responded with status 400/i end @@ -127,7 +129,7 @@ module Incognia stub_signup_request_500 expect { - subject.request(:post, "v2/onboarding/signups") + instance.request(:post, "v2/onboarding/signups") }.to raise_exception APIError end @@ -136,17 +138,19 @@ module Incognia stub_request_timeout("v2/onboarding/signups") expect { - subject.request(:post, "v2/onboarding/signups") + instance.request(:post, "v2/onboarding/signups") }.to raise_exception APIError end end end - context "#credentials" do + describe "#credentials" do + before { Singleton.__init__(described_class) } + it "requests an access token from the /token endpoint" do stub = stub_token_request - credentials = subject.credentials + credentials = instance.credentials expect(credentials).to_not be_nil expect(stub).to have_been_made.once @@ -155,17 +159,17 @@ module Incognia it "provides the access_token and expires_in" do stub_token_request - credentials = subject.credentials + credentials = instance.credentials expect(credentials.access_token).to be expect(credentials.expires_in).to be expect(credentials.generated_at).to be end - it "caches the access_token" do + it "caches the access_token in any instance" do stub = stub_token_request - 2.times { subject.credentials } + 2.times { described_class.instance.credentials } expect(stub).to have_been_made.once end @@ -174,8 +178,8 @@ module Incognia it "calls the /token endpoint again" do stub = stub_token_request - subject.credentials - Timecop.travel(Time.now + 1200) { subject.credentials } + instance.credentials + Timecop.travel(Time.now + 1200) { instance.credentials } expect(stub).to have_been_made.twice end @@ -186,7 +190,7 @@ module Incognia stub_token_request_401 expect { - subject.credentials + instance.credentials }.to raise_exception APIAuthenticationError end end @@ -196,7 +200,7 @@ module Incognia stub_request_timeout("v2/token") expect { - subject.credentials + instance.credentials }.to raise_exception APIError end end diff --git a/spec/incognia_spec.rb b/spec/incognia_spec.rb index 8517b48..c431b2b 100644 --- a/spec/incognia_spec.rb +++ b/spec/incognia_spec.rb @@ -2,12 +2,38 @@ require 'securerandom' module Incognia + RSpec.describe '.configure' do + it 'sets configuration on Configuration.instance' do + config = { + client_id: SecureRandom.uuid, + client_secret: SecureRandom.uuid, + host: 'https://api.incognia.com/api' + } + + Incognia.configure(**config) + + expect(Configuration.instance.client_id).to eq(config[:client_id]) + expect(Configuration.instance.client_secret).to eq(config[:client_secret]) + expect(Configuration.instance.host).to eq(config[:host]) + end + end + + RSpec.describe '.config' do + it 'returns the instance of Configuration' do + expect(Incognia.config).to eq(Configuration.instance) + end + end + RSpec.describe Incognia::Api do - subject(:api) do - Api.new(client_id: 'client_id', client_secret: 'client_secret') + before do + Incognia.configure( + client_id: 'client_id', + client_secret: 'client_secret', + host: 'https://api.incognia.com/api' + ) end - describe "#register_signup" do + describe ".register_signup" do let(:locale) { "en-US" } let(:country_name) { "United States of America" } let(:country_code) { "US" } @@ -41,13 +67,13 @@ module Incognia let(:structured_address) { Address.new(structured: structured_format ) } let(:address) { Address.new(line: line_format) } let(:coordinates_address) { Address.new(coordinates: coordinates_format) } - let(:installation_id) { SecureRandom.uuid } + let(:request_token) { SecureRandom.uuid } it "when successful returns the resource" do stub_token_request stub_signup_request - signup = api.register_signup(installation_id: installation_id, address: address) + signup = described_class.register_signup(request_token: request_token, address: address) expected = JSON.parse(unknown_signup_fixture, symbolize_names: true) expect(signup.id). @@ -58,66 +84,73 @@ module Incognia end context "HTTP request" do - it "hits the endpoint with installation_id" do - stub_token_request + shared_examples_for 'receiving one of the required tokens' do |token_name| + let(:token_value) { SecureRandom.uuid } - stub = stub_signup_request - stub.with( - body: { installation_id: installation_id }, - headers: { - 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ - } - ) + it "hits the endpoint with #{token_name}" do + stub_token_request - api.register_signup(installation_id: installation_id) + stub = stub_signup_request + stub.with( + body: { token_name => token_value }, + headers: { + 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ + } + ) - expect(stub).to have_been_made.once + described_class.register_signup(token_name => token_value) + + expect(stub).to have_been_made.once + end end - it "hits the endpoint with installation_id and address_line" do + it_behaves_like 'receiving one of the required tokens', :request_token + it_behaves_like 'receiving one of the required tokens', :installation_id + it_behaves_like 'receiving one of the required tokens', :session_token + + it "hits the endpoint with request_token and address_line" do stub_token_request - stub = stub_signup_request - stub.with( - body: { installation_id: installation_id, address_line: line_format }, + stub = stub_signup_request.with( + body: { request_token: request_token, address_line: line_format }, headers: { 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ } ) - api.register_signup(installation_id: installation_id, address: address) + described_class.register_signup(request_token: request_token, address: address) expect(stub).to have_been_made.once end - it "hits the endpoint with installation_id and structured_address" do + it "hits the endpoint with request_token and structured_address" do stub_token_request stub = stub_signup_request stub.with( - body: { installation_id: installation_id, structured_address: structured_format }, + body: { request_token: request_token, structured_address: structured_format }, headers: { 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ } ) - api.register_signup(installation_id: installation_id, address: structured_address) + described_class.register_signup(request_token: request_token, address: structured_address) expect(stub).to have_been_made.once end - it "hits the endpoint with installation_id and coordinates" do + it "hits the endpoint with request_token and coordinates" do stub_token_request stub = stub_signup_request stub.with( - body: { installation_id: installation_id, address_coordinates: coordinates_format }, + body: { request_token: request_token, address_coordinates: coordinates_format }, headers: { 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ } ) - api.register_signup(installation_id: installation_id, address: coordinates_address) + described_class.register_signup(request_token: request_token, address: coordinates_address) expect(stub).to have_been_made.once end @@ -128,14 +161,14 @@ module Incognia stub_token_request stub = stub_signup_request.with( - body: { installation_id: installation_id }.merge(opts), + body: { request_token: request_token }.merge(opts), headers: { 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ } ) - login = api.register_signup( - installation_id: installation_id, + described_class.register_signup( + request_token: request_token, **opts ) @@ -154,19 +187,18 @@ module Incognia end end end - end - describe "#register_login" do - let(:installation_id) { SecureRandom.uuid } + describe ".register_login" do + let(:request_token) { SecureRandom.uuid } let(:account_id) { SecureRandom.uuid } it "when successful returns the resource" do stub_token_request stub_login_request - login = api.register_login( - installation_id: installation_id, + login = described_class.register_login( + request_token: request_token, account_id: account_id ) @@ -177,27 +209,36 @@ module Incognia end context "HTTP request" do - it "hits the endpoint with installation_id and account_id" do - stub_token_request + shared_examples_for 'receiving one of the required tokens with account_id' do |token_name| + let(:token_value) { SecureRandom.uuid } - stub = stub_login_request.with( - body: { - type: 'login', - installation_id: installation_id, account_id: account_id - }, - headers: { - 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ - } - ) + it "hits the endpoint with #{token_name} and account_id" do + stub_token_request - login = api.register_login( - installation_id: installation_id, - account_id: account_id - ) + stub = stub_login_request.with( + body: { + type: 'login', + account_id: account_id, + token_name => token_value + }, + headers: { + 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ + } + ) - expect(stub).to have_been_made.once + described_class.register_login( + account_id: account_id, + token_name => token_value + ) + + expect(stub).to have_been_made.once + end end + it_behaves_like 'receiving one of the required tokens with account_id', :request_token + it_behaves_like 'receiving one of the required tokens with account_id', :installation_id + it_behaves_like 'receiving one of the required tokens with account_id', :session_token + context 'when receiving any other optional arguments' do shared_examples_for 'receiving optional args' do |optional_arguments| it "hits the endpoint also with #{optional_arguments}" do @@ -206,16 +247,16 @@ module Incognia stub = stub_login_request.with( body: { type: 'login', - installation_id: installation_id, - account_id: account_id, + request_token: request_token, + account_id: account_id }.merge(opts), headers: { 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ } ) - login = api.register_login( - installation_id: installation_id, + described_class.register_login( + request_token: request_token, account_id: account_id, **opts ) @@ -238,16 +279,16 @@ module Incognia end - describe "#register_payment" do - let(:installation_id) { SecureRandom.uuid } + describe ".register_payment" do + let(:request_token) { SecureRandom.uuid } let(:account_id) { SecureRandom.uuid } it "when successful returns the resource" do stub_token_request stub_payment_request - payment = api.register_payment( - installation_id: installation_id, + payment = described_class.register_payment( + request_token: request_token, account_id: account_id ) @@ -258,27 +299,36 @@ module Incognia end context "HTTP request" do - it "hits the endpoint with installation_id and account_id" do - stub_token_request + shared_examples_for 'receiving one of the required tokens with account_id' do |token_name| + let(:token_value) { SecureRandom.uuid } - stub = stub_payment_request.with( - body: { - type: 'payment', - installation_id: installation_id, account_id: account_id - }, - headers: { - 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ - } - ) + it "hits the endpoint with #{token_name} and account_id" do + stub_token_request - payment = api.register_payment( - installation_id: installation_id, - account_id: account_id - ) + stub = stub_payment_request.with( + body: { + type: 'payment', + account_id: account_id, + token_name => token_value + }, + headers: { + 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ + } + ) - expect(stub).to have_been_made.once + described_class.register_payment( + account_id: account_id, + token_name => token_value + ) + + expect(stub).to have_been_made.once + end end + it_behaves_like 'receiving one of the required tokens with account_id', :request_token + it_behaves_like 'receiving one of the required tokens with account_id', :installation_id + it_behaves_like 'receiving one of the required tokens with account_id', :session_token + context 'when receiving any other optional arguments' do shared_examples_for 'receiving optional args' do |optional_arguments| it "hits the endpoint also with #{optional_arguments}" do @@ -287,7 +337,7 @@ module Incognia stub = stub_payment_request.with( body: { type: 'payment', - installation_id: installation_id, + request_token: request_token, account_id: account_id }.merge(opts), headers: { @@ -295,8 +345,8 @@ module Incognia } ) - payment = api.register_payment( - installation_id: installation_id, + described_class.register_payment( + request_token: request_token, account_id: account_id, **opts ) @@ -305,7 +355,7 @@ module Incognia end end - it_behaves_like 'receiving optional args', 'external_id', 'payment request', 'aaa' do + it_behaves_like 'receiving optional args', 'external_id' do let(:opts) { { external_id: 'external-id' } } end it_behaves_like 'receiving optional args', 'payment_value' do @@ -318,20 +368,33 @@ module Incognia end end - describe "#register_feedback" do + describe ".register_feedback" do let(:event) { Incognia::Constants::FeedbackEvent.constants.sample.to_s } let(:timestamp) { 1655749693000 } let(:expires_at) { '2024-03-13T10:12:01Z' } - before { stub_token_request } + before do + allow(described_class).to receive(:warn) + + stub_token_request + end + it "when successful returns true" do stub_register_feedback_request - feedback_registered = api.register_feedback(event: event, timestamp: timestamp) + feedback_registered = described_class.register_feedback(event: event, timestamp: timestamp) expect(feedback_registered).to be(true) end + it "warns about the deprecation of timestamp" do + stub_register_feedback_request + + expect(described_class).to receive(:warn).with("Deprecation warning: use occurred_at instead of timestamp") + + described_class.register_feedback(event: event, timestamp: timestamp) + end + context "HTTP request" do it "hits the endpoint with event, timestamp and expires_at" do stub = stub_register_feedback_request @@ -342,7 +405,7 @@ module Incognia } ) - api.register_feedback(event: event, timestamp: timestamp, expires_at: expires_at) + described_class.register_feedback(event: event, timestamp: timestamp, expires_at: expires_at) expect(stub).to have_been_made.once end @@ -358,7 +421,7 @@ module Incognia } ) - api.register_feedback(event: event, timestamp: timestamp) + described_class.register_feedback(event: event, timestamp: timestamp) expect(stub).to have_been_made.once end @@ -375,7 +438,7 @@ module Incognia } ) - api.register_feedback(event: event, timestamp: timestamp) + described_class.register_feedback(event: event, timestamp: timestamp) expect(stub).to have_been_made.once end @@ -390,7 +453,41 @@ module Incognia } ) - api.register_feedback(event: event) + described_class.register_feedback(event: event) + + expect(stub).to have_been_made.once + end + end + + context "when receiving occurred_at as a Time" do + let(:occurred_at) { Time.now } + + it "hits the endpoint with expires_at in RFC3339" do + stub = stub_register_feedback_request.with( + body: { event: event, occurred_at: occurred_at.to_datetime.rfc3339 }, + headers: { + 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ + } + ) + + described_class.register_feedback(event: event, occurred_at: occurred_at) + + expect(stub).to have_been_made.once + end + end + + context "when receiving occurred_at as a DateTime" do + let(:occurred_at) { DateTime.now } + + it "hits the endpoint with occurred_at in RFC3339" do + stub = stub_register_feedback_request.with( + body: { event: event, occurred_at: occurred_at.to_datetime.rfc3339 }, + headers: { + 'Content-Type' => 'application/json', 'Authorization' => /Bearer.*/ + } + ) + + described_class.register_feedback(event: event, occurred_at: occurred_at) expect(stub).to have_been_made.once end @@ -407,7 +504,7 @@ module Incognia } ) - api.register_feedback(event: event, occurred_at: occurred_at) + described_class.register_feedback(event: event, occurred_at: occurred_at) expect(stub).to have_been_made.once end @@ -424,7 +521,7 @@ module Incognia } ) - api.register_feedback(event: event, occurred_at: occurred_at) + described_class.register_feedback(event: event, occurred_at: occurred_at) expect(stub).to have_been_made.once end @@ -441,7 +538,7 @@ module Incognia } ) - api.register_feedback(event: event, expires_at: expires_at) + described_class.register_feedback(event: event, expires_at: expires_at) expect(stub).to have_been_made.once end @@ -458,7 +555,7 @@ module Incognia } ) - api.register_feedback(event: event, expires_at: expires_at) + described_class.register_feedback(event: event, expires_at: expires_at) expect(stub).to have_been_made.once end @@ -473,7 +570,7 @@ module Incognia } ) - api.register_feedback(event: event) + described_class.register_feedback(event: event) expect(stub).to have_been_made.once end @@ -491,13 +588,13 @@ module Incognia } ) - api.register_feedback(event: event, timestamp: timestamp, expires_at: expires_at, id_name => id) + described_class.register_feedback(event: event, timestamp: timestamp, expires_at: expires_at, id_name => id) expect(stub).to have_been_made.once end end - it_behaves_like 'receiving ids', :installation_id + it_behaves_like 'receiving ids', :request_token it_behaves_like 'receiving ids', :account_id it_behaves_like 'receiving ids', :external_id it_behaves_like 'receiving ids', :signup_id