Skip to content

Commit

Permalink
feat: Add TimeTracking service to ClickUp client
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Schmitt committed Jun 27, 2024
1 parent 2cb1ba7 commit 80e48ae
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 11 deletions.
2 changes: 2 additions & 0 deletions clickup/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Client struct {
Tasks *TasksService
TaskTemplates *TaskTemplatesService
Teams *TeamsService
TimeTrackings *TimeTrackingsService
SharedHierarchy *SharedHierarchyService
Spaces *SpacesService
Folders *FoldersService
Expand Down Expand Up @@ -138,6 +139,7 @@ func NewClient(httpClient *http.Client, APIKey string) *Client {
c.Tasks = (*TasksService)(&c.common)
c.TaskTemplates = (*TaskTemplatesService)(&c.common)
c.Teams = (*TeamsService)(&c.common)
c.TimeTrackings = (*TimeTrackingsService)(&c.common)
c.SharedHierarchy = (*SharedHierarchyService)(&c.common)
c.Spaces = (*SpacesService)(&c.common)
c.Folders = (*FoldersService)(&c.common)
Expand Down
22 changes: 11 additions & 11 deletions clickup/spaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type DueDates struct {
RemapClosedDueDate bool `json:"remap_closed_due_date"`
}

type TimeTracking struct {
type TimeTrackingFeature struct {
Enabled bool `json:"enabled"`
}

Expand Down Expand Up @@ -60,15 +60,15 @@ type Portfolios struct {
}

type Features struct {
DueDates DueDates `json:"due_dates"`
TimeTracking TimeTracking `json:"time_tracking"`
Tags Tags `json:"tags"`
TimeEstimates TimeEstimates `json:"time_estimates"`
Checklists Checklists `json:"checklists"`
CustomFields CustomFields `json:"custom_fields"`
RemapDependencies RemapDependencies `json:"remap_dependencies"`
DependencyWarning DependencyWarning `json:"dependency_warning"`
Portfolios Portfolios `json:"portfolios"`
DueDates DueDates `json:"due_dates"`
TimeTracking TimeTrackingFeature `json:"time_tracking"`
Tags Tags `json:"tags"`
TimeEstimates TimeEstimates `json:"time_estimates"`
Checklists Checklists `json:"checklists"`
CustomFields CustomFields `json:"custom_fields"`
RemapDependencies RemapDependencies `json:"remap_dependencies"`
DependencyWarning DependencyWarning `json:"dependency_warning"`
Portfolios Portfolios `json:"portfolios"`
}

type Space struct {
Expand All @@ -88,7 +88,7 @@ type Space struct {
Sprints struct {
Enabled bool `json:"enabled"`
} `json:"sprints"`
TimeTracking TimeTracking `json:"time_tracking"`
TimeTracking TimeTrackingFeature `json:"time_tracking"`
Points struct {
Enabled bool `json:"enabled"`
} `json:"points"`
Expand Down
104 changes: 104 additions & 0 deletions clickup/time_tracking.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package clickup

import (
"context"
"fmt"
)

type TimeTrackingsService service

type GetTimeTrackingResponse struct {
Data []TimeTracking `json:"data"`
}

// See https://clickup.com/api/clickupreference/operation/Createatimeentry/
type TimeTrackingRequest struct {
Description string `json:"description,omitempty"`
Tags []TimeTrackingTag `json:"tags,omitempty"`
Start int64 `json:"start"`
End int64 `json:"end,omitempty"`
Stop int64 `json:"stop,omitempty"`
Billable bool `json:"billable,omitempty"`
Duration int32 `json:"duration"`
Assignee int `json:"assignee,omitempty"`
Tid string `json:"tid,omitempty"`
}

type TimeTrackingTag struct {
Name string `json:"name"`
TagBg string `json:"tag_bg"`
TagFg string `json:"tag_fg"`
Creator int `json:"creator"`
}

type TimeTracking struct {
ID string `json:"id"`
Wid string `json:"wid"`
User User `json:"user"`
Billable bool `json:"billable"`
Start string `json:"start"`
End string `json:"end"`
Duration string `json:"duration"`
Description string `json:"description"`
Source string `json:"source"`
At string `json:"at"`
TaskLocation TaskLocation `json:"task_location"`
Task []TimeTrackingTag `json:"task_"`
TaskURL string `json:"task_url"`
}

type TaskLocation struct {
ListID int `json:"list_id"`
FolderID int `json:"folder_id"`
SpaceID int `json:"space_id"`
ListName string `json:"list_name"`
FolderName string `json:"folder_name"`
SpaceName string `json:"space_name"`
}

type CreateTimeTrackingOptions struct {
CustomTaskIDs bool `url:"custom_task_ids,omitempty"`
TeamID int `url:"team_id,omitempty"`
}

func (s *TimeTrackingsService) CreateTimeTracking(ctx context.Context, teamID string, opts *CreateTimeTrackingOptions, ttr *TimeTrackingRequest) (*TimeTracking, *Response, error) {
u := fmt.Sprintf("team/%s/time_entries", teamID)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

req, err := s.client.NewRequest("POST", u, ttr)
if err != nil {
return nil, nil, err
}

timeTracking := new(TimeTracking)
resp, err := s.client.Do(ctx, req, timeTracking)
if err != nil {
return nil, resp, err
}

return timeTracking, resp, nil
}

func (s *TimeTrackingsService) GetTimeTracking(ctx context.Context, teamID string, timerID string, opts *CreateTimeTrackingOptions, ttr *TimeTrackingRequest) (*GetTimeTrackingResponse, *Response, error) {
u := fmt.Sprintf("team/%s/time_entries/%s", teamID, timerID)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

req, err := s.client.NewRequest("GET", u, ttr)
if err != nil {
return nil, nil, err
}

getTimeTrackingResponse := new(GetTimeTrackingResponse)
resp, err := s.client.Do(ctx, req, getTimeTrackingResponse)
if err != nil {
return nil, resp, err
}

return getTimeTrackingResponse, resp, nil
}
43 changes: 43 additions & 0 deletions clickup/time_tracking_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package clickup

import (
"context"
"fmt"
"path/filepath"
"runtime"
"testing"
"time"
)

func TestTimeTrackingService_CreateTimeTracking(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()

teamID := "teamID"
taskID := "taskID"
assigneeID := 1234
var dur int32 = 120000

start := time.Now().Add(time.Millisecond * time.Duration(-dur))

_, _, err := client.TimeTrackings.CreateTimeTracking(context.Background(), teamID,
&CreateTimeTrackingOptions{},
&TimeTrackingRequest{
Description: "description",
Start: start.Unix() * 1000,
Duration: dur,
Assignee: assigneeID,
Tid: taskID,
Billable: true,
})

OK(t, err)
}

func OK(tb testing.TB, err error) {
if err != nil {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error())
tb.FailNow()
}
}

0 comments on commit 80e48ae

Please sign in to comment.