

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Executando transações do DynamoDB no AWS AppSync
<a name="tutorial-dynamodb-transact"></a>

**nota**  
Agora, oferecemos suporte principalmente ao runtime do APPSYNC\$1JS e sua documentação. Considere usar o runtime do APPSYNC\$1JS e seus guias disponíveis [aqui](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html).

AWS AppSync suporta o uso de operações de transação do Amazon DynamoDB em uma ou mais tabelas em uma única região. As operações compatíveis são `TransactGetItems` e `TransactWriteItems`. Ao usar esses recursos no AWS AppSync, você pode realizar tarefas como:
+ Enviar uma lista de chaves em uma única consulta e retornar os resultados de uma tabela
+ Ler os registros de uma ou mais tabelas em uma única consulta
+ Grave registros na transação em uma ou mais tabelas de uma all-or-nothing forma
+ Executar transações quando algumas condições são atendidas

## Permissões
<a name="permissions"></a>

Como outros resolvedores, você precisa criar uma fonte de dados AWS AppSync e criar uma função ou usar uma existente. Como operações de transação exigem diferentes permissões em tabelas do DynamoDB, é necessário conceder as permissões de função configuradas para ações de leitura e gravação:

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/TABLENAME",
                "arn:aws:dynamodb:us-east-1:111122223333:table/TABLENAME/*"
            ]
        }
    ]
}
```

------

 **Observação**: as funções estão vinculadas às fontes de dados em AWS AppSync, e os resolvedores nos campos são invocados em relação a uma fonte de dados. As fontes de dados configuradas para buscar no DynamoDB têm apenas uma tabela especificada, para manter a configuração simples. Portanto, ao executar uma operação de transação para várias tabelas em um único resolvedor, que é uma tarefa mais avançada, é necessário conceder acesso à função na fonte de dados para qualquer tabela com a qual o resolvedor interage. Isso é feito no campo **Recurso** na política do IAM acima. A configuração das chamadas de transação nas tabelas é feita no modelo de resolvedor, descrita abaixo.

## Fonte de dados
<a name="data-source"></a>

Para simplificar, usaremos a mesma fonte de dados para todos os resolvedores usados neste tutorial. Na guia **Fontes de dados**, crie uma nova fonte de dados do DynamoDB e nomeie-a. **TransactTutorial** O nome da tabela pode ser qualquer coisa, pois os nomes de tabelas são especificados como parte do modelo de mapeamento da solicitação para operações de transação. Chamaremos a tabela de `empty`.

Teremos duas tabelas chamadas **savingAccounts** e **checkingAccounts**, ambas com `accountNumber` como chave de partição, e uma tabela **transactionHistory** com `transactionId` como chave de partição.

Para esse tutorial, qualquer função com a seguinte política em linha funcionará. Substitua `region` e `accountId` por sua região e seu ID da conta:

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/savingAccounts",
                "arn:aws:dynamodb:us-east-1:111122223333:table/savingAccounts/*",
                "arn:aws:dynamodb:us-east-1:111122223333:table/checkingAccounts",
                "arn:aws:dynamodb:us-east-1:111122223333:table/checkingAccounts/*",
                "arn:aws:dynamodb:us-east-1:111122223333:table/transactionHistory",
                "arn:aws:dynamodb:us-east-1:111122223333:table/transactionHistory/*"
            ]
        }
    ]
}
```

------

## Transações
<a name="transactions"></a>

Neste exemplo, o contexto é uma transação bancária clássica, onde usaremos `TransactWriteItems` para:
+ Transferir dinheiro de contas poupanças para contas correntes
+ Gerar novos registros para cada transação

E, então, vamos usar `TransactGetItems` para recuperar detalhes de contas poupanças e contas correntes.

**Atenção**  
`TransactWriteItems` não é compatível quando usado com detecção e resolução de conflitos. Essas configurações devem ser desativadas para evitar possíveis erros.

Nós definimos nosso esquema GraphQL da seguinte forma:

```
type SavingAccount {
    accountNumber: String!
    username: String
    balance: Float
}

type CheckingAccount {
    accountNumber: String!
    username: String
    balance: Float
}

type TransactionHistory {
    transactionId: ID!
    from: String
    to: String
    amount: Float
}

type TransactionResult {
    savingAccounts: [SavingAccount]
    checkingAccounts: [CheckingAccount]
    transactionHistory: [TransactionHistory]
}

input SavingAccountInput {
    accountNumber: String!
    username: String
    balance: Float
}

input CheckingAccountInput {
    accountNumber: String!
    username: String
    balance: Float
}

input TransactionInput {
    savingAccountNumber: String!
    checkingAccountNumber: String!
    amount: Float!
}

type Query {
    getAccounts(savingAccountNumbers: [String], checkingAccountNumbers: [String]): TransactionResult
}

type Mutation {
    populateAccounts(savingAccounts: [SavingAccountInput], checkingAccounts: [CheckingAccountInput]): TransactionResult
    transferMoney(transactions: [TransactionInput]): TransactionResult
}

schema {
    query: Query
    mutation: Mutation
}
```

### TransactWriteItems - Preencher contas
<a name="transactwriteitems-populate-accounts"></a>

Para transferir dinheiro entre contas, precisamos preencher a tabela com os detalhes. Usaremos a operação do GraphQL `Mutation.populateAccounts` para fazer isso.

Na seção Esquema, clique em **Anexar** ao lado da operação `Mutation.populateAccounts`. Vá para VTL Unit Resolvers e escolha a mesma fonte de dados do `TransactTutorial`.

Agora, use o seguinte modelo de mapeamento da solicitação:

 **Modelo de mapeamento da solicitação** 

```
#set($savingAccountTransactPutItems = [])
#set($index = 0)
#foreach($savingAccount in ${ctx.args.savingAccounts})
    #set($keyMap = {})
    $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($savingAccount.accountNumber)))
    #set($attributeValues = {})
    $util.qr($attributeValues.put("username", $util.dynamodb.toString($savingAccount.username)))
    $util.qr($attributeValues.put("balance", $util.dynamodb.toNumber($savingAccount.balance)))
    #set($index = $index + 1)
    #set($savingAccountTransactPutItem = {"table": "savingAccounts",
        "operation": "PutItem",
        "key": $keyMap,
        "attributeValues": $attributeValues})
    $util.qr($savingAccountTransactPutItems.add($savingAccountTransactPutItem))
#end

#set($checkingAccountTransactPutItems = [])
#set($index = 0)
#foreach($checkingAccount in ${ctx.args.checkingAccounts})
    #set($keyMap = {})
    $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($checkingAccount.accountNumber)))
    #set($attributeValues = {})
    $util.qr($attributeValues.put("username", $util.dynamodb.toString($checkingAccount.username)))
    $util.qr($attributeValues.put("balance", $util.dynamodb.toNumber($checkingAccount.balance)))
    #set($index = $index + 1)
    #set($checkingAccountTransactPutItem = {"table": "checkingAccounts",
        "operation": "PutItem",
        "key": $keyMap,
        "attributeValues": $attributeValues})
    $util.qr($checkingAccountTransactPutItems.add($checkingAccountTransactPutItem))
#end

#set($transactItems = [])
$util.qr($transactItems.addAll($savingAccountTransactPutItems))
$util.qr($transactItems.addAll($checkingAccountTransactPutItems))

{
    "version" : "2018-05-29",
    "operation" : "TransactWriteItems",
    "transactItems" : $util.toJson($transactItems)
}
```

E o seguinte modelo de mapeamento de resposta:

 **Modelo de mapeamento da resposta** 

```
#if ($ctx.error)
    $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons)
#end

#set($savingAccounts = [])
#foreach($index in [0..2])
    $util.qr($savingAccounts.add(${ctx.result.keys[$index]}))
#end

#set($checkingAccounts = [])
#foreach($index in [3..5])
    $util.qr($checkingAccounts.add(${ctx.result.keys[$index]}))
#end

#set($transactionResult = {})
$util.qr($transactionResult.put('savingAccounts', $savingAccounts))
$util.qr($transactionResult.put('checkingAccounts', $checkingAccounts))

$util.toJson($transactionResult)
```

Salve o resolvedor e navegue até a seção **Consultas** do AWS AppSync console para preencher as contas.

Execute a seguinte mutação:

```
mutation populateAccounts {
  populateAccounts (
    savingAccounts: [
      {accountNumber: "1", username: "Tom", balance: 100},
      {accountNumber: "2", username: "Amy", balance: 90},
      {accountNumber: "3", username: "Lily", balance: 80},
    ]
    checkingAccounts: [
      {accountNumber: "1", username: "Tom", balance: 70},
      {accountNumber: "2", username: "Amy", balance: 60},
      {accountNumber: "3", username: "Lily", balance: 50},
    ]) {
    savingAccounts {
      accountNumber
    }
    checkingAccounts {
      accountNumber
    }
  }
}
```

Nós preenchemos três contas poupanças e três contas correntes em uma mutação.

Use o console do DynamoDB para confirmar se os dados aparecem nas tabelas **savingAccounts** e **checkingAccounts**.

### TransactWriteItems - Transferir dinheiro
<a name="transactwriteitems-transfer-money"></a>

Anexe um resolvedor à mutação `transferMoney` com o seguinte **Modelo de mapeamento da solicitação**. Observe se os valores de `amounts`, `savingAccountNumbers` e `checkingAccountNumbers` são os mesmos.

```
#set($amounts = [])
#foreach($transaction in ${ctx.args.transactions})
    #set($attributeValueMap = {})
    $util.qr($attributeValueMap.put(":amount", $util.dynamodb.toNumber($transaction.amount)))
    $util.qr($amounts.add($attributeValueMap))
#end

#set($savingAccountTransactUpdateItems = [])
#set($index = 0)
#foreach($transaction in ${ctx.args.transactions})
    #set($keyMap = {})
    $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($transaction.savingAccountNumber)))
    #set($update = {})
    $util.qr($update.put("expression", "SET balance = balance - :amount"))
    $util.qr($update.put("expressionValues", $amounts[$index]))
    #set($index = $index + 1)
    #set($savingAccountTransactUpdateItem = {"table": "savingAccounts",
        "operation": "UpdateItem",
        "key": $keyMap,
        "update": $update})
    $util.qr($savingAccountTransactUpdateItems.add($savingAccountTransactUpdateItem))
#end

#set($checkingAccountTransactUpdateItems = [])
#set($index = 0)
#foreach($transaction in ${ctx.args.transactions})
    #set($keyMap = {})
    $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($transaction.checkingAccountNumber)))
    #set($update = {})
    $util.qr($update.put("expression", "SET balance = balance + :amount"))
    $util.qr($update.put("expressionValues", $amounts[$index]))
    #set($index = $index + 1)
    #set($checkingAccountTransactUpdateItem = {"table": "checkingAccounts",
        "operation": "UpdateItem",
        "key": $keyMap,
        "update": $update})
    $util.qr($checkingAccountTransactUpdateItems.add($checkingAccountTransactUpdateItem))
#end

#set($transactionHistoryTransactPutItems = [])
#foreach($transaction in ${ctx.args.transactions})
    #set($keyMap = {})
    $util.qr($keyMap.put("transactionId", $util.dynamodb.toString(${utils.autoId()})))
    #set($attributeValues = {})
    $util.qr($attributeValues.put("from", $util.dynamodb.toString($transaction.savingAccountNumber)))
    $util.qr($attributeValues.put("to", $util.dynamodb.toString($transaction.checkingAccountNumber)))
    $util.qr($attributeValues.put("amount", $util.dynamodb.toNumber($transaction.amount)))
    #set($transactionHistoryTransactPutItem = {"table": "transactionHistory",
        "operation": "PutItem",
        "key": $keyMap,
        "attributeValues": $attributeValues})
    $util.qr($transactionHistoryTransactPutItems.add($transactionHistoryTransactPutItem))
#end

#set($transactItems = [])
$util.qr($transactItems.addAll($savingAccountTransactUpdateItems))
$util.qr($transactItems.addAll($checkingAccountTransactUpdateItems))
$util.qr($transactItems.addAll($transactionHistoryTransactPutItems))

{
    "version" : "2018-05-29",
    "operation" : "TransactWriteItems",
    "transactItems" : $util.toJson($transactItems)
}
```

Teremos três transações bancárias em uma única operação `TransactWriteItems`. Use o seguinte **Modelo de mapeamento de resposta**:

```
#if ($ctx.error)
    $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons)
#end

#set($savingAccounts = [])
#foreach($index in [0..2])
    $util.qr($savingAccounts.add(${ctx.result.keys[$index]}))
#end

#set($checkingAccounts = [])
#foreach($index in [3..5])
    $util.qr($checkingAccounts.add(${ctx.result.keys[$index]}))
#end

#set($transactionHistory = [])
#foreach($index in [6..8])
    $util.qr($transactionHistory.add(${ctx.result.keys[$index]}))
#end

#set($transactionResult = {})
$util.qr($transactionResult.put('savingAccounts', $savingAccounts))
$util.qr($transactionResult.put('checkingAccounts', $checkingAccounts))
$util.qr($transactionResult.put('transactionHistory', $transactionHistory))

$util.toJson($transactionResult)
```

Agora navegue até a seção **Consultas** do console AWS AppSync e execute a mutação **transferMoney** da seguinte forma:

```
mutation write {
  transferMoney(
    transactions: [
      {savingAccountNumber: "1", checkingAccountNumber: "1", amount: 7.5},
      {savingAccountNumber: "2", checkingAccountNumber: "2", amount: 6.0},
      {savingAccountNumber: "3", checkingAccountNumber: "3", amount: 3.3}
    ]) {
    savingAccounts {
      accountNumber
    }
    checkingAccounts {
      accountNumber
    }
    transactionHistory {
      transactionId
    }
  }
}
```

Nós enviamos duas transações bancárias em uma mutação. Use o console do DynamoDB para validar se os dados aparecem nas tabelas **savingAccounts**, **checkingAccounts** e **transactionHistory**.

### TransactGetItems - Recuperar contas
<a name="transactgetitems-retrieve-accounts"></a>

Para recuperar os detalhes das contas poupanças e contas-correntes em uma única solicitação transacional, anexaremos um resolvedor à operação do GraphQL `Query.getAccounts` no nosso esquema. Selecione **Anexar**, vá para VTL Unit Resolvers e, na próxima tela, escolha a mesma fonte de dados do `TransactTutorial` criada no início do tutorial. Configure os modelos da seguinte maneira:

 **Modelo de mapeamento da solicitação** 

```
#set($savingAccountsTransactGets = [])
#foreach($savingAccountNumber in ${ctx.args.savingAccountNumbers})
    #set($savingAccountKey = {})
    $util.qr($savingAccountKey.put("accountNumber", $util.dynamodb.toString($savingAccountNumber)))
    #set($savingAccountTransactGet = {"table": "savingAccounts", "key": $savingAccountKey})
    $util.qr($savingAccountsTransactGets.add($savingAccountTransactGet))
#end

#set($checkingAccountsTransactGets = [])
#foreach($checkingAccountNumber in ${ctx.args.checkingAccountNumbers})
    #set($checkingAccountKey = {})
    $util.qr($checkingAccountKey.put("accountNumber", $util.dynamodb.toString($checkingAccountNumber)))
    #set($checkingAccountTransactGet = {"table": "checkingAccounts", "key": $checkingAccountKey})
    $util.qr($checkingAccountsTransactGets.add($checkingAccountTransactGet))
#end

#set($transactItems = [])
$util.qr($transactItems.addAll($savingAccountsTransactGets))
$util.qr($transactItems.addAll($checkingAccountsTransactGets))

{
    "version" : "2018-05-29",
    "operation" : "TransactGetItems",
    "transactItems" : $util.toJson($transactItems)
}
```

 **Modelo de mapeamento da resposta** 

```
#if ($ctx.error)
    $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons)
#end

#set($savingAccounts = [])
#foreach($index in [0..2])
    $util.qr($savingAccounts.add(${ctx.result.items[$index]}))
#end

#set($checkingAccounts = [])
#foreach($index in [3..4])
    $util.qr($checkingAccounts.add($ctx.result.items[$index]))
#end

#set($transactionResult = {})
$util.qr($transactionResult.put('savingAccounts', $savingAccounts))
$util.qr($transactionResult.put('checkingAccounts', $checkingAccounts))

$util.toJson($transactionResult)
```

Salve o resolvedor e navegue até **as seções Consultas** do AWS AppSync console. Para recuperar as contas poupanças e contas correntes, execute a seguinte consulta:

```
query getAccounts {
  getAccounts(
    savingAccountNumbers: ["1", "2", "3"],
    checkingAccountNumbers: ["1", "2"]
  ) {
    savingAccounts {
      accountNumber
      username
      balance
    }
    checkingAccounts {
      accountNumber
      username
      balance
    }
  }
}
```

Demonstramos com sucesso o uso das transações do DynamoDB usando. AWS AppSync