From 7fdc19f0419756f21765a9ccb280701a1b9700df Mon Sep 17 00:00:00 2001 From: Gabriel Odess-Gillett Date: Thu, 17 Sep 2026 10:51:03 -0500 Subject: [PATCH 1/7] feat: serialize error payloads --- .gitignore | 1 + lib/leopard/message_wrapper.rb | 2 +- test/lib/message_wrapper.rb | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 219c2b6..8fc4f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ Gemfile.lock # Used by RuboCop. Remote config files pulled in from inherit_from directive. .rubocop_cache # .rubocop-https?--* +.idea diff --git a/lib/leopard/message_wrapper.rb b/lib/leopard/message_wrapper.rb index 6dd1426..8216d43 100644 --- a/lib/leopard/message_wrapper.rb +++ b/lib/leopard/message_wrapper.rb @@ -39,7 +39,7 @@ def respond(payload) # # @return [void] def respond_with_error(err, &) - raw.respond_with_error(err, &) + raw.respond_with_error(err.is_a?(Exception) ? err : serialize(err), &) end private diff --git a/test/lib/message_wrapper.rb b/test/lib/message_wrapper.rb index a5cc268..5376f72 100644 --- a/test/lib/message_wrapper.rb +++ b/test/lib/message_wrapper.rb @@ -75,11 +75,11 @@ def respond_with_error(err) assert_equal [err], msg.error_args end - it 'passes hash payloads through when responding with error' do + it 'serializes hash payloads when responding with error' do err = { 'description' => 'broken', 'code' => 422 } wrapper.respond_with_error(err) - assert_equal [err], msg.error_args + assert_equal ['{"description":"broken","code":422}'], msg.error_args end it 'forwards blocks when responding with error' do From b7c81a6dd44c326aec57c4e32971e87b4ca75eab Mon Sep 17 00:00:00 2001 From: Gabriel Odess-Gillett Date: Thu, 17 Sep 2026 13:03:52 -0500 Subject: [PATCH 2/7] fix: only serialize for metrics --- Gemfile.lock | 2 +- lib/leopard/message_wrapper.rb | 2 +- lib/leopard/metrics_server.rb | 2 ++ lib/leopard/version.rb | 2 +- test/lib/message_wrapper.rb | 4 ++-- test/lib/nats_api_server.rb | 17 +++++++++++++++++ 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 717b384..9e8a069 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - leopard (0.2.9) + leopard (0.2.11) concurrent-ruby (~> 1.1) dry-configurable (~> 1.3) dry-monads (~> 1.9) diff --git a/lib/leopard/message_wrapper.rb b/lib/leopard/message_wrapper.rb index 8216d43..6dd1426 100644 --- a/lib/leopard/message_wrapper.rb +++ b/lib/leopard/message_wrapper.rb @@ -39,7 +39,7 @@ def respond(payload) # # @return [void] def respond_with_error(err, &) - raw.respond_with_error(err.is_a?(Exception) ? err : serialize(err), &) + raw.respond_with_error(err, &) end private diff --git a/lib/leopard/metrics_server.rb b/lib/leopard/metrics_server.rb index 185c19f..6248d61 100644 --- a/lib/leopard/metrics_server.rb +++ b/lib/leopard/metrics_server.rb @@ -2,6 +2,7 @@ require 'socket' require 'erb' +require 'json' module Rubyists module Leopard @@ -62,6 +63,7 @@ def close_client(client) def write_metrics_response(client, request_line, workers) if request_line&.start_with?('GET /metrics') body = prometheus_metrics(workers) + body = body.is_a?(String) ? body : JSON.generate(body) client.write "HTTP/1.1 200 OK\r\n" \ "Content-Type: text/plain; version=0.0.4\r\n" \ "Content-Length: #{body.bytesize}\r\n\r\n#{body}" diff --git a/lib/leopard/version.rb b/lib/leopard/version.rb index b33a0bd..dee514e 100644 --- a/lib/leopard/version.rb +++ b/lib/leopard/version.rb @@ -3,7 +3,7 @@ module Rubyists module Leopard # x-release-please-start-version - VERSION = '0.2.10' + VERSION = '0.2.11' # x-release-please-end end end diff --git a/test/lib/message_wrapper.rb b/test/lib/message_wrapper.rb index 5376f72..a5cc268 100644 --- a/test/lib/message_wrapper.rb +++ b/test/lib/message_wrapper.rb @@ -75,11 +75,11 @@ def respond_with_error(err) assert_equal [err], msg.error_args end - it 'serializes hash payloads when responding with error' do + it 'passes hash payloads through when responding with error' do err = { 'description' => 'broken', 'code' => 422 } wrapper.respond_with_error(err) - assert_equal ['{"description":"broken","code":422}'], msg.error_args + assert_equal [err], msg.error_args end it 'forwards blocks when responding with error' do diff --git a/test/lib/nats_api_server.rb b/test/lib/nats_api_server.rb index 180dca9..654151d 100755 --- a/test/lib/nats_api_server.rb +++ b/test/lib/nats_api_server.rb @@ -322,5 +322,22 @@ def instance_variable_get(name) assert_equal expected_metrics, @klass.send(:prometheus_metrics, workers) end + + it 'serializes hash metrics payloads before writing the response' do + client = Object.new + response = nil + client.define_singleton_method(:write) { |payload| response = payload } + + @klass.stub(:prometheus_metrics, { error: 'boom' }) do + @klass.send(:write_metrics_response, client, 'GET /metrics HTTP/1.1', []) + end + + expected_body = '{"error":"boom"}' + expected_response = "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/plain; version=0.0.4\r\n" \ + "Content-Length: #{expected_body.bytesize}\r\n\r\n#{expected_body}" + + assert_equal expected_response, response + end end end From bb3b70286fa0d6016d2b5906f97daf0aeccfecb4 Mon Sep 17 00:00:00 2001 From: Gabriel Odess-Gillett Date: Thu, 17 Sep 2026 13:07:05 -0500 Subject: [PATCH 3/7] chore: rubocop --- lib/leopard/metrics_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/leopard/metrics_server.rb b/lib/leopard/metrics_server.rb index 6248d61..22f6e05 100644 --- a/lib/leopard/metrics_server.rb +++ b/lib/leopard/metrics_server.rb @@ -63,7 +63,7 @@ def close_client(client) def write_metrics_response(client, request_line, workers) if request_line&.start_with?('GET /metrics') body = prometheus_metrics(workers) - body = body.is_a?(String) ? body : JSON.generate(body) + body = JSON.generate(body) unless body.is_a?(String) client.write "HTTP/1.1 200 OK\r\n" \ "Content-Type: text/plain; version=0.0.4\r\n" \ "Content-Length: #{body.bytesize}\r\n\r\n#{body}" From ec145677608a0a5c6e9a3fcc95815e0efcb9cf73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 17 Sep 2026 18:12:47 +0000 Subject: [PATCH 4/7] chore: align version metadata sources Co-authored-by: gabeodess <65213+gabeodess@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 033e2d4..0ed71f7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.2.10" + ".": "0.2.11" } diff --git a/.version.txt b/.version.txt index 13dead7..d3b5ba4 100644 --- a/.version.txt +++ b/.version.txt @@ -1 +1 @@ -0.2.10 +0.2.11 From 24196d1307dab4153beb1c4142c7cf5ca786d336 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 17 Sep 2026 19:31:15 +0000 Subject: [PATCH 5/7] fix: set metrics JSON content type Co-authored-by: gabeodess <65213+gabeodess@users.noreply.github.com> --- lib/leopard/metrics_server.rb | 10 ++++++++-- test/lib/nats_api_server.rb | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/leopard/metrics_server.rb b/lib/leopard/metrics_server.rb index 22f6e05..10c8136 100644 --- a/lib/leopard/metrics_server.rb +++ b/lib/leopard/metrics_server.rb @@ -63,9 +63,15 @@ def close_client(client) def write_metrics_response(client, request_line, workers) if request_line&.start_with?('GET /metrics') body = prometheus_metrics(workers) - body = JSON.generate(body) unless body.is_a?(String) + content_type = 'text/plain; version=0.0.4' + + unless body.is_a?(String) + body = JSON.generate(body) + content_type = 'application/json' + end + client.write "HTTP/1.1 200 OK\r\n" \ - "Content-Type: text/plain; version=0.0.4\r\n" \ + "Content-Type: #{content_type}\r\n" \ "Content-Length: #{body.bytesize}\r\n\r\n#{body}" else client.write "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n" diff --git a/test/lib/nats_api_server.rb b/test/lib/nats_api_server.rb index 654151d..34e931b 100755 --- a/test/lib/nats_api_server.rb +++ b/test/lib/nats_api_server.rb @@ -334,7 +334,7 @@ def instance_variable_get(name) expected_body = '{"error":"boom"}' expected_response = "HTTP/1.1 200 OK\r\n" \ - "Content-Type: text/plain; version=0.0.4\r\n" \ + "Content-Type: application/json\r\n" \ "Content-Length: #{expected_body.bytesize}\r\n\r\n#{expected_body}" assert_equal expected_response, response From b57c9c7cebd8398e551346b624754245f8459e14 Mon Sep 17 00:00:00 2001 From: Gabriel Odess-Gillett Date: Thu, 17 Sep 2026 14:36:12 -0500 Subject: [PATCH 6/7] chore: too many lines --- lib/leopard/metrics_server.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/leopard/metrics_server.rb b/lib/leopard/metrics_server.rb index 10c8136..96c43e2 100644 --- a/lib/leopard/metrics_server.rb +++ b/lib/leopard/metrics_server.rb @@ -63,12 +63,8 @@ def close_client(client) def write_metrics_response(client, request_line, workers) if request_line&.start_with?('GET /metrics') body = prometheus_metrics(workers) - content_type = 'text/plain; version=0.0.4' - - unless body.is_a?(String) - body = JSON.generate(body) - content_type = 'application/json' - end + body = JSON.generate(body) unless body.is_a?(String) + content_type = body.is_a?(String) ? 'text/plain; version=0.0.4' : 'application/json' client.write "HTTP/1.1 200 OK\r\n" \ "Content-Type: #{content_type}\r\n" \ From 0af4b0ba3c322ad734730f45eac925a73a760c73 Mon Sep 17 00:00:00 2001 From: Gabriel Odess-Gillett Date: Thu, 17 Sep 2026 14:44:14 -0500 Subject: [PATCH 7/7] fix: content type calculation --- lib/leopard/metrics_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/leopard/metrics_server.rb b/lib/leopard/metrics_server.rb index 96c43e2..0b89965 100644 --- a/lib/leopard/metrics_server.rb +++ b/lib/leopard/metrics_server.rb @@ -63,8 +63,8 @@ def close_client(client) def write_metrics_response(client, request_line, workers) if request_line&.start_with?('GET /metrics') body = prometheus_metrics(workers) - body = JSON.generate(body) unless body.is_a?(String) content_type = body.is_a?(String) ? 'text/plain; version=0.0.4' : 'application/json' + body = JSON.generate(body) unless body.is_a?(String) client.write "HTTP/1.1 200 OK\r\n" \ "Content-Type: #{content_type}\r\n" \