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

Commit34f6e72

Browse files
authored
feat(coderd): add lookup task by name in httpmw.TaskParam (#20647)
* Adds a `GetTaskByOwnerIDAndName` query* Updates `httpmw.TaskParam` to fall back to task name if no task byUUID found.* Updates the `TaskByIdentifier` used in `cli/` to use direct lookup instead of searching.
1 parent46b2f3d commit34f6e72

File tree

13 files changed

+438
-224
lines changed

13 files changed

+438
-224
lines changed

‎cli/exp_task_delete_test.go‎

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,14 @@ 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/experimental/tasks"&&r.URL.Query().Get("q")=="owner:\"me\"":
59+
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks/me/exists":
6060
c.nameResolves.Add(1)
61-
httpapi.Write(r.Context(),w,http.StatusOK,struct {
62-
Tasks []codersdk.Task`json:"tasks"`
63-
Countint`json:"count"`
64-
}{
65-
Tasks: []codersdk.Task{{
61+
httpapi.Write(r.Context(),w,http.StatusOK,
62+
codersdk.Task{
6663
ID:taskID,
6764
Name:"exists",
6865
OwnerName:"me",
69-
}},
70-
Count:1,
71-
})
66+
})
7267
caser.Method==http.MethodDelete&&r.URL.Path=="/api/experimental/tasks/me/"+id1:
7368
c.deleteCalls.Add(1)
7469
w.WriteHeader(http.StatusAccepted)
@@ -107,27 +102,21 @@ func TestExpTaskDelete(t *testing.T) {
107102
name:"Multiple_YesFlag",
108103
args: []string{"--yes","first",id4},
109104
buildHandler:func(c*testCounters) http.HandlerFunc {
110-
firstID:=uuid.MustParse(id3)
111105
returnfunc(w http.ResponseWriter,r*http.Request) {
112106
switch {
113-
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks"&&r.URL.Query().Get("q")=="owner:\"me\"":
107+
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks/me/first":
114108
c.nameResolves.Add(1)
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,
109+
httpapi.Write(r.Context(),w,http.StatusOK, codersdk.Task{
110+
ID:uuid.MustParse(id3),
111+
Name:"first",
112+
OwnerName:"me",
125113
})
126114
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks/me/"+id4:
115+
c.nameResolves.Add(1)
127116
httpapi.Write(r.Context(),w,http.StatusOK, codersdk.Task{
128117
ID:uuid.MustParse(id4),
129118
OwnerName:"me",
130-
Name:"uuid-task-2",
119+
Name:"uuid-task-4",
131120
})
132121
caser.Method==http.MethodDelete&&r.URL.Path=="/api/experimental/tasks/me/"+id3:
133122
c.deleteCalls.Add(1)
@@ -141,7 +130,7 @@ func TestExpTaskDelete(t *testing.T) {
141130
}
142131
},
143132
wantDeleteCalls:2,
144-
wantNameResolves:1,
133+
wantNameResolves:2,
145134
wantDeletedMessage:2,
146135
},
147136
{
@@ -174,20 +163,14 @@ func TestExpTaskDelete(t *testing.T) {
174163
taskID:=uuid.MustParse(id5)
175164
returnfunc(w http.ResponseWriter,r*http.Request) {
176165
switch {
177-
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks"&&r.URL.Query().Get("q")=="owner:\"me\"":
166+
caser.Method==http.MethodGet&&r.URL.Path=="/api/experimental/tasks/me/bad":
178167
c.nameResolves.Add(1)
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,
168+
httpapi.Write(r.Context(),w,http.StatusOK, codersdk.Task{
169+
ID:taskID,
170+
Name:"bad",
171+
OwnerName:"me",
189172
})
190-
caser.Method==http.MethodDelete&&r.URL.Path=="/api/experimental/tasks/me/"+id5:
173+
caser.Method==http.MethodDelete&&r.URL.Path=="/api/experimental/tasks/me/bad":
191174
httpapi.InternalServerError(w,xerrors.New("boom"))
192175
default:
193176
httpapi.InternalServerError(w,xerrors.New("unwanted path: "+r.Method+" "+r.URL.Path))

‎cli/exp_task_status_test.go‎

Lines changed: 30 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,9 @@ func Test_TaskStatus(t *testing.T) {
3636
hf:func(ctx context.Context,_ time.Time)func(w http.ResponseWriter,r*http.Request) {
3737
returnfunc(w http.ResponseWriter,r*http.Request) {
3838
switchr.URL.Path {
39-
case"/api/experimental/tasks":
40-
ifr.URL.Query().Get("q")=="owner:\"me\"" {
41-
httpapi.Write(ctx,w,http.StatusOK,struct {
42-
Tasks []codersdk.Task`json:"tasks"`
43-
Countint`json:"count"`
44-
}{
45-
Tasks: []codersdk.Task{},
46-
Count:0,
47-
})
48-
return
49-
}
39+
case"/api/experimental/tasks/me/doesnotexist":
40+
httpapi.ResourceNotFound(w)
41+
return
5042
default:
5143
t.Errorf("unexpected path: %s",r.URL.Path)
5244
}
@@ -60,35 +52,7 @@ func Test_TaskStatus(t *testing.T) {
6052
hf:func(ctx context.Context,now time.Time)func(w http.ResponseWriter,r*http.Request) {
6153
returnfunc(w http.ResponseWriter,r*http.Request) {
6254
switchr.URL.Path {
63-
case"/api/experimental/tasks":
64-
ifr.URL.Query().Get("q")=="owner:\"me\"" {
65-
httpapi.Write(ctx,w,http.StatusOK,struct {
66-
Tasks []codersdk.Task`json:"tasks"`
67-
Countint`json:"count"`
68-
}{
69-
Tasks: []codersdk.Task{{
70-
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
71-
Name:"exists",
72-
OwnerName:"me",
73-
WorkspaceStatus:codersdk.WorkspaceStatusRunning,
74-
CreatedAt:now,
75-
UpdatedAt:now,
76-
CurrentState:&codersdk.TaskStateEntry{
77-
State:codersdk.TaskStateWorking,
78-
Timestamp:now,
79-
Message:"Thinking furiously...",
80-
},
81-
WorkspaceAgentHealth:&codersdk.WorkspaceAgentHealth{
82-
Healthy:true,
83-
},
84-
WorkspaceAgentLifecycle:ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
85-
Status:codersdk.TaskStatusActive,
86-
}},
87-
Count:1,
88-
})
89-
return
90-
}
91-
case"/api/experimental/tasks/me/11111111-1111-1111-1111-111111111111":
55+
case"/api/experimental/tasks/me/exists":
9256
httpapi.Write(ctx,w,http.StatusOK, codersdk.Task{
9357
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
9458
WorkspaceStatus:codersdk.WorkspaceStatusRunning,
@@ -124,30 +88,21 @@ func Test_TaskStatus(t *testing.T) {
12488
varcalls atomic.Int64
12589
returnfunc(w http.ResponseWriter,r*http.Request) {
12690
switchr.URL.Path {
127-
case"/api/experimental/tasks":
128-
ifr.URL.Query().Get("q")=="owner:\"me\"" {
129-
// Return initial task state for --watch test
130-
httpapi.Write(ctx,w,http.StatusOK,struct {
131-
Tasks []codersdk.Task`json:"tasks"`
132-
Countint`json:"count"`
133-
}{
134-
Tasks: []codersdk.Task{{
135-
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
136-
Name:"exists",
137-
OwnerName:"me",
138-
WorkspaceStatus:codersdk.WorkspaceStatusPending,
139-
CreatedAt:now.Add(-5*time.Second),
140-
UpdatedAt:now.Add(-5*time.Second),
141-
WorkspaceAgentHealth:&codersdk.WorkspaceAgentHealth{
142-
Healthy:true,
143-
},
144-
WorkspaceAgentLifecycle:ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
145-
Status:codersdk.TaskStatusPending,
146-
}},
147-
Count:1,
148-
})
149-
return
150-
}
91+
case"/api/experimental/tasks/me/exists":
92+
httpapi.Write(ctx,w,http.StatusOK, codersdk.Task{
93+
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
94+
Name:"exists",
95+
OwnerName:"me",
96+
WorkspaceStatus:codersdk.WorkspaceStatusPending,
97+
CreatedAt:now.Add(-5*time.Second),
98+
UpdatedAt:now.Add(-5*time.Second),
99+
WorkspaceAgentHealth:&codersdk.WorkspaceAgentHealth{
100+
Healthy:true,
101+
},
102+
WorkspaceAgentLifecycle:ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
103+
Status:codersdk.TaskStatusPending,
104+
})
105+
return
151106
case"/api/experimental/tasks/me/11111111-1111-1111-1111-111111111111":
152107
defercalls.Add(1)
153108
switchcalls.Load() {
@@ -263,40 +218,18 @@ func Test_TaskStatus(t *testing.T) {
263218
ts:=time.Date(2025,8,26,12,34,56,0,time.UTC)
264219
returnfunc(w http.ResponseWriter,r*http.Request) {
265220
switchr.URL.Path {
266-
case"/api/experimental/tasks":
267-
ifr.URL.Query().Get("q")=="owner:\"me\"" {
268-
httpapi.Write(ctx,w,http.StatusOK,struct {
269-
Tasks []codersdk.Task`json:"tasks"`
270-
Countint`json:"count"`
271-
}{
272-
Tasks: []codersdk.Task{{
273-
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
274-
Name:"exists",
275-
OwnerName:"me",
276-
WorkspaceStatus:codersdk.WorkspaceStatusRunning,
277-
CreatedAt:ts,
278-
UpdatedAt:ts,
279-
CurrentState:&codersdk.TaskStateEntry{
280-
State:codersdk.TaskStateWorking,
281-
Timestamp:ts.Add(time.Second),
282-
Message:"Thinking furiously...",
283-
},
284-
WorkspaceAgentHealth:&codersdk.WorkspaceAgentHealth{
285-
Healthy:true,
286-
},
287-
WorkspaceAgentLifecycle:ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
288-
Status:codersdk.TaskStatusActive,
289-
}},
290-
Count:1,
291-
})
292-
return
293-
}
294-
case"/api/experimental/tasks/me/11111111-1111-1111-1111-111111111111":
221+
case"/api/experimental/tasks/me/exists":
295222
httpapi.Write(ctx,w,http.StatusOK, codersdk.Task{
296-
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
297-
WorkspaceStatus:codersdk.WorkspaceStatusRunning,
298-
CreatedAt:ts,
299-
UpdatedAt:ts,
223+
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
224+
Name:"exists",
225+
OwnerName:"me",
226+
WorkspaceAgentHealth:&codersdk.WorkspaceAgentHealth{
227+
Healthy:true,
228+
},
229+
WorkspaceAgentLifecycle:ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
230+
WorkspaceStatus:codersdk.WorkspaceStatusRunning,
231+
CreatedAt:ts,
232+
UpdatedAt:ts,
300233
CurrentState:&codersdk.TaskStateEntry{
301234
State:codersdk.TaskStateWorking,
302235
Timestamp:ts.Add(time.Second),

‎coderd/aitasks_test.go‎

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,13 @@ func TestTasks(t *testing.T) {
156156
t.Parallel()
157157

158158
var (
159-
client,db=coderdtest.NewWithDatabase(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
160-
ctx=testutil.Context(t,testutil.WaitLong)
161-
user=coderdtest.CreateFirstUser(t,client)
162-
template=createAITemplate(t,client,user)
163-
wantPrompt="review my code"
164-
exp=codersdk.NewExperimentalClient(client)
159+
client,db=coderdtest.NewWithDatabase(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
160+
ctx=testutil.Context(t,testutil.WaitLong)
161+
user=coderdtest.CreateFirstUser(t,client)
162+
anotherUser,_=coderdtest.CreateAnotherUser(t,client,user.OrganizationID)
163+
template=createAITemplate(t,client,user)
164+
wantPrompt="review my code"
165+
exp=codersdk.NewExperimentalClient(client)
165166
)
166167

167168
task,err:=exp.CreateTask(ctx,"me", codersdk.CreateTaskRequest{
@@ -211,6 +212,24 @@ func TestTasks(t *testing.T) {
211212
assert.Equal(t,taskAppID,updated.WorkspaceAppID.UUID,"workspace app id should match")
212213
assert.NotEmpty(t,updated.WorkspaceStatus,"task status should not be empty")
213214

215+
// Fetch the task by name and verify the same result
216+
byName,err:=exp.TaskByOwnerAndName(ctx,codersdk.Me,task.Name)
217+
require.NoError(t,err)
218+
require.Equal(t,byName,updated)
219+
220+
// Another member user should not be able to fetch the task
221+
otherClient:=codersdk.NewExperimentalClient(anotherUser)
222+
_,err=otherClient.TaskByID(ctx,task.ID)
223+
require.Error(t,err,"fetching task should fail by ID for another member user")
224+
varsdkErr*codersdk.Error
225+
require.ErrorAs(t,err,&sdkErr)
226+
require.Equal(t,http.StatusNotFound,sdkErr.StatusCode())
227+
// Also test by name
228+
_,err=otherClient.TaskByOwnerAndName(ctx,task.OwnerName,task.Name)
229+
require.Error(t,err,"fetching task should fail by name for another member user")
230+
require.ErrorAs(t,err,&sdkErr)
231+
require.Equal(t,http.StatusNotFound,sdkErr.StatusCode())
232+
214233
// Stop the workspace
215234
coderdtest.MustTransitionWorkspace(t,client,task.WorkspaceID.UUID,codersdk.WorkspaceTransitionStart,codersdk.WorkspaceTransitionStop)
216235

@@ -654,7 +673,7 @@ func TestTasks(t *testing.T) {
654673
coderdtest.AwaitWorkspaceBuildJobCompleted(t,client,ws.LatestBuild.ID)
655674

656675
// Fetch the task by ID via experimental API and verify fields.
657-
task,err=exp.TaskByID(ctx,task.ID)
676+
task,err=exp.TaskByIdentifier(ctx,task.ID.String())
658677
require.NoError(t,err)
659678
require.NotZero(t,task.WorkspaceBuildNumber)
660679
require.True(t,task.WorkspaceAgentID.Valid)

‎coderd/database/dbauthz/dbauthz.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2989,6 +2989,10 @@ func (q *querier) GetTaskByID(ctx context.Context, id uuid.UUID) (database.Task,
29892989
returnfetch(q.log,q.auth,q.db.GetTaskByID)(ctx,id)
29902990
}
29912991

2992+
func (q*querier)GetTaskByOwnerIDAndName(ctx context.Context,arg database.GetTaskByOwnerIDAndNameParams) (database.Task,error) {
2993+
returnfetch(q.log,q.auth,q.db.GetTaskByOwnerIDAndName)(ctx,arg)
2994+
}
2995+
29922996
func (q*querier)GetTaskByWorkspaceID(ctx context.Context,workspaceID uuid.UUID) (database.Task,error) {
29932997
returnfetch(q.log,q.auth,q.db.GetTaskByWorkspaceID)(ctx,workspaceID)
29942998
}

‎coderd/database/dbauthz/dbauthz_test.go‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,6 +2375,17 @@ func (s *MethodTestSuite) TestTasks() {
23752375
dbm.EXPECT().GetTaskByID(gomock.Any(),task.ID).Return(task,nil).AnyTimes()
23762376
check.Args(task.ID).Asserts(task,policy.ActionRead).Returns(task)
23772377
}))
2378+
s.Run("GetTaskByOwnerIDAndName",s.Mocked(func(dbm*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
2379+
task:=testutil.Fake(s.T(),faker, database.Task{})
2380+
dbm.EXPECT().GetTaskByOwnerIDAndName(gomock.Any(), database.GetTaskByOwnerIDAndNameParams{
2381+
OwnerID:task.OwnerID,
2382+
Name:task.Name,
2383+
}).Return(task,nil).AnyTimes()
2384+
check.Args(database.GetTaskByOwnerIDAndNameParams{
2385+
OwnerID:task.OwnerID,
2386+
Name:task.Name,
2387+
}).Asserts(task,policy.ActionRead).Returns(task)
2388+
}))
23782389
s.Run("DeleteTask",s.Mocked(func(dbm*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
23792390
task:=testutil.Fake(s.T(),faker, database.Task{})
23802391
arg:= database.DeleteTaskParams{

‎coderd/database/dbmetrics/querymetrics.go‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/dbmock/dbmock.go‎

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/querier.go‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp