Skip to content

Conversation

@jsbroks
Copy link
Member

@jsbroks jsbroks commented Apr 6, 2025

Summary by CodeRabbit

  • New Features
    • Introduced deployment version selector rules to enable version-based policy evaluation.
    • Added a new "Version Channels" section to the sidebar and a dedicated page displaying policies with version channels.
    • Integrated dialogs and tables for creating, editing, and managing version channel rules.
    • Enhanced API endpoints to support the creation, updating, and deletion of deployment version selectors.
  • Tests
    • Added comprehensive test suites to verify version selector rule logic.
  • Chores
    • Updated database schema, URL generation, and job processing to support the new features.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 6, 2025

Walkthrough

This pull request integrates deployment version selector functionality into the system. It adds a new rule in the rule engine, updates the API endpoints to manage version selectors, and revises database schema and relations. The UI is enhanced with new components and sidebar entries for version channels, while tests covering the new logic are introduced. Additionally, a minor update in job dispatch flow marks valid jobs as in progress once their variables are resolved.

Changes

File(s) Change Summary
packages/rule-engine/src/evaluate.ts
packages/rule-engine/src/rules/deployment-version-selector-rule.ts
packages/rule-engine/src/rules/__tests__/deployment-version-selector-rule.test.ts
packages/rule-engine/src/types.ts
Added new versionSelector function in evaluation logic; introduced DeploymentVersionSelectorRule class and tests; updated the Policy type to use PolicyRuleDeploymentVersionSelector.
apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/PolicyTable.tsx
apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/rule-themes.tsx
apps/webservice/src/app/[workspaceSlug]/(app)/policies/layout.tsx
apps/webservice/src/app/[workspaceSlug]/(app)/policies/page.tsx
Updated UI to include the new "version-selector" rule type; added sidebar group/link for "Deployment Rules" and updated rule counts on the policies page.
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx
apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/CreateRuleDialog.tsx
apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleDialog.tsx
apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleTable.tsx
Introduced new components and a dedicated page for managing version channel rules, including creation, editing, and deletion functionalities via dialogs.
packages/api/src/router/policy.ts
apps/webservice/src/app/urls.ts
Added new API endpoints to create, update, and delete deployment version selectors; extended URL builder with a dynamic path method for policies.
packages/db/drizzle/0085_demonic_union_jack.sql
packages/db/drizzle/meta/_journal.json
packages/db/src/schema/policy-relations.ts
packages/db/src/schema/policy.ts
Renamed the deployment version selector table to policy_rule_deployment_version_selector; updated constraints, foreign key relations, and schema definitions accordingly.
packages/job-dispatch/src/job-dispatch.ts Added new code in the dispatch method to update the status of valid jobs with resolved variables to JobStatus.InProgress.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant EvaluateRepo as EvaluateRepository
    participant Selector as versionSelector
    participant Rule as DeploymentVersionSelectorRule
    participant DB as Database

    Client->>EvaluateRepo: Request policy evaluation
    EvaluateRepo->>Selector: Invoke versionSelector(policy)
    Selector->>Rule: Create DeploymentVersionSelectorRule(getApplicableVersionIds)
    Rule->>Rule: Execute filter(context, releases)
    Rule->>DB: Query applicable version IDs
    DB-->>Rule: Return matching IDs
    Rule-->>Selector: Return filtered results
    Selector-->>EvaluateRepo: Append rule outcome
    EvaluateRepo-->>Client: Return evaluation result
Loading
sequenceDiagram
    participant User
    participant Dialog as VersionChannelRuleDialog
    participant API as PolicyRouter/API
    participant DB as Database

    User->>Dialog: Open creation/edit dialog
    Dialog->>API: Fetch eligible policies / Submit form data
    API->>DB: Create/Update deployment version selector
    DB-->>API: Confirm mutation
    API-->>Dialog: Return success response
    Dialog-->>User: Display success notification
Loading

Possibly related PRs

  • fix: Update frontend components for deployment version refactor #386: The changes in the main PR, which introduce a new function for handling deployment version selection and modifications to related rules, are related to the retrieved PR, which focuses on renaming components and variables to align with the deployment version context, indicating a shared focus on deployment versioning logic.
  • init deny rule #421: The changes in the main PR are related to the retrieved PR as both involve the introduction of new rules for deployment evaluation, specifically focusing on the handling of version selectors and deny rules within the rule engine. Both PRs modify or introduce classes and functions that are integral to the rule evaluation logic.
  • fix: Init release channel to dep vers channel frontend refactor #383: The changes in the main PR, which introduce a new function for handling deployment version selectors and modify the evaluation logic accordingly, are related to the retrieved PR, which refactors components to align with the new deployment version channel terminology and functionality. Both PRs focus on the same underlying concept of deployment version channels, indicating a direct connection at the code level.

Suggested reviewers

  • jsbroks

Poem

I'm a hopping rabbit, quick on my feet,
Code fields have grown with a brand new beat.
Version selectors now lead the way,
In rules and schemas, they brightly play.
With each new line, I cheer and smile,
Hop along, coding in rabbit style!
🐰💻


📜 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 03afd66 and 7b6476c.

📒 Files selected for processing (1)
  • packages/db/drizzle/meta/_journal.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/db/drizzle/meta/_journal.json
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
  • GitHub Check: Typecheck

🪧 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 plan to trigger planning for file edits and PR creation.
  • @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: 0

🧹 Nitpick comments (2)
packages/rule-engine/src/evaluate.ts (1)

99-99: Consider ordering rules carefully.

Combining denyWindows rules before or after the version selector may affect which releases are excluded first. If the evaluation order matters, consider reordering the spread arrays to reflect the intended logic.

packages/rule-engine/src/rules/deployment-version-selector-rule.ts (1)

49-66: Potential performance concerns with large version sets.

inArray with many IDs could become costly. If dealing with large version lists, consider chunking or indexing to optimize DB queries.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between afdeed7 and 43fe882.

📒 Files selected for processing (2)
  • packages/rule-engine/src/evaluate.ts (3 hunks)
  • packages/rule-engine/src/rules/deployment-version-selector-rule.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/rule-engine/src/evaluate.ts
  • packages/rule-engine/src/rules/deployment-version-selector-rule.ts
🧬 Code Definitions (1)
packages/rule-engine/src/evaluate.ts (2)
packages/rule-engine/src/types.ts (1)
  • Policy (66-69)
packages/rule-engine/src/rules/deployment-version-selector-rule.ts (2)
  • DeploymentVersionSelectorRule (20-47)
  • getApplicableVersionIds (49-66)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
🔇 Additional comments (7)
packages/rule-engine/src/evaluate.ts (2)

9-12: Looks good on the new imports.

Importing DeploymentVersionSelectorRule and getApplicableVersionIds here cleanly integrates the version selector logic. No issues noted.


26-34: Function name clarity.

The versionSelector function is straightforward, returning a new rule instance if policy is not null. The approach is concise and easy to maintain.

packages/rule-engine/src/rules/deployment-version-selector-rule.ts (5)

1-4: Database imports usage is acceptable.

Imports from @ctrlplane/db and @ctrlplane/db/client appear safe. No injection concerns noted given the parameterized usage in queries.


5-11: Relevant type imports established.

The imported types ensure clarity in function signatures and rule definitions.


13-13: Rejection reason defined.

A concise, meaningful constant for rejection. Future customization/localization might be desirable, but this is fine for now.


15-18: Type signature is flexible.

Allowing both Promise<string[]> and string[] return types provides versatility for synchronous or asynchronous retrieval. Make sure all consumers handle both cases.


20-47: Rule implementation is solid.

Implements the DeploymentResourceRule interface cleanly. The filtering logic is straightforward, maintaining rejection reasons in a map. Overall, well-structured.

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: 2

🧹 Nitpick comments (5)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/page.tsx (1)

23-37: Version selectors page implementation

The VersionSelectorsPage component correctly fetches workspace data and policies, with proper error handling for the not found case. The filtering of policies to include only those with a deploymentVersionSelector is appropriate.

Consider removing the eslint-disable comment on line 35 by using a more type-safe approach:

-    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
-    (p) => p.deploymentVersionSelector,
+    (p) => p.deploymentVersionSelector !== null && p.deploymentVersionSelector !== undefined,
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/_components/VersionSelectorTable.tsx (2)

29-43: Consider using specific types instead of any

The deploymentVersionSelector property is typed as any, which reduces type safety. Consider defining a more specific type structure based on the actual data shape.

type DeploymentVersionSelector = {
  id: string;
  name: string;
  description: string | null;
-  deploymentVersionSelector: any;
+  deploymentVersionSelector: {
+    type: string;
+    operator?: string;
+    conditions?: Array<unknown>;
+    value?: string;
+    key?: string;
+  };
};

49-91: Improve readability of condition summary rendering

The renderConditionSummary function uses nested ternary operators in multiple places, which can make the code harder to read and maintain. Consider refactoring to use more explicit if statements or switch cases.

function renderConditionSummary(condition: any): string {
  if (!condition) return "No conditions";

  // If it's a comparison condition (and/or)
  if (condition.type === "comparison") {
    const operator = condition.operator === "and" ? "AND" : "OR";
    if (condition.conditions.length === 0) return "Empty condition";
    return `${operator} (${condition.conditions.length} condition${condition.conditions.length !== 1 ? "s" : ""})`;
  }

  // For tag conditions
  if (condition.type === "tag") {
    const op = condition.operator === "equals" ? "=" : "~";
    return `tag ${op} ${condition.value}`;
  }

  // For metadata conditions
  if (condition.type === "metadata") {
    const op = condition.operator === "equals" ? "=" : "~";
    return `metadata.${condition.key} ${op} ${condition.value}`;
  }

  // For version conditions
  if (condition.type === "version") {
    const op = condition.operator === "equals" ? "=" : "~";
    return `version ${op} ${condition.value}`;
  }

  // For created date conditions
  if (condition.type === "created-at") {
-    const op =
-      condition.operator === "before"
-        ? "<"
-        : condition.operator === "after"
-          ? ">"
-          : condition.operator === "before-or-on"
-            ? "<="
-            : ">=";
+    let op;
+    switch (condition.operator) {
+      case "before":
+        op = "<";
+        break;
+      case "after":
+        op = ">";
+        break;
+      case "before-or-on":
+        op = "<=";
+        break;
+      default: // assuming "after-or-on"
+        op = ">=";
+        break;
+    }
    return `created ${op} ${new Date(condition.value).toLocaleDateString()}`;
  }

  return "Complex condition";
}
packages/rule-engine/src/rules/__tests__/deployment-version-selector-rule.test.ts (2)

122-131: Consider adding more test cases for getApplicableVersionIds.

While you've tested the null selector case, consider adding tests for:

  1. Empty version IDs array
  2. A selector that returns an empty array
  3. Error handling scenarios where the function throws an exception
// Example additional test cases:
it("should handle empty version IDs array", () => {
  const func = getApplicableVersionIds(null);
  const result = func(context, []);
  expect(result).toEqual([]);
});

it("should handle selector returning empty array", () => {
  const mockSelector = {
    // Configuration for a selector that returns no versions
  };
  const func = getApplicableVersionIds(mockSelector);
  const result = func(context, ["ver-1", "ver-2"]);
  expect(result).toEqual([]);
});

it("should handle errors in selector processing", async () => {
  const errorSelector = {
    // Configuration that would cause an error
  };
  const func = getApplicableVersionIds(errorSelector);
  
  // Test depending on your error handling approach
  await expect(func(context, ["ver-1"])).rejects.toThrow();
  // Or if you handle errors by returning all versions:
  // expect(await func(context, ["ver-1"])).toEqual(["ver-1"]);
});

1-132: Consider adding an integration test with a real selector configuration.

The current tests focus on mocking getApplicableVersionIds behavior, but an integration test with a realistic selector configuration would verify that the actual logic for parsing and applying selectors works correctly.

it("should apply a real version selector configuration", async () => {
  // Create a real selector configuration
  const versionSelector = {
    versions: ["v1.0.0"], // or whatever format your selector uses
  };
  
  // Use the actual getApplicableVersionIds function
  const rule = new DeploymentVersionSelectorRule(
    getApplicableVersionIds(versionSelector)
  );
  
  const result = await rule.filter(context, releases);
  
  // Verify expected behavior
  expect(result.allowedReleases.length).toBe(1);
  expect(result.allowedReleases.at(0)?.id).toBe("rel-1");
});
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 43fe882 and e97c0f1.

📒 Files selected for processing (9)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/PolicyTable.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/rule-themes.tsx (4 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/layout.tsx (2 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/page.tsx (3 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/_components/VersionSelectorTable.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/page.tsx (1 hunks)
  • apps/webservice/src/app/urls.ts (1 hunks)
  • packages/api/src/router/policy.ts (2 hunks)
  • packages/rule-engine/src/rules/__tests__/deployment-version-selector-rule.test.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.

  • apps/webservice/src/app/urls.ts
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/page.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/PolicyTable.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/rule-themes.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/page.tsx
  • packages/api/src/router/policy.ts
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/_components/VersionSelectorTable.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/layout.tsx
  • packages/rule-engine/src/rules/__tests__/deployment-version-selector-rule.test.ts
🧬 Code Definitions (3)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/page.tsx (1)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/_components/VersionSelectorTable.tsx (1)
  • VersionSelectorTable (93-192)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/_components/VersionSelectorTable.tsx (1)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/rule-themes.tsx (1)
  • getTypeColorClass (61-82)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/layout.tsx (2)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/SidebarLink.tsx (1)
  • SidebarLink (9-32)
apps/webservice/src/app/urls.ts (1)
  • urls (214-214)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (23)
apps/webservice/src/app/urls.ts (1)

48-48: Good addition for enhancing URL construction flexibility.

The path method provides a clean way to construct policy URLs with any subpath, which is useful for the new version selector feature.

apps/webservice/src/app/[workspaceSlug]/(app)/policies/layout.tsx (2)

11-11: LGTM: New icon import for version selectors.

The IconTag import is appropriately added for the new version selectors feature.


79-92: Well-structured sidebar addition for version selectors.

The new sidebar group for "Deployment Rules" with the "Version Selectors" link follows the existing pattern and correctly uses the newly added path method from the URL utilities.

apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/PolicyTable.tsx (1)

38-39: LGTM: Added version selector rule detection.

The condition to check for policy.deploymentVersionSelector follows the same pattern as the existing deny window check, and the ESLint disable comment is appropriate since TypeScript might be inferring the property is always defined.

apps/webservice/src/app/[workspaceSlug]/(app)/policies/page.tsx (3)

12-12: LGTM: Added icon import for version selectors.

The IconTag import is correctly added to support the new version selectors UI element.


54-56: LGTM: Added count for version selectors.

The count calculation for version selectors correctly filters policies with the deploymentVersionSelector property.


75-81: Well-structured addition to rule categories.

The "Version Selectors" category is properly integrated into the existing ruleCategories array with appropriate title, icon, count reference, and description.

packages/api/src/router/policy.ts (2)

32-36: Policy queries now include deploymentVersionSelector

The addition of deploymentVersionSelector to the with clause in the list query is a good enhancement that supports the version selector functionality. This change ensures that when policies are fetched, their associated version selector data is included in the response.


49-53: deploymentVersionSelector correctly added to byId query

Similar to the list query, the byId query now includes the deploymentVersionSelector in its with clause. This change maintains consistency between the two query methods and ensures that version selector data is available when fetching a specific policy.

apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/rule-themes.tsx (5)

9-9: Added IconTag import for the version selector icon

The IconTag is appropriately imported to represent the version selector visually.


12-12: RuleType type updated to include version-selector

The RuleType has been properly extended to include "version-selector" as a specific string literal type, which helps with type safety throughout the application.


30-31: Added icon representation for version-selector

The version-selector icon implementation follows the same pattern as other rule types, using the IconTag with appropriate styling.


53-54: Added label for version-selector

The human-readable label "Version Selector" has been added for the version-selector rule type, maintaining consistency with the labeling approach used for other rule types.


77-78: Added styling for version-selector

The color class implementation for version-selector follows the established pattern with a distinct indigo color scheme, providing visual differentiation from other rule types.

apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/page.tsx (2)

75-95: Well-structured explanatory card

The explanatory card provides clear information about version selectors, which is helpful for users. The use of IconTag maintains consistent visual language with the rule-themes definitions.


97-97: Passing filtered policies to table component

The VersionSelectorTable component is correctly used with the filtered policies passed as props.

apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-selectors/_components/VersionSelectorTable.tsx (2)

156-160: Disabled switch component

The Switch component for policy status is disabled. If this is intentional (for a read-only view), consider adding a tooltip to explain why the status cannot be changed, or implement the toggle functionality if it's meant to be interactive.

Is the disabled state of the Switch component intentional? If enabling/disabling policies is supported elsewhere in the application, this should be consistent.


138-153: Good use of Tooltip for condition details

The tooltip showing the full JSON structure of the condition provides good developer/power-user experience while keeping the UI clean with a summarized version visible by default.

packages/rule-engine/src/rules/__tests__/deployment-version-selector-rule.test.ts (5)

1-12: Imports look well-structured.

The imports are properly organized, separating test utilities, types, and the components under test.


14-63: Test fixtures are comprehensive and well-structured.

The test setup provides a good foundation with realistic test data. The sample releases have different version IDs, tags, and metadata, which will help test various filtering scenarios.


65-82: Good test case for the default behavior.

This test properly verifies that all releases are allowed when no version selector is provided, which is an important edge case to cover.


84-100: Well-structured test for core filtering functionality.

This test effectively verifies that releases are properly filtered based on the version selector, including checking rejection reasons.


102-120: Good coverage of async behavior.

Testing the asynchronous nature of the getApplicableVersionIds function ensures that the rule will work correctly in real-world scenarios where database queries might be involved.

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: 2

🧹 Nitpick comments (2)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx (1)

52-114: Consider adding a loading state while data is being fetched.

Since this component fetches data asynchronously, it's good practice to show a loading state while waiting for the data. Next.js supports React Suspense boundaries which can be used to show a loading UI.

// In a parent layout file or in this component
+import { Suspense } from "react";
+import { Skeleton } from "@ctrlplane/ui/skeleton";

// Wrap the component or relevant parts with Suspense
+<Suspense fallback={<VersionChannelsPageSkeleton />}>
   <VersionChannelsPage params={params} />
+</Suspense>

// Create a skeleton component
+function VersionChannelsPageSkeleton() {
+  return (
+    <div className="flex h-full flex-col">
+      <div className="flex items-center gap-2 p-4 border-b">
+        <Skeleton className="h-4 w-40" />
+      </div>
+      <div className="p-6">
+        <Skeleton className="h-6 w-48 mb-2" />
+        <Skeleton className="h-4 w-96 mb-6" />
+        <Skeleton className="h-32 w-full mb-8" />
+        <Skeleton className="h-64 w-full" />
+      </div>
+    </div>
+  );
+}
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/_components/VersionChannelTable.tsx (1)

117-118: Clarify the nested deploymentVersionSelector property naming.

The property access policy.deploymentVersionSelector.deploymentVersionSelector appears redundant and potentially confusing. Consider renaming one of these for clarity or adding a comment explaining this structure:

-          const condition =
-            policy.deploymentVersionSelector.deploymentVersionSelector;
+          // The outer deploymentVersionSelector is the object, 
+          // inner one is the actual rule condition
+          const condition = policy.deploymentVersionSelector.deploymentVersionSelector;

// Or alternatively, rename for clarity:
+          const versionSelectorRule = policy.deploymentVersionSelector.deploymentVersionSelector;
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e97c0f1 and b5f9388.

📒 Files selected for processing (4)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/layout.tsx (2 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/page.tsx (4 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/_components/VersionChannelTable.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/layout.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/page.tsx
🧰 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.

  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/_components/VersionChannelTable.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx
🧬 Code Definitions (1)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx (1)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/_components/VersionChannelTable.tsx (1)
  • VersionChannelTable (87-188)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (6)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx (2)

76-79: Add functionality to the "Create Version Channel Rule" button.

The "Create Version Channel Rule" button is currently static with no click handler or navigation link. Consider implementing the intended functionality:

-          <Button variant="outline" size="sm">
+          <Button 
+            variant="outline" 
+            size="sm"
+            asChild
+          >
+            <Link href={`/${workspaceSlug}/policies/create?type=version-channel`}>
               <IconPlus className="mr-2 h-4 w-4" />
               Create Version Channel Rule
+            </Link>
           </Button>

Or if it should trigger a modal/dialog:

-          <Button variant="outline" size="sm">
+          <Button 
+            variant="outline" 
+            size="sm"
+            onClick={() => {
+              // Open modal or navigate to creation page
+            }}
+          >
             <IconPlus className="mr-2 h-4 w-4" />
             Create Version Channel Rule
           </Button>

112-112: Good use of the filtered policies to display version channels.

The component correctly filters the policies to only show those with version channels and passes them to the VersionChannelTable component. This ensures users only see relevant information.

apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/_components/VersionChannelTable.tsx (4)

174-178: Implement removal of channel rule or disable the menu item.

The "Remove Channel Rule" dropdown item has a TODO comment and doesn't have any functionality. Either:

  1. Implement the removal functionality:
-                    <DropdownMenuItem className="text-destructive focus:text-destructive">
-                      <Trash2 className="mr-2 h-4 w-4" />
-                      Remove Channel Rule
-                      {/* TODO: Implement removal of just the channel rule */}
+                    <DropdownMenuItem 
+                      className="text-destructive focus:text-destructive"
+                      onClick={() => handleRemoveChannelRule(policy.id)}
+                    >
+                      <Trash2 className="mr-2 h-4 w-4" />
+                      Remove Channel Rule
                     </DropdownMenuItem>
  1. Or disable the menu item until implementation is complete:
-                    <DropdownMenuItem className="text-destructive focus:text-destructive">
+                    <DropdownMenuItem 
+                      className="text-destructive focus:text-destructive"
+                      disabled
+                      title="Coming soon"
+                    >
                       <Trash2 className="mr-2 h-4 w-4" />
                       Remove Channel Rule
                       {/* TODO: Implement removal of just the channel rule */}
                     </DropdownMenuItem>

94-101: Great empty state handling with appropriate UI feedback.

The component correctly handles the case when no policies with version channels are found, providing clear feedback to the user with a well-designed empty state.


131-146: Well-implemented tooltip for displaying complex condition details.

The use of tooltips to show the full JSON representation of the condition while displaying a more user-friendly summary in the table is an excellent UX decision. It allows users to see detailed information without cluttering the UI.


43-85: Robust condition summary function supporting multiple condition types.

The renderConditionSummary function handles a variety of condition types with appropriate formatting. The function is well-structured with clear logic for each condition type, making it maintainable and extensible.

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)
packages/rule-engine/src/evaluate.ts (1)

32-150: Consider documenting the new versionSelector function and rule evaluation order.

The new versionSelector function lacks documentation comments, unlike some other functions in the file. Also, the order of rules in the evaluation pipeline may be significant, but there's no documentation explaining the reasoning for this specific sequence.

Consider adding JSDoc comments to the new function and explaining the rule evaluation order:

+/**
+ * Creates deployment version selector rules based on the provided policy.
+ * 
+ * @param policy - The policy containing deployment version selector configuration
+ * @returns An array of DeploymentVersionSelectorRule instances, or an empty array if the policy is null
+ */
const versionSelector = (policy: Policy | null) =>
  policy == null
    ? []
    : [
        new DeploymentVersionSelectorRule(
          getApplicableVersionIds(policy.deploymentVersionSelector),
        ),
      ];

// Later in evaluateRepository
+  // Rules are evaluated in order: deny windows first, then version selectors, 
+  // followed by various approval rules
  const rules = [
    ...denyWindows(policy),
    ...versionSelector(policy),
    ...versionUserApprovalRule(policy?.versionUserApprovals),
    ...versionAnyApprovalRule(policy?.versionAnyApprovals),
    ...versionRoleApprovalRule(policy?.versionRoleApprovals),
  ];
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 78d486a and d63fd5d.

📒 Files selected for processing (3)
  • apps/event-worker/src/workers/job-dispatch/github.ts (1 hunks)
  • packages/job-dispatch/src/job-dispatch.ts (1 hunks)
  • packages/rule-engine/src/evaluate.ts (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/job-dispatch/src/job-dispatch.ts
🧰 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/rule-engine/src/evaluate.ts
  • apps/event-worker/src/workers/job-dispatch/github.ts
🧬 Code Definitions (2)
packages/rule-engine/src/evaluate.ts (2)
packages/rule-engine/src/types.ts (1)
  • Policy (66-72)
packages/rule-engine/src/rules/deployment-version-selector-rule.ts (2)
  • DeploymentVersionSelectorRule (20-47)
  • getApplicableVersionIds (49-66)
apps/event-worker/src/workers/job-dispatch/github.ts (2)
apps/webservice/src/app/api/github/octokit.ts (1)
  • octokit (12-22)
packages/db/src/schema/job.ts (1)
  • JobStatus (136-136)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (4)
apps/event-worker/src/workers/job-dispatch/github.ts (1)

177-190: Good improvement to job status tracking.

The change from a direct await to a Promise chain adds the crucial step of updating the job status to InProgress upon successful workflow dispatch. This improves the job lifecycle tracking without sacrificing error handling.

The implementation:

  1. Maintains clear and predictable control flow using Promise chains
  2. Preserves error handling via the surrounding try/catch block
  3. Properly updates job state at the appropriate time in the workflow

This change aligns with the provided coding guidelines which explicitly allow Promise chains as an acceptable pattern.

packages/rule-engine/src/evaluate.ts (3)

9-12: Import declaration follows established patterns.

The new imports for DeploymentVersionSelectorRule and getApplicableVersionIds are structured correctly and follow the existing import pattern in the file.


32-39: LGTM: Well-implemented version selector function.

The new versionSelector function follows the same pattern as other rule generator functions in the file:

  • Takes a nullable policy parameter
  • Returns an empty array for null policies
  • Creates the appropriate rule instance when a policy is provided
  • Properly handles the policy.deploymentVersionSelector property

The implementation is concise and aligns well with the codebase style.


146-146: Correctly integrates version selector rules.

The new version selector rules are properly integrated into the rule engine evaluation pipeline, placed appropriately in the sequence of rules to be evaluated.

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 (3)
packages/api/src/router/policy.ts (3)

230-246: Consider unique constraint collisions.
These lines introduce the createDeploymentVersionSelector endpoint. Because policyId is unique in policy_rule_deployment_version_selector, multiple inserts for the same policy will likely fail with a database constraint error. Consider handling this gracefully (e.g., returning a user-facing message) if attempting to create more than one version selector per policy.


248-271: Validate existence of the record when updating.
Currently, if no deployment version selector record exists for the specified policyId, the DB update will return an empty array, causing .then(takeFirst) to throw. Consider handling this case gracefully and returning a clear error or fallback response if the record doesn't exist.


273-288: Handle a missing record scenario when deleting.
If a version selector does not exist for the specified policyId, the delete query could return an empty result, which again triggers .then(takeFirst) to throw. You might want to return a 404 or a user-friendly message in such a case.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d8ffbe5 and d53e462.

📒 Files selected for processing (2)
  • packages/api/src/router/policy.ts (4 hunks)
  • packages/db/src/schema/policy.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/db/src/schema/policy.ts
🧰 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/api/src/router/policy.ts
🧬 Code Definitions (1)
packages/api/src/router/policy.ts (3)
packages/api/src/trpc.ts (1)
  • protectedProcedure (150-150)
packages/db/src/schema/policy.ts (3)
  • createPolicyRuleDeploymentVersionSelector (114-115)
  • policyRuleDeploymentVersionSelector (55-74)
  • updatePolicyRuleDeploymentVersionSelector (120-121)
packages/db/src/common.ts (1)
  • takeFirst (9-13)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
🔇 Additional comments (5)
packages/api/src/router/policy.ts (5)

8-8: Import usage looks consistent.
The createPolicyRuleDeploymentVersionSelector import is properly referenced later in the code. No immediate issues found.


12-12: Import usage looks consistent.
The policyRuleDeploymentVersionSelector import is used as expected. No issues found.


16-16: Import usage looks consistent.
The updatePolicyRuleDeploymentVersionSelector import is referenced correctly in the update method.


35-39: No concerns with including deploymentVersionSelector in the policy list query.
The addition of deploymentVersionSelector: true is consistent and appears to align with retrieving relevant policy data in one query.


52-56: Ensures consistency with the policy retrieval by ID.
Including deploymentVersionSelector in the with clause mirrors the pattern used in the list query. This keeps retrieval consistent.

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 (6)
apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleDialog.tsx (4)

92-93: Remove or implement the TODO comment.

There's a TODO comment about adding a createMutation if needed. Since this component is for editing an existing rule, it's unlikely that you need a createMutation here. Consider removing this comment if it's no longer relevant.

-  // TODO: Add createMutation if needed

99-101: Clarify the comment about create vs. update logic.

The comment suggests uncertainty about whether to create or update based on the initial state. Since this is specifically an edit dialog for an existing rule (as indicated by the component name and the fact that it receives an existing policy), the logic should always be update. Consider removing this comment to avoid confusion.

-    // Decide whether to create or update based on initial state?
-    // For now, assuming update as it's triggered from an existing rule
     updateMutation.mutate({

198-198: Remove unnecessary Fragment.

The Fragment element is redundant since it contains no children. You can remove it and use null or an empty string instead.

-          <></>
+          {null}
🧰 Tools
🪛 Biome (1.9.4)

[error] 198-198: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


82-83: Consider uncommenting the invalidation call or remove if unnecessary.

There's a commented-out invalidation call for the specific policy. If this invalidation is necessary to ensure UI consistency after updates, consider uncommenting it. Otherwise, if it's truly unnecessary, the comment should be removed to keep the codebase clean.

-        // Consider invalidating policy.byId if used elsewhere
-        // utils.policy.byId.invalidate({ id: policy.id });
+        utils.policy.byId.invalidate(policy.id);
apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleTable.tsx (2)

63-64: Consider more specific invalidation.

The comment suggests being more specific with invalidation queries by using workspaceId if the list was fetched with it. This would be more efficient than invalidating all policy lists. Consider implementing this optimization if possible.

-        // Consider invalidating by workspaceId if list was fetched with it
-        queryClient.invalidateQueries({ queryKey: listPoliciesQueryKey });
+        // Assuming policies are fetched with workspaceId
+        if (policies.length > 0) {
+          const workspaceId = policies[0].workspaceId;
+          queryClient.invalidateQueries({ 
+            queryKey: [...listPoliciesQueryKey, workspaceId] 
+          });
+        } else {
+          queryClient.invalidateQueries({ queryKey: listPoliciesQueryKey });
+        }

80-88: Consider removing redundant empty state handling.

The comment suggests that this empty state handling is already managed by the parent page component. If that's the case, consider removing this check to reduce code duplication and maintain a single source of truth for empty state handling.

-  if (policies.length === 0) {
-    // This case should technically be handled by the parent page now,
-    // but include a fallback just in case.
-    return (
-      <p className="p-6 text-center text-sm text-muted-foreground">
-        No policies with Version Selector rules found.
-      </p>
-    );
-  }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5a9b8cb and 03afd66.

📒 Files selected for processing (6)
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/CreateRuleDialog.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleDialog.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleTable.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/PolicyTable.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/page.tsx (4 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/_components/PolicyTable.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/page.tsx
🧰 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.

  • apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleTable.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleDialog.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/CreateRuleDialog.tsx
🪛 Biome (1.9.4)
apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/RuleDialog.tsx

[error] 198-198: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (2)
apps/webservice/src/app/[workspaceSlug]/(app)/policies/version-channels/page.tsx (1)

1-120: LGTM! Well-structured component with proper error handling.

The component is well organized with clear separation of concerns between the main page and the data rendering component. It handles loading states appropriately with Skeleton components and provides meaningful feedback for edge cases (no policies found).

While there's no explicit try/catch blocks for API calls, the component does handle loading and error states through conditional rendering, which aligns with the coding guidelines that allow for alternative error handling patterns beyond try/catch blocks.

apps/webservice/src/app/[workspaceSlug]/(app)/_components/policy/version-selector/CreateRuleDialog.tsx (1)

1-255: LGTM! Well-implemented form dialog with proper validation and error handling.

The component follows best practices for React form implementation:

  • Uses react-hook-form with Zod for validation
  • Handles loading and error states appropriately
  • Provides clear user feedback through toast notifications
  • Properly resets form state when the dialog is closed

The form submission logic correctly validates that a valid deployment version condition is set before proceeding with the mutation, and the UI is clear and intuitive.

@jsbroks jsbroks closed this Apr 9, 2025
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