Node.js 如何集成Google日历
Google开发者控制台提供了一个平台,可以通过集成Google提供的服务和工具来构建一流的应用程序。
Google日历 是这些服务之一,使用日历可以提供诸如计划/创建事件的功能,在诸如待办事项应用程序或会议安排器等项目中非常有优势。
在开发具有多个功能的项目时,通过使用API来执行较小的任务,开发人员的工作将更加轻松,并且可以专注于应用程序的目的。我们可以使用Google日历API来代替编写自己的逻辑来存储日历属性、用户权限和事件管理细节,该API具有集成的安全和日历模块。我们将省去协调重叠事件、发送提醒、通知等麻烦。
本文介绍了在NodeJS应用程序中集成Google日历API并使用服务帐户进行身份验证创建新事件的方法。我们的目标是在NodeJS应用程序中集成Google日历服务。我们需要测试日历方法是否在我们的后端NodeJS应用程序中正常工作,并在用户提供的凭据错误或缺少权限的情况下使用适当的错误处理机制。本项目中集成的日历的2个功能是事件查看和添加事件。
方法:
- 首先,在Google开发者控制台上创建一个新的Google云项目。这是使用Google云服务、管理API和权限以及控制项目协作者的起始步骤。
- 创建新项目后,下一步是启用API。一个项目可以启用多个API,我们只需要一个即可,即“Google日历API”。
- 现在我们需要了解在Google Workspace中如何进行身份验证和授权。首先,让我们复习一下这两个术语的定义。
- 身份验证 :验证身份的过程。
- 授权 :验证应用程序/用户是否可以访问某些资源。
- Google Workspace提供2种类型的身份验证:用户身份验证和应用程序身份验证。我们将使用 应用程序身份验证 ,其中应用程序代表用户进行身份验证以使用Google服务。
为什么我们要使用“应用程序身份验证”
- 由于我们的问题陈述是将日历服务集成到NodeJS中,我们不需要处理任何前端代码。通过应用程序身份验证,无需用户登录,而我们的服务器将直接与Google服务器通信。
现在我们的身份将如何被验证或身份验证将如何进行?
- 在Google Workspace中,身份验证需要“凭据”。凭据是一种“身份标识”,将验证应用程序/用户的身份。
- 在Google提供的3种身份验证凭据中(API密钥,OAuth 2同意,服务帐号),我们将使用“ 服务帐号 ”。服务帐号主要用于“服务器之间的交互”,如果有必要,也可以扩展到访问用户数据。当我们有一个后端应用程序将直接与Google服务器通信时,服务帐号非常有用。
- 服务帐号将是 属于我们的应用程序的帐号 而不是用户。我们的应用程序将处理调用日历API的工作 而不涉及用户直接参与 。
- 此帐号的凭据要求创建一个密钥,并以JSON文件的形式下载到用户的系统中。JSON文件中的数据是我们应用程序中将使用的凭据。
- 授权将由我们编写的代码执行。Google将日历存储在云中,我们的应用程序需要获取访问云存储的数据/资源的权限。
- 每当我们访问日历方法时,服务帐号凭据和我们的方法调用将合并为一个请求(以进行授权的API调用)并发送到Google授权服务器。此请求将由“客户端”传递,可以通过使用Google的客户端库或直接通过HTTP/REST来实现。
- 在我们的项目中,我们将通过声明一个JWT客户端来发送HTTP请求。然后服务器将返回一个访问令牌。我们的应用程序将使用此访问令牌对日历进行更改。
- 下一步是在 Google 日历网站上创建一个新的日历(文中进一步提到),并将服务帐户电子邮件添加为用户,并授予权限以更改日历。
- 最后,我们使用Express创建我们的Node.js应用程序,并安装“ googleapis ”(一个用于访问Google API的Node.js客户端库)包。
- 我们将使用 calendar.events 调用对日历执行2个操作:
- 查看当前日期的事件
- 添加新事件
现在让我们从上述方法的步骤实践开始。
首先,我们将看到在Google开发者控制台中设置项目和凭证。
步骤1: 打开google控制台,并点击下拉菜单以选择一个项目。我们将创建一个新项目。点击右上角的“新项目”。给其一个合适的项目名称,然后点击创建。
步骤2: 创建项目后,将显示Google控制台仪表板。请确保您正在查看新创建项目的仪表板,以添加Google日历功能。
步骤3: 单击位于左侧“更多产品”部分下方的“APIs & Services”选项卡,然后单击“启用APIs和服务”。
步骤4: API库将显示在屏幕上。从这里,我们可以选择我们想要为项目启用的API。由于我们要集成Google日历,可以在搜索框中输入“日历”,然后选择“Google日历API”。
步骤5: Google日历API的所有详细信息将显示在页面上,包括API的文档和服务。点击 启用 将此API添加到您的项目中。
步骤6: 现在我们将为我们的应用程序创建服务账户。点击“创建凭据”来创建一个服务账户。
步骤7: 选择“Google日历API”作为凭证的API类型。
步骤8: 现在将出现提示,问您“要访问哪些数据?”由于我们正在创建一个服务帐户,所以点击“应用程序数据”和“下一步”。
步骤9: 接下来输入服务帐户详细信息,如帐户名称和服务帐户ID将同时生成。然后点击“创建并继续”。
步骤10: 我们将为应用程序选择“Owner”角色,以便给与服务账号访问权限。添加角色后,点击继续。
步骤11: 这一步是可选的。您可以选择将管理员权限授予特定的人或一组可以管理该服务账户的人。点击“完成”。
步骤12: 服务账号已设置完成。现在需要创建此服务账号的凭据。与此服务账号关联的密钥可以下载为一个JSON文件。我们将在以后的步骤中将其称为“服务密钥文件”。点击服务账号下方的电子邮件。
步骤13: 转到KEYS标签,然后点击ADD KEY。
步骤14: 我们将创建一个新的密钥,并选择JSON作为密钥类型。点击“创建”。
步骤15: 私钥已创建并以JSON文件的形式下载。请记住,这个文件非常敏感,其 凭证不得公开!
步骤16: 我们仍然需要项目编号才能配置日历。点击右上角的三点菜单选项,然后点击“项目设置”。
步骤17: 复制项目编号并将其存放在安全的地方以备将来使用。
步骤18: 现在我们将设置一个Google日历并检索其ID。该日历将需要我们在JSON文件中获得的凭据。进入Google日历,并通过点击“其他日历”旁边的“+”按钮创建一个新日历。
步骤19: 输入名称和描述,然后点击“创建日历”。
步骤20: 点击新创建的日历旁边的三点菜单,然后点击“设置和共享”。
步骤21: 向下滚动到“集成日历”部分,复制日历ID。将此ID存储在安全的地方以备将来使用。打开JSON服务密钥文件并复制“客户端电子邮件”。转到“与特定人分享”部分,通过粘贴已复制的客户端电子邮件来“添加人员”。
现在我们将设置Express应用程序。
步骤1: 创建一个适当名称的新文件夹。我们将使用“Google_Calendar”。
步骤2: 在终端上运行以下命令:
npm init -y
npm i express googleapis
项目结构: 它将如下所示。
步骤3: 创建一个index.js文件。这将是我们将集成Google日历的主要文件。首先,我们将查看事件。在此文件中,逻辑序列如下:
- 引入所需的包,如express和googleapis。
- 声明从JSON“服务密钥文件”中获取的所有必要凭据,例如 私钥、客户端电子邮件 等等。
- 使用我们在之前步骤中获取的 项目编号和日历ID 。
- 使用凭据声明一个JWT(JSON Web Token)客户端。 此JWT客户端将使用服务帐户凭据对我们的应用程序进行身份验证,以访问Google服务器。
- SCOPE定义了使用JWT客户端关联的API调用的授权级别。由于我们首先要显示即将发生的事件,所以SCOPE是“只读”的。
- JWT客户端将用于向Google服务器发出请求。
- Google服务器将返回访问令牌。
- 此访问令牌将用于调用Google API。
- 使用项目编号和JWT客户端来定义一个日历对象。
- 声明主页路由,此路由将显示按照开始时间排序的日历中最接近的10个即将发生的事件。如果未安排将来或当前日期的事件,则显示“未找到即将发生的事件”。
- 通过calendar.events.list访问事件。从此调用返回的结果将使用JSON.stringify返回给浏览器。
在 index.js 文件中插入以下代码:
文件名:index.js
//index.js code for integrating Google Calendar
const express = require('express');
const { google } = require('googleapis');
const app = express();
const SCOPES = 'https://www.googleapis.com/auth/calendar.readonly';
const GOOGLE_PRIVATE_KEY="<private-key>"
const GOOGLE_CLIENT_EMAIL = "<client-email>"
const GOOGLE_PROJECT_NUMBER = "<project-number>"
const GOOGLE_CALENDAR_ID = "<calendar-id>"
const jwtClient = new google.auth.JWT(
GOOGLE_CLIENT_EMAIL,
null,
GOOGLE_PRIVATE_KEY,
SCOPES
);
const calendar = google.calendar({
version: 'v3',
project: GOOGLE_PROJECT_NUMBER,
auth: jwtClient
});
app.get('/', (req, res) => {
calendar.events.list({
calendarId: GOOGLE_CALENDAR_ID,
timeMin: (new Date()).toISOString(),
maxResults: 10,
singleEvents: true,
orderBy: 'startTime',
}, (error, result) => {
if (error) {
res.send(JSON.stringify({ error: error }));
} else {
if (result.data.items.length) {
res.send(JSON.stringify({ events: result.data.items }));
} else {
res.send(JSON.stringify({ message: 'No upcoming events found.' }));
}
}
});
});
app.listen(3000, () => console.log(`App listening on port 3000!`));
// This code is contributed by Yashi Shukla
私钥,客户电子邮件可以从JSON文件中获取。您之前存储的日历ID和项目编号在此处将被使用。
运行应用程序的步骤:
node index.js
或
nodemon index.js
输出结果: 结果可通过以下链接在浏览器上查看: http://localhost:3000/
此路由将在日历上显示10个即将发生的事件,如果没有添加任何事件,则显示“未找到即将发生的事件”。
由于这是一个新创建的日历,目前没有即将到来的活动。
步骤4: 添加新活动。谷歌提供了不同的方法来管理和控制日历中添加的活动。我们可以使用 calendar.events.insert() 方法来添加新的活动。我们需要在设置谷歌日历的第6步中做一些更改来添加用户的角色。返回到“与特定人共享”部分,将服务账户的角色从“查看所有活动详细信息”更改为“更改活动”。
步骤5: 由于我们现在要编辑事件,我们的应用程序需要以服务帐户的身份进行身份验证。对于这种类型,在命令提示符中(项目的根目录)执行以下操作,将KEY_PATH替换为JSON服务密钥文件的完整路径。
set GOOGLE_APPLICATION_CREDENTIALS=KEY_PATH
步骤6: 在 index.js 中创建一个用于添加事件(/createEvent)的新路由。为简单起见,我们将硬编码事件的详细信息。根据Calendar API的文档,事件对象可以定义为:
//A sample event object for calendar
var event = {
'summary': 'My first event!',
'location': 'Hyderabad,India',
'description': 'First event with nodeJS!',
'start': {
'dateTime': '2022-01-12T09:00:00-07:00',
'timeZone': 'Asia/Dhaka',
},
'end': {
'dateTime': '2022-01-14T17:00:00-07:00',
'timeZone': 'Asia/Dhaka',
},
'attendees': [],
'reminders': {
'useDefault': false,
'overrides': [
{'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 10},
],
},
};
您可以根据自己的选择给日期、时间、地点和描述输入自己的值。
步骤7: JWT 客户端为 API 调用定义了“只读”范围。然而,如果我们想要添加新的事件,一个“只读”范围是不够的。
- 因此现在,让我们声明另一个名为“auth”的客户端,其中包含用于身份验证的凭据,如JSON 服务密钥文件的路径和授权范围为“ https://www.googleapis.com/auth/calendar ”,以便完全访问 Google Calendar API。
- auth 客户端将执行与 JWT 客户端类似的后台过程,从 Google 认证服务器请求访问令牌以进行 API 调用。
以下是声明“auth”客户端的代码:
//Creating an aunthenticated client to call events.insert()
const auth = new google.auth.GoogleAuth({
keyFile: '<FULL-PATH-OF-JSON-FILE>',
scopes: 'https://www.googleapis.com/auth/calendar', //full access to edit calendar
});
auth.getClient().then(a=>{
calendar.events.insert({
auth:a,
calendarId: GOOGLE_CALENDAR_ID,
resource: event,
}, function(err, event) {
if (err) {
console.log('There was an error contacting the Calendar service: ' + err);
return;
}
console.log('Event created: %s', event.data);
res.jsonp("Event successfully created!");
});
})
索引文件index.js中的最终代码如下:
- 为允许用户向日历添加新事件创建了一个名为“/createEvent”的新路由。
- 由于我们只使用了NodeJS,事件详情将被硬编码。声明了一个事件对象,其中包含关键字段的必要值,如描述、名称、概要、开始时间、结束时间等。
- 我们的应用程序可以使用“ calendar.events.insert ”来添加此事件。此操作由“ google.auth.GoogleAuth ”的“auth”客户端进行身份验证和授权。
- 添加事件后,当用户再次访问主页路由时,如果事件的开始日期是当前日期或未来日期,则将显示新的事件详情。
下面是 index.js 的最终代码:
文件名:index.js
//Final index.js code
const express = require('express');
const { google } = require('googleapis');
const app = express();
const SCOPES = 'https://www.googleapis.com/auth/calendar.readonly';
const GOOGLE_PRIVATE_KEY="<private-key>";
const GOOGLE_CLIENT_EMAIL = "<client-email>"
const GOOGLE_PROJECT_NUMBER = "<project-number>"
const GOOGLE_CALENDAR_ID = "<calendar-id>"
const jwtClient = new google.auth.JWT(
GOOGLE_CLIENT_EMAIL,
null,
GOOGLE_PRIVATE_KEY,
SCOPES
);
const calendar = google.calendar({
version: 'v3',
project: GOOGLE_PROJECT_NUMBER,
auth: jwtClient
});
app.get('/', (req, res) => {
calendar.events.list({
calendarId: GOOGLE_CALENDAR_ID,
timeMin: (new Date()).toISOString(),
maxResults: 10,
singleEvents: true,
orderBy: 'startTime',
}, (error, result) => {
if (error) {
res.send(JSON.stringify({ error: error }));
} else {
if (result.data.items.length) {
res.send(JSON.stringify({ events: result.data.items }));
} else {
res.send(JSON.stringify({ message: 'No upcoming events found.' }));
}
}
});
});
app.get("/createEvent",(req,res)=>{
var event = {
'summary': 'My first event!',
'location': 'Hyderabad,India',
'description': 'First event with nodeJS!',
'start': {
'dateTime': '2022-01-12T09:00:00-07:00',
'timeZone': 'Asia/Dhaka',
},
'end': {
'dateTime': '2022-01-14T17:00:00-07:00',
'timeZone': 'Asia/Dhaka',
},
'attendees': [],
'reminders': {
'useDefault': false,
'overrides': [
{'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 10},
],
},
};
const auth = new google.auth.GoogleAuth({
keyFile: '<full-path-of-JSON-file>',
scopes: 'https://www.googleapis.com/auth/calendar',
});
auth.getClient().then(a=>{
calendar.events.insert({
auth:a,
calendarId: GOOGLE_CALENDAR_ID,
resource: event,
}, function(err, event) {
if (err) {
console.log('There was an error contacting the Calendar service: ' + err);
return;
}
console.log('Event created: %s', event.data);
res.jsonp("Event successfully created!");
});
})
})
app.listen(3000, () => console.log(`App listening on port 3000!`));
// This code is contributed by Yashi Shukla
现在正在检查事件创建。打开浏览器并转到 http://localhost:3000/createEvent。
输出:
以下输出将会显示
现在你还记得,在输出部分之前,显示的是“没有即将到来的事件”。现在再次打开http://localhost:3000/。你新创建的事件详情将会显示出来!
前往Google日历,您还可以在那里看到新增的事件。
最终输出: