Closed5

DynamoDBをAPIGatewayから叩く

yorosuyorosu

API Gateway、Lambda、および DynamoDB の統合をテストする

DynamoDB

パーティションキーのみでソートキーのないテーブルを作成
パーティションキーはsubs_name

Lambda

Pythonで作成

IAMはDynamoDBとCloudWatch Logsにアクセスできるポリシーを設定

import boto3
import json
from decimal import Decimal

# define the DynamoDB table that Lambda will connect to
tableName = 'subs_in_use_table'

# create the DynamoDB resource
dynamo = boto3.resource('dynamodb').Table(tableName)

print('Loading function')

def lambda_handler(event, context):
    '''Provide an event that contains the following keys:

      - operation: one of the operations in the operations dict below
      - payload: a JSON object containing parameters to pass to the 
                 operation being performed
    '''

    #Decimal型の場合はint型に変換
    def decimal_default_proc(obj):
        if isinstance(obj, Decimal):
            return int(obj)
        raise TypeError

    # define the functions used to perform the CRUD operations
    def ddb_create(x):
        dynamo.put_item(**x)
        return {
            'statusCode': 200,
            'body': json.dumps({'message': 'Item created successfully'})
        }

    def ddb_read(x):
        response = dynamo.get_item(**x)
        if 'Item' in response:
            item = response['Item']
            return {
                'statusCode': 200,
                'body': json.dumps(item,default=decimal_default_proc)
            }
        else:
            return {
                'statusCode': 404,
                'body': json.dumps({'message': 'Item not found'})
            }

    def ddb_update(x):
        dynamo.update_item(**x)
        return {
            'statusCode': 200,
            'body': json.dumps({'message': 'Item updated successfully'})
        }

        
    def ddb_delete(x):
        dynamo.delete_item(**x)
        return {
            'statusCode': 200,
            'body': json.dumps({'message': 'Item deleted successfully'})
        }

    event_body = json.loads( event['body'] )
    print(event_body)
    
    operation = event_body['operation']

    operations = {
        'create': ddb_create,
        'read': ddb_read,
        'update': ddb_update,
        'delete': ddb_delete,
    }

    if operation in operations:
        return operations[operation](event_body.get('payload'))
    else:
        raise ValueError('Unrecognized operation "{}"'.format(operation))

API Gateway

この記事を見て、REST APIのPOSTメソッドを作成

テスト

POST=>put_item()

リクエスト本文に以下のJSONを設定してテスト実行

{"operation":"create","payload": {"Item": {"subs_name": "Netflix","cost": 1000,"period": "monthly","update_date": "2024-06-01"}}}

以下のような結果が返ってきたため、成功

POST=>get_item()

リクエスト本文に以下のJSONを設定してテスト実行

{"operation":"read","payload":{"Key":{"subs_name":"Netflix"}}}

以下のような結果が返ってきたため、成功

エラー解決

[ERROR] TypeError: Object of type Decimal is not JSON serializableのエラーが出たため以下の記事を見て解決した

https://qiita.com/r-wakatsuki/items/452476fd86487ad88f1c#2-dynamodbにint型で登録した値が取り出し時にdecimal型になる

POST=>update_item()

リクエスト本文に以下のJSONを設定してテスト実行

{"operation":"update",
    "payload":{
        "Key":{"subs_name":"Netflix"},
        "UpdateExpression":"SET cost = :cost",
        "ExpressionAttributeValues":{":cost": 1490}
    }
}

以下のような結果が返ってきたため、成功

DynamoDBもちゃんと更新されてる

POST=>delete_item()

リクエスト本文に以下のJSONを設定してテスト実行

{"operation":"delete","payload":{"Key":{"subs_name":"Netflix"}}}

以下のような結果が返ってきたため、成功

yorosuyorosu

HTTPリクエストメソッドを使い分けるように改良する

API Gateway

Postメソッドにしか対応していなかったが、

  • GET
  • POST
  • PUT
  • DELETE
    を用意して使い分けるようにした。

Lambda

import logging

import boto3
import json
from decimal import Decimal

# define the DynamoDB table that Lambda will connect to
tableName = 'subs_in_use_table'

# create the DynamoDB resource
dynamo = boto3.resource('dynamodb').Table(tableName)

print('Loading function')

def lambda_handler(event, context):
    '''Provide an event that contains the following keys:

      - operation: one of the operations in the operations dict below
      - payload: a JSON object containing parameters to pass to the 
                 operation being performed
    '''

    #Decimal型の場合はint型に変換
    def decimal_default_proc(obj):
        if isinstance(obj, Decimal):
            return int(obj)
        raise TypeError

    # define the functions used to perform the CRUD operations
    def ddb_create(x):
        dynamo.put_item(**x)
        return {
            'statusCode': 200,
            'body': json.dumps({'message': 'Item created successfully'})
        }

    def ddb_read(x):
        response = dynamo.get_item(**x)
        if 'Item' in response:
            item = response['Item']
            return {
                'statusCode': 200,
                'body': json.dumps(item,default=decimal_default_proc)
            }
        else:
            return {
                'statusCode': 404,
                'body': json.dumps({'message': 'Item not found'})
            }

    def ddb_update(x):
        dynamo.update_item(**x)
        return {
            'statusCode': 200,
            'body': json.dumps({'message': 'Item updated successfully'})
        }

        
    def ddb_delete(x):
        dynamo.delete_item(**x)
        return {
            'statusCode': 200,
            'body': json.dumps({'message': 'Item deleted successfully'})
        }

    def ddb_scan():
        response = dynamo.scan()
        print(response)
        if 'Items' in response:
            items = response['Items']
            return {
                'statusCode': 200,
                'body': json.dumps(items,default=decimal_default_proc)
            }
        else:
            return {
                'statusCode': 404,
                'body': json.dumps({'message': 'Item not found'})
            }    
    
    
    print("Event:", event)

    operations = {
        'GET': ddb_scan,
        'POST': {
            'read': ddb_read,
            'create': ddb_create
        },
        'PUT': {
            'update': ddb_update
        },
        'DELETE': {
            'delete': ddb_delete
        }
    }
    
    httpMethod = event['httpMethod']
    
    if httpMethod == 'GET':
        return operations[httpMethod]()
    else:
        event_body = json.loads(event['body'])
        operation = event_body['operation']
        if operation in operations[httpMethod]:
            return operations[httpMethod][operation](event_body.get('payload'))
        else:
            raise ValueError('Unrecognized operation "{}"'.format(operation))
yorosuyorosu

API Gateway、Lambda、および DynamoDB の統合をテストする➁

DynamoDB

パーティションキーとソートキーの存在するテーブルを作成
パーティションキーはuserid、ソートキーはsubs_name

Lambda

Pythonで作成

IAMはDynamoDBとCloudWatch Logsにアクセスできるポリシーを設定

import logging

import boto3
import json
from decimal import Decimal

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# define the DynamoDB table that Lambda will connect to
tableName = 'subscription_in_use_table'

# create the DynamoDB resource
dynamo = boto3.resource('dynamodb').Table(tableName)

print('Loading function')

def lambda_handler(event, context):
    '''Provide an event that contains the following keys:

      - operation: one of the operations in the operations dict below
      - payload: a JSON object containing parameters to pass to the 
                 operation being performed
    '''

    #Decimal型の場合はint型に変換
    def decimal_default_proc(obj):
        if isinstance(obj, Decimal):
            return int(obj)
        raise TypeError

    # define the functions used to perform the CRUD operations
    def ddb_create(x):
        dynamo.put_item(**x)
        return {
            'statusCode': 200,
            'headers': { 
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Headers": "Content-Type"
            },
            'body': json.dumps({'message': 'Item created successfully'})
        }

    def ddb_read(x):
        response = dynamo.get_item(**x)
        if 'Item' in response:
            item = response['Item']
            return {
                'statusCode': 200,
                'headers': { 
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Headers": "Content-Type"
                },
                'body': json.dumps(item,default=decimal_default_proc)
            }
        else:
            return {
                'statusCode': 404,
                'headers': { 
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Headers": "Content-Type"
                },
                'body': json.dumps({'message': 'Item not found'})
            }

    def ddb_update(x):
        dynamo.update_item(**x)
        return {
            'statusCode': 200,
            'headers': { 
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Headers": "Content-Type"
            },            
            'body': json.dumps({'message': 'Item updated successfully'})
        }

        
    def ddb_delete(x):
        dynamo.delete_item(**x)
        return {
            'statusCode': 200,
            'headers': { 
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Headers": "Content-Type"
            },
            'body': json.dumps({'message': 'Item deleted successfully'})
        }

    def ddb_scan():
        response = dynamo.scan()
        print(response)
        if 'Items' in response:
            items = response['Items']
            return {
                'statusCode': 200,
                'headers': { 
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Headers": "Content-Type"
                },
                'body': json.dumps(items,default=decimal_default_proc)
            }
        else:
            return {
                'statusCode': 404,
                'headers': { 
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Headers": "Content-Type"
                },                
                'body': json.dumps({'message': 'Item not found'})
            }    
    
    print("Event:", event)

    operations = {
        'GET': ddb_scan,
        'POST': {
            'read': ddb_read,
            'create': ddb_create
        },
        'PUT': {
            'update': ddb_update
        },
        'DELETE': {
            'delete': ddb_delete
        }
    }
    
    httpMethod = event['httpMethod']
    
    if httpMethod == 'GET':
        return operations[httpMethod]()
    else:
        event_body = json.loads(event['body'])
        operation = event_body['operation']
        if operation in operations[httpMethod]:
            return operations[httpMethod][operation](event_body.get('payload'))
        else:
            raise ValueError('Unrecognized operation "{}"'.format(operation))

API Gateway

REST APIを作成
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/services-apigateway-tutorial.html#services-apigateway-tutorial-function

テスト

POST=>put_item()

リクエスト本文に以下のJSONを設定してテスト実行

{"operation":"create","payload": {"Item": { "userid":"(ユーザーID)", "subs_name": "U-Next","cost": 2189,"period": "monthly","update_date": "2024-06-01"}}}

以下のような結果が返ってきたため、成功

/DynamoDBManager - POST メソッドテストの結果
リクエスト
/DynamoDBManager
レイテンシー ms
1216
ステータス
200
レスポンス本文
{"message": "Item created successfully"}
レスポンスヘッダー
{
  "Access-Control-Allow-Headers": "Content-Type",
  "Access-Control-Allow-Origin": "*",
  "X-Amzn-Trace-Id": "Root=1-66519345-e43b5b6a27257c7e1e79928f;Parent=06b460853d21af20;Sampled=0;lineage=c412f566:0"
}

POST=>get_item()

リクエスト本文に以下のJSONを設定してテスト実行

{"operation": "read", "payload": {"Key": {
    "userid": "(ユーザーID)",
    "subs_name": "Netflix"
}}}

以下のような結果が返ってきたため、成功

/DynamoDBManager - POST メソッドテストの結果
リクエスト
/DynamoDBManager
レイテンシー ms
1157
ステータス
200
レスポンス本文
{"subs_name": "Netflix", "cost": 1490, "period": "monthly", "userid": "(ユーザーID)", "update_date": "2024-06-01"}

GET=>scan()

実行すると以下のような結果が返ってきたため成功

/DynamoDBManager - GET メソッドテストの結果
リクエスト
/DynamoDBManager
レイテンシー ms
1184
ステータス
200
レスポンス本文
[{"subs_name": "Netflix", "cost": 990, "period": "monthly", "userid": "(ユーザーID)", "update_date": "2024-06-01"}, {"subs_name": "U-Next", "cost": 2189, "period": "monthly", "userid": "(ユーザーID)", "update_date": "2024-06-01"}, {"subs_name": "\u30e9\u30b8\u30b3\u30d7\u30ec\u30df\u30a2\u30e0", "cost": 385, "period": "monthly", "userid": "(ユーザーID)", "update_date": "2024-06-01"}]
レスポンスヘッダー
{
  "Access-Control-Allow-Headers": "Content-Type",
  "Access-Control-Allow-Origin": "*",
  "X-Amzn-Trace-Id": "Root=1-66527ea8-4f0f4b6659e36020ffd57ab9;Parent=4e327a9e5f158d86;Sampled=0;lineage=c412f566:0"
}

PUT=>update_item()

リクエスト本文に以下のJSONを設定してテスト実行

{"operation":"update",
    "payload":{
        "Key":{ "userid": "(ユーザーID)","subs_name":"Netflix"},
        "UpdateExpression":"SET cost = :cost",
        "ExpressionAttributeValues":{":cost": 1490}
    }
}

以下のような結果が返ってきたため、成功

/DynamoDBManager - PUT メソッドテストの結果
リクエスト
/DynamoDBManager
レイテンシー ms
102
ステータス
200
レスポンス本文
{"message": "Item updated successfully"}
レスポンスヘッダー
{
  "Access-Control-Allow-Headers": "Content-Type",
  "Access-Control-Allow-Origin": "*",
  "X-Amzn-Trace-Id": "Root=1-66527ee8-cf05c5e855e573a4f116c697;Parent=7147d67fc8e7e895;Sampled=0;lineage=c412f566:0"
}

DELETE=>delete_item()

リクエスト本文に以下のJSONを設定してテスト実行

{"operation":"delete","payload":{"Key":{ "userid": "(ユーザーID)","subs_name":"Netflix"}}}

以下のような結果が返ってきたため、成功

/DynamoDBManager - DELETE メソッドテストの結果
リクエスト
/DynamoDBManager
レイテンシー ms
485
ステータス
200
レスポンス本文
{"message": "Item deleted successfully"}
レスポンスヘッダー
{
  "Access-Control-Allow-Headers": "Content-Type",
  "Access-Control-Allow-Origin": "*",
  "X-Amzn-Trace-Id": "Root=1-66527f4b-f012b9d4c2af9835912a23b7;Parent=20337fe346708d8b;Sampled=0;lineage=c412f566:0"
}
このスクラップは2024/05/19にクローズされました