Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
CommandSeparator,
} from "@ctrlplane/ui/command";
import { Dialog, DialogContent, DialogTrigger } from "@ctrlplane/ui/dialog";
import { ColumnOperator } from "@ctrlplane/validators/conditions";

import { api } from "~/trpc/react";

Expand All @@ -40,8 +41,8 @@ export const SearchDialog: React.FC<{ children: React.ReactNode }> = ({
conditions: [
{
type: "name",
operator: "like",
value: `%${search}%`,
operator: ColumnOperator.Contains,
value: search,
},
],
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ export const ColumnConditionRender: React.FC<ColumnConditionRenderProps> = ({
</SelectTrigger>
<SelectContent>
<SelectItem value={ColumnOperator.Equals}>Equals</SelectItem>
<SelectItem value={ColumnOperator.Like}>Like</SelectItem>
<SelectItem value={ColumnOperator.Contains}>Contains</SelectItem>
<SelectItem value={ColumnOperator.StartsWith}>
Starts with
</SelectItem>
<SelectItem value={ColumnOperator.EndsWith}>Ends with</SelectItem>
<SelectItem value={ColumnOperator.Regex}>Regex</SelectItem>
</SelectContent>
</Select>
</div>
<div className="col-span-7">
<Input
placeholder={
operator === ColumnOperator.Regex
? "^[a-zA-Z]+$"
: operator === ColumnOperator.Like
? "%value%"
: "Value"
operator === ColumnOperator.Regex ? "^[a-zA-Z]+$" : "Value"
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider enhancing regex input validation and guidance.

While the regex placeholder is helpful, consider these improvements for better user experience:

  1. Add real-time validation for regex patterns
  2. Use a more generic regex example that demonstrates common use cases
  3. Provide visual feedback for invalid patterns

Here's a suggested implementation:

- placeholder={operator === ColumnOperator.Regex ? "^[a-zA-Z]+$" : "Value"}
+ placeholder={operator === ColumnOperator.Regex ? "e.g., ^user-\\d+$" : "Value"}
+ pattern={operator === ColumnOperator.Regex ? ".*" : undefined}
+ onInvalid={operator === ColumnOperator.Regex ? (e) => {
+   e.target.setCustomValidity("Invalid regex pattern")
+ } : undefined}

Committable suggestion skipped: line range outside the PR's diff.

}
value={value}
onChange={(e) => setValue(e.target.value)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
HoverCardTrigger,
} from "@ctrlplane/ui/hover-card";
import {
ColumnOperator,
ComparisonOperator,
DateOperator,
MetadataOperator,
Expand Down Expand Up @@ -57,6 +58,9 @@ const operatorVerbs = {
[DateOperator.Before]: "before",
[DateOperator.AfterOrOn]: "after or on",
[DateOperator.BeforeOrOn]: "before or on",
[ColumnOperator.StartsWith]: "starts with",
[ColumnOperator.EndsWith]: "ends with",
[ColumnOperator.Contains]: "contains",
Comment on lines +61 to +63
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

Based on the codebase analysis, I can now generate the final response:

Remove MetadataOperator.Like and consolidate with ColumnOperator.Contains

The codebase shows that MetadataOperator.Like and ColumnOperator.Contains serve the same purpose, with Contains being the newer, more specific operator. The Like operator is still being used in several places:

  • Database schema files (job.ts, release.ts, resource.ts)
  • Metadata condition validators
  • UI components for condition rendering

Please:

  1. Remove MetadataOperator.Like from the metadata-condition.ts enum
  2. Update database schema files to use Contains instead of Like
  3. Update UI components to remove the Like option from metadata condition selectors
🔗 Analysis chain

Verify removal of MetadataOperator.Like.

While the new ColumnOperator entries are good additions, there seems to be an overlap between MetadataOperator.Like and ColumnOperator.Contains - both mapping to "contains". According to the PR objective, we should remove the "like" condition.

Let's verify the usage of MetadataOperator.Like in the codebase:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if MetadataOperator.Like is still being used elsewhere in the codebase

# Search for MetadataOperator.Like usage
echo "Searching for MetadataOperator.Like usage..."
rg "MetadataOperator\.Like"

# Search for potential string literal "like" in conditions
echo "Searching for 'like' string literals in condition files..."
rg -g "**/condition*.{ts,tsx}" '"like"'

Length of output: 1366


Script:

#!/bin/bash
# Let's check the implementation details of MetadataOperator and ColumnOperator

# First, let's find the enum definitions
echo "=== Searching for MetadataOperator definition ==="
ast-grep --pattern 'enum MetadataOperator {
  $$$
}'

echo -e "\n=== Searching for ColumnOperator definition ==="
ast-grep --pattern 'enum ColumnOperator {
  $$$
}'

# Let's also check if there are any direct usages of these operators in condition-related files
echo -e "\n=== Checking condition files for operator usage ==="
fd "condition.*\.(ts|tsx)$" --exec cat {}

Length of output: 135584

};

const ConditionBadge: React.FC<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ import {
} from "@ctrlplane/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@ctrlplane/ui/popover";
import {
ColumnOperator,
ComparisonOperator,
FilterType,
} from "@ctrlplane/validators/conditions";
import {
ResourceFilterType,
ResourceOperator,
} from "@ctrlplane/validators/resources";
import { ResourceFilterType } from "@ctrlplane/validators/resources";

import type { JobConditionRenderProps } from "./job-condition-props";
import { api } from "~/trpc/react";
Expand All @@ -49,8 +47,8 @@ export const JobTargetConditionRender: React.FC<

const searchFilter: ResourceCondition = {
type: ResourceFilterType.Name,
operator: ResourceOperator.Like,
value: `%${searchDebounced}%`,
operator: ColumnOperator.Contains,
value: searchDebounced,
};

const systemQ = api.system.bySlug.useQuery(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
HoverCardContent,
HoverCardTrigger,
} from "@ctrlplane/ui/hover-card";
import { DateOperator } from "@ctrlplane/validators/conditions";
import { ColumnOperator, DateOperator } from "@ctrlplane/validators/conditions";
import {
isComparisonCondition,
isCreatedAtCondition,
Expand All @@ -42,6 +42,9 @@ const operatorVerbs = {
[DateOperator.Before]: "before",
[DateOperator.AfterOrOn]: "after or on",
[DateOperator.BeforeOrOn]: "before or on",
[ColumnOperator.StartsWith]: "starts with",
[ColumnOperator.EndsWith]: "ends with",
[ColumnOperator.Contains]: "contains",
Comment on lines +45 to +47
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

Found remaining "like" operator usages that need migration

The verification revealed that the "like" operator is still present in several validator files:

  • packages/validators/src/releases/conditions/release-condition.ts
  • packages/validators/src/resources/conditions/resource-condition.ts
  • packages/validators/src/conditions/metadata-condition.ts

These files still define and use the "like" operator, which should be migrated to the new specific operators (startsWith, endsWith, contains) for consistency with the UI changes.

🔗 Analysis chain

Verify complete migration from "like" operator.

Let's ensure all usages of the old "like" operator have been properly migrated to the new column operators.


Let me try a different search strategy to find any remaining usages of the "like" operator in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining "like" string literals in TypeScript/JavaScript files
# that might indicate missed migrations
rg -t ts -t tsx -t js "['\"](like|LIKE)['\"]" -g '!{*.test.*,*.spec.*}'

# Search for ColumnOperator.Like usage that should have been migrated
ast-grep --pattern 'ColumnOperator.Like'

Length of output: 31028


Script:

#!/bin/bash
# Search for ColumnOperator enum definition and its usages
ast-grep --pattern 'enum ColumnOperator'

# Search for any remaining "like" string literals specifically in the context of operators
rg -t ts -t js "operator.*like" -g '!{*.test.*,*.spec.*}'

# Search for any comparison with "like" in TypeScript/JavaScript files
rg -t ts -t js "=.*['\"]like['\"]" -g '!{*.test.*,*.spec.*}'

Length of output: 497

};

const ConditionBadge: React.FC<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ export const ComparisonConditionRender: React.FC<
onClick={() =>
addCondition({
type: ResourceFilterType.Name,
operator: ResourceOperator.Like,
operator: ColumnOperator.Equals,
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using Contains instead of Equals for Name filtering

Using ColumnOperator.Equals for name matching might be too restrictive for users. Names often require partial matches for better usability.

Consider using ColumnOperator.Contains instead:

-  operator: ColumnOperator.Equals,
+  operator: ColumnOperator.Contains,

This would allow users to find resources by partial name matches, which is a common use case in resource filtering.

📝 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
operator: ColumnOperator.Equals,
operator: ColumnOperator.Contains,

value: "",
})
}
Expand All @@ -299,7 +299,7 @@ export const ComparisonConditionRender: React.FC<
onClick={() =>
addCondition({
type: ResourceFilterType.Identifier,
operator: ColumnOperator.Like,
operator: ColumnOperator.Equals,
value: "",
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,24 @@
import type { ColumnOperatorType } from "@ctrlplane/validators/conditions";
import type { NameCondition } from "@ctrlplane/validators/resources";

import { cn } from "@ctrlplane/ui";
import { Input } from "@ctrlplane/ui/input";

import type { TargetConditionRenderProps } from "./target-condition-props";
import { ColumnConditionRender } from "../filter/ColumnConditionRender";

export const NameConditionRender: React.FC<
TargetConditionRenderProps<NameCondition>
> = ({ condition, onChange, className }) => {
const setValue = (value: string) =>
onChange({ ...condition, value: `%${value}%` });
const setOperator = (operator: ColumnOperatorType) =>
onChange({ ...condition, operator });
const setValue = (value: string) => onChange({ ...condition, value });

return (
<div className={cn("flex w-full items-center gap-2", className)}>
<div className="grid w-full grid-cols-12">
<div
className={cn(
"col-span-3 flex items-center rounded-l-md border bg-transparent px-3 text-sm text-muted-foreground",
)}
>
Name contains
</div>
<div className="col-span-9">
<Input
placeholder="Value"
value={condition.value.replace(/^%|%$/g, "")}
onChange={(e) => setValue(e.target.value)}
className="rounded-l-none rounded-r-md bg-transparent hover:bg-neutral-800/50"
/>
</div>
</div>
</div>
<ColumnConditionRender
operator={condition.operator}
value={condition.value}
setOperator={setOperator}
setValue={setValue}
className={className}
title="Name"
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
HoverCardContent,
HoverCardTrigger,
} from "@ctrlplane/ui/hover-card";
import { ColumnOperator } from "@ctrlplane/validators/conditions";
import {
isComparisonCondition,
isIdentifierCondition,
Expand All @@ -40,6 +41,9 @@ const operatorVerbs = {
),
[ResourceOperator.Regex]: "matches",
[ResourceOperator.Like]: "contains",
[ColumnOperator.StartsWith]: "starts with",
[ColumnOperator.EndsWith]: "ends with",
[ColumnOperator.Contains]: "contains",
};

const ConditionBadge: React.FC<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
CommandList,
} from "@ctrlplane/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@ctrlplane/ui/popover";
import { ColumnOperator } from "@ctrlplane/validators/conditions";
import {
ResourceFilterType,
ResourceOperator,
Expand Down Expand Up @@ -74,8 +75,8 @@ const useResourcesWithSearch = (
environmentFilters,
{
type: ResourceFilterType.Name,
operator: ResourceOperator.Like,
value: `%${search}%`,
operator: ColumnOperator.Contains,
value: search,
},
],
};
Expand Down
15 changes: 10 additions & 5 deletions packages/db/src/schema/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
exists,
gt,
gte,
ilike,
isNull,
like,
lt,
Expand Down Expand Up @@ -208,11 +209,15 @@ const buildCreatedAtCondition = (cond: CreatedAtCondition): SQL => {
};

const buildVersionCondition = (cond: VersionCondition): SQL => {
if (cond.operator === ColumnOperator.Like)
return like(release.version, cond.value);
if (cond.operator === ColumnOperator.Regex)
return sql`${release.version} ~ ${cond.value}`;
return eq(release.version, cond.value);
if (cond.operator === ColumnOperator.Equals)
return eq(release.version, cond.value);
if (cond.operator === ColumnOperator.StartsWith)
return ilike(release.version, `${cond.value}%`);
if (cond.operator === ColumnOperator.EndsWith)
return ilike(release.version, `%${cond.value}`);
if (cond.operator === ColumnOperator.Contains)
return ilike(release.version, `%${cond.value}%`);
return sql`${release.version} ~ ${cond.value}`;
};

const buildCondition = (tx: Tx, cond: JobCondition): SQL => {
Expand Down
9 changes: 7 additions & 2 deletions packages/db/src/schema/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
exists,
gt,
gte,
ilike,
like,
lt,
lte,
Expand Down Expand Up @@ -284,8 +285,12 @@ const buildCreatedAtCondition = (cond: CreatedAtCondition): SQL => {
const buildVersionCondition = (cond: VersionCondition): SQL => {
if (cond.operator === ColumnOperator.Equals)
return eq(release.version, cond.value);
if (cond.operator === ColumnOperator.Like)
return like(release.version, cond.value);
if (cond.operator === ColumnOperator.StartsWith)
return ilike(release.version, `${cond.value}%`);
if (cond.operator === ColumnOperator.EndsWith)
return ilike(release.version, `%${cond.value}`);
if (cond.operator === ColumnOperator.Contains)
return ilike(release.version, `%${cond.value}%`);
return sql`${release.version} ~ ${cond.value}`;
};

Expand Down
34 changes: 30 additions & 4 deletions packages/db/src/schema/resource.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import type { MetadataCondition } from "@ctrlplane/validators/conditions";
import type {
IdentifierCondition,
NameCondition,
ResourceCondition,
} from "@ctrlplane/validators/resources";
import type { InferInsertModel, InferSelectModel, SQL } from "drizzle-orm";
import { exists, like, not, notExists, or, relations, sql } from "drizzle-orm";
import {
exists,
ilike,
like,
not,
notExists,
or,
relations,
sql,
} from "drizzle-orm";
import {
boolean,
json,
Expand Down Expand Up @@ -210,20 +220,36 @@ const buildMetadataCondition = (tx: Tx, cond: MetadataCondition): SQL => {
};

const buildIdentifierCondition = (tx: Tx, cond: IdentifierCondition): SQL => {
if (cond.operator === ColumnOperator.Like)
return like(resource.identifier, cond.value);
if (cond.operator === ColumnOperator.Equals)
return eq(resource.identifier, cond.value);
if (cond.operator === ColumnOperator.StartsWith)
return ilike(resource.identifier, `${cond.value}%`);
if (cond.operator === ColumnOperator.EndsWith)
return ilike(resource.identifier, `%${cond.value}`);
if (cond.operator === ColumnOperator.Contains)
return ilike(resource.identifier, `%${cond.value}%`);
return sql`${resource.identifier} ~ ${cond.value}`;
};

const buildNameCondition = (tx: Tx, cond: NameCondition): SQL => {
if (cond.operator === ColumnOperator.Equals)
return eq(resource.name, cond.value);
if (cond.operator === ColumnOperator.StartsWith)
return ilike(resource.name, `${cond.value}%`);
if (cond.operator === ColumnOperator.EndsWith)
return ilike(resource.name, `%${cond.value}`);
if (cond.operator === ColumnOperator.Contains)
return ilike(resource.name, `%${cond.value}%`);
return sql`${resource.name} ~ ${cond.value}`;
};

const buildCondition = (tx: Tx, cond: ResourceCondition): SQL => {
if (cond.type === ResourceFilterType.Metadata)
return buildMetadataCondition(tx, cond);
if (cond.type === ResourceFilterType.Kind)
return eq(resource.kind, cond.value);
if (cond.type === ResourceFilterType.Name)
return like(resource.name, cond.value);
return buildNameCondition(tx, cond);
if (cond.type === ResourceFilterType.Provider)
return eq(resource.providerId, cond.value);
if (cond.type === ResourceFilterType.Identifier)
Expand Down
9 changes: 4 additions & 5 deletions packages/validators/src/conditions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ export * from "./date-condition.js";

export enum ColumnOperator {
Equals = "equals",
Like = "like",
Regex = "regex",
StartsWith = "starts-with",
EndsWith = "ends-with",
Contains = "contains",
Comment on lines +9 to +11
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

Based on the results, I can now generate the final response:

Migration from Like to new string operators needs attention

The verification reveals several files still using the "like" operator that need to be updated:

  • packages/validators/src/resources/conditions/resource-condition.ts
  • packages/validators/src/releases/conditions/release-condition.ts
  • packages/validators/src/conditions/metadata-condition.ts

The new operators (StartsWith, EndsWith, Contains) are properly referenced in the UI components and database schema, but the validator conditions still use the old Like operator.

🔗 Analysis chain

Improved string matching granularity, but verify migration and naming.

The change from a generic Like operator to specific string matching operators (StartsWith, EndsWith, Contains) provides better control and clarity. However, this is a breaking change that requires careful migration.

Let's verify all usages have been updated:

Consider using consistent casing for enum values:

 export enum ColumnOperator {
   Equals = "equals",
   Regex = "regex",
-  StartsWith = "starts-with",
-  EndsWith = "ends-with",
-  Contains = "contains",
+  StartsWith = "startsWith",
+  EndsWith = "endsWith",
+  Contains = "contains",
 }
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining uses of the old "like" operator
rg -i "\"like\"" --type ts

# Search for uses of the new operators to ensure proper adoption
rg -i "\"(starts-with|ends-with|contains)\"" --type ts

# Search for ColumnOperator usage to verify migration
ast-grep --pattern 'ColumnOperator.$_'

Length of output: 39934

}

export const columnOperator = z
.literal(ColumnOperator.Equals)
.or(z.literal(ColumnOperator.Like))
.or(z.literal(ColumnOperator.Regex));
export const columnOperator = z.nativeEnum(ColumnOperator);

export type ColumnOperatorType = z.infer<typeof columnOperator>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,3 @@ export const identifierCondition = z.object({
});

export type IdentifierCondition = z.infer<typeof identifierCondition>;

export type IdentifierOperator = IdentifierCondition["operator"];
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { z } from "zod";

import { columnOperator } from "../../conditions/index.js";

export const nameCondition = z.object({
type: z.literal("name"),
operator: z.literal("like"),
operator: columnOperator,
value: z.string().min(1),
});

Expand Down
Loading