From 4672eb571e28344777fce0d15a6d350f8b0a58af Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 28 Sep 2022 18:41:56 +0100 Subject: [PATCH 01/14] feat(v2/appconfigdata): refactoring new api calls --- .../utilities/parameters/appconfig.py | 38 ++- .../utilities/parameters/base.py | 4 +- poetry.lock | 234 +++++++++++++++--- pyproject.toml | 1 + 4 files changed, 222 insertions(+), 55 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index 380e355d673..cb7b38abd5c 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -11,7 +11,7 @@ from botocore.config import Config if TYPE_CHECKING: - from mypy_boto3_appconfig import AppConfigClient + from mypy_boto3_appconfigdata import AppConfigDataClient from ...shared import constants from ...shared.functions import resolve_env_var_choice @@ -34,8 +34,8 @@ class AppConfigProvider(BaseProvider): Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional Boto3 session to create a boto3_client from - boto3_client: AppConfigClient, optional - Boto3 AppConfig Client to use, boto3_session will be ignored if both are provided + boto3_client: AppConfigDataClient, optional + Boto3 AppConfigData Client to use, boto3_session will be ignored if both are provided Example ------- @@ -73,7 +73,7 @@ def __init__( application: Optional[str] = None, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, - boto3_client: Optional["AppConfigClient"] = None, + boto3_client: Optional["AppConfigDataClient"] = None, ): """ Initialize the App Config client @@ -81,8 +81,8 @@ def __init__( super().__init__() - self.client: "AppConfigClient" = self._build_boto3_client( - service_name="appconfig", client=boto3_client, session=boto3_session, config=config + self.client: "AppConfigDataClient" = self._build_boto3_client( + service_name="appconfigdata", client=boto3_client, session=boto3_session, config=config ) self.application = resolve_env_var_choice( @@ -91,6 +91,10 @@ def __init__( self.environment = environment self.current_version = "" + # new appconfidgdata apis + self.api_token = "" + self.last_returned_value = "" + def _get(self, name: str, **sdk_options) -> str: """ Retrieve a parameter value from AWS App config. @@ -102,14 +106,24 @@ def _get(self, name: str, **sdk_options) -> str: sdk_options: dict, optional Dictionary of options that will be passed to the client's get_configuration API call """ + if not self.api_token: + print("TOKEN") + sdk_options["ConfigurationProfileIdentifier"] = name + sdk_options["ApplicationIdentifier"] = self.application + sdk_options["EnvironmentIdentifier"] = self.environment + + response_configuration = self.client.start_configuration_session(**sdk_options) + + self.api_token = response_configuration["InitialConfigurationToken"] + + response = self.client.get_latest_configuration(ConfigurationToken=self.api_token) + return_value = response["Content"].read() + self.api_token = response["NextPollConfigurationToken"] - sdk_options["Configuration"] = name - sdk_options["Application"] = self.application - sdk_options["Environment"] = self.environment - sdk_options["ClientId"] = CLIENT_ID + if return_value: + self.last_returned_value = return_value - response = self.client.get_configuration(**sdk_options) - return response["Content"].read() # read() of botocore.response.StreamingBody + return self.last_returned_value def _get_multiple(self, path: str, **sdk_options) -> Dict[str, str]: """ diff --git a/aws_lambda_powertools/utilities/parameters/base.py b/aws_lambda_powertools/utilities/parameters/base.py index ce03b757618..b76b16e1dd8 100644 --- a/aws_lambda_powertools/utilities/parameters/base.py +++ b/aws_lambda_powertools/utilities/parameters/base.py @@ -15,7 +15,7 @@ from .exceptions import GetParameterError, TransformParameterError if TYPE_CHECKING: - from mypy_boto3_appconfig import AppConfigClient + from mypy_boto3_appconfigdata import AppConfigDataClient from mypy_boto3_dynamodb import DynamoDBServiceResource from mypy_boto3_secretsmanager import SecretsManagerClient from mypy_boto3_ssm import SSMClient @@ -28,7 +28,7 @@ TRANSFORM_METHOD_JSON = "json" TRANSFORM_METHOD_BINARY = "binary" SUPPORTED_TRANSFORM_METHODS = [TRANSFORM_METHOD_JSON, TRANSFORM_METHOD_BINARY] -ParameterClients = Union["AppConfigClient", "SecretsManagerClient", "SSMClient"] +ParameterClients = Union["AppConfigDataClient", "SecretsManagerClient", "SSMClient"] class BaseProvider(ABC): diff --git a/poetry.lock b/poetry.lock index 724530c4902..4fa87c3c212 100644 --- a/poetry.lock +++ b/poetry.lock @@ -751,6 +751,17 @@ python-versions = ">=3.7" [package.dependencies] typing-extensions = ">=4.1.0" +[[package]] +name = "mypy-boto3-appconfigdata" +version = "1.24.36.post1" +description = "Type annotations for boto3.AppConfigData 1.24.36 service generated with mypy-boto3-builder 7.10.0" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +typing-extensions = ">=4.1.0" + [[package]] name = "mypy-boto3-cloudformation" version = "1.24.36.post1" @@ -1375,7 +1386,7 @@ pydantic = ["pydantic", "email-validator"] [metadata] lock-version = "1.1" python-versions = "^3.7.4" -content-hash = "03d9159363c50e6138020a6d20bafc2d5e29785fdf54e9475a61ae01327e10e2" +content-hash = "6eced9fba14d87a4e215ed02c999aa7f6a8a22b36f46da853a383379bab87851" [metadata.files] attrs = [ @@ -1394,7 +1405,10 @@ aws-cdk-lib = [ {file = "aws-cdk.aws-apigatewayv2-integrations-alpha-2.42.1a0.tar.gz", hash = "sha256:a22a842ce30171791e9c756053be6f1003ce2db69f470e5744647706756ea74e"}, {file = "aws_cdk.aws_apigatewayv2_integrations_alpha-2.42.1a0-py3-none-any.whl", hash = "sha256:eb5f901b288bfbb4f82d1ca8d4946c69c906a13f37c7d7dbad3446e1fc1b55f5"}, ] -aws-xray-sdk = [] +aws-xray-sdk = [ + {file = "aws-xray-sdk-2.10.0.tar.gz", hash = "sha256:9b14924fd0628cf92936055864655354003f0b1acc3e1c3ffde6403d0799dd7a"}, + {file = "aws_xray_sdk-2.10.0-py2.py3-none-any.whl", hash = "sha256:7551e81a796e1a5471ebe84844c40e8edf7c218db33506d046fec61f7495eda4"}, +] bandit = [ {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, @@ -1432,7 +1446,10 @@ botocore = [ {file = "botocore-1.27.76-py3-none-any.whl", hash = "sha256:0d4a67801e5a4be4cd84795320ad9fb8c315ed1e7a63e1191b2b3f7a7171d43c"}, {file = "botocore-1.27.76.tar.gz", hash = "sha256:b5c32922eba727a466f171dcc281f309d2a313e2f6dc592d43044caad96de338"}, ] -cattrs = [] +cattrs = [ + {file = "cattrs-22.1.0-py3-none-any.whl", hash = "sha256:d55c477b4672f93606e992049f15d526dc7867e6c756cd6256d4af92e2b1e364"}, + {file = "cattrs-22.1.0.tar.gz", hash = "sha256:94b67b64cf92c994f8784c40c082177dc916e0489a73a9a36b24eb18a9db40c6"}, +] certifi = [ {file = "certifi-2022.9.14-py3-none-any.whl", hash = "sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516"}, {file = "certifi-2022.9.14.tar.gz", hash = "sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5"}, @@ -1449,7 +1466,10 @@ click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] -colorama = [] +colorama = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] constructs = [ {file = "constructs-10.1.107-py3-none-any.whl", hash = "sha256:9c764457e12f1a7eb32b7c919794e04c475f790dddd64380e7f453bf41ab84da"}, {file = "constructs-10.1.107.tar.gz", hash = "sha256:f0ba2f67c5e7da4808057be204fee2325ff9cc63ee7dcc6a83e3c3f6121cb27d"}, @@ -1506,7 +1526,10 @@ coverage = [ {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"}, {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"}, ] -decorator = [] +decorator = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] dnspython = [ {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"}, {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"}, @@ -1523,7 +1546,10 @@ exceptiongroup = [ {file = "exceptiongroup-1.0.0rc9-py3-none-any.whl", hash = "sha256:2e3c3fc1538a094aab74fad52d6c33fc94de3dfee3ee01f187c0e0c72aec5337"}, {file = "exceptiongroup-1.0.0rc9.tar.gz", hash = "sha256:9086a4a21ef9b31c72181c77c040a074ba0889ee56a7b289ff0afb0d97655f96"}, ] -execnet = [] +execnet = [ + {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, + {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, +] fastjsonschema = [ {file = "fastjsonschema-2.16.2-py3-none-any.whl", hash = "sha256:21f918e8d9a1a4ba9c22e09574ba72267a6762d47822db9add95f6454e51cc1c"}, {file = "fastjsonschema-2.16.2.tar.gz", hash = "sha256:01e366f25d9047816fe3d288cbfc3e10541daf0af2044763f3d0ade42476da18"}, @@ -1544,7 +1570,10 @@ flake8-bugbear = [ {file = "flake8-bugbear-22.9.11.tar.gz", hash = "sha256:39236c0e97160d1ab05d9f87422173d16e925a6220b3635bfc4aee766bf8194a"}, {file = "flake8_bugbear-22.9.11-py3-none-any.whl", hash = "sha256:e74350a4cfc670e184f3433c223b1e7378f1cf8345ded6c8f12ac1a50c5df22b"}, ] -flake8-builtins = [] +flake8-builtins = [ + {file = "flake8-builtins-1.5.3.tar.gz", hash = "sha256:09998853b2405e98e61d2ff3027c47033adbdc17f9fe44ca58443d876eb00f3b"}, + {file = "flake8_builtins-1.5.3-py2.py3-none-any.whl", hash = "sha256:7706babee43879320376861897e5d1468e396a40b8918ed7bccf70e5f90b8687"}, +] flake8-comprehensions = [ {file = "flake8-comprehensions-3.10.0.tar.gz", hash = "sha256:181158f7e7aa26a63a0a38e6017cef28c6adee71278ce56ce11f6ec9c4905058"}, {file = "flake8_comprehensions-3.10.0-py3-none-any.whl", hash = "sha256:dad454fd3d525039121e98fa1dd90c46bc138708196a4ebbc949ad3c859adedb"}, @@ -1557,18 +1586,28 @@ flake8-eradicate = [ {file = "flake8-eradicate-1.3.0.tar.gz", hash = "sha256:e4c98f00d17dc8653e3388cac2624cd81e9735de2fd4a8dcf99029633ebd7a63"}, {file = "flake8_eradicate-1.3.0-py3-none-any.whl", hash = "sha256:85a71e0c5f4e07f7c6c5fec520483561fd6bd295417d622855bdeade99242e3d"}, ] -flake8-fixme = [] +flake8-fixme = [ + {file = "flake8-fixme-1.1.1.tar.gz", hash = "sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a"}, + {file = "flake8_fixme-1.1.1-py2.py3-none-any.whl", hash = "sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac"}, +] flake8-isort = [ {file = "flake8-isort-4.2.0.tar.gz", hash = "sha256:26571500cd54976bbc0cf1006ffbcd1a68dd102f816b7a1051b219616ba9fee0"}, {file = "flake8_isort-4.2.0-py3-none-any.whl", hash = "sha256:5b87630fb3719bf4c1833fd11e0d9534f43efdeba524863e15d8f14a7ef6adbf"}, ] -flake8-variables-names = [] -future = [] +flake8-variables-names = [ + {file = "flake8_variables_names-0.0.4.tar.gz", hash = "sha256:d6fa0571a807c72940b5773827c5760421ea6f8206595ff0a8ecfa01e42bf2cf"}, +] +future = [ + {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +] ghp-import = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] -gitdb = [] +gitdb = [ + {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, + {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, +] gitpython = [ {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, @@ -1581,8 +1620,14 @@ importlib-metadata = [ {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, ] -iniconfig = [] -isort = [] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, @@ -1599,7 +1644,10 @@ mako = [ {file = "Mako-1.2.2-py3-none-any.whl", hash = "sha256:8efcb8004681b5f71d09c983ad5a9e6f5c40601a6ec469148753292abc0da534"}, {file = "Mako-1.2.2.tar.gz", hash = "sha256:3724869b363ba630a272a5f89f68c070352137b8fd1757650017b7e06fda163f"}, ] -mando = [] +mando = [ + {file = "mando-0.6.4-py2.py3-none-any.whl", hash = "sha256:4ce09faec7e5192ffc3c57830e26acba0fd6cd11e1ee81af0d4df0657463bd1c"}, + {file = "mando-0.6.4.tar.gz", hash = "sha256:79feb19dc0f097daa64a1243db578e7674909b75f88ac2220f1c065c10a0d960"}, +] markdown = [ {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, @@ -1646,8 +1694,14 @@ markupsafe = [ {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, ] -mccabe = [] -mergedeep = [] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mergedeep = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] mike = [ {file = "mike-1.1.2-py3-none-any.whl", hash = "sha256:4c307c28769834d78df10f834f57f810f04ca27d248f80a75f49c6fa2d1527ca"}, {file = "mike-1.1.2.tar.gz", hash = "sha256:56c3f1794c2d0b5fdccfa9b9487beb013ca813de2e3ad0744724e9d34d40b77b"}, @@ -1656,17 +1710,50 @@ mkdocs = [ {file = "mkdocs-1.3.1-py3-none-any.whl", hash = "sha256:fda92466393127d2da830bc6edc3a625a14b436316d1caf347690648e774c4f0"}, {file = "mkdocs-1.3.1.tar.gz", hash = "sha256:a41a2ff25ce3bbacc953f9844ba07d106233cd76c88bac1f59cb1564ac0d87ed"}, ] -mkdocs-git-revision-date-plugin = [] +mkdocs-git-revision-date-plugin = [ + {file = "mkdocs_git_revision_date_plugin-0.3.2-py3-none-any.whl", hash = "sha256:2e67956cb01823dd2418e2833f3623dee8604cdf223bddd005fe36226a56f6ef"}, +] mkdocs-material = [ {file = "mkdocs_material-8.5.3-py3-none-any.whl", hash = "sha256:d194c38041d1e83560221022b3f85eec4604b35e44f5c3a488c24b88542074ed"}, {file = "mkdocs_material-8.5.3.tar.gz", hash = "sha256:43b0aa707d6f9acd836024cab2dce9330957c94a4e1e41c23ee6c8ce67b4d8c5"}, ] -mkdocs-material-extensions = [] -mypy = [] +mkdocs-material-extensions = [ + {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, + {file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"}, +] +mypy = [ + {file = "mypy-0.971-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"}, + {file = "mypy-0.971-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5"}, + {file = "mypy-0.971-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3"}, + {file = "mypy-0.971-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655"}, + {file = "mypy-0.971-cp310-cp310-win_amd64.whl", hash = "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103"}, + {file = "mypy-0.971-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca"}, + {file = "mypy-0.971-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417"}, + {file = "mypy-0.971-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09"}, + {file = "mypy-0.971-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8"}, + {file = "mypy-0.971-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0"}, + {file = "mypy-0.971-cp37-cp37m-win_amd64.whl", hash = "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2"}, + {file = "mypy-0.971-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27"}, + {file = "mypy-0.971-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856"}, + {file = "mypy-0.971-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71"}, + {file = "mypy-0.971-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27"}, + {file = "mypy-0.971-cp38-cp38-win_amd64.whl", hash = "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58"}, + {file = "mypy-0.971-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6"}, + {file = "mypy-0.971-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe"}, + {file = "mypy-0.971-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9"}, + {file = "mypy-0.971-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf"}, + {file = "mypy-0.971-cp39-cp39-win_amd64.whl", hash = "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0"}, + {file = "mypy-0.971-py3-none-any.whl", hash = "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9"}, + {file = "mypy-0.971.tar.gz", hash = "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56"}, +] mypy-boto3-appconfig = [ {file = "mypy-boto3-appconfig-1.24.36.post1.tar.gz", hash = "sha256:e1916b3754915cb411ef977083500e1f30f81f7b3aea6ff5eed1cec91944dea6"}, {file = "mypy_boto3_appconfig-1.24.36.post1-py3-none-any.whl", hash = "sha256:a5dbe549dbebf4bc7a6cfcbfa9dff89ceb4983c042b785763ee656504bdb49f6"}, ] +mypy-boto3-appconfigdata = [ + {file = "mypy-boto3-appconfigdata-1.24.36.post1.tar.gz", hash = "sha256:48c0b29a99f5e5a54a4585a4b3661bc00c7db40e481c5d014a4bfd86d1ae645e"}, + {file = "mypy_boto3_appconfigdata-1.24.36.post1-py3-none-any.whl", hash = "sha256:2bc495e6b6bd358d78d30f84b750d17ac326b2b4356a7786d0d1334812416edd"}, +] mypy-boto3-cloudformation = [ {file = "mypy-boto3-cloudformation-1.24.36.post1.tar.gz", hash = "sha256:ed7df9ae3a8390a145229122a1489d0a58bbf9986cb54f0d7a65ed54f12c8e63"}, {file = "mypy_boto3_cloudformation-1.24.36.post1-py3-none-any.whl", hash = "sha256:b39020c13a876bb18908aad22326478d0ac3faec0bdac0d2c11dc318c9dcf149"}, @@ -1703,8 +1790,14 @@ mypy-boto3-xray = [ {file = "mypy-boto3-xray-1.24.36.post1.tar.gz", hash = "sha256:104f1ecf7f1f6278c582201e71a7ab64843d3a3fdc8f23295cf68788cc77e9bb"}, {file = "mypy_boto3_xray-1.24.36.post1-py3-none-any.whl", hash = "sha256:97b9f0686c717c8be99ac06cb52febaf71712b4e4cd0b61ed2eb5ed012a9b5fd"}, ] -mypy-extensions = [] -packaging = [] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] pathspec = [ {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, @@ -1713,15 +1806,29 @@ pbr = [ {file = "pbr-5.10.0-py2.py3-none-any.whl", hash = "sha256:da3e18aac0a3c003e9eea1a81bd23e5a3a75d745670dcf736317b7d966887fdf"}, {file = "pbr-5.10.0.tar.gz", hash = "sha256:cfcc4ff8e698256fc17ea3ff796478b050852585aa5bae79ecd05b2ab7b39b9a"}, ] -pdoc3 = [] +pdoc3 = [ + {file = "pdoc3-0.10.0-py3-none-any.whl", hash = "sha256:ba45d1ada1bd987427d2bf5cdec30b2631a3ff5fb01f6d0e77648a572ce6028b"}, + {file = "pdoc3-0.10.0.tar.gz", hash = "sha256:5f22e7bcb969006738e1aa4219c75a32f34c2d62d46dc9d2fb2d3e0b0287e4b7"}, +] platformdirs = [ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, ] -pluggy = [] -publication = [] -py = [] -py-cpuinfo = [] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +publication = [ + {file = "publication-0.0.3-py2.py3-none-any.whl", hash = "sha256:0248885351febc11d8a1098d5c8e3ab2dabcf3e8c0c96db1e17ecd12b53afbe6"}, + {file = "publication-0.0.3.tar.gz", hash = "sha256:68416a0de76dddcdd2930d1c8ef853a743cc96c82416c4e4d3b5d901c6276dc4"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +py-cpuinfo = [ + {file = "py-cpuinfo-8.0.0.tar.gz", hash = "sha256:5f269be0e08e33fd959de96b34cd4aeeeacac014dd8305f70eb28d06de2345c5"}, +] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, @@ -1772,22 +1879,46 @@ pygments = [ {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] -pymdown-extensions = [] -pyparsing = [] +pymdown-extensions = [ + {file = "pymdown_extensions-9.5-py3-none-any.whl", hash = "sha256:ec141c0f4983755349f0c8710416348d1a13753976c028186ed14f190c8061c4"}, + {file = "pymdown_extensions-9.5.tar.gz", hash = "sha256:3ef2d998c0d5fa7eb09291926d90d69391283561cf6306f85cd588a5eb5befa0"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] pytest = [ {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, ] -pytest-asyncio = [] -pytest-benchmark = [] -pytest-cov = [] -pytest-forked = [] +pytest-asyncio = [ + {file = "pytest-asyncio-0.16.0.tar.gz", hash = "sha256:7496c5977ce88c34379df64a66459fe395cd05543f0a2f837016e7144391fcfb"}, + {file = "pytest_asyncio-0.16.0-py3-none-any.whl", hash = "sha256:5f2a21273c47b331ae6aa5b36087047b4899e40f03f18397c0e65fa5cca54e9b"}, +] +pytest-benchmark = [ + {file = "pytest-benchmark-3.4.1.tar.gz", hash = "sha256:40e263f912de5a81d891619032983557d62a3d85843f9a9f30b98baea0cd7b47"}, + {file = "pytest_benchmark-3.4.1-py2.py3-none-any.whl", hash = "sha256:36d2b08c4882f6f997fd3126a3d6dfd70f3249cde178ed8bbc0b73db7c20f809"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +pytest-forked = [ + {file = "pytest-forked-1.4.0.tar.gz", hash = "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e"}, + {file = "pytest_forked-1.4.0-py3-none-any.whl", hash = "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"}, +] pytest-mock = [ {file = "pytest-mock-3.8.2.tar.gz", hash = "sha256:77f03f4554392558700295e05aed0b1096a20d4a60a4f3ddcde58b0c31c8fca2"}, {file = "pytest_mock-3.8.2-py3-none-any.whl", hash = "sha256:8a9e226d6c0ef09fcf20c94eb3405c388af438a90f3e39687f84166da82d5948"}, ] -pytest-xdist = [] -python-dateutil = [] +pytest-xdist = [ + {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, + {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] python-snappy = [ {file = "python-snappy-0.6.1.tar.gz", hash = "sha256:b6a107ab06206acc5359d4c5632bd9b22d448702a79b3169b0c62e0fb808bb2a"}, {file = "python_snappy-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b7f920eaf46ebf41bd26f9df51c160d40f9e00b7b48471c3438cb8d027f7fb9b"}, @@ -1873,10 +2004,22 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] -pyyaml-env-tag = [] -radon = [] -requests = [] -retry = [] +pyyaml-env-tag = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] +radon = [ + {file = "radon-5.1.0-py2.py3-none-any.whl", hash = "sha256:fa74e018197f1fcb54578af0f675d8b8e2342bd8e0b72bef8197bc4c9e645f36"}, + {file = "radon-5.1.0.tar.gz", hash = "sha256:cb1d8752e5f862fb9e20d82b5f758cbc4fb1237c92c9a66450ea0ea7bf29aeee"}, +] +requests = [ + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, +] +retry = [ + {file = "retry-0.9.2-py2.py3-none-any.whl", hash = "sha256:ccddf89761fa2c726ab29391837d4327f819ea14d244c232a1d24c67a2f98606"}, + {file = "retry-0.9.2.tar.gz", hash = "sha256:f8bfa8b99b69c4506d6f5bd3b0aabf77f98cdb17f3c9fc3f5ca820033336fba4"}, +] s3transfer = [ {file = "s3transfer-0.6.0-py3-none-any.whl", hash = "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd"}, {file = "s3transfer-0.6.0.tar.gz", hash = "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"}, @@ -1885,8 +2028,14 @@ six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -smmap = [] -stevedore = [] +smmap = [ + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, +] +stevedore = [ + {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"}, + {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"}, +] tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -2034,7 +2183,10 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -xenon = [] +xenon = [ + {file = "xenon-0.9.0-py2.py3-none-any.whl", hash = "sha256:994c80c7f1c6d40596b600b93734d85a5739208f31895ef99f1e4d362caf9e35"}, + {file = "xenon-0.9.0.tar.gz", hash = "sha256:d2b9cb6c6260f771a432c1e588e51fddb17858f88f73ef641e7532f7a5f58fb8"}, +] zipp = [ {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, diff --git a/pyproject.toml b/pyproject.toml index 418d5cc4ee9..ed1ddcb96e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,7 @@ python-snappy = "^0.6.1" mkdocs-material = "^8.5.0" filelock = "^3.8.0" checksumdir = "^1.2.0" +mypy-boto3-appconfigdata = "^1.24.36" [tool.poetry.extras] pydantic = ["pydantic", "email-validator"] From dc2b500e8a4df2962211fbfe9339169609045a85 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Thu, 29 Sep 2022 13:07:56 +0100 Subject: [PATCH 02/14] feat(v2/appconfigdata): refactoring new api calls --- .../utilities/parameters/appconfig.py | 25 +++------- docs/upgrade.md | 7 +++ tests/e2e/parameters/__init__.py | 0 tests/e2e/parameters/conftest.py | 19 +++++++ .../handlers/parameter_string_handler.py | 8 +++ tests/e2e/parameters/infrastructure.py | 21 ++++++++ tests/e2e/parameters/test_parameter.py | 27 ++++++++++ tests/functional/test_utilities_parameters.py | 50 +++++++++++++++---- 8 files changed, 131 insertions(+), 26 deletions(-) create mode 100644 tests/e2e/parameters/__init__.py create mode 100644 tests/e2e/parameters/conftest.py create mode 100644 tests/e2e/parameters/handlers/parameter_string_handler.py create mode 100644 tests/e2e/parameters/infrastructure.py create mode 100644 tests/e2e/parameters/test_parameter.py diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index cb7b38abd5c..64fbc2c959e 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -5,7 +5,6 @@ import os from typing import TYPE_CHECKING, Any, Dict, Optional, Union -from uuid import uuid4 import boto3 from botocore.config import Config @@ -17,8 +16,6 @@ from ...shared.functions import resolve_env_var_choice from .base import DEFAULT_MAX_AGE_SECS, DEFAULT_PROVIDERS, BaseProvider -CLIENT_ID = str(uuid4()) - class AppConfigProvider(BaseProvider): """ @@ -91,8 +88,7 @@ def __init__( self.environment = environment self.current_version = "" - # new appconfidgdata apis - self.api_token = "" + self.next_call = "" self.last_returned_value = "" def _get(self, name: str, **sdk_options) -> str: @@ -104,21 +100,18 @@ def _get(self, name: str, **sdk_options) -> str: name: str Name of the configuration sdk_options: dict, optional - Dictionary of options that will be passed to the client's get_configuration API call + Dictionary of options that will be passed to the client's start_configuration_session API call """ - if not self.api_token: - print("TOKEN") + if not self.next_call: sdk_options["ConfigurationProfileIdentifier"] = name sdk_options["ApplicationIdentifier"] = self.application sdk_options["EnvironmentIdentifier"] = self.environment - response_configuration = self.client.start_configuration_session(**sdk_options) + self.next_call = response_configuration["InitialConfigurationToken"] - self.api_token = response_configuration["InitialConfigurationToken"] - - response = self.client.get_latest_configuration(ConfigurationToken=self.api_token) - return_value = response["Content"].read() - self.api_token = response["NextPollConfigurationToken"] + response = self.client.get_latest_configuration(ConfigurationToken=self.next_call) + return_value = response["Configuration"].read() + self.next_call = response["NextPollConfigurationToken"] if return_value: self.last_returned_value = return_value @@ -159,7 +152,7 @@ def get_app_config( max_age: int Maximum age of the cached value sdk_options: dict, optional - Dictionary of options that will be passed to the boto client get_configuration API call + Dictionary of options that will be passed to the boto client start_configuration_session API call Raises ------ @@ -194,8 +187,6 @@ def get_app_config( if "appconfig" not in DEFAULT_PROVIDERS: DEFAULT_PROVIDERS["appconfig"] = AppConfigProvider(environment=environment, application=application) - sdk_options["ClientId"] = CLIENT_ID - return DEFAULT_PROVIDERS["appconfig"].get( name, max_age=max_age, transform=transform, force_fetch=force_fetch, **sdk_options ) diff --git a/docs/upgrade.md b/docs/upgrade.md index 37e9a318522..d1a39cb10be 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -13,6 +13,7 @@ Changes at a glance: * The API for **event handler's `Response`** has minor changes to support multi value headers and cookies. * The **legacy SQS batch processor** was removed. * The **Idempotency key** format changed slightly, invalidating all the existing cached results. +* The **Feature Flags and AppConfig Parameter utility** API calls have changed and you must add new IAM permissions to Lambda. ???+ important Powertools for Python v2 drops suport for Python 3.6, following the Python 3.6 End-Of-Life (EOL) reached on December 23, 2021. @@ -154,3 +155,9 @@ Prior to this change, the Idempotency key was generated using only the caller fu After this change, the key is generated using the `module name` + `qualified function name` + `idempotency key` (e.g: `app.classExample.function#app.handler#282e83393862a613b612c00283fef4c8`). Using qualified names prevents distinct functions with the same name to contend for the same Idempotency key. + +## Feature Flags and AppConfig Parameter utility + +The current API is [deprecated](https://docs.aws.amazon.com/appconfig/2019-10-09/APIReference/API_GetConfiguration.html) for new accounts and we had to change this. + +No changes are required to your code, but you must add `appconfig:GetLatestConfiguration` and `appconfig:StartConfigurationSession` IAM permissions for this to work in your Lambdas. diff --git a/tests/e2e/parameters/__init__.py b/tests/e2e/parameters/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/e2e/parameters/conftest.py b/tests/e2e/parameters/conftest.py new file mode 100644 index 00000000000..f4c9d7396dd --- /dev/null +++ b/tests/e2e/parameters/conftest.py @@ -0,0 +1,19 @@ +import pytest + +from tests.e2e.parameters.infrastructure import ParametersStack + + +@pytest.fixture(autouse=True, scope="module") +def infrastructure(tmp_path_factory, worker_id): + """Setup and teardown logic for E2E test infrastructure + + Yields + ------ + Dict[str, str] + CloudFormation Outputs from deployed infrastructure + """ + stack = ParametersStack() + try: + yield stack.deploy() + finally: + stack.delete() diff --git a/tests/e2e/parameters/handlers/parameter_string_handler.py b/tests/e2e/parameters/handlers/parameter_string_handler.py new file mode 100644 index 00000000000..5a35ae41558 --- /dev/null +++ b/tests/e2e/parameters/handlers/parameter_string_handler.py @@ -0,0 +1,8 @@ +from aws_lambda_powertools.utilities import parameters + + +def lambda_handler(event, context): + # Retrieve a single parameter + value = parameters.get_parameter("sample_string") + + return {"value": value} diff --git a/tests/e2e/parameters/infrastructure.py b/tests/e2e/parameters/infrastructure.py new file mode 100644 index 00000000000..49aa4e16b21 --- /dev/null +++ b/tests/e2e/parameters/infrastructure.py @@ -0,0 +1,21 @@ +from typing import Any + +from aws_cdk import CfnOutput +from aws_cdk import aws_ssm as ssm + +from tests.e2e.utils.infrastructure import BaseInfrastructure + + +class ParametersStack(BaseInfrastructure): + def create_resources(self): + functions = self.create_lambda_functions() + self._create_parameter_string(function=functions) + + def _create_parameter_string(self, function: Any): + parameter = ssm.StringParameter( + self.stack, id="string_parameter", parameter_name="sample_string", string_value="Lambda Powertools" + ) + + parameter.grant_read(function["ParameterStringHandler"]) + + CfnOutput(self.stack, "ParameterString", value=parameter.parameter_name) diff --git a/tests/e2e/parameters/test_parameter.py b/tests/e2e/parameters/test_parameter.py new file mode 100644 index 00000000000..a7a7fb300f6 --- /dev/null +++ b/tests/e2e/parameters/test_parameter.py @@ -0,0 +1,27 @@ +import json + +import pytest + +from tests.e2e.utils import data_fetcher + + +@pytest.fixture +def parameter_string_handler_fn_arn(infrastructure: dict) -> str: + return infrastructure.get("ParameterStringHandlerArn", "") + + +@pytest.fixture +def parameter_name(infrastructure: dict) -> str: + return infrastructure.get("ParameterString", "") + + +def test_simple_parameter_string(parameter_string_handler_fn_arn: str): + # GIVEN + expected_return = json.dumps({"value": "Lambda Powertools"}) + + # WHEN + parameter_execution, _ = data_fetcher.get_lambda_response(lambda_arn=parameter_string_handler_fn_arn) + parameter_value = parameter_execution["Payload"].read().decode("utf-8") + + # THEN + assert parameter_value == expected_return diff --git a/tests/functional/test_utilities_parameters.py b/tests/functional/test_utilities_parameters.py index 2b8291db47b..a3dfdb548ae 100644 --- a/tests/functional/test_utilities_parameters.py +++ b/tests/functional/test_utilities_parameters.py @@ -1641,12 +1641,24 @@ def test_appconf_provider_get_configuration_json_content_type(mock_name, config) # Stub the boto3 client stubber = stub.Stubber(provider.client) - response = {"Content": mock_value, "ConfigurationVersion": "1", "ContentType": "application/json"} - stubber.add_response("get_configuration", response) + + # start_configuration_session response + response_start = {"InitialConfigurationToken": "initial_token"} + stubber.add_response("start_configuration_session", response_start) + + # get_latest_configuration response + response_latest = { + "Configuration": mock_value, + "NextPollConfigurationToken": "initial_token", + "ContentType": "application/json", + } + stubber.add_response("get_latest_configuration", response_latest) stubber.activate() try: - value = provider.get(mock_name, transform="json", ClientConfigurationVersion="2") + value = provider.get( + mock_name, transform="json", ApplicationIdentifier=application, EnvironmentIdentifier=environment + ) assert value == mock_body_json stubber.assert_no_pending_responses() @@ -1659,7 +1671,7 @@ def test_appconf_provider_get_configuration_json_content_type_with_custom_client Test get_configuration.get with default values """ - client = boto3.client("appconfig", config=config) + client = boto3.client("appconfigdata", config=config) # Create a new provider environment = "dev" @@ -1672,12 +1684,23 @@ def test_appconf_provider_get_configuration_json_content_type_with_custom_client # Stub the boto3 client stubber = stub.Stubber(provider.client) - response = {"Content": mock_value, "ConfigurationVersion": "1", "ContentType": "application/json"} - stubber.add_response("get_configuration", response) + # start_configuration_session response + response_start = {"InitialConfigurationToken": "initial_token"} + stubber.add_response("start_configuration_session", response_start) + + # get_latest_configuration response + response_latest = { + "Configuration": mock_value, + "NextPollConfigurationToken": "initial_token", + "ContentType": "application/json", + } + stubber.add_response("get_latest_configuration", response_latest) stubber.activate() try: - value = provider.get(mock_name, transform="json", ClientConfigurationVersion="2") + value = provider.get( + mock_name, transform="json", ApplicationIdentifier=application, EnvironmentIdentifier=environment + ) assert value == mock_body_json stubber.assert_no_pending_responses() @@ -1701,8 +1724,17 @@ def test_appconf_provider_get_configuration_no_transform(mock_name, config): # Stub the boto3 client stubber = stub.Stubber(provider.client) - response = {"Content": mock_value, "ConfigurationVersion": "1", "ContentType": "application/json"} - stubber.add_response("get_configuration", response) + # start_configuration_session response + response_start = {"InitialConfigurationToken": "initial_token"} + stubber.add_response("start_configuration_session", response_start) + + # get_latest_configuration response + response_latest = { + "Configuration": mock_value, + "NextPollConfigurationToken": "initial_token", + "ContentType": "application/json", + } + stubber.add_response("get_latest_configuration", response_latest) stubber.activate() try: From 012b04519e7c2f7e7617e239a29c472b6b324023 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Sat, 1 Oct 2022 14:45:27 +0100 Subject: [PATCH 03/14] feat(v2/appconfigdata): adding more e2e tests --- .../parameter_appconfig_freeform_handler.py | 11 +++ .../handlers/parameter_string_handler.py | 3 +- tests/e2e/parameters/infrastructure.py | 81 +++++++++++++++++++ tests/e2e/parameters/test_parameter.py | 31 +++++++ 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/parameters/handlers/parameter_appconfig_freeform_handler.py diff --git a/tests/e2e/parameters/handlers/parameter_appconfig_freeform_handler.py b/tests/e2e/parameters/handlers/parameter_appconfig_freeform_handler.py new file mode 100644 index 00000000000..51b56eba95a --- /dev/null +++ b/tests/e2e/parameters/handlers/parameter_appconfig_freeform_handler.py @@ -0,0 +1,11 @@ +from aws_lambda_powertools.utilities import parameters +from aws_lambda_powertools.utilities.typing import LambdaContext + + +def lambda_handler(event: dict, context: LambdaContext): + # Retrieve a single configuration, latest version + value: bytes = parameters.get_app_config( + name=event.get("name"), environment=event.get("environment"), application=event.get("application") + ) + + return value diff --git a/tests/e2e/parameters/handlers/parameter_string_handler.py b/tests/e2e/parameters/handlers/parameter_string_handler.py index 5a35ae41558..8e5975e21e3 100644 --- a/tests/e2e/parameters/handlers/parameter_string_handler.py +++ b/tests/e2e/parameters/handlers/parameter_string_handler.py @@ -1,7 +1,8 @@ from aws_lambda_powertools.utilities import parameters +from aws_lambda_powertools.utilities.typing import LambdaContext -def lambda_handler(event, context): +def lambda_handler(event: dict, context: LambdaContext): # Retrieve a single parameter value = parameters.get_parameter("sample_string") diff --git a/tests/e2e/parameters/infrastructure.py b/tests/e2e/parameters/infrastructure.py index 49aa4e16b21..c3406836b2a 100644 --- a/tests/e2e/parameters/infrastructure.py +++ b/tests/e2e/parameters/infrastructure.py @@ -1,6 +1,9 @@ +from pyclbr import Function from typing import Any from aws_cdk import CfnOutput +from aws_cdk import aws_appconfig as appconfig +from aws_cdk import aws_iam as iam from aws_cdk import aws_ssm as ssm from tests.e2e.utils.infrastructure import BaseInfrastructure @@ -10,6 +13,7 @@ class ParametersStack(BaseInfrastructure): def create_resources(self): functions = self.create_lambda_functions() self._create_parameter_string(function=functions) + self._create_app_config(function=functions) def _create_parameter_string(self, function: Any): parameter = ssm.StringParameter( @@ -19,3 +23,80 @@ def _create_parameter_string(self, function: Any): parameter.grant_read(function["ParameterStringHandler"]) CfnOutput(self.stack, "ParameterString", value=parameter.parameter_name) + + def _create_app_config(self, function: Any): + + cfn_application = appconfig.CfnApplication(self.stack, id="appconfig-app", name="appe2e", description="appe2e") + CfnOutput(self.stack, "AppConfigApplication", value=cfn_application.name) + + cfn_environment = appconfig.CfnEnvironment( + self.stack, "appconfig-env", application_id=cfn_application.ref, name="enve2e", description="enve2e" + ) + CfnOutput(self.stack, "AppConfigEnvironment", value=cfn_environment.name) + + cfn_deployment_strategy = appconfig.CfnDeploymentStrategy( + self.stack, + "appconfig-deployment-strategy", + deployment_duration_in_minutes=0, + final_bake_time_in_minutes=0, + growth_factor=100, + name="deploymente2e", + description="deploymente2e", + replicate_to="NONE", + growth_type="LINEAR", + ) + + self._create_app_config_freeform( + app=cfn_application, environment=cfn_environment, strategy=cfn_deployment_strategy, function=function + ) + + def _create_app_config_freeform( + self, + app: appconfig.CfnApplication, + environment: appconfig.CfnEnvironment, + strategy: appconfig.CfnDeploymentStrategy, + function: Function, + ): + + cfn_configuration_profile = appconfig.CfnConfigurationProfile( + self.stack, + "appconfig-profile", + application_id=app.ref, + location_uri="hosted", + type="AWS.Freeform", + name="profilee2e", + description="profilee2e", + ) + CfnOutput(self.stack, "AppConfigProfile", value=cfn_configuration_profile.name) + + cfn_hosted_configuration_version = appconfig.CfnHostedConfigurationVersion( + self.stack, + "appconfig-hosted-deploy", + application_id=app.ref, + configuration_profile_id=cfn_configuration_profile.ref, + content='{"save_history": {"default": true}}', + content_type="application/json", + description="hostedconfiguratione2e", + ) + + appconfig.CfnDeployment( + self.stack, + "appconfig-deployment", + application_id=app.ref, + configuration_profile_id=cfn_configuration_profile.ref, + configuration_version=cfn_hosted_configuration_version.ref, + deployment_strategy_id=strategy.ref, + environment_id=environment.ref, + description="deployment", + ) + + function["ParameterAppconfigFreeformHandler"].add_to_role_policy( + iam.PolicyStatement( + effect=iam.Effect.ALLOW, + actions=[ + "appconfig:GetLatestConfiguration", + "appconfig:StartConfigurationSession", + ], + resources=["*"], + ) + ) diff --git a/tests/e2e/parameters/test_parameter.py b/tests/e2e/parameters/test_parameter.py index a7a7fb300f6..0f1ae2c3f7c 100644 --- a/tests/e2e/parameters/test_parameter.py +++ b/tests/e2e/parameters/test_parameter.py @@ -10,6 +10,21 @@ def parameter_string_handler_fn_arn(infrastructure: dict) -> str: return infrastructure.get("ParameterStringHandlerArn", "") +@pytest.fixture +def parameter_appconfig_freeform_handler_fn_arn(infrastructure: dict) -> str: + return infrastructure.get("ParameterAppconfigFreeformHandlerArn", "") + + +@pytest.fixture +def parameter_appconfig_freeform_handler_fn(infrastructure: dict) -> str: + return infrastructure.get("ParameterAppconfigFreeformHandler", "") + + +@pytest.fixture +def secret_handler_fn_arn(infrastructure: dict) -> str: + return infrastructure.get("SecretHandlerArn", "") + + @pytest.fixture def parameter_name(infrastructure: dict) -> str: return infrastructure.get("ParameterString", "") @@ -25,3 +40,19 @@ def test_simple_parameter_string(parameter_string_handler_fn_arn: str): # THEN assert parameter_value == expected_return + + +def test_get_parameter_appconfig_freeform( + parameter_appconfig_freeform_handler_fn_arn: str, parameter_appconfig_freeform_handler_fn: str +): + # GIVEN + payload = json.dumps({"name": "profilee2e", "environment": "enve2e", "application": "appe2e"}) + expected_return = json.dumps({"save_history": {"default": True}}) + + # WHEN + parameter_execution, _ = data_fetcher.get_lambda_response( + lambda_arn=parameter_appconfig_freeform_handler_fn_arn, payload=payload + ) + parameter_value = parameter_execution["Payload"].read().decode("utf-8") + + assert parameter_value == expected_return From 0f6b4310d42a77f7e76b77bf1a7d9c4733df2964 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Mon, 10 Oct 2022 22:25:04 +0100 Subject: [PATCH 04/14] feat(v2/appconfigdata): fixing small things --- .../utilities/parameters/appconfig.py | 3 ++ docs/utilities/feature_flags.md | 2 +- docs/utilities/parameters.md | 17 ++++---- tests/e2e/parameters/infrastructure.py | 2 + tests/e2e/parameters/test_appconfig.py | 36 ++++++++++++++++ tests/e2e/parameters/test_parameter.py | 41 ++----------------- tests/functional/test_utilities_parameters.py | 10 ----- 7 files changed, 55 insertions(+), 56 deletions(-) create mode 100644 tests/e2e/parameters/test_appconfig.py diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index 64fbc2c959e..c6be74e44a7 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -109,6 +109,9 @@ def _get(self, name: str, **sdk_options) -> str: response_configuration = self.client.start_configuration_session(**sdk_options) self.next_call = response_configuration["InitialConfigurationToken"] + # The new AppConfig APIs require two API calls to return the configuration + # First we start the session and after that we retrieve the configuration + # We need to store the token to use in the next execution response = self.client.get_latest_configuration(ConfigurationToken=self.next_call) return_value = response["Configuration"].read() self.next_call = response["NextPollConfigurationToken"] diff --git a/docs/utilities/feature_flags.md b/docs/utilities/feature_flags.md index 1d586d9377d..859d9dd1914 100644 --- a/docs/utilities/feature_flags.md +++ b/docs/utilities/feature_flags.md @@ -38,7 +38,7 @@ If you want to learn more about feature flags, their variations and trade-offs, ### IAM Permissions -Your Lambda function must have `appconfig:GetConfiguration` IAM permission in order to fetch configuration from AWS AppConfig. +Your Lambda function must have `appconfig:GetLatestConfiguration` and `appconfig:StartConfigurationSession` IAM permissions in order to fetch configuration from AWS AppConfig. ### Required resources diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 2559044b632..6b7d64b66b9 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -24,14 +24,15 @@ This utility requires additional permissions to work as expected. ???+ note Different parameter providers require different permissions. -| Provider | Function/Method | IAM Permission | -| ------------------- | ---------------------------------------------------- | ------------------------------- | -| SSM Parameter Store | `get_parameter`, `SSMProvider.get` | `ssm:GetParameter` | -| SSM Parameter Store | `get_parameters`, `SSMProvider.get_multiple` | `ssm:GetParametersByPath` | -| Secrets Manager | `get_secret`, `SecretsManager.get` | `secretsmanager:GetSecretValue` | -| DynamoDB | `DynamoDBProvider.get` | `dynamodb:GetItem` | -| DynamoDB | `DynamoDBProvider.get_multiple` | `dynamodb:Query` | -| App Config | `AppConfigProvider.get_app_config`, `get_app_config` | `appconfig:GetConfiguration` | +| Provider | Function/Method | IAM Permission | +| ------------------- | -----------------------------------------------------------------| -----------------------------------------------------------------------------| +| SSM Parameter Store | `get_parameter`, `SSMProvider.get` | `ssm:GetParameter` | +| SSM Parameter Store | `get_parameters`, `SSMProvider.get_multiple` | `ssm:GetParametersByPath` | +| SSM Parameter Store | If using `decrypt=True` | You must add an additional permission `kms:Decrypt` | +| Secrets Manager | `get_secret`, `SecretsManager.get` | `secretsmanager:GetSecretValue` | +| DynamoDB | `DynamoDBProvider.get` | `dynamodb:GetItem` | +| DynamoDB | `DynamoDBProvider.get_multiple` | `dynamodb:Query` | +| App Config | `get_app_config`, `AppConfigProvider.get_app_config` | `appconfig:GetLatestConfiguration` and `appconfig:StartConfigurationSession` | ### Fetching parameters diff --git a/tests/e2e/parameters/infrastructure.py b/tests/e2e/parameters/infrastructure.py index c3406836b2a..aa5ff0117f8 100644 --- a/tests/e2e/parameters/infrastructure.py +++ b/tests/e2e/parameters/infrastructure.py @@ -23,6 +23,7 @@ def _create_parameter_string(self, function: Any): parameter.grant_read(function["ParameterStringHandler"]) CfnOutput(self.stack, "ParameterString", value=parameter.parameter_name) + CfnOutput(self.stack, "ParameterStringValue", value=parameter.string_value) def _create_app_config(self, function: Any): @@ -78,6 +79,7 @@ def _create_app_config_freeform( content_type="application/json", description="hostedconfiguratione2e", ) + CfnOutput(self.stack, "AppConfigConfigurationValue", value=cfn_hosted_configuration_version.content) appconfig.CfnDeployment( self.stack, diff --git a/tests/e2e/parameters/test_appconfig.py b/tests/e2e/parameters/test_appconfig.py new file mode 100644 index 00000000000..938a3adf898 --- /dev/null +++ b/tests/e2e/parameters/test_appconfig.py @@ -0,0 +1,36 @@ +import json + +import pytest + +from tests.e2e.utils import data_fetcher + + +@pytest.fixture +def parameter_appconfig_freeform_handler_fn_arn(infrastructure: dict) -> str: + return infrastructure.get("ParameterAppconfigFreeformHandlerArn", "") + + +@pytest.fixture +def parameter_appconfig_freeform_handler_fn(infrastructure: dict) -> str: + return infrastructure.get("ParameterAppconfigFreeformHandler", "") + + +@pytest.fixture +def parameter_appconfig_freeform_value(infrastructure: dict) -> str: + return infrastructure.get("AppConfigConfigurationValue", "") + + +def test_get_parameter_appconfig_freeform( + parameter_appconfig_freeform_handler_fn_arn: str, parameter_appconfig_freeform_value: str +): + # GIVEN + payload = json.dumps({"name": "profilee2e", "environment": "enve2e", "application": "appe2e"}) + expected_return = parameter_appconfig_freeform_value + + # WHEN + parameter_execution, _ = data_fetcher.get_lambda_response( + lambda_arn=parameter_appconfig_freeform_handler_fn_arn, payload=payload + ) + parameter_value = parameter_execution["Payload"].read().decode("utf-8") + + assert parameter_value == expected_return diff --git a/tests/e2e/parameters/test_parameter.py b/tests/e2e/parameters/test_parameter.py index 0f1ae2c3f7c..26fa5e0cb59 100644 --- a/tests/e2e/parameters/test_parameter.py +++ b/tests/e2e/parameters/test_parameter.py @@ -1,5 +1,3 @@ -import json - import pytest from tests.e2e.utils import data_fetcher @@ -11,28 +9,13 @@ def parameter_string_handler_fn_arn(infrastructure: dict) -> str: @pytest.fixture -def parameter_appconfig_freeform_handler_fn_arn(infrastructure: dict) -> str: - return infrastructure.get("ParameterAppconfigFreeformHandlerArn", "") - - -@pytest.fixture -def parameter_appconfig_freeform_handler_fn(infrastructure: dict) -> str: - return infrastructure.get("ParameterAppconfigFreeformHandler", "") - - -@pytest.fixture -def secret_handler_fn_arn(infrastructure: dict) -> str: - return infrastructure.get("SecretHandlerArn", "") - - -@pytest.fixture -def parameter_name(infrastructure: dict) -> str: - return infrastructure.get("ParameterString", "") +def parameter_string_value(infrastructure: dict) -> str: + return infrastructure.get("ParameterStringValue", "") -def test_simple_parameter_string(parameter_string_handler_fn_arn: str): +def test_simple_parameter_string(parameter_string_handler_fn_arn: str, parameter_string_value: str): # GIVEN - expected_return = json.dumps({"value": "Lambda Powertools"}) + expected_return = parameter_string_value # WHEN parameter_execution, _ = data_fetcher.get_lambda_response(lambda_arn=parameter_string_handler_fn_arn) @@ -40,19 +23,3 @@ def test_simple_parameter_string(parameter_string_handler_fn_arn: str): # THEN assert parameter_value == expected_return - - -def test_get_parameter_appconfig_freeform( - parameter_appconfig_freeform_handler_fn_arn: str, parameter_appconfig_freeform_handler_fn: str -): - # GIVEN - payload = json.dumps({"name": "profilee2e", "environment": "enve2e", "application": "appe2e"}) - expected_return = json.dumps({"save_history": {"default": True}}) - - # WHEN - parameter_execution, _ = data_fetcher.get_lambda_response( - lambda_arn=parameter_appconfig_freeform_handler_fn_arn, payload=payload - ) - parameter_value = parameter_execution["Payload"].read().decode("utf-8") - - assert parameter_value == expected_return diff --git a/tests/functional/test_utilities_parameters.py b/tests/functional/test_utilities_parameters.py index a3dfdb548ae..251c38252ca 100644 --- a/tests/functional/test_utilities_parameters.py +++ b/tests/functional/test_utilities_parameters.py @@ -1639,14 +1639,10 @@ def test_appconf_provider_get_configuration_json_content_type(mock_name, config) encoded_message = json.dumps(mock_body_json).encode("utf-8") mock_value = StreamingBody(BytesIO(encoded_message), len(encoded_message)) - # Stub the boto3 client stubber = stub.Stubber(provider.client) - - # start_configuration_session response response_start = {"InitialConfigurationToken": "initial_token"} stubber.add_response("start_configuration_session", response_start) - # get_latest_configuration response response_latest = { "Configuration": mock_value, "NextPollConfigurationToken": "initial_token", @@ -1682,13 +1678,10 @@ def test_appconf_provider_get_configuration_json_content_type_with_custom_client encoded_message = json.dumps(mock_body_json).encode("utf-8") mock_value = StreamingBody(BytesIO(encoded_message), len(encoded_message)) - # Stub the boto3 client stubber = stub.Stubber(provider.client) - # start_configuration_session response response_start = {"InitialConfigurationToken": "initial_token"} stubber.add_response("start_configuration_session", response_start) - # get_latest_configuration response response_latest = { "Configuration": mock_value, "NextPollConfigurationToken": "initial_token", @@ -1722,13 +1715,10 @@ def test_appconf_provider_get_configuration_no_transform(mock_name, config): encoded_message = json.dumps(mock_body_json).encode("utf-8") mock_value = StreamingBody(BytesIO(encoded_message), len(encoded_message)) - # Stub the boto3 client stubber = stub.Stubber(provider.client) - # start_configuration_session response response_start = {"InitialConfigurationToken": "initial_token"} stubber.add_response("start_configuration_session", response_start) - # get_latest_configuration response response_latest = { "Configuration": mock_value, "NextPollConfigurationToken": "initial_token", From 85354b6a3fed291a4147b763e4ad0dd70c35f2a3 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Tue, 11 Oct 2022 18:08:29 +0100 Subject: [PATCH 05/14] feat(v2/appconfigdata): adding documentation and fix e2e tests --- docs/utilities/feature_flags.md | 4 ++++ tests/e2e/parameters/handlers/parameter_string_handler.py | 2 +- tests/e2e/parameters/{test_parameter.py => test_ssm.py} | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) rename tests/e2e/parameters/{test_parameter.py => test_ssm.py} (89%) diff --git a/docs/utilities/feature_flags.md b/docs/utilities/feature_flags.md index 859d9dd1914..296216b349b 100644 --- a/docs/utilities/feature_flags.md +++ b/docs/utilities/feature_flags.md @@ -27,6 +27,10 @@ If you want to learn more about feature flags, their variations and trade-offs, * [Feature Toggles (aka Feature Flags) - Pete Hodgson](https://martinfowler.com/articles/feature-toggles.html) * [AWS Lambda Feature Toggles Made Simple - Ran Isenberg](https://isenberg-ran.medium.com/aws-lambda-feature-toggles-made-simple-580b0c444233) * [Feature Flags Getting Started - CloudBees](https://www.cloudbees.com/blog/ultimate-feature-flag-guide) +* [Best Practices for validating AWS AppConfig Feature Flags and Configuration Data](https://aws.amazon.com/pt/blogs/mt/best-practices-for-validating-aws-appconfig-feature-flags-and-configuration-data/) + +???+ note + Optimize your Lambda execution time by putting your feature settings in a single [Configuration](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html). ## Key features diff --git a/tests/e2e/parameters/handlers/parameter_string_handler.py b/tests/e2e/parameters/handlers/parameter_string_handler.py index 8e5975e21e3..485bca564d4 100644 --- a/tests/e2e/parameters/handlers/parameter_string_handler.py +++ b/tests/e2e/parameters/handlers/parameter_string_handler.py @@ -4,6 +4,6 @@ def lambda_handler(event: dict, context: LambdaContext): # Retrieve a single parameter - value = parameters.get_parameter("sample_string") + value: str = parameters.get_parameter("sample_string") return {"value": value} diff --git a/tests/e2e/parameters/test_parameter.py b/tests/e2e/parameters/test_ssm.py similarity index 89% rename from tests/e2e/parameters/test_parameter.py rename to tests/e2e/parameters/test_ssm.py index 26fa5e0cb59..13b154a50cc 100644 --- a/tests/e2e/parameters/test_parameter.py +++ b/tests/e2e/parameters/test_ssm.py @@ -1,3 +1,5 @@ +import json + import pytest from tests.e2e.utils import data_fetcher @@ -15,7 +17,7 @@ def parameter_string_value(infrastructure: dict) -> str: def test_simple_parameter_string(parameter_string_handler_fn_arn: str, parameter_string_value: str): # GIVEN - expected_return = parameter_string_value + expected_return = json.dumps({"value": parameter_string_value}) # WHEN parameter_execution, _ = data_fetcher.get_lambda_response(lambda_arn=parameter_string_handler_fn_arn) From 93d26b71b7ae3fb5779f0151c51aa465aee52b35 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Tue, 11 Oct 2022 18:21:10 +0100 Subject: [PATCH 06/14] feat(v2/appconfigdata): removing Any typing --- tests/e2e/parameters/infrastructure.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/e2e/parameters/infrastructure.py b/tests/e2e/parameters/infrastructure.py index aa5ff0117f8..e270e3004d8 100644 --- a/tests/e2e/parameters/infrastructure.py +++ b/tests/e2e/parameters/infrastructure.py @@ -1,5 +1,4 @@ from pyclbr import Function -from typing import Any from aws_cdk import CfnOutput from aws_cdk import aws_appconfig as appconfig @@ -12,20 +11,20 @@ class ParametersStack(BaseInfrastructure): def create_resources(self): functions = self.create_lambda_functions() - self._create_parameter_string(function=functions) - self._create_app_config(function=functions) + self._create_parameter_string(function=functions["ParameterStringHandler"]) + self._create_app_config(function=functions["ParameterAppconfigFreeformHandler"]) - def _create_parameter_string(self, function: Any): + def _create_parameter_string(self, function: Function): parameter = ssm.StringParameter( self.stack, id="string_parameter", parameter_name="sample_string", string_value="Lambda Powertools" ) - parameter.grant_read(function["ParameterStringHandler"]) + parameter.grant_read(function) CfnOutput(self.stack, "ParameterString", value=parameter.parameter_name) CfnOutput(self.stack, "ParameterStringValue", value=parameter.string_value) - def _create_app_config(self, function: Any): + def _create_app_config(self, function: Function): cfn_application = appconfig.CfnApplication(self.stack, id="appconfig-app", name="appe2e", description="appe2e") CfnOutput(self.stack, "AppConfigApplication", value=cfn_application.name) @@ -92,7 +91,7 @@ def _create_app_config_freeform( description="deployment", ) - function["ParameterAppconfigFreeformHandler"].add_to_role_policy( + function.add_to_role_policy( iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=[ From 610e2a6efb541886eb119fdcf2e39fbc6c269194 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Thu, 13 Oct 2022 22:15:36 +0100 Subject: [PATCH 07/14] feat(v2/appconfigdata): wording + variable name --- .../utilities/parameters/appconfig.py | 14 +++++++------- docs/upgrade.md | 6 +++--- docs/utilities/feature_flags.md | 5 ++--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index c6be74e44a7..a3a340a62be 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -88,7 +88,7 @@ def __init__( self.environment = environment self.current_version = "" - self.next_call = "" + self._next_token = "" # nosec - token for get_latest_configuration executions self.last_returned_value = "" def _get(self, name: str, **sdk_options) -> str: @@ -100,21 +100,21 @@ def _get(self, name: str, **sdk_options) -> str: name: str Name of the configuration sdk_options: dict, optional - Dictionary of options that will be passed to the client's start_configuration_session API call + SDK options to propagate to `start_configuration_session` API call """ - if not self.next_call: + if not self._next_token: sdk_options["ConfigurationProfileIdentifier"] = name sdk_options["ApplicationIdentifier"] = self.application sdk_options["EnvironmentIdentifier"] = self.environment response_configuration = self.client.start_configuration_session(**sdk_options) - self.next_call = response_configuration["InitialConfigurationToken"] + self._next_token = response_configuration["InitialConfigurationToken"] # The new AppConfig APIs require two API calls to return the configuration # First we start the session and after that we retrieve the configuration # We need to store the token to use in the next execution - response = self.client.get_latest_configuration(ConfigurationToken=self.next_call) + response = self.client.get_latest_configuration(ConfigurationToken=self._next_token) return_value = response["Configuration"].read() - self.next_call = response["NextPollConfigurationToken"] + self._next_token = response["NextPollConfigurationToken"] if return_value: self.last_returned_value = return_value @@ -155,7 +155,7 @@ def get_app_config( max_age: int Maximum age of the cached value sdk_options: dict, optional - Dictionary of options that will be passed to the boto client start_configuration_session API call + SDK options to propagate to `start_configuration_session` API call Raises ------ diff --git a/docs/upgrade.md b/docs/upgrade.md index d1a39cb10be..f6b3c7e9d00 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -13,7 +13,7 @@ Changes at a glance: * The API for **event handler's `Response`** has minor changes to support multi value headers and cookies. * The **legacy SQS batch processor** was removed. * The **Idempotency key** format changed slightly, invalidating all the existing cached results. -* The **Feature Flags and AppConfig Parameter utility** API calls have changed and you must add new IAM permissions to Lambda. +* The **Feature Flags and AppConfig Parameter utility** API calls have changed and you must update your IAM permissions. ???+ important Powertools for Python v2 drops suport for Python 3.6, following the Python 3.6 End-Of-Life (EOL) reached on December 23, 2021. @@ -158,6 +158,6 @@ Using qualified names prevents distinct functions with the same name to contend ## Feature Flags and AppConfig Parameter utility -The current API is [deprecated](https://docs.aws.amazon.com/appconfig/2019-10-09/APIReference/API_GetConfiguration.html) for new accounts and we had to change this. +AWS AppConfig deprecated the current API (GetConfiguration) - [more details here](https://github.com/awslabs/aws-lambda-powertools-python/issues/1506#issuecomment-1266645884). -No changes are required to your code, but you must add `appconfig:GetLatestConfiguration` and `appconfig:StartConfigurationSession` IAM permissions for this to work in your Lambdas. +You must update your IAM permissions to allow `appconfig:GetLatestConfiguration` and `appconfig:StartConfigurationSession`. There are no code changes required. diff --git a/docs/utilities/feature_flags.md b/docs/utilities/feature_flags.md index 296216b349b..a42b903a453 100644 --- a/docs/utilities/feature_flags.md +++ b/docs/utilities/feature_flags.md @@ -27,10 +27,9 @@ If you want to learn more about feature flags, their variations and trade-offs, * [Feature Toggles (aka Feature Flags) - Pete Hodgson](https://martinfowler.com/articles/feature-toggles.html) * [AWS Lambda Feature Toggles Made Simple - Ran Isenberg](https://isenberg-ran.medium.com/aws-lambda-feature-toggles-made-simple-580b0c444233) * [Feature Flags Getting Started - CloudBees](https://www.cloudbees.com/blog/ultimate-feature-flag-guide) -* [Best Practices for validating AWS AppConfig Feature Flags and Configuration Data](https://aws.amazon.com/pt/blogs/mt/best-practices-for-validating-aws-appconfig-feature-flags-and-configuration-data/) ???+ note - Optimize your Lambda execution time by putting your feature settings in a single [Configuration](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html). + AWS AppConfig requires two API calls to fetch configuration for the first time. You can improve latency by consolidating your feature settings in a single [Configuration](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html). ## Key features @@ -42,7 +41,7 @@ If you want to learn more about feature flags, their variations and trade-offs, ### IAM Permissions -Your Lambda function must have `appconfig:GetLatestConfiguration` and `appconfig:StartConfigurationSession` IAM permissions in order to fetch configuration from AWS AppConfig. +Your Lambda function IAM Role must have `appconfig:GetLatestConfiguration` and `appconfig:StartConfigurationSession` IAM permissions before using this feature. ### Required resources From fb3e4b84fd4742003b36b227b3b69484417e1939 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Thu, 13 Oct 2022 22:21:52 +0100 Subject: [PATCH 08/14] feat(v2/appconfigdata): wording + variable name --- tests/functional/test_utilities_parameters.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/functional/test_utilities_parameters.py b/tests/functional/test_utilities_parameters.py index 251c38252ca..123c2fdbcc2 100644 --- a/tests/functional/test_utilities_parameters.py +++ b/tests/functional/test_utilities_parameters.py @@ -1640,15 +1640,15 @@ def test_appconf_provider_get_configuration_json_content_type(mock_name, config) mock_value = StreamingBody(BytesIO(encoded_message), len(encoded_message)) stubber = stub.Stubber(provider.client) - response_start = {"InitialConfigurationToken": "initial_token"} - stubber.add_response("start_configuration_session", response_start) + response_start_config_session = {"InitialConfigurationToken": "initial_token"} + stubber.add_response("start_configuration_session", response_start_config_session) - response_latest = { + response_get_latest_config = { "Configuration": mock_value, "NextPollConfigurationToken": "initial_token", "ContentType": "application/json", } - stubber.add_response("get_latest_configuration", response_latest) + stubber.add_response("get_latest_configuration", response_get_latest_config) stubber.activate() try: @@ -1679,15 +1679,15 @@ def test_appconf_provider_get_configuration_json_content_type_with_custom_client mock_value = StreamingBody(BytesIO(encoded_message), len(encoded_message)) stubber = stub.Stubber(provider.client) - response_start = {"InitialConfigurationToken": "initial_token"} - stubber.add_response("start_configuration_session", response_start) + response_start_config_session = {"InitialConfigurationToken": "initial_token"} + stubber.add_response("start_configuration_session", response_start_config_session) - response_latest = { + response_get_latest_config = { "Configuration": mock_value, "NextPollConfigurationToken": "initial_token", "ContentType": "application/json", } - stubber.add_response("get_latest_configuration", response_latest) + stubber.add_response("get_latest_configuration", response_get_latest_config) stubber.activate() try: @@ -1716,15 +1716,15 @@ def test_appconf_provider_get_configuration_no_transform(mock_name, config): mock_value = StreamingBody(BytesIO(encoded_message), len(encoded_message)) stubber = stub.Stubber(provider.client) - response_start = {"InitialConfigurationToken": "initial_token"} - stubber.add_response("start_configuration_session", response_start) + response_start_config_session = {"InitialConfigurationToken": "initial_token"} + stubber.add_response("start_configuration_session", response_start_config_session) - response_latest = { + response_get_latest_config = { "Configuration": mock_value, "NextPollConfigurationToken": "initial_token", "ContentType": "application/json", } - stubber.add_response("get_latest_configuration", response_latest) + stubber.add_response("get_latest_configuration", response_get_latest_config) stubber.activate() try: From 6a3a5a73def7cd9c0c2fe029c462cf785f167b2d Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Thu, 13 Oct 2022 23:18:24 +0100 Subject: [PATCH 09/14] feat(v2/appconfigdata): e2e changes --- .../handlers/parameter_string_handler.py | 9 ----- tests/e2e/parameters/infrastructure.py | 36 ++++++++++--------- tests/e2e/parameters/test_appconfig.py | 29 +++++++++++++-- tests/e2e/parameters/test_ssm.py | 27 -------------- 4 files changed, 46 insertions(+), 55 deletions(-) delete mode 100644 tests/e2e/parameters/handlers/parameter_string_handler.py delete mode 100644 tests/e2e/parameters/test_ssm.py diff --git a/tests/e2e/parameters/handlers/parameter_string_handler.py b/tests/e2e/parameters/handlers/parameter_string_handler.py deleted file mode 100644 index 485bca564d4..00000000000 --- a/tests/e2e/parameters/handlers/parameter_string_handler.py +++ /dev/null @@ -1,9 +0,0 @@ -from aws_lambda_powertools.utilities import parameters -from aws_lambda_powertools.utilities.typing import LambdaContext - - -def lambda_handler(event: dict, context: LambdaContext): - # Retrieve a single parameter - value: str = parameters.get_parameter("sample_string") - - return {"value": value} diff --git a/tests/e2e/parameters/infrastructure.py b/tests/e2e/parameters/infrastructure.py index e270e3004d8..f5eb4d5aa3a 100644 --- a/tests/e2e/parameters/infrastructure.py +++ b/tests/e2e/parameters/infrastructure.py @@ -3,34 +3,31 @@ from aws_cdk import CfnOutput from aws_cdk import aws_appconfig as appconfig from aws_cdk import aws_iam as iam -from aws_cdk import aws_ssm as ssm +from tests.e2e.utils.data_builder import build_service_name from tests.e2e.utils.infrastructure import BaseInfrastructure class ParametersStack(BaseInfrastructure): def create_resources(self): functions = self.create_lambda_functions() - self._create_parameter_string(function=functions["ParameterStringHandler"]) self._create_app_config(function=functions["ParameterAppconfigFreeformHandler"]) - def _create_parameter_string(self, function: Function): - parameter = ssm.StringParameter( - self.stack, id="string_parameter", parameter_name="sample_string", string_value="Lambda Powertools" - ) - - parameter.grant_read(function) - - CfnOutput(self.stack, "ParameterString", value=parameter.parameter_name) - CfnOutput(self.stack, "ParameterStringValue", value=parameter.string_value) - def _create_app_config(self, function: Function): - cfn_application = appconfig.CfnApplication(self.stack, id="appconfig-app", name="appe2e", description="appe2e") + service_name = build_service_name() + + cfn_application = appconfig.CfnApplication( + self.stack, id="appconfig-app", name=f"appe2e{service_name}", description="appe2e" + ) CfnOutput(self.stack, "AppConfigApplication", value=cfn_application.name) cfn_environment = appconfig.CfnEnvironment( - self.stack, "appconfig-env", application_id=cfn_application.ref, name="enve2e", description="enve2e" + self.stack, + "appconfig-env", + application_id=cfn_application.ref, + name=f"enve2e{service_name}", + description="enve2e", ) CfnOutput(self.stack, "AppConfigEnvironment", value=cfn_environment.name) @@ -40,14 +37,18 @@ def _create_app_config(self, function: Function): deployment_duration_in_minutes=0, final_bake_time_in_minutes=0, growth_factor=100, - name="deploymente2e", + name=f"deploymente2e{service_name}", description="deploymente2e", replicate_to="NONE", growth_type="LINEAR", ) self._create_app_config_freeform( - app=cfn_application, environment=cfn_environment, strategy=cfn_deployment_strategy, function=function + app=cfn_application, + environment=cfn_environment, + strategy=cfn_deployment_strategy, + function=function, + service_name=service_name, ) def _create_app_config_freeform( @@ -56,6 +57,7 @@ def _create_app_config_freeform( environment: appconfig.CfnEnvironment, strategy: appconfig.CfnDeploymentStrategy, function: Function, + service_name: str, ): cfn_configuration_profile = appconfig.CfnConfigurationProfile( @@ -64,7 +66,7 @@ def _create_app_config_freeform( application_id=app.ref, location_uri="hosted", type="AWS.Freeform", - name="profilee2e", + name=f"profilee2e{service_name}", description="profilee2e", ) CfnOutput(self.stack, "AppConfigProfile", value=cfn_configuration_profile.name) diff --git a/tests/e2e/parameters/test_appconfig.py b/tests/e2e/parameters/test_appconfig.py index 938a3adf898..0129adb1515 100644 --- a/tests/e2e/parameters/test_appconfig.py +++ b/tests/e2e/parameters/test_appconfig.py @@ -20,11 +20,36 @@ def parameter_appconfig_freeform_value(infrastructure: dict) -> str: return infrastructure.get("AppConfigConfigurationValue", "") +@pytest.fixture +def parameter_appconfig_freeform_application(infrastructure: dict) -> str: + return infrastructure.get("AppConfigApplication", "") + + +@pytest.fixture +def parameter_appconfig_freeform_environment(infrastructure: dict) -> str: + return infrastructure.get("AppConfigEnvironment", "") + + +@pytest.fixture +def parameter_appconfig_freeform_profile(infrastructure: dict) -> str: + return infrastructure.get("AppConfigProfile", "") + + def test_get_parameter_appconfig_freeform( - parameter_appconfig_freeform_handler_fn_arn: str, parameter_appconfig_freeform_value: str + parameter_appconfig_freeform_handler_fn_arn: str, + parameter_appconfig_freeform_value: str, + parameter_appconfig_freeform_application: str, + parameter_appconfig_freeform_environment: str, + parameter_appconfig_freeform_profile: str, ): # GIVEN - payload = json.dumps({"name": "profilee2e", "environment": "enve2e", "application": "appe2e"}) + payload = json.dumps( + { + "name": parameter_appconfig_freeform_profile, + "environment": parameter_appconfig_freeform_environment, + "application": parameter_appconfig_freeform_application, + } + ) expected_return = parameter_appconfig_freeform_value # WHEN diff --git a/tests/e2e/parameters/test_ssm.py b/tests/e2e/parameters/test_ssm.py deleted file mode 100644 index 13b154a50cc..00000000000 --- a/tests/e2e/parameters/test_ssm.py +++ /dev/null @@ -1,27 +0,0 @@ -import json - -import pytest - -from tests.e2e.utils import data_fetcher - - -@pytest.fixture -def parameter_string_handler_fn_arn(infrastructure: dict) -> str: - return infrastructure.get("ParameterStringHandlerArn", "") - - -@pytest.fixture -def parameter_string_value(infrastructure: dict) -> str: - return infrastructure.get("ParameterStringValue", "") - - -def test_simple_parameter_string(parameter_string_handler_fn_arn: str, parameter_string_value: str): - # GIVEN - expected_return = json.dumps({"value": parameter_string_value}) - - # WHEN - parameter_execution, _ = data_fetcher.get_lambda_response(lambda_arn=parameter_string_handler_fn_arn) - parameter_value = parameter_execution["Payload"].read().decode("utf-8") - - # THEN - assert parameter_value == expected_return From df636cee858913b50d4abb03a62b849eee464496 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Fri, 14 Oct 2022 14:56:34 +0100 Subject: [PATCH 10/14] feat(v2/appconfigdata): small fix documentation --- docs/utilities/feature_flags.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/utilities/feature_flags.md b/docs/utilities/feature_flags.md index a42b903a453..789f583f540 100644 --- a/docs/utilities/feature_flags.md +++ b/docs/utilities/feature_flags.md @@ -3,11 +3,12 @@ title: Feature flags description: Utility --- -???+ note - This is currently in Beta, as we might change Store parameters in the next release. - The feature flags utility provides a simple rule engine to define when one or multiple features should be enabled depending on the input. +???+ info + We currently only support AppConfig using [freeform configuration profile](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html#appconfig-creating-configuration-and-profile-free-form-configurations). + We are planning to add support for the [feature flag configuration profile](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html#appconfig-creating-configuration-and-profile-feature-flags) in future releases. + ## Terminology Feature flags are used to modify behaviour without changing the application's code. These flags can be **static** or **dynamic**. From 4bdff11f197cb5effc5f9d082fba5bd91c07a899 Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Fri, 14 Oct 2022 18:30:48 +0200 Subject: [PATCH 11/14] docs(parameters): remove premature backlog commitment --- docs/utilities/feature_flags.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/utilities/feature_flags.md b/docs/utilities/feature_flags.md index 789f583f540..f35dd88106b 100644 --- a/docs/utilities/feature_flags.md +++ b/docs/utilities/feature_flags.md @@ -7,7 +7,6 @@ The feature flags utility provides a simple rule engine to define when one or mu ???+ info We currently only support AppConfig using [freeform configuration profile](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html#appconfig-creating-configuration-and-profile-free-form-configurations). - We are planning to add support for the [feature flag configuration profile](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html#appconfig-creating-configuration-and-profile-feature-flags) in future releases. ## Terminology From 92f8fd6e0eb78d4090687ce11b34252e29f0e608 Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Fri, 14 Oct 2022 18:31:05 +0200 Subject: [PATCH 12/14] chore: make resource naming traceable --- tests/e2e/parameters/infrastructure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/parameters/infrastructure.py b/tests/e2e/parameters/infrastructure.py index f5eb4d5aa3a..23f7a644746 100644 --- a/tests/e2e/parameters/infrastructure.py +++ b/tests/e2e/parameters/infrastructure.py @@ -18,7 +18,7 @@ def _create_app_config(self, function: Function): service_name = build_service_name() cfn_application = appconfig.CfnApplication( - self.stack, id="appconfig-app", name=f"appe2e{service_name}", description="appe2e" + self.stack, id="appconfig-app", name=f"powertools-e2e-{service_name}", description="Lambda Powertools End-to-End testing for AppConfig" ) CfnOutput(self.stack, "AppConfigApplication", value=cfn_application.name) From 1eb935eddaab3def1d1243e1b4832353216781e1 Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Fri, 14 Oct 2022 18:31:11 +0200 Subject: [PATCH 13/14] chore: make resource naming traceable --- tests/e2e/parameters/infrastructure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/parameters/infrastructure.py b/tests/e2e/parameters/infrastructure.py index 23f7a644746..63f7f267415 100644 --- a/tests/e2e/parameters/infrastructure.py +++ b/tests/e2e/parameters/infrastructure.py @@ -26,7 +26,7 @@ def _create_app_config(self, function: Function): self.stack, "appconfig-env", application_id=cfn_application.ref, - name=f"enve2e{service_name}", + name=f"powertools-e2e{service_name}", description="enve2e", ) CfnOutput(self.stack, "AppConfigEnvironment", value=cfn_environment.name) From f3abe063039c1d3d3910e1b53513d5b6b4cbae80 Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Fri, 14 Oct 2022 18:31:18 +0200 Subject: [PATCH 14/14] chore: make resource naming traceable --- tests/e2e/parameters/infrastructure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/parameters/infrastructure.py b/tests/e2e/parameters/infrastructure.py index 63f7f267415..e1dfb13bcdd 100644 --- a/tests/e2e/parameters/infrastructure.py +++ b/tests/e2e/parameters/infrastructure.py @@ -27,7 +27,7 @@ def _create_app_config(self, function: Function): "appconfig-env", application_id=cfn_application.ref, name=f"powertools-e2e{service_name}", - description="enve2e", + description="Lambda Powertools End-to-End testing environment", ) CfnOutput(self.stack, "AppConfigEnvironment", value=cfn_environment.name)