Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitac9864f

Browse files
committed
feat(coderd): use task data model for list
Updatescoder/internal#976
1 parentd46a3c7 commitac9864f

27 files changed

+808
-599
lines changed

‎cli/exp_task.go‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package cli
22

33
import (
4+
"context"
5+
"strings"
6+
7+
"github.com/google/uuid"
8+
"golang.org/x/xerrors"
9+
10+
"github.com/coder/coder/v2/codersdk"
411
"github.com/coder/serpent"
512
)
613

@@ -23,3 +30,71 @@ func (r *RootCmd) tasksCommand() *serpent.Command {
2330
}
2431
returncmd
2532
}
33+
34+
funcsplitTaskIdentifier(identifierstring) (ownerstring,taskNamestring,errerror) {
35+
parts:=strings.Split(identifier,"/")
36+
37+
switchlen(parts) {
38+
case1:
39+
owner=codersdk.Me
40+
taskName=parts[0]
41+
case2:
42+
owner=parts[0]
43+
taskName=parts[1]
44+
default:
45+
return"","",xerrors.Errorf("invalid task identifier: %q",identifier)
46+
}
47+
returnowner,taskName,nil
48+
}
49+
50+
// resolveTask fetches and returns a task by an identifier, which may be either
51+
// a UUID, a bare name (for a task owned by the current user), or a "user/task"
52+
// combination, where user is either a username or UUID.
53+
//
54+
// Since there is no TaskByOwnerAndName endpoint yet, this function uses the
55+
// list endpoint with filtering when a name is provided.
56+
funcresolveTask(ctx context.Context,client*codersdk.Client,identifierstring) (codersdk.Task,error) {
57+
exp:=codersdk.NewExperimentalClient(client)
58+
59+
identifier=strings.TrimSpace(identifier)
60+
61+
// Try parsing as UUID first.
62+
iftaskID,err:=uuid.Parse(identifier);err==nil {
63+
returnexp.TaskByID(ctx,taskID)
64+
}
65+
66+
// Not a UUID, treat as identifier.
67+
owner,taskName,err:=splitTaskIdentifier(identifier)
68+
iferr!=nil {
69+
return codersdk.Task{},err
70+
}
71+
72+
tasks,err:=exp.Tasks(ctx,&codersdk.TasksFilter{
73+
Owner:owner,
74+
})
75+
iferr!=nil {
76+
return codersdk.Task{},xerrors.Errorf("list tasks for owner %q: %w",owner,err)
77+
}
78+
79+
iftaskID,err:=uuid.Parse(taskName);err==nil {
80+
// Find task by ID.
81+
for_,task:=rangetasks {
82+
iftask.ID==taskID {
83+
returntask,nil
84+
}
85+
}
86+
}else {
87+
// Find task by name.
88+
for_,task:=rangetasks {
89+
iftask.Name==taskName {
90+
returntask,nil
91+
}
92+
}
93+
}
94+
95+
// Mimic resource not found from API.
96+
varnotFoundErrerror=&codersdk.Error{
97+
Response: codersdk.Response{Message:"Resource not found or you do not have access to this resource"},
98+
}
99+
return codersdk.Task{},xerrors.Errorf("task %q not found for owner %q: %w",taskName,owner,notFoundErr)
100+
}

‎cli/exp_task_delete.go‎

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"strings"
66
"time"
77

8-
"github.com/google/uuid"
98
"golang.org/x/xerrors"
109

1110
"github.com/coder/pretty"
@@ -47,43 +46,19 @@ func (r *RootCmd) taskDelete() *serpent.Command {
4746
}
4847
exp:=codersdk.NewExperimentalClient(client)
4948

50-
typetoDeletestruct {
51-
ID uuid.UUID
52-
Ownerstring
53-
Displaystring
54-
}
55-
56-
varitems []toDelete
49+
vartasks []codersdk.Task
5750
for_,identifier:=rangeinv.Args {
58-
identifier=strings.TrimSpace(identifier)
59-
ifidentifier=="" {
60-
returnxerrors.New("task identifier cannot be empty or whitespace")
61-
}
62-
63-
// Check task identifier, try UUID first.
64-
ifid,err:=uuid.Parse(identifier);err==nil {
65-
task,err:=exp.TaskByID(ctx,id)
66-
iferr!=nil {
67-
returnxerrors.Errorf("resolve task %q: %w",identifier,err)
68-
}
69-
display:=fmt.Sprintf("%s/%s",task.OwnerName,task.Name)
70-
items=append(items,toDelete{ID:id,Display:display,Owner:task.OwnerName})
71-
continue
72-
}
73-
74-
// Non-UUID, treat as a workspace identifier (name or owner/name).
75-
ws,err:=namedWorkspace(ctx,client,identifier)
51+
task,err:=resolveTask(ctx,client,identifier)
7652
iferr!=nil {
7753
returnxerrors.Errorf("resolve task %q: %w",identifier,err)
7854
}
79-
display:=ws.FullName()
80-
items=append(items,toDelete{ID:ws.ID,Display:display,Owner:ws.OwnerName})
55+
tasks=append(tasks,task)
8156
}
8257

8358
// Confirm deletion of the tasks.
8459
vardisplayList []string
85-
for_,it:=rangeitems {
86-
displayList=append(displayList,it.Display)
60+
for_,task:=rangetasks {
61+
displayList=append(displayList,fmt.Sprintf("%s/%s",task.OwnerName,task.Name))
8762
}
8863
_,err=cliui.Prompt(inv, cliui.PromptOptions{
8964
Text:fmt.Sprintf("Delete these tasks: %s?",pretty.Sprint(cliui.DefaultStyles.Code,strings.Join(displayList,", "))),
@@ -94,12 +69,13 @@ func (r *RootCmd) taskDelete() *serpent.Command {
9469
returnerr
9570
}
9671

97-
for_,item:=rangeitems {
98-
iferr:=exp.DeleteTask(ctx,item.Owner,item.ID);err!=nil {
99-
returnxerrors.Errorf("delete task %q: %w",item.Display,err)
72+
fori,task:=rangetasks {
73+
display:=displayList[i]
74+
iferr:=exp.DeleteTask(ctx,task.OwnerName,task.ID);err!=nil {
75+
returnxerrors.Errorf("delete task %q: %w",display,err)
10076
}
10177
_,_=fmt.Fprintln(
102-
inv.Stdout,"Deleted task "+pretty.Sprint(cliui.DefaultStyles.Keyword,item.Display)+" at "+cliui.Timestamp(time.Now()),
78+
inv.Stdout,"Deleted task "+pretty.Sprint(cliui.DefaultStyles.Keyword,display)+" at "+cliui.Timestamp(time.Now()),
10379
)
10480
}
10581

‎cli/exp_task_delete_test.go‎

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,18 @@ func TestExpTaskDelete(t *testing.T) {
5656
taskID:=uuid.MustParse(id1)
5757
returnfunc(w http.ResponseWriter,r*http.Request) {
5858
switch {
59-
caser.Method==http.MethodGet&&r.URL.Path=="/api/v2/users/me/workspace/exists":
59+
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks"&&r.URL.Query().Get("owner")=="me":
6060
c.nameResolves.Add(1)
61-
httpapi.Write(r.Context(),w,http.StatusOK, codersdk.Workspace{
62-
ID:taskID,
63-
Name:"exists",
64-
OwnerName:"me",
61+
httpapi.Write(r.Context(),w,http.StatusOK,struct {
62+
Tasks []codersdk.Task`json:"tasks"`
63+
Countint`json:"count"`
64+
}{
65+
Tasks: []codersdk.Task{{
66+
ID:taskID,
67+
Name:"exists",
68+
OwnerName:"me",
69+
}},
70+
Count:1,
6571
})
6672
caser.Method==http.MethodDelete&&r.URL.Path=="/api/experimental/tasks/me/"+id1:
6773
c.deleteCalls.Add(1)
@@ -104,12 +110,18 @@ func TestExpTaskDelete(t *testing.T) {
104110
firstID:=uuid.MustParse(id3)
105111
returnfunc(w http.ResponseWriter,r*http.Request) {
106112
switch {
107-
caser.Method==http.MethodGet&&r.URL.Path=="/api/v2/users/me/workspace/first":
113+
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks"&&r.URL.Query().Get("owner")=="me":
108114
c.nameResolves.Add(1)
109-
httpapi.Write(r.Context(),w,http.StatusOK, codersdk.Workspace{
110-
ID:firstID,
111-
Name:"first",
112-
OwnerName:"me",
115+
httpapi.Write(r.Context(),w,http.StatusOK,struct {
116+
Tasks []codersdk.Task`json:"tasks"`
117+
Countint`json:"count"`
118+
}{
119+
Tasks: []codersdk.Task{{
120+
ID:firstID,
121+
Name:"first",
122+
OwnerName:"me",
123+
}},
124+
Count:1,
113125
})
114126
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks/me/"+id4:
115127
httpapi.Write(r.Context(),w,http.StatusOK, codersdk.Task{
@@ -139,8 +151,14 @@ func TestExpTaskDelete(t *testing.T) {
139151
buildHandler:func(_*testCounters) http.HandlerFunc {
140152
returnfunc(w http.ResponseWriter,r*http.Request) {
141153
switch {
142-
caser.Method==http.MethodGet&&r.URL.Path=="/api/v2/users/me/workspace/doesnotexist":
143-
httpapi.ResourceNotFound(w)
154+
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks"&&r.URL.Query().Get("owner")=="me":
155+
httpapi.Write(r.Context(),w,http.StatusOK,struct {
156+
Tasks []codersdk.Task`json:"tasks"`
157+
Countint`json:"count"`
158+
}{
159+
Tasks: []codersdk.Task{},
160+
Count:0,
161+
})
144162
default:
145163
httpapi.InternalServerError(w,xerrors.New("unwanted path: "+r.Method+" "+r.URL.Path))
146164
}
@@ -156,12 +174,18 @@ func TestExpTaskDelete(t *testing.T) {
156174
taskID:=uuid.MustParse(id5)
157175
returnfunc(w http.ResponseWriter,r*http.Request) {
158176
switch {
159-
caser.Method==http.MethodGet&&r.URL.Path=="/api/v2/users/me/workspace/bad":
177+
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks"&&r.URL.Query().Get("owner")=="me":
160178
c.nameResolves.Add(1)
161-
httpapi.Write(r.Context(),w,http.StatusOK, codersdk.Workspace{
162-
ID:taskID,
163-
Name:"bad",
164-
OwnerName:"me",
179+
httpapi.Write(r.Context(),w,http.StatusOK,struct {
180+
Tasks []codersdk.Task`json:"tasks"`
181+
Countint`json:"count"`
182+
}{
183+
Tasks: []codersdk.Task{{
184+
ID:taskID,
185+
Name:"bad",
186+
OwnerName:"me",
187+
}},
188+
Count:1,
165189
})
166190
caser.Method==http.MethodDelete&&r.URL.Path=="/api/experimental/tasks/me/"+id5:
167191
httpapi.InternalServerError(w,xerrors.New("boom"))

‎cli/exp_task_internal_test.go‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package cli
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/coder/coder/v2/codersdk"
10+
)
11+
12+
funcTest_splitTaskIdentifier(t*testing.T) {
13+
t.Parallel()
14+
15+
tests:= []struct {
16+
namestring
17+
identifierstring
18+
expectedOwnerstring
19+
expectedTaskstring
20+
expectErrbool
21+
}{
22+
{
23+
name:"bare task name",
24+
identifier:"mytask",
25+
expectedOwner:codersdk.Me,
26+
expectedTask:"mytask",
27+
expectErr:false,
28+
},
29+
{
30+
name:"owner/task format",
31+
identifier:"alice/her-task",
32+
expectedOwner:"alice",
33+
expectedTask:"her-task",
34+
expectErr:false,
35+
},
36+
{
37+
name:"uuid/task format",
38+
identifier:"550e8400-e29b-41d4-a716-446655440000/task1",
39+
expectedOwner:"550e8400-e29b-41d4-a716-446655440000",
40+
expectedTask:"task1",
41+
expectErr:false,
42+
},
43+
{
44+
name:"owner/uuid format",
45+
identifier:"alice/3abe1dcf-cd87-4078-8b54-c0e2058ad2e2",
46+
expectedOwner:"alice",
47+
expectedTask:"3abe1dcf-cd87-4078-8b54-c0e2058ad2e2",
48+
expectErr:false,
49+
},
50+
{
51+
name:"too many slashes",
52+
identifier:"owner/task/extra",
53+
expectErr:true,
54+
},
55+
{
56+
name:"empty parts acceptable",
57+
identifier:"/task",
58+
expectedOwner:"",
59+
expectedTask:"task",
60+
expectErr:false,
61+
},
62+
}
63+
64+
for_,tt:=rangetests {
65+
t.Run(tt.name,func(t*testing.T) {
66+
t.Parallel()
67+
owner,taskName,err:=splitTaskIdentifier(tt.identifier)
68+
iftt.expectErr {
69+
require.Error(t,err)
70+
}else {
71+
require.NoError(t,err)
72+
assert.Equal(t,tt.expectedOwner,owner)
73+
assert.Equal(t,tt.expectedTask,taskName)
74+
}
75+
})
76+
}
77+
}

‎cli/exp_task_list.go‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"golang.org/x/xerrors"
99

1010
"github.com/coder/coder/v2/cli/cliui"
11+
"github.com/coder/coder/v2/coderd/util/slice"
1112
"github.com/coder/coder/v2/codersdk"
1213
"github.com/coder/serpent"
1314
)
@@ -98,10 +99,10 @@ func (r *RootCmd) taskList() *serpent.Command {
9899
Options: serpent.OptionSet{
99100
{
100101
Name:"status",
101-
Description:"Filter by task status (e.g. running, failed, etc).",
102+
Description:"Filter by task status.",
102103
Flag:"status",
103104
Default:"",
104-
Value:serpent.StringOf(&statusFilter),
105+
Value:serpent.EnumOf(&statusFilter,slice.ToStrings(codersdk.AllTaskStatuses())...),
105106
},
106107
{
107108
Name:"all",
@@ -142,8 +143,8 @@ func (r *RootCmd) taskList() *serpent.Command {
142143
}
143144

144145
tasks,err:=exp.Tasks(ctx,&codersdk.TasksFilter{
145-
Owner:targetUser,
146-
WorkspaceStatus:statusFilter,
146+
Owner:targetUser,
147+
Status:codersdk.TaskStatus(statusFilter),
147148
})
148149
iferr!=nil {
149150
returnxerrors.Errorf("list tasks: %w",err)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp