Теперь, когда в таблице есть элемент игры, можно научиться моделировать действие, выполняемое игроком в ходе игры.
Это можно сделать двумя способами. В рамках первого способа нужно получить элемент, используя API GetItem. Затем необходимо обновить элемент игры в приложении в соответствии с выполненным игроком действием. Наконец, следует заменить существующий элемент в DynamoDB с помощью API PutItem. Хотя этот способ работает, в нем требуется множество запросов к таблице DynamoDB и есть риск перезаписи изменений, произошедших в период между получением и перезаписыванием элемента игры.
Второй способ – использовать вызов API UpdateItem в DynamoDB. API UpdateItem позволяет обновить элемент DynamoDB с помощью одного запроса. Вам нужно указать элемент и атрибуты для изменения, а также условия, которые требуется объявить для элемента. Это предпочтительный способ внесения изменений в элемент, поскольку не требуется отправлять множество вызовов к базе данных.
В каталоге scripts/ размещен файл performMove.js со следующим кодом:
const AWS = require('aws-sdk')
const documentClient = new AWS.DynamoDB.DocumentClient()
const performMove = async ({ gameId, user, changedHeap, changedHeapValue }) => {
if (changedHeapValue < 0 ) {
throw new Error('Cannot set heap value below 0')
}
const params = {
TableName: 'turn-based-game',
Key: {
gameId: gameId
},
UpdateExpression: `SET lastMoveBy = :user, ${changedHeap} = :changedHeapValue`,
ConditionExpression: `(user1 = :user OR user2 = :user) AND lastMoveBy <> :user AND ${changedHeap} > :changedHeapValue`,
ExpressionAttributeValues: {
':user': user,
':changedHeapValue': changedHeapValue,
},
ReturnValues: 'ALL_NEW'
}
try {
const resp = await documentClient.update(params).promise()
console.log('Updated game: ', resp.Attributes)
} catch (error) {
console.log('Error updating item: ', error.message)
}
}
performMove({ gameId: '5b5ee7d8', user: 'theseconduser', changedHeap: 'heap1', changedHeapValue: 3 })
Это немного сложный скрипт. Рассмотрим его поэтапно.
Как и в предыдущем скрипте, вы импортируете пакет AWS SDK и создаете инстанс клиента документов DynamoDB.
Затем нужно определить метод performMove. Он схож с внутренним методом, используемым в приложении, когда пользователь запрашивает выполнение действия. Этот скрипт собирает параметры для вызова API UpdateItem. Сначала он изменяет два атрибута игры – последнего пользователя, выполнившего действие, и количество элементов в измененной куче.
Затем параметры API UpdateItem делают некоторые утверждения о текущем состоянии игры. До обновления элемента вычисляется выражение ConditionExpression, чтобы подтвердить, что элемент находится в нужном состоянии. В выражении условия нужно сделать следующие три утверждения.
- Что пользователь, запрашивающий выполнение действия, является одним из двух игроков.
- Что сейчас очередь пользователя, запрашивающего выполнение действия.
- Что текущее значение изменяемой кучи выше значения, которое для нее будет установлено.
Наконец, в параметрах указан атрибут ReturnValue со значением ALL_NEW. Это означает, что DynamoDB возвращает весь элемент после обновления его значений. После этого приложение может оценить игру, чтобы определить, есть ли победитель.
В конце файла приведен пример вызова этого метода в приложении. Выполните скрипт с помощью следующей команды:
В терминале отобразятся такие выходные данные:
Updated game: { heap2: 4,
heap1: 3,
heap3: 5,
gameId: '5b5ee7d8',
user2: 'theseconduser',
user1: 'myfirstuser',
lastMoveBy: 'theseconduser' }
Как видно, запись осуществлена и игра обновлена.
Попробуйте снова запустить скрипт в терминале. В результате вы получите:
На этот раз запросы с условием завершились сбоем. Запрашивающий пользователь, theseconduser, был последним игроком, выполнившим действие. У измененной кучи, heap1, уже было значение 3, а значит пользователь ничего не изменил. Из-за этого запрос был отклонен.