【问题标题】:Is it possible to watch Directory API changes from Google App Maker/Google Apps Script?是否可以从 Google App Maker/Google Apps Script 观看 Directory API 更改?
【发布时间】:2019-01-28 17:16:37
【问题描述】:

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

在我的新应用中,我有一个员工数据源,其中包含与最初通过 Directory API 填充的评估本身的关系。阅读文档here,似乎我应该能够在用户资源上设置监视以查找用户更新并解析它们以在我的员工数据源中进行适当的名称和经理更改。但是,我无法弄清楚监视请求的接收 URL 应该是什么。

如果有人在 Google App Maker 中成功地做到了这一点,甚至只是在 Google Apps 脚本中,我很想知道您是如何做到的。

编辑添加:

我创建了一个愚蠢的小 GAS 测试功能,看看我是否可以让下面的 @dimu-designs 解决方案工作。不幸的是,我只是收到一个错误的请求错误。这是我所拥有的:

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);
}

Address 是当前的网络应用 URL。

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

【问题讨论】:

    标签: google-apps-script google-app-maker


    【解决方案1】:

    这是一个更全面的答案。

    Google 支持跨其许多 API 的推送通知。然而,它们之间存在许多微妙(而不是那么微妙)的差异。一些利用 webhook 的工具主要将其数据负载作为 HTTP 标头发送;例如 Drive API 和 Calendar API。其他人将他们的有效负载混合在 HTTP 标头和 POST 正文中(例如:AdminDirectory API)。而且它变得更加疯狂,一些 API 完全利用了不同的机制(例如:GMail API 利用 Cloud PubSub)。

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


    第 1 步 - 将独立脚本部署为 Web 应用程序

    让我们从以下模板脚本开始,并从 Apps Script Editor 菜单将其部署为 Web App 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 步 - 验证/验证域所有权并添加/注册域

    注意: 自 2019 年 8 月起,无法再使用此方法验证 GAS Web 应用 URL。 Google Cloud Functions 可能是可行的 替代

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

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

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

    从 Apps 脚本编辑器菜单中选择 Publish > Register in Chrome Web Store。向 Chrome 网上应用店注册已发布的网络应用程序也会验证 URL 的域(无需摆弄搜索控制台)。

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

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


    第 3 步 - 提出观看请求

    对于此步骤,应为您的应用脚本项目和 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
            }
        );
    }
    

    请注意,Directory API 推送通知有一个过期时间,最长为开始监视后的 6 小时,因此必须定期更新它以确保将通知发送到端点 URL。通常,您可以使用基于时间的触发器每 5 小时左右调用一次此函数。


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

    与其他 API 的推送机制不同,Directory API 会在发送通知的同时发送一个 POST 正文,因此可以保证在发送通知时触发 doPost(e) 方法。定制 doPost(e) 触发器以处理传入事件并重新部署 Web 应用程序:

    function doPost(e) {
    
        switch(e.parameter.event) {
            case "update":
                // do update stuff
                break;
    
            case "add":
                break;
    
            case "delete":
                break;
        }
    
        return ContentService.createTextOutput("POST message");
    
    }
    

    请记住一个警告。更新事件的推送通知仅告诉您用户的数据已更新,它不会告诉您确切的更改内容。但这是另一个问题的问题。

    请注意,我遗漏了很多细节,但这应该足以让您启动并运行。

    【讨论】:

    • ...最后,所有的部分组合在一起!非常感谢您非常将这么多不同地方的这么多位进行了非常清晰的组装。
    • 我遵循了所有这些步骤,但我仍然可以在日志中看到我的 doPost 函数从未达到过。我如何确定监视请求确实有效?
    • @Benbb96 更改代码后,您是否将 Web 应用重新部署到新版本?如果没有,您需要将您的 Web 应用程序重新部署到新版本,以确保任何更改都生效(仅单击更新按钮是不够的)。它是 GAS Web 应用程序的一个已知怪癖......
    • @DimuDesigns 是的,我每次在 doPost() 中进行更改时都会这样做。我还检查了 URL 是否已更改,但似乎没有更改。
    • @Benbb96 您使用Logger.log()console.log() 的哪种记录方法?您不能将Logger.logdoPost() 一起使用,您需要使用StackDriver 的console.log() 方法。 console.log() 生成的日志应显示在 Apps Script Developer Dashboard/HubMy Executions 部分下(在列表中找到您的项目名称,然后单击最右侧的展开图标)。
    【解决方案2】:

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

    更新:还有来自 Directory API 的 PUSH notifications

    【讨论】:

    • 我的理解是,给定资源类型的 watch 方法应该取代通知。对于用户,该文档是here。我只是不清楚如何在没有接收推送通知的 URL 的情况下在 GAS 中实现它。
    • @dimu-designs 的回答表明这也行不通。不过谢谢你的建议。
    • @LynnHoffman 是的,但对于您的需要,emailkind 文件可能已经足够好了。然后去获取详细信息。 {“种类”:“admin#directory#user”,“id”:“111220860655841818702”,“etag”:“\”Mf8RAMnABsVfQ47MMT_18MHAdRE/evLIDlz2Fd9zbAqwvIp7Pzq8UAw\“”,“primaryEmail”:“user@mydomain.com”}
    • @JonPellant 这还不够。为了做任何有价值的事情,OP 需要知道触发通知的事件并存储在 X-Goog-Resource-State HTTP 标头中。
    • @JonPellant 但是您的方法可能仍然适用...请参阅我的更新回复
    【解决方案3】:

    我能够使用基于 Heroku 的 Node.js 应用程序作为中介 API 为本地资源(电子表格)设置推送通知。 Node 应用程序捕获自定义请求标头并构建有效负载以供 GAS Web 应用程序的 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);
    

    挑战在于验证该域地址。有一些方法可以验证独立(不是文件绑定!)GAS 网络应用程序,但是,正如之前的海报所提到的,Apps Script 网络应用程序无法访问自定义标头。

    启用 Pub/Sub API 并创建主题和订阅后,转到 API 和服务 -> 凭据 -> 域验证。它为您提供了一些验证域的选项,包括提供 html 文件。下载谷歌生成的文件。值得庆幸的是,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); 
    }
    

    然后像这样在你的路由器对象中包含处理程序:

    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;
    
    }
    

    【讨论】:

      【解决方案4】:

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

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

      不过,您可以将 Google Cloud Functions(一种 GCP 服务)与 GAS 结合使用,但您必须熟悉 Node.js。


      编辑

      在考虑了这一点并审查了您的要求后,我相信有一种方法可以仅使用 GAS 来解决这个问题。

      您可以通过setting the event parameter when initializing the watch 为每个用户/域的给定事件(您的用例中的“更新”事件)设置一个唯一的推送通知通道。因此,只有在发生更新事件时才会触发 GAS Web 应用程序;您实际上并不需要依赖 HTTP 标头来确定事件类型。

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

      【讨论】:

      • 很遗憾,这不是我想要的答案。但它肯定回答了我的问题。谢谢,@Dimu!
      • @LynnHoffman 好吧,您可以使用 Heroku 作为中间服务来做到这一点。请看我的回答
      • 我不知道我是否只是误解,但是当我尝试创建频道时,我不断收到错误请求错误。我将发布我的测试功能作为对我的问题的更新。
      • 自 2014 年 9 月以来,使用 GAS 接收网络挂钩的能力一直是一个活跃的问题/功能请求:https://issuetracker.google.com/issues/36761910。我会考虑使用 Google Cloud Function 作为替代方案。
      • @LynnHoffman Drive API Push Notifications 是一种特殊情况,因为 X-Goog-Resource-State HTTP 标头是跟踪驱动器文件更改的唯一方法。然而,管理目录 API 的推送通知是另一回事,它确实提供了更多的余地,因为您可以通过 url 参数跟踪事件。所以你应该仍然可以使用 GAS Web 应用程序来做到这一点。我将进行一些实验并写一些东西。
      猜你喜欢
      • 1970-01-01
      • 2019-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-25
      • 1970-01-01
      • 2017-02-26
      • 1970-01-01
      相关资源
      最近更新 更多