使用回调和 Google 表格暂停和恢复工作流


Google 表格是基于云的 电子表格解决方案,支持实时协作并提供相关工具, 直观呈现、处理和传达数据。

本教程演示了如何创建和部署可创建 回调端点(或 webhook),将回调网址保存到 Google 表格, 暂停执行,然后等待 Google 表格进行人工审批 重新启动该工作流。 详细了解如何使用回调

目标

在此教程中,您将学习以下操作:

  1. 在 Google 云端硬盘中创建一个新文件夹。此文件夹用于存储您的 并允许工作流在电子表格中写入数据。
  2. 创建一个 Google 表格电子表格来记录批准情况并启动 对工作流进行回调
  3. 使用 Google Apps 脚本 一个基于云的 JavaScript 平台,让您可以以编程方式创建、读取 和修改 Google Workspace 产品,以触发恢复已暂停的广告 工作流程。
  4. 创建并部署一个工作流 Google Sheets API 连接器 将数据附加到电子表格中。工作流执行、暂停,然后 通过电子表格批准回调后恢复。 详细了解 Workflows 连接器
  5. 测试整个过程,并确认工作流按预期继续运行。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

本教程还使用 Google Workspace。 Google 的免费个人应用所未包含的企业级服务 都是可计费的

准备工作

你可以在 Google Cloud 控制台中运行下列某些命令,也可以通过 在终端或 Cloud Shell 中使用 Google Cloud CLI。

您的组织定义的安全限制条件可能会导致您无法完成以下步骤。如需了解相关问题排查信息,请参阅在受限的 Google Cloud 环境中开发应用

控制台

  1. 在 Google Cloud 控制台的“项目选择器”页面上,选择或创建 Google Cloud 项目

    转到“项目选择器”

  2. 确保您的 Google Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能

  3. 启用 Compute Engine、Google 表格和 Workflows API。

    启用 API

  4. 记下 Compute Engine 默认服务账号 因为您要将其与本教程中测试的工作流相关联 目的。已启用 Compute Engine API 的新项目 创建此服务账号 编辑者角色,并使用以下电子邮件地址格式:

    PROJECT_NUMBER-compute@developer.gserviceaccount.com

    您可以在以下位置找到项目编号: 欢迎 页面

    对于生产环境,我们强烈建议 创建新的服务账号 授予其一个或多个 IAM 角色,这些角色包含 所需的最低权限 并遵循 最小权限

gcloud

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. 确保您的 Google Cloud 项目已启用结算功能。 了解如何检查项目是否已启用结算功能

  3. 启用 Compute Engine、Google 表格和 Workflows API。

    gcloud services enable \
        compute.googleapis.com \
        sheets.googleapis.com \
        workflows.googleapis.com
    
  4. 记下 Compute Engine 默认服务账号 因为您要将其与本教程中测试的工作流相关联 目的。已启用 Compute Engine API 的新项目 创建此服务账号 编辑者角色,并使用以下电子邮件地址格式:

    PROJECT_NUMBER-compute@developer.gserviceaccount.com

    您可以检索项目编号:

    gcloud projects describe PROJECT_ID
    

    对于生产环境,我们强烈建议 创建新的服务账号 授予其一个或多个 IAM 角色,这些角色包含 所需的最低权限 并遵循 最小权限

在 Google 云端硬盘中创建新文件夹

在 Google 云端硬盘中创建一个新文件夹。此文件夹用于存储您的 电子表格。通过为共享文件夹设置权限,您的工作流 允许写入电子表格。

  1. 转到 drive.google.com
  2. 点击新建 > 新建文件夹
  3. 为该文件夹输入一个名称。
  4. 点击创建
  5. 右键点击新文件夹,然后选择共享
  6. 添加 Compute Engine 默认服务的电子邮件地址 。

    这将授予服务账号访问该文件夹的权限。当您将 将服务账号与您的工作流相关联,则该工作流将具有修改 访问文件夹中的任何文件。 详细了解如何共享文件、文件夹和驾车

  7. 选择编辑者角色。

  8. 清除通知对方复选框。

  9. 点击分享

使用 Google 表格创建电子表格

当您通过 Google 表格创建电子表格时,该电子表格会保存在 Google 云端硬盘。默认情况下,该电子表格会保存在以下位置的根文件夹中: 云端硬盘。不提供直接创建电子表格的选项 指定文件夹中的内容。不过, 是备选方法,包括在之后将电子表格移至特定文件夹, 即可创建它,如此示例中所示。如需了解详情,请参阅 使用 Google 云端硬盘文件夹

  1. 访问 sheets.google.com

  2. 点击 New Plus

    此操作会创建并打开您的新电子表格。每个电子表格都有一个 spreadsheetId 值, 包含字母、数字、连字符或下划线。您可以 Google 表格网址中的电子表格 ID:

    https://docs.google.com/spreadsheets/d/spreadsheetId/edit#gid=0

  3. 请记下此 ID,因为您在创建工作流时会用到它。

  4. 添加列标题以匹配以下示例:

    用于记录批准的电子表格示例

    请注意,G 列中的值 Approved? 用于启动 回调

  5. 将电子表格移到您创建的 Google 云端硬盘文件夹中 之前:

    1. 在电子表格中,依次选择文件 > 移动
    2. 导航到您创建的文件夹。
    3. 点击移动

您还可以使用 Google Sheets API 连接器 创建电子表格。请注意,在使用连接器时, 可以从 resp 结果中检索 spreadsheetId。 例如:

- create_spreadsheet:
    call: googleapis.sheets.v4.spreadsheets.create
    args:
      body:
      connector_params:
        scopes: ${driveScope}
    result: resp
- assign_sheet_id:
    assign:
      - sheetId: ${resp.spreadsheetId}

使用 Apps 脚本扩展 Google 表格的功能

利用 Apps 脚本,您可以通过编程方式创建、读取和修改 Google 表格。专为表格操作的大多数脚本 数组,与电子表格中的单元格、行和列进行交互。对于 有关在 Google 表格中使用 Apps 脚本的说明,请参阅 参阅自定义函数快速入门

  1. 如需通过 Google 表格创建 Apps 脚本项目,请执行以下操作:

    1. 打开您的 Google 表格电子表格。
    2. 依次选择扩展程序 > Apps 脚本
    3. 在脚本编辑器中,点击未命名项目
    4. 为项目命名,然后点击重命名

    您的脚本现已绑定到电子表格, 使脚本具有特殊功能,能够更改用户界面或 并在打开电子表格后回复。

    一个脚本项目代表一组 Apps 脚本 文件和资源。脚本项目中的代码文件包含 .gs

  2. 您可以使用 Apps 脚本来编写自定义函数, 可在 Google 表格中使用,就像内置函数一样。自定义函数包括 使用标准 JavaScript 创建。创建一个函数:

    1. 打开您的 Apps 脚本项目。
    2. 点击 Editor
    3. 脚本文件显示为名为 Code.gs 的项目文件。如需修改文件,请按以下步骤操作: 选择它。
    4. 将脚本编辑器中的任何代码替换为以下代码,如下所示: 并将其作为输入传递给工作流 执行:

      function handleEdit(e) {
        var range = e.range.getA1Notation();
        var sheet = e.source;
      
        if (range.length > 1 && range[0] === 'G') {
          if (e.value == "TRUE") {
            Logger.log("Approved: TRUE");
      
            var row = range.slice(1);
            var url = sheet.getRange('E' + row).getCell(1, 1).getValue();
            var approver = sheet.getRange('F' + row).getCell(1, 1).getValue();
      
            callback(url, approver);
          }
          else {
            Logger.log("Approved: FALSE");
          }
        }
      }
      
      function callback(url, approver) {
        const headers = {
          "Authorization": "Bearer " + ScriptApp.getOAuthToken()
        };
      
        var payload = {
          'approver': approver
        };
      
        const params = {
          "method": 'POST',
          "contentType": 'application/json',
          "headers": headers,
          "payload": JSON.stringify(payload)
        };
      
      
        Logger.log("Workflow callback request to " + url);
        var response = UrlFetchApp.fetch(url, params);
        Logger.log(response);
      }
    5. 点击“保存”

  3. Apps 脚本 可安装的触发器 当出现特定条件时,允许脚本项目执行指定的函数 例如当电子表格被打开或编辑时。创建触发器:

    1. 打开您的 Apps 脚本项目。
    2. 点击触发器
    3. 点击添加触发器
    4. 为 YOUR_PROJECT_NAME 添加触发器对话框中,配置 触发器: <ph type="x-smartling-placeholder">
        </ph>
      1. 选择要运行的函数列表中,选择 handleEdit
      2. 选择应运行哪个部署列表中,选择 Head
      3. 选择事件来源列表中,选择来自电子表格
      4. 选择事件类型列表中,选择修改时
      5. 失败通知设置列表中,选择每天通知我
    5. 点击保存
    6. 如果系统提示您选择 Google 账号,请选择 账号,然后点击允许

      这样一来,您的 Apps 脚本项目就能够查看、修改、 创建和删除您的 Google 表格电子表格;并连接到 外部服务

  4. Apps 脚本项目清单文件 是一个 JSON 文件,用于指定需要 Apps 脚本需要成功运行脚本。请注意, 为了保护您的 Apps 脚本,Apps 脚本编辑器默认隐藏清单文件 项目设置。修改清单文件:

    1. 打开您的 Apps 脚本项目。
    2. 点击 Project Settings 图标
    3. 选择显示“appsscript.json”“清单文件”复选框。
    4. 点击 Editor
    5. 清单文件显示为名为 appsscript.json 的项目文件。接收者 选择文件
    6. oauthScopes 字段指定字符串数组。要设置 授权范围 添加一个数组,其中包含您希望支持的范围。例如:

      {
        "timeZone": "America/Toronto",
        "dependencies": {
        },
        "exceptionLogging": "STACKDRIVER",
        "runtimeVersion": "V8",
        "oauthScopes": [
          "https://www.googleapis.com/auth/script.external_request",
          "https://www.googleapis.com/auth/cloud-platform",
          "https://www.googleapis.com/auth/spreadsheets"
        ]
      }

      这会将显式范围设置为:

      • 连接到外部服务
      • 查看、修改、配置和删除您的 Google Cloud 数据,并查看 您 Google 账号的电子邮件地址
      • 查看、修改和删除您使用 Google 表格创建的所有电子表格以及创建这种电子表格
    7. 点击“保存”

部署向电子表格写入数据并使用回调的工作流

部署一个工作流,该工作流会在有回调时执行、暂停,然后恢复 已通过电子表格批准。工作流会写入 Google 表格 使用 Google Sheets API 连接器创建电子表格。

控制台

  1. 在 Google Cloud 控制台中,前往 Workflows 页面:

    进入 Workflows

  2. 点击 创建

  3. 输入新工作流的名称:workflows-awaits-callback-sheets

  4. 区域列表中,选择 us-central1(爱荷华)

  5. 对于服务账号,选择 Compute Engine 默认值 服务账号 (PROJECT_NUMBER-compute@developer.gserviceaccount.com)。

  6. 点击下一步

  7. 在工作流编辑器中,为工作流输入以下定义:

    main:
      steps:
        - init:
            assign:
            # Replace with your sheetId and make sure the service account
            # for the workflow has write permissions to the sheet
            - sheetId: "10hieAH6b-oMeIVT_AerSLNxQck14IGhgi8ign-x2x8g"
        - before_sheets_callback:
            call: sys.log
            args:
              severity: INFO
              data: ${"Execute steps here before waiting for callback from sheets"}
        - wait_for_sheets_callback:
            call: await_callback_sheets
            args:
              sheetId: ${sheetId}
            result: await_callback_result
        - after_sheets_callback:
            call: sys.log
            args:
              severity: INFO
              data: ${"Execute steps here after receiving callback from sheets"}
        - returnResult:
            return: ${await_callback_result}
    
    await_callback_sheets:
        params: [sheetId]
        steps:
            - init:
                assign:
                  - project_id: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
                  - location: ${sys.get_env("GOOGLE_CLOUD_LOCATION")}
                  - workflow_id: ${sys.get_env("GOOGLE_CLOUD_WORKFLOW_ID")}
                  - execution_id: ${sys.get_env("GOOGLE_CLOUD_WORKFLOW_EXECUTION_ID")}
            - create_callback:
                call: events.create_callback_endpoint
                args:
                  http_callback_method: POST
                result: callback_details
            - save_callback_to_sheets:
                call: googleapis.sheets.v4.spreadsheets.values.append
                args:
                    range: ${"Sheet1!A1:G1"}
                    spreadsheetId: ${sheetId}
                    valueInputOption: RAW
                    body:
                        majorDimension: "ROWS"
                        values:
                          - ["${project_id}", "${location}", "${workflow_id}", "${execution_id}", "${callback_details.url}", "", "FALSE"]
            - log_and_await_callback:
                try:
                  steps:
                    - log_await_start:
                        call: sys.log
                        args:
                          severity: INFO
                          data: ${"Started waiting for callback from sheet " + sheetId}
                    - await_callback:
                        call: events.await_callback
                        args:
                          callback: ${callback_details}
                          timeout: 3600
                        result: callback_request
                    - log_await_stop:
                        call: sys.log
                        args:
                          severity: INFO
                          data: ${"Stopped waiting for callback from sheet " + sheetId}
                except:
                    as: e
                    steps:
                        - log_error:
                            call: sys.log
                            args:
                                severity: "ERROR"
                                text: ${"Received error " + e.message}
            - check_null_await_result:
                switch:
                  - condition: ${callback_request == null}
                    return: null
            - log_await_result:
                call: sys.log
                args:
                  severity: INFO
                  data: ${"Approved by " + callback_request.http_request.body.approver}
            - return_await_result:
                return: ${callback_request.http_request.body}
  8. 请务必将占位符 sheetId 值替换为 spreadsheetId

  9. 点击部署

gcloud

  1. 为工作流创建源代码文件:

    touch workflows-awaits-callback-sheets.yaml
    
  2. 在文本编辑器中,将以下工作流复制到源代码文件中:

    main:
      steps:
        - init:
            assign:
            # Replace with your sheetId and make sure the service account
            # for the workflow has write permissions to the sheet
            - sheetId: "10hieAH6b-oMeIVT_AerSLNxQck14IGhgi8ign-x2x8g"
        - before_sheets_callback:
            call: sys.log
            args:
              severity: INFO
              data: ${"Execute steps here before waiting for callback from sheets"}
        - wait_for_sheets_callback:
            call: await_callback_sheets
            args:
              sheetId: ${sheetId}
            result: await_callback_result
        - after_sheets_callback:
            call: sys.log
            args:
              severity: INFO
              data: ${"Execute steps here after receiving callback from sheets"}
        - returnResult:
            return: ${await_callback_result}
    
    await_callback_sheets:
        params: [sheetId]
        steps:
            - init:
                assign:
                  - project_id: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
                  - location: ${sys.get_env("GOOGLE_CLOUD_LOCATION")}
                  - workflow_id: ${sys.get_env("GOOGLE_CLOUD_WORKFLOW_ID")}
                  - execution_id: ${sys.get_env("GOOGLE_CLOUD_WORKFLOW_EXECUTION_ID")}
            - create_callback:
                call: events.create_callback_endpoint
                args:
                  http_callback_method: POST
                result: callback_details
            - save_callback_to_sheets:
                call: googleapis.sheets.v4.spreadsheets.values.append
                args:
                    range: ${"Sheet1!A1:G1"}
                    spreadsheetId: ${sheetId}
                    valueInputOption: RAW
                    body:
                        majorDimension: "ROWS"
                        values:
                          - ["${project_id}", "${location}", "${workflow_id}", "${execution_id}", "${callback_details.url}", "", "FALSE"]
            - log_and_await_callback:
                try:
                  steps:
                    - log_await_start:
                        call: sys.log
                        args:
                          severity: INFO
                          data: ${"Started waiting for callback from sheet " + sheetId}
                    - await_callback:
                        call: events.await_callback
                        args:
                          callback: ${callback_details}
                          timeout: 3600
                        result: callback_request
                    - log_await_stop:
                        call: sys.log
                        args:
                          severity: INFO
                          data: ${"Stopped waiting for callback from sheet " + sheetId}
                except:
                    as: e
                    steps:
                        - log_error:
                            call: sys.log
                            args:
                                severity: "ERROR"
                                text: ${"Received error " + e.message}
            - check_null_await_result:
                switch:
                  - condition: ${callback_request == null}
                    return: null
            - log_await_result:
                call: sys.log
                args:
                  severity: INFO
                  data: ${"Approved by " + callback_request.http_request.body.approver}
            - return_await_result:
                return: ${callback_request.http_request.body}
  3. 请务必将占位符 sheetId 值替换为 spreadsheetId

  4. 输入以下命令以部署工作流:

    gcloud workflows deploy workflows-awaits-callback-sheets \
        --source=workflows-awaits-callback-sheets.yaml \
        --location=us-central1 \
        --service-account=PROJECT_NUMBER-compute@developer.gserviceaccount.com

    PROJECT_NUMBER 替换为您的 Google Cloud 项目编号。您可以检索项目编号:

    gcloud projects describe PROJECT_ID
    

测试端到端流程

执行工作流以测试端到端流程。执行工作流会运行 与工作流相关联的当前工作流定义。

控制台

  1. 在 Google Cloud 控制台中,前往 Workflows 页面:

    进入 Workflows

  2. 工作流程页面上,选择 workflows-awaits-callback-sheets 工作流,转到其详情页面。

  3. 工作流详情页面上,点击 执行

  4. 再次点击执行

    工作流开始运行,并且执行状态应为 Running。通过 日志也表明工作流已暂停并正在等待:

    Execute steps here before waiting for callback from sheets
    ...
    Started waiting for callback from sheet 1JlNFFnqs760M_KDqeeeDc_qtrABZDxoalyCmRE39dpM
  5. 验证工作流是否已将回调详细信息写入 您的电子表格。

    例如,您应该会在 执行 ID 列:回调网址中的回调端点 列,在已批准?列中返回 FALSE

  6. 在电子表格中,将 FALSE 更改为 TRUE

    一两分钟后,执行应该会恢复,然后完成 执行状态为 Succeeded 的请求。

gcloud

  1. 打开终端。

  2. 执行工作流:

      gcloud workflows run workflows-awaits-callback-sheets

    工作流开始运行,输出应指明该工作流 已暂停并正在等待:

      Waiting for execution [a8361789-90e0-467f-8bd7-ea1c81977820] to complete...working.

  3. 验证工作流是否已将回调详细信息写入 您的电子表格。

    例如,您应该会在 执行 ID 列:回调网址中的回调端点 列,在已批准?列中返回 FALSE

  4. 在电子表格中,将 FALSE 更改为 TRUE

    一两分钟后,执行应该会恢复,然后完成 且执行状态为 SUCCEEDED

清理

如果您为本教程创建了一个新项目,请删除项目。 如果您使用的是现有项目,希望保留此项目且不保留本教程中添加的任何更改,请删除为教程创建的资源

删除项目

为了避免产生费用,最简单的方法是删除您为本教程创建的项目。

要删除项目,请执行以下操作:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

删除在本教程中创建的资源

  1. 删除 Google 云端硬盘中的文件
  2. 删除工作流

后续步骤