From 47bf7364dbebb89acecddf653dc09e8f3970dc7a Mon Sep 17 00:00:00 2001 From: Mark Kuhn Date: Mon, 15 Aug 2022 10:25:57 -0700 Subject: [PATCH] add function to strip nonascii and stringify numbers (#84) * Add function to strip non-ascii characters and stringify numbers Co-authored-by: Mark Kuhn --- .../logger/metrics_context.py | 20 ++++---- .../serializers/log_serializer.py | 4 ++ tests/serializer/test_log_serializer.py | 50 +++++++++++++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/aws_embedded_metrics/logger/metrics_context.py b/aws_embedded_metrics/logger/metrics_context.py index 47ed1dd..3f46894 100644 --- a/aws_embedded_metrics/logger/metrics_context.py +++ b/aws_embedded_metrics/logger/metrics_context.py @@ -58,30 +58,30 @@ def put_metric(self, key: str, value: float, unit: str = None) -> None: self.metrics[key] = Metric(value, unit) @staticmethod - def validate_dimension_set(dimensions: Dict[str, str]) -> None: + def validate_dimension_set(dimension_set: Dict[str, str]) -> None: """ Validates dimension set length is not more than MAX_DIMENSION_SET_SIZE """ - if len(dimensions) > MAX_DIMENSION_SET_SIZE: + if len(dimension_set) > MAX_DIMENSION_SET_SIZE: raise DimensionSetExceededError( f"Maximum number of dimensions per dimension set allowed are {MAX_DIMENSION_SET_SIZE}") - def put_dimensions(self, dimensions: Dict[str, str]) -> None: + def put_dimensions(self, dimension_set: Dict[str, str]) -> None: """ Adds dimensions to the context. ``` context.put_dimensions({ "k1": "v1", "k2": "v2" }) ``` """ - if dimensions is None: + if dimension_set is None: # TODO add ability to define failure strategy return - self.validate_dimension_set(dimensions) + self.validate_dimension_set(dimension_set) - self.dimensions.append(dimensions) + self.dimensions.append(dimension_set) - def set_dimensions(self, dimensionSets: List[Dict[str, str]]) -> None: + def set_dimensions(self, dimension_sets: List[Dict[str, str]]) -> None: """ Overwrite all dimensions. ``` @@ -92,10 +92,10 @@ def set_dimensions(self, dimensionSets: List[Dict[str, str]]) -> None: """ self.should_use_default_dimensions = False - for dimensionSet in dimensionSets: - self.validate_dimension_set(dimensionSet) + for dimension_set in dimension_sets: + self.validate_dimension_set(dimension_set) - self.dimensions = dimensionSets + self.dimensions = dimension_sets def set_default_dimensions(self, default_dimensions: Dict) -> None: """ diff --git a/aws_embedded_metrics/serializers/log_serializer.py b/aws_embedded_metrics/serializers/log_serializer.py index 156e868..09e93fa 100644 --- a/aws_embedded_metrics/serializers/log_serializer.py +++ b/aws_embedded_metrics/serializers/log_serializer.py @@ -31,6 +31,10 @@ def serialize(context: MetricsContext) -> List[str]: dimensions_properties: Dict[str, str] = {} for dimension_set in context.get_dimensions(): + + # Stringify numerical values and strip non-ascii characters from dimension set values + dimension_set = {k: str(v).encode('ascii', 'ignore').decode('ascii') for k, v in dimension_set.items()} + keys = list(dimension_set.keys()) if len(keys) > MAX_DIMENSION_SET_SIZE: err_msg = (f"Maximum number of dimensions per dimension set allowed are {MAX_DIMENSION_SET_SIZE}. " diff --git a/tests/serializer/test_log_serializer.py b/tests/serializer/test_log_serializer.py index e892922..650c4d0 100644 --- a/tests/serializer/test_log_serializer.py +++ b/tests/serializer/test_log_serializer.py @@ -32,6 +32,56 @@ def test_serialize_dimensions(): assert_json_equality(result_json, expected) +def test_serialize_dimensions_with_numeric_value(): + # arrange + expected_key = fake.word() + value = fake.random.randrange(0, 100) + expected_value = str(value) + + dimension = {} + dimension[expected_key] = value + + expected_dimension = {} + expected_dimension[expected_key] = expected_value + + expected = {**get_empty_payload(), **expected_dimension} + expected["_aws"]["CloudWatchMetrics"][0]["Dimensions"].append([expected_key]) + + context = get_context() + context.put_dimensions(dimension) + + # act + result_json = serializer.serialize(context)[0] + + # assert + assert_json_equality(result_json, expected) + + +def test_serialize_dimenions_with_non_ascii_values(): + # arrange + expected_key = fake.word() + value = "asciiĆ©šŸ¤”" + expected_value = "ascii" + + dimension = {} + dimension[expected_key] = value + + expected_dimension = {} + expected_dimension[expected_key] = expected_value + + expected = {**get_empty_payload(), **expected_dimension} + expected["_aws"]["CloudWatchMetrics"][0]["Dimensions"].append([expected_key]) + + context = get_context() + context.put_dimensions(dimension) + + # act + result_json = serializer.serialize(context)[0] + + # assert + assert_json_equality(result_json, expected) + + def test_serialize_properties(): # arrange expected_key = fake.word()