Conversation
WalkthroughThis change refactors the resource relationship visualization feature. It removes the previous, complex recursive data fetching and graph-building logic, replacing it with a simplified structure. The React components and supporting files for node and edge generation are restructured, with a new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Page
participant API
participant DB
Client->>Page: Request visualize page (with workspaceSlug, resourceId)
Page->>API: api.resource.relationships({ resourceId })
API->>DB: Fetch resource by ID
API->>DB: getResourceParents(resourceId)
API->>DB: getResourceChildren(resourceId)
API-->>Page: { resource, parents, children }
Page->>RelationshipsDiagramProvider: Pass resource, parents, children
RelationshipsDiagramProvider->>RelationshipsDiagram: Render diagram
RelationshipsDiagram->>ReactFlow: Render nodes and edges
Poem
Note ⚡️ AI Code Reviews for VS Code, Cursor, WindsurfCodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback. Note ⚡️ Faster reviews with cachingCodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure ✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceNode.tsx (1)
21-22: VerifyuseParamsgeneric – type definition may not exist in all Next.js versions
useParams<{ resourceId: string }>()compiles only on the very latestnext/navigationtypings.
If your project still targets ≤ 13.4, the generic overload is not available and this will silently fall back toany, defeating the purpose of the explicit cast.Consider guarding the call with a local helper, or simply narrowing the returned record:
const resourceId = useParams()?.resourceId as string | undefined;This keeps the code compatible with older minor versions while preserving the type guarantee.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/page.tsx (1)
31-39: Lossy type-casting ofrelationship.typehides potential data issuesCasting with
as schema.ResourceDependencyTypesuppresses compile-time guarantees: if the backend ever returns an unexpected string, the compiler will not warn.Prefer an explicit runtime guard:
function assertDependencyType( value: string, ): asserts value is schema.ResourceDependencyType { if (!['uses', 'depends_on', 'extends'].includes(value)) { throw new Error(`Unknown dependency type ${value}`); } } const parents = Object.values(relationships.parents).map((p) => { assertDependencyType(p.type); return { ...p, type: p.type }; });You keep exhaustive safety without sprinkling
ascasts.apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsx (2)
37-43: Initial node objects recreated on every render – wrap inuseMemo
getNodes([...])executes on every render even though its result is used only as the initial state foruseNodesState. The extra allocations are tiny but unnecessary and can complicate React DevTools diffing.-const [nodes, _, onNodesChange] = useNodesState<{ label: string }>( - getNodes([resource, ...parents.map((p) => p.target), ...children.map((c) => c.source)]), -); +const initialNodes = React.useMemo( + () => + getNodes([ + resource, + ...parents.map((p) => p.target), + ...children.map((c) => c.source), + ]), + [resource, parents, children], +); +const [nodes, _, onNodesChange] = useNodesState<ResourceNodeData>(initialNodes);(Where
ResourceNodeDatamirrors the full data shape.)
84-96:useNodesState<{ label: string }>under-types the node dataEach node’s
datacontainsid,kind,version, etc., not just alabel.
Under-typing loses autocompletion and may hide future refactor errors.-const [nodes, _, onNodesChange] = useNodesState<{ label: string }>(initialNodes); +type ResourceNodeData = schema.Resource & { label: string }; +const [nodes, _, onNodesChange] = useNodesState<ResourceNodeData>(initialNodes);Aligning generics with reality keeps the compiler working for you.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
📒 Files selected for processing (10)
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsx(1 hunks)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceNode.tsx(2 hunks)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceVisualizationDiagram.tsx(0 hunks)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/edges.ts(0 hunks)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/DeploymentNode.tsx(0 hunks)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/EnvironmentNode.tsx(0 hunks)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/ProviderNode.tsx(0 hunks)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/nodes.ts(0 hunks)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/page.tsx(1 hunks)packages/api/src/router/resources.ts(2 hunks)
💤 Files with no reviewable changes (6)
- apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/EnvironmentNode.tsx
- apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/ProviderNode.tsx
- apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/edges.ts
- apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceVisualizationDiagram.tsx
- apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/nodes.ts
- apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/DeploymentNode.tsx
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: **Note on Error Handling:** Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error...
**/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceNode.tsxapps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/page.tsxapps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsxpackages/api/src/router/resources.ts
🧬 Code Graph Analysis (1)
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/page.tsx (2)
packages/db/src/schema/resource.ts (1)
resource(59-87)apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsx (1)
RelationshipsDiagramProvider(119-131)
🪛 Biome (1.9.4)
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/page.tsx
[error] 45-45: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsx
[error] 127-127: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Typecheck
- GitHub Check: Lint
- GitHub Check: build (linux/amd64)
🔇 Additional comments (3)
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsx (1)
119-129: Static-analysis “children prop” warning is a false positive
childrenhere refers to resource dependencies, not React children, so the lint rule does not apply. No action required.🧰 Tools
🪛 Biome (1.9.4)
[error] 127-127: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
packages/api/src/router/resources.ts (2)
19-19: Added new import for retrieving resource children.This import now complements the existing
getResourceParentsfunction, enabling the simplified relationship visualization.
246-258: Well-executed simplification of the relationships endpoint.The query handler has been greatly simplified by:
- Directly fetching the resource using a single query
- Using the imported
getResourceParentsandgetResourceChildrenfunctions to fetch relationships- Returning a clean, straightforward structure with
resource,parents, andchildrenThis approach improves code readability and maintainability by removing the previous complex recursive implementation.
| <div | ||
| className={cn( | ||
| "flex w-[250px] cursor-pointer flex-col gap-2 rounded-md border bg-neutral-900/30 px-4 py-3", | ||
| data.isBaseNode && "bg-neutral-800/60", | ||
| data.id === resourceId && "bg-neutral-800/60", | ||
| )} | ||
| onClick={() => setResourceId(data.id)} | ||
| > |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Replace clickable <div> with a semantic <button> for a11y & keyboard support
The node is an interactive element but is rendered as a bare div.
Screen-reader users won’t recognise it as clickable and keyboard users cannot focus it.
-<div
- className={cn(
- "flex w-[250px] cursor-pointer flex-col gap-2 rounded-md border bg-neutral-900/30 px-4 py-3",
- data.id === resourceId && "bg-neutral-800/60",
- )}
- onClick={() => setResourceId(data.id)}
->
+<button
+ type="button"
+ className={cn(
+ "flex w-[250px] cursor-pointer flex-col gap-2 rounded-md border bg-neutral-900/30 px-4 py-3 text-left",
+ data.id === resourceId && "bg-neutral-800/60",
+ )}
+ onClick={() => setResourceId(data.id)}
+>
...
-</div>
+</button>Benefits: built-in focus handling, space/enter activation, and correct ARIA semantics without extra work.
📝 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.
| <div | |
| className={cn( | |
| "flex w-[250px] cursor-pointer flex-col gap-2 rounded-md border bg-neutral-900/30 px-4 py-3", | |
| data.isBaseNode && "bg-neutral-800/60", | |
| data.id === resourceId && "bg-neutral-800/60", | |
| )} | |
| onClick={() => setResourceId(data.id)} | |
| > | |
| <button | |
| type="button" | |
| className={cn( | |
| "flex w-[250px] cursor-pointer flex-col gap-2 rounded-md border bg-neutral-900/30 px-4 py-3 text-left", | |
| data.id === resourceId && "bg-neutral-800/60", | |
| )} | |
| onClick={() => setResourceId(data.id)} | |
| > | |
| {/* existing children/content */} | |
| </button> |
🤖 Prompt for AI Agents
In
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceNode.tsx
around lines 25 to 31, replace the clickable <div> element with a semantic
<button> element to improve accessibility. This change ensures the element is
focusable and operable via keyboard, and provides correct ARIA semantics
automatically. Update the className and onClick handler to the <button> while
removing any non-button attributes that are invalid for <button>.
| type PageProps = { | ||
| params: Promise<{ workspaceSlug: string; resourceId: string }>; | ||
| }; |
There was a problem hiding this comment.
params should not be a Promise – remove the unnecessary await
Next.js pages receive props synchronously:
type PageProps = { params: { workspaceSlug: string; resourceId: string } };Declaring it as Promise<...> forces every access to be await-ed, adds cognitive load, and can mis-lead readers about the runtime behaviour.
-type PageProps = {
- params: Promise<{ workspaceSlug: string; resourceId: string }>;
-};
+type PageProps = {
+ params: { workspaceSlug: string; resourceId: string };
+};Remove the corresponding await usages (await props.params) in both generateMetadata and RelationshipsPage.
This keeps types honest and eliminates redundant micro-tasks.
🤖 Prompt for AI Agents
In
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/page.tsx
around lines 8 to 10, the type of `params` is incorrectly declared as a Promise,
which is unnecessary because Next.js pages receive props synchronously. Change
the type of `params` to be a plain object without Promise, and remove all
`await` usages on `props.params` in both `generateMetadata` and
`RelationshipsPage` functions to keep the types accurate and avoid redundant
asynchronous handling.
Summary by CodeRabbit
New Features
Refactor
Bug Fixes