Skip to content

Conversation

@zacharyblasczyk
Copy link
Member

@zacharyblasczyk zacharyblasczyk commented Mar 16, 2025

Summary by CodeRabbit

  • New Features
    • Introduced a new API endpoint that allows users to update existing deployments.
    • Enhanced the update process with clear, user-facing responses for successful updates, missing deployments, and update failures.
    • Implemented permission checks to ensure secure and authorized updates.
    • Added new properties to the deployment schema for improved configuration options.
    • Introduced a new schema for flexible update payloads.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 16, 2025

Walkthrough

This pull request introduces a new PATCH operation to update existing deployments. The changes include updating the OpenAPI specifications (both in TypeScript and JSON formats) to define the new endpoint and its associated schemas, parameters, and responses. Additionally, a corresponding PATCH route handler has been implemented with authentication, authorization, and error handling to process update requests for deployments.

Changes

File(s) Change Summary
apps/webservice/.../openapi.ts
openapi.v1.json
Added a new PATCH operation for updating deployments with operation ID updateDeployment. Defined path parameter (deploymentId as UUID), request body referencing UpdateDeployment schema, and responses for 200, 404, and 500 HTTP statuses. New properties added to the Deployment schema: retryCount, timeout.
apps/webservice/.../route.ts Implemented the PATCH method handler for deployments. It retrieves the deploymentId, applies authentication and authorization (checking for DeploymentUpdate), updates the deployment if it exists, and handles errors appropriately.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant R as Route Handler
    participant DB as Database
    C->>R: PATCH /deployments/{deploymentId} (with update data)
    R->>R: Authenticate & Authorize (DeploymentUpdate)
    R->>DB: Fetch deployment by ID
    DB-->>R: Deployment found / not found
    alt Deployment exists
        R->>DB: Update deployment
        DB-->>R: Updated deployment info
        R-->>C: 200 OK with updated data
    else Deployment not found
        R-->>C: 404 Not Found error
    end
Loading

Possibly Related PRs

  • fix: Deployment timeouts #278: Related to the introduction of a timeout feature and modifications to the deployment schema, both involving updates to the deployment structure and functionality.
  • fix: Create deployment endpoints #296: Directly related to modifications in the OpenAPI specification and handling of deployment updates in the same file structure.

Poem

I'm a rabbit with a code-filled heart,
Hopping through changes, a brand new start.
My paths now update with a gentle patch,
With checks and guards, no bug can catch.
I twirl my ears in digital delight,
Celebrating code as day turns to night! 🐰🌟

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 3

🧹 Nitpick comments (3)
apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts (3)

82-86: Consider using consistent authorization pattern

The authorization implementation in the PATCH route uses an async function with await on params, unlike the GET and DELETE methods. While this works, it introduces inconsistency in the codebase pattern.

Consider aligning with the approach used in other routes for better code consistency:

- authz(async ({ can, extra: { params } }) => {
-   return can
-     .perform(Permission.DeploymentUpdate)
-     .on({ type: "deployment", id: (await params).deploymentId });
- }),
+ authz(({ can, extra: { params } }) =>
+   can
+     .perform(Permission.DeploymentUpdate)
+     .on({ type: "deployment", id: params.deploymentId }),
+ ),

88-89: Review type definition for params

Unlike GET and DELETE routes that use a direct string type for deploymentId, the PATCH route uses a Promise wrapped type. This creates inconsistency with other route handlers.

Consider adapting the type to match other route handlers for consistency:

- .handle<{ db: Tx }, { params: Promise<{ deploymentId: string }> }>(
+ .handle<{ db: Tx }, { params: { deploymentId: string } }>(

117-117: Redundant error object in logging

The error logging includes the error object twice, which is redundant.

- logger.error("Failed to update deployment", { error: error });
+ logger.error("Failed to update deployment", { error });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1864a00 and 23bc64a.

📒 Files selected for processing (3)
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/openapi.ts (1 hunks)
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts (1 hunks)
  • openapi.v1.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: **Note on Error Handling:** Avoid strict en...

**/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/openapi.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
🔇 Additional comments (1)
apps/webservice/src/app/api/v1/deployments/[deploymentId]/openapi.ts (1)

91-144:

✅ Verification successful

PATCH operation schema configuration looks good

The OpenAPI specification for the PATCH operation is well-structured with appropriate parameters, request body, and response definitions.

Verify that the UpdateDeployment schema referenced in the request body is defined in your schema components:


🏁 Script executed:

#!/bin/bash
# Search for the UpdateDeployment schema definition in the codebase
rg "UpdateDeployment" --type ts

Length of output: 468


PATCH Operation Schema Verified: UpdateDeployment Schema Exists

The UpdateDeployment schema is defined in packages/db/src/schema/deployment.ts and referenced correctly in the PATCH operation configuration in apps/webservice/src/app/api/v1/deployments/[deploymentId]/openapi.ts. No issues found.

Copy link
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 (2)
apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts (2)

90-94: Simplify deploymentId extraction

If you keep the Promise-based params, consider simplifying the extraction of deploymentId with object destructuring:

- const deploymentId = (await params).deploymentId;
+ const { deploymentId } = await params;

116-117: Simplify error logging syntax

The error object doesn't need to be wrapped in an additional object.

- logger.error("Failed to update deployment", { error: error });
+ logger.error("Failed to update deployment", { error });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23bc64a and a9ea3bd.

📒 Files selected for processing (1)
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: **Note on Error Handling:** Avoid strict en...

**/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (2)
apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts (2)

93-94: Missing request body validation

The request body is extracted but there's no validation of the input data, which could lead to security issues if invalid or malicious data is provided.

Consider adding validation for the request body using a schema validator or a manual check of required fields and types before proceeding with the database update.


107-113: Direct database update without field filtering

The implementation directly sets the entire request body to the database update without filtering fields, which could potentially allow updating fields that shouldn't be updatable (e.g., sensitive or system fields).

Add field filtering to ensure only allowed fields can be updated:

- .update(SCHEMA.deployment)
- .set(body)
+ .update(SCHEMA.deployment)
+ .set({
+   name: body.name,
+   description: body.description,
+   // Only include fields that should be updatable
+ })

Comment on lines +82 to +88
authz(async ({ can, extra: { params } }) => {
return can
.perform(Permission.DeploymentUpdate)
.on({ type: "deployment", id: (await params).deploymentId });
}),
)
.handle<{ db: Tx }, { params: Promise<{ deploymentId: string }> }>(
Copy link
Contributor

@coderabbitai coderabbitai bot Mar 16, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Inconsistent deploymentId handling between endpoints

The authz middleware and handler type definitions in the PATCH endpoint handle params as a Promise, while GET and DELETE access it directly. This inconsistency could lead to confusion and potential bugs.

- authz(async ({ can, extra: { params } }) => {
-   return can
-     .perform(Permission.DeploymentUpdate)
-     .on({ type: "deployment", id: (await params).deploymentId });
- }),
+ authz(({ can, extra: { params } }) =>
+   can
+     .perform(Permission.DeploymentUpdate)
+     .on({ type: "deployment", id: params.deploymentId }),
+ ),

And update the handler type definition accordingly:

- .handle<{ db: Tx }, { params: Promise<{ deploymentId: string }> }>(
+ .handle<{ db: Tx }, { params: { deploymentId: string } }>(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
authz(async ({ can, extra: { params } }) => {
return can
.perform(Permission.DeploymentUpdate)
.on({ type: "deployment", id: (await params).deploymentId });
}),
)
.handle<{ db: Tx }, { params: Promise<{ deploymentId: string }> }>(
authz(({ can, extra: { params } }) =>
can
.perform(Permission.DeploymentUpdate)
.on({ type: "deployment", id: params.deploymentId }),
),
)
.handle<{ db: Tx }, { params: { deploymentId: string } }>(

Copy link
Member Author

Choose a reason for hiding this comment

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

This was specifically done because In Next.js 15, the params prop in dynamic routes is handled as a Promise within async contexts.

Otherwise I get this error.

@ctrlplane/webservice:dev: Error: Route "/api/v1/deployments/[deploymentId]" used `params.deploymentId`. `params` should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
@ctrlplane/webservice:dev:     at <unknown> (src/app/api/v1/deployments/[deploymentId]/route.ts:85:45)
@ctrlplane/webservice:dev:     at <unknown> (src/app/api/v1/auth.ts:32:28)
@ctrlplane/webservice:dev:     at next (src/app/api/v1/middleware.ts:46:15)
@ctrlplane/webservice:dev:     at authn (src/app/api/v1/auth.ts:20:9)
@ctrlplane/webservice:dev:   83 |       can
@ctrlplane/webservice:dev:   84 |         .perform(Permission.DeploymentUpdate)
@ctrlplane/webservice:dev: > 85 |         .on({ type: "deployment", id: params.deploymentId }),
@ctrlplane/webservice:dev:      |                                             ^
@ctrlplane/webservice:dev:   86 |     ),
@ctrlplane/webservice:dev:   87 |   )
@ctrlplane/webservice:dev:   88 |   .handle<{ db: Tx }, { params: { deploymentId: string } }>(
@ctrlplane/webservice:dev: Error: Route "/api/v1/deployments/[deploymentId]" used `params.deploymentId`. `params` should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
@ctrlplane/webservice:dev:     at <unknown> (src/app/api/v1/deployments/[deploymentId]/route.ts:91:36)
@ctrlplane/webservice:dev:     at next (src/app/api/v1/middleware.ts:44:17)
@ctrlplane/webservice:dev:     at <unknown> (src/app/api/v1/auth.ts:43:13)
@ctrlplane/webservice:dev:   89 |     async ({ db, req }, { params }) => {
@ctrlplane/webservice:dev:   90 |       try {
@ctrlplane/webservice:dev: > 91 |         const deploymentId = params.deploymentId;
@ctrlplane/webservice:dev:      |                                    ^
@ctrlplane/webservice:dev:   92 |
@ctrlplane/webservice:dev:   93 |         const body = await req.json();
@ctrlplane/webservice:dev:   94 |

If you have another solution, I am open to it. But I tried reverting both parts, and couldn't get this to work fully without both the await on the authz and the Promise on the params.

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

@zacharyblasczyk Thank you for the explanation! You're absolutely right about this Next.js 15 behavior change. In Next.js 15, route handlers must await dynamic route parameters in async contexts before accessing their properties.

Your implementation of handling params as a Promise is the correct approach given this constraint. However, I notice that the GET and DELETE endpoints haven't been updated to the same pattern yet - they might work now but could potentially face the same issue in certain scenarios.

For consistency across all endpoints, you might consider updating the GET and DELETE handlers to follow the same pattern:

// For GET and DELETE endpoints:
authz(async ({ can, extra: { params } }) =>
  can
    .perform(Permission.DeploymentGet/Delete)
    .on({ type: "deployment", id: (await params).deploymentId }),
),

And update their handler type definitions to match. This would make all three endpoints consistent while complying with Next.js 15's requirement.

ʕ •ᴥ•ʔ


🌐 Web query:

Next.js 15 dynamic route params as Promise in async contexts

💡 Result:

Next.js 15 introduces significant changes to how dynamic route parameters (params) are handled, requiring developers to adopt asynchronous patterns when working with routing data. Here's a breakdown of the key changes and implementation strategies:

Core Changes in Next.js 15

Dynamic route parameters now return Promises instead of direct values[5][8]. This enables better server-side optimizations and prepares for future features like Partial Prerendering (PPR)[8]. Attempting to access params synchronously will throw warnings/errors:

// ❌ Old approach (v14 and earlier)
function Page({ params }) {
  return <div>{params.slug}</div> // Error in v15
}

Implementation Patterns

Server Components

export default async function Page({ params }) {
  const { slug } = await params // ✅ Proper async access
  return <div>Post: {slug}</div>
}

Client Components

'use client'
import { use } from 'react'

function ClientPage({ paramsPromise }) {
  const { slug } = use(paramsPromise) // ✅ React hook for promises
  return <div>{slug}</div>
}
Component Type Access Method Example
Server await params const { id } = await params
Client use(paramsPromise) const params = use(paramsPromise)

TypeScript Integration

Next.js 15 provides enhanced type safety for dynamic routes. Use helper types with generateStaticParams:

export async function generateStaticParams() {
  return [{ slug: 'post-1' }, { slug: 'post-2' }]
}

export default function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  // Type-safe params access
}

Migration Strategies

  1. Codemod Automation:
    npx @next/codemod@canary next-async-request-api .
  2. Manual Updates:
    • Convert synchronous params access to await params
    • Update Client Components to use React's use() hook
  3. Error Handling:
    • Address @next-codemod-error comments
    • Use @next-codemod-ignore for special cases[5]

Advanced Configuration

Control static generation with generateStaticParams:

export async function generateStaticParams() {
  const posts = await fetchPosts()
  return posts.map(post => ({ slug: post.slug }))
}

export const dynamicParams = false // Block unmapped routes[10]

These changes enable Next.js to optimize both static generation and dynamic rendering while maintaining backward compatibility. Developers should prioritize migrating to async patterns to avoid deprecation warnings and ensure future compatibility[7][8].

Citations:


@zacharyblasczyk You're absolutely correct in your implementation! Next.js 15 indeed requires awaiting dynamic route parameters in async contexts before accessing their properties. Your approach with handling the params as a Promise and using (await params).deploymentId is exactly what Next.js 15 requires.

Based on the web search results, this is a significant architectural change in Next.js 15, where dynamic route parameters now return Promises instead of direct values. The error message you're seeing is the expected behavior when trying to access params synchronously in an async context.

For consistency across your codebase, you might want to eventually update the GET and DELETE endpoints to follow the same pattern, as they could potentially encounter similar issues in certain scenarios:

// For GET and DELETE endpoints:
authz(async ({ can, extra: { params } }) =>
  can
    .perform(Permission.DeploymentGet) // or DeploymentDelete
    .on({ type: "deployment", id: (await params).deploymentId }),
),

Next.js also provides a codemod to help automate these changes:

npx @next/codemod@canary next-async-request-api .

Your current implementation for the PATCH endpoint is the correct approach, and I recommend keeping it as is.

ʕ •ᴥ•ʔ


✏️ Learnings added
Learnt from: zacharyblasczyk
PR: ctrlplanedev/ctrlplane#382
File: apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts:82-88
Timestamp: 2025-03-16T19:41:44.019Z
Learning: In Next.js 15, params in dynamic routes must be awaited before accessing their properties in async contexts. For example, `params.deploymentId` should be accessed as `(await params).deploymentId` in async functions.

Learnt from: zacharyblasczyk
PR: ctrlplanedev/ctrlplane#382
File: apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts:82-88
Timestamp: 2025-03-16T19:41:44.019Z
Learning: In Next.js 15, dynamic route parameters (params) return Promises instead of direct values in async contexts. When accessing properties like `params.deploymentId` in an async function, use `(await params).deploymentId` to avoid the "params should be awaited before using its properties" error. This applies to API routes, page components, and other async contexts.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
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: 0

🧹 Nitpick comments (1)
openapi.v1.json (1)

2967-2978: Additional Deployment Properties Update
New properties—retryCount (integer), timeout (integer, nullable), and resourceFilter (object, nullable with additional properties)—have been added to the Deployment schema. These changes appear appropriate for capturing advanced configuration details. Please confirm that they are intended to be optional and that their types align with your business logic.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5eccf30 and a5c4aee.

📒 Files selected for processing (2)
  • apps/webservice/src/app/api/v1/openapi.ts (2 hunks)
  • openapi.v1.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/webservice/src/app/api/v1/openapi.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (2)
openapi.v1.json (2)

117-188: PATCH Operation for Updating a Deployment
The new patch endpoint is clearly defined with a proper summary, operationId (updateDeployment), the required path parameter (deploymentId with UUID validation), and a request body that references the UpdateDeployment schema. The responses for status codes 200, 404, and 500 are consistently structured. Please verify that the error response formats (with an "error" property) are aligned with your API’s overall error handling strategy.


2989-3005:

❓ Verification inconclusive

Examination of the UpdateDeployment Schema Composition
The UpdateDeployment schema is defined using an allOf composition that references the existing Deployment schema while also allowing additional properties. However, note that the base Deployment schema has several required fields (such as "name", "slug", "description", "systemId", and "jobAgentConfig"). Using allOf in this way will inherit those required constraints, which may conflict with the intent of having all update fields optional except for "id". Please verify whether this behavior is desired; if not, consider constructing a schema that explicitly marks all fields (other than "id") as optional to truly support partial updates.


I'll run a script to inspect the definition of the Deployment schema in openapi.v1.json to verify whether it includes the required fields noted in the review comment (like "name", "slug", "description", "systemId", and "jobAgentConfig"). This will help determine if the use of allOf in UpdateDeployment inadvertently inherits these required constraints.


🏁 Script executed:

#!/bin/bash
# Extract the "Deployment" schema definition along with context from the OpenAPI file.
rg -A 50 '"Deployment": {' openapi.v1.json

Length of output: 138


Action Required: Verify Inherited Required Fields in UpdateDeployment Schema
The current “UpdateDeployment” schema uses an allOf composition to incorporate the base “Deployment” schema while adding flexibility by allowing additional properties. However, if the “Deployment” schema does indeed mark several fields (e.g., "name", "slug", "description", "systemId", "jobAgentConfig") as required, then these constraints will be inherited by “UpdateDeployment”—which contradicts the intent of making update fields optional (besides "id").

  • Please confirm that the “Deployment” schema (located at #/components/schemas/Deployment within openapi.v1.json) includes these required fields. You might use a command like:
    jq '.components.schemas.Deployment.required' openapi.v1.json
  • If the required constraints from “Deployment” are not desired for update operations, consider refactoring the “UpdateDeployment” schema to explicitly mark fields (other than "id") as optional.

jobAgentConfig: { type: "object", additionalProperties: true },
retryCount: { type: "integer" },
timeout: { type: "integer", nullable: true },
resourceFilter: {
Copy link
Member

Choose a reason for hiding this comment

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

I think we are refactoring this to resource selector?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yah, I think for now I omit it because of that.

openapi.v1.json Outdated
"type": "integer",
"nullable": true
},
"resourceFilter": {
Copy link
Member

Choose a reason for hiding this comment

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

selector

Copy link
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: 0

🧹 Nitpick comments (1)
openapi.v1.json (1)

3132-3148: Consider refining the UpdateDeployment schema definition.

The UpdateDeployment schema correctly extends the Deployment schema, making fields optional for updates. However, there are some redundancies and potential issues:

  1. additionalProperties: true appears twice (lines 3141 and 3147)
  2. The schema requires the "id" field, but in a PATCH request, the ID is typically provided in the URL path, not the request body

Consider this improved definition:

  "UpdateDeployment": {
    "type": "object",
    "description": "Schema for updating a deployment (all fields optional)",
    "allOf": [
      {
        "$ref": "#/components/schemas/Deployment"
      },
      {
        "type": "object",
-       "additionalProperties": true
+       "properties": {
+         "name": { "type": "string" },
+         "slug": { "type": "string" },
+         "description": { "type": "string" },
+         "jobAgentId": { "type": "string", "format": "uuid", "nullable": true },
+         "jobAgentConfig": { "type": "object", "additionalProperties": true },
+         "retryCount": { "type": "integer" },
+         "timeout": { "type": "integer", "nullable": true }
+       }
      }
    ],
-   "required": [
-     "id"
-   ],
    "additionalProperties": true
  },
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5c4aee and 9e1af1c.

📒 Files selected for processing (3)
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts (1 hunks)
  • apps/webservice/src/app/api/v1/openapi.ts (2 hunks)
  • openapi.v1.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/webservice/src/app/api/v1/openapi.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: **Note on Error Handling:** Avoid strict en...

**/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts
🧠 Learnings (1)
apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts (2)
Learnt from: zacharyblasczyk
PR: ctrlplanedev/ctrlplane#382
File: apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts:82-88
Timestamp: 2025-03-16T19:41:44.129Z
Learning: In Next.js 15, dynamic route parameters (params) return Promises instead of direct values in async contexts. When accessing properties like `params.deploymentId` in an async function, use `(await params).deploymentId` to avoid the "params should be awaited before using its properties" error. This applies to API routes, page components, and other async contexts.
Learnt from: zacharyblasczyk
PR: ctrlplanedev/ctrlplane#382
File: apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts:82-88
Timestamp: 2025-03-16T19:41:44.129Z
Learning: In Next.js 15, params in dynamic routes must be awaited before accessing their properties in async contexts. For example, `params.deploymentId` should be accessed as `(await params).deploymentId` in async functions.
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (8)
apps/webservice/src/app/api/v1/deployments/[deploymentId]/route.ts (6)

82-87: Good implementation of async parameter handling for Next.js 15.

Your implementation correctly handles the dynamic route parameters as Promises in Next.js 15, as required by the framework. The async function and await on params is the correct approach.


91-91: Clean parameter extraction using object destructuring.

The destructuring of deploymentId from the awaited params is clean and follows Next.js 15 best practices.


95-103: Proper request validation with detailed error response.

The implementation correctly validates the request body against a schema and returns a detailed error response when validation fails. This is a security best practice that helps prevent invalid data from affecting the database.


105-115: Good existence check before update operation.

Checking if the deployment exists before attempting to update it prevents potential errors and provides a clear 404 response to clients when the resource is not found.


117-123: Direct database update without field filtering.

The implementation directly sets the entire request body to the database update without filtering fields, which could potentially allow updating fields that shouldn't be updatable (e.g., sensitive or system fields).

Add field filtering to ensure only allowed fields can be updated:

- .update(SCHEMA.deployment)
- .set(body)
+ .update(SCHEMA.deployment)
+ .set({
+   name: body.name,
+   description: body.description,
+   // Only include fields that should be updatable
+ })

125-131: Comprehensive error handling with logging.

The error handling implementation catches exceptions, logs the error details, and returns an appropriate 500 response without exposing sensitive error information to clients.

openapi.v1.json (2)

117-188: Well-structured PATCH endpoint definition.

The PATCH operation is properly defined with all required components - summary, operationId, parameters, request body, and responses. The response codes (200, 404, 500) align with the implementation in the route handler.


3114-3121: Proper schema update for new optional fields.

The additions to the Deployment schema for retryCount and timeout are well-defined with appropriate types. Making timeout nullable is a good choice for optional fields.

@zacharyblasczyk zacharyblasczyk merged commit 2d75001 into main Mar 17, 2025
6 checks passed
@zacharyblasczyk zacharyblasczyk deleted the zacharyb/patch-deployment branch March 17, 2025 03:11
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.

3 participants