Mercurial > code > home > repos > gcalendarwatch
changeset 49:2991c1166852
start calsync in go. Calendar list seems to sync
author | drewp@bigasterisk.com |
---|---|
date | Mon, 19 Aug 2024 13:25:03 -0700 |
parents | f2dd88b9964c |
children | dade5bbd02e3 |
files | calsync/cal_sync.go calsync/convert/convert.go calsync/event_sync.go calsync/gcalclient/gcalclient.go calsync/gcalclient/service.go calsync/go.mod calsync/go.sum calsync/main.go calsync/mongoclient/mongoclient.go |
diffstat | 9 files changed, 798 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/cal_sync.go Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,29 @@ +package main + +import ( + "log" + + "bigasterisk.com/go/gcalendarwatch/convert" + "bigasterisk.com/go/gcalendarwatch/gcalclient" + "bigasterisk.com/go/gcalendarwatch/mongoclient" +) + +func updateMongoCalsToMatchGoogle(mc *mongoclient.MongoClient, gc *gcalclient.GCalClient) (err error) { + log.Println("updateMongoCalsToMatchGoogle") + + seen := make(map[string]bool) + + cals, err := gc.AllCalendars() + if err != nil { + return err + } + + for _, cal := range cals { + calUrl := gcalclient.MakeCalUrl(cal.Id) + log.Println("syncing", calUrl) + seen[calUrl] = true + mc.UpsertOneCal(convert.MongoCalFromGoogleCal(cal)) + } + + return mc.DeleteCalsNotInSet(seen) +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/convert/convert.go Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,68 @@ +package convert + +import ( + "net/url" + "time" + + "bigasterisk.com/go/gcalendarwatch/gcalclient" + "bigasterisk.com/go/gcalendarwatch/mongoclient" + "google.golang.org/api/calendar/v3" +) + +func MongoCalFromGoogleCal(cal *calendar.CalendarListEntry) mongoclient.MongoCal { + return mongoclient.MongoCal{ + Url: gcalclient.MakeCalUrl(cal.Id), + GoogleId: cal.Id, + Summary: cal.Summary, + Description: cal.Description, + } +} + +func MakeEventUrl(calUrl string, evId string) string { + return calUrl + "/" + url.QueryEscape(evId) +} + +func MakeEventUrl2(cal mongoclient.MongoCal, evId string) string { + return MakeEventUrl3(cal.GoogleId, evId) +} + +func MakeEventUrl3(googleCalId string, evId string) string { + return MakeEventUrl("http://bigasterisk.com/calendar/"+ + url.QueryEscape(googleCalId), evId) +} + +func MongoEventFromGoogleEvent2( + calUrl string, + ev *gcalclient.CalendarEvent, + now time.Time, +) mongoclient.MongoEvent { + return mongoEventFromGoogleEvent(calUrl, ev, now) + +} +func MongoEventFromGoogleEvent( + cal *calendar.CalendarListEntry, + ev *gcalclient.CalendarEvent, + now time.Time, +) mongoclient.MongoEvent { + return mongoEventFromGoogleEvent(MakeEventUrl3(cal.Id, ev.Id), ev, now) +} + +func mongoEventFromGoogleEvent(calUrl string, ev *gcalclient.CalendarEvent, now time.Time) mongoclient.MongoEvent { + return mongoclient.MongoEvent{ + Url: calUrl, + GoogleId: ev.Event.Id, + HtmlLink: ev.HtmlLink, + Title: ev.Summary, //? + // FeedId : ev.Event.FeedId, // or calid? + // FeedTitle : ev.Event.FeedTitle, // /or what + EndTimeUnspecified: ev.Event.EndTimeUnspecified, + // Start : ev.Start.DateTime, + // StartDate : ev.Event.StartDate, + // StartTime : ev.Event.StartTime, + // End : ev.Event.End, + // EndDate : ev.Event.EndDate, + // EndTime : ev.Event.EndTime, + LastUpdated: now, + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/event_sync.go Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,25 @@ +package main + +import ( + "log" + "time" + + "bigasterisk.com/go/gcalendarwatch/convert" + "bigasterisk.com/go/gcalendarwatch/gcalclient" + "bigasterisk.com/go/gcalendarwatch/mongoclient" +) + +func updateMongoEventsToMatchGoogle(mc *mongoclient.MongoClient, gc *gcalclient.GCalClient) error { + t := time.Now() + events, err := gc.FindEvents(t) + if err != nil { + return err + } + log.Println("Found", len(events), "events") + // todo: wipe mongo time period + log.Println("Upcoming events:") + for _, ev := range events { + mc.UpsertOneEvent(convert.MongoEventFromGoogleEvent2(ev.CalendarUrl, ev, t)) + } + return nil +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/gcalclient/gcalclient.go Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,81 @@ +package gcalclient + +import ( + "context" + "log" + "net/url" + "time" + + "google.golang.org/api/calendar/v3" +) + +type GCalClient struct { + ctx context.Context + srv *calendar.Service +} + +// Same as calendar.Event, but includes the source calendar url +type CalendarEvent struct { + *calendar.Event + CalendarUrl string +} + +func MakeCalUrl(calId string) string { + return "http://bigasterisk.com/calendar/" + url.QueryEscape(calId) +} + +func New(ctx context.Context) (*GCalClient, error) { + // If modifying these scopes, delete your previously saved token.json. + 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(100).Do() + if err != nil { + return nil, err + } + list.Items = list.Items[:4] + return list.Items, nil +} + +// FindEvents considers all calendars +func (gc *GCalClient) FindEvents(s time.Time) ([]*CalendarEvent, error) { + cals, err := gc.AllCalendars() + if err != nil { + return nil, err + } + + ret := make([]*CalendarEvent, 0) + for _, cal := range cals { + calUrl := MakeCalUrl(cal.Id) + log.Println(" getting events from ", calUrl) + list, err := gc.srv. + Events.List(cal.Id). + ShowDeleted(false). + SingleEvents(true). + TimeMin(s.Format(time.RFC3339)). + MaxResults(2). + OrderBy("startTime"). + Do() + if err != nil { + return nil, err + } + for _, event := range list.Items { + ev := &CalendarEvent{ + Event: event, + CalendarUrl: calUrl, + } + ret = append(ret, ev) + } + } + return ret, nil +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/gcalclient/service.go Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,86 @@ +package gcalclient + +import ( + "context" + "encoding/json" + "fmt" + "log" + "net/http" + "os" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + "google.golang.org/api/calendar/v3" + "google.golang.org/api/option" +) + +// Retrieve a token, saves the token, then returns the generated client. +func getClient(config *oauth2.Config) *http.Client { + // The file token.json stores the user's access and refresh tokens, and is + // created automatically when the authorization flow completes for the first + // time. + tokFile := "token.json" + tok, err := tokenFromFile(tokFile) + if err != nil { + tok = getTokenFromWeb(config) + saveToken(tokFile, tok) + } + return config.Client(context.Background(), tok) +} + +// Request a token from the web, then returns the retrieved token. +func getTokenFromWeb(config *oauth2.Config) *oauth2.Token { + authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline) + fmt.Printf("Go to the following link in your browser then type the "+ + "authorization code: \n%v\n", authURL) + + var authCode string + if _, err := fmt.Scan(&authCode); err != nil { + log.Fatalf("Unable to read authorization code: %v", err) + } + + tok, err := config.Exchange(context.TODO(), authCode) + if err != nil { + log.Fatalf("Unable to retrieve token from web: %v", err) + } + return tok +} + +// Retrieves a token from a local file. +func tokenFromFile(file string) (*oauth2.Token, error) { + f, err := os.Open(file) + if err != nil { + return nil, err + } + defer f.Close() + tok := &oauth2.Token{} + err = json.NewDecoder(f).Decode(tok) + return tok, err +} + +// Saves a token to a file path. +func saveToken(path string, token *oauth2.Token) { + fmt.Printf("Saving credential file to: %s\n", path) + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Fatalf("Unable to cache oauth token: %v", err) + } + defer f.Close() + json.NewEncoder(f).Encode(token) +} + +func newService(ctx context.Context) (error, *calendar.Service) { + b, err := os.ReadFile("./credentials.json") + if err != nil { + log.Fatalf("Unable to read client secret file: %v", err) + } + + config, err := google.ConfigFromJSON(b, calendar.CalendarReadonlyScope) + if err != nil { + log.Fatalf("Unable to parse client secret file to config: %v", err) + } + client := getClient(config) + + srv, err := calendar.NewService(ctx, option.WithHTTPClient(client)) + return err, srv +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/go.mod Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,41 @@ +module bigasterisk.com/go/gcalendarwatch + +go 1.21.1 + +require ( + cloud.google.com/go/auth v0.8.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/s2a-go v0.1.8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.13.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/klauspost/compress v1.13.6 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + go.mongodb.org/mongo-driver v1.16.1 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/api v0.192.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect + google.golang.org/grpc v1.64.1 // indirect + google.golang.org/protobuf v1.34.2 // indirect +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/go.sum Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,181 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= +cloud.google.com/go/auth v0.8.1 h1:QZW9FjC5lZzN864p13YxvAtGUlQ+KgRL+8Sg45Z6vxo= +cloud.google.com/go/auth v0.8.1/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc= +cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI= +cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= +github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8= +go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.192.0 h1:PljqpNAfZaaSpS+TnANfnNAXKdzHM/B9bKhwRlo7JP0= +google.golang.org/api v0.192.0/go.mod h1:9VcphjvAxPKLmSxVSzPlSRXy/5ARMEw5bf58WoVXafQ= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf h1:OqdXDEakZCVtDiZTjcxfwbHPCT11ycCEsTKesBVKvyY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/main.go Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,87 @@ +package main + +/* +let python continue to serve these: + Route('/', getRoot), + Route('/graph/calendar/upcoming', StaticGraph(agendaGraph)), + use https://github.com/cayleygraph/quad + Route('/graph/calendar/upcoming/events', GraphEvents(agendaGraph)), + use https://github.com/tmaxmax/go-sse + Route('/graph/calendar/countdown', StaticGraph(countdownGraph)), + Route('/graph/calendar/countdown/events', GraphEvents(countdownGraph)), + Route('/graph/currentEvents', StaticGraph(currentEventsGraph)), + Route('/graph/currentEvents/events', GraphEvents(currentEventsGraph)), +*/ + +import ( + "context" + "log" + "net/http" + + "bigasterisk.com/go/gcalendarwatch/gcalclient" + "bigasterisk.com/go/gcalendarwatch/mongoclient" + "github.com/gorilla/mux" +) + +func main() { + ctx := context.Background() + + log.SetFlags(log.LstdFlags | log.Lshortfile) + gc, err := gcalclient.New(ctx) + if err != nil { + log.Fatal(err) + } + defer gc.Close() + + mc, err := mongoclient.New(ctx) + if err != nil { + log.Fatal(err) + } + defer mc.Close() + + err = updateMongoCalsToMatchGoogle(mc, gc) + if err != nil { + log.Fatal(err) + } + + err = updateMongoEventsToMatchGoogle(mc, gc) + if err != nil { + log.Fatal(err) + } + + /* + ------------------ + connect to mongodb with these ops: + save cals list + get/set incremental token + add/edit/del events + collection.find({"startTime": {"$gte": t1, "$lt": t2}}).sort([("startTime", 1)]) + collection.find({"startTime": {"$lte": now}, "endTime": {"$gte": now}})) + + connect to https://github.com/googleapis/google-api-go-client/tree/main/calendar/v3 and: + get all my cals + + subscribe to events + + get cal event changes from incremental token + + get cal events in range, for initial fill? + + write add/edit/del changes to mongo + */ + + r := mux.NewRouter() + http.Handle("/", r) + + home := func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("calsync service for calendar updates")) + } + r.HandleFunc("/", home) + r.HandleFunc("/gcalendarwatch", home) + + notificationHandler := func(w http.ResponseWriter, r *http.Request) { + } + r.HandleFunc("/gcalendarwatch/notification", notificationHandler).Methods("POST") + log.Println(("serving /gcalendarwatch/notification on :8080")) + log.Fatal(http.ListenAndServe(":8080", nil)) +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calsync/mongoclient/mongoclient.go Mon Aug 19 13:25:03 2024 -0700 @@ -0,0 +1,200 @@ +package mongoclient + +import ( + "context" + "log" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type MongoClient struct { + ctx context.Context + client *mongo.Client + db *mongo.Database + calsCollection *mongo.Collection + eventsCollection *mongo.Collection +} + +// docs in calsCollection +type MongoCal struct { + Url string `bson:"_id"` // bigasterisk.com/... + GoogleId string `bson:"googleId"` + Summary string `bson:"summary"` + Description string `bson:"description"` +} + +// docs in eventsCollection +type MongoEvent struct { + // e.g. + // { + // "_id" : "http://bigasterisk.com/calendar/08s5m1_20140929T030000Z", + // "htmlLink" : "https://www.google.com/calendar/event?eid=MDhz", + // "title" : "meeting", + // "feedId" : "drewpca@gmail.com", + // "feedTitle" : "drewpca@gmail.com", + // "endTimeUnspecified" : false, + // "start" : "2014-09-28T20:00:00-07:00", + // "startDate" : "2014-09-28", + // "startTime" : ISODate("2014-09-29T03:00:00Z"), + // "end" : "2014-09-28T21:00:00-07:00", + // "endDate" : "2014-09-28", + // "endTime" : ISODate("2014-09-29T04:00:00Z") + // } + + Url string `bson:"_id"` + GoogleId string `bson:"googleId"` + HtmlLink string `bson:"htmlLink"` + Title string `bson:"title"` + FeedId string `bson:"feedId"` + FeedTitle string `bson:"feedTitle"` + EndTimeUnspecified bool `bson:"endTimeUnspecified"` + Start string `bson:"start"` + StartDate string `bson:"startDate"` + StartTime time.Time `bson:"startTime"` + End string `bson:"end"` + EndDate string `bson:"endDate"` + EndTime time.Time `bson:"endTime"` + LastUpdated time.Time `bson:"lastUpdated"` +} + +func New(ctx context.Context) (*MongoClient, error) { + log.Println("todo: using fixed ip") + mclient, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://10.43.98.221:27017")) + if err != nil { + return nil, err + } + db := mclient.Database("pim") + return &MongoClient{ + ctx, + mclient, + db, + db.Collection("test_gcalendar_cals"), + db.Collection("test_gcalendar")}, + nil +} + +func (c *MongoClient) Close() { + c.client.Disconnect(c.ctx) +} + +func (c *MongoClient) GetAllCals() ([]MongoCal, error) { + cur, err := c.calsCollection.Find(c.ctx, bson.D{}) + if err != nil { + return nil, err + } + defer cur.Close(c.ctx) + + var cals []MongoCal + for cur.Next(c.ctx) { + var cal MongoCal + if err := cur.Decode(&cal); err != nil { + return nil, err + } + cals = append(cals, cal) + } + return cals, nil +} + +func (c *MongoClient) GetOneCal(calId string) (MongoCal, error) { + res := c.calsCollection.FindOne(c.ctx, bson.M{"_id": calId}) + var cal MongoCal + err := res.Decode(&cal) + return cal, err +} + +func (c *MongoClient) UpsertOneCal(cal MongoCal) error { + filter := bson.M{"_id": cal.Url} + update := bson.M{ + "$set": cal, + // bson.M{ + // "googleId": cal.GoogleId, + // "summary": cal.Summary, + // "description": cal.Description, + // }, + } + _, err := c.calsCollection.UpdateOne(c.ctx, filter, update, options.Update().SetUpsert(true)) + if err != nil { + return err + } + return nil +} + +func (c *MongoClient) DeleteCalsNotInSet(urlsToKeep map[string]bool) error { + curs, err := c.calsCollection.Find(c.ctx, bson.M{}) + if err != nil { + return err + } + defer curs.Close(c.ctx) + + for curs.Next(c.ctx) { + var doc bson.M + err = curs.Decode(&doc) + if err != nil { + return err + } + calUrl, ok := doc["_id"].(string) + if !ok { + continue + } + if !urlsToKeep[calUrl] { + log.Println(" cleaning up", calUrl) + _, err = c.calsCollection.DeleteOne(c.ctx, bson.M{"_id": doc["_id"]}) + if err != nil { + return err + } + } + } + return curs.Err() +} + +func (c *MongoClient) UpsertOneEvent(ev MongoEvent) error { + filter := bson.M{"_id": ev.Url} + setFields := bson.M{ + "googleId": ev.GoogleId, + "lastUpdated": ev.LastUpdated, + "htmlLink": ev.HtmlLink, + "title": ev.Title, + } + update := bson.M{"$set": setFields} + _, err := c.eventsCollection.UpdateOne(c.ctx, filter, update, options.Update().SetUpsert(true)) + if err != nil { + return err + } + log.Println(" mongo upserted", ev.Url) + return nil +} + +// func (c *MongoClient) FindEventsIntersecting(t1, t2 time.Time) ([]MongoEvent, error) { +// cur, err := c.eventsCollection.Find(c.ctx, bson.M{ +// "end": bson.M{"$gte": t1}, +// "start": bson.M{"$lte": t2}, +// }) // todo: allday evs +// if err != nil { +// return nil, err +// } +// defer cur.Close(c.ctx) + +// var events []MongoEvent +// for cur.Next(c.ctx) { +// var ev MongoEvent +// if err := cur.Decode(&ev); err != nil { +// return nil, err +// } +// events = append(events, ev) +// } +// return events, nil +// } + +// func (c *MongoClient) CurrentEvents(now time.Time) ([]MongoEvent, error) { +// return c.FindEventsIntersecting(now, now) +// } +// func (c *MongoClient) UpdateOrInsertEvent(ev MongoEvent) error { +// return errors.New("todo") +// } + +// func (c *MongoClient) DeleteEvent(ev MongoEvent) error { +// return errors.New("todo") +// }