From e5888e1b1b1651ed9ec1cc2a740797e3cbaaf6cc Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Sat, 31 Dec 2022 13:03:42 +0200 Subject: [PATCH 1/3] chore: pip update + add mypy --- Makefile | 9 +- Pipfile | 2 + Pipfile.lock | 199 +++++++++++------- README.md | 2 +- cdk/app.py | 17 ++ cdk/{my_service => }/cdk.context.json | 0 cdk/{my_service => }/cdk.json | 0 cdk/my_service/app.py | 14 -- .../my_service/configuration/__init__.py | 0 .../configuration/configuration_construct.py | 2 +- .../configuration/json/dev_configuration.json | 0 .../configuration/schema.py | 0 .../{service_stack => }/constants.py | 0 .../{service_stack => }/service_construct.py | 6 +- .../{service_stack => }/service_stack.py | 6 +- cdk/setup.py | 4 +- dev_requirements.txt | 29 +-- .../dynamic_configuration}/__init__.py | 0 .../dynamic_configuration/cdk_appconfig.py | 4 +- .../evaluate_feature_flags.py | 6 +- .../parse_configuration.py | 2 +- .../environment_variables}/__init__.py | 0 .../environment_variables/env_vars.py | 8 +- .../environment_variables/my_handler.py | 2 +- .../input_validation/__init__.py | 0 .../best_practices/logger/__init__.py | 0 .../best_practices/metrics/__init__.py | 0 .../best_practices/tracer/__init__.py | 0 docs/pipeline.md | 1 + lambda_requirements.txt | 4 +- mypy.ini | 58 +++++ service/handlers/create_order.py | 2 +- service/handlers/schemas/env_vars.py | 16 +- service/handlers/schemas/input.py | 6 +- .../handlers/utils/dynamic_configuration.py | 8 +- service/handlers/utils/env_vars_parser.py | 8 +- service/logic/handle_create_request.py | 6 +- service/schemas/output.py | 5 +- tests/unit/test_env_vars_parser.py | 2 +- tests/unit/test_parse_configuration.py | 2 +- tests/utils.py | 2 +- 41 files changed, 279 insertions(+), 153 deletions(-) create mode 100755 cdk/app.py rename cdk/{my_service => }/cdk.context.json (100%) rename cdk/{my_service => }/cdk.json (100%) delete mode 100755 cdk/my_service/app.py rename __init__.py => cdk/my_service/configuration/__init__.py (100%) rename cdk/my_service/{service_stack => }/configuration/configuration_construct.py (97%) rename cdk/my_service/{service_stack => }/configuration/json/dev_configuration.json (100%) rename cdk/my_service/{service_stack => }/configuration/schema.py (100%) rename cdk/my_service/{service_stack => }/constants.py (100%) rename cdk/my_service/{service_stack => }/service_construct.py (96%) rename cdk/my_service/{service_stack => }/service_stack.py (81%) rename {cdk/my_service/service_stack => docs/examples/best_practices/dynamic_configuration}/__init__.py (100%) rename {cdk/my_service/service_stack/configuration => docs/examples/best_practices/environment_variables}/__init__.py (100%) create mode 100644 docs/examples/best_practices/input_validation/__init__.py create mode 100644 docs/examples/best_practices/logger/__init__.py create mode 100644 docs/examples/best_practices/metrics/__init__.py create mode 100644 docs/examples/best_practices/tracer/__init__.py create mode 100644 mypy.ini diff --git a/Makefile b/Makefile index 2b00b980..9914d3cf 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,8 @@ dev: lint: @echo "Running flake8" flake8 service/* cdk/* tests/* docs/examples/* --exclude patterns='build,cdk.json,cdk.context.json,.yaml' + @echo "Running mypy" + make mypy-lint complex: @echo "Running Radon" @@ -22,6 +24,9 @@ sort: pre-commit: pre-commit run -a --show-diff-on-failure +mypy-lint: + mypy --pretty service docs/examples cdk + deps: pipenv requirements --dev > dev_requirements.txt pipenv requirements > lambda_requirements.txt @@ -47,10 +52,10 @@ deploy: make deps mkdir -p .build/lambdas ; cp -r service .build/lambdas mkdir -p .build/common_layer ; pipenv requirements > .build/common_layer/requirements.txt - cdk deploy --app="python3 ${PWD}/cdk/my_service/app.py" --require-approval=never + cdk deploy --app="python3 ${PWD}/cdk/app.py" --require-approval=never destroy: - cdk destroy --app="python3 ${PWD}/cdk/my_service/app.py" --force + cdk destroy --app="python3 ${PWD}/cdk/app.py" --force docs: mkdocs serve diff --git a/Pipfile b/Pipfile index e6cdb394..72211a31 100644 --- a/Pipfile +++ b/Pipfile @@ -23,6 +23,8 @@ boto3 = "*" mkdocs-material = "*" mkdocs-git-revision-date-plugin = "*" setuptools = ">=65.5.1" +types-cachetools = "*" +mypy = "*" [packages] aws-lambda-powertools= {extras = ["all"],version = "*"} diff --git a/Pipfile.lock b/Pipfile.lock index 64a145a6..315db0cf 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2d9f962f2ed0e3269c6f35928e54032c7d2661512746ad8eb916cb0b572a02c8" + "sha256": "f636571265771441c569ad6b933aeb35648e1568db5c229be2d13843733eeaae" }, "pipfile-spec": 6, "requires": { @@ -36,11 +36,11 @@ }, "botocore": { "hashes": [ - "sha256:18ab8e95345a6d0d2653ce65d261a0aef6fef8a57a35a89e3cea6ffe315e92fc", - "sha256:3afa4fec9f7713caa05116563b38f81bec7bd20585d517155484d3f25efab5aa" + "sha256:78761227d986d393956b6d08fdadcfe142748828e0e9db33f2f4c42a482dcd35", + "sha256:b670b7f8958a2908167081efb6ea39794bf61d618be729984629a63d85cf8bfe" ], "markers": "python_version >= '3.7'", - "version": "==1.29.37" + "version": "==1.29.41" }, "cachetools": { "hashes": [ @@ -75,44 +75,44 @@ }, "pydantic": { "hashes": [ - "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42", - "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624", - "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e", - "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559", - "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709", - "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9", - "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d", - "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52", - "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda", - "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912", - "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c", - "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525", - "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe", - "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41", - "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b", - "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283", - "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965", - "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c", - "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410", - "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5", - "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116", - "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98", - "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f", - "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644", - "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13", - "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd", - "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254", - "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6", - "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488", - "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5", - "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c", - "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1", - "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a", - "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2", - "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d", - "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236" - ], - "version": "==1.10.2" + "sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72", + "sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423", + "sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f", + "sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c", + "sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06", + "sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53", + "sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774", + "sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6", + "sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c", + "sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f", + "sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6", + "sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3", + "sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817", + "sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903", + "sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a", + "sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e", + "sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d", + "sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85", + "sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00", + "sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28", + "sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3", + "sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024", + "sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4", + "sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e", + "sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d", + "sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa", + "sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854", + "sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15", + "sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648", + "sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8", + "sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c", + "sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857", + "sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f", + "sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416", + "sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978", + "sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d" + ], + "version": "==1.10.4" }, "python-dateutil": { "hashes": [ @@ -228,19 +228,19 @@ }, "aws-cdk-lib": { "hashes": [ - "sha256:230507e036060a69f9251f1e2d08b2505096f39c65089db31b984ea5a0346098", - "sha256:f8255be5e5760bc9cf33acbbfa79fb539f85a325f4204e432cab9edd2599743b" + "sha256:457e366a7d34bb391be19f2b51ee2cc1b16fd6f0cd5fa23aa0fbbd2c80a70eca", + "sha256:e53ae6590ca1f36f16780021ef260345a6e251b4195b0389d9d5f693b2fa187a" ], "markers": "python_version ~= '3.7'", - "version": "==2.56.1" + "version": "==2.58.1" }, "aws-cdk.asset-awscli-v1": { "hashes": [ - "sha256:5039a8ad35b140d8db61e19ccfa590b474072071b31c1c322011465bad4bcc56", - "sha256:79f8bd8f784c8e7c6cf4fa410ddecbae393e9d4850e42bfeeb4768662d89fb68" + "sha256:6211ff042c292178b89383c64c2c84d1e2fed818619b92eb67d32cbd92991cc1", + "sha256:fbd3a5988ddfec8aacd5ccf0ecfbf689a2a21daef1d196e22710630631f60985" ], "markers": "python_version ~= '3.7'", - "version": "==2.2.42" + "version": "==2.2.46" }, "aws-cdk.asset-kubectl-v20": { "hashes": [ @@ -260,27 +260,27 @@ }, "aws-cdk.aws-lambda-python-alpha": { "hashes": [ - "sha256:7be52988fded2e52936e14bc11e216549ade69abd0dd7f0312da6a7bf5d670a7", - "sha256:ff8c4cc3774844b80fd3f320b1a6b29f804d0bf303210f850da81b80c4e337cb" + "sha256:2781116b56b53cf05a8e637d8027a387444918ccf7dd22f8083be2f9237f4214", + "sha256:63692b7eb97eed14358ae889630d1bc0115ac15b874ff931209250fdf089474d" ], "markers": "python_version ~= '3.7'", - "version": "==2.54.0a0" + "version": "==2.58.1a0" }, "boto3": { "hashes": [ - "sha256:7a6766c7177a9c6f85365e02aabd96ca4d72e08bc5cb127cb51b0a97ac9b9d1b", - "sha256:82b790b1dabd0746b028d2013b5d4d636a41f3aaf25520081f4c173cb6eb395d" + "sha256:05a5ce3af2d7419e39d93498c7f56fd5c2cc17870c92c4abc75659553b0b16de", + "sha256:8cbea352f28ec6b241f348356bcb8f331fc433bec3ad76ebf6194227f1a7f613" ], "index": "pypi", - "version": "==1.26.37" + "version": "==1.26.41" }, "botocore": { "hashes": [ - "sha256:18ab8e95345a6d0d2653ce65d261a0aef6fef8a57a35a89e3cea6ffe315e92fc", - "sha256:3afa4fec9f7713caa05116563b38f81bec7bd20585d517155484d3f25efab5aa" + "sha256:78761227d986d393956b6d08fdadcfe142748828e0e9db33f2f4c42a482dcd35", + "sha256:b670b7f8958a2908167081efb6ea39794bf61d618be729984629a63d85cf8bfe" ], "markers": "python_version >= '3.7'", - "version": "==1.29.37" + "version": "==1.29.41" }, "cattrs": { "hashes": [ @@ -296,11 +296,11 @@ }, "cdk-nag": { "hashes": [ - "sha256:59b29b4f31cd5de57b56536a2ffe8b576a2506d9d8b55f2e1cf7452f8a6b8936", - "sha256:692092cd45658d208874457f8ec4e87b2c06870a2dfa2f1456a73a4cba4004b0" + "sha256:d8c529043fe2bcb9b864940158c719d4e6dd4b2dfdfd03c226ea6b9808259fb6", + "sha256:ea326f6d01447cd786b627dfa385dfcd57008b0951286ed8ca3049f16b66cf54" ], "markers": "python_version ~= '3.7'", - "version": "==2.21.43" + "version": "==2.21.47" }, "certifi": { "hashes": [ @@ -344,11 +344,11 @@ }, "constructs": { "hashes": [ - "sha256:178f82f33620a581c7c27b52830407c31240369621e6c8bbb2119d023a659c23", - "sha256:2ddaef9faba03c01b1557a4b65cf42ddd655a48b476d1f221f000cb87527f360" + "sha256:9aef27855c21eb8d54a8af7a1f5a4080da3424a66c1d69202e58090d0946bbc8", + "sha256:fe878def863b35b507a2c66f5e217334fa42594a66a3befc9c67d405abaeb4f2" ], "markers": "python_version ~= '3.7'", - "version": "==10.1.201" + "version": "==10.1.205" }, "coverage": { "extras": [ @@ -427,11 +427,11 @@ }, "filelock": { "hashes": [ - "sha256:7565f628ea56bfcd8e54e42bdc55da899c85c1abfe1b5bcfd147e9188cebb3b2", - "sha256:8df285554452285f79c035efb0c861eb33a4bcfa5b7a137016e32e6a90f9792c" + "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de", + "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d" ], "markers": "python_version >= '3.7'", - "version": "==3.8.2" + "version": "==3.9.0" }, "flake8": { "hashes": [ @@ -465,11 +465,11 @@ }, "gitpython": { "hashes": [ - "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f", - "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd" + "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8", + "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882" ], "index": "pypi", - "version": "==3.1.29" + "version": "==3.1.30" }, "identify": { "hashes": [ @@ -642,6 +642,49 @@ "markers": "python_version >= '3.7'", "version": "==1.1.1" }, + "mypy": { + "hashes": [ + "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d", + "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6", + "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf", + "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f", + "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813", + "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33", + "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad", + "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05", + "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297", + "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06", + "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd", + "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243", + "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305", + "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476", + "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711", + "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70", + "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5", + "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461", + "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab", + "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c", + "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d", + "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135", + "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93", + "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648", + "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a", + "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb", + "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3", + "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372", + "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb", + "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef" + ], + "index": "pypi", + "version": "==0.991" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" + }, "nodeenv": { "hashes": [ "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e", @@ -660,11 +703,11 @@ }, "platformdirs": { "hashes": [ - "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca", - "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e" + "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490", + "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2" ], "markers": "python_version >= '3.7'", - "version": "==2.6.0" + "version": "==2.6.2" }, "pluggy": { "hashes": [ @@ -758,7 +801,7 @@ "sha256:acb739f89fabb3d798c099e9e0c035003062367a441910aaaf2281bc1972ee14", "sha256:fcc653f65fe3035b478820b5284fbf0f52803622ee3f60a2faed7a7d3ba1f41e" ], - "markers": "python_version >= '3.7' and python_version < '4.0'", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==2.0.4" }, "pytest-mock": { @@ -852,7 +895,7 @@ "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" ], - "markers": "python_version >= '3.7' and python_version < '4.0'", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==2.28.1" }, "s3transfer": { @@ -907,6 +950,14 @@ "markers": "python_full_version >= '3.5.3'", "version": "==2.13.3" }, + "types-cachetools": { + "hashes": [ + "sha256:069cfc825697cd51445c1feabbe4edc1fae2b2315870e7a9a179a7c4a5851bee", + "sha256:b496b7e364ba050c4eaadcc6582f2c9fbb04f8ee7141eb3b311a8589dbd4506a" + ], + "index": "pypi", + "version": "==5.2.1" + }, "typing-extensions": { "hashes": [ "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", diff --git a/README.md b/README.md index 798311a5..44016dd6 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ This project aims to reduce cognitive load and answer these questions for you by - Python Serverless service with a recommended file structure. - CDK infrastructure with infrastructure tests and security tests. -- CI/CD pipelines based on Github actions that deploys to AWS with python linters, complexity checks and style formatters. +- CI/CD pipelines based on Github actions that deploys to AWS with python linters, static code analysis, complexity checks and style formatters. - The AWS Lambda handler embodies Serverless best practices and has all the bells and whistles for a proper production ready handler. - AWS Lambda handler uses [AWS Lambda Powertools](https://awslabs.github.io/aws-lambda-powertools-python/). - Unit, integration and E2E tests. diff --git a/cdk/app.py b/cdk/app.py new file mode 100755 index 00000000..abb12a08 --- /dev/null +++ b/cdk/app.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +import os + +from aws_cdk import App, Environment +from boto3 import client, session +from my_service.service_stack import ServiceStack, get_stack_name + +account = client('sts').get_caller_identity()['Account'] +region = session.Session().region_name +app = App() +my_stack = ServiceStack( + app, + get_stack_name(), + env=Environment(account=os.environ.get('AWS_DEFAULT_ACCOUNT', account), region=os.environ.get('AWS_DEFAULT_REGION', region)), +) + +app.synth() diff --git a/cdk/my_service/cdk.context.json b/cdk/cdk.context.json similarity index 100% rename from cdk/my_service/cdk.context.json rename to cdk/cdk.context.json diff --git a/cdk/my_service/cdk.json b/cdk/cdk.json similarity index 100% rename from cdk/my_service/cdk.json rename to cdk/cdk.json diff --git a/cdk/my_service/app.py b/cdk/my_service/app.py deleted file mode 100755 index dd71c10c..00000000 --- a/cdk/my_service/app.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 -import os - -from aws_cdk import App, Environment -from boto3 import client, session -from service_stack.service_stack import ServiceStack, get_stack_name - -account = client('sts').get_caller_identity()['Account'] -region = session.Session().region_name -app = App() -my_stack = ServiceStack(app, get_stack_name(), - env=Environment(account=os.environ.get('AWS_DEFAULT_ACCOUNT', account), region=os.environ.get('AWS_DEFAULT_REGION', region))) - -app.synth() diff --git a/__init__.py b/cdk/my_service/configuration/__init__.py similarity index 100% rename from __init__.py rename to cdk/my_service/configuration/__init__.py diff --git a/cdk/my_service/service_stack/configuration/configuration_construct.py b/cdk/my_service/configuration/configuration_construct.py similarity index 97% rename from cdk/my_service/service_stack/configuration/configuration_construct.py rename to cdk/my_service/configuration/configuration_construct.py index fbd7c8c3..fa9161e2 100644 --- a/cdk/my_service/service_stack/configuration/configuration_construct.py +++ b/cdk/my_service/configuration/configuration_construct.py @@ -3,7 +3,7 @@ import aws_cdk.aws_appconfig as appconfig from constructs import Construct -from my_service.service_stack.configuration.schema import FeatureFlagsConfiguration +from my_service.configuration.schema import FeatureFlagsConfiguration DEFAULT_DEPLOYMENT_STRATEGY = 'AppConfig.AllAtOnce' diff --git a/cdk/my_service/service_stack/configuration/json/dev_configuration.json b/cdk/my_service/configuration/json/dev_configuration.json similarity index 100% rename from cdk/my_service/service_stack/configuration/json/dev_configuration.json rename to cdk/my_service/configuration/json/dev_configuration.json diff --git a/cdk/my_service/service_stack/configuration/schema.py b/cdk/my_service/configuration/schema.py similarity index 100% rename from cdk/my_service/service_stack/configuration/schema.py rename to cdk/my_service/configuration/schema.py diff --git a/cdk/my_service/service_stack/constants.py b/cdk/my_service/constants.py similarity index 100% rename from cdk/my_service/service_stack/constants.py rename to cdk/my_service/constants.py diff --git a/cdk/my_service/service_stack/service_construct.py b/cdk/my_service/service_construct.py similarity index 96% rename from cdk/my_service/service_stack/service_construct.py rename to cdk/my_service/service_construct.py index 0dac1411..f119a0bc 100644 --- a/cdk/my_service/service_stack/service_construct.py +++ b/cdk/my_service/service_construct.py @@ -1,4 +1,4 @@ -import my_service.service_stack.constants as constants +import my_service.constants as constants from aws_cdk import CfnOutput, Duration, RemovalPolicy, aws_apigateway from aws_cdk import aws_dynamodb as dynamodb from aws_cdk import aws_iam as iam @@ -34,8 +34,8 @@ def _build_db(self, id_prefix: str) -> dynamodb.Table: CfnOutput(self, id=constants.TABLE_NAME_OUTPUT, value=table.table_name).override_logical_id(constants.TABLE_NAME_OUTPUT) return table - def _build_api_gw(self) -> aws_apigateway.LambdaRestApi: - rest_api: aws_apigateway.LambdaRestApi = aws_apigateway.RestApi( + def _build_api_gw(self) -> aws_apigateway.RestApi: + rest_api: aws_apigateway.RestApi = aws_apigateway.RestApi( self, 'service-rest-api', rest_api_name='Service Rest API', diff --git a/cdk/my_service/service_stack/service_stack.py b/cdk/my_service/service_stack.py similarity index 81% rename from cdk/my_service/service_stack/service_stack.py rename to cdk/my_service/service_stack.py index 8728420b..5894f109 100644 --- a/cdk/my_service/service_stack/service_stack.py +++ b/cdk/my_service/service_stack.py @@ -4,9 +4,9 @@ from aws_cdk import Stack from constructs import Construct from git import Repo -from my_service.service_stack.configuration.configuration_construct import ConfigurationStore -from my_service.service_stack.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME -from my_service.service_stack.service_construct import ApiConstruct +from my_service.configuration.configuration_construct import ConfigurationStore +from my_service.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME +from my_service.service_construct import ApiConstruct def get_stack_name() -> str: diff --git a/cdk/setup.py b/cdk/setup.py index 9e5fa01d..b90f6a2b 100755 --- a/cdk/setup.py +++ b/cdk/setup.py @@ -6,7 +6,7 @@ here = path.abspath(path.dirname(__file__)) setup( - name='aws-lambda-handler-cookbook', + name='service-cdk', version='2.2', description='CDK code for deploying an AWS Lambda handler that implements the best practices described at https://www.ranthebuilder.cloud', classifiers=[ @@ -25,6 +25,6 @@ 'aws-cdk-lib>=2.0.0', 'constructs>=10.0.0', 'cdk-nag>2.0.0', - 'aws-cdk.aws-lambda-python-alpha==2.54.0-alpha.0', + 'aws-cdk.aws-lambda-python-alpha==2.58.1-alpha.0', ], ) diff --git a/dev_requirements.txt b/dev_requirements.txt index 93e6c420..1e379f11 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,30 +1,30 @@ -i https://pypi.org/simple attrs==22.2.0; python_version >= '3.6' -aws-cdk-lib==2.56.1; python_version ~= '3.7' -aws-cdk.asset-awscli-v1==2.2.42; python_version ~= '3.7' +aws-cdk-lib==2.58.1; python_version ~= '3.7' +aws-cdk.asset-awscli-v1==2.2.46; python_version ~= '3.7' aws-cdk.asset-kubectl-v20==2.1.1; python_version ~= '3.7' aws-cdk.asset-node-proxy-agent-v5==2.0.38; python_version ~= '3.7' -aws-cdk.aws-lambda-python-alpha==2.54.0a0; python_version ~= '3.7' -boto3==1.26.37 -botocore==1.29.37; python_version >= '3.7' +aws-cdk.aws-lambda-python-alpha==2.58.1a0; python_version ~= '3.7' +boto3==1.26.41 +botocore==1.29.41; python_version >= '3.7' cattrs==22.2.0; python_version >= '3.7' -e ./cdk -cdk-nag==2.21.43; python_version ~= '3.7' +cdk-nag==2.21.47; python_version ~= '3.7' certifi==2022.12.7; python_version >= '3.6' cfgv==3.3.1; python_full_version >= '3.6.1' charset-normalizer==2.1.1; python_version >= '3.6' click==8.1.3; python_version >= '3.7' colorama==0.4.6; python_version >= '3.5' -constructs==10.1.201; python_version ~= '3.7' +constructs==10.1.205; python_version ~= '3.7' coverage[toml]==7.0.1; python_version >= '3.7' distlib==0.3.6 exceptiongroup==1.1.0; python_version < '3.11' -filelock==3.8.2; python_version >= '3.7' +filelock==3.9.0; python_version >= '3.7' flake8==6.0.0 future==0.18.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' ghp-import==2.1.0 gitdb==4.0.10; python_version >= '3.7' -gitpython==3.1.29 +gitpython==3.1.30 identify==2.5.11; python_version >= '3.7' idna==3.4; python_version >= '3.5' importlib-metadata==5.2.0; python_version < '3.10' @@ -42,9 +42,11 @@ mkdocs==1.4.2; python_version >= '3.7' mkdocs-git-revision-date-plugin==0.3.2 mkdocs-material==8.5.11 mkdocs-material-extensions==1.1.1; python_version >= '3.7' +mypy==0.991 +mypy-extensions==0.4.3 nodeenv==1.7.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6' packaging==22.0; python_version >= '3.7' -platformdirs==2.6.0; python_version >= '3.7' +platformdirs==2.6.2; python_version >= '3.7' pluggy==1.0.0; python_version >= '3.6' pre-commit==2.21.0 publication==0.0.3 @@ -56,14 +58,14 @@ pymdown-extensions==9.9; python_version >= '3.7' pytest==7.2.0 pytest-cov==4.0.0 pytest-html==3.2.0 -pytest-metadata==2.0.4; python_version >= '3.7' and python_version < '4.0' +pytest-metadata==2.0.4; python_version >= '3.7' and python_version < '4' pytest-mock==3.10.0 python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' python-dotenv==0.21.0 pyyaml==6.0; python_version >= '3.6' pyyaml-env-tag==0.1; python_version >= '3.6' radon==5.1.0 -requests==2.28.1; python_version >= '3.7' and python_version < '4.0' +requests==2.28.1; python_version >= '3.7' and python_version < '4' s3transfer==0.6.0; python_version >= '3.7' -e ./cdk setuptools==65.6.3 @@ -71,6 +73,7 @@ six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3 smmap==5.0.0; python_version >= '3.6' tomli==2.0.1; python_version < '3.11' typeguard==2.13.3; python_full_version >= '3.5.3' +types-cachetools==5.2.1 typing-extensions==4.4.0; python_version >= '3.7' urllib3==1.26.13; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' virtualenv==20.17.1; python_version >= '3.6' @@ -83,5 +86,5 @@ aws-xray-sdk==2.11.0 cachetools==5.2.0 fastjsonschema==2.16.2 mypy-boto3-dynamodb==1.26.24 -pydantic==1.10.2 +pydantic==1.10.4 wrapt==1.14.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' diff --git a/cdk/my_service/service_stack/__init__.py b/docs/examples/best_practices/dynamic_configuration/__init__.py similarity index 100% rename from cdk/my_service/service_stack/__init__.py rename to docs/examples/best_practices/dynamic_configuration/__init__.py diff --git a/docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py b/docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py index 70762e02..f5527e5a 100644 --- a/docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py +++ b/docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py @@ -1,7 +1,7 @@ from aws_cdk import Stack from constructs import Construct -from my_service.service_stack.configuration.configuration_construct import ConfigurationStore -from my_service.service_stack.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME +from my_service.configuration.configuration_construct import ConfigurationStore +from my_service.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME class DynamicConfigurationStack(Stack): diff --git a/docs/examples/best_practices/dynamic_configuration/evaluate_feature_flags.py b/docs/examples/best_practices/dynamic_configuration/evaluate_feature_flags.py index 155e64d3..0261b855 100644 --- a/docs/examples/best_practices/dynamic_configuration/evaluate_feature_flags.py +++ b/docs/examples/best_practices/dynamic_configuration/evaluate_feature_flags.py @@ -15,20 +15,20 @@ @init_environment_variables(model=MyHandlerEnvVars) def my_handler(event: Dict[str, Any], context: LambdaContext) -> Dict[str, Any]: try: - my_configuration: MyConfiguration = parse_configuration(model=MyConfiguration) + my_configuration: MyConfiguration = parse_configuration(model=MyConfiguration) # type: ignore logger.debug('fetched dynamic configuration', extra={'configuration': my_configuration.dict()}) except (SchemaValidationError, ConfigurationStoreError) as exc: logger.exception(f'dynamic configuration error, error={str(exc)}') return build_response(http_status=HTTPStatus.INTERNAL_SERVER_ERROR, body={}) - campaign: bool = get_dynamic_configuration_store().evaluate( + campaign = get_dynamic_configuration_store().evaluate( name='ten_percent_off_campaign', context={}, default=False, ) logger.debug('campaign feature flag value', extra={'campaign': campaign}) - premium: bool = get_dynamic_configuration_store().evaluate( + premium = get_dynamic_configuration_store().evaluate( name='premium_features', context={'customer_name': 'RanTheBuilder'}, default=False, diff --git a/docs/examples/best_practices/dynamic_configuration/parse_configuration.py b/docs/examples/best_practices/dynamic_configuration/parse_configuration.py index e450d58a..9f5854bf 100644 --- a/docs/examples/best_practices/dynamic_configuration/parse_configuration.py +++ b/docs/examples/best_practices/dynamic_configuration/parse_configuration.py @@ -15,7 +15,7 @@ @init_environment_variables(model=MyHandlerEnvVars) def my_handler(event: Dict[str, Any], context: LambdaContext) -> Dict[str, Any]: try: - my_configuration: MyConfiguration = parse_configuration(model=MyConfiguration) + my_configuration: MyConfiguration = parse_configuration(model=MyConfiguration) # type: ignore except (SchemaValidationError, ConfigurationStoreError) as exc: logger.exception(f'dynamic configuration error, error={str(exc)}') return build_response(http_status=HTTPStatus.INTERNAL_SERVER_ERROR, body={}) diff --git a/cdk/my_service/service_stack/configuration/__init__.py b/docs/examples/best_practices/environment_variables/__init__.py similarity index 100% rename from cdk/my_service/service_stack/configuration/__init__.py rename to docs/examples/best_practices/environment_variables/__init__.py diff --git a/docs/examples/best_practices/environment_variables/env_vars.py b/docs/examples/best_practices/environment_variables/env_vars.py index f3ee1e9b..94ad90a0 100644 --- a/docs/examples/best_practices/environment_variables/env_vars.py +++ b/docs/examples/best_practices/environment_variables/env_vars.py @@ -1,10 +1,10 @@ -from typing import Literal +from typing import Annotated, Literal -from pydantic import BaseModel, HttpUrl, constr +from pydantic import BaseModel, Field, HttpUrl class MyHandlerEnvVars(BaseModel): REST_API: HttpUrl - ROLE_ARN: constr(min_length=20, max_length=2048) - POWERTOOLS_SERVICE_NAME: constr(min_length=1) + ROLE_ARN: Annotated[str, Field(min_length=20, max_length=2048)] + POWERTOOLS_SERVICE_NAME: Annotated[str, Field(min_length=1)] LOG_LEVEL: Literal['DEBUG', 'INFO', 'ERROR', 'CRITICAL', 'WARNING', 'EXCEPTION'] diff --git a/docs/examples/best_practices/environment_variables/my_handler.py b/docs/examples/best_practices/environment_variables/my_handler.py index b331d431..ca06c1fe 100644 --- a/docs/examples/best_practices/environment_variables/my_handler.py +++ b/docs/examples/best_practices/environment_variables/my_handler.py @@ -10,5 +10,5 @@ @init_environment_variables(model=MyHandlerEnvVars) def my_handler(event: Dict[str, Any], context: LambdaContext) -> Dict[str, Any]: - env_vars: MyHandlerEnvVars = get_environment_variables(model=MyHandlerEnvVars) # noqa: F841 + env_vars = get_environment_variables(model=MyHandlerEnvVars) # noqa: F841 return {'statusCode': HTTPStatus.OK, 'headers': {'Content-Type': 'application/json'}, 'body': json.dumps({'message': 'success'})} diff --git a/docs/examples/best_practices/input_validation/__init__.py b/docs/examples/best_practices/input_validation/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/examples/best_practices/logger/__init__.py b/docs/examples/best_practices/logger/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/examples/best_practices/metrics/__init__.py b/docs/examples/best_practices/metrics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/examples/best_practices/tracer/__init__.py b/docs/examples/best_practices/tracer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/pipeline.md b/docs/pipeline.md index 9cd056a8..84d5d38d 100644 --- a/docs/pipeline.md +++ b/docs/pipeline.md @@ -13,6 +13,7 @@ All steps can be run locally using the makefile. See details below: - Install dev dependencies - Run pre-commit checks as defined in `.pre-commit-config.yaml` - Lint with flake8 as defined in `.flake8` - run `make lint` in the IDE +- Static type check with mypy as defined in `.mypy.ini` - run `make mypy-lint` in the IDE - Verify that Python imports are sorted according to standard - run `make sort` in the IDE - Python formatter Yapf as defined in `.style` - run `make yapf` in the IDE - Python complexity checks: radon and xenon - run `make complex` in the IDE diff --git a/lambda_requirements.txt b/lambda_requirements.txt index fb1f3b93..8922fb06 100644 --- a/lambda_requirements.txt +++ b/lambda_requirements.txt @@ -1,12 +1,12 @@ -i https://pypi.org/simple aws-lambda-powertools[all]==2.5.0 aws-xray-sdk==2.11.0 -botocore==1.29.37; python_version >= '3.7' +botocore==1.29.41; python_version >= '3.7' cachetools==5.2.0 fastjsonschema==2.16.2 jmespath==1.0.1; python_version >= '3.7' mypy-boto3-dynamodb==1.26.24 -pydantic==1.10.2 +pydantic==1.10.4 python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' typing-extensions==4.4.0; python_version >= '3.7' diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..30d3fb91 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,58 @@ +[mypy] +warn_return_any=False +warn_unused_configs=True +no_implicit_optional=True +warn_redundant_casts=True +warn_unused_ignores=True +show_column_numbers = True +show_error_codes = True +show_error_context = True +ignore_missing_imports = True + +[mypy-jmespath] +ignore_missing_imports=True + +[mypy-jmespath.exceptions] +ignore_missing_imports=True + +[mypy-jmespath.functions] +ignore_missing_imports=True + +[mypy-boto3] +ignore_missing_imports = True + +[mypy-botocore] +ignore_missing_imports = True + +[mypy-botocore.response] +ignore_missing_imports = True + +[mypy-boto3.dynamodb.conditions] +ignore_missing_imports = True + +[mypy-botocore.config] +ignore_missing_imports = True + +[mypy-botocore.compat] +ignore_missing_imports = True + +[mypy-botocore.exceptions] +ignore_missing_imports = True + +[mypy-aws_xray_sdk.ext.aiohttp.client] +ignore_missing_imports = True + +[mypy-dataclasses] +ignore_missing_imports = True + +[mypy-orjson] +ignore_missing_imports = True + +[mypy-aiohttp] +ignore_missing_imports = True + +[mypy-snappy] +ignore_missing_imports = True + +[mypy-ijson] +ignore_missing_imports = True diff --git a/service/handlers/create_order.py b/service/handlers/create_order.py index c4fd1aa2..fadd9f13 100644 --- a/service/handlers/create_order.py +++ b/service/handlers/create_order.py @@ -29,7 +29,7 @@ def create_order(event: Dict[str, Any], context: LambdaContext) -> Dict[str, Any logger.debug('environment variables', extra=env_vars.dict()) try: - my_configuration: MyConfiguration = parse_configuration(model=MyConfiguration) + my_configuration: MyConfiguration = parse_configuration(model=MyConfiguration) # type: ignore logger.debug('fetched dynamic configuration', extra={'configuration': my_configuration.dict()}) except (SchemaValidationError, ConfigurationStoreError) as exc: logger.exception(f'dynamic configuration error, error={str(exc)}') diff --git a/service/handlers/schemas/env_vars.py b/service/handlers/schemas/env_vars.py index 0d290ef0..4e83103e 100644 --- a/service/handlers/schemas/env_vars.py +++ b/service/handlers/schemas/env_vars.py @@ -1,21 +1,21 @@ -from typing import Literal +from typing import Annotated, Literal -from pydantic import BaseModel, HttpUrl, PositiveInt, constr +from pydantic import BaseModel, Field, HttpUrl, PositiveInt class Observability(BaseModel): - POWERTOOLS_SERVICE_NAME: constr(min_length=1) + POWERTOOLS_SERVICE_NAME: Annotated[str, Field(min_length=1)] LOG_LEVEL: Literal['DEBUG', 'INFO', 'ERROR', 'CRITICAL', 'WARNING', 'EXCEPTION'] class DynamicConfiguration(BaseModel): - CONFIGURATION_APP: constr(min_length=1) - CONFIGURATION_ENV: constr(min_length=1) - CONFIGURATION_NAME: constr(min_length=1) + CONFIGURATION_APP: Annotated[str, Field(min_length=1)] + CONFIGURATION_ENV: Annotated[str, Field(min_length=1)] + CONFIGURATION_NAME: Annotated[str, Field(min_length=1)] CONFIGURATION_MAX_AGE_MINUTES: PositiveInt class MyHandlerEnvVars(Observability, DynamicConfiguration): REST_API: HttpUrl - ROLE_ARN: constr(min_length=20, max_length=2048) - TABLE_NAME: constr(min_length=1) + ROLE_ARN: Annotated[str, Field(min_length=20, max_length=2048)] + TABLE_NAME: Annotated[str, Field(min_length=1)] diff --git a/service/handlers/schemas/input.py b/service/handlers/schemas/input.py index a4bb1bf9..631c2beb 100644 --- a/service/handlers/schemas/input.py +++ b/service/handlers/schemas/input.py @@ -1,6 +1,8 @@ -from pydantic import BaseModel, PositiveInt, constr +from typing import Annotated + +from pydantic import BaseModel, Field, PositiveInt class Input(BaseModel): - customer_name: constr(min_length=1, max_length=20) + customer_name: Annotated[str, Field(min_length=1, max_length=20)] order_item_count: PositiveInt diff --git a/service/handlers/utils/dynamic_configuration.py b/service/handlers/utils/dynamic_configuration.py index 6d2c59db..e03bc02c 100644 --- a/service/handlers/utils/dynamic_configuration.py +++ b/service/handlers/utils/dynamic_configuration.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, TypeVar +from typing import Any, Dict, Type, TypeVar, Union from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags from aws_lambda_powertools.utilities.feature_flags.exceptions import SchemaValidationError @@ -9,7 +9,7 @@ Model = TypeVar('Model', bound=BaseModel) -_DYNAMIC_CONFIGURATION: FeatureFlags = None +_DYNAMIC_CONFIGURATION: Union[FeatureFlags, None] = None _DEFAULT_FEATURE_FLAGS_ROOT = 'features' # all feature flags reside in the JSON under this key @@ -35,7 +35,7 @@ def get_dynamic_configuration_store() -> FeatureFlags: return _DYNAMIC_CONFIGURATION -def parse_configuration(model: Model) -> BaseModel: +def parse_configuration(model: Type[Model]) -> Type[BaseModel]: """ Get configuration JSON from AWS AppConfig and parse it into a pydantic data-class instance. Args: model (Model): pydantic schema to load the JSON into @@ -46,6 +46,6 @@ def parse_configuration(model: Model) -> BaseModel: """ try: conf_json: Dict[str, Any] = get_dynamic_configuration_store().store.get_raw_configuration - return model.parse_obj(conf_json) + return model.parse_obj(conf_json) # type: ignore except (ValidationError, TypeError) as exc: raise SchemaValidationError(f'appconfig schema failed pydantic validation, exception={str(exc)}') from exc diff --git a/service/handlers/utils/env_vars_parser.py b/service/handlers/utils/env_vars_parser.py index 255cc8e3..6ceac54c 100644 --- a/service/handlers/utils/env_vars_parser.py +++ b/service/handlers/utils/env_vars_parser.py @@ -1,6 +1,6 @@ import os from functools import lru_cache -from typing import Any, TypeVar +from typing import Any, Type, TypeVar from aws_lambda_powertools.middleware_factory import lambda_handler_decorator from pydantic import BaseModel, ValidationError @@ -9,9 +9,9 @@ @lru_cache -def __parse_model(model: Model) -> BaseModel: +def __parse_model(model: Type[Model]) -> Model: try: - return model(**os.environ) + return model.parse_obj(os.environ) except (ValidationError, TypeError) as exc: raise ValueError(f'failed to load environment variables, exception={str(exc)}') from exc @@ -22,5 +22,5 @@ def init_environment_variables(handler, event, context, model: Model) -> Any: return handler(event, context) -def get_environment_variables(model: Model) -> BaseModel: +def get_environment_variables(model: Type[Model]) -> Model: return __parse_model(model) diff --git a/service/logic/handle_create_request.py b/service/logic/handle_create_request.py index 4b2cb310..9d316264 100644 --- a/service/logic/handle_create_request.py +++ b/service/logic/handle_create_request.py @@ -19,13 +19,13 @@ def handle_create_request(customer_name: str, order_item_count: int, table_name: # feature flags example config_store = get_dynamic_configuration_store() - campaign: bool = config_store.evaluate( + campaign = config_store.evaluate( name=FeatureFlagsNames.TEN_PERCENT_CAMPAIGN.value, context={}, default=False, ) logger.debug('campaign feature flag value', extra={'campaign': campaign}) - premium: bool = config_store.evaluate( + premium = config_store.evaluate( name=FeatureFlagsNames.PREMIUM.value, context={'customer_name': customer_name}, default=False, @@ -43,7 +43,7 @@ def _get_db_handler(table_name: str) -> Table: return dynamodb.Table(table_name) -def _create_order_in_db(table_name: str, customer_name: str, order_item_count: str): +def _create_order_in_db(table_name: str, customer_name: str, order_item_count: int): order_id = str(uuid.uuid4()) try: table: Table = _get_db_handler(table_name) diff --git a/service/schemas/output.py b/service/schemas/output.py index 34218d91..a74cb678 100644 --- a/service/schemas/output.py +++ b/service/schemas/output.py @@ -1,11 +1,12 @@ +from typing import Annotated from uuid import UUID -from pydantic import BaseModel, PositiveInt, constr, validator +from pydantic import BaseModel, Field, PositiveInt, validator class Output(BaseModel): order_item_count: PositiveInt - customer_name: constr(min_length=1, max_length=20) + customer_name: Annotated[str, Field(min_length=1, max_length=20)] order_id: str @validator('order_id') diff --git a/tests/unit/test_env_vars_parser.py b/tests/unit/test_env_vars_parser.py index bc23b7c4..66002781 100644 --- a/tests/unit/test_env_vars_parser.py +++ b/tests/unit/test_env_vars_parser.py @@ -5,7 +5,7 @@ import pytest from pydantic import BaseModel, HttpUrl -from cdk.my_service.service_stack.constants import POWER_TOOLS_LOG_LEVEL, POWERTOOLS_SERVICE_NAME, SERVICE_NAME +from cdk.my_service.constants import POWER_TOOLS_LOG_LEVEL, POWERTOOLS_SERVICE_NAME, SERVICE_NAME from service.handlers.utils.env_vars_parser import get_environment_variables, init_environment_variables from tests.utils import generate_context diff --git a/tests/unit/test_parse_configuration.py b/tests/unit/test_parse_configuration.py index 3230cf66..d97aefd9 100644 --- a/tests/unit/test_parse_configuration.py +++ b/tests/unit/test_parse_configuration.py @@ -5,7 +5,7 @@ from aws_lambda_powertools.utilities.feature_flags.exceptions import SchemaValidationError from pydantic import BaseModel -from cdk.my_service.service_stack.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME +from cdk.my_service.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME from service.handlers.utils.dynamic_configuration import parse_configuration MOCKED_SCHEMA = {'region': 'us-east-1'} diff --git a/tests/utils.py b/tests/utils.py index b4c2ceef..084c49ba 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,7 +4,7 @@ import boto3 from aws_lambda_powertools.utilities.typing import LambdaContext -from cdk.my_service.service_stack.service_stack import get_stack_name +from cdk.my_service.service_stack import get_stack_name def generate_context() -> LambdaContext: From e43751d90e4fc6d5ce6b95d0aacb8be5c4858ad9 Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Sat, 31 Dec 2022 13:23:58 +0200 Subject: [PATCH 2/3] fix tests --- Makefile | 6 ++--- Pipfile | 1 + Pipfile.lock | 27 +++++++++++++------ cdk/app.py => app.py | 3 ++- cdk.context.json | 1 + cdk/cdk.json => cdk.json | 0 cdk/cdk.context.json | 3 --- .../configuration/configuration_construct.py | 3 ++- cdk/my_service/service_construct.py | 3 ++- cdk/my_service/service_stack.py | 7 ++--- dev_requirements.txt | 7 ++--- docs/best_practices/dynamic_configuration.md | 4 +-- docs/cdk.md | 6 ++--- .../dynamic_configuration/lambda_cdk.py | 3 ++- tests/e2e/test_create_order.py | 2 +- tests/e2e/test_infra/test_cdk.py | 2 +- tests/e2e/test_infra/test_cdk_nag.py | 2 +- tests/integration/conftest.py | 20 ++++++++++++++ tests/integration/test_create_order.py | 26 +----------------- 19 files changed, 69 insertions(+), 57 deletions(-) rename cdk/app.py => app.py (85%) create mode 100644 cdk.context.json rename cdk/cdk.json => cdk.json (100%) delete mode 100644 cdk/cdk.context.json create mode 100644 tests/integration/conftest.py diff --git a/Makefile b/Makefile index 9914d3cf..34e20cf6 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ pre-commit: pre-commit run -a --show-diff-on-failure mypy-lint: - mypy --pretty service docs/examples cdk + mypy --pretty service docs/examples cdk tests deps: pipenv requirements --dev > dev_requirements.txt @@ -52,10 +52,10 @@ deploy: make deps mkdir -p .build/lambdas ; cp -r service .build/lambdas mkdir -p .build/common_layer ; pipenv requirements > .build/common_layer/requirements.txt - cdk deploy --app="python3 ${PWD}/cdk/app.py" --require-approval=never + cdk deploy --app="python3 ${PWD}/app.py" --require-approval=never destroy: - cdk destroy --app="python3 ${PWD}/cdk/app.py" --force + cdk destroy --app="python3 ${PWD}/app.py" --force docs: mkdocs serve diff --git a/Pipfile b/Pipfile index 72211a31..6c48fea9 100644 --- a/Pipfile +++ b/Pipfile @@ -25,6 +25,7 @@ mkdocs-git-revision-date-plugin = "*" setuptools = ">=65.5.1" types-cachetools = "*" mypy = "*" +types-requests = "*" [packages] aws-lambda-powertools= {extras = ["all"],version = "*"} diff --git a/Pipfile.lock b/Pipfile.lock index 315db0cf..7bb9c5a2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f636571265771441c569ad6b933aeb35648e1568db5c229be2d13843733eeaae" + "sha256": "ef06c001b9d23e4162119bac04a2f7e9a71d6befba1c76e94c2d36df26e87ab8" }, "pipfile-spec": 6, "requires": { @@ -290,10 +290,6 @@ "markers": "python_version >= '3.7'", "version": "==22.2.0" }, - "cdk": { - "editable": true, - "path": "./cdk" - }, "cdk-nag": { "hashes": [ "sha256:d8c529043fe2bcb9b864940158c719d4e6dd4b2dfdfd03c226ea6b9808259fb6", @@ -801,7 +797,7 @@ "sha256:acb739f89fabb3d798c099e9e0c035003062367a441910aaaf2281bc1972ee14", "sha256:fcc653f65fe3035b478820b5284fbf0f52803622ee3f60a2faed7a7d3ba1f41e" ], - "markers": "python_version >= '3.7' and python_version < '4'", + "markers": "python_version >= '3.7' and python_version < '4.0'", "version": "==2.0.4" }, "pytest-mock": { @@ -895,7 +891,7 @@ "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" ], - "markers": "python_version >= '3.7' and python_version < '4'", + "markers": "python_version >= '3.7' and python_version < '4.0'", "version": "==2.28.1" }, "s3transfer": { @@ -908,7 +904,7 @@ }, "service-cdk": { "editable": true, - "path": "cdk" + "path": "./cdk" }, "setuptools": { "hashes": [ @@ -958,6 +954,21 @@ "index": "pypi", "version": "==5.2.1" }, + "types-requests": { + "hashes": [ + "sha256:0ae38633734990d019b80f5463dfa164ebd3581998ac8435f526da6fe4d598c3", + "sha256:b6a2fca8109f4fdba33052f11ed86102bddb2338519e1827387137fefc66a98b" + ], + "index": "pypi", + "version": "==2.28.11.7" + }, + "types-urllib3": { + "hashes": [ + "sha256:ed6b9e8a8be488796f72306889a06a3fc3cb1aa99af02ab8afb50144d7317e49", + "sha256:eec5556428eec862b1ac578fb69aab3877995a99ffec9e5a12cf7fbd0cc9daee" + ], + "version": "==1.26.25.4" + }, "typing-extensions": { "hashes": [ "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", diff --git a/cdk/app.py b/app.py similarity index 85% rename from cdk/app.py rename to app.py index abb12a08..f8e1965d 100755 --- a/cdk/app.py +++ b/app.py @@ -3,7 +3,8 @@ from aws_cdk import App, Environment from boto3 import client, session -from my_service.service_stack import ServiceStack, get_stack_name + +from cdk.my_service.service_stack import ServiceStack, get_stack_name account = client('sts').get_caller_identity()['Account'] region = session.Session().region_name diff --git a/cdk.context.json b/cdk.context.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/cdk.context.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/cdk/cdk.json b/cdk.json similarity index 100% rename from cdk/cdk.json rename to cdk.json diff --git a/cdk/cdk.context.json b/cdk/cdk.context.json deleted file mode 100644 index aa268d6d..00000000 --- a/cdk/cdk.context.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "@aws-cdk/core:enableStackNameDuplicates": "true" -} diff --git a/cdk/my_service/configuration/configuration_construct.py b/cdk/my_service/configuration/configuration_construct.py index fa9161e2..7d24be5b 100644 --- a/cdk/my_service/configuration/configuration_construct.py +++ b/cdk/my_service/configuration/configuration_construct.py @@ -3,7 +3,8 @@ import aws_cdk.aws_appconfig as appconfig from constructs import Construct -from my_service.configuration.schema import FeatureFlagsConfiguration + +from cdk.my_service.configuration.schema import FeatureFlagsConfiguration DEFAULT_DEPLOYMENT_STRATEGY = 'AppConfig.AllAtOnce' diff --git a/cdk/my_service/service_construct.py b/cdk/my_service/service_construct.py index f119a0bc..2bfd9308 100644 --- a/cdk/my_service/service_construct.py +++ b/cdk/my_service/service_construct.py @@ -1,4 +1,3 @@ -import my_service.constants as constants from aws_cdk import CfnOutput, Duration, RemovalPolicy, aws_apigateway from aws_cdk import aws_dynamodb as dynamodb from aws_cdk import aws_iam as iam @@ -7,6 +6,8 @@ from aws_cdk.aws_lambda_python_alpha import PythonLayerVersion from constructs import Construct +import cdk.my_service.constants as constants + class ApiConstruct(Construct): diff --git a/cdk/my_service/service_stack.py b/cdk/my_service/service_stack.py index 5894f109..ad70f8f8 100644 --- a/cdk/my_service/service_stack.py +++ b/cdk/my_service/service_stack.py @@ -4,9 +4,10 @@ from aws_cdk import Stack from constructs import Construct from git import Repo -from my_service.configuration.configuration_construct import ConfigurationStore -from my_service.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME -from my_service.service_construct import ApiConstruct + +from cdk.my_service.configuration.configuration_construct import ConfigurationStore +from cdk.my_service.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME +from cdk.my_service.service_construct import ApiConstruct def get_stack_name() -> str: diff --git a/dev_requirements.txt b/dev_requirements.txt index 1e379f11..550cfc90 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -8,7 +8,6 @@ aws-cdk.aws-lambda-python-alpha==2.58.1a0; python_version ~= '3.7' boto3==1.26.41 botocore==1.29.41; python_version >= '3.7' cattrs==22.2.0; python_version >= '3.7' --e ./cdk cdk-nag==2.21.47; python_version ~= '3.7' certifi==2022.12.7; python_version >= '3.6' cfgv==3.3.1; python_full_version >= '3.6.1' @@ -58,14 +57,14 @@ pymdown-extensions==9.9; python_version >= '3.7' pytest==7.2.0 pytest-cov==4.0.0 pytest-html==3.2.0 -pytest-metadata==2.0.4; python_version >= '3.7' and python_version < '4' +pytest-metadata==2.0.4; python_version >= '3.7' and python_version < '4.0' pytest-mock==3.10.0 python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' python-dotenv==0.21.0 pyyaml==6.0; python_version >= '3.6' pyyaml-env-tag==0.1; python_version >= '3.6' radon==5.1.0 -requests==2.28.1; python_version >= '3.7' and python_version < '4' +requests==2.28.1; python_version >= '3.7' and python_version < '4.0' s3transfer==0.6.0; python_version >= '3.7' -e ./cdk setuptools==65.6.3 @@ -74,6 +73,8 @@ smmap==5.0.0; python_version >= '3.6' tomli==2.0.1; python_version < '3.11' typeguard==2.13.3; python_full_version >= '3.5.3' types-cachetools==5.2.1 +types-requests==2.28.11.7 +types-urllib3==1.26.25.4 typing-extensions==4.4.0; python_version >= '3.7' urllib3==1.26.13; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' virtualenv==20.17.1; python_version >= '3.6' diff --git a/docs/best_practices/dynamic_configuration.md b/docs/best_practices/dynamic_configuration.md index 2fccec46..eae643e9 100644 --- a/docs/best_practices/dynamic_configuration.md +++ b/docs/best_practices/dynamic_configuration.md @@ -47,7 +47,7 @@ Let's review its advantages: 1. Application (service) 2. Environment 3. Custom deployment strategy - immediate deploy, 0 minutes wait, no validations or AWS CloudWatch alerts -4. The JSON configuration. It uploads the files ‘cdk/my_service/service_stack/configuration/json/{environment}_configuration.json’, where environment is a construct argument (default is 'dev') +4. The JSON configuration. It uploads the files ‘cdk/my_service/configuration/json/{environment}_configuration.json’, where environment is a construct argument (default is 'dev') The construct **validates** the JSON file and verifies that feature flags syntax is valid and exists under the 'features' key. Feature flags are optional. @@ -72,7 +72,7 @@ Args: --8<-- "docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py" ``` -The JSON configuration that is uploaded to AWS AppConfig resides under ``cdk/my_service/service_stack/configuration/json/dev_configuration.json`` +The JSON configuration that is uploaded to AWS AppConfig resides under ``cdk/my_service/configuration/json/dev_configuration.json`` ``dev`` represents the default environment. You can add multiple configurations for different environments. diff --git a/docs/cdk.md b/docs/cdk.md index 6b535734..77ffbd2b 100644 --- a/docs/cdk.md +++ b/docs/cdk.md @@ -24,7 +24,7 @@ In order to add a new dependency, add it to the Pipfile under the [packages] sec ### **CDK Constants** -All ASW Lambda function configurations are saved as constants at the `cdk.my_service.service_stack.constants.py` file and can easily be changed. +All ASW Lambda function configurations are saved as constants at the `cdk.my_service.constants.py` file and can easily be changed. - Memory size - Timeout in seconds @@ -35,8 +35,8 @@ All ASW Lambda function configurations are saved as constants at the `cdk.my_ser ### **Deployed Resources** -- AWS Cloudformation stack: **cdk.my_service.service_stack.service_stack.py** which is consisted of one construct -- Construct: **cdk.my_service.service_stack.service_construct.py** which includes: +- AWS Cloudformation stack: **cdk.my_service.service_stack.py** which is consisted of one construct +- Construct: **cdk.my_service.service_construct.py** which includes: - **Lambda Layer** - deployment optimization meant to be used with multiple handlers under the same API GW, sharing code logic and dependencies. You can read more about it in Yan - Cui's [blog](https://medium.com/theburningmonk-com/lambda-layer-not-a-package-manager-but-a-deployment-optimization-85ddcae40a96){:target="_blank" rel="noopener"} - **Lambda Function** - The Lambda handler function itself. Handler code is taken from the service `folder`. - **Lambda Role** - The role of the Lambda function. diff --git a/docs/examples/best_practices/dynamic_configuration/lambda_cdk.py b/docs/examples/best_practices/dynamic_configuration/lambda_cdk.py index c3dea375..8091561a 100644 --- a/docs/examples/best_practices/dynamic_configuration/lambda_cdk.py +++ b/docs/examples/best_practices/dynamic_configuration/lambda_cdk.py @@ -1,8 +1,9 @@ -import my_service.service_stack.constants as constants from aws_cdk import Duration, aws_apigateway from aws_cdk import aws_iam as iam from aws_cdk import aws_lambda as _lambda +import cdk.my_service.constants as constants + def _build_lambda_role(self) -> iam.Role: return iam.Role( diff --git a/tests/e2e/test_create_order.py b/tests/e2e/test_create_order.py index 7a1734a8..f177645d 100644 --- a/tests/e2e/test_create_order.py +++ b/tests/e2e/test_create_order.py @@ -4,7 +4,7 @@ import pytest import requests -from cdk.my_service.service_stack.constants import APIGATEWAY, GW_RESOURCE +from cdk.my_service.constants import APIGATEWAY, GW_RESOURCE from service.handlers.schemas.input import Input from tests.utils import get_stack_output diff --git a/tests/e2e/test_infra/test_cdk.py b/tests/e2e/test_infra/test_cdk.py index 6e70a942..7b676072 100644 --- a/tests/e2e/test_infra/test_cdk.py +++ b/tests/e2e/test_infra/test_cdk.py @@ -1,7 +1,7 @@ from aws_cdk import App from aws_cdk.assertions import Template -from cdk.my_service.service_stack.service_stack import ServiceStack +from cdk.my_service.service_stack import ServiceStack def test_synthesizes_properly(): diff --git a/tests/e2e/test_infra/test_cdk_nag.py b/tests/e2e/test_infra/test_cdk_nag.py index d1a546e2..442bbce7 100644 --- a/tests/e2e/test_infra/test_cdk_nag.py +++ b/tests/e2e/test_infra/test_cdk_nag.py @@ -1,7 +1,7 @@ from aws_cdk import App, Aspects from cdk_nag import AwsSolutionsChecks, HIPAASecurityChecks -from cdk.my_service.service_stack.service_stack import ServiceStack +from cdk.my_service.service_stack import ServiceStack def test_cdk_nag_default(): diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py new file mode 100644 index 00000000..c699785b --- /dev/null +++ b/tests/integration/conftest.py @@ -0,0 +1,20 @@ +import os + +import pytest + +from cdk.my_service.constants import CONFIGURATION_NAME, ENVIRONMENT, POWER_TOOLS_LOG_LEVEL, POWERTOOLS_SERVICE_NAME, SERVICE_NAME, TABLE_NAME_OUTPUT +from tests.utils import get_stack_output + + +@pytest.fixture(scope='module', autouse=True) +def init(): + os.environ[POWERTOOLS_SERVICE_NAME] = SERVICE_NAME + os.environ[POWER_TOOLS_LOG_LEVEL] = 'DEBUG' + os.environ['REST_API'] = 'https://www.ranthebuilder.cloud/api' + os.environ['ROLE_ARN'] = 'arn:partition:service:region:account-id:resource-type:resource-id' + os.environ['CONFIGURATION_APP'] = SERVICE_NAME + os.environ['CONFIGURATION_ENV'] = ENVIRONMENT + os.environ['CONFIGURATION_NAME'] = CONFIGURATION_NAME + os.environ['CONFIGURATION_MAX_AGE_MINUTES'] = '5' + os.environ['AWS_DEFAULT_REGION'] = 'us-east-1' # used for appconfig mocked boto calls + os.environ['TABLE_NAME'] = get_stack_output(TABLE_NAME_OUTPUT) diff --git a/tests/integration/test_create_order.py b/tests/integration/test_create_order.py index c9e17876..28ba3dd2 100644 --- a/tests/integration/test_create_order.py +++ b/tests/integration/test_create_order.py @@ -1,23 +1,13 @@ import json -import os from http import HTTPStatus from typing import Any, Dict -import pytest from aws_lambda_powertools.utilities.feature_flags.exceptions import SchemaValidationError from botocore.exceptions import ClientError -from cdk.my_service.service_stack.constants import ( - CONFIGURATION_NAME, - ENVIRONMENT, - POWER_TOOLS_LOG_LEVEL, - POWERTOOLS_SERVICE_NAME, - SERVICE_NAME, - TABLE_NAME_OUTPUT, -) from service.handlers.create_order import create_order from service.handlers.schemas.input import Input -from tests.utils import generate_api_gw_event, generate_context, get_stack_output +from tests.utils import generate_api_gw_event, generate_context MOCKED_SCHEMA = { 'features': { @@ -53,20 +43,6 @@ def mock_exception_dynamic_configuration(mocker) -> None: mocker.patch('aws_lambda_powertools.utilities.parameters.AppConfigProvider.get', side_effect=SchemaValidationError('error')) -@pytest.fixture(scope='module', autouse=True) -def init(): - os.environ[POWERTOOLS_SERVICE_NAME] = SERVICE_NAME - os.environ[POWER_TOOLS_LOG_LEVEL] = 'DEBUG' - os.environ['REST_API'] = 'https://www.ranthebuilder.cloud/api' - os.environ['ROLE_ARN'] = 'arn:partition:service:region:account-id:resource-type:resource-id' - os.environ['CONFIGURATION_APP'] = SERVICE_NAME - os.environ['CONFIGURATION_ENV'] = ENVIRONMENT - os.environ['CONFIGURATION_NAME'] = CONFIGURATION_NAME - os.environ['CONFIGURATION_MAX_AGE_MINUTES'] = '5' - os.environ['AWS_DEFAULT_REGION'] = 'us-east-1' # used for appconfig mocked boto calls - os.environ['TABLE_NAME'] = get_stack_output(TABLE_NAME_OUTPUT) - - def test_handler_200_ok(mocker): mock_dynamic_configuration(mocker, MOCKED_SCHEMA) customer_name = 'RanTheBuilder' From da286e3b1a0b8ad8a06da27217298a93cf7afd0c Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Sat, 31 Dec 2022 13:31:54 +0200 Subject: [PATCH 3/3] fix lint --- .github/workflows/serverless-service.yml | 2 +- .../best_practices/dynamic_configuration/cdk_appconfig.py | 5 +++-- mypy.ini | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/serverless-service.yml b/.github/workflows/serverless-service.yml index 0b8c08b2..1b9fe690 100644 --- a/.github/workflows/serverless-service.yml +++ b/.github/workflows/serverless-service.yml @@ -43,7 +43,7 @@ jobs: - name: pre commit run: | make pre-commit - - name: Lint with flake8 + - name: Lint with flake8 and mypy run: | make lint - name: file format diff --git a/docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py b/docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py index f5527e5a..9ce09057 100644 --- a/docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py +++ b/docs/examples/best_practices/dynamic_configuration/cdk_appconfig.py @@ -1,7 +1,8 @@ from aws_cdk import Stack from constructs import Construct -from my_service.configuration.configuration_construct import ConfigurationStore -from my_service.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME + +from cdk.my_service.configuration.configuration_construct import ConfigurationStore +from cdk.my_service.constants import CONFIGURATION_NAME, ENVIRONMENT, SERVICE_NAME class DynamicConfigurationStack(Stack): diff --git a/mypy.ini b/mypy.ini index 30d3fb91..b2da3431 100644 --- a/mypy.ini +++ b/mypy.ini @@ -7,7 +7,6 @@ warn_unused_ignores=True show_column_numbers = True show_error_codes = True show_error_context = True -ignore_missing_imports = True [mypy-jmespath] ignore_missing_imports=True @@ -56,3 +55,6 @@ ignore_missing_imports = True [mypy-ijson] ignore_missing_imports = True + +[mypy-setuptools] +ignore_missing_imports = True