今天一開始我們先來調整一下我們之前對Expiry宣告的類型,昨天仔細看發現oauth官方的Token(oauth2.Token)結構如下:
type Token struct {
	// AccessToken is the token that authorizes and authenticates
	// the requests.
	AccessToken string `json:"access_token"`
	// TokenType is the type of token.
	// The Type method returns either this or "Bearer", the default.
	TokenType string `json:"token_type,omitempty"`
	// RefreshToken is a token that's used by the application
	// (as opposed to the user) to refresh the access token
	// if it expires.
	RefreshToken string `json:"refresh_token,omitempty"`
	// Expiry is the optional expiration time of the access token.
	//
	// If zero, TokenSource implementations will reuse the same
	// token forever and RefreshToken or equivalent
	// mechanisms for that TokenSource will not be used.
	Expiry time.Time `json:"expiry,omitempty"`
	// raw optionally contains extra metadata from the server
	// when updating a token.
	raw interface{}
	// expiryDelta is used to calculate when a token is considered
	// expired, by subtracting from Expiry. If zero, defaultExpiryDelta
	// is used.
	expiryDelta time.Duration
}
他的Expiry是存time.Time,但是我們在dynamodb存的確是string,雖然是有辦法轉換,但為了漂亮一點我們還是來改一下宣告。
先到adapter/dynamodb/oauth.go找到我們宣告的GoogleOAuthToken,將Expiry從string改成time.Time,這樣各個欄位存進去之前的類型就與oauth2的類型一致了。
// internal\adapter\dynamodb\oauth.go
type GoogleOAuthToken struct {
	PK           string    `dynamodbav:"PK"`
	AccessToken  string    `dynamodbav:"access_token"`
	TokenType    string    `dynamodbav:"token_type"`
	RefreshToken string    `dynamodbav:"refresh_token"`
	Expiry       time.Time `dynamodbav:"expiry"`
}
接著到drive service的login_service.go,我們把Expiry從tok.Expiry.String()換成tok.Expiry,這樣就好囉。
// internal\app\service\drive\login_service.go
func (dr *GoogleDriveService) Login(ctx context.Context, lineID string, authCode string) error {
	tok, err := dr.driveServiceGoogleOA.UserOAuthToken(authCode)
	if err != nil {
		return err
	}
	dToken := dynamodb.GoogleOAuthToken{
		PK:           lineID,
		AccessToken:  tok.AccessToken,
		TokenType:    tok.TokenType,
		RefreshToken: tok.RefreshToken,
		Expiry:       tok.Expiry,
	}
	err = dr.driveServiceDynamodb.AddGoogleOAuthToken(dToken)
	if err != nil {
		return err
	}
	return nil
}
接著我們來嘗試從Dynamodb取得token,並用取出來的token調用使用者的Google drive。
我們到drive service的drive_service.go,把ListFiles的輸入從原本的authCode改成lineID。原本透過dr.driveServiceGoogleOA.UserOAuthToken(authCode)去Google取得token,我們現在換成透過lineID從Dynamodb查詢上次登入儲存的token。
// internal\app\service\drive\drive_service.go
func (dr *GoogleDriveService) ListFiles(ctx context.Context, lineID string) (map[string]string, error) {
	// token改成去db取
	dToken, err := dr.driveServiceDynamodb.GetGoogleOAuthToken(lineID)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	// 把token轉成oauth2的格式
	tok := oauth2.Token{
		AccessToken:  dToken.AccessToken,
		TokenType:    dToken.TokenType,
		RefreshToken: dToken.RefreshToken,
		Expiry:       dToken.Expiry,
	}
	d, err := dr.driveServiceGoogleOA.NewGoogleDrive(ctx, &tok)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	result, err := d.ListFiles(10)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	return result, nil
}
最後我們回Callback,加入message.Text == "list"的條件來測試,取得lineID後丟到DriveService.ListFiles,並把取得到的資料清單,直接印出來後回傳。
// internal\router\api\v1\callback.go
func Callback(app *app.Application) gin.HandlerFunc {
...
for _, event := range events {
			if event.Type == linebot.EventTypeMessage {
				switch message := event.Message.(type) {
				case *linebot.TextMessage:
					if message.Text == "login" {
						lineID := event.Source.UserID
						authURL := app.DriveService.LoginURL(ctx, lineID)
						if _, err = app.LineBotClient.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(authURL)).Do(); err != nil {
							log.Println(err)
						}
						return
					}
					if message.Text == "list" {
						lineID := event.Source.UserID
						res, err := app.DriveService.ListFiles(ctx, lineID)
						if err != nil {
							log.Println(err)
							return
						}
						if _, err = app.LineBotClient.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(fmt.Sprintln(res))).Do(); err != nil {
							log.Println(err)
						}
						return
					}
...
重新OAuth Login,讓Dynamodb存有我們新的token後,我們到linebot list一下,可以看到成功的從Dynamodb查詢到token並且能順利取得Google Drive的資料了~

那今天就先寫到這邊,大家連假愉快,我們明天見~