Implement TaskParams builder (Options Pattern)
- TaskParams struct with fluent setter methods - NewTask(title) constructor - Setters: WithDescription, InColumn, WithCategory, WithOwner, WithCreator, WithColor, WithPriority, WithScore, WithDueDate, WithStartDate, InSwimlane, WithReference, WithTags - toCreateTaskRequest for conversion to CreateTaskRequest - BoardScope.CreateTaskFromParams for fluent task creation - Comprehensive test coverage for all setters and chaining
This commit is contained in:
parent
f04ea8866f
commit
28e517289a
4 changed files with 409 additions and 1 deletions
|
|
@ -7,7 +7,7 @@
|
||||||
{"id":"kanboard-api-5fb","title":"Implement TaskUpdateParams builder","description":"Implement TaskUpdateParams for fluent task update configuration.\n\n## Requirements\n- TaskUpdateParams struct with pointer fields\n- NewTaskUpdate() *TaskUpdateParams constructor\n- Fluent setter methods:\n - SetTitle(title string) *TaskUpdateParams\n - SetDescription(desc string) *TaskUpdateParams\n - SetColor(colorID string) *TaskUpdateParams\n - SetOwner(ownerID int) *TaskUpdateParams\n - SetCategory(categoryID int) *TaskUpdateParams\n - SetPriority(priority int) *TaskUpdateParams\n - SetDueDate(date time.Time) *TaskUpdateParams\n- Internal method to convert to UpdateTaskRequest\n\n## Files to create\n- task_update_params.go\n\n## Example usage\n```go\nparams := kanboard.NewTaskUpdate().\n SetTitle(\"New Title\").\n SetPriority(2)\n```\n\n## Acceptance criteria\n- Only set fields are included in update request\n- All setters return *TaskUpdateParams for chaining","status":"open","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:40.814955926+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:35:40.814955926+01:00","dependencies":[{"issue_id":"kanboard-api-5fb","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:43:31.134402453+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"kanboard-api-5fb","title":"Implement TaskUpdateParams builder","description":"Implement TaskUpdateParams for fluent task update configuration.\n\n## Requirements\n- TaskUpdateParams struct with pointer fields\n- NewTaskUpdate() *TaskUpdateParams constructor\n- Fluent setter methods:\n - SetTitle(title string) *TaskUpdateParams\n - SetDescription(desc string) *TaskUpdateParams\n - SetColor(colorID string) *TaskUpdateParams\n - SetOwner(ownerID int) *TaskUpdateParams\n - SetCategory(categoryID int) *TaskUpdateParams\n - SetPriority(priority int) *TaskUpdateParams\n - SetDueDate(date time.Time) *TaskUpdateParams\n- Internal method to convert to UpdateTaskRequest\n\n## Files to create\n- task_update_params.go\n\n## Example usage\n```go\nparams := kanboard.NewTaskUpdate().\n SetTitle(\"New Title\").\n SetPriority(2)\n```\n\n## Acceptance criteria\n- Only set fields are included in update request\n- All setters return *TaskUpdateParams for chaining","status":"open","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:35:40.814955926+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:35:40.814955926+01:00","dependencies":[{"issue_id":"kanboard-api-5fb","depends_on_id":"kanboard-api-cyc","type":"blocks","created_at":"2026-01-15T17:43:31.134402453+01:00","created_by":"Oliver Jakoubek"}]}
|
||||||
{"id":"kanboard-api-70e","title":"Add GoDoc documentation to all public APIs","description":"Add comprehensive GoDoc comments to all exported types and functions.\n\n## Requirements\n- Package-level documentation in doc.go\n- All exported types documented\n- All exported functions documented with examples\n- All exported constants/variables documented\n\n## Documentation standards\n- Start with the name being documented\n- Include usage examples where helpful\n- Document error conditions\n- Note thread-safety characteristics\n\n## Files to create/modify\n- doc.go (package documentation)\n- All .go files with exported symbols\n\n## Acceptance criteria\n- `go doc` produces clean output\n- All exports have documentation\n- Examples are runnable","status":"open","priority":2,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:54.342657373+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:36:54.342657373+01:00","dependencies":[{"issue_id":"kanboard-api-70e","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:46:55.508211397+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"kanboard-api-70e","title":"Add GoDoc documentation to all public APIs","description":"Add comprehensive GoDoc comments to all exported types and functions.\n\n## Requirements\n- Package-level documentation in doc.go\n- All exported types documented\n- All exported functions documented with examples\n- All exported constants/variables documented\n\n## Documentation standards\n- Start with the name being documented\n- Include usage examples where helpful\n- Document error conditions\n- Note thread-safety characteristics\n\n## Files to create/modify\n- doc.go (package documentation)\n- All .go files with exported symbols\n\n## Acceptance criteria\n- `go doc` produces clean output\n- All exports have documentation\n- Examples are runnable","status":"open","priority":2,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-15T17:36:54.342657373+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-15T17:36:54.342657373+01:00","dependencies":[{"issue_id":"kanboard-api-70e","depends_on_id":"kanboard-api-uls","type":"blocks","created_at":"2026-01-15T17:46:55.508211397+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-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":"open","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-15T17:35:40.439513879+01:00","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":"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-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"}]}
|
||||||
|
|
|
||||||
|
|
@ -42,3 +42,10 @@ func (b *BoardScope) CreateTask(ctx context.Context, req CreateTaskRequest) (*Ta
|
||||||
req.ProjectID = b.projectID
|
req.ProjectID = b.projectID
|
||||||
return b.client.CreateTask(ctx, req)
|
return b.client.CreateTask(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateTaskFromParams creates a new task in the project using TaskParams.
|
||||||
|
// This provides a fluent interface for task creation.
|
||||||
|
func (b *BoardScope) CreateTaskFromParams(ctx context.Context, params *TaskParams) (*Task, error) {
|
||||||
|
req := params.toCreateTaskRequest(b.projectID)
|
||||||
|
return b.client.CreateTask(ctx, req)
|
||||||
|
}
|
||||||
|
|
|
||||||
160
task_params.go
Normal file
160
task_params.go
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
package kanboard
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// TaskParams is a fluent builder for task creation configuration.
|
||||||
|
// It is a pure configuration object with no I/O.
|
||||||
|
type TaskParams struct {
|
||||||
|
title string
|
||||||
|
description *string
|
||||||
|
columnID *int
|
||||||
|
categoryID *int
|
||||||
|
ownerID *int
|
||||||
|
creatorID *int
|
||||||
|
colorID *string
|
||||||
|
priority *int
|
||||||
|
score *int
|
||||||
|
dueDate *int64
|
||||||
|
startDate *int64
|
||||||
|
swimlaneID *int
|
||||||
|
reference *string
|
||||||
|
tags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTask creates a new TaskParams with the given title.
|
||||||
|
func NewTask(title string) *TaskParams {
|
||||||
|
return &TaskParams{
|
||||||
|
title: title,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDescription sets the task description.
|
||||||
|
func (p *TaskParams) WithDescription(desc string) *TaskParams {
|
||||||
|
p.description = &desc
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// InColumn sets the column ID for the task.
|
||||||
|
func (p *TaskParams) InColumn(columnID int) *TaskParams {
|
||||||
|
p.columnID = &columnID
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCategory sets the category ID for the task.
|
||||||
|
func (p *TaskParams) WithCategory(categoryID int) *TaskParams {
|
||||||
|
p.categoryID = &categoryID
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOwner sets the owner (assignee) ID for the task.
|
||||||
|
func (p *TaskParams) WithOwner(ownerID int) *TaskParams {
|
||||||
|
p.ownerID = &ownerID
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCreator sets the creator ID for the task.
|
||||||
|
func (p *TaskParams) WithCreator(creatorID int) *TaskParams {
|
||||||
|
p.creatorID = &creatorID
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithColor sets the color ID for the task.
|
||||||
|
func (p *TaskParams) WithColor(colorID string) *TaskParams {
|
||||||
|
p.colorID = &colorID
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPriority sets the priority for the task.
|
||||||
|
func (p *TaskParams) WithPriority(priority int) *TaskParams {
|
||||||
|
p.priority = &priority
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithScore sets the complexity score for the task.
|
||||||
|
func (p *TaskParams) WithScore(score int) *TaskParams {
|
||||||
|
p.score = &score
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDueDate sets the due date for the task.
|
||||||
|
func (p *TaskParams) WithDueDate(date time.Time) *TaskParams {
|
||||||
|
ts := date.Unix()
|
||||||
|
p.dueDate = &ts
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithStartDate sets the start date for the task.
|
||||||
|
func (p *TaskParams) WithStartDate(date time.Time) *TaskParams {
|
||||||
|
ts := date.Unix()
|
||||||
|
p.startDate = &ts
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// InSwimlane sets the swimlane ID for the task.
|
||||||
|
func (p *TaskParams) InSwimlane(swimlaneID int) *TaskParams {
|
||||||
|
p.swimlaneID = &swimlaneID
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithReference sets the external reference for the task.
|
||||||
|
func (p *TaskParams) WithReference(ref string) *TaskParams {
|
||||||
|
p.reference = &ref
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTags sets the tags for the task.
|
||||||
|
func (p *TaskParams) WithTags(tags ...string) *TaskParams {
|
||||||
|
p.tags = tags
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// toCreateTaskRequest converts TaskParams to a CreateTaskRequest.
|
||||||
|
// The projectID is required and must be provided by the caller.
|
||||||
|
func (p *TaskParams) toCreateTaskRequest(projectID int) CreateTaskRequest {
|
||||||
|
req := CreateTaskRequest{
|
||||||
|
Title: p.title,
|
||||||
|
ProjectID: projectID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.description != nil {
|
||||||
|
req.Description = *p.description
|
||||||
|
}
|
||||||
|
if p.columnID != nil {
|
||||||
|
req.ColumnID = *p.columnID
|
||||||
|
}
|
||||||
|
if p.categoryID != nil {
|
||||||
|
req.CategoryID = *p.categoryID
|
||||||
|
}
|
||||||
|
if p.ownerID != nil {
|
||||||
|
req.OwnerID = *p.ownerID
|
||||||
|
}
|
||||||
|
if p.creatorID != nil {
|
||||||
|
req.CreatorID = *p.creatorID
|
||||||
|
}
|
||||||
|
if p.colorID != nil {
|
||||||
|
req.ColorID = *p.colorID
|
||||||
|
}
|
||||||
|
if p.priority != nil {
|
||||||
|
req.Priority = *p.priority
|
||||||
|
}
|
||||||
|
if p.score != nil {
|
||||||
|
req.Score = *p.score
|
||||||
|
}
|
||||||
|
if p.dueDate != nil {
|
||||||
|
req.DateDue = *p.dueDate
|
||||||
|
}
|
||||||
|
if p.startDate != nil {
|
||||||
|
req.DateStarted = *p.startDate
|
||||||
|
}
|
||||||
|
if p.swimlaneID != nil {
|
||||||
|
req.SwimlaneID = *p.swimlaneID
|
||||||
|
}
|
||||||
|
if p.reference != nil {
|
||||||
|
req.Reference = *p.reference
|
||||||
|
}
|
||||||
|
if len(p.tags) > 0 {
|
||||||
|
req.Tags = p.tags
|
||||||
|
}
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
241
task_params_test.go
Normal file
241
task_params_test.go
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
package kanboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewTask(t *testing.T) {
|
||||||
|
params := NewTask("Test Task")
|
||||||
|
|
||||||
|
if params.title != "Test Task" {
|
||||||
|
t.Errorf("expected title='Test Task', got %s", params.title)
|
||||||
|
}
|
||||||
|
if params.description != nil {
|
||||||
|
t.Error("expected description to be nil")
|
||||||
|
}
|
||||||
|
if params.columnID != nil {
|
||||||
|
t.Error("expected columnID to be nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskParams_Chaining(t *testing.T) {
|
||||||
|
dueDate := time.Date(2025, 12, 31, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
params := NewTask("My Task").
|
||||||
|
WithDescription("Task details").
|
||||||
|
InColumn(2).
|
||||||
|
WithCategory(5).
|
||||||
|
WithOwner(10).
|
||||||
|
WithColor("blue").
|
||||||
|
WithPriority(2).
|
||||||
|
WithDueDate(dueDate).
|
||||||
|
WithTags("urgent", "backend").
|
||||||
|
WithReference("JIRA-123")
|
||||||
|
|
||||||
|
if params.title != "My Task" {
|
||||||
|
t.Errorf("expected title='My Task', got %s", params.title)
|
||||||
|
}
|
||||||
|
if *params.description != "Task details" {
|
||||||
|
t.Errorf("expected description='Task details', got %s", *params.description)
|
||||||
|
}
|
||||||
|
if *params.columnID != 2 {
|
||||||
|
t.Errorf("expected columnID=2, got %d", *params.columnID)
|
||||||
|
}
|
||||||
|
if *params.categoryID != 5 {
|
||||||
|
t.Errorf("expected categoryID=5, got %d", *params.categoryID)
|
||||||
|
}
|
||||||
|
if *params.ownerID != 10 {
|
||||||
|
t.Errorf("expected ownerID=10, got %d", *params.ownerID)
|
||||||
|
}
|
||||||
|
if *params.colorID != "blue" {
|
||||||
|
t.Errorf("expected colorID='blue', got %s", *params.colorID)
|
||||||
|
}
|
||||||
|
if *params.priority != 2 {
|
||||||
|
t.Errorf("expected priority=2, got %d", *params.priority)
|
||||||
|
}
|
||||||
|
if *params.dueDate != dueDate.Unix() {
|
||||||
|
t.Errorf("expected dueDate=%d, got %d", dueDate.Unix(), *params.dueDate)
|
||||||
|
}
|
||||||
|
if len(params.tags) != 2 || params.tags[0] != "urgent" || params.tags[1] != "backend" {
|
||||||
|
t.Errorf("expected tags=['urgent', 'backend'], got %v", params.tags)
|
||||||
|
}
|
||||||
|
if *params.reference != "JIRA-123" {
|
||||||
|
t.Errorf("expected reference='JIRA-123', got %s", *params.reference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskParams_WithScore(t *testing.T) {
|
||||||
|
params := NewTask("Task").WithScore(8)
|
||||||
|
|
||||||
|
if *params.score != 8 {
|
||||||
|
t.Errorf("expected score=8, got %d", *params.score)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskParams_WithCreator(t *testing.T) {
|
||||||
|
params := NewTask("Task").WithCreator(5)
|
||||||
|
|
||||||
|
if *params.creatorID != 5 {
|
||||||
|
t.Errorf("expected creatorID=5, got %d", *params.creatorID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskParams_WithStartDate(t *testing.T) {
|
||||||
|
startDate := time.Date(2025, 6, 15, 0, 0, 0, 0, time.UTC)
|
||||||
|
params := NewTask("Task").WithStartDate(startDate)
|
||||||
|
|
||||||
|
if *params.startDate != startDate.Unix() {
|
||||||
|
t.Errorf("expected startDate=%d, got %d", startDate.Unix(), *params.startDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskParams_InSwimlane(t *testing.T) {
|
||||||
|
params := NewTask("Task").InSwimlane(3)
|
||||||
|
|
||||||
|
if *params.swimlaneID != 3 {
|
||||||
|
t.Errorf("expected swimlaneID=3, got %d", *params.swimlaneID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskParams_toCreateTaskRequest(t *testing.T) {
|
||||||
|
dueDate := time.Date(2025, 12, 31, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
params := NewTask("Test Task").
|
||||||
|
WithDescription("Details").
|
||||||
|
InColumn(2).
|
||||||
|
WithCategory(5).
|
||||||
|
WithOwner(10).
|
||||||
|
WithColor("blue").
|
||||||
|
WithPriority(2).
|
||||||
|
WithScore(8).
|
||||||
|
WithDueDate(dueDate).
|
||||||
|
WithTags("urgent", "backend").
|
||||||
|
WithReference("REF-123")
|
||||||
|
|
||||||
|
req := params.toCreateTaskRequest(42)
|
||||||
|
|
||||||
|
if req.Title != "Test Task" {
|
||||||
|
t.Errorf("expected Title='Test Task', got %s", req.Title)
|
||||||
|
}
|
||||||
|
if req.ProjectID != 42 {
|
||||||
|
t.Errorf("expected ProjectID=42, got %d", req.ProjectID)
|
||||||
|
}
|
||||||
|
if req.Description != "Details" {
|
||||||
|
t.Errorf("expected Description='Details', got %s", req.Description)
|
||||||
|
}
|
||||||
|
if req.ColumnID != 2 {
|
||||||
|
t.Errorf("expected ColumnID=2, got %d", req.ColumnID)
|
||||||
|
}
|
||||||
|
if req.CategoryID != 5 {
|
||||||
|
t.Errorf("expected CategoryID=5, got %d", req.CategoryID)
|
||||||
|
}
|
||||||
|
if req.OwnerID != 10 {
|
||||||
|
t.Errorf("expected OwnerID=10, got %d", req.OwnerID)
|
||||||
|
}
|
||||||
|
if req.ColorID != "blue" {
|
||||||
|
t.Errorf("expected ColorID='blue', got %s", req.ColorID)
|
||||||
|
}
|
||||||
|
if req.Priority != 2 {
|
||||||
|
t.Errorf("expected Priority=2, got %d", req.Priority)
|
||||||
|
}
|
||||||
|
if req.Score != 8 {
|
||||||
|
t.Errorf("expected Score=8, got %d", req.Score)
|
||||||
|
}
|
||||||
|
if req.DateDue != dueDate.Unix() {
|
||||||
|
t.Errorf("expected DateDue=%d, got %d", dueDate.Unix(), req.DateDue)
|
||||||
|
}
|
||||||
|
if len(req.Tags) != 2 {
|
||||||
|
t.Errorf("expected 2 tags, got %d", len(req.Tags))
|
||||||
|
}
|
||||||
|
if req.Reference != "REF-123" {
|
||||||
|
t.Errorf("expected Reference='REF-123', got %s", req.Reference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskParams_toCreateTaskRequest_MinimalParams(t *testing.T) {
|
||||||
|
params := NewTask("Simple Task")
|
||||||
|
|
||||||
|
req := params.toCreateTaskRequest(1)
|
||||||
|
|
||||||
|
if req.Title != "Simple Task" {
|
||||||
|
t.Errorf("expected Title='Simple Task', got %s", req.Title)
|
||||||
|
}
|
||||||
|
if req.ProjectID != 1 {
|
||||||
|
t.Errorf("expected ProjectID=1, got %d", req.ProjectID)
|
||||||
|
}
|
||||||
|
// Unset fields should have zero values
|
||||||
|
if req.Description != "" {
|
||||||
|
t.Errorf("expected Description='', got %s", req.Description)
|
||||||
|
}
|
||||||
|
if req.ColumnID != 0 {
|
||||||
|
t.Errorf("expected ColumnID=0, got %d", req.ColumnID)
|
||||||
|
}
|
||||||
|
if len(req.Tags) != 0 {
|
||||||
|
t.Errorf("expected 0 tags, got %d", len(req.Tags))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBoardScope_CreateTaskFromParams(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req JSONRPCRequest
|
||||||
|
json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
|
||||||
|
callCount++
|
||||||
|
if callCount == 1 {
|
||||||
|
// First call: createTask
|
||||||
|
if req.Method != "createTask" {
|
||||||
|
t.Errorf("expected method=createTask, got %s", req.Method)
|
||||||
|
}
|
||||||
|
|
||||||
|
params := req.Params.(map[string]any)
|
||||||
|
if params["title"] != "Fluent Task" {
|
||||||
|
t.Errorf("expected title='Fluent Task', got %v", params["title"])
|
||||||
|
}
|
||||||
|
if params["project_id"].(float64) != 1 {
|
||||||
|
t.Errorf("expected project_id=1, got %v", params["project_id"])
|
||||||
|
}
|
||||||
|
if params["description"] != "Task details" {
|
||||||
|
t.Errorf("expected description='Task details', got %v", params["description"])
|
||||||
|
}
|
||||||
|
if params["color_id"] != "red" {
|
||||||
|
t.Errorf("expected color_id='red', got %v", params["color_id"])
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := JSONRPCResponse{
|
||||||
|
JSONRPC: "2.0",
|
||||||
|
ID: req.ID,
|
||||||
|
Result: json.RawMessage(`42`),
|
||||||
|
}
|
||||||
|
json.NewEncoder(w).Encode(resp)
|
||||||
|
} else {
|
||||||
|
resp := JSONRPCResponse{
|
||||||
|
JSONRPC: "2.0",
|
||||||
|
ID: req.ID,
|
||||||
|
Result: json.RawMessage(`{"id": "42", "title": "Fluent Task", "project_id": "1", "is_active": "1"}`),
|
||||||
|
}
|
||||||
|
json.NewEncoder(w).Encode(resp)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client := NewClient(server.URL).WithAPIToken("test-token")
|
||||||
|
|
||||||
|
params := NewTask("Fluent Task").
|
||||||
|
WithDescription("Task details").
|
||||||
|
WithColor("red")
|
||||||
|
|
||||||
|
task, err := client.Board(1).CreateTaskFromParams(context.Background(), params)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(task.ID) != 42 {
|
||||||
|
t.Errorf("expected ID=42, got %d", task.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue