Lambda(Python3.6)でDynamoDBに登録された項目が日付型であるかチェックしてみる

どうも!大阪オフィスの西村祐二です。

DynamoDBで提供されている属性の型は大きく分けて
スカラー型、ドキュメント型、セット型となっています。

DynamoDBの命名ルールおよびデータ型

そのため、下記画像のように日付と時刻を登録するときは文字列データ型(String型)を使用するのがほとんどだと思います。

2017-10-20_20_01_56

ただ、こちらが想定するフォーマット以外でも登録することができてしまい、
登録された時刻をみて、ある処理を実行するときなど
処理が失敗してしまう可能性があります。

今回はLambdaを使って登録されている項目のフォーマットが正しいかチェックしてみたいと思います。

DynamoDBのテーブル作成

今回はCloudFormationを使ってDynamoDBのテーブルを作成します。

テンプレートファイルを作成します。
今回作成するテーブルはプライマリーキー「Date」、ソートキー「Time」とし、
属性のデータ型を文字列データの「S」として設定しています。

$ vi dynamodb.yml

---
AWSTemplateFormatVersion: "2010-09-09"

Resources:
  myDynamoDBTable:
    Type: "AWS::DynamoDB::Table"
    Properties:
      TableName: "dynamo-test"
      AttributeDefinitions:
        - AttributeName: Date
          AttributeType: S
        - AttributeName: Time
          AttributeType: S
      KeySchema:
        - AttributeName: Date
          KeyType: HASH
        - AttributeName: Time
          KeyType: RANGE
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1

下記コマンドにてテーブルを作成します。

$ aws cloudformation validate-template --template-body file://dynamodb.yml
$ aws cloudformation create-stack dynamodb-test --template-body file://dynamodb.yml

成功したら下記画像のように作成されているはずです。

2017-10-20_21_56_52

Lambda関数の作成

AWS Management Consoleにログインして、AWS Lambda Dashboardを開きます。
Lambda関数一覧の画面で、[関数の作成]ボタンをクリックします。
[一から作成]ボタンをクリックします。
名前を[dynamodb-check]とし、
ロールにはDynamoDBのテーブルをスキャンするので
"dynamodb:Scan"を付与しておいてください。
[関数の作成]ボタンをクリックします。

移動した画面で、ランタイムのドロップダウンリストからPython 3.6を選択します。
下記、プログラムをコピペし、保存ボタンをクリックします。

import boto3
import logging
from datetime import datetime

# dynamodb
DYNAMO_TABLE_NAME = 'dynamodb-test'
dynamodb = boto3.resource('dynamodb')
table    = dynamodb.Table(DYNAMO_TABLE_NAME)

def format_check(data):
    try:
        for i in range(len(data)):
            dynamo_datetime = str(data[i]['Date'])+ " " + str(data[i]['Time'])
            ## 日時フォーマットに変換 (ex:2017-10-19 01:01)
            get_datetime = datetime.strptime(dynamo_datetime, '%Y-%m-%d %H:%M')
            print(get_datetime)
        print('ok')
    except Exception as e:
        logger.exception("{}".format(e))
        return "ERROR End"

def handler(event, context):
    try:
        response = table.scan()
        data = response['Items']
        format_check(data)
    except Exception as e:
        logger.exception("{}".format(e))
        return "ERROR End"    

簡単に解説します。
ポイントは15行目です。

get_datetime = datetime.strptime(dynamo_datetime, '%Y-%m-%d %H:%M')

pythonのdatetime.strptimeはかなり便利で、
日付や時刻に対応する書式文字列から datetime オブジェクトに変換してくれます。

また、strptimeの第二引数は第一引数のフォーマットを渡すため、
これにマッチしないとエラーとなります。
ここでエラーになるということは、つまり、想定しないフォーマットで入力されていることになります。

今回の場合は
Date:2017-10-19
のような日付
Time:01:01
のような時刻が
入力されている想定です。

また、入力してほしいフォーマットが
Date:2017/10/19
のような日付の場合は
第二引数を%Y/%m/%dのように対応する形に変更すれば、OKです。

get_datetime = datetime.strptime(dynamo_datetime, '%Y/%m/%d %H:%M')

動作確認

DynamoDBにいろいろなデータをいれて、
動作を確認してみたいと思います。

正しいフォーマットでいれてみる

[項目の作成]をクリックして、
Date:2017-10-20
Time:20:20
を登録してみました。

2017-10-20 22.54.13

作成したLambda関数を実行させてみると
下記ログが出力され問題なく、変換できていることがわかります。

2017-10-20_23_01_43

時刻を一桁のままでいれてみる

[項目の作成]をクリックして、
Date:2017-10-20
Time:1:1
を登録してみました。

2017-10-20 23.08.28

作成したLambda関数を実行させてみると
下記ログが出力され問題なく、変換できていることがわかります。

2017-10-20_23_08_44

全角を入れてみる

[項目の作成]をクリックして、
Date:2017−10−20
Time:20:20
を登録してみました。

2017-10-20 23.15.50

作成したLambda関数を実行させてみると
下記ログが出力され想定どおり、エラー出力ができていることがわかります。

2017-10-20_23_14_08

ありえない日程をいれてみる

[項目の作成]をクリックして、
Date:2017-15-10
Time:20:20
を登録してみました。

2017-10-20 23.19.37

作成したLambda関数を実行させてみると
下記ログが出力され想定どおり、エラー出力ができていることがわかります。

2017-10-20_23_20_04

ありえない時刻をいれてみる

[項目の作成]をクリックして、
Date:2017-10-20
Time:25:20
を登録してみました。

2017-10-20 23.28.52

作成したLambda関数を実行させてみると
下記ログが出力され想定どおり、エラー出力ができていることがわかります。

2017-10-20_23_29_02

追加で、
Date:2017-10-20
Time:20:80
を登録してみました。

2017-10-20 23.21.01

作成したLambda関数を実行させてみると
下記ログが出力され
今回はなぜかunconverted dataというようなエラーが出力されました。

2017-10-20_23_22_31

さいごに

いかがだったでしょうか。
Lambdaをつかって
DynamoDBに登録された項目が日付型であるかチェックしてみました。
あとは、Lambdaで定期的に確認しエラーが出力されれば
ユーザにメールを送信すればとりあえずは良いかと思います。
もっといいやり方があれば教えてください。

誰かのためになれば幸いです。