Compare commits

...

2 Commits

Author SHA1 Message Date
Peter Steinberger
b5d9d263bf fix(calendar): land SA timezone lookup note (#325) (thanks @markwatson)
Some checks failed
ci / test (push) Has been cancelled
ci / worker (push) Has been cancelled
ci / windows (push) Has been cancelled
ci / darwin-cgo-build (push) Has been cancelled
2026-03-07 17:53:29 +00:00
Mark Watson
a66712806c fix(calendar): use Calendars.Get for TZ lookup to fix SA 404 2026-03-07 17:52:46 +00:00
7 changed files with 17 additions and 13 deletions

View File

@ -19,6 +19,7 @@
- Auth: keep Keep-only service-account fallback isolated to Keep commands so other Google services do not accidentally pick it up. (#414) — thanks @jgwesterlund.
- Contacts: send the required `copyMask` when deleting "other contacts", avoiding People API 400 errors. (#384) — thanks @rbansal42.
- Calendar: hide cancelled/deleted events from `calendar events` list output by explicitly setting `showDeleted=false`. (#362) — thanks @sharukh010.
- Calendar: use `Calendars.Get` for timezone lookups so service-account flows dont 404 on `calendarList/primary`. (#325) — thanks @markwatson.
- Gmail: fall back to `MimeType` charset hints when `Content-Type` headers are missing so GBK/GB2312 message bodies decode correctly. (#428) — thanks @WinnCook.
- Gmail: add a fetch delay in `watch serve` so History API reads don't race message indexing. (#397) — thanks @salmonumbrella.
- Gmail: allow Workspace-managed send-as aliases with empty verification status in `send` and `drafts create`. (#407) — thanks @salmonumbrella.

View File

@ -159,7 +159,7 @@ func TestCalendarCreateCmd_RecurringOffsetTimezoneFallback(t *testing.T) {
"id": "ev3",
})
return
case r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/users/me/calendarList/"):
case r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/calendars/") && !strings.Contains(r.URL.Path, "/events"):
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "primary",
@ -533,8 +533,8 @@ func TestCalendarUpdateCmd_SendUpdates(t *testing.T) {
},
})
return
case r.Method == http.MethodGet && strings.HasPrefix(path, "/users/me/calendarList/"):
// getCalendarLocation() fetches the calendar timezone via CalendarList.Get.
case r.Method == http.MethodGet && strings.HasPrefix(path, "/calendars/") && !strings.Contains(path, "/events"):
// getCalendarLocation() fetches the calendar timezone.
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "cal",

View File

@ -107,7 +107,7 @@ func TestCalendarDeleteCmd_SendUpdates(t *testing.T) {
},
})
return
case r.Method == http.MethodGet && strings.HasPrefix(path, "/users/me/calendarList/"):
case r.Method == http.MethodGet && strings.HasPrefix(path, "/calendars/") && !strings.Contains(path, "/events"):
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "cal",

View File

@ -20,7 +20,7 @@ func TestCalendarTimeCmd_JSON(t *testing.T) {
t.Cleanup(func() { newCalendarService = origNew })
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/users/me/calendarList/primary") && r.Method == http.MethodGet {
if r.URL.Path == "/calendars/primary" && r.Method == http.MethodGet {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "primary",
@ -81,7 +81,7 @@ func TestCalendarTimeCmd_TableOutput(t *testing.T) {
t.Cleanup(func() { newCalendarService = origNew })
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/users/me/calendarList/primary") && r.Method == http.MethodGet {
if r.URL.Path == "/calendars/primary" && r.Method == http.MethodGet {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "primary",
@ -276,7 +276,7 @@ func TestCalendarTimeCmd_CustomCalendar(t *testing.T) {
t.Cleanup(func() { newCalendarService = origNew })
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/users/me/calendarList/custom-cal-id@example.com") && r.Method == http.MethodGet {
if strings.Contains(r.URL.Path, "/calendars/custom-cal-id@example.com") && r.Method == http.MethodGet {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "custom-cal-id@example.com",

View File

@ -21,8 +21,8 @@ import (
// user's timezone from their primary calendar.
func withPrimaryCalendar(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Handle primary calendar list request for timezone
if strings.Contains(r.URL.Path, "/calendarList/primary") && r.Method == http.MethodGet {
// Handle primary calendar request for timezone
if r.URL.Path == "/calendars/primary" && r.Method == http.MethodGet {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "primary",

View File

@ -31,13 +31,15 @@ type TimeRange struct {
}
// getCalendarLocation fetches a calendar's timezone and returns it as a location.
// Uses Calendars.Get (not CalendarList.Get) so it works for service accounts
// whose "primary" calendar may not appear in their CalendarList.
func getCalendarLocation(ctx context.Context, svc *calendar.Service, calendarID string) (string, *time.Location, error) {
calendarID = strings.TrimSpace(calendarID)
if calendarID == "" {
return "", nil, fmt.Errorf("calendarId required")
}
cal, err := svc.CalendarList.Get(calendarID).Context(ctx).Do()
cal, err := svc.Calendars.Get(calendarID).Context(ctx).Do()
if err != nil {
return "", nil, fmt.Errorf("failed to get calendar %q: %w", calendarID, err)
}
@ -52,8 +54,10 @@ func getCalendarLocation(ctx context.Context, svc *calendar.Service, calendarID
}
// getUserTimezone fetches the timezone from the user's primary calendar.
// Uses Calendars.Get (not CalendarList.Get) so it works for service accounts
// whose "primary" calendar may not appear in their CalendarList.
func getUserTimezone(ctx context.Context, svc *calendar.Service) (*time.Location, error) {
cal, err := svc.CalendarList.Get("primary").Context(ctx).Do()
cal, err := svc.Calendars.Get("primary").Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("failed to get primary calendar: %w", err)
}

View File

@ -5,7 +5,6 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
@ -17,7 +16,7 @@ func newCalendarServiceWithTimezone(t *testing.T, tz string) *calendar.Service {
t.Helper()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/calendarList/primary") && r.Method == http.MethodGet {
if r.URL.Path == "/calendars/primary" && r.Method == http.MethodGet {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "primary",