From 487641cba6597ba6e44bf0cde31d896b782a15f8 Mon Sep 17 00:00:00 2001 From: Khushal Bhasin Date: Wed, 21 Jan 2026 14:06:11 +0530 Subject: [PATCH 1/3] refactor: batched commands into single command --- backend/controllers/edit_task.go | 28 +++++- backend/models/request_body.go | 20 ++++ backend/utils/tw/edit_task.go | 158 +++++++++++++++---------------- 3 files changed, 120 insertions(+), 86 deletions(-) diff --git a/backend/controllers/edit_task.go b/backend/controllers/edit_task.go index 1e65a851..c26630d3 100644 --- a/backend/controllers/edit_task.go +++ b/backend/controllers/edit_task.go @@ -120,13 +120,33 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { job := Job{ Name: "Edit Task", Execute: func() error { - logStore.AddLog("INFO", fmt.Sprintf("Editing task ID: %s", taskUUID), uuid, "Edit Task") - err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskUUID, tags, project, start, entry, wait, end, depends, due, recur, annotations) + logStore.AddLog("INFO", fmt.Sprintf("Editing task UUID: %s", taskUUID), uuid, "Edit Task") + + // Construct parameters struct for batched command execution + params := models.EditTaskParams{ + UUID: uuid, + TaskUUID: taskUUID, + Email: email, + EncryptionSecret: encryptionSecret, + Description: description, + Tags: tags, + Project: project, + Start: start, + Entry: entry, + Wait: wait, + End: end, + Depends: depends, + Due: due, + Recur: recur, + Annotations: annotations, + } + + err := tw.EditTaskInTaskwarrior(params) if err != nil { - logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task ID %s: %v", taskUUID, err), uuid, "Edit Task") + logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task UUID %s: %v", taskUUID, err), uuid, "Edit Task") return err } - logStore.AddLog("INFO", fmt.Sprintf("Successfully edited task ID: %s", taskUUID), uuid, "Edit Task") + logStore.AddLog("INFO", fmt.Sprintf("Successfully edited task UUID: %s", taskUUID), uuid, "Edit Task") return nil }, } diff --git a/backend/models/request_body.go b/backend/models/request_body.go index 89126f5c..05df03d9 100644 --- a/backend/models/request_body.go +++ b/backend/models/request_body.go @@ -48,6 +48,26 @@ type EditTaskRequestBody struct { Recur string `json:"recur"` Annotations []Annotation `json:"annotations"` } + +// EditTaskParams encapsulates all parameters needed to edit a task in Taskwarrior +// This struct is used to reduce parameter bloat and improve maintainability +type EditTaskParams struct { + UUID string // User's UUID for Taskwarrior sync + TaskUUID string // Task's permanent UUID (used instead of volatile taskID) + Email string // User's email for temp directory naming + EncryptionSecret string // Encryption secret for Taskwarrior sync + Description string // Task description + Tags []string // Tags to add/remove (prefix with +/-) + Project string // Project name + Start string // Start date + Entry string // Entry date + Wait string // Wait date + End string // End date + Depends []string // Task dependencies (UUIDs) + Due string // Due date + Recur string // Recurrence pattern + Annotations []Annotation // Task annotations +} type CompleteTaskRequestBody struct { Email string `json:"email"` EncryptionSecret string `json:"encryptionSecret"` diff --git a/backend/utils/tw/edit_task.go b/backend/utils/tw/edit_task.go index 3207a5b6..4e71f64d 100644 --- a/backend/utils/tw/edit_task.go +++ b/backend/utils/tw/edit_task.go @@ -9,126 +9,118 @@ import ( "strings" ) -func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string, entry string, wait string, end string, depends []string, due string, recur string, annotations []models.Annotation) error { - if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil { - return fmt.Errorf("error deleting Taskwarrior data: %v", err) - } - tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) +// EditTaskInTaskwarrior edits a task in Taskwarrior using batched command execution +// This function uses a single 'task modify' command for better performance instead of +// spawning multiple shell processes for each field modification. +func EditTaskInTaskwarrior(params models.EditTaskParams) error { + // Create isolated temporary directory for this operation + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", params.Email)) if err != nil { return fmt.Errorf("failed to create temporary directory: %v", err) } defer os.RemoveAll(tempDir) + // Configure Taskwarrior with user's encryption and sync settings origin := os.Getenv("CONTAINER_ORIGIN") - if err := SetTaskwarriorConfig(tempDir, encryptionSecret, origin, uuid); err != nil { + if err := SetTaskwarriorConfig(tempDir, params.EncryptionSecret, origin, params.UUID); err != nil { return err } + // Sync to get latest task data if err := SyncTaskwarrior(tempDir); err != nil { return err } - // Escape the double quotes in the description and format it - if err := utils.ExecCommand("task", taskID, "modify", description); err != nil { - return fmt.Errorf("failed to edit task: %v", err) - } + // Build batched modify command arguments + // Format: task modify ... + modifyArgs := []string{params.TaskUUID, "modify"} - // Handle project - if project != "" { - if err := utils.ExecCommand("task", taskID, "modify", "project:"+project); err != nil { - return fmt.Errorf("failed to set project %s: %v", project, err) - } + // Add description if provided + if params.Description != "" { + modifyArgs = append(modifyArgs, params.Description) } - // Handle wait date - if wait != "" { - // Convert `2025-11-29` -> `2025-11-29T00:00:00` - formattedWait := wait + "T00:00:00" - - if err := utils.ExecCommand("task", taskID, "modify", "wait:"+formattedWait); err != nil { - return fmt.Errorf("failed to set wait date %s: %v", formattedWait, err) - } + // Add project if provided + if params.Project != "" { + modifyArgs = append(modifyArgs, "project:"+params.Project) } - // Handle tags - if len(tags) > 0 { - for _, tag := range tags { - if strings.HasPrefix(tag, "+") { - // Add tag - tagValue := strings.TrimPrefix(tag, "+") - if err := utils.ExecCommand("task", taskID, "modify", "+"+tagValue); err != nil { - return fmt.Errorf("failed to add tag %s: %v", tagValue, err) - } - } else if strings.HasPrefix(tag, "-") { - // Remove tag - tagValue := strings.TrimPrefix(tag, "-") - if err := utils.ExecCommand("task", taskID, "modify", "-"+tagValue); err != nil { - return fmt.Errorf("failed to remove tag %s: %v", tagValue, err) - } - } else { - // Add tag without prefix - if err := utils.ExecCommand("task", taskID, "modify", "+"+tag); err != nil { - return fmt.Errorf("failed to add tag %s: %v", tag, err) - } - } - } + // Add wait date if provided + // Convert date format from YYYY-MM-DD to YYYY-MM-DDTHH:MM:SS + if params.Wait != "" { + formattedWait := params.Wait + "T00:00:00" + modifyArgs = append(modifyArgs, "wait:"+formattedWait) } - // Handle start date - if start != "" { - if err := utils.ExecCommand("task", taskID, "modify", "start:"+start); err != nil { - return fmt.Errorf("failed to set start date %s: %v", start, err) - } + // Add start date if provided + if params.Start != "" { + modifyArgs = append(modifyArgs, "start:"+params.Start) } - // Handle entry date - if entry != "" { - if err := utils.ExecCommand("task", taskID, "modify", "entry:"+entry); err != nil { - return fmt.Errorf("failed to set entry date %s: %v", entry, err) - } + // Add entry date if provided + if params.Entry != "" { + modifyArgs = append(modifyArgs, "entry:"+params.Entry) } - // Handle end date - if end != "" { - if err := utils.ExecCommand("task", taskID, "modify", "end:"+end); err != nil { - return fmt.Errorf("failed to set end date %s: %v", end, err) - } + // Add end date if provided + if params.End != "" { + modifyArgs = append(modifyArgs, "end:"+params.End) } - // Handle depends - always set to ensure clearing works - dependsStr := strings.Join(depends, ",") - if err := utils.ExecCommand("task", taskID, "modify", "depends:"+dependsStr); err != nil { - return fmt.Errorf("failed to set depends %s: %v", dependsStr, err) + // Add dependencies + // Always set to ensure clearing works (empty string clears dependencies) + dependsStr := strings.Join(params.Depends, ",") + modifyArgs = append(modifyArgs, "depends:"+dependsStr) + + // Add due date if provided + // Convert date format from YYYY-MM-DD to YYYY-MM-DDTHH:MM:SS + if params.Due != "" { + formattedDue := params.Due + "T00:00:00" + modifyArgs = append(modifyArgs, "due:"+formattedDue) } - // Handle due date - if due != "" { - // Convert `2025-11-29` -> `2025-11-29T00:00:00` - formattedDue := due + "T00:00:00" + // Add recurrence pattern if provided + // This will automatically set the rtype field in Taskwarrior + if params.Recur != "" { + modifyArgs = append(modifyArgs, "recur:"+params.Recur) + } - if err := utils.ExecCommand("task", taskID, "modify", "due:"+formattedDue); err != nil { - return fmt.Errorf("failed to set due date %s: %v", formattedDue, err) + // Add tags (supports +tag to add, -tag to remove) + // Tags are processed individually as they have special prefix syntax + for _, tag := range params.Tags { + if strings.HasPrefix(tag, "+") { + // Add tag (already has + prefix) + modifyArgs = append(modifyArgs, tag) + } else if strings.HasPrefix(tag, "-") { + // Remove tag (already has - prefix) + modifyArgs = append(modifyArgs, tag) + } else { + // Add tag without prefix + modifyArgs = append(modifyArgs, "+"+tag) } } - // Handle recur - this will automatically set rtype field - if recur != "" { - if err := utils.ExecCommand("task", taskID, "modify", "recur:"+recur); err != nil { - return fmt.Errorf("failed to set recur %s: %v", recur, err) - } + // Execute the batched modify command + // This single command replaces 10+ individual shell process spawns + if err := utils.ExecCommand("task", modifyArgs...); err != nil { + return fmt.Errorf("failed to edit task: %v", err) } - // Handle annotations - if len(annotations) >= 0 { - output, err := utils.ExecCommandForOutputInDir(tempDir, "task", taskID, "export") + // Handle annotations separately as they require individual commands + // Annotations cannot be batched with the modify command + if len(params.Annotations) >= 0 { + // First, remove all existing annotations + output, err := utils.ExecCommandForOutputInDir(tempDir, "task", params.TaskUUID, "export") if err == nil { var tasks []map[string]interface{} if err := json.Unmarshal(output, &tasks); err == nil && len(tasks) > 0 { if existingAnnotations, ok := tasks[0]["annotations"].([]interface{}); ok { + // Remove each existing annotation for _, ann := range existingAnnotations { if annMap, ok := ann.(map[string]interface{}); ok { if desc, ok := annMap["description"].(string); ok { - utils.ExecCommand("task", taskID, "denotate", desc) + // Ignore errors on denotate as annotation might not exist + utils.ExecCommand("task", params.TaskUUID, "denotate", desc) } } } @@ -136,18 +128,20 @@ func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID st } } - for _, annotation := range annotations { + // Add new annotations + for _, annotation := range params.Annotations { if annotation.Description != "" { - if err := utils.ExecCommand("task", taskID, "annotate", annotation.Description); err != nil { + if err := utils.ExecCommand("task", params.TaskUUID, "annotate", annotation.Description); err != nil { return fmt.Errorf("failed to add annotation %s: %v", annotation.Description, err) } } } } - // Sync Taskwarrior again + // Sync changes back to server if err := SyncTaskwarrior(tempDir); err != nil { return err } + return nil } From 9e9d6337aa51330fe668265f80d0324f5ae4e9b3 Mon Sep 17 00:00:00 2001 From: Khushal Bhasin Date: Fri, 23 Jan 2026 22:15:52 +0530 Subject: [PATCH 2/3] fix: removed comments --- backend/controllers/edit_task.go | 1 - backend/models/request_body.go | 32 ++++++++++++++--------------- backend/utils/tw/edit_task.go | 35 -------------------------------- 3 files changed, 15 insertions(+), 53 deletions(-) diff --git a/backend/controllers/edit_task.go b/backend/controllers/edit_task.go index c26630d3..4cef098e 100644 --- a/backend/controllers/edit_task.go +++ b/backend/controllers/edit_task.go @@ -122,7 +122,6 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { Execute: func() error { logStore.AddLog("INFO", fmt.Sprintf("Editing task UUID: %s", taskUUID), uuid, "Edit Task") - // Construct parameters struct for batched command execution params := models.EditTaskParams{ UUID: uuid, TaskUUID: taskUUID, diff --git a/backend/models/request_body.go b/backend/models/request_body.go index 05df03d9..21c62ffa 100644 --- a/backend/models/request_body.go +++ b/backend/models/request_body.go @@ -49,24 +49,22 @@ type EditTaskRequestBody struct { Annotations []Annotation `json:"annotations"` } -// EditTaskParams encapsulates all parameters needed to edit a task in Taskwarrior -// This struct is used to reduce parameter bloat and improve maintainability type EditTaskParams struct { - UUID string // User's UUID for Taskwarrior sync - TaskUUID string // Task's permanent UUID (used instead of volatile taskID) - Email string // User's email for temp directory naming - EncryptionSecret string // Encryption secret for Taskwarrior sync - Description string // Task description - Tags []string // Tags to add/remove (prefix with +/-) - Project string // Project name - Start string // Start date - Entry string // Entry date - Wait string // Wait date - End string // End date - Depends []string // Task dependencies (UUIDs) - Due string // Due date - Recur string // Recurrence pattern - Annotations []Annotation // Task annotations + UUID string + TaskUUID string + Email string + EncryptionSecret string + Description string + Tags []string + Project string + Start string + Entry string + Wait string + End string + Depends []string + Due string + Recur string + Annotations []Annotation } type CompleteTaskRequestBody struct { Email string `json:"email"` diff --git a/backend/utils/tw/edit_task.go b/backend/utils/tw/edit_task.go index 4e71f64d..35654103 100644 --- a/backend/utils/tw/edit_task.go +++ b/backend/utils/tw/edit_task.go @@ -9,117 +9,84 @@ import ( "strings" ) -// EditTaskInTaskwarrior edits a task in Taskwarrior using batched command execution -// This function uses a single 'task modify' command for better performance instead of -// spawning multiple shell processes for each field modification. func EditTaskInTaskwarrior(params models.EditTaskParams) error { - // Create isolated temporary directory for this operation tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", params.Email)) if err != nil { return fmt.Errorf("failed to create temporary directory: %v", err) } defer os.RemoveAll(tempDir) - // Configure Taskwarrior with user's encryption and sync settings origin := os.Getenv("CONTAINER_ORIGIN") if err := SetTaskwarriorConfig(tempDir, params.EncryptionSecret, origin, params.UUID); err != nil { return err } - // Sync to get latest task data if err := SyncTaskwarrior(tempDir); err != nil { return err } - // Build batched modify command arguments - // Format: task modify ... modifyArgs := []string{params.TaskUUID, "modify"} - // Add description if provided if params.Description != "" { modifyArgs = append(modifyArgs, params.Description) } - // Add project if provided if params.Project != "" { modifyArgs = append(modifyArgs, "project:"+params.Project) } - // Add wait date if provided - // Convert date format from YYYY-MM-DD to YYYY-MM-DDTHH:MM:SS if params.Wait != "" { formattedWait := params.Wait + "T00:00:00" modifyArgs = append(modifyArgs, "wait:"+formattedWait) } - // Add start date if provided if params.Start != "" { modifyArgs = append(modifyArgs, "start:"+params.Start) } - // Add entry date if provided if params.Entry != "" { modifyArgs = append(modifyArgs, "entry:"+params.Entry) } - // Add end date if provided if params.End != "" { modifyArgs = append(modifyArgs, "end:"+params.End) } - // Add dependencies - // Always set to ensure clearing works (empty string clears dependencies) dependsStr := strings.Join(params.Depends, ",") modifyArgs = append(modifyArgs, "depends:"+dependsStr) - // Add due date if provided - // Convert date format from YYYY-MM-DD to YYYY-MM-DDTHH:MM:SS if params.Due != "" { formattedDue := params.Due + "T00:00:00" modifyArgs = append(modifyArgs, "due:"+formattedDue) } - // Add recurrence pattern if provided - // This will automatically set the rtype field in Taskwarrior if params.Recur != "" { modifyArgs = append(modifyArgs, "recur:"+params.Recur) } - // Add tags (supports +tag to add, -tag to remove) - // Tags are processed individually as they have special prefix syntax for _, tag := range params.Tags { if strings.HasPrefix(tag, "+") { - // Add tag (already has + prefix) modifyArgs = append(modifyArgs, tag) } else if strings.HasPrefix(tag, "-") { - // Remove tag (already has - prefix) modifyArgs = append(modifyArgs, tag) } else { - // Add tag without prefix modifyArgs = append(modifyArgs, "+"+tag) } } - // Execute the batched modify command - // This single command replaces 10+ individual shell process spawns if err := utils.ExecCommand("task", modifyArgs...); err != nil { return fmt.Errorf("failed to edit task: %v", err) } - // Handle annotations separately as they require individual commands - // Annotations cannot be batched with the modify command if len(params.Annotations) >= 0 { - // First, remove all existing annotations output, err := utils.ExecCommandForOutputInDir(tempDir, "task", params.TaskUUID, "export") if err == nil { var tasks []map[string]interface{} if err := json.Unmarshal(output, &tasks); err == nil && len(tasks) > 0 { if existingAnnotations, ok := tasks[0]["annotations"].([]interface{}); ok { - // Remove each existing annotation for _, ann := range existingAnnotations { if annMap, ok := ann.(map[string]interface{}); ok { if desc, ok := annMap["description"].(string); ok { - // Ignore errors on denotate as annotation might not exist utils.ExecCommand("task", params.TaskUUID, "denotate", desc) } } @@ -128,7 +95,6 @@ func EditTaskInTaskwarrior(params models.EditTaskParams) error { } } - // Add new annotations for _, annotation := range params.Annotations { if annotation.Description != "" { if err := utils.ExecCommand("task", params.TaskUUID, "annotate", annotation.Description); err != nil { @@ -138,7 +104,6 @@ func EditTaskInTaskwarrior(params models.EditTaskParams) error { } } - // Sync changes back to server if err := SyncTaskwarrior(tempDir); err != nil { return err } From a730942dc62ef540d35f6e3f768129cd318d93b6 Mon Sep 17 00:00:00 2001 From: Khushal Bhasin Date: Sat, 24 Jan 2026 23:07:31 +0530 Subject: [PATCH 3/3] fix: removed unused EditTaskParams struct --- backend/controllers/edit_task.go | 36 ++++++++--------- backend/models/request_body.go | 17 -------- backend/utils/tw/edit_task.go | 58 +++++++++++++++------------- backend/utils/tw/taskwarrior_test.go | 8 ++-- 4 files changed, 52 insertions(+), 67 deletions(-) diff --git a/backend/controllers/edit_task.go b/backend/controllers/edit_task.go index 4cef098e..f76e8467 100644 --- a/backend/controllers/edit_task.go +++ b/backend/controllers/edit_task.go @@ -122,25 +122,23 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { Execute: func() error { logStore.AddLog("INFO", fmt.Sprintf("Editing task UUID: %s", taskUUID), uuid, "Edit Task") - params := models.EditTaskParams{ - UUID: uuid, - TaskUUID: taskUUID, - Email: email, - EncryptionSecret: encryptionSecret, - Description: description, - Tags: tags, - Project: project, - Start: start, - Entry: entry, - Wait: wait, - End: end, - Depends: depends, - Due: due, - Recur: recur, - Annotations: annotations, - } - - err := tw.EditTaskInTaskwarrior(params) + err := tw.EditTaskInTaskwarrior( + uuid, + taskUUID, + email, + encryptionSecret, + description, + project, + start, + entry, + wait, + end, + due, + recur, + tags, + depends, + annotations, + ) if err != nil { logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task UUID %s: %v", taskUUID, err), uuid, "Edit Task") return err diff --git a/backend/models/request_body.go b/backend/models/request_body.go index 21c62ffa..b9be6499 100644 --- a/backend/models/request_body.go +++ b/backend/models/request_body.go @@ -49,23 +49,6 @@ type EditTaskRequestBody struct { Annotations []Annotation `json:"annotations"` } -type EditTaskParams struct { - UUID string - TaskUUID string - Email string - EncryptionSecret string - Description string - Tags []string - Project string - Start string - Entry string - Wait string - End string - Depends []string - Due string - Recur string - Annotations []Annotation -} type CompleteTaskRequestBody struct { Email string `json:"email"` EncryptionSecret string `json:"encryptionSecret"` diff --git a/backend/utils/tw/edit_task.go b/backend/utils/tw/edit_task.go index 35654103..59a49054 100644 --- a/backend/utils/tw/edit_task.go +++ b/backend/utils/tw/edit_task.go @@ -9,15 +9,19 @@ import ( "strings" ) -func EditTaskInTaskwarrior(params models.EditTaskParams) error { - tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", params.Email)) +func EditTaskInTaskwarrior( + uuid, taskUUID, email, encryptionSecret, description, project, start, entry, wait, end, due, recur string, + tags, depends []string, + annotations []models.Annotation, +) error { + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) if err != nil { return fmt.Errorf("failed to create temporary directory: %v", err) } defer os.RemoveAll(tempDir) origin := os.Getenv("CONTAINER_ORIGIN") - if err := SetTaskwarriorConfig(tempDir, params.EncryptionSecret, origin, params.UUID); err != nil { + if err := SetTaskwarriorConfig(tempDir, encryptionSecret, origin, uuid); err != nil { return err } @@ -25,46 +29,46 @@ func EditTaskInTaskwarrior(params models.EditTaskParams) error { return err } - modifyArgs := []string{params.TaskUUID, "modify"} + modifyArgs := []string{taskUUID, "modify"} - if params.Description != "" { - modifyArgs = append(modifyArgs, params.Description) + if description != "" { + modifyArgs = append(modifyArgs, description) } - if params.Project != "" { - modifyArgs = append(modifyArgs, "project:"+params.Project) + if project != "" { + modifyArgs = append(modifyArgs, "project:"+project) } - if params.Wait != "" { - formattedWait := params.Wait + "T00:00:00" + if wait != "" { + formattedWait := wait + "T00:00:00" modifyArgs = append(modifyArgs, "wait:"+formattedWait) } - if params.Start != "" { - modifyArgs = append(modifyArgs, "start:"+params.Start) + if start != "" { + modifyArgs = append(modifyArgs, "start:"+start) } - if params.Entry != "" { - modifyArgs = append(modifyArgs, "entry:"+params.Entry) + if entry != "" { + modifyArgs = append(modifyArgs, "entry:"+entry) } - if params.End != "" { - modifyArgs = append(modifyArgs, "end:"+params.End) + if end != "" { + modifyArgs = append(modifyArgs, "end:"+end) } - dependsStr := strings.Join(params.Depends, ",") + dependsStr := strings.Join(depends, ",") modifyArgs = append(modifyArgs, "depends:"+dependsStr) - if params.Due != "" { - formattedDue := params.Due + "T00:00:00" + if due != "" { + formattedDue := due + "T00:00:00" modifyArgs = append(modifyArgs, "due:"+formattedDue) } - if params.Recur != "" { - modifyArgs = append(modifyArgs, "recur:"+params.Recur) + if recur != "" { + modifyArgs = append(modifyArgs, "recur:"+recur) } - for _, tag := range params.Tags { + for _, tag := range tags { if strings.HasPrefix(tag, "+") { modifyArgs = append(modifyArgs, tag) } else if strings.HasPrefix(tag, "-") { @@ -78,8 +82,8 @@ func EditTaskInTaskwarrior(params models.EditTaskParams) error { return fmt.Errorf("failed to edit task: %v", err) } - if len(params.Annotations) >= 0 { - output, err := utils.ExecCommandForOutputInDir(tempDir, "task", params.TaskUUID, "export") + if len(annotations) >= 0 { + output, err := utils.ExecCommandForOutputInDir(tempDir, "task", taskUUID, "export") if err == nil { var tasks []map[string]interface{} if err := json.Unmarshal(output, &tasks); err == nil && len(tasks) > 0 { @@ -87,7 +91,7 @@ func EditTaskInTaskwarrior(params models.EditTaskParams) error { for _, ann := range existingAnnotations { if annMap, ok := ann.(map[string]interface{}); ok { if desc, ok := annMap["description"].(string); ok { - utils.ExecCommand("task", params.TaskUUID, "denotate", desc) + utils.ExecCommand("task", taskUUID, "denotate", desc) } } } @@ -95,9 +99,9 @@ func EditTaskInTaskwarrior(params models.EditTaskParams) error { } } - for _, annotation := range params.Annotations { + for _, annotation := range annotations { if annotation.Description != "" { - if err := utils.ExecCommand("task", params.TaskUUID, "annotate", annotation.Description); err != nil { + if err := utils.ExecCommand("task", taskUUID, "annotate", annotation.Description); err != nil { return fmt.Errorf("failed to add annotation %s: %v", annotation.Description, err) } } diff --git a/backend/utils/tw/taskwarrior_test.go b/backend/utils/tw/taskwarrior_test.go index e980db18..9241c85e 100644 --- a/backend/utils/tw/taskwarrior_test.go +++ b/backend/utils/tw/taskwarrior_test.go @@ -25,7 +25,7 @@ func TestSyncTaskwarrior(t *testing.T) { } func TestEditTaskInATaskwarrior(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", nil, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z", "weekly", []models.Annotation{{Description: "test annotation"}}) + err := EditTaskInTaskwarrior("uuid", "taskuuid", "email", "encryptionSecret", "description", "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", "2025-12-01T18:30:00.000Z", "weekly", []string{}, []string{}, []models.Annotation{{Description: "test annotation"}}) if err != nil { t.Errorf("EditTaskInTaskwarrior() failed: %v", err) } else { @@ -202,7 +202,7 @@ func TestAddTaskToTaskwarriorWithWaitDateWithTags(t *testing.T) { } func TestEditTaskWithTagAddition(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "+important"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z", "daily", []models.Annotation{}) + err := EditTaskInTaskwarrior("uuid", "taskuuid", "email", "encryptionSecret", "description", "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", "2025-12-01T18:30:00.000Z", "daily", []string{"+urgent", "+important"}, []string{}, []models.Annotation{}) if err != nil { t.Errorf("EditTaskInTaskwarrior with tag addition failed: %v", err) } else { @@ -211,7 +211,7 @@ func TestEditTaskWithTagAddition(t *testing.T) { } func TestEditTaskWithTagRemoval(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"-work", "-lowpriority"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z", "monthly", []models.Annotation{}) + err := EditTaskInTaskwarrior("uuid", "taskuuid", "email", "encryptionSecret", "description", "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", "2025-12-01T18:30:00.000Z", "monthly", []string{"-work", "-lowpriority"}, []string{}, []models.Annotation{}) if err != nil { t.Errorf("EditTaskInTaskwarrior with tag removal failed: %v", err) } else { @@ -220,7 +220,7 @@ func TestEditTaskWithTagRemoval(t *testing.T) { } func TestEditTaskWithMixedTagOperations(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "-work", "normal"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z", "yearly", []models.Annotation{}) + err := EditTaskInTaskwarrior("uuid", "taskuuid", "email", "encryptionSecret", "description", "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", "2025-12-01T18:30:00.000Z", "yearly", []string{"+urgent", "-work", "normal"}, []string{}, []models.Annotation{}) if err != nil { t.Errorf("EditTaskInTaskwarrior with mixed tag operations failed: %v", err) } else {