Skip to content

Conversation

@adityachoudhari26
Copy link
Contributor

@adityachoudhari26 adityachoudhari26 commented Jun 2, 2025

Summary by CodeRabbit

  • New Features

    • Deployment variables now clearly distinguish between direct and reference values, each with their own dedicated fields and unique identifiers.
    • Improved UI handling for deployment variable values, with type-specific rendering and enhanced structure for value selection.
  • Bug Fixes

    • Fixed issues with variable value detection and display by using more robust type guards and updated rendering logic.
  • Refactor

    • Internal restructuring of deployment variable value storage and API schemas for greater clarity and maintainability.
    • Database schema updated to separate direct and reference variable values into distinct tables.
    • Simplified and centralized deployment variable resolution logic in backend and rule engine.
    • Removed deprecated dialog for editing variable values from UI.
  • Documentation

    • OpenAPI and schema documentation updated to reflect new structure and naming conventions for deployment variables.
  • Chores

    • End-to-end and API tests updated to align with the new deployment variable structure and validation rules.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 2, 2025

Walkthrough

This change refactors the handling of deployment variable values by splitting them into distinct "direct" and "reference" types throughout the backend, database schema, API, Node SDK, and frontend. New tables, types, and type guards are introduced, and all related logic, tests, and UI components are updated to reflect this separation.

Changes

File(s) / Path(s) Summary
packages/db/drizzle/0110_lying_virginia_dare.sql, packages/db/drizzle/meta/_journal.json Database migration: adds deployment_variable_value_direct and deployment_variable_value_reference tables, drops old columns and enum, updates migration journal.
packages/db/src/schema/deployment-variables.ts Refactors schema: splits variable values into direct/reference types, new tables/types, updates type guards and validation.
packages/db/src/utils/deployment-variables/get-deployment-variables.ts, upsert-deployment-variables.ts, upsert-direct-value.ts, upsert-reference-value.ts, index.ts Adds/updates helpers for retrieving and upserting direct and reference variable values.
packages/db/src/utils/index.ts, packages/db/package.json Aggregates utility exports and adds new dependency.
packages/api/src/router/deployment-variable/by-deployment-id.ts, deployment-variable.ts Refactors API router logic to use new direct/reference value types and helpers.
apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/openapi.ts, route.ts Updates OpenAPI schema and route logic to use direct/reference arrays, new naming, and helpers.
openapi.v1.json, packages/node-sdk/src/schema.ts, e2e/api/schema.ts Updates OpenAPI and SDK schemas to reflect new direct/reference value structure and naming.
e2e/api/entities-builder.ts, entity-fixtures.ts Updates test entity builders and fixtures to use direct/reference arrays.
e2e/tests/api/deployment-variable.spec.ts, release.spec.ts, resource-variables.spec.ts, yaml-import.spec.ts, yaml-import.spec.yaml Refactors tests and YAML specs to use new direct/reference arrays and updated fields.
packages/rule-engine/src/manager/variables/db-variable-providers.ts, resolve-reference-variable.ts Refactors rule engine variable resolution for new value types and resolution order.
apps/webservice/src/app/[workspaceSlug]/.../variables/VariableTable.tsx, EditVariableDialog.tsx, VariableValueDropdown.tsx, page.tsx Updates frontend variable table, dialogs, and pages to use type guards and new value structure.
apps/webservice/src/app/[workspaceSlug]/.../resources/(raw)/[resourceId]/variables/page.tsx Removes rendering of deployment variable value in table.

Sequence Diagram(s)

sequenceDiagram
    participant UI
    participant API
    participant DB
    participant Utils

    UI->>API: Request deployment variables
    API->>Utils: getDeploymentVariables(deploymentId)
    Utils->>DB: Query deployment variables
    Utils->>DB: Query directValues for each variable
    Utils->>DB: Query referenceValues for each variable
    Utils->>Utils: Decrypt sensitive direct values
    Utils->>API: Return variables with directValues, referenceValues, defaultValue
    API->>UI: Respond with new structure
Loading
sequenceDiagram
    participant API
    participant Utils
    participant DB

    API->>Utils: upsertDeploymentVariable(deploymentId, createVariable)
    Utils->>DB: Upsert deployment variable
    Utils->>DB: Upsert directValues (if any)
    Utils->>DB: Upsert referenceValues (if any)
    Utils->>DB: Set default value ID
    Utils->>DB: Fetch updated variable with values
    Utils->>API: Return updated variable
Loading

Possibly related PRs

  • ctrlplanedev/ctrlplane#561: Introduces a discriminated union with a valueType enum to distinguish direct and reference variable values within a single table and schema; related to handling reference variable values.
  • ctrlplanedev/ctrlplane#562: Adds asynchronous resolution of reference variable values and updates resource variable resolution; related through handling and resolution of reference deployment variable values.
  • ctrlplanedev/ctrlplane#164: Introduces the EditVariableDialog component, which is refactored in this PR for type guard usage.

Suggested reviewers

  • jsbroks

Poem

In the garden of variables, a split did appear,
Direct and reference, now crystal clear.
Tables and types, all neatly aligned,
With helpers and guards, their logic combined.
A hop and a skip, through schemas anew—
This bunny approves, and so should you!
🐇✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e17939b and 0e85e68.

📒 Files selected for processing (1)
  • packages/rule-engine/src/manager/variables/db-variable-providers.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rule-engine/src/manager/variables/db-variable-providers.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate sequence diagram to generate a sequence diagram of the changes in 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: 7

🧹 Nitpick comments (7)
apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/page.tsx (1)

37-43: LGTM! Consider the long-term UI strategy for value type distinction.

The transformation logic correctly combines directValues and referenceValues into a single values array for UI compatibility. This is a sensible approach during the refactor transition.

However, consider whether the UI should eventually distinguish between direct and reference values for better user experience (e.g., different styling, icons, or grouping).

apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/EditVariableDialog.tsx (1)

5-5: Consider using specific imports for better tree-shaking

While the namespace import works, consider importing only the specific type guards you need for better tree-shaking and clearer dependencies.

-import * as SCHEMA from "@ctrlplane/db/schema";
+import {
+  isDeploymentVariableValueDirect,
+  isDeploymentVariableValueReference,
+} from "@ctrlplane/db/schema";

Then update the usage:

-{SCHEMA.isDeploymentVariableValueDirect(v) &&
+{isDeploymentVariableValueDirect(v) &&
packages/db/src/utils/deployment-variables/upsert-direct-value.ts (1)

117-141: Optimize update logic to avoid unnecessary writes

The current implementation updates all fields even when only some may have changed. Consider checking if the resourceSelector or sensitive flag has actually changed before updating.

When the value hash matches, you're still updating the resourceSelector and sensitive fields. Consider checking if these have actually changed to avoid unnecessary database writes:

// In updateExistingDirectVariableValue, check if updates are needed
const needsVariableValueUpdate = 
  existingValue.deployment_variable_value.resourceSelector !== updateDirectValue.resourceSelector;

const needsDirectValueUpdate = 
  existingValue.deployment_variable_value_direct.sensitive !== updateDirectValue.sensitive;

// Only update tables that have changes
apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/route.ts (1)

61-110: Consider consistent return types for better predictability.

The function returns undefined when there's no defaultValueId but null when the reference value is not found. This inconsistency could lead to confusion.

Consider returning null consistently:

 const getDefaultValue = async (db: Tx, variable: schema.DeploymentVariable) => {
   const { defaultValueId } = variable;
-  if (defaultValueId == null) return undefined;
+  if (defaultValueId == null) return null;
packages/db/src/utils/deployment-variables/upsert-deployment-variables.ts (2)

39-49: Consider using a custom error class for validation errors.

The function throws a generic Error which makes it harder to distinguish validation errors from other errors in the calling code.

Consider creating a custom error class:

class ValidationError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'ValidationError';
  }
}

// Then in the function:
if (numDefault > 1) throw new ValidationError("Only one default value is allowed");

66-80: Consider adding error context for debugging parallel operations.

The Promise.all operations could fail partially, making debugging difficult. Consider wrapping the operations to add context.

Example approach:

const directValuePromises = (directValues ?? []).map(async (dv, index) => {
  try {
    const value = await upsertDirectVariableValue(tx, variable.id, dv);
    if (dv.isDefault)
      await setDefaultVariableValue(tx, variable.id, value.id);
    return value;
  } catch (error) {
    throw new Error(`Failed to upsert direct value at index ${index}: ${error.message}`);
  }
});
packages/db/src/schema/deployment-variables.ts (1)

86-89: Consider enforcing valueHash for sensitive values.

The valueHash field is optional, but it's unclear when it should be populated. If this field is meant to store a hash of sensitive values for comparison or audit purposes, consider adding a database constraint or application-level validation to ensure it's always populated when sensitive is 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 b056db4 and c87c628.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (32)
  • apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/EditVariableDialog.tsx (2 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/VariableTable.tsx (5 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/VariableValueDropdown.tsx (2 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/page.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/variables/page.tsx (1 hunks)
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/openapi.ts (5 hunks)
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/route.ts (4 hunks)
  • e2e/api/entities-builder.ts (3 hunks)
  • e2e/api/entity-fixtures.ts (2 hunks)
  • e2e/api/schema.ts (7 hunks)
  • e2e/tests/api/deployment-variable.spec.ts (5 hunks)
  • e2e/tests/api/release.spec.ts (6 hunks)
  • e2e/tests/api/resource-variables.spec.ts (3 hunks)
  • e2e/tests/api/yaml-import.spec.ts (1 hunks)
  • e2e/tests/api/yaml-import.spec.yaml (2 hunks)
  • openapi.v1.json (6 hunks)
  • packages/api/src/router/deployment-variable/by-deployment-id.ts (2 hunks)
  • packages/api/src/router/deployment-variable/deployment-variable.ts (4 hunks)
  • packages/db/drizzle/0110_lying_virginia_dare.sql (1 hunks)
  • packages/db/drizzle/meta/_journal.json (1 hunks)
  • packages/db/package.json (1 hunks)
  • packages/db/src/index.ts (1 hunks)
  • packages/db/src/schema/deployment-variables.ts (2 hunks)
  • packages/db/src/utils/deployment-variables/get-deployment-variables.ts (1 hunks)
  • packages/db/src/utils/deployment-variables/index.ts (1 hunks)
  • packages/db/src/utils/deployment-variables/upsert-deployment-variables.ts (1 hunks)
  • packages/db/src/utils/deployment-variables/upsert-direct-value.ts (1 hunks)
  • packages/db/src/utils/deployment-variables/upsert-reference-value.ts (1 hunks)
  • packages/db/src/utils/index.ts (1 hunks)
  • packages/node-sdk/src/schema.ts (7 hunks)
  • packages/rule-engine/src/manager/variables/db-variable-providers.ts (4 hunks)
  • packages/rule-engine/src/manager/variables/resolve-reference-variable.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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.

  • packages/db/src/utils/deployment-variables/index.ts
  • apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/variables/page.tsx
  • e2e/tests/api/resource-variables.spec.ts
  • apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/page.tsx
  • e2e/tests/api/yaml-import.spec.ts
  • apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/VariableValueDropdown.tsx
  • packages/db/src/utils/deployment-variables/upsert-direct-value.ts
  • packages/rule-engine/src/manager/variables/resolve-reference-variable.ts
  • packages/db/src/index.ts
  • packages/db/src/utils/index.ts
  • packages/api/src/router/deployment-variable/deployment-variable.ts
  • apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/VariableTable.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/EditVariableDialog.tsx
  • e2e/api/entities-builder.ts
  • e2e/tests/api/release.spec.ts
  • packages/db/src/utils/deployment-variables/upsert-deployment-variables.ts
  • e2e/api/entity-fixtures.ts
  • packages/db/src/utils/deployment-variables/get-deployment-variables.ts
  • packages/db/src/utils/deployment-variables/upsert-reference-value.ts
  • packages/api/src/router/deployment-variable/by-deployment-id.ts
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/route.ts
  • e2e/api/schema.ts
  • e2e/tests/api/deployment-variable.spec.ts
  • packages/db/src/schema/deployment-variables.ts
  • packages/rule-engine/src/manager/variables/db-variable-providers.ts
  • apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/openapi.ts
  • packages/node-sdk/src/schema.ts
🧠 Learnings (1)
packages/api/src/router/deployment-variable/by-deployment-id.ts (1)
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#181
File: packages/api/src/router/deployment.ts:116-131
Timestamp: 2024-10-29T02:04:50.312Z
Learning: In `packages/api/src/router/deployment.ts`, the `list.byDeploymentId` procedure requires multiple database queries due to limitations of the `releaseMatchesCondition` function.
🧬 Code Graph Analysis (5)
apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/VariableValueDropdown.tsx (2)
apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/variable-data.ts (1)
  • VariableValue (3-7)
packages/db/src/schema/deployment-variables.ts (1)
  • DeploymentVariable (51-51)
packages/db/src/utils/deployment-variables/get-deployment-variables.ts (2)
packages/db/src/schema/deployment-variables.ts (2)
  • DirectDeploymentVariableValue (117-118)
  • DeploymentVariable (51-51)
packages/db/src/common.ts (1)
  • Tx (22-22)
e2e/tests/api/deployment-variable.spec.ts (1)
e2e/tests/fixtures.ts (1)
  • test (11-26)
packages/rule-engine/src/manager/variables/db-variable-providers.ts (4)
packages/db/src/schema/deployment-variables.ts (3)
  • DirectDeploymentVariableValue (117-118)
  • ReferenceDeploymentVariableValue (165-166)
  • DeploymentVariableValue (168-170)
packages/db/src/utils/deployment-variables/get-deployment-variables.ts (2)
  • getDeploymentVariables (80-99)
  • getResolvedDirectValue (8-17)
packages/rule-engine/src/manager/variables/types.ts (1)
  • MaybeVariable (9-9)
packages/rule-engine/src/manager/variables/resolve-reference-variable.ts (1)
  • getReferenceVariableValue (8-31)
packages/node-sdk/src/schema.ts (1)
e2e/api/schema.ts (1)
  • components (892-1421)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Lint
🔇 Additional comments (70)
packages/db/package.json (1)

55-55: LGTM: Dependency addition aligns with refactoring needs.

The ts-is-present dependency addition follows the established catalog versioning pattern and supports the type presence checks needed for the deployment variable refactoring.

packages/db/drizzle/meta/_journal.json (1)

775-781: LGTM: Migration entry follows established pattern.

The new migration entry for the deployment variable refactoring is properly structured and follows the existing journal format consistently.

packages/db/src/utils/deployment-variables/index.ts (1)

1-2: LGTM: Clean utility module aggregation.

The re-export pattern provides a unified entry point for deployment variable utilities. The .js extensions are appropriate for ESM module compatibility.

packages/db/src/index.ts (1)

9-9: LGTM! Clean consolidation of utility exports.

This change effectively consolidates utility exports into a single index module, improving import management without affecting the public API.

packages/rule-engine/src/manager/variables/resolve-reference-variable.ts (1)

12-12: LGTM! Type alignment with refactored schema.

The type update from schema.DeploymentVariableValueReference to schema.ReferenceDeploymentVariableValue correctly aligns with the refactored schema that separates deployment variable values into distinct direct and reference types.

e2e/tests/api/yaml-import.spec.ts (2)

126-129: LGTM! Correct adaptation to new API structure.

The test correctly updates from using values to directValues array, maintaining proper test coverage for the refactored deployment variable schema.


133-135: Excellent type safety with the type guard pattern.

The addition of the type guard ("value" in defaultValue!) before accessing the value property is a robust approach that prevents runtime errors and ensures type safety when working with the potentially polymorphic defaultValue structure.

packages/db/src/utils/index.ts (1)

1-3: LGTM: Clean utility module aggregation.

This new index file properly aggregates utility exports for deployment variables and resource management, supporting the broader refactoring effort with a clean centralized export pattern.

e2e/tests/api/yaml-import.spec.yaml (2)

87-89: LGTM: Consistent with deployment variable refactoring.

The changes from values to directValues and default to isDefault properly align with the broader refactoring to separate direct and reference variable values and improve property naming clarity.


112-112: LGTM: Simplified policy structure.

Removing the unnecessary list wrapper for versionAnyApprovals improves the configuration structure by making it a direct object with requiredApprovalsCount.

e2e/tests/api/release.spec.ts (5)

147-159: LGTM: Enhanced variable structure with explicit properties.

The test properly reflects the new API structure with directValues array and explicit properties (sensitive, resourceSelector, isDefault). The addition of a second value object improves test coverage for multiple values scenarios.


368-380: LGTM: Consistent test structure maintained.

The variable creation request body correctly uses the new directValues structure with all required properties, maintaining consistency with the updated API schema.


505-517: LGTM: Test coverage expanded with multiple values.

The test case properly demonstrates the new structure while testing multiple deployment variable values, which is important for validating the array-based approach.


623-635: LGTM: Consistent API usage throughout tests.

All test cases consistently use the new directValues structure, ensuring comprehensive validation of the refactored deployment variable handling.


752-764: LGTM: Complete test suite alignment.

The remaining test cases properly implement the new deployment variable structure, ensuring the entire test suite validates the refactored API consistently.

Also applies to: 881-893

e2e/tests/api/resource-variables.spec.ts (3)

340-351: LGTM: Proper reference value structure implementation.

The test correctly uses referenceValues array for reference-type deployment variables, eliminating the need for the valueType discriminator since the array placement now determines the value type.


497-508: LGTM: Consistent reference variable testing.

The test case properly validates reference variable functionality using the new structure, ensuring that variable references to related resources work correctly with the refactored API.


671-682: LGTM: Complete migration to reference values structure.

All reference variable test cases consistently use the new referenceValues array structure, providing comprehensive validation of the refactored deployment variable reference handling.

apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/EditVariableDialog.tsx (1)

119-126: Type-safe rendering implementation looks good!

The use of type guards to conditionally render direct vs reference values is well-implemented and aligns with the schema refactor. The JSON stringification for object values ensures proper display.

packages/db/src/utils/deployment-variables/get-deployment-variables.ts (5)

8-17: LGTM! Good handling of sensitive values.

The function correctly differentiates between sensitive and non-sensitive values, and properly handles the decryption process with appropriate type conversions.


19-40: Well-structured database query.

The function correctly joins the tables and efficiently merges the results using the spread operator.


42-66: Consistent implementation with proper ordering.

Good addition of the orderBy clause to ensure reference values are returned in a predictable order.


68-78: Efficient concurrent data fetching.

Excellent use of Promise.all to fetch direct and reference values in parallel, improving performance.


80-99: Well-implemented main retrieval function.

The function properly handles:

  • Ordering variables by key for consistent results
  • Concurrent processing of variable values
  • Correct resolution of the default value from the combined values array
packages/db/src/utils/deployment-variables/upsert-reference-value.ts (4)

7-31: Correct query for finding existing reference values.

The function properly uses the unique combination of variableId, reference, and path to identify existing values.


32-58: Proper two-step insertion with transaction safety.

The function correctly handles the insertion order required by the foreign key relationship. Since it operates within a transaction context (Tx), atomicity is maintained.


60-89: Consistent update implementation.

The function properly updates both related tables within the transaction context, maintaining data consistency.


91-115: Well-implemented upsert pattern.

The function correctly implements the upsert logic by checking for existence first, preventing duplicate entries while allowing updates to existing values.

packages/api/src/router/deployment-variable/deployment-variable.ts (2)

248-253: Good refactoring to use centralized upsert logic.

The mutation now cleanly delegates to the upsertDeploymentVariable utility while maintaining queue updates and return values.


248-251:

✅ Verification successful

Verify database access for upsertDeploymentVariable.

The mutation no longer uses the ctx parameter. Please ensure that upsertDeploymentVariable has proper database access.


🏁 Script executed:

#!/bin/bash
# Description: Check the implementation of upsertDeploymentVariable to ensure it has database access

# Search for the upsertDeploymentVariable function implementation
ast-grep --pattern 'export const upsertDeploymentVariable = $_'

# Also check with ripgrep for more context
rg -A 10 "export.*upsertDeploymentVariable" --type ts

Length of output: 6188


No DB access issue – upsertDeploymentVariable uses db.transaction internally

Verified that the upsertDeploymentVariable function (packages/db/src/utils/deployment-variables/upsert-deployment-variables.ts) wraps its logic in db.transaction(...), so it already has full database access without needing the ctx parameter in the mutation. Removing ctx here is safe.

apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/VariableTable.tsx (5)

13-13: LGTM: Centralized schema import added.

The addition of the schema import enables the use of centralized type guard functions, which is a good practice for maintaining type safety and consistency across the codebase.


138-142: LGTM: Consistent type guard usage replaces direct property checks.

The replacement of value.valueType === "direct" with schema.isDeploymentVariableValueDirect(value) centralizes type checking logic and improves maintainability. This pattern is applied consistently throughout the component for icon rendering, tooltip content, and conditional value display.

Also applies to: 146-148, 153-157, 211-215, 219-221, 226-230


159-159: LGTM: Simplified path handling.

The removal of the nullish coalescing operator (??) from value.path usage suggests the schema now guarantees this property is always present, which simplifies the code.

Also applies to: 232-232


287-291: LGTM: Type guard determines valueType prop.

Using the type guard to dynamically determine the valueType prop value maintains consistency with the new type checking approach and ensures the correct type is passed to child components.


413-418: LGTM: Search functionality updated for new schema.

The search keys now correctly serialize either the direct value or the reference reference property based on the type guard, maintaining search functionality while adapting to the new variable value structure.

e2e/api/entity-fixtures.ts (3)

23-40: LGTM: Well-structured directValues schema.

The directValues array schema correctly defines:

  • Nullable union type for value supporting string, number, boolean, object, and array types
  • Optional sensitive boolean flag
  • Nullable resourceSelector for flexibility
  • Optional isDefault boolean for marking default values

The schema structure aligns with the new deployment variable value model.


41-59: LGTM: Comprehensive referenceValues schema.

The referenceValues array schema properly defines:

  • Nullable resourceSelector for flexible resource targeting
  • Required reference string for the reference identifier
  • Required path array of strings for property access
  • Nullable defaultValue with the same union type as direct values
  • Optional isDefault boolean for default value marking

This structure supports the reference-based variable resolution pattern.


112-113: LGTM: PolicyFixture simplified.

The change from an optional array of objects to an optional single object with requiredApprovalsCount simplifies the policy approval structure, which appears to be a separate but related improvement.

packages/db/drizzle/0110_lying_virginia_dare.sql (4)

1-8: LGTM: Well-designed direct values table.

The deployment_variable_value_direct table properly includes:

  • UUID primary key with automatic generation
  • Foreign key to the main variable value table
  • JSONB value field for flexible data storage
  • value_hash for potential indexing/comparison optimization
  • sensitive boolean flag with appropriate default
  • Unique constraint on variable_value_id ensuring one-to-one relationship

The structure supports efficient storage and querying of direct variable values.


10-17: LGTM: Comprehensive reference values table.

The deployment_variable_value_reference table effectively captures:

  • UUID primary key with automatic generation
  • Foreign key to the main variable value table
  • Required reference and path fields for resource property access
  • Optional JSONB default_value for fallback scenarios
  • Unique constraint ensuring one-to-one relationship with variable values

This design supports the reference-based variable resolution pattern.


19-20: LGTM: Proper foreign key constraints with cascade delete.

The foreign key constraints correctly reference the parent table with ON DELETE cascade ensuring data integrity when variable values are removed. The ON UPDATE no action is appropriate for UUID primary keys.


21-27: LGTM: Clean removal of legacy schema elements.

The migration properly removes the deprecated columns (value_type, value, sensitive, reference, path, default_value) and the value_type enum, completing the transition to the new separated table structure.

e2e/api/entities-builder.ts (3)

44-58: LGTM: Cache interface updated for separated value types.

The interface correctly defines:

  • directValues array with id, value, sensitive, resourceSelector, and isDefault properties
  • referenceValues array with id, resourceSelector, reference, path, defaultValue, and isDefault properties

Both arrays are optional, maintaining flexibility for deployments without variables. The structure aligns with the new API schema.


316-323: LGTM: Proper API request body construction.

The request body construction correctly:

  • Maps over directValues and referenceValues arrays separately
  • Ensures resourceSelector fields are explicitly set to null when undefined
  • Maintains the original structure while adapting to the new schema requirements

This handles the API contract properly and prevents undefined values from being sent.


337-350: LGTM: Robust response handling with UUID assignment.

The response processing effectively:

  • Destructures directValues and referenceValues from the API response
  • Assigns UUIDs to entries missing an id using faker for test consistency
  • Updates the cache with the properly structured variable data

The defensive UUID assignment ensures test stability even if the API response format varies.

e2e/tests/api/deployment-variable.spec.ts (2)

130-131: Good use of find() for test resilience!

Using find() to locate the variable by key instead of array indexing makes the test more robust against changes in the order of returned variables.


264-391: Comprehensive test coverage for variable updates!

This test thoroughly validates the update functionality including resourceSelector changes for both direct and reference values.

apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/route.ts (1)

49-50: Clean refactor using centralized helper!

The GET handler is now much simpler by delegating to getDeploymentVariables, improving maintainability.

packages/api/src/router/deployment-variable/by-deployment-id.ts (4)

17-25: LGTM!

The helper function correctly fetches release targets with their associated resources.


48-50: Verify the sensitive value masking behavior.

The code masks sensitive direct values as "***". Please confirm this is the intended behavior for UI display purposes and that the actual values are properly handled when needed for deployment operations.


67-106: Well-structured helper function!

The function correctly processes both direct and reference values, identifies unmatched resources, and properly assigns the default value to them.


108-128: Clean implementation!

The procedure follows a clear flow and properly leverages the helper functions to resolve deployment variables by resource.

apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/openapi.ts (1)

11-132: Well-structured schema refactoring!

The separation of direct and reference variable values into distinct schemas with "WithId" variants provides better type safety and clarity. The renaming from "default" to "isDefault" improves semantic clarity.

packages/rule-engine/src/manager/variables/db-variable-providers.ts (1)

96-104: Clear documentation of variable precedence!

The comment clearly explains the resolution order, which helps maintainability.

packages/db/src/schema/deployment-variables.ts (3)

102-102: Clarify the purpose of null values in direct deployment variable values.

The schema allows value to be null for direct values, which seems counterintuitive. Consider whether null values should be handled differently (e.g., by not creating a value record, or using reference values with null defaults).


172-179: Clean type guard implementation.

The simplified type guards using property presence checks are a good improvement over discriminator fields. This approach is more intuitive and reduces complexity.


207-216: Well-structured schema update for separated value types.

The schema correctly reflects the new architecture with separate directValues and referenceValues arrays. Making both optional provides good flexibility for variable creation scenarios.

e2e/api/schema.ts (2)

913-953: Correct OpenAPI schema generation for the new variable value types.

The auto-generated schemas properly reflect the backend's architectural change to separate direct and reference deployment variable values. The addition of ID fields to the value types enables better tracking and management.


1316-1336: Good addition of concurrency control to policies.

The optional concurrency field allows for better control over parallel policy executions while maintaining backward compatibility by making it nullable.

openapi.v1.json (6)

4769-4784: Correctly renamed base variable value schema.

The new BaseDeploymentVariableValue replaces the old base schema, makes resourceSelector required (nullable), and renames the default flag to isDefault, improving clarity and aligning with the direct/reference split.


4785-4822: Direct value schema properly updated.

DirectDeploymentVariableValue now extends the base type, removes the valueType discriminator, and allows a nullable value, accurately reflecting the new design for direct variable values.


4823-4841: Added unique IDs for direct value entries.

The DirectDeploymentVariableValueWithId schema correctly layers on a required UUID id, enabling unique identification of each direct value record.


4842-4886: Reference value schema aligns with refactor.

ReferenceDeploymentVariableValue properly extends the base, drops the old discriminator, and defines path, reference, and nullable defaultValue, fully supporting the separated reference values model.


4887-4905: Added unique IDs for reference value entries.

The ReferenceDeploymentVariableValueWithId schema correctly introduces a required UUID id, ensuring reference values can be uniquely tracked.


4906-4954: DeploymentVariable schema updated to separate value lists.

Splitting into directValues and referenceValues arrays and centralizing defaultValue as a union of the two WithId schemas creates a clearer API contract and enhances type safety.

packages/node-sdk/src/schema.ts (5)

913-938: LGTM! Well-structured type hierarchy for deployment variable values.

The refactoring properly separates direct and reference variable values with:

  • Clear inheritance from BaseDeploymentVariableValue
  • Appropriate nullable types for resourceSelector, value, and defaultValue
  • Consistent ID extensions for both direct and reference types
  • Required sensitive field for direct values

944-948: LGTM! Consistent schema update for DeploymentVariable.

The changes correctly implement the separation of variable values:

  • Distinct arrays for directValues and referenceValues using the WithId variants
  • Flexible defaultValue that can be either type
  • Maintains type safety with proper union types

1189-1190: LGTM! Standard soft-delete implementation.

The addition of nullable deletedAt timestamp follows best practices for soft-delete functionality.


1315-1316: LGTM! Clean implementation of policy concurrency support.

The nullable integer type for PolicyConcurrency appropriately handles both concurrency limits and unlimited cases (null).

Also applies to: 1334-1334


2022-2023: LGTM! API operations correctly updated to match schema changes.

The changes maintain consistency across all operations:

  • createDeploymentVariable properly uses the new separated value arrays
  • Policy operations (updatePolicy, upsertPolicy) include the new concurrency field

This ensures the API contract is properly maintained throughout the SDK.

Also applies to: 2653-2653, 2756-2756

<TableRow key={v.key}>
<TableCell>{v.key}</TableCell>
<TableCell>{String(v.value.value)}</TableCell>
<TableCell></TableCell>
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 2, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve user experience with empty value cell.

The deployment variable value cell is now completely empty, which could confuse users. Consider:

  • Adding a placeholder message explaining the refactoring
  • Implementing proper value display using the new direct/reference structure
  • Adding a "Not available" or similar indicator

This appears to be an incomplete implementation that needs user-facing improvements.

- <TableCell></TableCell>
+ <TableCell className="text-muted-foreground">
+   <span className="italic">Value display pending refactor</span>
+ </TableCell>
📝 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
<TableCell></TableCell>
<TableCell className="text-muted-foreground">
<span className="italic">Value display pending refactor</span>
</TableCell>
🤖 Prompt for AI Agents
In
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/variables/page.tsx
at line 252, the TableCell for deployment variable value is empty, which can
confuse users. Update this cell to display a meaningful placeholder such as "Not
available" or a message explaining the refactoring. Additionally, implement
logic to properly render the variable value using the new direct/reference
structure to improve clarity and user experience.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will address in separate PR

Copy link
Contributor

Choose a reason for hiding this comment

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

@adityachoudhari26 That sounds like a good approach to keep this PR focused on the refactoring!

Would you like me to open a GitHub issue to track implementing proper deployment variable value display using the new direct/reference structure? This would help ensure the UI improvement doesn't get lost.

Comment on lines 28 to 35
const EditVariableValueDialog: React.FC<{
value: VariableValue;
variable: schema.DeploymentVariable;
onClose: () => void;
children: React.ReactNode;
}> = ({ value, variable, children, onClose }) => {
const [open, setOpen] = useState(false);
const update = api.deployment.variable.value.update.useMutation();
const router = useRouter();

const form = useForm({
schema: schema.updateDeploymentVariableValue,
defaultValues: {
...value,
default: variable.defaultValueId === value.id,
},
});

const onSubmit = form.handleSubmit((data) =>
update
.mutateAsync({ id: value.id, data })
.then(() => router.refresh())
.then(onClose),
);

return (
<Dialog
open={open}
onOpenChange={(open) => {
setOpen(open);
if (!open) onClose();
}}
>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="min-w-[1000px]">
<DialogHeader>
<DialogTitle>Edit variable value</DialogTitle>
</DialogHeader>

<Form {...form}>
<form onSubmit={onSubmit} className="space-y-3">
<FormField
control={form.control}
name="value"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Value</FormLabel>
<FormControl>
<>
{variable.config?.type === "string" && (
<VariableStringInput
{...variable.config}
value={String(value)}
onChange={onChange}
/>
)}
{variable.config?.type === "choice" && (
<VariableChoiceSelect
{...variable.config}
value={String(value)}
onSelect={onChange}
/>
)}
{variable.config?.type === "boolean" && (
<VariableBooleanInput
value={value === "" ? null : Boolean(value)}
onChange={onChange}
/>
)}
{variable.config?.type === "number" && (
<Input
type="number"
value={Number(value)}
onChange={(e) => onChange(e.target.valueAsNumber)}
/>
)}
</>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="resourceSelector"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Resource selector</FormLabel>
<FormControl>
<ResourceConditionRender
condition={value ?? defaultCondition}
onChange={onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="default"
render={({ field: { value, onChange } }) => (
<FormItem>
<div className="flex items-center gap-2">
<FormLabel>Default</FormLabel>
<FormControl>
<Switch checked={value} onCheckedChange={onChange} />
</FormControl>
</div>
</FormItem>
)}
/>

<DialogFooter>
<Button
variant="outline"
type="button"
onClick={() => form.setValue("resourceSelector", null)}
>
Clear selector
</Button>
<div className="flex-grow" />
<Button type="submit">Save</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}> = () => {
return null;
};
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 2, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Non-functional component still being used in the UI

The EditVariableValueDialog component has been replaced with a stub that returns null, but it's still being used in the dropdown menu (lines 93-105). This creates a confusing user experience where the "Edit" button is visible but does nothing when clicked.

Either remove the usage of this component from the dropdown menu or add a TODO comment explaining when the implementation will be restored:

-}> = () => {
-  return null;
+}> = () => {
+  // TODO: Implement after variable value refactoring is complete
+  return null;
 };

Or remove the edit option entirely:

       <DropdownMenuContent>
         <DropdownMenuGroup>
-          <EditVariableValueDialog
-            value={value}
-            variable={variable}
-            onClose={() => setOpen(false)}
-          >
-            <DropdownMenuItem
-              onSelect={(e) => e.preventDefault()}
-              className="flex items-center gap-2"
-            >
-              <IconPencil className="h-4 w-4" />
-              Edit
-            </DropdownMenuItem>
-          </EditVariableValueDialog>
           <DeleteVariableValueDialog

🏁 Script executed:

#!/bin/bash
# Locate definition and usages of EditVariableValueDialog
rg "EditVariableValueDialog" -n apps/webservice/src/app

Length of output: 668


Address non-functional EditVariableValueDialog usage

The EditVariableValueDialog is a stub returning null but still rendered in the dropdown (lines 93–105), so the “Edit” option does nothing and will confuse users.

Please either implement the dialog (or add a clear TODO) or remove its usage entirely:

• File: apps/webservice/src/app/[workspaceSlug]/…/VariableValueDropdown.tsx
– Definition at lines 28–35
– Usage at lines 93–105

Option 1: Add a TODO in the stub

 const EditVariableValueDialog: React.FC<{
   value: VariableValue;
   variable: schema.DeploymentVariable;
   onClose: () => void;
   children: React.ReactNode;
 }> = () => {
+  // TODO: implement EditVariableValueDialog after refactoring variable values
   return null;
 };

Option 2: Remove the edit menu item

        <DropdownMenuContent>
          <DropdownMenuGroup>
-          <EditVariableValueDialog
-            value={value}
-            variable={variable}
-            onClose={() => setOpen(false)}
-          >
-            <DropdownMenuItem
-              onSelect={(e) => e.preventDefault()}
-              className="flex items-center gap-2"
-            >
-              <IconPencil className="h-4 w-4" />
-              Edit
-            </DropdownMenuItem>
-          </EditVariableValueDialog>
           <DeleteVariableValueDialog
             value={value}
             variable={variable}
📝 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
const EditVariableValueDialog: React.FC<{
value: VariableValue;
variable: schema.DeploymentVariable;
onClose: () => void;
children: React.ReactNode;
}> = ({ value, variable, children, onClose }) => {
const [open, setOpen] = useState(false);
const update = api.deployment.variable.value.update.useMutation();
const router = useRouter();
const form = useForm({
schema: schema.updateDeploymentVariableValue,
defaultValues: {
...value,
default: variable.defaultValueId === value.id,
},
});
const onSubmit = form.handleSubmit((data) =>
update
.mutateAsync({ id: value.id, data })
.then(() => router.refresh())
.then(onClose),
);
return (
<Dialog
open={open}
onOpenChange={(open) => {
setOpen(open);
if (!open) onClose();
}}
>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="min-w-[1000px]">
<DialogHeader>
<DialogTitle>Edit variable value</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={onSubmit} className="space-y-3">
<FormField
control={form.control}
name="value"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Value</FormLabel>
<FormControl>
<>
{variable.config?.type === "string" && (
<VariableStringInput
{...variable.config}
value={String(value)}
onChange={onChange}
/>
)}
{variable.config?.type === "choice" && (
<VariableChoiceSelect
{...variable.config}
value={String(value)}
onSelect={onChange}
/>
)}
{variable.config?.type === "boolean" && (
<VariableBooleanInput
value={value === "" ? null : Boolean(value)}
onChange={onChange}
/>
)}
{variable.config?.type === "number" && (
<Input
type="number"
value={Number(value)}
onChange={(e) => onChange(e.target.valueAsNumber)}
/>
)}
</>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="resourceSelector"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Resource selector</FormLabel>
<FormControl>
<ResourceConditionRender
condition={value ?? defaultCondition}
onChange={onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="default"
render={({ field: { value, onChange } }) => (
<FormItem>
<div className="flex items-center gap-2">
<FormLabel>Default</FormLabel>
<FormControl>
<Switch checked={value} onCheckedChange={onChange} />
</FormControl>
</div>
</FormItem>
)}
/>
<DialogFooter>
<Button
variant="outline"
type="button"
onClick={() => form.setValue("resourceSelector", null)}
>
Clear selector
</Button>
<div className="flex-grow" />
<Button type="submit">Save</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}> = () => {
return null;
};
const EditVariableValueDialog: React.FC<{
value: VariableValue;
variable: schema.DeploymentVariable;
onClose: () => void;
children: React.ReactNode;
}> = () => {
// TODO: implement EditVariableValueDialog after refactoring variable values
return null;
};
🤖 Prompt for AI Agents
In
apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/VariableValueDropdown.tsx
around lines 28 to 35 and usage at lines 93 to 105, the EditVariableValueDialog
component is a stub returning null but still used in the dropdown menu, causing
the Edit button to do nothing. To fix this, either add a clear TODO comment
inside the EditVariableValueDialog stub indicating when the implementation will
be restored or remove the edit menu item and all references to
EditVariableValueDialog from the dropdown menu to avoid confusing users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will address in separate PR

Copy link
Contributor

Choose a reason for hiding this comment

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

@adityachoudhari26 Understood! That makes sense to keep this PR focused on the variable refactoring.

Would you like me to create a GitHub issue to track the EditVariableValueDialog fix as a follow-up task? I can include the details about the non-functional component and reference this PR and comment.

Comment on lines +33 to +49
const getValueAndHash = (
value: string | number | boolean | object | null,
sensitive: boolean,
) => {
const stringifiedValue =
typeof value === "object" ? JSON.stringify(value) : String(value);

const valueHash = createHash("md5")
.update(stringifiedValue, "utf8")
.digest("hex");

const maybeEncryptedValue = sensitive
? variablesAES256().encrypt(stringifiedValue)
: value;

return { value: maybeEncryptedValue, valueHash };
};
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 2, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Security concern with MD5 and null value handling issue

  1. MD5 is cryptographically broken and shouldn't be used for security purposes. While it might be acceptable for deduplication, consider using SHA-256 for better collision resistance.

  2. The null value handling on line 38 will convert null to the string "null", which may not be the intended behavior.

-import { createHash } from "crypto";
+import { createHash } from "crypto";

 const getValueAndHash = (
   value: string | number | boolean | object | null,
   sensitive: boolean,
 ) => {
   const stringifiedValue =
-    typeof value === "object" ? JSON.stringify(value) : String(value);
+    value === null 
+      ? "null" 
+      : typeof value === "object" 
+        ? JSON.stringify(value) 
+        : String(value);

-  const valueHash = createHash("md5")
+  const valueHash = createHash("sha256")
     .update(stringifiedValue, "utf8")
     .digest("hex");

Consider if you really want null to be stringified as "null" or if it should be handled differently.

📝 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
const getValueAndHash = (
value: string | number | boolean | object | null,
sensitive: boolean,
) => {
const stringifiedValue =
typeof value === "object" ? JSON.stringify(value) : String(value);
const valueHash = createHash("md5")
.update(stringifiedValue, "utf8")
.digest("hex");
const maybeEncryptedValue = sensitive
? variablesAES256().encrypt(stringifiedValue)
: value;
return { value: maybeEncryptedValue, valueHash };
};
import { createHash } from "crypto";
const getValueAndHash = (
value: string | number | boolean | object | null,
sensitive: boolean,
) => {
const stringifiedValue =
value === null
? "null"
: typeof value === "object"
? JSON.stringify(value)
: String(value);
const valueHash = createHash("sha256")
.update(stringifiedValue, "utf8")
.digest("hex");
const maybeEncryptedValue = sensitive
? variablesAES256().encrypt(stringifiedValue)
: value;
return { value: maybeEncryptedValue, valueHash };
};
🤖 Prompt for AI Agents
In packages/db/src/utils/deployment-variables/upsert-direct-value.ts around
lines 33 to 49, replace the use of MD5 hashing with SHA-256 to improve security
and collision resistance. Also, modify the handling of null values so that null
is not converted to the string "null"; instead, handle null explicitly, for
example by returning null or an empty string before hashing and encryption,
depending on the intended logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we're only using it for deduplication

Copy link
Contributor

Choose a reason for hiding this comment

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

@adityachoudhari26 Thanks for the clarification! You're right that for deduplication purposes, MD5 can be acceptable since the primary concerns are performance and basic collision avoidance rather than cryptographic security. The actual security is handled separately by the AES256 encryption for sensitive values.

While SHA-256 would provide better collision resistance even for deduplication, I understand the performance trade-offs, especially if you're processing many variable values.

However, I'd still like to highlight the null value handling on line 38: String(null) will convert null to the string "null". Is this the intended behavior for your deduplication logic, or should null values be handled differently?


✏️ Learnings added
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#580
File: packages/db/src/utils/deployment-variables/upsert-direct-value.ts:33-49
Timestamp: 2025-06-02T17:54:43.093Z
Learning: In the ctrlplane codebase, MD5 hashing is acceptable for deduplication purposes in deployment variable values, as the actual security encryption is handled separately via AES256. The MD5 hash is used purely for checking if a variable value with the same content already exists to avoid duplicates.

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 Author

Choose a reason for hiding this comment

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

thats fine, because we want every null value to be treated the same, so they will still all hash to the same thing

Copy link
Contributor

Choose a reason for hiding this comment

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

@adityachoudhari26 Perfect! That makes complete sense for the deduplication logic. Having all null values consistently hash to the same value ensures proper deduplication behavior across deployment variables. Thanks for explaining the design rationale - that resolves my concern about the null value handling.


✏️ Learnings added
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#580
File: packages/db/src/utils/deployment-variables/upsert-direct-value.ts:33-49
Timestamp: 2025-06-02T17:59:53.630Z
Learning: In the ctrlplane codebase's deployment variable deduplication logic, null values are intentionally converted to the string "null" using String(null) so that all null values hash to the same value for consistent deduplication behavior across deployment variables.

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.

Comment on lines +54 to +65
.mutation(async ({ ctx, input }) => {
const { variableId, data } = input;
const variable = await ctx.db.query.deploymentVariable.findFirst({
where: eq(deploymentVariable.id, variableId),
});
if (variable == null) throw new Error("Variable not found");
const valueInsert = { ...data, variableId };
console.log(valueInsert);
// const value = await upsertVariableValue(ctx.db, valueInsert);
// await updateDeploymentVariableQueue.add(variableId, variable);
// return value;
}),
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 2, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Mutation is non-functional with commented out logic.

The value.create mutation currently only logs to console and doesn't perform any database operations or return a value. This breaks the API contract and will cause client errors.

The commented out code suggests incomplete refactoring. Either:

  1. Complete the refactoring to use the new upsert utilities
  2. Restore the previous working implementation
  3. Remove this mutation if it's no longer needed
🤖 Prompt for AI Agents
In packages/api/src/router/deployment-variable/deployment-variable.ts lines 54
to 65, the mutation currently only logs data and does not perform any database
operations or return a value, breaking the API contract. To fix this, either
uncomment and properly integrate the upsertVariableValue call and the
updateDeploymentVariableQueue addition to complete the mutation logic, ensuring
it performs the intended database update and returns the updated value, or if
refactoring is incomplete, restore the previous working implementation or remove
the mutation if it is no longer needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do in separate PR

Copy link
Contributor

Choose a reason for hiding this comment

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

@adityachoudhari26 Sounds good! Would you like me to create a GitHub issue to track fixing the non-functional value.create mutation as a follow-up task? This would help ensure it doesn't get forgotten during the refactoring process.

if (isDirect) expect(receivedDefaultValue!.value).toBe(valueB);
});

test("shoudl fail if more than one default value is provided", async ({
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in test name.

-  test("shoudl fail if more than one default value is provided", async ({
+  test("should fail if more than one default value is provided", async ({
📝 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
test("shoudl fail if more than one default value is provided", async ({
test("should fail if more than one default value is provided", async ({
🤖 Prompt for AI Agents
In e2e/tests/api/deployment-variable.spec.ts at line 218, there is a typo in the
test name "shoudl fail if more than one default value is provided". Correct the
typo by changing "shoudl" to "should" to fix the test name.

default: { type: "boolean" },
isDefault: { type: "boolean" },
},
required: ["resourceSelector"],
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 2, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify backward compatibility for required resourceSelector.

Making resourceSelector a required field in BaseDeploymentVariableValue could be a breaking change if existing data or API clients expect it to be optional. Please ensure this change is compatible with existing deployments or that proper migration is in place.


🏁 Script executed:

#!/bin/bash
# Check if there are any deployment variable values without resourceSelector in the codebase
rg -A 5 "deploymentVariableValue.*resourceSelector.*null|undefined" --type ts

Length of output: 67365


🏁 Script executed:

#!/bin/bash
# List all occurrences of resourceSelector in e2e API tests to identify where null/undefined is still used
rg "resourceSelector" -n e2e/tests/api

Length of output: 7516


ResourceSelector Required―Tests and Specs Need Updates

Adding resourceSelector as a required field in BaseDeploymentVariableValue is a breaking change: numerous e2e tests and YAML specs still pass null or undefined. Please update all affected tests and specs to include a valid resourceSelector or adjust the schema to provide a default.

Affected locations include, but are not limited to:

  • e2e/tests/api/release.spec.ts (many resourceSelector: null entries)
  • e2e/tests/api/deployment-variable.spec.ts (resourceSelector: null / expectations on null)
  • e2e/tests/api/environments.spec.ts (resourceSelector: undefined / .toBeNull() checks)
  • e2e/tests/api/deployments.spec.ts and other YAML-based spec files under e2e/tests/api

Ensure each test or spec supplies a valid selector or that the API gracefully handles missing selectors before merging.

🤖 Prompt for AI Agents
In
apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/openapi.ts
at line 21, making resourceSelector required in BaseDeploymentVariableValue is a
breaking change. To fix this, update all affected e2e tests and YAML specs that
currently pass null or undefined for resourceSelector by providing a valid
resourceSelector value. Alternatively, modify the schema to supply a default
resourceSelector or adjust the API to handle missing selectors gracefully,
ensuring backward compatibility before merging.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

its a required field, but its marked as nullable. so resourceSelector: null is valid

Copy link
Contributor

Choose a reason for hiding this comment

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

@adityachoudhari26 You're absolutely right! Thank you for the clarification.

I misunderstood the distinction between optional and required+nullable fields. In this schema:

  • required: ["resourceSelector"] means the field must be present in the object
  • nullable: true means the field's value can explicitly be null

So resourceSelector: null is indeed valid - the field is present (satisfying the required constraint) but has a null value (which is allowed). This maintains backward compatibility with existing tests and API usage.

This is not a breaking change as I initially thought. The schema correctly enforces that the field must be included while allowing null values.


✏️ Learnings added
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#580
File: apps/webservice/src/app/api/v1/deployments/[deploymentId]/variables/openapi.ts:21-21
Timestamp: 2025-06-02T17:55:33.871Z
Learning: In OpenAPI schemas, a field marked as both "required" and "nullable" means the field must be present in the request/response object, but its value can be explicitly set to null. This is different from optional fields where the field can be omitted entirely. For example, `resourceSelector: null` is valid when resourceSelector is required+nullable, but would be invalid if resourceSelector was required+non-nullable.

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.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@adityachoudhari26 adityachoudhari26 merged commit 99dcf43 into main Jun 2, 2025
10 checks passed
@adityachoudhari26 adityachoudhari26 deleted the refactor-upsert-deployment-variable branch June 2, 2025 18:05
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.

2 participants