Skip to content

Commit ff91ceb

Browse files
committed
refactor task status/state
1 parent 59c9c7e commit ff91ceb

File tree

3 files changed

+93
-118
lines changed

3 files changed

+93
-118
lines changed

coderd/aitasks.go

Lines changed: 52 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package coderd
22

33
import (
4+
"context"
45
"database/sql"
56
"errors"
67
"fmt"
@@ -190,49 +191,60 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
190191
createWorkspace(ctx, aReq, apiKey.UserID, api, owner, createReq, rw, r)
191192
}
192193

193-
// tasksListResponse wraps a list of experimental tasks.
194-
//
195-
// Experimental: Response shape is experimental and may change.
196-
type tasksListResponse struct {
197-
Tasks []codersdk.Task `json:"tasks"`
198-
Count int `json:"count"`
199-
}
200-
201-
func mapTaskStatus(ws codersdk.Workspace) codersdk.TaskStatus {
202-
switch ws.LatestBuild.Status {
203-
case codersdk.WorkspaceStatusPending:
204-
return codersdk.TaskStatusPending
205-
206-
case codersdk.WorkspaceStatusStarting:
207-
return codersdk.TaskStatusStarting
194+
// tasksFromWorkspaces converts a slice of API workspaces into tasks, fetching
195+
// prompts and mapping status/state.
196+
func (api *API) tasksFromWorkspaces(ctx context.Context, apiWorkspaces []codersdk.Workspace) ([]codersdk.Task, error) {
197+
// Fetch prompts for each workspace build and map by build ID.
198+
buildIDs := make([]uuid.UUID, 0, len(apiWorkspaces))
199+
for _, ws := range apiWorkspaces {
200+
buildIDs = append(buildIDs, ws.LatestBuild.ID)
201+
}
202+
parameters, err := api.Database.GetWorkspaceBuildParametersByBuildIDs(ctx, buildIDs)
203+
if err != nil {
204+
return nil, err
205+
}
206+
promptsByBuildID := make(map[uuid.UUID]string, len(parameters))
207+
for _, p := range parameters {
208+
if p.Name == codersdk.AITaskPromptParameterName {
209+
promptsByBuildID[p.WorkspaceBuildID] = p.Value
210+
}
211+
}
208212

209-
case codersdk.WorkspaceStatusRunning:
213+
tasks := make([]codersdk.Task, 0, len(apiWorkspaces))
214+
for _, ws := range apiWorkspaces {
215+
var currentState *codersdk.TaskStateEntry
210216
if ws.LatestAppStatus != nil {
211-
switch ws.LatestAppStatus.State {
212-
case codersdk.WorkspaceAppStatusStateWorking:
213-
return codersdk.TaskStatusWorking
214-
case codersdk.WorkspaceAppStatusStateIdle:
215-
return codersdk.TaskStatusIdle
216-
case codersdk.WorkspaceAppStatusStateComplete:
217-
return codersdk.TaskStatusCompleted
218-
case codersdk.WorkspaceAppStatusStateFailure:
219-
return codersdk.TaskStatusFailed
217+
currentState = &codersdk.TaskStateEntry{
218+
Timestamp: ws.LatestAppStatus.CreatedAt,
219+
State: codersdk.TaskState(ws.LatestAppStatus.State),
220+
Message: ws.LatestAppStatus.Message,
221+
URI: ws.LatestAppStatus.URI,
220222
}
221223
}
222-
return codersdk.TaskStatusStarting
223-
224-
case codersdk.WorkspaceStatusStopping, codersdk.WorkspaceStatusStopped:
225-
return codersdk.TaskStatusStopping
226-
227-
case codersdk.WorkspaceStatusDeleting, codersdk.WorkspaceStatusDeleted:
228-
return codersdk.TaskStatusDeleting
224+
tasks = append(tasks, codersdk.Task{
225+
ID: ws.ID,
226+
OrganizationID: ws.OrganizationID,
227+
OwnerID: ws.OwnerID,
228+
Name: ws.Name,
229+
TemplateID: ws.TemplateID,
230+
WorkspaceID: uuid.NullUUID{Valid: true, UUID: ws.ID},
231+
CreatedAt: ws.CreatedAt,
232+
UpdatedAt: ws.UpdatedAt,
233+
Prompt: promptsByBuildID[ws.LatestBuild.ID],
234+
Status: ws.LatestBuild.Status,
235+
CurrentState: currentState,
236+
})
237+
}
229238

230-
case codersdk.WorkspaceStatusFailed, codersdk.WorkspaceStatusCanceling, codersdk.WorkspaceStatusCanceled:
231-
return codersdk.TaskStatusFailed
239+
return tasks, nil
240+
}
232241

233-
default:
234-
return codersdk.TaskStatusPending
235-
}
242+
// tasksListResponse wraps a list of experimental tasks.
243+
//
244+
// Experimental: Response shape is experimental and may change.
245+
type tasksListResponse struct {
246+
Tasks []codersdk.Task `json:"tasks"`
247+
Count int `json:"count"`
236248
}
237249

238250
// tasksList is an experimental endpoint to list AI tasks by mapping
@@ -323,41 +335,14 @@ func (api *API) tasksList(rw http.ResponseWriter, r *http.Request) {
323335
return
324336
}
325337

326-
// Fetch prompts for each workspace build and map by build ID.
327-
buildIDs := make([]uuid.UUID, 0, len(apiWorkspaces))
328-
for _, ws := range apiWorkspaces {
329-
buildIDs = append(buildIDs, ws.LatestBuild.ID)
330-
}
331-
parameters, err := api.Database.GetWorkspaceBuildParametersByBuildIDs(ctx, buildIDs)
338+
tasks, err := api.tasksFromWorkspaces(ctx, apiWorkspaces)
332339
if err != nil {
333340
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
334341
Message: "Internal error fetching task prompts.",
335342
Detail: err.Error(),
336343
})
337344
return
338345
}
339-
promptsByBuildID := make(map[uuid.UUID]string, len(parameters))
340-
for _, p := range parameters {
341-
if p.Name == codersdk.AITaskPromptParameterName {
342-
promptsByBuildID[p.WorkspaceBuildID] = p.Value
343-
}
344-
}
345-
346-
tasks := make([]codersdk.Task, 0, len(apiWorkspaces))
347-
for _, ws := range apiWorkspaces {
348-
tasks = append(tasks, codersdk.Task{
349-
ID: ws.ID,
350-
OrganizationID: ws.OrganizationID,
351-
OwnerID: ws.OwnerID,
352-
Name: ws.Name,
353-
TemplateID: ws.TemplateID,
354-
WorkspaceID: uuid.NullUUID{Valid: true, UUID: ws.ID},
355-
Prompt: promptsByBuildID[ws.LatestBuild.ID],
356-
Status: mapTaskStatus(ws),
357-
CreatedAt: ws.CreatedAt,
358-
UpdatedAt: ws.UpdatedAt,
359-
})
360-
}
361346

362347
httpapi.Write(ctx, rw, http.StatusOK, tasksListResponse{
363348
Tasks: tasks,
@@ -432,35 +417,14 @@ func (api *API) taskGet(rw http.ResponseWriter, r *http.Request) {
432417
return
433418
}
434419

435-
// Fetch the AI prompt from the build parameters.
436-
params, err := api.Database.GetWorkspaceBuildParametersByBuildIDs(ctx, []uuid.UUID{ws.LatestBuild.ID})
420+
tasks, err := api.tasksFromWorkspaces(ctx, []codersdk.Workspace{ws})
437421
if err != nil {
438422
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
439423
Message: "Internal error fetching task prompt.",
440424
Detail: err.Error(),
441425
})
442426
return
443427
}
444-
prompt := ""
445-
for _, p := range params {
446-
if p.Name == codersdk.AITaskPromptParameterName {
447-
prompt = p.Value
448-
break
449-
}
450-
}
451-
452-
resp := codersdk.Task{
453-
ID: ws.ID,
454-
OrganizationID: ws.OrganizationID,
455-
OwnerID: ws.OwnerID,
456-
Name: ws.Name,
457-
TemplateID: ws.TemplateID,
458-
WorkspaceID: uuid.NullUUID{Valid: true, UUID: ws.ID},
459-
Prompt: prompt,
460-
Status: mapTaskStatus(ws),
461-
CreatedAt: ws.CreatedAt,
462-
UpdatedAt: ws.UpdatedAt,
463-
}
464428

465-
httpapi.Write(ctx, rw, http.StatusOK, resp)
429+
httpapi.Write(ctx, rw, http.StatusOK, tasks[0])
466430
}

codersdk/aitasks.go

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -72,44 +72,51 @@ func (c *ExperimentalClient) CreateTask(ctx context.Context, user string, reques
7272
return workspace, nil
7373
}
7474

75-
// TaskStatus represents the high-level lifecycle of a task.
75+
// TaskState represents the high-level lifecycle of a task.
7676
//
7777
// Experimental: This type is experimental and may change in the future.
78-
type TaskStatus string
78+
type TaskState string
7979

8080
const (
81-
TaskStatusPending TaskStatus = "pending"
82-
TaskStatusStarting TaskStatus = "starting"
83-
TaskStatusStopping TaskStatus = "stopping"
84-
TaskStatusDeleting TaskStatus = "deleting"
85-
TaskStatusWorking TaskStatus = "working"
86-
TaskStatusIdle TaskStatus = "idle"
87-
TaskStatusCompleted TaskStatus = "completed"
88-
TaskStatusFailed TaskStatus = "failed"
81+
TaskStateWorking TaskState = "working"
82+
TaskStateIdle TaskState = "idle"
83+
TaskStateCompleted TaskState = "completed"
84+
TaskStateFailed TaskState = "failed"
8985
)
9086

91-
// TasksFilter filters the list of tasks.
87+
// Task represents a task.
9288
//
9389
// Experimental: This type is experimental and may change in the future.
94-
type TasksFilter struct {
95-
// Owner can be a username, UUID, or "me"
96-
Owner string `json:"owner,omitempty"`
90+
type Task struct {
91+
ID uuid.UUID `json:"id" format:"uuid"`
92+
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
93+
OwnerID uuid.UUID `json:"owner_id" format:"uuid"`
94+
Name string `json:"name"`
95+
TemplateID uuid.UUID `json:"template_id" format:"uuid"`
96+
WorkspaceID uuid.NullUUID `json:"workspace_id" format:"uuid"`
97+
Prompt string `json:"prompt"`
98+
Status WorkspaceStatus `json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted"`
99+
CurrentState *TaskStateEntry `json:"current_state"`
100+
CreatedAt time.Time `json:"created_at" format:"date-time"`
101+
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
97102
}
98103

99-
// Task represents a task.
104+
// TaskStateEntry represents a single entry in the task's state history.
100105
//
101106
// Experimental: This type is experimental and may change in the future.
102-
type Task struct {
103-
ID uuid.UUID `json:"id" format:"uuid"`
104-
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
105-
OwnerID uuid.UUID `json:"owner_id" format:"uuid"`
106-
Name string `json:"name"`
107-
TemplateID uuid.UUID `json:"template_id" format:"uuid"`
108-
WorkspaceID uuid.NullUUID `json:"workspace_id" format:"uuid"`
109-
Prompt string `json:"prompt"`
110-
Status TaskStatus `json:"status" enum:"pending,starting,stopping,deleting,working,idle,completed,failed"`
111-
CreatedAt time.Time `json:"created_at" format:"date-time"`
112-
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
107+
type TaskStateEntry struct {
108+
Timestamp time.Time `json:"timestamp" format:"date-time"`
109+
State TaskState `json:"state" enum:"working,idle,completed,failed"`
110+
Message string `json:"message"`
111+
URI string `json:"uri"`
112+
}
113+
114+
// TasksFilter filters the list of tasks.
115+
//
116+
// Experimental: This type is experimental and may change in the future.
117+
type TasksFilter struct {
118+
// Owner can be a username, UUID, or "me"
119+
Owner string `json:"owner,omitempty"`
113120
}
114121

115122
// Tasks lists all tasks belonging to the user or specified owner.

site/src/api/typesGenerated.ts

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)