alexaスキルをvisual studio codeでデバッグする(DynamoDBを使う場合)

[前回](https://qiita.com/miyu1/items/5571a7fbff7f75963871)に引き続き、今度はDynamo DBを使ったスキルをローカルデバッグする環境を作っていきます。
前回と同じく、alexa-hostedスキルの場合です。

DynamoDBを使うためのコードは[公式ドキュメント](https://developer.amazon.com/ja-JP/docs/alexa/hosted-skills/alexa-hosted-skills-session-persistence.html#persistence-python)の通りで本番環境だと問題なく動くのですが、vscodeに持ってくると全く動いてくれないので、順番に設定していきます。

#環境
環境を再掲しておきます。前回と同じです。

- **OS** mac OS 10.15.7(Catalina)
- **visual studio code** 1.59.1
- **python** 3.9.4 (homebrewからインストール)
- **ASK Toolkit Extension** 2.8.0

#1. pythonパッケージのインストール
公式ドキュメントのようにrequirements.txtを修正して、パッケージをインストールします。

```:lambda/requirements.txt
boto3==1.9.216
ask-sdk-core==1.11.0
ask-sdk-dynamodb-persistence-adapter==1.15.0
``` 

```sh
$ pip install -r requirements.txt
```

#2. 環境変数の設定
本番環境ではDynamoDBにアクセスするために環境変数を使っていますので、本番環境の環境変数を取得するために、以下のようにlambda_function.pyを変更します。

```lambda_function.py
import os
...
print("environments:", os.environ)
ddb_region = os.environ.get('DYNAMODB_PERSISTENCE_REGION')
ddb_table_name = os.environ.get('DYNAMODB_PERSISTENCE_TABLE_NAME')
...
```

deployして、本番環境でsimulatorテストしてみてください。

ところで、本番環境でprint文を実行した結果はどこに行くのでしょうか。
答えはCloudWatchログです。web上のalexa developer consoleの「コードエディタ」タグにCloudWatchへのリンクがありますので、そこから開くことができます。
ただ私の環境ではデフォルトではus-east-1(バージニア北部)リージョンのページが開くのですが、そのままだと「ロググループが存在しません」というエラーが出てしまい、ログをみることができなくて悩んでいました。
私の環境ではリージョンをus-west-2(オレゴン)にするとログを見ることができました。(リージョンはCloudWatchページの右上あたりで選択することができます)
![CloudWatchLog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793527/1adced75-5e6a-4c17-b658-dcc8b9dc56d7.png)

さて私の環境では環境変数は
- DYNAMODB_PERSISTENCE_REGION=us-east-1
- DYNAMODB_PERSISTENCE_TABLE_NAME=1ca5e6da-87d5-47e7-a25d-e46599c41fb
になっています。
ちなみにテーブル名はスキルIDに一致しています。

launch.jsonを変更して、環境変数を設定します。後述の理由で、テーブル名の方だけ設定します。

```:.vscode/launch.json
...
    "configurations": [
        {
            "name": "Debug Alexa Skill (Python)",
            "type": "python",
            "request": "launch",
            "program": "${command:ask.debugAdapterPath}",
            "pythonPath": "${command:python.interpreterPath}",
            "args": [
                "--accessToken",
                "${command:ask.accessToken}",
                "--skillId",
                "${command:ask.skillIdFromWorkspace}",
                "--skillHandler",
                "lambda_handler",
                "--skillFilePath",
                "${workspaceFolder}/lambda/lambda_function.py",
                "--region",
                "FE"
            ],
            "console": "internalConsole",
            "cwd": "${workspaceFolder}/lambda",
            "justMyCode": false,
            "env": {
                "DYNAMODB_PERSISTENCE_TABLE_NAME" : "1ca5e6da-87d5-47e7-a25d-e46599c41fbf"
            }
        },
...
```

#3. .awsフォルダの設定
$HOMEディレクトリに、.awsというディレクトリを作ります。すでにあるかもしれません。
そこにcredentialsファイルとconfigファイルを作成します。

```:$HOME/.aws/credentials
[default]
aws_access_key_id=XXXXXX
aws_secret_access_key=XXXXXXXXX
```

```:$HOME/.aws/config
[default]
region=us-east-1
```

後述しますがDynamoDBローカルを使うので、credentialsの値は何でも構いません。すでに値が入っていればそのままでいいと思います。
configのregionは環境変数DYNAMODB_PERSISTENCE_REGIONに合わせておいた方がいいかと思います。

この設定をしていないと、DynamoDbAdapterのimportの時点でNoRegionErrorがraiseされてしまいます。

#4. DynamoDBローカル
本来なら、本番環境と同じDynamoDBにアクセスできる方がテスト環境としてはいいと思うのですが、残念ながらうまくいきませんでした。
というのも、CloudWatchログも同じなので上記画像を見るとわかるかと思いますが、alexaスキルがアクセスするDynamoDBはVoiceHubSSORole?にフェデレーションログインしてアクセスするもので、個人アカウントとは別のアカウントになります。
この特殊アカウントではIAM(Identity and Access Management)を設定する権限もありませんし、credentialが取得できないのでローカルからアクセスする方法がわかりませんでした。
こういったデバッグをしたい場合は、alexa-hostedではなくてawsのlambdaを使ってねという事なのでしょうか…

そういう理由でローカルにDynamoDBサーバーを立てて、デバッグではこちらを使う事にします。
やり方は[awsドキュメント](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html)の通りなのですが、今回はdockerを使ってみようと思います。
dockerは[docker公式サイト](https://www.docker.com)からインストールしています。
適当なディレクトリを作り、docker-compose.ymlというファイルを作ります。

```:docker-compose.yml
version: '3.8'
services:
  dynamodb-local:
    command: "-jar DynamoDBLocal.jar -sharedDb -dbPath ./data"
    image: "amazon/dynamodb-local:latest"
    container_name: dynamodb-local
    ports:
      - "8000:8000"
    volumes:
      - "./docker/dynamodb:/home/dynamodblocal/data"
    working_dir: /home/dynamodblocal
```

起動します

```sh
$ docker-compose up
Creating network "dynamodb-local_default" with the default driver
Pulling dynamodb-local (amazon/dynamodb-local:latest)...
latest: Pulling from amazon/dynamodb-local
2cbe74538cb5: Pull complete
137077f50205: Pull complete
58932e640a40: Pull complete
Digest: sha256:bdd26570dc0e0ae49e1ea9d49ff662a6a1afe9121dd25793dc40d02802e7e806
Status: Downloaded newer image for amazon/dynamodb-local:latest
Creating dynamodb-local ... done
Attaching to dynamodb-local
dynamodb-local    | Initializing DynamoDB Local with the following configuration:
dynamodb-local    | Port:	8000
dynamodb-local    | InMemory:	false
dynamodb-local    | DbPath:	./data
dynamodb-local    | SharedDb:	true
dynamodb-local    | shouldDelayTransientStatuses:	false
dynamodb-local    | CorsParams:	*
dynamodb-local    | 
```

#5. テーブルの作成
DynamoDBローカルサーバーにテーブルを作成します。
下記pythonプログラムを実行してください。(table_nameは変更してください)

```python  
import boto3

dynamodb = boto3.resource('dynamodb', endpoint_url="http://localhost:8000")

table_name = "1ca5e6da-87d5-47e7-a25d-e46599c41fbf" 
dynamodb.create_table(
    TableName=table_name,
    KeySchema=[
        {
            'AttributeName': 'id',
            'KeyType': 'HASH'  # Partition key
        }
    ],
    AttributeDefinitions=[
        {
            'AttributeName': 'id',
            'AttributeType': 'S'
        },
    ],
    ProvisionedThroughput={
        'ReadCapacityUnits': 10,
        'WriteCapacityUnits': 10
    }
)
```

テーブルを作ると、先ほどdockerを立ち上げたディレクトリーの下に、docker/dynamodb/shared-local-instance.dbというファイルが作成されます。

#6. lambda_function.pyの変更
最後に、vscodeでデバッグ中はDynamoDBローカルを使うようにコードを変更します。
デバッグ中かどうかを判定するために、環境変数DYNAMODB_PERSISTENCE_REGIONがあるかどうかで判断する事にします。
そのため、先ほどlaunch.jsonにこの環境変数を設定しませんでした。

```python:lambda_function.py
ddb_region = os.environ.get('DYNAMODB_PERSISTENCE_REGION')
ddb_table_name = os.environ.get('DYNAMODB_PERSISTENCE_TABLE_NAME')

from ask_sdk_dynamodb.adapter import DynamoDbAdapter

if ddb_region :
    ddb_resource = boto3.resource('dynamodb', region_name=ddb_region)
else :
    ddb_resource = boto3.resource('dynamodb', endpoint_url="http://localhost:8000")

dynamodb_adapter = DynamoDbAdapter(table_name=ddb_table_name, create_table=False, dynamodb_resource=ddb_resource)
```

他の部分は[公式ドキュメント](https://developer.amazon.com/ja-JP/docs/alexa/hosted-skills/alexa-hosted-skills-session-persistence.html#persistence-python)通りで動いてくれました。

#まとめ
以上で、DynamoDBを使ってもvscodeでデバッグできるようになりました。
直接サーバーのDynamoDBを使う方法がないかと色々調べるのに苦労しましたが、できないとあきらめれば何とかなりました。
DynamoDBで調べていても、alexa-hostedの場合なのかawsの場合なのかわかりずらくて、なかなかいい情報に行き当たらなかったのでまとめてみました。
どなたかの助けになれば幸いです。