refactor(api): split computation graph detail formatting

This commit is contained in:
2026-03-31 22:19:09 +02:00
parent a539e748a5
commit 59690b86ac
2 changed files with 117 additions and 112 deletions
@@ -0,0 +1,111 @@
export type GraphNodeLike = {
id: string;
domain: string;
};
export type GraphLinkLike = {
source: string;
target: string;
};
export type ResourceGraphSnapshot = {
nodes: GraphNodeLike[];
links: GraphLinkLike[];
meta: {
resourceEid: string;
resourceName: string;
[key: string]: unknown;
};
};
export type ProjectGraphSnapshot = {
nodes: GraphNodeLike[];
links: GraphLinkLike[];
meta: {
projectCode: string;
projectName: string;
[key: string]: unknown;
};
};
function filterGraphData<
TNode extends GraphNodeLike,
TLink extends GraphLinkLike,
>(input: {
nodes: TNode[];
links: TLink[];
domain?: string;
includeLinks?: boolean;
}) {
const requestedDomain = input.domain?.trim().toUpperCase();
const nodes = requestedDomain
? input.nodes.filter((node) => node.domain === requestedDomain)
: input.nodes;
const selectedNodeIds = new Set(nodes.map((node) => node.id));
const links = input.includeLinks
? input.links.filter((link) => selectedNodeIds.has(link.source) && selectedNodeIds.has(link.target))
: [];
return {
requestedDomain: requestedDomain ?? null,
includedLinks: input.includeLinks ?? false,
selectedNodeCount: nodes.length,
selectedLinkCount: links.length,
nodes,
...(input.includeLinks ? { links } : {}),
};
}
function getAvailableDomains<TNode extends { domain: string }>(nodes: TNode[]) {
return [...new Set(nodes.map((node) => node.domain))];
}
export function formatResourceGraphDetail(input: {
resourceId: string;
graph: ResourceGraphSnapshot;
domain?: string;
includeLinks?: boolean;
}) {
return {
resource: {
id: input.resourceId,
eid: input.graph.meta.resourceEid,
displayName: input.graph.meta.resourceName,
},
availableDomains: getAvailableDomains(input.graph.nodes),
totalNodeCount: input.graph.nodes.length,
totalLinkCount: input.graph.links.length,
...filterGraphData({
nodes: input.graph.nodes,
links: input.graph.links,
...(input.domain ? { domain: input.domain } : {}),
...(input.includeLinks !== undefined ? { includeLinks: input.includeLinks } : {}),
}),
meta: input.graph.meta,
};
}
export function formatProjectGraphDetail(input: {
projectId: string;
graph: ProjectGraphSnapshot;
domain?: string;
includeLinks?: boolean;
}) {
return {
project: {
id: input.projectId,
shortCode: input.graph.meta.projectCode,
name: input.graph.meta.projectName,
},
availableDomains: getAvailableDomains(input.graph.nodes),
totalNodeCount: input.graph.nodes.length,
totalLinkCount: input.graph.links.length,
...filterGraphData({
nodes: input.graph.nodes,
links: input.graph.links,
...(input.domain ? { domain: input.domain } : {}),
...(input.includeLinks !== undefined ? { includeLinks: input.includeLinks } : {}),
}),
meta: input.graph.meta,
};
}
@@ -1,118 +1,12 @@
import { z } from "zod";
import {
formatProjectGraphDetail,
formatResourceGraphDetail,
type ProjectGraphSnapshot,
type ResourceGraphSnapshot,
} from "./computation-graph-detail-support.js";
import { controllerProcedure } from "../trpc.js";
type GraphNodeLike = {
id: string;
domain: string;
};
type GraphLinkLike = {
source: string;
target: string;
};
type ResourceGraphSnapshot = {
nodes: GraphNodeLike[];
links: GraphLinkLike[];
meta: {
resourceEid: string;
resourceName: string;
[key: string]: unknown;
};
};
type ProjectGraphSnapshot = {
nodes: GraphNodeLike[];
links: GraphLinkLike[];
meta: {
projectCode: string;
projectName: string;
[key: string]: unknown;
};
};
function filterGraphData<
TNode extends GraphNodeLike,
TLink extends GraphLinkLike,
>(input: {
nodes: TNode[];
links: TLink[];
domain?: string;
includeLinks?: boolean;
}) {
const requestedDomain = input.domain?.trim().toUpperCase();
const nodes = requestedDomain
? input.nodes.filter((node) => node.domain === requestedDomain)
: input.nodes;
const selectedNodeIds = new Set(nodes.map((node) => node.id));
const links = input.includeLinks
? input.links.filter((link) => selectedNodeIds.has(link.source) && selectedNodeIds.has(link.target))
: [];
return {
requestedDomain: requestedDomain ?? null,
includedLinks: input.includeLinks ?? false,
selectedNodeCount: nodes.length,
selectedLinkCount: links.length,
nodes,
...(input.includeLinks ? { links } : {}),
};
}
function getAvailableDomains<TNode extends { domain: string }>(nodes: TNode[]) {
return [...new Set(nodes.map((node) => node.domain))];
}
function formatResourceGraphDetail(input: {
resourceId: string;
graph: ResourceGraphSnapshot;
domain?: string;
includeLinks?: boolean;
}) {
return {
resource: {
id: input.resourceId,
eid: input.graph.meta.resourceEid,
displayName: input.graph.meta.resourceName,
},
availableDomains: getAvailableDomains(input.graph.nodes),
totalNodeCount: input.graph.nodes.length,
totalLinkCount: input.graph.links.length,
...filterGraphData({
nodes: input.graph.nodes,
links: input.graph.links,
...(input.domain ? { domain: input.domain } : {}),
...(input.includeLinks !== undefined ? { includeLinks: input.includeLinks } : {}),
}),
meta: input.graph.meta,
};
}
function formatProjectGraphDetail(input: {
projectId: string;
graph: ProjectGraphSnapshot;
domain?: string;
includeLinks?: boolean;
}) {
return {
project: {
id: input.projectId,
shortCode: input.graph.meta.projectCode,
name: input.graph.meta.projectName,
},
availableDomains: getAvailableDomains(input.graph.nodes),
totalNodeCount: input.graph.nodes.length,
totalLinkCount: input.graph.links.length,
...filterGraphData({
nodes: input.graph.nodes,
links: input.graph.links,
...(input.domain ? { domain: input.domain } : {}),
...(input.includeLinks !== undefined ? { includeLinks: input.includeLinks } : {}),
}),
meta: input.graph.meta,
};
}
export function createComputationGraphDetailProcedures(input: {
resourceGraphInputSchema: z.AnyZodObject;
projectGraphInputSchema: z.AnyZodObject;