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

Google 表格是一种基于云端的 电子表格解决方案,支持实时协作,并提供用于 直观呈现、处理和交流数据的工具。

本教程演示了如何创建和部署工作流,该工作流会创建回调端点(或 Webhook)、将回调网址保存到 Google 表格、暂停执行,然后等待通过 Google 表格电子表格进行人工批准以重启工作流。 详细了解如何使用回调

目标

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

  1. 在 Google 云端硬盘中创建新文件夹。此文件夹用于存储电子表格,并允许工作流写入电子表格。
  2. 创建 Google 表格电子表格以捕获批准并向工作流发起回调。
  3. 使用 Google Apps 脚本(一种基于云端的 JavaScript 平台,可让您以编程方式创建、读取和修改 Google Workspace 产品)在通过更新电子表格批准请求时触发暂停的工作流恢复。
  4. 创建和部署工作流,该工作流会调用 Google 表格 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 的新项目会创建此服务帐号,该账号具有 IAM 基本 Editor 角色,并且采用以下电子邮件格式:

    PROJECT_NUMBER-compute@developer.gserviceaccount.com

    您可以在 控制台 的 Google Cloud 欢迎页面上找到项目编号。

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

gcloud

  1. 在 Google Cloud 控制台中,激活 Cloud Shell。

    激活 Cloud Shell

    Cloud Shell 会话随即会在控制台的底部启动,并显示命令行提示符。 Google Cloud Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟来完成初始化。

  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 的新项目会创建此服务帐号,该账号具有 IAM 基本 Editor 角色,并且采用以下电子邮件格式:

    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 表格 API 直接在指定文件夹中创建电子表格。不过,您可以采用其他方法,包括在创建电子表格后将其移动到特定文件夹,如本示例所示。如需了解详情,请参阅 使用 Google 云端硬盘文件夹

  1. 转到 sheets.google.com

  2. 点击新建 Plus

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

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

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

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

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

    请注意,G 列(已批准?)中的值用于在工作流中发起回调。

  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 脚本与 Google 表格搭配使用,请参阅 自定义函数快速入门

  1. 从 Google 表格创建 Apps 脚本项目:

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

    您的脚本现在已绑定 到电子表格,这会赋予脚本特殊能力,使其能够更改 用户界面或在电子表格打开时做出响应。

    脚本项目表示 Apps 脚本文件和资源的集合。脚本项目中的代码文件具有 .gs 扩展名。

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

    1. 打开 Apps 脚本项目。
    2. 点击编辑器
    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 添加触发器 对话框中,配置触发器:
      1. 选择要运行的函数 列表中,选择 handleEdit
      2. 选择应运行哪个部署 列表中,选择 Head
      3. 选择事件来源 列表中,选择来自电子表格
      4. 选择事件类型 列表中,选择修改时
      5. 失败通知设置 列表中,选择每天通知我
    5. 点击保存
    6. 如果您收到选择 Google 账号的提示,请选择相应的账号,然后点击允许

      这允许您的 Apps 脚本项目查看、修改、创建和删除 Google 表格电子表格;以及连接到外部服务。

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

    1. 打开 Apps 脚本项目。
    2. 点击项目设置 图标。
    3. 选中在编辑器中显示“appsscript.json”清单文件 复选框。
    4. 点击编辑器
    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 Sheets API 连接器写入 Google 表格电子表格。

控制台

  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 页面上,选择 workflows-awaits-callback-sheets 工作流以转到其详情页面。

  3. 工作流详细信息 页面上,点击 执行

  4. 再次点击执行

    工作流开始运行,其执行状态应为正在运行 。日志还表明工作流已暂停并正在等待:

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

    例如,您应该在 执行 ID 列中看到工作流执行 ID,在 回调网址 列中看到回调端点,在 已批准? 列中看到 FALSE

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

    一两分钟后,执行应恢复,然后完成 ,执行状态为成功

gcloud

  1. 打开终端。

  2. 执行工作流:

      gcloud workflows run workflows-awaits-callback-sheets

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

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

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

    例如,您应该在 执行 ID 列中看到工作流执行 ID,在 回调网址 列中看到回调端点,在 已批准? 列中看到 FALSE

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

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

清理

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

删除项目

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

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

  1. 在 Google Cloud 控制台中,前往 管理资源 页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击 关闭以删除项目。

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

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

后续步骤