条件式
PutItem
、UpdateItem
、および DeleteItem
の各 DynamoDB 処理を使用して DynamoDB のオブジェクトをミューテーションする場合、オプションで、処理を実行する前に、DynamoDB にある既存のオブジェクトの状態に基づいてリクエストが成功するかどうかを制御する条件式を指定することができます。
AWS AppSync DynamoDB のリゾルバーを使用して、PutItem
、UpdateItem
、および DeleteItem
の各リクエストマッピングドキュメントに条件式を指定することができます。また、条件チェックでエラーが検出され、オブジェクトが更新されなかった場合に従う処理も指定できます。
例 1
以下の PutItem
マッピングドキュメントには条件式がありません。その結果、同じキーに対応する項目がすでにある場合でも、項目は DynamoDB に挿入され、それにより既存の項目が上書きされます。
{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } } }
例 2
次の PutItem
マッピングドキュメントには条件式があります。この場合、同じキーの項目が DynamoDB に存在しない場合のみ処理が成功します。
{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } }, "condition" : { "expression" : "attribute_not_exists(id)" } }
デフォルトでは、条件チェックでエラーが検出されると、AWS AppSync DynamoDB のリゾルバーは、ミューテーションに関するエラーを返すとともに、GraphQL レスポンスの error
セクションの data
フィールドで DynamoDB のオブジェクトの現在値を返します。ただし、AWS AppSync DynamoDB のリゾルバーにより追加の機能を提供して、一般的なエッジケースを開発者が処理することもできます。
-
AWS AppSync DynamoDB のリゾルバーにより、DynamoDB の現在値が必要な結果と一致すると判断できる場合、処理は成功として扱われます。
-
エラーを返す代わりに、リゾルバーを設定してカスタムの Lambda 関数を呼び出し、AWS AppSync DynamoDB のリゾルバーがエラーを処理する方法を決定することができます。
これらの詳細については、「条件チェックでのエラーを処理する」セクションを参照してください。
DynamoDB の条件式の詳細については、「DynamoDB ConditionExpressions のドキュメント」を参照してください。
条件を指定する
PutItem
、UpdateItem
、および DeleteItem
の各リクエストマッピングドキュメントはすべて、オプションで condition
セクションが指定できます。省略した場合、条件チェックは実行されません。指定した場合、処理が成功するには、条件が true となる必要があります。
condition
セクションは以下の構造を持ちます。
"condition" : { "expression" : "someExpression" "expressionNames" : { "#foo" : "foo" }, "expressionValues" : { ":bar" : ... typed value }, "equalsIgnore" : [ "version" ], "consistentRead" : true, "conditionalCheckFailedHandler" : { "strategy" : "Custom", "lambdaArn" : "arn:..." } }
以下のフィールドに条件を指定します。
-
expression
-
更新式そのものを指定します。条件式の記述方法の詳細については、DynamoDB ConditionExpressions のドキュメントを参照してください。このフィールドの指定は必須です。
-
expressionNames
-
式の属性名のプレースホルダーを示します。キー - 値のペアの形式になります。キーは expression で使用される名前のプレースホルダーに対応し、値は DynamoDB の項目の属性名と一致する文字列でなければなりません。このフィールドはオプションであり、expression で使用される式の属性名のプレースホルダーのみを入力します。
-
expressionValues
-
式の属性値のプレースホルダーを示します。キー - 値のペアの形式になります。キーは expression で使用される値のプレースホルダーに対応し、値は型付き値でなければなりません。「型付き値」を指定する方法の詳細については、「型システム (リクエストマッピング)」を参照してください。この指定は必須です。このフィールドはオプションであり、expression で使用される式の属性値のプレースホルダーのみを入力します。
残りのフィールドは、AWS AppSync DynamoDB のリゾルバーに条件チェックで検出したエラーを処理する方法を指示します。
-
equalsIgnore
-
PutItem
処理の使用時に条件チェックでエラーが検出された場合、AWS AppSync DynamoDB のリゾルバーは DynamoDB に現在ある項目と、書き込もうとした項目とを比較します。これらが同じ場合、処理は成功として扱われます。equalsIgnore
フィールドを使用して、AWS AppSync がこの比較を実行する際に無視する属性のリストを指定することができます。たとえば、唯一の違いがversion
属性である場合、オペレーションは成功として扱われます。このフィールドはオプションです。 -
consistentRead
-
条件チェックでエラーが検出された場合、AWS AppSync は強力な整合性のある読み込みを使用して DynamoDB から項目の現在の値を取得します。このフィールドを使用して、結果整合性のある読み込みを代わりに使用するよう AWS AppSync DynamoDB のリゾルバーに指示することができます。このフィールドはオプションであり、デフォルトは
true
です。 -
conditionalCheckFailedHandler
-
このセクションでは、AWS AppSync DynamoDB のリゾルバーが DynamoDB の現在値と期待値を比較した後、条件チェックでエラーが検出された場合に、これを処理する方法を指定することができます。このセクションはオプションです。省略した場合、デフォルトの処理は
Reject
です。-
strategy
-
AWS AppSync DynamoDB のリゾルバーが DynamoDB の現在値と期待値を比較した後に行う処理です。このフィールドは必須であり、以下を値を設定できます。
-
Reject
-
このミューテーションは失敗し、ミューテーションに関するエラーが返されます。また、DynamoDB のオブジェクトの現在値が GraphQL レスポンスの
error
セクションのdata
フィールドで返されます。 -
Custom
-
AWS AppSync DynamoDB のリゾルバーはカスタムの Lambda 関数を呼び出して、条件チェックで検出したエラーの処理方法を決定します。
strategy
がCustom
に設定されている場合、lambdaArn
フィールドには、呼び出す Lambda 関数の ARN が含まれている必要があります。
-
-
lambdaArn
-
AWS AppSync DynamoDB のリゾルバーが、条件チェックで検出されたエラーを処理する方法を決定するために呼び出す Lambda 関数の ARN です。このフィールドは、
strategy
がCustom
に設定されている場合のみ指定する必要があります。この機能の使用方法の詳細については、「条件チェックでのエラーを処理する」を参照してください。
-
条件チェックでのエラーを処理する
デフォルトでは、条件チェックでエラーが検出されると、AWS の AppSync DynamoDB リゾルバーは、ミューテーションに関するエラーを返すとともに、GraphQL レスポンスの error
セクションの data
フィールドで DynamoDB のオブジェクトの現在値を返します。ただし、AWS AppSync DynamoDB のリゾルバーにより追加の機能を提供して、一般的なエッジケースを開発者が処理することもできます。
-
AWS AppSync DynamoDB のリゾルバーにより、DynamoDB の現在値が必要な結果と一致すると判断できる場合、処理は成功として扱われます。
-
エラーを返す代わりに、リゾルバーを設定してカスタムの Lambda 関数を呼び出し、AWS AppSync DynamoDB のリゾルバーがエラーを処理する方法を決定することができます。
このプロセスのフローチャートは次のとおりです。
必要な結果をチェックする
条件チェックでエラーが検出された場合、AWS の AppSync DynamoDB リゾルバーは GetItem
DynamoDB リクエストを実行し、DynamoDB から項目の現在値を取得します。デフォルトでは、強力な整合性のある読み込みを使用しますが、condition
ブロックの consistentRead
フィールドを使用してこれを設定し、この値を期待した結果と比較することができます。
-
PutItem
処理では、AWS の AppSync DynamoDB リゾルバーは、equalsIgnore
にリストされた以外の属性について、現在値と書き込もうとした値を比較します。項目が同じ場合は、処理は成功として扱われ、DynamoDB から取得された項目が返されます。それ以外の場合は、設定された処理に従います。たとえば、
PutItem
リクエストマッピングドキュメントが以下のようになっているとします。{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } }, "attributeValues" : { "name" : { "S" : "Steve" }, "version" : { "N" : 2 } }, "condition" : { "expression" : "version = :expectedVersion", "expressionValues" : { ":expectedVersion" : { "N" : 1 } }, "equalsIgnore": [ "version" ] } }
現在、DynamoDB にある項目は以下のようになりました。
{ "id" : { "S" : "1" }, "name" : { "S" : "Steve" }, "version" : { "N" : 8 } }
この場合、AWS の AppSync DynamoDB リゾルバーは書き込もうとした項目を現在の値と比較し、
version
フィールドのみ異なっていることを検出します。ただし、version
フィールドは無視するよう設定されているため、処理は成功として扱われ、DynamoDB から取得された項目が返されます。 -
DeleteItem
処理では、 AWS の AppSync DynamoDB リゾルバーは項目が DynamoDB から返されたことを確認します。項目が返されなかった場合、処理は成功として扱われます。それ以外の場合は、設定された処理に従います。 -
UpdateItem
処理では、 AWS の AppSync DynamoDBリゾルバーには、DynamoDB に現在ある項目が期待した結果と一致するかどうかを判定するための十分な情報がないため、設定された処理に従います。
DynamoDB のオブジェクトの現在の状態が期待した結果と異なる場合、AWS の AppSync DynamoDB リゾルバーは設定された処理に従い、ミューテーションを拒否するか、Lambda 関数を呼び出して次の処理を決定します。
「reject」戦略に従う
Reject
の戦略に従う場合、AWS の AppSync DynamoDB リゾルバーは、ミューテーションに関するエラーを返すとともに、GraphQL レスポンスの error
セクションの data
フィールドで DynamoDB のオブジェクトの現在値を返します。DynamoDB から返された項目がレスポンスマッピングテンプレートにより入力され、クライアントが期待する形式に変換されます。また、選択設定によりフィルタ処理も行われます。
たとえば、次のミューテーションリクエストが指定されたとします。
mutation { updatePerson(id: 1, name: "Steve", expectedVersion: 1) { Name theVersion } }
DynamoDB から返された項目が以下のようになっているとします。
{ "id" : { "S" : "1" }, "name" : { "S" : "Steve" }, "version" : { "N" : 8 } }
また、レスポンスマッピングテンプレートは以下のようになっているとします。
{ "id" : $util.toJson($context.result.id), "Name" : $util.toJson($context.result.name), "theVersion" : $util.toJson($context.result.version) }
GraphQL レスポンスは以下のようになります。
{ "data": null, "errors": [ { "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)" "errorType": "DynamoDB:ConditionalCheckFailedException", "data": { "Name": "Steve", "theVersion": 8 }, ... } ] }
また、返されたオブジェクトのフィールドすべてが他のリゾルバーによって入力され、そのミューテーションが成功した場合、オブジェクトが error
セクションに返されたときに、それらのフィールドは解決されません。
「custom」戦略に従う
Custom
戦略に従う場合、AWS の AppSync DynamoDB リゾルバーは Lambda 関数を呼び出して、以下の処理を決定します。Lambda 関数は以下のオプションのいずれかを選択します。
-
ミューテーション を
reject
する これを指定すると、AWS の AppSync DynamoDB リゾルバーは設定された戦略がReject
されたものとして処理し、前のセクションで説明したように、ミューテーションに関するエラーと DynamoDB のオブジェクトの現在値を返します。 -
ミューテーション を
discard
する これを指定すると、AWS の AppSync DynamoDB リゾルバーは条件チェックで検出されたエラーを通知することなく無視し、DynamoDB の値を返します。 -
ミューテーション を
retry
する これを指定すると、AWS の AppSync DynamoDB リゾルバーは新しいリクエストマッピングドキュメントを使用してミューテーションを再試行します。
Lambda 呼び出しリクエスト
AWS の AppSync DynamoDB リゾルバーは、lambdaArn
で指定された Lambda 関数を呼び出します。また、データソースに設定されたものと同じ service-role-arn
を使用します。呼び出しのペイロードは以下の構造を持ちます。
{ "arguments": { ... }, "requestMapping": {... }, "currentValue": { ... }, "resolver": { ... }, "identity": { ... } }
各フィールドの定義は以下のようになります。
-
arguments
-
GraphQL ミューテーションの引数です。これは、
$context.arguments
のリクエストマッピングドキュメントで使用できる引数と同じです。 -
requestMapping
-
この処理のリクエストマッピングドキュメントです。
-
currentValue
-
DynamoDB のオブジェクトの現在値。
-
resolver
-
AWS AppSync リゾルバーに関する情報。
-
identity
-
呼び出し元に関する情報。これは、
$context.identity
のリクエストマッピングドキュメントで使用できる識別情報と同じです。
完全なペイロードの例を次に示します。
{ "arguments": { "id": "1", "name": "Steve", "expectedVersion": 1 }, "requestMapping": { "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } }, "attributeValues" : { "name" : { "S" : "Steve" }, "version" : { "N" : 2 } }, "condition" : { "expression" : "version = :expectedVersion", "expressionValues" : { ":expectedVersion" : { "N" : 1 } }, "equalsIgnore": [ "version" ] } }, "currentValue": { "id" : { "S" : "1" }, "name" : { "S" : "Steve" }, "version" : { "N" : 8 } }, "resolver": { "tableName": "People", "awsRegion": "us-west-2", "parentType": "Mutation", "field": "updatePerson", "outputType": "Person" }, "identity": { "accountId": "123456789012", "sourceIp": "x.x.x.x", "user": "AIDAAAAAAAAAAAAAAAAAA", "userArn": "arn:aws:iam::123456789012:user/appsync" } }
Lambda 呼び出しレスポンス
Lambda 関数は、呼び出しペイロードを確認し、任意のビジネスロジックを適用して、AWS の AppSync DynamoDB リゾルバーがエラーを処理する方法を決定することができます。条件チェックで検出されたエラーを処理するために、以下の 3 つのオプションが指定できます。
-
ミューテーション を
reject
する このオプションのレスポンスペイロードは次の構造を持ちます。{ "action": "reject" }
これを指定すると、AWS の AppSync DynamoDB リゾルバーは設定された戦略が
Reject
されたものとして処理し、上記のセクションで説明したように、ミューテーションに関するエラーと DynamoDB のオブジェクトの現在値を返します。 -
ミューテーション を
discard
する このオプションのレスポンスペイロードは次の構造を持ちます。{ "action": "discard" }
これを指定すると、AWS の AppSync DynamoDB リゾルバーは条件チェックで検出されたエラーを通知することなく無視し、DynamoDB の値を返します。
-
ミューテーション を
retry
する このオプションのレスポンスペイロードは次の構造を持ちます。{ "action": "retry", "retryMapping": { ... } }
これを指定すると、AWS の AppSync DynamoDB リゾルバーは新しいリクエストマッピングドキュメントを使用してミューテーションを再試行します。
retryMapping
セクションの構造は DynamoDB の処理によって異なり、その処理の完全なリクエストマッピングドキュメントのサブセットとなります。PutItem
の場合、retryMapping
セクションは次の構造を持ちます。attributeValues
フィールドについては、「PutItem」を参照してください。{ "attributeValues": { ... }, "condition": { "equalsIgnore" = [ ... ], "consistentRead" = true } }
UpdateItem
の場合、retryMapping
セクションは次の構造を持ちます。update
セクションについては、「UpdateItem」を参照してください。{ "update" : { "expression" : "someExpression" "expressionNames" : { "#foo" : "foo" }, "expressionValues" : { ":bar" : ... typed value } }, "condition": { "consistentRead" = true } }
DeleteItem
の場合、retryMapping
セクションは次の構造を持ちます。{ "condition": { "consistentRead" = true } }
使用する別の処理やキーを指定する方法はありません。AWS AppSync DynamoDB のリゾルバーは、同じオブジェクトに対する同じ処理の再試行のみが可能です。また、
condition
セクションではconditionalCheckFailedHandler
は指定できません。再試行が失敗した場合、AWS の AppSync DynamoDB リゾルバーはReject
の戦略に従います。
以下は、失敗した PutItem
リクエストを処理する Lambda 関数の例です。ビジネスロジックは呼び出し元を調べます。呼び出し元が jeffTheAdmin
の場合は、リクエストを再試行して、現在 DynamoDB にある項目の version
と expectedVersion
を更新します。それ以外の場合は、ミューテーションを拒否します。
exports.handler = (event, context, callback) => { console.log("Event: "+ JSON.stringify(event)); // Business logic goes here. var response; if ( event.identity.user == "jeffTheAdmin" ) { response = { "action" : "retry", "retryMapping" : { "attributeValues" : event.requestMapping.attributeValues, "condition" : { "expression" : event.requestMapping.condition.expression, "expressionValues" : event.requestMapping.condition.expressionValues } } } response.retryMapping.attributeValues.version = { "N" : event.currentValue.version.N + 1 } response.retryMapping.condition.expressionValues[':expectedVersion'] = event.currentValue.version } else { response = { "action" : "reject" } } console.log("Response: "+ JSON.stringify(response)) callback(null, response) };