使用 Gmail API 以及 Google Calendar API 開發 G Suite 應用的經驗

訊息揭露:最近到新公司後,在新生訓練期間被要求隨機組隊模擬做專案,而我在這個專案中寫了一些程式碼,這篇文章只是把題目變形後,就我貢獻程式碼的部份記錄一下開發經驗。

專案的背景,是要在採用 G Suite 處理公司內部郵件、行事曆或文件檔案等單位上寫一個處理這些訊息的應用程式。作為多年 developer 的我,自然直接就想到要研究 Gmail API 以及 Google Calendar API 來擷取 G Suite 上的資料來做處理,但是這兩組 APIs 都提供了許多程式語言的作法,但因為團隊中有專業的前端工程師,所以我就從善如流,全部採用它們的 JavaScript SDK 來完成,簡化與同事間的合作。最後我們也就完成了一個 single page, pure JavaScript 的小應用程式。

處理身份驗證

啟用 API 以及產生 OAuth 用戶端 ID 及密鑰

既然是要存取郵件與行事曆上個人的資訊,在使用 APIs 前當然是要先做身份驗證,還有要為開發的應用程式驗明正身。所以第一步就是要到 Google API Console 中建立或選取一個專案(如果同時有使用 Google Cloud Platform 專案是相通的),並且為這個專案啟用 Gmail API 以及 Google Calendar API 的使用權限。

啟用 Gmail API

啟用 Gmail 以及 Google Calendar 這兩個 API 之後,你可以分別為這兩個 API 的使用權限來設定不同的 OAuth 驗證憑證,但是既然我的應用程式要同時用到這兩組 APIs,所以我選擇到 API 與服務的憑證頁面來建立 OAuth 用戶端 ID

建立 OAuth 用戶端 ID 以便我的前端應用程式進行身份驗證

第一次建立 OAuth 用戶端 ID 時,會先要建立這個應用程式的基本資料,以便讓用戶在做身份驗證時能瞭解這個應用程式,而且身份驗證完畢後可以根據應用程式要求的範圍存取到用戶的資料,所以也是在這裡設定,同時也會讓使用者看到會存取到多少資料內容。

設定此應用程式用 Google APIs 做完身份驗證後能存取的基本資料範圍

建立 OAuth 用戶端 ID 時,因為這是一個 Web(Browser)-based 應用程式,所以選擇網路應用程式,再設定名稱已授權的 JavaScript 來源 (OAuth 中的 callback URL,可以設定多個 URLs 來套用於開發機及線上環境等等)。

設定 OAuth 用戶端 ID 以取得 id 及 secret key

設定完成後,就會產生 OAuth ID 以及密鑰資訊,請將它們妥善儲存好。

產生的 OAuth ID 及密鑰

在網頁中加入登入及身份驗證

在需要呼叫 Gmal 以及 Google Calendar API 的頁面中,可以直接加入 Google 帳號(也是 G Suite 帳號)的登入按鈕,簡單的作法如下:

首先在 HTML 頁面中的 <head> 區間加入一段 meta tag:

<meta name="google-signin-client_id" content="放上上述的 OAuth 用戶端 ID">

在 HTML 中於要呈現 Google 帳號登入登出按鈕的位置放上 <div id="signin"></div>

加入下述兩個載入 JavaScript 的 tag,分別用於載入 Gmail/Google Calendar API library 以及身份驗證的 library,並且加上 onload 參數設定當 JavaScript library 載入完畢後呼叫函式做後續的動作。

<script src="https://apis.google.com/js/client.js?onload=loadAPIs" async defer></script>
<script src="https://apis.google.com/js/platform.js?onload=renderButton" async defer></script>

接下來就是在 JavaScript 中定義 loadAPIs 以及 renderButton 這兩個函式。

/**
* 載入 Gmail 以及 Google Calendar 完整的 API 函式庫。
*/
function loadAPIs() {
gapi.client.load('gmail', 'v1');
gapi.client.load('calendar', 'v3');
}
/**
* 繪製 Google 帳號(或 G Suite 帳號)的登入登出按鈕。
*/
function renderButton() {
// 定義 API 存取範圍,在我的需求中只需要讀取 gmail/cal 的內容,沒有要寫入
var gmailScope = 'https://www.googleapis.com/auth/gmail.readonly',
calendarScope = 'https://www.googleapis.com/auth/calendar.readonly';
  gapi.signin2.render('signin', {
'scope': ['profile', gmailScope, calendarScope].join(' '),
'onsuccess': onSigninSuccess,
'onfailure': onSigninFailure
});
}
/**
* 登入成功後的 callback 函式。
* @param {object?} googleUser - 登入成功的用戶物件。
*/
function onSigninSuccess(googleUser) {
// 取得用戶 profile
var profile = googleUser.getBasicProfile();
...
}
/**
* 登入失敗的 callback 函式。
*/
function onSigninFailure() {
// 處理登入失敗,或是用戶不授權
...
}
/**
* 如果需要將使用者登出。
*/
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
// 完成登出後的 callback function ...
});
}

這樣一來,畫面上便會有畫好的 Google 帳號登入按鈕讓使用者完成身份驗證了。

使用 SDK

基本上 API 呼叫的部份就是看著參考資料或範例,再根據需求來呼叫對應的函式來處理。

Gmail API 的部份

例如要搜尋郵件中的訊息,可以參考 Gmail API 中的 Users.messages: list 這個 API 的文件,而程式碼大概會像是這樣:

var request = gapi.client.gmail.users.messages.list({
userId: profile.getId(), // 這個 userId 是指登入的使用者
q: 搜尋的關鍵字詞
});
request.execute(function(resp) {
// 取得符合搜尋字詞的郵件列表
var messages = resp.messages;
  messages.forEach(function(message) {
// 取得單封完整郵件內容
var fullMessage = gapi.client.gmail.users.messages.get({
userId: profile.getId(),
id: message.id,
format: 'full'
});
...
});
...
});

Google Calendar API 的部份

例如要找某個行事曆上的事件,我採用了 Events:list 的 API,使用範例如下:

// 一個帳號下可能有多個行事曆,需要使用其它 API 來取得 calendar ID
// 不過最主要的那個行事曆可以使用 'primary' 來指定。
gapi.client.calendar.events.list({ calendarId: 'primary', q: 搜尋關鍵字})
.then(function(resp) {
// 取得符合搜尋條件的行事曆事件列表
var calEvents = resp.result.items;
    calEvents.forEach(function(calEvent) {
var summary = calEvent.summary; // 取得行事曆內容
...
});
});

參考資料