Validate an input variable

This guide explains how to validate an input variable.

When defining an input variable, as a best practice, validate that the user enters an appropriate value. For example, if you ask the user to input a numeral, verifying that they enter 1 instead of a verifies that your step runs without error.

There are two ways to validate an input variable:

  • Client-side validation : With client-side validation, you verify the user's input directly on their device. The user receives immediate feedback and can correct any errors in their input while configuring the step.
  • Server-side validation : Server-side validation lets you run logic on the server during validation, which is useful when you need to lookup information that the client doesn't have, like data in other systems or databases.

Client-side validation

There are two ways to implement client-side validation:

  • For basic validation, like verifying a widget contains fewer than a certain number of chracters or contains the @ symbol, invoke the Validation class of the Google Workspace add-on's Card service.
  • For robust validation, like comparing widget values with other widget values, you can add Common Expression Language (CEL) validation to the following supported card widgets using CardService .

Invoke the Validation class

The following example validates that a TextInput widget contains 10 or fewer characters:

Apps Script

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

For additional validation options use CEL validation.

CEL Validation

Common Expression Language (CEL) validation offers instant input checks without the latency of server-side validation by offloading input value checks that are not dependent on the lookup of data from other services to the client side.

You can also use CEL to create card behaviors, like displaying or hiding a widget depending on the result of the validation. This kind of behavior is useful for showing or hiding an error message that helps users correct their inputs.

Building a complete CEL validation involves the following components:

  • ExpressionData in Card: Contains the specified validation logic and widget triggering logic when one of the defined Conditions is met.

    • Id : A unique identifier for the ExpressionData within the current Card.
    • Expression : The CEL string that defines the validation logic (e.g., "value1 == value2" ).
    • Conditions : A list of conditions that contains a selection of predefined validation results (SUCCESS or FAILURE). Conditions are tied to the widget-side EventAction through Triggers with a shared actionRuleId .
    • Card-level EventAction : Activates CEL validations in the Card and associates the ExpressionData field to result widgets through post-event triggers.
      • actionRuleId : Unique ID for this EventAction .
      • ExpressionDataAction : Set to START_EXPRESSION_EVALUATION to indicate this action starts CEL evaluation.
      • Trigger : Connects the Conditions to Widget-side EventActions based on the actionRuleId .
  • Widget-level EventAction : Controls the result widget's behavior when the success or failure condition is met. A result widget, for example, can be a TextParagraph that contains an error message which only becomes visible when the validation fails.

    • actionRuleId : Matches the actionRuleId in the card-side Trigger .
    • CommonWidgetAction : Defines actions that don't involve evaluations, such as updating widget visibility.
      • UpdateVisibilityAction : An action that updates a widget's visibility state (VISIBLE or HIDDEN).

The following example demonstrates how to implement CEL validation to check if two text inputs are equal. An error message is shown if they are not equal.

  • When the failCondition is met (inputs are not equal), the error message widget is set to VISIBLE and appears.
    Figure 1: When the failCondition is met (inputs are not equal), the error message widget is set to VISIBLE and appears.
  • When the successCondition is met (inputs are equal), the error message widget is set to HIDDEN and doesn't appear.
    Figure 2: When the successCondition is met (inputs are equal), the error message widget is set to HIDDEN and doesn't appear.

Here're the example application code and JSON manifest file:

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 manifest file

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

Supported CEL validation widgets and operations

Card widgets that support CEL validation

The following widgets support CEL validation:

  • TextInput
  • SelectionInput
  • DateTimePicker

Supported CEL validation operations

  • Arithmetic operations
    • + : Adds two int64 , uint64 , or double numbers.
    • - : Subtracts two int64 , uint64 , or double numbers.
    • * : Multiplies two int64 , uint64 , or double numbers.
    • / : Divides two int64 , uint64 , or double numbers (integer division).
    • % : Computes the modulo of two int64 or uint64 numbers.
    • - : Negates an int64 or uint64 number.
  • Logical operations:
    • && : Performs a logical AND operation on two boolean values.
    • || : Performs a logical OR operation on two boolean values.
    • ! : Performs a logical NOT operation on a boolean value.
  • Comparison Operations:
    • == : Checks if two values are equal. Supports numbers and lists.
    • != : Checks if two values are not equal. Supports numbers and lists.
    • < : Checks if the first int64 , uint64 , or double number is less than the second.
    • <= : Checks if the first int64 , uint64 , or double number is less than or equal to the second.
    • > : Checks if the first int64 , uint64 , or double number is greater than the second.
    • >= : Checks if the first int64 , uint64 , or double number is greater than or equal to the second.
  • List Operations:
    • in : Checks if a value is present in a list. Supports numbers, strings and nested lists.
    • size : Returns the number of items in a list. Supports numbers and nested lists.

Unsupported CEL validation scenarios

  • Incorrect Argument Sizes for Binary Operations : Binary operations (for example, add_int64 , equals) require exactly two arguments. Providing a different number of arguments will throw an error.
  • Incorrect Argument Sizes for Unary Operations : Unary operations (for example, negate_int64 ) require exactly one argument. Providing a different number of arguments will throw an error.
  • Unsupported Types in Numerical Operations : Numerical binary and unary operations only accept number arguments. Providing other types (for example, boolean) will throw an error.

Server-side validation

With server-side validation, you can run server-side logic by specifying the onSaveFunction() in your step's code. When the user navigates away from the step's configuration card, onSaveFunction() runs and lets you verify the user's input.

If the user's input is valid, return saveWorkflowAction .

If the user's input is invalid, return a configuration card that displays an error message to the user that explains how to resolve the error.

Because server-side validation is asynchronous, the user might not know about the input error until they publish their flow.

Each validated input's id in the manifest file must match a card widget's name in the code.

The following example validates that a user text input includes the "@" sign:

Manifest file

The manifest file excerpt specifies an onSaveFunction() named "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" 
  
 } 
  
 } 
  
 ] 
  
 } 
  
 } 
 } 
 

Application code

The step's code includes a function called onSave() . It validates that a user-inputted string includes @. If it does, it saves the flow step. If it doesn't, it returns a configuration card with an error message explaining how to fix the error.

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" 
 ]; 
  
 // Base card. 
  
 var 
  
 card 
  
 = 
  
 { 
  
 "sections" 
 : 
  
 [ 
  
 { 
  
 "header" 
 : 
  
 "Collect Email" 
 , 
  
 "widgets" 
 : 
  
 [ 
  
 { 
  
 "textInput" 
 : 
  
 { 
  
 "name" 
 : 
  
 "email" 
 , 
  
 "label" 
 : 
  
 "email address" 
 , 
  
 "hostAppDataSource" 
  
 : 
  
 { 
  
 "workflowDataSource" 
  
 : 
  
 { 
  
 "includeVariables" 
  
 : 
  
 true 
  
 } 
  
 } 
  
 } 
  
 } 
  
 ] 
  
 } 
  
 ] 
  
 }; 
  
 // Validate that the email address contains an "@" sign: 
  
 if 
 ( 
 email 
 . 
 includes 
 ( 
 "@" 
 )) 
  
 { 
  
 // If successfully validated, save and proceed. 
  
 // To clear any previous validation errors, push an updated 
  
 // card that doesn't include an error message. 
  
 return 
  
 { 
  
 "action" 
 : 
  
 { 
  
 "navigations" 
 : 
  
 [{ 
  
 "pushCard" 
 : 
  
 card 
  
 }] 
  
 }, 
  
 "hostAppAction" 
  
 : 
  
 { 
  
 "workflowAction" 
  
 : 
  
 { 
  
 "saveWorkflowAction" 
  
 : 
  
 {} 
  
 } 
  
 } 
  
 }; 
  
 // If the input is invalid, return a card with an error message 
  
 // and a validation error action. 
  
 } 
  
 else 
  
 { 
  
 // Add an error message to the card. 
  
 card 
 . 
 sections 
 [ 
 0 
 ]. 
 widgets 
 . 
 push 
 ({ 
  
 "textParagraph" 
 : 
  
 { 
  
 "text" 
 : 
  
 "<b>Error:</b> Email addresses must include the '@' sign." 
 , 
  
 "maxLines" 
 : 
  
 1 
  
 } 
  
 }); 
  
 return 
  
 { 
  
 "action" 
 : 
  
 { 
  
 "navigations" 
 : 
  
 [{ 
  
 "pushCard" 
 : 
  
 card 
  
 }] 
  
 }, 
  
 "hostAppAction" 
 : 
  
 { 
  
 "workflowValidationErrorAction" 
 : 
  
 { 
  
 "severity" 
 : 
  
 "CRITICAL" 
  
 } 
  
 } 
  
 }; 
  
 } 
 } 
 
Create a Mobile Website
View Site in Mobile | Classic
Share by: