From 1b7566c783e6a15e502b1637973ac28e068cc81a Mon Sep 17 00:00:00 2001 From: Dogan AY Date: Fri, 23 Jan 2026 10:33:48 +0100 Subject: [PATCH] fix(rpcagent): symbolize params keys in base route --- .../routes/base_route.rb | 19 +++++++-- .../routes/base_route_spec.rb | 42 +++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/packages/forest_admin_rpc_agent/lib/forest_admin_rpc_agent/routes/base_route.rb b/packages/forest_admin_rpc_agent/lib/forest_admin_rpc_agent/routes/base_route.rb index f205c317d..b52e8b672 100644 --- a/packages/forest_admin_rpc_agent/lib/forest_admin_rpc_agent/routes/base_route.rb +++ b/packages/forest_admin_rpc_agent/lib/forest_admin_rpc_agent/routes/base_route.rb @@ -39,16 +39,16 @@ def register_rails(router) # Skip authentication for health check (root path) if @url == '/' - params = request.query_parameters.merge(request.request_parameters) - result = handle_request({ params: params, caller: nil, request: request }) + params = deep_symbolize_keys(request.query_parameters.merge(request.request_parameters)) + result = handle_request({ params: params.with_indifferent_access, caller: nil, request: request }) build_rails_response(result) else auth_middleware = ForestAdminRpcAgent::Middleware::Authentication.new(->(_env) { [200, {}, ['OK']] }) status, headers, response = auth_middleware.call(request.env) if status == 200 - params = request.query_parameters.merge(request.request_parameters) - result = handle_request({ params: params, caller: headers[:caller], request: request }) + params = deep_symbolize_keys(request.query_parameters.merge(request.request_parameters)) + result = handle_request({ params: params.with_indifferent_access, caller: headers[:caller], request: request }) build_rails_response(result) else [status, headers, response] @@ -87,6 +87,17 @@ def get_collection_safe(datasource, collection_name) private + def deep_symbolize_keys(obj) + case obj + when Hash + obj.transform_keys(&:to_sym).transform_values { |v| deep_symbolize_keys(v) } + when Array + obj.map { |v| deep_symbolize_keys(v) } + else + obj + end + end + def serialize_response(result) return result if result.is_a?(String) && (result.start_with?('{', '[')) diff --git a/packages/forest_admin_rpc_agent/spec/lib/forest_admin_rpc_agent/routes/base_route_spec.rb b/packages/forest_admin_rpc_agent/spec/lib/forest_admin_rpc_agent/routes/base_route_spec.rb index 7f75e9954..1c7578647 100644 --- a/packages/forest_admin_rpc_agent/spec/lib/forest_admin_rpc_agent/routes/base_route_spec.rb +++ b/packages/forest_admin_rpc_agent/spec/lib/forest_admin_rpc_agent/routes/base_route_spec.rb @@ -121,6 +121,48 @@ def handle_request(_params) end end end + + describe '#build_rails_response' do + context 'when result is a Hash with :status key' do + it 'returns the correct response structure' do + result = { status: 201, content: { id: 1 } } + response = route.send(:build_rails_response, result) + + expect(response[0]).to eq(201) + expect(response[1]).to eq({ 'Content-Type' => 'application/json' }) + expect(response[2]).to eq(['{"id":1}']) + end + + it 'merges custom headers when provided' do + result = { status: 200, content: { data: 'test' }, headers: { 'X-Custom-Header' => 'value' } } + response = route.send(:build_rails_response, result) + + expect(response[0]).to eq(200) + expect(response[1]).to eq({ 'Content-Type' => 'application/json', 'X-Custom-Header' => 'value' }) + expect(response[2]).to eq(['{"data":"test"}']) + end + + it 'returns empty body when content is nil' do + result = { status: 204 } + response = route.send(:build_rails_response, result) + + expect(response[0]).to eq(204) + expect(response[1]).to eq({ 'Content-Type' => 'application/json' }) + expect(response[2]).to eq(['']) + end + end + + context 'when result is not a Hash with :status key' do + it 'returns 200 with serialized result' do + result = { test: 'data' } + response = route.send(:build_rails_response, result) + + expect(response[0]).to eq(200) + expect(response[1]).to eq({ 'Content-Type' => 'application/json' }) + expect(response[2]).to eq(['{"test":"data"}']) + end + end + end end end end