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 01e9056e5..420626621 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 @@ -33,25 +33,9 @@ def register_sinatra(app) def register_rails(router) handler = proc do |hash| - request = ActionDispatch::Request.new(hash) - - # Skip authentication for health check (root path) - if @url == '/' - params = request.query_parameters.merge(request.request_parameters) - result = handle_request({ params: params, caller: nil }) - [200, { 'Content-Type' => 'application/json' }, [serialize_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] }) - [200, { 'Content-Type' => 'application/json' }, [serialize_response(result)]] - else - [status, headers, response] - end - end + handle_rails_request(hash) + rescue StandardError => e + handle_rails_exception(e) end router.match @url, @@ -74,11 +58,53 @@ def get_collection_safe(datasource, collection_name) private + def handle_rails_request(hash) + request = ActionDispatch::Request.new(hash) + + # Skip authentication for health check (root path) + if @url == '/' + params = request.query_parameters.merge(request.request_parameters) + result = handle_request({ params: params, caller: nil }) + [200, { 'Content-Type' => 'application/json' }, [serialize_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] }) + [200, { 'Content-Type' => 'application/json' }, [serialize_response(result)]] + else + [status, headers, response] + end + end + end + def serialize_response(result) return result if result.is_a?(String) && (result.start_with?('{', '[')) result.to_json end + + def handle_rails_exception(exception) + http_exception = ForestAdminAgent::Http::ErrorTranslator.translate(exception) + + headers = { 'Content-Type' => 'application/json' } + headers.merge!(http_exception.custom_headers || {}) + + data = { + errors: [ + { + name: http_exception.name, + detail: http_exception.message, + status: http_exception.status, + data: http_exception.data + }.compact + ] + } + + [http_exception.status, headers, [data.to_json]] + end end end end 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..e3c137ff3 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 @@ -120,6 +120,40 @@ def handle_request(_params) ) end end + + end + + describe '#handle_rails_exception' do + it 'returns JSON error response with correct structure for NotFoundError' do + error = ForestAdminAgent::Http::Exceptions::NotFoundError.new('Resource not found') + + status, headers, body = route.send(:handle_rails_exception, error) + + expect(status).to eq(404) + expect(headers['Content-Type']).to eq('application/json') + expect(headers['x-error-type']).to eq('object-not-found') + + parsed_body = JSON.parse(body[0]) + expect(parsed_body).to have_key('errors') + expect(parsed_body['errors']).to be_an(Array) + expect(parsed_body['errors'][0]['name']).to eq('NotFoundError') + expect(parsed_body['errors'][0]['detail']).to eq('Resource not found') + expect(parsed_body['errors'][0]['status']).to eq(404) + end + + it 'returns JSON error response for other exceptions' do + error = ForestAdminAgent::Http::Exceptions::UnprocessableError.new('Invalid data') + + status, headers, body = route.send(:handle_rails_exception, error) + + expect(status).to eq(422) + expect(headers['Content-Type']).to eq('application/json') + + parsed_body = JSON.parse(body[0]) + expect(parsed_body['errors'][0]['name']).to eq('UnprocessableError') + expect(parsed_body['errors'][0]['detail']).to eq('Invalid data') + expect(parsed_body['errors'][0]['status']).to eq(422) + end end end end