Implement Task operations
Add tasks.go with TaskService for full task CRUD: - client.Tasks(checklistID) returns TaskService - List(ctx) - get all tasks in checklist - Get(ctx, taskID) - get single task with parent hierarchy - Create(ctx, builder) - create task using TaskBuilder - Update(ctx, taskID, req) - update task properties - Delete(ctx, taskID) - permanently delete task - Close(ctx, taskID) - mark task as completed - Reopen(ctx, taskID) - reopen closed/invalidated task - Invalidate(ctx, taskID) - mark task as invalidated (strikethrough) TaskBuilder fluent interface for task creation: - NewTask(content) - create builder - WithParent(id) - set parent for subtask - WithPosition(pos) - set position in list - WithDueDate(due) - set due date using DueDate type - WithPriority(level) - set priority (0=normal, 1=highest, 2=high) - WithTags(tags...) - set tags Automatic DueDate parsing from DueDateRaw (ISO 8601 format). Closes checkvist-api-rl9
This commit is contained in:
parent
e5ee947fa4
commit
3aa2a284de
2 changed files with 221 additions and 1 deletions
220
tasks.go
220
tasks.go
|
|
@ -1,3 +1,223 @@
|
|||
package checkvist
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// tasks.go contains the TaskService for CRUD operations on tasks within a checklist.
|
||||
|
||||
// TaskService provides operations on tasks within a specific checklist.
|
||||
type TaskService struct {
|
||||
client *Client
|
||||
checklistID int
|
||||
}
|
||||
|
||||
// Tasks returns a TaskService for performing task operations on the specified checklist.
|
||||
func (c *Client) Tasks(checklistID int) *TaskService {
|
||||
return &TaskService{
|
||||
client: c,
|
||||
checklistID: checklistID,
|
||||
}
|
||||
}
|
||||
|
||||
// List returns all tasks in the checklist.
|
||||
func (s *TaskService) List(ctx context.Context) ([]Task, error) {
|
||||
path := fmt.Sprintf("/checklists/%d/tasks.json", s.checklistID)
|
||||
|
||||
var tasks []Task
|
||||
if err := s.client.doGet(ctx, path, &tasks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse due dates
|
||||
for i := range tasks {
|
||||
parseDueDate(&tasks[i])
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// Get returns a single task by ID, including its parent hierarchy.
|
||||
func (s *TaskService) Get(ctx context.Context, taskID int) (*Task, error) {
|
||||
path := fmt.Sprintf("/checklists/%d/tasks/%d.json", s.checklistID, taskID)
|
||||
|
||||
var task Task
|
||||
if err := s.client.doGet(ctx, path, &task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parseDueDate(&task)
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
// CreateTaskRequest represents the request body for creating a task.
|
||||
type CreateTaskRequest struct {
|
||||
Content string `json:"content"`
|
||||
ParentID int `json:"parent_id,omitempty"`
|
||||
Position int `json:"position,omitempty"`
|
||||
Due string `json:"due,omitempty"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
Tags string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// TaskBuilder provides a fluent interface for building task creation requests.
|
||||
type TaskBuilder struct {
|
||||
content string
|
||||
parentID int
|
||||
position int
|
||||
due string
|
||||
priority int
|
||||
tags []string
|
||||
}
|
||||
|
||||
// NewTask creates a new TaskBuilder with the given content.
|
||||
func NewTask(content string) *TaskBuilder {
|
||||
return &TaskBuilder{content: content}
|
||||
}
|
||||
|
||||
// WithParent sets the parent task ID for creating a subtask.
|
||||
func (b *TaskBuilder) WithParent(parentID int) *TaskBuilder {
|
||||
b.parentID = parentID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPosition sets the position of the task within its siblings.
|
||||
func (b *TaskBuilder) WithPosition(position int) *TaskBuilder {
|
||||
b.position = position
|
||||
return b
|
||||
}
|
||||
|
||||
// WithDueDate sets the due date using a DueDate value.
|
||||
func (b *TaskBuilder) WithDueDate(due DueDate) *TaskBuilder {
|
||||
b.due = due.String()
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPriority sets the priority level (1 = highest, 2 = high, 0 = normal).
|
||||
func (b *TaskBuilder) WithPriority(priority int) *TaskBuilder {
|
||||
b.priority = priority
|
||||
return b
|
||||
}
|
||||
|
||||
// WithTags sets the tags for the task.
|
||||
func (b *TaskBuilder) WithTags(tags ...string) *TaskBuilder {
|
||||
b.tags = tags
|
||||
return b
|
||||
}
|
||||
|
||||
// build converts the TaskBuilder to a CreateTaskRequest.
|
||||
func (b *TaskBuilder) build() CreateTaskRequest {
|
||||
req := CreateTaskRequest{
|
||||
Content: b.content,
|
||||
ParentID: b.parentID,
|
||||
Position: b.position,
|
||||
Due: b.due,
|
||||
Priority: b.priority,
|
||||
}
|
||||
if len(b.tags) > 0 {
|
||||
for i, tag := range b.tags {
|
||||
if i > 0 {
|
||||
req.Tags += ", "
|
||||
}
|
||||
req.Tags += tag
|
||||
}
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
// Create creates a new task using a TaskBuilder.
|
||||
func (s *TaskService) Create(ctx context.Context, builder *TaskBuilder) (*Task, error) {
|
||||
path := fmt.Sprintf("/checklists/%d/tasks.json", s.checklistID)
|
||||
body := builder.build()
|
||||
|
||||
var task Task
|
||||
if err := s.client.doPost(ctx, path, body, &task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parseDueDate(&task)
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
// UpdateTaskRequest represents the request body for updating a task.
|
||||
type UpdateTaskRequest struct {
|
||||
Content *string `json:"content,omitempty"`
|
||||
ParentID *int `json:"parent_id,omitempty"`
|
||||
Position *int `json:"position,omitempty"`
|
||||
Due *string `json:"due,omitempty"`
|
||||
Priority *int `json:"priority,omitempty"`
|
||||
Tags *string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// Update updates an existing task.
|
||||
func (s *TaskService) Update(ctx context.Context, taskID int, req UpdateTaskRequest) (*Task, error) {
|
||||
path := fmt.Sprintf("/checklists/%d/tasks/%d.json", s.checklistID, taskID)
|
||||
|
||||
var task Task
|
||||
if err := s.client.doPut(ctx, path, req, &task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parseDueDate(&task)
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
// Delete permanently deletes a task.
|
||||
func (s *TaskService) Delete(ctx context.Context, taskID int) error {
|
||||
path := fmt.Sprintf("/checklists/%d/tasks/%d.json", s.checklistID, taskID)
|
||||
return s.client.doDelete(ctx, path)
|
||||
}
|
||||
|
||||
// Close marks a task as completed.
|
||||
func (s *TaskService) Close(ctx context.Context, taskID int) (*Task, error) {
|
||||
path := fmt.Sprintf("/checklists/%d/tasks/%d/close.json", s.checklistID, taskID)
|
||||
|
||||
var task Task
|
||||
if err := s.client.doPost(ctx, path, nil, &task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parseDueDate(&task)
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
// Reopen reopens a closed or invalidated task.
|
||||
func (s *TaskService) Reopen(ctx context.Context, taskID int) (*Task, error) {
|
||||
path := fmt.Sprintf("/checklists/%d/tasks/%d/reopen.json", s.checklistID, taskID)
|
||||
|
||||
var task Task
|
||||
if err := s.client.doPost(ctx, path, nil, &task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parseDueDate(&task)
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
// Invalidate marks a task as invalidated (strikethrough).
|
||||
func (s *TaskService) Invalidate(ctx context.Context, taskID int) (*Task, error) {
|
||||
path := fmt.Sprintf("/checklists/%d/tasks/%d/invalidate.json", s.checklistID, taskID)
|
||||
|
||||
var task Task
|
||||
if err := s.client.doPost(ctx, path, nil, &task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parseDueDate(&task)
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
// parseDueDate attempts to parse the DueDateRaw string into a time.Time.
|
||||
// It supports ISO 8601 date format (YYYY-MM-DD).
|
||||
func parseDueDate(task *Task) {
|
||||
if task.DueDateRaw == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Try to parse as ISO date
|
||||
if t, err := time.Parse("2006-01-02", task.DueDateRaw); err == nil {
|
||||
task.DueDate = &t
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue