## 経緯
[[LocalStack]]がユーザーアカウントとトークン利用必須となり、商用利用には有償になったので代替を探していたところ、[[Moto]]がいいのではというのを見かけたので試してみる。
目的はテストのモック化ではなく、ローカルでの起動。登録は[[🦉Jumeaux]]([[Python]])、参照は[[🧊Miroir CLI]]([[Go]])のツールで実施する。
## 起動
[[Docker]]を使う。[[Docker Compose]]のファイル。
`docker-compose.yml`
```yaml
services:
motoserver:
image: motoserver/moto:latest
ports:
- "3456:3000"
environment:
- MOTO_PORT=3000
```
```console
docker compose up -d
```
## 動作確認
[[DynamoDB]]のテーブルと、[[S3]]のバケットを作成する。
```console
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=ap-northeast-1
export AWS_ENDPOINT_URL=http://localhost:3456
aws dynamodb create-table \
--table-name miroir \
--attribute-definitions AttributeName=hashkey,AttributeType=S \
--key-schema AttributeName=hashkey,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
aws s3api create-bucket \
--create-bucket-configuration LocationConstraint=ap-northeast-1 \
--bucket mamansoft-miroir
```
いくつか動作確認する。
### [[DynamoDB]]
```console
# テーブル一覧
$ aws dynamodb list-tables
{
"TableNames": [
"miroir"
]
}
```
```console
# 登録
aws dynamodb put-item --table-name miroir --item '{
"hashkey": {"S":"sample-001"},
"title": {"S":"sample title"},
"same_count": {"N":"10"},
"different_count": {"N":"2"},
"check_status": {"S":"todo"}
}'
```
```console
# 取得
aws dynamodb get-item --table-name miroir --key '{
"hashkey": {"S":"sample-001"}
}'
# 全件取得
aws dynamodb scan --table-name miroir
```
```console
# 更新
aws dynamodb update-item \
--table-name miroir \
--key '{"hashkey":{"S":"sample-001"}}' \
--update-expression 'SET check_status = :s' \
--expression-attribute-values '{":s":{"S":"done"}}'
```
```console
# 削除
aws dynamodb delete-item \
--table-name miroir \
--key '{"hashkey":{"S":"sample-001"}}'
```
```console
# テーブル削除
aws dynamodb delete-table \
--table-name miroir
```
### S3
```console
# バケット一覧
aws s3api list-buckets
```
```console
# ファイルのアップロード
echo '{"hello":"world"}' > sample.json
aws s3api put-object \
--bucket mamansoft-miroir \
--key results/sample-001/report.json \
--body sample.json
```
```console
# オブジェクト一覧取得
aws s3api list-objects-v2 \
--bucket mamansoft-miroir
```
```console
# オブジェクト取得
aws s3api get-object \
--bucket mamansoft-miroir \
--key results/sample-001/report.json \
downloaded.json
```
```console
# メタデータ確認
aws s3api head-object \
--bucket mamansoft-miroir \
--key results/sample-001/report.json
```
```console
# オブジェクト削除
aws s3api delete-object \
--bucket mamansoft-miroir \
--key results/sample-001/report.json
```
```console
# 再帰的にすべてのオブジェクトを削除
aws s3 rm s3://mamansoft-miroir \
--recursive
```
```console
# バケット削除
aws s3api delete-bucket \
--bucket mamansoft-miroir
```
## [[🦉Jumeaux]]で動かす
### バケットとテーブルの初期化
さっきと同じ流れをもう一度。
```console
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=ap-northeast-1
export AWS_ENDPOINT_URL=http://localhost:3456
aws dynamodb create-table \
--table-name miroir \
--attribute-definitions AttributeName=hashkey,AttributeType=S \
--key-schema AttributeName=hashkey,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
aws s3api create-bucket \
--create-bucket-configuration LocationConstraint=ap-northeast-1 \
--bucket mamansoft-miroir
```
```console
$ aws dynamodb list-tables
{
"TableNames": [
"miroir"
]
}
$ aws s3api list-buckets
{
"Buckets": [
{
"Name": "mamansoft-miroir",
"CreationDate": "2026-03-11T04:33:01+00:00"
}
],
"Owner": {
"DisplayName": "webfile",
"ID": "bcaf1ffd86f41161ca5fb16fd081034f"
},
"Prefix": null
}
```
### Jumeauxプロジェクトの準備
`config.yml` `requests/` `api/` の作成。
```console
uv run python jumeaux/main.py init ignore
```
設定変更。`final` の部分だけ。
`config.yml`
```yaml
final:
- name: json
- name: viewer
- name: miroir
config:
table: miroir
bucket: mamansoft-miroir
cache_max_age: 120
# 追加
endpoints:
dynamodb: http://localhost:3456
s3: http://localhost:3456
```
APIサーバー起動。
```console
make server
```
### 実行
```console
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=ap-northeast-1
uv run python jumeaux/main.py run requests
```