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

Commit433f9c4

Browse files
refactor: modify task creation endpoint to return a task, not workspace (#19637)
Relates tocoder/internal#898Refactor the `POST /api/experimental/tasks/{user}` endpoint to return acodersdk.Task instead of a codersdk.Workspace
1 parent6e55ed8 commit433f9c4

File tree

9 files changed

+121
-111
lines changed

9 files changed

+121
-111
lines changed

‎cli/exp_taskcreate.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (r *RootCmd) taskCreate() *serpent.Command {
104104
templateVersionPresetID=preset.ID
105105
}
106106

107-
workspace,err:=expClient.CreateTask(ctx,codersdk.Me, codersdk.CreateTaskRequest{
107+
task,err:=expClient.CreateTask(ctx,codersdk.Me, codersdk.CreateTaskRequest{
108108
TemplateVersionID:templateVersionID,
109109
TemplateVersionPresetID:templateVersionPresetID,
110110
Prompt:taskInput,
@@ -116,8 +116,8 @@ func (r *RootCmd) taskCreate() *serpent.Command {
116116
_,_=fmt.Fprintf(
117117
inv.Stdout,
118118
"The task %s has been created at %s!\n",
119-
cliui.Keyword(workspace.Name),
120-
cliui.Timestamp(workspace.CreatedAt),
119+
cliui.Keyword(task.Name),
120+
cliui.Timestamp(task.CreatedAt),
121121
)
122122

123123
returnnil

‎coderd/aitasks.go‎

Lines changed: 60 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,72 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
188188
WorkspaceOwner:owner.Username,
189189
},
190190
})
191-
192191
defercommitAudit()
193192
w,err:=createWorkspace(ctx,aReq,apiKey.UserID,api,owner,createReq,r)
194193
iferr!=nil {
195194
httperror.WriteResponseError(ctx,rw,err)
196195
return
197196
}
198197

199-
httpapi.Write(ctx,rw,http.StatusCreated,w)
198+
task:=taskFromWorkspace(w,req.Prompt)
199+
httpapi.Write(ctx,rw,http.StatusCreated,task)
200+
}
201+
202+
functaskFromWorkspace(ws codersdk.Workspace,initialPromptstring) codersdk.Task {
203+
// TODO(DanielleMaywood):
204+
// This just picks up the first agent it discovers.
205+
// This approach _might_ break when a task has multiple agents,
206+
// depending on which agent was found first.
207+
//
208+
// We explicitly do not have support for running tasks
209+
// inside of a sub agent at the moment, so we can be sure
210+
// that any sub agents are not the agent we're looking for.
211+
vartaskAgentID uuid.NullUUID
212+
vartaskAgentLifecycle*codersdk.WorkspaceAgentLifecycle
213+
vartaskAgentHealth*codersdk.WorkspaceAgentHealth
214+
for_,resource:=rangews.LatestBuild.Resources {
215+
for_,agent:=rangeresource.Agents {
216+
ifagent.ParentID.Valid {
217+
continue
218+
}
219+
220+
taskAgentID= uuid.NullUUID{Valid:true,UUID:agent.ID}
221+
taskAgentLifecycle=&agent.LifecycleState
222+
taskAgentHealth=&agent.Health
223+
break
224+
}
225+
}
226+
227+
varcurrentState*codersdk.TaskStateEntry
228+
ifws.LatestAppStatus!=nil {
229+
currentState=&codersdk.TaskStateEntry{
230+
Timestamp:ws.LatestAppStatus.CreatedAt,
231+
State:codersdk.TaskState(ws.LatestAppStatus.State),
232+
Message:ws.LatestAppStatus.Message,
233+
URI:ws.LatestAppStatus.URI,
234+
}
235+
}
236+
237+
return codersdk.Task{
238+
ID:ws.ID,
239+
OrganizationID:ws.OrganizationID,
240+
OwnerID:ws.OwnerID,
241+
OwnerName:ws.OwnerName,
242+
Name:ws.Name,
243+
TemplateID:ws.TemplateID,
244+
TemplateName:ws.TemplateName,
245+
TemplateDisplayName:ws.TemplateDisplayName,
246+
TemplateIcon:ws.TemplateIcon,
247+
WorkspaceID: uuid.NullUUID{Valid:true,UUID:ws.ID},
248+
WorkspaceAgentID:taskAgentID,
249+
WorkspaceAgentLifecycle:taskAgentLifecycle,
250+
WorkspaceAgentHealth:taskAgentHealth,
251+
CreatedAt:ws.CreatedAt,
252+
UpdatedAt:ws.UpdatedAt,
253+
InitialPrompt:initialPrompt,
254+
Status:ws.LatestBuild.Status,
255+
CurrentState:currentState,
256+
}
200257
}
201258

202259
// tasksFromWorkspaces converts a slice of API workspaces into tasks, fetching
@@ -221,60 +278,7 @@ func (api *API) tasksFromWorkspaces(ctx context.Context, apiWorkspaces []codersd
221278

222279
tasks:=make([]codersdk.Task,0,len(apiWorkspaces))
223280
for_,ws:=rangeapiWorkspaces {
224-
// TODO(DanielleMaywood):
225-
// This just picks up the first agent it discovers.
226-
// This approach _might_ break when a task has multiple agents,
227-
// depending on which agent was found first.
228-
//
229-
// We explicitly do not have support for running tasks
230-
// inside of a sub agent at the moment, so we can be sure
231-
// that any sub agents are not the agent we're looking for.
232-
vartaskAgentID uuid.NullUUID
233-
vartaskAgentLifecycle*codersdk.WorkspaceAgentLifecycle
234-
vartaskAgentHealth*codersdk.WorkspaceAgentHealth
235-
for_,resource:=rangews.LatestBuild.Resources {
236-
for_,agent:=rangeresource.Agents {
237-
ifagent.ParentID.Valid {
238-
continue
239-
}
240-
241-
taskAgentID= uuid.NullUUID{Valid:true,UUID:agent.ID}
242-
taskAgentLifecycle=&agent.LifecycleState
243-
taskAgentHealth=&agent.Health
244-
break
245-
}
246-
}
247-
248-
varcurrentState*codersdk.TaskStateEntry
249-
ifws.LatestAppStatus!=nil {
250-
currentState=&codersdk.TaskStateEntry{
251-
Timestamp:ws.LatestAppStatus.CreatedAt,
252-
State:codersdk.TaskState(ws.LatestAppStatus.State),
253-
Message:ws.LatestAppStatus.Message,
254-
URI:ws.LatestAppStatus.URI,
255-
}
256-
}
257-
258-
tasks=append(tasks, codersdk.Task{
259-
ID:ws.ID,
260-
OrganizationID:ws.OrganizationID,
261-
OwnerID:ws.OwnerID,
262-
OwnerName:ws.OwnerName,
263-
Name:ws.Name,
264-
TemplateID:ws.TemplateID,
265-
TemplateName:ws.TemplateName,
266-
TemplateDisplayName:ws.TemplateDisplayName,
267-
TemplateIcon:ws.TemplateIcon,
268-
WorkspaceID: uuid.NullUUID{Valid:true,UUID:ws.ID},
269-
WorkspaceAgentID:taskAgentID,
270-
WorkspaceAgentLifecycle:taskAgentLifecycle,
271-
WorkspaceAgentHealth:taskAgentHealth,
272-
CreatedAt:ws.CreatedAt,
273-
UpdatedAt:ws.UpdatedAt,
274-
InitialPrompt:promptsByBuildID[ws.LatestBuild.ID],
275-
Status:ws.LatestBuild.Status,
276-
CurrentState:currentState,
277-
})
281+
tasks=append(tasks,taskFromWorkspace(ws,promptsByBuildID[ws.LatestBuild.ID]))
278282
}
279283

280284
returntasks,nil

‎coderd/aitasks_test.go‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -419,19 +419,23 @@ func TestTasksCreate(t *testing.T) {
419419
expClient:=codersdk.NewExperimentalClient(client)
420420

421421
// When: We attempt to create a Task.
422-
workspace,err:=expClient.CreateTask(ctx,"me", codersdk.CreateTaskRequest{
422+
task,err:=expClient.CreateTask(ctx,"me", codersdk.CreateTaskRequest{
423423
TemplateVersionID:template.ActiveVersionID,
424424
Prompt:taskPrompt,
425425
})
426426
require.NoError(t,err)
427-
coderdtest.AwaitWorkspaceBuildJobCompleted(t,client,workspace.LatestBuild.ID)
427+
require.True(t,task.WorkspaceID.Valid)
428+
429+
ws,err:=client.Workspace(ctx,task.WorkspaceID.UUID)
430+
require.NoError(t,err)
431+
coderdtest.AwaitWorkspaceBuildJobCompleted(t,client,ws.LatestBuild.ID)
428432

429433
// Then: We expect a workspace to have been created.
430-
assert.NotEmpty(t,workspace.Name)
431-
assert.Equal(t,template.ID,workspace.TemplateID)
434+
assert.NotEmpty(t,task.Name)
435+
assert.Equal(t,template.ID,task.TemplateID)
432436

433437
// And: We expect it to have the "AI Prompt" parameter correctly set.
434-
parameters,err:=client.WorkspaceBuildParameters(ctx,workspace.LatestBuild.ID)
438+
parameters,err:=client.WorkspaceBuildParameters(ctx,ws.LatestBuild.ID)
435439
require.NoError(t,err)
436440
require.Len(t,parameters,1)
437441
assert.Equal(t,codersdk.AITaskPromptParameterName,parameters[0].Name)

‎codersdk/aitasks.go‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,23 @@ type CreateTaskRequest struct {
5353
Promptstring`json:"prompt"`
5454
}
5555

56-
func (c*ExperimentalClient)CreateTask(ctx context.Context,userstring,requestCreateTaskRequest) (Workspace,error) {
56+
func (c*ExperimentalClient)CreateTask(ctx context.Context,userstring,requestCreateTaskRequest) (Task,error) {
5757
res,err:=c.Request(ctx,http.MethodPost,fmt.Sprintf("/api/experimental/tasks/%s",user),request)
5858
iferr!=nil {
59-
returnWorkspace{},err
59+
returnTask{},err
6060
}
6161
deferres.Body.Close()
6262

6363
ifres.StatusCode!=http.StatusCreated {
64-
returnWorkspace{},ReadBodyAsError(res)
64+
returnTask{},ReadBodyAsError(res)
6565
}
6666

67-
varworkspaceWorkspace
68-
iferr:=json.NewDecoder(res.Body).Decode(&workspace);err!=nil {
69-
returnWorkspace{},err
67+
vartaskTask
68+
iferr:=json.NewDecoder(res.Body).Decode(&task);err!=nil {
69+
returnTask{},err
7070
}
7171

72-
returnworkspace,nil
72+
returntask,nil
7373
}
7474

7575
// TaskState represents the high-level lifecycle of a task.

‎site/src/api/api.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2686,8 +2686,8 @@ class ExperimentalApiMethods {
26862686
createTask=async(
26872687
user:string,
26882688
req:TypesGen.CreateTaskRequest,
2689-
):Promise<TypesGen.Workspace>=>{
2690-
constresponse=awaitthis.axios.post<TypesGen.Workspace>(
2689+
):Promise<TypesGen.Task>=>{
2690+
constresponse=awaitthis.axios.post<TypesGen.Task>(
26912691
`/api/experimental/tasks/${user}`,
26922692
req,
26932693
);

‎site/src/pages/TasksPage/TaskPrompt.tsx‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import{API}from"api/api";
12
import{getErrorDetail,getErrorMessage}from"api/errors";
23
import{templateVersionPresets}from"api/queries/templates";
34
importtype{
45
Preset,
6+
Task,
57
Template,
68
TemplateVersionExternalAuth,
79
}from"api/typesGenerated";
@@ -28,13 +30,12 @@ import {
2830
import{useAuthenticated}from"hooks/useAuthenticated";
2931
import{useExternalAuth}from"hooks/useExternalAuth";
3032
import{RedoIcon,RotateCcwIcon,SendIcon}from"lucide-react";
31-
import{AI_PROMPT_PARAMETER_NAME,typeTask}from"modules/tasks/tasks";
33+
import{AI_PROMPT_PARAMETER_NAME}from"modules/tasks/tasks";
3234
import{typeFC,useEffect,useState}from"react";
3335
import{useMutation,useQuery,useQueryClient}from"react-query";
3436
import{useNavigate}from"react-router";
3537
importTextareaAutosizefrom"react-textarea-autosize";
3638
import{docs}from"utils/docs";
37-
import{data}from"./data";
3839

3940
consttextareaPlaceholder="Prompt your AI agent to start a task...";
4041

@@ -64,7 +65,7 @@ export const TaskPrompt: FC<TaskPromptProps> = ({
6465
<CreateTaskForm
6566
templates={templates}
6667
onSuccess={(task)=>{
67-
navigate(`/tasks/${task.workspace.owner_name}/${task.workspace.name}`);
68+
navigate(`/tasks/${task.owner_name}/${task.name}`);
6869
}}
6970
/>
7071
);
@@ -188,12 +189,11 @@ const CreateTaskForm: FC<CreateTaskFormProps> = ({ templates, onSuccess }) => {
188189

189190
constcreateTaskMutation=useMutation({
190191
mutationFn:async({ prompt}:CreateTaskMutationFnProps)=>
191-
data.createTask(
192+
API.experimental.createTask(user.id,{
192193
prompt,
193-
user.id,
194-
selectedTemplate.active_version_id,
195-
selectedPresetId,
196-
),
194+
template_version_id:selectedTemplate.active_version_id,
195+
template_version_preset_id:selectedPresetId,
196+
}),
197197
onSuccess:async(task)=>{
198198
awaitqueryClient.invalidateQueries({
199199
queryKey:["tasks"],

‎site/src/pages/TasksPage/TasksPage.stories.tsx‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
MockAIPromptPresets,
33
MockNewTaskData,
44
MockPresets,
5+
MockTask,
56
MockTasks,
67
MockTemplate,
78
MockTemplateVersionExternalAuthGithub,
@@ -19,7 +20,6 @@ import { API } from "api/api";
1920
import{MockUsers}from"pages/UsersPage/storybookData/users";
2021
import{expect,spyOn,userEvent,waitFor,within}from"storybook/test";
2122
import{reactRouterParameters}from"storybook-addon-remix-react-router";
22-
import{data}from"./data";
2323
importTasksPagefrom"./TasksPage";
2424

2525
constmeta:Meta<typeofTasksPage>={
@@ -248,7 +248,7 @@ export const CreateTaskSuccessfully: Story = {
248248
spyOn(API.experimental,"getTasks")
249249
.mockResolvedValueOnce(MockTasks)
250250
.mockResolvedValue([MockNewTaskData, ...MockTasks]);
251-
spyOn(data,"createTask").mockResolvedValue(MockNewTaskData);
251+
spyOn(API.experimental,"createTask").mockResolvedValue(MockTask);
252252
},
253253
play:async({ canvasElement, step})=>{
254254
constcanvas=within(canvasElement);
@@ -272,7 +272,7 @@ export const CreateTaskError: Story = {
272272
beforeEach:()=>{
273273
spyOn(API,"getTemplates").mockResolvedValue([MockTemplate]);
274274
spyOn(API.experimental,"getTasks").mockResolvedValue(MockTasks);
275-
spyOn(data,"createTask").mockRejectedValue(
275+
spyOn(API.experimental,"createTask").mockRejectedValue(
276276
mockApiError({
277277
message:"Failed to create task",
278278
detail:"You don't have permission to create tasks.",
@@ -301,7 +301,7 @@ export const WithAuthenticatedExternalAuth: Story = {
301301
spyOn(API.experimental,"getTasks")
302302
.mockResolvedValueOnce(MockTasks)
303303
.mockResolvedValue([MockNewTaskData, ...MockTasks]);
304-
spyOn(data,"createTask").mockResolvedValue(MockNewTaskData);
304+
spyOn(API.experimental,"createTask").mockResolvedValue(MockTask);
305305
spyOn(API,"getTemplateVersionExternalAuth").mockResolvedValue([
306306
MockTemplateVersionExternalAuthGithubAuthenticated,
307307
]);
@@ -327,7 +327,7 @@ export const MissingExternalAuth: Story = {
327327
spyOn(API.experimental,"getTasks")
328328
.mockResolvedValueOnce(MockTasks)
329329
.mockResolvedValue([MockNewTaskData, ...MockTasks]);
330-
spyOn(data,"createTask").mockResolvedValue(MockNewTaskData);
330+
spyOn(API.experimental,"createTask").mockResolvedValue(MockTask);
331331
spyOn(API,"getTemplateVersionExternalAuth").mockResolvedValue([
332332
MockTemplateVersionExternalAuthGithub,
333333
]);
@@ -353,7 +353,7 @@ export const ExternalAuthError: Story = {
353353
spyOn(API.experimental,"getTasks")
354354
.mockResolvedValueOnce(MockTasks)
355355
.mockResolvedValue([MockNewTaskData, ...MockTasks]);
356-
spyOn(data,"createTask").mockResolvedValue(MockNewTaskData);
356+
spyOn(API.experimental,"createTask").mockResolvedValue(MockTask);
357357
spyOn(API,"getTemplateVersionExternalAuth").mockRejectedValue(
358358
mockApiError({
359359
message:"Failed to load external auth",

‎site/src/pages/TasksPage/data.ts‎

Lines changed: 0 additions & 24 deletions
This file was deleted.

‎site/src/testHelpers/entities.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4903,6 +4903,32 @@ export const MockTasks = [
49034903
},
49044904
];
49054905

4906+
exportconstMockTask:TypesGen.Task={
4907+
id:"test-task",
4908+
name:"task-wild-test-123",
4909+
organization_id:MockOrganization.id,
4910+
owner_id:MockUserOwner.id,
4911+
owner_name:MockUserOwner.username,
4912+
template_id:MockTemplate.id,
4913+
template_name:MockTemplate.name,
4914+
template_display_name:MockTemplate.display_name,
4915+
template_icon:MockTemplate.icon,
4916+
workspace_id:MockWorkspace.id,
4917+
workspace_agent_id:MockWorkspaceAgent.id,
4918+
workspace_agent_lifecycle:MockWorkspaceAgent.lifecycle_state,
4919+
workspace_agent_health:MockWorkspaceAgent.health,
4920+
initial_prompt:"Perform some task",
4921+
status:"running",
4922+
current_state:{
4923+
timestamp:"2022-05-17T17:39:01.382927298Z",
4924+
state:"idle",
4925+
message:"Should I continue?",
4926+
uri:"https://dev.coder.com",
4927+
},
4928+
created_at:"2022-05-17T17:39:01.382927298Z",
4929+
updated_at:"2022-05-17T17:39:01.382927298Z",
4930+
};
4931+
49064932
exportconstMockNewTaskData={
49074933
prompt:"Create a new task",
49084934
workspace:{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp