diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..8c18f1a --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--format documentation +--color diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..005119b --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.4.1 diff --git a/Gemfile.lock b/Gemfile.lock index 735b54d..197585d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -44,10 +44,10 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.6) - webmock (2.3.2) + webmock (3.7.6) addressable (>= 2.3.6) crack (>= 0.3.2) - hashdiff + hashdiff (>= 0.4.0, < 2.0.0) PLATFORMS ruby @@ -55,9 +55,9 @@ PLATFORMS DEPENDENCIES bundler flanks-api-ruby! - rake (~> 10.0) - rspec (~> 3.0) - webmock (~> 2.3.1) + rake + rspec + webmock BUNDLED WITH - 1.16.1 + 1.17.3 diff --git a/README.md b/README.md index 174cb8e..6f78a0c 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,10 @@ Changelog --------- * v.0.1.0 TBD + +About Flanks +------------ + +* [Public site](https://www.flanks.io/) +* [Documentation](https://docs.flanks.io/) +* [Platform](https://platform.flanks.io/) diff --git a/flanks.gemspec b/flanks.gemspec index 7f08297..cf63299 100644 --- a/flanks.gemspec +++ b/flanks.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |spec| spec.add_dependency "rest-client", "~> 2.0.2" spec.add_development_dependency "bundler" - spec.add_development_dependency "rake", "~> 10.0" - spec.add_development_dependency "rspec", "~> 3.0" - spec.add_development_dependency "webmock", "~> 2.3.1" + spec.add_development_dependency "rake" + spec.add_development_dependency "rspec" + spec.add_development_dependency "webmock" end diff --git a/lib/flanks.rb b/lib/flanks.rb index cc59870..5ddd4e8 100644 --- a/lib/flanks.rb +++ b/lib/flanks.rb @@ -1,2 +1,13 @@ require 'flanks/version' require 'flanks/configuration' +require 'flanks/base' +require 'flanks/error' +require 'flanks/resource' +require 'flanks/collection' + +require 'flanks/resources/account' +require 'flanks/resources/bank' +require 'flanks/resources/link' +require 'flanks/resources/link_code' +require 'flanks/resources/token' +require 'flanks/resources/transaction' diff --git a/lib/flanks/base.rb b/lib/flanks/base.rb new file mode 100644 index 0000000..6ef7e4a --- /dev/null +++ b/lib/flanks/base.rb @@ -0,0 +1,92 @@ +require 'rest-client' +require 'json' + +module Flanks + BASE_URL = 'https://api.flanks.io' + SENSIBLE_HEADERS = %w{Authorization} + SENSIBLE_PARAMS = %w{client_secret password credentials_token code} + + class << self + attr_accessor :configuration + + def configuration + @configuration ||= Configuration.new + end + + def configure + yield(configuration) + end + + def api_call(method:, path:, params: {}, token: nil) + url = Flanks.const_get(:BASE_URL) + path + + headers = { 'Content-Type' => 'application/json' } + + unless token.nil? + headers.merge!(Authorization: "Bearer #{token}") + end + + log_request(method, url, headers, params) + + if method == :post + payload = params.to_json + else + headers.merge!(params: params) + end + + request_params = { method: method, url: url, headers: headers } + request_params.merge!(payload: payload) unless payload.nil? + + begin + response = RestClient::Request.execute(request_params) + return {} if response.empty? + + JSON.parse(response) + rescue StandardError => error + # TODO handle properly + response = JSON.parse(error.response) + raise Error.new(response['error']) + end + end + + def log_request(method, url, headers, params) + log_message("") + log_message("=> #{method.upcase} #{url}") + + log_message("* Headers") + headers.each do |key, value| + safe_value = if SENSIBLE_HEADERS.include?(key.to_s) + "" + else + value + end + + log_message("#{key}: #{safe_value}") + end + + if params.any? + log_message("* Params") + params.each do |key, value| + safe_value = if SENSIBLE_PARAMS.include?(key.to_s) + "" + else + value + end + + log_message("#{key}: #{safe_value}") + end + else + log_message("* No params") + end + end + + def log_message(message) + return if message.nil? + + logger = Flanks.configuration.logger + return if logger.nil? + + logger.info(message) + end + end +end diff --git a/lib/flanks/collection.rb b/lib/flanks/collection.rb new file mode 100644 index 0000000..286bd83 --- /dev/null +++ b/lib/flanks/collection.rb @@ -0,0 +1,55 @@ +module Flanks + class Collection < Array + def initialize(response, item_class, token = nil) + @item_class = item_class + @token = token + populate!(response, item_class) + end + + def next_page? + !@next_page_uri.nil? + end + + def previous_page? + !@previous_page_uri.nil? + end + + def next_page! + return unless next_page? + + uri = URI.parse(@next_page_uri) + params = if uri.query + Hash[URI::decode_www_form(uri.query)] + else + {} + end + + response = Bankin.api_call(:get, uri.path, params, @token) + populate!(response, @item_class) + self + end + + def load_all! + while next_page? do + next_page! + end + self + end + + private + + def populate!(response, item_klass) + response.each do |item| + self << item_klass.new(item, @token) + end + + # TODO review + # set_pagination(response['pagination']) + end + + def set_pagination(pagination_data) + @next_page_uri = pagination_data['next_uri'] + @previous_page_uri = pagination_data['previous_uri'] + end + end +end diff --git a/lib/flanks/configuration.rb b/lib/flanks/configuration.rb index 99954c0..a26876e 100644 --- a/lib/flanks/configuration.rb +++ b/lib/flanks/configuration.rb @@ -1,4 +1,5 @@ module Flanks class Configuration + attr_accessor :client_id, :client_secret, :username, :password, :logger end end diff --git a/lib/flanks/error.rb b/lib/flanks/error.rb new file mode 100644 index 0000000..3d62a70 --- /dev/null +++ b/lib/flanks/error.rb @@ -0,0 +1,9 @@ +module Flanks + class Error < StandardError + attr_reader :message + + def initialize(message) + @message = message + end + end +end diff --git a/lib/flanks/resource.rb b/lib/flanks/resource.rb new file mode 100644 index 0000000..3a82b3c --- /dev/null +++ b/lib/flanks/resource.rb @@ -0,0 +1,101 @@ +module Flanks + class Resource + def initialize(data, token = nil) + @token = token + generate_attr_readers + set_data(data) + end + + def fields + self.class.fields + end + + def resources + self.class.resources + end + + def collections + self.class.collections + end + + private + + def generate_attr_readers + fields.each do |field| + define_singleton_method(field) do + load_data! if instance_variable_get("@#{field}").nil? + instance_variable_get("@#{field}") + end + end + + (resources + collections).each do |resource| + define_singleton_method(resource[:name]) do + load_data! if instance_variable_get("@#{resource[:name]}").nil? + instance_variable_get("@#{resource[:name]}") + end + end + end + + def set_data(data) + fields.each do |field| + next unless data.key?(field.to_s) + instance_variable_set("@#{field}", data[field.to_s]) + end + + resources.each do |resource| + next unless data.key?(resource[:name].to_s) + klass = Object.const_get("Flanks::#{resource[:klass]}") + instance_variable_set("@#{resource[:name]}", klass.new(data[resource[:name].to_s], @token)) + end + + collections.each do |collection| + next unless data[collection[:name].to_s] + klass = Object.const_get("Flanks::#{collection[:klass]}") + arr = data[collection[:name].to_s].map { |item | klass.new(item, @token) } + instance_variable_set("@#{collection[:name]}", arr) + instance_variable_set("@#{collection[:name]}_ids", arr.map(&:id)) + end + end + + def load_data! + return if @loaded + response = Flanks.api_call(method: :get, path: @resource_uri, token: @token) + set_data(response) + @loaded = true + end + + class << self + def fields + @fields || [:resource_type, :resource_uri] + end + + def resources + @resources || [] + end + + def collections + @collections || [] + end + + def auth_delegate(name, options) + define_method(name) do |*args| + klass = Object.const_get("Flanks::#{options[:class]}") + klass.send(options[:method], self.token, *args) + end + end + + def has_fields(*new_fields) + @fields = (fields + new_fields).uniq + end + + def has_resource(name, klass) + @resources = resources << { name: name, klass: klass } + end + + def has_collection(name, klass) + @collections = collections << { name: name, klass: klass } + attr_reader :"#{name}_ids" + end + end + end +end diff --git a/lib/flanks/resources/account.rb b/lib/flanks/resources/account.rb new file mode 100644 index 0000000..0bdc495 --- /dev/null +++ b/lib/flanks/resources/account.rb @@ -0,0 +1,20 @@ +module Flanks + class Account < Resource + RESOURCE_PATH = '/v0/bank/credentials/account' + + has_fields :iban, :entity, :alias, :balance, :currency, :description, + :isOwner, :numOwners, :owners, :_id + + def self.list(token:, credentials_token:) + response = Flanks.api_call( + method: :post, + path: RESOURCE_PATH, + token: token, + params: { + credentials_token: credentials_token + } + ) + Collection.new(response, self) + end + end +end diff --git a/lib/flanks/resources/bank.rb b/lib/flanks/resources/bank.rb new file mode 100644 index 0000000..06509bf --- /dev/null +++ b/lib/flanks/resources/bank.rb @@ -0,0 +1,16 @@ +module Flanks + class Bank < Resource + RESOURCE_PATH = '/v0/bank/available' + + has_fields :id, :name + + def self.list(token:) + response = Flanks.api_call( + method: :get, + path: RESOURCE_PATH, + token: token + ) + Collection.new(response, self) + end + end +end diff --git a/lib/flanks/resources/link.rb b/lib/flanks/resources/link.rb new file mode 100644 index 0000000..1a4d05f --- /dev/null +++ b/lib/flanks/resources/link.rb @@ -0,0 +1,19 @@ +module Flanks + class Link < Resource + RESOURCE_PATH = '/v0/platform/link' + + has_fields :message, :credentials_token, :extra + + def self.create(token:, code:) + response = Flanks.api_call( + method: :post, + path: RESOURCE_PATH, + token: token, + params: { + code: code + } + ) + new(response) + end + end +end diff --git a/lib/flanks/resources/link_code.rb b/lib/flanks/resources/link_code.rb new file mode 100644 index 0000000..857f83b --- /dev/null +++ b/lib/flanks/resources/link_code.rb @@ -0,0 +1,19 @@ +module Flanks + class LinkCode < Resource + RESOURCE_PATH = '/v0/platform/link' + + has_fields :code + + def self.pending(token:) + response = Flanks.api_call( + method: :get, + path: RESOURCE_PATH, + token: token, + params: { + client_id: Flanks.configuration.client_id + } + ) + Collection.new(response, self) + end + end +end diff --git a/lib/flanks/resources/token.rb b/lib/flanks/resources/token.rb new file mode 100644 index 0000000..3e5ab96 --- /dev/null +++ b/lib/flanks/resources/token.rb @@ -0,0 +1,22 @@ +module Flanks + class Token < Resource + RESOURCE_PATH = '/v0/token' + + has_fields :access_token, :expires_in, :token_type, :scope, :refresh_token + + def self.create + response = Flanks.api_call( + method: :post, + path: RESOURCE_PATH, + params: { + client_id: Flanks.configuration.client_id, + client_secret: Flanks.configuration.client_secret, + username: Flanks.configuration.username, + password: Flanks.configuration.password, + grant_type: 'password' + } + ) + new(response) + end + end +end diff --git a/lib/flanks/resources/transaction.rb b/lib/flanks/resources/transaction.rb new file mode 100644 index 0000000..6304f5f --- /dev/null +++ b/lib/flanks/resources/transaction.rb @@ -0,0 +1,24 @@ +module Flanks + class Transaction < Resource + RESOURCE_PATH = '/v0/bank/credentials/data' + + has_fields :_id, :account_id, :amount, :balance, :entity, :cardNumber, + :category, :currency, :operationDate, :valueDate, + :description, :productDescription, :transfer + + def self.list(token:, credentials_token:, account_id:) + response = Flanks.api_call( + method: :post, + path: RESOURCE_PATH, + token: token, + params: { + credentials_token: credentials_token, + query: { + "account_id" => [account_id] + } + } + ) + Collection.new(response, self) + end + end +end diff --git a/spec/flanks/base_spec.rb b/spec/flanks/base_spec.rb new file mode 100644 index 0000000..8df96f0 --- /dev/null +++ b/spec/flanks/base_spec.rb @@ -0,0 +1,243 @@ +require "spec_helper" + +describe Flanks do + before { configure_flanks } + + describe "#configuration" do + it "returns a Configuration instance" do + expect(subject.configuration).to be_a(Flanks::Configuration) + end + end + + describe "#configure" do + it "returns the credentials properly" do + expect(subject.configuration.client_id).to eq('a_client_id') + expect(subject.configuration.client_secret).to eq('the_super_secret_stuff') + expect(subject.configuration.username).to eq('an_email') + expect(subject.configuration.password).to eq('le_password') + end + end + + describe "#api_call" do + let(:path) { '/v0/some/endpoint' } + let(:params) { { a: :b, c: :d, e: :f } } + let(:api_call) { + subject.api_call(method: method, path: path, params: params, token: token) + } + + before do + allow(RestClient::Request).to receive(:execute) { {} } + end + + context "for a GET request" do + let(:method) { :get } + + context "with a bearer token" do + let(:token) { 'tok123456789' } + + it "works" do + expect(subject) + .to receive(:log_request) + .with( + :get, + "https://api.flanks.io/v0/some/endpoint", + { + 'Content-Type' => 'application/json', + Authorization: 'Bearer tok123456789' + }, + { a: :b, c: :d, e: :f } + ) + + expect(RestClient::Request) + .to receive(:execute) + .with( + method: :get, + url: "https://api.flanks.io/v0/some/endpoint", + headers: { + 'Content-Type' => 'application/json', + Authorization: 'Bearer tok123456789', + params: { a: :b, c: :d, e: :f } + } + ) + + api_call + end + end + end + + context "for a POST request" do + let(:method) { :post } + + context "with a bearer token" do + let(:token) { 'tok123456789' } + + it "works" do + expect(subject) + .to receive(:log_request) + .with( + :post, + "https://api.flanks.io/v0/some/endpoint", + { + 'Content-Type' => 'application/json', + Authorization: 'Bearer tok123456789' + }, + { a: :b, c: :d, e: :f } + ) + + expect(RestClient::Request) + .to receive(:execute) + .with( + method: :post, + url: "https://api.flanks.io/v0/some/endpoint", + headers: { + 'Content-Type' => 'application/json', + Authorization: 'Bearer tok123456789' + }, + payload: "{\"a\":\"b\",\"c\":\"d\",\"e\":\"f\"}" + ) + + api_call + end + end + + context "with client credentials" do + let(:token) { nil } + + it "works" do + expect(subject) + .to receive(:log_request) + .with( + :post, + "https://api.flanks.io/v0/some/endpoint", + { + 'Content-Type' => 'application/json' + }, + { a: :b, c: :d, e: :f } + ) + + expect(RestClient::Request) + .to receive(:execute) + .with( + method: :post, + url: "https://api.flanks.io/v0/some/endpoint", + headers: { + 'Content-Type' => 'application/json' + }, + payload: "{\"a\":\"b\",\"c\":\"d\",\"e\":\"f\"}" + ) + + api_call + end + end + end + end + + describe "#log_request" do + let(:method) { :cuca } + let(:url) { "https://some.where/over/the/rainbow" } + let(:headers) { + { Authorization: "Bearer 123142515", cuca: :monga, a: :b } + } + let(:log_request) { + subject.log_request(method, url, headers, params) + } + + context "without params" do + let(:params) { {} } + + it "works" do + [ + "", + "=> CUCA https://some.where/over/the/rainbow", + "* Headers", + "Authorization: ", + "cuca: monga", + "a: b", + "* No params" + ].each do |unique_message| + expect(subject).to receive(:log_message).with(unique_message).once + end + + log_request + end + end + + context "with some params" do + let(:params) { + { + client_secret: "the_super_secret_stuff", + password: 'mipassword', + b: :c, + credentials_token: 'eltokencito', + code: 'ruby', + d: :e + } + } + + it "works" do + [ + "", + "=> CUCA https://some.where/over/the/rainbow", + "* Headers", + "Authorization: ", + "cuca: monga", + "a: b", + "* Params", + "client_secret: ", + "password: ", + "b: c", + "credentials_token: ", + "code: ", + "d: e", + ].each do |unique_message| + expect(subject).to receive(:log_message).with(unique_message).once + end + + log_request + end + end + end + + describe "#log_message" do + context "without a message" do + it "does not call the logger" do + expect(Logger).not_to receive(:new) + + subject.log_message(nil) + end + end + + context "with a non-empty message" do + context "with a nil Flanks.configuration.logger" do + before do + allow(Flanks).to receive_message_chain(:configuration, :logger) { + nil + } + end + + it "does not call the logger" do + expect_any_instance_of(Logger).not_to receive(:info) + + subject.log_message("cucamonga") + end + end + + context "with a set-up logger" do + before do + logger = double(Logger) + allow(logger).to receive(:info) { } + + allow(Flanks).to receive_message_chain(:configuration, :logger) { + logger + } + end + + it "calls the logger properly" do + expect(Flanks.configuration.logger).to receive(:info).with("cucamonga").once + + subject.log_message("cucamonga") + end + end + end + end +end diff --git a/spec/flanks/resources/account_spec.rb b/spec/flanks/resources/account_spec.rb new file mode 100644 index 0000000..47ff54d --- /dev/null +++ b/spec/flanks/resources/account_spec.rb @@ -0,0 +1,59 @@ +require "spec_helper" + +describe Flanks::Account do + before { configure_flanks } + + describe "#list" do + before do + stub_request(:post, "https://api.flanks.io/v0/bank/credentials/account"). + with( + headers: { + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer THIS_IS_EL_BUEN_TOKEN' + }, + body: "{\"credentials_token\":\"THE_TOKCREDENTIALS\"}" + ). + to_return(status: 200, body: response_json(resource: 'account', action: 'list')) + end + + it "works" do + accounts = Flanks::Account.list( + token: 'THIS_IS_EL_BUEN_TOKEN', + credentials_token: 'THE_TOKCREDENTIALS' + ) + + expect(accounts.class).to eq(Flanks::Collection) + expect(accounts.size).to eq(2) + + account1, account2 = accounts + + expect(account1.class).to eq(Flanks::Account) + expect(account1.iban).to eq("ES4501822763517295596839") + expect(account1.entity).to eq("whatthebank") + expect(account1.alias).to eq("Cuenta Random de Ahorros") + expect(account1.balance).to eq(1034.19) + expect(account1.currency).to eq("EUR") + expect(account1.description).to eq("La boena descrepsión") + expect(account1.isOwner).to be_truthy + expect(account1.numOwners).to eq(3) + expect(account1.owners).to eq( + ["Juanita admin", "Pepito colaborador", "Florencia gestora"] + ) + expect(account1._id).to eq("abc12340a0011234fda") + + expect(account2.class).to eq(Flanks::Account) + expect(account2.iban).to eq("ES8620956428346945419764") + expect(account2.entity).to eq("whatthebank") + expect(account2.alias).to eq("Cuenta Random de Inversiones") + expect(account2.balance).to eq(180_291.55) + expect(account2.currency).to eq("EUR") + expect(account2.description).to eq("Long term money") + expect(account2.isOwner).to be_truthy + expect(account2.numOwners).to eq(1) + expect(account2.owners).to eq( + ["Juanita admin"] + ) + expect(account2._id).to eq("def78940a0011234fda") + end + end +end diff --git a/spec/flanks/resources/bank_spec.rb b/spec/flanks/resources/bank_spec.rb new file mode 100644 index 0000000..280fa35 --- /dev/null +++ b/spec/flanks/resources/bank_spec.rb @@ -0,0 +1,39 @@ +require "spec_helper" + +describe Flanks::Bank do + before { configure_flanks } + + describe "#list" do + before do + stub_request(:get, "https://api.flanks.io/v0/bank/available"). + with( + headers: { + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer THIS_IS_EL_BUEN_TOKEN' + } + ). + to_return(status: 200, body: response_json(resource: 'bank', action: 'list')) + end + + it "works" do + banks = Flanks::Bank.list(token: 'THIS_IS_EL_BUEN_TOKEN') + + expect(banks.class).to eq(Flanks::Collection) + expect(banks.size).to eq(3) + + bank1, bank2, bank3 = banks + + expect(bank1.class).to eq(Flanks::Bank) + expect(bank1.id).to eq("12") + expect(bank1.name).to eq("Banc 1") + + expect(bank2.class).to eq(Flanks::Bank) + expect(bank2.id).to eq("13") + expect(bank2.name).to eq("Banc 2") + + expect(bank3.class).to eq(Flanks::Bank) + expect(bank3.id).to eq("14") + expect(bank3.name).to eq("Banc 3") + end + end +end diff --git a/spec/flanks/resources/link_code_spec.rb b/spec/flanks/resources/link_code_spec.rb new file mode 100644 index 0000000..24908df --- /dev/null +++ b/spec/flanks/resources/link_code_spec.rb @@ -0,0 +1,31 @@ +require "spec_helper" + +describe Flanks::LinkCode do + before { configure_flanks } + + describe "#pending" do + before do + stub_request(:get, "https://api.flanks.io/v0/platform/link?client_id=a_client_id"). + with( + headers: { + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer THIS_IS_EL_BUEN_TOKEN' + } + ). + to_return(status: 200, body: response_json(resource: 'link_code', action: 'pending')) + end + + it "works" do + link_codes = Flanks::LinkCode.pending(token: 'THIS_IS_EL_BUEN_TOKEN') + + expect(link_codes.class).to eq(Flanks::Collection) + expect(link_codes.size).to eq(3) + expect(link_codes[0].class).to eq(Flanks::LinkCode) + expect(link_codes[0].code).to eq("a_first_codego") + expect(link_codes[1].class).to eq(Flanks::LinkCode) + expect(link_codes[1].code).to eq("el_codi_numbru_2") + expect(link_codes[2].class).to eq(Flanks::LinkCode) + expect(link_codes[2].code).to eq("cuca_code_3") + end + end +end diff --git a/spec/flanks/resources/link_spec.rb b/spec/flanks/resources/link_spec.rb new file mode 100644 index 0000000..301c9f5 --- /dev/null +++ b/spec/flanks/resources/link_spec.rb @@ -0,0 +1,31 @@ +require "spec_helper" + +describe Flanks::Link do + before { configure_flanks } + + describe "#create" do + before do + stub_request(:post, "https://api.flanks.io/v0/platform/link"). + with( + headers: { + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer THIS_IS_EL_BUEN_TOKEN' + }, + body: "{\"code\":\"EL_LINK_CODIO\"}" + ). + to_return(status: 200, body: response_json(resource: 'link', action: 'create')) + end + + it "works" do + link = Flanks::Link.create(token: 'THIS_IS_EL_BUEN_TOKEN', code: 'EL_LINK_CODIO') + + expect(link.class).to eq(Flanks::Link) + expect(link.message).to eq("Successfully retrieved") + expect(link.credentials_token).to eq("THE_CREDENTIALS_TOKEN_FOR_YOU") + expect(link.extra.class).to eq(Hash) + expect(link.extra.keys).to eq(%w{a_key b_key}) + expect(link.extra['a_key']).to eq("a_content") + expect(link.extra['b_key']).to eq("b_content") + end + end +end diff --git a/spec/flanks/resources/token_spec.rb b/spec/flanks/resources/token_spec.rb new file mode 100644 index 0000000..a26b6ec --- /dev/null +++ b/spec/flanks/resources/token_spec.rb @@ -0,0 +1,27 @@ +require "spec_helper" + +describe Flanks::Token do + before { configure_flanks } + + describe "#create" do + before do + stub_request(:post, "https://api.flanks.io/v0/token"). + with( + body: '{"client_id":"a_client_id","client_secret":"the_super_secret_stuff","username":"an_email","password":"le_password","grant_type":"password"}', + headers: { 'Content-Type' => 'application/json' } + ). + to_return(status: 200, body: response_json(resource: 'token', action: 'create')) + end + + it "works" do + token = Flanks::Token.create + + expect(token.class).to eq(Flanks::Token) + expect(token.access_token).to eq("aPscYAeKMbvLpWYgNYvtHUtsMOHxMT") + expect(token.expires_in).to eq(36000) + expect(token.token_type).to eq("Bearer") + expect(token.scope).to eq("read write aggregation payment") + expect(token.refresh_token).to eq("VwkqYDisjvIWNKJGjbFuOcyEacQchT") + end + end +end diff --git a/spec/flanks/resources/transaction_spec.rb b/spec/flanks/resources/transaction_spec.rb new file mode 100644 index 0000000..672f074 --- /dev/null +++ b/spec/flanks/resources/transaction_spec.rb @@ -0,0 +1,92 @@ +require "spec_helper" + +describe Flanks::Bank do + before { configure_flanks } + + describe "#list" do + before do + stub_request(:post, "https://api.flanks.io/v0/bank/credentials/data"). + with( + headers: { + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer THIS_IS_EL_BUEN_TOKEN' + }, + body: "{\"credentials_token\":\"THE_TOKCREDENTIALS\",\"query\":{\"account_id\":[\"A91515\"]}}" + ). + to_return(status: 200, body: response_json(resource: 'transaction', action: 'list')) + end + + it "works" do + transactions = Flanks::Transaction.list( + token: 'THIS_IS_EL_BUEN_TOKEN', + credentials_token: 'THE_TOKCREDENTIALS', + account_id: 'A91515' + ) + + expect(transactions.class).to eq(Flanks::Collection) + expect(transactions.size).to eq(3) + + transaction1, transaction2, transaction3 = transactions + + expect(transaction1.class).to eq(Flanks::Transaction) + expect(transaction1._id).to eq("T1230045") + expect(transaction1.account_id).to eq("A91515") + expect(transaction1.amount).to eq(-12.34) + expect(transaction1.balance).to eq(913.33) + expect(transaction1.entity).to eq("whatthebank") + expect(transaction1.cardNumber).to eq("abc") + expect(transaction1.category).to eq("Sport") + expect(transaction1.currency).to eq("EUR") + expect(transaction1.operationDate).to eq("2019-10-11") + expect(transaction1.valueDate).to eq("2019-10-12") + expect(transaction1.description).to eq("A description") + expect(transaction1.productDescription).to eq("A product description") + expect(transaction1.transfer).to eq({ + "beneficiary" => "John Benefits", + "ibanBeneficiary" => "ES5700752532614289557739", + "ibanPayer" => "ES5904877762281252243113", + "payer" => "Max Payer", + }) + + expect(transaction2.class).to eq(Flanks::Transaction) + expect(transaction2._id).to eq("T1230046") + expect(transaction2.account_id).to eq("A91515") + expect(transaction2.amount).to eq(3.11) + expect(transaction2.balance).to eq(916.44) + expect(transaction2.entity).to eq("whatthebank") + expect(transaction2.cardNumber).to eq("def") + expect(transaction2.category).to eq("Leisure") + expect(transaction2.currency).to eq("EUR") + expect(transaction2.operationDate).to eq("2019-10-13") + expect(transaction2.valueDate).to eq("2019-10-14") + expect(transaction2.description).to eq("Another description") + expect(transaction2.productDescription).to eq("Another product description") + expect(transaction2.transfer).to eq({ + "beneficiary" => "Max Payer", + "ibanBeneficiary" => "ES5904877762281252243113", + "ibanPayer" => "ES2731905381475958758479", + "payer" => "The Dude", + }) + + expect(transaction3.class).to eq(Flanks::Transaction) + expect(transaction3._id).to eq("T1230047") + expect(transaction3.account_id).to eq("A91515") + expect(transaction3.amount).to eq(4.99) + expect(transaction3.balance).to eq(921.43) + expect(transaction3.entity).to eq("whatthebank") + expect(transaction3.cardNumber).to eq("ghi") + expect(transaction3.category).to eq("Finance") + expect(transaction3.currency).to eq("EUR") + expect(transaction3.operationDate).to eq("2019-10-15") + expect(transaction3.valueDate).to eq("2019-10-16") + expect(transaction3.description).to eq("The final description") + expect(transaction3.productDescription).to eq("The final product description") + expect(transaction3.transfer).to eq({ + "beneficiary" => "Max Payer", + "ibanBeneficiary" => "ES5904877762281252243113", + "ibanPayer" => "ES6801287657415495297851", + "payer" => "Random Guy", + }) + end + end +end diff --git a/spec/flanks_spec.rb b/spec/flanks/version_spec.rb similarity index 56% rename from spec/flanks_spec.rb rename to spec/flanks/version_spec.rb index c31d012..4a51e23 100644 --- a/spec/flanks_spec.rb +++ b/spec/flanks/version_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" -describe Flanks do - it "has a version number" do +describe "Flanks::VERSION" do + it "is the proper one" do expect(Flanks::VERSION).to eq("0.1.0") end end diff --git a/spec/responses/account/list.json b/spec/responses/account/list.json new file mode 100644 index 0000000..166a208 --- /dev/null +++ b/spec/responses/account/list.json @@ -0,0 +1,30 @@ +[ + { + "iban": "ES4501822763517295596839", + "entity": "whatthebank", + "alias": "Cuenta Random de Ahorros", + "balance": 1034.19, + "currency": "EUR", + "description": "La boena descrepsión", + "isOwner": true, + "numOwners": 3, + "owners": [ + "Juanita admin", "Pepito colaborador", "Florencia gestora" + ], + "_id": "abc12340a0011234fda" + }, + { + "iban": "ES8620956428346945419764", + "entity": "whatthebank", + "alias": "Cuenta Random de Inversiones", + "balance": 180291.55, + "currency": "EUR", + "description": "Long term money", + "isOwner": true, + "numOwners": 1, + "owners": [ + "Juanita admin" + ], + "_id": "def78940a0011234fda" + } +] diff --git a/spec/responses/bank/list.json b/spec/responses/bank/list.json new file mode 100644 index 0000000..c687457 --- /dev/null +++ b/spec/responses/bank/list.json @@ -0,0 +1,14 @@ +[ + { + "id": "12", + "name": "Banc 1" + }, + { + "id": "13", + "name": "Banc 2" + }, + { + "id": "14", + "name": "Banc 3" + } +] diff --git a/spec/responses/link/create.json b/spec/responses/link/create.json new file mode 100644 index 0000000..435a975 --- /dev/null +++ b/spec/responses/link/create.json @@ -0,0 +1,8 @@ +{ + "message": "Successfully retrieved", + "credentials_token": "THE_CREDENTIALS_TOKEN_FOR_YOU", + "extra": { + "a_key": "a_content", + "b_key": "b_content" + } +} diff --git a/spec/responses/link_code/pending.json b/spec/responses/link_code/pending.json new file mode 100644 index 0000000..aa8e871 --- /dev/null +++ b/spec/responses/link_code/pending.json @@ -0,0 +1,11 @@ +[ + { + "code": "a_first_codego" + }, + { + "code": "el_codi_numbru_2" + }, + { + "code": "cuca_code_3" + } +] diff --git a/spec/responses/token/create.json b/spec/responses/token/create.json new file mode 100644 index 0000000..a0aa5f0 --- /dev/null +++ b/spec/responses/token/create.json @@ -0,0 +1,7 @@ +{ + "access_token": "aPscYAeKMbvLpWYgNYvtHUtsMOHxMT", + "expires_in": 36000, + "token_type": "Bearer", + "scope": "read write aggregation payment", + "refresh_token": "VwkqYDisjvIWNKJGjbFuOcyEacQchT" +} diff --git a/spec/responses/transaction/list.json b/spec/responses/transaction/list.json new file mode 100644 index 0000000..e04d280 --- /dev/null +++ b/spec/responses/transaction/list.json @@ -0,0 +1,62 @@ +[ + { + "_id": "T1230045", + "account_id": "A91515", + "amount": -12.34, + "balance": 913.33, + "entity": "whatthebank", + "cardNumber": "abc", + "category": "Sport", + "currency": "EUR", + "operationDate": "2019-10-11", + "valueDate": "2019-10-12", + "description": "A description", + "productDescription": "A product description", + "transfer": { + "beneficiary": "John Benefits", + "ibanBeneficiary": "ES5700752532614289557739", + "ibanPayer": "ES5904877762281252243113", + "payer": "Max Payer" + } + }, + { + "_id": "T1230046", + "account_id": "A91515", + "amount": 3.11, + "balance": 916.44, + "entity": "whatthebank", + "cardNumber": "def", + "category": "Leisure", + "currency": "EUR", + "operationDate": "2019-10-13", + "valueDate": "2019-10-14", + "description": "Another description", + "productDescription": "Another product description", + "transfer": { + "beneficiary": "Max Payer", + "ibanBeneficiary": "ES5904877762281252243113", + "ibanPayer": "ES2731905381475958758479", + "payer": "The Dude" + } + }, + { + "_id": "T1230047", + "account_id": "A91515", + "amount": 4.99, + "balance": 921.43, + "entity": "whatthebank", + "cardNumber": "ghi", + "category": "Finance", + "currency": "EUR", + "operationDate": "2019-10-15", + "valueDate": "2019-10-16", + "description": "The final description", + "productDescription": "The final product description", + "transfer": { + "beneficiary": "Max Payer", + "ibanBeneficiary": "ES5904877762281252243113", + "ibanPayer": "ES6801287657415495297851", + "payer": "Random Guy" + } + } +] diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 19bc2e7..c2c00e2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,13 +2,16 @@ require 'webmock/rspec' require "flanks" -def response_json(filename) - path = File.join('spec', 'responses', "#{filename}.json") +def response_json(resource:, action:) + path = File.join('spec', 'responses', resource, "#{action}.json") File.read(path) end -def configure_bankin +def configure_flanks Flanks.configure do |config| - # TODO + config.client_id = 'a_client_id' + config.client_secret = 'the_super_secret_stuff' + config.username = 'an_email' + config.password = 'le_password' end end