Skip to content

feat(connections): re-assign widgets to a different connection (#510) - #588

Merged
alfredo1996 merged 2 commits into
release/2.0from
feat/issue-510-reassign-widgets
Apr 21, 2026
Merged

feat(connections): re-assign widgets to a different connection (#510)#588
alfredo1996 merged 2 commits into
release/2.0from
feat/issue-510-reassign-widgets

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented Apr 20, 2026

Copy link
Copy Markdown
Owner

Summary

Users can now migrate widgets to another connection instead of having to pick between "Leave alone" or "Delete anyway, break them all".

Server

  • New endpoint: POST /api/connections/{id}/reassign body { targetConnectionId }
  • reassignConnectionWidgets() walks editable dashboards (owner + editor-shared; admin sees all-in-tenant) and swaps connectionId via a single jsonb_set UPDATE
  • Guards: source ownership, target exists in same tenant, same connector type (Cypher won't run on PG and vice versa)
  • Returns { dashboardsUpdated, widgetsReassigned }

Client

  • useReassignConnection hook (invalidates connection-usage + dashboards on success)
  • Delete-connection dialog now has a "Re-assign widgets to another connection…" button when the connection is in use
  • Secondary dialog:
    • Lists compatible (same-type) connections
    • Shows helpful message when none available
    • Error state for failed reassigns
    • On success, closes and invalidates queries — the delete dialog can then be reopened with 0 widgets remaining

Scope notes (from issue)

  • Query compatibility is NOT validated. Only type compatibility is enforced — broken widget queries will show their usual error state at runtime.
  • The "then delete" chaining is intentionally left to the user's next click. Reassign is the atomic operation; delete is a separate decision.

Test plan

  • 10 new route tests (auth, validation, ownership, same-id, target-missing, type-mismatch, admin-path, happy-path × 2, error)
  • Full suite: 2036 pass
  • Build clean

Closes #510

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added connection reassignment functionality: users can now reassign widgets to alternative compatible connections instead of losing them when deleting a connection.
  • Tests

    • Added comprehensive test coverage for the connection reassignment feature.

Closes #510 — users can now migrate widgets to another connection
instead of having to "Delete anyway" and break them.

Server:
- POST /api/connections/{id}/reassign with { targetConnectionId }
- reassignConnectionWidgets() walks editable dashboards (owner + editor
  shares, admin sees all-in-tenant) and swaps connectionId on matching
  widgets via a single jsonb_set UPDATE
- Guards: source ownership, target exists in same tenant, same type
- Returns { dashboardsUpdated, widgetsReassigned }

Client:
- useReassignConnection hook
- Delete-connection dialog exposes a "Re-assign widgets to another
  connection…" button when the connection is in use
- Secondary dialog lists compatible (same-type) connections; shows a
  helpful message when none are available

Tests:
- 10 new route tests (auth, validation, ownership, type-mismatch,
  admin-path, happy-path, error propagation)
- Full suite: 2036 pass

Closes #510

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 20, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@alfredo1996 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 58 minutes and 11 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 58 minutes and 11 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 011f7bde-69bc-4205-a491-47238f1da1d1

📥 Commits

Reviewing files that changed from the base of the PR and between 5f3e082 and f114dea.

📒 Files selected for processing (1)
  • app/src/lib/__tests__/db/connection-reassign.test.ts

Walkthrough

This PR implements a feature to re-assign widgets from one connection to another before deletion. It adds a new API endpoint (POST /api/connections/{id}/reassign), database logic for updating widgets across editable dashboards, UI dialog for selecting target connections, a React hook for the mutation, and comprehensive test coverage for all validation paths.

Changes

Cohort / File(s) Summary
Frontend UI
app/src/app/(dashboard)/connections/page.tsx
Added reassign dialog flow triggered from delete confirmation, with dropdown selection of compatible (same-type) target connections and error handling.
API Route Handler
app/src/app/api/connections/[id]/reassign/route.ts
New POST endpoint validating request auth, checking source/target ownership and tenant scoping, enforcing type matching, and calling the reassign service function.
Database Logic
app/src/lib/db/connection-reassign.ts
Core reassignment function that updates widget connectionId values across editable dashboards using tenant-scoped queries, with separate logic for admin vs. non-admin access.
React Hook
app/src/hooks/use-connections.ts
New useReassignConnection hook and ReassignResult interface for the mutation, invalidating ["connection-usage"] and ["dashboards"] queries on success.
API Tests
app/src/app/api/connections/[id]/reassign/__tests__/route.test.ts
Comprehensive test suite covering authentication, validation errors (missing field, same source/target, type mismatch, missing connections), ownership checks, and success scenarios with both admin and non-admin sessions.

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as Frontend Dialog
    participant API as API Route<br/>/reassign
    participant DB as Database
    
    User->>UI: Selects target connection
    UI->>UI: Validate selection
    User->>UI: Clicks "Re-assign"
    UI->>API: POST /api/connections/{id}/reassign<br/>{ targetConnectionId }
    
    rect rgba(100, 150, 200, 0.5)
    API->>API: Authenticate user
    API->>API: Validate body & rules
    API->>DB: Check source/target exist<br/>& ownership
    API->>DB: Verify type match
    end
    
    DB->>DB: Update layoutJson:<br/>swap connectionId across<br/>editable dashboards
    DB-->>API: ReassignResult
    
    API-->>UI: { dashboardsUpdated,<br/>widgetsReassigned }
    UI->>UI: Close dialog
    UI->>UI: Invalidate queries
    UI-->>User: Success feedback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • #512 — Adds related connection-management flows (usage counts, force-delete) to the same connections page and hooks file
  • #349 — Modifies the connections page UI/dialog logic in parallel

Suggested labels

enhancement, pkg:app, area:widgets

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary feature: adding widget re-assignment functionality when deleting connections, which matches the main objective of the PR.
Linked Issues check ✅ Passed All critical objectives from #510 are implemented: POST /api/connections/{id}/reassign endpoint with tenant/auth checks [#510], type-compatibility enforcement [#510], connection-ownership validation [#510], UI dialog with compatible connection dropdown [#510], re-assign widget count responses [#510], and comprehensive route tests [#510].
Out of Scope Changes check ✅ Passed All changes directly support the re-assign feature scope: server endpoint and database logic, client hook and UI dialog, and corresponding tests—no unrelated modifications detected.

✏️ 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 feat/issue-510-reassign-widgets

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

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
app/src/app/api/connections/[id]/reassign/__tests__/route.test.ts (1)

54-58: Avoid any for the route handler type.

The exported route type is available, so this can stay strict without an eslint suppression.

♻️ Proposed fix
-  let POST: (
-    req: Request,
-    ctx: { params: Promise<{ id: string }> },
-    // eslint-disable-next-line `@typescript-eslint/no-explicit-any`
-  ) => Promise<any>;
+  let POST: typeof import("../route").POST;

As per coding guidelines, **/*.{ts,tsx}: TypeScript strict mode enforced. No any type without a comment explaining why.

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

In `@app/src/app/api/connections/`[id]/reassign/__tests__/route.test.ts around
lines 54 - 58, The test declares a POST variable using Promise<any> and disables
eslint; instead import and use the exported route handler type from the route
implementation so the type is strict. Replace the ad-hoc signature for the
test-level POST with the actual exported POST type from the route module
(reference the test variable name POST and the exported POST in the route file)
and remove the eslint-disable comment; ensure the ctx param type matches the
route's declared params type rather than using any.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/app/api/connections/`[id]/reassign/route.ts:
- Around line 43-45: The route currently only checks role/isAdmin (via
requireSession() and isAdmin) but does not enforce the user's can_write
permission before performing the mutation; add a server-side check using the
session's canWrite (or can_write) property immediately after obtaining the
session (where requireSession() is called) and before any reassign logic
(including the code around the reassign handlers referenced later at lines
~94-100), and if can_write is false return a 403/forbidden response (or throw an
appropriate error) to prevent read-only sessions from modifying dashboard/widget
layout.

In `@app/src/lib/db/connection-reassign.ts`:
- Around line 13-39: The SQL predicate in editableDashboardsScope incorrectly
checks for an 'owner' share role (s.role IN ('editor', 'owner')) even though
shareRoleEnum only permits 'viewer' and 'editor'; update the WHERE clause in
editableDashboardsScope to remove 'owner' (use s.role IN ('editor') or s.role =
'editor' as appropriate) and make the same change to any other SQL that queries
the "dashboard_share" table for s.role IN ('editor','owner') (e.g., the
reassignment/query logic that references "dashboard_share" and s.role) so the
query uses only valid roles.
- Around line 120-152: In the UPDATE "dashboard" statement (the db.execute block
that sets "layoutJson"), wrap the inner jsonb_agg that builds the widgets array
with COALESCE(..., '[]'::jsonb) so pages with no widgets get an empty array
instead of null; also preserve array order by changing the
jsonb_array_elements(...) calls to use WITH ORDINALITY and re-aggregate ordering
(use the ordinality index in the outer jsonb_agg ORDER BY) for both the widgets
aggregation and the pages aggregation to maintain original ordering when calling
jsonb_set on page->'widgets' and when aggregating pages.

---

Nitpick comments:
In `@app/src/app/api/connections/`[id]/reassign/__tests__/route.test.ts:
- Around line 54-58: The test declares a POST variable using Promise<any> and
disables eslint; instead import and use the exported route handler type from the
route implementation so the type is strict. Replace the ad-hoc signature for the
test-level POST with the actual exported POST type from the route module
(reference the test variable name POST and the exported POST in the route file)
and remove the eslint-disable comment; ensure the ctx param type matches the
route's declared params type rather than using any.
🪄 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: CHILL

Plan: Pro

Run ID: 28553b97-8756-4ee2-80b8-ace571f4c751

📥 Commits

Reviewing files that changed from the base of the PR and between 39b0ab1 and 5f3e082.

📒 Files selected for processing (5)
  • app/src/app/(dashboard)/connections/page.tsx
  • app/src/app/api/connections/[id]/reassign/__tests__/route.test.ts
  • app/src/app/api/connections/[id]/reassign/route.ts
  • app/src/hooks/use-connections.ts
  • app/src/lib/db/connection-reassign.ts

Comment thread app/src/app/api/connections/[id]/reassign/route.ts
Comment thread app/src/lib/db/connection-reassign.ts
Comment thread app/src/lib/db/connection-reassign.ts
8 new tests: same-id no-op, unused-source no-op, non-admin + admin
success paths, null/empty count handling, error propagation from both
the count and UPDATE queries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@alfredo1996
alfredo1996 merged commit ee8388f into release/2.0 Apr 21, 2026
13 checks passed
@alfredo1996
alfredo1996 deleted the feat/issue-510-reassign-widgets branch April 21, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants