This commit is contained in:
Erik Winter 2024-09-11 07:50:27 +02:00
parent 423effa1b4
commit 5ddeb5bb5a
7 changed files with 238 additions and 9 deletions

87
cal/client.go Normal file
View File

@ -0,0 +1,87 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
type Client struct {
baseURL string
apiKey string
c *http.Client
}
func NewClient(url, apiKey string) *Client {
return &Client{
baseURL: url,
apiKey: apiKey,
c: &http.Client{
Timeout: 10 * time.Second,
},
}
}
func (c *Client) Update(items []Item) error {
body, err := json.Marshal(items)
if err != nil {
return fmt.Errorf("could not marhal body: %v", err)
}
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/sync", c.baseURL), bytes.NewReader(body))
if err != nil {
return fmt.Errorf("could not create request: %v", err)
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey))
res, err := c.c.Do(req)
if err != nil {
return fmt.Errorf("could not make request: %v", err)
}
if res.StatusCode != http.StatusNoContent {
return fmt.Errorf("server returned status %d", res.StatusCode)
}
return nil
}
func (c *Client) Updated(ks []Kind, ts time.Time) ([]Item, error) {
ksStr := make([]string, 0, len(ks))
for _, k := range ks {
ksStr = append(ksStr, string(k))
}
u := fmt.Sprintf("%s/sync?ks=%s", c.baseURL, strings.Join(ksStr, ","))
if !ts.IsZero() {
u = fmt.Sprintf("%s&ts=", url.QueryEscape(ts.Format(time.RFC3339)))
}
req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil {
return nil, fmt.Errorf("could not create request: %v", err)
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey))
res, err := c.c.Do(req)
if err != nil {
return nil, fmt.Errorf("could not get response: %v", err)
}
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server returned status %d", res.StatusCode)
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("could not read response body: %v", err)
}
var items []Item
if err := json.Unmarshal(body, &items); err != nil {
return nil, fmt.Errorf("could not unmarshal response body: %v", err)
}
return items, nil
}

25
cal/go.mod Normal file
View File

@ -0,0 +1,25 @@
module code.ewintr.nl/planner/cal
go 1.21.5
require (
code.ewintr.nl/planner/sync v0.0.0-20240912051930-423effa1b447
github.com/google/uuid v1.6.0
)
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/sys v0.22.0 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.33.0 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)
replace code.ewintr.nl/planner/sync => ../sync

37
cal/go.sum Normal file
View File

@ -0,0 +1,37 @@
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
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/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sys v0.6.0/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/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/sqlite v1.33.0 h1:WWkA/T2G17okiLGgKAj4/RMIvgyMT19yQ038160IeYk=
modernc.org/sqlite v1.33.0/go.mod h1:9uQ9hF/pCZoYZK73D/ud5Z7cIRIILSZI8NdIemVMTX8=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

39
cal/main.go Normal file
View File

@ -0,0 +1,39 @@
package main
import (
"fmt"
"os"
"time"
)
func main() {
fmt.Println("cal")
c := NewClient("http://localhost:8092", "testKey")
items, err := c.Updated([]Kind{KindEvent}, time.Time{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("%+v\n", items)
i := Item{
ID: "id-1",
Kind: KindEvent,
Updated: time.Now(),
Body: "body",
}
if err := c.Update([]Item{i}); err != nil {
fmt.Println(err)
os.Exit(1)
}
items, err = c.Updated([]Kind{KindEvent}, time.Time{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("%+v\n", items)
}

35
cal/planner.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"time"
"github.com/google/uuid"
)
type Kind string
const (
KindTask Kind = "task"
KindEvent Kind = "event"
)
var (
KnownKinds = []Kind{KindTask, KindEvent}
)
type Item struct {
ID string `json:"id"`
Kind Kind `json:"kind"`
Updated time.Time `json:"updated"`
Deleted bool `json:"deleted"`
Body string `json:"body"`
}
func NewItem(k Kind, body string) Item {
return Item{
ID: uuid.New().String(),
Kind: k,
Updated: time.Now(),
Body: body,
}
}

View File

@ -1,4 +1,4 @@
module code.ewintr.nl/planner
module code.ewintr.nl/planner/sync
go 1.21.5

View File

@ -4,6 +4,7 @@ import (
"database/sql"
"errors"
"fmt"
"strings"
"time"
_ "modernc.org/sqlite"
@ -56,12 +57,11 @@ VALUES
(?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE
SET
kind=?,
updated=?,
deleted=?,
body=?`,
item.ID, item.Kind, item.Updated.Format(timestampFormat), item.Deleted, item.Body,
item.Kind, item.Updated.Format(timestampFormat), item.Deleted, item.Body); err != nil {
kind=excluded.kind,
updated=excluded.updated,
deleted=excluded.deleted,
body=excluded.body`,
item.ID, item.Kind, item.Updated.Format(timestampFormat), item.Deleted, item.Body); err != nil {
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
return nil
@ -80,8 +80,14 @@ WHERE updated > ?`
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
} else {
query = fmt.Sprintf("%s AND kind in (?)", query)
rows, err = s.db.Query(query, t.Format(timestampFormat), ks)
args := []any{t.Format(timestampFormat)}
ph := make([]string, 0, len(ks))
for _, k := range ks {
args = append(args, string(k))
ph = append(ph, "?")
}
query = fmt.Sprintf("%s AND kind in (%s)", query, strings.Join(ph, ","))
rows, err = s.db.Query(query, args...)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}