single module
This commit is contained in:
parent
225c0db847
commit
671deb355a
4
Makefile
4
Makefile
|
@ -1,6 +1,4 @@
|
||||||
|
|
||||||
sync-run:
|
sync-run:
|
||||||
cd sync && PLANNER_DB_PATH=test.db PLANNER_PORT=8092 PLANNER_API_KEY=testKey go run .
|
cd sync/service && PLANNER_DB_PATH=test.db PLANNER_PORT=8092 PLANNER_API_KEY=testKey go run .
|
||||||
|
|
||||||
sync-build-and-push:
|
|
||||||
cd sync && docker build . -t codeberg.org/ewintr/syncservice && docker push codeberg.org/ewintr/syncservice
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
module go-mod.ewintr.nl/planner/cal
|
|
||||||
|
|
||||||
go 1.21.5
|
|
||||||
|
|
||||||
require github.com/google/uuid v1.6.0
|
|
12
cal/main.go
12
cal/main.go
|
@ -6,14 +6,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/sync/client"
|
"go-mod.ewintr.nl/planner/sync/client"
|
||||||
"go-mod.ewintr.nl/planner/sync/planner"
|
"go-mod.ewintr.nl/planner/sync/item"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("cal")
|
fmt.Println("cal")
|
||||||
|
|
||||||
c := client.NewClient("http://localhost:8092", "testKey")
|
c := client.NewClient("http://localhost:8092", "testKey")
|
||||||
items, err := c.Updated([]planner.Kind{planner.KindEvent}, time.Time{})
|
items, err := c.Updated([]item.Kind{item.KindEvent}, time.Time{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -21,18 +21,18 @@ func main() {
|
||||||
|
|
||||||
fmt.Printf("%+v\n", items)
|
fmt.Printf("%+v\n", items)
|
||||||
|
|
||||||
i := planner.Item{
|
i := item.Item{
|
||||||
ID: "id-1",
|
ID: "id-1",
|
||||||
Kind: planner.KindEvent,
|
Kind: item.KindEvent,
|
||||||
Updated: time.Now(),
|
Updated: time.Now(),
|
||||||
Body: "body",
|
Body: "body",
|
||||||
}
|
}
|
||||||
if err := c.Update([]planner.Item{i}); err != nil {
|
if err := c.Update([]item.Item{i}); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
items, err = c.Updated([]planner.Kind{planner.KindEvent}, time.Time{})
|
items, err = c.Updated([]item.Kind{item.KindEvent}, time.Time{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package service
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
module go-mod.ewintr.nl/planner
|
||||||
|
|
||||||
|
go 1.21.5
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // 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/libc v1.55.3 // indirect
|
||||||
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
|
modernc.org/memory v1.8.0 // indirect
|
||||||
|
modernc.org/sqlite v1.33.1 // indirect
|
||||||
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
|
modernc.org/token v1.1.0 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,29 @@
|
||||||
|
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/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/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/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=
|
||||||
|
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/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
|
||||||
|
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
|
||||||
|
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.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM=
|
||||||
|
modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
|
||||||
|
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=
|
|
@ -1,5 +0,0 @@
|
||||||
module go-mod.ewintr.nl/planner/sync-client
|
|
||||||
|
|
||||||
go 1.21.5
|
|
||||||
|
|
||||||
require github.com/google/uuid v1.6.0
|
|
|
@ -1,2 +0,0 @@
|
||||||
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=
|
|
|
@ -1,3 +0,0 @@
|
||||||
module go-mod.ewintr.nl/planner/sync-service
|
|
||||||
|
|
||||||
go 1.21.5
|
|
|
@ -1,15 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrNotFound = errors.New("not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Syncer interface {
|
|
||||||
Update(item Item) error
|
|
||||||
Updated(kind []Kind, t time.Time) ([]Item, error)
|
|
||||||
}
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/sync/item"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
@ -27,7 +29,7 @@ func NewClient(url, apiKey string) *Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Update(items []Item) error {
|
func (c *Client) Update(items []item.Item) error {
|
||||||
body, err := json.Marshal(items)
|
body, err := json.Marshal(items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not marhal body: %v", err)
|
return fmt.Errorf("could not marhal body: %v", err)
|
||||||
|
@ -49,7 +51,7 @@ func (c *Client) Update(items []Item) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Updated(ks []Kind, ts time.Time) ([]Item, error) {
|
func (c *Client) Updated(ks []item.Kind, ts time.Time) ([]item.Item, error) {
|
||||||
ksStr := make([]string, 0, len(ks))
|
ksStr := make([]string, 0, len(ks))
|
||||||
for _, k := range ks {
|
for _, k := range ks {
|
||||||
ksStr = append(ksStr, string(k))
|
ksStr = append(ksStr, string(k))
|
||||||
|
@ -78,7 +80,7 @@ func (c *Client) Updated(ks []Kind, ts time.Time) ([]Item, error) {
|
||||||
return nil, fmt.Errorf("could not read response body: %v", err)
|
return nil, fmt.Errorf("could not read response body: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var items []Item
|
var items []item.Item
|
||||||
if err := json.Unmarshal(body, &items); err != nil {
|
if err := json.Unmarshal(body, &items); err != nil {
|
||||||
return nil, fmt.Errorf("could not unmarshal response body: %v", err)
|
return nil, fmt.Errorf("could not unmarshal response body: %v", err)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package client
|
package item
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/sync/item"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
@ -67,17 +69,17 @@ func (s *Server) SyncGet(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ks := make([]Kind, 0)
|
ks := make([]item.Kind, 0)
|
||||||
ksStr := r.URL.Query().Get("ks")
|
ksStr := r.URL.Query().Get("ks")
|
||||||
if ksStr != "" {
|
if ksStr != "" {
|
||||||
for _, k := range strings.Split(ksStr, ",") {
|
for _, k := range strings.Split(ksStr, ",") {
|
||||||
if !slices.Contains(KnownKinds, Kind(k)) {
|
if !slices.Contains(item.KnownKinds, item.Kind(k)) {
|
||||||
msg := fmt.Sprintf("unknown kind: %s", k)
|
msg := fmt.Sprintf("unknown kind: %s", k)
|
||||||
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
||||||
s.logger.Info(msg)
|
s.logger.Info(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ks = append(ks, Kind(k))
|
ks = append(ks, item.Kind(k))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +113,7 @@ func (s *Server) SyncPost(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
|
|
||||||
var items []Item
|
var items []item.Item
|
||||||
if err := json.Unmarshal(body, &items); err != nil {
|
if err := json.Unmarshal(body, &items); err != nil {
|
||||||
msg := err.Error()
|
msg := err.Error()
|
||||||
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
||||||
|
@ -119,33 +121,33 @@ func (s *Server) SyncPost(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range items {
|
for _, it := range items {
|
||||||
if item.ID == "" {
|
if it.ID == "" {
|
||||||
msg := "item without an id"
|
msg := "item without an id"
|
||||||
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
||||||
s.logger.Info(msg)
|
s.logger.Info(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if item.Kind == "" {
|
if it.Kind == "" {
|
||||||
msg := fmt.Sprintf("item %s does not have a kind", item.ID)
|
msg := fmt.Sprintf("item %s does not have a kind", it.ID)
|
||||||
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
||||||
s.logger.Info(msg)
|
s.logger.Info(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !slices.Contains(KnownKinds, item.Kind) {
|
if !slices.Contains(item.KnownKinds, it.Kind) {
|
||||||
msg := fmt.Sprintf("items %s does not have a know kind", item.ID)
|
msg := fmt.Sprintf("items %s does not have a know kind", it.ID)
|
||||||
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
http.Error(w, fmtError(msg), http.StatusBadRequest)
|
||||||
s.logger.Info(msg)
|
s.logger.Info(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if item.Body == "" {
|
if it.Body == "" {
|
||||||
msg := fmt.Sprintf(`{"error":"item %s does not have a body"}`, item.ID)
|
msg := fmt.Sprintf(`{"error":"item %s does not have a body"}`, it.ID)
|
||||||
http.Error(w, msg, http.StatusBadRequest)
|
http.Error(w, msg, http.StatusBadRequest)
|
||||||
s.logger.Info(msg)
|
s.logger.Info(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.Updated = time.Now()
|
it.Updated = time.Now()
|
||||||
if err := s.syncer.Update(item); err != nil {
|
if err := s.syncer.Update(it); err != nil {
|
||||||
msg := err.Error()
|
msg := err.Error()
|
||||||
http.Error(w, fmtError(msg), http.StatusInternalServerError)
|
http.Error(w, fmtError(msg), http.StatusInternalServerError)
|
||||||
s.logger.Error(msg)
|
s.logger.Error(msg)
|
|
@ -14,6 +14,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/sync/item"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServerServeHTTP(t *testing.T) {
|
func TestServerServeHTTP(t *testing.T) {
|
||||||
|
@ -56,10 +58,10 @@ func TestSyncGet(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
mem := NewMemory()
|
mem := NewMemory()
|
||||||
|
|
||||||
items := []Item{
|
items := []item.Item{
|
||||||
{ID: "id-0", Kind: KindEvent, Updated: now.Add(-10 * time.Minute)},
|
{ID: "id-0", Kind: item.KindEvent, Updated: now.Add(-10 * time.Minute)},
|
||||||
{ID: "id-1", Kind: KindEvent, Updated: now.Add(-5 * time.Minute)},
|
{ID: "id-1", Kind: item.KindEvent, Updated: now.Add(-5 * time.Minute)},
|
||||||
{ID: "id-2", Kind: KindTask, Updated: now.Add(time.Minute)},
|
{ID: "id-2", Kind: item.KindTask, Updated: now.Add(time.Minute)},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
|
@ -76,7 +78,7 @@ func TestSyncGet(t *testing.T) {
|
||||||
ts time.Time
|
ts time.Time
|
||||||
ks []string
|
ks []string
|
||||||
expStatus int
|
expStatus int
|
||||||
expItems []Item
|
expItems []item.Item
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "full",
|
name: "full",
|
||||||
|
@ -87,13 +89,13 @@ func TestSyncGet(t *testing.T) {
|
||||||
name: "new",
|
name: "new",
|
||||||
ts: now.Add(-6 * time.Minute),
|
ts: now.Add(-6 * time.Minute),
|
||||||
expStatus: http.StatusOK,
|
expStatus: http.StatusOK,
|
||||||
expItems: []Item{items[1], items[2]},
|
expItems: []item.Item{items[1], items[2]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "kind",
|
name: "kind",
|
||||||
ks: []string{string(KindTask)},
|
ks: []string{string(item.KindTask)},
|
||||||
expStatus: http.StatusOK,
|
expStatus: http.StatusOK,
|
||||||
expItems: []Item{items[2]},
|
expItems: []item.Item{items[2]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unknown kind",
|
name: "unknown kind",
|
||||||
|
@ -121,7 +123,7 @@ func TestSyncGet(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var actItems []Item
|
var actItems []item.Item
|
||||||
actBody, err := io.ReadAll(res.Result().Body)
|
actBody, err := io.ReadAll(res.Result().Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("exp nil, got %v", err)
|
t.Errorf("exp nil, got %v", err)
|
||||||
|
@ -155,7 +157,7 @@ func TestSyncPost(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
reqBody []byte
|
reqBody []byte
|
||||||
expStatus int
|
expStatus int
|
||||||
expItems []Item
|
expItems []item.Item
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
|
@ -180,9 +182,9 @@ func TestSyncPost(t *testing.T) {
|
||||||
{"id":"id-2","kind":"event","updated":"2024-09-06T08:12:00Z","deleted":false,"body":"item2"}
|
{"id":"id-2","kind":"event","updated":"2024-09-06T08:12:00Z","deleted":false,"body":"item2"}
|
||||||
]`),
|
]`),
|
||||||
expStatus: http.StatusNoContent,
|
expStatus: http.StatusNoContent,
|
||||||
expItems: []Item{
|
expItems: []item.Item{
|
||||||
{ID: "id-1", Kind: KindEvent, Updated: time.Date(2024, 9, 6, 8, 0, 0, 0, time.UTC)},
|
{ID: "id-1", Kind: item.KindEvent, Updated: time.Date(2024, 9, 6, 8, 0, 0, 0, time.UTC)},
|
||||||
{ID: "id-2", Kind: KindEvent, Updated: time.Date(2024, 9, 6, 12, 0, 0, 0, time.UTC)},
|
{ID: "id-2", Kind: item.KindEvent, Updated: time.Date(2024, 9, 6, 12, 0, 0, 0, time.UTC)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -201,7 +203,7 @@ func TestSyncPost(t *testing.T) {
|
||||||
t.Errorf("exp %v, got %v", tc.expStatus, res.Result().StatusCode)
|
t.Errorf("exp %v, got %v", tc.expStatus, res.Result().StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
actItems, err := mem.Updated([]Kind{}, time.Time{})
|
actItems, err := mem.Updated([]item.Kind{}, time.Time{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("exp nil, git %v", err)
|
t.Errorf("exp nil, git %v", err)
|
||||||
}
|
}
|
|
@ -8,8 +8,6 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"go-mod.ewintr.nl/planner/sync/planner"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -29,7 +27,7 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := planner.NewSqlite(dbPath)
|
repo, err := NewSqlite(dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not open sqlite db: %s", err.Error())
|
fmt.Printf("could not open sqlite db: %s", err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -43,7 +41,7 @@ func main() {
|
||||||
})
|
})
|
||||||
|
|
||||||
address := fmt.Sprintf(":%d", port)
|
address := fmt.Sprintf(":%d", port)
|
||||||
srv := planner.NewServer(repo, apiKey, logger)
|
srv := NewServer(repo, apiKey, logger)
|
||||||
go http.ListenAndServe(address, srv)
|
go http.ListenAndServe(address, srv)
|
||||||
|
|
||||||
logger.Info("service started")
|
logger.Info("service started")
|
|
@ -3,26 +3,28 @@ package main
|
||||||
import (
|
import (
|
||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/sync/item"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Memory struct {
|
type Memory struct {
|
||||||
items map[string]Item
|
items map[string]item.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemory() *Memory {
|
func NewMemory() *Memory {
|
||||||
return &Memory{
|
return &Memory{
|
||||||
items: make(map[string]Item),
|
items: make(map[string]item.Item),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memory) Update(item Item) error {
|
func (m *Memory) Update(item item.Item) error {
|
||||||
m.items[item.ID] = item
|
m.items[item.ID] = item
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memory) Updated(kinds []Kind, timestamp time.Time) ([]Item, error) {
|
func (m *Memory) Updated(kinds []item.Kind, timestamp time.Time) ([]item.Item, error) {
|
||||||
result := make([]Item, 0)
|
result := make([]item.Item, 0)
|
||||||
|
|
||||||
for _, i := range m.items {
|
for _, i := range m.items {
|
||||||
timeOK := timestamp.IsZero() || i.Updated.Equal(timestamp) || i.Updated.After(timestamp)
|
timeOK := timestamp.IsZero() || i.Updated.Equal(timestamp) || i.Updated.After(timestamp)
|
|
@ -3,6 +3,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/sync/item"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMemoryItem(t *testing.T) {
|
func TestMemoryItem(t *testing.T) {
|
||||||
|
@ -11,7 +13,7 @@ func TestMemoryItem(t *testing.T) {
|
||||||
mem := NewMemory()
|
mem := NewMemory()
|
||||||
|
|
||||||
t.Log("start empty")
|
t.Log("start empty")
|
||||||
actItems, actErr := mem.Updated([]Kind{}, time.Time{})
|
actItems, actErr := mem.Updated([]item.Kind{}, time.Time{})
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
|
@ -20,11 +22,11 @@ func TestMemoryItem(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("add one")
|
t.Log("add one")
|
||||||
t1 := NewItem(Kind("kinda"), "test")
|
t1 := item.NewItem(item.Kind("kinda"), "test")
|
||||||
if actErr := mem.Update(t1); actErr != nil {
|
if actErr := mem.Update(t1); actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
actItems, actErr = mem.Updated([]Kind{}, time.Time{})
|
actItems, actErr = mem.Updated([]item.Kind{}, time.Time{})
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
|
@ -38,11 +40,11 @@ func TestMemoryItem(t *testing.T) {
|
||||||
before := time.Now()
|
before := time.Now()
|
||||||
|
|
||||||
t.Log("add second")
|
t.Log("add second")
|
||||||
t2 := NewItem(Kind("kindb"), "test 2")
|
t2 := item.NewItem(item.Kind("kindb"), "test 2")
|
||||||
if actErr := mem.Update(t2); actErr != nil {
|
if actErr := mem.Update(t2); actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
actItems, actErr = mem.Updated([]Kind{}, time.Time{})
|
actItems, actErr = mem.Updated([]item.Kind{}, time.Time{})
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +58,7 @@ func TestMemoryItem(t *testing.T) {
|
||||||
t.Errorf("exp %v, got %v", actItems[1].ID, t2.ID)
|
t.Errorf("exp %v, got %v", actItems[1].ID, t2.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
actItems, actErr = mem.Updated([]Kind{}, before)
|
actItems, actErr = mem.Updated([]item.Kind{}, before)
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
|
@ -72,7 +74,7 @@ func TestMemoryItem(t *testing.T) {
|
||||||
if actErr := mem.Update(t1); actErr != nil {
|
if actErr := mem.Update(t1); actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
actItems, actErr = mem.Updated([]Kind{}, before)
|
actItems, actErr = mem.Updated([]item.Kind{}, before)
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
||||||
|
@ -87,7 +89,7 @@ func TestMemoryItem(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("select kind")
|
t.Log("select kind")
|
||||||
actItems, actErr = mem.Updated([]Kind{"kinda"}, time.Time{})
|
actItems, actErr = mem.Updated([]item.Kind{"kinda"}, time.Time{})
|
||||||
if actErr != nil {
|
if actErr != nil {
|
||||||
t.Errorf("exp nil, got %v", actErr)
|
t.Errorf("exp nil, got %v", actErr)
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/sync/item"
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ func NewSqlite(dbPath string) (*Sqlite, error) {
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sqlite) Update(item Item) error {
|
func (s *Sqlite) Update(item item.Item) error {
|
||||||
if _, err := s.db.Exec(`
|
if _, err := s.db.Exec(`
|
||||||
INSERT INTO items
|
INSERT INTO items
|
||||||
(id, kind, updated, deleted, body)
|
(id, kind, updated, deleted, body)
|
||||||
|
@ -67,7 +68,7 @@ body=excluded.body`,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sqlite) Updated(ks []Kind, t time.Time) ([]Item, error) {
|
func (s *Sqlite) Updated(ks []item.Kind, t time.Time) ([]item.Item, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, kind, updated, deleted, body
|
SELECT id, kind, updated, deleted, body
|
||||||
FROM items
|
FROM items
|
||||||
|
@ -93,10 +94,10 @@ WHERE updated > ?`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]Item, 0)
|
result := make([]item.Item, 0)
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var item Item
|
var item item.Item
|
||||||
if err := rows.Scan(&item.ID, &item.Kind, &item.Updated, &item.Deleted, &item.Body); err != nil {
|
if err := rows.Scan(&item.ID, &item.Kind, &item.Updated, &item.Deleted, &item.Body); err != nil {
|
||||||
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||||
}
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/sync/item"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotFound = errors.New("not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Syncer interface {
|
||||||
|
Update(item item.Item) error
|
||||||
|
Updated(kind []item.Kind, t time.Time) ([]item.Item, error)
|
||||||
|
}
|
Loading…
Reference in New Issue