

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-js"></a>

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 devolver los resultados desde una tabla
+ Leer registros desde una o varias tablas en una única consulta
+ Escribir los registros de las transacciones en una o más tablas de all-or-nothing alguna manera
+ Ejecutar transacciones cuando se cumplan algunas condiciones

## Permisos
<a name="permissions-js"></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 AWS AppSync resolutores de los campos se invocan en función de una fuente de datos. Los orígenes de datos configurados para recuperar información de DynamoDB solo tienen especificada una tabla para 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 el código del solucionador, que se describe a continuación.

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

En aras de la simplicidad, vamos a utilizar el mismo origen de datos para todos los solucionadores que se utilizan en este tutorial. 

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`. Puede utilizar los siguientes comandos de la CLI para crear las tablas. Asegúrese de reemplazar `region` por la región.

**Con la CLI**

```
aws dynamodb create-table --table-name savingAccounts \
  --attribute-definitions AttributeName=accountNumber,AttributeType=S \
  --key-schema AttributeName=accountNumber,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region

aws dynamodb create-table --table-name checkingAccounts \
  --attribute-definitions AttributeName=accountNumber,AttributeType=S \
  --key-schema AttributeName=accountNumber,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region

aws dynamodb create-table --table-name transactionHistory \
  --attribute-definitions AttributeName=transactionId,AttributeType=S \
  --key-schema AttributeName=transactionId,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region
```

En la AWS AppSync consola, en **Fuentes de datos**, cree una nueva fuente de datos de DynamoDB y asígnele un nombre. **TransactTutorial** Seleccione **savingAccounts** en la tabla (aunque la tabla específica no importa cuando se utilizan transacciones). Elija crear un nuevo rol y el origen de datos. Puede revisar la configuración del origen de datos para ver el nombre del rol generado. En la consola de IAM, puede añadir una política en línea que permita que el origen de datos interactúe con todas las tablas.

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-js"></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
}
```

### TransactWriteItems - Rellene las cuentas
<a name="transactwriteitems-populate-accounts-js"></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`. Elija el origen de datos `TransactTutorial` y, a continuación, seleccione **Crear**.

Ahora utilice el siguiente código:

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { savingAccounts, checkingAccounts } = ctx.args

	const savings = savingAccounts.map(({ accountNumber, ...rest }) => {
		return {
			table: 'savingAccounts',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ accountNumber }),
			attributeValues: util.dynamodb.toMapValues(rest),
		}
	})

	const checkings = checkingAccounts.map(({ accountNumber, ...rest }) => {
		return {
			table: 'checkingAccounts',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ accountNumber }),
			attributeValues: util.dynamodb.toMapValues(rest),
		}
	})
	return {
		version: '2018-05-29',
		operation: 'TransactWriteItems',
		transactItems: [...savings, ...checkings],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}
	const { savingAccounts: sInput, checkingAccounts: cInput } = ctx.args
	const keys = ctx.result.keys
	const savingAccounts = sInput.map((_, i) => keys[i])
	const sLength = sInput.length
	const checkingAccounts = cInput.map((_, i) => keys[sLength + i])
	return { savingAccounts, checkingAccounts }
}
```

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 tres cuentas de ahorro y tres 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-js"></a>

Asocie un solucionador a la mutación `transferMoney` con el siguiente código. Para cada transferencia, necesitamos un modificador de éxito tanto en la cuenta corriente como en la de ahorros, y debemos hacer un seguimiento de la transferencia en las transacciones.

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const transactions = ctx.args.transactions

	const savings = []
	const checkings = []
	const history = []
	transactions.forEach((t) => {
		const { savingAccountNumber, checkingAccountNumber, amount } = t
		savings.push({
			table: 'savingAccounts',
			operation: 'UpdateItem',
			key: util.dynamodb.toMapValues({ accountNumber: savingAccountNumber }),
			update: {
				expression: 'SET balance = balance - :amount',
				expressionValues: util.dynamodb.toMapValues({ ':amount': amount }),
			},
		})
		checkings.push({
			table: 'checkingAccounts',
			operation: 'UpdateItem',
			key: util.dynamodb.toMapValues({ accountNumber: checkingAccountNumber }),
			update: {
				expression: 'SET balance = balance + :amount',
				expressionValues: util.dynamodb.toMapValues({ ':amount': amount }),
			},
		})
		history.push({
			table: 'transactionHistory',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ transactionId: util.autoId() }),
			attributeValues: util.dynamodb.toMapValues({
				from: savingAccountNumber,
				to: checkingAccountNumber,
				amount,
			}),
		})
	})

	return {
		version: '2018-05-29',
		operation: 'TransactWriteItems',
		transactItems: [...savings, ...checkings, ...history],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}
	const tInput = ctx.args.transactions
	const tLength = tInput.length
	const keys = ctx.result.keys
	const savingAccounts = tInput.map((_, i) => keys[tLength * 0 + i])
	const checkingAccounts = tInput.map((_, i) => keys[tLength * 1 + i])
	const transactionHistory = tInput.map((_, i) => keys[tLength * 2 + i])
	return { savingAccounts, checkingAccounts, transactionHistory }
}
```

Ahora, navega a la sección **Consultas** de la AWS AppSync consola y ejecuta 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 tres 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 - Recupera cuentas
<a name="transactgetitems-retrieve-accounts-js"></a>

Con el fin de recuperar los detalles de las cuentas de ahorro y corriente en una sola solicitud transaccional, vamos a asociar un solucionador a la operación `Query.getAccounts` de GraphQL en nuestro esquema. Seleccione **Asociar** y elija el origen de datos `TransactTutorial` creado al inicio del tutorial. Utilice el siguiente código: 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { savingAccountNumbers, checkingAccountNumbers } = ctx.args

	const savings = savingAccountNumbers.map((accountNumber) => {
		return { table: 'savingAccounts', key: util.dynamodb.toMapValues({ accountNumber }) }
	})
	const checkings = checkingAccountNumbers.map((accountNumber) => {
		return { table: 'checkingAccounts', key: util.dynamodb.toMapValues({ accountNumber }) }
	})
	return {
		version: '2018-05-29',
		operation: 'TransactGetItems',
		transactItems: [...savings, ...checkings],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}

	const { savingAccountNumbers: sInput, checkingAccountNumbers: cInput } = ctx.args
	const items = ctx.result.items
	const savingAccounts = sInput.map((_, i) => items[i])
	const sLength = sInput.length
	const checkingAccounts = cInput.map((_, i) => items[sLength + i])
	return { savingAccounts, checkingAccounts }
}
```

Guarde el solucionador y vaya a las secciones **Queries (Consultas)** de la consola de AWS AppSync . Para recuperar las cuentas de ahorro y corriente, 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