From 61431dfaeb7447ad29f8ad78a88d13ffe8a7cb18 Mon Sep 17 00:00:00 2001 From: Oliver Jakoubek Date: Wed, 14 Jan 2026 13:22:29 +0100 Subject: [PATCH] Implement data models for Checkvist API entities Add models.go with all data structures: - Checklist struct with fields for ID, Name, Public, Archived, etc. - Task struct with full task data including status, priority, due dates - TaskStatus enum (StatusOpen, StatusClosed, StatusInvalidated) - Note struct for task comments - User struct for user information - Tags type as map[string]bool for efficient lookup - DueDate struct with smart syntax support and constructors (DueAt, DueString, DueInDays) and constants (DueToday, DueTomorrow, DueNextWeek, DueNextMonth) Closes checkvist-api-e9p --- .beads/issues.jsonl | 2 +- models.go | 161 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 1 deletion(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 02aae37..5d3ed06 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -16,7 +16,7 @@ {"id":"checkvist-api-br3","title":"Core API Operations","description":"Phase 2: Implement CRUD operations for Checklists, Tasks, and Notes. All P0 (must-have) features for the library.","status":"open","priority":0,"issue_type":"epic","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:30:53.20627925+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T12:30:53.20627925+01:00"} {"id":"checkvist-api-c2k","title":"Implement Checklist operations","description":"Create checklists.go with ChecklistService:\n- client.Checklists() returns ChecklistService\n- List(ctx) ([]Checklist, error) - GET /checklists.json\n- Get(ctx, id) (*Checklist, error) - GET /checklists/{id}.json\n- Create(ctx, name) (*Checklist, error) - POST /checklists.json\n- Update(ctx, id, name) (*Checklist, error) - PUT /checklists/{id}.json\n- Delete(ctx, id) error - DELETE /checklists/{id}.json\n- Support archived filter in List\nContext support for all methods.","status":"open","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:30:53.566197933+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T12:30:53.566197933+01:00","dependencies":[{"issue_id":"checkvist-api-c2k","depends_on_id":"checkvist-api-8u6","type":"blocks","created_at":"2026-01-14T12:32:54.533462004+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"checkvist-api-c2k","depends_on_id":"checkvist-api-lpn","type":"blocks","created_at":"2026-01-14T12:32:54.859645166+01:00","created_by":"Oliver Jakoubek"}]} {"id":"checkvist-api-cb8","title":"Extended Features","description":"Phase 3: Implement P1 (should-have) features including client-side filtering and builder patterns for fluent interfaces.","status":"open","priority":1,"issue_type":"epic","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:30:55.624242123+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T12:30:55.624242123+01:00"} -{"id":"checkvist-api-e9p","title":"Implement data models","description":"Create models.go with all data structures:\n- Checklist struct (ID, Name, Public, Archived, ReadOnly, TaskCount, TaskCompleted, Tags, TagsAsText, UpdatedAt)\n- Task struct (ID, ChecklistID, ParentID, Content, Status, Position, Priority, Tags, TagsAsText, DueDateRaw, DueDate, AssigneeIDs, CommentsCount, UpdateLine, UpdatedAt, CreatedAt, Children, Notes)\n- TaskStatus enum (StatusOpen=0, StatusClosed=1, StatusInvalidated=2)\n- Note struct (ID, TaskID, Comment, UpdatedAt, CreatedAt)\n- Tags type (map[string]bool)\n- User struct (ID, Username, Email)\n- DueDate struct with constructors (DueAt, DueString, DueInDays) and constants (DueToday, DueTomorrow, DueNextWeek, DueNextMonth)","status":"open","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:31:06.900391036+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T12:31:06.900391036+01:00","dependencies":[{"issue_id":"checkvist-api-e9p","depends_on_id":"checkvist-api-5wr","type":"blocks","created_at":"2026-01-14T12:32:46.433908937+01:00","created_by":"Oliver Jakoubek"}]} +{"id":"checkvist-api-e9p","title":"Implement data models","description":"Create models.go with all data structures:\n- Checklist struct (ID, Name, Public, Archived, ReadOnly, TaskCount, TaskCompleted, Tags, TagsAsText, UpdatedAt)\n- Task struct (ID, ChecklistID, ParentID, Content, Status, Position, Priority, Tags, TagsAsText, DueDateRaw, DueDate, AssigneeIDs, CommentsCount, UpdateLine, UpdatedAt, CreatedAt, Children, Notes)\n- TaskStatus enum (StatusOpen=0, StatusClosed=1, StatusInvalidated=2)\n- Note struct (ID, TaskID, Comment, UpdatedAt, CreatedAt)\n- Tags type (map[string]bool)\n- User struct (ID, Username, Email)\n- DueDate struct with constructors (DueAt, DueString, DueInDays) and constants (DueToday, DueTomorrow, DueNextWeek, DueNextMonth)","status":"closed","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:31:06.900391036+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T13:22:22.273934664+01:00","closed_at":"2026-01-14T13:22:22.273934664+01:00","close_reason":"Closed","dependencies":[{"issue_id":"checkvist-api-e9p","depends_on_id":"checkvist-api-5wr","type":"blocks","created_at":"2026-01-14T12:32:46.433908937+01:00","created_by":"Oliver Jakoubek"}]} {"id":"checkvist-api-lpn","title":"Implement authentication with auto token renewal","description":"Add to client.go:\n- Authenticate(ctx context.Context) error - explicit login\n- refreshToken(ctx context.Context) error - token renewal\n- ensureAuthenticated(ctx context.Context) error - auto-auth before requests\n- CurrentUser(ctx context.Context) (*User, error) - get logged in user\n- Token management: store token and expiry, auto-refresh before expiry\n- Thread-safe token access using mutex\n- Support for optional 2FA token\nAPI endpoints:\n- POST /auth/login.json?version=2 (login)\n- POST /auth/refresh_token.json?version=2 (refresh)\n- GET /auth/curr_user.json (current user)\nToken sent via X-Client-Token header","status":"open","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:31:08.358878117+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T12:31:08.358878117+01:00","dependencies":[{"issue_id":"checkvist-api-lpn","depends_on_id":"checkvist-api-ymg","type":"blocks","created_at":"2026-01-14T12:32:47.656124681+01:00","created_by":"Oliver Jakoubek"}]} {"id":"checkvist-api-mnh","title":"Implement error types and sentinel errors","description":"Create errors.go with:\n- APIError struct (StatusCode, Message, RequestID, Err) with Error() and Unwrap() methods\n- Sentinel errors: ErrUnauthorized, ErrNotFound, ErrRateLimited, ErrBadRequest, ErrServerError\n- Helper function to create APIError from HTTP response","status":"open","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:31:07.619359293+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T12:31:07.619359293+01:00","dependencies":[{"issue_id":"checkvist-api-mnh","depends_on_id":"checkvist-api-5wr","type":"blocks","created_at":"2026-01-14T12:32:46.754134799+01:00","created_by":"Oliver Jakoubek"}]} {"id":"checkvist-api-nrk","title":"Create README with quickstart","description":"Create README.md (in English) with:\n- Project description\n- Installation: go get code.beautifulmachines.dev/jakoubek/checkvist-api\n- Quick start example (init client, list checklists, create task)\n- API overview (Checklists, Tasks, Notes, Filters)\n- Builder pattern examples\n- Error handling examples\n- Configuration options\n- Link to GoDoc\n- License (MIT)","status":"open","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:31:38.724338606+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T12:31:38.724338606+01:00","dependencies":[{"issue_id":"checkvist-api-nrk","depends_on_id":"checkvist-api-c2k","type":"blocks","created_at":"2026-01-14T12:33:15.785698203+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"checkvist-api-nrk","depends_on_id":"checkvist-api-rl9","type":"blocks","created_at":"2026-01-14T12:33:16.125115134+01:00","created_by":"Oliver Jakoubek"}]} diff --git a/models.go b/models.go index 1118018..97d91d9 100644 --- a/models.go +++ b/models.go @@ -1,4 +1,165 @@ package checkvist +import ( + "fmt" + "time" +) + // models.go contains data structures for Checkvist entities: // Checklist, Task, Note, User, Tags, TaskStatus, and DueDate. + +// TaskStatus represents the status of a task. +type TaskStatus int + +const ( + // StatusOpen indicates the task is open/incomplete. + StatusOpen TaskStatus = 0 + // StatusClosed indicates the task is completed. + StatusClosed TaskStatus = 1 + // StatusInvalidated indicates the task has been invalidated. + StatusInvalidated TaskStatus = 2 +) + +// String returns the string representation of the TaskStatus. +func (s TaskStatus) String() string { + switch s { + case StatusOpen: + return "open" + case StatusClosed: + return "closed" + case StatusInvalidated: + return "invalidated" + default: + return fmt.Sprintf("unknown(%d)", s) + } +} + +// Tags represents a set of tags as a map for efficient lookup. +type Tags map[string]bool + +// Checklist represents a Checkvist checklist. +type Checklist struct { + // ID is the unique identifier of the checklist. + ID int `json:"id"` + // Name is the title of the checklist. + Name string `json:"name"` + // Public indicates whether the checklist is publicly accessible. + Public bool `json:"public"` + // Archived indicates whether the checklist is archived. + Archived bool `json:"archived"` + // ReadOnly indicates whether the checklist is read-only for the current user. + ReadOnly bool `json:"read_only"` + // TaskCount is the total number of tasks in the checklist. + TaskCount int `json:"task_count"` + // TaskCompleted is the number of completed tasks in the checklist. + TaskCompleted int `json:"task_completed"` + // Tags contains the parsed tags from TagsAsText. + Tags Tags `json:"-"` + // TagsAsText is the raw tags string from the API. + TagsAsText string `json:"tags_as_text"` + // UpdatedAt is the timestamp of the last update. + UpdatedAt time.Time `json:"updated_at"` +} + +// Task represents a task within a Checkvist checklist. +type Task struct { + // ID is the unique identifier of the task. + ID int `json:"id"` + // ChecklistID is the ID of the checklist this task belongs to. + ChecklistID int `json:"checklist_id"` + // ParentID is the ID of the parent task, or 0 if this is a root task. + ParentID int `json:"parent_id"` + // Content is the text content of the task. + Content string `json:"content"` + // Status is the current status of the task (open, closed, invalidated). + Status TaskStatus `json:"status"` + // Position is the position of the task within its siblings. + Position int `json:"position"` + // Priority is the priority level (1 = highest, 2 = high, 0 = normal). + Priority int `json:"priority"` + // Tags contains the parsed tags from TagsAsText. + Tags Tags `json:"-"` + // TagsAsText is the raw tags string from the API. + TagsAsText string `json:"tags_as_text"` + // DueDateRaw is the raw due date string from the API. + DueDateRaw string `json:"due"` + // DueDate is the parsed due date, if available in ISO format. + DueDate *time.Time `json:"-"` + // AssigneeIDs contains the IDs of users assigned to this task. + AssigneeIDs []int `json:"assignee_ids"` + // CommentsCount is the number of notes/comments on this task. + CommentsCount int `json:"comments_count"` + // UpdateLine contains brief update information. + UpdateLine string `json:"update_line"` + // UpdatedAt is the timestamp of the last update. + UpdatedAt time.Time `json:"updated_at"` + // CreatedAt is the timestamp when the task was created. + CreatedAt time.Time `json:"created_at"` + // Children contains nested child tasks (when fetched with tree structure). + Children []*Task `json:"tasks,omitempty"` + // Notes contains the comments/notes attached to this task. + Notes []Note `json:"notes,omitempty"` +} + +// Note represents a comment/note on a task. +type Note struct { + // ID is the unique identifier of the note. + ID int `json:"id"` + // TaskID is the ID of the task this note belongs to. + TaskID int `json:"task_id"` + // Comment is the text content of the note. + Comment string `json:"comment"` + // UpdatedAt is the timestamp of the last update. + UpdatedAt time.Time `json:"updated_at"` + // CreatedAt is the timestamp when the note was created. + CreatedAt time.Time `json:"created_at"` +} + +// User represents a Checkvist user. +type User struct { + // ID is the unique identifier of the user. + ID int `json:"id"` + // Username is the user's display name. + Username string `json:"username"` + // Email is the user's email address. + Email string `json:"email"` +} + +// DueDate represents a due date for task creation using Checkvist's smart syntax. +type DueDate struct { + value string +} + +// Common due date constants using Checkvist's smart syntax. +var ( + // DueToday sets the due date to today. + DueToday = DueDate{value: "^today"} + // DueTomorrow sets the due date to tomorrow. + DueTomorrow = DueDate{value: "^tomorrow"} + // DueNextWeek sets the due date to next week. + DueNextWeek = DueDate{value: "^next week"} + // DueNextMonth sets the due date to next month. + DueNextMonth = DueDate{value: "^next month"} +) + +// DueAt creates a DueDate from a Go time.Time value. +func DueAt(t time.Time) DueDate { + return DueDate{value: "^" + t.Format("2006-01-02")} +} + +// DueString creates a DueDate from a raw smart syntax string. +// The string should use Checkvist's smart syntax (e.g., "^2026-02-01", "^friday"). +func DueString(s string) DueDate { + return DueDate{value: s} +} + +// DueInDays creates a DueDate for n days from now. +func DueInDays(n int) DueDate { + t := time.Now().AddDate(0, 0, n) + return DueDate{value: "^" + t.Format("2006-01-02")} +} + +// String returns the smart syntax string for the due date. +func (d DueDate) String() string { + return d.value +}