是否可以从 Google App Maker/Google Apps 脚本中查看目录 API 的变化?

Is it possible to watch Directory API changes from Google App Maker/Google Apps Script?

我正在 Google App Maker 中开发性能评估应用程序。我们当前工具面临的挑战之一是,当一个人的经理发生变化或一个人的名字发生变化时,它不会与我们的 G Suite 目录同步——他们现有的评估与该人的旧名字相关联,我们有手动更改。

在我的新应用程序中,我有一个员工数据源,其中包含与最初通过目录 API 填充的评估本身的关系。阅读文档 here,似乎我应该能够在 Users 资源上设置监视以查找用户更新并解析它们以在我的 Employees 数据源中进行适当的名称和经理更改。不过,我想不通的是接收 URL 应该用于手表请求。

如果有人在 Google App Maker 中成功完成此操作,或者甚至仅在 Google Apps 脚本中成功完成此操作,我很想知道您是如何做到的。

编辑添加:

我创建了一个愚蠢的小 GAS 测试函数,看看我是否可以让下面的 @dimu-designs 解决方案起作用。不幸的是,我刚收到一个 Bad Request 错误。这是我拥有的:

function setUserWatch() {
  var optionalArgs = {
    "event": "update"
  };

  var resource = {
    "id": "10ff4786-4363-4681-abc8-28166022425b",
    "type": "web_hook",
    "address": "https://script.google.com/a/.../...hXlw/exec"
  };
  AdminDirectory.Users.watch(resource);
}

地址是当前的网络应用URL。

编辑添加更多内容: 自 2014 年 9 月以来,使用 GAS 接收网络挂钩的(不)能力一直是一个活跃的 issue/feature 请求 -- https://issuetracker.google.com/issues/36761910 -- @dimu-designs 已经有一段时间了。

您可以使用 GAS 和 Admin SDK 执行此操作。目录 API 支持 Notifications(请注意,这将被弃用,因此不确定是什么替代了此功能)。然后您可以设置一个 GMAIL 脚本来执行您需要对通知执行的操作。

更新:还有 PUSH notifications 来自目录 API。

很遗憾,您不能,至少不能单独使用 Apps 脚本。

管理员目录推送通知需要 web-hook URL 端点来接收通知。您可能认为部署 GAS 网络应用程序并将其 URL 用作端点就足够了。但是 Admin Directory Push notifications 的问题在于它的数据负载驻留在自定义 HTTP headers 中,无法从 GAS Web App 访问。 (这也适用于跨其他 API 的推送通知,包括 Drive 和 Calendar API)

然而,您可以将 Google Cloud Functions(一项 GCP 服务)与 GAS 结合使用,但您必须了解 Node.js。


编辑

经过深思熟虑并查看您的要求后,我相信有一种方法可以通过使用 GAS 来实现。

您可以通过 setting the event parameter when initializing the watch 为每个 user/domain(您用例中的 'update' 事件)的给定事件设置唯一的推送通知渠道。因此,只有在发生更新事件时才会触发 GAS 网络应用程序;您真的不需要依赖 HTTP header 来确定事件类型。

如果您想跟踪多个事件,您只需为每个事件创建一个唯一的通道,并为每个事件使用相同的 GAS Web 应用端点。您可以通过检查 POST 请求中发送的事件参数来区分事件。这应该消除对 middle-man 服务的需求,例如 Heroku 或 Google Cloud Functions。

我能够使用 Heroku-based Node.js 应用程序作为中介 API 为本地资源(电子表格)设置推送通知。 Node 应用程序捕获自定义请求 headers 并构建要由 GAS 网络应用程序的 doPost(e) 函数使用的有效负载。

构造watch请求的代码很简单

  //get the unique id
  var channelId = Utilities.getUuid();  
  //build the resource object    
  var resource = {  
    "id": channelId,
    "type": "web_hook",
    "address": "https://yourapp.herokuapp.com/drivesub    
  }

//watch the resource
Drive.Files.watch(resource, fileId);

挑战在于验证该域地址。有一些方法可以验证独立的(不是 file-bound!)GAS 网络应用程序,但是,正如之前的发帖者所提到的,Apps 脚本网络应用程序无法访问自定义 headers.

启用 Pub/Sub API 并创建主题和订阅后,转到 APIs & Services -> Credentials -> Domain verification。它为您提供了一些验证域的选项,包括提供 html 文件。下载 Google 生成的文件。值得庆幸的是,Heroku 使部署 Node 应用程序变得非常容易

https://devcenter.heroku.com/articles/getting-started-with-nodejs

验证域后,您可以将订阅推送数据到 Heroku 上的端点 URL。

我只是为路由处理程序创建了 js 文件,并创建了一个专门用于域验证的文件

handlers.verifyDomain = function(callback){
    //Synchronously read from the static html file. Async method fs.readFile() doesn't make the file available to the router callback
    var file = fs.readFileSync('./google/google_file.html');
    callback(200, file); 
}

然后在您的路由器 object 中包含处理程序,如下所示:

var router = {
    "google_file.html": handlers.verifyDomain
}

最后,在您的服务器启动函数中,从 URL 获取路径(有多种方法),并执行处理程序;

    var routeHandler = router(path);
    routerHandler(function(statusCode, file){ 
      //do something 
     });

返回域验证工具并加载 HTML 页面以验证 URL。验证后,剩下的唯一步骤就是创建 GAS 网络应用程序并发布到它。

返回节点应用。请注意,我的端点是 https://yourapp.herokuapp.com/drivesub

//The code for posting data to GAS web app. Of course, you need to
// update your router with router['driveSub'] = handlers.driveSub

handlers.driveSub = function(data, callback){
    var headers = data.headers;
    var options = {
        method:"POST",
        uri: your_gas_app_url, 
        json:{"headers":headers}
    };
    //Use NPM to install the request module
    request.post(options, function(err, httpResponse, body){
        console.log(err, body);

    });
    callback(200, data);

    }

Apps 脚本应用 - 不要忘记发布它。

function doPost(req) {

var postData = req.postData["contents"];
var headers = JSON.parse(postData)["headers"];
//take action
return 200;

}

这是一个更全面的答案。

Google 支持在他们的许多 API 中推送通知。然而,它们之间存在许多细微(而不是那么细微)的差异。一些利用 webhook 的人主要以 HTTP headers 的形式发送数据负载;例如驱动器 API 和日历 API。其他人将他们的有效负载混合在 HTTP headers 和 POST body(例如:AdminDirectory API)中。它变得更加疯狂,一些 API 完全使用不同的机制(例如:GMail API 利用 Cloud PubSub)。

每个都有细微差别,但您的目标是在 GAS 应用程序中利用 AdminDirectory 推送通知。为此,您需要一个 GAS Web 应用程序,其 URL 可以用作 web-hook 端点。


第 1 步 - 将 Stand-Alone 脚本部署为 Web 应用程序

让我们从以下模板脚本开始,并从 Apps 脚本编辑器菜单将其部署为 Web 应用程序 Publish > Deploy As Web App:

/** HTTP GET request handler */
function doGet(e) {
    return ContentService.createTextOutput("GET message");
}

/** HTTP POST request handler */
function doPost(e) {
    return ContentService.createTextOutput("POST message");
}

第 2 步 - Verify/Validate 域所有权和 Add/Register 域

NOTE: As of August 2019, GAS Web App URLs can no longer be verified using this method. Google Cloud Functions may be a viable alternative.

部署网络应用程序后,您现在必须验证并注册接收域 url,在本例中也是网络应用程序 url。此 url 采用以下形式:

https://script.google.com/macros/s/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/exec

从技术上讲,您不能拥有 GAS 网络应用 url 的域。值得庆幸的是,Google 的 App Script Gods 确实提供了一种机制来验证和注册 GAS 网络应用程序 url。

来自 Apps 脚本编辑器菜单 select Publish > Register in Chrome Web Store。在 Chrome 网上商店注册已发布的网络应用程序也会验证 URL 的域(无需 fiddle 使用搜索控制台)。

验证后,您需要 add the "domain" via the Domain verification page in the API Console。 "domain" 是 url 中没有 'exec' 的所有内容,因此您将添加如下所示的字符串:

https://script.google.com/macros/s/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/


第 3 步 - 提出观看请求

对于此步骤,应为您的 App 脚本项目和 API 控制台启用 AdminSDK/Directory API 服务。

创建一个生成监视请求的函数(可以针对其他事件类型重新设计):

function startUpdateWatch() {
    var channel = AdminDirectory.newChannel(),
        receivingURL = "https://script.google.com/macros/s/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/exec",
        gSuiteDomain = "[business-name].com",
        event = "update";

    channel.id = Utilities.getUuid();
    channel.type = "web_hook";
    channel.address = receivingURL + "?domain=" + gSuiteDomain + "&event=" + event;
    channel.expiration = Date.now() + 21600000; // max of 6 hours in the future; Note: watch must be renew before expiration to keep sending notifications

    AdminDirectory.Users.watch(
        channel, 
        {
            "domain":gSuiteDomain,
            "event":event
        }
    );
}

请注意,目录 API 推送通知有期限,从启动手表起最多 6 小时,因此必须定期更新以确保通知发送到端点 URL。通常,您可以使用 time-based 触发器每 5 小时左右调用一次此函数。


第 4 步 - 更新 doPost(e) 触发器以处理传入通知

与其他 API 的推送机制不同,目录 API 发送 POST body 及其通知,因此 doPost(e) 方法是保证在发送通知时触发。定制 doPost(e) 触发器以处理传入事件和 re-deploy 网络应用程序:

function doPost(e) {

    switch(e.parameter.event) {
        case "update":
            // do update stuff
            break;

        case "add":
            break;

        case "delete":
            break;
    }

    return ContentService.createTextOutput("POST message");

}

有一点需要牢记。更新事件的推送通知只会告诉您用户数据已更新,它不会告诉您确切的更改内容。但这是另一个问题的问题。

请注意,我遗漏了大量细节,但这应该足以让您振作起来 运行。