AWS: ChatGPT で DynamoDB の UpdateCommand を再学習してみる

# 背景
ChatGPT を使って学習の効率化をしたい、という思い。
GPT3.0 API を使ったりとかもやってみたけれど、まずどんなもんか？をやっぱり体験しておこう、と

# やりとりの流れ
timestampserver を追加する Lambda のサンプルコードちょうだい、みたいなこといったら・・

```typescript
import { DynamoDB, Lambda } from 'aws-sdk';

const dynamo = new DynamoDB.DocumentClient();

exports.handler = async (event: any, context: Lambda.Context) => {
  const user = event.Records[0].dynamodb.NewImage;
  user.timestampserver = new Date().toISOString();

  const params = {
    TableName: 'user',
    Item: user
  };

  try {
    await dynamo.put(params).promise();
    return { status: 'success' };
  } catch (err) {
    return { status: 'error', error: err };
  }
};

```

V2 コードを返してくれた・・
V3 にしてーってお願いしても、反応してくれなかったので、
v3 ライブラリとして、 "@aws-sdk/lib-dynamodb" 使って！ってお願いしたところ

```typescript
const { DynamoDBClient, PutItemCommand } = require('@aws-sdk/client-dynamodb');

const dynamo = new DynamoDBClient({});

exports.handler = async (event) => {
  const user = event.Records[0].dynamodb.NewImage;
  user.timestampserver = new Date().toISOString();

  const params = {
    TableName: 'user',
    Item: user
  };

  try {
    await dynamo.send(new PutItemCommand(params));
    return { status: 'success' };
  } catch (err) {
    return { status: 'error', error: err };
  }
};
```

何故か、下層の client-dynamodb を使って返してくれた。
一応 v3 だけど、それじゃない・・
それに、Put じゃ置換になっちゃうじゃんって突っ込んだら、Update には変えてくれた

```typescript
const { DynamoDBClient, UpdateCommand } = require('@aws-sdk/client-dynamodb');

const dynamo = new DynamoDBClient({});

exports.handler = async (event) => {
  const user = event.Records[0].dynamodb.NewImage;
  user.timestampserver = new Date().toISOString();

  const params = {
    TableName: 'user',
    Key: {
      id: user.id
    },
    UpdateExpression: 'SET timestampserver = :timestampserver',
    ExpressionAttributeValues: {
      ':timestampserver': user.timestampserver
    }
  };

  try {
    await dynamo.send(new UpdateCommand(params));
    return { status: 'success' };
  } catch (err) {
    return { status: 'error', error: err };
  }
};

```

で、lib 使うっていいながら、lib 使ってないじゃんって突っ込んだら

> My apologies, you are correct. Here is a sample code using @aws-sdk/lib-dynamodb:

と謝罪してくれて出てきたのがこちら

```typescript
const { DynamoDB } = require('@aws-sdk/lib-dynamodb');

const dynamo = new DynamoDB({});

exports.handler = async (event) => {
  const user = event.Records[0].dynamodb.NewImage;
  user.timestampserver = new Date().toISOString();

  const params = {
    TableName: 'user',
    Key: {
      id: user.id
    },
    UpdateExpression: 'SET timestampserver = :timestampserver',
    ExpressionAttributeValues: {
      ':timestampserver': { S: user.timestampserver }
    }
  };

  try {
    await dynamo.updateItem(params).promise();
    return { status: 'success' };
  } catch (err) {
    return { status: 'error', error: err };
  }
};
```

結構いい感じになってきた。
ただ、"ExpressionAttributeNames" が無いので突っ込んだら理解して修正してくれた

```typescript
const { DynamoDB } = require('@aws-sdk/lib-dynamodb');

const dynamo = new DynamoDB({});

exports.handler = async (event) => {
  const user = event.Records[0].dynamodb.NewImage;
  user.timestampserver = new Date().toISOString();

  const params = {
    TableName: 'user',
    Key: {
      id: user.id
    },
    UpdateExpression: 'SET #timestampserver = :timestampserver',
    ExpressionAttributeNames: {
      '#timestampserver': 'timestampserver'
    },
    ExpressionAttributeValues: {
      ':timestampserver': { S: user.timestampserver }
    }
  };

  try {
    await dynamo.updateItem(params).promise();
    return { status: 'success' };
  } catch (err) {
    return { status: 'error', error: err };
  }
};
```

で、require やめてほしかったので、

> can you write like es2020 module?

って言ってみたら、簡単に書き換えてくれた :laughing: 

```typescript: 
import { DynamoDB } from '@aws-sdk/lib-dynamodb';

const dynamo = new DynamoDB({});

export const handler = async (event) => {
  const user = event.Records[0].dynamodb.NewImage;
  user.timestampserver = new Date().toISOString();

  const params = {
    TableName: 'user',
    Key: {
      id: user.id
    },
    UpdateExpression: 'SET #timestampserver = :timestampserver',
    ExpressionAttributeNames: {
      '#timestampserver': 'timestampserver'
    },
    ExpressionAttributeValues: {
      ':timestampserver': { S: user.timestampserver }
    }
  };

  try {
    await dynamo.updateItem(params).promise();
    return { status: 'success' };
  } catch (err) {
    return { status: 'error', error: err };
  }
};
```

timestampserver の値は、event に含まれる "ApproximateCreationDateTime" を利用してほしかったのでそれを教えたところ
ああ、それね、みたいにして直してくれたのが以下

```typescript
import { DynamoDB } from '@aws-sdk/lib-dynamodb';

const dynamo = new DynamoDB({});

export const handler = async (event) => {
  const user = event.Records[0].dynamodb.NewImage;
  user.timestampserver = event.Records[0].dynamodb.ApproximateCreationDateTime;

  const params = {
    TableName: 'user',
    Key: {
      id: user.id
    },
    UpdateExpression: 'SET #timestampserver = :timestampserver',
    ExpressionAttributeNames: {
      '#timestampserver': 'timestampserver'
    },
    ExpressionAttributeValues: {
      ':timestampserver': { S: user.timestampserver }
    }
  };

  try {
    await dynamo.updateItem(params).promise();
    return { status: 'success' };
  } catch (err) {
    return { status: 'error', error: err };
  }
};
```

ついでに、client config に "convertClassInstanceToMap" とか使ってって言ったらでてきたのが

```typescript
import { DynamoDB, UpdateCommand } from '@aws-sdk/lib-dynamodb';
import { convertClassInstanceToMap } from '@aws-sdk/util-dynamodb';

class User {
  constructor(id, timestampserver) {
    this.id = id;
    this.timestampserver = timestampserver;
  }
}

export const handler = async (event) => {
  const user = event.Records[0].dynamodb.NewImage;
  user.timestampserver = event.Records[0].dynamodb.ApproximateCreationDateTime;

  const userInstance = new User(user.id, user.timestampserver);
  const marshallOptions = convertClassInstanceToMap(userInstance);

  const dynamo = new DynamoDB.DocumentClient({
    convertEmptyValues: true,
    ...marshallOptions
  });

  const params = {
    TableName: 'user',
    Key: {
      id: user.id
    },
    UpdateExpression: 'SET #timestampserver = :timestampserver',
    ExpressionAttributeNames: {
      '#timestampserver': 'timestampserver'
    },
    ExpressionAttributeValues: {
      ':timestampserver': userInstance.timestampserver
    }
  };

  try {
    await dynamo.update(params).promise();
    return { status: 'success' };
  } catch (err) {
    return { status: 'error', error: err };
  }
};
```

最後に event property を聞いてみたら、こんな感じで教えてくれた。
sample data とか作るのにも適してそうね・・ほんと凄い
```typescript
{
  "Records": [
    {
      "eventID": "1",
      "eventName": "INSERT",
      "eventVersion": "1.0",
      "eventSource": "aws:dynamodb",
      "awsRegion": "us-west-2",
      "dynamodb": {
        "ApproximateCreationDateTime": 1593801900,
        "Keys": {
          "Id": {
            "N": "101"
          }
        },
        "NewImage": {
          "Message": {
            "S": "New item!"
          },
          "Id": {
            "N": "101"
          }
        },
        "StreamViewType": "NEW_AND_OLD_IMAGES",
        "SequenceNumber": "111",
        "SizeBytes": 26
      },
      "eventSourceARN": "arn:aws:dynamodb:us-west-2:account-id:table/ExampleTable/stream/2015-06-27T00:48:05.899"
    }
  ]
}

```

# あとがき
なるほど、話題の AI は流石ですね。

初学者にこそめっちゃ便利ですね。
TypeScript/JavaScript すら知らなかった三か月前の自分にとってはこれだけで以下の学びがあっただろうな、と
- require to import の書き換え
- V2 vs V3 の比較
- event のプロパティの把握
    - NewImage, ApproximateCreationDateTime なんかの使い方とか
- オブジェクトなどの結合方法
- Put/PutItem/Update などの使い方
- LambdaHandler の例

利用制限がある以上、毎日一時間とか、調べたいことを ChatGPT と共に学んでみるのが良さそうですね。