概要
AWS lambda functionが呼び出された際に、AWS dynamoDBのテーブルにデータを追加していく方法についてまとめます。
今回は、API Gateway と lambdaで構築した line botに対して送信されたデータを dynamoDBに追加していくところまで説明します。
IAMユーザの設定
AWSの IAM manageページから、新規にIAMユーザを作成します。
このユーザは、Dynamo DBに対する書き込み、読み込み権限そして、lambda関数からdynamoを使う権限を付与します。
「ユーザを追加ボタン」でユーザ名を入力し、「アクセスの種類」で、プログラムによるアクセスと、AWSマネジメントコンソールへのアクセスを許可します。
ユーザの作成を完了すると、 「アクセスキーID」と、「シークレットアクセスキー」が取得できます。 この2つをlambdaからdynamoDBへのアクセス時に必要なので、メモしておきます。
作成後、IAMユーザの設定画面に移り、「アクセス権限を追加」ボタンをおし、アクセス権限を追加していきます。
予めグループを作成し、そのグループにアクセス権限を追加する方法もありますが、今回はユーザに対してアクセス権限を追加します。
「アクセス許可の付与」画面で、「既存のポリシーを直接アタッチ」項目を選択し、もともとAWS側で用意されているポリシーをユーザに追加します。 DynamoDB用のアクセス権限を追加するため、「AmazonDynamoDBFullAccess」ポリシーを探し、選択します。
これでIAMユーザを作成し、そのユーザに対してlambdaからDynamoDBへのデータの読み書きする権限を付与できました。
DynamoDBの設定
DynamoDBにデータを追加するためのテーブルを作成します。
DynamoDBのマネジメントコンソールにアクセスし、「テーブルの作成」ボタンからテーブル作成画面に遷移します
テーブルの設定はデフォルトにし、テーブル名、プライマリーキーを設定します。
プライマリーキーはDynamoDBのテーブル内のアイテム毎にユニークなIDのことです。
本稿では、テーブル名はmessage プライマリーキーは_id と設定していきます
これでDynamoDBのテーブルを作成することができました。
lambda関数実装
IAMユーザの作成、DynamoDBのテーブルも作成できたので、次にlambda関数で、linebotに対して送られたメッセージをDynamoDBに保存する関数を実装していきます。
前回の 「aws lambdaとapi gatewayで linebotを作成する」で作成したlambda関数をベースに、DynamoDBにデータを追加する処理を実装します。
AWSの各サービスに対するクライアントsdkは公式で用意されており、
npm の aws-sdkモジュールがあるので、コレを利用します。
www.npmjs.com
DynamoDBへのアクセス時に、先程IAMユーザの作成時に生成されたAPIアクセスキーとシークレットアクセスキーを利用します。
コードに直接埋め込むのは管理上よくないので、lambdaの環境変数に指定します。
- DYNAMO_API_ACCESS_KEY : DynamoDBアクセス用IAMユーザのアクセスキー
- DYNAMO_USER_SECRET_ACCESS_KEY : DynamoDBアクセス用IAMユーザのシークレットアクセスキー
DynamoDB用クライアントインスタンスの作成は下記の用な感じで行います。
exports.handlerの内部で記述すると、関数が読み込まれるたびに初期化が走るため、exports.handlerの外部で初期化させます。
const https = require('https'); const AWS = require('aws-sdk'); // dynamoDBクライアントインスタンス初期化 // IAMユーザのAPIアクセスキー、シークレットアクセスキーを指定する。 // region apiVersionも指定する。 const dynamo = new AWS.DynamoDB({ region: 'ap-northeast-1', apiVersion: '2012-08-10', accessKeyId: process.env.DYNAMO_API_ACCESS_KEY, secretAccessKey: process.env.DYNAMO_USER_SECRET_ACCESS_KEY });
登録するデータは、linebotから来るリクエストデータにはいっている、ユーザID、メッセージタイプ(text や sticker[スタンプ]など)、ユーザが入力したテキスト を登録していきます。
メッセージが送信される毎にデータを登録していくため、ユニークキーであるidには一意なものを指定します。
ここでは、とりあえず $USERID$DATETIME を指定します。
データの登録は、DynamoDBインスタンスのputItemメソッドを使って行います。
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#putItem-property
上記ドキュメントに書かれているサンプルコードですが、パラメータにテーブル名、そして 追加するアイテムを指定してきます。
アイテム内の各アトリビュートは、文字列(S)や数値(N) など、それぞれ登録するデータの型をキーに指定します。
このあたりの仕様はドキュメントを参照してください。
/* This example adds a new item to the Music table. */ var params = { Item: { "AlbumTitle": { S: "Somewhat Famous" }, "Artist": { S: "No One You Know" }, "SongTitle": { S: "Call Me Today" } }, ReturnConsumedCapacity: "TOTAL", TableName: "Music" }; dynamodb.putItem(params, function(err, data) { if (err) console.log(err, err.stack); // an error occurred else console.log(data); // successful response /* data = { ConsumedCapacity: { CapacityUnits: 1, TableName: "Music" } } */ });
実際に書いたlambda関数は以下になります。
const https = require('https'); const AWS = require('aws-sdk'); // dynamoDBクライアントインスタンス初期化 // IAMユーザのAPIアクセスキー、シークレットアクセスキーを指定する。 // region apiVersionも指定する。 const dynamo = new AWS.DynamoDB({ region: 'ap-northeast-1', apiVersion: '2012-08-10', accessKeyId: process.env.DYNAMO_API_ACCESS_KEY, secretAccessKey: process.env.DYNAMO_USER_SECRET_ACCESS_KEY }); const tableName = 'message'; // linebotから送信されたメッセージデータから返信用メッセージデータを作成する // 本稿では、来たメッセージをそのまま返す function createResponseMessage(messageData) { const message = messageData.message; let resMessage; if (message.type === 'text') { // text resMessage = { type: message.type, text: message.text } } else if (message.type === 'sticker') { // stamp resMessage = { type: message.type, packageId: message.packageId, stickerId: message.stickerId }; } return resMessage; } exports.handler = (event, context, callback) => { const messageData = event.events && event.events[0]; const replyToken = messageData.replyToken; const message = messageData.message; const text = message.text; const source = messageData.source; const resMessage = createResponseMessage(messageData); const data = JSON.stringify({ replyToken: replyToken, messages: [resMessage] }); // LINE reply apiのレスポンス設定 const Authorization = 'Bearer ' + process.env.ENTER_ACCESS_TOKEN; opts = { hostname: 'api.line.me', path: '/v2/bot/message/reply', headers: { "Content-type": "application/json; charset=UTF-8", "Authorization": Authorization }, method: 'POST', }; const req = https.request(opts, function(res) { res.on('data', function(res) { console.log(res.toString()); }).on('error', function(e) { console.log('ERROR: ' + e.stack); }); }); req.write(data); req.end(); // DynamoDBへのメッセージデータ追加 const userId = source.userId; const params = { TableName: tableName, Item: { _id: { S: userId + '_' + Date.now(), }, userId: { S: userId }, messageType: { S: message.type } } }; // スタンプでなく文字列が投稿されたとき、textアトリビュートを追加する if (message.type === 'text') { params.Item['text'] = { S: message.text }; } dynamo.putItem(params, function(err, data) { if (err) { console.error("Error occured", err); } console.error(data); }); };
最後に、linebotに対してメッセージを送信したあと、DynamoDBコンソールでデータが追加されていることを確認します。