אימות משתנה קלט

במדריך הזה מוסבר איך לאמת משתנה קלט.

כשמגדירים משתנה קלט, מומלץ לוודא שהמשתמש מזין ערך מתאים. לדוגמה, אם מבקשים מהמשתמש להזין ספרה, אימות ההזנה של 1 במקום a מאמת שהשלב פועל ללא שגיאה.

יש שתי דרכים לאמת משתנה קלט:

  • אימות בצד הלקוח: באימות בצד הלקוח, המערכת מאמתת את הקלט של המשתמש ישירות במכשיר שלו. המשתמש מקבל משוב מיידי ויכול לתקן שגיאות בהזנה שלו במהלך הגדרת השלב.
  • אימות בצד השרת: אימות בצד השרת מאפשר להריץ לוגיקה בשרת במהלך האימות. זה שימושי כשצריך לחפש מידע שלא קיים בצד הלקוח, כמו נתונים במערכות או במסדי נתונים אחרים.

אימות בצד הלקוח

יש שתי דרכים להטמיע אימות בצד הלקוח:

  • כדי לבצע אימות בסיסי, כמו אימות של ווידג'ט שמכיל פחות ממספר מסוים של תווים או שמכיל את הסמל @, מפעילים את המחלקה Validation של שירות הכרטיסים של תוסף Google Workspace.
  • כדי לבצע אימות חזק, כמו השוואה בין ערכי ווידג'טים, אפשר להוסיף אימות באמצעות Common Expression Language ‏ (CEL) לווידג'טים הנתמכים הבאים של כרטיסים באמצעות CardService.

הפעלת המחלקה Validation

בדוגמה הבאה מוודאים שווידג'ט TextInput מכיל 10 תווים או פחות:

Apps Script

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

כדי להשתמש באפשרויות אימות נוספות, צריך להשתמש באימות CEL.

אימות CEL

אימות באמצעות Common Expression Language (CEL) מאפשר לבצע בדיקות מיידיות של קלט בלי ההשהיה שמאפיינת אימות בצד השרת. זאת באמצעות העברת בדיקות של ערכי קלט שלא תלויות באחזור נתונים משירותים אחרים לצד הלקוח.

אפשר גם להשתמש ב-CEL כדי ליצור התנהגויות של כרטיסים, כמו הצגה או הסתרה של ווידג'ט בהתאם לתוצאה של האימות. התנהגות כזו שימושית להצגה או להסתרה של הודעת שגיאה שעוזרת למשתמשים לתקן את הקלט שלהם.

כדי ליצור אימות CEL מלא, צריך להשתמש ברכיבים הבאים:

  • ExpressionData בכרטיס: מכיל את לוגיקת האימות שצוינה ואת לוגיקת ההפעלה של הווידג'ט כשמתקיים אחד מהתנאים המוגדרים.

    • Id: מזהה ייחודי של ExpressionData בתוך Card הנוכחי.
    • Expression: מחרוזת CEL שמגדירה את לוגיקת האימות (למשל, "value1 == value2").
    • Conditions: רשימה של תנאים שמכילה מבחר של תוצאות אימות מוגדרות מראש (SUCCESS או FAILURE). התנאים קשורים ל-EventAction בצד הווידג'ט דרך Triggers עם actionRuleId משותף.
    • ברמת הכרטיס EventAction: מפעיל אימותים של CEL בכרטיס ומשייך את השדה ExpressionData לווידג'טים של תוצאות באמצעות טריגרים של אחרי אירוע.
      • actionRuleId: מזהה ייחודי של EventAction.
      • ExpressionDataAction: הערך צריך להיות START_EXPRESSION_EVALUATION כדי לציין שהפעולה הזו מתחילה את ההערכה של CEL.
      • Trigger: מחבר את Conditions לצד הווידג'ט EventActions על סמך actionRuleId.
  • EventAction ברמת הווידג'ט: קובעת את ההתנהגות של ווידג'ט התוצאות כשמתקיים תנאי ההצלחה או הכשל. לדוגמה, ווידג'ט של תוצאה יכול להיות TextParagraph שמכיל הודעת שגיאה שמוצגת רק אם האימות נכשל.

    • actionRuleId: זהה ל-actionRuleId בצד הכרטיס Trigger.
    • CommonWidgetAction: מגדיר פעולות שלא כוללות הערכות, כמו עדכון של חשיפת הווידג'ט.
      • UpdateVisibilityAction: פעולה שמעדכנת את מצב החשיפה של הווידג'ט (VISIBLE או HIDDEN).

בדוגמה הבאה אפשר לראות איך מטמיעים אימות של CEL כדי לבדוק אם שני קלטים של טקסט שווים. אם הם לא שווים, מוצגת הודעת שגיאה.

  • כשהתנאי failCondition מתקיים (הערכים של inputs לא שווים), הווידג'ט של הודעת השגיאה מוגדר ל-VISIBLE ומופיע.
    איור 1: כשמתקיים התנאי failCondition (הערכים של קלט 1 וקלט 2 לא שווים), הווידג'ט של הודעת השגיאה מוגדר ל-VISIBLE ומופיע.
  • כשמתקיים התנאי successCondition (הערכים של inputs זהים), הווידג'ט של הודעת השגיאה מוגדר כ-HIDDEN (מוסתר) ולא מופיע.
    איור 2: כשמתקיים התנאי successCondition (הערכים של הקלט שווים), הווידג'ט של הודעת השגיאה מוגדר ל-HIDDEN ולא מוצג.

דוגמה לקוד האפליקציה ולקובץ המניפסט בפורמט 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();
}

קובץ מניפסט 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"
          }
        }
      ]
    }
  }
}

ווידג'טים ופעולות נתמכים של אימות CEL

ווידג'טים של כרטיסים שתומכים באימות CEL

הווידג'טים הבאים תומכים באימות CEL:

  • TextInput
  • SelectionInput
  • DateTimePicker

פעולות אימות נתמכות של CEL

  • פעולות אריתמטיות
    • +: מוסיפה שני מספרים מסוג int64,‏ uint64 או double.
    • -: מחסירה שני מספרים מסוג int64,‏ uint64 או double.
    • *: מכפילה שני מספרים מסוג int64,‏ uint64 או double.
    • /: מחלקת שני מספרים מסוג int64,‏ uint64 או double (חלוקת מספרים שלמים).
    • %: מחשבת את המודולו של שני מספרים מסוג int64 או uint64.
    • -: הופך את הערך של מספר int64 או uint64.
  • פעולות לוגיות:
    • &&: מבצעת פעולת AND לוגית על שני ערכים בוליאניים.
    • ||: מבצעת פעולת OR לוגית על שני ערכים בוליאניים.
    • !: מבצעת פעולה לוגית NOT על ערך בוליאני.
  • פעולות השוואה:
    • ==: בודקת אם שני ערכים שווים. יש תמיכה במספרים וברשימות.
    • !=: בודקת אם שני ערכים לא שווים. יש תמיכה במספרים וברשימות.
    • <: בודקת אם המספר הראשון int64, uint64 או double קטן מהמספר השני.
    • <=: בודקת אם המספר הראשון int64, uint64 או double קטן מהמספר השני או שווה לו.
    • >: בודקת אם המספר הראשון int64, uint64 או double גדול מהמספר השני.
    • >=: הפונקציה בודקת אם המספר הראשון, int64, uint64 או double, גדול מהמספר השני או שווה לו.
  • הצגת רשימה של פעולות:
    • in: בודקת אם ערך מסוים מופיע ברשימה. יש תמיכה במספרים, במחרוזות וברשימות מקוננות.
    • size: מחזירה את מספר הפריטים ברשימה. יש תמיכה במספרים וברשימות מקוננות.

תרחישי אימות של CEL שלא נתמכים

  • גודלי ארגומנטים שגויים לפעולות בינאריות: פעולות בינאריות (לדוגמה, add_int64, equals) דורשות בדיוק שני ארגומנטים. אם תספקו מספר אחר של ארגומנטים, תופיע שגיאה.
  • גודלי ארגומנטים שגויים לפעולות אונריות: פעולות אונריות (לדוגמה, negate_int64) דורשות ארגומנט אחד בדיוק. אם תספקו מספר אחר של ארגומנטים, תופיע שגיאה.
  • סוגים שלא נתמכים בפעולות מספריות: פעולות מספריות בינאריות ואונאריות מקבלות רק ארגומנטים מספריים. אם תספקו סוגים אחרים (לדוגמה, בוליאני), תופיע שגיאה.

אימות בצד השרת

בעזרת אימות בצד השרת, אתם יכולים להריץ לוגיקה בצד השרת על ידי ציון onSaveFunction() בקוד של השלב. כשהמשתמש עובר מכרטיס ההגדרות של השלב, הפונקציה onSaveFunction() מופעלת ומאפשרת לכם לאמת את הקלט של המשתמש.

אם הקלט של המשתמש תקין, מחזירים saveWorkflowAction.

אם הקלט של המשתמש לא תקין, מחזירים כרטיס הגדרה שמציג הודעת שגיאה למשתמש עם הסבר איך לפתור את השגיאה.

האימות בצד השרת הוא אסינכרוני, ולכן יכול להיות שהמשתמש לא ידע על שגיאת הקלט עד שהוא יפרסם את התהליך.

כל ערך מאומת של id בקובץ המניפסט צריך להיות זהה לערך של name בווידג'ט של כרטיס בקוד.

בדוגמה הבאה מוודאים שקלט הטקסט של המשתמש כולל את התו '@':

קובץ מניפסט

קטע מקובץ המניפסט שמציין פונקציה בשם onSaveFunction():

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

קוד אפליקציה

הקוד של השלב כולל פונקציה בשם onSave(). הפונקציה בודקת אם מחרוזת שהוזנה על ידי המשתמש כוללת את התו @. אם כן, היא שומרת את שלב התהליך. אם לא, היא מחזירה כרטיס הגדרה עם הודעת שגיאה שמסבירה איך לתקן את השגיאה.

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