Skip to content

refactor stitch UI to get related objects - #119

Merged
pramodnarayana merged 6 commits into
developmentfrom
feature/t023-stitches-ui
Apr 6, 2026
Merged

pramodnarayana merged 6 commits into
developmentfrom
feature/t023-stitches-ui

Conversation

@pramodnarayana

@pramodnarayana pramodnarayana commented Apr 6, 2026 •

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Stitch detail page with panels for mappings, configuration, schedule, and dependencies
    • Dependency discovery UI to list/select related objects
    • Advanced connector configuration panel with typed fields
    • JSONata-based mapping transformations and mapping summary view
    • "Run Now" schedule trigger and schedule controls
  • Refactor

    • Removed workspace-level connection assignment UI and navigation link
  • Documentation

    • Architecture tasks and mapping approach updated in docs

@coderabbitai

coderabbitai Bot commented Apr 6, 2026 •

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds connector discovery and config endpoints, Redis-cached backend discovery methods, piece-framework types and piece implementations, and multiple frontend UI components/pages to surface related-object discovery, connector config, mapping transforms, and scheduling for stitches.

Changes

Cohort / File(s) Summary
Metadata discovery service
apps/api/src/modules/stitches/metadata-discovery.service.ts
Added describeRelatedObjects and describeConfig with Redis caching, connection/credentials resolving, piece lookup, TTL caching, and graceful error handling.
API controller & stitch service
apps/api/src/modules/stitches/metadata.controller.ts, apps/api/src/modules/stitches/stitches.service.ts
Added two GET endpoints for related-objects and config discovery; extracted object-name validation helper; eagerly load fieldMappings in stitch queries.
Frontend metadata API types & calls
apps/web/src/modules/stitches/api/metadata.api.ts
Added RelatedObjectDescriptor and ConfigOption types and listRelatedObjects / describeConfig client API functions.
Frontend stitch API changes
apps/web/src/modules/stitches/api/stitches.api.ts
Added optional config and fieldMappings to responses/payloads and new triggerSchedule API call.
UI components
apps/web/src/modules/stitches/components/DependencyList.tsx, .../MappingSummary.tsx, .../SchedulePanel.tsx, .../StitchConfigPanel.tsx
Added dependency selector, mapping summary (shows transforms), schedule panel with run/interval controls, and connector config form renderer.
Mapping canvas updates
apps/web/src/modules/stitches/components/MappingCanvas.tsx
Added optional transform per mapping row and propagate transforms in emitted mapping rules; UI/grid updated accordingly.
Pages & routing
apps/web/src/modules/stitches/pages/CreateStitchPage.tsx, .../StitchDetailPage.tsx, apps/web/src/app/routes/TenantRoutes.tsx
Create flow updated to include config, dependency selection, and config tab; added StitchDetailPage route and page with config editing, mapping summary, schedule, and dependency panels.
Workspace UI cleanup
apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx, apps/web/src/shared/components/layout/WorkspaceExplorer.tsx
Removed workspace-level connection assignment UI and its navigation link; simplified workspace detail to fetch/display workspace only.
Piece framework & pieces
engine/platform/piece-framework/src/piece.ts, engine/application/pieces/quickbooks/src/index.ts, engine/application/pieces/salesforce/src/index.ts
Added ConfigOption and RelatedObjectDescriptor types and optional describeRelatedObjects/describeConfig to Piece; implemented these methods for QuickBooks and Salesforce pieces.
Tests, docs, misc
engine/platform/core/src/.../*.spec.ts, docs/architecture/master/tasks.md, packages/database/...sql, apps/web/package.json
Fixed test import paths, updated docs task statuses, minor SQL comment update, and added lodash.isequal dependency for config dirty-checking.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Client as Web Client
    participant Controller as MetadataController
    participant Service as MetadataDiscoveryService
    participant Redis as Redis Cache
    participant Piece as Connector Piece

    User->>Client: request related objects (connectionId, objectName)
    Client->>Controller: GET /connections/{id}/objects/{name}/related
    Controller->>Service: describeRelatedObjects(orgId, connectionId, objectName)
    Service->>Redis: GET meta:related:{connectionId}:{objectName}
    alt cache hit
        Redis-->>Service: cached JSON
        Service-->>Controller: Parsed RelatedObjectDescriptor[]
    else cache miss
        Service->>Service: resolve connection & credentials
        Service->>Piece: describeRelatedObjects(credentials, objectName)
        Piece-->>Service: RelatedObjectDescriptor[] or error
        Service->>Redis: SET meta:related:{connectionId}:{objectName} (TTL)
        Service-->>Controller: RelatedObjectDescriptor[]
    end
    Controller-->>Client: 200 RelatedObjectDescriptor[]
    Client-->>User: render dependencies
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped through pieces, fields, and cache,
Found references, mapped each match,
JSONata twirls where transforms play,
Schedules tick and configs stay,
Stitches sewn — a rabbit's quick batch 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.90% 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 directly addresses the main change: refactoring the stitch UI to retrieve and display related objects, which is a core feature added across API controllers, services, and new UI components throughout the changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/t023-stitches-ui

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx (1)

19-33: ⚠️ Potential issue | 🟠 Major

Reset loading state at fetch start to avoid false “not found” rendering.

Line 22 clears workspace, but wsLoading is never set back to true for subsequent fetches. On id changes, users can see an incorrect not-found/error state while data is still loading.

Suggested fix
   const fetchWorkspace = useCallback(async () => {
-    if (!id) return;
+    if (!id) {
+      setWorkspace(null);
+      setError('Workspace id is missing.');
+      setWsLoading(false);
+      return;
+    }
     const seq = ++fetchSeqRef.current;
+    setWsLoading(true);
+    setError(null);
     setWorkspace(null);
     try {
       const ws = await getWorkspace(id);
       if (seq !== fetchSeqRef.current) return;
       setWorkspace(ws);
-      setError(null);
     } catch (e: unknown) {
       if (seq !== fetchSeqRef.current) return;
       setError(e instanceof Error ? e.message : 'Failed to load workspace.');
     } finally {
       if (seq === fetchSeqRef.current) setWsLoading(false);
     }
   }, [id]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx` around lines
19 - 33, The fetch logic clears workspace but never sets the loading flag back
to true, causing stale "not found" UI on id changes; update fetchWorkspace to
call setWsLoading(true) at the start of the fetch (after the id check and
before/after setWorkspace(null)) so wsLoading is true for the duration of the
request, and keep the existing finally block that sets setWsLoading(false) when
seq matches fetchSeqRef.current; reference fetchWorkspace, fetchSeqRef,
setWorkspace, and setWsLoading when making the change.
apps/api/src/modules/stitches/metadata.controller.ts (1)

52-58: 🧹 Nitpick | 🔵 Trivial

Consider extracting the objectName validation to a reusable utility.

The same regex /^[\w]{1,255}$/ and error message are duplicated in describeFields and describeRelatedObjects. Extracting to a shared validation function or custom pipe would reduce duplication and ensure consistency if the pattern needs updating.

♻️ Example extraction
// In a shared validation utility or as a method in the controller
private validateObjectName(objectName: string): void {
  if (!/^[\w]{1,255}$/.test(objectName)) {
    throw new BadRequestException(
      'objectName must be 1-255 alphanumeric/underscore characters.',
    );
  }
}

Also applies to: 73-77

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

In `@apps/api/src/modules/stitches/metadata.controller.ts` around lines 52 - 58,
Extract the duplicated objectName validation into a single reusable function or
pipe and call it from both describeFields and describeRelatedObjects;
specifically, create a validateObjectName utility (or Nest pipe) that checks
/^[\w]{1,255}$/ and throws the BadRequestException with the existing message,
then replace the inline regex checks in describeFields and
describeRelatedObjects with a call to this new validateObjectName to remove
duplication and ensure consistency.
🤖 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/api/src/modules/stitches/metadata-discovery.service.ts`:
- Around line 318-323: Check the piece capability before fetching credentials:
in metadata-discovery.service.ts, move the piece lookup
(this.pieceRegistry.getPiece(connection.appName)) and the guard that returns []
or 404 when !piece or !piece.describeRelatedObjects to occur before awaiting
this.resolveCredentials(connectionId); similarly update the other block that
calls resolveCredentials (the code handling describeObjects/findRelatedObjects
around lines 352-364) so capability checks run first and only call
resolveCredentials when the piece exists and implements the discovery hook.
- Around line 325-335: The current flow calls piece.describeRelatedObjects(...)
and caches the empty default on any exception, so transient errors are stored;
change the logic so that this.redis.set(redisKey, JSON.stringify(related), 'EX',
TTL_SECONDS) is only executed when describeRelatedObjects succeeds: move the
redis.set call into the try block (or set a success flag) and avoid writing to
redis after the catch; keep the existing this.logger.warn(...) in the catch and
still return the in-memory related variable but do not persist it to redis on
failure.

In `@apps/web/src/modules/stitches/components/DependencyList.tsx`:
- Around line 12-15: The component flashes an empty state because loading is
initialized false; in the DependencyList component change the loading useState
initialization from useState(false) to useState(true) so the spinner shows
immediately on mount (leave error and relatedObjects as-is and keep existing
setLoading usage in effects).
- Around line 71-72: The component is using the array index (idx) as the React
key in the relatedObjects.map render, which can cause unstable keys; change the
key to a stable unique identifier from the item (e.g., use mod.objectName or
another unique field on RelatedObjectDescriptor) by replacing key={idx} with
key={mod.objectName} (or appropriate unique property) inside the map callback to
ensure stable reconciliation.

In `@apps/web/src/modules/stitches/components/MappingSummary.tsx`:
- Around line 29-61: The current key for each rendered rule uses
`${fm.sourceCanonical}-${idx}` inside mappings.map(...) and
fm.mappingRules.map(...), which can collide if the same sourceCanonical appears
across different field mappings; update the key to include a unique identifier
for the parent mapping (e.g., include the outer mapping index from mappings.map
or a unique field mapping id like fm.id) or flatten mappings into a single list
with a stable id per rule, then use that stable unique id (for example
`${fmId}-${idx}` or rule.id) as the React key to prevent collisions.

In `@apps/web/src/modules/stitches/components/RelatedObjectsPanel.tsx`:
- Around line 60-61: In RelatedObjectsPanel, replace the unstable array index
key used in relatedObjects.map (currently "key={idx}") with a stable composite
key built from the item's unique fields—e.g., use a combination of objectName
and relationField (or another guaranteed-unique identifier on the item) to
ensure consistent keys across renders; mirror the same approach used for
DependencyList so keys remain stable and avoid rendering bugs.
- Around line 12-83: RelatedObjectsPanel duplicates the fetch/state logic used
in DependencyList; extract that logic into a shared hook (e.g.,
useRelatedObjects(connectionId, objectName)) and have RelatedObjectsPanel call
useRelatedObjects(stitch.srcConnectionId, stitch.sourceObject) instead of
maintaining its own useEffect/useState; ensure the hook returns { loading,
error, relatedObjects } and both RelatedObjectsPanel and DependencyList consume
those values for their respective UI rendering.
- Around line 12-15: The RelatedObjectsPanel component currently sets loading to
false causing an empty-state flash; update the useState initializer for loading
in RelatedObjectsPanel to true so the spinner shows immediately (mirror the
pattern used in DependencyList), and ensure existing setLoading(false) calls
remain unchanged so loading is cleared when data or error is received; locate
the useState hook for loading in RelatedObjectsPanel to make this one-line
change.

In `@apps/web/src/modules/stitches/components/SchedulePanel.tsx`:
- Around line 60-66: The catch block in SchedulePanel.tsx uses a fragile inline
type assertion for the thrown error when calling toast; create a small reusable
utility (e.g., extractErrorMessage(e: unknown)) that safely inspects unknown
errors for Axios-style response.data.message and falls back to Error.message or
a default string, then replace the inline assertion in the catch block (the
toast call using (e as { response?: { data?: { message?: string } } })...) with
description: extractErrorMessage(e); keep the utility exported or colocated so
it can be reused by other components.
- Around line 129-134: The UI currently shows a static "Pending execution"
whenever stitch.scheduleEnabled is true; update SchedulePanel.tsx to compute and
display a real next-run message instead: when stitch.scheduleEnabled is true use
stitch.lastScheduledAt and stitch.syncIntervalMinutes to calculate remaining
minutes (nextRun = lastScheduledAt + syncIntervalMinutes) and render "Next sync
in ~X min" (with a sensible fallback like "Enabled" or "No schedule info" if
lastScheduledAt or syncIntervalMinutes are missing), and ensure the component
updates/reacts to time changes (e.g., recalc on mount and interval) rather than
always showing "Pending execution".

In `@apps/web/src/modules/stitches/components/StitchConfigPanel.tsx`:
- Around line 36-40: The catch block in StitchConfigPanel.tsx currently swallows
404 errors (err.response?.status !== 404) which hides real missing-connection
failures; remove that special-case and always call setError(err.message ||
'Failed to load configuration options') in the catch so all errors (including
404 from missing connection) surface, leaving describeConfig to return [] for
legitimately empty schemas; update the catch handling around the describeConfig
call (the err variable and setError invocation) accordingly.
- Around line 19-47: The load effect can apply stale responses when connectionId
changes; update the load logic in the useEffect (the async load function that
calls describeConfig) to cancel or ignore stale requests and to clear schema on
missing connectors: either create an AbortController per invocation and pass its
signal into describeConfig (and check for AbortError before applying results),
or use a request-counter stored in a ref (increment at start of load and capture
currentId; only call setSchema, onChange and setError if currentId matches) so
slower responses are ignored; also reset the schema at the start of a new load
(e.g., setSchema(null) or []) and when a 404 occurs explicitly clear the schema
instead of silently ignoring it; keep using connectionId, describeConfig,
setSchema, onChange, setError, and setLoading as the referenced symbols.

In `@apps/web/src/modules/stitches/pages/StitchDetailPage.tsx`:
- Around line 94-95: Replace the brittle JSON.stringify comparison used to set
isConfigDirty with a stable deep equality check: import and use a deep-equal
utility (e.g., lodash.isequal or deep-equal) to compare configDraft against
stitch.config (or {}), and update the isConfigDirty assignment to use that
deep-equal function; ensure the import and the identifier isEqual (or chosen
name) are referenced where isConfigDirty is computed so key ordering won't cause
false positives.

In `@packages/database/drizzle/0003_add_gem_indexes.sql`:
- Around line 21-22: The migration creates blocking indexes using CREATE INDEX
on global_entity_map (gem_source_app_idx, gem_dest_app_idx) despite the header
instructing manual CONCURRENTLY creation; to fix, either replace the CREATE
INDEX statements with a no-op placeholder (e.g., leave the migration as a SELECT
1) so the migration runner won't apply blocking indexes and then create the
indexes manually with CREATE INDEX CONCURRENTLY, or if you intend to allow
blocking, remove/update the header comments and keep the CREATE INDEX
lines—update the file so the SQL and header are consistent.

---

Outside diff comments:
In `@apps/api/src/modules/stitches/metadata.controller.ts`:
- Around line 52-58: Extract the duplicated objectName validation into a single
reusable function or pipe and call it from both describeFields and
describeRelatedObjects; specifically, create a validateObjectName utility (or
Nest pipe) that checks /^[\w]{1,255}$/ and throws the BadRequestException with
the existing message, then replace the inline regex checks in describeFields and
describeRelatedObjects with a call to this new validateObjectName to remove
duplication and ensure consistency.

In `@apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx`:
- Around line 19-33: The fetch logic clears workspace but never sets the loading
flag back to true, causing stale "not found" UI on id changes; update
fetchWorkspace to call setWsLoading(true) at the start of the fetch (after the
id check and before/after setWorkspace(null)) so wsLoading is true for the
duration of the request, and keep the existing finally block that sets
setWsLoading(false) when seq matches fetchSeqRef.current; reference
fetchWorkspace, fetchSeqRef, setWorkspace, and setWsLoading when making the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5a65e138-d833-4bd8-85a5-54d164e0f57b

📥 Commits

Reviewing files that changed from the base of the PR and between e617c16 and 0510a2f.

📒 Files selected for processing (21)
  • apps/api/src/modules/stitches/metadata-discovery.service.ts
  • apps/api/src/modules/stitches/metadata.controller.ts
  • apps/api/src/modules/stitches/stitches.service.ts
  • apps/web/src/app/routes/TenantRoutes.tsx
  • apps/web/src/modules/stitches/api/metadata.api.ts
  • apps/web/src/modules/stitches/api/stitches.api.ts
  • apps/web/src/modules/stitches/components/DependencyList.tsx
  • apps/web/src/modules/stitches/components/MappingCanvas.tsx
  • apps/web/src/modules/stitches/components/MappingSummary.tsx
  • apps/web/src/modules/stitches/components/RelatedObjectsPanel.tsx
  • apps/web/src/modules/stitches/components/SchedulePanel.tsx
  • apps/web/src/modules/stitches/components/StitchConfigPanel.tsx
  • apps/web/src/modules/stitches/pages/CreateStitchPage.tsx
  • apps/web/src/modules/stitches/pages/StitchDetailPage.tsx
  • apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx
  • apps/web/src/shared/components/layout/WorkspaceExplorer.tsx
  • docs/architecture/master/tasks.md
  • engine/application/pieces/quickbooks/src/index.ts
  • engine/application/pieces/salesforce/src/index.ts
  • engine/platform/piece-framework/src/piece.ts
  • packages/database/drizzle/0003_add_gem_indexes.sql

Comment thread apps/api/src/modules/stitches/metadata-discovery.service.ts Outdated
Comment thread apps/api/src/modules/stitches/metadata-discovery.service.ts
Comment thread apps/web/src/modules/stitches/components/DependencyList.tsx Outdated
Comment thread apps/web/src/modules/stitches/components/DependencyList.tsx Outdated
Comment thread apps/web/src/modules/stitches/components/MappingSummary.tsx Outdated
Comment thread apps/web/src/modules/stitches/components/SchedulePanel.tsx
Comment thread apps/web/src/modules/stitches/components/StitchConfigPanel.tsx
Comment thread apps/web/src/modules/stitches/components/StitchConfigPanel.tsx Outdated
Comment thread apps/web/src/modules/stitches/pages/StitchDetailPage.tsx Outdated
Comment thread packages/database/drizzle/0003_add_gem_indexes.sql Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 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/api/src/modules/stitches/metadata-discovery.service.ts`:
- Around line 365-371: The call to piece.describeConfig should be wrapped in a
try/catch like describeRelatedObjects to prevent exceptions bubbling up; change
the block around piece.describeConfig(credentials) (the variable config) to
catch any errors, log or warn as appropriate, set config = [] on failure, and
still call this.redis.set(redisKey, JSON.stringify(config), 'EX', TTL_SECONDS)
so we cache the empty result; reference piece.describeConfig,
describeRelatedObjects, config, this.redis.set and TTL_SECONDS when making the
change.

In `@apps/web/src/modules/stitches/components/DependencyList.tsx`:
- Around line 119-124: The checkbox input in DependencyList.tsx currently has an
empty onChange (checked={isChecked} onChange={() => {}}), which breaks keyboard
accessibility; update the input to either call the same row toggle handler used
by the parent row (e.g., replace onChange={() => {}} with the existing toggle
handler so the checkbox toggles when focused and Enter/Space is pressed) or, if
the checkbox is purely presentational, mark it readOnly and ensure appropriate
ARIA attributes are set; ensure the change targets the <input ...
checked={isChecked} onChange={...}/> line and uses the component's existing
toggle function name (or add a small handleCheckboxChange that invokes the row
toggle).

In `@apps/web/src/modules/stitches/components/SchedulePanel.tsx`:
- Around line 36-41: Replace the inline error handling in handleIntervalChange
and handleToggle with the shared extractor to ensure Axios-style messages are
preserved: use extractErrorMessage(e) in the toast description instead of the
current e instanceof Error ? e.message : '...'. Also change the catch in
handleTrigger (if different) to use the same extractErrorMessage function so all
three handlers (handleTrigger, handleIntervalChange, handleToggle) consistently
call extractErrorMessage(e) for their toast descriptions.
- Around line 1-16: The file places the utility function extractErrorMessage
between import statements which breaks the convention of having imports at the
top; move the entire extractErrorMessage function so it appears after all import
statements (i.e., below the import block and before the component code),
ensuring any references (e.g., calls to extractErrorMessage within SchedulePanel
or other functions in this file) continue to resolve without changing its
signature or behavior.

In `@apps/web/src/modules/stitches/components/StitchConfigPanel.tsx`:
- Line 15: In StitchConfigPanel, the loading state is initialized to false which
can produce a brief empty-state flash; change the useState initial value in the
component (the const [loading, setLoading] = useState(...) call) from false to
true so the UI shows a loading state until your effect finishes and calls
setLoading(false), ensuring you still setLoading(false) in all success/error
paths in the existing load/config-fetch logic.

In `@apps/web/src/modules/stitches/pages/StitchDetailPage.tsx`:
- Around line 54-70: handleConfigSave currently updates the stitch via
updateStitch and sets the returned stitch in setStitch but does not sync
configDraft, causing isConfigDirty to remain true if the backend normalizes
config; after a successful update (inside the try block, after
setStitch(updated)) call setConfigDraft(updated.config) (or the
normalized/serialized value returned by updateStitch) so the local draft matches
the server response; ensure you reference the updated object from updateStitch
and update the configDraft state (e.g., setConfigDraft) with the server-provided
config to clear the dirty flag and preserve types/shape.
- Around line 95-96: StitchConfigPanel's mount-applied schema defaults are
mutating configDraft and causing immediate dirty state; to fix, create a
baseline that includes schema defaults and compare configDraft against that
instead of raw stitch.config: when StitchDetailPage loads, compute a baseline
like baselineConfig = mergeDefaults(stitch.config || {}, schemaDefaults) (obtain
schemaDefaults the same way StitchConfigPanel does) and change isConfigDirty to
!isEqual(configDraft, baselineConfig); alternatively, stop StitchConfigPanel
from calling onChange when only applying defaults (i.e., only call onChange for
user-initiated edits) so configDraft isn't updated by default population.
Reference: StitchDetailPage (configDraft, isConfigDirty, stitch.config) and
StitchConfigPanel (onChange, schema default application).

In `@apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx`:
- Around line 19-23: The fetchWorkspace callback returns early when the route
param id is missing but leaves wsLoading true, causing a permanent loading
state; update fetchWorkspace (the function that uses fetchSeqRef, setWsLoading
and setWorkspace) to clear the loading state before returning (e.g., call
setWsLoading(false) and optionally setWorkspace(null) when !id) or move the id
existence check higher so you never set wsLoading(true) unless id is present;
ensure any early-return paths reset wsLoading to false to avoid the stuck
“Loading workspace…” UI.

In `@packages/database/drizzle/0003_add_gem_indexes.sql`:
- Line 21: The migration comment contains an accidental Arabic token "الد"
within the placeholder line "SELECT 1; -- Placeholder to satisfy الد migration
runner safely." — remove the "الد" token so the comment reads clearly (e.g.,
"SELECT 1; -- Placeholder to satisfy migration runner safely.") to avoid
confusion during runbooks; update the placeholder comment in the migration file
(look for the "SELECT 1;" line) and keep the SQL unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1445e73c-c561-442e-a634-32e618a1dc27

📥 Commits

Reviewing files that changed from the base of the PR and between 0510a2f and b6cfa0c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • apps/api/src/modules/stitches/metadata-discovery.service.ts
  • apps/api/src/modules/stitches/metadata.controller.ts
  • apps/web/package.json
  • apps/web/src/modules/stitches/components/DependencyList.tsx
  • apps/web/src/modules/stitches/components/MappingSummary.tsx
  • apps/web/src/modules/stitches/components/SchedulePanel.tsx
  • apps/web/src/modules/stitches/components/StitchConfigPanel.tsx
  • apps/web/src/modules/stitches/pages/CreateStitchPage.tsx
  • apps/web/src/modules/stitches/pages/StitchDetailPage.tsx
  • apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx
  • engine/application/pieces/salesforce/src/index.ts
  • packages/database/drizzle/0003_add_gem_indexes.sql

Comment thread apps/api/src/modules/stitches/metadata-discovery.service.ts
Comment thread apps/web/src/modules/stitches/components/DependencyList.tsx
Comment thread apps/web/src/modules/stitches/components/SchedulePanel.tsx Outdated
Comment on lines +36 to +41
} catch (e) {
toast({
title: 'Update failed',
description: e instanceof Error ? e.message : 'Could not update interval',
variant: 'destructive',
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Inconsistent error message extraction across handlers.

handleTrigger uses extractErrorMessage (line 73) while handleIntervalChange (line 39) and handleToggle (line 56) use inline e instanceof Error ? e.message : '...'. This inconsistency means the latter handlers won't extract Axios-style response.data.message errors properly.

♻️ Use extractErrorMessage consistently
     } catch (e) {
       toast({
         title: 'Update failed',
-        description: e instanceof Error ? e.message : 'Could not update interval',
+        description: extractErrorMessage(e, 'Could not update interval'),
         variant: 'destructive',
       });
     } finally {
     } catch (e) {
       toast({
         title: 'Toggle failed',
-        description: e instanceof Error ? e.message : 'Could not toggle schedule',
+        description: extractErrorMessage(e, 'Could not toggle schedule'),
         variant: 'destructive',
       });
     } finally {

Also applies to: 53-58

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

In `@apps/web/src/modules/stitches/components/SchedulePanel.tsx` around lines 36 -
41, Replace the inline error handling in handleIntervalChange and handleToggle
with the shared extractor to ensure Axios-style messages are preserved: use
extractErrorMessage(e) in the toast description instead of the current e
instanceof Error ? e.message : '...'. Also change the catch in handleTrigger (if
different) to use the same extractErrorMessage function so all three handlers
(handleTrigger, handleIntervalChange, handleToggle) consistently call
extractErrorMessage(e) for their toast descriptions.

Comment thread apps/web/src/modules/stitches/components/StitchConfigPanel.tsx Outdated
Comment thread apps/web/src/modules/stitches/pages/StitchDetailPage.tsx
Comment thread apps/web/src/modules/stitches/pages/StitchDetailPage.tsx
Comment thread apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx
Comment thread packages/database/drizzle/0003_add_gem_indexes.sql Outdated
@pramodnarayana
pramodnarayana marked this pull request as draft April 6, 2026 11:44
@pramodnarayana
pramodnarayana marked this pull request as ready for review April 6, 2026 11:44
@pramodnarayana
pramodnarayana marked this pull request as draft April 6, 2026 11:46
@pramodnarayana
pramodnarayana marked this pull request as ready for review April 6, 2026 11:47
@pramodnarayana

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx (1)

20-23: ⚠️ Potential issue | 🟡 Minor

Reset workspace/error when route id is absent.

Loading now exits correctly, but this branch can still leave stale workspace or prior error if the component remains mounted while id becomes undefined.

💡 Proposed fix
   if (!id) {
+    setWorkspace(null);
+    setError('Workspace not found.');
     setWsLoading(false);
     return;
   }

Use this read-only check to confirm whether this component can render without id (or be reused across param transitions):

#!/bin/bash
set -euo pipefail

echo "1) Find where WorkspaceDetailPage is mounted in routes"
rg -n -C3 '\bWorkspaceDetailPage\b' --iglob '*.ts' --iglob '*.tsx'

echo
echo "2) Inspect workspace route path patterns (:id vs :id?)"
rg -n -C3 'workspaces/:id\??|path\s*[:=].*workspaces' --iglob '*.ts' --iglob '*.tsx'

echo
echo "3) Re-check guard logic in page component"
rg -n -C4 'useParams<\{ id: string \}>|if \(!id\)' apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx` around lines
20 - 23, In the WorkspaceDetailPage component, the early-return branch "if (!id)
{ setWsLoading(false); return; }" can leave stale workspace or error state;
update that branch to also clear those states (call setWorkspace(null or
undefined) and setError(null or undefined)) before setWsLoading(false) so the
component doesn't render stale data when params drop out; ensure you reference
the existing state setters (setWorkspace, setError, setWsLoading) used in this
component and keep the early-return behavior intact.
packages/database/drizzle/0000_salty_stryfe.sql (1)

324-326: ⚠️ Potential issue | 🟠 Major

Keep GEM app-index creation consistent with the online-migration contract.

Lines 325-326 add transactional CREATE INDEX statements for gem_source_app_idx and gem_dest_app_idx, which conflicts with the out-of-band CONCURRENTLY workflow documented in packages/database/drizzle/0003_add_gem_indexes.sql and weakens what packages/database/drizzle/0004_verify_gem_indexes.sql is intended to enforce.

Proposed adjustment
 CREATE INDEX "gem_stitch_idx" ON "global_entity_map" USING btree ("stitch_id");--> statement-breakpoint
-CREATE INDEX "gem_source_app_idx" ON "global_entity_map" USING btree ("source_app_id");--> statement-breakpoint
-CREATE INDEX "gem_dest_app_idx" ON "global_entity_map" USING btree ("dest_app_id");
+-- gem_source_app_idx / gem_dest_app_idx are created out-of-band with
+-- CREATE INDEX CONCURRENTLY (see 0003_add_gem_indexes.sql).
#!/bin/bash
set -euo pipefail

echo "== packages/database/drizzle/0000_salty_stryfe.sql (Lines 318-332) =="
nl -ba packages/database/drizzle/0000_salty_stryfe.sql | sed -n '318,332p'

echo
echo "== packages/database/drizzle/0003_add_gem_indexes.sql (Lines 1-40) =="
nl -ba packages/database/drizzle/0003_add_gem_indexes.sql | sed -n '1,40p'

echo
echo "== packages/database/drizzle/0004_verify_gem_indexes.sql (Lines 1-40) =="
nl -ba packages/database/drizzle/0004_verify_gem_indexes.sql | sed -n '1,40p'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/database/drizzle/0000_salty_stryfe.sql` around lines 324 - 326, The
two transactional CREATE INDEX statements for gem_source_app_idx and
gem_dest_app_idx conflict with the out-of-band CONCURRENTLY workflow; remove the
transactional lines creating "gem_source_app_idx" and "gem_dest_app_idx" from
this baseline so index creation is handled by the online migration
(0003_add_gem_indexes.sql using CREATE INDEX CONCURRENTLY) and verified by
0004_verify_gem_indexes.sql.
🤖 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/api/src/modules/stitches/metadata-discovery.service.ts`:
- Around line 365-379: The code unconditionally writes an empty config to Redis
when piece.describeConfig throws, which caches failures; modify the logic in
metadata-discovery.service.ts so that redis.set(redisKey,
JSON.stringify(config), 'EX', TTL_SECONDS) is only called on successful
describeConfig resolution (i.e., move the redis.set into the try block after
await piece.describeConfig(credentials) or add a conditional that only sets when
piece.describeConfig succeeded), keep the existing logger.warn in the catch and
do not cache on catch, and ensure references to redis.set, redisKey,
TTL_SECONDS, piece.describeConfig, and describeRelatedObjects are used to locate
and mirror the successful-only caching behavior.

---

Duplicate comments:
In `@apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx`:
- Around line 20-23: In the WorkspaceDetailPage component, the early-return
branch "if (!id) { setWsLoading(false); return; }" can leave stale workspace or
error state; update that branch to also clear those states (call
setWorkspace(null or undefined) and setError(null or undefined)) before
setWsLoading(false) so the component doesn't render stale data when params drop
out; ensure you reference the existing state setters (setWorkspace, setError,
setWsLoading) used in this component and keep the early-return behavior intact.

In `@packages/database/drizzle/0000_salty_stryfe.sql`:
- Around line 324-326: The two transactional CREATE INDEX statements for
gem_source_app_idx and gem_dest_app_idx conflict with the out-of-band
CONCURRENTLY workflow; remove the transactional lines creating
"gem_source_app_idx" and "gem_dest_app_idx" from this baseline so index creation
is handled by the online migration (0003_add_gem_indexes.sql using CREATE INDEX
CONCURRENTLY) and verified by 0004_verify_gem_indexes.sql.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 64c0eae0-1b8f-41cc-a56c-88b1d3071241

📥 Commits

Reviewing files that changed from the base of the PR and between b6cfa0c and 8569d8a.

📒 Files selected for processing (10)
  • apps/api/src/modules/stitches/metadata-discovery.service.ts
  • apps/web/src/modules/stitches/components/DependencyList.tsx
  • apps/web/src/modules/stitches/components/SchedulePanel.tsx
  • apps/web/src/modules/stitches/components/StitchConfigPanel.tsx
  • apps/web/src/modules/stitches/pages/StitchDetailPage.tsx
  • apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx
  • engine/platform/core/src/pieces/piece-registry.service.spec.ts
  • engine/platform/core/src/storage-resolver/storage-resolver.service.spec.ts
  • packages/database/drizzle/0000_salty_stryfe.sql
  • packages/database/drizzle/0003_add_gem_indexes.sql

Comment thread apps/api/src/modules/stitches/metadata-discovery.service.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/api/src/modules/stitches/metadata-discovery.service.ts`:
- Around line 365-381: The current try/catch around both piece.describeConfig
and this.redis.set can overwrite a valid config when the Redis write fails;
change the logic so successful piece.describeConfig results are preserved even
if caching fails. Call await piece.describeConfig(credentials) first (assign to
config) and only wrap the this.redis.set(redisKey, JSON.stringify(config), 'EX',
TTL_SECONDS) in its own try/catch that logs via this.logger.warn (including
connection.appName and the error) but does not modify the config variable;
alternatively, ensure that only failures from piece.describeConfig reset config
= [] while failures from this.redis.set only log and leave config intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f428495a-d3d7-4838-810e-6ccd710f8a1b

📥 Commits

Reviewing files that changed from the base of the PR and between 8569d8a and d2076fd.

📒 Files selected for processing (2)
  • apps/api/src/modules/stitches/metadata-discovery.service.ts
  • apps/web/src/modules/workspaces/pages/WorkspaceDetailPage.tsx

Comment thread apps/api/src/modules/stitches/metadata-discovery.service.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/api/src/modules/stitches/metadata-discovery.service.ts`:
- Around line 389-392: There is a duplicate, unreachable "return config;" in the
MetadataDiscoveryService method that returns the configuration; remove the
second/extra "return config;" so the method only returns once, and verify there
is no other dead code after the remaining return statement (look for the
duplicate "return config" text to locate the exact spot).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7fe10e38-a5c4-4318-995b-9ccb21492a29

📥 Commits

Reviewing files that changed from the base of the PR and between d2076fd and 1a57cc5.

📒 Files selected for processing (1)
  • apps/api/src/modules/stitches/metadata-discovery.service.ts

Comment thread apps/api/src/modules/stitches/metadata-discovery.service.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@pramodnarayana
pramodnarayana merged commit bf4f0a9 into development Apr 6, 2026
2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Apr 8, 2026
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.

1 participant