Skip to content

Commit 9b5a99c

Browse files
chore: finish making that change
1 parent 27b5823 commit 9b5a99c

File tree

7 files changed

+75
-27
lines changed

7 files changed

+75
-27
lines changed

coderd/aitasks.go

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"net/http"
8+
"slices"
89
"strings"
910

1011
"github.com/google/uuid"
@@ -75,10 +76,10 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
7576
ctx = r.Context()
7677
apiKey = httpmw.APIKey(r)
7778
auditor = api.Auditor.Load()
78-
member = httpmw.OrganizationMemberParam(r)
79+
mems = httpmw.OrganizationMembersParam(r)
7980
)
8081

81-
var req codersdk.CreateAITasksRequest
82+
var req codersdk.CreateTaskRequest
8283
if !httpapi.Read(ctx, rw, r, &req) {
8384
return
8485
}
@@ -112,10 +113,49 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
112113
},
113114
}
114115

115-
owner := workspaceOwner{
116-
ID: member.UserID,
117-
Username: member.Username,
118-
AvatarURL: member.AvatarURL,
116+
var owner workspaceOwner
117+
if mems.User != nil {
118+
// This user fetch is an optimization path for the most common case of creating a
119+
// task for 'Me'.
120+
//
121+
// This is also required to allow `owners` to create workspaces for users
122+
// that are not in an organization.
123+
owner = workspaceOwner{
124+
ID: mems.User.ID,
125+
Username: mems.User.Username,
126+
AvatarURL: mems.User.AvatarURL,
127+
}
128+
} else {
129+
// A task can still be created if the caller can read the organization
130+
// member. The organization is required, which can be sourced from the
131+
// template.
132+
//
133+
// TODO: This code gets called twice for each workspace build request.
134+
// This is inefficient and costs at most 2 extra RTTs to the DB.
135+
// This can be optimized. It exists as it is now for code simplicity.
136+
// The most common case is to create a workspace for 'Me'. Which does
137+
// not enter this code branch.
138+
template, ok := requestTemplate(ctx, rw, createReq, api.Database)
139+
if !ok {
140+
return
141+
}
142+
143+
// If the caller can find the organization membership in the same org
144+
// as the template, then they can continue.
145+
orgIndex := slices.IndexFunc(mems.Memberships, func(mem httpmw.OrganizationMember) bool {
146+
return mem.OrganizationID == template.OrganizationID
147+
})
148+
if orgIndex == -1 {
149+
httpapi.ResourceNotFound(rw)
150+
return
151+
}
152+
153+
member := mems.Memberships[orgIndex]
154+
owner = workspaceOwner{
155+
ID: member.UserID,
156+
Username: member.Username,
157+
AvatarURL: member.AvatarURL,
158+
}
119159
}
120160

121161
aReq, commitAudit := audit.InitRequest[database.WorkspaceTable](rw, &audit.RequestParams{

coderd/aitasks_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func TestAITasksPrompts(t *testing.T) {
142142
})
143143
}
144144

145-
func TestAITasksCreate(t *testing.T) {
145+
func TestTaskCreate(t *testing.T) {
146146
t.Parallel()
147147

148148
t.Run("OK", func(t *testing.T) {
@@ -175,7 +175,7 @@ func TestAITasksCreate(t *testing.T) {
175175
expClient := codersdk.NewExperimentalClient(client)
176176

177177
// When: We attempt to create a Task.
178-
workspace, err := expClient.AITasksCreate(ctx, codersdk.CreateAITasksRequest{
178+
workspace, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{
179179
Name: taskName,
180180
TemplateVersionID: template.ActiveVersionID,
181181
Prompt: taskPrompt,
@@ -216,7 +216,7 @@ func TestAITasksCreate(t *testing.T) {
216216
expClient := codersdk.NewExperimentalClient(client)
217217

218218
// When: We attempt to create a Task.
219-
_, err := expClient.AITasksCreate(ctx, codersdk.CreateAITasksRequest{
219+
_, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{
220220
Name: taskName,
221221
TemplateVersionID: template.ActiveVersionID,
222222
Prompt: taskPrompt,
@@ -250,7 +250,7 @@ func TestAITasksCreate(t *testing.T) {
250250
expClient := codersdk.NewExperimentalClient(client)
251251

252252
// When: We attempt to create a Task with an invalid template version ID.
253-
_, err := expClient.AITasksCreate(ctx, codersdk.CreateAITasksRequest{
253+
_, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{
254254
Name: taskName,
255255
TemplateVersionID: uuid.New(),
256256
Prompt: taskPrompt,

coderd/coderd.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,11 @@ func New(options *Options) *API {
998998
r.Route("/tasks", func(r chi.Router) {
999999
r.Use(apiRateLimiter)
10001000

1001-
r.Post("/{user}", api.tasksCreate)
1001+
r.Route("/{user}", func(r chi.Router) {
1002+
r.Use(httpmw.ExtractOrganizationMembersParam(options.Database, api.HTTPAuth.Authorize))
1003+
1004+
r.Post("/", api.tasksCreate)
1005+
})
10021006
})
10031007
r.Route("/mcp", func(r chi.Router) {
10041008
r.Use(

codersdk/aitasks.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package codersdk
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"net/http"
78
"strings"
89

@@ -45,15 +46,15 @@ func (c *ExperimentalClient) AITaskPrompts(ctx context.Context, buildIDs []uuid.
4546
return prompts, json.NewDecoder(res.Body).Decode(&prompts)
4647
}
4748

48-
type CreateAITasksRequest struct {
49+
type CreateTaskRequest struct {
4950
Name string `json:"name"`
5051
TemplateVersionID uuid.UUID `json:"template_version_id" format:"uuid"`
5152
TemplateVersionPresetID uuid.UUID `json:"template_version_preset_id,omitempty" format:"uuid"`
5253
Prompt string `json:"prompt"`
5354
}
5455

55-
func (c *ExperimentalClient) AITasksCreate(ctx context.Context, request CreateAITasksRequest) (Workspace, error) {
56-
res, err := c.Request(ctx, http.MethodPost, "/api/experimental/aitasks", request)
56+
func (c *ExperimentalClient) CreateTask(ctx context.Context, user string, request CreateTaskRequest) (Workspace, error) {
57+
res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/experimental/tasks/%s", user), request)
5758
if err != nil {
5859
return Workspace{}, err
5960
}

site/src/api/api.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,11 +2666,12 @@ class ExperimentalApiMethods {
26662666
return response.data;
26672667
};
26682668

2669-
createAITask = async (
2670-
req: TypesGen.CreateAITasksRequest,
2669+
createTask = async (
2670+
user: string,
2671+
req: TypesGen.CreateTaskRequest,
26712672
): Promise<TypesGen.Workspace> => {
26722673
const response = await this.axios.post<TypesGen.Workspace>(
2673-
"/api/experimental/aitasks",
2674+
`/api/experimental/tasks/${user}`,
26742675
req,
26752676
);
26762677

site/src/api/typesGenerated.ts

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

site/src/pages/TasksPage/TasksPage.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ type TaskFormProps = {
232232
};
233233

234234
const TaskForm: FC<TaskFormProps> = ({ templates, onSuccess }) => {
235+
const { user } = useAuthenticated();
235236
const queryClient = useQueryClient();
236237
const [selectedTemplateId, setSelectedTemplateId] = useState<string>(
237238
templates[0].id,
@@ -292,7 +293,7 @@ const TaskForm: FC<TaskFormProps> = ({ templates, onSuccess }) => {
292293
templateVersionId,
293294
presetId,
294295
}: CreateTaskMutationFnProps) =>
295-
data.createTask(prompt, templateVersionId, presetId),
296+
data.createTask(prompt, user.id, templateVersionId, presetId),
296297
onSuccess: async (task) => {
297298
await queryClient.invalidateQueries({
298299
queryKey: ["tasks"],
@@ -726,6 +727,7 @@ export const data = {
726727

727728
async createTask(
728729
prompt: string,
730+
userId: string,
729731
templateVersionId: string,
730732
presetId: string | null = null,
731733
): Promise<Task> {
@@ -739,7 +741,7 @@ export const data = {
739741
}
740742
}
741743

742-
const workspace = await API.experimental.createAITask({
744+
const workspace = await API.experimental.createTask(userId, {
743745
name: `task-${generateWorkspaceName()}`,
744746
template_version_id: templateVersionId,
745747
template_version_preset_id: preset_id || undefined,

0 commit comments

Comments
 (0)