From cd37734c99f9eb43e3487ba3529568153a512615 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 5 May 2026 08:48:51 +0100 Subject: [PATCH] refactor(googleapi): share service constructor setup --- internal/googleapi/admin_directory.go | 9 +----- internal/googleapi/appscript.go | 9 +----- internal/googleapi/calendar.go | 9 +----- internal/googleapi/chat.go | 9 +----- internal/googleapi/classroom.go | 9 +----- internal/googleapi/client.go | 44 +++++++++++++++++++++++++++ internal/googleapi/cloudidentity.go | 9 +----- internal/googleapi/docs.go | 9 +----- internal/googleapi/drive.go | 9 +----- internal/googleapi/forms.go | 9 +----- internal/googleapi/gmail.go | 9 +----- internal/googleapi/keep.go | 8 +---- internal/googleapi/people.go | 25 ++------------- internal/googleapi/sheets.go | 10 ++---- internal/googleapi/slides.go | 9 +----- internal/googleapi/tasks.go | 9 +----- 16 files changed, 62 insertions(+), 133 deletions(-) diff --git a/internal/googleapi/admin_directory.go b/internal/googleapi/admin_directory.go index 624e564..107118e 100644 --- a/internal/googleapi/admin_directory.go +++ b/internal/googleapi/admin_directory.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" admin "google.golang.org/api/admin/directory/v1" @@ -12,11 +11,5 @@ import ( // NewAdminDirectory creates an Admin SDK Directory service for user and group management. // This API requires domain-wide delegation with a service account to manage Workspace users. func NewAdminDirectory(ctx context.Context, email string) (*admin.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceAdmin, email); err != nil { - return nil, fmt.Errorf("admin directory options: %w", err) - } else if svc, err := admin.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create admin directory service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceAdmin, "admin directory", admin.NewService) } diff --git a/internal/googleapi/appscript.go b/internal/googleapi/appscript.go index 65c9921..dd3f215 100644 --- a/internal/googleapi/appscript.go +++ b/internal/googleapi/appscript.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/script/v1" @@ -10,11 +9,5 @@ import ( ) func NewAppScript(ctx context.Context, email string) (*script.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceAppScript, email); err != nil { - return nil, fmt.Errorf("appscript options: %w", err) - } else if svc, err := script.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create appscript service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceAppScript, "appscript", script.NewService) } diff --git a/internal/googleapi/calendar.go b/internal/googleapi/calendar.go index 12a4c47..a3d41ec 100644 --- a/internal/googleapi/calendar.go +++ b/internal/googleapi/calendar.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/calendar/v3" @@ -10,11 +9,5 @@ import ( ) func NewCalendar(ctx context.Context, email string) (*calendar.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceCalendar, email); err != nil { - return nil, fmt.Errorf("calendar options: %w", err) - } else if svc, err := calendar.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create calendar service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceCalendar, "calendar", calendar.NewService) } diff --git a/internal/googleapi/chat.go b/internal/googleapi/chat.go index fa58406..34d8e77 100644 --- a/internal/googleapi/chat.go +++ b/internal/googleapi/chat.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/chat/v1" ) @@ -17,11 +16,5 @@ const ( ) func NewChat(ctx context.Context, email string) (*chat.Service, error) { - if opts, err := optionsForAccountScopes(ctx, "chat", email, []string{scopeChatSpaces, scopeChatMessages, scopeChatMemberships, scopeChatReadStateRO, scopeChatReactionsCreate, scopeChatReactionsRO}); err != nil { - return nil, fmt.Errorf("chat options: %w", err) - } else if svc, err := chat.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create chat service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForScopes(ctx, email, "chat", "chat", []string{scopeChatSpaces, scopeChatMessages, scopeChatMemberships, scopeChatReadStateRO, scopeChatReactionsCreate, scopeChatReactionsRO}, chat.NewService) } diff --git a/internal/googleapi/classroom.go b/internal/googleapi/classroom.go index 35fd180..8a9f5fb 100644 --- a/internal/googleapi/classroom.go +++ b/internal/googleapi/classroom.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/classroom/v1" @@ -10,11 +9,5 @@ import ( ) func NewClassroom(ctx context.Context, email string) (*classroom.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceClassroom, email); err != nil { - return nil, fmt.Errorf("classroom options: %w", err) - } else if svc, err := classroom.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create classroom service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceClassroom, "classroom", classroom.NewService) } diff --git a/internal/googleapi/client.go b/internal/googleapi/client.go index c5ce082..dea048c 100644 --- a/internal/googleapi/client.go +++ b/internal/googleapi/client.go @@ -41,6 +41,50 @@ func optionsForAccount(ctx context.Context, service googleauth.Service, email st return optionsForAccountScopes(ctx, string(service), email, scopes) } +type googleServiceFactory[T any] func(context.Context, ...option.ClientOption) (*T, error) + +func newGoogleServiceForAccount[T any]( + ctx context.Context, + email string, + service googleauth.Service, + label string, + factory googleServiceFactory[T], +) (*T, error) { + opts, err := optionsForAccount(ctx, service, email) + if err != nil { + return nil, fmt.Errorf("%s options: %w", label, err) + } + return newGoogleService(ctx, label, opts, factory) +} + +func newGoogleServiceForScopes[T any]( + ctx context.Context, + email string, + serviceLabel string, + errorLabel string, + scopes []string, + factory googleServiceFactory[T], +) (*T, error) { + opts, err := optionsForAccountScopes(ctx, serviceLabel, email, scopes) + if err != nil { + return nil, fmt.Errorf("%s options: %w", errorLabel, err) + } + return newGoogleService(ctx, errorLabel, opts, factory) +} + +func newGoogleService[T any]( + ctx context.Context, + label string, + opts []option.ClientOption, + factory googleServiceFactory[T], +) (*T, error) { + svc, err := factory(ctx, opts...) + if err != nil { + return nil, fmt.Errorf("create %s service: %w", label, err) + } + return svc, nil +} + // IsADCMode reports whether Application Default Credentials mode is active. // When GOG_AUTH_MODE=adc, the CLI authenticates using the ambient credentials // (e.g. GKE Workload Identity, GOOGLE_APPLICATION_CREDENTIALS, or gcloud ADC) diff --git a/internal/googleapi/cloudidentity.go b/internal/googleapi/cloudidentity.go index d982f01..8cc6a39 100644 --- a/internal/googleapi/cloudidentity.go +++ b/internal/googleapi/cloudidentity.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/cloudidentity/v1" ) @@ -14,11 +13,5 @@ const ( // NewCloudIdentityGroups creates a Cloud Identity service for reading groups. // This API allows non-admin users to list groups they belong to and view group members. func NewCloudIdentityGroups(ctx context.Context, email string) (*cloudidentity.Service, error) { - if opts, err := optionsForAccountScopes(ctx, "cloudidentity", email, []string{scopeCloudIdentityGroupsRO}); err != nil { - return nil, fmt.Errorf("cloudidentity options: %w", err) - } else if svc, err := cloudidentity.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create cloudidentity service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForScopes(ctx, email, "cloudidentity", "cloudidentity", []string{scopeCloudIdentityGroupsRO}, cloudidentity.NewService) } diff --git a/internal/googleapi/docs.go b/internal/googleapi/docs.go index 740cb8a..ddec4eb 100644 --- a/internal/googleapi/docs.go +++ b/internal/googleapi/docs.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/docs/v1" @@ -10,11 +9,5 @@ import ( ) func NewDocs(ctx context.Context, email string) (*docs.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceDocs, email); err != nil { - return nil, fmt.Errorf("docs options: %w", err) - } else if svc, err := docs.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create docs service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceDocs, "docs", docs.NewService) } diff --git a/internal/googleapi/drive.go b/internal/googleapi/drive.go index 5e1bde7..f2c2574 100644 --- a/internal/googleapi/drive.go +++ b/internal/googleapi/drive.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/drive/v3" @@ -10,11 +9,5 @@ import ( ) func NewDrive(ctx context.Context, email string) (*drive.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceDrive, email); err != nil { - return nil, fmt.Errorf("drive options: %w", err) - } else if svc, err := drive.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create drive service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceDrive, "drive", drive.NewService) } diff --git a/internal/googleapi/forms.go b/internal/googleapi/forms.go index 05a6af8..b3df7ba 100644 --- a/internal/googleapi/forms.go +++ b/internal/googleapi/forms.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/forms/v1" @@ -10,11 +9,5 @@ import ( ) func NewForms(ctx context.Context, email string) (*forms.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceForms, email); err != nil { - return nil, fmt.Errorf("forms options: %w", err) - } else if svc, err := forms.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create forms service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceForms, "forms", forms.NewService) } diff --git a/internal/googleapi/gmail.go b/internal/googleapi/gmail.go index 2f1da34..224659f 100644 --- a/internal/googleapi/gmail.go +++ b/internal/googleapi/gmail.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/gmail/v1" @@ -10,11 +9,5 @@ import ( ) func NewGmail(ctx context.Context, email string) (*gmail.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceGmail, email); err != nil { - return nil, fmt.Errorf("gmail options: %w", err) - } else if svc, err := gmail.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create gmail service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceGmail, "gmail", gmail.NewService) } diff --git a/internal/googleapi/keep.go b/internal/googleapi/keep.go index 432bd1a..a36c8c3 100644 --- a/internal/googleapi/keep.go +++ b/internal/googleapi/keep.go @@ -13,13 +13,7 @@ import ( ) func NewKeep(ctx context.Context, email string) (*keep.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceKeep, email); err != nil { - return nil, fmt.Errorf("keep options: %w", err) - } else if svc, err := keep.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create keep service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceKeep, "keep", keep.NewService) } func NewKeepWithServiceAccount(ctx context.Context, serviceAccountPath, impersonateEmail string) (*keep.Service, error) { diff --git a/internal/googleapi/people.go b/internal/googleapi/people.go index 5670f3d..0d11bb1 100644 --- a/internal/googleapi/people.go +++ b/internal/googleapi/people.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/people/v1" ) @@ -14,31 +13,13 @@ const ( ) func NewPeopleContacts(ctx context.Context, email string) (*people.Service, error) { - if opts, err := optionsForAccountScopes(ctx, "contacts", email, []string{scopeContactsWrite}); err != nil { - return nil, fmt.Errorf("contacts options: %w", err) - } else if svc, err := people.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create contacts service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForScopes(ctx, email, "contacts", "contacts", []string{scopeContactsWrite}, people.NewService) } func NewPeopleOtherContacts(ctx context.Context, email string) (*people.Service, error) { - if opts, err := optionsForAccountScopes(ctx, "contacts", email, []string{scopeContactsOtherRO}); err != nil { - return nil, fmt.Errorf("contacts options: %w", err) - } else if svc, err := people.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create contacts service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForScopes(ctx, email, "contacts", "contacts", []string{scopeContactsOtherRO}, people.NewService) } func NewPeopleDirectory(ctx context.Context, email string) (*people.Service, error) { - if opts, err := optionsForAccountScopes(ctx, "contacts", email, []string{scopeDirectoryRO}); err != nil { - return nil, fmt.Errorf("contacts options: %w", err) - } else if svc, err := people.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create contacts service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForScopes(ctx, email, "contacts", "contacts", []string{scopeDirectoryRO}, people.NewService) } diff --git a/internal/googleapi/sheets.go b/internal/googleapi/sheets.go index f7ade54..5eaa476 100644 --- a/internal/googleapi/sheets.go +++ b/internal/googleapi/sheets.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "log/slog" "google.golang.org/api/sheets/v4" @@ -13,15 +12,10 @@ import ( func NewSheets(ctx context.Context, email string) (*sheets.Service, error) { slog.Debug("creating sheets service", "email", email) - opts, err := optionsForAccount(ctx, googleauth.ServiceSheets, email) - if err != nil { - return nil, fmt.Errorf("sheets options: %w", err) - } - - svc, err := sheets.NewService(ctx, opts...) + svc, err := newGoogleServiceForAccount(ctx, email, googleauth.ServiceSheets, "sheets", sheets.NewService) if err != nil { slog.Error("failed to create sheets service", "email", email, "error", err) - return nil, fmt.Errorf("create sheets service: %w", err) + return nil, err } slog.Debug("sheets service created successfully", "email", email) diff --git a/internal/googleapi/slides.go b/internal/googleapi/slides.go index f42f0fd..639520c 100644 --- a/internal/googleapi/slides.go +++ b/internal/googleapi/slides.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/slides/v1" @@ -10,11 +9,5 @@ import ( ) func NewSlides(ctx context.Context, email string) (*slides.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceSlides, email); err != nil { - return nil, fmt.Errorf("slides options: %w", err) - } else if svc, err := slides.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create slides service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceSlides, "slides", slides.NewService) } diff --git a/internal/googleapi/tasks.go b/internal/googleapi/tasks.go index 1c3e64d..a150947 100644 --- a/internal/googleapi/tasks.go +++ b/internal/googleapi/tasks.go @@ -2,7 +2,6 @@ package googleapi import ( "context" - "fmt" "google.golang.org/api/tasks/v1" @@ -10,11 +9,5 @@ import ( ) func NewTasks(ctx context.Context, email string) (*tasks.Service, error) { - if opts, err := optionsForAccount(ctx, googleauth.ServiceTasks, email); err != nil { - return nil, fmt.Errorf("tasks options: %w", err) - } else if svc, err := tasks.NewService(ctx, opts...); err != nil { - return nil, fmt.Errorf("create tasks service: %w", err) - } else { - return svc, nil - } + return newGoogleServiceForAccount(ctx, email, googleauth.ServiceTasks, "tasks", tasks.NewService) }