From bfe3369caf3f0ac46ae71ddf480a9b2883a57fef Mon Sep 17 00:00:00 2001 From: Matteo Figus Date: Fri, 6 Jun 2025 00:38:21 +0100 Subject: [PATCH 1/3] fix: prevent splitting Bedrock Agent parameters with commas This fixes issue #6520 where Bedrock Agent parameters containing commas (like SQL queries) were being truncated because the resolved_query_string_parameters method was splitting them by commas. The fix overrides the resolved_query_string_parameters method in the BedrockAgentEvent class to preserve parameter values without splitting them by commas. --- .../data_classes/bedrock_agent_event.py | 13 ++++++ .../_pydantic/test_bedrock_agent.py | 45 ++++++++++++++++++- .../test_bedrock_agent_event.py | 31 +++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py b/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py index 1b3c57be124..be7bddcb64e 100644 --- a/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py +++ b/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py @@ -112,6 +112,19 @@ def query_string_parameters(self) -> dict[str, str]: parameters = self.get("parameters") or [] return {x["name"]: x["value"] for x in parameters} + @cached_property + def resolved_query_string_parameters(self) -> dict[str, list[str]]: + """ + Override the base implementation to prevent splitting parameter values by commas. + + For Bedrock Agent events, parameters are already properly structured and should not + be split by commas as they might contain commas as part of their actual values + (e.g., SQL queries). + """ + # Return each parameter value as a single-item list without splitting by commas + parameters = self.get("parameters") or [] + return {x["name"]: [x["value"]] for x in parameters} + @property def resolved_headers_field(self) -> dict[str, Any]: return {} diff --git a/tests/functional/event_handler/_pydantic/test_bedrock_agent.py b/tests/functional/event_handler/_pydantic/test_bedrock_agent.py index fff0f8b7d42..4ad45c25773 100644 --- a/tests/functional/event_handler/_pydantic/test_bedrock_agent.py +++ b/tests/functional/event_handler/_pydantic/test_bedrock_agent.py @@ -5,7 +5,7 @@ from typing_extensions import Annotated from aws_lambda_powertools.event_handler import BedrockAgentResolver, BedrockResponse, Response, content_types -from aws_lambda_powertools.event_handler.openapi.params import Body +from aws_lambda_powertools.event_handler.openapi.params import Body, Query from aws_lambda_powertools.utilities.data_classes import BedrockAgentEvent from tests.functional.utils import load_event @@ -343,3 +343,46 @@ def handler() -> Optional[Dict]: # THEN the OpenAPI schema must contain the "x-requireConfirmation" extension at the operation level assert schema["paths"]["/"]["get"]["x-requireConfirmation"] == "ENABLED" +def test_bedrock_agent_with_comma_parameters(): + # GIVEN a Bedrock Agent resolver + app = BedrockAgentResolver() + received_query = None + + @app.post("/sql-query", description="Run a SQL query") + def run_sql_query(query: Annotated[str, Query()]): + nonlocal received_query + received_query = query + return {"result": "Query executed"} + + # WHEN calling the event handler with a parameter containing commas + event = { + "actionGroup": "TestActionGroup", + "messageVersion": "1.0", + "sessionId": "12345678912345", + "sessionAttributes": {}, + "promptSessionAttributes": {}, + "inputText": "Run a SQL query", + "agent": { + "alias": "TEST", + "name": "test", + "version": "1", + "id": "test123", + }, + "httpMethod": "POST", + "apiPath": "/sql-query", + "parameters": [ + { + "name": "query", + "type": "string", + "value": "SELECT a.source_name, b.thing FROM table", + }, + ], + } + + result = app(event, {}) + + # THEN the parameter with commas should be correctly passed to the handler + assert received_query == "SELECT a.source_name, b.thing FROM table" + assert result["response"]["httpStatusCode"] == 200 + body = json.loads(result["response"]["responseBody"]["application/json"]["body"]) + assert body["result"] == "Query executed" diff --git a/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py b/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py index 3b10b060a8d..598bf857fed 100644 --- a/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py +++ b/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py @@ -62,3 +62,34 @@ def test_bedrock_agent_event_with_post(): assert properties[1].name == raw_properties[1]["name"] assert properties[1].type == raw_properties[1]["type"] assert properties[1].value == raw_properties[1]["value"] + +def test_bedrock_agent_event_with_comma_parameters(): + event = { + "actionGroup": "TestActionGroup", + "messageVersion": "1.0", + "sessionId": "12345678912345", + "sessionAttributes": {}, + "promptSessionAttributes": {}, + "inputText": "Run a SQL query", + "agent": { + "alias": "TEST", + "name": "test", + "version": "1", + "id": "test123", + }, + "httpMethod": "POST", + "apiPath": "/sql-query", + "parameters": [ + { + "name": "query", + "type": "string", + "value": "SELECT a.source_name, b.thing FROM table", + }, + ], + } + + parsed_event = BedrockAgentEvent(event) + + assert parsed_event.query_string_parameters["query"] == "SELECT a.source_name, b.thing FROM table" + assert parsed_event.resolved_query_string_parameters["query"] == ["SELECT a.source_name, b.thing FROM table"] + assert len(parsed_event.resolved_query_string_parameters["query"]) == 1 From 346095537a703cdd5a9f1b5022db01392afdae0b Mon Sep 17 00:00:00 2001 From: Matteo Figus Date: Fri, 6 Jun 2025 00:48:37 +0100 Subject: [PATCH 2/3] style: format test files --- tests/functional/event_handler/_pydantic/test_bedrock_agent.py | 2 ++ .../required_dependencies/test_bedrock_agent_event.py | 1 + 2 files changed, 3 insertions(+) diff --git a/tests/functional/event_handler/_pydantic/test_bedrock_agent.py b/tests/functional/event_handler/_pydantic/test_bedrock_agent.py index 4ad45c25773..322efce90f1 100644 --- a/tests/functional/event_handler/_pydantic/test_bedrock_agent.py +++ b/tests/functional/event_handler/_pydantic/test_bedrock_agent.py @@ -343,6 +343,8 @@ def handler() -> Optional[Dict]: # THEN the OpenAPI schema must contain the "x-requireConfirmation" extension at the operation level assert schema["paths"]["/"]["get"]["x-requireConfirmation"] == "ENABLED" + + def test_bedrock_agent_with_comma_parameters(): # GIVEN a Bedrock Agent resolver app = BedrockAgentResolver() diff --git a/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py b/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py index 598bf857fed..7a2e661cf45 100644 --- a/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py +++ b/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py @@ -63,6 +63,7 @@ def test_bedrock_agent_event_with_post(): assert properties[1].type == raw_properties[1]["type"] assert properties[1].value == raw_properties[1]["value"] + def test_bedrock_agent_event_with_comma_parameters(): event = { "actionGroup": "TestActionGroup", From 7fbd9fc92bfdddad60186e35edac0ab6e9f951df Mon Sep 17 00:00:00 2001 From: Matteo Figus Date: Fri, 6 Jun 2025 12:28:55 +0100 Subject: [PATCH 3/3] fix: address PR feedback for Bedrock Agent parameters with commas - Change @cached_property to @property for resolved_query_string_parameters - Simplify the functional test by directly returning the query parameter - Remove redundant unit test --- .../data_classes/bedrock_agent_event.py | 2 +- .../utilities/data_classes/common.py | 2 +- .../_pydantic/test_bedrock_agent.py | 9 ++---- .../test_bedrock_agent_event.py | 32 ------------------- 4 files changed, 4 insertions(+), 41 deletions(-) diff --git a/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py b/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py index be7bddcb64e..ad1fae31518 100644 --- a/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py +++ b/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py @@ -112,7 +112,7 @@ def query_string_parameters(self) -> dict[str, str]: parameters = self.get("parameters") or [] return {x["name"]: x["value"] for x in parameters} - @cached_property + @property def resolved_query_string_parameters(self) -> dict[str, list[str]]: """ Override the base implementation to prevent splitting parameter values by commas. diff --git a/aws_lambda_powertools/utilities/data_classes/common.py b/aws_lambda_powertools/utilities/data_classes/common.py index ecc9a2033ab..f6a6b3e5a0e 100644 --- a/aws_lambda_powertools/utilities/data_classes/common.py +++ b/aws_lambda_powertools/utilities/data_classes/common.py @@ -168,7 +168,7 @@ def query_string_parameters(self) -> dict[str, str]: def multi_value_query_string_parameters(self) -> dict[str, list[str]]: return self.get("multiValueQueryStringParameters") or {} - @cached_property + @property def resolved_query_string_parameters(self) -> dict[str, list[str]]: """ This property determines the appropriate query string parameter to be used diff --git a/tests/functional/event_handler/_pydantic/test_bedrock_agent.py b/tests/functional/event_handler/_pydantic/test_bedrock_agent.py index 322efce90f1..9c46fe24eb7 100644 --- a/tests/functional/event_handler/_pydantic/test_bedrock_agent.py +++ b/tests/functional/event_handler/_pydantic/test_bedrock_agent.py @@ -348,13 +348,10 @@ def handler() -> Optional[Dict]: def test_bedrock_agent_with_comma_parameters(): # GIVEN a Bedrock Agent resolver app = BedrockAgentResolver() - received_query = None @app.post("/sql-query", description="Run a SQL query") def run_sql_query(query: Annotated[str, Query()]): - nonlocal received_query - received_query = query - return {"result": "Query executed"} + return {"result": query} # WHEN calling the event handler with a parameter containing commas event = { @@ -384,7 +381,5 @@ def run_sql_query(query: Annotated[str, Query()]): result = app(event, {}) # THEN the parameter with commas should be correctly passed to the handler - assert received_query == "SELECT a.source_name, b.thing FROM table" - assert result["response"]["httpStatusCode"] == 200 body = json.loads(result["response"]["responseBody"]["application/json"]["body"]) - assert body["result"] == "Query executed" + assert body["result"] == "SELECT a.source_name, b.thing FROM table" diff --git a/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py b/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py index 7a2e661cf45..3b10b060a8d 100644 --- a/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py +++ b/tests/unit/data_classes/required_dependencies/test_bedrock_agent_event.py @@ -62,35 +62,3 @@ def test_bedrock_agent_event_with_post(): assert properties[1].name == raw_properties[1]["name"] assert properties[1].type == raw_properties[1]["type"] assert properties[1].value == raw_properties[1]["value"] - - -def test_bedrock_agent_event_with_comma_parameters(): - event = { - "actionGroup": "TestActionGroup", - "messageVersion": "1.0", - "sessionId": "12345678912345", - "sessionAttributes": {}, - "promptSessionAttributes": {}, - "inputText": "Run a SQL query", - "agent": { - "alias": "TEST", - "name": "test", - "version": "1", - "id": "test123", - }, - "httpMethod": "POST", - "apiPath": "/sql-query", - "parameters": [ - { - "name": "query", - "type": "string", - "value": "SELECT a.source_name, b.thing FROM table", - }, - ], - } - - parsed_event = BedrockAgentEvent(event) - - assert parsed_event.query_string_parameters["query"] == "SELECT a.source_name, b.thing FROM table" - assert parsed_event.resolved_query_string_parameters["query"] == ["SELECT a.source_name, b.thing FROM table"] - assert len(parsed_event.resolved_query_string_parameters["query"]) == 1