From f3c64cca5aa600e2a19f69873c7c0cbd72d707a4 Mon Sep 17 00:00:00 2001 From: Erik Winter Date: Thu, 29 Jul 2021 07:01:24 +0200 Subject: [PATCH] update command --- cmd/cli/command/command.go | 28 ++++++++++- cmd/cli/command/done.go | 16 +----- cmd/cli/command/update.go | 86 +++++++++++++++++++++++++++++++++ cmd/cli/command/update_test.go | 64 ++++++++++++++++++++++++ internal/process/update.go | 17 +++++-- internal/process/update_test.go | 69 ++++++++++++++++++-------- 6 files changed, 241 insertions(+), 39 deletions(-) create mode 100644 cmd/cli/command/update.go create mode 100644 cmd/cli/command/update_test.go diff --git a/cmd/cli/command/command.go b/cmd/cli/command/command.go index 255328b..992babe 100644 --- a/cmd/cli/command/command.go +++ b/cmd/cli/command/command.go @@ -2,13 +2,16 @@ package command import ( "errors" + "fmt" "strconv" "git.ewintr.nl/gte/internal/configuration" + "git.ewintr.nl/gte/internal/storage" ) var ( ErrInvalidAmountOfArgs = errors.New("invalid amount of args") + ErrCouldNotFindTask = errors.New("could not find task") ) type Command interface { @@ -47,11 +50,34 @@ func parseTaskCommand(id int, tArgs []string, conf *configuration.Configuration) return NewShow(id, conf) } - cmd, _ := tArgs[0], tArgs[1:] + cmd, cmdArgs := tArgs[0], tArgs[1:] switch cmd { case "done": + fallthrough + case "del": return NewDone(id, conf) + case "mod": + return NewUpdate(id, conf, cmdArgs) default: return NewShow(id, conf) } } + +func findId(id int, local storage.LocalRepository) (string, error) { + localIds, err := local.LocalIds() + if err != nil { + return "", fmt.Errorf("%w: %v", ErrCouldNotFindTask, err) + } + var tId string + for remoteId, localId := range localIds { + if localId == id { + tId = remoteId + break + } + } + if tId == "" { + return "", ErrCouldNotFindTask + } + + return tId, nil +} diff --git a/cmd/cli/command/done.go b/cmd/cli/command/done.go index aae1fab..34275b7 100644 --- a/cmd/cli/command/done.go +++ b/cmd/cli/command/done.go @@ -1,8 +1,6 @@ package command import ( - "fmt" - "git.ewintr.nl/gte/cmd/cli/format" "git.ewintr.nl/gte/internal/configuration" "git.ewintr.nl/gte/internal/process" @@ -17,7 +15,7 @@ type Done struct { func (d *Done) Cmd() string { return "done" } -func NewDone(id int, conf *configuration.Configuration) (*Done, error) { +func NewDone(localId int, conf *configuration.Configuration) (*Done, error) { local, err := storage.NewSqlite(conf.Sqlite()) if err != nil { return &Done{}, err @@ -25,20 +23,10 @@ func NewDone(id int, conf *configuration.Configuration) (*Done, error) { disp := storage.NewDispatcher(msend.NewSSLSMTP(conf.SMTP())) fields := process.UpdateFields{"done": "true"} - localIds, err := local.LocalIds() + tId, err := findId(localId, local) if err != nil { return &Done{}, err } - var tId string - for remoteId, localId := range localIds { - if localId == id { - tId = remoteId - break - } - } - if tId == "" { - return &Done{}, fmt.Errorf("could not find task") - } updater := process.NewUpdate(local, disp, tId, fields) diff --git a/cmd/cli/command/update.go b/cmd/cli/command/update.go new file mode 100644 index 0000000..9471c7e --- /dev/null +++ b/cmd/cli/command/update.go @@ -0,0 +1,86 @@ +package command + +import ( + "errors" + "fmt" + "strings" + + "git.ewintr.nl/gte/cmd/cli/format" + "git.ewintr.nl/gte/internal/configuration" + "git.ewintr.nl/gte/internal/process" + "git.ewintr.nl/gte/internal/storage" + "git.ewintr.nl/gte/internal/task" + "git.ewintr.nl/gte/pkg/msend" +) + +var ( + ErrFieldAlreadyUsed = errors.New("field was already used") +) + +type Update struct { + updater *process.Update +} + +func (u *Update) Cmd() string { return "update" } + +func NewUpdate(localId int, conf *configuration.Configuration, cmdArgs []string) (*Update, error) { + local, err := storage.NewSqlite(conf.Sqlite()) + if err != nil { + return &Update{}, err + } + + disp := storage.NewDispatcher(msend.NewSSLSMTP(conf.SMTP())) + fields, err := ParseTaskFieldArgs(cmdArgs) + if err != nil { + return &Update{}, err + } + tId, err := findId(localId, local) + if err != nil { + return &Update{}, err + } + + updater := process.NewUpdate(local, disp, tId, fields) + + return &Update{ + updater: updater, + }, nil +} + +func (u *Update) Do() string { + if err := u.updater.Process(); err != nil { + return format.FormatError(err) + } + + return "message sent\n" +} + +func ParseTaskFieldArgs(args []string) (process.UpdateFields, error) { + result := process.UpdateFields{} + + var action []string + for _, f := range args { + split := strings.SplitN(f, ":", 2) + if len(split) == 2 { + switch split[0] { + case "project": + if _, ok := result[task.FIELD_PROJECT]; ok { + return process.UpdateFields{}, fmt.Errorf("%w: %s", ErrFieldAlreadyUsed, task.FIELD_PROJECT) + } + result[task.FIELD_PROJECT] = split[1] + case "due": + if _, ok := result[task.FIELD_DUE]; ok { + return process.UpdateFields{}, fmt.Errorf("%w: %s", ErrFieldAlreadyUsed, task.FIELD_DUE) + } + result[task.FIELD_DUE] = split[1] + } + } else { + action = append(action, f) + } + } + + if len(action) > 0 { + result[task.FIELD_ACTION] = strings.Join(action, " ") + } + + return result, nil +} diff --git a/cmd/cli/command/update_test.go b/cmd/cli/command/update_test.go new file mode 100644 index 0000000..7174bea --- /dev/null +++ b/cmd/cli/command/update_test.go @@ -0,0 +1,64 @@ +package command_test + +import ( + "errors" + "strings" + "testing" + + "git.ewintr.nl/go-kit/test" + "git.ewintr.nl/gte/cmd/cli/command" + "git.ewintr.nl/gte/internal/process" + "git.ewintr.nl/gte/internal/task" +) + +func TestParseTaskFieldArgs(t *testing.T) { + for _, tc := range []struct { + name string + input string + expField process.UpdateFields + expErr error + }{ + { + name: "empty", + expField: process.UpdateFields{ + task.FIELD_ACTION: "", + }, + }, + { + name: "join action", + input: "some things to do", + expField: process.UpdateFields{ + task.FIELD_ACTION: "some things to do", + }, + }, + { + name: "all", + input: "project:project do stuff due:2021-08-06", + expField: process.UpdateFields{ + task.FIELD_ACTION: "do stuff", + task.FIELD_PROJECT: "project", + task.FIELD_DUE: "2021-08-06", + }, + }, + { + name: "no action", + input: "due:2021-08-06", + expField: process.UpdateFields{ + task.FIELD_DUE: "2021-08-06", + }, + }, + { + name: "two projects", + input: "project:project1 project:project2", + expField: process.UpdateFields{}, + expErr: command.ErrFieldAlreadyUsed, + }, + } { + t.Run(tc.name, func(t *testing.T) { + args := strings.Split(tc.input, " ") + act, err := command.ParseTaskFieldArgs(args) + test.Equals(t, tc.expField, act) + test.Assert(t, errors.Is(err, tc.expErr), "wrong err") + }) + } +} diff --git a/internal/process/update.go b/internal/process/update.go index cbcbc9f..90816a3 100644 --- a/internal/process/update.go +++ b/internal/process/update.go @@ -5,10 +5,11 @@ import ( "fmt" "git.ewintr.nl/gte/internal/storage" + "git.ewintr.nl/gte/internal/task" ) var ( - ErrUpdateTask = errors.New("could not update task") + ErrUpdateTask = errors.New("could not update tsk") ) // Update dispatches an updated version of a task @@ -31,21 +32,27 @@ func NewUpdate(local storage.LocalRepository, disp *storage.Dispatcher, taskId s } func (u *Update) Process() error { - task, err := u.local.FindById(u.taskId) + tsk, err := u.local.FindById(u.taskId) if err != nil { return fmt.Errorf("%w: %v", ErrUpdateTask, err) } for k, v := range u.updates { switch k { - case "done": + case task.FIELD_DONE: if v == "true" { - task.Done = true + tsk.Done = true } + case task.FIELD_DUE: + tsk.Due = task.NewDateFromString(v) + case task.FIELD_ACTION: + tsk.Action = v + case task.FIELD_PROJECT: + tsk.Project = v } } - if err := u.disp.Dispatch(task); err != nil { + if err := u.disp.Dispatch(tsk); err != nil { return fmt.Errorf("%w: %v", ErrUpdateTask, err) } diff --git a/internal/process/update_test.go b/internal/process/update_test.go index ec1a5f3..450ca33 100644 --- a/internal/process/update_test.go +++ b/internal/process/update_test.go @@ -15,29 +15,60 @@ func TestUpdate(t *testing.T) { Id: "id-1", Project: "project1", Action: "action1", + Due: task.NewDate(2021, 7, 29), Folder: task.FOLDER_PLANNED, } local := storage.NewMemory() - out := msend.NewMemory() - disp := storage.NewDispatcher(out) allTasks := []*task.Task{task1} - t.Run("done", func(t *testing.T) { - local.SetTasks(allTasks) - updates := process.UpdateFields{ - "done": "true", - } + for _, tc := range []struct { + name string + updates process.UpdateFields + exp *task.Task + }{ + { + name: "done", + updates: process.UpdateFields{ + task.FIELD_DONE: "true", + }, + exp: &task.Task{ + Id: "id-1", + Project: "project1", + Action: "action1", + Due: task.NewDate(2021, 7, 29), + Folder: task.FOLDER_PLANNED, + Done: true, + }, + }, + { + name: "fields", + updates: process.UpdateFields{ + task.FIELD_PROJECT: "project2", + task.FIELD_ACTION: "action2", + task.FIELD_DUE: "2021-08-01", + }, + exp: &task.Task{ + Id: "id-1", + Project: "project2", + Action: "action2", + Due: task.NewDate(2021, 8, 1), + Folder: task.FOLDER_PLANNED, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + local.SetTasks(allTasks) + out := msend.NewMemory() + disp := storage.NewDispatcher(out) - update := process.NewUpdate(local, disp, task1.Id, updates) - test.OK(t, update.Process()) - expTask := task1 - expTask.Done = true - expMsg := &msend.Message{ - Subject: expTask.FormatSubject(), - Body: expTask.FormatBody(), - } - test.Assert(t, len(out.Messages) == 1, "amount of messages was not one") - test.Equals(t, expMsg, out.Messages[0]) - - }) + update := process.NewUpdate(local, disp, task1.Id, tc.updates) + test.OK(t, update.Process()) + expMsg := &msend.Message{ + Subject: tc.exp.FormatSubject(), + Body: tc.exp.FormatBody(), + } + test.Assert(t, len(out.Messages) == 1, "amount of messages was not one") + test.Equals(t, expMsg, out.Messages[0]) + }) + } }