Mercurial > code > home > repos > gcalendarwatch
view calsync/gcalclient/gcalclient.go @ 57:24f662799710
WIP incremental sync now runs
author | drewp@bigasterisk.com |
---|---|
date | Thu, 05 Sep 2024 15:03:05 -0700 |
parents | 635ff76f867c |
children | 6c7151126a0b |
line wrap: on
line source
package gcalclient /* Note: this module keeps gcal *paging* private (we fetch all the pages), but *sync* is public. At least, callers may receive a syncToken and have to pass it back in. */ import ( "context" "crypto/md5" "fmt" "log" "net/url" "strings" "time" "bigasterisk.com/go/gcalendarwatch/mongoclient" "google.golang.org/api/calendar/v3" ) const urlBase = "http://bigasterisk.com/calendar/" type GCalClient struct { ctx context.Context srv *calendar.Service } // Same as calendar.Event, but includes our urls type CalendarEvent struct { *calendar.Event CalendarUrl string EventUrl string } func MakeCalUrl(calId string) string { return urlBase + url.QueryEscape(calId) } func MakeEventUrl(calUrl string, evId string) string { return calUrl + "/" + url.QueryEscape(evId) } func New(ctx context.Context) (*GCalClient, error) { err, srv := newService(ctx) if err != nil { log.Fatalf("Unable to retrieve Calendar client: %v", err) } return &GCalClient{ctx, srv}, nil } func (gc *GCalClient) Close() { // todo: disconnect watches if possible } func (gc *GCalClient) AllCalendars() ([]*calendar.CalendarListEntry, error) { // todo: pagination list, err := gc.srv.CalendarList.List().MaxResults( /*maxResults*/ 100).Do() if err != nil { return nil, err } // do not submit debugFilterCals(list) return list.Items, nil } func debugFilterCals(list *calendar.CalendarList) { log.Println("filtering cal list") ret := make([]*calendar.CalendarListEntry, 0) for _, cal := range list.Items { if strings.Contains(cal.Id, "drewp") || strings.Contains(cal.Id, "east") { ret = append(ret, cal) } } list.Items = ret } func shortDebugHash(pageToken string) string { if pageToken == "" { return "(empty)" } return fmt.Sprintf("%x", md5.Sum([]byte(pageToken))) } func (gc *GCalClient) ListEventsInRange(cal mongoclient.MongoCal, t1, t2 time.Time) ( events []CalendarEvent, nextSyncToken string, err error) { mongoclient.LogWithCal(cal, "ListEventsInRange", t1, "to", t2) call := func(pageToken string) *calendar.EventsListCall { return rangedEventsCall(gc.srv, cal.GoogleId, t1, t2, pageToken) } events, nextSyncToken, err = readEventsPages(cal, call) if err != nil { return nil, "", err } return events, nextSyncToken, nil } func (gc *GCalClient) ListEventUpdates(cal mongoclient.MongoCal, syncToken string) ( events []CalendarEvent, nextSyncToken string, err error) { mongoclient.LogWithCal(cal, "ListEventUpdates", syncToken) call := func(pageToken string) *calendar.EventsListCall { return syncEventsCall(gc.srv, cal.GoogleId, syncToken, pageToken) } events, nextSyncToken, err = readEventsPages(cal, call) if err != nil { return nil, "", err } return events, nextSyncToken, nil } func readEventsPages(cal mongoclient.MongoCal, call func(string) *calendar.EventsListCall) ( events []CalendarEvent, syncToken string, err error) { events = make([]CalendarEvent, 0) err = nil pageToken := "" for { mongoclient.LogWithCal(cal, "getting another page", shortDebugHash(pageToken)) pageResult, err2 := call(pageToken).Do() if err2 != nil { log.Fatal(err2) return nil, "", err2 } // Placement is important! This must run even if the result set is empty. syncToken = pageResult.NextSyncToken mongoclient.LogWithCal(cal, "got page with", len(pageResult.Items), "events") if len(pageResult.Items) == 0 { break } for _, ev := range pageResult.Items { if ev.Status == "cancelled" { log.Fatal("todo") } events = append(events, CalendarEvent{ Event: ev, CalendarUrl: cal.Url, EventUrl: MakeEventUrl(cal.Url, ev.Id), }) } if pageResult.NextPageToken == "" { break } pageToken = pageResult.NextPageToken } mongoclient.LogWithCal(cal, "total events read: ", len(events), "with syncToken", syncToken) return events, syncToken, nil }