Skip to content

Commit c6f6399

Browse files
authored
feat(coderd): use task data model when fetching a task (#20380)
Updates coder/internal#976
1 parent 9855460 commit c6f6399

3 files changed

Lines changed: 56 additions & 78 deletions

File tree

‎cli/exp_task_status_test.go‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ import (
2424
func Test_TaskStatus(t *testing.T) {
2525
t.Parallel()
2626

27-
t.Skip("TODO(mafredri): Remove, fixed down-stack!")
28-
2927
for _, tc := range []struct {
3028
args []string
3129
expectOutput string
@@ -90,7 +88,9 @@ func Test_TaskStatus(t *testing.T) {
9088
Healthy: true,
9189
},
9290
WorkspaceAgentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
91+
Status: codersdk.TaskStatusActive,
9392
})
93+
return
9494
default:
9595
t.Errorf("unexpected path: %s", r.URL.Path)
9696
}
@@ -125,7 +125,9 @@ STATE CHANGED STATUS HEALTHY STATE MESSAGE
125125
Healthy: true,
126126
},
127127
WorkspaceAgentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
128+
Status: codersdk.TaskStatusPending,
128129
})
130+
return
129131
case 1:
130132
httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{
131133
ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"),
@@ -136,7 +138,9 @@ STATE CHANGED STATUS HEALTHY STATE MESSAGE
136138
},
137139
WorkspaceAgentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
138140
UpdatedAt: now.Add(-4 * time.Second),
141+
Status: codersdk.TaskStatusActive,
139142
})
143+
return
140144
case 2:
141145
httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{
142146
ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"),
@@ -152,7 +156,9 @@ STATE CHANGED STATUS HEALTHY STATE MESSAGE
152156
Timestamp: now.Add(-3 * time.Second),
153157
Message: "Reticulating splines...",
154158
},
159+
Status: codersdk.TaskStatusActive,
155160
})
161+
return
156162
case 3:
157163
httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{
158164
ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"),
@@ -168,13 +174,16 @@ STATE CHANGED STATUS HEALTHY STATE MESSAGE
168174
Timestamp: now.Add(-2 * time.Second),
169175
Message: "Splines reticulated successfully!",
170176
},
177+
Status: codersdk.TaskStatusActive,
171178
})
179+
return
172180
default:
173181
httpapi.InternalServerError(w, xerrors.New("too many calls!"))
174182
return
175183
}
176184
default:
177185
httpapi.InternalServerError(w, xerrors.Errorf("unexpected path: %q", r.URL.Path))
186+
return
178187
}
179188
}
180189
},
@@ -188,16 +197,18 @@ STATE CHANGED STATUS HEALTHY STATE MESSAGE
188197
"owner_name": "",
189198
"name": "",
190199
"template_id": "00000000-0000-0000-0000-000000000000",
200+
"template_version_id": "00000000-0000-0000-0000-000000000000",
191201
"template_name": "",
192202
"template_display_name": "",
193203
"template_icon": "",
194204
"workspace_id": null,
205+
"workspace_status": "running",
195206
"workspace_agent_id": null,
196207
"workspace_agent_lifecycle": null,
197208
"workspace_agent_health": null,
198209
"workspace_app_id": null,
199210
"initial_prompt": "",
200-
"status": "running",
211+
"status": "active",
201212
"current_state": {
202213
"timestamp": "2025-08-26T12:34:57Z",
203214
"state": "working",
@@ -226,7 +237,9 @@ STATE CHANGED STATUS HEALTHY STATE MESSAGE
226237
Timestamp: ts.Add(time.Second),
227238
Message: "Thinking furiously...",
228239
},
240+
Status: codersdk.TaskStatusActive,
229241
})
242+
return
230243
default:
231244
t.Errorf("unexpected path: %s", r.URL.Path)
232245
}

‎coderd/aitasks.go‎

Lines changed: 12 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"github.com/coder/coder/v2/coderd/rbac/policy"
2626
"github.com/coder/coder/v2/coderd/searchquery"
2727
"github.com/coder/coder/v2/coderd/taskname"
28-
"github.com/coder/coder/v2/coderd/util/slice"
2928
"github.com/coder/coder/v2/codersdk"
3029

3130
aiagentapi "github.com/coder/agentapi-sdk-go"
@@ -604,25 +603,22 @@ func (api *API) tasksList(rw http.ResponseWriter, r *http.Request) {
604603
func (api *API) taskGet(rw http.ResponseWriter, r *http.Request) {
605604
ctx := r.Context()
606605
apiKey := httpmw.APIKey(r)
606+
task := httpmw.TaskParam(r)
607607

608-
idStr := chi.URLParam(r, "task")
609-
taskID, err := uuid.Parse(idStr)
610-
if err != nil {
611-
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
612-
Message: fmt.Sprintf("Invalid UUID %q for task ID.", idStr),
608+
if !task.WorkspaceID.Valid {
609+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
610+
Message: "Internal error fetching task.",
611+
Detail: "Task workspace ID is invalid.",
613612
})
614613
return
615614
}
616615

617-
// For now, taskID = workspaceID, once we have a task data model in
618-
// the DB, we can change this lookup.
619-
workspaceID := taskID
620-
workspace, err := api.Database.GetWorkspaceByID(ctx, workspaceID)
621-
if httpapi.Is404Error(err) {
622-
httpapi.ResourceNotFound(rw)
623-
return
624-
}
616+
workspace, err := api.Database.GetWorkspaceByID(ctx, task.WorkspaceID.UUID)
625617
if err != nil {
618+
if httpapi.Is404Error(err) {
619+
httpapi.ResourceNotFound(rw)
620+
return
621+
}
626622
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
627623
Message: "Internal error fetching workspace.",
628624
Detail: err.Error(),
@@ -642,34 +638,6 @@ func (api *API) taskGet(rw http.ResponseWriter, r *http.Request) {
642638
httpapi.ResourceNotFound(rw)
643639
return
644640
}
645-
if data.builds[0].HasAITask == nil || !*data.builds[0].HasAITask {
646-
// TODO(DanielleMaywood):
647-
// This is a temporary workaround. When a task has just been created, but
648-
// not yet provisioned, the workspace build will not have `HasAITask` set.
649-
//
650-
// When we reach this code flow, it is _either_ because the workspace is
651-
// not a task, or it is a task that has not yet been provisioned. This
652-
// endpoint should rarely be called with a non-task workspace so we
653-
// should be fine with this extra database call to check if it has the
654-
// special "AI Task" parameter.
655-
parameters, err := api.Database.GetWorkspaceBuildParameters(ctx, data.builds[0].ID)
656-
if err != nil {
657-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
658-
Message: "Internal error fetching workspace build parameters.",
659-
Detail: err.Error(),
660-
})
661-
return
662-
}
663-
664-
_, hasAITask := slice.Find(parameters, func(t database.WorkspaceBuildParameter) bool {
665-
return t.Name == codersdk.AITaskPromptParameterName
666-
})
667-
668-
if !hasAITask {
669-
httpapi.ResourceNotFound(rw)
670-
return
671-
}
672-
}
673641

674642
appStatus := codersdk.WorkspaceAppStatus{}
675643
if len(data.appStatuses) > 0 {
@@ -692,16 +660,8 @@ func (api *API) taskGet(rw http.ResponseWriter, r *http.Request) {
692660
return
693661
}
694662

695-
tasks, err := api.tasksFromWorkspaces(ctx, []codersdk.Workspace{ws})
696-
if err != nil {
697-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
698-
Message: "Internal error fetching task prompt and state.",
699-
Detail: err.Error(),
700-
})
701-
return
702-
}
703-
704-
httpapi.Write(ctx, rw, http.StatusOK, tasks[0])
663+
taskResp := taskFromDBTaskAndWorkspace(task, ws)
664+
httpapi.Write(ctx, rw, http.StatusOK, taskResp)
705665
}
706666

707667
// @Summary Delete AI task by ID

‎coderd/aitasks_test.go‎

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -274,34 +274,37 @@ func TestTasks(t *testing.T) {
274274
t.Run("Get", func(t *testing.T) {
275275
t.Parallel()
276276

277-
t.Skip("TODO(mafredri): Remove, fixed down-stack!")
278-
279277
var (
280278
client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
281279
ctx = testutil.Context(t, testutil.WaitLong)
282280
user = coderdtest.CreateFirstUser(t, client)
283281
template = createAITemplate(t, client, user)
284-
// Create a workspace (task) with a specific prompt.
285282
wantPrompt = "review my code"
286-
workspace = coderdtest.CreateWorkspace(t, client, template.ID, func(req *codersdk.CreateWorkspaceRequest) {
287-
req.RichParameterValues = []codersdk.WorkspaceBuildParameter{
288-
{Name: codersdk.AITaskPromptParameterName, Value: wantPrompt},
289-
}
290-
})
283+
exp = codersdk.NewExperimentalClient(client)
291284
)
292285

293-
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
294-
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
286+
task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{
287+
TemplateVersionID: template.ActiveVersionID,
288+
Input: wantPrompt,
289+
})
290+
require.NoError(t, err)
291+
require.True(t, task.WorkspaceID.Valid)
292+
293+
// Get the workspace and wait for it to be ready.
294+
ws, err := client.Workspace(ctx, task.WorkspaceID.UUID)
295+
require.NoError(t, err)
296+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
297+
ws = coderdtest.MustWorkspace(t, client, task.WorkspaceID.UUID)
295298
// Assert invariant: the workspace has exactly one resource with one agent with one app.
296299
require.Len(t, ws.LatestBuild.Resources, 1)
297300
require.Len(t, ws.LatestBuild.Resources[0].Agents, 1)
298301
agentID := ws.LatestBuild.Resources[0].Agents[0].ID
299302
taskAppID := ws.LatestBuild.Resources[0].Agents[0].Apps[0].ID
300303

301304
// Insert an app status for the workspace
302-
_, err := db.InsertWorkspaceAppStatus(dbauthz.AsSystemRestricted(ctx), database.InsertWorkspaceAppStatusParams{
305+
_, err = db.InsertWorkspaceAppStatus(dbauthz.AsSystemRestricted(ctx), database.InsertWorkspaceAppStatusParams{
303306
ID: uuid.New(),
304-
WorkspaceID: workspace.ID,
307+
WorkspaceID: task.WorkspaceID.UUID,
305308
CreatedAt: dbtime.Now(),
306309
AgentID: agentID,
307310
AppID: taskAppID,
@@ -311,31 +314,33 @@ func TestTasks(t *testing.T) {
311314
require.NoError(t, err)
312315

313316
// Fetch the task by ID via experimental API and verify fields.
314-
exp := codersdk.NewExperimentalClient(client)
315-
task, err := exp.TaskByID(ctx, workspace.ID)
317+
updated, err := exp.TaskByID(ctx, task.ID)
316318
require.NoError(t, err)
317319

318-
assert.Equal(t, workspace.ID, task.ID, "task ID should match workspace ID")
319-
assert.Equal(t, workspace.Name, task.Name, "task name should map from workspace name")
320-
assert.Equal(t, wantPrompt, task.InitialPrompt, "task prompt should match the AI Prompt parameter")
321-
assert.Equal(t, workspace.ID, task.WorkspaceID.UUID, "workspace id should match")
322-
assert.NotEmpty(t, task.WorkspaceStatus, "task status should not be empty")
320+
assert.Equal(t, task.ID, updated.ID, "task ID should match")
321+
assert.Equal(t, task.Name, updated.Name, "task name should match")
322+
assert.Equal(t, wantPrompt, updated.InitialPrompt, "task prompt should match the AI Prompt parameter")
323+
assert.Equal(t, task.WorkspaceID.UUID, updated.WorkspaceID.UUID, "workspace id should match")
324+
assert.Equal(t, ws.LatestBuild.BuildNumber, updated.WorkspaceBuildNumber, "workspace build number should match")
325+
assert.Equal(t, agentID, updated.WorkspaceAgentID.UUID, "workspace agent id should match")
326+
assert.Equal(t, taskAppID, updated.WorkspaceAppID.UUID, "workspace app id should match")
327+
assert.NotEmpty(t, updated.WorkspaceStatus, "task status should not be empty")
323328

324329
// Stop the workspace
325-
coderdtest.MustTransitionWorkspace(t, client, workspace.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop)
330+
coderdtest.MustTransitionWorkspace(t, client, task.WorkspaceID.UUID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop)
326331

327332
// Verify that the previous status still remains
328-
updated, err := exp.TaskByID(ctx, workspace.ID)
333+
updated, err = exp.TaskByID(ctx, task.ID)
329334
require.NoError(t, err)
330335
assert.NotNil(t, updated.CurrentState, "current state should not be nil")
331336
assert.Equal(t, "all done", updated.CurrentState.Message)
332337
assert.Equal(t, codersdk.TaskStateComplete, updated.CurrentState.State)
333338

334339
// Start the workspace again
335-
coderdtest.MustTransitionWorkspace(t, client, workspace.ID, codersdk.WorkspaceTransitionStop, codersdk.WorkspaceTransitionStart)
340+
coderdtest.MustTransitionWorkspace(t, client, task.WorkspaceID.UUID, codersdk.WorkspaceTransitionStop, codersdk.WorkspaceTransitionStart)
336341

337342
// Verify that the status from the previous build is no longer present
338-
updated, err = exp.TaskByID(ctx, workspace.ID)
343+
updated, err = exp.TaskByID(ctx, task.ID)
339344
require.NoError(t, err)
340345
assert.Nil(t, updated.CurrentState, "current state should be nil")
341346
})

0 commit comments

Comments
 (0)