Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Hooks reference resource + trigger hooks on filter change for env #220

Merged
merged 3 commits into from
Nov 18, 2024

Conversation

adityachoudhari26
Copy link
Contributor

@adityachoudhari26 adityachoudhari26 commented Nov 18, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced resource filtering and event handling during environment updates.
    • Introduced new event handling for resource deletions, improving responsiveness.
    • Streamlined event handling logic focused solely on resource removal.
  • Bug Fixes

    • Updated event handling functions to maintain consistency with new naming conventions.
  • Documentation

    • Updated validation schemas to reflect changes from "target" to "resource."
  • Chores

    • Renamed various functions and variables for clarity and consistency across the codebase.

Copy link
Contributor

coderabbitai bot commented Nov 18, 2024

Walkthrough

The pull request implements significant modifications across multiple files to enhance resource filtering and event handling in the context of environment updates. Key changes include the introduction of new types and functions related to resource management, renaming of existing functions and variables to reflect a shift from "target" to "resource," and updates to event handling logic. The updates ensure that the system accurately tracks and manages resources, generating appropriate events during resource deletions and updates.

Changes

File Path Change Summary
packages/api/src/router/environment.ts Enhanced resource filtering and event handling in the update method, added checks for resource filter changes, created sysFilter for removed resources, and dispatched jobs for new resources.
packages/api/src/router/resources.ts Renamed import and usage of getEventsForTargetDeleted to getEventsForResourceDeleted in the resourceRouter deletion logic.
packages/events/src/handlers/target-removed.ts Renamed handleTargetRemoved to handleResourceRemoved, updated parameter types and variable names to reflect the resource context.
packages/events/src/index.ts Replaced isTargetRemoved with isResourceRemoved, updated event handling logic to reflect the new resource terminology, and simplified error handling.
packages/events/src/triggers/deployment-deleted.ts Renamed variables from targets to resources, updated action strings and payload structures to align with the new terminology.
packages/events/src/triggers/environment-deleted.ts Updated variable names from targets to resources and adjusted logic accordingly to maintain consistency with new naming conventions.
packages/events/src/triggers/target-deleted.ts Renamed function and parameter from getEventsForTargetDeleted to getEventsForResourceDeleted, updated variable names and action strings to reflect the resource context.
packages/job-dispatch/src/resource.ts Modified deleteResources to include event handling for deleted resources, added logic for fetching resources, and integrated new event functions.
packages/validators/src/events/hooks/index.ts Replaced target-related types and events with ResourceRemoved, simplified event handling logic, and introduced a new type guard function.
packages/validators/src/events/hooks/target.ts Renamed schemas from target to resource, updated action strings, and removed obsolete types related to targets.

Possibly related PRs

Suggested reviewers

  • jsbroks

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • 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 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: 6

🧹 Outside diff range and nitpick comments (12)
packages/validators/src/events/hooks/target.ts (1)

4-8: Consider adding type constraints to config field

The schema structure looks good, and the renaming from 'target' to 'resource' is consistent with the PR objectives. However, using z.record(z.any()) for config provides maximum flexibility at the cost of type safety.

Consider defining a more specific schema for the config field if there are known common properties or types:

const resource = z.object({
  id: z.string().uuid(),
  name: z.string(),
-  config: z.record(z.any()),
+  config: z.record(z.union([z.string(), z.number(), z.boolean()])),
});
packages/validators/src/events/hooks/index.ts (1)

3-4: Consider renaming the target.js file to maintain consistency

The imports are from a file named "target.js" but the types have been renamed to use "resource" terminology. This naming inconsistency could cause confusion.

Consider renaming "target.js" to "resource.js" to maintain consistency with the new terminology.

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

14-15: Consider improving error handling and type safety.

Two suggestions for improvement:

  1. The simplified error message Unhandled event provides less context than before. Consider including the event type or action for easier debugging.
  2. Add type narrowing validation after the condition to ensure type safety.
-  if (isResourceRemoved(event)) return handleResourceRemoved(event);
-  throw new Error(`Unhandled event`);
+  if (isResourceRemoved(event)) {
+    return handleResourceRemoved(event);
+  }
+  throw new Error(`Unhandled event type: ${event.type}`);
packages/events/src/handlers/target-removed.ts (2)

1-7: File name needs to be updated to match the new terminology

The file is still named target-removed.ts while the codebase has moved to use "resource" terminology. Consider renaming the file to resource-removed.ts for consistency.


22-28: Consider adding error handling and logging

The runbook dispatch could benefit from:

  1. Try-catch blocks to handle potential dispatch failures
  2. Logging for monitoring and debugging purposes

Here's a suggested improvement:

  const resourceId = resource.id;
  const deploymentId = deployment.id;
- const handleRunhooksPromises = runhooks.map(({ runhook }) =>
-   dispatchRunbook(db, runhook.runbookId, { resourceId, deploymentId }),
- );
+ const handleRunhooksPromises = runhooks.map(async ({ runhook }) => {
+   try {
+     console.log(`Dispatching runbook ${runhook.runbookId} for resource ${resourceId}`);
+     await dispatchRunbook(db, runhook.runbookId, { resourceId, deploymentId });
+   } catch (error) {
+     console.error(`Failed to dispatch runbook ${runhook.runbookId}:`, error);
+     throw error;
+   }
+ });

  await Promise.all(handleRunhooksPromises);
packages/events/src/triggers/target-deleted.ts (1)

39-42: Consider enhancing error handling and logging.

While the null check is present, consider adding explicit error handling and logging for better debugging capabilities.

-    const matchedResource = await db.query.resource.findFirst({
-      where: SCHEMA.resourceMatchesMetadata(db, systemFilter),
-    });
-    if (matchedResource == null) return [];
+    try {
+      const matchedResource = await db.query.resource.findFirst({
+        where: SCHEMA.resourceMatchesMetadata(db, systemFilter),
+      });
+      if (matchedResource == null) {
+        console.debug('No matching resource found for system filter');
+        return [];
+      }
+    } catch (error) {
+      console.error('Failed to query resource:', error);
+      throw error;
+    }
packages/events/src/triggers/environment-deleted.ts (1)

Line range hint 42-56: Add documentation for complex filtering logic

While the implementation is correct, the complex filtering logic to determine system-wide resource removal could benefit from documentation.

Consider adding comments to explain the purpose:

+ // Create a negative filter that excludes resources still referenced by other environments
  const removedFromSystemFilter: ResourceCondition = {
    type: ResourceFilterType.Comparison,
    operator: ComparisonOperator.Or,
    not: true,
    conditions: envFilters,
  };

+ // Query resources that are both matched by the environment filter and not referenced elsewhere
  const removedFromSystemResources =
    envFilters.length > 0
      ? await db
packages/api/src/router/resources.ts (1)

585-587: Consider adding error logging for failed promises

While the error handling using Promise.allSettled is robust, adding error logging for failed promises would help with debugging and monitoring. Consider logging the rejected promises:

-      const events = (
-        await Promise.allSettled(resources.map(getEventsForResourceDeleted))
-      ).flatMap((r) => (r.status === "fulfilled" ? r.value : []));
+      const results = await Promise.allSettled(resources.map(getEventsForResourceDeleted));
+      const events = results.flatMap((r) => {
+        if (r.status === "rejected") {
+          console.error("Failed to get events for resource deletion:", r.reason);
+          return [];
+        }
+        return r.value;
+      });
packages/api/src/router/environment.ts (4)

244-247: Clarify Variable Scope and Naming

Consider renaming the isOtherEnv variable to otherEnvCondition for better clarity, as it represents a condition used in a query, not a boolean value.

Apply this diff to rename the variable:

-              const isOtherEnv = and(
+              const otherEnvCondition = and(

283-310: Ensure Proper Handling of Empty removedFromSystemResources

After fetching removedFromSystemResources, it's good practice to check if the array is not empty before proceeding to generate events and handle them. This avoids unnecessary processing when there are no resources to handle.

Add a condition to check before proceeding:

                const removedFromSystemResources =
                  await ctx.db.query.resource.findMany({
                    where: and(
                      inArray(
                        resource.id,
                        removedResources.map((r) => r.id),
                      ),
                      resourceMatchesMetadata(ctx.db, sysFilter),
                    ),
                  });

+               if (removedFromSystemResources.length > 0) {
                  const events = removedFromSystemResources.flatMap((resource) =>
                    (sys?.deployments ?? []).map((deployment) => ({
                      action: "deployment.resource.removed" as const,
                      payload: { deployment, resource },
                    })),
                  );

                  const handleEventPromises = events.map(handleEvent);
                  await Promise.allSettled(handleEventPromises);
+               }

308-309: Consider Using Promise.all Instead of Promise.allSettled

If you want to fail fast on the first rejection of a promise, Promise.all might be more appropriate than Promise.allSettled, which waits for all promises to settle regardless of their outcome.

Apply this diff to change the promise handling:

-               await Promise.allSettled(handleEventPromises);
+               await Promise.all(handleEventPromises);

283-310: Optimize Event Handling Logic

To improve readability and maintainability, consider extracting the event generation and handling logic into a separate function.

Example:

const generateAndHandleRemovalEvents = async (resources, deployments) => {
  const events = resources.flatMap((resource) =>
    deployments.map((deployment) => ({
      action: "deployment.resource.removed" as const,
      payload: { deployment, resource },
    })),
  );
  const handleEventPromises = events.map(handleEvent);
  await Promise.all(handleEventPromises);
};

// Usage:
if (removedFromSystemResources.length > 0) {
  await generateAndHandleRemovalEvents(removedFromSystemResources, sys.deployments ?? []);
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5846cc8 and 755c049.

📒 Files selected for processing (10)
  • packages/api/src/router/environment.ts (4 hunks)
  • packages/api/src/router/resources.ts (2 hunks)
  • packages/events/src/handlers/target-removed.ts (1 hunks)
  • packages/events/src/index.ts (1 hunks)
  • packages/events/src/triggers/deployment-deleted.ts (1 hunks)
  • packages/events/src/triggers/environment-deleted.ts (3 hunks)
  • packages/events/src/triggers/target-deleted.ts (2 hunks)
  • packages/job-dispatch/src/resource.ts (2 hunks)
  • packages/validators/src/events/hooks/index.ts (1 hunks)
  • packages/validators/src/events/hooks/target.ts (1 hunks)
🔇 Additional comments (20)
packages/validators/src/events/hooks/target.ts (2)

14-14: LGTM: Type export is consistent with schema changes

The type inference and naming are consistent with the schema changes.


10-13: Verify event handler updates for action literal change

The schema structure is correct and the renaming is consistent. However, changing the action literal from "deployment.target.removed" to "deployment.resource.removed" requires verification of all consuming event handlers.

✅ Verification successful

Action literal change has been properly propagated

The verification shows that the new action literal "deployment.resource.removed" is consistently used across the codebase:

  • Defined in validators package (packages/validators/src/events/hooks/target.ts)
  • Listed in hook actions (packages/validators/src/events/hooks/index.ts)
  • Used in event handlers (packages/events/src/handlers/target-removed.ts)
  • Properly triggered in multiple scenarios:
    • Environment deletion (packages/events/src/triggers/environment-deleted.ts)
    • Deployment deletion (packages/events/src/triggers/deployment-deleted.ts)
    • Target deletion (packages/events/src/triggers/target-deleted.ts)
    • Environment router (packages/api/src/router/environment.ts)

No references to the old action literal "deployment.target.removed" were found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the old action literal
rg "deployment\.target\.removed" --type ts

# Search for event handler implementations
ast-grep --pattern 'function handle$_(event: $_) {
  $$$
}'

Length of output: 106


Script:

#!/bin/bash
# Search for event handlers or consumers using the new action literal
rg "deployment\.resource\.removed" --type ts -A 5

# Search for any event handler or consumer patterns
ast-grep --pattern 'case "deployment.resource.removed": {
  $$$
}'

# Search for event handler class methods
ast-grep --pattern 'class $_ {
  $$$
  handle(event: $_) {
    $$$
  }
  $$$
}'

Length of output: 2680

packages/validators/src/events/hooks/index.ts (2)

16-16: Verify action string updates across the codebase

The hookActionsList has been updated to use the new "resource" terminology. Ensure all consumers of these action strings have been updated accordingly.

#!/bin/bash
# Description: Check for any remaining references to old action strings

# Search for old target-related action strings
rg -l "deployment\.target\.(removed|deleted)" --type ts

# Search for places where hookActions enum is used
rg -A 3 "hookActions\." --type ts

8-8: Verify event handler compatibility

The hookEvent constant now only handles resourceRemoved events. This change could impact existing event handlers that were previously handling other event types.

✅ Verification successful

Let me gather more information about the event handling to verify if this change is safe.


Based on the search results, I can now provide a final response:

Change is safe and properly aligned with codebase

The change to hookEvent is consistent with the existing implementation:

  • The only event type being handled is ResourceRemoved as evidenced by handleEvent function in packages/events/src/index.ts
  • The event handling infrastructure (hookActions, isResourceRemoved, handleResourceRemoved) is already set up specifically for resource removal events
  • No other event types are referenced or handled in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to target events in the codebase

# Search for any remaining target event handlers
rg -l "targetRemoved|targetDeleted|TargetRemoved|TargetDeleted" --type ts

# Search for direct usage of hookEvent to verify all consumers are updated
rg -A 3 "hookEvent" --type ts

Length of output: 1750


Script:

#!/bin/bash
# Search for any references to hookEvent in test files and implementation
rg -A 5 "hookEvent|ResourceRemoved" --type ts

# Search for any event-related imports and type usage
ast-grep --pattern 'import { $_ } from "$_/events/hooks"'

Length of output: 5550

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

5-5: LGTM: Import changes align with resource terminology.

The import changes consistently reflect the shift from "target" to "resource" terminology.

Also applies to: 7-7

packages/events/src/handlers/target-removed.ts (2)

8-9: LGTM! Function signature and payload destructuring are properly updated

The changes correctly reflect the shift from "target" to "resource" terminology.


11-20: Verify consistent update of action types across the codebase

The action type has been updated to "deployment.resource.removed". Let's ensure this change is consistent across all related files.

✅ Verification successful

Based on the search results, I can generate the final response as we have comprehensive information about the usage of action types across the codebase.

Action type "deployment.resource.removed" is consistently used across the codebase

The verification shows that:

  • The old action type "deployment.target.removed" is not present anywhere in the codebase
  • The new action type "deployment.resource.removed" is consistently used across all relevant files:
    • Properly defined in validators (packages/validators/src/events/hooks/)
    • Correctly used in API routes (packages/api/src/router/environment.ts)
    • Consistently implemented in event triggers and handlers (packages/events/src/)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the old action type
rg "deployment\.target\.removed" --type ts

# Search for the new action type to verify consistent usage
rg "deployment\.resource\.removed" --type ts

Length of output: 790

packages/events/src/triggers/deployment-deleted.ts (2)

37-39: Verify event handler compatibility with new action name.

The event structure looks correct, but we should verify that all consumers of this event are updated to handle the new deployment.resource.removed action.

✅ Verification successful

Let me search for event handlers using a different approach to verify the compatibility.


Let me try one final search pattern to find event handlers.


Event handler compatibility verified for deployment.resource.removed

The verification shows the event action is properly integrated:

  • Validated in schema: packages/validators/src/events/hooks/target.ts
  • Handled in target removal: packages/events/src/handlers/target-removed.ts
  • Referenced consistently across multiple files without any legacy deployment.target.removed references
  • Event schema and handler implementation are aligned with the new action name
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for handlers of the old and new event action names
# to ensure complete migration

# Search for old event action name
echo "Checking for old event action references:"
rg "deployment\.target\.removed"

# Search for new event action name
echo "Checking for new event action references:"
rg "deployment\.resource\.removed"

# Search for potential event handlers
echo "Checking for related event handlers:"
rg -A 5 "case 'deployment\.(target|resource)\.removed'"

Length of output: 1103


Script:

#!/bin/bash
# Search for files that might contain event handlers
echo "Searching for potential event handler files:"
fd "handler" --type f

# Search for switch/case statements or event handling patterns in these files
echo "Checking event handling patterns:"
ast-grep --pattern 'case $_ : { $$$_ }'

# Search for any direct references to deployment events in handler files
echo "Checking deployment event handlers:"
rg -A 5 "deployment\." $(fd "handler" --type f)

Length of output: 494


Script:

#!/bin/bash
# Search for files containing event handling logic
echo "Searching for event handler implementations:"
rg -l "handleEvent|EventHandler|processEvent|onEvent"

# Check the content of these files for deployment event handling
echo "Checking event handler contents:"
rg -B2 -A5 "deployment\.resource\.removed" packages/events/src/handlers/target-removed.ts

# Look for event validator schemas
echo "Checking event validator schemas:"
rg -B2 -A5 "deployment\.resource\.removed" packages/validators/src/events/hooks/target.ts

Length of output: 1305


33-35: Verify database query compatibility with resource filtering.

The database query looks correct, but let's verify that the resourceMatchesMetadata function properly handles the resource filtering logic.

✅ Verification successful

Based on the gathered information, I can now generate the final response:

Database query is correctly implemented and compatible with resource filtering.

The resourceMatchesMetadata function is properly implemented in packages/db/src/schema/resource.ts and handles null/empty metadata cases appropriately. The query in the deployment-deleted trigger correctly uses this function to filter resources based on system filters. The implementation maintains backward compatibility while adopting the new resource terminology.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of resourceMatchesMetadata function
# and its compatibility with resource filtering

# Check the implementation of resourceMatchesMetadata
ast-grep --pattern 'export const resourceMatchesMetadata = $$$'

# Look for related test cases
rg -l "resourceMatchesMetadata.*test" 

Length of output: 1103


Script:

#!/bin/bash
# Let's try a different approach to find the schema definition

# Search for SCHEMA object definition
rg -A 10 "export const SCHEMA = {" 

# Search for resource-related schema definitions
rg "resource(Matches|Filter|Query)" -A 5

# Look for the actual database query implementation
ast-grep --pattern 'query.resource.findMany({
  $$$
})'

Length of output: 49175


Script:

#!/bin/bash
# Let's check the schema definition and implementation in the db package

# Look for the schema definition in db package
rg -A 5 "export const resource = " packages/db/src/

# Check for any test files related to resource queries
fd -e test.ts -e spec.ts . -x rg -l "resource.*findMany"

# Look for the actual implementation of resourceMatchesMetadata
rg -A 10 "export function resourceMatchesMetadata" packages/db/src/

Length of output: 1290

packages/events/src/triggers/target-deleted.ts (3)

12-15: LGTM! Clear and informative documentation.

The documentation clearly explains the function's purpose and includes a crucial note about timing requirements for implementers.


49-50: Verify event consumers handle the updated action string.

The event action has changed from "deployment.target.removed" to "deployment.resource.removed". This needs verification across event consumers.

#!/bin/bash
# Description: Verify that event consumers are updated to handle the new action string

# Search for any remaining references to the old action string
echo "Checking for any remaining references to 'deployment.target.removed'..."
rg "deployment\.target\.removed"

# Search for handlers of the new action string
echo "Checking for handlers of new action 'deployment.resource.removed'..."
rg "deployment\.resource\.removed"

# Search for event handler definitions that might need updates
echo "Checking event handler definitions..."
ast-grep --pattern 'action: "deployment.$_.$_"'

17-18: Verify the function rename impact across the codebase.

The function rename from getEventsForTargetDeleted to getEventsForResourceDeleted needs to be verified for consistent usage.

✅ Verification successful

Let me generate additional scripts to verify the function rename impact.


Function rename has been properly implemented across the codebase

The verification shows that:

  • All references to getEventsForTargetDeleted have been replaced with getEventsForResourceDeleted
  • The new function is correctly imported and used in:
    • packages/job-dispatch/src/resource.ts
    • packages/api/src/router/resources.ts
    • packages/events/src/triggers/target-deleted.ts (function definition)

The git history confirms this was part of a coordinated rename effort, with all necessary changes made consistently.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all references to the old function name have been updated.

# Search for any remaining references to the old function name
echo "Checking for any remaining references to 'getEventsForTargetDeleted'..."
rg "getEventsForTargetDeleted"

# Search for references to the new function name to confirm proper usage
echo "Checking usage of new function name 'getEventsForResourceDeleted'..."
rg "getEventsForResourceDeleted"

Length of output: 1217


Script:

#!/bin/bash
# Search for any remaining references to the old function name
echo "Searching for 'getEventsForTargetDeleted'..."
rg "getEventsForTargetDeleted"

# Search for references to the new function name
echo "Searching for 'getEventsForResourceDeleted'..."
rg "getEventsForResourceDeleted"

# Check git history for the function rename
echo "Checking git history for the function rename..."
git log -p | rg -A 5 -B 5 "getEventsForTargetDeleted|getEventsForResourceDeleted"

Length of output: 5172

packages/events/src/triggers/environment-deleted.ts (2)

15-19: LGTM: Resource query implementation is correct

The database query correctly fetches resources using the environment's resource filter, with appropriate early return optimization.


60-62: Verify event type change impact

The event generation logic is correct, but let's verify that all consumers handle the new "deployment.resource.removed" event type correctly.

✅ Verification successful

The search results show that the new event type "deployment.resource.removed" is consistently used across the codebase in validators, API routes, triggers, and handlers. There's no usage of the old "deployment.target.removed" event type, indicating a complete migration. Let's verify the event handler implementation to ensure it properly processes this event type.


Event type change is properly implemented and handled

The verification shows that:

  • The new event type "deployment.resource.removed" is consistently used across the codebase
  • The event handler (target-removed.ts) correctly processes this event type
  • The event schema is properly defined in the validators
  • No traces of the old event type "deployment.target.removed" remain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for consumers of the old and new event types
echo "Checking for old event type usage:"
rg "deployment\.target\.removed" --type ts

echo -e "\nChecking for new event type usage:"
rg "deployment\.resource\.removed" --type ts

echo -e "\nChecking for event handlers:"
ast-grep --pattern 'action: "deployment.$_.$_"'

Length of output: 1077


Script:

#!/bin/bash
# Check the implementation of the target-removed handler
echo "Checking target-removed handler implementation:"
cat packages/events/src/handlers/target-removed.ts

# Check for any event type validation schemas
echo -e "\nChecking event validation schemas:"
rg "deployment\.resource\.removed" -A 5 -B 5 packages/validators/src/events/hooks/target.ts

Length of output: 1628

packages/api/src/router/resources.ts (1)

20-20: LGTM! Import renamed for consistency

The import statement has been correctly updated to use the new function name, maintaining consistency with the terminology change from "target" to "resource".

packages/job-dispatch/src/resource.ts (1)

22-22: Import statement is appropriate

The import of getEventsForResourceDeleted and handleEvent from "@ctrlplane/events" integrates the necessary functions for event handling related to resource deletion.

packages/api/src/router/environment.ts (4)

2-2: Import Statement Approved

The import of ResourceCondition from @ctrlplane/validators/resources is appropriate and necessary for the updated logic.


12-13: Inclusion of isNotNull Function

Adding isNotNull to the imported functions from @ctrlplane/db is correct, as it is used in subsequent query conditions.


32-35: Importing New Comparison Operators

The import of ComparisonOperator and FilterType from @ctrlplane/validators/conditions is appropriate for defining filter conditions.


301-307: Potential Issue with sys?.deployments Being Undefined

While accessing (sys?.deployments ?? []), if sys is null or deployments is undefined, it defaults to an empty array, resulting in no events being generated. Ensure that this is the intended behavior.

Run the following script to check if sys?.deployments could be undefined:

✅ Verification successful

Based on the schema files and type definitions found, I can now generate the final response:

The Null-Coalescing Behavior for sys?.deployments is Correct

The code's behavior of defaulting to an empty array when sys is null or deployments is undefined is the correct implementation because:

  1. The system schema shows that deployments is a relation field defined in systemRelations, meaning it's an optional array that could be undefined if not included in the query.
  2. When no events need to be generated (empty array case), this is the desired behavior as it safely handles cases where:
    • The system doesn't exist (sys is null)
    • The system exists but deployments weren't loaded in the query
    • The system exists but has no deployments
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if `deployments` can be undefined when `sys` is present.

# Expected: All systems with non-null `deployments` property.

# Check systems where `deployments` might be undefined
ast-grep --pattern $'const sys = await $_.db.query.system.findFirst({
  where: $_,
  with: { $_ },
});'

Length of output: 104


Script:

#!/bin/bash
# Let's try a different approach to find system queries and their usage patterns
# First, let's find the file content around the code snippet
rg -B 10 -A 10 "const events = removedFromSystemResources\.flatMap" 

# Also search for system type definitions to understand the structure
rg -l "type.*System.*=.*{" 

# And look for deployment-related queries
rg -B 5 "system.*findFirst.*deployment"

Length of output: 1801


Script:

#!/bin/bash
# Let's fix the regex and try again to find system type definitions
rg -l "type\s+System\s*=\s*\{"

# Look for system query with deployments in the with clause
rg -B 5 "findFirst.*\{.*deployments"

# Check the schema file for system and deployment relationship
fd -g "*schema*" -x cat {}

Length of output: 32063


Script:

#!/bin/bash
# Let's search for the schema definition in drizzle schema files
fd -g "*.ts" -g "!*.test.ts" -g "!*.spec.ts" --full-path "./packages/db/src/schema" -x cat {}

# Also search for system type definitions in the codebase
rg "interface.*System.*{" -A 10

Length of output: 347


Script:

#!/bin/bash
# Fix the fd command to search for schema files
fd -e ts -E "*.test.ts" -E "*.spec.ts" . "./packages/db/src/schema" -x cat {}

# Fix the regex for system interface search
rg "interface\s+System\s*\{" -A 10

# Also look for system-related types and schemas
rg "type\s+System\s*=" -A 10

Length of output: 70847

Comment on lines +12 to +13
export const isResourceRemoved = (event: HookEvent): event is ResourceRemoved =>
true;
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 the type guard implementation - Critical Security Issue

The current implementation of isResourceRemoved always returns true, which defeats the purpose of type checking. This could lead to runtime errors if invalid events are processed as ResourceRemoved events.

Implement proper type checking:

-export const isResourceRemoved = (event: HookEvent): event is ResourceRemoved =>
-  true;
+export const isResourceRemoved = (event: HookEvent): event is ResourceRemoved => {
+  return event.type === "deployment.resource.removed" &&
+         "resourceId" in event &&
+         typeof event.resourceId === "string";
+};
📝 Committable suggestion

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

Suggested change
export const isResourceRemoved = (event: HookEvent): event is ResourceRemoved =>
true;
export const isResourceRemoved = (event: HookEvent): event is ResourceRemoved => {
return event.type === "deployment.resource.removed" &&
"resourceId" in event &&
typeof event.resourceId === "string";
};

Comment on lines +5 to +15
import { isResourceRemoved } from "@ctrlplane/validators/events";

import { handleTargetRemoved } from "./handlers/target-removed.js";
import { handleResourceRemoved } from "./handlers/index.js";

export * from "./triggers/index.js";
export * from "./handlers/index.js";

export const handleEvent = async (event: HookEvent) => {
await db.insert(SCHEMA.event).values(event);
if (isTargetRemoved(event)) return handleTargetRemoved(event);
throw new Error(`Unhandled event: ${event.action}`);
if (isResourceRemoved(event)) return handleResourceRemoved(event);
throw new Error(`Unhandled event`);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Inconsistent terminology transition detected in events package

The codebase shows an incomplete transition from "target" to "resource" terminology:

  • packages/events/src/handlers/target-removed.ts imports ResourceRemoved type but the file itself uses old "target" terminology
  • packages/events/src/triggers/target-deleted.ts still uses "target" in filename while importing resource-related types
  • The event handling logic in index.ts uses "resource" terminology (isResourceRemoved, handleResourceRemoved) but references files with "target" terminology

This inconsistency should be addressed by renaming the following files to maintain consistency:

  • packages/events/src/handlers/target-removed.tsresource-removed.ts
  • packages/events/src/triggers/target-deleted.tsresource-deleted.ts
🔗 Analysis chain

Verify consistent terminology across codebase.

Let's ensure the terminology change from "target" to "resource" is consistent across the codebase.


Let me analyze the results more deeply with additional scripts to understand the context and nature of these occurrences.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining "target" terminology that should be "resource"

# Search for remaining "target" usage in relevant contexts
echo "Checking for remaining 'target' terminology in event-related files:"
rg -i "target.*(?:event|hook|remove|delete)" --type ts

# Search for potential inconsistencies where both terms are used
echo -e "\nChecking for files using both 'target' and 'resource' terminology:"
rg -l "target" --type ts | xargs rg -l "resource"

Length of output: 9318


Script:

#!/bin/bash
# Check the content of specific files that show the terminology transition
echo "Checking event handler implementations:"
rg -A 5 "isResourceRemoved|isTargetRemoved" --type ts

echo -e "\nChecking event type definitions:"
ast-grep --pattern 'type $_ = {
  $$$
  (target|resource)
  $$$
}'

echo -e "\nChecking specific imports in events package:"
rg -A 2 "from.*validators/events" packages/events/src/

Length of output: 3219

Comment on lines 351 to 358
const resources = await tx.query.resource.findMany({
where: inArray(resource.id, resourceIds),
});
const eventsPromises = resources.map(getEventsForResourceDeleted);
const events = (await Promise.allSettled(eventsPromises)).flatMap((r) =>
r.status === "fulfilled" ? r.value : [],
);
await Promise.allSettled(events.map(handleEvent));
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

Handle rejected promises when processing events

The current implementation uses Promise.allSettled to process event promises but does not handle rejected cases. This could lead to silent failures during event handling, potentially causing issues downstream. It's important to handle rejected promises to ensure reliability and maintainability.

Apply this diff to handle rejected promises:

+ const eventsResults = await Promise.allSettled(eventsPromises);
+ const events = eventsResults.flatMap((r) =>
+   r.status === "fulfilled" ? r.value : []
+ );
+ eventsResults
+   .filter((r) => r.status === "rejected")
+   .forEach((r) =>
+     log.error("Failed to get events for resource deletion", { error: r.reason })
+   );

- const events = (await Promise.allSettled(eventsPromises)).flatMap((r) =>
-   r.status === "fulfilled" ? r.value : [],
- );

+ const handleEventResults = await Promise.allSettled(events.map(handleEvent));
+ handleEventResults
+   .filter((r) => r.status === "rejected")
+   .forEach((r) =>
+     log.error("Failed to handle event", { error: r.reason })
+   );

- await Promise.allSettled(events.map(handleEvent));

Additionally, consider whether resource deletion should proceed if event handling fails. Depending on system requirements, you may need to halt the deletion to maintain data integrity if events are not processed successfully.

📝 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 resources = await tx.query.resource.findMany({
where: inArray(resource.id, resourceIds),
});
const eventsPromises = resources.map(getEventsForResourceDeleted);
const events = (await Promise.allSettled(eventsPromises)).flatMap((r) =>
r.status === "fulfilled" ? r.value : [],
);
await Promise.allSettled(events.map(handleEvent));
const resources = await tx.query.resource.findMany({
where: inArray(resource.id, resourceIds),
});
const eventsPromises = resources.map(getEventsForResourceDeleted);
const eventsResults = await Promise.allSettled(eventsPromises);
const events = eventsResults.flatMap((r) =>
r.status === "fulfilled" ? r.value : []
);
eventsResults
.filter((r) => r.status === "rejected")
.forEach((r) =>
log.error("Failed to get events for resource deletion", { error: r.reason })
);
const handleEventResults = await Promise.allSettled(events.map(handleEvent));
handleEventResults
.filter((r) => r.status === "rejected")
.forEach((r) =>
log.error("Failed to handle event", { error: r.reason })
);

Comment on lines +269 to 270
newQuery,
oldQuery && not(oldQuery),
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

Logical Error in Query Conditions

The condition oldQuery && not(oldQuery) will always evaluate to false, resulting in no resources being returned. This is likely a logical error in constructing the query for newResources.

To find resources that match newQuery and do not match oldQuery, the condition should be:

                    newQuery,
-                   oldQuery && not(oldQuery),
+                   not(oldQuery),

So the corrected query becomes:

where: and(
  eq(resource.workspaceId, oldEnv.system.workspaceId),
  newQuery,
  not(oldQuery),
),

Comment on lines +277 to +278
oldQuery,
newQuery && not(newQuery),
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

Logical Error in Query Conditions

Similarly, the condition newQuery && not(newQuery) will always evaluate to false, resulting in no resources being returned for removedResources. This should be corrected.

To find resources that match oldQuery and do not match newQuery, the condition should be:

                  oldQuery,
-                 newQuery && not(newQuery),
+                 not(newQuery),

So the corrected query becomes:

where: and(
  eq(resource.workspaceId, oldEnv.system.workspaceId),
  oldQuery,
  not(newQuery),
),

Comment on lines +248 to +251
const sys = await ctx.db.query.system.findFirst({
where: eq(system.id, oldEnv.system.id),
with: { environments: { where: isOtherEnv }, deployments: true },
});
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

Error Handling for sys Variable

Ensure that the sys variable is properly handled in case findFirst returns null. This will prevent potential runtime errors when accessing properties of sys later in the code.

Consider adding a check or default value:

              const sys = await ctx.db.query.system.findFirst({
                where: eq(system.id, oldEnv.system.id),
                with: { environments: { where: isOtherEnv }, deployments: true },
              });
+             if (!sys) {
+               throw new Error(`System with ID ${oldEnv.system.id} not found.`);
+             }
📝 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 sys = await ctx.db.query.system.findFirst({
where: eq(system.id, oldEnv.system.id),
with: { environments: { where: isOtherEnv }, deployments: true },
});
const sys = await ctx.db.query.system.findFirst({
where: eq(system.id, oldEnv.system.id),
with: { environments: { where: isOtherEnv }, deployments: true },
});
if (!sys) {
throw new Error(`System with ID ${oldEnv.system.id} not found.`);
}

Comment on lines +244 to +251
const isOtherEnv = and(
isNotNull(environment.resourceFilter),
ne(environment.id, input.id),
);
const sys = await ctx.db.query.system.findFirst({
where: eq(system.id, oldEnv.system.id),
with: { environments: { where: isOtherEnv }, deployments: true },
});
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const isOtherEnv = and(
isNotNull(environment.resourceFilter),
ne(environment.id, input.id),
);
const sys = await ctx.db.query.system.findFirst({
where: eq(system.id, oldEnv.system.id),
with: { environments: { where: isOtherEnv }, deployments: true },
});
const where = and(
isNotNull(environment.resourceFilter),
ne(environment.id, input.id),
);
const sys = await ctx.db.query.system.findFirst({
where: eq(system.id, oldEnv.system.id),
with: { environments: { where }, deployments: true },
});

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