From 92d61003cb6328aa374b67e2feaf6095832a7493 Mon Sep 17 00:00:00 2001 From: Malcolm Burtenshaw Date: Thu, 4 Jan 2024 12:49:20 -0700 Subject: [PATCH 1/5] Add custom task type support --- clickup/tasks.go | 3 +++ clickup/tasks_test.go | 2 ++ 2 files changed, 5 insertions(+) diff --git a/clickup/tasks.go b/clickup/tasks.go index d6577eb..e64e916 100644 --- a/clickup/tasks.go +++ b/clickup/tasks.go @@ -16,6 +16,7 @@ type GetTasksResponse struct { type GetBulkTasksTimeInStatusResponse map[string]TasksInStatus +// See https://clickup.com/api/clickupreference/operation/CreateTask/ type TaskRequest struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` @@ -33,6 +34,7 @@ type TaskRequest struct { LinksTo string `json:"links_to,omitempty"` CheckRequiredCustomFields bool `json:"check_required_custom_fields,omitempty"` CustomFields []CustomFieldInTaskRequest `json:"custom_fields,omitempty"` + CustomItemId int `json:"custom_item_id,omitempty"` // To create a task that doesn't use a custom task type, either don't include this field in the request body, or send 'null'. To create this task as a Milestone, send a value of 1. To use a custom task type, send the custom task type ID as defined in your Workspace, such as 2. } type CustomFieldInTaskRequest struct { @@ -43,6 +45,7 @@ type CustomFieldInTaskRequest struct { type Task struct { ID string `json:"id"` CustomID string `json:"custom_id"` + CustomItemId int `json:"custom_item_id"` // A null value means this item is a task. A value of 1 is a Milestone. Any other number is a custom task type. Name string `json:"name"` TextContent string `json:"text_content"` Description string `json:"description"` diff --git a/clickup/tasks_test.go b/clickup/tasks_test.go index a2c692c..ef5c8c1 100644 --- a/clickup/tasks_test.go +++ b/clickup/tasks_test.go @@ -19,6 +19,7 @@ func TestTasksService_GetTask(t *testing.T) { `{ "id": "9hx", "custom_id":null, + "custom_item_id": null, "name": "Task Name", "text_content": "New Task Description", "description": "New Task Description", @@ -81,6 +82,7 @@ func TestTasksService_GetTask(t *testing.T) { want := &Task{ ID: "9hx", + CustomItemId: 0, Name: "Task Name", TextContent: "New Task Description", Description: "New Task Description", From 7bb9e0a9c6f9c9725081f4a9e438618ecafa4bcf Mon Sep 17 00:00:00 2001 From: Malcolm Burtenshaw Date: Thu, 4 Jan 2024 13:36:59 -0700 Subject: [PATCH 2/5] Add custom task types service --- README.md | 2 ++ clickup/client.go | 2 ++ clickup/custom_task_types.go | 38 +++++++++++++++++++++++++++++++ clickup/custom_task_types_test.go | 33 +++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 clickup/custom_task_types.go create mode 100644 clickup/custom_task_types_test.go diff --git a/README.md b/README.md index efe4122..1aaf39a 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,8 @@ If you are new to pull requests, checkout Collaborating on projects using issues - [x] Update Webhook - [x] Delete Webhook - [x] Get Webhooks +- [x] Custom Task Types + - [x] Get Custom Task Types ## License This project is released under the terms of the [MIT license](http://en.wikipedia.org/wiki/MIT_License). diff --git a/clickup/client.go b/clickup/client.go index bdf1799..b18e1e7 100644 --- a/clickup/client.go +++ b/clickup/client.go @@ -49,6 +49,7 @@ type Client struct { Checklists *ChecklistsService Comments *CommentsService CustomFields *CustomFieldsService + CustomTaskTypes *CustomTaskTypesService Dependencies *DependenciesService Goals *GoalsService Tasks *TasksService @@ -131,6 +132,7 @@ func NewClient(httpClient *http.Client, APIKey string) *Client { c.Checklists = (*ChecklistsService)(&c.common) c.Comments = (*CommentsService)(&c.common) c.CustomFields = (*CustomFieldsService)(&c.common) + c.CustomTaskTypes = (*CustomTaskTypesService)(&c.common) c.Dependencies = (*DependenciesService)(&c.common) c.Goals = (*GoalsService)(&c.common) c.Tasks = (*TasksService)(&c.common) diff --git a/clickup/custom_task_types.go b/clickup/custom_task_types.go new file mode 100644 index 0000000..67be67e --- /dev/null +++ b/clickup/custom_task_types.go @@ -0,0 +1,38 @@ +package clickup + +import ( + "context" + "fmt" +) + +type CustomTaskTypesService service + +// See https://clickup.com/api/clickupreference/operation/GetCustomItems/ +type GetCustomTaskTypesResponse struct { + CustomItems []CustomItem `json:"custom_items"` // Array of custom task types. +} + +// See https://clickup.com/api/clickupreference/operation/GetCustomItems/ +type CustomItem struct { + Id int32 `json:"id"` // Custom task type ID. + Name string `json:"name"` // Custom task type name. + NamePlural string `json:"name_plural"` // Custom task type plural name. + Description string `json:"description"` // Custom task type description. +} + +// See https://clickup.com/api/clickupreference/operation/GetCustomItems/ +func (s *CustomTaskTypesService) GetCustomTaskTypes(ctx context.Context, teamId string) ([]CustomItem, *Response, error) { + u := fmt.Sprintf("team/%s/custom_item", teamId) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + gcttr := new(GetCustomTaskTypesResponse) + resp, err := s.client.Do(ctx, req, gcttr) + if err != nil { + return nil, resp, err + } + + return gcttr.CustomItems, resp, nil +} diff --git a/clickup/custom_task_types_test.go b/clickup/custom_task_types_test.go new file mode 100644 index 0000000..a195bac --- /dev/null +++ b/clickup/custom_task_types_test.go @@ -0,0 +1,33 @@ +package clickup + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCustomTaskTypesService_GetCustomTaskTypes(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc(("/team/1234/custom_item"), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"custom_items": [{"id": 1300, "name": "Bug", "name_plural": "Bugs", "description": "Custom item type for bugs."}]}`) + }) + + ctx := context.Background() + customTaskTypes, _, err := client.CustomTaskTypes.GetCustomTaskTypes(ctx, "1234") + if err != nil { + t.Errorf("CustomTaskTypes.GetCustomTaskTypes returned error: %v", err) + } + + want := []CustomItem{ + {Id: 1300, Name: "Bug", NamePlural: "Bugs", Description: "Custom item type for bugs."}, + } + if !cmp.Equal(customTaskTypes, want) { + t.Errorf("CustomTaskTypes.GetCustomTaskTypes returned %+v, want %+v", customTaskTypes, want) + } +} From f58dc65dcf7a8d0e104b2bed27d0c828ac6f827b Mon Sep 17 00:00:00 2001 From: Malcolm Burtenshaw Date: Thu, 4 Jan 2024 14:35:02 -0700 Subject: [PATCH 3/5] Include undocumented custom task type properties --- clickup/custom_task_types.go | 9 +++++++++ clickup/custom_task_types_test.go | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/clickup/custom_task_types.go b/clickup/custom_task_types.go index 67be67e..e441be8 100644 --- a/clickup/custom_task_types.go +++ b/clickup/custom_task_types.go @@ -18,6 +18,15 @@ type CustomItem struct { Name string `json:"name"` // Custom task type name. NamePlural string `json:"name_plural"` // Custom task type plural name. Description string `json:"description"` // Custom task type description. + + // Not documented in API explorer + Avatar CustomItemAvatar `json:"avatar"` // Custom task icon data. +} + +// Not documented in API explorer. Comments are observations. +type CustomItemAvatar struct { + Source string `json:"source"` // null (ClickUp Milestone Glyph), fas (Font Awesome Solid), fab (Font Awesome Brands). + Value string `json:"value"` // null is for ClickUp Glyphs, e.g., Task and Milestone. } // See https://clickup.com/api/clickupreference/operation/GetCustomItems/ diff --git a/clickup/custom_task_types_test.go b/clickup/custom_task_types_test.go index a195bac..505808c 100644 --- a/clickup/custom_task_types_test.go +++ b/clickup/custom_task_types_test.go @@ -15,7 +15,7 @@ func TestCustomTaskTypesService_GetCustomTaskTypes(t *testing.T) { mux.HandleFunc(("/team/1234/custom_item"), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprint(w, `{"custom_items": [{"id": 1300, "name": "Bug", "name_plural": "Bugs", "description": "Custom item type for bugs."}]}`) + fmt.Fprint(w, `{"custom_items": [{"id": 1300, "name": "Bug", "name_plural": "Bugs", "description": "Custom item type for bugs.", "avatar": { "source": "fas", "value": "bug" }}]}`) }) ctx := context.Background() @@ -25,7 +25,7 @@ func TestCustomTaskTypesService_GetCustomTaskTypes(t *testing.T) { } want := []CustomItem{ - {Id: 1300, Name: "Bug", NamePlural: "Bugs", Description: "Custom item type for bugs."}, + {Id: 1300, Name: "Bug", NamePlural: "Bugs", Description: "Custom item type for bugs.", Avatar: CustomItemAvatar{Source: "fas", Value: "bug"}}, } if !cmp.Equal(customTaskTypes, want) { t.Errorf("CustomTaskTypes.GetCustomTaskTypes returned %+v, want %+v", customTaskTypes, want) From 8af2f1aba198ad1499d364cae9aa4a626a1cfff1 Mon Sep 17 00:00:00 2001 From: Malcolm Burtenshaw Date: Thu, 4 Jan 2024 14:37:22 -0700 Subject: [PATCH 4/5] Reflect that the API doesn't list properties as required --- clickup/custom_task_types.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clickup/custom_task_types.go b/clickup/custom_task_types.go index e441be8..4261b51 100644 --- a/clickup/custom_task_types.go +++ b/clickup/custom_task_types.go @@ -9,24 +9,24 @@ type CustomTaskTypesService service // See https://clickup.com/api/clickupreference/operation/GetCustomItems/ type GetCustomTaskTypesResponse struct { - CustomItems []CustomItem `json:"custom_items"` // Array of custom task types. + CustomItems []CustomItem `json:"custom_items,omitempty"` // Array of custom task types. } // See https://clickup.com/api/clickupreference/operation/GetCustomItems/ type CustomItem struct { - Id int32 `json:"id"` // Custom task type ID. - Name string `json:"name"` // Custom task type name. - NamePlural string `json:"name_plural"` // Custom task type plural name. - Description string `json:"description"` // Custom task type description. + Id int32 `json:"id,omitempty"` // Custom task type ID. + Name string `json:"name,omitempty"` // Custom task type name. + NamePlural string `json:"name_plural,omitempty"` // Custom task type plural name. + Description string `json:"description,omitempty"` // Custom task type description. // Not documented in API explorer - Avatar CustomItemAvatar `json:"avatar"` // Custom task icon data. + Avatar CustomItemAvatar `json:"avatar,omitempty"` // Custom task icon data. } // Not documented in API explorer. Comments are observations. type CustomItemAvatar struct { - Source string `json:"source"` // null (ClickUp Milestone Glyph), fas (Font Awesome Solid), fab (Font Awesome Brands). - Value string `json:"value"` // null is for ClickUp Glyphs, e.g., Task and Milestone. + Source string `json:"source,omitempty"` // null (ClickUp Milestone Glyph), fas (Font Awesome Solid), fab (Font Awesome Brands). + Value string `json:"value,omitempty"` // null is for ClickUp Glyphs, e.g., Task and Milestone. } // See https://clickup.com/api/clickupreference/operation/GetCustomItems/ From 2eb7e0ccc5cca0473643c0889c3c278ed1be4ff4 Mon Sep 17 00:00:00 2001 From: Malcolm Burtenshaw Date: Thu, 4 Jan 2024 14:58:12 -0700 Subject: [PATCH 5/5] Conform to surrounding whitespace --- clickup/tasks.go | 4 ++-- clickup/tasks_test.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clickup/tasks.go b/clickup/tasks.go index e64e916..d05c6e8 100644 --- a/clickup/tasks.go +++ b/clickup/tasks.go @@ -34,7 +34,7 @@ type TaskRequest struct { LinksTo string `json:"links_to,omitempty"` CheckRequiredCustomFields bool `json:"check_required_custom_fields,omitempty"` CustomFields []CustomFieldInTaskRequest `json:"custom_fields,omitempty"` - CustomItemId int `json:"custom_item_id,omitempty"` // To create a task that doesn't use a custom task type, either don't include this field in the request body, or send 'null'. To create this task as a Milestone, send a value of 1. To use a custom task type, send the custom task type ID as defined in your Workspace, such as 2. + CustomItemId int `json:"custom_item_id,omitempty"` // To create a task that doesn't use a custom task type, either don't include this field in the request body, or send 'null'. To create this task as a Milestone, send a value of 1. To use a custom task type, send the custom task type ID as defined in your Workspace, such as 2. } type CustomFieldInTaskRequest struct { @@ -45,7 +45,7 @@ type CustomFieldInTaskRequest struct { type Task struct { ID string `json:"id"` CustomID string `json:"custom_id"` - CustomItemId int `json:"custom_item_id"` // A null value means this item is a task. A value of 1 is a Milestone. Any other number is a custom task type. + CustomItemId int `json:"custom_item_id"` // A null value means this item is a task. A value of 1 is a Milestone. Any other number is a custom task type. Name string `json:"name"` TextContent string `json:"text_content"` Description string `json:"description"` diff --git a/clickup/tasks_test.go b/clickup/tasks_test.go index ef5c8c1..05a69a2 100644 --- a/clickup/tasks_test.go +++ b/clickup/tasks_test.go @@ -19,7 +19,7 @@ func TestTasksService_GetTask(t *testing.T) { `{ "id": "9hx", "custom_id":null, - "custom_item_id": null, + "custom_item_id": null, "name": "Task Name", "text_content": "New Task Description", "description": "New Task Description", @@ -81,11 +81,11 @@ func TestTasksService_GetTask(t *testing.T) { } want := &Task{ - ID: "9hx", + ID: "9hx", CustomItemId: 0, - Name: "Task Name", - TextContent: "New Task Description", - Description: "New Task Description", + Name: "Task Name", + TextContent: "New Task Description", + Description: "New Task Description", Status: TaskStatus{ Status: "in progress", Color: "#d3d3d3",