

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Realizar transacciones de DynamoDB en AWS AppSync
<a name="tutorial-dynamodb-transact"></a>

**nota**  
Ahora admitimos de forma básica el tiempo de ejecución APPSYNC\$1JS y su documentación. Considere la opción de utilizar el tiempo de ejecución APPSYNC\$1JS y sus guías [aquí](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html).

AWS AppSync admite el uso de operaciones de transacciones de Amazon DynamoDB en una o más tablas de una sola región. Las operaciones admitidas son `TransactGetItems` y `TransactWriteItems`. Al utilizar estas funciones en AWS AppSync, puede realizar tareas como:
+ Transferir una lista de claves en una sola consulta y obtener los resultados desde una tabla
+ Leer registros de una o varias tablas en una única consulta
+ Escriba los registros de transacciones en una o más tablas de all-or-nothing alguna manera
+ Ejecutar transacciones cuando se cumplan algunas condiciones

## Permisos
<a name="permissions"></a>

Al igual que otros solucionadores, debe crear una fuente de datos AWS AppSync y crear un rol o usar uno existente. Dado que las operaciones de transacciones requieren diferentes permisos para las tablas de DynamoDB, debe conceder los permisos de rol configurados para las acciones de lectura o escritura:

------
#### [ 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/*"
            ]
        }
    ]
}
```

------

 **Nota**: Los roles están vinculados a las fuentes de datos y los resolutores de los campos se invocan en función de una fuente de datos. AWS AppSync Los orígenes de datos configurados para recuperar información de DynamoDB solo tienen especificada una tabla a fin de que la configuración siga siendo sencilla. Por lo tanto, al realizar una operación de transacciones en varias tablas con un único solucionador, que es una tarea más avanzada, debe conceder al rol de ese origen de datos acceso a cualquier tabla con la que el solucionador vaya a interactuar. Esto se hace en el campo **Resource (Recurso)** de la política de IAM anterior. La configuración de las tablas en las que se realizan llamadas de transacciones se lleva a cabo en la plantilla de solucionador, que se describe a continuación.

## Origen de datos
<a name="data-source"></a>

En aras de la simplicidad, vamos a utilizar el mismo origen de datos para todos los solucionadores que se utilizan en este tutorial. En la pestaña **Fuentes de datos**, cree una nueva fuente de datos de DynamoDB y asígnele un nombre. **TransactTutorial** El nombre de la tabla pueden ser uno cualquiera, ya que los nombre de las tablas se especifican como parte de la plantilla de mapeo de solicitud para las operaciones de transacciones. Llamaremos a la tabla `empty`.

Tendremos dos tablas denominadas **savingAccounts** y **checkingAccounts**, ambas con la clave de partición `accountNumber`, y una tabla **transactionHistory** con la clave de partición `transactionId`.

En este tutorial funcionará cualquier rol con la siguiente política insertada. Sustituya `region` y `accountId` por su región y su identificador de cuenta.

------
#### [ 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/*"
            ]
        }
    ]
}
```

------

## Transacciones
<a name="transactions"></a>

Para este ejemplo, el contexto es una transacción bancaria clásica, en la que usaremos `TransactWriteItems` para:
+ Transferir dinero de cuentas de ahorro a cuentas corrientes
+ Generar nuevos registros de transacciones para cada transacción

Y, a continuación, usaremos `TransactGetItems` para recuperar los detalles de las cuentas de ahorro y las cuentas corrientes.

**aviso**  
`TransactWriteItems` no se admite cuando se utiliza con la detección y resolución de conflictos. Esta configuración debe estar deshabilitada para evitar posibles errores.

Definimos nuestro esquema GraphQL de la siguiente manera:

```
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 - Rellenar cuentas
<a name="transactwriteitems-populate-accounts"></a>

Para transferir dinero entre cuentas, tenemos que rellenar la tabla con los detalles. Para ello, utilizaremos la operación `Mutation.populateAccounts` de GraphQL.

En la sección Esquema, haga clic en **Asociar** junto a la operación `Mutation.populateAccounts`. Vaya a Solucionadores de unidades VTL y, a continuación, elija el mismo origen de datos `TransactTutorial`.

Ahora, use la siguiente plantilla de mapeo de solicitud.

 **Plantilla de mapeo de solicitud** 

```
#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)
}
```

Y la siguiente plantilla de mapeo de respuesta:

 **Plantilla de mapeo de respuesta** 

```
#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)
```

Guarde la resolución y vaya a la sección **Consultas** de la AWS AppSync consola para rellenar las cuentas.

Ejecute la mutación siguiente:

```
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
    }
  }
}
```

Hemos rellenado 3 cuentas de ahorro y 3 cuentas corrientes en una mutación.

Utilice la consola de DynamoDB para validar que los datos se muestren en las tablas **savingAccounts** y **checkingAccounts**.

### TransactWriteItems - Transfiere dinero
<a name="transactwriteitems-transfer-money"></a>

Asocie un solucionador a la mutación `transferMoney` con la siguiente **plantilla de mapeo de solicitud**. Tenga en cuenta que los valores de `amounts`, `savingAccountNumbers` y `checkingAccountNumbers` son los mismos.

```
#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)
}
```

Tendremos 3 transacciones bancarias en una sola operación de `TransactWriteItems`. Use la siguiente **plantilla de mapeo de respuesta**:

```
#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)
```

Ahora, vaya a la sección **Queries (Consultas)** de la consola de AWS AppSync y ejecute la mutación **transferMoney** de la siguiente manera:

```
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
    }
  }
}
```

Hemos enviado 2 transacciones bancarias en una mutación. Utilice la consola de DynamoDB para validar que los datos se muestren en las tablas **savingAccounts**, **checkingAccounts** y **transactionHistory**.

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

Con el fin de recuperar los detalles de las cuentas de ahorro y cuentas corrientes en una sola solicitud transaccional, vamos a asociar un solucionador a la operación `Query.getAccounts` de GraphQL en nuestro esquema. Seleccione **Asociar**, vaya a Solucionadores de unidades VTL y, en la siguiente pantalla, elija el mismo origen de datos de `TransactTutorial` creado al inicio del tutorial. Configure las plantillas de la siguiente manera:

 **Plantilla de mapeo de solicitud** 

```
#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)
}
```

 **Plantilla de mapeo de respuesta** 

```
#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)
```

Guarde la resolución y vaya a las secciones de **consultas** de la AWS AppSync consola. Para recuperar las cuentas de ahorro y las cuentas corrientes, ejecute la siguiente consulta:

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

Hemos demostrado con éxito el uso de transacciones de DynamoDB mediante. AWS AppSync