Skip to content

chore: migrate relationship rules to db#824

Open
adityachoudhari26 wants to merge 16 commits intomainfrom
relationship-rules-db
Open

chore: migrate relationship rules to db#824
adityachoudhari26 wants to merge 16 commits intomainfrom
relationship-rules-db

Conversation

@adityachoudhari26
Copy link
Member

@adityachoudhari26 adityachoudhari26 commented Mar 3, 2026

Summary by CodeRabbit

  • New Features
    • Introduced relationship rules: create, store, list, update and delete relationship-driven rules with selectors, matchers and metadata, persisted in the database.
  • Migration / Chores
    • Legacy in-memory relationship rules are migrated to the DB-backed store during restore; tests and workspace setup now use DB-backed relationship rules.

@CLAassistant
Copy link

CLAassistant commented Mar 3, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ adityachoudhari26
❌ jsbroks
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e717cbf and 52ae277.

📒 Files selected for processing (1)
  • apps/workspace-engine/svc/workspaceconsumer/consumer.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/workspace-engine/svc/workspaceconsumer/consumer.go

📝 Walkthrough

Walkthrough

Adds a new RelationshipRule entity end-to-end: DB schema and migration, SQL queries and sqlc mappings, generated Go accessors, repository interfaces and DB/in-memory implementations, store wiring and migration, and Drizzle/TypeScript schema exports.

Changes

Cohort / File(s) Summary
DB schema & migrations
apps/workspace-engine/pkg/db/queries/schema.sql, packages/db/drizzle/0160_futuristic_catseye.sql, packages/db/drizzle/meta/_journal.json
Create relationship_rule table (id, name, description, workspace_id, from_type, to_type, relationship_type, reference, from_selector, to_selector, matcher JSONB, metadata JSONB) and add migration/journal entry.
SQL queries & sqlc config
apps/workspace-engine/pkg/db/queries/relationship_rules.sql, apps/workspace-engine/pkg/db/sqlc.yaml
Add queries: GetByID, ListByWorkspaceID, Upsert (ON CONFLICT), Delete; map matcher[]byte, metadatamap[string]string for codegen.
Generated Go models & accessors
apps/workspace-engine/pkg/db/models.go, apps/workspace-engine/pkg/db/relationship_rules.sql.go
Add RelationshipRule struct and generated CRUD methods/types including UpsertRelationshipRuleParams.
Repository interfaces & DB implementation
apps/workspace-engine/pkg/workspace/store/repository/interfaces.go, apps/workspace-engine/pkg/workspace/store/repository/db/repo.go, apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/repo.go, .../mapper.go
Introduce RelationshipRuleRepo interface; DB repo exposing per-workspace RelationshipRules(); mapper functions ToOapi / ToUpsertParams; DB-backed repo methods Get/Set/Remove/Items wired to SQL.
In-memory repo & adapter
apps/workspace-engine/pkg/workspace/store/repository/memory/repo.go, apps/workspace-engine/pkg/workspace/store/repository/repo.go
Rename in-memory field to private, add accessor RelationshipRules() returning repository adapter; add interface method to central Repo.
Store integration & migration
apps/workspace-engine/pkg/workspace/store/relationships.go, apps/workspace-engine/pkg/workspace/store/store.go
Switch RelationshipRules to use repository interface, add SetRepo setter, use DB repo via WithDBRelationshipRules(ctx), and migrate legacy in-memory relationship rules during Store.Restore.
Wiring: consumer, tests
apps/workspace-engine/svc/workspaceconsumer/consumer.go, apps/workspace-engine/test/integration/dbtest.go, apps/workspace-engine/pkg/persistence/integration_test.go
Enable DB-backed relationship rules in manager and tests; update tests to use new accessor pattern.
TypeScript Drizzle schema export
packages/db/src/schema/relationship-rule.ts, packages/db/src/schema/index.ts
Add Drizzle ORM relationshipRule table definition and re-export it from schema index.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Store
    participant RelRepo as RelationshipRuleRepo
    participant DB as Database

    rect rgba(0, 150, 200, 0.5)
    Note over Client,DB: Get Operation
    Client->>Store: Get(id)
    Store->>RelRepo: Get(id)
    RelRepo->>DB: GetRelationshipRuleByID(id)
    DB-->>RelRepo: row
    RelRepo->>RelRepo: ToOapi(row)
    RelRepo-->>Store: *oapi.RelationshipRule
    Store-->>Client: *oapi.RelationshipRule
    end

    rect rgba(150, 100, 200, 0.5)
    Note over Client,DB: Upsert Operation
    Client->>Store: Upsert(relationship)
    Store->>RelRepo: Set(relationship)
    RelRepo->>RelRepo: ToUpsertParams(relationship)
    RelRepo->>DB: UpsertRelationshipRule(params)
    DB-->>RelRepo: success/error
    RelRepo-->>Store: error?
    Store-->>Client: success/error
    end

    rect rgba(200, 100, 100, 0.5)
    Note over Client,DB: List Operation
    Client->>Store: Items()
    Store->>RelRepo: Items()
    RelRepo->>DB: ListRelationshipRulesByWorkspaceID(wsID)
    DB-->>RelRepo: []rows
    RelRepo->>RelRepo: map ToOapi(each row)
    RelRepo-->>Store: map[string]*oapi.RelationshipRule
    Store-->>Client: map[string]*oapi.RelationshipRule
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • jsbroks

Poem

🐰 In burrows of code I hop and sniff,
A rule took root and grew quite swift,
From schema soil to mapper sky,
Repos now store how relations tie.
Hooray — I nibble carrots, then I skiff! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'chore: migrate relationship rules to db' clearly and concisely summarizes the main change: migrating relationship rules storage from in-memory to database-backed implementation. It accurately reflects the changeset's primary objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch relationship-rules-db

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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

🧹 Nitpick comments (3)
apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/repo.go (1)

55-74: Items() silently returns empty map on errors.

While logging is present, callers cannot distinguish "no rules exist" from "failed to fetch rules." This could lead to incorrect behavior if a caller assumes an empty map means the workspace has no rules when in reality the DB query failed.

Based on learnings, the user prefers simplicity, so if this behavior is intentional for resilience, consider documenting this contract on the interface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/repo.go`
around lines 55 - 74, Repo.Items currently swallows errors and returns an empty
map, making callers unable to distinguish "no rules" vs "DB/parse failure";
change the Items signature to return (map[string]*oapi.RelationshipRule, error),
update the implementation to return a non-nil error when
uuid.Parse(r.workspaceID) fails or when
db.GetQueries(...).ListRelationshipRulesByWorkspaceID(...) returns an error, and
keep the successful path returning the populated map and nil error (use
ToOapi(row) unchanged). Also update all callers of Repo.Items to handle the
error (and tests) or, if you intend to keep the silent resilience, document this
contract on the interface and add a comment above Repo.Items to make the
behavior explicit.
apps/workspace-engine/pkg/workspace/store/relationships.go (2)

40-51: Unused ctx parameter in Remove.

Same observation as Upsert—the context is not propagated to repository calls. Consider aligning the signature with actual usage or updating the repository interface to accept context for future-proofing (e.g., for tracing, cancellation).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/pkg/workspace/store/relationships.go` around lines 40 -
51, The Remove method on RelationshipRules currently accepts a ctx but never
uses it; either remove the ctx parameter from Remove to match current repository
methods, or update the repository interface and implementations so repo.Get and
repo.Remove accept context and then pass ctx through (i.e., change signatures
for repo.Get and repo.Remove and call r.repo.Get(ctx, id) / r.repo.Remove(ctx,
id) before calling r.store.changeset.RecordDelete(relationship)). Ensure you
update all implementations and tests that call RelationshipRules.Remove or the
repo methods to match the new signatures.

27-34: Unused ctx parameter in Upsert.

The ctx parameter is passed but not used in the method body—r.repo.Set does not accept a context. If the repository interface doesn't require context propagation, consider removing it from the signature for consistency, or pass it through if the interface is expected to evolve.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/pkg/workspace/store/relationships.go` around lines 27 -
34, The Upsert method on RelationshipRules currently takes an unused ctx
parameter; remove the unused context from the signature to keep the API
consistent (or if you intend to propagate context, change r.repo.Set to accept a
context and forward ctx into it). Concretely, update RelationshipRules.Upsert to
remove the ctx parameter (and remove the context import if unused), adjust all
callers to call Upsert(relationship) instead of Upsert(ctx, relationship), and
ensure behavior around r.repo.Set and r.store.changeset.RecordUpsert remains
unchanged; alternatively, if choosing context propagation, change the repo.Set
signature to Set(ctx context.Context, relationship *oapi.RelationshipRule) and
forward ctx from RelationshipRules.Upsert into r.repo.Set.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/workspace-engine/pkg/db/queries/relationship_rules.sql`:
- Around line 14-18: The ON CONFLICT (id) DO UPDATE currently overwrites rows
across workspaces and even updates workspace_id; change the DO UPDATE to stop
setting workspace_id and to only apply the update when the existing row belongs
to the same workspace (add a WHERE clause that checks
relationship_rules.workspace_id = EXCLUDED.workspace_id). In short: remove
workspace_id from the SET list in the ON CONFLICT handler and add a WHERE
condition on the DO UPDATE to guard updates to rows that match the same
workspace.

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/mapper.go`:
- Around line 13-21: The selectorFromString function silently swallows errors
from json.Marshal and sel.UnmarshalJSON which can return a partially initialized
oapi.Selector; change selectorFromString to surface failures: make its signature
return (*oapi.Selector, error), check and return errors from
json.Marshal(oapi.CelSelector{Cel: s}) and from sel.UnmarshalJSON(celJSON), and
update callers to handle the error (or, if you prefer fallback behavior, log the
marshal/unmarshal errors at debug/warn before returning nil). Reference:
selectorFromString, oapi.Selector, json.Marshal, sel.UnmarshalJSON.
- Around line 45-46: The code silently ignores JSON unmarshal errors for the
Matcher field: when calling json.Unmarshal(row.Matcher, &matcher) the error is
discarded, which can hide corrupted/invalid data; update the mapper (around
matcher / RelationshipRule_Matcher handling) to capture the returned error, and
on failure log a warning or error (including the raw row.Matcher and the error)
via the package logger or processLogger so callers can detect bad data and
debugging info is preserved; ensure the mapper still returns a sensible zero
value or propagates the error depending on existing function error-handling
conventions.
- Around line 23-32: selectorToString currently only checks sel.AsCelSelector()
and returns empty on failure, losing JsonSelector data; update it to also
attempt sel.AsJsonSelector() when AsCelSelector() fails and return a stable
string representation of the JSON selector (e.g., compact JSON by marshaling the
JsonSelector value or using its Raw/Value field if present). Ensure the updated
logic still returns "" for nil sel, still prefers CelSelector when present
(using cel.Cel), and falls back to the JsonSelector string when available (use
json.Marshal on the JsonSelector struct or its inner field and return the
resulting string, handling any marshal error by returning "").

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/repo.go`:
- Around line 29-32: The Get method in repo.go is swallowing DB errors from
GetRelationshipRuleByID and returning (nil, false), making callers unable to
tell "not found" vs DB failure; update Get (the method that calls
db.GetQueries(r.ctx).GetRelationshipRuleByID) to surface the error: if
GetRelationshipRuleByID returns an error, log it (using the repository logger)
and return (nil, false, err) or propagate the error to the caller consistent
with the repo interface (or at minimum return an explicit error along with the
not-found flag) so DB/network/timeout failures are not silently discarded.

---

Nitpick comments:
In `@apps/workspace-engine/pkg/workspace/store/relationships.go`:
- Around line 40-51: The Remove method on RelationshipRules currently accepts a
ctx but never uses it; either remove the ctx parameter from Remove to match
current repository methods, or update the repository interface and
implementations so repo.Get and repo.Remove accept context and then pass ctx
through (i.e., change signatures for repo.Get and repo.Remove and call
r.repo.Get(ctx, id) / r.repo.Remove(ctx, id) before calling
r.store.changeset.RecordDelete(relationship)). Ensure you update all
implementations and tests that call RelationshipRules.Remove or the repo methods
to match the new signatures.
- Around line 27-34: The Upsert method on RelationshipRules currently takes an
unused ctx parameter; remove the unused context from the signature to keep the
API consistent (or if you intend to propagate context, change r.repo.Set to
accept a context and forward ctx into it). Concretely, update
RelationshipRules.Upsert to remove the ctx parameter (and remove the context
import if unused), adjust all callers to call Upsert(relationship) instead of
Upsert(ctx, relationship), and ensure behavior around r.repo.Set and
r.store.changeset.RecordUpsert remains unchanged; alternatively, if choosing
context propagation, change the repo.Set signature to Set(ctx context.Context,
relationship *oapi.RelationshipRule) and forward ctx from
RelationshipRules.Upsert into r.repo.Set.

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/repo.go`:
- Around line 55-74: Repo.Items currently swallows errors and returns an empty
map, making callers unable to distinguish "no rules" vs "DB/parse failure";
change the Items signature to return (map[string]*oapi.RelationshipRule, error),
update the implementation to return a non-nil error when
uuid.Parse(r.workspaceID) fails or when
db.GetQueries(...).ListRelationshipRulesByWorkspaceID(...) returns an error, and
keep the successful path returning the populated map and nil error (use
ToOapi(row) unchanged). Also update all callers of Repo.Items to handle the
error (and tests) or, if you intend to keep the silent resilience, document this
contract on the interface and add a comment above Repo.Items to make the
behavior explicit.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1b21841 and e717cbf.

📒 Files selected for processing (21)
  • apps/workspace-engine/pkg/db/models.go
  • apps/workspace-engine/pkg/db/queries/relationship_rules.sql
  • apps/workspace-engine/pkg/db/queries/schema.sql
  • apps/workspace-engine/pkg/db/relationship_rules.sql.go
  • apps/workspace-engine/pkg/db/sqlc.yaml
  • apps/workspace-engine/pkg/persistence/integration_test.go
  • apps/workspace-engine/pkg/workspace/store/relationships.go
  • apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/mapper.go
  • apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/repo.go
  • apps/workspace-engine/pkg/workspace/store/repository/db/repo.go
  • apps/workspace-engine/pkg/workspace/store/repository/interfaces.go
  • apps/workspace-engine/pkg/workspace/store/repository/memory/repo.go
  • apps/workspace-engine/pkg/workspace/store/repository/repo.go
  • apps/workspace-engine/pkg/workspace/store/store.go
  • apps/workspace-engine/svc/workspaceconsumer/consumer.go
  • apps/workspace-engine/test/integration/dbtest.go
  • packages/db/drizzle/0160_futuristic_catseye.sql
  • packages/db/drizzle/meta/0160_snapshot.json
  • packages/db/drizzle/meta/_journal.json
  • packages/db/src/schema/index.ts
  • packages/db/src/schema/relationship-rule.ts

Comment on lines +14 to +18
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name, description = EXCLUDED.description, workspace_id = EXCLUDED.workspace_id,
from_type = EXCLUDED.from_type, to_type = EXCLUDED.to_type, relationship_type = EXCLUDED.relationship_type,
reference = EXCLUDED.reference, from_selector = EXCLUDED.from_selector, to_selector = EXCLUDED.to_selector,
matcher = EXCLUDED.matcher, metadata = EXCLUDED.metadata;
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 | 🟠 Major

Guard upsert conflicts against cross-workspace overwrites.

On conflict, Line 15 currently updates workspace_id and all fields for any matching id. Add a workspace guard and avoid updating workspace_id itself.

Suggested SQL change
 ON CONFLICT (id) DO UPDATE
-SET name = EXCLUDED.name, description = EXCLUDED.description, workspace_id = EXCLUDED.workspace_id,
+SET name = EXCLUDED.name, description = EXCLUDED.description,
     from_type = EXCLUDED.from_type, to_type = EXCLUDED.to_type, relationship_type = EXCLUDED.relationship_type,
     reference = EXCLUDED.reference, from_selector = EXCLUDED.from_selector, to_selector = EXCLUDED.to_selector,
-    matcher = EXCLUDED.matcher, metadata = EXCLUDED.metadata;
+    matcher = EXCLUDED.matcher, metadata = EXCLUDED.metadata
+WHERE relationship_rule.workspace_id = EXCLUDED.workspace_id;
📝 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
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name, description = EXCLUDED.description, workspace_id = EXCLUDED.workspace_id,
from_type = EXCLUDED.from_type, to_type = EXCLUDED.to_type, relationship_type = EXCLUDED.relationship_type,
reference = EXCLUDED.reference, from_selector = EXCLUDED.from_selector, to_selector = EXCLUDED.to_selector,
matcher = EXCLUDED.matcher, metadata = EXCLUDED.metadata;
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name, description = EXCLUDED.description,
from_type = EXCLUDED.from_type, to_type = EXCLUDED.to_type, relationship_type = EXCLUDED.relationship_type,
reference = EXCLUDED.reference, from_selector = EXCLUDED.from_selector, to_selector = EXCLUDED.to_selector,
matcher = EXCLUDED.matcher, metadata = EXCLUDED.metadata
WHERE relationship_rule.workspace_id = EXCLUDED.workspace_id;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/pkg/db/queries/relationship_rules.sql` around lines 14
- 18, The ON CONFLICT (id) DO UPDATE currently overwrites rows across workspaces
and even updates workspace_id; change the DO UPDATE to stop setting workspace_id
and to only apply the update when the existing row belongs to the same workspace
(add a WHERE clause that checks relationship_rules.workspace_id =
EXCLUDED.workspace_id). In short: remove workspace_id from the SET list in the
ON CONFLICT handler and add a WHERE condition on the DO UPDATE to guard updates
to rows that match the same workspace.

Comment on lines +13 to +21
func selectorFromString(s string) *oapi.Selector {
if s == "" {
return nil
}
sel := &oapi.Selector{}
celJSON, _ := json.Marshal(oapi.CelSelector{Cel: s})
_ = sel.UnmarshalJSON(celJSON)
return sel
}
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 | 🟡 Minor

Silent error handling may mask issues.

Both json.Marshal and sel.UnmarshalJSON errors are discarded. If the input string s contains an invalid CEL expression or if marshaling fails, this returns a partially initialized Selector without indication of failure.

Consider logging at debug/warn level if this is intentional fallback behavior, or returning an error if callers should handle invalid selectors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/mapper.go`
around lines 13 - 21, The selectorFromString function silently swallows errors
from json.Marshal and sel.UnmarshalJSON which can return a partially initialized
oapi.Selector; change selectorFromString to surface failures: make its signature
return (*oapi.Selector, error), check and return errors from
json.Marshal(oapi.CelSelector{Cel: s}) and from sel.UnmarshalJSON(celJSON), and
update callers to handle the error (or, if you prefer fallback behavior, log the
marshal/unmarshal errors at debug/warn before returning nil). Reference:
selectorFromString, oapi.Selector, json.Marshal, sel.UnmarshalJSON.

Comment on lines +23 to +32
func selectorToString(sel *oapi.Selector) string {
if sel == nil {
return ""
}
cel, err := sel.AsCelSelector()
if err == nil && cel.Cel != "" {
return cel.Cel
}
return ""
}
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find where oapi.Selector is defined and check the struct
rg -n "type Selector" --type=go -A 10 | head -50

Repository: ctrlplanedev/ctrlplane

Length of output: 3881


🏁 Script executed:

#!/bin/bash
# Check for all As* methods on Selector types
rg -n "func.*\*Selector.*As[A-Z]" --type=go

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Look at the mapper.go file itself to understand context
cat -n apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/mapper.go | head -50

Repository: ctrlplanedev/ctrlplane

Length of output: 1421


🏁 Script executed:

#!/bin/bash
# Find where selectorToString is actually used
rg -n "selectorToString" --type=go -B 2 -A 2

Repository: ctrlplanedev/ctrlplane

Length of output: 5149


🏁 Script executed:

#!/bin/bash
# Get more context around the Selector definition in oapi.gen.go
sed -n '1025,1200p' apps/workspace-engine/pkg/oapi/oapi.gen.go

Repository: ctrlplanedev/ctrlplane

Length of output: 6114


🏁 Script executed:

#!/bin/bash
# Search for AsCelSelector and other As methods
rg -n "AsCelSelector|func.*As[A-Z].*Selector" --type=go -A 5 | head -100

Repository: ctrlplanedev/ctrlplane

Length of output: 7796


🏁 Script executed:

#!/bin/bash
# Look for all selector-related types in oapi.gen.go
rg -n "type.*Selector" apps/workspace-engine/pkg/oapi/oapi.gen.go

Repository: ctrlplanedev/ctrlplane

Length of output: 484


🏁 Script executed:

#!/bin/bash
# Search for JsonSelector instantiation or usage in relationship rules context
rg -n "JsonSelector" --type=go | head -30

Repository: ctrlplanedev/ctrlplane

Length of output: 4327


🏁 Script executed:

#!/bin/bash
# Check if there's any handling of JsonSelector anywhere in the codebase
rg -n "AsJsonSelector" --type=go

Repository: ctrlplanedev/ctrlplane

Length of output: 683


selectorToString does not handle JsonSelector variant.

The Selector union has two variants: CelSelector and JsonSelector. This function only attempts AsCelSelector() and silently returns an empty string if it fails, which causes data loss when a JsonSelector is passed. Tests and production code (e.g., pkg/selector/match.go, versionselector.go) demonstrate that JsonSelector is actively used.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/mapper.go`
around lines 23 - 32, selectorToString currently only checks sel.AsCelSelector()
and returns empty on failure, losing JsonSelector data; update it to also
attempt sel.AsJsonSelector() when AsCelSelector() fails and return a stable
string representation of the JSON selector (e.g., compact JSON by marshaling the
JsonSelector value or using its Raw/Value field if present). Ensure the updated
logic still returns "" for nil sel, still prefers CelSelector when present
(using cel.Cel), and falls back to the JsonSelector string when available (use
json.Marshal on the JsonSelector struct or its inner field and return the
resulting string, handling any marshal error by returning "").

Comment on lines +45 to +46
matcher := oapi.RelationshipRule_Matcher{}
_ = json.Unmarshal(row.Matcher, &matcher)
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 | 🟡 Minor

Silent unmarshal error for Matcher field.

If row.Matcher contains invalid JSON, the error is discarded and an empty RelationshipRule_Matcher is returned. This could mask data corruption issues.

Consider at minimum logging a warning when unmarshal fails:

🛡️ Proposed fix
 	matcher := oapi.RelationshipRule_Matcher{}
-	_ = json.Unmarshal(row.Matcher, &matcher)
+	if err := json.Unmarshal(row.Matcher, &matcher); err != nil {
+		log.Warn("Failed to unmarshal matcher", "id", row.ID, "error", err)
+	}
📝 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
matcher := oapi.RelationshipRule_Matcher{}
_ = json.Unmarshal(row.Matcher, &matcher)
matcher := oapi.RelationshipRule_Matcher{}
if err := json.Unmarshal(row.Matcher, &matcher); err != nil {
log.Warn("Failed to unmarshal matcher", "id", row.ID, "error", err)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/mapper.go`
around lines 45 - 46, The code silently ignores JSON unmarshal errors for the
Matcher field: when calling json.Unmarshal(row.Matcher, &matcher) the error is
discarded, which can hide corrupted/invalid data; update the mapper (around
matcher / RelationshipRule_Matcher handling) to capture the returned error, and
on failure log a warning or error (including the raw row.Matcher and the error)
via the package logger or processLogger so callers can detect bad data and
debugging info is preserved; ensure the mapper still returns a sensible zero
value or propagates the error depending on existing function error-handling
conventions.

Comment on lines +29 to +32
row, err := db.GetQueries(r.ctx).GetRelationshipRuleByID(r.ctx, uid)
if err != nil {
return nil, false
}
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 | 🟡 Minor

DB errors silently discarded in Get.

When GetRelationshipRuleByID fails (network issue, connection timeout, etc.), the error is swallowed and callers cannot distinguish "not found" from "DB failure." Consider returning an error or at minimum logging:

🛡️ Proposed fix to log DB errors
 	row, err := db.GetQueries(r.ctx).GetRelationshipRuleByID(r.ctx, uid)
 	if err != nil {
+		if err != pgx.ErrNoRows {
+			log.Warn("Failed to get relationship rule", "id", id, "error", err)
+		}
 		return nil, false
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/relationshiprules/repo.go`
around lines 29 - 32, The Get method in repo.go is swallowing DB errors from
GetRelationshipRuleByID and returning (nil, false), making callers unable to
tell "not found" vs DB failure; update Get (the method that calls
db.GetQueries(r.ctx).GetRelationshipRuleByID) to surface the error: if
GetRelationshipRuleByID returns an error, log it (using the repository logger)
and return (nil, false, err) or propagate the error to the caller consistent
with the repo interface (or at minimum return an explicit error along with the
not-found flag) so DB/network/timeout failures are not silently discarded.

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