開始之前我們調整一下之前在adapter\google\oauth.go
的OAuthLoginURL()
,把原本寫死的第一個參數”state-token”改成外部代入的lineID,這個lineID可以在驗證完成後被帶到api/v1/ouath-login
,也就是在處理Redirect URL時跟OAuth token一起取出。
// internal\adapter\google\oauth.go
func (oa *GoogleOAuth) OAuthLoginURL(lineID string) (oauthURL string) {
oauthURL = oa.Config.AuthCodeURL(lineID, oauth2.AccessTypeOffline, oauth2.ApprovalForce) // oauth2.ApprovalForce
return oauthURL
}
adapter的interface也要記得更新
// internal\adapter\google\interface.go
type GoogleOAuthI interface {
OAuthLoginURL(lineID string) (oauthURL string)
UserOAuthToken(authCode string) (*oauth2.Token, error)
NewGoogleDrive(ctx context.Context, tok *oauth2.Token) (*GoogleDrive, error)
}
接著到Service的interface.go,把OAuthLoginURL
的地方換一下之後,為了要將OAuth Token存到Dynamodb中,我們得在drive Service中能調用Dynamodb,所以這邊做一個DriveServiceDynamodbI
,放入要使用的方法。
// internal\app\service\drive\interface.go
package drive
import (
"context"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
dynamodbAdapter "github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/dynamodb"
"github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/google"
"golang.org/x/oauth2"
)
type DriveServiceGoogleOAuthI interface {
OAuthLoginURL(lineID string) (oauthURL string)
UserOAuthToken(authCode string) (*oauth2.Token, error)
NewGoogleDrive(ctx context.Context, tok *oauth2.Token) (*google.GoogleDrive, error)
}
type DriveServiceDynamodbI interface {
GetGoogleOAuthToken(line_userid string) (dynamodbAdapter.GoogleOAuthToken, error)
CreateGoogleOAuthTable() (*types.TableDescription, error)
AddGoogleOAuthToken(tok dynamodbAdapter.GoogleOAuthToken) error
TxUpdateGoogleOAuthToken(tok dynamodbAdapter.GoogleOAuthToken) (*dynamodb.TransactWriteItemsOutput, error)
}
接著更新service.go,讓NewGoogleDriveService
的時候能把Dynamodb放進GoogleDriveService
中,讓GoogleDriveService
能直接調用到Dynamodb
// internal\app\service\drive\service.go
package drive
import "context"
type GoogleDriveService struct {
driveServiceGoogleOA DriveServiceGoogleOAuthI
driveServiceDynamodb DriveServiceDynamodbI
}
type GoogleDriveServiceParam struct {
DriveServiceGoogleOA DriveServiceGoogleOAuthI
DriveServiceDynamodb DriveServiceDynamodbI
}
func NewGoogleDriveService(_ context.Context, param GoogleDriveServiceParam) *GoogleDriveService {
return &GoogleDriveService{
driveServiceGoogleOA: param.DriveServiceGoogleOA,
driveServiceDynamodb: param.DriveServiceDynamodb,
}
}
最後到application.go,新增DriveServiceDynamodb: dynamodb
,注入到DriveService裡面
// internal\app\application.go
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,
DriveServiceDynamodb: dynamodb,
}),
}
return app
}
接著我們在drive service下面,建立一個login_service.go專門處理登入相關的操作。LoginURL
將傳入的lineID代入state,生成對應的Redirect URL;Login
會把從Redirect URL中解出的authCode和lineID,拿去取得OAuthToken
後製作成dynamodb定義的格式,最後調用AddGoogleOAuthToken
把製作好的token存進去。
// internal\app\service\drive\login_service.go
package drive
import (
"context"
"github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/dynamodb"
)
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.String(),
}
err = dr.driveServiceDynamodb.AddGoogleOAuthToken(dToken)
if err != nil {
return err
}
return nil
}
func (dr *GoogleDriveService) LoginURL(ctx context.Context, lineID string) string {
oauthURL := dr.driveServiceGoogleOA.OAuthLoginURL(lineID)
return oauthURL
}
最後我們回到router層把callback和OAuthLogin改成調用app中對應的service
// internal\router\api\v1\callback.go
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
}
...
// internal\router\api\v1\oauth.go
func OAuthLogin(app *app.Application) gin.HandlerFunc {
return func(c *gin.Context) {
authCode := c.Query("code")
lineID := c.Query("state") //get lineid
err := app.DriveService.Login(c.Request.Context(), lineID, authCode)
if err != nil {
log.Printf("Unable to retrieve DriveService.Login %v", err)
c.String(http.StatusInternalServerError, "Unable to retrieve DriveService.Login")
return
}
_, err = c.Writer.Write([]byte("<html><title>Login</title> <body> Authorized successfully, please close this window</body></html>"))
if err != nil {
log.Printf("Unable to write HTML: %v", err)
}
}
}
登入後打開Dynamodb admin,就可以看到成功存進去囉~
那今天我們就先寫到這邊,明天再見囉~