diff --git a/.github/workflows/pipelock.yml b/.github/workflows/pipelock.yml index e010f6780..7b3b46af9 100644 --- a/.github/workflows/pipelock.yml +++ b/.github/workflows/pipelock.yml @@ -29,3 +29,4 @@ jobs: config/locales/views/reports/ docs/hosting/ai.md app/models/provider/binance.rb + workers/preview/package-lock.json diff --git a/app/models/provider/enable_banking.rb b/app/models/provider/enable_banking.rb index 8acfad9c5..88df7ec62 100644 --- a/app/models/provider/enable_banking.rb +++ b/app/models/provider/enable_banking.rb @@ -160,7 +160,7 @@ class Provider::EnableBanking # @param psu_headers [Hash] Optional PSU context headers required by some ASPSPs # @return [Hash] Transactions and continuation_key for pagination def get_account_transactions(account_id:, date_from: nil, date_to: nil, - continuation_key: nil, transaction_status: nil, psu_headers: {}) + continuation_key: nil, transaction_status: nil, psu_headers: {}, retried_date_from: false) encoded_id = CGI.escape(account_id.to_s) query_params = {} query_params[:transaction_status] = transaction_status if transaction_status.present? @@ -175,6 +175,22 @@ class Provider::EnableBanking ) handle_response(response) + rescue EnableBankingError => e + corrected_date_from = e.corrected_date_from + + if !retried_date_from && e.wrong_transactions_period? && corrected_date_from.present? && corrected_date_from != date_from + get_account_transactions( + account_id: account_id, + date_from: corrected_date_from, + date_to: date_to, + continuation_key: continuation_key, + transaction_status: transaction_status, + psu_headers: psu_headers, + retried_date_from: true + ) + else + raise + end rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e raise EnableBankingError.new("Exception during GET request: #{e.message}", :request_failed) end @@ -237,7 +253,8 @@ class Provider::EnableBanking when 408 raise EnableBankingError.new("Request timeout from Enable Banking API", :timeout) when 422 - raise EnableBankingError.new("Validation error from Enable Banking API: #{response.body}", :validation_error) + response_data = parse_response_body(response) + raise EnableBankingError.new("Validation error from Enable Banking API: #{response.body}", :validation_error, response_data: response_data) when 429 raise EnableBankingError.new("Rate limit exceeded. Please try again later.", :rate_limited) else @@ -255,11 +272,28 @@ class Provider::EnableBanking end class EnableBankingError < StandardError - attr_reader :error_type + attr_reader :error_type, :response_data - def initialize(message, error_type = :unknown) + def initialize(message, error_type = :unknown, response_data: nil) super(message) @error_type = error_type + @response_data = response_data + end + + def wrong_transactions_period? + error_type == :validation_error && response_data.is_a?(Hash) && response_data[:error] == "WRONG_TRANSACTIONS_PERIOD" + end + + def corrected_date_from + value = response_data&.dig(:detail, :date_from) + + if value.is_a?(Date) + value + elsif value.present? + Date.iso8601(value) + end + rescue ArgumentError + nil end end end diff --git a/test/models/provider/enable_banking_test.rb b/test/models/provider/enable_banking_test.rb new file mode 100644 index 000000000..e09c90c7e --- /dev/null +++ b/test/models/provider/enable_banking_test.rb @@ -0,0 +1,64 @@ +require "test_helper" +require "ostruct" +require "openssl" + +class Provider::EnableBankingTest < ActiveSupport::TestCase + setup do + key = OpenSSL::PKey::RSA.new(2048) + @provider = Provider::EnableBanking.new(application_id: "test_app_id", client_certificate: key.to_pem) + end + + test "get_account_transactions retries with corrected date_from from WRONG_TRANSACTIONS_PERIOD" do + requested_queries = [] + + validation_response = OpenStruct.new( + code: 422, + body: { + error: "WRONG_TRANSACTIONS_PERIOD", + detail: { + message: "Maximum days in the past allowed for transaction list is 120", + date_from: "2026-01-17" + } + }.to_json + ) + + success_response = OpenStruct.new( + code: 200, + body: { transactions: [] }.to_json + ) + + Provider::EnableBanking.expects(:get).twice.with do |_url, options| + requested_queries << options[:query].dup + true + end.returns(validation_response, success_response) + + result = @provider.get_account_transactions( + account_id: "acct_123", + date_from: Date.new(2025, 12, 1), + transaction_status: "BOOK" + ) + + assert_equal [], result[:transactions] + assert_equal "2025-12-01", requested_queries.first[:date_from] + assert_equal "2026-01-17", requested_queries.second[:date_from] + end + + test "validation errors expose parsed response data" do + response = OpenStruct.new( + code: 422, + body: { + error: "WRONG_TRANSACTIONS_PERIOD", + detail: { date_from: "2026-01-17" } + }.to_json + ) + + error = assert_raises Provider::EnableBanking::EnableBankingError do + @provider.send(:handle_response, response) + end + + assert_equal :validation_error, error.error_type + assert_equal "WRONG_TRANSACTIONS_PERIOD", error.response_data[:error] + assert_equal Date.new(2026, 1, 17), error.corrected_date_from + assert error.wrong_transactions_period? + end +end