diff --git a/packages/api/src/router/computation-graph-detail-support.ts b/packages/api/src/router/computation-graph-detail-support.ts new file mode 100644 index 0000000..e668299 --- /dev/null +++ b/packages/api/src/router/computation-graph-detail-support.ts @@ -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(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, + }; +} diff --git a/packages/api/src/router/computation-graph-detail.ts b/packages/api/src/router/computation-graph-detail.ts index 937bf0a..48a6642 100644 --- a/packages/api/src/router/computation-graph-detail.ts +++ b/packages/api/src/router/computation-graph-detail.ts @@ -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(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;