Validar uma variável de entrada

Neste guia, explicamos como validar uma variável de entrada.

Ao definir uma variável de entrada, valide se o usuário insere um valor adequado. Por exemplo, se você pedir ao usuário para inserir um numeral, verifique se ele inseriu 1 em vez de a para garantir que a etapa seja executada sem erros.

Há duas maneiras de validar uma variável de entrada:

  • Validação do lado do cliente: com a validação do lado do cliente, você verifica a entrada do usuário diretamente no dispositivo dele. O usuário recebe feedback imediato e pode corrigir erros na entrada ao configurar a etapa.
  • Validação do lado do servidor: permite executar a lógica no servidor durante a validação, o que é útil quando você precisa pesquisar informações que o cliente não tem, como dados em outros sistemas ou bancos de dados.

Validação do lado do cliente

Há duas maneiras de implementar a validação do lado do cliente:

  • Para validação básica, como verificar se um widget contém menos de um determinado número de caracteres ou o símbolo @, invoque a classe Validation do serviço de card do complemento do Google Workspace.
  • Para uma validação robusta, como comparar valores de widgets com outros valores, adicione a validação da Common Expression Language (CEL) aos seguintes widgets de card compatíveis usando CardService.

Invocar a classe Validation

O exemplo a seguir valida se um widget TextInput contém 10 ou menos caracteres:

Apps Script

const validation = CardService.newValidation().setCharacterLimit('10').setInputType(
    CardService.InputType.TEXT);

Para outras opções de validação, use a validação do CEL.

Validação de CEL

A validação da Common Expression Language (CEL) oferece verificações instantâneas de entrada sem a latência da validação do lado do servidor. Isso é feito ao descarregar as verificações de valor de entrada que não dependem da pesquisa de dados de outros serviços para o lado do cliente.

Você também pode usar a CEL para criar comportamentos de card, como mostrar ou ocultar um widget dependendo do resultado da validação. Esse tipo de comportamento é útil para mostrar ou ocultar uma mensagem de erro que ajuda os usuários a corrigir as entradas.

A criação de uma validação completa da CEL envolve os seguintes componentes:

  • ExpressionData no card: contém a lógica de validação especificada e a lógica de acionamento de widget quando uma das condições definidas é atendida.

    • Id: um identificador exclusivo do ExpressionData no Card atual.
    • Expression: a string CEL que define a lógica de validação (por exemplo, "value1 == value2").
    • Conditions: uma lista de condições que contém uma seleção de resultados de validação predefinidos (SUCCESS ou FAILURE). As condições são vinculadas ao EventAction do lado do widget por Triggers com um actionRuleId compartilhado.
    • EventAction no nível do card: ativa as validações de CEL no card e associa o campo ExpressionData aos widgets de resultados usando acionadores pós-evento.
      • actionRuleId: ID exclusivo para este EventAction.
      • ExpressionDataAction: defina como START_EXPRESSION_EVALUATION para indicar que essa ação inicia a avaliação de CEL.
      • Trigger: conecta o Conditions ao EventActions do lado do widget com base no actionRuleId.
  • EventAction no nível do widget: controla o comportamento do widget de resultado quando a condição de sucesso ou falha é atendida. Por exemplo, um widget de resultado pode ser um TextParagraph que contém uma mensagem de erro que só fica visível quando a validação falha.

    • actionRuleId: corresponde ao actionRuleId no Trigger do lado do cartão.
    • CommonWidgetAction: define ações que não envolvem avaliações, como atualizar a visibilidade do widget.
      • UpdateVisibilityAction: uma ação que atualiza o estado de visibilidade de um widget (VISIBLE ou HIDDEN).

O exemplo a seguir demonstra como implementar a validação da CEL para verificar se duas entradas de texto são iguais. Uma mensagem de erro será exibida se eles não forem iguais.

  • Quando a failCondition é atendida (as entradas não são iguais), o widget de mensagem de erro é definido como VISIBLE e aparece.
    Figura 1:quando a condição failCondition é atendida (as entradas não são iguais), o widget de mensagem de erro é definido como VISIBLE e aparece.
  • Quando a condição de sucesso é atendida (as entradas são iguais), o widget de mensagem de erro é definido como "HIDDEN" e não aparece.
    Figura 2:quando a successCondition é atendida (as entradas são iguais), o widget de mensagem de erro é definido como HIDDEN e não aparece.

Confira o exemplo de código do aplicativo e o arquivo de manifesto JSON:

Apps Script

function onConfig() {

  // Create a Card
  let card = CardService.newCardBuilder();

  const textInput_1 = CardService.newTextInput()
    .setTitle("Input number 1")
    .setFieldName("value1"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.
  const textInput_2 = CardService.newTextInput()
    .setTitle("Input number 2")
    .setFieldName("value2"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.
  let sections = CardService.newCardSection()
    .setHeader("Two number equals")
    .addWidget(textInput_1)
    .addWidget(textInput_2);

  // CEL Validation

  // Define Conditions
  const condition_success = CardService.newCondition()
    .setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID")
    .setExpressionDataCondition(
      CardService.newExpressionDataCondition()
      .setConditionType(
        CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_SUCCESS));
  const condition_fail = CardService.newCondition()
    .setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID")
    .setExpressionDataCondition(
      CardService.newExpressionDataCondition()
      .setConditionType(
        CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_FAILURE));

  // Define Card-side EventAction
  const expressionDataAction = CardService.newExpressionDataAction()
    .setActionType(
      CardService.ExpressionDataActionType.START_EXPRESSION_EVALUATION);
  // Define Triggers for each Condition respectively
  const trigger_success = CardService.newTrigger()
    .setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID");
  const trigger_failure = CardService.newTrigger()
    .setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID");

  const eventAction = CardService.newEventAction()
    .setActionRuleId("CEL_TEXTINPUT_EVALUATION_RULE_ID")
    .setExpressionDataAction(expressionDataAction)
    .addPostEventTrigger(trigger_success)
    .addPostEventTrigger(trigger_failure);

  // Define ExpressionData for the current Card
  const expressionData = CardService.newExpressionData()
    .setId("expData_id")
    .setExpression("value1 == value2") // CEL expression
    .addCondition(condition_success)
    .addCondition(condition_fail)
    .addEventAction(eventAction);
  card = card.addExpressionData(expressionData);

  // Create Widget-side EventActions and a widget to display error message
  const widgetEventActionFail = CardService.newEventAction()
    .setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID")
    .setCommonWidgetAction(
      CardService.newCommonWidgetAction()
      .setUpdateVisibilityAction(
        CardService.newUpdateVisibilityAction()
        .setVisibility(
          CardService.Visibility.VISIBLE)));
  const widgetEventActionSuccess = CardService.newEventAction()
    .setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID")
    .setCommonWidgetAction(
      CardService.newCommonWidgetAction()
      .setUpdateVisibilityAction(
        CardService.newUpdateVisibilityAction()
        .setVisibility(
          CardService.Visibility.HIDDEN)));
  const errorWidget = CardService.newTextParagraph()
    .setText("The first and second value must match.")
    .setVisibility(CardService.Visibility.HIDDEN) // Initially hidden
    .addEventAction(widgetEventActionFail)
    .addEventAction(widgetEventActionSuccess);
  sections = sections.addWidget(errorWidget);

  card = card.addSection(sections);
  // Build and return the Card
  return card.build();
}

Arquivo de manifesto JSON

{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "addOns": {
    "common": {
      "name": "CEL validation example",
      "logoUrl": "https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png",
      "useLocaleFromApp": true
    },
    "flows": {
      "workflowElements": [
        {
          "id": "actionElement",
          "state": "ACTIVE",
          "name": "CEL Demo",
          "description": "Demonstrates CEL Validation",
          "workflowAction": {
            "inputs": [
              {
                "id": "value1",
                "description": "The first number",
                "cardinality": "SINGLE",
                "dataType": {
                  "basicType": "INTEGER"
                }
              },
              {
                "id": "value2",
                "description": "The second number",
                "cardinality": "SINGLE",
                "dataType": {
                  "basicType": "INTEGER"
                }
              }
            ],
            "onConfigFunction": "onConfig",
            "onExecuteFunction": "onExecute"
          }
        }
      ]
    }
  }
}

Widgets e operações de validação da CEL compatíveis

Widgets de card que oferecem suporte à validação de CEL

Os seguintes widgets são compatíveis com a validação da CEL:

  • TextInput
  • SelectionInput
  • DateTimePicker

Operações de validação de CEL compatíveis

  • Operações aritméticas
    • +: adiciona dois números int64, uint64 ou double.
    • -: subtrai dois números int64, uint64 ou double.
    • *: multiplica dois números int64, uint64 ou double.
    • /: divide dois números int64, uint64 ou double (divisão inteira).
    • %: calcula o módulo de dois números int64 ou uint64.
    • -: nega um número int64 ou uint64.
  • Operações lógicas:
    • &&: executa uma operação lógica AND em dois valores booleanos.
    • ||: executa uma operação lógica OR em dois valores booleanos.
    • !: executa uma operação lógica NOT em um valor booleano.
  • Operações de comparação:
    • ==: verifica se dois valores são iguais. Aceita números e listas.
    • !=: verifica se dois valores são diferentes. Aceita números e listas.
    • <: verifica se o primeiro número int64, uint64 ou double é menor que o segundo.
    • <=: verifica se o primeiro número int64, uint64 ou double é menor ou igual ao segundo.
    • >: verifica se o primeiro número int64, uint64 ou double é maior que o segundo.
    • >=: verifica se o primeiro número int64, uint64 ou double é maior ou igual ao segundo.
  • Listar operações:
    • in: verifica se um valor está presente em uma lista. Aceita números, strings e listas aninhadas.
    • size: retorna o número de itens em uma lista. Aceita números e listas aninhadas.

Cenários de validação CEL sem suporte

  • Tamanhos de argumentos incorretos para operações binárias: as operações binárias (por exemplo, add_int64, igual a) exigem exatamente dois argumentos. Fornecer um número diferente de argumentos vai gerar um erro.
  • Tamanhos de argumentos incorretos para operações unárias: as operações unárias (por exemplo, negate_int64) exigem exatamente um argumento. Fornecer um número diferente de argumentos vai gerar um erro.
  • Tipos não aceitos em operações numéricas: operações numéricas binárias e unárias só aceitam argumentos numéricos. Fornecer outros tipos (por exemplo, booleano) vai gerar um erro.

Validação do servidor

Com a validação do lado do servidor, é possível executar a lógica do lado do servidor especificando o onSaveFunction() no código da etapa. Quando o usuário sai do card de configuração da etapa, o onSaveFunction() é executado e permite verificar a entrada do usuário.

Se a entrada do usuário for válida, retorne saveWorkflowAction.

Se a entrada do usuário for inválida, retorne um card de configuração que mostre uma mensagem de erro explicando como resolver o problema.

Como a validação do lado do servidor é assíncrona, o usuário só descobre o erro de entrada quando publica o fluxo.

Cada id de entrada validada no arquivo de manifesto precisa corresponder a um name de widget de card no código.

O exemplo a seguir valida se uma entrada de texto do usuário inclui o sinal "@":

Arquivo de manifesto

O trecho do arquivo de manifesto especifica um onSaveFunction() chamado "onSave":

JSON

{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "addOns": {
    "common": {
      "name": "Server-side validation example",
      "logoUrl": "https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png",
      "useLocaleFromApp": true
    },
    "flows": {
      "workflowElements": [
        {
          "id": "actionElement",
          "state": "ACTIVE",
          "name": "Calculate",
          "description": "Asks the user for an email address",
          "workflowAction": {
            "inputs": [
              {
                "id": "email",
                "description": "email address",
                "cardinality": "SINGLE",
                "required": true,
                "dataType": {
                  "basicType": "STRING"
                }
              }
            ],
            "onConfigFunction": "onConfigCalculate",
            "onExecuteFunction": "onExecuteCalculate",
            "onSaveFunction": "onSave"
          }
        }
      ]
    }
  }
}

Código do aplicativo

O código da etapa inclui uma função chamada onSave(). Ele valida se uma string inserida pelo usuário inclui @. Se sim, salva a etapa do fluxo. Caso contrário, ele retorna um card de configuração com uma mensagem de erro explicando como corrigir o problema.

Apps Script

/**
 * Validates user input asynchronously when the user
 * navigates away from a step's configuration card.
*/
function onSave(event) {

  // "email" matches the input ID specified in the manifest file.
  var email = event.workflow.actionInvocation.inputs["email"];

  // Validate that the email address contains an "@" sign:
  if(email.includes("@")) {

  // If successfully validated, save and proceed.
    return {
      "hostAppAction" : {
        "workflowAction" : {
          "saveWorkflowAction" : {}
        }
      }
    };

  // If the input is invalid, return a card with an error message
  } else {

var card = {
    "sections": [
      {
        "header": "Collect Email",
        "widgets": [
          {
            "textInput": {
              "name": "email",
              "label": "email address",
              "hostAppDataSource" : {
                "workflowDataSource" : {
                  "includeVariables" : true
                }
              }
            }
          },
          {
            "textParagraph": {
              "text": "<b>Error:</b> Email addresses must include the '@' sign.",
              "maxLines": 1
            }
          }
        ]
      }
    ]
  };
  return pushCard(card);
  }
}