教學課程:交易解析器 - AWS AppSync

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

教學課程:交易解析器

注意

我們現在主要支援 APPSYNC_JS 執行階段及其說明文件。請考慮在此處使用 APPSYNC_JS 執行階段及其指南。

AWS AppSync 支援在單一區域中的一或多個表格中使用 Amazon DynamoDB 交易操作。支援的操作包括 TransactGetItemsTransactWriteItems。您可以在 AWS AppSync 中使用這些功能來執行工作,例如:

  • 在單次查詢中傳遞索引鍵清單,並從資料表傳回結果

  • 在單次查詢中讀取一個或多個資料表的記錄

  • 以某 all-or-nothing 種方式將事務中的記錄寫入一個或多個表

  • 符合某些條件時執行交易

許可

如同其他的解析程式,您需要在 AWS AppSync 中建立資料來源,然後建立角色或使用現有的角色。由於交易操作需要 DynamoDB 表上的不同權限,因此您需要授與已設定的角色權限以進行讀取或寫入動作:

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

注意:在 AWS AppSync 中,角色會連結至資料來源,而會針對資料來源叫用欄位上的解析程式。設定為針對 DynamoDB 擷取的資料來源只會指定一個表格,以保持組態簡單。因此,在單一解析程式中針對多個資料表執行交易操作時 (這是一項更為進階的工作),您必須授予權限給該資料來源上的角色,以存取解析程式將會與其互動的所有資料表。這會在上述 IAM 政策的 Resource (資源) 欄位中完成。針對資料表設定交易呼叫是在解析程式範本中完成,我們將會在下列內容中說明此範本。

資料來源

為簡單起見,我們將針對本教學課程中使用的所有解析程式,使用相同的資料來源。在資料來源索引標籤上,建立新 DynamoDB 資料來源並為其命名。TransactTutorial資料表可以使用任何名稱,因為資料表名稱在交易操作中是指定為請求映射範本的一部分。我們會將資料表命名為 empty

我們將兩個資料表稱為 savingAccountscheckingAccounts,兩者皆具有 accountNumber 做為分割區索引鍵,以及 transactionHistory 資料表,具有 transactionId 做為分割區索引鍵。

在本教學課程中,具備下列內嵌政策的任何角色皆可使用。將 regionaccountId 更換為您的區域和帳戶 ID:

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

交易

在此範例中,內容是傳統的銀行交易,我們將使用 TransactWriteItems 以進行下列操作:

  • 從存款帳戶轉帳至支票帳戶

  • 為每筆交易產生新的交易記錄

然後,我們會使用 TransactGetItems 擷取存款帳戶和支票帳戶中的詳細資料。

定義 GraphQl 結構描述,如下所示:

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 -填入帳戶

為了在帳戶之間轉帳,我們需要在表格中填入詳細資料。我們將使用 GraphQL 操作 Mutation.populateAccounts 來執行此作業。

在「架構」部分中,單擊Mutation.populateAccounts操作旁邊的「附加」。前往 VTL 單位解析器,然後選擇相同TransactTutorial的資料來源。

現在使用下列請求映射範本:

請求映射範本

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

以及下列回應映射範本:

回應映射範本

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

儲存解析程式並導覽至AWS AppSync 主控台的 [查詢] 區段以填入帳戶。

執行下列的變動:

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

我們在一個變動中填入 3 個存款帳戶和 3 個支票帳戶。

使用 DynamoDB 主控台驗證資料是否顯示在「儲存帳戶」和「checkingAccounts」表格中。

TransactWriteItems -轉賬

使用以下請求映射模板將解析器附加到transferMoney突變。請注意,amountssavingAccountNumbers 以及 checkingAccountNumbers 的值是相同的。

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

我們將會在單一 TransactWriteItems 操作中有 3 筆銀行交易。使用下列回應映射範本

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

現在導航到AWS AppSync 控制台的「查詢」部分,然後執行 transferMoney 突變,如下所示:

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

我們已傳送單一變動中的 2 筆銀行交易。使用 DynamoDB 主控台驗證資料是否顯示在「儲存帳戶」、「checking Accounts」和「transactionHistory 記錄」表格中。

TransactGetItems -檢索帳戶

為了擷取單一交易請求中存款帳戶和支票帳戶的詳細資料,我們會將解析程式附加至結構模式中的 Query.getAccounts GraphQL 操作。選取「貼附」,移至「VTL 單位解析器」,然後在下一個畫面中,挑選自學課程開始時建立的相同TransactTutorial資料來源。設定範本,如下所示:

請求映射範本

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

回應映射範本

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

儲存解析程式並瀏覽至主控台的 [查詢] 區段。AWS AppSync 若要擷取存款帳戶和支票帳戶,請執行下列查詢:

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

我們已經成功地示範了使用. AWS AppSync