Skip to content

feat: visualize relationships#568

Merged
adityachoudhari26 merged 1 commit intomainfrom
visualize-relationships
May 16, 2025
Merged

feat: visualize relationships#568
adityachoudhari26 merged 1 commit intomainfrom
visualize-relationships

Conversation

@adityachoudhari26
Copy link
Copy Markdown
Member

@adityachoudhari26 adityachoudhari26 commented May 16, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a new interactive diagram to visualize resource relationships, showing parent and child dependencies with improved styling and layout.
  • Refactor

    • Simplified the underlying data fetching for resource relationships, resulting in faster and more direct loading of relationship diagrams.
    • Updated the visualization page to use the new diagram and metadata generation approach.
  • Bug Fixes

    • Improved accuracy and consistency in highlighting the main resource node within the diagram.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented May 16, 2025

Walkthrough

This 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 RelationshipsDiagram and provider introduced, and the page logic updated to match the new API and component structure.

Changes

File(s) Change Summary
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsx Added new React components RelationshipsDiagram and RelationshipsDiagramProvider to visualize resource relationships using React Flow. Components accept resource, parent, and child relationship data, generate nodes and edges, and manage layout and interaction.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceNode.tsx Updated ResourceNode to determine base node styling by matching the node's ID with the current route parameter (resourceId), removing the isBaseNode prop.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceVisualizationDiagram.tsx Deleted. Previously provided ResourceVisualizationDiagram and provider, handling data fetching, error/loading states, and rendering the resource relationship graph.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/edges.ts Deleted. Contained logic for generating graph edges between resources, environments, deployments, and providers for visualization.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/DeploymentNode.tsx Deleted. Defined the DeploymentNode React component for deployment visualization with status indicators and handles.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/EnvironmentNode.tsx Deleted. Defined the EnvironmentNode React component for environment visualization with icon, label, and handles.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/ProviderNode.tsx Deleted. Defined the ProviderNode React component (and internal helpers) for provider visualization with conditional icons and handles.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/nodes/nodes.ts Deleted. Provided node type definitions, mapping, and functions for generating nodes for resources, providers, environments, and deployments from relationship data.
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/page.tsx Refactored: Replaced the old page component with RelationshipsPage using the new RelationshipsDiagramProvider. Updated data fetching to match the new API structure. Added a generateMetadata function for dynamic page metadata. Updated props and imports.
packages/api/src/router/resources.ts Removed recursive helper functions for fetching and enriching resource nodes and relationships. Simplified the relationships query to directly return the resource and its parent/child relationships using existing queries.

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
Loading

Poem

In the garden of code, a diagram grew,
Old roots were trimmed, and new edges flew.
Parents and children now dance in the flow,
Simpler and lighter, relationships show.
The rabbit hops by, with a smile and a cheer—
"Your diagrams are clearer, the path is sincere!"
🐇✨

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit 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.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit 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 Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (4)
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/ResourceNode.tsx (1)

21-22: Verify useParams generic – type definition may not exist in all Next.js versions

useParams<{ resourceId: string }>() compiles only on the very latest next/navigation typings.
If your project still targets ≤ 13.4, the generic overload is not available and this will silently fall back to any, 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 of relationship.type hides potential data issues

Casting with as schema.ResourceDependencyType suppresses 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 as casts.

apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsx (2)

37-43: Initial node objects recreated on every render – wrap in useMemo

getNodes([...]) executes on every render even though its result is used only as the initial state for useNodesState. 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 ResourceNodeData mirrors the full data shape.)


84-96: useNodesState<{ label: string }> under-types the node data

Each node’s data contains id, kind, version, etc., not just a label.
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

📥 Commits

Reviewing files that changed from the base of the PR and between a1f90c5 and bc43b97.

📒 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.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/page.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/visualize/RelationshipsDiagram.tsx
  • packages/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

children here 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 getResourceParents function, enabling the simplified relationship visualization.


246-258: Well-executed simplification of the relationships endpoint.

The query handler has been greatly simplified by:

  1. Directly fetching the resource using a single query
  2. Using the imported getResourceParents and getResourceChildren functions to fetch relationships
  3. Returning a clean, straightforward structure with resource, parents, and children

This approach improves code readability and maintainability by removing the previous complex recursive implementation.

Comment on lines 25 to 31
<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)}
>
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.

🛠️ 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.

Suggested change
<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>.

Comment on lines +8 to +10
type PageProps = {
params: Promise<{ workspaceSlug: string; resourceId: string }>;
};
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.

⚠️ Potential issue

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.

@adityachoudhari26 adityachoudhari26 merged commit 360e2c8 into main May 16, 2025
5 of 6 checks passed
@adityachoudhari26 adityachoudhari26 deleted the visualize-relationships branch May 16, 2025 04:15
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