diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b1c34d087..735e4fd2af1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ## Bug Fixes +* **event_source:** fix decode headers with signed bytes ([#6878](https://github.com/aws-powertools/powertools-lambda-python/issues/6878)) +* **logger:** caplog working with parent Logger ([#6847](https://github.com/aws-powertools/powertools-lambda-python/issues/6847)) * **logger:** fix exception on flush without buffer ([#6794](https://github.com/aws-powertools/powertools-lambda-python/issues/6794)) ## Features @@ -14,29 +16,30 @@ ## Maintenance -* **ci:** new pre-release 3.15.2a2 ([#6865](https://github.com/aws-powertools/powertools-lambda-python/issues/6865)) +* **ci:** new pre-release 3.15.2a3 ([#6876](https://github.com/aws-powertools/powertools-lambda-python/issues/6876)) * **ci:** new pre-release 3.15.2a0 ([#6852](https://github.com/aws-powertools/powertools-lambda-python/issues/6852)) * **ci:** new pre-release 3.15.2a1 ([#6860](https://github.com/aws-powertools/powertools-lambda-python/issues/6860)) -* **ci:** new pre-release 3.15.2a3 ([#6876](https://github.com/aws-powertools/powertools-lambda-python/issues/6876)) +* **ci:** new pre-release 3.15.2a2 ([#6865](https://github.com/aws-powertools/powertools-lambda-python/issues/6865)) * **ci:** fix command to replace layer number ([#6868](https://github.com/aws-powertools/powertools-lambda-python/issues/6868)) * **deps:** bump valkey-glide from 1.3.5 to 2.0.1 ([#6871](https://github.com/aws-powertools/powertools-lambda-python/issues/6871)) * **deps:** bump pydantic-settings from 2.9.1 to 2.10.1 ([#6872](https://github.com/aws-powertools/powertools-lambda-python/issues/6872)) -* **deps:** bump datadog-lambda from 6.110.0 to 6.111.0 ([#6857](https://github.com/aws-powertools/powertools-lambda-python/issues/6857)) * **deps:** bump redis from 5.3.0 to 6.2.0 ([#6827](https://github.com/aws-powertools/powertools-lambda-python/issues/6827)) +* **deps:** bump datadog-lambda from 6.110.0 to 6.111.0 ([#6857](https://github.com/aws-powertools/powertools-lambda-python/issues/6857)) * **deps:** bump pydantic from 2.11.5 to 2.11.7 ([#6844](https://github.com/aws-powertools/powertools-lambda-python/issues/6844)) * **deps:** bump docker/setup-buildx-action from 3.10.0 to 3.11.1 ([#6823](https://github.com/aws-powertools/powertools-lambda-python/issues/6823)) -* **deps-dev:** bump boto3-stubs from 1.38.42 to 1.38.43 ([#6864](https://github.com/aws-powertools/powertools-lambda-python/issues/6864)) +* **deps-dev:** bump cfn-lint from 1.35.4 to 1.36.1 ([#6855](https://github.com/aws-powertools/powertools-lambda-python/issues/6855)) * **deps-dev:** bump pytest from 8.4.0 to 8.4.1 ([#6874](https://github.com/aws-powertools/powertools-lambda-python/issues/6874)) * **deps-dev:** bump aws-cdk from 2.1019.1 to 2.1019.2 ([#6875](https://github.com/aws-powertools/powertools-lambda-python/issues/6875)) +* **deps-dev:** bump boto3-stubs from 1.38.41 to 1.38.42 ([#6858](https://github.com/aws-powertools/powertools-lambda-python/issues/6858)) * **deps-dev:** bump sentry-sdk from 2.29.1 to 2.31.0 ([#6870](https://github.com/aws-powertools/powertools-lambda-python/issues/6870)) * **deps-dev:** bump aws-cdk from 2.1018.1 to 2.1019.1 ([#6837](https://github.com/aws-powertools/powertools-lambda-python/issues/6837)) * **deps-dev:** bump mypy from 1.16.0 to 1.16.1 ([#6828](https://github.com/aws-powertools/powertools-lambda-python/issues/6828)) * **deps-dev:** bump boto3-stubs from 1.38.43 to 1.38.44 ([#6873](https://github.com/aws-powertools/powertools-lambda-python/issues/6873)) * **deps-dev:** bump boto3-stubs from 1.38.34 to 1.38.41 ([#6845](https://github.com/aws-powertools/powertools-lambda-python/issues/6845)) * **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.200.1a0 to 2.202.0a0 ([#6846](https://github.com/aws-powertools/powertools-lambda-python/issues/6846)) -* **deps-dev:** bump cfn-lint from 1.35.4 to 1.36.1 ([#6855](https://github.com/aws-powertools/powertools-lambda-python/issues/6855)) * **deps-dev:** bump bandit from 1.8.3 to 1.8.5 ([#6856](https://github.com/aws-powertools/powertools-lambda-python/issues/6856)) -* **deps-dev:** bump boto3-stubs from 1.38.41 to 1.38.42 ([#6858](https://github.com/aws-powertools/powertools-lambda-python/issues/6858)) +* **deps-dev:** bump boto3-stubs from 1.38.42 to 1.38.43 ([#6864](https://github.com/aws-powertools/powertools-lambda-python/issues/6864)) +* **deps-dev:** bump boto3-stubs from 1.38.44 to 1.38.45 ([#6880](https://github.com/aws-powertools/powertools-lambda-python/issues/6880)) diff --git a/aws_lambda_powertools/logging/logger.py b/aws_lambda_powertools/logging/logger.py index 9f9ca1baf54..154d8ee6353 100644 --- a/aws_lambda_powertools/logging/logger.py +++ b/aws_lambda_powertools/logging/logger.py @@ -374,6 +374,9 @@ def _init_logger( if not self._is_deduplication_disabled: logger.debug("Adding filter in root logger to suppress child logger records to bubble up") for handler in logging.root.handlers: + # skip suppressing pytest's handler, allowing caplog fixture usage + if type(handler).__name__ == "LogCaptureHandler" and type(handler).__module__ == "_pytest.logging": + continue # It'll add a filter to suppress any child logger from self.service # Example: `Logger(service="order")`, where service is Order # It'll reject all loggers starting with `order` e.g. order.checkout, order.shared diff --git a/aws_lambda_powertools/shared/functions.py b/aws_lambda_powertools/shared/functions.py index 2d92af54360..16f51da1cb9 100644 --- a/aws_lambda_powertools/shared/functions.py +++ b/aws_lambda_powertools/shared/functions.py @@ -291,3 +291,19 @@ def sanitize_xray_segment_name(name: str) -> str: def get_tracer_id() -> str | None: xray_trace_id = os.getenv(constants.XRAY_TRACE_ID_ENV) return xray_trace_id.split(";")[0].replace("Root=", "") if xray_trace_id else None + + +def decode_header_bytes(byte_list): + """ + Decode a list of byte values that might be signed. + If any negative values exist, handle them as signed bytes. + Otherwise use the normal bytes construction. + """ + has_negative = any(b < 0 for b in byte_list) + + if not has_negative: + # Use normal bytes construction if all values are positive + return bytes(byte_list) + # Convert signed bytes to unsigned (0-255 range) + unsigned_bytes = [(b & 0xFF) for b in byte_list] + return bytes(unsigned_bytes) diff --git a/aws_lambda_powertools/shared/version.py b/aws_lambda_powertools/shared/version.py index 9492dfe1932..9cec0bd0045 100644 --- a/aws_lambda_powertools/shared/version.py +++ b/aws_lambda_powertools/shared/version.py @@ -1,3 +1,3 @@ """Exposes version constant to avoid circular dependencies.""" -VERSION = "3.15.2a3" +VERSION = "3.15.2a4" diff --git a/aws_lambda_powertools/utilities/data_classes/kafka_event.py b/aws_lambda_powertools/utilities/data_classes/kafka_event.py index 094bd4bed6f..53d23530cec 100644 --- a/aws_lambda_powertools/utilities/data_classes/kafka_event.py +++ b/aws_lambda_powertools/utilities/data_classes/kafka_event.py @@ -4,6 +4,7 @@ from functools import cached_property from typing import TYPE_CHECKING, Any +from aws_lambda_powertools.shared.functions import decode_header_bytes from aws_lambda_powertools.utilities.data_classes.common import CaseInsensitiveDict, DictWrapper if TYPE_CHECKING: @@ -110,7 +111,7 @@ def headers(self) -> list[dict[str, list[int]]]: @cached_property def decoded_headers(self) -> dict[str, bytes]: """Decodes the headers as a single dictionary.""" - return CaseInsensitiveDict((k, bytes(v)) for chunk in self.headers for k, v in chunk.items()) + return CaseInsensitiveDict((k, decode_header_bytes(v)) for chunk in self.headers for k, v in chunk.items()) class KafkaEventBase(DictWrapper): diff --git a/aws_lambda_powertools/utilities/kafka/consumer_records.py b/aws_lambda_powertools/utilities/kafka/consumer_records.py index 6da8f9fa1fa..1fa6afba15c 100644 --- a/aws_lambda_powertools/utilities/kafka/consumer_records.py +++ b/aws_lambda_powertools/utilities/kafka/consumer_records.py @@ -4,6 +4,7 @@ from functools import cached_property from typing import TYPE_CHECKING, Any +from aws_lambda_powertools.shared.functions import decode_header_bytes from aws_lambda_powertools.utilities.data_classes.common import CaseInsensitiveDict from aws_lambda_powertools.utilities.data_classes.kafka_event import KafkaEventBase, KafkaEventRecordBase from aws_lambda_powertools.utilities.kafka.deserializer.deserializer import get_deserializer @@ -115,7 +116,9 @@ def original_headers(self) -> list[dict[str, list[int]]]: @cached_property def headers(self) -> dict[str, bytes]: """Decodes the headers as a single dictionary.""" - return CaseInsensitiveDict((k, bytes(v)) for chunk in self.original_headers for k, v in chunk.items()) + return CaseInsensitiveDict( + (k, decode_header_bytes(v)) for chunk in self.original_headers for k, v in chunk.items() + ) class ConsumerRecords(KafkaEventBase): diff --git a/aws_lambda_powertools/utilities/parser/models/kafka.py b/aws_lambda_powertools/utilities/parser/models/kafka.py index 717d47ff26c..b22c3a2613a 100644 --- a/aws_lambda_powertools/utilities/parser/models/kafka.py +++ b/aws_lambda_powertools/utilities/parser/models/kafka.py @@ -3,7 +3,7 @@ from pydantic import BaseModel, field_validator -from aws_lambda_powertools.shared.functions import base64_decode, bytes_to_string +from aws_lambda_powertools.shared.functions import base64_decode, bytes_to_string, decode_header_bytes SERVERS_DELIMITER = "," @@ -28,9 +28,7 @@ class KafkaRecordModel(BaseModel): # key is optional; only decode if not None @field_validator("key", mode="before") def decode_key(cls, value): - if value is not None: - return base64_decode(value) - return value + return base64_decode(value) if value is not None else value @field_validator("value", mode="before") def data_base64_decode(cls, value): @@ -41,7 +39,7 @@ def data_base64_decode(cls, value): def decode_headers_list(cls, value): for header in value: for key, values in header.items(): - header[key] = bytes(values) + header[key] = decode_header_bytes(values) return value @@ -51,7 +49,7 @@ class KafkaBaseEventModel(BaseModel): @field_validator("bootstrapServers", mode="before") def split_servers(cls, value): - return None if not value else value.split(SERVERS_DELIMITER) + return value.split(SERVERS_DELIMITER) if value else None class KafkaSelfManagedEventModel(KafkaBaseEventModel): diff --git a/provenance/3.15.2a4/multiple.intoto.jsonl b/provenance/3.15.2a4/multiple.intoto.jsonl new file mode 100644 index 00000000000..94a745dc30d --- /dev/null +++ b/provenance/3.15.2a4/multiple.intoto.jsonl @@ -0,0 +1 @@ +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"certificate":{"rawBytes":"MIIHZjCCBuygAwIBAgIUSqSJdw5r7NjQc3ZbM6jEyvjziE8wCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUwNjI3MDgwNzU1WhcNMjUwNjI3MDgxNzU1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiphMR8EBKIOl2XZY5jXiM9dXDxJTndHY01GZ33ixPHwuNw01MbG/rcRkxrnEDO+29PHLPH8A9aHAPfh5v+2vXqOCBgswggYHMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUlbVyc2l7Hsko59oeGqCKAaV0tqkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wgYQGA1UdEQEB/wR6MHiGdmh0dHBzOi8vZ2l0aHViLmNvbS9zbHNhLWZyYW1ld29yay9zbHNhLWdpdGh1Yi1nZW5lcmF0b3IvLmdpdGh1Yi93b3JrZmxvd3MvZ2VuZXJhdG9yX2dlbmVyaWNfc2xzYTMueW1sQHJlZnMvdGFncy92Mi4xLjAwOQYKKwYBBAGDvzABAQQraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTAWBgorBgEEAYO/MAECBAhzY2hlZHVsZTA2BgorBgEEAYO/MAEDBCg3ZDk4MWZmOGYxNWUwZmJkNjYyNzg1N2YyNDllMGEzODU0OGMxZGUyMBkGCisGAQQBg78wAQQEC1ByZS1SZWxlYXNlMDUGCisGAQQBg78wAQUEJ2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbjAgBgorBgEEAYO/MAEGBBJyZWZzL2hlYWRzL2RldmVsb3AwOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMIGGBgorBgEEAYO/MAEJBHgMdmh0dHBzOi8vZ2l0aHViLmNvbS9zbHNhLWZyYW1ld29yay9zbHNhLWdpdGh1Yi1nZW5lcmF0b3IvLmdpdGh1Yi93b3JrZmxvd3MvZ2VuZXJhdG9yX2dlbmVyaWNfc2xzYTMueW1sQHJlZnMvdGFncy92Mi4xLjAwOAYKKwYBBAGDvzABCgQqDChmN2RkOGM1NGMyMDY3YmFmYzEyY2E3YTU1NTk1ZDVlZTliNzUyMDRhMB0GCisGAQQBg78wAQsEDwwNZ2l0aHViLWhvc3RlZDBKBgorBgEEAYO/MAEMBDwMOmh0dHBzOi8vZ2l0aHViLmNvbS9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24wOAYKKwYBBAGDvzABDQQqDCg3ZDk4MWZmOGYxNWUwZmJkNjYyNzg1N2YyNDllMGEzODU0OGMxZGUyMCIGCisGAQQBg78wAQ4EFAwScmVmcy9oZWFkcy9kZXZlbG9wMBkGCisGAQQBg78wAQ8ECwwJMjIxOTE5Mzc5MDEGCisGAQQBg78wARAEIwwhaHR0cHM6Ly9naXRodWIuY29tL2F3cy1wb3dlcnRvb2xzMBkGCisGAQQBg78wAREECwwJMTI5MTI3NjM4MH8GCisGAQQBg78wARIEcQxvaHR0cHM6Ly9naXRodWIuY29tL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi8uZ2l0aHViL3dvcmtmbG93cy9wcmUtcmVsZWFzZS55bWxAcmVmcy9oZWFkcy9kZXZlbG9wMDgGCisGAQQBg78wARMEKgwoN2Q5ODFmZjhmMTVlMGZiZDY2Mjc4NTdmMjQ5ZTBhMzg1NDhjMWRlMjAYBgorBgEEAYO/MAEUBAoMCHNjaGVkdWxlMG4GCisGAQQBg78wARUEYAxeaHR0cHM6Ly9naXRodWIuY29tL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9hY3Rpb25zL3J1bnMvMTU5MjEzMjU3NjEvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABl7Bty9sAAAQDAEcwRQIhALLzkVZa/UTxgk+UgotPRPNfP/Amd7z+/anDF+K581LkAiByqvTz0F7OEuB4DccSPxrQcXcykNZ1EPzV7rkrJVr5rDAKBggqhkjOPQQDAwNoADBlAjEAg0RuAs1g/b5+lIdbG0HEQ4GiNscSSgCU4yzMnLKd3uevkQt9TqmgDw2aO56rLJLIAjB52yvOj50lP+oZZHyViHv0WwLgY2ngzq+6HGzM4GFueW00fS+2i0whKvbHArHqUW4="}, "tlogEntries":[{"logIndex":"253130795", "logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion":{"kind":"dsse", "version":"0.0.1"}, "integratedTime":"1751011675", "inclusionPromise":{"signedEntryTimestamp":"MEQCIDJWYzUbNgSCTe7GV6LYl1eco6x7hFhqt2FjrtxJxb+NAiA5bCZY3OmJtwr3Uh+thVjUe+7SSxkQWjGN4pzvkHcIGQ=="}, "inclusionProof":{"logIndex":"131226533", "rootHash":"Z8ltLsNq1RbB5M+jR3hAHYPYwCGiQGHF7PQ9y3SeZO8=", "treeSize":"131226539", "hashes":["vylor26fZZWnHIyAW+ezV+J4nH7P7wBILRGfx/zeNpQ=", "ATaFS+kj8JmeyzhmD9bMYR58OZk8l4f9e7atIG48V6Q=", "cQM3qaXO8byDkIrazosAzJfRnfJnENxXkiskavXM+FE=", "CuWAPW9Mp6qVPMOg9bvL0WixTp3bJ5Exuq2m7pOsrNg=", "bs6tT5AK1jJ47nLu1TiUO4QeWPOHbaf+KpSTl6WXqzI=", "nZxOHwZdqBMShzi1a6kZfP1sZXgmoe8gp0KZENEG7aI=", "R8WTi+SLGk66IIlzHawq1Gl0K9snu1wKLR9PkD/X03M=", "e/RrnZrP6PkRbMkPeBWXKIAisveXbZu9goJzWGOgJt8=", "hfKaXPkc99HVcV7gI+fYsF2u5cY/8wCl8UFvWli41b4=", "JVA5LbC8a5m+N4kki71KJ9wxAXuY88hLYboGVMa88a0=", "XJO8ZWHz8zcKvJTrUno6un9YmKZk+JMTZa5m4c5+yHc=", "3hc2zTS9zpqGSF7lS1RUOnd1t3dwMDL1hVPmJvyeKNY=", "hZIQL5oJvm8nz5HypifZ+F1KS+ka0z4zS5VZqAH4rSo=", "kfUq42hz8nJRB20ieKzxcdO9zCMAEilw4dkTY2jJD+E=", "jV8Bj9STv71W0t2yRin169EhZWag7dBJ4vBuLH3ULBQ=", "63G35ZWA2JgOE3bXu0oKhro3tiR4IDPH1IgMp21/pjk=", "mta5fH/gFwxJ/0fT8yGpn3sFCY0G1RY555Iflm0LInM=", "7v8qPHNDLerpduaMx06eb/MwgoQwczTn/cYGKX/9wZ4="], "checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n131226539\nZ8ltLsNq1RbB5M+jR3hAHYPYwCGiQGHF7PQ9y3SeZO8=\n\n— rekor.sigstore.dev wNI9ajBGAiEAoow2OrkTPnDIobUAKxSM46QiJrP9Nkp7yJJTOK0wjdsCIQD9TltgWbQMreh9Zz0Tc79JXYnVtbVDnQeYhuxpyI0c+w==\n"}}, "canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiYjVjNWE1NTJmMDc3NThhOTJkOWNjNjEzNjc3Mjg1N2M5ZTMxYjZmNDg4ZTgyZTJkYzVkZjNkZmJjYTliYTc2MCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjI4YWI0MDhiMGNiMTEwOTM0NjQxMzEwYmYwM2QwYjBmOTZhNWM0ODg3MGMxMWEzMTU0NTg3ZmQwYWY4YThhOGIifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVVQ0lRRFlhV0poMUJka082Y2F2S3kvZFZJWXlOVTQzWWllOWFGQjlBRXJ3WnV3dEFJZ2ZkOGpDMlJoZ3JRdGxGNnZoNkJrdmpXRGtSeEQ2QlpOZGV4UTdmTHlwWjA9IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VoYWFrTkRRblY1WjBGM1NVSkJaMGxWVTNGVFNtUjNOWEkzVG1wUll6TmFZazAyYWtWNWRtcDZhVVU0ZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwVmQwNXFTVE5OUkdkM1RucFZNVmRvWTA1TmFsVjNUbXBKTTAxRVozaE9lbFV4VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVnBjR2hOVWpoRlFrdEpUMnd5V0ZwWk5XcFlhVTA1WkZoRWVFcFVibVJJV1RBeFIxb0tNek5wZUZCSWQzVk9kekF4VFdKSEwzSmpVbXQ0Y201RlJFOHJNamxRU0V4UVNEaEJPV0ZJUVZCbWFEVjJLekoyV0hGUFEwSm5jM2RuWjFsSVRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVnNZbFo1Q21NeWJEZEljMnR2TlRsdlpVZHhRMHRCWVZZd2RIRnJkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMmRaVVVkQk1WVmtSVkZGUWk5M1VqWk5TR2xIWkcxb01HUklRbnBQYVRoMldqSnNNR0ZJVm1sTWJVNTJZbE01ZW1KSVRtaE1WMXA1V1ZjeGJBcGtNamw1WVhrNWVtSklUbWhNVjJSd1pFZG9NVmxwTVc1YVZ6VnNZMjFHTUdJelNYWk1iV1J3WkVkb01WbHBPVE5pTTBweVdtMTRkbVF6VFhaYU1sWjFDbHBZU21oa1J6bDVXREprYkdKdFZubGhWMDVtWXpKNGVsbFVUWFZsVnpGelVVaEtiRnB1VFhaa1IwWnVZM2s1TWsxcE5IaE1ha0YzVDFGWlMwdDNXVUlLUWtGSFJIWjZRVUpCVVZGeVlVaFNNR05JVFRaTWVUa3dZakowYkdKcE5XaFpNMUp3WWpJMWVreHRaSEJrUjJneFdXNVdlbHBZU21waU1qVXdXbGMxTUFwTWJVNTJZbFJCVjBKbmIzSkNaMFZGUVZsUEwwMUJSVU5DUVdoNldUSm9iRnBJVm5OYVZFRXlRbWR2Y2tKblJVVkJXVTh2VFVGRlJFSkRaek5hUkdzMENrMVhXbTFQUjFsNFRsZFZkMXB0U210T2FsbDVUbnBuTVU0eVdYbE9SR3hzVFVkRmVrOUVWVEJQUjAxNFdrZFZlVTFDYTBkRGFYTkhRVkZSUW1jM09IY0tRVkZSUlVNeFFubGFVekZUV2xkNGJGbFlUbXhOUkZWSFEybHpSMEZSVVVKbk56aDNRVkZWUlVveVJqTmplVEYzWWpOa2JHTnVVblppTW5oNlRETkNkZ3BrTWxaNVpFYzVkbUpJVFhSaVIwWjBXVzFTYUV4WVFqVmtSMmgyWW1wQlowSm5iM0pDWjBWRlFWbFBMMDFCUlVkQ1FrcDVXbGRhZWt3eWFHeFpWMUo2Q2t3eVVteGtiVlp6WWpOQmQwOTNXVXRMZDFsQ1FrRkhSSFo2UVVKRFFWRjBSRU4wYjJSSVVuZGplbTkyVEROU2RtRXlWblZNYlVacVpFZHNkbUp1VFhVS1dqSnNNR0ZJVm1sa1dFNXNZMjFPZG1KdVVteGlibEYxV1RJNWRFMUpSMGRDWjI5eVFtZEZSVUZaVHk5TlFVVktRa2huVFdSdGFEQmtTRUo2VDJrNGRncGFNbXd3WVVoV2FVeHRUblppVXpsNllraE9hRXhYV25sWlZ6RnNaREk1ZVdGNU9YcGlTRTVvVEZka2NHUkhhREZaYVRGdVdsYzFiR050UmpCaU0wbDJDa3h0WkhCa1IyZ3hXV2s1TTJJelNuSmFiWGgyWkROTmRsb3lWblZhV0Vwb1pFYzVlVmd5Wkd4aWJWWjVZVmRPWm1NeWVIcFpWRTExWlZjeGMxRklTbXdLV201TmRtUkhSbTVqZVRreVRXazBlRXhxUVhkUFFWbExTM2RaUWtKQlIwUjJla0ZDUTJkUmNVUkRhRzFPTWxKclQwZE5NVTVIVFhsTlJGa3pXVzFHYlFwWmVrVjVXVEpGTTFsVVZURk9WR3N4V2tSV2JGcFViR2xPZWxWNVRVUlNhRTFDTUVkRGFYTkhRVkZSUW1jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacENreFhhSFpqTTFKc1drUkNTMEpuYjNKQ1owVkZRVmxQTDAxQlJVMUNSSGROVDIxb01HUklRbnBQYVRoMldqSnNNR0ZJVm1sTWJVNTJZbE01YUdRelRYUUtZMGM1TTFwWVNqQmlNamx6WTNrNWQySXpaR3hqYmxKMllqSjRla3hYZUdoaVYwcHJXVk14ZDJWWVVtOWlNalIzVDBGWlMwdDNXVUpDUVVkRWRucEJRZ3BFVVZGeFJFTm5NMXBFYXpSTlYxcHRUMGRaZUU1WFZYZGFiVXByVG1wWmVVNTZaekZPTWxsNVRrUnNiRTFIUlhwUFJGVXdUMGROZUZwSFZYbE5RMGxIQ2tOcGMwZEJVVkZDWnpjNGQwRlJORVZHUVhkVFkyMVdiV041T1c5YVYwWnJZM2s1YTFwWVdteGlSemwzVFVKclIwTnBjMGRCVVZGQ1p6YzRkMEZST0VVS1EzZDNTazFxU1hoUFZFVTFUWHBqTlUxRVJVZERhWE5IUVZGUlFtYzNPSGRCVWtGRlNYZDNhR0ZJVWpCalNFMDJUSGs1Ym1GWVVtOWtWMGwxV1RJNWRBcE1Na1l6WTNreGQySXpaR3hqYmxKMllqSjRlazFDYTBkRGFYTkhRVkZSUW1jM09IZEJVa1ZGUTNkM1NrMVVTVFZOVkVrelRtcE5ORTFJT0VkRGFYTkhDa0ZSVVVKbk56aDNRVkpKUldOUmVIWmhTRkl3WTBoTk5reDVPVzVoV0ZKdlpGZEpkVmt5T1hSTU1rWXpZM2t4ZDJJelpHeGpibEoyWWpKNGVrd3pRbllLWkRKV2VXUkhPWFppU0UxMFlrZEdkRmx0VW1oTVdFSTFaRWRvZG1KcE9IVmFNbXd3WVVoV2FVd3paSFpqYlhSdFlrYzVNMk41T1hkamJWVjBZMjFXY3dwYVYwWjZXbE0xTldKWGVFRmpiVlp0WTNrNWIxcFhSbXRqZVRscldsaGFiR0pIT1hkTlJHZEhRMmx6UjBGUlVVSm5OemgzUVZKTlJVdG5kMjlPTWxFMUNrOUVSbTFhYW1odFRWUldiRTFIV21sYVJGa3lUV3BqTkU1VVpHMU5hbEUxV2xSQ2FFMTZaekZPUkdocVRWZFNiRTFxUVZsQ1oyOXlRbWRGUlVGWlR5OEtUVUZGVlVKQmIwMURTRTVxWVVkV2EyUlhlR3hOUnpSSFEybHpSMEZSVVVKbk56aDNRVkpWUlZsQmVHVmhTRkl3WTBoTk5reDVPVzVoV0ZKdlpGZEpkUXBaTWpsMFRESkdNMk41TVhkaU0yUnNZMjVTZG1JeWVIcE1NMEoyWkRKV2VXUkhPWFppU0UxMFlrZEdkRmx0VW1oTVdFSTFaRWRvZG1KcE9XaFpNMUp3Q21JeU5YcE1NMG94WW01TmRrMVVWVFZOYWtWNlRXcFZNMDVxUlhaWldGSXdXbGN4ZDJSSVRYWk5WRUZYUW1kdmNrSm5SVVZCV1U4dlRVRkZWMEpCWjAwS1FtNUNNVmx0ZUhCWmVrTkNhV2RaUzB0M1dVSkNRVWhYWlZGSlJVRm5VamhDU0c5QlpVRkNNa0ZPTURsTlIzSkhlSGhGZVZsNGEyVklTbXh1VG5kTGFRcFRiRFkwTTJwNWRDODBaVXRqYjBGMlMyVTJUMEZCUVVKc04wSjBlVGx6UVVGQlVVUkJSV04zVWxGSmFFRk1USHByVmxwaEwxVlVlR2RySzFWbmIzUlFDbEpRVG1aUUwwRnRaRGQ2S3k5aGJrUkdLMHMxT0RGTWEwRnBRbmx4ZGxSNk1FWTNUMFYxUWpSRVkyTlRVSGh5VVdOWVkzbHJUbG94UlZCNlZqZHlhM0lLU2xaeU5YSkVRVXRDWjJkeGFHdHFUMUJSVVVSQmQwNXZRVVJDYkVGcVJVRm5NRkoxUVhNeFp5OWlOU3RzU1dSaVJ6QklSVkUwUjJsT2MyTlRVMmREVlFvMGVYcE5ia3hMWkROMVpYWnJVWFE1VkhGdFowUjNNbUZQTlRaeVRFcE1TVUZxUWpVeWVYWlBhalV3YkZBcmIxcGFTSGxXYVVoMk1GZDNUR2RaTW01bkNucHhLelpJUjNwTk5FZEdkV1ZYTURCbVV5c3lhVEIzYUV0MllraEJja2h4VlZjMFBRb3RMUzB0TFVWT1JDQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENnPT0ifV19fQ=="}]}, "dsseEnvelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInN1YmplY3QiOlt7Im5hbWUiOiIuL2F3c19sYW1iZGFfcG93ZXJ0b29scy0zLjE1LjJhNC1weTMtbm9uZS1hbnkud2hsIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImIxNGRmNDBhN2M2OWI3MTFiNjdhYjI3MGJhNmU3ZWIzMDAzYTc5Y2UwNzZkNTU0ZTkxODgwODE2MGM2YTdhYjkifX0seyJuYW1lIjoiLi9hd3NfbGFtYmRhX3Bvd2VydG9vbHMtMy4xNS4yYTQudGFyLmd6IiwiZGlnZXN0Ijp7InNoYTI1NiI6ImY2NDhkMjFiN2RlNGU5YTA5Y2MzNGZhMDRkMzVlNDRlMDE5YTU2YTM4Y2VlYmQxYmE3NmM0OGM0YzQ3NjFkMTIifX1dLCJwcmVkaWNhdGUiOnsiYnVpbGRlciI6eyJpZCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9zbHNhLWZyYW1ld29yay9zbHNhLWdpdGh1Yi1nZW5lcmF0b3IvLmdpdGh1Yi93b3JrZmxvd3MvZ2VuZXJhdG9yX2dlbmVyaWNfc2xzYTMueW1sQHJlZnMvdGFncy92Mi4xLjAifSwiYnVpbGRUeXBlIjoiaHR0cHM6Ly9naXRodWIuY29tL3Nsc2EtZnJhbWV3b3JrL3Nsc2EtZ2l0aHViLWdlbmVyYXRvci9nZW5lcmljQHYxIiwiaW52b2NhdGlvbiI6eyJjb25maWdTb3VyY2UiOnsidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob25AcmVmcy9oZWFkcy9kZXZlbG9wIiwiZGlnZXN0Ijp7InNoYTEiOiI3ZDk4MWZmOGYxNWUwZmJkNjYyNzg1N2YyNDllMGEzODU0OGMxZGUyIn0sImVudHJ5UG9pbnQiOiIuZ2l0aHViL3dvcmtmbG93cy9wcmUtcmVsZWFzZS55bWwifSwicGFyYW1ldGVycyI6eyJ2YXJzIjp7fX0sImVudmlyb25tZW50Ijp7ImdpdGh1Yl9hY3RvciI6ImxlYW5kcm9kYW1hc2NlbmEiLCJnaXRodWJfYWN0b3JfaWQiOiI0Mjk1MTczIiwiZ2l0aHViX2Jhc2VfcmVmIjoiIiwiZ2l0aHViX2V2ZW50X25hbWUiOiJzY2hlZHVsZSIsImdpdGh1Yl9ldmVudF9wYXlsb2FkIjp7ImVudGVycHJpc2UiOnsiYXZhdGFyX3VybCI6Imh0dHBzOi8vYXZhdGFycy5naXRodWJ1c2VyY29udGVudC5jb20vYi8xMjkwP3Y9NCIsImNyZWF0ZWRfYXQiOiIyMDE5LTExLTEzVDE4OjA1OjQxWiIsImRlc2NyaXB0aW9uIjoiIiwiaHRtbF91cmwiOiJodHRwczovL2dpdGh1Yi5jb20vZW50ZXJwcmlzZXMvYW1hem9uIiwiaWQiOjEyOTAsIm5hbWUiOiJBbWF6b24iLCJub2RlX2lkIjoiTURFd09rVnVkR1Z5Y0hKcGMyVXhNamt3Iiwic2x1ZyI6ImFtYXpvbiIsInVwZGF0ZWRfYXQiOiIyMDI1LTA1LTAxVDE2OjI1OjUyWiIsIndlYnNpdGVfdXJsIjoiaHR0cHM6Ly93d3cuYW1hem9uLmNvbS8ifSwib3JnYW5pemF0aW9uIjp7ImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3UvMTI5MTI3NjM4P3Y9NCIsImRlc2NyaXB0aW9uIjoiIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vb3Jncy9hd3MtcG93ZXJ0b29scy9ldmVudHMiLCJob29rc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvYXdzLXBvd2VydG9vbHMvaG9va3MiLCJpZCI6MTI5MTI3NjM4LCJpc3N1ZXNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzL2F3cy1wb3dlcnRvb2xzL2lzc3VlcyIsImxvZ2luIjoiYXdzLXBvd2VydG9vbHMiLCJtZW1iZXJzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vb3Jncy9hd3MtcG93ZXJ0b29scy9tZW1iZXJzey9tZW1iZXJ9Iiwibm9kZV9pZCI6Ik9fa2dET0I3SlUxZyIsInB1YmxpY19tZW1iZXJzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vb3Jncy9hd3MtcG93ZXJ0b29scy9wdWJsaWNfbWVtYmVyc3svbWVtYmVyfSIsInJlcG9zX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vb3Jncy9hd3MtcG93ZXJ0b29scy9yZXBvcyIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vb3Jncy9hd3MtcG93ZXJ0b29scyJ9LCJyZXBvc2l0b3J5Ijp7ImFsbG93X2ZvcmtpbmciOnRydWUsImFyY2hpdmVfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24ve2FyY2hpdmVfZm9ybWF0fXsvcmVmfSIsImFyY2hpdmVkIjpmYWxzZSwiYXNzaWduZWVzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL2Fzc2lnbmVlc3svdXNlcn0iLCJibG9ic191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9naXQvYmxvYnN7L3NoYX0iLCJicmFuY2hlc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9icmFuY2hlc3svYnJhbmNofSIsImNsb25lX3VybCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24uZ2l0IiwiY29sbGFib3JhdG9yc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9jb2xsYWJvcmF0b3Jzey9jb2xsYWJvcmF0b3J9IiwiY29tbWVudHNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vY29tbWVudHN7L251bWJlcn0iLCJjb21taXRzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL2NvbW1pdHN7L3NoYX0iLCJjb21wYXJlX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL2NvbXBhcmUve2Jhc2V9Li4ue2hlYWR9IiwiY29udGVudHNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vY29udGVudHMveytwYXRofSIsImNvbnRyaWJ1dG9yc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9jb250cmlidXRvcnMiLCJjcmVhdGVkX2F0IjoiMjAxOS0xMS0xNVQxMjoyNjoxMloiLCJjdXN0b21fcHJvcGVydGllcyI6eyJzdGFnaW5nIjoiZmFsc2UiLCJ2aXNpYmlsaXR5LWFsbG93LWludGVybmFsIjoiZmFsc2UiLCJ2aXNpYmlsaXR5LWFsbG93LXByaXZhdGUiOiJmYWxzZSIsInZpc2liaWxpdHktYWxsb3ctcHVibGljIjoidHJ1ZSJ9LCJkZWZhdWx0X2JyYW5jaCI6ImRldmVsb3AiLCJkZXBsb3ltZW50c191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9kZXBsb3ltZW50cyIsImRlc2NyaXB0aW9uIjoiQSBkZXZlbG9wZXIgdG9vbGtpdCB0byBpbXBsZW1lbnQgU2VydmVybGVzcyBiZXN0IHByYWN0aWNlcyBhbmQgaW5jcmVhc2UgZGV2ZWxvcGVyIHZlbG9jaXR5LiIsImRpc2FibGVkIjpmYWxzZSwiZG93bmxvYWRzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL2Rvd25sb2FkcyIsImV2ZW50c191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9ldmVudHMiLCJmb3JrIjpmYWxzZSwiZm9ya3MiOjQzNCwiZm9ya3NfY291bnQiOjQzNCwiZm9ya3NfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vZm9ya3MiLCJmdWxsX25hbWUiOiJhd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24iLCJnaXRfY29tbWl0c191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9naXQvY29tbWl0c3svc2hhfSIsImdpdF9yZWZzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL2dpdC9yZWZzey9zaGF9IiwiZ2l0X3RhZ3NfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vZ2l0L3RhZ3N7L3NoYX0iLCJnaXRfdXJsIjoiZ2l0Oi8vZ2l0aHViLmNvbS9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24uZ2l0IiwiaGFzX2Rpc2N1c3Npb25zIjp0cnVlLCJoYXNfZG93bmxvYWRzIjp0cnVlLCJoYXNfaXNzdWVzIjp0cnVlLCJoYXNfcGFnZXMiOmZhbHNlLCJoYXNfcHJvamVjdHMiOnRydWUsImhhc193aWtpIjpmYWxzZSwiaG9tZXBhZ2UiOiJodHRwczovL2RvY3MucG93ZXJ0b29scy5hd3MuZGV2L2xhbWJkYS9weXRob24vbGF0ZXN0LyIsImhvb2tzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL2hvb2tzIiwiaHRtbF91cmwiOiJodHRwczovL2dpdGh1Yi5jb20vYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uIiwiaWQiOjIyMTkxOTM3OSwiaXNfdGVtcGxhdGUiOmZhbHNlLCJpc3N1ZV9jb21tZW50X3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL2lzc3Vlcy9jb21tZW50c3svbnVtYmVyfSIsImlzc3VlX2V2ZW50c191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9pc3N1ZXMvZXZlbnRzey9udW1iZXJ9IiwiaXNzdWVzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL2lzc3Vlc3svbnVtYmVyfSIsImtleXNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24va2V5c3sva2V5X2lkfSIsImxhYmVsc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9sYWJlbHN7L25hbWV9IiwibGFuZ3VhZ2UiOiJQeXRob24iLCJsYW5ndWFnZXNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vbGFuZ3VhZ2VzIiwibGljZW5zZSI6eyJrZXkiOiJtaXQtMCIsIm5hbWUiOiJNSVQgTm8gQXR0cmlidXRpb24iLCJub2RlX2lkIjoiTURjNlRHbGpaVzV6WlRReCIsInNwZHhfaWQiOiJNSVQtMCIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vbGljZW5zZXMvbWl0LTAifSwibWVyZ2VzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL21lcmdlcyIsIm1pbGVzdG9uZXNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vbWlsZXN0b25lc3svbnVtYmVyfSIsIm1pcnJvcl91cmwiOm51bGwsIm5hbWUiOiJwb3dlcnRvb2xzLWxhbWJkYS1weXRob24iLCJub2RlX2lkIjoiTURFd09sSmxjRzl6YVhSdmNua3lNakU1TVRrek56az0iLCJub3RpZmljYXRpb25zX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL25vdGlmaWNhdGlvbnN7P3NpbmNlLGFsbCxwYXJ0aWNpcGF0aW5nfSIsIm9wZW5faXNzdWVzIjo1OCwib3Blbl9pc3N1ZXNfY291bnQiOjU4LCJvd25lciI6eyJhdmF0YXJfdXJsIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzEyOTEyNzYzOD92PTQiLCJldmVudHNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9hd3MtcG93ZXJ0b29scy9ldmVudHN7L3ByaXZhY3l9IiwiZm9sbG93ZXJzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvYXdzLXBvd2VydG9vbHMvZm9sbG93ZXJzIiwiZm9sbG93aW5nX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvYXdzLXBvd2VydG9vbHMvZm9sbG93aW5ney9vdGhlcl91c2VyfSIsImdpc3RzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvYXdzLXBvd2VydG9vbHMvZ2lzdHN7L2dpc3RfaWR9IiwiZ3JhdmF0YXJfaWQiOiIiLCJodG1sX3VybCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9hd3MtcG93ZXJ0b29scyIsImlkIjoxMjkxMjc2MzgsImxvZ2luIjoiYXdzLXBvd2VydG9vbHMiLCJub2RlX2lkIjoiT19rZ0RPQjdKVTFnIiwib3JnYW5pemF0aW9uc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL2F3cy1wb3dlcnRvb2xzL29yZ3MiLCJyZWNlaXZlZF9ldmVudHNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9hd3MtcG93ZXJ0b29scy9yZWNlaXZlZF9ldmVudHMiLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL2F3cy1wb3dlcnRvb2xzL3JlcG9zIiwic2l0ZV9hZG1pbiI6ZmFsc2UsInN0YXJyZWRfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9hd3MtcG93ZXJ0b29scy9zdGFycmVkey9vd25lcn17L3JlcG99Iiwic3Vic2NyaXB0aW9uc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL2F3cy1wb3dlcnRvb2xzL3N1YnNjcmlwdGlvbnMiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9hd3MtcG93ZXJ0b29scyIsInVzZXJfdmlld190eXBlIjoicHVibGljIn0sInByaXZhdGUiOmZhbHNlLCJwdWxsc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9wdWxsc3svbnVtYmVyfSIsInB1c2hlZF9hdCI6IjIwMjUtMDYtMjZUMjM6NTc6MzdaIiwicmVsZWFzZXNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vcmVsZWFzZXN7L2lkfSIsInNpemUiOjEzMjEyMiwic3NoX3VybCI6ImdpdEBnaXRodWIuY29tOmF3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi5naXQiLCJzdGFyZ2F6ZXJzX2NvdW50IjozMDg2LCJzdGFyZ2F6ZXJzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL3N0YXJnYXplcnMiLCJzdGF0dXNlc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi9zdGF0dXNlcy97c2hhfSIsInN1YnNjcmliZXJzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL3N1YnNjcmliZXJzIiwic3Vic2NyaXB0aW9uX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uL3N1YnNjcmlwdGlvbiIsInN2bl91cmwiOiJodHRwczovL2dpdGh1Yi5jb20vYXdzLXBvd2VydG9vbHMvcG93ZXJ0b29scy1sYW1iZGEtcHl0aG9uIiwidGFnc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2F3cy1wb3dlcnRvb2xzL3Bvd2VydG9vbHMtbGFtYmRhLXB5dGhvbi90YWdzIiwidGVhbXNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vdGVhbXMiLCJ0b3BpY3MiOlsiYXdzIiwiYXdzLWxhbWJkYSIsImxhbWJkYSIsInB5dGhvbiIsInNlcnZlcmxlc3MiXSwidHJlZXNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24vZ2l0L3RyZWVzey9zaGF9IiwidXBkYXRlZF9hdCI6IjIwMjUtMDYtMjZUMjM6NTQ6NTBaIiwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob24iLCJ2aXNpYmlsaXR5IjoicHVibGljIiwid2F0Y2hlcnMiOjMwODYsIndhdGNoZXJzX2NvdW50IjozMDg2LCJ3ZWJfY29tbWl0X3NpZ25vZmZfcmVxdWlyZWQiOnRydWV9LCJzY2hlZHVsZSI6IjAgOCAqICogMS01Iiwid29ya2Zsb3ciOiIuZ2l0aHViL3dvcmtmbG93cy9wcmUtcmVsZWFzZS55bWwifSwiZ2l0aHViX2hlYWRfcmVmIjoiIiwiZ2l0aHViX3JlZiI6InJlZnMvaGVhZHMvZGV2ZWxvcCIsImdpdGh1Yl9yZWZfdHlwZSI6ImJyYW5jaCIsImdpdGh1Yl9yZXBvc2l0b3J5X2lkIjoiMjIxOTE5Mzc5IiwiZ2l0aHViX3JlcG9zaXRvcnlfb3duZXIiOiJhd3MtcG93ZXJ0b29scyIsImdpdGh1Yl9yZXBvc2l0b3J5X293bmVyX2lkIjoiMTI5MTI3NjM4IiwiZ2l0aHViX3J1bl9hdHRlbXB0IjoiMSIsImdpdGh1Yl9ydW5faWQiOiIxNTkyMTMyNTc2MSIsImdpdGh1Yl9ydW5fbnVtYmVyIjoiMjcyIiwiZ2l0aHViX3NoYTEiOiI3ZDk4MWZmOGYxNWUwZmJkNjYyNzg1N2YyNDllMGEzODU0OGMxZGUyIn19LCJtZXRhZGF0YSI6eyJidWlsZEludm9jYXRpb25JRCI6IjE1OTIxMzI1NzYxLTEiLCJjb21wbGV0ZW5lc3MiOnsicGFyYW1ldGVycyI6dHJ1ZSwiZW52aXJvbm1lbnQiOmZhbHNlLCJtYXRlcmlhbHMiOmZhbHNlfSwicmVwcm9kdWNpYmxlIjpmYWxzZX0sIm1hdGVyaWFscyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9hd3MtcG93ZXJ0b29scy9wb3dlcnRvb2xzLWxhbWJkYS1weXRob25AcmVmcy9oZWFkcy9kZXZlbG9wIiwiZGlnZXN0Ijp7InNoYTEiOiI3ZDk4MWZmOGYxNWUwZmJkNjYyNzg1N2YyNDllMGEzODU0OGMxZGUyIn19XX19", "payloadType":"application/vnd.in-toto+json", "signatures":[{"sig":"MEUCIQDYaWJh1BdkO6cavKy/dVIYyNU43Yie9aFB9AErwZuwtAIgfd8jC2RhgrQtlF6vh6BkvjWDkRxD6BZNdexQ7fLypZ0="}]}} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index c215eccb2bc..0d60dd62b36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "aws_lambda_powertools" -version = "3.15.2a3" +version = "3.15.2a4" description = "Powertools for AWS Lambda (Python) is a developer toolkit to implement Serverless best practices and increase developer velocity." authors = ["Amazon Web Services"] include = ["aws_lambda_powertools/py.typed", "THIRD-PARTY-LICENSES"] diff --git a/tests/events/kafkaEventMsk.json b/tests/events/kafkaEventMsk.json index 6c27594460c..a91980b8ecc 100644 --- a/tests/events/kafkaEventMsk.json +++ b/tests/events/kafkaEventMsk.json @@ -104,6 +104,28 @@ "dataFormat": "AVRO", "schemaId": "1234" } + }, + { + "topic":"mymessage-with-unsigned", + "partition":0, + "offset":15, + "timestamp":1545084650987, + "timestampType":"CREATE_TIME", + "key": null, + "value":"eyJrZXkiOiJ2YWx1ZSJ9", + "headers":[ + { + "headerKey":[104, 101, 108, 108, 111, 45, 119, 111, 114, 108, 100, 45, -61, -85] + } + ], + "valueSchemaMetadata": { + "dataFormat": "AVRO", + "schemaId": "1234" + }, + "keySchemaMetadata": { + "dataFormat": "AVRO", + "schemaId": "1234" + } } ] } diff --git a/tests/functional/logger/required_dependencies/test_logger.py b/tests/functional/logger/required_dependencies/test_logger.py index e799dce9b60..2a960582e3f 100644 --- a/tests/functional/logger/required_dependencies/test_logger.py +++ b/tests/functional/logger/required_dependencies/test_logger.py @@ -15,6 +15,7 @@ from typing import TYPE_CHECKING, Any import pytest +from _pytest.logging import LogCaptureHandler from aws_lambda_powertools import Logger from aws_lambda_powertools.logging import correlation_paths @@ -1556,3 +1557,24 @@ def handler(event, context): # THEN we must be able to inject context log = capture_logging_output(stdout) assert request_id == log["correlation_id"] + + +def test_non_preconfigured_logger_with_caplog(caplog, service_name): + caplog.set_level("INFO") + logger = Logger(service=service_name) + logger.info("testing, testing...") + pytest_handler_existence = any(isinstance(item, LogCaptureHandler) for item in logger._logger.root.handlers) + + assert pytest_handler_existence is True + assert len(caplog.records) == 1 + assert caplog.records[0].message == "testing, testing..." + + +def test_child_logger_with_caplog(caplog): + caplog.set_level("INFO") + logger = Logger(child=True) + logger.info("testing, testing...") + pytest_handler_existence = any(isinstance(item, LogCaptureHandler) for item in logger._logger.root.handlers) + + assert len(caplog.records) == 1 + assert pytest_handler_existence is True diff --git a/tests/unit/data_classes/required_dependencies/test_kafka_event.py b/tests/unit/data_classes/required_dependencies/test_kafka_event.py index fc7bbf12a1a..98e933ab94a 100644 --- a/tests/unit/data_classes/required_dependencies/test_kafka_event.py +++ b/tests/unit/data_classes/required_dependencies/test_kafka_event.py @@ -21,7 +21,7 @@ def test_kafka_msk_event(): assert parsed_event.decoded_bootstrap_servers == bootstrap_servers_list records = list(parsed_event.records) - assert len(records) == 3 + assert len(records) == 4 record = records[0] raw_record = raw_event["records"]["mytopic-0"][0] assert record.topic == raw_record["topic"] @@ -40,9 +40,10 @@ def test_kafka_msk_event(): assert record.value_schema_metadata.schema_id == raw_record["valueSchemaMetadata"]["schemaId"] assert parsed_event.record == records[0] - for i in range(1, 3): + for i in range(1, 4): record = records[i] assert record.key is None + assert record.decoded_headers is not None def test_kafka_self_managed_event(): @@ -90,5 +91,5 @@ def test_kafka_record_property_with_stopiteration_error(): # WHEN calling record property thrice # THEN raise StopIteration with pytest.raises(StopIteration): - for _ in range(4): + for _ in range(5): assert parsed_event.record.topic is not None diff --git a/tests/unit/parser/_pydantic/test_kafka.py b/tests/unit/parser/_pydantic/test_kafka.py index 779756831a9..4a49bac1fce 100644 --- a/tests/unit/parser/_pydantic/test_kafka.py +++ b/tests/unit/parser/_pydantic/test_kafka.py @@ -17,7 +17,7 @@ def test_kafka_msk_event_with_envelope(): ) for i in range(3): assert parsed_event[i].key == "value" - assert len(parsed_event) == 3 + assert len(parsed_event) == 4 def test_kafka_self_managed_event_with_envelope(): @@ -70,7 +70,7 @@ def test_kafka_msk_event(): assert parsed_event.eventSourceArn == raw_event["eventSourceArn"] records = list(parsed_event.records["mytopic-0"]) - assert len(records) == 3 + assert len(records) == 4 record: KafkaRecordModel = records[0] raw_record = raw_event["records"]["mytopic-0"][0] assert record.topic == raw_record["topic"] @@ -88,6 +88,6 @@ def test_kafka_msk_event(): assert record.keySchemaMetadata.schemaId == "1234" assert record.valueSchemaMetadata.dataFormat == "AVRO" assert record.valueSchemaMetadata.schemaId == "1234" - for i in range(1, 3): + for i in range(1, 4): record: KafkaRecordModel = records[i] assert record.key is None