

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Esecuzione di transazioni DynamoDB in AWS AppSync
<a name="tutorial-dynamodb-transact-js"></a>

AWS AppSync supporta l'utilizzo di operazioni di transazione Amazon DynamoDB su una o più tabelle in una singola regione. Le operazioni supportate sono `TransactGetItems` e `TransactWriteItems`. Utilizzando queste funzionalità in AWS AppSync, puoi eseguire attività come:
+ Passare un elenco di chiavi in una singola query e restituire i risultati da una tabella
+ Leggere i record da una o più tabelle in una singola query
+ Scrittura dei record nelle transazioni su una o più tabelle in qualsiasi all-or-nothing modo
+ Esecuzione di transazioni quando vengono soddisfatte alcune condizioni

## Permissions
<a name="permissions-js"></a>

Come altri resolver, è necessario creare un'origine dati in AWS AppSync e creare un ruolo o utilizzarne uno esistente. Poiché le operazioni di transazione richiedono autorizzazioni diverse sulle tabelle DynamoDB, è necessario concedere ai ruoli configurati le autorizzazioni per le azioni di lettura o scrittura:

------
#### [ 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**  
I ruoli sono legati alle fonti di dati in AWS AppSync e i resolver sui campi vengono richiamati su un'origine dati. Le origini dati configurate per il recupero con DynamoDB hanno solo una tabella specificata per semplificare le configurazioni. Pertanto, quando si esegue un'operazione di transazione su più tabelle in un singolo resolver, che è una delle attività più avanzate, è necessario concedere il ruolo sull'accesso all'origine dati a tutte le tabelle con cui interagirà il resolver. Questo potrebbe essere fatto nel campo **Risorsa** nella policy di IAM indicata in precedenza. La configurazione delle chiamate di transazione rispetto alle tabelle viene eseguita nel codice del resolver, che descriveremo di seguito.

## Origine dati
<a name="data-source-js"></a>

Per semplicità, useremo la stessa origine dati per tutti i resolver usati in questo tutorial. 

**Avremo due tabelle chiamate **SavingAccounts** **e** CheckingAccounts, entrambe `accountNumber` con la chiave di partizione e una tabella TransactionHistory con come chiave di partizione.** `transactionId` È possibile utilizzare i comandi CLI riportati di seguito per creare le tabelle. Assicurati di sostituirlo `region` con la tua regione.

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

Nella AWS AppSync console, in **Sorgenti dati**, crea una nuova origine dati DynamoDB e assegnale un nome. **TransactTutorial** Seleziona **SavingAccounts** come tabella (sebbene la tabella specifica non abbia importanza quando si utilizzano le transazioni). Scegli di creare un nuovo ruolo e l'origine dati. Puoi rivedere la configurazione dell'origine dati per vedere il nome del ruolo generato. Nella console IAM, puoi aggiungere una policy in linea che consente all'origine dati di interagire con tutte le tabelle.

Sostituisci `region` e `accountID` con la tua regione e l'ID dell'account:

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

------

## Transazioni
<a name="transactions-js"></a>

Per questo esempio, il contesto è una classica transazione bancaria, in cui useremo `TransactWriteItems` per:
+ Trasferire denaro dai conti di deposito ai conti correnti
+ Generare nuovi record di transazione per ogni transazione

Quindi, useremo `TransactGetItems` per recuperare i dettagli dai conti di deposito ai conti correnti.

**avvertimento**  
`TransactWriteItems`non è supportato se utilizzato per il rilevamento e la risoluzione dei conflitti. Queste impostazioni devono essere disabilitate per evitare possibili errori.

Definiamo il nostro schema GraphQL come segue:

```
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 - Compila gli account
<a name="transactwriteitems-populate-accounts-js"></a>

Al fine di trasferire denaro tra gli account, abbiamo bisogno di popolare la tabella con i dettagli. Per farlo, useremo l'operazione GraphQL `Mutation.populateAccounts`.

Nella sezione Schema, fai clic su **Allega** accanto all'`Mutation.populateAccounts`operazione. Scegli l'origine `TransactTutorial` dati e scegli **Crea**.

Ora usa il seguente codice:

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

Salva il resolver e vai alla sezione **Query** della AWS AppSync console per popolare gli account.

Eseguire la mutazione seguente:

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

Abbiamo popolato tre conti di risparmio e tre conti correnti in un'unica mutazione.

****Utilizza la console DynamoDB per verificare che i dati vengano visualizzati nelle tabelle SavingAccounts e CheckingAccounts.****

### TransactWriteItems - Trasferisci denaro
<a name="transactwriteitems-transfer-money-js"></a>

Collega un resolver alla `transferMoney` mutazione con il seguente codice. Per ogni trasferimento, abbiamo bisogno di un modificatore di successo sia per il conto corrente che per il conto di risparmio e dobbiamo tenere traccia del trasferimento nelle transazioni.

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

Ora, vai alla sezione **Queries** della AWS AppSync console ed esegui la mutazione **TransferMoney** come segue:

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

Abbiamo inviato tre transazioni bancarie in un'unica mutazione. ****Utilizza la console DynamoDB per verificare che i dati vengano visualizzati nelle tabelle **SavingAccounts**, CheckingAccounts e TransactionHistory.****

### TransactGetItems - Recupera gli account
<a name="transactgetitems-retrieve-accounts-js"></a>

Per recuperare i dettagli dai conti di risparmio e correnti in un'unica richiesta transazionale, collegheremo un resolver all'operazione `Query.getAccounts` GraphQL sul nostro schema. Seleziona **Allega**, scegli la stessa fonte di `TransactTutorial` dati creata all'inizio del tutorial. Eseguire il seguente codice: 

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

Salvare il resolver e passare alle sezioni **Query** della console AWS AppSync . Per recuperare i conti di risparmio e i conti correnti, esegui la seguente query:

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

Abbiamo dimostrato con successo l'uso delle transazioni DynamoDB utilizzando. AWS AppSync