この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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で眺めると、色々勉強になります。