From e8fa3b285b97e4d74f1292bc8f662b05a0ca7587 Mon Sep 17 00:00:00 2001 From: Neil Ramsay Date: Mon, 17 Apr 2023 10:43:33 +1200 Subject: [PATCH 1/7] feat: Add __str__ to Data Classes base DictWrapper Add in a __str__ method to the base class used by all Data Classes. This provides a dictionary string primarily for debugging purposes. To construct the dictionary a helper method _str_helper recursively works through `properties` in each instance of a Data Class. Some Data Classes have sensitive properties, such as credentials, which can be excluded by setting the `_sensitive_properties` attribute (list of strings). These properties will have their value replaced with "[SENSITIVE]", which allows a developer to see a value arrived, but was hidden as it is sensitive. --- .../utilities/data_classes/common.py | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/utilities/data_classes/common.py b/aws_lambda_powertools/utilities/data_classes/common.py index fa0d479af8a..d9b75475ff9 100644 --- a/aws_lambda_powertools/utilities/data_classes/common.py +++ b/aws_lambda_powertools/utilities/data_classes/common.py @@ -1,7 +1,7 @@ import base64 import json from collections.abc import Mapping -from typing import Any, Dict, Iterator, Optional +from typing import Any, Dict, Iterator, List, Optional from aws_lambda_powertools.shared.headers_serializer import BaseHeadersSerializer @@ -28,6 +28,47 @@ def __iter__(self) -> Iterator: def __len__(self) -> int: return len(self._data) + def __str__(self) -> str: + return str(self._str_helper()) + + def _str_helper(self) -> Dict[str, Any]: + """ + Recursively get a Dictionary of DictWrapper properties primarily + for use by __str__ for debugging purposes. + + Will remove "raw_event" properties, and any defined by the Data Class + `_sensitive_properties` list field. + This should be used in case where secrets, such as access keys, are + stored in the Data Class but should not be logged out. + """ + properties = self._properties() + sensitive_properties = ["raw_event"] + if hasattr(self, "_sensitive_properties"): + sensitive_properties.extend(self._sensitive_properties) # pyright: ignore + + result: Dict[str, Any] = {} + for property_key in properties: + if property_key in sensitive_properties: + result[property_key] = "[SENSITIVE]" + else: + try: + property_value = getattr(self, property_key) + result[property_key] = property_value + + if issubclass(property_value.__class__, DictWrapper): + result[property_key] = property_value._str_helper() + elif isinstance(property_value, list): + for seq, item in enumerate(property_value): + if issubclass(item.__class__, DictWrapper): + result[property_key][seq] = item._str_helper() + except Exception as e: + result[property_key] = f"[EXCEPTION {type(e)}]" + + return result + + def _properties(self) -> List[str]: + return [p for p in dir(self.__class__) if isinstance(getattr(self.__class__, p), property)] + def get(self, key: str, default: Optional[Any] = None) -> Optional[Any]: return self._data.get(key, default) From d9d1bc7f23983de2c25e66e6c48182078d35e36a Mon Sep 17 00:00:00 2001 From: Neil Ramsay Date: Mon, 17 Apr 2023 12:25:20 +1200 Subject: [PATCH 2/7] docs: Data Classes str() Update Data Classes documentation to mention Data Classes can be inspected with str(), and include example. --- docs/utilities/data_classes.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/utilities/data_classes.md b/docs/utilities/data_classes.md index 169133788ad..ba5fa46c4d2 100644 --- a/docs/utilities/data_classes.md +++ b/docs/utilities/data_classes.md @@ -12,6 +12,7 @@ Event Source Data Classes utility provides classes self-describing Lambda event * Type hinting and code completion for common event types * Helper functions for decoding/deserializing nested fields * Docstrings for fields contained in event schemas +* Implement str() to recursively inspect contents **Background** @@ -52,6 +53,22 @@ Same example as above, but using the `event_source` decorator if 'helloworld' in event.path and event.http_method == 'GET': do_something_with(event.body, user) ``` + +Log Data Event for Troubleshooting + +=== "app.py" + + ```python hl_lines="4 8" + from aws_lambda_powertools.utilities.data_classes import event_source, APIGatewayProxyEvent + from aws_lambda_powertools.logging.logger import Logger + + logger = Logger(service="hello_logs", level="DEBUG") + + @event_source(data_class=APIGatewayProxyEvent) + def lambda_handler(event: APIGatewayProxyEvent, context): + logger.debug(event) + ``` + **Autocomplete with self-documented properties and methods** ![Utilities Data Classes](../media/utilities_data_classes.png) From 9c904f07220b0a9c2dfc513dafb563dbfd61cf34 Mon Sep 17 00:00:00 2001 From: Neil Ramsay Date: Mon, 17 Apr 2023 12:25:34 +1200 Subject: [PATCH 3/7] test: Data Classes str() Add tests for DictWrapper str() functionality, including Data Classes with: - no properties - single property - sensitive property - recursive properties (Data Classes inside Data Classes) - exceptions reading properties - exceptions when transforming a list with DictWrapper subclasses inside --- tests/functional/test_data_classes.py | 168 ++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py index 5e2aad30e8e..ed2798660af 100644 --- a/tests/functional/test_data_classes.py +++ b/tests/functional/test_data_classes.py @@ -126,6 +126,174 @@ class DataClassSample(DictWrapper): assert event_source.items() == data.items() +def test_dict_wrapper_str_no_property(): + """ + Checks that the _properties function returns + only the "raw_event", and the resulting string + notes it as sensitive. + """ + + class DataClassSample(DictWrapper): + attribute = None + + def function(self): + pass + + event_source = DataClassSample({}) + assert event_source._properties() == ["raw_event"] + assert str(event_source) == "{'raw_event': '[SENSITIVE]'}" + + +def test_dict_wrapper_str_single_property(): + """ + Checks that the _properties function returns + the defined property "data_property", and + resulting string includes the property value. + """ + + class DataClassSample(DictWrapper): + attribute = None + + def function(self): + pass + + @property + def data_property(self): + return "value" + + event_source = DataClassSample({}) + assert event_source._properties() == ["data_property", "raw_event"] + assert event_source._str_helper() == {"data_property": "value", "raw_event": "[SENSITIVE]"} + assert str(event_source) == "{'data_property': 'value', 'raw_event': '[SENSITIVE]'}" + + +def test_dict_wrapper_str_property_exception(): + """ + Check the recursive _str_helper function handles + exceptions that may occur when accessing properties + """ + + class DataClassSample(DictWrapper): + attribute = None + + def function(self): + pass + + @property + def data_property(self): + raise Exception() + + event_source = DataClassSample({}) + assert event_source._str_helper() == { + "data_property": "[EXCEPTION ]", + "raw_event": "[SENSITIVE]", + } + assert str(event_source) == "{'data_property': \"[EXCEPTION ]\", 'raw_event': '[SENSITIVE]'}" + + +def test_dict_wrapper_str_property_list_exception(): + """ + Check that _str_helper properly handles exceptions + that occur when recursively working through items + in a list property. + """ + + class BrokenDataClass(DictWrapper): + @property + def broken_data_property(self): + raise Exception() + + class DataClassSample(DictWrapper): + attribute = None + + def function(self): + pass + + @property + def data_property(self): + return ["string", 0, 0.0, BrokenDataClass({})] + + event_source = DataClassSample({}) + assert event_source._properties() == ["data_property", "raw_event"] + assert event_source._str_helper() == { + "data_property": [ + "string", + 0, + 0.0, + {"broken_data_property": "[EXCEPTION ]", "raw_event": "[SENSITIVE]"}, + ], + "raw_event": "[SENSITIVE]", + } + event_str = ( + "{'data_property': ['string', 0, 0.0, {'broken_data_property': " + + "\"[EXCEPTION ]\", 'raw_event': '[SENSITIVE]'}], 'raw_event': '[SENSITIVE]'}" + ) + assert str(event_source) == event_str + + +def test_dict_wrapper_str_recursive_property(): + """ + Check that the _str_helper function recursively + handles Data Classes within Data Classes + """ + + class DataClassTerminal(DictWrapper): + attribute = None + + def function(self): + pass + + @property + def terminal_property(self): + return "end-recursion" + + class DataClassRecursive(DictWrapper): + attribute = None + + def function(self): + pass + + @property + def data_property(self): + return DataClassTerminal({}) + + event_source = DataClassRecursive({}) + assert event_source._properties() == ["data_property", "raw_event"] + assert event_source._str_helper() == { + "data_property": {"raw_event": "[SENSITIVE]", "terminal_property": "end-recursion"}, + "raw_event": "[SENSITIVE]", + } + assert ( + str(event_source) + == "{'data_property': {'raw_event': '[SENSITIVE]', 'terminal_property': 'end-recursion'}," + + " 'raw_event': '[SENSITIVE]'}" + ) + + +def test_dict_wrapper_sensitive_properties_property(): + """ + Checks that the _str_helper function correctly + handles _sensitive_properties + """ + + class DataClassSample(DictWrapper): + attribute = None + + def function(self): + pass + + _sensitive_properties = ["data_property"] + + @property + def data_property(self): + return "value" + + event_source = DataClassSample({}) + assert event_source._properties() == ["data_property", "raw_event"] + assert event_source._str_helper() == {"data_property": "[SENSITIVE]", "raw_event": "[SENSITIVE]"} + assert str(event_source) == "{'data_property': '[SENSITIVE]', 'raw_event': '[SENSITIVE]'}" + + def test_cloud_watch_dashboard_event(): event = CloudWatchDashboardCustomWidgetEvent(load_event("cloudWatchDashboardEvent.json")) assert event.describe is False From 6362732cec84d10a4cb5d03e57c3825e66275253 Mon Sep 17 00:00:00 2001 From: Neil Ramsay Date: Mon, 17 Apr 2023 10:50:33 +1200 Subject: [PATCH 4/7] feat: Add str sensitive for CodePipeline Data Add _sensitive_properties for CodePipelineArtifactCredentials to prevent logging out the secret_access_key and session_token properties. --- .../utilities/data_classes/code_pipeline_job_event.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py b/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py index a4139ebbe68..c502aacb090 100644 --- a/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py +++ b/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py @@ -80,6 +80,8 @@ def location(self) -> CodePipelineLocation: class CodePipelineArtifactCredentials(DictWrapper): + _sensitive_properties = ["secret_access_key", "session_token"] + @property def access_key_id(self) -> str: return self["accessKeyId"] From 5b1aa2b9f0ecf55d7b749628c92cf7edc8c08760 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Mon, 24 Apr 2023 22:23:28 +0100 Subject: [PATCH 5/7] feat(debug): docs + small changes --- .../utilities/data_classes/common.py | 6 ++- docs/utilities/data_classes.md | 26 +++++++++- examples/event_sources/src/debugging.py | 9 ++++ .../event_sources/src/debugging_event.json | 34 ++++++++++++ .../event_sources/src/debugging_output.json | 50 ++++++++++++++++++ tests/functional/test_data_classes.py | 52 +++++-------------- 6 files changed, 136 insertions(+), 41 deletions(-) create mode 100644 examples/event_sources/src/debugging.py create mode 100644 examples/event_sources/src/debugging_event.json create mode 100644 examples/event_sources/src/debugging_output.json diff --git a/aws_lambda_powertools/utilities/data_classes/common.py b/aws_lambda_powertools/utilities/data_classes/common.py index d9b75475ff9..5c1fea14731 100644 --- a/aws_lambda_powertools/utilities/data_classes/common.py +++ b/aws_lambda_powertools/utilities/data_classes/common.py @@ -55,14 +55,16 @@ def _str_helper(self) -> Dict[str, Any]: property_value = getattr(self, property_key) result[property_key] = property_value + # Checks whether the class is a subclass of the parent class to perform a recursive operation. if issubclass(property_value.__class__, DictWrapper): result[property_key] = property_value._str_helper() + # Checks if the key is a list and if it is a subclass of the parent class elif isinstance(property_value, list): for seq, item in enumerate(property_value): if issubclass(item.__class__, DictWrapper): result[property_key][seq] = item._str_helper() - except Exception as e: - result[property_key] = f"[EXCEPTION {type(e)}]" + except Exception: + result[property_key] = "[Cannot be deserialized]" return result diff --git a/docs/utilities/data_classes.md b/docs/utilities/data_classes.md index ba5fa46c4d2..a859a9f237b 100644 --- a/docs/utilities/data_classes.md +++ b/docs/utilities/data_classes.md @@ -12,7 +12,6 @@ Event Source Data Classes utility provides classes self-describing Lambda event * Type hinting and code completion for common event types * Helper functions for decoding/deserializing nested fields * Docstrings for fields contained in event schemas -* Implement str() to recursively inspect contents **Background** @@ -1121,3 +1120,28 @@ This example is based on the AWS Blog post [Introducing Amazon S3 Object Lambda for record in event.records: do_something_with(record.body) ``` + +## Advanced + +### Debugging + +Alternatively, you can print out the fields to obtain more information. All classes come with a `__str__` method that generates a dictionary string which can be quite useful for debugging. + +However, certain events may contain sensitive fields such as `secret_access_key` and `session_token`, which are labeled as sensitive to prevent any accidental disclosure of confidential information. + +!!! warning "Some fields, like JSON dictionaries, can't be processed and will display as "[Cannot be deserialized]"" + +=== "debugging.py" + ```python hl_lines="5" + --8<-- "examples/event_sources/src/debugging.py" + ``` + +=== "debugging_event.json" + ```json hl_lines="28 29" + --8<-- "examples/event_sources/src/debugging_event.json" + ``` +=== "debugging_output.json" + ```json hl_lines="16 17 18" + --8<-- "examples/event_sources/src/debugging_output.json" + ``` + ``` diff --git a/examples/event_sources/src/debugging.py b/examples/event_sources/src/debugging.py new file mode 100644 index 00000000000..a03bf823885 --- /dev/null +++ b/examples/event_sources/src/debugging.py @@ -0,0 +1,9 @@ +from aws_lambda_powertools.utilities.data_classes import ( + CodePipelineJobEvent, + event_source, +) + + +@event_source(data_class=CodePipelineJobEvent) +def lambda_handler(event, context): + print(event) diff --git a/examples/event_sources/src/debugging_event.json b/examples/event_sources/src/debugging_event.json new file mode 100644 index 00000000000..a95c3d57e86 --- /dev/null +++ b/examples/event_sources/src/debugging_event.json @@ -0,0 +1,34 @@ +{ + "CodePipeline.job": { + "id": "11111111-abcd-1111-abcd-111111abcdef", + "accountId": "111111111111", + "data": { + "actionConfiguration": { + "configuration": { + "FunctionName": "MyLambdaFunctionForAWSCodePipeline", + "UserParameters": "some-input-such-as-a-URL" + } + }, + "inputArtifacts": [ + { + "name": "ArtifactName", + "revision": null, + "location": { + "type": "S3", + "s3Location": { + "bucketName": "the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890", + "objectKey": "the name of the application, for example CodePipelineDemoApplication.zip" + } + } + } + ], + "outputArtifacts": [], + "artifactCredentials": { + "accessKeyId": "AKIAIOSFODNN7EXAMPLE", + "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + "sessionToken": "MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w0BAQUFADCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wHhcNMTEwNDI1MjA0NTIxWhcNMTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ21uUSfwfEvySWtC2XADZ4nB+BLYgVIk60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9TrDHudUZg3qX4waLG5M43q7Wgc/MbQITxOUSQv7c7ugFFDzQGBzZswY6786m86gpEIbb3OhjZnzcvQAaRHhdlQWIMm2nrAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4nUhVVxYUntneD9+h8Mg9q6q+auNKyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0FkbFFBjvSfpJIlJ00zbhNYS5f6GuoEDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTbNYiytVbZPQUQ5Yaxu2jXnimvw3rrszlaEXAMPLE=" + }, + "continuationToken": "A continuation token if continuing job" + } + } + } diff --git a/examples/event_sources/src/debugging_output.json b/examples/event_sources/src/debugging_output.json new file mode 100644 index 00000000000..f13d6380afe --- /dev/null +++ b/examples/event_sources/src/debugging_output.json @@ -0,0 +1,50 @@ +{ + "account_id":"111111111111", + "data":{ + "action_configuration":{ + "configuration":{ + "decoded_user_parameters":"[Cannot be deserialized]", + "function_name":"MyLambdaFunctionForAWSCodePipeline", + "raw_event":"[SENSITIVE]", + "user_parameters":"some-input-such-as-a-URL" + }, + "raw_event":"[SENSITIVE]" + }, + "artifact_credentials":{ + "access_key_id":"AKIAIOSFODNN7EXAMPLE", + "expiration_time":"None", + "raw_event":"[SENSITIVE]", + "secret_access_key":"[SENSITIVE]", + "session_token":"[SENSITIVE]" + }, + "continuation_token":"A continuation token if continuing job", + "encryption_key":"None", + "input_artifacts":[ + { + "location":{ + "get_type":"S3", + "raw_event":"[SENSITIVE]", + "s3_location":{ + "bucket_name":"the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890", + "key":"the name of the application, for example CodePipelineDemoApplication.zip", + "object_key":"the name of the application, for example CodePipelineDemoApplication.zip", + "raw_event":"[SENSITIVE]" + } + }, + "name":"ArtifactName", + "raw_event":"[SENSITIVE]", + "revision":"None" + } + ], + "output_artifacts":[ + + ], + "raw_event":"[SENSITIVE]" + }, + "decoded_user_parameters":"[Cannot be deserialized]", + "get_id":"11111111-abcd-1111-abcd-111111abcdef", + "input_bucket_name":"the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890", + "input_object_key":"the name of the application, for example CodePipelineDemoApplication.zip", + "raw_event":"[SENSITIVE]", + "user_parameters":"some-input-such-as-a-URL" + } diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py index ed2798660af..8d20d856c9a 100644 --- a/tests/functional/test_data_classes.py +++ b/tests/functional/test_data_classes.py @@ -136,11 +136,10 @@ def test_dict_wrapper_str_no_property(): class DataClassSample(DictWrapper): attribute = None - def function(self): + def function(self) -> None: pass event_source = DataClassSample({}) - assert event_source._properties() == ["raw_event"] assert str(event_source) == "{'raw_event': '[SENSITIVE]'}" @@ -154,16 +153,14 @@ def test_dict_wrapper_str_single_property(): class DataClassSample(DictWrapper): attribute = None - def function(self): + def function(self) -> None: pass @property - def data_property(self): + def data_property(self) -> str: return "value" event_source = DataClassSample({}) - assert event_source._properties() == ["data_property", "raw_event"] - assert event_source._str_helper() == {"data_property": "value", "raw_event": "[SENSITIVE]"} assert str(event_source) == "{'data_property': 'value', 'raw_event': '[SENSITIVE]'}" @@ -176,7 +173,7 @@ def test_dict_wrapper_str_property_exception(): class DataClassSample(DictWrapper): attribute = None - def function(self): + def function(self) -> None: pass @property @@ -184,11 +181,7 @@ def data_property(self): raise Exception() event_source = DataClassSample({}) - assert event_source._str_helper() == { - "data_property": "[EXCEPTION ]", - "raw_event": "[SENSITIVE]", - } - assert str(event_source) == "{'data_property': \"[EXCEPTION ]\", 'raw_event': '[SENSITIVE]'}" + assert str(event_source) == "{'data_property': '[Cannot be deserialized]', 'raw_event': '[SENSITIVE]'}" def test_dict_wrapper_str_property_list_exception(): @@ -206,27 +199,17 @@ def broken_data_property(self): class DataClassSample(DictWrapper): attribute = None - def function(self): + def function(self) -> None: pass @property - def data_property(self): + def data_property(self) -> list: return ["string", 0, 0.0, BrokenDataClass({})] event_source = DataClassSample({}) - assert event_source._properties() == ["data_property", "raw_event"] - assert event_source._str_helper() == { - "data_property": [ - "string", - 0, - 0.0, - {"broken_data_property": "[EXCEPTION ]", "raw_event": "[SENSITIVE]"}, - ], - "raw_event": "[SENSITIVE]", - } event_str = ( "{'data_property': ['string', 0, 0.0, {'broken_data_property': " - + "\"[EXCEPTION ]\", 'raw_event': '[SENSITIVE]'}], 'raw_event': '[SENSITIVE]'}" + + "'[Cannot be deserialized]', 'raw_event': '[SENSITIVE]'}], 'raw_event': '[SENSITIVE]'}" ) assert str(event_source) == event_str @@ -240,29 +223,24 @@ def test_dict_wrapper_str_recursive_property(): class DataClassTerminal(DictWrapper): attribute = None - def function(self): + def function(self) -> None: pass @property - def terminal_property(self): + def terminal_property(self) -> str: return "end-recursion" class DataClassRecursive(DictWrapper): attribute = None - def function(self): + def function(self) -> None: pass @property - def data_property(self): + def data_property(self) -> DataClassTerminal: return DataClassTerminal({}) event_source = DataClassRecursive({}) - assert event_source._properties() == ["data_property", "raw_event"] - assert event_source._str_helper() == { - "data_property": {"raw_event": "[SENSITIVE]", "terminal_property": "end-recursion"}, - "raw_event": "[SENSITIVE]", - } assert ( str(event_source) == "{'data_property': {'raw_event': '[SENSITIVE]', 'terminal_property': 'end-recursion'}," @@ -279,18 +257,16 @@ def test_dict_wrapper_sensitive_properties_property(): class DataClassSample(DictWrapper): attribute = None - def function(self): + def function(self) -> None: pass _sensitive_properties = ["data_property"] @property - def data_property(self): + def data_property(self) -> str: return "value" event_source = DataClassSample({}) - assert event_source._properties() == ["data_property", "raw_event"] - assert event_source._str_helper() == {"data_property": "[SENSITIVE]", "raw_event": "[SENSITIVE]"} assert str(event_source) == "{'data_property': '[SENSITIVE]', 'raw_event': '[SENSITIVE]'}" From 16e69b929d41e8be684923edb7c129db9e6bae25 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Tue, 25 Apr 2023 11:47:21 +0100 Subject: [PATCH 6/7] feat(debug): line editing --- docs/utilities/data_classes.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/utilities/data_classes.md b/docs/utilities/data_classes.md index a859a9f237b..ad5b4bb8d33 100644 --- a/docs/utilities/data_classes.md +++ b/docs/utilities/data_classes.md @@ -1127,12 +1127,12 @@ This example is based on the AWS Blog post [Introducing Amazon S3 Object Lambda Alternatively, you can print out the fields to obtain more information. All classes come with a `__str__` method that generates a dictionary string which can be quite useful for debugging. -However, certain events may contain sensitive fields such as `secret_access_key` and `session_token`, which are labeled as sensitive to prevent any accidental disclosure of confidential information. +However, certain events may contain sensitive fields such as `secret_access_key` and `session_token`, which are labeled as `[SENSITIVE]` to prevent any accidental disclosure of confidential information. -!!! warning "Some fields, like JSON dictionaries, can't be processed and will display as "[Cannot be deserialized]"" +!!! warning "Some fields contain user-supplied data, which can be plain text or JSON. If deserialization of this field fails, it will appear as [Cannot be deserialized]" === "debugging.py" - ```python hl_lines="5" + ```python hl_lines="9" --8<-- "examples/event_sources/src/debugging.py" ``` From 659e97bf09c237cf31752636397e89af1acdc0d7 Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Wed, 26 Apr 2023 11:17:16 +0200 Subject: [PATCH 7/7] chore: line editing and optimize for mobile Signed-off-by: Heitor Lessa --- docs/utilities/data_classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utilities/data_classes.md b/docs/utilities/data_classes.md index ad5b4bb8d33..04779ccf0f5 100644 --- a/docs/utilities/data_classes.md +++ b/docs/utilities/data_classes.md @@ -1129,7 +1129,7 @@ Alternatively, you can print out the fields to obtain more information. All clas However, certain events may contain sensitive fields such as `secret_access_key` and `session_token`, which are labeled as `[SENSITIVE]` to prevent any accidental disclosure of confidential information. -!!! warning "Some fields contain user-supplied data, which can be plain text or JSON. If deserialization of this field fails, it will appear as [Cannot be deserialized]" +!!! warning "If we fail to deserialize a field value (e.g., JSON), they will appear as `[Cannot be deserialized]`" === "debugging.py" ```python hl_lines="9"