iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0

我們仔細看昨天的流程會發現,要調用Google Drive之前一定要先拿使用者的token去建立*http.Client然後再交給drive.NewService去作操作,調用其他Google的API也是相似的作法,也就是要先有GoogleOAuth才有GoogleDrive,所以我們先來微調一下昨天的程式。

// oauth.go
package google

import (
	"context"
	"log"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/drive/v3"
	"google.golang.org/api/option"
)

type GoogleOAuth struct {
	Config *oauth2.Config
}

func NewGoogleOAuth(clientID string, clientSecret string, redirectURL string) *GoogleOAuth {
	return &GoogleOAuth{
		Config: &oauth2.Config{
			ClientID:     clientID,
			ClientSecret: clientSecret,
			Endpoint:     google.Endpoint,
			Scopes:       []string{drive.DriveScope},
			RedirectURL:  redirectURL,
		},
	}
}

func (oa *GoogleOAuth) OAuthLoginURL() (oauthURL string) {
	oauthURL = oa.Config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
	return oauthURL
}

func (oa *GoogleOAuth) UserOAuthToken(authCode string) (*oauth2.Token, error) {
	token, err := oa.Config.Exchange(context.TODO(), authCode)
	if err != nil {
		log.Printf("Unable to retrieve token from web %v", err)
		return nil, err
	}
	return token, nil
}

func (oa *GoogleOAuth) NewGoogleDrive(ctx context.Context, tok *oauth2.Token) (*GoogleDrive, error) {
	client := oa.Config.Client(ctx, tok)
	srv, err := drive.NewService(ctx, option.WithHTTPClient(client))
	if err != nil {
		log.Printf("Unable to retrieve Drive client: %v", err)
		return nil, err
	}
	return &GoogleDrive{
		Service: srv,
	}, nil
}

我們把NewGoogleDrive拉到oauth.go,並且變成GoogleOAuth的方法,然後把原本NewClientByUserToken放進去NewGoogleDrive裡面,畢竟NewClient現在的目的就是去NewGoogleDrive,我們讓GoogleOAuth變成對外的統一入口,來操作各種GoogleAPI。如果以後有其他GoogleAPI服務要用,就在這邊繼續NewGoogleXXX就好了。而drive.go就保持存放專門操作google drive的部分~

接著我們在/adapter/google下建立interface.go,讓GoogleOAuth的方法對外

package google

import (
	"context"

	"golang.org/x/oauth2"
)

type GoogleOAuthI interface {
	OAuthLoginURL() (oauthURL string)
	UserOAuthToken(authCode string) (*oauth2.Token, error)
	NewGoogleDrive(ctx context.Context, tok *oauth2.Token) (*GoogleDrive, error)
}

下一步,我們來去實現Service層

  1. 首先,像Sample Service一樣,我們建立以下對應的檔案

    https://ithelp.ithome.com.tw/upload/images/20231003/20115990XW8gDs6Yjx.png

  2. 建立interface.go並宣告DriveServiceGoogleOAuthI ,我們複製GoogleOAuthI的方法,讓GoogleOAuth能被注入近來這個Service。

    // interface.go
    package drive
    
    import (
    	"context"
    
    	"github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/google"
    	"golang.org/x/oauth2"
    )
    
    type DriveServiceGoogleOAuthI interface {
    	OAuthLoginURL() (oauthURL string)
    	UserOAuthToken(authCode string) (*oauth2.Token, error)
    	NewGoogleDrive(ctx context.Context, tok *oauth2.Token) (*google.GoogleDrive, error)
    }
    
  3. 建立service.go,讓New一個GoogleDriveService的時候,要放入有實現DriveServiceGoogleOAuthI的實例。

    // service.go
    package drive
    
    import "context"
    
    type GoogleDriveService struct {
    	driveServiceGoogleOA DriveServiceGoogleOAuthI
    }
    
    type GoogleDriveServiceParam struct {
    	DriveServiceGoogleOA DriveServiceGoogleOAuthI
    }
    
    func NewGoogleDriveService(_ context.Context, param GoogleDriveServiceParam) *GoogleDriveService {
    	return &GoogleDriveService{
    		driveServiceGoogleOA: param.DriveServiceGoogleOA,
    	}
    }
    
  4. 建立drive_service.go,先寫一個ListFiles(),這部分之後還會改,這邊先確定可以正確調用GoogleOAuthGoogleDrive的方法。

    // drive_service.go
    package drive
    
    import (
    	"context"
    	"log"
    )
    
    func (dr *GoogleDriveService) ListFiles(authCode string, ctx context.Context) (map[string]string, error) {
    	tok, err := dr.driveServiceGoogleOA.UserOAuthToken(authCode)
    	if err != nil {
    		log.Panicln(err)
    		return nil, err
    	}
    	d, err := dr.driveServiceGoogleOA.NewGoogleDrive(ctx, tok)
    	if err != nil {
    		log.Panicln(err)
    		return nil, err
    	}
    
    	result, err := d.ListFiles(10)
    	if err != nil {
    		log.Panicln(err)
    		return nil, err
    	}
    
    	return result, nil
    }
    
  5. 接著我們到application.go,把Drive Service註冊進Application中,並且提供外部NewApplication的時候,放入實現google.GoogleOAuthI的oauth ,然後注入到GoogleDriveService中。

    // application.go
    package app
    
    import (
    	"context"
    
    	"github.com/line/line-bot-sdk-go/v7/linebot"
    
    	"github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/dynamodb"
    	"github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/google"
    	serviceDrive "github.com/onepiece010938/Line2GoogleDriveBot/internal/app/service/drive"
    	serviceSample "github.com/onepiece010938/Line2GoogleDriveBot/internal/app/service/sample"
    )
    
    type Application struct {
    	SampleService *serviceSample.SampleService
    	DriveService  *serviceDrive.GoogleDriveService
    	LineBotClient *linebot.Client
    }
    
    func NewApplication(ctx context.Context, dynamodb dynamodb.DynamodbI, oauth google.GoogleOAuthI, lineBotClient *linebot.Client) *Application {
    
    	app := &Application{
    		LineBotClient: lineBotClient,
    		SampleService: serviceSample.NewSampleService(ctx, serviceSample.SampleServiceParam{
    			SampleServiceDynamodb: dynamodb,
    		}),
    		DriveService: serviceDrive.NewGoogleDriveService(ctx, serviceDrive.GoogleDriveServiceParam{
    			DriveServiceGoogleOA: oauth,
    		}),
    	}
    	return app
    }
    
  6. 最後我們到server.go,在StartNgrokServer()NewGinLambda()NewApplication的地方補上NewGoogleOAuth並注入到Application中

    // StartNgrokServer()
    oauth := google.NewGoogleOAuth(os.Getenv("ClientID"), os.Getenv("ClientSecret"), os.Getenv("RedirectURL"))
    
    app := app.NewApplication(rootCtx, db, oauth, lineClient)
    
    // NewGinLambda()
    googleClientID, err := ssmsvc.FindParameter(rootCtx, ssmsvc.Client, "GOOGLE_CLIENT_ID")
    	if err != nil {
    		log.Println(err)
    	}
    	googleClientSecret, err := ssmsvc.FindParameter(rootCtx, ssmsvc.Client, "GOOGLE_CLIENT_SECRET")
    	if err != nil {
    		log.Println(err)
    	}
    	redirectURL, err := ssmsvc.FindParameter(rootCtx, ssmsvc.Client, "REDIRECT_URL")
    	if err != nil {
    		log.Println(err)
    	}
    	oauth := google.NewGoogleOAuth(googleClientID, googleClientSecret, redirectURL)
    
    	app := app.NewApplication(rootCtx, db, oauth, lineClientLambda)
    

那我們今天就先到這兒,明天見囉~


上一篇
Day17 GoogleDrive API 03
下一篇
Day19 調整SSM+補用testify測試
系列文
Golang LineBot X GoogleDrive:LINE有各種限制!? 那就丟上Drive吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言