rename event to task

This commit is contained in:
Erik Winter 2024-12-24 08:00:23 +01:00
parent 5985356c99
commit dcdc75887f
27 changed files with 430 additions and 428 deletions

BIN
dist/plan vendored

Binary file not shown.

BIN
dist/plannersync vendored

Binary file not shown.

View File

@ -10,12 +10,12 @@ import (
type Kind string type Kind string
const ( const (
KindTask Kind = "task" KindSchedule Kind = "schedule"
KindEvent Kind = "event" KindTask Kind = "task"
) )
var ( var (
KnownKinds = []Kind{KindTask, KindEvent} KnownKinds = []Kind{KindSchedule, KindTask}
) )
type Item struct { type Item struct {

View File

@ -8,14 +8,14 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
) )
type EventBody struct { type TaskBody struct {
Title string `json:"title"` Title string `json:"title"`
Time Time `json:"time"` Time Time `json:"time"`
Duration time.Duration `json:"duration"` Duration time.Duration `json:"duration"`
} }
func (e EventBody) MarshalJSON() ([]byte, error) { func (e TaskBody) MarshalJSON() ([]byte, error) {
type Alias EventBody type Alias TaskBody
return json.Marshal(&struct { return json.Marshal(&struct {
Duration string `json:"duration"` Duration string `json:"duration"`
*Alias *Alias
@ -25,8 +25,8 @@ func (e EventBody) MarshalJSON() ([]byte, error) {
}) })
} }
func (e *EventBody) UnmarshalJSON(data []byte) error { func (e *TaskBody) UnmarshalJSON(data []byte) error {
type Alias EventBody type Alias TaskBody
aux := &struct { aux := &struct {
Duration string `json:"duration"` Duration string `json:"duration"`
*Alias *Alias
@ -45,70 +45,70 @@ func (e *EventBody) UnmarshalJSON(data []byte) error {
return nil return nil
} }
type Event struct { type Task struct {
ID string `json:"id"` ID string `json:"id"`
Date Date `json:"date"` Date Date `json:"date"`
Recurrer Recurrer `json:"recurrer"` Recurrer Recurrer `json:"recurrer"`
RecurNext Date `json:"recurNext"` RecurNext Date `json:"recurNext"`
EventBody TaskBody
} }
func NewEvent(i Item) (Event, error) { func NewTask(i Item) (Task, error) {
if i.Kind != KindEvent { if i.Kind != KindTask {
return Event{}, fmt.Errorf("item is not an event") return Task{}, fmt.Errorf("item is not an task")
} }
var e Event var t Task
if err := json.Unmarshal([]byte(i.Body), &e); err != nil { if err := json.Unmarshal([]byte(i.Body), &t); err != nil {
return Event{}, fmt.Errorf("could not unmarshal item body: %v", err) return Task{}, fmt.Errorf("could not unmarshal item body: %v", err)
} }
e.ID = i.ID t.ID = i.ID
e.Date = i.Date t.Date = i.Date
e.Recurrer = i.Recurrer t.Recurrer = i.Recurrer
e.RecurNext = i.RecurNext t.RecurNext = i.RecurNext
return e, nil return t, nil
} }
func (e Event) Item() (Item, error) { func (t Task) Item() (Item, error) {
body, err := json.Marshal(e.EventBody) body, err := json.Marshal(t.TaskBody)
if err != nil { if err != nil {
return Item{}, fmt.Errorf("could not marshal event body to json") return Item{}, fmt.Errorf("could not marshal task body to json")
} }
return Item{ return Item{
ID: e.ID, ID: t.ID,
Kind: KindEvent, Kind: KindTask,
Date: e.Date, Date: t.Date,
Recurrer: e.Recurrer, Recurrer: t.Recurrer,
RecurNext: e.RecurNext, RecurNext: t.RecurNext,
Body: string(body), Body: string(body),
}, nil }, nil
} }
func (e Event) Valid() bool { func (t Task) Valid() bool {
if e.Title == "" { if t.Title == "" {
return false return false
} }
if e.Date.IsZero() { if t.Date.IsZero() {
return false return false
} }
if e.Duration.Seconds() < 1 { if t.Duration.Seconds() < 1 {
return false return false
} }
return true return true
} }
func EventDiff(a, b Event) string { func TaskDiff(a, b Task) string {
aJSON, _ := json.Marshal(a) aJSON, _ := json.Marshal(a)
bJSON, _ := json.Marshal(b) bJSON, _ := json.Marshal(b)
return cmp.Diff(string(aJSON), string(bJSON)) return cmp.Diff(string(aJSON), string(bJSON))
} }
func EventDiffs(a, b []Event) string { func TaskDiffs(a, b []Task) string {
aJSON, _ := json.Marshal(a) aJSON, _ := json.Marshal(a)
bJSON, _ := json.Marshal(b) bJSON, _ := json.Marshal(b)

View File

@ -8,7 +8,7 @@ import (
"go-mod.ewintr.nl/planner/item" "go-mod.ewintr.nl/planner/item"
) )
func TestNewEvent(t *testing.T) { func TestNewTask(t *testing.T) {
t.Parallel() t.Parallel()
oneHour, err := time.ParseDuration("1h") oneHour, err := time.ParseDuration("1h")
@ -16,17 +16,17 @@ func TestNewEvent(t *testing.T) {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
for _, tc := range []struct { for _, tc := range []struct {
name string name string
it item.Item it item.Item
expEvent item.Event expTask item.Task
expErr bool expErr bool
}{ }{
{ {
name: "wrong kind", name: "wrong kind",
it: item.Item{ it: item.Item{
ID: "a", ID: "a",
Date: item.NewDate(2024, 9, 20), Date: item.NewDate(2024, 9, 20),
Kind: item.KindTask, Kind: item.KindSchedule,
Body: `{ Body: `{
"title":"title", "title":"title",
"time":"08:00", "time":"08:00",
@ -39,7 +39,7 @@ func TestNewEvent(t *testing.T) {
name: "invalid json", name: "invalid json",
it: item.Item{ it: item.Item{
ID: "a", ID: "a",
Kind: item.KindEvent, Kind: item.KindTask,
Body: `{"id":"a"`, Body: `{"id":"a"`,
}, },
expErr: true, expErr: true,
@ -48,7 +48,7 @@ func TestNewEvent(t *testing.T) {
name: "valid", name: "valid",
it: item.Item{ it: item.Item{
ID: "a", ID: "a",
Kind: item.KindEvent, Kind: item.KindTask,
Date: item.NewDate(2024, 9, 20), Date: item.NewDate(2024, 9, 20),
Recurrer: item.NewRecurrer("2024-12-08, daily"), Recurrer: item.NewRecurrer("2024-12-08, daily"),
Body: `{ Body: `{
@ -57,11 +57,11 @@ func TestNewEvent(t *testing.T) {
"duration":"1h" "duration":"1h"
}`, }`,
}, },
expEvent: item.Event{ expTask: item.Task{
ID: "a", ID: "a",
Date: item.NewDate(2024, 9, 20), Date: item.NewDate(2024, 9, 20),
Recurrer: item.NewRecurrer("2024-12-08, daily"), Recurrer: item.NewRecurrer("2024-12-08, daily"),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Time: item.NewTime(8, 0), Time: item.NewTime(8, 0),
Duration: oneHour, Duration: oneHour,
@ -70,21 +70,21 @@ func TestNewEvent(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
actEvent, actErr := item.NewEvent(tc.it) actTask, actErr := item.NewTask(tc.it)
if tc.expErr != (actErr != nil) { if tc.expErr != (actErr != nil) {
t.Errorf("exp nil, got %v", actErr) t.Errorf("exp nil, got %v", actErr)
} }
if tc.expErr { if tc.expErr {
return return
} }
if diff := item.EventDiff(tc.expEvent, actEvent); diff != "" { if diff := item.TaskDiff(tc.expTask, actTask); diff != "" {
t.Errorf("(+exp, -got)\n%s", diff) t.Errorf("(+exp, -got)\n%s", diff)
} }
}) })
} }
} }
func TestEventItem(t *testing.T) { func TestTaskItem(t *testing.T) {
t.Parallel() t.Parallel()
oneHour, err := time.ParseDuration("1h") oneHour, err := time.ParseDuration("1h")
@ -93,24 +93,24 @@ func TestEventItem(t *testing.T) {
} }
for _, tc := range []struct { for _, tc := range []struct {
name string name string
event item.Event tsk item.Task
expItem item.Item expItem item.Item
expErr bool expErr bool
}{ }{
{ {
name: "empty", name: "empty",
expItem: item.Item{ expItem: item.Item{
Kind: item.KindEvent, Kind: item.KindTask,
Updated: time.Time{}, Updated: time.Time{},
Body: `{"duration":"0s","title":"","time":"00:00"}`, Body: `{"duration":"0s","title":"","time":"00:00"}`,
}, },
}, },
{ {
name: "normal", name: "normal",
event: item.Event{ tsk: item.Task{
ID: "a", ID: "a",
Date: item.NewDate(2024, 9, 23), Date: item.NewDate(2024, 9, 23),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Time: item.NewTime(8, 0), Time: item.NewTime(8, 0),
Duration: oneHour, Duration: oneHour,
@ -118,7 +118,7 @@ func TestEventItem(t *testing.T) {
}, },
expItem: item.Item{ expItem: item.Item{
ID: "a", ID: "a",
Kind: item.KindEvent, Kind: item.KindTask,
Updated: time.Time{}, Updated: time.Time{},
Date: item.NewDate(2024, 9, 23), Date: item.NewDate(2024, 9, 23),
Body: `{"duration":"1h0m0s","title":"title","time":"08:00"}`, Body: `{"duration":"1h0m0s","title":"title","time":"08:00"}`,
@ -126,7 +126,7 @@ func TestEventItem(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
actItem, actErr := tc.event.Item() actItem, actErr := tc.tsk.Item()
if tc.expErr != (actErr != nil) { if tc.expErr != (actErr != nil) {
t.Errorf("exp nil, got %v", actErr) t.Errorf("exp nil, got %v", actErr)
} }
@ -140,7 +140,7 @@ func TestEventItem(t *testing.T) {
} }
} }
func TestEventValidate(t *testing.T) { func TestTaskValidate(t *testing.T) {
t.Parallel() t.Parallel()
oneHour, err := time.ParseDuration("1h") oneHour, err := time.ParseDuration("1h")
@ -149,19 +149,19 @@ func TestEventValidate(t *testing.T) {
} }
for _, tc := range []struct { for _, tc := range []struct {
name string name string
event item.Event tsk item.Task
exp bool exp bool
}{ }{
{ {
name: "empty", name: "empty",
}, },
{ {
name: "missing title", name: "missing title",
event: item.Event{ tsk: item.Task{
ID: "a", ID: "a",
Date: item.NewDate(2024, 9, 20), Date: item.NewDate(2024, 9, 20),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Time: item.NewTime(8, 0), Time: item.NewTime(8, 0),
Duration: oneHour, Duration: oneHour,
}, },
@ -169,9 +169,9 @@ func TestEventValidate(t *testing.T) {
}, },
{ {
name: "no date", name: "no date",
event: item.Event{ tsk: item.Task{
ID: "a", ID: "a",
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Time: item.NewTime(8, 0), Time: item.NewTime(8, 0),
Duration: oneHour, Duration: oneHour,
@ -180,10 +180,10 @@ func TestEventValidate(t *testing.T) {
}, },
{ {
name: "no duration", name: "no duration",
event: item.Event{ tsk: item.Task{
ID: "a", ID: "a",
Date: item.NewDate(2024, 9, 20), Date: item.NewDate(2024, 9, 20),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Time: item.NewTime(8, 0), Time: item.NewTime(8, 0),
}, },
@ -191,10 +191,10 @@ func TestEventValidate(t *testing.T) {
}, },
{ {
name: "valid", name: "valid",
event: item.Event{ tsk: item.Task{
ID: "a", ID: "a",
Date: item.NewDate(2024, 9, 20), Date: item.NewDate(2024, 9, 20),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Time: item.NewTime(8, 0), Time: item.NewTime(8, 0),
Duration: oneHour, Duration: oneHour,
@ -204,7 +204,7 @@ func TestEventValidate(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
if act := tc.event.Valid(); tc.exp != act { if act := tc.tsk.Valid(); tc.exp != act {
t.Errorf("exp %v, got %v", tc.exp, act) t.Errorf("exp %v, got %v", tc.exp, act)
} }

View File

@ -11,15 +11,15 @@ import (
type Add struct { type Add struct {
localIDRepo storage.LocalID localIDRepo storage.LocalID
eventRepo storage.Event taskRepo storage.Task
syncRepo storage.Sync syncRepo storage.Sync
argSet *ArgSet argSet *ArgSet
} }
func NewAdd(localRepo storage.LocalID, eventRepo storage.Event, syncRepo storage.Sync) Command { func NewAdd(localRepo storage.LocalID, taskRepo storage.Task, syncRepo storage.Sync) Command {
return &Add{ return &Add{
localIDRepo: localRepo, localIDRepo: localRepo,
eventRepo: eventRepo, taskRepo: taskRepo,
syncRepo: syncRepo, syncRepo: syncRepo,
argSet: &ArgSet{ argSet: &ArgSet{
Flags: map[string]Flag{ Flags: map[string]Flag{
@ -75,21 +75,21 @@ func (add *Add) Execute(main []string, flags map[string]string) error {
func (add *Add) do() error { func (add *Add) do() error {
as := add.argSet as := add.argSet
rec := as.GetRecurrer(FlagRec) rec := as.GetRecurrer(FlagRec)
e := item.Event{ tsk := item.Task{
ID: uuid.New().String(), ID: uuid.New().String(),
Date: as.GetDate(FlagOn), Date: as.GetDate(FlagOn),
Recurrer: rec, Recurrer: rec,
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: as.Main, Title: as.Main,
Time: as.GetTime(FlagAt), Time: as.GetTime(FlagAt),
Duration: as.GetDuration(FlagFor), Duration: as.GetDuration(FlagFor),
}, },
} }
if rec != nil { if rec != nil {
e.RecurNext = rec.First() tsk.RecurNext = rec.First()
} }
if err := add.eventRepo.Store(e); err != nil { if err := add.taskRepo.Store(tsk); err != nil {
return fmt.Errorf("could not store event: %v", err) return fmt.Errorf("could not store event: %v", err)
} }
@ -97,11 +97,11 @@ func (add *Add) do() error {
if err != nil { if err != nil {
return fmt.Errorf("could not create next local id: %v", err) return fmt.Errorf("could not create next local id: %v", err)
} }
if err := add.localIDRepo.Store(e.ID, localID); err != nil { if err := add.localIDRepo.Store(tsk.ID, localID); err != nil {
return fmt.Errorf("could not store local id: %v", err) return fmt.Errorf("could not store local id: %v", err)
} }
it, err := e.Item() it, err := tsk.Item()
if err != nil { if err != nil {
return fmt.Errorf("could not convert event to sync item: %v", err) return fmt.Errorf("could not convert event to sync item: %v", err)
} }

View File

@ -19,11 +19,11 @@ func TestAdd(t *testing.T) {
anHour := time.Hour anHour := time.Hour
for _, tc := range []struct { for _, tc := range []struct {
name string name string
main []string main []string
flags map[string]string flags map[string]string
expErr bool expErr bool
expEvent item.Event expTask item.Task
}{ }{
{ {
name: "empty", name: "empty",
@ -48,10 +48,10 @@ func TestAdd(t *testing.T) {
flags: map[string]string{ flags: map[string]string{
command.FlagOn: aDate.String(), command.FlagOn: aDate.String(),
}, },
expEvent: item.Event{ expTask: item.Task{
ID: "title", ID: "title",
Date: aDate, Date: aDate,
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Duration: aDay, Duration: aDay,
}, },
@ -65,10 +65,10 @@ func TestAdd(t *testing.T) {
command.FlagAt: aTime.String(), command.FlagAt: aTime.String(),
command.FlagFor: anHourStr, command.FlagFor: anHourStr,
}, },
expEvent: item.Event{ expTask: item.Task{
ID: "title", ID: "title",
Date: aDate, Date: aDate,
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Time: aTime, Time: aTime,
Duration: anHour, Duration: anHour,
@ -86,10 +86,10 @@ func TestAdd(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
eventRepo := memory.NewEvent() taskRepo := memory.NewTask()
localRepo := memory.NewLocalID() localRepo := memory.NewLocalID()
syncRepo := memory.NewSync() syncRepo := memory.NewSync()
cmd := command.NewAdd(localRepo, eventRepo, syncRepo) cmd := command.NewAdd(localRepo, taskRepo, syncRepo)
actParseErr := cmd.Execute(tc.main, tc.flags) != nil actParseErr := cmd.Execute(tc.main, tc.flags) != nil
if tc.expErr != actParseErr { if tc.expErr != actParseErr {
t.Errorf("exp %v, got %v", tc.expErr, actParseErr) t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
@ -98,12 +98,12 @@ func TestAdd(t *testing.T) {
return return
} }
actEvents, err := eventRepo.FindAll() actTasks, err := taskRepo.FindAll()
if err != nil { if err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
if len(actEvents) != 1 { if len(actTasks) != 1 {
t.Errorf("exp 1, got %d", len(actEvents)) t.Errorf("exp 1, got %d", len(actTasks))
} }
actLocalIDs, err := localRepo.FindAll() actLocalIDs, err := localRepo.FindAll()
@ -113,15 +113,15 @@ func TestAdd(t *testing.T) {
if len(actLocalIDs) != 1 { if len(actLocalIDs) != 1 {
t.Errorf("exp 1, got %v", len(actLocalIDs)) t.Errorf("exp 1, got %v", len(actLocalIDs))
} }
if _, ok := actLocalIDs[actEvents[0].ID]; !ok { if _, ok := actLocalIDs[actTasks[0].ID]; !ok {
t.Errorf("exp true, got %v", ok) t.Errorf("exp true, got %v", ok)
} }
if actEvents[0].ID == "" { if actTasks[0].ID == "" {
t.Errorf("exp string not te be empty") t.Errorf("exp string not te be empty")
} }
tc.expEvent.ID = actEvents[0].ID tc.expTask.ID = actTasks[0].ID
if diff := item.EventDiff(tc.expEvent, actEvents[0]); diff != "" { if diff := item.TaskDiff(tc.expTask, actTasks[0]); diff != "" {
t.Errorf("(exp -, got +)\n%s", diff) t.Errorf("(exp -, got +)\n%s", diff)
} }

View File

@ -9,15 +9,15 @@ import (
type Delete struct { type Delete struct {
localIDRepo storage.LocalID localIDRepo storage.LocalID
eventRepo storage.Event taskRepo storage.Task
syncRepo storage.Sync syncRepo storage.Sync
localID int localID int
} }
func NewDelete(localIDRepo storage.LocalID, eventRepo storage.Event, syncRepo storage.Sync) Command { func NewDelete(localIDRepo storage.LocalID, taskRepo storage.Task, syncRepo storage.Sync) Command {
return &Delete{ return &Delete{
localIDRepo: localIDRepo, localIDRepo: localIDRepo,
eventRepo: eventRepo, taskRepo: taskRepo,
syncRepo: syncRepo, syncRepo: syncRepo,
} }
} }
@ -41,23 +41,23 @@ func (del *Delete) do() error {
if err != nil { if err != nil {
return fmt.Errorf("could not get local ids: %v", err) return fmt.Errorf("could not get local ids: %v", err)
} }
for eid, lid := range idMap { for tskID, lid := range idMap {
if del.localID == lid { if del.localID == lid {
id = eid id = tskID
} }
} }
if id == "" { if id == "" {
return fmt.Errorf("could not find local id") return fmt.Errorf("could not find local id")
} }
e, err := del.eventRepo.Find(id) tsk, err := del.taskRepo.Find(id)
if err != nil { if err != nil {
return fmt.Errorf("could not get event: %v", err) return fmt.Errorf("could not get task: %v", err)
} }
it, err := e.Item() it, err := tsk.Item()
if err != nil { if err != nil {
return fmt.Errorf("could not convert event to sync item: %v", err) return fmt.Errorf("could not convert task to sync item: %v", err)
} }
it.Deleted = true it.Deleted = true
if err := del.syncRepo.Store(it); err != nil { if err := del.syncRepo.Store(it); err != nil {
@ -68,8 +68,8 @@ func (del *Delete) do() error {
return fmt.Errorf("could not delete local id: %v", err) return fmt.Errorf("could not delete local id: %v", err)
} }
if err := del.eventRepo.Delete(id); err != nil { if err := del.taskRepo.Delete(id); err != nil {
return fmt.Errorf("could not delete event: %v", err) return fmt.Errorf("could not delete task: %v", err)
} }
return nil return nil

View File

@ -13,10 +13,10 @@ import (
func TestDelete(t *testing.T) { func TestDelete(t *testing.T) {
t.Parallel() t.Parallel()
e := item.Event{ e := item.Task{
ID: "id", ID: "id",
Date: item.NewDate(2024, 10, 7), Date: item.NewDate(2024, 10, 7),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "name", Title: "name",
}, },
} }
@ -43,7 +43,7 @@ func TestDelete(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
eventRepo := memory.NewEvent() eventRepo := memory.NewTask()
syncRepo := memory.NewSync() syncRepo := memory.NewSync()
if err := eventRepo.Store(e); err != nil { if err := eventRepo.Store(e); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)

View File

@ -8,13 +8,13 @@ import (
type List struct { type List struct {
localIDRepo storage.LocalID localIDRepo storage.LocalID
eventRepo storage.Event taskRepo storage.Task
} }
func NewList(localIDRepo storage.LocalID, eventRepo storage.Event) Command { func NewList(localIDRepo storage.LocalID, taskRepo storage.Task) Command {
return &List{ return &List{
localIDRepo: localIDRepo, localIDRepo: localIDRepo,
eventRepo: eventRepo, taskRepo: taskRepo,
} }
} }
@ -31,7 +31,7 @@ func (list *List) do() error {
if err != nil { if err != nil {
return fmt.Errorf("could not get local ids: %v", err) return fmt.Errorf("could not get local ids: %v", err)
} }
all, err := list.eventRepo.FindAll() all, err := list.taskRepo.FindAll()
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,16 +11,16 @@ import (
func TestList(t *testing.T) { func TestList(t *testing.T) {
t.Parallel() t.Parallel()
eventRepo := memory.NewEvent() taskRepo := memory.NewTask()
localRepo := memory.NewLocalID() localRepo := memory.NewLocalID()
e := item.Event{ e := item.Task{
ID: "id", ID: "id",
Date: item.NewDate(2024, 10, 7), Date: item.NewDate(2024, 10, 7),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "name", Title: "name",
}, },
} }
if err := eventRepo.Store(e); err != nil { if err := taskRepo.Store(e); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
if err := localRepo.Store(e.ID, 1); err != nil { if err := localRepo.Store(e.ID, 1); err != nil {
@ -47,7 +47,7 @@ func TestList(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cmd := command.NewList(localRepo, eventRepo) cmd := command.NewList(localRepo, taskRepo)
actErr := cmd.Execute(tc.main, nil) != nil actErr := cmd.Execute(tc.main, nil) != nil
if tc.expErr != actErr { if tc.expErr != actErr {
t.Errorf("exp %v, got %v", tc.expErr, actErr) t.Errorf("exp %v, got %v", tc.expErr, actErr)

View File

@ -14,15 +14,15 @@ type Sync struct {
client client.Client client client.Client
syncRepo storage.Sync syncRepo storage.Sync
localIDRepo storage.LocalID localIDRepo storage.LocalID
eventRepo storage.Event taskRepo storage.Task
} }
func NewSync(client client.Client, syncRepo storage.Sync, localIDRepo storage.LocalID, eventRepo storage.Event) Command { func NewSync(client client.Client, syncRepo storage.Sync, localIDRepo storage.LocalID, taskRepo storage.Task) Command {
return &Sync{ return &Sync{
client: client, client: client,
syncRepo: syncRepo, syncRepo: syncRepo,
localIDRepo: localIDRepo, localIDRepo: localIDRepo,
eventRepo: eventRepo, taskRepo: taskRepo,
} }
} }
@ -52,7 +52,7 @@ func (sync *Sync) do() error {
if err != nil { if err != nil {
return fmt.Errorf("could not find timestamp of last update: %v", err) return fmt.Errorf("could not find timestamp of last update: %v", err)
} }
recItems, err := sync.client.Updated([]item.Kind{item.KindEvent}, ts) recItems, err := sync.client.Updated([]item.Kind{item.KindTask}, ts)
if err != nil { if err != nil {
return fmt.Errorf("could not receive updates: %v", err) return fmt.Errorf("could not receive updates: %v", err)
} }
@ -63,8 +63,8 @@ func (sync *Sync) do() error {
if err := sync.localIDRepo.Delete(ri.ID); err != nil && !errors.Is(err, storage.ErrNotFound) { if err := sync.localIDRepo.Delete(ri.ID); err != nil && !errors.Is(err, storage.ErrNotFound) {
return fmt.Errorf("could not delete local id: %v", err) return fmt.Errorf("could not delete local id: %v", err)
} }
if err := sync.eventRepo.Delete(ri.ID); err != nil && !errors.Is(err, storage.ErrNotFound) { if err := sync.taskRepo.Delete(ri.ID); err != nil && !errors.Is(err, storage.ErrNotFound) {
return fmt.Errorf("could not delete event: %v", err) return fmt.Errorf("could not delete task: %v", err)
} }
continue continue
} }
@ -76,16 +76,16 @@ func (sync *Sync) do() error {
return fmt.Errorf("could not get local ids: %v", err) return fmt.Errorf("could not get local ids: %v", err)
} }
for _, u := range updated { for _, u := range updated {
var eBody item.EventBody var tskBody item.TaskBody
if err := json.Unmarshal([]byte(u.Body), &eBody); err != nil { if err := json.Unmarshal([]byte(u.Body), &tskBody); err != nil {
return fmt.Errorf("could not unmarshal event body: %v", err) return fmt.Errorf("could not unmarshal task body: %v", err)
} }
e := item.Event{ tsk := item.Task{
ID: u.ID, ID: u.ID,
EventBody: eBody, TaskBody: tskBody,
} }
if err := sync.eventRepo.Store(e); err != nil { if err := sync.taskRepo.Store(tsk); err != nil {
return fmt.Errorf("could not store event: %v", err) return fmt.Errorf("could not store task: %v", err)
} }
lid, ok := lidMap[u.ID] lid, ok := lidMap[u.ID]
if !ok { if !ok {

View File

@ -17,7 +17,7 @@ func TestSyncParse(t *testing.T) {
syncClient := client.NewMemory() syncClient := client.NewMemory()
syncRepo := memory.NewSync() syncRepo := memory.NewSync()
localIDRepo := memory.NewLocalID() localIDRepo := memory.NewLocalID()
eventRepo := memory.NewEvent() taskRepo := memory.NewTask()
for _, tc := range []struct { for _, tc := range []struct {
name string name string
@ -39,7 +39,7 @@ func TestSyncParse(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cmd := command.NewSync(syncClient, syncRepo, localIDRepo, eventRepo) cmd := command.NewSync(syncClient, syncRepo, localIDRepo, taskRepo)
actErr := cmd.Execute(tc.main, nil) != nil actErr := cmd.Execute(tc.main, nil) != nil
if tc.expErr != actErr { if tc.expErr != actErr {
t.Errorf("exp %v, got %v", tc.expErr, actErr) t.Errorf("exp %v, got %v", tc.expErr, actErr)
@ -54,11 +54,11 @@ func TestSyncSend(t *testing.T) {
syncClient := client.NewMemory() syncClient := client.NewMemory()
syncRepo := memory.NewSync() syncRepo := memory.NewSync()
localIDRepo := memory.NewLocalID() localIDRepo := memory.NewLocalID()
eventRepo := memory.NewEvent() taskRepo := memory.NewTask()
it := item.Item{ it := item.Item{
ID: "a", ID: "a",
Kind: item.KindEvent, Kind: item.KindTask,
Body: `{ Body: `{
"title":"title", "title":"title",
"start":"2024-10-18T08:00:00Z", "start":"2024-10-18T08:00:00Z",
@ -77,12 +77,12 @@ func TestSyncSend(t *testing.T) {
}{ }{
{ {
name: "single", name: "single",
ks: []item.Kind{item.KindEvent}, ks: []item.Kind{item.KindTask},
expItems: []item.Item{it}, expItems: []item.Item{it},
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cmd := command.NewSync(syncClient, syncRepo, localIDRepo, eventRepo) cmd := command.NewSync(syncClient, syncRepo, localIDRepo, taskRepo)
if err := cmd.Execute([]string{"sync"}, nil); err != nil { if err := cmd.Execute([]string{"sync"}, nil); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
@ -115,31 +115,31 @@ func TestSyncReceive(t *testing.T) {
for _, tc := range []struct { for _, tc := range []struct {
name string name string
present []item.Event present []item.Task
updated []item.Item updated []item.Item
expEvent []item.Event expTask []item.Task
expLocalID map[string]int expLocalID map[string]int
}{ }{
{ {
name: "no new", name: "no new",
expEvent: []item.Event{}, expTask: []item.Task{},
expLocalID: map[string]int{}, expLocalID: map[string]int{},
}, },
{ {
name: "new", name: "new",
updated: []item.Item{{ updated: []item.Item{{
ID: "a", ID: "a",
Kind: item.KindEvent, Kind: item.KindTask,
Body: `{ Body: `{
"title":"title", "title":"title",
"start":"2024-10-23T08:00:00Z", "start":"2024-10-23T08:00:00Z",
"duration":"1h" "duration":"1h"
}`, }`,
}}, }},
expEvent: []item.Event{{ expTask: []item.Task{{
ID: "a", ID: "a",
Date: item.NewDate(2024, 10, 23), Date: item.NewDate(2024, 10, 23),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Duration: oneHour, Duration: oneHour,
}, },
@ -150,27 +150,27 @@ func TestSyncReceive(t *testing.T) {
}, },
{ {
name: "update existing", name: "update existing",
present: []item.Event{{ present: []item.Task{{
ID: "a", ID: "a",
Date: item.NewDate(2024, 10, 23), Date: item.NewDate(2024, 10, 23),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "title", Title: "title",
Duration: oneHour, Duration: oneHour,
}, },
}}, }},
updated: []item.Item{{ updated: []item.Item{{
ID: "a", ID: "a",
Kind: item.KindEvent, Kind: item.KindTask,
Body: `{ Body: `{
"title":"new title", "title":"new title",
"start":"2024-10-23T08:00:00Z", "start":"2024-10-23T08:00:00Z",
"duration":"1h" "duration":"1h"
}`, }`,
}}, }},
expEvent: []item.Event{{ expTask: []item.Task{{
ID: "a", ID: "a",
Date: item.NewDate(2024, 10, 23), Date: item.NewDate(2024, 10, 23),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "new title", Title: "new title",
Duration: oneHour, Duration: oneHour,
}, },
@ -185,10 +185,10 @@ func TestSyncReceive(t *testing.T) {
syncClient := client.NewMemory() syncClient := client.NewMemory()
syncRepo := memory.NewSync() syncRepo := memory.NewSync()
localIDRepo := memory.NewLocalID() localIDRepo := memory.NewLocalID()
eventRepo := memory.NewEvent() taskRepo := memory.NewTask()
for i, p := range tc.present { for i, p := range tc.present {
if err := eventRepo.Store(p); err != nil { if err := taskRepo.Store(p); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
if err := localIDRepo.Store(p.ID, i+1); err != nil { if err := localIDRepo.Store(p.ID, i+1); err != nil {
@ -200,17 +200,17 @@ func TestSyncReceive(t *testing.T) {
} }
// sync // sync
cmd := command.NewSync(syncClient, syncRepo, localIDRepo, eventRepo) cmd := command.NewSync(syncClient, syncRepo, localIDRepo, taskRepo)
if err := cmd.Execute([]string{"sync"}, nil); err != nil { if err := cmd.Execute([]string{"sync"}, nil); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
// check result // check result
actEvents, err := eventRepo.FindAll() actTasks, err := taskRepo.FindAll()
if err != nil { if err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
if diff := item.EventDiffs(tc.expEvent, actEvents); diff != "" { if diff := item.TaskDiffs(tc.expTask, actTasks); diff != "" {
t.Errorf("(exp +, got -)\n%s", diff) t.Errorf("(exp +, got -)\n%s", diff)
} }
actLocalIDs, err := localIDRepo.FindAll() actLocalIDs, err := localIDRepo.FindAll()

View File

@ -10,16 +10,16 @@ import (
type Update struct { type Update struct {
localIDRepo storage.LocalID localIDRepo storage.LocalID
eventRepo storage.Event taskRepo storage.Task
syncRepo storage.Sync syncRepo storage.Sync
argSet *ArgSet argSet *ArgSet
localID int localID int
} }
func NewUpdate(localIDRepo storage.LocalID, eventRepo storage.Event, syncRepo storage.Sync) Command { func NewUpdate(localIDRepo storage.LocalID, taskRepo storage.Task, syncRepo storage.Sync) Command {
return &Update{ return &Update{
localIDRepo: localIDRepo, localIDRepo: localIDRepo,
eventRepo: eventRepo, taskRepo: taskRepo,
syncRepo: syncRepo, syncRepo: syncRepo,
argSet: &ArgSet{ argSet: &ArgSet{
Flags: map[string]Flag{ Flags: map[string]Flag{
@ -67,47 +67,47 @@ func (update *Update) do() error {
if err != nil { if err != nil {
return fmt.Errorf("could not get local ids: %v", err) return fmt.Errorf("could not get local ids: %v", err)
} }
for eid, lid := range idMap { for tid, lid := range idMap {
if update.localID == lid { if update.localID == lid {
id = eid id = tid
} }
} }
if id == "" { if id == "" {
return fmt.Errorf("could not find local id") return fmt.Errorf("could not find local id")
} }
e, err := update.eventRepo.Find(id) tsk, err := update.taskRepo.Find(id)
if err != nil { if err != nil {
return fmt.Errorf("could not find event") return fmt.Errorf("could not find task")
} }
if as.Main != "" { if as.Main != "" {
e.Title = as.Main tsk.Title = as.Main
} }
if as.IsSet(FlagOn) { if as.IsSet(FlagOn) {
e.Date = as.GetDate(FlagOn) tsk.Date = as.GetDate(FlagOn)
} }
if as.IsSet(FlagAt) { if as.IsSet(FlagAt) {
e.Time = as.GetTime(FlagAt) tsk.Time = as.GetTime(FlagAt)
} }
if as.IsSet(FlagFor) { if as.IsSet(FlagFor) {
e.Duration = as.GetDuration(FlagFor) tsk.Duration = as.GetDuration(FlagFor)
} }
if as.IsSet(FlagRec) { if as.IsSet(FlagRec) {
e.Recurrer = as.GetRecurrer(FlagRec) tsk.Recurrer = as.GetRecurrer(FlagRec)
} }
if !e.Valid() { if !tsk.Valid() {
return fmt.Errorf("event is unvalid") return fmt.Errorf("task is unvalid")
} }
if err := update.eventRepo.Store(e); err != nil { if err := update.taskRepo.Store(tsk); err != nil {
return fmt.Errorf("could not store event: %v", err) return fmt.Errorf("could not store task: %v", err)
} }
it, err := e.Item() it, err := tsk.Item()
if err != nil { if err != nil {
return fmt.Errorf("could not convert event to sync item: %v", err) return fmt.Errorf("could not convert task to sync item: %v", err)
} }
if err := update.syncRepo.Store(it); err != nil { if err := update.syncRepo.Store(it); err != nil {
return fmt.Errorf("could not store sync item: %v", err) return fmt.Errorf("could not store sync item: %v", err)

View File

@ -13,7 +13,7 @@ import (
func TestUpdateExecute(t *testing.T) { func TestUpdateExecute(t *testing.T) {
t.Parallel() t.Parallel()
eid := "c" tskID := "c"
lid := 3 lid := 3
oneHour, err := time.ParseDuration("1h") oneHour, err := time.ParseDuration("1h")
if err != nil { if err != nil {
@ -28,12 +28,12 @@ func TestUpdateExecute(t *testing.T) {
} }
for _, tc := range []struct { for _, tc := range []struct {
name string name string
localID int localID int
main []string main []string
flags map[string]string flags map[string]string
expEvent item.Event expTask item.Task
expErr bool expErr bool
}{ }{
{ {
name: "no args", name: "no args",
@ -48,10 +48,10 @@ func TestUpdateExecute(t *testing.T) {
name: "name", name: "name",
localID: lid, localID: lid,
main: []string{"update", fmt.Sprintf("%d", lid), "updated"}, main: []string{"update", fmt.Sprintf("%d", lid), "updated"},
expEvent: item.Event{ expTask: item.Task{
ID: eid, ID: tskID,
Date: item.NewDate(2024, 10, 6), Date: item.NewDate(2024, 10, 6),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: "updated", Title: "updated",
Time: aTime, Time: aTime,
Duration: oneHour, Duration: oneHour,
@ -74,10 +74,10 @@ func TestUpdateExecute(t *testing.T) {
flags: map[string]string{ flags: map[string]string{
"on": "2024-10-02", "on": "2024-10-02",
}, },
expEvent: item.Event{ expTask: item.Task{
ID: eid, ID: tskID,
Date: item.NewDate(2024, 10, 2), Date: item.NewDate(2024, 10, 2),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: title, Title: title,
Time: aTime, Time: aTime,
Duration: oneHour, Duration: oneHour,
@ -100,10 +100,10 @@ func TestUpdateExecute(t *testing.T) {
flags: map[string]string{ flags: map[string]string{
"at": "11:00", "at": "11:00",
}, },
expEvent: item.Event{ expTask: item.Task{
ID: eid, ID: tskID,
Date: item.NewDate(2024, 10, 6), Date: item.NewDate(2024, 10, 6),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: title, Title: title,
Time: item.NewTime(11, 0), Time: item.NewTime(11, 0),
Duration: oneHour, Duration: oneHour,
@ -118,10 +118,10 @@ func TestUpdateExecute(t *testing.T) {
"on": "2024-10-02", "on": "2024-10-02",
"at": "11:00", "at": "11:00",
}, },
expEvent: item.Event{ expTask: item.Task{
ID: eid, ID: tskID,
Date: item.NewDate(2024, 10, 2), Date: item.NewDate(2024, 10, 2),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: title, Title: title,
Time: item.NewTime(11, 0), Time: item.NewTime(11, 0),
Duration: oneHour, Duration: oneHour,
@ -144,10 +144,10 @@ func TestUpdateExecute(t *testing.T) {
flags: map[string]string{ flags: map[string]string{
"for": "2h", "for": "2h",
}, },
expEvent: item.Event{ expTask: item.Task{
ID: eid, ID: tskID,
Date: item.NewDate(2024, 10, 6), Date: item.NewDate(2024, 10, 6),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: title, Title: title,
Time: aTime, Time: aTime,
Duration: twoHour, Duration: twoHour,
@ -168,11 +168,11 @@ func TestUpdateExecute(t *testing.T) {
flags: map[string]string{ flags: map[string]string{
"rec": "2024-12-08, daily", "rec": "2024-12-08, daily",
}, },
expEvent: item.Event{ expTask: item.Task{
ID: eid, ID: tskID,
Date: aDate, Date: aDate,
Recurrer: item.NewRecurrer("2024-12-08, daily"), Recurrer: item.NewRecurrer("2024-12-08, daily"),
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: title, Title: title,
Time: aTime, Time: aTime,
Duration: oneHour, Duration: oneHour,
@ -181,13 +181,13 @@ func TestUpdateExecute(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
eventRepo := memory.NewEvent() taskRepo := memory.NewTask()
localIDRepo := memory.NewLocalID() localIDRepo := memory.NewLocalID()
syncRepo := memory.NewSync() syncRepo := memory.NewSync()
if err := eventRepo.Store(item.Event{ if err := taskRepo.Store(item.Task{
ID: eid, ID: tskID,
Date: aDate, Date: aDate,
EventBody: item.EventBody{ TaskBody: item.TaskBody{
Title: title, Title: title,
Time: aTime, Time: aTime,
Duration: oneHour, Duration: oneHour,
@ -195,11 +195,11 @@ func TestUpdateExecute(t *testing.T) {
}); err != nil { }); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
if err := localIDRepo.Store(eid, lid); err != nil { if err := localIDRepo.Store(tskID, lid); err != nil {
t.Errorf("exp nil, ,got %v", err) t.Errorf("exp nil, ,got %v", err)
} }
cmd := command.NewUpdate(localIDRepo, eventRepo, syncRepo) cmd := command.NewUpdate(localIDRepo, taskRepo, syncRepo)
actParseErr := cmd.Execute(tc.main, tc.flags) != nil actParseErr := cmd.Execute(tc.main, tc.flags) != nil
if tc.expErr != actParseErr { if tc.expErr != actParseErr {
t.Errorf("exp %v, got %v", tc.expErr, actParseErr) t.Errorf("exp %v, got %v", tc.expErr, actParseErr)
@ -208,11 +208,11 @@ func TestUpdateExecute(t *testing.T) {
return return
} }
actEvent, err := eventRepo.Find(eid) actTask, err := taskRepo.Find(tskID)
if err != nil { if err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
if diff := item.EventDiff(tc.expEvent, actEvent); diff != "" { if diff := item.TaskDiff(tc.expTask, actTask); diff != "" {
t.Errorf("(exp -, got +)\n%s", diff) t.Errorf("(exp -, got +)\n%s", diff)
} }
updated, err := syncRepo.FindAll() updated, err := syncRepo.FindAll()

View File

@ -27,7 +27,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
localIDRepo, eventRepo, syncRepo, err := sqlite.NewSqlites(conf.DBPath) localIDRepo, taskRepo, syncRepo, err := sqlite.NewSqlites(conf.DBPath)
if err != nil { if err != nil {
fmt.Printf("could not open db file: %s\n", err) fmt.Printf("could not open db file: %s\n", err)
os.Exit(1) os.Exit(1)
@ -37,11 +37,11 @@ func main() {
cli := command.CLI{ cli := command.CLI{
Commands: []command.Command{ Commands: []command.Command{
command.NewAdd(localIDRepo, eventRepo, syncRepo), command.NewAdd(localIDRepo, taskRepo, syncRepo),
command.NewList(localIDRepo, eventRepo), command.NewList(localIDRepo, taskRepo),
command.NewUpdate(localIDRepo, eventRepo, syncRepo), command.NewUpdate(localIDRepo, taskRepo, syncRepo),
command.NewDelete(localIDRepo, eventRepo, syncRepo), command.NewDelete(localIDRepo, taskRepo, syncRepo),
command.NewSync(syncClient, syncRepo, localIDRepo, eventRepo), command.NewSync(syncClient, syncRepo, localIDRepo, taskRepo),
}, },
} }

View File

@ -1,67 +0,0 @@
package memory
import (
"sort"
"sync"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/storage"
)
type Event struct {
events map[string]item.Event
mutex sync.RWMutex
}
func NewEvent() *Event {
return &Event{
events: make(map[string]item.Event),
}
}
func (r *Event) Find(id string) (item.Event, error) {
r.mutex.RLock()
defer r.mutex.RUnlock()
event, exists := r.events[id]
if !exists {
return item.Event{}, storage.ErrNotFound
}
return event, nil
}
func (r *Event) FindAll() ([]item.Event, error) {
r.mutex.RLock()
defer r.mutex.RUnlock()
events := make([]item.Event, 0, len(r.events))
for _, event := range r.events {
events = append(events, event)
}
sort.Slice(events, func(i, j int) bool {
return events[i].ID < events[j].ID
})
return events, nil
}
func (r *Event) Store(e item.Event) error {
r.mutex.Lock()
defer r.mutex.Unlock()
r.events[e.ID] = e
return nil
}
func (r *Event) Delete(id string) error {
r.mutex.Lock()
defer r.mutex.Unlock()
if _, exists := r.events[id]; !exists {
return storage.ErrNotFound
}
delete(r.events, id)
return nil
}

View File

@ -0,0 +1,67 @@
package memory
import (
"sort"
"sync"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/storage"
)
type Task struct {
tasks map[string]item.Task
mutex sync.RWMutex
}
func NewTask() *Task {
return &Task{
tasks: make(map[string]item.Task),
}
}
func (t *Task) Find(id string) (item.Task, error) {
t.mutex.RLock()
defer t.mutex.RUnlock()
task, exists := t.tasks[id]
if !exists {
return item.Task{}, storage.ErrNotFound
}
return task, nil
}
func (t *Task) FindAll() ([]item.Task, error) {
t.mutex.RLock()
defer t.mutex.RUnlock()
tasks := make([]item.Task, 0, len(t.tasks))
for _, event := range t.tasks {
tasks = append(tasks, event)
}
sort.Slice(tasks, func(i, j int) bool {
return tasks[i].ID < tasks[j].ID
})
return tasks, nil
}
func (t *Task) Store(tsk item.Task) error {
t.mutex.Lock()
defer t.mutex.Unlock()
t.tasks[tsk.ID] = tsk
return nil
}
func (t *Task) Delete(id string) error {
t.mutex.Lock()
defer t.mutex.Unlock()
if _, exists := t.tasks[id]; !exists {
return storage.ErrNotFound
}
delete(t.tasks, id)
return nil
}

View File

@ -6,50 +6,50 @@ import (
"go-mod.ewintr.nl/planner/item" "go-mod.ewintr.nl/planner/item"
) )
func TestEvent(t *testing.T) { func TestTask(t *testing.T) {
t.Parallel() t.Parallel()
mem := NewEvent() mem := NewTask()
t.Log("empty") t.Log("empty")
actEvents, actErr := mem.FindAll() actTasks, actErr := mem.FindAll()
if actErr != nil { if actErr != nil {
t.Errorf("exp nil, got %v", actErr) t.Errorf("exp nil, got %v", actErr)
} }
if len(actEvents) != 0 { if len(actTasks) != 0 {
t.Errorf("exp 0, got %d", len(actEvents)) t.Errorf("exp 0, got %d", len(actTasks))
} }
t.Log("store") t.Log("store")
e1 := item.Event{ tsk1 := item.Task{
ID: "id-1", ID: "id-1",
} }
if err := mem.Store(e1); err != nil { if err := mem.Store(tsk1); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
e2 := item.Event{ tsk2 := item.Task{
ID: "id-2", ID: "id-2",
} }
if err := mem.Store(e2); err != nil { if err := mem.Store(tsk2); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
} }
t.Log("find one") t.Log("find one")
actEvent, actErr := mem.Find(e1.ID) actTask, actErr := mem.Find(tsk1.ID)
if actErr != nil { if actErr != nil {
t.Errorf("exp nil, got %v", actErr) t.Errorf("exp nil, got %v", actErr)
} }
if actEvent.ID != e1.ID { if actTask.ID != tsk1.ID {
t.Errorf("exp %v, got %v", e1.ID, actEvent.ID) t.Errorf("exp %v, got %v", tsk1.ID, actTask.ID)
} }
t.Log("find all") t.Log("find all")
actEvents, actErr = mem.FindAll() actTasks, actErr = mem.FindAll()
if actErr != nil { if actErr != nil {
t.Errorf("exp nil, got %v", actErr) t.Errorf("exp nil, got %v", actErr)
} }
if diff := item.EventDiffs([]item.Event{e1, e2}, actEvents); diff != "" { if diff := item.TaskDiffs([]item.Task{tsk1, tsk2}, actTasks); diff != "" {
t.Errorf("(exp -, got +)\n%s", diff) t.Errorf("(exp -, got +)\n%s", diff)
} }
} }

View File

@ -1,114 +0,0 @@
package sqlite
import (
"database/sql"
"fmt"
"time"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/storage"
)
type SqliteEvent struct {
db *sql.DB
}
func (s *SqliteEvent) Store(event item.Event) error {
var recurStr string
if event.Recurrer != nil {
recurStr = event.Recurrer.String()
}
if _, err := s.db.Exec(`
INSERT INTO events
(id, title, date, time, duration, recurrer)
VALUES
(?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE
SET
title=?,
date=?,
time=?,
duration=?,
recurrer=?
`,
event.ID, event.Title, event.Date.String(), event.Time.String(), event.Duration.String(), recurStr,
event.Title, event.Date.String(), event.Time.String(), event.Duration.String(), recurStr); err != nil {
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
return nil
}
func (s *SqliteEvent) Find(id string) (item.Event, error) {
var event item.Event
var dateStr, timeStr, recurStr, durStr string
err := s.db.QueryRow(`
SELECT id, title, date, time, duration, recurrer
FROM events
WHERE id = ?`, id).Scan(&event.ID, &event.Title, &dateStr, &timeStr, &durStr, &recurStr)
switch {
case err == sql.ErrNoRows:
return item.Event{}, fmt.Errorf("event not found: %w", err)
case err != nil:
return item.Event{}, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
event.Date = item.NewDateFromString(dateStr)
event.Time = item.NewTimeFromString(timeStr)
dur, err := time.ParseDuration(durStr)
if err != nil {
return item.Event{}, fmt.Errorf("could not unmarshal recurrer: %v", err)
}
event.Duration = dur
event.Recurrer = item.NewRecurrer(recurStr)
return event, nil
}
func (s *SqliteEvent) FindAll() ([]item.Event, error) {
rows, err := s.db.Query(`
SELECT id, title, date, time, duration, recurrer
FROM events`)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
result := make([]item.Event, 0)
defer rows.Close()
for rows.Next() {
var event item.Event
var dateStr, timeStr, recurStr, durStr string
if err := rows.Scan(&event.ID, &event.Title, &dateStr, &timeStr, &durStr, &recurStr); err != nil {
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
dur, err := time.ParseDuration(durStr)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
event.Date = item.NewDateFromString(dateStr)
event.Time = item.NewTimeFromString(timeStr)
event.Duration = dur
event.Recurrer = item.NewRecurrer(recurStr)
result = append(result, event)
}
return result, nil
}
func (s *SqliteEvent) Delete(id string) error {
result, err := s.db.Exec(`
DELETE FROM events
WHERE id = ?`, id)
if err != nil {
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
if rowsAffected == 0 {
return storage.ErrNotFound
}
return nil
}

View File

@ -42,6 +42,7 @@ var migrations = []string{
`ALTER TABLE events ADD COLUMN date TEXT NOT NULL DEFAULT ''`, `ALTER TABLE events ADD COLUMN date TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE events ADD COLUMN time TEXT NOT NULL DEFAULT ''`, `ALTER TABLE events ADD COLUMN time TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE items ADD COLUMN recur_next TEXT NOT NULL DEFAULT ''`, `ALTER TABLE items ADD COLUMN recur_next TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE events RENAME TO tasks`,
} }
var ( var (
@ -51,7 +52,7 @@ var (
ErrSqliteFailure = errors.New("sqlite returned an error") ErrSqliteFailure = errors.New("sqlite returned an error")
) )
func NewSqlites(dbPath string) (*LocalID, *SqliteEvent, *SqliteSync, error) { func NewSqlites(dbPath string) (*LocalID, *SqliteTask, *SqliteSync, error) {
db, err := sql.Open("sqlite", dbPath) db, err := sql.Open("sqlite", dbPath)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("%w: %v", ErrInvalidConfiguration, err) return nil, nil, nil, fmt.Errorf("%w: %v", ErrInvalidConfiguration, err)
@ -60,7 +61,7 @@ func NewSqlites(dbPath string) (*LocalID, *SqliteEvent, *SqliteSync, error) {
sl := &LocalID{ sl := &LocalID{
db: db, db: db,
} }
se := &SqliteEvent{ se := &SqliteTask{
db: db, db: db,
} }
ss := &SqliteSync{ ss := &SqliteSync{

114
plan/storage/sqlite/task.go Normal file
View File

@ -0,0 +1,114 @@
package sqlite
import (
"database/sql"
"fmt"
"time"
"go-mod.ewintr.nl/planner/item"
"go-mod.ewintr.nl/planner/plan/storage"
)
type SqliteTask struct {
db *sql.DB
}
func (t *SqliteTask) Store(tsk item.Task) error {
var recurStr string
if tsk.Recurrer != nil {
recurStr = tsk.Recurrer.String()
}
if _, err := t.db.Exec(`
INSERT INTO tasks
(id, title, date, time, duration, recurrer)
VALUES
(?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE
SET
title=?,
date=?,
time=?,
duration=?,
recurrer=?
`,
tsk.ID, tsk.Title, tsk.Date.String(), tsk.Time.String(), tsk.Duration.String(), recurStr,
tsk.Title, tsk.Date.String(), tsk.Time.String(), tsk.Duration.String(), recurStr); err != nil {
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
return nil
}
func (t *SqliteTask) Find(id string) (item.Task, error) {
var tsk item.Task
var dateStr, timeStr, recurStr, durStr string
err := t.db.QueryRow(`
SELECT id, title, date, time, duration, recurrer
FROM tasks
WHERE id = ?`, id).Scan(&tsk.ID, &tsk.Title, &dateStr, &timeStr, &durStr, &recurStr)
switch {
case err == sql.ErrNoRows:
return item.Task{}, fmt.Errorf("event not found: %w", err)
case err != nil:
return item.Task{}, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
tsk.Date = item.NewDateFromString(dateStr)
tsk.Time = item.NewTimeFromString(timeStr)
dur, err := time.ParseDuration(durStr)
if err != nil {
return item.Task{}, fmt.Errorf("could not unmarshal recurrer: %v", err)
}
tsk.Duration = dur
tsk.Recurrer = item.NewRecurrer(recurStr)
return tsk, nil
}
func (t *SqliteTask) FindAll() ([]item.Task, error) {
rows, err := t.db.Query(`
SELECT id, title, date, time, duration, recurrer
FROM tasks`)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
result := make([]item.Task, 0)
defer rows.Close()
for rows.Next() {
var tsk item.Task
var dateStr, timeStr, recurStr, durStr string
if err := rows.Scan(&tsk.ID, &tsk.Title, &dateStr, &timeStr, &durStr, &recurStr); err != nil {
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
dur, err := time.ParseDuration(durStr)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
tsk.Date = item.NewDateFromString(dateStr)
tsk.Time = item.NewTimeFromString(timeStr)
tsk.Duration = dur
tsk.Recurrer = item.NewRecurrer(recurStr)
result = append(result, tsk)
}
return result, nil
}
func (s *SqliteTask) Delete(id string) error {
result, err := s.db.Exec(`
DELETE FROM tasks
WHERE id = ?`, id)
if err != nil {
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
}
if rowsAffected == 0 {
return storage.ErrNotFound
}
return nil
}

View File

@ -27,10 +27,10 @@ type Sync interface {
LastUpdate() (time.Time, error) LastUpdate() (time.Time, error)
} }
type Event interface { type Task interface {
Store(event item.Event) error Store(task item.Task) error
Find(id string) (item.Event, error) Find(id string) (item.Task, error)
FindAll() ([]item.Event, error) FindAll() ([]item.Task, error)
Delete(id string) error Delete(id string) error
} }

View File

@ -16,9 +16,9 @@ func TestMemory(t *testing.T) {
now := time.Now() now := time.Now()
items := []item.Item{ items := []item.Item{
{ID: "a", Kind: item.KindTask, Updated: now.Add(-15 * time.Minute)}, {ID: "a", Kind: item.KindSchedule, Updated: now.Add(-15 * time.Minute)},
{ID: "b", Kind: item.KindEvent, Updated: now.Add(-10 * time.Minute)}, {ID: "b", Kind: item.KindTask, Updated: now.Add(-10 * time.Minute)},
{ID: "c", Kind: item.KindTask, Updated: now.Add(-5 * time.Minute)}, {ID: "c", Kind: item.KindSchedule, Updated: now.Add(-5 * time.Minute)},
} }
if err := mem.Update(items); err != nil { if err := mem.Update(items); err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
@ -37,12 +37,12 @@ func TestMemory(t *testing.T) {
}, },
{ {
name: "kind", name: "kind",
ks: []item.Kind{item.KindEvent}, ks: []item.Kind{item.KindTask},
expItems: []item.Item{items[1]}, expItems: []item.Item{items[1]},
}, },
{ {
name: "timestamp", name: "timestamp",
ks: []item.Kind{item.KindTask, item.KindEvent}, ks: []item.Kind{item.KindSchedule, item.KindTask},
ts: now.Add(-10 * time.Minute), ts: now.Add(-10 * time.Minute),
expItems: items[1:], expItems: items[1:],
}, },

View File

@ -59,9 +59,9 @@ func TestSyncGet(t *testing.T) {
mem := NewMemory() mem := NewMemory()
items := []item.Item{ items := []item.Item{
{ID: "id-0", Kind: item.KindEvent, Updated: now.Add(-10 * time.Minute)}, {ID: "id-0", Kind: item.KindTask, Updated: now.Add(-10 * time.Minute)},
{ID: "id-1", Kind: item.KindEvent, Updated: now.Add(-5 * time.Minute)}, {ID: "id-1", Kind: item.KindTask, Updated: now.Add(-5 * time.Minute)},
{ID: "id-2", Kind: item.KindTask, Updated: now.Add(time.Minute)}, {ID: "id-2", Kind: item.KindSchedule, Updated: now.Add(time.Minute)},
} }
for _, item := range items { for _, item := range items {
@ -93,7 +93,7 @@ func TestSyncGet(t *testing.T) {
}, },
{ {
name: "kind", name: "kind",
ks: []string{string(item.KindTask)}, ks: []string{string(item.KindSchedule)},
expStatus: http.StatusOK, expStatus: http.StatusOK,
expItems: []item.Item{items[2]}, expItems: []item.Item{items[2]},
}, },
@ -171,20 +171,20 @@ func TestSyncPost(t *testing.T) {
{ {
name: "invalid item", name: "invalid item",
reqBody: []byte(`[ reqBody: []byte(`[
{"id":"id-1","kind":"event","updated":"2024-09-06T08:00:00Z"}, {"id":"id-1","kind":"task","updated":"2024-09-06T08:00:00Z"},
]`), ]`),
expStatus: http.StatusBadRequest, expStatus: http.StatusBadRequest,
}, },
{ {
name: "normal", name: "normal",
reqBody: []byte(`[ reqBody: []byte(`[
{"id":"id-1","kind":"event","updated":"2024-09-06T08:00:00Z","deleted":false,"body":"item"}, {"id":"id-1","kind":"task","updated":"2024-09-06T08:00:00Z","deleted":false,"body":"item"},
{"id":"id-2","kind":"event","updated":"2024-09-06T08:12:00Z","deleted":false,"body":"item2"} {"id":"id-2","kind":"task","updated":"2024-09-06T08:12:00Z","deleted":false,"body":"item2"}
]`), ]`),
expStatus: http.StatusNoContent, expStatus: http.StatusNoContent,
expItems: []item.Item{ expItems: []item.Item{
{ID: "id-1", Kind: item.KindEvent, Updated: time.Date(2024, 9, 6, 8, 0, 0, 0, time.UTC)}, {ID: "id-1", Kind: item.KindTask, Updated: time.Date(2024, 9, 6, 8, 0, 0, 0, time.UTC)},
{ID: "id-2", Kind: item.KindEvent, Updated: time.Date(2024, 9, 6, 12, 0, 0, 0, time.UTC)}, {ID: "id-2", Kind: item.KindTask, Updated: time.Date(2024, 9, 6, 12, 0, 0, 0, time.UTC)},
}, },
}, },
} { } {

View File

@ -27,6 +27,7 @@ var migrations = []string{
ALTER COLUMN recur_next SET NOT NULL, ALTER COLUMN recur_next SET NOT NULL,
ALTER COLUMN recur_next SET DEFAULT ''`, ALTER COLUMN recur_next SET DEFAULT ''`,
`ALTER TABLE items ADD COLUMN date TEXT NOT NULL DEFAULT ''`, `ALTER TABLE items ADD COLUMN date TEXT NOT NULL DEFAULT ''`,
`UPDATE items SET kind='task'`,
} }
var ( var (

View File

@ -19,12 +19,12 @@ func TestRecur(t *testing.T) {
testItem := item.Item{ testItem := item.Item{
ID: "test-1", ID: "test-1",
Kind: item.KindEvent, Kind: item.KindTask,
Updated: now, Updated: now,
Deleted: false, Deleted: false,
Recurrer: item.NewRecurrer("2024-01-01, daily"), Recurrer: item.NewRecurrer("2024-01-01, daily"),
RecurNext: today, RecurNext: today,
Body: `{"title":"Test Event","start":"2024-01-01T10:00:00Z","duration":"30m"}`, Body: `{"title":"Test task","start":"2024-01-01T10:00:00Z","duration":"30m"}`,
} }
// Store the item // Store the item
@ -38,7 +38,7 @@ func TestRecur(t *testing.T) {
} }
// Verify results // Verify results
items, err := mem.Updated([]item.Kind{item.KindEvent}, now) items, err := mem.Updated([]item.Kind{item.KindTask}, now)
if err != nil { if err != nil {
t.Errorf("failed to get updated items: %v", err) t.Errorf("failed to get updated items: %v", err)
} }