处理错误

本指南介绍了如何管理在流步骤运行时发生的错误。您可以指定失败的步骤是否需要用户采取行动来解决,或者是否可以重试。

  • 返回可操作的错误:在错误日志中附加一个按钮,该按钮可将用户引导至相应步骤的配置卡片,以便用户修改输入内容来解决错误。如需将错误标记为可操作,请返回 AddOnsResponseService.ErrorActionability.ACTIONABLE。如需将错误标记为“无法采取行动”,请返回 AddOnsResponseService.ErrorActionability.NOT_ACTIONABLE
  • 在出错后重试相应步骤:流程会尝试再次运行相应步骤,最多重试 5 次,然后停止。如需将错误标记为可重试的错误,请返回 AddOnsResponseService.ErrorRetryability.RETRYABLE。如需标记无法重试的错误,请返回 AddOnsResponseService.ErrorRetryability.NOT_RETRYABLE

您还可以使用 chip、超链接和样式化文本创建自定义错误日志,以便为用户提供有关错误的更详细背景信息。

返回可操作的错误

以下示例构建了一个向用户询问负数的步骤。如果用户输入正数,该步骤会返回一个可操作的错误,提示用户更正输入。

以下清单文件定义了该步骤的输入、输出以及要调用的配置和执行函数。

JSON

{
  "timeZone": "America/Toronto",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "addOns": {
    "common": {
      "name": "Retry Errors Example",
      "logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png",
      "useLocaleFromApp": true
    },
    "flows": {
      "workflowElements": [
        {
          "id": "returnElementError",
          "state": "ACTIVE",
          "name": "Return Element Error Action",
          "description": "To notify the user that some error has occurred",
          "workflowAction": {
            "inputs": [
              {
                "id": "value1",
                "description": "The input from the user",
                "cardinality": "SINGLE",
                "dataType": {
                  "basicType": "STRING"
                }
              }
            ],
            "outputs": [
              {
                "id": "output_1",
                "description": "The output",
                "cardinality": "SINGLE",
                "dataType": {
                  "basicType": "STRING"
                }
              }
            ],
            "onConfigFunction": "onConfiguration",
            "onExecuteFunction": "onExecution"
          }
        }
      ]
    }
  }
}

以下代码构建了配置卡并处理了执行逻辑,包括错误处理。

Apps 脚本

/**
 * Returns a configuration card for the step.
 * This card contains a text input field for the user.
 */
function onConfiguration() {
  let section = CardService.newCardSection()
    .addWidget(CardService.newTextInput()
      .setFieldName("value1")
      .setId("value1")
      .setTitle("Please input negative numbers!"));
  const card = CardService.newCardBuilder().addSection(section).build();
  return card;
}

/**
 * Gets an integer value from variable data, handling both string and integer formats.
 * @param {Object} variableData The variable data object from the event.
 * @return {number} The extracted integer value.
 */
function getIntValue(variableData) {
  if (variableData.stringValues) {
    return parseInt(variableData.stringValues[0]);
  }
  return variableData.integerValues[0];
}

/**
 * Executes the step.
 * If the user input is a positive number, it throws an error and returns an
 * actionable error message. Otherwise, it returns the input as an output variable.
 * @param {Object} e The event object from the workflow.
 */
function onExecution(e) {
  try {
    var input_value = getIntValue(e.workflow.actionInvocation.inputs["value1"]);
    if (input_value > 0) {
      throw new Error('Found invalid positive input value!');
    }

    // If execution is successful, return the output variable and a log.
    const styledText_1 = AddOnsResponseService.newStyledText()
      .setText("Execution completed, the number you entered was: ")
      .addStyle(AddOnsResponseService.TextStyle.ITALIC)
      .addStyle(AddOnsResponseService.TextStyle.UNDERLINE);
    const styledText_2 = AddOnsResponseService.newStyledText().setText(input_value)
      .setFontWeight(AddOnsResponseService.FontWeight.BOLD);

    const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()
      .setVariables({
        "output_1": AddOnsResponseService.newVariableData().addStringValue(input_value)
      })
      .setLog(AddOnsResponseService.newWorkflowTextFormat()
        .addTextFormatElement(
          AddOnsResponseService.newTextFormatElement().setStyledText(styledText_1)
        ).addTextFormatElement(
          AddOnsResponseService.newTextFormatElement().setStyledText(styledText_2)
        ));

    let hostAppAction = AddOnsResponseService.newHostAppAction().setWorkflowAction(workflowAction);
    return AddOnsResponseService.newRenderActionBuilder().setHostAppAction(hostAppAction).build();

  } catch (err) {
    // If an error occurs, return an actionable error action.
    Logger.log('An error occurred: ' + err.message);

    const workflowAction = AddOnsResponseService.newReturnElementErrorAction()
      // Sets the user-facing error message.
      .setErrorLog(
        AddOnsResponseService.newWorkflowTextFormat()
        .addTextFormatElement(
          AddOnsResponseService.newTextFormatElement().setText("Failed because of invalid input values!"))
      )
      // Makes the error actionable, allowing the user to correct the input.
      .setErrorActionability(AddOnsResponseService.ErrorActionability.ACTIONABLE)
      // Specifies that the error is not automatically retried.
      .setErrorRetryability(AddOnsResponseService.ErrorRetryability.NOT_RETRYABLE);

    let hostAppAction = AddOnsResponseService.newHostAppAction().setWorkflowAction(workflowAction);
    return AddOnsResponseService.newRenderActionBuilder().setHostAppAction(hostAppAction).build();
  }
}

在出错后重试相应步骤

以下示例构建了一个模拟临时故障的步骤。如果发生错误,该步骤会返回可重试的错误,从而导致流程重新运行该步骤。

清单文件定义了相应步骤。

JSON

{
  "timeZone": "America/Toronto",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "addOns": {
    "common": {
      "name": "Retry Errors Example",
      "logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png",
      "useLocaleFromApp": true
    },
    "flows": {
      "workflowElements": [
        {
          "id": "retryError",
          "state": "ACTIVE",
          "name": "Retry an error",
          "description": "Simulates a temporary failure and retries the step.",
          "workflowAction": {
            "inputs": [
              {
                "id": "value1",
                "description": "Any input value",
                "cardinality": "SINGLE",
                "dataType": {
                  "basicType": "STRING"
                }
              }
            ],
            "outputs": [
              {
                "id": "output_1",
                "description": "The output",
                "cardinality": "SINGLE",
                "dataType": {
                  "basicType": "STRING"
                }
              }
            ],
            "onConfigFunction": "onRetryConfiguration",
            "onExecuteFunction": "onRetryExecution"
          }
        }
      ]
    }
  }
}

以下代码可构建配置卡并处理重试逻辑。

Apps 脚本

/**
 * Returns a configuration card for the step.
 * This card contains a text input field for the user.
 */
function onRetryConfiguration() {
  let section = CardService.newCardSection()
    .addWidget(CardService.newTextInput()
      .setFieldName("value1")
      .setId("value1")
      .setTitle("Enter any value"));
  const card = CardService.newCardBuilder().addSection(section).build();
  return card;
}

/**
 * Executes the step and simulates a transient error.
 * This function fails 80% of the time. When it fails, it returns an
 * error that can be retried.
 * @param {Object} e The event object from the workflow.
 */
function onRetryExecution(e) {
  try {
    // Simulate a transient error that fails 80% of the time.
    if (Math.random() < 0.8) {
      throw new Error('Simulated transient failure!');
    }

    // If execution is successful, return the output variable and a log.
    var input_value = e.workflow.actionInvocation.inputs["value1"].stringValues[0];
    const styledText = AddOnsResponseService.newStyledText()
      .setText(`Execution succeeded for input: ${input_value}`);

    const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()
      .setVariables({
        "output_1": AddOnsResponseService.newVariableData().addStringValue(input_value)
      })
      .setLog(AddOnsResponseService.newWorkflowTextFormat()
        .addTextFormatElement(
          AddOnsResponseService.newTextFormatElement().setStyledText(styledText)
        ));

    let hostAppAction = AddOnsResponseService.newHostAppAction().setWorkflowAction(workflowAction);
    return AddOnsResponseService.newRenderActionBuilder().setHostAppAction(hostAppAction).build();

  } catch (err) {
    // If a transient error occurs, return an error message saying the step tries to run again.
    Logger.log('A error occurred, trying to run the step again: ' + err.message);

    const workflowAction = AddOnsResponseService.newReturnElementErrorAction()
      // Sets the user-facing error message.
      .setErrorLog(
        AddOnsResponseService.newWorkflowTextFormat()
        .addTextFormatElement(
          AddOnsResponseService.newTextFormatElement().setText("A temporary error occurred. The step will be retried."))
      )
      // Makes the error not actionable by the user.
      .setErrorActionability(AddOnsResponseService.ErrorActionability.NOT_ACTIONABLE)
      // Specifies that the error is automatically retried.
      .setErrorRetryability(AddOnsResponseService.ErrorRetryability.RETRYABLE);

    let hostAppAction = AddOnsResponseService.newHostAppAction().setWorkflowAction(workflowAction);
    return AddOnsResponseService.newRenderActionBuilder().setHostAppAction(hostAppAction).build();
  }
}