[日本語Alexa] Alexa-SDK Ver2 DynamoDB利用時のTips 〜リージョンを指定したり、プライマリーキーを変えて、スキルで共通のデータを保存したり〜
1 はじめに
本日、Alexa Blogに、SDK V1で自動生成されたDynamoDBテーブルをV2で引き継ぐための手法が公開されていました。
ASK SDK V1で作られたDynamoDBテーブルをそのままV2で使うためのコツ
上記ブログでは、DynamoDbPersistenceAdapterを自前で作成することで、プライマリキーやアトリビュート名をデフォルトのものから置き換える手法が紹介されています。
const dynamoDbPersistenceAdapter = new DynamoDbPersistenceAdapter({ tableName : 'persistence-demo', partitionKeyName: 'userId', attributesName: 'mapAttr' })
Alexa SDK V2では、かなりの勢いでラッパーされているため、デフォルト値を使用するのであれば、非常にシンプルにスキルを作成することができます。しかし、一部でも、デフォルト値を変更したいと考えると、そのドキュメントがまだ少ないため、ソースコードを確認する作業になります。
今回は、私がソースコードから得た情報で、DynamoDBのデフォルト値を変更する手法を2つ紹介させて下さい。 なお、本記事の内容は、私が手元で試しただけのものであることを、予めご了承下さい。
2 リージョン
以下のサンプルは、DynamoDBにテーブルを作成し、counterをインクリメントするだけのものです。
const Alexa = require('ask-sdk'); const tableName = 'test-sdk-v2-dynamodb'; let skill; exports.handler = async function (event, context) { if (!skill) { skill = Alexa.SkillBuilders.standard() .addRequestHandlers(TestHandler) .withTableName(tableName) // テーブル名指定 .withAutoCreateTable(true) //テーブル作成もスキルから行う .create(); } return skill.invoke(event); } const TestHandler = { canHandle(handlerInput) { return true; }, async handle(handlerInput) { // 永続化情報の取得 let attributes = await handlerInput.attributesManager.getPersistentAttributes() // 初期化 if(!attributes.counter){ attributes.counter = 0; } attributes.counter++; // 永続化情報の保存 handlerInput.attributesManager.setPersistentAttributes(attributes); await handlerInput.attributesManager.savePersistentAttributes(); return handlerInput.responseBuilder .speak('こんにちは') .getResponse(); } };
呼び出される度に、counterがインクリメントされます。
しかし、このスキルは、Lambdaがデプロイされたリージョンと同じリージョンのDynamoDBにテーブルを作成します。
DynamoDBのリージョンを自由に指定したい場合は、下記のように自前でDynamoDBクライアントを生成します。下記の例では、バージニアにテーブルが作成されることになります。
const Alexa = require('ask-sdk'); const AWS = require('aws-sdk'); const tableName = 'test-sdk-v2-dynamodb'; let skill; exports.handler = async function (event, context) { if (!skill) { skill = Alexa.SkillBuilders.standard() .addRequestHandlers(TestHandler) .withTableName(tableName) // テーブル名指定 .withAutoCreateTable(true) //テーブル作成もスキルから行う .withDynamoDbClient( new AWS.DynamoDB({ apiVersion: "latest", region: "us-east-1" }) ) .create(); } return skill.invoke(event); }
3 プライマリーキー
(1) userId
DynamoDBを利用した永続化では、Alexaから送られてきたUserIdを元にプライマリーキーを生成しています。
UserIDは、スキルが「有効」になっていから「無効」にされるまで、同一ユーザーであれば、利用するデバイスに関係なく同じとなるため、利用者の固有データを保存するというニーズでは、非常に理にかなった仕様です。
参考:Alexaからスキルに送られてくる各種IDの一意性については、下記のブログをご参照下さい。
(2) キー値の設定
ユーザーごとの情報を保存するのであれば、デフォルトの動作で全く問題ありませんが、ちょっとニーズが変わって、例えば、このスキルを使用した際の情報(全ユーザーをまたがった情報)や、日毎の情報(日毎の最高値や、トータルなど)を保存したい場合も、プライマリーキーを差し替えることで、AttributesManagerで同じように利用できるようになります。
ここで、StandardSkillBuilderを確認すると、DynamoDB関連のメソッドにwithPartitionKeyGeneratorというのがあり、PartitionKeyGeneratorというオブジェクトで初期化されている事がわかります。
StandardSkillBuilder.d.ts
export interface StandardSkillBuilder extends BaseSkillBuilder { withTableName(tableName: string): this; withAutoCreateTable(autoCreateTable: boolean): this; withPartitionKeyGenerator(partitionKeyGenerator: PartitionKeyGenerator): this; withDynamoDbClient(customDynamoDBClient: DynamoDB): this; }
そして、PartitionKeyGeneratorsの実装を確認すると下記のようになっていました。userIdがAlexaのリクエストからuserIdを抽出しているのが分かります。
PartitionKeyGenerators.ts
export const PartitionKeyGenerators = { /** * Gets attributes id using user id. * @param {RequestEnvelope} requestEnvelope * @returns {string} */ userId(requestEnvelope : RequestEnvelope) : string { if (!(requestEnvelope && requestEnvelope.context && requestEnvelope.context.System && requestEnvelope.context.System.user && requestEnvelope.context.System.user.userId)) { throw createAskSdkError( 'PartitionKeyGenerators', 'Cannot retrieve user id from request envelope!', ); } return requestEnvelope.context.System.user.userId; }, /** * Gets attributes id using device id. * @param {RequestEnvelope} requestEnvelope * @returns {string} */ deviceId(requestEnvelope : RequestEnvelope) : string { if (!(requestEnvelope && requestEnvelope.context && requestEnvelope.context.System && requestEnvelope.context.System.device && requestEnvelope.context.System.device.deviceId)) { throw createAskSdkError( 'PartitionKeyGenerators', 'Cannot retrieve device id from request envelope!', ); } return requestEnvelope.context.System.device.deviceId; }, };
withPartitionKeyGeneratorを使用して、キーのジェネレーターを差し替えるコードは、下記のようになります。
const tableName = 'test-sdk-v2-dynamodb'; let skill; exports.handler = async function (event, context) { if (!skill) { skill = Alexa.SkillBuilders.standard() .addRequestHandlers(TestHandler) .withTableName(tableName) // テーブル名指定 .withAutoCreateTable(true) //テーブル作成もスキルから行う .withPartitionKeyGenerator(OriginKey) .create(); } return skill.invoke(event); } function OriginKey() { return 'original-key'; }
このスキルを実行すると、全てのユーザーの情報が、origin-keyに保存されます。
OriginKey()を日付に応じた文字列を返すようにすれば、日毎のデータもが保存できることになります。
4 最後に
今回は、DynamoDBのデフォルト値を変更することで、自由にリージョンを変えたり、プライマリーキーを変更して、AttributesManagerを使用する要領を紹介させて頂きました。
デフォルト値の変更は、ドキュメントがない場合、どうしてもソースコードの確認が必要ですが、幸い、Alea SDK V2はTypeScriptで書かれているため、VSCodeで簡単に追跡することができます。
GutHubからコードをダウンロードして、VSCodeで眺めると、色々勉強になります。