Compare commits
2 commits
536ccf6ccf
...
b97877f7fc
| Author | SHA1 | Date | |
|---|---|---|---|
| b97877f7fc | |||
| d92a1b90c2 |
6 changed files with 67 additions and 5 deletions
|
|
@ -27,6 +27,7 @@
|
||||||
{"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":"closed","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-14T13:23:43.296883265+01:00","closed_at":"2026-01-14T13:23:43.296883265+01:00","close_reason":"Closed","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-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":"closed","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-14T13:23:43.296883265+01:00","closed_at":"2026-01-14T13:23:43.296883265+01:00","close_reason":"Closed","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":"closed","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-14T13:48:30.163762004+01:00","closed_at":"2026-01-14T13:48:30.163762004+01:00","close_reason":"Closed","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"}]}
|
{"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":"closed","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-14T13:48:30.163762004+01:00","closed_at":"2026-01-14T13:48:30.163762004+01:00","close_reason":"Closed","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"}]}
|
||||||
{"id":"checkvist-api-otm","title":"Fix DueDate parsing: API returns slashes, parser expects dashes","description":"## Problem\n\nWhen fetching tasks via `TaskService.List()`, `DueDate` is always `nil` even for tasks that have a due date set.\n\n## Root Cause\n\nThe Checkvist API returns due dates in slash format (`2026/01/15`), but `parseDueDate()` in `tasks.go:250` only parses the ISO format with dashes:\n\n```go\n// Current implementation - only supports dashes\nif t, err := time.Parse(\"2006-01-02\", task.DueDateRaw); err == nil {\n task.DueDate = &t\n}\n```\n\nAPI response format: `\"due\": \"2026/01/15\"` (slashes)\nExpected by parser: `\"2026-01-15\"` (dashes)\n\n## Solution\n\nExtend `parseDueDate()` to also parse the slash format:\n\n```go\nfunc parseDueDate(task *Task) {\n if task.DueDateRaw == \"\" {\n return\n }\n \n // Try multiple formats\n formats := []string{\n \"2006-01-02\", // ISO format (dashes)\n \"2006/01/02\", // Checkvist API format (slashes)\n }\n \n for _, format := range formats {\n if t, err := time.Parse(format, task.DueDateRaw); err == nil {\n task.DueDate = &t\n return\n }\n }\n}\n```\n\n## Affected Code\n\n- `tasks.go:244-253` - `parseDueDate()` function\n\n## Acceptance Criteria\n\n- [ ] Tasks with due dates have `DueDate` correctly parsed\n- [ ] Both slash format (`2026/01/15`) and dash format (`2026-01-15`) work\n- [ ] Unit test added for slash format parsing","status":"closed","priority":2,"issue_type":"bug","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T09:05:02.138578452+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T09:29:14.390484526+01:00","closed_at":"2026-01-15T09:29:14.390484526+01:00","close_reason":"Closed"}
|
{"id":"checkvist-api-otm","title":"Fix DueDate parsing: API returns slashes, parser expects dashes","description":"## Problem\n\nWhen fetching tasks via `TaskService.List()`, `DueDate` is always `nil` even for tasks that have a due date set.\n\n## Root Cause\n\nThe Checkvist API returns due dates in slash format (`2026/01/15`), but `parseDueDate()` in `tasks.go:250` only parses the ISO format with dashes:\n\n```go\n// Current implementation - only supports dashes\nif t, err := time.Parse(\"2006-01-02\", task.DueDateRaw); err == nil {\n task.DueDate = &t\n}\n```\n\nAPI response format: `\"due\": \"2026/01/15\"` (slashes)\nExpected by parser: `\"2026-01-15\"` (dashes)\n\n## Solution\n\nExtend `parseDueDate()` to also parse the slash format:\n\n```go\nfunc parseDueDate(task *Task) {\n if task.DueDateRaw == \"\" {\n return\n }\n \n // Try multiple formats\n formats := []string{\n \"2006-01-02\", // ISO format (dashes)\n \"2006/01/02\", // Checkvist API format (slashes)\n }\n \n for _, format := range formats {\n if t, err := time.Parse(format, task.DueDateRaw); err == nil {\n task.DueDate = &t\n return\n }\n }\n}\n```\n\n## Affected Code\n\n- `tasks.go:244-253` - `parseDueDate()` function\n\n## Acceptance Criteria\n\n- [ ] Tasks with due dates have `DueDate` correctly parsed\n- [ ] Both slash format (`2026/01/15`) and dash format (`2026-01-15`) work\n- [ ] Unit test added for slash format parsing","status":"closed","priority":2,"issue_type":"bug","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T09:05:02.138578452+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T09:29:14.390484526+01:00","closed_at":"2026-01-15T09:29:14.390484526+01:00","close_reason":"Closed"}
|
||||||
|
{"id":"checkvist-api-rcj","title":"Fix Task.tasks field: expect []int not []*Task","description":"## Problem\n\nWhen decoding API responses containing the `tasks` field, JSON unmarshaling fails with:\n```\njson: cannot unmarshal number into Go struct field Task.tasks of type checkvist.Task\n```\n\n## Root Cause\n\nIn `models.go:144`, the `Children` field is incorrectly typed:\n```go\nChildren []*Task `json:\"tasks,omitempty\"`\n```\n\nAccording to the Checkvist API documentation, the `tasks` field is:\n> \"tasks [JSON] Javascript array of children task IDs\"\n\nThe API returns an **array of integers** (child task IDs), not an array of full Task objects.\n\n## Solution\n\nChange the field type from `[]*Task` to `[]int` and rename appropriately:\n```go\nChildIDs []int `json:\"tasks,omitempty\"` // IDs of child tasks\n```\n\nThis is a breaking change for users who relied on `Children []*Task`, but the current implementation was incorrect and would fail on real API responses anyway.\n\n## Acceptance Criteria\n\n- [ ] `Task.ChildIDs` correctly unmarshals as `[]int`\n- [ ] Update test fixtures to include `tasks` field with sample child IDs\n- [ ] Tests pass with real API response structure\n- [ ] Update CHANGELOG.md noting the breaking change","status":"in_progress","priority":2,"issue_type":"bug","owner":"mail@oliverjakoubek.de","created_at":"2026-01-18T14:32:44.748293162+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-18T14:34:44.774842767+01:00"}
|
||||||
{"id":"checkvist-api-rl9","title":"Implement Task operations","description":"Create tasks.go with TaskService:\n- client.Tasks(checklistID) returns TaskService\n- List(ctx) ([]Task, error) - GET /checklists/{id}/tasks.json\n- Get(ctx, taskID) (*Task, error) - GET /checklists/{id}/tasks/{task_id}.json (includes parent hierarchy)\n- Create(ctx, builder *TaskBuilder) (*Task, error) - POST /checklists/{id}/tasks.json\n- Update(ctx, taskID, opts) (*Task, error) - PUT /checklists/{id}/tasks/{task_id}.json\n- Delete(ctx, taskID) error - DELETE /checklists/{id}/tasks/{task_id}.json\n- Close(ctx, taskID) (*Task, error) - POST /checklists/{id}/tasks/{task_id}/close.json\n- Reopen(ctx, taskID) (*Task, error) - POST /checklists/{id}/tasks/{task_id}/reopen.json\n- Invalidate(ctx, taskID) (*Task, error) - POST /checklists/{id}/tasks/{task_id}/invalidate.json\nParse DueDate from DueDateRaw when retrieving tasks.","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:30:53.90629838+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T13:39:29.443727155+01:00","closed_at":"2026-01-14T13:39:29.443727155+01:00","close_reason":"Closed","dependencies":[{"issue_id":"checkvist-api-rl9","depends_on_id":"checkvist-api-8u6","type":"blocks","created_at":"2026-01-14T12:32:55.247292478+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"checkvist-api-rl9","depends_on_id":"checkvist-api-lpn","type":"blocks","created_at":"2026-01-14T12:32:55.536931433+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"checkvist-api-rl9","title":"Implement Task operations","description":"Create tasks.go with TaskService:\n- client.Tasks(checklistID) returns TaskService\n- List(ctx) ([]Task, error) - GET /checklists/{id}/tasks.json\n- Get(ctx, taskID) (*Task, error) - GET /checklists/{id}/tasks/{task_id}.json (includes parent hierarchy)\n- Create(ctx, builder *TaskBuilder) (*Task, error) - POST /checklists/{id}/tasks.json\n- Update(ctx, taskID, opts) (*Task, error) - PUT /checklists/{id}/tasks/{task_id}.json\n- Delete(ctx, taskID) error - DELETE /checklists/{id}/tasks/{task_id}.json\n- Close(ctx, taskID) (*Task, error) - POST /checklists/{id}/tasks/{task_id}/close.json\n- Reopen(ctx, taskID) (*Task, error) - POST /checklists/{id}/tasks/{task_id}/reopen.json\n- Invalidate(ctx, taskID) (*Task, error) - POST /checklists/{id}/tasks/{task_id}/invalidate.json\nParse DueDate from DueDateRaw when retrieving tasks.","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:30:53.90629838+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T13:39:29.443727155+01:00","closed_at":"2026-01-14T13:39:29.443727155+01:00","close_reason":"Closed","dependencies":[{"issue_id":"checkvist-api-rl9","depends_on_id":"checkvist-api-8u6","type":"blocks","created_at":"2026-01-14T12:32:55.247292478+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"checkvist-api-rl9","depends_on_id":"checkvist-api-lpn","type":"blocks","created_at":"2026-01-14T12:32:55.536931433+01:00","created_by":"Oliver Jakoubek"}]}
|
||||||
{"id":"checkvist-api-tjk","title":"Implement TaskBuilder fluent interface","description":"Enhance task creation with builder pattern:\n- NewTask(content string) *TaskBuilder\n- WithTags(tags ...string) *TaskBuilder\n- WithDueDate(due DueDate) *TaskBuilder\n- WithPriority(p int) *TaskBuilder\n- WithParent(parentID int64) *TaskBuilder\n- WithPosition(pos int) *TaskBuilder\n- Build() returns parameters for API call\nTaskBuilder should be chainable and return itself for fluent usage:\n checkvist.NewTask(\"Content\").WithTags(\"tag1\").WithDueDate(checkvist.DueTomorrow).WithPriority(1)","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:30:55.929907579+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T14:30:42.405125498+01:00","closed_at":"2026-01-14T14:30:42.405125498+01:00","close_reason":"TaskBuilder war bereits vollständig implementiert und getestet","dependencies":[{"issue_id":"checkvist-api-tjk","depends_on_id":"checkvist-api-rl9","type":"blocks","created_at":"2026-01-14T12:33:02.20339206+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"checkvist-api-tjk","title":"Implement TaskBuilder fluent interface","description":"Enhance task creation with builder pattern:\n- NewTask(content string) *TaskBuilder\n- WithTags(tags ...string) *TaskBuilder\n- WithDueDate(due DueDate) *TaskBuilder\n- WithPriority(p int) *TaskBuilder\n- WithParent(parentID int64) *TaskBuilder\n- WithPosition(pos int) *TaskBuilder\n- Build() returns parameters for API call\nTaskBuilder should be chainable and return itself for fluent usage:\n checkvist.NewTask(\"Content\").WithTags(\"tag1\").WithDueDate(checkvist.DueTomorrow).WithPriority(1)","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:30:55.929907579+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T14:30:42.405125498+01:00","closed_at":"2026-01-14T14:30:42.405125498+01:00","close_reason":"TaskBuilder war bereits vollständig implementiert und getestet","dependencies":[{"issue_id":"checkvist-api-tjk","depends_on_id":"checkvist-api-rl9","type":"blocks","created_at":"2026-01-14T12:33:02.20339206+01:00","created_by":"Oliver Jakoubek"}]}
|
||||||
{"id":"checkvist-api-v2f","title":"Write unit tests for Tasks","description":"Create tasks_test.go with tests:\n- TestTasks_List\n- TestTasks_Get\n- TestTasks_Get_WithParentHierarchy\n- TestTasks_Create\n- TestTasks_Create_WithBuilder\n- TestTasks_Update\n- TestTasks_Delete\n- TestTasks_Close\n- TestTasks_Reopen\n- TestTasks_Invalidate\n- TestDueDate_Parsing\nUse table-driven tests. Create testdata/tasks/ fixtures.","status":"closed","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:31:37.538754679+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T13:46:01.50434559+01:00","closed_at":"2026-01-14T13:46:01.50434559+01:00","close_reason":"Closed","dependencies":[{"issue_id":"checkvist-api-v2f","depends_on_id":"checkvist-api-rl9","type":"blocks","created_at":"2026-01-14T12:33:13.81085058+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"checkvist-api-v2f","title":"Write unit tests for Tasks","description":"Create tasks_test.go with tests:\n- TestTasks_List\n- TestTasks_Get\n- TestTasks_Get_WithParentHierarchy\n- TestTasks_Create\n- TestTasks_Create_WithBuilder\n- TestTasks_Update\n- TestTasks_Delete\n- TestTasks_Close\n- TestTasks_Reopen\n- TestTasks_Invalidate\n- TestDueDate_Parsing\nUse table-driven tests. Create testdata/tasks/ fixtures.","status":"closed","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-14T12:31:37.538754679+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-14T13:46:01.50434559+01:00","closed_at":"2026-01-14T13:46:01.50434559+01:00","close_reason":"Closed","dependencies":[{"issue_id":"checkvist-api-v2f","depends_on_id":"checkvist-api-rl9","type":"blocks","created_at":"2026-01-14T12:33:13.81085058+01:00","created_by":"Oliver Jakoubek"}]}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **BREAKING**: `Task.Children []*Task` renamed to `Task.ChildIDs []int` to match API response format (API returns array of child task IDs, not full task objects)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- **Tasks**: Fix `Close()`, `Reopen()`, and `Invalidate()` to handle API array response format
|
- **Tasks**: Fix `Close()`, `Reopen()`, and `Invalidate()` to handle API array response format
|
||||||
|
|
|
||||||
|
|
@ -140,8 +140,8 @@ type Task struct {
|
||||||
UpdatedAt APITime `json:"updated_at"`
|
UpdatedAt APITime `json:"updated_at"`
|
||||||
// CreatedAt is the timestamp when the task was created.
|
// CreatedAt is the timestamp when the task was created.
|
||||||
CreatedAt APITime `json:"created_at"`
|
CreatedAt APITime `json:"created_at"`
|
||||||
// Children contains nested child tasks (when fetched with tree structure).
|
// ChildIDs contains IDs of child tasks (returned by API as array of integers).
|
||||||
Children []*Task `json:"tasks,omitempty"`
|
ChildIDs []int `json:"tasks,omitempty"`
|
||||||
// Notes contains the comments/notes attached to this task.
|
// Notes contains the comments/notes attached to this task.
|
||||||
Notes []Note `json:"notes,omitempty"`
|
Notes []Note `json:"notes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,60 @@ func TestTasks_Get(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTask_ChildIDs_Unmarshal(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/auth/login.json":
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"token": "test-token"})
|
||||||
|
case "/checklists/1/tasks/101.json":
|
||||||
|
w.Write(loadFixture(t, "testdata/tasks/single.json"))
|
||||||
|
case "/checklists/1/tasks.json":
|
||||||
|
w.Write(loadFixture(t, "testdata/tasks/list.json"))
|
||||||
|
default:
|
||||||
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client := NewClient("user@example.com", "api-key", WithBaseURL(server.URL))
|
||||||
|
|
||||||
|
t.Run("single task with child IDs", func(t *testing.T) {
|
||||||
|
task, err := client.Tasks(1).Get(context.Background(), 101)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
expectedChildIDs := []int{201, 202, 203}
|
||||||
|
if len(task.ChildIDs) != len(expectedChildIDs) {
|
||||||
|
t.Fatalf("expected %d child IDs, got %d", len(expectedChildIDs), len(task.ChildIDs))
|
||||||
|
}
|
||||||
|
for i, id := range expectedChildIDs {
|
||||||
|
if task.ChildIDs[i] != id {
|
||||||
|
t.Errorf("expected ChildIDs[%d] = %d, got %d", i, id, task.ChildIDs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("list with mixed child IDs", func(t *testing.T) {
|
||||||
|
tasks, err := client.Tasks(1).List(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
// First task has child IDs [201, 202]
|
||||||
|
if len(tasks[0].ChildIDs) != 2 {
|
||||||
|
t.Errorf("expected 2 child IDs for first task, got %d", len(tasks[0].ChildIDs))
|
||||||
|
}
|
||||||
|
if tasks[0].ChildIDs[0] != 201 || tasks[0].ChildIDs[1] != 202 {
|
||||||
|
t.Errorf("expected ChildIDs [201, 202], got %v", tasks[0].ChildIDs)
|
||||||
|
}
|
||||||
|
// Second task has empty child IDs
|
||||||
|
if len(tasks[1].ChildIDs) != 0 {
|
||||||
|
t.Errorf("expected 0 child IDs for second task, got %d", len(tasks[1].ChildIDs))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestTasks_Create(t *testing.T) {
|
func TestTasks_Create(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
|
||||||
6
testdata/tasks/list.json
vendored
6
testdata/tasks/list.json
vendored
|
|
@ -13,7 +13,8 @@
|
||||||
"comments_count": 0,
|
"comments_count": 0,
|
||||||
"update_line": "",
|
"update_line": "",
|
||||||
"updated_at": "2026/01/14 10:00:00 +0000",
|
"updated_at": "2026/01/14 10:00:00 +0000",
|
||||||
"created_at": "2026/01/10 09:00:00 +0000"
|
"created_at": "2026/01/10 09:00:00 +0000",
|
||||||
|
"tasks": [201, 202]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 102,
|
"id": 102,
|
||||||
|
|
@ -29,6 +30,7 @@
|
||||||
"comments_count": 3,
|
"comments_count": 3,
|
||||||
"update_line": "",
|
"update_line": "",
|
||||||
"updated_at": "2026/01/14 11:00:00 +0000",
|
"updated_at": "2026/01/14 11:00:00 +0000",
|
||||||
"created_at": "2026/01/11 10:00:00 +0000"
|
"created_at": "2026/01/11 10:00:00 +0000",
|
||||||
|
"tasks": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
3
testdata/tasks/single.json
vendored
3
testdata/tasks/single.json
vendored
|
|
@ -12,5 +12,6 @@
|
||||||
"comments_count": 0,
|
"comments_count": 0,
|
||||||
"update_line": "",
|
"update_line": "",
|
||||||
"updated_at": "2026/01/14 10:00:00 +0000",
|
"updated_at": "2026/01/14 10:00:00 +0000",
|
||||||
"created_at": "2026/01/10 09:00:00 +0000"
|
"created_at": "2026/01/10 09:00:00 +0000",
|
||||||
|
"tasks": [201, 202, 203]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue