使用无服务器和 Vue 探索数据:使用无服务器函数自动更新 GitHub 文件

Avatar of Sarah Drasner
Sarah Drasner

DigitalOcean 为您的旅程各个阶段提供云产品。从 价值 200 美元的免费积分 开始!

我与 Simona Cotin、John Papa、Jessie Frazelle、Burke Holland 和 Paige Bailey 等优秀的人才在一个大型团队中工作。我们经常交流,因为这是开发者倡导者的工作内容之一,而且我们也经常被问到将在哪里发言。在大多数情况下,我们每个人都管理自己的网站,在网站上列出所有这些演讲,但这对于想要探索的人来说并不是一个很好的体验,所以我做了一个演示,可以轻松地查看谁在哪个会议上发言,时间是什么,以及所有这些信息的链接。为了好玩,我使用了 three.js,这样你就可以快速可视化我们都访问了多少地方。

你可以查看 此处的实时演示,或者在 GitHub 上 探索代码

在本教程中,我将介绍如何通过使用无服务器函数从 Google 获取我们所有演讲地点的地理位置数据来设置地球仪。我还将介绍如何使用 Vuex(基本上是 Vue 版本的 Redux)来存储所有这些数据并将其输出到表格和地球仪,以及如何在 Vue 中使用计算属性来使对该表格的排序超级高效和流畅。

文章系列

  1. 使用无服务器函数自动更新 GitHub 文件(您就在这里!)
  2. 过滤和使用数据

无服务器函数

到底是怎么回事?

最近我在推特上说,“无服务器是一件非常有趣的事情,但标题最吸引人。”我在这里要坚持这一点,并说,每个人都会告诉你的第一件事是,无服务器是一个错误的名称,因为你实际上仍在使用服务器。这是真的。那么为什么称之为无服务器呢?无服务器的承诺是减少设置和维护服务器的时间。你基本上是让服务为你处理维护和扩展,并将你的需求缩减为这样声明的函数:当收到此请求时,运行此代码。出于这个原因,有时人们将它们称为函数即服务或 FaaS。

这有用吗?当然!我喜欢在不需要的时候不必照看服务器,而且付款也会自动扩展,这意味着你不会为任何你不使用的东西付费。

FaaS 是否适合始终使用?嗯,不完全是。如果你想管理小的执行,它非常有用。无服务器函数可以检索数据,可以发送电子邮件通知,甚至可以做一些事情,例如动态裁剪图像。但是对于任何可能占用资源或大量计算的进程,能够像平常一样与服务器通信实际上可能更有效。

我们的演示在这里是一个很好的例子,说明我们想要使用无服务器的地方。我们主要只是维护和更新一个 JSON 文件。我们将拥有所有初始的演讲者数据,并且需要从 Google 获取地理位置数据来创建我们的地球仪。我们还可以让它通过 GitHub 提交来触发。让我们深入了解。

创建无服务器函数

我们将从一个大的 JSON 文件开始,该文件是我从同事演讲活动的电子表格中输出的。该文件包含我创建表格所需的一切,但为了创建地球仪,我将使用 来自 Google 数据艺术的这个 webgl-globe,我将对其进行修改。你可以在自述文件中看到,我最终将格式化我的数据以提取年份,但我还需要我们访问的每个地点的纬度和经度

var data = [
    [
    'seriesA', [ latitude, longitude, magnitude, latitude, longitude, magnitude, ... ]
    ],
    [
    'seriesB', [ latitude, longitude, magnitude, latitude, longitude, magnitude, ... ]
    ]
];

最终,我还要减少每年重复出现的实例以使数量级变得更合理,但我们将在这个系列的第二部分中处理对数据的修改,并在 Vue 中完成。

要开始,如果你还没有,请创建一个 免费的 Azure 试用帐户。然后转到门户网站:preview.portal.azure.com

在里面,你会看到一个侧边栏,其中包含许多选项。顶部将显示新建。单击它。

Showing the sidebar in Azure where you can create a new service

接下来,我们将从列表中选择函数应用,并填写我们函数的新名称。这将为我们提供一些选项。你可以看到它将自动获取我们的资源组、订阅并创建一个存储帐户。它还将使用资源组中的位置数据,因此,令人高兴的是,它很容易填充,如以下 GIF 所示。

Create a new function

默认值可能非常适合你的需求。如上面的 GIF 所示,它将自动填充大多数字段,这些字段仅来自应用程序名称。你可能需要根据大多数流量来自哪里或从一个中点(例如,如果你在旧金山和纽约都有很多流量),最好选择美国中部的一个位置来更改你的位置。

托管计划可以是消耗型(默认值)或应用服务计划。我选择消耗型,因为资源会动态添加或删除,这是整个无服务器概念的魔力。如果你想获得更高层次的控制或细节,你可能想要应用服务计划,但请记住,这意味着你将手动扩展和添加资源,因此你需要做更多的工作。

Modify the function once it's created

你将被带到一个屏幕,其中显示有关你的函数的很多信息。检查是否一切正常,然后单击侧边栏上的函数加号。

Add in the actual function

从那里你可以选择一个模板,我们将向下翻页并从给定的选项中选择 GitHub Webhook – JavaScript。

choose the github javascript template from the templates in the portal

选择此选项将把你带到一个页面,其中包含一个`index.js`文件。如果你愿意,你可以输入代码,但他们提供了一些默认代码来运行初始测试,以查看一切是否正常。在我们创建函数之前,让我们先测试一下,以确保一切看起来都正常。

New default template

我们将点击顶部的保存和运行按钮,以下是我们的结果。你可以看到输出给了我们一个评论,我们在绿色中获得了 200 OK 的状态,并且获得了一些日志来验证我们的 GitHub webhook 成功触发。

testing the default output

非常好!现在是最有趣的部分:让我们编写自己的函数。

编写我们的第一个无服务器函数

在我们的例子中,我们拥有所有演讲的地理位置数据,这是我们需要创建表格的,但为了创建我们地球仪的 JSON 文件,我们需要更多的数据:我们需要所有演讲活动的纬度和经度。JSON 文件将由我们的 Vuex 中心存储读取,我们可以将需要读取的部分传递给每个组件。

我用于无服务器函数的文件存储在我的 GitHub 存储库中,你可以在这里浏览整个文件,但让我们也一起看一下

我要提到的第一件事是,我已经用配置选项填充了这些变量,用于本教程的目的,因为我不想把所有私人信息都告诉你。我的意思是,这很好,我们是朋友,但我们刚认识。

// GitHub configuration is read from process.env
let GH_USER = process.env.GH_USER;
let GH_KEY = process.env.GH_KEY;
let GH_REPO = process.env.GH_REPO;
let GH_FILE = process.env.GH_FILE;

在现实世界中,我可以直接插入数据

// GitHub configuration is read from process.env
let GH_USER = sdras;

…等等。为了使用这些环境变量(如果你也希望存储它们并保持它们私密),你可以像上面一样使用它们,然后转到仪表板中的你的函数。在那里你会看到一个名为已配置功能的区域。单击应用程序设置,你将被带到一个页面,其中有一个表格,你可以在其中输入这些信息。

使用我们的数据集

首先,我们将从 GitHub 检索原始 JSON 文件并对其进行解码/解析。我们将使用一种方法,该方法从 GitHub 响应中获取文件并对其进行 Base64 编码(此处提供更多信息)。

module.exports = function(context, data) {
 // Make the context available globally
 gContext = context;

 getGithubJson(githubFilename(), (data, err) => {
   if (!err) {
     // No error; base64 decode and JSON parse the data from the Github response
     let content = JSON.parse(
       new Buffer(data.content, 'base64').toString('ascii')
     );

接下来,我们将获取原始数据中每个项目的地理信息,如果一切顺利,我们将将其推送到 GitHub,否则,将会出错。我们将有两个错误:一个用于一般错误,另一个用于我们收到正确响应但存在地理错误的情况,这样我们就可以区分它们。你会注意到我们使用的是 `gContext.log` 将输出发送到我们的门户控制台。

getGeo(makeIterator(content), (updatedContent, err) => {
       if (!err) {
         // we need to base64 encode the JSON to embed it into the PUT (dear god, why)
         let updatedContentB64 = new Buffer(
           JSON.stringify(updatedContent, null, 2)
         ).toString('base64');
         let pushData = {
           path: GH_FILE,
           message: 'Looked up locations, beep boop.',
           content: updatedContentB64,
           sha: data.sha
         };
         putGithubJson(githubFilename(), pushData, err => {
           context.log('All done!');
           context.done();
         });
       } else {
         gContext.log('All done with get Geo error: ' + err);
         context.done();
       }
     });
   } else {
     gContext.log('All done with error: ' + err);
     context.done();
   }
 });
};

太好了!现在,给定一个条目数组(包装在迭代器中),我们将遍历每个条目并使用 Google 地图 API 填充纬度和经度。请注意,我们还会缓存位置,以尝试节省一些 API 调用。

function getGeo(itr, cb) {
 let curr = itr.next();
 if (curr.done) {
   // All done processing- pass the (now-populated) entries to the next callback
   cb(curr.data);
   return;
 }

 let location = curr.value.Location;

现在让我们检查缓存,看看我们是否已经查找过这个位置

 if (location in GEO_CACHE) {
   gContext.log(
     'Cached ' +
       location +
       ' -> ' +
       GEO_CACHE[location].lat +
       ' ' +
       GEO_CACHE[location].long
   );
   curr.value.Latitude = GEO_CACHE[location].lat;
   curr.value.Longitude = GEO_CACHE[location].long;
   getGeo(itr, cb);
   return;
 }

然后,如果没有在缓存中找到任何内容,我们将进行查找并缓存结果,或者让我们知道我们没有找到任何内容

 getGoogleJson(location, (data, err) => {
   if (err) {
     gContext.log('Error on ' + location + ' :' + err);
   } else {
     if (data.results.length > 0) {
       let info = {
         lat: data.results[0].geometry.location.lat,
         long: data.results[0].geometry.location.lng
       };
       GEO_CACHE[location] = info;
       curr.value.Latitude = info.lat;
       curr.value.Longitude = info.long;
       gContext.log(location + ' -> ' + info.lat + ' ' + info.long);
     } else {
       gContext.log(
         "Didn't find anything for " + location + ' ::' + JSON.stringify(data)
       );
     }
   }
   setTimeout(() => getGeo(itr, cb), 1000);
 });
}

我们在过程中使用了一些辅助函数,这些函数有助于获取 Google JSON,以及获取和放入 GitHub JSON。

现在,如果我们在门户中运行此函数,我们将看到我们的输出

Running the function and seeing the console: status 200 ok

它起作用了!我们的无服务器函数使用所有新数据更新了我们的 JSON 文件。我真的很喜欢我可以使用后端服务,而无需离开我熟悉的 JavaScript。我们只需要 git pull 就可以使用此文件作为我们 Vuex 中央存储中的状态。这将允许我们填充表格,我们将在本系列的下一部分中解决这个问题,我们还将使用它来更新我们的地球仪。如果你想自己尝试无服务器函数并查看其运行情况,你可以使用 免费试用帐户 创建一个。

文章系列

  1. 使用无服务器函数自动更新 GitHub 文件(您就在这里!)
  2. 过滤和使用数据