Expressões de condição - AWS AppSync GraphQL

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Expressões de condição

Quando objetos sofrem mutação no DynamoDB usando as operações PutItem, UpdateItem e DeleteItem do DynamoDB, opcionalmente, é possível especificar uma expressão de condição que controla se a solicitação deve ser bem-sucedida ou não, com base no estado do objeto que já está no DynamoDB antes que a operação seja realizada.

O resolvedor do DynamoDB do AWS AppSync permite que uma expressão de condição seja especificada nos documentos de mapeamento da solicitação PutItem, UpdateItem e DeleteItem, além de uma estratégia para seguir se a condição falhar e o objeto não for atualizado.

Exemplo 1

O documento de mapeamento PutItem a seguir não tem uma expressão de condição. Como resultado, ele coloca um item no DynamoDB mesmo se um item com a mesma chave já existir, sobrescrevendo o item existente.

{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } } }

Exemplo 2

O seguinte documento de mapeamento PutItem possui uma expressão de condição que permite que a operação seja bem-sucedida somente se um item com a mesma chave não existir no DynamoDB.

{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } }, "condition" : { "expression" : "attribute_not_exists(id)" } }

Por padrão, se a verificação da condição falhar, o resolvedor DynamoDB do AWS AppSync retornará um erro para a mutação. No entanto, o resolvedor do DynamoDB do AWS AppSync oferece alguns atributos adicionais para ajudar os desenvolvedores a lidar com alguns problemas em parâmetros comuns:

  • Se o resolvedor do DynamoDB do AWS AppSync puder determinar que o valor atual no DynamoDB corresponde ao resultado desejado, ele tratará a operação como bem-sucedida.

  • Em vez de retornar um erro, você pode configurar o resolvedor a fim de invocar uma função do Lambda personalizada para decidir como o resolvedor do DynamoDB do AWS AppSync deve lidar com a falha.

Isso é descrito com mais detalhes na seção Tratamento de uma falha de verificação da condição.

Para obter mais informações sobre as expressões de condições do DynamoDB, consulte a Documentação ConditionExpressions do DynamoDB.

Especificação de uma condição

Os documentos de mapeamento da solicitação PutItem, UpdateItem e DeleteItem permitem que uma seção condition opcional seja especificada. Se omitida, nenhuma verificação de condição é feita. Se especificada, a condição deve ser verdadeira para que a operação seja bem-sucedida.

A seção condition tem a seguinte estrutura:

"condition" : { "expression" : "someExpression" "expressionNames" : { "#foo" : "foo" }, "expressionValues" : { ":bar" : ... typed value }, "equalsIgnore" : [ "version" ], "consistentRead" : true, "conditionalCheckFailedHandler" : { "strategy" : "Custom", "lambdaArn" : "arn:..." } }

Os campos a seguir especificam a condição:

expression

A própria expressão de atualização. Para obter mais informações sobre como gravar expressões de condição, consulte a Documentação ConditionExpressions do DynamoDB. Esse campo deve ser especificado.

expressionNames

As substituições para espaços reservados de nome do atributo da expressão, na forma de pares de chave/valor. A chave corresponde a um espaço reservado de nome usado na expressão e o valor deve ser uma string que corresponde ao nome do atributo do item no DynamoDB. Esse campo é opcional e deve ser preenchido apenas por substituições para espaços reservados de nome do atributo da expressão usados na expressão.

expressionValues

As substituições para espaços reservados de valor do atributo da expressão, na forma de pares chave-valor. A chave corresponde a um espaço reservado de valor usado na expressão e o valor deve ser um valor digitado. Para obter mais informações sobre como especificar um "valor digitado", consulte Sistema de tipo (Mapeamento de solicitação). Isso deve ser especificado. Esse campo é opcional e deve ser preenchido apenas por substituições para espaços reservados de valor do atributo da expressão usados na expressão.

Os campos restantes informam ao resolvedor do DynamoDB do AWS AppSync como lidar com uma falha de verificação da condição:

equalsIgnore

Quando uma verificação de condição falha usando a operação PutItem, o resolvedor do DynamoDB do AWS AppSync compara o item que está atualmente no DynamoDB em relação ao item que tentou gravar. Se forem os mesmos, ele tratará a operação como bem-sucedida. Você pode usar o campo equalsIgnore para especificar uma lista de atributos que o AWS AppSync deve ignorar ao executar essa comparação. Por exemplo, se a única diferença era um atributo version, ele trata a operação como bem-sucedida. Esse campo é opcional.

consistentRead

Quando uma verificação de condição falhar, o AWS AppSync receberá o valor atual do item do DynamoDB usando uma leitura altamente consistente. Use esse campo para informar ao resolvedor do DynamoDB do AWS AppSync para, em vez disso, usar uma leitura final consistente. Esse campo é opcional e usa como padrão true.

conditionalCheckFailedHandler

Essa seção permite especificar como o resolvedor do DynamoDB do AWS AppSync trata uma falha de verificação da condição depois de comparar o valor atual no DynamoDB em relação ao resultado esperado. Esta seção é opcional. Se omitida, o padrão será uma estratégia de Reject.

strategy

A estratégia que o resolvedor do DynamoDB do AWS AppSync assume depois de comparar o valor atual no DynamoDB em relação ao resultado esperado. Esse campo é obrigatório e tem os valores possíveis a seguir:

Reject

A mutação falha, e um erro é adicionado à resposta do GraphQL.

Custom

O resolvedor do DynamoDB do AWS AppSync invoca uma função do Lambda personalizada para decidir como lidar com a falha de verificação da condição. Quando a strategy estiver definida como Custom, o campo lambdaArn deve conter o ARN da função do Lambda a ser invocada.

lambdaArn

O ARN da função do Lambda a ser invocada que determina como o resolvedor do DynamoDB do AWS AppSync deve lidar com a falha de verificação da condição. Esse campo deve ser especificado somente quando strategy for definida como Custom. Para obter mais informações sobre como usar esse atributo, consulte Tratamento de uma falha de verificação da condição.

Tratamento de uma falha de verificação da condição

Por padrão, quando uma verificação de condição falha, o resolvedor DynamoDB do AWS AppSync retorna um erro para a mutação e o valor atual do objeto no DynamoDB. No entanto, o resolvedor do DynamoDB do AWS AppSync oferece alguns atributos adicionais para ajudar os desenvolvedores a lidar com alguns problemas em parâmetros comuns:

  • Se o resolvedor do DynamoDB do AWS AppSync puder determinar que o valor atual no DynamoDB corresponde ao resultado desejado, ele tratará a operação como bem-sucedida.

  • Em vez de retornar um erro, você pode configurar o resolvedor a fim de invocar uma função do Lambda personalizada para decidir como o resolvedor do DynamoDB do AWS AppSync deve lidar com a falha.

O fluxograma para esse processo é:

Flowchart showing process for transforming requests with mutation attempts and value checks.

Verificação do resultado desejado

Quando a verificação de condição falha, o resolvedor do DynamoDB do AWS AppSync realiza uma solicitação GetItem do DynamoDB para obter o valor atual do item do DynamoDB. Por padrão, ele usa uma leitura fortemente consistente, mas isso pode ser configurado usando o campo consistentRead no bloco condition e compará-lo com o resultado esperado:

  • Para a operação PutItem, o resolvedor do DynamoDB do AWS AppSync compara o valor atual com aquele que tentou gravar, excluindo todos os atributos listados em equalsIgnore na comparação. Se os itens forem os mesmos, ele tratará a operação como bem-sucedida e retornará o item recuperado do DynamoDB. Caso contrário, ele seguirá a estratégia configurada.

    Por exemplo, se o documento de mapeamento da solicitação PutItem se parecer com o seguinte:

    { "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" ] } }

    E o item que está atualmente no DynamoDB se parecer com o seguinte:

    { "id" : { "S" : "1" }, "name" : { "S" : "Steve" }, "version" : { "N" : 8 } }

    O resolvedor do DynamoDB do AWS AppSync comparará o item que tentou gravar com o valor atual. Note que a única diferença é o campo version, mas como está configurado para ignorar o campo version, ele trata a operação como bem-sucedida e retorna o item recuperado do DynamoDB.

  • Para a operação DeleteItem, o resolvedor do DynamoDB do AWS AppSync verifica se um item foi retornado do DynamoDB. Se nenhum item foi retornado, ele tratará a operação como bem-sucedida. Caso contrário, ele seguirá a estratégia configurada.

  • Para a operação UpdateItem, o resolvedor do DynamoDB do AWS AppSync não possui informações suficientes para determinar se o item que está atualmente no DynamoDB corresponde ao resultado esperado e, portanto, segue a estratégia configurada.

Se o estado atual do objeto no for diferente do resultado esperado, o resolvedor do DynamoDB do AWS AppSync seguirá a estratégia configurada, para rejeitar a mutação ou invocar uma função do Lambda a fim de determinar o que fazer a seguir.

Seguir a estratégia "Rejeitar"

Ao seguir a estratégia Reject, o resolvedor DynamoDB do AWS AppSync retorna um erro para a mutação.

Por exemplo, considere a solicitação de mutação a seguir:

mutation { updatePerson(id: 1, name: "Steve", expectedVersion: 1) { Name theVersion } }

Se o item retornado do DynamoDB for semelhante ao seguinte:

{ "id" : { "S" : "1" }, "name" : { "S" : "Steve" }, "version" : { "N" : 8 } }

E o modelo de mapeamento de resposta é semelhante ao seguinte:

{ "id" : $util.toJson($context.result.id), "Name" : $util.toJson($context.result.name), "theVersion" : $util.toJson($context.result.version) }

A resposta do GraphQL é semelhante à seguinte:

{ "data": null, "errors": [ { "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)" "errorType": "DynamoDB:ConditionalCheckFailedException", ... } ] }

Além disso, se qualquer campo no objeto retornado for preenchido por outros resolvedores e a mutação foi bem-sucedida, eles não serão resolvidos quando o objeto for retornado na seção error.

Seguir a estratégia "Personalizada"

Ao seguir a estratégia Custom, o resolvedor do DynamoDB do AWS AppSync invoca uma função do Lambda para decidir o que fazer a seguir. A Função Lambda escolhe um destas opções a seguir:

  • reject a mutação. Isso orienta o resolvedor do DynamoDB do AWS AppSync a se comportar como se a estratégia configurada fosse Reject, retornando um erro para a mutação e o valor atual do objeto no DynamoDB, conforme descrito na seção anterior.

  • discard a mutação. Isso orienta o resolvedor do DynamoDB do AWS AppSync a ignorar silenciosamente a falha de verificação da condição e retorna o valor no DynamoDB.

  • retry a mutação. Isso orienta o resolvedor do DynamoDB do AWS AppSync a tentar novamente a mutação com um novo documento de mapeamento da solicitação.

A solicitação de invocação do Lambda

O resolvedor do DynamoDB do AWS AppSync invoca a função do Lambda especificada no lambdaArn. Ele usa o mesmo service-role-arn configurado na fonte de dados. A carga da invocação tem a seguinte estrutura:

{ "arguments": { ... }, "requestMapping": {... }, "currentValue": { ... }, "resolver": { ... }, "identity": { ... } }

Os campos são definidos da seguinte forma:

arguments

Os argumentos da mutação do GraphQL. Isso é o mesmo que os argumentos disponíveis para o documento de mapeamento da solicitação em $context.arguments.

requestMapping

O documento de mapeamento da solicitação para essa operação.

currentValue

O valor atual do objeto no DynamoDB.

resolver

Informações sobre o resolvedor do AWS AppSync.

identity

Informações sobre o chamador. Isso é o mesmo que as informações de identidade disponíveis para o documento de mapeamento da solicitação em $context.identity.

Um exemplo completo da carga:

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

A resposta de invocação do Lambda

A função do Lambda pode inspecionar o payload da invocação e aplicar qualquer lógica de negócios para decidir como o resolvedor do DynamoDB do AWS AppSync deve tratar a falha. Existem três opções para tratamento da falha de verificação da condição:

  • reject a mutação. A carga da resposta para essa opção deve ter a seguinte estrutura:

    { "action": "reject" }

    Isso orienta o resolvedor do DynamoDB do AWS AppSync a se comportar como se a estratégia configurada fosse Reject, retornando um erro para a mutação e o valor atual do objeto no DynamoDB, conforme descrito na seção anterior.

  • discard a mutação. A carga da resposta para essa opção deve ter a seguinte estrutura:

    { "action": "discard" }

    Isso orienta o resolvedor do DynamoDB do AWS AppSync a ignorar silenciosamente a falha de verificação da condição e retorna o valor no DynamoDB.

  • retry a mutação. A carga da resposta para essa opção deve ter a seguinte estrutura:

    { "action": "retry", "retryMapping": { ... } }

    Isso orienta o resolvedor do DynamoDB do AWS AppSync a tentar novamente a mutação com um novo documento de mapeamento da solicitação. A estrutura da seção retryMapping depende da operação do DynamoDB e é um subconjunto do documento de mapeamento da solicitação completo para essa operação.

    Em PutItem, a seção retryMapping tem a seguinte estrutura. Para obter uma descrição do campo attributeValues, consulte PutItem.

    { "attributeValues": { ... }, "condition": { "equalsIgnore" = [ ... ], "consistentRead" = true } }

    Em UpdateItem, a seção retryMapping tem a seguinte estrutura. Para obter uma descrição da seção update, consulte UpdateItem.

    { "update" : { "expression" : "someExpression" "expressionNames" : { "#foo" : "foo" }, "expressionValues" : { ":bar" : ... typed value } }, "condition": { "consistentRead" = true } }

    Em DeleteItem, a seção retryMapping tem a seguinte estrutura.

    { "condition": { "consistentRead" = true } }

    Não há como especificar uma operação ou chave diferente na qual trabalhar. O resolvedor do DynamoDB do AWS AppSync somente permite novas tentativas da mesma operação no mesmo objeto. Além disso, a seção condition não permite que um conditionalCheckFailedHandler seja especificado. Se a nova tentativa falhar, o resolvedor do DynamoDB do AWS AppSync seguirá a estratégia Reject.

Veja aqui um exemplo de função do Lambda para lidar com uma solicitação PutItem com falha. A lógica de negócios analisa quem fez a chamada. Se foi feita pelo jeffTheAdmin, ela tentará novamente a solicitação, atualizando version e expectedVersion do item atualmente no DynamoDB. Caso contrário, ela rejeitará a mutação.

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