Valider une variable d'entrée

Ce guide explique comment valider une variable d'entrée.

Lorsque vous définissez une variable d'entrée, nous vous recommandons de vérifier que l'utilisateur saisit une valeur appropriée. Par exemple, si vous demandez à l'utilisateur de saisir un chiffre, vérifiez qu'il saisit 1 au lieu de a pour vous assurer que l'étape s'exécute sans erreur.

Il existe deux façons de valider une variable d'entrée :

  • Validation côté client: Avec la validation côté client, vous vérifiez l'entrée de l'utilisateur directement sur son appareil. L'utilisateur reçoit un feedback immédiat et peut corriger les erreurs dans son entrée lors de la configuration de l'étape.
  • Validation côté serveur : la validation côté serveur vous permet d’exécuter une logique sur le serveur lors de la validation, ce qui est utile lorsque vous devez rechercher des informations que le client ne possède pas, comme des données dans d’autres systèmes ou bases de données.

Validation côté client

Il existe deux façons d'implémenter la validation côté client :

  • Pour une validation de base, comme vérifier qu'un widget contient moins d'un certain nombre de caractères ou qu'il contient le @ symbole, appelez la classe Validation du service de carte du module complémentaire Google Workspace.
  • Pour une validation robuste, comme comparer les valeurs de widget avec d'autres valeurs de widget vous pouvez ajouter une validation CEL (Common Expression Language) aux widgets de carte compatibles suivants à l'aide de CardService.

Appeler la classe Validation

L'exemple suivant valide qu'un widget TextInput contient 10 caractères ou moins :

Apps Script

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

Pour d'autres options de validation, utilisez la validation CEL.

Validation CEL

La validation CEL (Common Expression Language) offre des vérifications instantanées des entrées sans la latence de la validation côté serveur en déchargeant les vérifications de valeurs d'entrée qui ne dépendent pas de la recherche de données provenant d'autres services côté client.

Vous pouvez également utiliser CEL pour créer des comportements de carte, comme afficher ou masquer un widget en fonction du résultat de la validation. Ce type de comportement est utile pour afficher ou masquer un message d'erreur qui aide les utilisateurs à corriger leurs entrées.

La création d'une validation CEL complète implique les composants suivants :

  • ExpressionData dans la carte : contient la logique de validation spécifiée et la logique de déclenchement du widget lorsque l'une des conditions définies est remplie.

    • Id: identifiant unique de l'élément ExpressionData dans la carte actuelle.
    • Expression : chaîne CEL qui définit la logique de validation (par exemple, "value1 == value2").
    • Conditions: liste de conditions contenant une sélection de résultats de validation prédéfinis (SUCCESS ou FAILURE). Les conditions sont liées à l'élément EventAction côté widget via Triggers avec un actionRuleId partagé.
    • EventAction au niveau de la carte : active les validations CEL dans la carte et associe le champ ExpressionData aux widgets de résultat via des déclencheurs post-événement.
      • actionRuleId : ID unique de cet élément EventAction.
      • ExpressionDataAction: défini sur START_EXPRESSION_EVALUATION pour indiquer que cette action démarre l'évaluation CEL.
      • Trigger: connecte les Conditions aux éléments EventActions côté widget en fonction de l'élément actionRuleId.
  • EventAction au niveau du widget : contrôle le comportement du widget de résultat lorsque la condition de réussite ou d'échec est remplie. Un widget de résultat, par exemple, peut être un élément TextParagraph qui contient un message d'erreur qui n'est visible que lorsque la validation échoue.

    • actionRuleId : correspond à l'élément actionRuleId dans le Trigger côté carte.
    • CommonWidgetAction: définit les actions qui n'impliquent pas d'évaluations, comme la mise à jour de la visibilité du widget.
      • UpdateVisibilityAction: action qui met à jour l'état de visibilité d'un widget (VISIBLE ou HIDDEN).

L'exemple suivant montre comment implémenter la validation CEL pour vérifier si deux entrées de texte sont égales. Un message d'erreur s'affiche si elles ne sont pas égales.

  • Lorsque la failCondition est remplie (les entrées ne sont pas égales), le widget de message d'erreur est défini sur VISIBLE et s'affiche.
    Figure 1 : lorsque la failCondition est remplie (les entrées ne sont pas égales), le widget de message d'erreur est défini sur VISIBLE et s’affiche.
  • Lorsque la condition successCondition est remplie (les entrées sont égales), le widget de message d'erreur est défini sur HIDDEN et n'apparaît pas.
    Figure 2 : Lorsque la successCondition est remplie (les entrées sont égales), le widget de message d’erreur est défini sur HIDDEN et ne s’affiche pas.

Voici l'exemple de code d'application et le fichier manifeste JSON :

Apps Script

function onConfig() {
  // Create a Card
  let cardBuilder = CardService.newCardBuilder();

  const textInput_1 = CardService.newTextInput()
    .setTitle("Input field 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 field 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("Enter same values for the two input fields")
    .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("<font color=\"#FF0000\"><b>Error:</b> Please enter the same values for both input fields.</font>")
    .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();
}

Fichier manifeste 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": "cel_validation_demo",
          "state": "ACTIVE",
          "name": "CEL Demo",
          "description": "Demonstrates CEL Validation",
          "workflowAction": {
            "inputs": [
              {
                "id": "value1",
                "description": "The first number",
                "cardinality": "SINGLE",
                "dataType": {
                  "basicType": "STRING"
                }
              },
              {
                "id": "value2",
                "description": "The second number",
                "cardinality": "SINGLE",
                "dataType": {
                  "basicType": "STRING"
                }
              }
            ],
            "onConfigFunction": "onConfig",
            "onExecuteFunction": "onExecute"
          }
        }
      ]
    }
  }
}

Widgets et opérations de validation CEL compatibles

Widgets de carte compatibles avec la validation CEL

Les widgets suivants sont compatibles avec la validation CEL :

  • TextInput
  • SelectionInput
  • DateTimePicker

Opérations de validation CEL compatibles

  • Opérations arithmétiques
    • + : ajoute deux nombres int64, uint64 ou double.
    • - : soustrait deux nombres int64, uint64 ou double.
    • * : multiplie deux nombres int64, uint64 ou double.
    • / : divise deux nombres int64, uint64 ou double (division entière).
    • % : calcule le modulo de deux int64 ou uint64 nombres.
    • - : inverse un nombre int64 ou uint64.
  • Opérations logiques :
    • && : effectue une opération logique AND sur deux valeurs booléennes.
    • || : effectue une opération logique OR sur deux valeurs booléennes.
    • ! : effectue une opération logique NOT sur une valeur booléenne.
  • Opérations de comparaison :
    • == : vérifie si deux valeurs sont égales. Compatible avec les nombres et les listes.
    • != : vérifie si deux valeurs ne sont pas égales. Compatible avec les nombres et les listes.
    • < : vérifie si le premier nombre int64, uint64 ou double est inférieur au second.
    • <= : vérifie si le premier nombre int64, uint64 ou double est inférieur ou égal au second.
    • > : vérifie si le premier nombre int64, uint64 ou double est supérieur au second.
    • >= : vérifie si le premier nombre int64, uint64 ou double est supérieur ou égal au second.
  • Opérations de liste :
    • in : vérifie si une valeur est présente dans une liste. Compatible avec les nombres, les chaînes et les listes imbriquées.
    • size : renvoie le nombre d'éléments dans une liste. Compatible avec les nombres et les listes imbriquées.

Scénarios de validation CEL non compatibles

  • Tailles d'arguments incorrectes pour les opérations binaires : les opérations binaires (par exemple, add_int64, equals) nécessitent exactement deux arguments. Si vous fournissez un nombre d'arguments différent, une erreur sera générée.
  • Tailles d'arguments incorrectes pour les opérations unaires : les opérations unaires (par exemple, negate_int64) nécessitent exactement un argument. Si vous fournissez un nombre d'arguments différent, une erreur sera générée.
  • Types non compatibles dans les opérations numériques : les opérations binaires et unaires numériques n'acceptent que les arguments numériques. Si vous fournissez d'autres types (par exemple, booléen), une erreur sera générée.

Validation côté serveur

Avec la validation côté serveur, vous pouvez exécuter une logique côté serveur en spécifiant onSaveFunction() dans le code de votre étape. Lorsque l'utilisateur quitte la carte de configuration de l'étape, onSaveFunction() s'exécute et vous permet de vérifier l'entrée de l'utilisateur.

Si l'entrée de l'utilisateur est valide, renvoyez saveWorkflowAction.

Si l'entrée de l'utilisateur n'est pas valide, renvoyez une carte de configuration qui affiche un message d'erreur expliquant à l'utilisateur comment résoudre l'erreur.

Étant donné que la validation côté serveur est asynchrone, l'utilisateur peut ne pas être informé de l'erreur d'entrée tant qu'il n'a pas publié son flux.

L'id de chaque entrée validée dans le fichier manifeste doit correspondre au name d'un widget de carte dans le code.

L'exemple suivant valide qu'une entrée de texte utilisateur inclut le signe "@" :

Fichier manifeste

L'extrait du fichier manifeste spécifie un onSaveFunction() nommé "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": "server_validation_demo",
          "state": "ACTIVE",
          "name": "Email address validation",
          "description": "Asks the user for an email address",
          "workflowAction": {
            "inputs": [
              {
                "id": "email",
                "description": "email address",
                "cardinality": "SINGLE",
                "required": true,
                "dataType": {
                  "basicType": "STRING"
                }
              }
            ],
            "onConfigFunction": "onConfig",
            "onExecuteFunction": "onExecute",
            "onSaveFunction": "onSave"
          }
        }
      ]
    }
  }
}

Code d'application

Le code de l'étape inclut une fonction appelée onSave(). Il valide qu'une chaîne saisie par l'utilisateur inclut @. Si c'est le cas, il enregistre l'étape. Sinon, il renvoie une carte de configuration avec un message d'erreur expliquant comment corriger l'erreur.

Apps Script

// A helper method to push a card interface
function pushCard(card) {
  const navigation = AddOnsResponseService.newNavigation()
    .pushCard(card);

  const action = AddOnsResponseService.newAction()
    .addNavigation(navigation);

  return AddOnsResponseService.newRenderActionBuilder()
    .setAction(action)
    .build();
}

function onConfig() {
  const emailInput = CardService.newTextInput()
    .setFieldName("email")
    .setTitle("User e-mail")
    .setId("email");

  const saveButton = CardService.newTextButton()
    .setText("Save!")
    .setOnClickAction(
      CardService.newAction()
        .setFunctionName('onSave')
    )

  const sections = CardService.newCardSection()
    .setHeader("Server-side validation")
    .setId("section_1")
    .addWidget(emailInput)
    .addWidget(saveButton);

  let card = CardService.newCardBuilder()
    .addSection(sections)
    .build();

  return pushCard(card);
}

function onExecute(event) {
}

/**
* Validates user input asynchronously when the user
* navigates away from a step's configuration card.
*/
function onSave(event) {
  console.log(JSON.stringify(event, null, 2));

  // "email" matches the input ID specified in the manifest file.
  var email = event.formInputs["email"][0];

  console.log(JSON.stringify(email, null, 2));

  // Validate that the email address contains an "@" sign:
  if (email.includes("@")) {
    // If successfully validated, save and proceed.
    const hostAppAction = AddOnsResponseService.newHostAppAction()
      .setWorkflowAction(
        AddOnsResponseService.newSaveWorkflowAction()
      );

    const textDeletion = AddOnsResponseService.newRemoveWidget()
      .setWidgetId("errorMessage");

    const modifyAction = AddOnsResponseService.newAction()
      .addModifyCard(
        AddOnsResponseService.newModifyCard()
          .setRemoveWidget(textDeletion)
      );

    return AddOnsResponseService.newRenderActionBuilder()
      .setHostAppAction(hostAppAction)
      .setAction(modifyAction)
      .build();

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

    const textParagraph = CardService.newTextParagraph()
      .setId("errorMessage")
      .setMaxLines(1)
      .setText("<font color=\"#FF0000\"><b>Error:</b> Email addresses must include the '@' sign.</font>");

    const emailInput = CardService.newTextInput()
      .setFieldName("email")
      .setTitle("User e-mail")
      .setId("email");

    const saveButton = CardService.newTextButton()
      .setText("Save!")
      .setOnClickAction(
        CardService.newAction().setFunctionName('onSave')
      )

    const sections = CardService.newCardSection()
      .setHeader("Server-side validation")
      .setId("section_1")
      .addWidget(emailInput)
      .addWidget(textParagraph) //Insert the error message
      .addWidget(saveButton);

    let card = CardService.newCardBuilder()
      .addSection(sections)
      .build();

    const navigation = AddOnsResponseService.newNavigation()
      .pushCard(card);

    const action = AddOnsResponseService.newAction()
      .addNavigation(navigation);

    const hostAppAction = AddOnsResponseService.newHostAppAction()
      .setWorkflowAction(
        AddOnsResponseService.newWorkflowValidationErrorAction()
          .setSeverity(AddOnsResponseService.ValidationErrorSeverity.CRITICAL)
      );

    return AddOnsResponseService.newRenderActionBuilder()
      .setHostAppAction(hostAppAction)
      .setAction(action)
      .build();
  }
}