diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md index c2b5a55cdb4..bbbb9caef30 100755 --- a/.chglog/CHANGELOG.tpl.md +++ b/.chglog/CHANGELOG.tpl.md @@ -5,8 +5,9 @@ {{ if .Unreleased.CommitGroups -}} {{ range .Unreleased.CommitGroups -}} ### {{ .Title }} + {{ range .Commits -}} -- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +* {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} {{ end }} {{ end -}} {{ end -}} @@ -17,22 +18,24 @@ ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} {{ range .CommitGroups -}} ### {{ .Title }} + {{ range .Commits -}} -- {{ if .Scope }}**{{ upperFirst .Scope }}:** {{ end }}{{ .Subject }} +* {{ if .Scope }}**{{ upperFirst .Scope }}:** {{ end }}{{ .Subject }} {{ end }} {{ end -}} {{- if .RevertCommits -}} ### Reverts {{ range .RevertCommits -}} -- {{ .Revert.Header }} +* {{ .Revert.Header }} {{ end }} {{ end -}} {{- if .MergeCommits -}} ### Pull Requests + {{ range .MergeCommits -}} -- {{ .Header }} +* {{ .Header }} {{ end }} {{ end -}} diff --git a/.chglog/config.yml b/.chglog/config.yml index 294b3289981..035aa04e2fa 100755 --- a/.chglog/config.yml +++ b/.chglog/config.yml @@ -20,7 +20,7 @@ options: perf: Performance Improvements refactor: Code Refactoring docs: Documentation - chore: Project maintenance + chore: Maintenance header: pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$" pattern_maps: diff --git a/CHANGELOG.md b/CHANGELOG.md index b2e950fa968..aea741921de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,28 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.17.1] - 2021-07-02 + +### Bug Fixes + +* **Validator:** Handle built-in custom formats like `date-time` when type is `string` ([#498](https://github.com/awslabs/aws-lambda-powertools-python/issues/498)) + +### Documentation + +* **Layers:** Add Layers example for Serverless framework & CDK ([#500](https://github.com/awslabs/aws-lambda-powertools-python/issues/500)) +* **Misc.:** Enable dark mode switch ([#471](https://github.com/awslabs/aws-lambda-powertools-python/issues/471)) +* **Tracer:** Additional scenario when to disable auto-capture for responses larger than 64K ([#499](https://github.com/awslabs/aws-lambda-powertools-python/issues/499)) + +### Maintenance + +* **deps:** bump boto3 from 1.17.101 to 1.17.102 ([#493](https://github.com/awslabs/aws-lambda-powertools-python/issues/493)) +* **deps:** bump boto3 from 1.17.91 to 1.17.101 ([#490](https://github.com/awslabs/aws-lambda-powertools-python/issues/490)) +* **deps:** bump email-validator from 1.1.2 to 1.1.3 ([#478](https://github.com/awslabs/aws-lambda-powertools-python/issues/478)) +* **deps:** bump boto3 from 1.17.89 to 1.17.91 ([#473](https://github.com/awslabs/aws-lambda-powertools-python/issues/473)) +* **deps-dev:** bump flake8-eradicate from 1.0.0 to 1.1.0 ([#492](https://github.com/awslabs/aws-lambda-powertools-python/issues/492)) +* **deps-dev:** bump isort from 5.8.0 to 5.9.1 ([#487](https://github.com/awslabs/aws-lambda-powertools-python/issues/487)) +* **deps-dev:** bump mkdocs-material from 7.1.7 to 7.1.9 ([#491](https://github.com/awslabs/aws-lambda-powertools-python/issues/491)) + ## [1.17.0] - 2021-06-08 ### Added diff --git a/aws_lambda_powertools/utilities/validation/base.py b/aws_lambda_powertools/utilities/validation/base.py index eb84f300ded..ec4165f4876 100644 --- a/aws_lambda_powertools/utilities/validation/base.py +++ b/aws_lambda_powertools/utilities/validation/base.py @@ -32,6 +32,7 @@ def validate_data_against_schema(data: Dict, schema: Dict, formats: Optional[Dic When JSON schema provided is invalid """ try: + formats = formats or {} fastjsonschema.validate(definition=schema, data=data, formats=formats) except (TypeError, AttributeError, fastjsonschema.JsonSchemaDefinitionException) as e: raise InvalidSchemaFormatError(f"Schema received: {schema}, Formats: {formats}. Error: {e}") diff --git a/docs/core/logger.md b/docs/core/logger.md index 45119ca51d6..43d367e171b 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -1099,3 +1099,9 @@ Here's an example where we persist `payment_id` not `request_id`. Note that `pay "payment_id": "123456789" } ``` + +**How do I aggregate and search Powertools logs across accounts?** + +As of now, ElasticSearch (ELK) or 3rd party solutions are best suited to this task. + +Please see this discussion for more information: https://github.com/awslabs/aws-lambda-powertools-python/issues/460 diff --git a/docs/core/tracer.md b/docs/core/tracer.md index fb99c6c702b..b9cd86862f5 100644 --- a/docs/core/tracer.md +++ b/docs/core/tracer.md @@ -201,10 +201,11 @@ tracer = Tracer(patch_modules=modules_to_be_patched) Use **`capture_response=False`** parameter in both `capture_lambda_handler` and `capture_method` decorators to instruct Tracer **not** to serialize function responses as metadata. -!!! info "This is commonly useful in two scenarios" +!!! info "This is commonly useful in three scenarios" 1. You might **return sensitive** information you don't want it to be added to your traces 2. You might manipulate **streaming objects that can be read only once**; this prevents subsequent calls from being empty + 3. You might return **more than 64K** of data _e.g., `message too long` error_ === "sensitive_data_scenario.py" ```python hl_lines="3 7" diff --git a/docs/index.md b/docs/index.md index 9edb82f4716..ce0573915fd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -47,23 +47,81 @@ Powertools is also available as a Lambda Layer, and it is distributed via the [A If using SAM, you can include this SAR App as part of your shared Layers stack, and lock to a specific semantic version. Once deployed, it'll be available across the account this is deployed to. -=== "template.yml" - -```yaml hl_lines="5-6 12-13" -AwsLambdaPowertoolsPythonLayer: - Type: AWS::Serverless::Application - Properties: - Location: - ApplicationId: arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer - SemanticVersion: 1.10.2 # change to latest semantic version available in SAR - -MyLambdaFunction: - Type: AWS::Serverless::Function - Properties: - Layers: - # fetch Layer ARN from SAR App stack output - - !GetAtt AwsLambdaPowertoolsPythonLayer.Outputs.LayerVersionArn -``` +=== "SAM" + + ```yaml hl_lines="5-6 12-13" + AwsLambdaPowertoolsPythonLayer: + Type: AWS::Serverless::Application + Properties: + Location: + ApplicationId: arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer + SemanticVersion: 1.17.0 # change to latest semantic version available in SAR + + MyLambdaFunction: + Type: AWS::Serverless::Function + Properties: + Layers: + # fetch Layer ARN from SAR App stack output + - !GetAtt AwsLambdaPowertoolsPythonLayer.Outputs.LayerVersionArn + ``` + +=== "Serverless framework" + + ```yaml hl_lines="5 8 10-11" + functions: + main: + handler: lambda_function.lambda_handler + layers: + - !GetAtt AwsLambdaPowertoolsPythonLayer.Outputs.LayerVersionArn + + resources: + Transform: AWS::Serverless-2016-10-31 + Resources: + AwsLambdaPowertoolsPythonLayer: + Type: AWS::Serverless::Application + Properties: + Location: + ApplicationId: arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer + # Find latest from github.com/awslabs/aws-lambda-powertools-python/releases + SemanticVersion: 1.17.0 + ``` + +=== "CDK" + + ```python hl_lines="14 22-23 31" + from aws_cdk import core, aws_sam as sam, aws_lambda + + POWERTOOLS_BASE_NAME = 'AWSLambdaPowertools' + # Find latest from github.com/awslabs/aws-lambda-powertools-python/releases + POWERTOOLS_VER = '1.17.0' + POWERTOOLS_ARN = 'arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer' + + class SampleApp(core.Construct): + + def __init__(self, scope: core.Construct, id_: str) -> None: + super().__init__(scope, id_) + + # Launches SAR App as CloudFormation nested stack and return Lambda Layer + powertools_app = sam.CfnApplication(self, + f'{POWERTOOLS_BASE_NAME}Application', + location={ + 'applicationId': POWERTOOLS_ARN, + 'semanticVersion': POWERTOOLS_VER + }, + ) + + powertools_layer_arn = powertools_app.get_att("Outputs.LayerVersionArn").to_string() + powertools_layer_version = aws_lambda.LayerVersion.from_layer_version_arn(self, f'{POWERTOOLS_BASE_NAME}', powertools_layer_arn) + + aws_lambda.Function(self, + 'sample-app-lambda', + runtime=aws_lambda.Runtime.PYTHON_3_8, + function_name='sample-lambda', + code=aws_lambda.Code.asset('./src'), + handler='app.handler', + layers: [powertools_layer_version] + ) + ``` ??? tip "Example of least-privileged IAM permissions to deploy Layer" diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index 831c864fd5f..f24b32faa14 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -4,6 +4,10 @@ .highlight .hll { background-color: lavender + + [data-md-color-scheme="slate"] { + background-color: rgb(69, 48, 164) + } } .md-typeset table:not([class]) { @@ -25,3 +29,7 @@ .md-typeset .admonition, .md-typeset details { font-size: 0.70rem } + +[data-md-color-scheme="slate"] { + --md-typeset-a-color: rgb(28, 152, 152) +} diff --git a/mkdocs.yml b/mkdocs.yml index 0a761ad9540..7ee0fd56236 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,7 +29,17 @@ nav: theme: name: material palette: - primary: deep purple + - scheme: default + primary: deep purple + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + - scheme: slate + primary: indigo + accent: teal + toggle: + icon: material/toggle-switch + name: Switch to light mode features: - navigation.sections - navigation.expand diff --git a/poetry.lock b/poetry.lock index ca9ac489f80..9a6f1fdbc04 100644 --- a/poetry.lock +++ b/poetry.lock @@ -81,20 +81,20 @@ d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] name = "boto3" -version = "1.17.89" +version = "1.17.102" description = "The AWS SDK for Python" category = "main" optional = false python-versions = ">= 2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.dependencies] -botocore = ">=1.20.89,<1.21.0" +botocore = ">=1.20.102,<1.21.0" jmespath = ">=0.7.1,<1.0.0" s3transfer = ">=0.4.0,<0.5.0" [[package]] name = "botocore" -version = "1.20.89" +version = "1.20.102" description = "Low-level, data-driven core of boto 3." category = "main" optional = false @@ -106,7 +106,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = ">=1.25.4,<1.27" [package.extras] -crt = ["awscrt (==0.11.15)"] +crt = ["awscrt (==0.11.24)"] [[package]] name = "certifi" @@ -183,11 +183,11 @@ trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"] [[package]] name = "email-validator" -version = "1.1.2" +version = "1.1.3" description = "A robust email syntax and deliverability validation library for Python 2.x/3.x." category = "main" optional = true -python-versions = "*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] dnspython = ">=1.15.0" @@ -294,7 +294,7 @@ six = "*" [[package]] name = "flake8-eradicate" -version = "1.0.0" +version = "1.1.0" description = "Flake8 plugin to find commented out code" category = "dev" optional = false @@ -413,16 +413,17 @@ python-versions = "*" [[package]] name = "isort" -version = "5.8.0" +version = "5.9.1" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.6.1,<4.0" [package.extras] pipfile_deprecated_finder = ["pipreqs", "requirementslib"] requirements_deprecated_finder = ["pipreqs", "pip-api"] colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] [[package]] name = "jinja2" @@ -591,7 +592,7 @@ mkdocs = ">=0.17" [[package]] name = "mkdocs-material" -version = "7.1.7" +version = "7.1.9" description = "A Material Design theme for MkDocs" category = "dev" optional = false @@ -1065,7 +1066,7 @@ pydantic = ["pydantic", "email-validator"] [metadata] lock-version = "1.1" python-versions = "^3.6.1" -content-hash = "3159635f02dd232e8271d6fd4f6b1b92cefb6f8b8ada60bda6929f3839515862" +content-hash = "c07aad70013171c8bb000d163f997efef709189584089f68d471f69eb8bb38c0" [metadata.files] appdirs = [ @@ -1092,12 +1093,12 @@ black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] boto3 = [ - {file = "boto3-1.17.89-py2.py3-none-any.whl", hash = "sha256:1f02cd513b130f9cd86c99836de6a0a5f78ea55110bdbc9011d9d78ff0fd3204"}, - {file = "boto3-1.17.89.tar.gz", hash = "sha256:06d8dca85a0bb66b7bf2721745895d44691c78dbe7eb3b146702aff85e34af34"}, + {file = "boto3-1.17.102-py2.py3-none-any.whl", hash = "sha256:6300e9ee9a404038113250bd218e2c4827f5e676efb14e77de2ad2dcb67679bc"}, + {file = "boto3-1.17.102.tar.gz", hash = "sha256:be4714f0475c1f5183eea09ddbf568ced6fa41b0fc9976f2698b8442e1b17303"}, ] botocore = [ - {file = "botocore-1.20.89-py2.py3-none-any.whl", hash = "sha256:e112f9a45db1c5a42f787e4b228a35da6e823bcba70f43f43005b4fb58066446"}, - {file = "botocore-1.20.89.tar.gz", hash = "sha256:ce0fa8bc260ad187824052805d224cee239d953bb4bfb1e52cf35ad79481b316"}, + {file = "botocore-1.20.102-py2.py3-none-any.whl", hash = "sha256:bdf08a4f7f01ead00d386848f089c08270499711447569c18d0db60023619c06"}, + {file = "botocore-1.20.102.tar.gz", hash = "sha256:2f57f7ceed1598d96cc497aeb45317db5d3b21a5aafea4732d0e561d0fc2a8fa"}, ] certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, @@ -1178,8 +1179,8 @@ dnspython = [ {file = "dnspython-2.1.0.zip", hash = "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"}, ] email-validator = [ - {file = "email-validator-1.1.2.tar.gz", hash = "sha256:1a13bd6050d1db4475f13e444e169b6fe872434922d38968c67cea9568cce2f0"}, - {file = "email_validator-1.1.2-py2.py3-none-any.whl", hash = "sha256:094b1d1c60d790649989d38d34f69e1ef07792366277a2cf88684d03495d018f"}, + {file = "email_validator-1.1.3-py2.py3-none-any.whl", hash = "sha256:5675c8ceb7106a37e40e2698a57c056756bf3f272cfa8682a4f87ebd95d8440b"}, + {file = "email_validator-1.1.3.tar.gz", hash = "sha256:aa237a65f6f4da067119b7df3f13e89c25c051327b2b5b66dc075f33d62480d7"}, ] eradicate = [ {file = "eradicate-2.0.0.tar.gz", hash = "sha256:27434596f2c5314cc9b31410c93d8f7e8885747399773cd088d3adea647a60c8"}, @@ -1213,8 +1214,8 @@ flake8-debugger = [ {file = "flake8_debugger-4.0.0-py3-none-any.whl", hash = "sha256:82e64faa72e18d1bdd0000407502ebb8ecffa7bc027c62b9d4110ce27c091032"}, ] flake8-eradicate = [ - {file = "flake8-eradicate-1.0.0.tar.gz", hash = "sha256:fe7167226676823d50cf540532302a6f576c5a398c5260692571a05ef72c5f5b"}, - {file = "flake8_eradicate-1.0.0-py3-none-any.whl", hash = "sha256:0fc4ab858a18c7ed630621b5345254c8f55be6060ea5c44a25e384d613618d1f"}, + {file = "flake8-eradicate-1.1.0.tar.gz", hash = "sha256:f5917d6dbca352efcd10c15fdab9c55c48f0f26f6a8d47898b25d39101f170a8"}, + {file = "flake8_eradicate-1.1.0-py3-none-any.whl", hash = "sha256:d8e39b684a37c257a53cda817d86e2d96c9ba3450ddc292742623a5dfee04d9e"}, ] flake8-fixme = [ {file = "flake8-fixme-1.1.1.tar.gz", hash = "sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a"}, @@ -1255,8 +1256,8 @@ iniconfig = [ {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] isort = [ - {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, - {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, + {file = "isort-5.9.1-py3-none-any.whl", hash = "sha256:8e2c107091cfec7286bc0f68a547d0ba4c094d460b732075b6fba674f1035c0c"}, + {file = "isort-5.9.1.tar.gz", hash = "sha256:83510593e07e433b77bd5bff0f6f607dbafa06d1a89022616f02d8b699cfcd56"}, ] jinja2 = [ {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, @@ -1342,8 +1343,8 @@ mkdocs-git-revision-date-plugin = [ {file = "mkdocs_git_revision_date_plugin-0.3.1-py3-none-any.whl", hash = "sha256:8ae50b45eb75d07b150a69726041860801615aae5f4adbd6b1cf4d51abaa03d5"}, ] mkdocs-material = [ - {file = "mkdocs-material-7.1.7.tar.gz", hash = "sha256:34d57af1e3e68ff4251feb82ced70545d8aa6064861ba76b1a15928399d21879"}, - {file = "mkdocs_material-7.1.7-py2.py3-none-any.whl", hash = "sha256:1725d02efed5d989258fd1620673e78a7171f82028f30c2da8d21e7539150221"}, + {file = "mkdocs-material-7.1.9.tar.gz", hash = "sha256:5a2fd487f769f382a7c979e869e4eab1372af58d7dec44c4365dd97ef5268cb5"}, + {file = "mkdocs_material-7.1.9-py2.py3-none-any.whl", hash = "sha256:92c8a2bd3bd44d5948eefc46ba138e2d3285cac658900112b6bf5722c7d067a5"}, ] mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, diff --git a/pyproject.toml b/pyproject.toml index b0ef085c31d..f7743f2eba2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "aws_lambda_powertools" -version = "1.17.0" +version = "1.17.1" description = "Python utilities for AWS Lambda functions including but not limited to tracing, logging and custom metric" authors = ["Amazon Web Services"] include = ["aws_lambda_powertools/py.typed"] @@ -39,7 +39,7 @@ flake8-debugger = "^4.0.0" flake8-fixme = "^1.1.1" flake8-isort = "^4.0.0" flake8-variables-names = "^0.0.4" -isort = "^5.8.0" +isort = "^5.9.1" pytest-cov = "^2.12.1" pytest-mock = "^3.5.1" pdoc3 = "^0.9.2" @@ -47,9 +47,9 @@ pytest-asyncio = "^0.15.1" bandit = "^1.7.0" radon = "^4.5.0" xenon = "^0.7.3" -flake8-eradicate = "^1.0.0" +flake8-eradicate = "^1.1.0" flake8-bugbear = "^21.3.2" -mkdocs-material = "^7.1.7" +mkdocs-material = "^7.1.9" mkdocs-git-revision-date-plugin = "^0.3.1" mike = "^0.6.0" diff --git a/tests/functional/validator/conftest.py b/tests/functional/validator/conftest.py index ab7a26012ba..b492e7846f1 100644 --- a/tests/functional/validator/conftest.py +++ b/tests/functional/validator/conftest.py @@ -565,3 +565,24 @@ def eventbridge_schema_registry_cloudtrail_v2_s3(): "x-amazon-events-detail-type": "AWS API Call via CloudTrail", "x-amazon-events-source": "aws.s3", } + + +@pytest.fixture +def schema_datetime_format(): + return { + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "Sample schema with string date-time format", + "description": "The root schema comprises the entire JSON document.", + "required": ["message"], + "properties": { + "message": { + "$id": "#/properties/message", + "type": "string", + "format": "date-time", + "title": "The message", + "examples": ["hello world"], + }, + }, + } diff --git a/tests/functional/validator/test_validator.py b/tests/functional/validator/test_validator.py index 4a773571ddc..d8986ba90de 100644 --- a/tests/functional/validator/test_validator.py +++ b/tests/functional/validator/test_validator.py @@ -151,3 +151,12 @@ def _func_echo_decoder(self, value): envelope="powertools_json(data).payload", jmespath_options=jmespath_opts, ) + + +def test_validate_date_time_format(schema_datetime_format): + raw_event = {"message": "2021-06-29T14:46:06.804Z"} + validate(event=raw_event, schema=schema_datetime_format) + + invalid_datetime = {"message": "2021-06-29T14"} + with pytest.raises(exceptions.SchemaValidationError, match="data.message must be date-time"): + validate(event=invalid_datetime, schema=schema_datetime_format)