Add comprehensive README.md documentation

Documents installation, quick start, authentication methods,
fluent and direct API usage, task operations, error handling,
thread safety, and configuration options.
This commit is contained in:
Oliver Jakoubek 2026-01-15 19:20:57 +01:00
commit 408fb60b54
2 changed files with 242 additions and 2 deletions

View file

@ -9,7 +9,7 @@
{"id":"kanboard-api-91a","title":"Implement Task API methods (CRUD)","description":"Implement direct API methods for core task operations.\n\n## Methods to implement\n- GetTask(ctx, taskID int) (*Task, error) - getTask\n- GetAllTasks(ctx, projectID int, status TaskStatus) ([]Task, error) - getAllTasks\n- CreateTask(ctx, req CreateTaskRequest) (*Task, error) - createTask\n- UpdateTask(ctx, req UpdateTaskRequest) error - updateTask\n- CloseTask(ctx, taskID int) error - closeTask\n- OpenTask(ctx, taskID int) error - openTask\n\n## Files to create\n- tasks.go\n\n## Acceptance criteria\n- CreateTask returns the created task\n- UpdateTask supports partial updates via pointer fields\n- Proper error types (ErrTaskNotFound, ErrTaskClosed, etc.)","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:16.9962543+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:26:49.591860359+01:00","closed_at":"2026-01-15T18:26:49.591860359+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-91a","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:42:53.291442735+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-91a","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:42:53.434459365+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-91a","title":"Implement Task API methods (CRUD)","description":"Implement direct API methods for core task operations.\n\n## Methods to implement\n- GetTask(ctx, taskID int) (*Task, error) - getTask\n- GetAllTasks(ctx, projectID int, status TaskStatus) ([]Task, error) - getAllTasks\n- CreateTask(ctx, req CreateTaskRequest) (*Task, error) - createTask\n- UpdateTask(ctx, req UpdateTaskRequest) error - updateTask\n- CloseTask(ctx, taskID int) error - closeTask\n- OpenTask(ctx, taskID int) error - openTask\n\n## Files to create\n- tasks.go\n\n## Acceptance criteria\n- CreateTask returns the created task\n- UpdateTask supports partial updates via pointer fields\n- Proper error types (ErrTaskNotFound, ErrTaskClosed, etc.)","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:16.9962543+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:26:49.591860359+01:00","closed_at":"2026-01-15T18:26:49.591860359+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-91a","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:42:53.291442735+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-91a","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:42:53.434459365+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-9k8","title":"Implement TaskParams builder (Options Pattern)","description":"Implement TaskParams for fluent task creation configuration.\n\n## Requirements\n- TaskParams struct with private fields\n- NewTask(title string) *TaskParams constructor\n- Fluent setter methods:\n - WithDescription(desc string) *TaskParams\n - InColumn(columnID int) *TaskParams\n - WithCategory(categoryID int) *TaskParams\n - WithOwner(ownerID int) *TaskParams\n - WithColor(colorID string) *TaskParams\n - WithPriority(priority int) *TaskParams\n - WithDueDate(date time.Time) *TaskParams\n - WithTags(tags ...string) *TaskParams\n - WithReference(ref string) *TaskParams\n- Internal method to convert to CreateTaskRequest\n\n## Files to create\n- task_params.go\n\n## Example usage\n```go\nparams := kanboard.NewTask(\"My Task\").\n WithDescription(\"Details\").\n InColumn(2).\n WithTags(\"urgent\", \"backend\")\n```\n\n## Acceptance criteria\n- Pure configuration object (no I/O)\n- All setters return *TaskParams for chaining\n- Unset optional fields remain nil","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:40.439513879+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:31:07.365703704+01:00","closed_at":"2026-01-15T18:31:07.365703704+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-9k8","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:43:31.070100884+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-9k8","title":"Implement TaskParams builder (Options Pattern)","description":"Implement TaskParams for fluent task creation configuration.\n\n## Requirements\n- TaskParams struct with private fields\n- NewTask(title string) *TaskParams constructor\n- Fluent setter methods:\n - WithDescription(desc string) *TaskParams\n - InColumn(columnID int) *TaskParams\n - WithCategory(categoryID int) *TaskParams\n - WithOwner(ownerID int) *TaskParams\n - WithColor(colorID string) *TaskParams\n - WithPriority(priority int) *TaskParams\n - WithDueDate(date time.Time) *TaskParams\n - WithTags(tags ...string) *TaskParams\n - WithReference(ref string) *TaskParams\n- Internal method to convert to CreateTaskRequest\n\n## Files to create\n- task_params.go\n\n## Example usage\n```go\nparams := kanboard.NewTask(\"My Task\").\n WithDescription(\"Details\").\n InColumn(2).\n WithTags(\"urgent\", \"backend\")\n```\n\n## Acceptance criteria\n- Pure configuration object (no I/O)\n- All setters return *TaskParams for chaining\n- Unset optional fields remain nil","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:40.439513879+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:31:07.365703704+01:00","closed_at":"2026-01-15T18:31:07.365703704+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-9k8","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:43:31.070100884+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-apl","title":"Implement Project API methods","description":"Implement direct API methods for project/board operations.\n\n## Methods to implement\n- GetAllProjects(ctx) ([]Project, error) - getAllProjects\n- GetProjectByID(ctx, projectID int) (*Project, error) - getProjectById\n- GetProjectByName(ctx, name string) (*Project, error) - getProjectByName (Nice-to-have)\n\n## Files to create\n- projects.go\n\n## Acceptance criteria\n- All methods use context for cancellation\n- Proper error handling and wrapping\n- Returns ErrProjectNotFound when appropriate","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:15.864764497+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:21:36.226187777+01:00","closed_at":"2026-01-15T18:21:36.226187777+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-apl","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:42:52.850751716+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-apl","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:42:52.946556814+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-apl","title":"Implement Project API methods","description":"Implement direct API methods for project/board operations.\n\n## Methods to implement\n- GetAllProjects(ctx) ([]Project, error) - getAllProjects\n- GetProjectByID(ctx, projectID int) (*Project, error) - getProjectById\n- GetProjectByName(ctx, name string) (*Project, error) - getProjectByName (Nice-to-have)\n\n## Files to create\n- projects.go\n\n## Acceptance criteria\n- All methods use context for cancellation\n- Proper error handling and wrapping\n- Returns ErrProjectNotFound when appropriate","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:15.864764497+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:21:36.226187777+01:00","closed_at":"2026-01-15T18:21:36.226187777+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-apl","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:42:52.850751716+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-apl","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:42:52.946556814+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-c3i","title":"Create README.md with documentation","description":"Create comprehensive README.md documentation in English.\n\n## Sections to include\n- Project overview and features\n- Installation instructions\n- Quick start example\n- API documentation overview\n- Authentication methods (API token, user/password)\n- Fluent API examples\n- Direct API examples\n- Error handling\n- Thread-safety notes\n- Tag operations warning (non-atomic)\n- License (MIT)\n\n## Example code to include\n```go\n// Client creation\nclient := kanboard.NewClient(\"https://kanboard.example.com\").\n WithAPIToken(\"my-token\").\n WithTimeout(30 * time.Second)\n\n// Fluent API\ntask, _ := client.Board(1).CreateTask(ctx,\n kanboard.NewTask(\"My Task\").WithDescription(\"Details\"))\n\n// Direct API\ntasks, _ := client.GetAllTasks(ctx, 1, kanboard.StatusActive)\n```\n\n## Files to create\n- README.md\n\n## Acceptance criteria\n- Clear installation instructions\n- Working code examples\n- Documents all major features","status":"open","priority":2,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:53.228407343+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:36:53.228407343+01:00","dependencies":[{"issue_id":"kanboard-api-c3i","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:46:55.445847253+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-c3i","title":"Create README.md with documentation","description":"Create comprehensive README.md documentation in English.\n\n## Sections to include\n- Project overview and features\n- Installation instructions\n- Quick start example\n- API documentation overview\n- Authentication methods (API token, user/password)\n- Fluent API examples\n- Direct API examples\n- Error handling\n- Thread-safety notes\n- Tag operations warning (non-atomic)\n- License (MIT)\n\n## Example code to include\n```go\n// Client creation\nclient := kanboard.NewClient(\"https://kanboard.example.com\").\n WithAPIToken(\"my-token\").\n WithTimeout(30 * time.Second)\n\n// Fluent API\ntask, _ := client.Board(1).CreateTask(ctx,\n kanboard.NewTask(\"My Task\").WithDescription(\"Details\"))\n\n// Direct API\ntasks, _ := client.GetAllTasks(ctx, 1, kanboard.StatusActive)\n```\n\n## Files to create\n- README.md\n\n## Acceptance criteria\n- Clear installation instructions\n- Working code examples\n- Documents all major features","status":"in_progress","priority":2,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:53.228407343+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T19:20:15.086461439+01:00","dependencies":[{"issue_id":"kanboard-api-c3i","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:46:55.445847253+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-cyc","title":"Implement entity structs (types.go)","description":"Implement all entity structs for Kanboard API responses.\n\n## Structs to implement\n- Project (ID, Name, Description, IsActive, Token, LastModified, etc.)\n- Task (ID, Title, Description, dates, ColorID, ProjectID, ColumnID, etc.)\n- Column (ID, Title, Position, ProjectID, TaskLimit, Description)\n- Category (ID, Name, ProjectID, ColorID)\n- Comment (ID, TaskID, UserID, DateCreation, Content, Username, etc.)\n- TaskLink (ID, LinkID, TaskID, OppositeTaskID, Label, Title)\n- TaskFile (ID, Name, Path, IsImage, TaskID, DateCreation, UserID, Size)\n- Tag (ID, Name, ProjectID, ColorID)\n- TaskStatus enum (StatusActive, StatusInactive)\n\n## Request structs\n- CreateTaskRequest\n- UpdateTaskRequest (with pointer fields for optional values)\n\n## Files to create\n- types.go\n\n## Acceptance criteria\n- All JSON tags match Kanboard API\n- Optional fields use pointers with omitempty\n- Timestamp fields use custom Timestamp type","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:34:55.484472208+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:18:40.163015715+01:00","closed_at":"2026-01-15T18:18:40.163015715+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-cyc","depends_on_id":"kanboard-api-25y","type":"blocks","created_at":"2026-01-15T17:42:48.385166815+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-cyc","title":"Implement entity structs (types.go)","description":"Implement all entity structs for Kanboard API responses.\n\n## Structs to implement\n- Project (ID, Name, Description, IsActive, Token, LastModified, etc.)\n- Task (ID, Title, Description, dates, ColorID, ProjectID, ColumnID, etc.)\n- Column (ID, Title, Position, ProjectID, TaskLimit, Description)\n- Category (ID, Name, ProjectID, ColorID)\n- Comment (ID, TaskID, UserID, DateCreation, Content, Username, etc.)\n- TaskLink (ID, LinkID, TaskID, OppositeTaskID, Label, Title)\n- TaskFile (ID, Name, Path, IsImage, TaskID, DateCreation, UserID, Size)\n- Tag (ID, Name, ProjectID, ColorID)\n- TaskStatus enum (StatusActive, StatusInactive)\n\n## Request structs\n- CreateTaskRequest\n- UpdateTaskRequest (with pointer fields for optional values)\n\n## Files to create\n- types.go\n\n## Acceptance criteria\n- All JSON tags match Kanboard API\n- Optional fields use pointers with omitempty\n- Timestamp fields use custom Timestamp type","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:34:55.484472208+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:18:40.163015715+01:00","closed_at":"2026-01-15T18:18:40.163015715+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-cyc","depends_on_id":"kanboard-api-25y","type":"blocks","created_at":"2026-01-15T17:42:48.385166815+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-fue","title":"Implement Task search and move methods","description":"Implement task search and movement API methods.\n\n## Methods to implement\n- SearchTasks(ctx, projectID int, query string) ([]Task, error) - searchTasks\n- MoveTaskPosition(ctx, projectID, taskID, columnID, position, swimlaneID int) error - moveTaskPosition\n- MoveTaskToProject(ctx, taskID, projectID int) error - moveTaskToProject\n\n## Files to create\n- tasks.go (extend)\n\n## Acceptance criteria\n- Search supports Kanboard query syntax\n- MoveTaskPosition handles column and position\n- Proper error handling for invalid moves","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:17.380817511+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:28:05.809359198+01:00","closed_at":"2026-01-15T18:28:05.809359198+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-fue","depends_on_id":"kanboard-api-91a","type":"blocks","created_at":"2026-01-15T17:42:53.59290181+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-fue","title":"Implement Task search and move methods","description":"Implement task search and movement API methods.\n\n## Methods to implement\n- SearchTasks(ctx, projectID int, query string) ([]Task, error) - searchTasks\n- MoveTaskPosition(ctx, projectID, taskID, columnID, position, swimlaneID int) error - moveTaskPosition\n- MoveTaskToProject(ctx, taskID, projectID int) error - moveTaskToProject\n\n## Files to create\n- tasks.go (extend)\n\n## Acceptance criteria\n- Search supports Kanboard query syntax\n- MoveTaskPosition handles column and position\n- Proper error handling for invalid moves","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:17.380817511+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:28:05.809359198+01:00","closed_at":"2026-01-15T18:28:05.809359198+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-fue","depends_on_id":"kanboard-api-91a","type":"blocks","created_at":"2026-01-15T17:42:53.59290181+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-h4u","title":"Implement Comment API methods","description":"Implement direct API methods for comment operations.\n\n## Methods to implement\n- GetAllComments(ctx, taskID int) ([]Comment, error) - getAllComments\n- CreateComment(ctx, taskID, userID int, content string) (*Comment, error) - createComment\n- UpdateComment(ctx, commentID int, content string) error - updateComment\n- RemoveComment(ctx, commentID int) error - removeComment\n\n## TaskScope methods to add\n- AddComment(ctx, content string) (*Comment, error)\n- GetComments(ctx) ([]Comment, error)\n\n## Files to create\n- comments.go\n- task_scope.go (extend)\n\n## Acceptance criteria\n- CreateComment returns the created comment\n- Returns ErrCommentNotFound when appropriate","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:08.156950163+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:38:19.679658075+01:00","closed_at":"2026-01-15T18:38:19.679658075+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-h4u","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:43:48.898295911+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-h4u","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:43:49.058259216+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-h4u","title":"Implement Comment API methods","description":"Implement direct API methods for comment operations.\n\n## Methods to implement\n- GetAllComments(ctx, taskID int) ([]Comment, error) - getAllComments\n- CreateComment(ctx, taskID, userID int, content string) (*Comment, error) - createComment\n- UpdateComment(ctx, commentID int, content string) error - updateComment\n- RemoveComment(ctx, commentID int) error - removeComment\n\n## TaskScope methods to add\n- AddComment(ctx, content string) (*Comment, error)\n- GetComments(ctx) ([]Comment, error)\n\n## Files to create\n- comments.go\n- task_scope.go (extend)\n\n## Acceptance criteria\n- CreateComment returns the created comment\n- Returns ErrCommentNotFound when appropriate","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:08.156950163+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:38:19.679658075+01:00","closed_at":"2026-01-15T18:38:19.679658075+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-h4u","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:43:48.898295911+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-h4u","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:43:49.058259216+01:00","created_by":"Oliver Jakoubek"}]}
@ -19,7 +19,7 @@
{"id":"kanboard-api-ob2","title":"Add MIT license","description":"Add MIT license file to the repository.\n\n## Files to create\n- LICENSE\n\n## Content\nStandard MIT license text with appropriate copyright holder.\n\n## Acceptance criteria\n- Valid MIT license\n- Proper copyright attribution","status":"open","priority":3,"issue_type":"chore","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:53.955909551+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:36:53.955909551+01:00"} {"id":"kanboard-api-ob2","title":"Add MIT license","description":"Add MIT license file to the repository.\n\n## Files to create\n- LICENSE\n\n## Content\nStandard MIT license text with appropriate copyright holder.\n\n## Acceptance criteria\n- Valid MIT license\n- Proper copyright attribution","status":"open","priority":3,"issue_type":"chore","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:53.955909551+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:36:53.955909551+01:00"}
{"id":"kanboard-api-s7k","title":"Implement error types and handling","description":"Implement comprehensive error types for the Kanboard API client.\n\n## Sentinel Errors\n- ErrConnectionFailed, ErrTimeout (network)\n- ErrUnauthorized, ErrForbidden (auth)\n- ErrNotFound, ErrProjectNotFound, ErrTaskNotFound, ErrColumnNotFound, ErrCommentNotFound (resources)\n- ErrAlreadyInLastColumn, ErrAlreadyInFirstColumn, ErrTaskClosed, ErrTaskOpen (logic)\n- ErrEmptyTitle, ErrInvalidProjectID (validation)\n- ErrAPIError (API)\n\n## APIError struct\n- Code int\n- Message string\n- Error() string method\n\n## Helper functions\n- IsNotFound(err error) bool\n- IsUnauthorized(err error) bool\n- IsAPIError(err error) bool\n\n## Files to create\n- errors.go\n\n## Acceptance criteria\n- All errors properly support errors.Is/errors.As\n- Error wrapping with %w for context preservation","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:34:54.484116412+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:13:03.195687148+01:00","closed_at":"2026-01-15T18:13:03.195687148+01:00","close_reason":"Closed"} {"id":"kanboard-api-s7k","title":"Implement error types and handling","description":"Implement comprehensive error types for the Kanboard API client.\n\n## Sentinel Errors\n- ErrConnectionFailed, ErrTimeout (network)\n- ErrUnauthorized, ErrForbidden (auth)\n- ErrNotFound, ErrProjectNotFound, ErrTaskNotFound, ErrColumnNotFound, ErrCommentNotFound (resources)\n- ErrAlreadyInLastColumn, ErrAlreadyInFirstColumn, ErrTaskClosed, ErrTaskOpen (logic)\n- ErrEmptyTitle, ErrInvalidProjectID (validation)\n- ErrAPIError (API)\n\n## APIError struct\n- Code int\n- Message string\n- Error() string method\n\n## Helper functions\n- IsNotFound(err error) bool\n- IsUnauthorized(err error) bool\n- IsAPIError(err error) bool\n\n## Files to create\n- errors.go\n\n## Acceptance criteria\n- All errors properly support errors.Is/errors.As\n- Error wrapping with %w for context preservation","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:34:54.484116412+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:13:03.195687148+01:00","closed_at":"2026-01-15T18:13:03.195687148+01:00","close_reason":"Closed"}
{"id":"kanboard-api-t36","title":"Implement MoveToNextColumn with column logic","description":"Implement TaskScope.MoveToNextColumn with automatic column position logic.\n\n## Method to implement\n- TaskScope.MoveToNextColumn(ctx) error\n\n## Workflow\n1. Get task via getTask\n2. Get columns for task's project via getColumns\n3. Sort columns by position\n4. Find current column position\n5. If last column: return ErrAlreadyInLastColumn\n6. Move to next column via moveTaskPosition\n\n## Files to modify\n- task_scope.go\n\n## Acceptance criteria\n- Correctly identifies next column by position\n- Returns ErrAlreadyInLastColumn for last column\n- Handles column gaps (positions 1, 3, 5 etc.)","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:29.900647957+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:41:56.137534164+01:00","closed_at":"2026-01-15T18:41:56.137534164+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-t36","depends_on_id":"kanboard-api-una","type":"blocks","created_at":"2026-01-15T17:45:32.256714732+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-t36","depends_on_id":"kanboard-api-l9b","type":"blocks","created_at":"2026-01-15T17:45:32.319227723+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-t36","title":"Implement MoveToNextColumn with column logic","description":"Implement TaskScope.MoveToNextColumn with automatic column position logic.\n\n## Method to implement\n- TaskScope.MoveToNextColumn(ctx) error\n\n## Workflow\n1. Get task via getTask\n2. Get columns for task's project via getColumns\n3. Sort columns by position\n4. Find current column position\n5. If last column: return ErrAlreadyInLastColumn\n6. Move to next column via moveTaskPosition\n\n## Files to modify\n- task_scope.go\n\n## Acceptance criteria\n- Correctly identifies next column by position\n- Returns ErrAlreadyInLastColumn for last column\n- Handles column gaps (positions 1, 3, 5 etc.)","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:29.900647957+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:41:56.137534164+01:00","closed_at":"2026-01-15T18:41:56.137534164+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-t36","depends_on_id":"kanboard-api-una","type":"blocks","created_at":"2026-01-15T17:45:32.256714732+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-t36","depends_on_id":"kanboard-api-l9b","type":"blocks","created_at":"2026-01-15T17:45:32.319227723+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-ukc","title":"Set up Mage build system","description":"Set up Mage build system for development automation.\n\n## Directory structure\n```\nmagefiles/\n├── go.mod\n├── go.sum\n└── magefile.go\n```\n\n## Targets to implement\n- Test() - Run all tests with race detection and coverage\n- Coverage() - Run tests and open HTML coverage report\n- Lint() - Run golangci-lint\n- Build() - Verify module compiles\n\n## Files to create\n- magefiles/go.mod\n- magefiles/magefile.go\n\n## Acceptance criteria\n- `mage test` runs tests with -race and -coverprofile\n- `mage lint` runs golangci-lint\n- `mage build` verifies compilation","status":"in_progress","priority":2,"issue_type":"chore","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:30.260235504+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T19:14:39.525927006+01:00"} {"id":"kanboard-api-ukc","title":"Set up Mage build system","description":"Set up Mage build system for development automation.\n\n## Directory structure\n```\nmagefiles/\n├── go.mod\n├── go.sum\n└── magefile.go\n```\n\n## Targets to implement\n- Test() - Run all tests with race detection and coverage\n- Coverage() - Run tests and open HTML coverage report\n- Lint() - Run golangci-lint\n- Build() - Verify module compiles\n\n## Files to create\n- magefiles/go.mod\n- magefiles/magefile.go\n\n## Acceptance criteria\n- `mage test` runs tests with -race and -coverprofile\n- `mage lint` runs golangci-lint\n- `mage build` verifies compilation","status":"closed","priority":2,"issue_type":"chore","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:30.260235504+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T19:20:02.610796083+01:00","closed_at":"2026-01-15T19:20:02.610796083+01:00","close_reason":"Mage build system implemented with test, coverage, lint, and build targets"}
{"id":"kanboard-api-uls","title":"Implement Client struct with fluent configuration","description":"Implement the main Client struct with fluent builder pattern for configuration.\n\n## Requirements\n- Client struct with baseURL, httpClient, auth, logger, requestID\n- NewClient(baseURL string) constructor\n- Fluent configuration methods:\n - WithAPIToken(token string) *Client\n - WithBasicAuth(username, password string) *Client\n - WithHTTPClient(client *http.Client) *Client\n - WithTimeout(timeout time.Duration) *Client\n - WithLogger(logger *slog.Logger) *Client\n- Default timeout of 30 seconds\n- Thread-safe for concurrent use\n\n## Files to create\n- client.go\n\n## Acceptance criteria\n- Client is immutable after configuration\n- Thread-safe concurrent usage\n- Configurable HTTP client and logger","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:34:54.051926732+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:14:16.530243958+01:00","closed_at":"2026-01-15T18:14:16.530243958+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-uls","depends_on_id":"kanboard-api-2g1","type":"blocks","created_at":"2026-01-15T17:42:48.457362117+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-uls","depends_on_id":"kanboard-api-k33","type":"blocks","created_at":"2026-01-15T17:42:48.576622508+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-uls","depends_on_id":"kanboard-api-s7k","type":"blocks","created_at":"2026-01-15T17:42:48.749342195+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-uls","title":"Implement Client struct with fluent configuration","description":"Implement the main Client struct with fluent builder pattern for configuration.\n\n## Requirements\n- Client struct with baseURL, httpClient, auth, logger, requestID\n- NewClient(baseURL string) constructor\n- Fluent configuration methods:\n - WithAPIToken(token string) *Client\n - WithBasicAuth(username, password string) *Client\n - WithHTTPClient(client *http.Client) *Client\n - WithTimeout(timeout time.Duration) *Client\n - WithLogger(logger *slog.Logger) *Client\n- Default timeout of 30 seconds\n- Thread-safe for concurrent use\n\n## Files to create\n- client.go\n\n## Acceptance criteria\n- Client is immutable after configuration\n- Thread-safe concurrent usage\n- Configurable HTTP client and logger","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:34:54.051926732+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:14:16.530243958+01:00","closed_at":"2026-01-15T18:14:16.530243958+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-uls","depends_on_id":"kanboard-api-2g1","type":"blocks","created_at":"2026-01-15T17:42:48.457362117+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-uls","depends_on_id":"kanboard-api-k33","type":"blocks","created_at":"2026-01-15T17:42:48.576622508+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-uls","depends_on_id":"kanboard-api-s7k","type":"blocks","created_at":"2026-01-15T17:42:48.749342195+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-una","title":"Implement TaskScope fluent builder (core methods)","description":"Implement TaskScope for fluent task-scoped operations (core methods).\n\n## Requirements\n- TaskScope struct with client and taskID\n- Client.Task(taskID int) *TaskScope method\n- Core TaskScope methods:\n - Get(ctx) (*Task, error)\n - Close(ctx) error\n - Open(ctx) error\n - MoveToColumn(ctx, columnID int) error\n - MoveToProject(ctx, projectID int) error\n - Update(ctx, params *TaskUpdateParams) error\n\n## Files to create\n- task_scope.go\n\n## Example usage\n```go\ntask, _ := client.Task(123).Get(ctx)\nclient.Task(123).Close(ctx)\nclient.Task(123).Update(ctx, kanboard.NewTaskUpdate().SetTitle(\"New\"))\n```\n\n## Acceptance criteria\n- All methods delegate to direct Client methods\n- Proper error propagation","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:41.173930396+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:34:32.369975567+01:00","closed_at":"2026-01-15T18:34:32.369975567+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-una","depends_on_id":"kanboard-api-91a","type":"blocks","created_at":"2026-01-15T17:43:31.198935686+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-una","depends_on_id":"kanboard-api-5fb","type":"blocks","created_at":"2026-01-15T17:43:31.261453756+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-una","title":"Implement TaskScope fluent builder (core methods)","description":"Implement TaskScope for fluent task-scoped operations (core methods).\n\n## Requirements\n- TaskScope struct with client and taskID\n- Client.Task(taskID int) *TaskScope method\n- Core TaskScope methods:\n - Get(ctx) (*Task, error)\n - Close(ctx) error\n - Open(ctx) error\n - MoveToColumn(ctx, columnID int) error\n - MoveToProject(ctx, projectID int) error\n - Update(ctx, params *TaskUpdateParams) error\n\n## Files to create\n- task_scope.go\n\n## Example usage\n```go\ntask, _ := client.Task(123).Get(ctx)\nclient.Task(123).Close(ctx)\nclient.Task(123).Update(ctx, kanboard.NewTaskUpdate().SetTitle(\"New\"))\n```\n\n## Acceptance criteria\n- All methods delegate to direct Client methods\n- Proper error propagation","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:41.173930396+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T18:34:32.369975567+01:00","closed_at":"2026-01-15T18:34:32.369975567+01:00","close_reason":"Closed","dependencies":[{"issue_id":"kanboard-api-una","depends_on_id":"kanboard-api-91a","type":"blocks","created_at":"2026-01-15T17:43:31.198935686+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-una","depends_on_id":"kanboard-api-5fb","type":"blocks","created_at":"2026-01-15T17:43:31.261453756+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"kanboard-api-xhf","title":"Create example programs","description":"Create example programs demonstrating library usage.\n\n## Examples to create\n```\nexamples/\n├── basic/\n│ └── main.go # Basic client setup and simple operations\n├── fluent/\n│ └── main.go # Fluent API demonstration\n└── search/\n └── main.go # Search functionality demo\n```\n\n## basic/main.go\n- Client creation with API token\n- Get all projects\n- Get tasks from a project\n- Create a simple task\n\n## fluent/main.go\n- Client configuration with all options\n- Task creation with TaskParams\n- Task updates with TaskUpdateParams\n- Tag operations\n\n## search/main.go\n- Project-specific search\n- Global search across all projects\n\n## Files to create\n- examples/basic/main.go\n- examples/fluent/main.go\n- examples/search/main.go\n\n## Acceptance criteria\n- Examples compile and are well-commented\n- Cover main use cases\n- Show both fluent and direct API styles","status":"open","priority":3,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:53.604889443+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:36:53.604889443+01:00","dependencies":[{"issue_id":"kanboard-api-xhf","depends_on_id":"kanboard-api-2ze","type":"blocks","created_at":"2026-01-15T17:46:55.571585285+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-xhf","depends_on_id":"kanboard-api-una","type":"blocks","created_at":"2026-01-15T17:46:55.63515762+01:00","created_by":"Oliver Jakoubek"}]} {"id":"kanboard-api-xhf","title":"Create example programs","description":"Create example programs demonstrating library usage.\n\n## Examples to create\n```\nexamples/\n├── basic/\n│ └── main.go # Basic client setup and simple operations\n├── fluent/\n│ └── main.go # Fluent API demonstration\n└── search/\n └── main.go # Search functionality demo\n```\n\n## basic/main.go\n- Client creation with API token\n- Get all projects\n- Get tasks from a project\n- Create a simple task\n\n## fluent/main.go\n- Client configuration with all options\n- Task creation with TaskParams\n- Task updates with TaskUpdateParams\n- Tag operations\n\n## search/main.go\n- Project-specific search\n- Global search across all projects\n\n## Files to create\n- examples/basic/main.go\n- examples/fluent/main.go\n- examples/search/main.go\n\n## Acceptance criteria\n- Examples compile and are well-commented\n- Cover main use cases\n- Show both fluent and direct API styles","status":"open","priority":3,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:53.604889443+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:36:53.604889443+01:00","dependencies":[{"issue_id":"kanboard-api-xhf","depends_on_id":"kanboard-api-2ze","type":"blocks","created_at":"2026-01-15T17:46:55.571585285+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"kanboard-api-xhf","depends_on_id":"kanboard-api-una","type":"blocks","created_at":"2026-01-15T17:46:55.63515762+01:00","created_by":"Oliver Jakoubek"}]}

240
README.md Normal file
View file

@ -0,0 +1,240 @@
# kanboard-api
Go library for the [Kanboard](https://kanboard.org/) JSON-RPC API. Provides a fluent, chainable API for integrating Kanboard into Go applications.
## Installation
```bash
go get code.beautifulmachines.dev/jakoubek/kanboard-api
```
## Quick Start
```go
package main
import (
"context"
"fmt"
"log"
"time"
kanboard "code.beautifulmachines.dev/jakoubek/kanboard-api"
)
func main() {
ctx := context.Background()
// Create client with API token
client := kanboard.NewClient("https://kanboard.example.com").
WithAPIToken("your-api-token").
WithTimeout(30 * time.Second)
// Create a task using the fluent API
task, err := client.Board(1).CreateTaskFromParams(ctx,
kanboard.NewTask("My Task").
WithDescription("Task description").
WithPriority(2).
WithTags("urgent", "backend"))
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created task: %d\n", task.ID)
}
```
## Authentication
The library supports two authentication methods:
### API Token (Recommended)
```go
client := kanboard.NewClient("https://kanboard.example.com").
WithAPIToken("your-api-token")
```
### Username/Password
```go
client := kanboard.NewClient("https://kanboard.example.com").
WithBasicAuth("admin", "password")
```
## API Usage
### Fluent API
The fluent API provides chainable, scoped operations:
```go
// Board-scoped operations
board := client.Board(projectID)
tasks, _ := board.GetTasks(ctx, kanboard.StatusActive)
columns, _ := board.GetColumns(ctx)
categories, _ := board.GetCategories(ctx)
// Task-scoped operations
task := client.Task(taskID)
task.Close(ctx)
task.MoveToNextColumn(ctx)
task.AddTag(ctx, "reviewed")
task.AddComment(ctx, userID, "Comment text")
```
### Direct API
For lower-level access, use the direct API methods:
```go
// Projects
projects, _ := client.GetAllProjects(ctx)
project, _ := client.GetProjectByID(ctx, projectID)
project, _ := client.GetProjectByName(ctx, "My Project")
// Tasks
tasks, _ := client.GetAllTasks(ctx, projectID, kanboard.StatusActive)
task, _ := client.GetTask(ctx, taskID)
task, _ := client.CreateTask(ctx, kanboard.CreateTaskRequest{
Title: "New Task",
ProjectID: projectID,
})
// Global search across all projects
results, _ := client.SearchTasksGlobal(ctx, "search query")
```
### Task Creation
Use `TaskParams` for fluent task creation:
```go
params := kanboard.NewTask("Task Title").
WithDescription("Detailed description").
WithCategory(categoryID).
WithOwner(userID).
WithColor("red").
WithPriority(3).
WithScore(5).
WithDueDate(time.Now().Add(7 * 24 * time.Hour)).
WithTags("feature", "v2.0").
InColumn(columnID).
InSwimlane(swimlaneID)
task, err := client.Board(projectID).CreateTaskFromParams(ctx, params)
```
### Task Updates
Use `TaskUpdateParams` for partial updates:
```go
updates := kanboard.NewTaskUpdate().
SetTitle("Updated Title").
SetDescription("New description").
SetPriority(1)
err := client.Task(taskID).Update(ctx, updates)
```
### Task Movement
```go
// Move to next/previous column in workflow
client.Task(taskID).MoveToNextColumn(ctx)
client.Task(taskID).MoveToPreviousColumn(ctx)
// Move to specific column
client.Task(taskID).MoveToColumn(ctx, columnID)
// Move to different project
client.Task(taskID).MoveToProject(ctx, newProjectID)
```
### Comments
```go
// Get all comments
comments, _ := client.Task(taskID).GetComments(ctx)
// Add a comment
comment, _ := client.Task(taskID).AddComment(ctx, userID, "Comment text")
```
### Links
```go
// Get task links
links, _ := client.Task(taskID).GetLinks(ctx)
// Link tasks together
client.Task(taskID).LinkTo(ctx, otherTaskID, linkID)
```
### Files
```go
// Get attached files
files, _ := client.Task(taskID).GetFiles(ctx)
// Upload a file
content := []byte("file content")
fileID, _ := client.Task(taskID).UploadFile(ctx, "document.txt", content)
```
## Error Handling
The library provides typed errors for common scenarios:
```go
task, err := client.GetTask(ctx, taskID)
if err != nil {
if kanboard.IsNotFound(err) {
// Handle not found
}
if kanboard.IsUnauthorized(err) {
// Handle auth failure
}
if kanboard.IsAPIError(err) {
// Handle Kanboard API error
}
}
```
Available sentinel errors:
- `ErrNotFound`, `ErrProjectNotFound`, `ErrTaskNotFound`, `ErrColumnNotFound`
- `ErrUnauthorized`, `ErrForbidden`
- `ErrConnectionFailed`, `ErrTimeout`
- `ErrAlreadyInLastColumn`, `ErrAlreadyInFirstColumn`
- `ErrEmptyTitle`, `ErrInvalidProjectID`
## Thread Safety
The client is safe for concurrent use by multiple goroutines. Request IDs are generated atomically.
## Tag Operations Warning
Kanboard's `setTaskTags` API **replaces all tags**. The library implements read-modify-write internally for `AddTag` and `RemoveTag` operations. This is not atomic - concurrent tag modifications may cause data loss.
```go
// Safe: single operation
task.SetTags(ctx, "tag1", "tag2", "tag3")
// Caution: concurrent calls to AddTag/RemoveTag may conflict
task.AddTag(ctx, "new-tag")
task.RemoveTag(ctx, "old-tag")
```
## Client Configuration
```go
client := kanboard.NewClient("https://kanboard.example.com").
WithAPIToken("token").
WithTimeout(60 * time.Second).
WithHTTPClient(customHTTPClient).
WithLogger(slog.Default())
```
## License
MIT License - see [LICENSE](LICENSE) for details.