From ddc24fcaceade35ae560866efa535b37249a314d Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Tue, 12 Apr 2022 18:53:58 -0700 Subject: [PATCH 1/3] fix(docs): Extract parameters code examples Changes: - Extract code examples - Run isort and black - Fix python examples - Update line highlights - Add make task Related to: - #1064 --- Makefile | 7 + .../parameters/app_config_provider.py | 15 + .../parameters/create_your_own_s3_provider.py | 53 +++ .../parameters/custom_caching_parameters.py | 16 + .../utilities/parameters/dynamodb_provider.py | 8 + .../dynamodb_provider_customization.py | 12 + .../dynamodb_provider_get_multiple.py | 13 + .../parameters/dynamodb_provider_local.py | 6 + .../parameters/fetching_app_config.py | 10 + .../utilities/parameters/fetching_secrets.py | 6 + .../parameters/force_fetch_parameters.py | 6 + .../parameters/parameters_custom_config.py | 12 + .../parameters/parameters_custom_session.py | 12 + .../parameters/parameters_sdk_args.py | 8 + .../parameters/parameters_transform.py | 5 + .../parameters/parameters_transform_auto.py | 7 + .../parameters_transform_providers.py | 11 + ...ters_transform_raise_on_transform_error.py | 19 + .../parameters/recursively_parameters.py | 12 + .../utilities/parameters/secrets_provider.py | 11 + .../utilities/parameters/ssm_provider.py | 16 + .../parameters/ssm_provider_get_options.py | 9 + .../parameters/testing_parameters_fixture.py | 16 + .../parameters/testing_parameters_index.py | 7 + .../parameters/testing_parameters_mock.py | 13 + .../parameters/testing_parameters_tests.py | 10 + docs/utilities/parameters.md | 341 +++--------------- 27 files changed, 369 insertions(+), 292 deletions(-) create mode 100644 docs/examples/utilities/parameters/app_config_provider.py create mode 100644 docs/examples/utilities/parameters/create_your_own_s3_provider.py create mode 100644 docs/examples/utilities/parameters/custom_caching_parameters.py create mode 100644 docs/examples/utilities/parameters/dynamodb_provider.py create mode 100644 docs/examples/utilities/parameters/dynamodb_provider_customization.py create mode 100644 docs/examples/utilities/parameters/dynamodb_provider_get_multiple.py create mode 100644 docs/examples/utilities/parameters/dynamodb_provider_local.py create mode 100644 docs/examples/utilities/parameters/fetching_app_config.py create mode 100644 docs/examples/utilities/parameters/fetching_secrets.py create mode 100644 docs/examples/utilities/parameters/force_fetch_parameters.py create mode 100644 docs/examples/utilities/parameters/parameters_custom_config.py create mode 100644 docs/examples/utilities/parameters/parameters_custom_session.py create mode 100644 docs/examples/utilities/parameters/parameters_sdk_args.py create mode 100644 docs/examples/utilities/parameters/parameters_transform.py create mode 100644 docs/examples/utilities/parameters/parameters_transform_auto.py create mode 100644 docs/examples/utilities/parameters/parameters_transform_providers.py create mode 100644 docs/examples/utilities/parameters/parameters_transform_raise_on_transform_error.py create mode 100644 docs/examples/utilities/parameters/recursively_parameters.py create mode 100644 docs/examples/utilities/parameters/secrets_provider.py create mode 100644 docs/examples/utilities/parameters/ssm_provider.py create mode 100644 docs/examples/utilities/parameters/ssm_provider_get_options.py create mode 100644 docs/examples/utilities/parameters/testing_parameters_fixture.py create mode 100644 docs/examples/utilities/parameters/testing_parameters_index.py create mode 100644 docs/examples/utilities/parameters/testing_parameters_mock.py create mode 100644 docs/examples/utilities/parameters/testing_parameters_tests.py diff --git a/Makefile b/Makefile index 73667eb5f58..a430f61b0c6 100644 --- a/Makefile +++ b/Makefile @@ -90,3 +90,10 @@ changelog: mypy: poetry run mypy --pretty aws_lambda_powertools + +format-examples: + poetry run isort docs/examples + poetry run black docs/examples/*/*/*.py + +lint-examples: + poetry run python3 -m py_compile docs/examples/*/*/*.py diff --git a/docs/examples/utilities/parameters/app_config_provider.py b/docs/examples/utilities/parameters/app_config_provider.py new file mode 100644 index 00000000000..24a1eaea6f9 --- /dev/null +++ b/docs/examples/utilities/parameters/app_config_provider.py @@ -0,0 +1,15 @@ +from botocore.config import Config + +from aws_lambda_powertools.utilities import parameters + +config = Config(region_name="us-west-1") +appconf_provider = parameters.AppConfigProvider( + environment="my_env", + application="my_app", + config=config, +) + + +def handler(event, context): + # Retrieve a single secret + value: bytes = appconf_provider.get("my_conf") diff --git a/docs/examples/utilities/parameters/create_your_own_s3_provider.py b/docs/examples/utilities/parameters/create_your_own_s3_provider.py new file mode 100644 index 00000000000..28cc584c9ae --- /dev/null +++ b/docs/examples/utilities/parameters/create_your_own_s3_provider.py @@ -0,0 +1,53 @@ +import copy +from typing import Dict + +import boto3 + +from aws_lambda_powertools.utilities import BaseProvider + + +class S3Provider(BaseProvider): + bucket_name = None + client = None + + def __init__(self, bucket_name: str): + # Initialize the client to your custom parameter store + # E.g.: + + self.bucket_name = bucket_name + self.client = boto3.client("s3") + + def _get(self, name: str, **sdk_options) -> str: + # Retrieve a single value + # E.g.: + + sdk_options["Bucket"] = self.bucket_name + sdk_options["Key"] = name + + response = self.client.get_object(**sdk_options) + return + + def _get_multiple(self, path: str, **sdk_options) -> Dict[str, str]: + # Retrieve multiple values + # E.g.: + + list_sdk_options = copy.deepcopy(sdk_options) + + list_sdk_options["Bucket"] = self.bucket_name + list_sdk_options["Prefix"] = path + + list_response = self.client.list_objects_v2(**list_sdk_options) + + parameters = {} + + for obj in list_response.get("Contents", []): + get_sdk_options = copy.deepcopy(sdk_options) + + get_sdk_options["Bucket"] = self.bucket_name + get_sdk_options["Key"] = obj["Key"] + + get_response = self.client.get_object(**get_sdk_options) + + parameters[obj["Key"]] = get_response["Body"].read().decode() + + return parameters diff --git a/docs/examples/utilities/parameters/custom_caching_parameters.py b/docs/examples/utilities/parameters/custom_caching_parameters.py new file mode 100644 index 00000000000..d603484a02d --- /dev/null +++ b/docs/examples/utilities/parameters/custom_caching_parameters.py @@ -0,0 +1,16 @@ +from botocore.config import Config + +from aws_lambda_powertools.utilities import parameters + +config = Config(region_name="us-west-1") +ssm_provider = parameters.SSMProvider(config=config) + + +def handler(event, context): + # Retrieve a single parameter + value = ssm_provider.get("/my/parameter", max_age=60) # 1 minute + + # Retrieve multiple parameters from a path prefix + values = ssm_provider.get_multiple("/my/path/prefix", max_age=60) + for k, v in values.items(): + print(f"{k}: {v}") diff --git a/docs/examples/utilities/parameters/dynamodb_provider.py b/docs/examples/utilities/parameters/dynamodb_provider.py new file mode 100644 index 00000000000..f17d7456008 --- /dev/null +++ b/docs/examples/utilities/parameters/dynamodb_provider.py @@ -0,0 +1,8 @@ +from aws_lambda_powertools.utilities import parameters + +dynamodb_provider = parameters.DynamoDBProvider(table_name="my-table") + + +def handler(event, context): + # Retrieve a value from DynamoDB + value = dynamodb_provider.get("my-parameter") diff --git a/docs/examples/utilities/parameters/dynamodb_provider_customization.py b/docs/examples/utilities/parameters/dynamodb_provider_customization.py new file mode 100644 index 00000000000..9b67658d2cf --- /dev/null +++ b/docs/examples/utilities/parameters/dynamodb_provider_customization.py @@ -0,0 +1,12 @@ +from aws_lambda_powertools.utilities import parameters + +dynamodb_provider = parameters.DynamoDBProvider( + table_name="my-table", + key_attr="MyKeyAttr", + sort_attr="MySortAttr", + value_attr="MyvalueAttr", +) + + +def handler(event, context): + value = dynamodb_provider.get("my-parameter") diff --git a/docs/examples/utilities/parameters/dynamodb_provider_get_multiple.py b/docs/examples/utilities/parameters/dynamodb_provider_get_multiple.py new file mode 100644 index 00000000000..31e3d43981b --- /dev/null +++ b/docs/examples/utilities/parameters/dynamodb_provider_get_multiple.py @@ -0,0 +1,13 @@ +from aws_lambda_powertools.utilities import parameters + +dynamodb_provider = parameters.DynamoDBProvider(table_name="my-table") + + +def handler(event, context): + # Retrieve multiple values by performing a Query on the DynamoDB table + # This returns a dict with the sort key attribute as dict key. + parameters = dynamodb_provider.get_multiple("my-hash-key") + for k, v in parameters.items(): + # k: param-a + # v: "my-value-a" + print(f"{k}: {v}") diff --git a/docs/examples/utilities/parameters/dynamodb_provider_local.py b/docs/examples/utilities/parameters/dynamodb_provider_local.py new file mode 100644 index 00000000000..b7e7dca8829 --- /dev/null +++ b/docs/examples/utilities/parameters/dynamodb_provider_local.py @@ -0,0 +1,6 @@ +from aws_lambda_powertools.utilities import parameters + +dynamodb_provider = parameters.DynamoDBProvider( + table_name="my-table", + endpoint_url="http://localhost:8000", +) diff --git a/docs/examples/utilities/parameters/fetching_app_config.py b/docs/examples/utilities/parameters/fetching_app_config.py new file mode 100644 index 00000000000..b6ea5014e10 --- /dev/null +++ b/docs/examples/utilities/parameters/fetching_app_config.py @@ -0,0 +1,10 @@ +from aws_lambda_powertools.utilities import parameters + + +def handler(event, context): + # Retrieve a single configuration, latest version + value: bytes = parameters.get_app_config( + name="my_configuration", + environment="my_env", + application="my_app", + ) diff --git a/docs/examples/utilities/parameters/fetching_secrets.py b/docs/examples/utilities/parameters/fetching_secrets.py new file mode 100644 index 00000000000..a62ba094c65 --- /dev/null +++ b/docs/examples/utilities/parameters/fetching_secrets.py @@ -0,0 +1,6 @@ +from aws_lambda_powertools.utilities import parameters + + +def handler(event, context): + # Retrieve a single secret + value = parameters.get_secret("my-secret") diff --git a/docs/examples/utilities/parameters/force_fetch_parameters.py b/docs/examples/utilities/parameters/force_fetch_parameters.py new file mode 100644 index 00000000000..d049c1f0c41 --- /dev/null +++ b/docs/examples/utilities/parameters/force_fetch_parameters.py @@ -0,0 +1,6 @@ +from aws_lambda_powertools.utilities import parameters + + +def handler(event, context): + # Retrieve a single parameter + value = parameters.get_parameter("/my/parameter", force_fetch=True) diff --git a/docs/examples/utilities/parameters/parameters_custom_config.py b/docs/examples/utilities/parameters/parameters_custom_config.py new file mode 100644 index 00000000000..79510421617 --- /dev/null +++ b/docs/examples/utilities/parameters/parameters_custom_config.py @@ -0,0 +1,12 @@ +from botocore.config import Config + +from aws_lambda_powertools.utilities import parameters + +boto_config = Config() +ssm_provider = parameters.SSMProvider(config=boto_config) + + +def handler(event, context): + # Retrieve a single parameter + value = ssm_provider.get("/my/parameter") + ... diff --git a/docs/examples/utilities/parameters/parameters_custom_session.py b/docs/examples/utilities/parameters/parameters_custom_session.py new file mode 100644 index 00000000000..d76fffe00ac --- /dev/null +++ b/docs/examples/utilities/parameters/parameters_custom_session.py @@ -0,0 +1,12 @@ +import boto3 + +from aws_lambda_powertools.utilities import parameters + +boto3_session = boto3.session.Session() +ssm_provider = parameters.SSMProvider(boto3_session=boto3_session) + + +def handler(event, context): + # Retrieve a single parameter + value = ssm_provider.get("/my/parameter") + ... diff --git a/docs/examples/utilities/parameters/parameters_sdk_args.py b/docs/examples/utilities/parameters/parameters_sdk_args.py new file mode 100644 index 00000000000..571d070de8a --- /dev/null +++ b/docs/examples/utilities/parameters/parameters_sdk_args.py @@ -0,0 +1,8 @@ +from aws_lambda_powertools.utilities import parameters + +secrets_provider = parameters.SecretsProvider() + + +def handler(event, context): + # The 'VersionId' argument will be passed to the underlying get_secret_value() call. + value = secrets_provider.get("my-secret", VersionId="e62ec170-6b01-48c7-94f3-d7497851a8d2") diff --git a/docs/examples/utilities/parameters/parameters_transform.py b/docs/examples/utilities/parameters/parameters_transform.py new file mode 100644 index 00000000000..eb7c3be0d8a --- /dev/null +++ b/docs/examples/utilities/parameters/parameters_transform.py @@ -0,0 +1,5 @@ +from aws_lambda_powertools.utilities import parameters + + +def handler(event, context): + value_from_json = parameters.get_parameter("/my/json/parameter", transform="json") diff --git a/docs/examples/utilities/parameters/parameters_transform_auto.py b/docs/examples/utilities/parameters/parameters_transform_auto.py new file mode 100644 index 00000000000..f85e6aa1b13 --- /dev/null +++ b/docs/examples/utilities/parameters/parameters_transform_auto.py @@ -0,0 +1,7 @@ +from aws_lambda_powertools.utilities import parameters + +ssm_provider = parameters.SSMProvider() + + +def handler(event, context): + values = ssm_provider.get_multiple("/param", transform="auto") diff --git a/docs/examples/utilities/parameters/parameters_transform_providers.py b/docs/examples/utilities/parameters/parameters_transform_providers.py new file mode 100644 index 00000000000..2451330e9c7 --- /dev/null +++ b/docs/examples/utilities/parameters/parameters_transform_providers.py @@ -0,0 +1,11 @@ +from aws_lambda_powertools.utilities import parameters + +ssm_provider = parameters.SSMProvider() + + +def handler(event, context): + # Transform a JSON string + value_from_json = ssm_provider.get("/my/json/parameter", transform="json") + + # Transform a Base64 encoded string + value_from_binary = ssm_provider.get("/my/binary/parameter", transform="binary") diff --git a/docs/examples/utilities/parameters/parameters_transform_raise_on_transform_error.py b/docs/examples/utilities/parameters/parameters_transform_raise_on_transform_error.py new file mode 100644 index 00000000000..542ad754439 --- /dev/null +++ b/docs/examples/utilities/parameters/parameters_transform_raise_on_transform_error.py @@ -0,0 +1,19 @@ +from aws_lambda_powertools.utilities import parameters + +ssm_provider = parameters.SSMProvider() + + +def handler(event, context): + # This will display: + # /param/a: [some value] + # /param/b: [some value] + # /param/c: None + values = ssm_provider.get_multiple("/param", transform="json") + for k, v in values.items(): + print(f"{k}: {v}") + + try: + # This will raise a TransformParameterError exception + values = ssm_provider.get_multiple("/param", transform="json", raise_on_transform_error=True) + except parameters.exceptions.TransformParameterError: + ... diff --git a/docs/examples/utilities/parameters/recursively_parameters.py b/docs/examples/utilities/parameters/recursively_parameters.py new file mode 100644 index 00000000000..5ff2fcb7161 --- /dev/null +++ b/docs/examples/utilities/parameters/recursively_parameters.py @@ -0,0 +1,12 @@ +from aws_lambda_powertools.utilities import parameters + + +def handler(event, context): + # Retrieve a single parameter + value = parameters.get_parameter("/my/parameter") + + # Retrieve multiple parameters from a path prefix recursively + # This returns a dict with the parameter name as key + values = parameters.get_parameters("/my/path/prefix") + for k, v in values.items(): + print(f"{k}: {v}") diff --git a/docs/examples/utilities/parameters/secrets_provider.py b/docs/examples/utilities/parameters/secrets_provider.py new file mode 100644 index 00000000000..f775ff3a7b8 --- /dev/null +++ b/docs/examples/utilities/parameters/secrets_provider.py @@ -0,0 +1,11 @@ +from botocore.config import Config + +from aws_lambda_powertools.utilities import parameters + +config = Config(region_name="us-west-1") +secrets_provider = parameters.SecretsProvider(config=config) + + +def handler(event, context): + # Retrieve a single secret + value = secrets_provider.get("my-secret") diff --git a/docs/examples/utilities/parameters/ssm_provider.py b/docs/examples/utilities/parameters/ssm_provider.py new file mode 100644 index 00000000000..b83b2bafa16 --- /dev/null +++ b/docs/examples/utilities/parameters/ssm_provider.py @@ -0,0 +1,16 @@ +from botocore.config import Config + +from aws_lambda_powertools.utilities import parameters + +config = Config(region_name="us-west-1") +ssm_provider = parameters.SSMProvider(config=config) # or boto3_session=boto3.Session() + + +def handler(event, context): + # Retrieve a single parameter + value = ssm_provider.get("/my/parameter") + + # Retrieve multiple parameters from a path prefix + values = ssm_provider.get_multiple("/my/path/prefix") + for k, v in values.items(): + print(f"{k}: {v}") diff --git a/docs/examples/utilities/parameters/ssm_provider_get_options.py b/docs/examples/utilities/parameters/ssm_provider_get_options.py new file mode 100644 index 00000000000..0dbcc743411 --- /dev/null +++ b/docs/examples/utilities/parameters/ssm_provider_get_options.py @@ -0,0 +1,9 @@ +from aws_lambda_powertools.utilities import parameters + +ssm_provider = parameters.SSMProvider() + + +def handler(event, context): + decrypted_value = ssm_provider.get("/my/encrypted/parameter", decrypt=True) + + no_recursive_values = ssm_provider.get_multiple("/my/path/prefix", recursive=False) diff --git a/docs/examples/utilities/parameters/testing_parameters_fixture.py b/docs/examples/utilities/parameters/testing_parameters_fixture.py new file mode 100644 index 00000000000..2a872172252 --- /dev/null +++ b/docs/examples/utilities/parameters/testing_parameters_fixture.py @@ -0,0 +1,16 @@ +import pytest +from src import index + + +@pytest.fixture +def mock_parameter_response(monkeypatch): + def mockreturn(name): + return "mock_value" + + monkeypatch.setattr(index.parameters, "get_parameter", mockreturn) + + +# Pass our fixture as an argument to all tests where we want to mock the get_parameter response +def test_handler(mock_parameter_response): + return_val = index.handler({}, {}) + assert return_val.get("message") == "mock_value" diff --git a/docs/examples/utilities/parameters/testing_parameters_index.py b/docs/examples/utilities/parameters/testing_parameters_index.py new file mode 100644 index 00000000000..ef94df54f25 --- /dev/null +++ b/docs/examples/utilities/parameters/testing_parameters_index.py @@ -0,0 +1,7 @@ +from aws_lambda_powertools.utilities import parameters + + +def handler(event, context): + # Retrieve a single parameter + value = parameters.get_parameter("my-parameter-name") + return {"message": value} diff --git a/docs/examples/utilities/parameters/testing_parameters_mock.py b/docs/examples/utilities/parameters/testing_parameters_mock.py new file mode 100644 index 00000000000..8ac789fd5a1 --- /dev/null +++ b/docs/examples/utilities/parameters/testing_parameters_mock.py @@ -0,0 +1,13 @@ +from unittest.mock import patch + +from src import index + + +# Replaces "aws_lambda_powertools.utilities.parameters.get_parameter" with a Mock object +@patch("aws_lambda_powertools.utilities.parameters.get_parameter") +def test_handler(get_parameter_mock): + get_parameter_mock.return_value = "mock_value" + + return_val = index.handler({}, {}) + get_parameter_mock.assert_called_with("my-parameter-name") + assert return_val.get("message") == "mock_value" diff --git a/docs/examples/utilities/parameters/testing_parameters_tests.py b/docs/examples/utilities/parameters/testing_parameters_tests.py new file mode 100644 index 00000000000..0a7679d990c --- /dev/null +++ b/docs/examples/utilities/parameters/testing_parameters_tests.py @@ -0,0 +1,10 @@ +from src import index + + +def test_handler(monkeypatch): + def mockreturn(name): + return "mock_value" + + monkeypatch.setattr(index.parameters, "get_parameter", mockreturn) + return_val = index.handler({}, {}) + assert return_val.get("message") == "mock_value" diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index d02a3feb73a..1da70bca755 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -39,30 +39,16 @@ You can retrieve a single parameter using `get_parameter` high-level function. For multiple parameters, you can use `get_parameters` and pass a path to retrieve them recursively. -```python hl_lines="1 5 9" title="Fetching multiple parameters recursively" -from aws_lambda_powertools.utilities import parameters - -def handler(event, context): - # Retrieve a single parameter - value = parameters.get_parameter("/my/parameter") - - # Retrieve multiple parameters from a path prefix recursively - # This returns a dict with the parameter name as key - values = parameters.get_parameters("/my/path/prefix") - for k, v in values.items(): - print(f"{k}: {v}") +```python hl_lines="1 6 10" title="Fetching multiple parameters recursively" +--8<-- "docs/examples/utilities/parameters/recursively_parameters.py" ``` ### Fetching secrets You can fetch secrets stored in Secrets Manager using `get_secrets`. -```python hl_lines="1 5" title="Fetching secrets" -from aws_lambda_powertools.utilities import parameters - -def handler(event, context): - # Retrieve a single secret - value = parameters.get_secret("my-secret") +```python hl_lines="1 6" title="Fetching secrets" +--8<-- "docs/examples/utilities/parameters/fetching_secrets.py" ``` ### Fetching app configurations @@ -71,12 +57,8 @@ You can fetch application configurations in AWS AppConfig using `get_app_config` The following will retrieve the latest version and store it in the cache. -```python hl_lines="1 5" title="Fetching latest config from AppConfig" -from aws_lambda_powertools.utilities import parameters - -def handler(event, context): - # Retrieve a single configuration, latest version - value: bytes = parameters.get_app_config(name="my_configuration", environment="my_env", application="my_app") +```python hl_lines="1 6-10" title="Fetching latest config from AppConfig" +--8<-- "docs/examples/utilities/parameters/fetching_app_config.py" ``` ## Advanced @@ -90,33 +72,16 @@ By default, we cache parameters retrieved in-memory for 5 seconds. You can adjust how long we should keep values in cache by using the param `max_age`, when using `get()` or `get_multiple()` methods across all providers. -```python hl_lines="9" title="Caching parameter(s) value in memory for longer than 5 seconds" -from aws_lambda_powertools.utilities import parameters -from botocore.config import Config - -config = Config(region_name="us-west-1") -ssm_provider = parameters.SSMProvider(config=config) - -def handler(event, context): - # Retrieve a single parameter - value = ssm_provider.get("/my/parameter", max_age=60) # 1 minute - - # Retrieve multiple parameters from a path prefix - values = ssm_provider.get_multiple("/my/path/prefix", max_age=60) - for k, v in values.items(): - print(f"{k}: {v}") +```python hl_lines="11 14" title="Caching parameter(s) value in memory for longer than 5 seconds" +--8<-- "docs/examples/utilities/parameters/custom_caching_parameters.py" ``` ### Always fetching the latest If you'd like to always ensure you fetch the latest parameter from the store regardless if already available in cache, use `force_fetch` param. -```python hl_lines="5" title="Forcefully fetching the latest parameter whether TTL has expired or not" -from aws_lambda_powertools.utilities import parameters - -def handler(event, context): - # Retrieve a single parameter - value = parameters.get_parameter("/my/parameter", force_fetch=True) +```python hl_lines="6" title="Forcefully fetching the latest parameter whether TTL has expired or not" +--8<-- "docs/examples/utilities/parameters/force_fetch_parameters.py" ``` ### Built-in provider class @@ -128,21 +93,8 @@ For greater flexibility such as configuring the underlying SDK client used by bu #### SSMProvider -```python hl_lines="5 9 12" title="Example with SSMProvider for further extensibility" -from aws_lambda_powertools.utilities import parameters -from botocore.config import Config - -config = Config(region_name="us-west-1") -ssm_provider = parameters.SSMProvider(config=config) # or boto3_session=boto3.Session() - -def handler(event, context): - # Retrieve a single parameter - value = ssm_provider.get("/my/parameter") - - # Retrieve multiple parameters from a path prefix - values = ssm_provider.get_multiple("/my/path/prefix") - for k, v in values.items(): - print(f"{k}: {v}") +```python hl_lines="6 11 14" title="Example with SSMProvider for further extensibility" +--8<-- "docs/examples/utilities/parameters/ssm_provider.py" ``` The AWS Systems Manager Parameter Store provider supports two additional arguments for the `get()` and `get_multiple()` methods: @@ -152,29 +104,14 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen | **decrypt** | `False` | Will automatically decrypt the parameter. | **recursive** | `True` | For `get_multiple()` only, will fetch all parameter values recursively based on a path prefix. -```python hl_lines="6 8" title="Example with get() and get_multiple()" -from aws_lambda_powertools.utilities import parameters - -ssm_provider = parameters.SSMProvider() - -def handler(event, context): - decrypted_value = ssm_provider.get("/my/encrypted/parameter", decrypt=True) - - no_recursive_values = ssm_provider.get_multiple("/my/path/prefix", recursive=False) +```python hl_lines="7 9" title="Example with get() and get_multiple()" +--8<-- "docs/examples/utilities/parameters/ssm_provider_get_options.py" ``` #### SecretsProvider -```python hl_lines="5 9" title="Example with SecretsProvider for further extensibility" -from aws_lambda_powertools.utilities import parameters -from botocore.config import Config - -config = Config(region_name="us-west-1") -secrets_provider = parameters.SecretsProvider(config=config) - -def handler(event, context): - # Retrieve a single secret - value = secrets_provider.get("my-secret") +```python hl_lines="6 11" title="Example with SecretsProvider for further extensibility" +--8<-- "docs/examples/utilities/parameters/secrets_provider.py" ``` #### DynamoDBProvider @@ -196,23 +133,16 @@ For single parameters, you must use `id` as the [partition key](https://docs.aws With this table, `dynamodb_provider.get("my-param")` will return `my-value`. === "app.py" - ```python hl_lines="3 7" - from aws_lambda_powertools.utilities import parameters - - dynamodb_provider = parameters.DynamoDBProvider(table_name="my-table") - def handler(event, context): - # Retrieve a value from DynamoDB - value = dynamodb_provider.get("my-parameter") + ```python hl_lines="3 8" + --8<-- "docs/examples/utilities/parameters/dynamodb_provider.py" ``` === "DynamoDB Local example" You can initialize the DynamoDB provider pointing to [DynamoDB Local](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) using `endpoint_url` parameter: - ```python hl_lines="3" - from aws_lambda_powertools.utilities import parameters - - dynamodb_provider = parameters.DynamoDBProvider(table_name="my-table", endpoint_url="http://localhost:8000") + ```python hl_lines="5" + --8<-- "docs/examples/utilities/parameters/dynamodb_provider_local.py" ``` **DynamoDB table structure for multiple values parameters** @@ -232,19 +162,9 @@ You can retrieve multiple parameters sharing the same `id` by having a sort key With this table, `dynamodb_provider.get_multiple("my-hash-key")` will return a dictionary response in the shape of `sk:value`. === "app.py" - ```python hl_lines="3 8" - from aws_lambda_powertools.utilities import parameters - - dynamodb_provider = parameters.DynamoDBProvider(table_name="my-table") - - def handler(event, context): - # Retrieve multiple values by performing a Query on the DynamoDB table - # This returns a dict with the sort key attribute as dict key. - parameters = dynamodb_provider.get_multiple("my-hash-key") - for k, v in parameters.items(): - # k: param-a - # v: "my-value-a" - print(f"{k}: {v}") + + ```python hl_lines="3 9" + --8<-- "docs/examples/utilities/parameters/dynamodb_provider_get_multiple.py" ``` === "parameters dict response" @@ -269,31 +189,13 @@ DynamoDB provider can be customized at initialization to match your table struct | **value_attr** | No | `value` | Name of the attribute containing the parameter value. ```python hl_lines="3-8" title="Customizing DynamoDBProvider to suit your table design" -from aws_lambda_powertools.utilities import parameters - -dynamodb_provider = parameters.DynamoDBProvider( - table_name="my-table", - key_attr="MyKeyAttr", - sort_attr="MySortAttr", - value_attr="MyvalueAttr" -) - -def handler(event, context): - value = dynamodb_provider.get("my-parameter") +--8<-- "docs/examples/utilities/parameters/dynamodb_provider_customization.py" ``` #### AppConfigProvider -```python hl_lines="5 9" title="Using AppConfigProvider" -from aws_lambda_powertools.utilities import parameters -from botocore.config import Config - -config = Config(region_name="us-west-1") -appconf_provider = parameters.AppConfigProvider(environment="my_env", application="my_app", config=config) - -def handler(event, context): - # Retrieve a single secret - value: bytes = appconf_provider.get("my_conf") +```python hl_lines="6-10 15" title="Using AppConfigProvider" +--8<-- "docs/examples/utilities/parameters/app_config_provider.py" ``` ### Create your own provider @@ -304,57 +206,8 @@ All transformation and caching logic is handled by the `get()` and `get_multiple Here is an example implementation using S3 as a custom parameter store: -```python hl_lines="3 6 17 27" title="Creating a S3 Provider to fetch parameters" -import copy - -from aws_lambda_powertools.utilities import BaseProvider -import boto3 - -class S3Provider(BaseProvider): - bucket_name = None - client = None - - def __init__(self, bucket_name: str): - # Initialize the client to your custom parameter store - # E.g.: - - self.bucket_name = bucket_name - self.client = boto3.client("s3") - - def _get(self, name: str, **sdk_options) -> str: - # Retrieve a single value - # E.g.: - - sdk_options["Bucket"] = self.bucket_name - sdk_options["Key"] = name - - response = self.client.get_object(**sdk_options) - return - - def _get_multiple(self, path: str, **sdk_options) -> Dict[str, str]: - # Retrieve multiple values - # E.g.: - - list_sdk_options = copy.deepcopy(sdk_options) - - list_sdk_options["Bucket"] = self.bucket_name - list_sdk_options["Prefix"] = path - - list_response = self.client.list_objects_v2(**list_sdk_options) - - parameters = {} - - for obj in list_response.get("Contents", []): - get_sdk_options = copy.deepcopy(sdk_options) - - get_sdk_options["Bucket"] = self.bucket_name - get_sdk_options["Key"] = obj["Key"] - - get_response = self.client.get_object(**get_sdk_options) - - parameters[obj["Key"]] = get_response["Body"].read().decode() - - return parameters +```python hl_lines="6 9 20 30" title="Creating a S3 Provider to fetch parameters" +--8<-- "docs/examples/utilities/parameters/create_your_own_s3_provider.py" ``` ### Deserializing values with transform parameter @@ -366,26 +219,14 @@ For parameters stored in JSON or Base64 format, you can use the `transform` argu === "High level functions" - ```python hl_lines="4" - from aws_lambda_powertools.utilities import parameters - - def handler(event, context): - value_from_json = parameters.get_parameter("/my/json/parameter", transform="json") + ```python hl_lines="5" + --8<-- "docs/examples/utilities/parameters/parameters_transform.py" ``` === "Providers" - ```python hl_lines="7 10" - from aws_lambda_powertools.utilities import parameters - - ssm_provider = parameters.SSMProvider() - - def handler(event, context): - # Transform a JSON string - value_from_json = ssm_provider.get("/my/json/parameter", transform="json") - - # Transform a Base64 encoded string - value_from_binary = ssm_provider.get("/my/binary/parameter", transform="binary") + ```python hl_lines="8 11" + --8<-- "docs/examples/utilities/parameters/parameters_transform_providers.py" ``` #### Partial transform failures with `get_multiple()` @@ -396,25 +237,8 @@ You can override this by setting the `raise_on_transform_error` argument to `Tru For example, if you have three parameters, */param/a*, */param/b* and */param/c*, but */param/c* is malformed: -```python hl_lines="9 16" title="Raising TransformParameterError at first malformed parameter" -from aws_lambda_powertools.utilities import parameters - -ssm_provider = parameters.SSMProvider() - -def handler(event, context): - # This will display: - # /param/a: [some value] - # /param/b: [some value] - # /param/c: None - values = ssm_provider.get_multiple("/param", transform="json") - for k, v in values.items(): - print(f"{k}: {v}") - - try: - # This will raise a TransformParameterError exception - values = ssm_provider.get_multiple("/param", transform="json", raise_on_transform_error=True) - except parameters.exceptions.TransformParameterError: - ... +```python hl_lines="11 17" title="Raising TransformParameterError at first malformed parameter" +--8<-- "docs/examples/utilities/parameters/parameters_transform_raise_on_transform_error.py" ``` #### Auto-transform values on suffix @@ -426,13 +250,8 @@ You can do this with a single request by using `transform="auto"`. This will ins ???+ info `transform="auto"` feature is available across all providers, including the high level functions. -```python hl_lines="6" title="Deserializing parameter values based on their suffix" -from aws_lambda_powertools.utilities import parameters - -ssm_provider = parameters.SSMProvider() - -def handler(event, context): - values = ssm_provider.get_multiple("/param", transform="auto") +```python hl_lines="7" title="Deserializing parameter values based on their suffix" +--8<-- "docs/examples/utilities/parameters/parameters_transform_auto.py" ``` For example, if you have two parameters with the following suffixes `.json` and `.binary`: @@ -455,14 +274,8 @@ The return of `ssm_provider.get_multiple("/param", transform="auto")` call will You can use arbitrary keyword arguments to pass it directly to the underlying SDK method. -```python hl_lines="8" title="" -from aws_lambda_powertools.utilities import parameters - -secrets_provider = parameters.SecretsProvider() - -def handler(event, context): - # The 'VersionId' argument will be passed to the underlying get_secret_value() call. - value = secrets_provider.get("my-secret", VersionId="e62ec170-6b01-48c7-94f3-d7497851a8d2") +```python hl_lines="7-8" title="" +--8<-- "docs/examples/utilities/parameters/parameters_sdk_args.py" ``` Here is the mapping between this utility's functions and methods and the underlying SDK: @@ -479,7 +292,6 @@ Here is the mapping between this utility's functions and methods and the underly | DynamoDB | `DynamoDBProvider.get_multiple` | `dynamodb` | ([Table resource](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#table)) | [query](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.query) | App Config | `get_app_config` | `appconfig` | [get_configuration](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appconfig.html#AppConfig.Client.get_configuration) | - ### Customizing boto configuration The **`config`** and **`boto3_session`** parameters enable you to pass in a custom [botocore config object](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html) or a custom [boto3 session](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html) when constructing any of the built-in provider classes. @@ -489,31 +301,13 @@ The **`config`** and **`boto3_session`** parameters enable you to pass in a cust === "Custom session" - ```python hl_lines="2 4 5" - from aws_lambda_powertools.utilities import parameters - import boto3 - - boto3_session = boto3.session.Session() - ssm_provider = parameters.SSMProvider(boto3_session=boto3_session) - - def handler(event, context): - # Retrieve a single parameter - value = ssm_provider.get("/my/parameter") - ... + ```python hl_lines="1 5-6" + --8<-- "docs/examples/utilities/parameters/parameters_custom_session.py" ``` === "Custom config" - ```python hl_lines="2 4 5" - from aws_lambda_powertools.utilities import parameters - from botocore.config import Config - - boto_config = Config() - ssm_provider = parameters.SSMProvider(config=boto_config) - - def handler(event, context): - # Retrieve a single parameter - value = ssm_provider.get("/my/parameter") - ... + ```python hl_lines="1 5-6" + --8<-- "docs/examples/utilities/parameters/parameters_custom_config.py" ``` ## Testing your code @@ -523,49 +317,23 @@ can be achieved in a number of ways - in this example, we use the [pytest monkey to patch the `parameters.get_parameter` method: === "tests.py" - ```python - from src import index - - def test_handler(monkeypatch): - - def mockreturn(name): - return "mock_value" - monkeypatch.setattr(index.parameters, "get_parameter", mockreturn) - return_val = index.handler({}, {}) - assert return_val.get('message') == 'mock_value' + ```python + --8<-- "docs/examples/utilities/parameters/testing_parameters_tests.py" ``` === "src/index.py" - ```python - from aws_lambda_powertools.utilities import parameters - def handler(event, context): - # Retrieve a single parameter - value = parameters.get_parameter("my-parameter-name") - return {"message": value} + ```python + --8<-- "docs/examples/utilities/parameters/testing_parameters_index.py" ``` If we need to use this pattern across multiple tests, we can avoid repetition by refactoring to use our own pytest fixture: === "tests.py" - ```python - import pytest - - from src import index - - @pytest.fixture - def mock_parameter_response(monkeypatch): - def mockreturn(name): - return "mock_value" - - monkeypatch.setattr(index.parameters, "get_parameter", mockreturn) - - # Pass our fixture as an argument to all tests where we want to mock the get_parameter response - def test_handler(mock_parameter_response): - return_val = index.handler({}, {}) - assert return_val.get('message') == 'mock_value' + ```python + --8<-- "docs/examples/utilities/parameters/testing_parameters_fixture.py" ``` Alternatively, if we need more fully featured mocking (for example checking the arguments passed to `get_parameter`), we @@ -575,16 +343,5 @@ object named `get_parameter_mock`. === "tests.py" ```python - from unittest.mock import patch - from src import index - - # Replaces "aws_lambda_powertools.utilities.parameters.get_parameter" with a Mock object - @patch("aws_lambda_powertools.utilities.parameters.get_parameter") - def test_handler(get_parameter_mock): - get_parameter_mock.return_value = 'mock_value' - - return_val = index.handler({}, {}) - get_parameter_mock.assert_called_with("my-parameter-name") - assert return_val.get('message') == 'mock_value' - + --8<-- "docs/examples/utilities/parameters/testing_parameters_mock.py" ``` From 31fe45ec28d7ac9579d509351be214cabecd9c3e Mon Sep 17 00:00:00 2001 From: Simon Thulbourn Date: Thu, 28 Apr 2022 17:34:36 +0100 Subject: [PATCH 2/3] Revert "fix(parser): Add missing fields for SESEvent (#1027)" (#1190) This reverts commit 797a10afac80544e2d69bcb7d624909436f2b12a. --- .../utilities/parser/models/__init__.py | 8 -- .../utilities/parser/models/ses.py | 34 +----- tests/events/sesEventS3.json | 114 ------------------ tests/functional/parser/test_ses.py | 58 +-------- 4 files changed, 8 insertions(+), 206 deletions(-) delete mode 100644 tests/events/sesEventS3.json diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index 34c8e6ce6a1..e3fb50a2d5d 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -37,11 +37,7 @@ SesModel, SesReceipt, SesReceiptAction, - SesReceiptActionBase, - SesReceiptBounceAction, - SesReceiptS3Action, SesReceiptVerdict, - SesReceiptWorkmailAction, SesRecordModel, ) from .sns import SnsModel, SnsNotificationModel, SnsRecordModel @@ -88,10 +84,6 @@ "SesMailHeaders", "SesReceipt", "SesReceiptAction", - "SesReceiptActionBase", - "SesReceiptBounceAction", - "SesReceiptWorkmailAction", - "SesReceiptS3Action", "SesReceiptVerdict", "SnsModel", "SnsNotificationModel", diff --git a/aws_lambda_powertools/utilities/parser/models/ses.py b/aws_lambda_powertools/utilities/parser/models/ses.py index 7cd655ea28c..70fd2e83978 100644 --- a/aws_lambda_powertools/utilities/parser/models/ses.py +++ b/aws_lambda_powertools/utilities/parser/models/ses.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import List, Optional, Union +from typing import List, Optional from pydantic import BaseModel, Field from pydantic.networks import EmailStr @@ -12,38 +12,12 @@ class SesReceiptVerdict(BaseModel): status: Literal["PASS", "FAIL", "GRAY", "PROCESSING_FAILED"] -class SesReceiptActionBase(BaseModel): - topicArn: Optional[str] - - -class SesReceiptAction(SesReceiptActionBase): +class SesReceiptAction(BaseModel): type: Literal["Lambda"] # noqa A003,VNE003 invocationType: Literal["Event"] functionArn: str -class SesReceiptS3Action(SesReceiptActionBase): - type: Literal["S3"] # noqa A003,VNE003 - topicArn: str - bucketName: str - objectKey: str - - -class SesReceiptBounceAction(SesReceiptActionBase): - type: Literal["Bounce"] # noqa A003,VNE003 - topicArn: str - smtpReplyCode: str - message: str - sender: str - statusCode: str - - -class SesReceiptWorkmailAction(SesReceiptActionBase): - type: Literal["WorkMail"] # noqa A003,VNE003 - topicArn: str - organizationArn: str - - class SesReceipt(BaseModel): timestamp: datetime processingTimeMillis: PositiveInt @@ -51,10 +25,8 @@ class SesReceipt(BaseModel): spamVerdict: SesReceiptVerdict virusVerdict: SesReceiptVerdict spfVerdict: SesReceiptVerdict - dkimVerdict: SesReceiptVerdict dmarcVerdict: SesReceiptVerdict - dmarcPolicy: Optional[Literal["quarantine", "reject", "none"]] - action: Union[SesReceiptAction, SesReceiptS3Action, SesReceiptBounceAction, SesReceiptWorkmailAction] + action: SesReceiptAction class SesMailHeaders(BaseModel): diff --git a/tests/events/sesEventS3.json b/tests/events/sesEventS3.json deleted file mode 100644 index dbea2d42ce1..00000000000 --- a/tests/events/sesEventS3.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "Records": [ - { - "eventVersion": "1.0", - "ses": { - "receipt": { - "timestamp": "2015-09-11T20:32:33.936Z", - "processingTimeMillis": 406, - "recipients": [ - "recipient@example.com" - ], - "spamVerdict": { - "status": "PASS" - }, - "virusVerdict": { - "status": "PASS" - }, - "spfVerdict": { - "status": "PASS" - }, - "dkimVerdict": { - "status": "PASS" - }, - "dmarcVerdict": { - "status": "PASS" - }, - "dmarcPolicy": "reject", - "action": { - "type": "S3", - "topicArn": "arn:aws:sns:us-east-1:012345678912:example-topic", - "bucketName": "my-S3-bucket", - "objectKey": "email" - } - }, - "mail": { - "timestamp": "2015-09-11T20:32:33.936Z", - "source": "0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com", - "messageId": "d6iitobk75ur44p8kdnnp7g2n800", - "destination": [ - "recipient@example.com" - ], - "headersTruncated": false, - "headers": [ - { - "name": "Return-Path", - "value": "<0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com>" - }, - { - "name": "Received", - "value": "from a9-183.smtp-out.amazonses.com (a9-183.smtp-out.amazonses.com [54.240.9.183]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id d6iitobk75ur44p8kdnnp7g2n800 for recipient@example.com; Fri, 11 Sep 2015 20:32:33 +0000 (UTC)" - }, - { - "name": "DKIM-Signature", - "value": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple; s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug; d=amazonses.com; t=1442003552; h=From:To:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Date:Message-ID:Feedback-ID; bh=DWr3IOmYWoXCA9ARqGC/UaODfghffiwFNRIb2Mckyt4=; b=p4ukUDSFqhqiub+zPR0DW1kp7oJZakrzupr6LBe6sUuvqpBkig56UzUwc29rFbJF hlX3Ov7DeYVNoN38stqwsF8ivcajXpQsXRC1cW9z8x875J041rClAjV7EGbLmudVpPX 4hHst1XPyX5wmgdHIhmUuh8oZKpVqGi6bHGzzf7g=" - }, - { - "name": "From", - "value": "sender@example.com" - }, - { - "name": "To", - "value": "recipient@example.com" - }, - { - "name": "Subject", - "value": "Example subject" - }, - { - "name": "MIME-Version", - "value": "1.0" - }, - { - "name": "Content-Type", - "value": "text/plain; charset=UTF-8" - }, - { - "name": "Content-Transfer-Encoding", - "value": "7bit" - }, - { - "name": "Date", - "value": "Fri, 11 Sep 2015 20:32:32 +0000" - }, - { - "name": "Message-ID", - "value": "<61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com>" - }, - { - "name": "X-SES-Outgoing", - "value": "2015.09.11-54.240.9.183" - }, - { - "name": "Feedback-ID", - "value": "1.us-east-1.Krv2FKpFdWV+KUYw3Qd6wcpPJ4Sv/pOPpEPSHn2u2o4=:AmazonSES" - } - ], - "commonHeaders": { - "returnPath": "0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com", - "from": [ - "sender@example.com" - ], - "date": "Fri, 11 Sep 2015 20:32:32 +0000", - "to": [ - "recipient@example.com" - ], - "messageId": "<61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com>", - "subject": "Example subject" - } - } - }, - "eventSource": "aws:ses" - } - ] -} diff --git a/tests/functional/parser/test_ses.py b/tests/functional/parser/test_ses.py index 34a44253514..d434e2350f8 100644 --- a/tests/functional/parser/test_ses.py +++ b/tests/functional/parser/test_ses.py @@ -1,22 +1,11 @@ from aws_lambda_powertools.utilities.parser import event_parser -from aws_lambda_powertools.utilities.parser.models import ( - SesModel, - SesReceiptBounceAction, - SesReceiptWorkmailAction, - SesRecordModel, -) +from aws_lambda_powertools.utilities.parser.models import SesModel, SesRecordModel from aws_lambda_powertools.utilities.typing import LambdaContext from tests.functional.utils import load_event @event_parser(model=SesModel) -def handle_ses(event: SesModel, _: LambdaContext) -> SesModel: - return event - - -def test_ses_trigger_lambda_event(): - event_dict = load_event("sesEvent.json") - event = handle_ses(event_dict, LambdaContext()) +def handle_ses(event: SesModel, _: LambdaContext): expected_address = "johndoe@example.com" records = event.Records record: SesRecordModel = records[0] @@ -40,10 +29,6 @@ def test_ses_trigger_lambda_event(): assert common_headers.to == [expected_address] assert common_headers.messageId == "<0123456789example.com>" assert common_headers.subject == "Test Subject" - assert common_headers.cc is None - assert common_headers.bcc is None - assert common_headers.sender is None - assert common_headers.reply_to is None receipt = record.ses.receipt convert_time = int(round(receipt.timestamp.timestamp() * 1000)) assert convert_time == 0 @@ -53,45 +38,12 @@ def test_ses_trigger_lambda_event(): assert receipt.virusVerdict.status == "PASS" assert receipt.spfVerdict.status == "PASS" assert receipt.dmarcVerdict.status == "PASS" - assert receipt.dmarcVerdict.status == "PASS" - assert receipt.dmarcPolicy is None action = receipt.action assert action.type == "Lambda" assert action.functionArn == "arn:aws:lambda:us-west-2:012345678912:function:Example" assert action.invocationType == "Event" - assert action.topicArn is None - -def test_ses_trigger_event_s3(): - event_dict = load_event("sesEventS3.json") - event = handle_ses(event_dict, LambdaContext()) - records = list(event.Records) - record = records[0] - receipt = record.ses.receipt - assert receipt.dmarcPolicy == "reject" - action = record.ses.receipt.action - assert action.type == "S3" - assert action.topicArn == "arn:aws:sns:us-east-1:012345678912:example-topic" - assert action.bucketName == "my-S3-bucket" - assert action.objectKey == "email" - - -def test_ses_trigger_event_bounce(): - event_dict = { - "type": "Bounce", - "topicArn": "arn:aws:sns:us-east-1:123456789012:topic:my-topic", - "smtpReplyCode": "5.1.1", - "message": "message", - "sender": "sender", - "statusCode": "550", - } - SesReceiptBounceAction(**event_dict) - -def test_ses_trigger_event_work_mail(): - event_dict = { - "type": "WorkMail", - "topicArn": "arn:aws:sns:us-east-1:123456789012:topic:my-topic", - "organizationArn": "arn", - } - SesReceiptWorkmailAction(**event_dict) +def test_ses_trigger_event(): + event_dict = load_event("sesEvent.json") + handle_ses(event_dict, LambdaContext()) From 831252a14dcd8c620f850eeacbc72eb0f569b6ee Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 29 Apr 2022 10:11:18 -0700 Subject: [PATCH 3/3] docs: Extract clear cache examples --- .../parameters/testing_parameters_app.py | 10 ++++ .../testing_parameters_clear_cache.py | 22 +++++++ .../testing_parameters_global_clear_caches.py | 24 ++++++++ docs/utilities/parameters.md | 59 ++----------------- 4 files changed, 60 insertions(+), 55 deletions(-) create mode 100644 docs/examples/utilities/parameters/testing_parameters_app.py create mode 100644 docs/examples/utilities/parameters/testing_parameters_clear_cache.py create mode 100644 docs/examples/utilities/parameters/testing_parameters_global_clear_caches.py diff --git a/docs/examples/utilities/parameters/testing_parameters_app.py b/docs/examples/utilities/parameters/testing_parameters_app.py new file mode 100644 index 00000000000..b505749e31b --- /dev/null +++ b/docs/examples/utilities/parameters/testing_parameters_app.py @@ -0,0 +1,10 @@ +from botocore.config import Config + +from aws_lambda_powertools.utilities import parameters + +ssm_provider = parameters.SSMProvider(config=Config(region_name="us-west-1")) + + +def handler(event, context): + value = ssm_provider.get("/my/parameter") + return {"message": value} diff --git a/docs/examples/utilities/parameters/testing_parameters_clear_cache.py b/docs/examples/utilities/parameters/testing_parameters_clear_cache.py new file mode 100644 index 00000000000..0d590a3938c --- /dev/null +++ b/docs/examples/utilities/parameters/testing_parameters_clear_cache.py @@ -0,0 +1,22 @@ +import pytest +from src import app + + +@pytest.fixture(scope="function", autouse=True) +def clear_parameters_cache(): + yield + app.ssm_provider.clear_cache() # This will clear SSMProvider cache + + +@pytest.fixture +def mock_parameter_response(monkeypatch): + def mockreturn(name): + return "mock_value" + + monkeypatch.setattr(app.ssm_provider, "get", mockreturn) + + +# Pass our fixture as an argument to all tests where we want to mock the get_parameter response +def test_handler(mock_parameter_response): + return_val = app.handler({}, {}) + assert return_val.get("message") == "mock_value" diff --git a/docs/examples/utilities/parameters/testing_parameters_global_clear_caches.py b/docs/examples/utilities/parameters/testing_parameters_global_clear_caches.py new file mode 100644 index 00000000000..27457889ee9 --- /dev/null +++ b/docs/examples/utilities/parameters/testing_parameters_global_clear_caches.py @@ -0,0 +1,24 @@ +import pytest +from src import app + +from aws_lambda_powertools.utilities import parameters + + +@pytest.fixture(scope="function", autouse=True) +def clear_parameters_cache(): + yield + parameters.clear_caches() # This will clear all providers cache + + +@pytest.fixture +def mock_parameter_response(monkeypatch): + def mockreturn(name): + return "mock_value" + + monkeypatch.setattr(app.ssm_provider, "get", mockreturn) + + +# Pass our fixture as an argument to all tests where we want to mock the get_parameter response +def test_handler(mock_parameter_response): + return_val = app.handler({}, {}) + assert return_val.get("message") == "mock_value" diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index b052174dd05..e1fd9ad79d7 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -346,74 +346,23 @@ object named `get_parameter_mock`. --8<-- "docs/examples/utilities/parameters/testing_parameters_mock.py" ``` - ### Clearing cache Parameters utility caches all parameter values for performance and cost reasons. However, this can have unintended interference in tests using the same parameter name. Within your tests, you can use `clear_cache` method available in [every provider](#built-in-provider-class). When using multiple providers or higher level functions like `get_parameter`, use `clear_caches` standalone function to clear cache globally. - === "clear_cache method" - ```python hl_lines="9" - import pytest - - from src import app - - - @pytest.fixture(scope="function", autouse=True) - def clear_parameters_cache(): - yield - app.ssm_provider.clear_cache() # This will clear SSMProvider cache - - @pytest.fixture - def mock_parameter_response(monkeypatch): - def mockreturn(name): - return "mock_value" - - monkeypatch.setattr(app.ssm_provider, "get", mockreturn) - - # Pass our fixture as an argument to all tests where we want to mock the get_parameter response - def test_handler(mock_parameter_response): - return_val = app.handler({}, {}) - assert return_val.get('message') == 'mock_value' + ```python hl_lines="8" + --8<-- "docs/examples/utilities/parameters/testing_parameters_clear_cache.py" ``` === "global clear_caches" ```python hl_lines="10" - import pytest - - from aws_lambda_powertools.utilities import parameters - from src import app - - - @pytest.fixture(scope="function", autouse=True) - def clear_parameters_cache(): - yield - parameters.clear_caches() # This will clear all providers cache - - @pytest.fixture - def mock_parameter_response(monkeypatch): - def mockreturn(name): - return "mock_value" - - monkeypatch.setattr(app.ssm_provider, "get", mockreturn) - - # Pass our fixture as an argument to all tests where we want to mock the get_parameter response - def test_handler(mock_parameter_response): - return_val = app.handler({}, {}) - assert return_val.get('message') == 'mock_value' + --8<-- "docs/examples/utilities/parameters/testing_parameters_global_clear_caches.py" ``` === "app.py" ```python - from aws_lambda_powertools.utilities import parameters - from botocore.config import Config - - ssm_provider = parameters.SSMProvider(config=Config(region_name="us-west-1")) - - - def handler(event, context): - value = ssm_provider.get("/my/parameter") - return {"message": value} + --8<-- "docs/examples/utilities/parameters/testing_parameters_app.py" ```