Skip to content

chore: add job agent selector field to oapi schemas#929

Merged
adityachoudhari26 merged 2 commits intomainfrom
add-job-agent-selector
Apr 7, 2026
Merged

chore: add job agent selector field to oapi schemas#929
adityachoudhari26 merged 2 commits intomainfrom
add-job-agent-selector

Conversation

@adityachoudhari26
Copy link
Copy Markdown
Member

@adityachoudhari26 adityachoudhari26 commented Apr 7, 2026

Resolves #916

Summary by CodeRabbit

  • New Features
    • Deployments support a new optional jobAgentSelector (CEL expression) to flexibly select job agents; when jobAgentId is provided and no selector sent, the selector defaults to matching that ID.
    • Added optional order (asc|desc) for listing deployment versions.
  • Bug Fixes / Validation
    • API now rejects non-UUID jobAgentId values.
  • Tests
    • End-to-end tests added for jobAgentSelector behavior and upserts.

Copilot AI review requested due to automatic review settings April 7, 2026 21:22
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3f64bd4f-8270-4ac0-8bf7-75b1703d513b

📥 Commits

Reviewing files that changed from the base of the PR and between 49dddc8 and 44a0dc2.

📒 Files selected for processing (1)
  • apps/api/src/routes/v1/workspaces/deployments.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/routes/v1/workspaces/deployments.ts

📝 Walkthrough

Walkthrough

Added an optional jobAgentSelector (CEL expression string) to deployment schemas and types; updated API handlers to accept, validate, default (from jobAgentId) and persist jobAgentSelector and jobAgentConfig; added e2e tests and regenerated OpenAPI artifacts.

Changes

Cohort / File(s) Summary
OpenAPI schema files
apps/api/openapi/openapi.json, apps/api/openapi/schemas/deployments.jsonnet, apps/workspace-engine/oapi/openapi.json, apps/workspace-engine/oapi/spec/schemas/deployments.jsonnet
Added optional jobAgentSelector: string property to CreateDeploymentRequest, UpsertDeploymentRequest, and Deployment schemas (CEL expression). Some request schemas document defaulting to jobAgent.id == "<jobAgentId>" when jobAgentSelector is omitted.
Type definitions / frontend API
apps/api/src/types/openapi.ts, apps/web/app/api/openapi.ts, e2e/api/schema.ts
Added optional jobAgentSelector?: string to CreateDeploymentRequest, Deployment, and UpsertDeploymentRequest types; frontend OpenAPI typings updated. Also added `order?: "asc"
API handlers
apps/api/src/routes/v1/workspaces/deployments.ts
Validated jobAgentId as UUID when present; set deployment.jobAgentSelector from request or derived from jobAgentId (or "false" when absent); updated formatDeployment to expose parsed jobAgentSelector and jobAgentConfig; added conditional upsert logic to write selector/config only when appropriate.
Workspace engine models
apps/workspace-engine/pkg/oapi/oapi.gen.go
Added JobAgentSelector *string to generated Deployment model (JSON tag jobAgentSelector,omitempty).
E2E tests & scripts
e2e/tests/api/deployments.spec.ts, e2e/package.json
Added tests covering explicit jobAgentSelector, defaulting from jobAgentId, and upsert replacement behavior; updated generate script OpenAPI source path.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(220,240,255,0.5)
    participant Client
  end
  rect rgba(200,255,220,0.5)
    participant API
  end
  rect rgba(255,230,200,0.5)
    participant DB
  end
  Client->>API: POST /workspaces/:id/deployments (body with jobAgentId or jobAgentSelector)
  API->>API: validate body (UUID for jobAgentId), derive selector if needed
  API->>DB: INSERT/UPSERT deployment (jobAgentSelector, jobAgentConfig conditional)
  DB-->>API: persist result
  API->>API: formatDeployment (parseSelector -> expose jobAgentSelector/jobAgentConfig)
  API-->>Client: 201 Created (deployment with jobAgentSelector)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • zacharyblasczyk
  • mleonidas

Poem

🐰 I stitched a CEL string, soft and clever,
To match the agents now and ever,
From id it folds a friendly guide,
Deployments hop with selector pride,
Hooray — the jobs find home together! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes an out-of-scope change: adding an 'order' query parameter to listDeploymentVersions in apps/web/app/api/openapi.ts, which is unrelated to the jobAgentSelector schema update. Remove the 'order' query parameter addition from listDeploymentVersions as it falls outside the scope of adding jobAgentSelector fields to deployment schemas.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a jobAgentSelector field to OpenAPI schemas across multiple packages.
Linked Issues check ✅ Passed The PR implements the linked issue #916 requirement to add an optional selector field to deployment CRUD schemas, with jobAgentSelector added to CreateDeploymentRequest, Deployment, and UpsertDeploymentRequest.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-job-agent-selector

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for a deployment-level jobAgentSelector (CEL expression) across OpenAPI schemas/types and validates the behavior via new E2E coverage. This aligns deployment creation/upsert with the newer “selector-driven job agent selection” model.

Changes:

  • Add jobAgentSelector to deployment schemas in API + workspace-engine OpenAPI sources and regenerated artifacts (TS + Go).
  • Update API deployment routes to persist/return jobAgentSelector and ensure jobAgentConfig is always present in responses.
  • Add E2E tests covering custom selector, defaulting selector from jobAgentId, and selector updates via upsert; update e2e OpenAPI generation input path.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
e2e/tests/api/deployments.spec.ts Adds E2E coverage for create/upsert/get behavior around jobAgentSelector.
e2e/package.json Points openapi-typescript generation at apps/api/openapi/openapi.json.
e2e/api/schema.ts Regenerated E2E OpenAPI TS types including jobAgentSelector.
apps/workspace-engine/pkg/oapi/oapi.gen.go Regenerated Go types to include jobAgentSelector on Deployment.
apps/workspace-engine/oapi/spec/schemas/deployments.jsonnet Adds jobAgentSelector to workspace-engine deployment schema source.
apps/workspace-engine/oapi/openapi.json Regenerated workspace-engine OpenAPI JSON including jobAgentSelector.
apps/web/app/api/openapi.ts Regenerated web OpenAPI TS types including jobAgentSelector (and order query type).
apps/api/src/types/openapi.ts Regenerated API OpenAPI TS types including jobAgentSelector.
apps/api/src/routes/v1/workspaces/deployments.ts Persists and returns jobAgentSelector; ensures jobAgentConfig defaults to {} in responses.
apps/api/openapi/schemas/deployments.jsonnet Adds jobAgentSelector to API deployment schema source with descriptions.
apps/api/openapi/openapi.json Regenerated API OpenAPI JSON including jobAgentSelector.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 231 to 248
const isValid = validResourceSelector(body.resourceSelector);
if (!isValid) throw new ApiError("Invalid resource selector", 400);

const id = uuidv4();

const jobAgentId = body.jobAgentId ?? body.jobAgents?.[0]?.ref;
const jobAgentConfig =
body.jobAgentConfig ?? body.jobAgents?.[0]?.config ?? {};

await db.insert(schema.deployment).values({
id,
name: body.name,
description: body.description ?? "",
resourceSelector: body.resourceSelector ?? "false",
jobAgentSelector: jobAgentId ? `jobAgent.id == "${jobAgentId}"` : "false",
jobAgentSelector:
body.jobAgentSelector ??
(jobAgentId ? `jobAgent.id == "${jobAgentId}"` : "false"),
jobAgentConfig,
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jobAgentSelector is accepted and persisted without any CEL validation, unlike resourceSelector. This can allow invalid expressions to be stored and later fail during evaluation. Consider validating body.jobAgentSelector (and possibly body.jobAgents[].selector) with the existing CEL parser (validResourceSelector or a renamed generalized helper) and returning 400 on parse failure with a clear error message.

Copilot uses AI. Check for mistakes.
Comment on lines +315 to +322
...(body.jobAgentSelector != null
? { jobAgentSelector: body.jobAgentSelector, jobAgentConfig }
: jobAgentId != null
? {
jobAgentSelector: `jobAgent.id == "${jobAgentId}"`,
jobAgentConfig,
}
: {}),
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the upsert conflict update branch, when body.jobAgentSelector is provided but jobAgentConfig is omitted, jobAgentConfig is computed as {} and will overwrite any existing config. If clients are allowed to update the selector independently, consider only updating jobAgentConfig when it is explicitly provided (or when jobAgents is provided), to avoid clobbering existing configuration.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
e2e/tests/api/deployments.spec.ts (1)

315-329: Optionally assert the second upsert response status for clearer failures.

You already validate the final state via GET; adding a direct status check here improves diagnosability when upsert fails.

Suggested test refinement
-    await api.PUT(
+    const updateRes = await api.PUT(
       "/v1/workspaces/{workspaceId}/deployments/{deploymentId}",
       {
         params: {
           path: { workspaceId: workspace.id, deploymentId },
         },
         body: {
           name,
           slug: name,
           jobAgentSelector: updatedSelector,
           jobAgentConfig: { env: "production" },
         },
       },
     );
+    expect(updateRes.response.status).toBe(202);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/api/deployments.spec.ts` around lines 315 - 329, Capture the
response returned by the second api.PUT call to update the deployment and assert
its HTTP status (e.g., status === 200) before proceeding to the GET;
specifically, replace the fire-and-forget api.PUT invocation around the block
that passes params.path { workspaceId, deploymentId } and body { name, slug,
jobAgentSelector: updatedSelector, jobAgentConfig } with a stored response
(e.g., const res = await api.PUT(...)) and add an assertion that res.status
equals the expected success code to make failures more diagnosable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/routes/v1/workspaces/deployments.ts`:
- Around line 245-247: The CEL selector currently injects jobAgentId directly
into the string via jobAgentSelector which allows quotes/backslashes to break
the expression; add a helper (e.g., escapeCelString or similar) that escapes
backslashes and double quotes (replace \ with \\ and " with \") and use it
wherever jobAgentId is interpolated (the jobAgentSelector assignment at
jobAgentSelector, and the other occurrences around lines 301-303 and 319-320) so
the constructed CEL expression is always a safely-quoted string.

---

Nitpick comments:
In `@e2e/tests/api/deployments.spec.ts`:
- Around line 315-329: Capture the response returned by the second api.PUT call
to update the deployment and assert its HTTP status (e.g., status === 200)
before proceeding to the GET; specifically, replace the fire-and-forget api.PUT
invocation around the block that passes params.path { workspaceId, deploymentId
} and body { name, slug, jobAgentSelector: updatedSelector, jobAgentConfig }
with a stored response (e.g., const res = await api.PUT(...)) and add an
assertion that res.status equals the expected success code to make failures more
diagnosable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8a4d6c0b-8890-47a6-b52c-e26741b3a744

📥 Commits

Reviewing files that changed from the base of the PR and between f75e3c4 and 49dddc8.

📒 Files selected for processing (11)
  • apps/api/openapi/openapi.json
  • apps/api/openapi/schemas/deployments.jsonnet
  • apps/api/src/routes/v1/workspaces/deployments.ts
  • apps/api/src/types/openapi.ts
  • apps/web/app/api/openapi.ts
  • apps/workspace-engine/oapi/openapi.json
  • apps/workspace-engine/oapi/spec/schemas/deployments.jsonnet
  • apps/workspace-engine/pkg/oapi/oapi.gen.go
  • e2e/api/schema.ts
  • e2e/package.json
  • e2e/tests/api/deployments.spec.ts

Comment on lines +245 to +247
jobAgentSelector:
body.jobAgentSelector ??
(jobAgentId ? `jobAgent.id == "${jobAgentId}"` : "false"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Escape jobAgentId before embedding it into CEL.

The derived selector is built with raw string interpolation, so IDs containing quotes/backslashes can break or alter the CEL expression.

🔧 Proposed fix
 const parseSelector = (raw: string | null | undefined): string | undefined => {
   if (raw == null || raw === "false") return undefined;
   return raw;
 };
+
+const deriveJobAgentSelector = (
+  explicitSelector: string | null | undefined,
+  jobAgentId: string | null | undefined,
+): string =>
+  explicitSelector ??
+  (jobAgentId != null ? `jobAgent.id == ${JSON.stringify(jobAgentId)}` : "false");

 ...
   await db.insert(schema.deployment).values({
     id,
     name: body.name,
     description: body.description ?? "",
     resourceSelector: body.resourceSelector ?? "false",
-    jobAgentSelector:
-      body.jobAgentSelector ??
-      (jobAgentId ? `jobAgent.id == "${jobAgentId}"` : "false"),
+    jobAgentSelector: deriveJobAgentSelector(body.jobAgentSelector, jobAgentId),
     jobAgentConfig,
     metadata: body.metadata ?? {},
     workspaceId,
   });

 ...
     .values({
       id: deploymentId,
       name: body.name,
       description: body.description ?? "",
       resourceSelector: body.resourceSelector ?? "false",
-      jobAgentSelector:
-        body.jobAgentSelector ??
-        (jobAgentId ? `jobAgent.id == "${jobAgentId}"` : "false"),
+      jobAgentSelector: deriveJobAgentSelector(body.jobAgentSelector, jobAgentId),
       jobAgentConfig,
       metadata: body.metadata ?? {},
       workspaceId,
     })
 ...
         ...(body.jobAgentSelector != null
           ? { jobAgentSelector: body.jobAgentSelector, jobAgentConfig }
           : jobAgentId != null
             ? {
-                jobAgentSelector: `jobAgent.id == "${jobAgentId}"`,
+                jobAgentSelector: `jobAgent.id == ${JSON.stringify(jobAgentId)}`,
                 jobAgentConfig,
               }
             : {}),

Also applies to: 301-303, 319-320

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/routes/v1/workspaces/deployments.ts` around lines 245 - 247, The
CEL selector currently injects jobAgentId directly into the string via
jobAgentSelector which allows quotes/backslashes to break the expression; add a
helper (e.g., escapeCelString or similar) that escapes backslashes and double
quotes (replace \ with \\ and " with \") and use it wherever jobAgentId is
interpolated (the jobAgentSelector assignment at jobAgentSelector, and the other
occurrences around lines 301-303 and 319-320) so the constructed CEL expression
is always a safely-quoted string.

@adityachoudhari26 adityachoudhari26 merged commit b9bc325 into main Apr 7, 2026
12 checks passed
@adityachoudhari26 adityachoudhari26 deleted the add-job-agent-selector branch April 7, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: update deployment CRUD schemas to include optional selector field

2 participants