refactor(googleapi): share service constructor setup

This commit is contained in:
Peter Steinberger 2026-05-05 08:48:51 +01:00
parent 20afed7f4b
commit cd37734c99
No known key found for this signature in database
16 changed files with 62 additions and 133 deletions

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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) {

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}