Проверить входную переменную

В этом руководстве объясняется, как проверить входную переменную.

При определении входной переменной рекомендуется проверять, вводит ли пользователь соответствующее значение. Например, если вы просите пользователя ввести цифру, проверка того, что он вводит 1 вместо a , гарантирует, что ваш шаг выполняется без ошибок.

Существует два способа проверки входной переменной:

  • Проверка на стороне клиента : При проверке на стороне клиента вы проверяете ввод пользователя непосредственно на его устройстве. Пользователь получает мгновенную обратную связь и может исправить любые ошибки во вводе при настройке шага.
  • Серверная валидация : Серверная валидация позволяет выполнять логику на сервере во время проверки, что полезно, когда необходимо найти информацию, которой нет у клиента, например, данные в других системах или базах данных.

Проверка на стороне клиента

Существует два способа реализации проверки данных на стороне клиента:

  • Для базовой проверки, например, проверки того, содержит ли виджет меньшее количество символов или содержит ли он символ @ , вызовите класс Validation службы Card дополнения 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 в текущей карточке.
    • Expression : строка CEL, определяющая логику проверки (например, "value1 == value2" ).
    • Conditions : Список условий, содержащий набор предопределенных результатов проверки (УСПЕХ или НЕУДАЧА). Условия связаны с EventAction на стороне виджета через Triggers с общим actionRuleId .
    • Событие уровня карточки (Cell-level 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 (входные данные не равны), виджет с сообщением об ошибке становится видимым и отображается.
    Рисунок 1: Когда выполняется условие failCondition (входные данные не равны), виджет сообщения об ошибке становится VISIBLE и отображается.
  • Когда выполняется условие successCondition (входные данные равны), виджет сообщения об ошибке устанавливается в состояние HIDDEN и не отображается.
    Рисунок 2: Когда выполняется условие successCondition (входные данные равны), виджет сообщения об ошибке устанавливается в HIDDEN и не отображается.

Ниже приведён пример кода приложения и 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();
}

файл манифеста 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"
          }
        }
      ]
    }
  }
}

Поддерживаемые виджеты и операции проверки 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() с именем "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"
          }
        }
      ]
    }
  }
}

Код приложения

В коде этого шага есть функция onSave() . Она проверяет, содержит ли введенная пользователем строка символ "@". Если да, то шаг сохраняется. Если нет, то возвращается карточка конфигурации с сообщением об ошибке, объясняющим, как ее исправить.

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