From b1596cd6af82b0e9871949b5fd25438732bb2e49 Mon Sep 17 00:00:00 2001 From: Julien Bourdeau Date: Wed, 15 Oct 2025 15:35:28 +0200 Subject: [PATCH] feat(integration): Raise BadGatewayError on 502 errors --- .../integrations/aggregator/base_service.rb | 9 +-- .../taxes/invoices/create_draft_service.rb | 2 +- .../taxes/invoices/create_service.rb | 2 +- .../invoices/create_draft_service_spec.rb | 53 ++++++++------- .../taxes/invoices/create_service_spec.rb | 65 +++++++++---------- 5 files changed, 67 insertions(+), 64 deletions(-) diff --git a/app/services/integrations/aggregator/base_service.rb b/app/services/integrations/aggregator/base_service.rb index aa91c0ca4db..48665ee0e13 100644 --- a/app/services/integrations/aggregator/base_service.rb +++ b/app/services/integrations/aggregator/base_service.rb @@ -7,7 +7,7 @@ module Aggregator class BaseService < BaseService BASE_URL = "https://api.nango.dev/" REQUEST_LIMIT_ERROR_CODE = "SSS_REQUEST_LIMIT_EXCEEDED" - BAD_REQUEST_ERROR = "502 Bad Gateway" + BAD_GATEWAY_ERROR = "502 Bad Gateway" def initialize(integration:, options: {}) @integration = integration @@ -162,14 +162,15 @@ def request_limit_error?(http_error) end def bad_gateway_error?(http_error) - http_error.error_body.include?(BAD_REQUEST_ERROR) + http_error.error_code.to_s == "502" || + http_error.error_body.include?(BAD_GATEWAY_ERROR) end def parse_response(response) JSON.parse(response.body) rescue JSON::ParserError - if response.body.include?(BAD_REQUEST_ERROR) - # NOTE: Sometimes, Anrock is responding with an HTTP 200 with a payload containing a 502 error... + if response.body.include?(BAD_GATEWAY_ERROR) + # NOTE: Sometimes, Anrok is responding with an HTTP 200 with a payload containing a 502 error... raise(Integrations::Aggregator::BadGatewayError.new(response.body, http_client.uri)) end diff --git a/app/services/integrations/aggregator/taxes/invoices/create_draft_service.rb b/app/services/integrations/aggregator/taxes/invoices/create_draft_service.rb index 7cac616cfc1..01bc88e0d17 100644 --- a/app/services/integrations/aggregator/taxes/invoices/create_draft_service.rb +++ b/app/services/integrations/aggregator/taxes/invoices/create_draft_service.rb @@ -23,7 +23,7 @@ def call result rescue LagoHttpClient::HttpError => e raise RequestLimitError(e) if request_limit_error?(e) - raise e if bad_gateway_error?(e) + raise Integrations::Aggregator::BadGatewayError.new(e.error_body, e.uri) if bad_gateway_error?(e) code = code(e) message = message(e) diff --git a/app/services/integrations/aggregator/taxes/invoices/create_service.rb b/app/services/integrations/aggregator/taxes/invoices/create_service.rb index b6264204172..129984548a6 100644 --- a/app/services/integrations/aggregator/taxes/invoices/create_service.rb +++ b/app/services/integrations/aggregator/taxes/invoices/create_service.rb @@ -25,7 +25,7 @@ def call result rescue LagoHttpClient::HttpError => e raise Integrations::Aggregator::RequestLimitError(e) if request_limit_error?(e) - raise e if bad_gateway_error?(e) + raise Integrations::Aggregator::BadGatewayError.new(e.error_body, e.uri) if bad_gateway_error?(e) code = code(e) message = message(e) diff --git a/spec/services/integrations/aggregator/taxes/invoices/create_draft_service_spec.rb b/spec/services/integrations/aggregator/taxes/invoices/create_draft_service_spec.rb index 7d4e3abb9d1..0c2113f4b67 100644 --- a/spec/services/integrations/aggregator/taxes/invoices/create_draft_service_spec.rb +++ b/spec/services/integrations/aggregator/taxes/invoices/create_draft_service_spec.rb @@ -9,7 +9,6 @@ let(:integration_customer) { create(:anrok_customer, integration:, customer:) } let(:customer) { create(:customer, :with_shipping_address, organization:) } let(:organization) { create(:organization) } - let(:lago_client) { instance_double(LagoHttpClient::Client) } let(:endpoint) { "https://api.nango.dev/v1/anrok/draft_invoices" } let(:add_on) { create(:add_on, organization:) } let(:add_on_two) { create(:add_on, organization:) } @@ -64,6 +63,7 @@ "Provider-Config-Key" => "anrok" } end + let(:response_status) { 200 } let(:params) do [ @@ -99,32 +99,24 @@ end before do - allow(LagoHttpClient::Client).to receive(:new) - .with(endpoint, retries_on: [OpenSSL::SSL::SSLError]) - .and_return(lago_client) - integration_customer integration_collection_mapping1 integration_mapping_add_on fee_add_on fee_add_on_two + + stub_request(:post, endpoint).with(body: params.to_json, headers:) + .and_return(status: response_status, body:) end describe "#call" do context "when service call is successful" do - let(:response) { instance_double(Net::HTTPOK) } - - before do - allow(lago_client).to receive(:post_with_response).with(kind_of(Array), headers).and_return(response) - allow(response).to receive(:body).and_return(body) - allow(lago_client).to receive(:uri).and_return(endpoint) - end - context "when taxes are successfully fetched" do - let(:body) do + let(:base_body) do path = Rails.root.join("spec/fixtures/integration_aggregator/taxes/invoices/success_response.json") File.read(path) end + let(:body) { base_body } it "returns fees" do result = service_call @@ -140,8 +132,8 @@ end context "when special rules applied" do - before do - parsed_body = JSON.parse(body) + let(:body) do + parsed_body = JSON.parse(base_body) parsed_body["succeededInvoices"].first["fees"].first["tax_amount_cents"] = 0 parsed_body["succeededInvoices"].first["fees"].first["tax_breakdown"] = [ { @@ -149,7 +141,7 @@ type: rule } ] - allow(response).to receive(:body).and_return(parsed_body.to_json) + parsed_body.to_json end special_rules = @@ -242,6 +234,8 @@ before do params.first["fees"].each { |fee| fee["item_code"] = nil } + stub_request(:post, endpoint).with(body: params.to_json, headers:) + .and_return(status: response_status, body:) end it "sends request to anrok with empty link to fallback item" do @@ -275,14 +269,29 @@ File.read(path) end - let(:http_error) { LagoHttpClient::HttpError.new(error_code, body, nil) } + context "when the body contains a bad gateway error" do + let(:response_status) { 200 } + let(:body) do + path = Rails.root.join("spec/fixtures/integration_aggregator/bad_gateway_error.html") + File.read(path) + end + + it "raises an HTTP error" do + expect { service_call }.to raise_error(Integrations::Aggregator::BadGatewayError) + end + end - before do - allow(lago_client).to receive(:post_with_response).with(params, headers).and_raise(http_error) + context "when the error code is 502" do + let(:response_status) { 502 } + let(:body) { "" } + + it "raises an HTTP error" do + expect { service_call }.to raise_error(Integrations::Aggregator::BadGatewayError) + end end - context "when it is a server error" do - let(:error_code) { Faker::Number.between(from: 500, to: 599) } + context "when it is another server error" do + let(:response_status) { Faker::Number.between(from: 500, to: 599) } it "returns an error" do result = service_call diff --git a/spec/services/integrations/aggregator/taxes/invoices/create_service_spec.rb b/spec/services/integrations/aggregator/taxes/invoices/create_service_spec.rb index 656d2a19cb6..d750f1ae994 100644 --- a/spec/services/integrations/aggregator/taxes/invoices/create_service_spec.rb +++ b/spec/services/integrations/aggregator/taxes/invoices/create_service_spec.rb @@ -9,7 +9,6 @@ let(:integration_customer) { create(:anrok_customer, integration:, customer:, external_customer_id: nil) } let(:customer) { create(:customer, organization:) } let(:organization) { create(:organization) } - let(:lago_client) { instance_double(LagoHttpClient::Client) } let(:endpoint) { "https://api.nango.dev/v1/anrok/finalized_invoices" } let(:add_on) { create(:add_on, organization:) } let(:add_on_two) { create(:add_on, organization:) } @@ -64,12 +63,12 @@ "Provider-Config-Key" => "anrok" } end + let(:response_status) { 200 } let(:params) do [ { - "id" => invoice.id, - "issuing_date" => invoice.issuing_date, + "issuing_date" => invoice.issuing_date.to_s, "currency" => invoice.currency, "contact" => { "external_id" => customer.external_id, @@ -94,33 +93,25 @@ "item_code" => "1", "amount_cents" => 200 } - ] + ], + "id" => invoice.id } ] end before do - allow(LagoHttpClient::Client).to receive(:new) - .with(endpoint, retries_on: [OpenSSL::SSL::SSLError]) - .and_return(lago_client) - integration_customer integration_collection_mapping1 integration_mapping_add_on fee_add_on fee_add_on_two + + stub_request(:post, endpoint).with(body: params.to_json, headers:) + .and_return(status: response_status, body:) end describe "#call" do context "when service call is successful" do - let(:response) { instance_double(Net::HTTPOK) } - - before do - allow(lago_client).to receive(:post_with_response).with(array_including(params), headers).and_return(response) - allow(response).to receive(:body).and_return(body) - allow(lago_client).to receive(:uri).and_return(endpoint) - end - context "when taxes are successfully fetched for finalized invoice" do let(:body) do path = Rails.root.join("spec/fixtures/integration_aggregator/taxes/invoices/success_response.json") @@ -158,8 +149,6 @@ let(:params) do [ { - "id" => invoice.id, - "type" => "salesInvoice", "issuing_date" => invoice.issuing_date, "currency" => invoice.currency, "contact" => { @@ -185,17 +174,19 @@ "item_key" => fee_add_on.item_key, "item_id" => fee_add_on.id, "item_code" => "m1", - "unit" => 0.00, + "unit" => "0.0", "amount" => "2.0" }, { "item_key" => fee_add_on_two.item_key, "item_id" => fee_add_on_two.id, "item_code" => "1", - "unit" => 0.00, + "unit" => "0.0", "amount" => "2.0" } - ] + ], + "id" => invoice.id, + "type" => "salesInvoice" } ] end @@ -232,8 +223,6 @@ let(:params) do [ { - "id" => invoice.id, - "type" => "returnInvoice", "issuing_date" => invoice.issuing_date, "currency" => invoice.currency, "contact" => { @@ -254,22 +243,24 @@ "region" => customer.billing_entity&.state, "country" => customer.billing_entity&.country }, - "fees" => an_array_matching([ + "fees" => [ { "item_key" => fee_add_on.item_key, "item_id" => fee_add_on.id, "item_code" => "m1", - "unit" => 0.00, + "unit" => "0.0", "amount" => "-2.0" }, { "item_key" => fee_add_on_two.item_key, "item_id" => fee_add_on_two.id, "item_code" => "1", - "unit" => 0.00, + "unit" => "0.0", "amount" => "-2.0" } - ]) + ], + "id" => invoice.id, + "type" => "returnInvoice" } ] end @@ -335,19 +326,21 @@ end context "when service call is not successful" do - let(:body) do - path = Rails.root.join("spec/fixtures/integration_aggregator/error_response.json") - File.read(path) - end + context "when the error code is 502" do + let(:response_status) { 502 } + let(:body) { "" } - let(:http_error) { LagoHttpClient::HttpError.new(error_code, body, nil) } - - before do - allow(lago_client).to receive(:post_with_response).with(params, headers).and_raise(http_error) + it "raises an HTTP error" do + expect { service_call }.to raise_error(Integrations::Aggregator::BadGatewayError) + end end context "when it is a server error" do - let(:error_code) { Faker::Number.between(from: 500, to: 599) } + let(:body) do + path = Rails.root.join("spec/fixtures/integration_aggregator/error_response.json") + File.read(path) + end + let(:response_status) { 500 } it "returns an error" do result = service_call