快速入門導覽課程說明如何設定及執行呼叫 Google Workspace API 的應用程式。
Google Workspace 快速入門導覽課程會使用 API 用戶端程式庫,處理驗證和授權流程的一些細節。建議您針對自己的應用程式使用用戶端程式庫。本快速入門導覽課程採用適用於測試環境的簡化驗證方式。對於實際工作環境,建議您先瞭解驗證和授權,再選擇應用程式適用的存取憑證。
建立 Go 指令列應用程式,向 Google Drive Activity API 提出要求。
目標
必要條件
設定環境
如要完成本快速入門導覽課程,請設定環境。
啟用 API
使用 Google API 前,請先在 Google Cloud 專案中啟用這些 API。您可以在單一 Google Cloud 專案中啟用一或多個 API。
如果您使用新的 Google Cloud 專案來完成本快速入門導覽課程,請設定 OAuth 同意畫面,然後將您自己新增為測試使用者。如果您已完成 Cloud 專案的這個步驟,請跳到下一節。
-
在 Google Cloud 控制台中,依序點選「選單」圖示 menu
>「API 和服務」>「OAuth 同意畫面」。
前往 OAuth 同意畫面
- 在「使用者類型」部分,選取「內部」,然後按一下「建立」。
- 填寫應用程式註冊表單,然後按一下「儲存並繼續」。
目前,您可以略過新增範圍,然後按一下「儲存並繼續」。日後建立用於 Google Workspace 機構外部的應用程式時,必須將「使用者類型」變更為「外部」,然後新增應用程式所需的授權範圍。
- 查看您的應用程式註冊摘要。如要變更,請按一下「編輯」。如果應用程式註冊正確無誤,請按一下「Back to Dashboard」(返回資訊主頁)。
為桌面應用程式授權憑證
如要驗證使用者並存取應用程式中的使用者資料,您必須建立一或多個 OAuth 2.0 用戶端 ID。用戶端 ID 可用來向 Google 的 OAuth 伺服器識別單一應用程式。如果您的應用程式是在多個平台上執行,您就必須為每個平台建立個別的用戶端 ID。
-
在 Google Cloud 控制台中,依序點選「選單」圖示 menu >「API 和服務」>「憑證」。
前往「憑證」頁面
- 依序按一下「建立憑證」>「OAuth 用戶端 ID」。
- 依序按一下「應用程式類型」>「電腦版應用程式」。
- 在「名稱」欄位中,輸入憑證名稱。這個名稱只會顯示在 Google Cloud 控制台中。
- 按一下「建立」,畫面上會顯示 OAuth 用戶端已建立的畫面,顯示您的新用戶端 ID 與用戶端密鑰。
- 按一下「OK」(確定)。新建立的憑證會顯示在「OAuth 2.0 用戶端 ID」下方。
- 將下載的 JSON 檔案儲存為
credentials.json
,然後將檔案移至工作目錄。
準備工作區
建立工作目錄:
mkdir quickstart
變更為工作目錄:
cd quickstart
初始化新模組:
go mod init quickstart
取得 Google Drive Activity API Go 用戶端程式庫和 OAuth2.0 套件:
go get google.golang.org/api/driveactivity/v2
go get golang.org/x/oauth2/google
設定範例
在工作目錄中,建立名為 quickstart.go
的檔案。
在檔案中貼上以下程式碼:
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"reflect"
"strings"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/driveactivity/v2"
"google.golang.org/api/option"
)
// Retrieve a token, saves the token, then returns the generated client.
func getClient(config *oauth2.Config) *http.Client {
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
tokFile := "token.json"
tok, err := tokenFromFile(tokFile)
if err != nil {
tok = getTokenFromWeb(config)
saveToken(tokFile, tok)
}
return config.Client(context.Background(), tok)
}
// Request a token from the web, then returns the retrieved token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser then type the "+
"authorization code: \n%v\n", authURL)
var authCode string
if _, err := fmt.Scan(&authCode); err != nil {
log.Fatalf("Unable to read authorization code: %v", err)
}
tok, err := config.Exchange(context.TODO(), authCode)
if err != nil {
log.Fatalf("Unable to retrieve token from web: %v", err)
}
return tok
}
// Retrieves a token from a local file.
func tokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
tok := &oauth2.Token{}
err = json.NewDecoder(f).Decode(tok)
return tok, err
}
// Saves a token to a file path.
func saveToken(path string, token *oauth2.Token) {
fmt.Printf("Saving credential file to: %s\n", path)
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Unable to cache oauth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}
// Returns a string representation of the first elements in a list.
func truncated(array []string) string {
return truncatedTo(array, 2)
}
// Returns a string representation of the first elements in a list.
func truncatedTo(array []string, limit int) string {
var contents string
var more string
if len(array) <= limit {
contents = strings.Join(array, ", ")
more = ""
} else {
contents = strings.Join(array[0:limit], ", ")
more = ", ..."
}
return fmt.Sprintf("[%s%s]", contents, more)
}
// Returns the name of a set property in an object, or else "unknown".
func getOneOf(m interface{}) string {
v := reflect.ValueOf(m)
for i := 0; i < v.NumField(); i++ {
if !v.Field(i).IsNil() {
return v.Type().Field(i).Name
}
}
return "unknown"
}
// Returns a time associated with an activity.
func getTimeInfo(activity *driveactivity.DriveActivity) string {
if activity.Timestamp != "" {
return activity.Timestamp
}
if activity.TimeRange != nil {
return activity.TimeRange.EndTime
}
return "unknown"
}
// Returns the type of action.
func getActionInfo(action *driveactivity.ActionDetail) string {
return getOneOf(*action)
}
// Returns user information, or the type of user if not a known user.
func getUserInfo(user *driveactivity.User) string {
if user.KnownUser != nil {
if user.KnownUser.IsCurrentUser {
return "people/me"
}
return user.KnownUser.PersonName
}
return getOneOf(*user)
}
// Returns actor information, or the type of actor if not a user.
func getActorInfo(actor *driveactivity.Actor) string {
if actor.User != nil {
return getUserInfo(actor.User)
}
return getOneOf(*actor)
}
// Returns information for a list of actors.
func getActorsInfo(actors []*driveactivity.Actor) []string {
actorsInfo := make([]string, len(actors))
for i := range actors {
actorsInfo[i] = getActorInfo(actors[i])
}
return actorsInfo
}
// Returns the type of a target and an associated title.
func getTargetInfo(target *driveactivity.Target) string {
if target.DriveItem != nil {
return fmt.Sprintf("driveItem:\"%s\"", target.DriveItem.Title)
}
if target.Drive != nil {
return fmt.Sprintf("drive:\"%s\"", target.Drive.Title)
}
if target.FileComment != nil {
parent := target.FileComment.Parent
if parent != nil {
return fmt.Sprintf("fileComment:\"%s\"", parent.Title)
}
return "fileComment:unknown"
}
return getOneOf(*target)
}
// Returns information for a list of targets.
func getTargetsInfo(targets []*driveactivity.Target) []string {
targetsInfo := make([]string, len(targets))
for i := range targets {
targetsInfo[i] = getTargetInfo(targets[i])
}
return targetsInfo
}
func main() {
ctx := context.Background()
b, err := os.ReadFile("credentials.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
// If modifying these scopes, delete your previously saved token.json.
config, err := google.ConfigFromJSON(b, driveactivity.DriveActivityReadonlyScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(config)
srv, err := driveactivity.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
log.Fatalf("Unable to retrieve driveactivity Client %v", err)
}
q := driveactivity.QueryDriveActivityRequest{PageSize: 10}
r, err := srv.Activity.Query(&q).Do()
if err != nil {
log.Fatalf("Unable to retrieve list of activities. %v", err)
}
fmt.Println("Recent Activity:")
if len(r.Activities) > 0 {
for _, a := range r.Activities {
time := getTimeInfo(a)
action := getActionInfo(a.PrimaryActionDetail)
actors := getActorsInfo(a.Actors)
targets := getTargetsInfo(a.Targets)
fmt.Printf("%s: %s, %s, %s\n", time, truncated(actors), action, truncated(targets))
}
} else {
fmt.Print("No activity.")
}
}
執行範例
在工作目錄中,建構並執行範例:
go run quickstart.go
-
首次執行範例時,系統會提示您授予存取權:
-
如果您尚未登入 Google 帳戶,請在系統提示時登入。如果您登入了多個帳戶,請選取一個用於授權的帳戶。
- 然後點選 [Accept]。
您的 Go 應用程式會執行並呼叫 Google Drive Activity API。
授權資訊會儲存在檔案系統中,因此下次您執行程式碼範例時,系統不會提示您取得授權。
後續步驟