Skip to content

Commit

Permalink
fix: Resource associations visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
adityachoudhari26 committed Dec 2, 2024
1 parent 21e76f9 commit 4c1156b
Show file tree
Hide file tree
Showing 14 changed files with 4,583 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ const createEdgesFromEnvironmentToDeployments = (
}));

const createEdgesFromDeploymentsToResources = (relationships: Relationships) =>
relationships.map((resource) => {
relationships.nodes.map((resource) => {
const { parent } = resource;
if (parent == null) return null;

const allReleaseJobTriggers = relationships
const allReleaseJobTriggers = relationships.nodes
.flatMap((r) => r.workspace.systems)
.flatMap((s) => s.environments)
.flatMap((e) => e.latestActiveReleases)
Expand All @@ -93,26 +93,48 @@ const createEdgesFromDeploymentsToResources = (relationships: Relationships) =>
});

export const getEdges = (relationships: Relationships) => {
const resourceToEnvEdges = relationships.flatMap((r) =>
const resourceToEnvEdges = relationships.nodes.flatMap((r) =>
createEdgesFromResourceToEnvironments(
r,
r.workspace.systems.flatMap((s) => s.environments),
),
);
const environmentToDeploymentEdges = relationships.flatMap((r) =>
const environmentToDeploymentEdges = relationships.nodes.flatMap((r) =>
r.workspace.systems.flatMap((s) =>
createEdgesFromEnvironmentToDeployments(s.environments, s.deployments),
),
);
const providerEdges = relationships.flatMap((r) =>
const providerEdges = relationships.nodes.flatMap((r) =>
r.provider != null ? [createEdgeFromProviderToResource(r.provider, r)] : [],
);
const deploymentEdges = createEdgesFromDeploymentsToResources(relationships);

const { resource } = relationships;

const fromEdges = relationships.associations.from.map((r) => ({
id: `${r.resource.id}-${resource.id}`,
source: r.resource.id,
target: resource.id,
style: { stroke: colors.neutral[800] },
markerEnd,
label: r.type,
}));

const toEdges = relationships.associations.to.map((r) => ({
id: `${resource.id}-${r.resource.id}`,
source: resource.id,
target: r.resource.id,
style: { stroke: colors.neutral[800] },
markerEnd,
label: r.type,
}));

return [
...resourceToEnvEdges,
...environmentToDeploymentEdges,
...providerEdges,
...deploymentEdges,
...fromEdges,
...toEdges,
].filter(isPresent);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Handle, Position } from "reactflow";
import { useEnvironmentDrawer } from "~/app/[workspaceSlug]/(app)/_components/environment-drawer/EnvironmentDrawer";

type Environment = NonNullable<
RouterOutputs["resource"]["relationships"][number]
>["workspace"]["systems"][number]["environments"][number];
RouterOutputs["resource"]["relationships"]
>["nodes"][number]["workspace"]["systems"][number]["environments"][number];

type EnvironmentNodeProps = NodeProps<{
label: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ export const nodeTypes: NodeTypes = {
};

const getResourceNodes = (relationships: Relationships) =>
relationships.map((r) => ({
relationships.nodes.map((r) => ({
id: r.id,
type: NodeType.Resource,
data: { ...r, label: r.identifier },
position: { x: 0, y: 0 },
}));

const getProviderNodes = (relationships: Relationships) =>
relationships
relationships.nodes
.map((r) =>
r.provider != null
? {
Expand All @@ -46,7 +46,7 @@ const getProviderNodes = (relationships: Relationships) =>
.filter(isPresent);

const getEnvironmentNodes = (relationships: Relationships) =>
relationships
relationships.nodes
.flatMap((r) => r.workspace.systems)
.flatMap((s) => s.environments.map((e) => ({ s, e })))
.map(({ s, e }) => ({
Expand All @@ -57,7 +57,7 @@ const getEnvironmentNodes = (relationships: Relationships) =>
}));

const getDeploymentNodes = (relationships: Relationships) =>
relationships.flatMap((r) =>
relationships.nodes.flatMap((r) =>
r.workspace.systems.flatMap((system) =>
system.environments.flatMap((environment) =>
system.deployments.map((deployment) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { notFound } from "next/navigation";

import { api } from "~/trpc/server";
import { ResourceVisualizationDiagramProvider } from "./ResourceVisualizationDiagram";

Expand All @@ -7,5 +9,6 @@ export default async function VisualizePage({
params: { targetId: string };
}) {
const relationships = await api.resource.relationships(targetId);
if (relationships == null) return notFound();
return <ResourceVisualizationDiagramProvider relationships={relationships} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ const getUndirectedGraph = (
const graph: Record<string, Set<string>> = {};

for (const relationship of relationships) {
if (!graph[relationship.sourceId]) graph[relationship.sourceId] = new Set();
if (!graph[relationship.targetId]) graph[relationship.targetId] = new Set();
graph[relationship.sourceId]!.add(relationship.targetId);
graph[relationship.targetId]!.add(relationship.sourceId);
if (!graph[relationship.fromIdentifier])
graph[relationship.fromIdentifier] = new Set();
if (!graph[relationship.toIdentifier])
graph[relationship.toIdentifier] = new Set();
graph[relationship.fromIdentifier]!.add(relationship.toIdentifier);
graph[relationship.toIdentifier]!.add(relationship.fromIdentifier);
}
return Object.fromEntries(
Object.entries(graph).map(([key, value]) => [key, Array.from(value)]),
Expand All @@ -101,23 +103,25 @@ const TargetDiagramDependencies: React.FC<DependenciesDiagramProps> = ({
}) => {
const [nodes, _, onNodesChange] = useNodesState(
targets.map((t) => ({
id: t.id,
id: t.identifier,
type: "target",
position: { x: 100, y: 100 },
data: {
...t,
targetId,
isOrphanNode: !relationships.some(
(r) => r.targetId === t.id || r.sourceId === t.id,
(r) =>
r.toIdentifier === t.identifier ||
r.fromIdentifier === t.identifier,
),
},
})),
);
const [edges, setEdges, onEdgesChange] = useEdgesState(
relationships.map((t) => ({
id: `${t.sourceId}-${t.targetId}`,
source: t.sourceId,
target: t.targetId,
id: `${t.fromIdentifier}-${t.toIdentifier}`,
source: t.fromIdentifier,
target: t.toIdentifier,
markerEnd: {
type: MarkerType.Arrow,
color: colors.neutral[700],
Expand All @@ -135,9 +139,9 @@ const TargetDiagramDependencies: React.FC<DependenciesDiagramProps> = ({
const resetEdges = () =>
setEdges(
relationships.map((t) => ({
id: `${t.sourceId}-${t.targetId}`,
source: t.sourceId,
target: t.targetId,
id: `${t.fromIdentifier}-${t.toIdentifier}`,
source: t.fromIdentifier,
target: t.toIdentifier,
markerEnd: {
type: MarkerType.Arrow,
color: colors.neutral[700],
Expand Down Expand Up @@ -174,14 +178,14 @@ const TargetDiagramDependencies: React.FC<DependenciesDiagramProps> = ({
const highlightedEdges = getHighlightedEdgesFromPath(nodesInPath);
const newEdges = relationships.map((t) => {
const isHighlighted = highlightedEdges.includes(
`${t.sourceId}-${t.targetId}`,
`${t.fromIdentifier}-${t.toIdentifier}`,
);
const color = isHighlighted ? colors.blue[500] : colors.neutral[700];

return {
id: `${t.sourceId}-${t.targetId}`,
source: t.sourceId,
target: t.targetId,
id: `${t.fromIdentifier}-${t.toIdentifier}`,
source: t.fromIdentifier,
target: t.toIdentifier,
markerEnd: { type: MarkerType.Arrow, color },
style: { stroke: color },
label: t.type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,25 @@ const TargetDiagram: React.FC<{
}> = ({ relationships, targets, targetId }) => {
const [nodes, _, onNodesChange] = useNodesState(
targets.map((t) => ({
id: t.id,
id: t.identifier,
type: "target",
position: { x: 100, y: 100 },
data: {
...t,
targetId,
isOrphanNode: !relationships.some(
(r) => r.targetId === t.id || r.sourceId === t.id,
(r) =>
r.toIdentifier === t.identifier ||
r.fromIdentifier === t.identifier,
),
},
})),
);
const [edges, __, onEdgesChange] = useEdgesState(
relationships.map((t) => ({
id: `${t.sourceId}-${t.targetId}`,
source: t.sourceId,
target: t.targetId,
id: `${t.fromIdentifier}-${t.toIdentifier}`,
source: t.fromIdentifier,
target: t.toIdentifier,
markerEnd: { type: MarkerType.Arrow, color: colors.neutral[700] },
style: { stroke: colors.neutral[700] },
label: t.type,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";

import { and, eq, isNull, takeFirstOrNull } from "@ctrlplane/db";
import { and, eq } from "@ctrlplane/db";
import * as SCHEMA from "@ctrlplane/db/schema";

import { authn } from "../../auth";
Expand All @@ -21,48 +21,29 @@ export const POST = request()
try {
const { body, db } = ctx;

const fromResource = await db
.select()
.from(SCHEMA.resource)
.where(
and(
eq(SCHEMA.resource.identifier, body.fromIdentifier),
eq(SCHEMA.resource.workspaceId, body.workspaceId),
isNull(SCHEMA.resource.deletedAt),
),
)
.then(takeFirstOrNull);
if (!fromResource)
return Response.json(
{ error: `${body.fromIdentifier} not found` },
{ status: 404 },
);
const inWorkspace = eq(SCHEMA.resource.workspaceId, body.workspaceId);
const fromResource = await db.query.resource.findFirst({
where: and(
inWorkspace,
eq(SCHEMA.resource.identifier, body.fromIdentifier),
),
});

const toResource = await db
.select()
.from(SCHEMA.resource)
.where(
and(
eq(SCHEMA.resource.identifier, body.toIdentifier),
eq(SCHEMA.resource.workspaceId, body.workspaceId),
isNull(SCHEMA.resource.deletedAt),
),
)
.then(takeFirstOrNull);
if (!toResource)
return Response.json(
{ error: `${body.toIdentifier} not found` },
{ status: 404 },
);
const toResource = await db.query.resource.findFirst({
where: and(
inWorkspace,
eq(SCHEMA.resource.identifier, body.toIdentifier),
),
});

await db.insert(SCHEMA.resourceRelationship).values({
sourceId: fromResource.id,
targetId: toResource.id,
type: body.type,
const relationship = await db.insert(SCHEMA.resourceRelationship).values({
...body,
fromIdentifier: fromResource?.identifier ?? body.fromIdentifier,
toIdentifier: toResource?.identifier ?? body.toIdentifier,
});

return Response.json(
{ message: "Relationship created" },
{ message: "Relationship created", relationship },
{ status: 200 },
);
} catch (error) {
Expand Down
Loading

0 comments on commit 4c1156b

Please sign in to comment.