From 2dd7aee6cabd69091f900ccdf9212a193f1e1be8 Mon Sep 17 00:00:00 2001 From: Matias Salles Date: Wed, 17 Sep 2025 00:05:15 -0300 Subject: [PATCH 1/5] include headers in response --- lib/nylas/handler/http_client.rb | 19 +++++---- spec/nylas/handler/http_client_spec.rb | 53 +++++++++++++++++++++----- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/lib/nylas/handler/http_client.rb b/lib/nylas/handler/http_client.rb index d373140c..6e0e7a6b 100644 --- a/lib/nylas/handler/http_client.rb +++ b/lib/nylas/handler/http_client.rb @@ -41,7 +41,7 @@ def execute(method:, path:, timeout:, headers: {}, query: {}, payload: nil, api_ content_type = response.headers["content-type"].downcase end - parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type) + parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type, response.headers) # Include headers in the response parsed_response[:headers] = response.headers unless parsed_response.nil? parsed_response @@ -311,37 +311,36 @@ def handle_response(http, get_request, path, &block) end # Parses the response from the Nylas API and evaluates for errors. - def parse_json_evaluate_error(http_code, response, path, content_type = nil) + def parse_json_evaluate_error(http_code, response, path, content_type = nil, headers = nil) begin response = parse_response(response) if content_type == "application/json" rescue Nylas::JsonParseError - handle_failed_response(http_code, response, path) + handle_failed_response(http_code, response, path, headers) raise end - handle_failed_response(http_code, response, path) + handle_failed_response(http_code, response, path, headers) response end # Handles failed responses from the Nylas API. - def handle_failed_response(http_code, response, path) + def handle_failed_response(http_code, response, path, headers = nil) return if HTTP_SUCCESS_CODES.include?(http_code) + puts response.inspect + puts response.class.name case response when Hash - raise error_hash_to_exception(response, http_code, path) + raise error_hash_to_exception(response, http_code, path, headers) else raise NylasApiError.parse_error_response(response, http_code) end end # Converts error hashes to exceptions. - def error_hash_to_exception(response, status_code, path) + def error_hash_to_exception(response, status_code, path, headers = nil) return if !response || !response.key?(:error) - # Safely get headers without risking KeyError - headers = response.key?(:headers) ? response[:headers] : nil - if %W[#{api_uri}/v3/connect/token #{api_uri}/v3/connect/revoke].include?(path) NylasOAuthError.new(response[:error], response[:error_description], response[:error_uri], response[:error_code], status_code) diff --git a/spec/nylas/handler/http_client_spec.rb b/spec/nylas/handler/http_client_spec.rb index 34057eda..1848146f 100644 --- a/spec/nylas/handler/http_client_spec.rb +++ b/spec/nylas/handler/http_client_spec.rb @@ -318,6 +318,35 @@ class TestHttpClient expect(response).to eq(response_json.merge(headers: mock_headers)) end + it "returns an error with headers" do + response_json = { + foo: "bar", + error: { + type: "api_error", + message: "An unexpected error occurred", + provider_error: "This is the provider error" + } + } + request_params = { method: :get, path: "https://test.api.nylas.com/foo", timeout: 30 } + mock_headers = { + "content-type" => "application/json", + "x-request-id" => "123", + "some-header" => "value" + } + mock_response = instance_double("HTTParty::Response", + body: response_json.to_json, + headers: mock_headers, + code: 429) + + allow(HTTParty).to receive(:get).and_return(mock_response) + + expect do + http_client.send(:execute, **request_params) + end.to raise_error(Nylas::NylasApiError) { |error| + expect(error.headers).to eq(mock_headers) + } + end + it "raises a timeout error" do request_params = { method: :get, path: "https://test.api.nylas.com/foo", timeout: 30 } allow(HTTParty).to receive(:get).and_raise(Net::OpenTimeout) @@ -462,22 +491,22 @@ class TestHttpClient type: "api_error", message: "An unexpected error occurred", provider_error: "This is the provider error" - }, - headers: { - "x-request-id": "request-id-from-headers", - "x-ratelimit-limit": "100", - "x-ratelimit-remaining": "99" } } + headers = { + "x-request-id": "request-id-from-headers", + "x-ratelimit-limit": "100", + "x-ratelimit-remaining": "99" + } - err_obj = http_client.send(:error_hash_to_exception, response, 400, "https://test.api.nylas.com/foo") + err_obj = http_client.send(:error_hash_to_exception, response, 400, "https://test.api.nylas.com/foo", headers) expect(err_obj).to be_a(Nylas::NylasApiError) expect(err_obj.message).to eq("An unexpected error occurred") expect(err_obj.request_id).to eq("request-id") expect(err_obj.provider_error).to eq("This is the provider error") expect(err_obj.type).to eq("api_error") - expect(err_obj.headers).to eq(response[:headers]) + expect(err_obj.headers).to eq(headers) end end @@ -555,11 +584,17 @@ class TestHttpClient provider_error: "This is the provider error" } } + headers = { + "x-request-id": "request-id-from-headers", + "x-ratelimit-limit": "100", + } expect do http_client.send(:parse_json_evaluate_error, 400, response.to_json, - "https://test.api.nylas.com/foo", "application/json") - end.to raise_error(Nylas::NylasApiError) + "https://test.api.nylas.com/foo", "application/json", headers) + end.to raise_error(Nylas::NylasApiError) { |error| + expect(error.headers).to eq(headers) + } end it "raises a NylasApiError for a non-JSON response" do From cdc1226918e89bb4145e5acdd11d30c57893dcde Mon Sep 17 00:00:00 2001 From: Matias Salles Date: Wed, 17 Sep 2025 01:12:52 -0300 Subject: [PATCH 2/5] remove puts --- lib/nylas/handler/http_client.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/nylas/handler/http_client.rb b/lib/nylas/handler/http_client.rb index 6e0e7a6b..b5104898 100644 --- a/lib/nylas/handler/http_client.rb +++ b/lib/nylas/handler/http_client.rb @@ -327,8 +327,6 @@ def parse_json_evaluate_error(http_code, response, path, content_type = nil, hea def handle_failed_response(http_code, response, path, headers = nil) return if HTTP_SUCCESS_CODES.include?(http_code) - puts response.inspect - puts response.class.name case response when Hash raise error_hash_to_exception(response, http_code, path, headers) From d26877ee397fe4e9526f9b034e9dc498c5b35e8b Mon Sep 17 00:00:00 2001 From: Aaron de Mello Date: Thu, 25 Sep 2025 10:50:11 -0400 Subject: [PATCH 3/5] fix: resolve SimpleCov XML parsing error with MultiFormatter - Configure SimpleCov to use both HTMLFormatter and CoberturaFormatter - Prevents REXML::ParseException during coverage report generation - Ensures robust coverage reporting for CI/CD pipelines --- spec/spec_helper.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0388c74e..4c2177c5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,13 +1,18 @@ # frozen_string_literal: true require "simplecov" +require "simplecov-cobertura" + SimpleCov.start do add_filter "/spec/" + + # Use multiple formatters to ensure coverage data is available + formatter SimpleCov::Formatter::MultiFormatter.new([ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::CoberturaFormatter + ]) end -require "simplecov-cobertura" -SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter - require "nylas" require "support/nylas_helpers" From eb67a4953a56779ecb972ab94cc64d70205a6b68 Mon Sep 17 00:00:00 2001 From: Aaron de Mello Date: Thu, 25 Sep 2025 11:02:52 -0400 Subject: [PATCH 4/5] fix: lint --- lib/nylas/handler/http_client.rb | 3 ++- spec/nylas/handler/http_client_spec.rb | 7 ++++--- spec/spec_helper.rb | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/nylas/handler/http_client.rb b/lib/nylas/handler/http_client.rb index b5104898..0eb14ec5 100644 --- a/lib/nylas/handler/http_client.rb +++ b/lib/nylas/handler/http_client.rb @@ -41,7 +41,8 @@ def execute(method:, path:, timeout:, headers: {}, query: {}, payload: nil, api_ content_type = response.headers["content-type"].downcase end - parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type, response.headers) + parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type, + response.headers) # Include headers in the response parsed_response[:headers] = response.headers unless parsed_response.nil? parsed_response diff --git a/spec/nylas/handler/http_client_spec.rb b/spec/nylas/handler/http_client_spec.rb index 1848146f..980f8c54 100644 --- a/spec/nylas/handler/http_client_spec.rb +++ b/spec/nylas/handler/http_client_spec.rb @@ -321,7 +321,7 @@ class TestHttpClient it "returns an error with headers" do response_json = { foo: "bar", - error: { + error: { type: "api_error", message: "An unexpected error occurred", provider_error: "This is the provider error" @@ -499,7 +499,8 @@ class TestHttpClient "x-ratelimit-remaining": "99" } - err_obj = http_client.send(:error_hash_to_exception, response, 400, "https://test.api.nylas.com/foo", headers) + err_obj = http_client.send(:error_hash_to_exception, response, 400, "https://test.api.nylas.com/foo", + headers) expect(err_obj).to be_a(Nylas::NylasApiError) expect(err_obj.message).to eq("An unexpected error occurred") @@ -586,7 +587,7 @@ class TestHttpClient } headers = { "x-request-id": "request-id-from-headers", - "x-ratelimit-limit": "100", + "x-ratelimit-limit": "100" } expect do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4c2177c5..799ee325 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,12 +5,12 @@ SimpleCov.start do add_filter "/spec/" - + # Use multiple formatters to ensure coverage data is available formatter SimpleCov::Formatter::MultiFormatter.new([ - SimpleCov::Formatter::HTMLFormatter, - SimpleCov::Formatter::CoberturaFormatter - ]) + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::CoberturaFormatter + ]) end require "nylas" From 28b5ef0360f86c9a1888d8d3beaf54a8f7b5a42b Mon Sep 17 00:00:00 2001 From: Aaron de Mello Date: Thu, 25 Sep 2025 11:04:21 -0400 Subject: [PATCH 5/5] Added access to response headers changelog update --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 662f2525..decded5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +### [Unreleased] +* Added access to response headers + ### [6.6.0] * Added support for `single_level` query parameter in Folders API for Microsoft accounts * Added support for `include_hidden_folders` query parameter in folders list endpoint for Microsoft accounts to control whether hidden folders are included in the response