-
Notifications
You must be signed in to change notification settings - Fork 961
refactor: create tasks in coderd instead of frontend #19280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Instead of creating tasks with a specialised call to `CreateWorkspace` on the frontend, we instead lift this to the backend and allow the frontend to simply call `CreateAITask`.
@coderabbitai review |
✅ Actions performedReview triggered.
|
coderd/aitasks.go
Outdated
hasAIPrompt, err := api.Database.GetTemplateVersionHasAIPrompt(ctx, req.TemplateVersionID) | ||
if err != nil { | ||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ | ||
Message: "Internal error fetching if template version has ai prompt.", | ||
Detail: err.Error(), | ||
}) | ||
return | ||
} | ||
if !hasAIPrompt { | ||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ | ||
Message: `Template does not have required parameter "AI Prompt"`, | ||
}) | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we're moving creation of tasks to the backend, I think it makes sense to validate the given template version ID actually corresponds to a task.
📝 WalkthroughWalkthroughIntroduces an experimental POST /api/experimental/aitasks endpoint to create AI tasks by embedding a prompt into a workspace creation request. Adds SDK and frontend support, database query and authorization checks for AI-prompt capability on template versions, metrics and mocks, routing, and tests. Changes
Sequence Diagram(s)sequenceDiagram
participant Web as Frontend (TasksPage)
participant SDK as SDK ExperimentalClient
participant API as Server /api/experimental/aitasks
participant MW as API Key Middleware
participant DB as DB + dbauthz
participant AUD as Audit
participant WS as Workspace Creator
Web->>SDK: AITasksCreate(request)
SDK->>API: POST /api/experimental/aitasks
API->>MW: Authenticate API key
MW-->>API: User context
API->>DB: Get user by API key
API->>DB: GetTemplateVersionHasAIPrompt(tvID)
DB-->>API: bool
API->>AUD: audit.InitRequest(WorkspaceCreate)
API->>WS: createWorkspace(payload with prompt)
WS-->>API: Workspace
API->>AUD: commit
API-->>SDK: 201 Workspace
SDK-->>Web: Workspace
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Suggested reviewers
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (9)
coderd/database/queries/templateversions.sql (1)
238-238
: Rename to align with column and existing query names ("AITask" vs "AIPrompt").The column is has_ai_task and related queries use AITask (e.g., UpdateTemplateVersionAITaskByJobID). Rename this query to avoid mixed terminology.
Apply within this file, then regenerate sqlc:
- -- name: GetTemplateVersionHasAIPrompt :one + -- name: GetTemplateVersionHasAITask :onesite/src/api/api.ts (1)
2669-2678
: Allow cancellation (AbortSignal) for better UX on long/aborted submissions.Add optional AbortSignal support so callers can cancel in-flight requests (e.g., route changes).
- createAITask = async ( - req: TypesGen.CreateAITasksRequest, - ): Promise<TypesGen.Workspace> => { - const response = await this.axios.post<TypesGen.Workspace>( - "/api/experimental/aitasks", - req, - ); - - return response.data; - }; + createAITask = async ( + req: TypesGen.CreateAITasksRequest, + signal?: AbortSignal, + ): Promise<TypesGen.Workspace> => { + const response = await this.axios.post<TypesGen.Workspace>( + "/api/experimental/aitasks", + req, + { signal }, + ); + return response.data; + };Nit: consider naming the method createAITasks to mirror the plural route/type. Only do this if you can update call sites in the same PR to avoid breakage.
coderd/database/querier.go (1)
358-358
: Naming consistency: prefer “HasAITask” to match schema and existing queries.Interface name stems from SQL. If you adopt the suggested SQL rename (GetTemplateVersionHasAITask), regenerate sqlc so this becomes:
- GetTemplateVersionHasAITask(ctx context.Context, id uuid.UUID) (bool, error)
Do not hand-edit this file; re-generate from the .sql change.
coderd/database/dbmetrics/querymetrics.go (1)
1541-1546
: Align wrapper and metric label with “AITask” naming if SQL is renamed.If you rename the SQL to GetTemplateVersionHasAITask, update this wrapper and label accordingly.
-func (m queryMetricsStore) GetTemplateVersionHasAIPrompt(ctx context.Context, id uuid.UUID) (bool, error) { +func (m queryMetricsStore) GetTemplateVersionHasAITask(ctx context.Context, id uuid.UUID) (bool, error) { start := time.Now() - r0, r1 := m.s.GetTemplateVersionHasAIPrompt(ctx, id) - m.queryLatencies.WithLabelValues("GetTemplateVersionHasAIPrompt").Observe(time.Since(start).Seconds()) + r0, r1 := m.s.GetTemplateVersionHasAITask(ctx, id) + m.queryLatencies.WithLabelValues("GetTemplateVersionHasAITask").Observe(time.Since(start).Seconds()) return r0, r1 }coderd/database/dbauthz/dbauthz_test.go (1)
1446-1459
: RBAC assertion for TemplateVersion AI-prompt check looks correctThis follows the established pattern where TemplateVersion reads authorize on the parent Template via RBACObject. No issues.
Optional:
- Add a brief comment clarifying the test intent (TemplateVersion read → Template read) to improve readability.
- Consider a negative-path variant (e.g., WithNotAuthorized) similar to other tests where applicable.
coderd/database/queries.sql.go (1)
12873-12887
: Nit: align naming with underlying column (has_ai_task) to avoid confusionThe method/SQL name uses “AIPrompt” while the column is has_ai_task. Since this file is generated, consider renaming the query in the source SQL to keep terminology consistent (e.g., “GetTemplateVersionHasAITask”). sqlc will then regenerate matching Go.
Example in coderd/database/queries/templateversions.sql:
-- name: GetTemplateVersionHasAITask :one SELECT EXISTS ( SELECT 1 FROM template_versions WHERE id = $1 AND has_ai_task );coderd/aitasks.go (1)
95-102
: Clarify error message wording (nit)Small copy nit: “whether template version has AI Prompt” reads clearer than “if template version has ai prompt.”
- Message: "Internal error fetching if template version has ai prompt.", + Message: "Internal error checking whether the template version has AI Prompt.",codersdk/aitasks.go (1)
56-64
: Drop trailing slash in POST path for consistencyStandardize the path to avoid relying on router slash normalization.
- res, err := c.Request(ctx, http.MethodPost, "/api/experimental/aitasks/", request) + res, err := c.Request(ctx, http.MethodPost, "/api/experimental/aitasks", request)site/src/pages/TasksPage/TasksPage.tsx (1)
742-747
: Use conditional property for preset_id (minor nit)Avoid sending undefined fields by conditionally including template_version_preset_id. Keeps payload minimal and avoids any strict decoder issues.
- const workspace = await API.experimental.createAITask({ - name: `task-${generateWorkspaceName()}`, - template_version_id: templateVersionId, - template_version_preset_id: preset_id || undefined, - prompt, - }); + const req = { + name: `task-${generateWorkspaceName()}`, + template_version_id: templateVersionId, + prompt, + ...(preset_id ? { template_version_preset_id: preset_id } : {}), + }; + const workspace = await API.experimental.createAITask(req);
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
coderd/aitasks.go
(2 hunks)coderd/aitasks_test.go
(2 hunks)coderd/coderd.go
(1 hunks)coderd/database/dbauthz/dbauthz.go
(1 hunks)coderd/database/dbauthz/dbauthz_test.go
(1 hunks)coderd/database/dbmetrics/querymetrics.go
(1 hunks)coderd/database/dbmock/dbmock.go
(1 hunks)coderd/database/querier.go
(1 hunks)coderd/database/queries.sql.go
(1 hunks)coderd/database/queries/templateversions.sql
(1 hunks)codersdk/aitasks.go
(1 hunks)site/src/api/api.ts
(1 hunks)site/src/api/typesGenerated.ts
(1 hunks)site/src/pages/TasksPage/TasksPage.tsx
(2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-07-21T14:32:43.064Z
Learnt from: CR
PR: coder/coder#0
File: .cursorrules:0-0
Timestamp: 2025-07-21T14:32:43.064Z
Learning: Applies to coderd/coderd.go : The REST API is defined in `coderd/coderd.go` and uses Chi for HTTP routing.
Applied to files:
coderd/coderd.go
coderd/aitasks_test.go
📚 Learning: 2025-07-21T14:32:43.064Z
Learnt from: CR
PR: coder/coder#0
File: .cursorrules:0-0
Timestamp: 2025-07-21T14:32:43.064Z
Learning: Applies to coderd/coderdtest/**/* : The `coderdtest` package in `coderd/coderdtest/` provides utilities for creating test instances of the Coder server, setting up test users and workspaces, and mocking external components.
Applied to files:
coderd/aitasks_test.go
📚 Learning: 2025-07-21T14:32:43.064Z
Learnt from: CR
PR: coder/coder#0
File: .cursorrules:0-0
Timestamp: 2025-07-21T14:32:43.064Z
Learning: Applies to coderdenttest/**/* : Enterprise features have dedicated test utilities in the `coderdenttest` package.
Applied to files:
coderd/aitasks_test.go
📚 Learning: 2025-07-21T14:32:43.064Z
Learnt from: CR
PR: coder/coder#0
File: .cursorrules:0-0
Timestamp: 2025-07-21T14:32:43.064Z
Learning: Applies to coderd/dbauthz/*.go : The database authorization (dbauthz) system enforces fine-grained access control across all database operations. All database operations must pass through this layer to ensure security.
Applied to files:
coderd/database/dbauthz/dbauthz.go
🧬 Code Graph Analysis (10)
site/src/api/typesGenerated.ts (1)
codersdk/aitasks.go (1)
CreateAITasksRequest
(48-53)
coderd/database/querier.go (1)
testutil/ctx.go (1)
Context
(9-13)
coderd/database/dbmetrics/querymetrics.go (2)
testutil/ctx.go (1)
Context
(9-13)coderd/database/dbtime/dbtime.go (1)
Now
(6-8)
site/src/api/api.ts (2)
codersdk/aitasks.go (1)
CreateAITasksRequest
(48-53)site/src/api/typesGenerated.ts (2)
CreateAITasksRequest
(426-431)Workspace
(3489-3521)
codersdk/aitasks.go (3)
codersdk/client_experimental.go (1)
ExperimentalClient
(6-8)site/src/api/typesGenerated.ts (2)
CreateAITasksRequest
(426-431)Workspace
(3489-3521)codersdk/client.go (1)
ReadBodyAsError
(391-465)
coderd/aitasks_test.go (8)
testutil/ctx.go (1)
Context
(9-13)testutil/duration.go (1)
WaitShort
(10-10)coderd/coderdtest/coderdtest.go (3)
CreateFirstUser
(743-754)AwaitTemplateVersionJobCompleted
(1065-1081)AwaitWorkspaceBuildJobCompleted
(1084-1099)provisioner/echo/serve.go (1)
Responses
(227-241)site/src/api/typesGenerated.ts (2)
Response
(2563-2567)CreateAITasksRequest
(426-431)codersdk/client_experimental.go (1)
NewExperimentalClient
(10-14)codersdk/aitasks.go (1)
CreateAITasksRequest
(48-53)codersdk/client.go (1)
Error
(469-477)
coderd/database/dbauthz/dbauthz_test.go (4)
coderd/database/models.go (4)
Organization
(3299-3309)User
(3760-3785)Template
(3509-3546)TemplateVersion
(3620-3638)coderd/database/modelmethods.go (1)
TemplateVersion
(180-183)coderd/rbac/policy/policy.go (1)
ActionRead
(10-10)codersdk/rbacresources_gen.go (1)
ActionRead
(57-57)
coderd/aitasks.go (6)
site/src/api/typesGenerated.ts (5)
APIKey
(18-29)CreateAITasksRequest
(426-431)Response
(2563-2567)CreateWorkspaceRequest
(598-607)WorkspaceBuildParameter
(3880-3883)codersdk/aitasks.go (1)
CreateAITasksRequest
(48-53)coderd/httpapi/httpapi.go (3)
Is404Error
(121-131)ResourceNotFound
(153-155)Write
(191-212)coderd/audit/request.go (2)
InitRequest
(350-446)RequestParams
(26-36)coderd/database/models.go (1)
WorkspaceTable
(4275-4295)coderd/audit/audit.go (1)
AdditionalFields
(19-25)
coderd/database/dbmock/dbmock.go (1)
testutil/ctx.go (1)
Context
(9-13)
site/src/pages/TasksPage/TasksPage.tsx (2)
site/src/pages/TaskPage/TaskPage.tsx (1)
data
(206-231)coderd/coderd.go (1)
API
(1636-1718)
🔇 Additional comments (10)
site/src/api/typesGenerated.ts (2)
425-432
: CreateAITasksRequest matches server schema (LGTM).Fields and optionality align with codersdk/aitasks.go (UUIDs as strings, preset_id optional). Generated location and naming look consistent with existing request types.
425-432
: createAITask payloads include required fields and use snake_caseI’ve verified the lone caller of
API.experimental.createAITask
in site/src/pages/TasksPage/TasksPage.tsx (line 742) supplies:
name
(required)template_version_id
(required)prompt
(required)template_version_preset_id
(optional)All keys are snake_case. No changes needed.
coderd/database/queries.sql.go (2)
12873-12887
: LGTM: correct EXISTS query and bool mappingThe query and sqlc mapping to a boolean look correct and efficient. Returning false when the row is missing or has_ai_task is false is appropriate, especially given prior authorization/version lookups upstream.
12873-12887
: Confirm whether archived template versions should be excludedIf the intent is to allow AI tasks only on active (non-archived) versions, consider adding AND archived = false. If this is enforced elsewhere (e.g., creating a workspace is already blocked for archived versions), feel free to ignore.
Proposed SQL (in the source .sql file):
SELECT EXISTS ( SELECT 1 FROM template_versions WHERE id = $1 AND has_ai_task AND archived = false );coderd/aitasks_test.go (1)
198-230
: Good negative-path coverage (400 on non-task template)The check for HTTP 400 and error type is solid. This guards the server-side validation as intended.
coderd/database/dbmock/dbmock.go (1)
3274-3287
: Mock addition LGTMMethod and recorder follow gomock conventions and match the interface signature.
coderd/aitasks.go (1)
20-66
: Verify RBAC for prompts retrieval to prevent data leakageThe prompts endpoint fetches build parameters by IDs. Ensure the underlying store enforces authorization for the caller (e.g., via dbauthz wrapper or the authorized query variant) so members cannot see prompts of builds they don’t have access to. The MultipleBuilds test expects this.
If RBAC is not guaranteed by the wrapper, switch to the authorized query (GetAuthorizedWorkspaceBuildParametersByBuildIDs) with the appropriate prepared authorization from the request context.
codersdk/aitasks.go (1)
48-53
: SDK request and handler flow looks goodRequest shape matches server expectations, status check and decode logic are correct.
Also applies to: 55-72
site/src/pages/TasksPage/TasksPage.tsx (1)
295-295
: No remaining createTask call sites to update
Verified thatcreateTask(
only appears inTasksPage.tsx
(lines 295 & 727) and that workspace APIs are untouched. No stale or legacy calls found—changes are good to go.coderd/database/dbauthz/dbauthz.go (1)
2877-2886
: Authz wrapper correctly gates AI-prompt presence checkThis follows the established dbauthz pattern: authorize via GetTemplateVersionByID, then query the store. Consistent with the requirement that all DB operations flow through dbauthz.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FE code looks good to me.
coderd/aitasks.go
Outdated
hasAITask, err := api.Database.GetTemplateVersionHasAITask(ctx, req.TemplateVersionID) | ||
if err != nil { | ||
if errors.Is(err, sql.ErrNoRows) || rbac.IsUnauthorizedError(err) { | ||
httpapi.ResourceNotFound(rw) | ||
return | ||
} | ||
|
||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ | ||
Message: "Internal error fetching whether the template version has an AI task.", | ||
Detail: err.Error(), | ||
}) | ||
return | ||
} | ||
if !hasAITask { | ||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ | ||
Message: `Template does not have required parameter "` + codersdk.AITaskPromptParameterName + `"`, | ||
}) | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: This might be better moved inside wsbuilder
as it runs inside a tx and has access to the parameters.
Instead of creating tasks with a specialised call to
CreateWorkspace
on the frontend, we instead lift this to the backend and allow the frontend to simply callCreateAITask
.