Skip to content

Commit 0a09481

Browse files
committed
feat: time entry commands allow client filter for project
1 parent 737626c commit 0a09481

File tree

16 files changed

+279
-125
lines changed

16 files changed

+279
-125
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
## [Unreleased]
1313

14+
### Added
15+
16+
- new flag `--client` to filter projects by client when managing time entries
17+
1418
## [v0.46.0] - 2023-12-06
1519

1620
### Added

pkg/cmd/project/edit/edit.go

-5
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,6 @@ func NewCmdEdit(
126126
return err
127127
}
128128

129-
if ids, err = search.GetProjectsByName(
130-
c, w, ids); err != nil {
131-
return err
132-
}
133-
134129
if client != nil && *client != "" {
135130
if *client, err = search.GetClientByName(
136131
c, w, *client); err != nil {

pkg/cmd/project/get/get.go

-98
This file was deleted.

pkg/cmd/task/delete/delete.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func NewCmdDelete(
7070

7171
if f.Config().IsAllowNameForID() {
7272
if project, err = search.GetProjectByName(
73-
c, w, project); err != nil {
73+
c, w, project, ""); err != nil {
7474
return err
7575
}
7676

pkg/cmd/task/done/done.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func NewCmdDone(
8888

8989
if f.Config().IsAllowNameForID() {
9090
if project, err = search.GetProjectByName(
91-
c, workspace, project); err != nil {
91+
c, workspace, project, ""); err != nil {
9292
return err
9393
}
9494

pkg/cmd/task/list/list.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func NewCmdList(
6363
if f.Config().IsAllowNameForID() &&
6464
p.ProjectID != "" {
6565
if p.ProjectID, err = search.GetProjectByName(
66-
c, workspace, p.ProjectID); err != nil {
66+
c, workspace, p.ProjectID, ""); err != nil {
6767
return err
6868
}
6969
}

pkg/cmd/task/quick-add/quick-add.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func NewCmdQuickAdd(
6161

6262
p, _ := cmd.Flags().GetString("project")
6363
if f.Config().IsAllowNameForID() {
64-
if p, err = search.GetProjectByName(c, w, p); err != nil {
64+
if p, err = search.GetProjectByName(c, w, p, ""); err != nil {
6565
return err
6666
}
6767
}

pkg/cmd/task/util/read-flags.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func TaskReadFlags(cmd *cobra.Command, f cmdutil.Factory) (p FlagsDTO, err error
7979
}
8080

8181
if p.ProjectID, err = search.GetProjectByName(
82-
c, p.Workspace, p.ProjectID); err != nil {
82+
c, p.Workspace, p.ProjectID, ""); err != nil {
8383
return p, err
8484
}
8585

pkg/cmd/time-entry/in/in_test.go

+152
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package in_test
22

33
import (
44
"bytes"
5+
"errors"
56
"io"
67
"testing"
78
"time"
@@ -150,6 +151,157 @@ func TestNewCmdIn_ShouldNotSetBillable_WhenNotAsked(t *testing.T) {
150151
return
151152
}
152153

154+
t.Fatalf("err: %s", err)
155+
})
156+
}
157+
}
158+
159+
func TestNewCmdIn_ShouldLookupProject_WithAndWithoutClient(t *testing.T) {
160+
defaultStart := timehlp.Today().Add(8 * time.Hour)
161+
162+
projects := []dto.Project{
163+
{ID: "p1", Name: "first", ClientID: "c1", ClientName: "other"},
164+
{ID: "p2", Name: "second", ClientID: "c2", ClientName: "me"},
165+
{ID: "p3", Name: "second", ClientID: "c3", ClientName: "clockify"},
166+
{ID: "p4", Name: "third"},
167+
{ID: "p5", Name: "notonclient", ClientID: "c3", ClientName: "clockify"},
168+
}
169+
170+
tts := []struct {
171+
name string
172+
args []string
173+
param api.CreateTimeEntryParam
174+
err error
175+
}{
176+
{
177+
name: "only project",
178+
args: []string{"-s=08:00", "-p=first"},
179+
param: api.CreateTimeEntryParam{
180+
Workspace: w.ID,
181+
Start: defaultStart,
182+
ProjectID: projects[0].ID,
183+
},
184+
},
185+
{
186+
name: "project and client",
187+
args: []string{"-s=08:00", "-p=second", "-c=me"},
188+
param: api.CreateTimeEntryParam{
189+
Workspace: w.ID,
190+
Start: defaultStart,
191+
ProjectID: projects[1].ID,
192+
},
193+
},
194+
{
195+
name: "project and other client",
196+
args: []string{"-s=08:00", "-p=second", "-c=clockify"},
197+
param: api.CreateTimeEntryParam{
198+
Workspace: w.ID,
199+
Start: defaultStart,
200+
ProjectID: projects[2].ID,
201+
},
202+
},
203+
{
204+
name: "project without client",
205+
args: []string{"-s=08:00", "-p=third"},
206+
param: api.CreateTimeEntryParam{
207+
Workspace: w.ID,
208+
Start: defaultStart,
209+
ProjectID: projects[3].ID,
210+
},
211+
},
212+
{
213+
name: "project does not exist",
214+
args: []string{"-s=08:00", "-p=notfound"},
215+
err: errors.New(
216+
"No project with id or name containing 'notfound' " +
217+
"was found"),
218+
},
219+
{
220+
name: "project does not exist in this client",
221+
args: []string{"-s=08:00", "-p=notonclient", "-c=me"},
222+
err: errors.New(
223+
"No project with id or name containing 'notonclient' " +
224+
"was found for client 'me'"),
225+
},
226+
}
227+
228+
for i := range tts {
229+
tt := &tts[i]
230+
231+
t.Run(tt.name, func(t *testing.T) {
232+
f := mocks.NewMockFactory(t)
233+
234+
f.EXPECT().GetUserID().Return("u", nil)
235+
f.EXPECT().GetWorkspaceID().Return(w.ID, nil)
236+
237+
f.EXPECT().Config().Return(&mocks.SimpleConfig{
238+
AllowNameForID: true,
239+
})
240+
241+
c := mocks.NewMockClient(t)
242+
f.EXPECT().Client().Return(c, nil)
243+
244+
c.EXPECT().GetProjects(api.GetProjectsParam{
245+
Workspace: w.ID,
246+
PaginationParam: api.AllPages(),
247+
}).
248+
Return(projects, nil)
249+
250+
c.EXPECT().GetTimeEntryInProgress(api.GetTimeEntryInProgressParam{
251+
Workspace: w.ID,
252+
UserID: "u",
253+
}).
254+
Return(nil, nil)
255+
256+
if tt.err == nil {
257+
c.EXPECT().GetProject(api.GetProjectParam{
258+
Workspace: w.ID,
259+
ProjectID: tt.param.ProjectID,
260+
}).
261+
Return(&dto.Project{ID: tt.param.ProjectID}, nil)
262+
263+
f.EXPECT().GetWorkspace().Return(w, nil)
264+
265+
c.EXPECT().Out(api.OutParam{
266+
Workspace: w.ID,
267+
UserID: "u",
268+
End: tt.param.Start,
269+
}).Return(api.ErrorNotFound)
270+
271+
c.EXPECT().CreateTimeEntry(tt.param).
272+
Return(dto.TimeEntryImpl{ID: "te"}, nil)
273+
}
274+
275+
called := false
276+
cmd := in.NewCmdIn(f, func(
277+
_ dto.TimeEntryImpl, _ io.Writer, _ util.OutputFlags) error {
278+
called = true
279+
return nil
280+
})
281+
282+
cmd.SilenceUsage = true
283+
cmd.SilenceErrors = true
284+
285+
out := bytes.NewBufferString("")
286+
cmd.SetOut(out)
287+
cmd.SetErr(out)
288+
289+
cmd.SetArgs(append(tt.args, "-q"))
290+
_, err := cmd.ExecuteC()
291+
292+
if tt.err != nil {
293+
assert.EqualError(t, err, tt.err.Error())
294+
return
295+
}
296+
297+
t.Cleanup(func() {
298+
assert.True(t, called)
299+
})
300+
301+
if assert.NoError(t, err) {
302+
return
303+
}
304+
153305
t.Fatalf("err: %s", err)
154306
})
155307
}

pkg/cmd/time-entry/report/util/report.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func ReportWithRange(
102102

103103
if rf.Project != "" && f.Config().IsAllowNameForID() {
104104
if rf.Project, err = search.GetProjectByName(
105-
c, workspace, rf.Project); err != nil {
105+
c, workspace, rf.Project, ""); err != nil {
106106
return err
107107
}
108108
}

pkg/cmd/time-entry/util/fill-with-flags.go

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ func FillTimeEntryWithFlags(flags flagSet) Step {
2929
dto.TaskID = ""
3030
}
3131
dto.ProjectID = p
32+
33+
if flags.Changed("client") {
34+
c, _ := flags.GetString("client")
35+
if c != dto.Client {
36+
dto.TaskID = ""
37+
}
38+
dto.Client = c
39+
}
3240
}
3341

3442
if flags.Changed("description") {

pkg/cmd/time-entry/util/flags.go

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ func AddTimeEntryFlags(
2929
cmd.Flags().BoolP("allow-incomplete", "A", false,
3030
"allow creation of incomplete time entries to be edited later")
3131

32+
cmd.Flags().StringP("client", "c", "", "client of the project to use for time entry")
33+
_ = cmdcompl.AddSuggestionsToFlag(cmd, "client",
34+
cmdcomplutil.NewClientAutoComplete(f))
35+
3236
cmd.Flags().StringP("project", "p", "", "project to use for time entry")
3337
_ = cmdcompl.AddSuggestionsToFlag(cmd, "project",
3438
cmdcomplutil.NewProjectAutoComplete(f))

pkg/cmd/time-entry/util/name-for-id.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func lookupProject(c api.Client) Step {
3434

3535
var err error
3636
te.ProjectID, err = search.GetProjectByName(
37-
c, te.Workspace, te.ProjectID)
37+
c, te.Workspace, te.ProjectID, te.Client)
3838
return te, err
3939
}
4040

pkg/cmd/time-entry/util/util.go

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type TimeEntryDTO struct {
1515
Workspace string
1616
UserID string
1717
ProjectID string
18+
Client string
1819
TaskID string
1920
Description string
2021
Start time.Time

0 commit comments

Comments
 (0)