Powertools for AWS Lambda (Python) の「Validation」を使うと AWS Lambda 関数に渡されたイベント情報のバリデーションを JSON Schema に沿って実現できる.例えば,必須パラメータ・文字数制限・ENUM・正規表現などをチェックできる👌
Powertools for AWS Lambda (Python) 自体は Tracer / Logger / Event Source Data Classes などをよく使うけど,Validation は今まで活用できてなく,試してみたらとても便利だったので,今回試した結果を簡単にまとめておく \( 'ω')/
検証環境
今回は AWS SAM を使って Amazon API Gateway (REST API) と AWS Lambda 関数を構築する.あくまでサンプルとして Amazon API Gateway の /
に POST リクエストを送るとバリデーションロジックを含んだ AWS Lambda 関数が実行されるようにした.また Powertools は Lambda Layer でセットアップする.
AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Resources: Function: Type: AWS::Serverless::Function Properties: FunctionName: powertools-validation CodeUri: src/ Handler: app.lambda_handler Runtime: python3.12 Architectures: - x86_64 Layers: - arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:67 Events: Api: Type: Api Properties: Path: / Method: POST
最終的なディレクトリ構成は以下のようになる💡
├── events │ └── event.json ├── samconfig.toml ├── src │ └── app.py └── template.yaml
👾 app.py
今回はバリデーションを紹介するサンプルとして,特にロジックはなく Amazon API Gateway への POST リクエストに対して 200 OK
もしくは 400 BAD_REQUEST
を返す実装にした.また AWS Lambda 関数のベストプラクティスを参考に Handler とロジックを分割して main()
にまとめてある(実際にはもっと細かく分割しても良さそう).
そして,今回は超簡易的な「TODO アプリ」を例として,title / category / description / link
というパラメータを受け取る API のバリデーションを実装した.パラメータごとのバリデーション要件は以下のコードの SCHEMA
を見てもらえればと❗️さらに Powertools for AWS Lambda (Python) の Validation ではバリデーションロジックを @validator
デコレータを使った実装と validate()
関数を使った実装から選べる.どちらも試してみて,個人的には以下の2つの理由から validate()
関数を使うのが良いと思った💡
validate()
関数は Lambda コンテキストに依存してなくてローカル開発がしやすかった- 例外発生時のハンドリングなどを柔軟に実装しやすかった
また validate()
関数を呼び出すときに envelope
を指定できて,イベントオブジェクトの中からバリデーションする箇所を限定できる.今回は Amazon API Gateway (REST API) から渡されるイベントをバリデーションするため,envelopes.API_GATEWAY_REST
を指定した.すると自動的に body
がバリデーション対象になる👌現状は8種類の envelope
が提供されている \( 'ω')/
- API_GATEWAY_HTTP
- API_GATEWAY_REST
- CLOUDWATCH_EVENTS_SCHEDULED
- CLOUDWATCH_LOGS
- EVENTBRIDGE
- KINESIS_DATA_STREAM
- SNS
- SQS
import json from aws_lambda_powertools.utilities.validation import SchemaValidationError, envelopes, validate from http import HTTPStatus from jmespath.exceptions import JMESPathTypeError SCHEMA = { '$schema': 'http://json-schema.org/draft-07/schema', 'type': 'object', 'required': ['title', 'category'], 'properties': { 'title': { 'type': 'string', 'pattern': '^[a-zA-Z0-9_]*$' }, 'category': { 'type': 'string', 'enum': ['Python', 'Go', 'Java'] }, 'description': { 'type': 'string', 'minLength': 10, 'maxLength': 100 }, 'link': { 'type': 'string', 'format': 'uri' } }, } def main(event): try: validate(event=event, schema=SCHEMA, envelope=envelopes.API_GATEWAY_REST) except JMESPathTypeError as e: return { 'statusCode': HTTPStatus.BAD_REQUEST, 'body': json.dumps({'message': str(e)}) } except json.JSONDecodeError as e: return { 'statusCode': HTTPStatus.BAD_REQUEST, 'body': json.dumps({'message': e.msg}) } except SchemaValidationError as e: return { 'statusCode': HTTPStatus.BAD_REQUEST, 'body': json.dumps({'message': e.validation_message}) } return { 'statusCode': HTTPStatus.OK, 'body': json.dumps({'message': 'ok'}) } def lambda_handler(event, context): return main(event) if __name__ == '__main__': with open('../events/event.json', 'r') as f: event = json.load(f) main(event)
動作確認
data must contain ['category', 'title'] properties
event.json
{}
必須の title
と category
プロパティがなくバリデーションエラー🙅♂️
$ curl -s -X POST --data @event.json ${ENDPOINT} {"message": "data must contain ['category', 'title'] properties"}
data.title must match pattern ^[a-zA-Z0-9_]*$
event.json
{ "title": "Powertoolsを試す", "category": "Python" }
title
に日本語を含んでいるためバリデーションエラー🙅♂️
$ curl -s -X POST --data @event.json ${ENDPOINT} {"message": "data.title must match pattern ^[a-zA-Z0-9_]*$"}
data.category must be one of ['Python', 'Go', 'Java']
event.json
{ "title": "Powertools", "category": "AWS" }
category
の ENUM 以外の値を指定しているためバリデーションエラー🙅♂️
$ curl -s -X POST --data @event.json ${ENDPOINT} {"message": "data.category must be one of ['Python', 'Go', 'Java']"}
data.description must be longer than or equal to 10 characters
event.json
{ "title": "Powertools", "category": "Python", "description": "" }
description
が10文字以上になっていないためバリデーションエラー🙅♂️
$ curl -s -X POST --data @event.json ${ENDPOINT} {"message": "data.description must be longer than or equal to 10 characters"}
data.link must be uri
event.json
{ "title": "Powertools", "category": "Python", "description": "Try Powertools for AWS Lambda.", "link": "docs.powertools.aws.dev" }
link
が URL 形式になっていないためバリデーションエラー🙅♂️
$ curl -s -X POST --data @event.json ${ENDPOINT} {"message": "data.link must be uri"}
まとめ
Powertools for AWS Lambda (Python) の「Validation」を使うと AWS Lambda 関数に渡されたイベント情報のバリデーションを柔軟に実装できてとても便利だった❗️
今後は積極的に使っていくぞー \( 'ω')/