diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py index fe51d68dab9..29601247b48 100644 --- a/aws_lambda_powertools/event_handler/api_gateway.py +++ b/aws_lambda_powertools/event_handler/api_gateway.py @@ -503,6 +503,18 @@ def _get_openapi_path( if request_body_oai: operation["requestBody"] = request_body_oai + # Validation failure response (422) will always be part of the schema + operation_responses: Dict[int, OpenAPIResponse] = { + 422: { + "description": "Validation Error", + "content": { + "application/json": { + "schema": {"$ref": COMPONENT_REF_PREFIX + "HTTPValidationError"}, + }, + }, + }, + } + # Add the response to the OpenAPI operation if self.responses: for status_code in list(self.responses): @@ -549,45 +561,34 @@ def _get_openapi_path( response["content"][content_type] = new_payload - operation["responses"] = self.responses + # Merge the user provided response with the default responses + operation_responses[status_code] = response else: # Set the default 200 response - responses = operation.setdefault("responses", {}) - success_response = responses.setdefault(200, {}) - success_response["description"] = self.response_description or _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION - success_response["content"] = {"application/json": {"schema": {}}} - json_response = success_response["content"].setdefault("application/json", {}) - - # Add the response schema to the OpenAPI 200 response - json_response.update( - self._openapi_operation_return( - param=dependant.return_param, - model_name_map=model_name_map, - field_mapping=field_mapping, - ), + response_schema = self._openapi_operation_return( + param=dependant.return_param, + model_name_map=model_name_map, + field_mapping=field_mapping, ) - # Add validation failure response (422) - operation["responses"][422] = { - "description": "Validation Error", - "content": { - "application/json": { - "schema": {"$ref": COMPONENT_REF_PREFIX + "HTTPValidationError"}, - }, - }, + # Add the response schema to the OpenAPI 200 response + operation_responses[200] = { + "description": self.response_description or _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + "content": {"application/json": response_schema}, } - # Add the validation error schema to the definitions, but only if it hasn't been added yet - if "ValidationError" not in definitions: - definitions.update( - { - "ValidationError": validation_error_definition, - "HTTPValidationError": validation_error_response_definition, - }, - ) - + operation["responses"] = operation_responses path[self.method.lower()] = operation + # Add the validation error schema to the definitions, but only if it hasn't been added yet + if "ValidationError" not in definitions: + definitions.update( + { + "ValidationError": validation_error_definition, + "HTTPValidationError": validation_error_response_definition, + }, + ) + # Generate the response schema return path, definitions diff --git a/aws_lambda_powertools/event_handler/openapi/types.py b/aws_lambda_powertools/event_handler/openapi/types.py index beafa0e566c..5a99ee76e98 100644 --- a/aws_lambda_powertools/event_handler/openapi/types.py +++ b/aws_lambda_powertools/event_handler/openapi/types.py @@ -28,7 +28,7 @@ "type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, }, - "msg": {"title": "Message", "type": "string"}, + # For security reasons, we hide **msg** details (don't leak Python, Pydantic or filenames) "type": {"title": "Error Type", "type": "string"}, }, "required": ["loc", "msg", "type"], diff --git a/tests/functional/event_handler/test_openapi_params.py b/tests/functional/event_handler/test_openapi_params.py index 38b0cbed307..2ac9c036f3f 100644 --- a/tests/functional/event_handler/test_openapi_params.py +++ b/tests/functional/event_handler/test_openapi_params.py @@ -52,7 +52,7 @@ def handler(): assert JSON_CONTENT_TYPE in response.content json_response = response.content[JSON_CONTENT_TYPE] - assert json_response.schema_ == Schema() + assert json_response.schema_ is None assert not json_response.examples assert not json_response.encoding diff --git a/tests/functional/event_handler/test_openapi_responses.py b/tests/functional/event_handler/test_openapi_responses.py index be5d9bca288..21a71d7dee3 100644 --- a/tests/functional/event_handler/test_openapi_responses.py +++ b/tests/functional/event_handler/test_openapi_responses.py @@ -50,8 +50,34 @@ def handler(): assert 202 in responses.keys() assert responses[202].description == "Custom response" - assert 200 not in responses.keys() - assert 422 not in responses.keys() + assert 200 not in responses.keys() # 200 was not added due to custom responses + assert 422 in responses.keys() # 422 is always added due to potential data validation errors + + +def test_openapi_422_default_response(): + app = APIGatewayRestResolver(enable_validation=True) + + @app.get("/") + def handler(): + return {"message": "hello world"} + + schema = app.get_openapi_schema() + responses = schema.paths["/"].get.responses + assert 422 in responses.keys() + assert responses[422].description == "Validation Error" + + +def test_openapi_422_custom_response(): + app = APIGatewayRestResolver(enable_validation=True) + + @app.get("/", responses={422: {"description": "Custom validation response"}}) + def handler(): + return {"message": "hello world"} + + schema = app.get_openapi_schema() + responses = schema.paths["/"].get.responses + assert 422 in responses.keys() + assert responses[422].description == "Custom validation response" def test_openapi_200_custom_schema():