@eventcatalog/core 2.58.2 → 2.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-PV4MP6U4.js → chunk-F5WMB6Q5.js} +1 -1
- package/dist/{chunk-ZBPULBAC.js → chunk-SCDNNFXH.js} +1 -1
- package/dist/{chunk-WK6GQM5P.js → chunk-T3QXQPTB.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +3 -3
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +67 -2
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +157 -41
- package/eventcatalog/src/components/Grids/ServiceGrid.tsx +78 -14
- package/eventcatalog/src/components/MDX/NodeGraph/Edges/MultilineEdgeLabel.tsx +52 -0
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +13 -0
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +4 -1
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Data.tsx +55 -16
- package/eventcatalog/src/components/SideBars/ContainerSideBar.astro +180 -0
- package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +41 -0
- package/eventcatalog/src/components/SideNav/ListViewSideBar/components/MessageList.tsx +1 -1
- package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +250 -59
- package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +3 -0
- package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +35 -1
- package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +2 -2
- package/eventcatalog/src/components/Tables/Table.tsx +22 -2
- package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +152 -0
- package/eventcatalog/src/components/Tables/columns/index.tsx +3 -0
- package/eventcatalog/src/content.config.ts +57 -1
- package/eventcatalog/src/layouts/DiscoverLayout.astro +11 -1
- package/eventcatalog/src/pages/architecture/architecture.astro +9 -1
- package/eventcatalog/src/pages/discover/[type]/_index.data.ts +1 -1
- package/eventcatalog/src/pages/discover/[type]/index.astro +11 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +11 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +50 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +2 -0
- package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +4 -1
- package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +19 -1
- package/eventcatalog/src/pages/docs/llm/llms.txt.ts +3 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +1 -1
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +80 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +52 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +9 -2
- package/eventcatalog/src/types/index.ts +20 -2
- package/eventcatalog/src/utils/collections/containers.ts +94 -0
- package/eventcatalog/src/utils/collections/icons.ts +3 -1
- package/eventcatalog/src/utils/collections/services.ts +15 -1
- package/eventcatalog/src/utils/collections/util.ts +4 -2
- package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +155 -0
- package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +188 -82
- package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getCollection } from 'astro:content';
|
|
2
|
+
import type { CollectionEntry } from 'astro:content';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { getVersionForCollectionItem, satisfies } from './util';
|
|
5
|
+
|
|
6
|
+
const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
|
|
7
|
+
|
|
8
|
+
export type Entity = CollectionEntry<'containers'> & {
|
|
9
|
+
catalog: {
|
|
10
|
+
path: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
type: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
getAllVersions?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// cache for build time
|
|
21
|
+
let cachedEntities: Record<string, Entity[]> = {
|
|
22
|
+
allVersions: [],
|
|
23
|
+
currentVersions: [],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const getContainers = async ({ getAllVersions = true }: Props = {}): Promise<Entity[]> => {
|
|
27
|
+
const cacheKey = getAllVersions ? 'allVersions' : 'currentVersions';
|
|
28
|
+
|
|
29
|
+
if (cachedEntities[cacheKey].length > 0) {
|
|
30
|
+
return cachedEntities[cacheKey];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const containers = await getCollection('containers', (container) => {
|
|
34
|
+
return (getAllVersions || !container.filePath?.includes('versioned')) && container.data.hidden !== true;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const services = await getCollection('services');
|
|
38
|
+
|
|
39
|
+
cachedEntities[cacheKey] = containers.map((container) => {
|
|
40
|
+
const { latestVersion, versions } = getVersionForCollectionItem(container, containers);
|
|
41
|
+
|
|
42
|
+
const servicesThatReferenceContainer = services.filter((service) => {
|
|
43
|
+
const references = [...(service.data.writesTo || []), ...(service.data.readsFrom || [])];
|
|
44
|
+
return references.some((item) => {
|
|
45
|
+
if (item.id != container.data.id) return false;
|
|
46
|
+
if (item.version == 'latest' || item.version == undefined) return container.data.version == latestVersion;
|
|
47
|
+
return satisfies(container.data.version, item.version);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const servicesThatWriteToContainer = services.filter((service) => {
|
|
52
|
+
return service.data?.writesTo?.some((item) => {
|
|
53
|
+
if (item.id != container.data.id) return false;
|
|
54
|
+
if (item.version == 'latest' || item.version == undefined) return container.data.version == latestVersion;
|
|
55
|
+
return satisfies(container.data.version, item.version);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const servicesThatReadFromContainer = services.filter((service) => {
|
|
60
|
+
return service.data?.readsFrom?.some((item) => {
|
|
61
|
+
if (item.id != container.data.id) return false;
|
|
62
|
+
if (item.version == 'latest' || item.version == undefined) return container.data.version == latestVersion;
|
|
63
|
+
return satisfies(container.data.version, item.version);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...container,
|
|
69
|
+
data: {
|
|
70
|
+
...container.data,
|
|
71
|
+
versions,
|
|
72
|
+
latestVersion,
|
|
73
|
+
services: servicesThatReferenceContainer,
|
|
74
|
+
servicesThatWriteToContainer,
|
|
75
|
+
servicesThatReadFromContainer,
|
|
76
|
+
},
|
|
77
|
+
catalog: {
|
|
78
|
+
path: path.join(container.collection, container.id.replace('/index.mdx', '')),
|
|
79
|
+
absoluteFilePath: path.join(PROJECT_DIR, container.collection, container.id.replace('/index.mdx', '/index.md')),
|
|
80
|
+
astroContentFilePath: path.join(process.cwd(), 'src', 'content', container.collection, container.id),
|
|
81
|
+
filePath: path.join(process.cwd(), 'src', 'catalog-files', container.collection, container.id.replace('/index.mdx', '')),
|
|
82
|
+
publicPath: path.join('/generated', container.collection, container.id.replace(`-${container.data.version}`, '')),
|
|
83
|
+
type: 'container',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// order them by the name of the event
|
|
89
|
+
cachedEntities[cacheKey].sort((a, b) => {
|
|
90
|
+
return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return cachedEntities[cacheKey];
|
|
94
|
+
};
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
VariableIcon,
|
|
12
12
|
MapIcon,
|
|
13
13
|
} from '@heroicons/react/24/outline';
|
|
14
|
-
import { BookText, Box } from 'lucide-react';
|
|
14
|
+
import { BookText, Box, DatabaseIcon } from 'lucide-react';
|
|
15
15
|
|
|
16
16
|
export const getIconForCollection = (collection: string) => {
|
|
17
17
|
switch (collection) {
|
|
@@ -41,6 +41,8 @@ export const getIconForCollection = (collection: string) => {
|
|
|
41
41
|
return MapIcon;
|
|
42
42
|
case 'entities':
|
|
43
43
|
return Box;
|
|
44
|
+
case 'containers':
|
|
45
|
+
return DatabaseIcon;
|
|
44
46
|
default:
|
|
45
47
|
return ServerIcon;
|
|
46
48
|
}
|
|
@@ -35,6 +35,7 @@ export const getServices = async ({ getAllVersions = true }: Props = {}): Promis
|
|
|
35
35
|
const commands = await getCollection('commands');
|
|
36
36
|
const queries = await getCollection('queries');
|
|
37
37
|
const entities = await getCollection('entities');
|
|
38
|
+
const containers = await getCollection('containers');
|
|
38
39
|
const allMessages = [...events, ...commands, ...queries];
|
|
39
40
|
|
|
40
41
|
// @ts-ignore // TODO: Fix this type
|
|
@@ -44,6 +45,8 @@ export const getServices = async ({ getAllVersions = true }: Props = {}): Promis
|
|
|
44
45
|
const sendsMessages = service.data.sends || [];
|
|
45
46
|
const receivesMessages = service.data.receives || [];
|
|
46
47
|
const serviceEntities = service.data.entities || [];
|
|
48
|
+
const serviceWritesTo = service.data.writesTo || [];
|
|
49
|
+
const serviceReadsFrom = service.data.readsFrom || [];
|
|
47
50
|
|
|
48
51
|
const sends = sendsMessages
|
|
49
52
|
.map((message: any) => getItemsFromCollectionByIdAndSemverOrLatest(allMessages, message.id, message.version))
|
|
@@ -60,10 +63,22 @@ export const getServices = async ({ getAllVersions = true }: Props = {}): Promis
|
|
|
60
63
|
.flat()
|
|
61
64
|
.filter((e: any) => e !== undefined);
|
|
62
65
|
|
|
66
|
+
const mappedWritesTo = serviceWritesTo
|
|
67
|
+
.map((container: any) => getItemsFromCollectionByIdAndSemverOrLatest(containers, container.id, container.version))
|
|
68
|
+
.flat()
|
|
69
|
+
.filter((e: any) => e !== undefined);
|
|
70
|
+
|
|
71
|
+
const mappedReadsFrom = serviceReadsFrom
|
|
72
|
+
.map((container: any) => getItemsFromCollectionByIdAndSemverOrLatest(containers, container.id, container.version))
|
|
73
|
+
.flat()
|
|
74
|
+
.filter((e: any) => e !== undefined);
|
|
75
|
+
|
|
63
76
|
return {
|
|
64
77
|
...service,
|
|
65
78
|
data: {
|
|
66
79
|
...service.data,
|
|
80
|
+
writesTo: mappedWritesTo,
|
|
81
|
+
readsFrom: mappedReadsFrom,
|
|
67
82
|
receives,
|
|
68
83
|
sends,
|
|
69
84
|
versions,
|
|
@@ -157,7 +172,6 @@ export const getSpecificationsForService = (service: CollectionEntry<CollectionT
|
|
|
157
172
|
filenameWithoutExtension: path.basename(spec.path, path.extname(spec.path)),
|
|
158
173
|
}));
|
|
159
174
|
};
|
|
160
|
-
|
|
161
175
|
// Get services for channel
|
|
162
176
|
export const getProducersAndConsumersForChannel = async (channel: CollectionEntry<'channels'>) => {
|
|
163
177
|
const messages = channel.data.messages ?? [];
|
|
@@ -87,8 +87,8 @@ export const getItemsFromCollectionByIdAndSemverOrLatest = <T extends { data: {
|
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
export const findMatchingNodes = (
|
|
90
|
-
nodesA: CollectionEntry<'events' | 'commands' | 'queries' | 'services'>[],
|
|
91
|
-
nodesB: CollectionEntry<'events' | 'commands' | 'queries' | 'services'>[]
|
|
90
|
+
nodesA: CollectionEntry<'events' | 'commands' | 'queries' | 'services' | 'containers'>[],
|
|
91
|
+
nodesB: CollectionEntry<'events' | 'commands' | 'queries' | 'services' | 'containers'>[]
|
|
92
92
|
) => {
|
|
93
93
|
// Track messages that are both sent and received
|
|
94
94
|
return nodesA.filter((nodeA) => {
|
|
@@ -108,6 +108,7 @@ export const resourceToCollectionMap = {
|
|
|
108
108
|
channel: 'channels',
|
|
109
109
|
user: 'users',
|
|
110
110
|
team: 'teams',
|
|
111
|
+
container: 'containers',
|
|
111
112
|
} as const;
|
|
112
113
|
|
|
113
114
|
export const collectionToResourceMap = {
|
|
@@ -120,6 +121,7 @@ export const collectionToResourceMap = {
|
|
|
120
121
|
channels: 'channel',
|
|
121
122
|
users: 'user',
|
|
122
123
|
teams: 'team',
|
|
124
|
+
containers: 'container',
|
|
123
125
|
} as const;
|
|
124
126
|
|
|
125
127
|
export const getDeprecatedDetails = (item: CollectionEntry<CollectionTypes>) => {
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { CollectionEntry } from 'astro:content';
|
|
2
|
+
import dagre from 'dagre';
|
|
3
|
+
import { calculatedNodes, createDagreGraph, createEdge, generatedIdForEdge, generateIdForNode } from './utils/utils';
|
|
4
|
+
import { MarkerType } from '@xyflow/react';
|
|
5
|
+
import { findMatchingNodes } from '@utils/collections/util';
|
|
6
|
+
import { getContainers } from '@utils/collections/containers';
|
|
7
|
+
|
|
8
|
+
type DagreGraph = any;
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
id: string;
|
|
12
|
+
version: string;
|
|
13
|
+
defaultFlow?: DagreGraph;
|
|
14
|
+
mode?: 'simple' | 'full';
|
|
15
|
+
channelRenderMode?: 'flat' | 'single';
|
|
16
|
+
collection?: CollectionEntry<'containers'>[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simple', channelRenderMode = 'flat' }: Props) => {
|
|
20
|
+
const containers = await getContainers();
|
|
21
|
+
|
|
22
|
+
const flow = defaultFlow || createDagreGraph({ ranksep: 300, nodesep: 50 });
|
|
23
|
+
const nodes = [] as any,
|
|
24
|
+
edges = [] as any;
|
|
25
|
+
|
|
26
|
+
const container = containers.find((container) => container.data.id === id && container.data.version === version);
|
|
27
|
+
|
|
28
|
+
// Nothing found...
|
|
29
|
+
if (!container) {
|
|
30
|
+
return {
|
|
31
|
+
nodes: [],
|
|
32
|
+
edges: [],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const servicesThatWriteToContainer = (container.data.servicesThatWriteToContainer as CollectionEntry<'services'>[]) || [];
|
|
37
|
+
const servicesThatReadFromContainer = (container.data.servicesThatReadFromContainer as CollectionEntry<'services'>[]) || [];
|
|
38
|
+
|
|
39
|
+
// Track nodes that are bth sent and received
|
|
40
|
+
const bothSentAndReceived = findMatchingNodes(servicesThatWriteToContainer, servicesThatReadFromContainer);
|
|
41
|
+
|
|
42
|
+
servicesThatWriteToContainer.forEach((service) => {
|
|
43
|
+
nodes.push({
|
|
44
|
+
id: generateIdForNode(service),
|
|
45
|
+
type: service?.collection,
|
|
46
|
+
sourcePosition: 'right',
|
|
47
|
+
targetPosition: 'left',
|
|
48
|
+
data: { mode, service: { ...service.data } },
|
|
49
|
+
position: { x: 250, y: 0 },
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!bothSentAndReceived.includes(service)) {
|
|
53
|
+
edges.push({
|
|
54
|
+
id: generatedIdForEdge(service, container),
|
|
55
|
+
source: generateIdForNode(service),
|
|
56
|
+
target: generateIdForNode(container),
|
|
57
|
+
label: 'writes to',
|
|
58
|
+
data: { service },
|
|
59
|
+
animated: false,
|
|
60
|
+
type: 'default',
|
|
61
|
+
style: {
|
|
62
|
+
strokeWidth: 1,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// The message itself
|
|
69
|
+
nodes.push({
|
|
70
|
+
id: generateIdForNode(container),
|
|
71
|
+
sourcePosition: 'right',
|
|
72
|
+
targetPosition: 'left',
|
|
73
|
+
data: {
|
|
74
|
+
mode,
|
|
75
|
+
data: {
|
|
76
|
+
...container.data,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
position: { x: 0, y: 0 },
|
|
80
|
+
type: 'data',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// // The messages the service sends
|
|
84
|
+
servicesThatReadFromContainer.forEach((service) => {
|
|
85
|
+
nodes.push({
|
|
86
|
+
id: generateIdForNode(service),
|
|
87
|
+
sourcePosition: 'left',
|
|
88
|
+
targetPosition: 'right',
|
|
89
|
+
data: { title: service?.data.id, mode, service: { ...service.data } },
|
|
90
|
+
position: { x: 0, y: 0 },
|
|
91
|
+
type: service?.collection,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (!bothSentAndReceived.includes(service)) {
|
|
95
|
+
edges.push(
|
|
96
|
+
createEdge({
|
|
97
|
+
id: generatedIdForEdge(service, container),
|
|
98
|
+
source: generateIdForNode(container),
|
|
99
|
+
target: generateIdForNode(service),
|
|
100
|
+
label: `reads from \n (${container.data.technology})`,
|
|
101
|
+
data: { service },
|
|
102
|
+
type: 'multiline',
|
|
103
|
+
// type: 'animatedData',
|
|
104
|
+
markerStart: {
|
|
105
|
+
type: MarkerType.ArrowClosed,
|
|
106
|
+
width: 40,
|
|
107
|
+
height: 40,
|
|
108
|
+
},
|
|
109
|
+
markerEnd: undefined,
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Handle messages that are both sent and received
|
|
116
|
+
bothSentAndReceived.forEach((_service) => {
|
|
117
|
+
if (container) {
|
|
118
|
+
edges.push(
|
|
119
|
+
createEdge({
|
|
120
|
+
id: generatedIdForEdge(container, _service) + '-both',
|
|
121
|
+
source: generateIdForNode(_service),
|
|
122
|
+
target: generateIdForNode(container),
|
|
123
|
+
label: `read and writes to \n (${container.data.technology})`,
|
|
124
|
+
type: 'multiline',
|
|
125
|
+
markerStart: {
|
|
126
|
+
type: MarkerType.ArrowClosed,
|
|
127
|
+
width: 40,
|
|
128
|
+
height: 40,
|
|
129
|
+
},
|
|
130
|
+
markerEnd: {
|
|
131
|
+
type: MarkerType.ArrowClosed,
|
|
132
|
+
width: 40,
|
|
133
|
+
height: 40,
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
nodes.forEach((node: any) => {
|
|
141
|
+
flow.setNode(node.id, { width: 150, height: 100 });
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
edges.forEach((edge: any) => {
|
|
145
|
+
flow.setEdge(edge.source, edge.target);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Render the diagram in memory getting hte X and Y
|
|
149
|
+
dagre.layout(flow);
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
nodes: calculatedNodes(flow, nodes),
|
|
153
|
+
edges,
|
|
154
|
+
};
|
|
155
|
+
};
|
|
@@ -21,6 +21,7 @@ interface Props {
|
|
|
21
21
|
mode?: 'simple' | 'full';
|
|
22
22
|
renderAllEdges?: boolean;
|
|
23
23
|
channelRenderMode?: 'single' | 'flat';
|
|
24
|
+
renderMessages?: boolean;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
const getSendsMessageByMessageType = (messageType: string) => {
|
|
@@ -55,9 +56,10 @@ export const getNodesAndEdges = async ({
|
|
|
55
56
|
mode = 'simple',
|
|
56
57
|
renderAllEdges = false,
|
|
57
58
|
channelRenderMode = 'flat',
|
|
59
|
+
renderMessages = true,
|
|
58
60
|
}: Props) => {
|
|
59
61
|
const flow = defaultFlow || createDagreGraph({ ranksep: 300, nodesep: 50 });
|
|
60
|
-
|
|
62
|
+
let nodes = [] as any,
|
|
61
63
|
edges = [] as any;
|
|
62
64
|
|
|
63
65
|
const services = await getCollection('services');
|
|
@@ -74,11 +76,14 @@ export const getNodesAndEdges = async ({
|
|
|
74
76
|
|
|
75
77
|
const receivesRaw = service?.data.receives || [];
|
|
76
78
|
const sendsRaw = service?.data.sends || [];
|
|
79
|
+
const writesToRaw = service?.data.writesTo || [];
|
|
80
|
+
const readsFromRaw = service?.data.readsFrom || [];
|
|
77
81
|
|
|
78
82
|
const events = await getCollection('events');
|
|
79
83
|
const commands = await getCollection('commands');
|
|
80
84
|
const queries = await getCollection('queries');
|
|
81
85
|
const channels = await getCollection('channels');
|
|
86
|
+
const containers = await getCollection('containers');
|
|
82
87
|
|
|
83
88
|
const messages = [...events, ...commands, ...queries];
|
|
84
89
|
|
|
@@ -92,51 +97,66 @@ export const getNodesAndEdges = async ({
|
|
|
92
97
|
.flat()
|
|
93
98
|
.filter((e) => e !== undefined);
|
|
94
99
|
|
|
100
|
+
const writesToHydrated = writesToRaw
|
|
101
|
+
.map((container) => getItemsFromCollectionByIdAndSemverOrLatest(containers, container.id, container.version))
|
|
102
|
+
.flat()
|
|
103
|
+
.filter((e) => e !== undefined);
|
|
104
|
+
|
|
105
|
+
const readsFromHydrated = readsFromRaw
|
|
106
|
+
.map((container) => getItemsFromCollectionByIdAndSemverOrLatest(containers, container.id, container.version))
|
|
107
|
+
.flat()
|
|
108
|
+
.filter((e) => e !== undefined);
|
|
109
|
+
|
|
95
110
|
const receives = (receivesHydrated as CollectionEntry<CollectionMessageTypes>[]) || [];
|
|
96
111
|
const sends = (sendsHydrated as CollectionEntry<CollectionMessageTypes>[]) || [];
|
|
112
|
+
const writesTo = (writesToHydrated as CollectionEntry<'containers'>[]) || [];
|
|
113
|
+
const readsFrom = (readsFromHydrated as CollectionEntry<'containers'>[]) || [];
|
|
97
114
|
|
|
98
115
|
// Track messages that are both sent and received
|
|
99
116
|
const bothSentAndReceived = findMatchingNodes(receives, sends);
|
|
117
|
+
const bothReadsAndWrites = findMatchingNodes(readsFrom, writesTo);
|
|
100
118
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// does the message have channels defined?
|
|
113
|
-
if (receive.data.channels) {
|
|
114
|
-
const { nodes: channelNodes, edges: channelEdges } = getChannelNodesAndEdges({
|
|
115
|
-
channels,
|
|
116
|
-
channelsToRender: receive.data.channels,
|
|
117
|
-
source: receive,
|
|
118
|
-
channelToTargetLabel: getReceivesMessageByMessageType(receive?.collection),
|
|
119
|
-
target: service,
|
|
120
|
-
mode,
|
|
121
|
-
currentNodes: nodes,
|
|
122
|
-
channelRenderMode,
|
|
119
|
+
if (renderMessages) {
|
|
120
|
+
// All the messages the service receives
|
|
121
|
+
receives.forEach((receive) => {
|
|
122
|
+
// Create the node for the message
|
|
123
|
+
nodes.push({
|
|
124
|
+
id: generateIdForNode(receive),
|
|
125
|
+
type: receive?.collection,
|
|
126
|
+
sourcePosition: 'right',
|
|
127
|
+
targetPosition: 'left',
|
|
128
|
+
data: { mode, message: { ...receive.data } },
|
|
123
129
|
});
|
|
124
130
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
131
|
+
// does the message have channels defined?
|
|
132
|
+
if (receive.data.channels) {
|
|
133
|
+
const { nodes: channelNodes, edges: channelEdges } = getChannelNodesAndEdges({
|
|
134
|
+
channels,
|
|
135
|
+
channelsToRender: receive.data.channels,
|
|
136
|
+
source: receive,
|
|
137
|
+
channelToTargetLabel: getReceivesMessageByMessageType(receive?.collection),
|
|
138
|
+
target: service,
|
|
139
|
+
mode,
|
|
140
|
+
currentNodes: nodes,
|
|
141
|
+
channelRenderMode,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
nodes.push(...channelNodes);
|
|
145
|
+
edges.push(...channelEdges);
|
|
146
|
+
} else {
|
|
147
|
+
// No channels, just link the message to the service
|
|
148
|
+
edges.push(
|
|
149
|
+
createEdge({
|
|
150
|
+
id: generatedIdForEdge(receive, service),
|
|
151
|
+
source: generateIdForNode(receive),
|
|
152
|
+
target: generateIdForNode(service),
|
|
153
|
+
label: getReceivesMessageByMessageType(receive?.collection),
|
|
154
|
+
data: { message: { ...receive.data } },
|
|
155
|
+
})
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
140
160
|
|
|
141
161
|
// The service itself
|
|
142
162
|
nodes.push({
|
|
@@ -148,66 +168,152 @@ export const getNodesAndEdges = async ({
|
|
|
148
168
|
type: service.collection,
|
|
149
169
|
});
|
|
150
170
|
|
|
151
|
-
//
|
|
152
|
-
|
|
171
|
+
// Any containers the service writes to
|
|
172
|
+
writesTo.forEach((writeTo) => {
|
|
153
173
|
nodes.push({
|
|
154
|
-
id: generateIdForNode(
|
|
174
|
+
id: generateIdForNode(writeTo),
|
|
155
175
|
sourcePosition: 'right',
|
|
156
176
|
targetPosition: 'left',
|
|
157
|
-
data: { mode,
|
|
158
|
-
type:
|
|
177
|
+
data: { mode, data: { ...writeTo.data } },
|
|
178
|
+
type: 'data',
|
|
159
179
|
});
|
|
160
180
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
channels,
|
|
164
|
-
channelsToRender: send.data.channels,
|
|
165
|
-
source: service,
|
|
166
|
-
target: send,
|
|
167
|
-
mode,
|
|
168
|
-
sourceToChannelLabel: `${getSendsMessageByMessageType(send?.collection)}`,
|
|
169
|
-
channelToTargetLabel: getSendsMessageByMessageType(send?.collection),
|
|
170
|
-
currentNodes: nodes,
|
|
171
|
-
channelRenderMode,
|
|
172
|
-
});
|
|
173
|
-
nodes.push(...channelNodes);
|
|
174
|
-
edges.push(...channelEdges);
|
|
175
|
-
} else {
|
|
176
|
-
// No channels, just link the message to the service
|
|
181
|
+
// If its not in the reads/and writes to, we need to add the edge
|
|
182
|
+
if (!bothReadsAndWrites.includes(writeTo)) {
|
|
177
183
|
edges.push(
|
|
178
184
|
createEdge({
|
|
179
|
-
id: generatedIdForEdge(service,
|
|
185
|
+
id: generatedIdForEdge(service, writeTo),
|
|
180
186
|
source: generateIdForNode(service),
|
|
181
|
-
target: generateIdForNode(
|
|
182
|
-
label:
|
|
183
|
-
|
|
187
|
+
target: generateIdForNode(writeTo),
|
|
188
|
+
label: `writes to \n (${writeTo.data?.technology})`,
|
|
189
|
+
type: 'multiline',
|
|
190
|
+
markerEnd: {
|
|
191
|
+
type: MarkerType.ArrowClosed,
|
|
192
|
+
color: '#666',
|
|
193
|
+
width: 40,
|
|
194
|
+
height: 40,
|
|
195
|
+
},
|
|
184
196
|
})
|
|
185
197
|
);
|
|
186
198
|
}
|
|
187
199
|
});
|
|
188
200
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
201
|
+
// Any containers the service reads from
|
|
202
|
+
readsFrom.forEach((readFrom) => {
|
|
203
|
+
nodes.push({
|
|
204
|
+
id: generateIdForNode(readFrom),
|
|
205
|
+
sourcePosition: 'right',
|
|
206
|
+
targetPosition: 'left',
|
|
207
|
+
data: { mode, data: { ...readFrom.data } },
|
|
208
|
+
type: 'data',
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// If its not in the reads/and writes to, we need to add the edge
|
|
212
|
+
if (!bothReadsAndWrites.includes(readFrom)) {
|
|
213
|
+
edges.push(
|
|
214
|
+
createEdge({
|
|
215
|
+
id: generatedIdForEdge(service, readFrom),
|
|
216
|
+
source: generateIdForNode(readFrom),
|
|
217
|
+
target: generateIdForNode(service),
|
|
218
|
+
label: `reads from \n (${readFrom.data?.technology})`,
|
|
219
|
+
type: 'multiline',
|
|
220
|
+
markerStart: {
|
|
221
|
+
type: MarkerType.ArrowClosed,
|
|
222
|
+
color: '#666',
|
|
223
|
+
width: 40,
|
|
224
|
+
height: 40,
|
|
225
|
+
},
|
|
226
|
+
markerEnd: undefined,
|
|
227
|
+
})
|
|
228
|
+
);
|
|
208
229
|
}
|
|
209
230
|
});
|
|
210
231
|
|
|
232
|
+
if (renderMessages) {
|
|
233
|
+
// The messages the service sends
|
|
234
|
+
sends.forEach((send, index) => {
|
|
235
|
+
nodes.push({
|
|
236
|
+
id: generateIdForNode(send),
|
|
237
|
+
sourcePosition: 'right',
|
|
238
|
+
targetPosition: 'left',
|
|
239
|
+
data: { mode, message: { ...send.data } },
|
|
240
|
+
type: send?.collection,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
if (send.data.channels) {
|
|
244
|
+
const { nodes: channelNodes, edges: channelEdges } = getChannelNodesAndEdges({
|
|
245
|
+
channels,
|
|
246
|
+
channelsToRender: send.data.channels,
|
|
247
|
+
source: service,
|
|
248
|
+
target: send,
|
|
249
|
+
mode,
|
|
250
|
+
sourceToChannelLabel: `${getSendsMessageByMessageType(send?.collection)}`,
|
|
251
|
+
channelToTargetLabel: getSendsMessageByMessageType(send?.collection),
|
|
252
|
+
currentNodes: nodes,
|
|
253
|
+
channelRenderMode,
|
|
254
|
+
});
|
|
255
|
+
nodes.push(...channelNodes);
|
|
256
|
+
edges.push(...channelEdges);
|
|
257
|
+
} else {
|
|
258
|
+
// No channels, just link the message to the service
|
|
259
|
+
edges.push(
|
|
260
|
+
createEdge({
|
|
261
|
+
id: generatedIdForEdge(service, send),
|
|
262
|
+
source: generateIdForNode(service),
|
|
263
|
+
target: generateIdForNode(send),
|
|
264
|
+
label: getSendsMessageByMessageType(send?.collection),
|
|
265
|
+
data: { message: { ...send.data } },
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Handle messages that are both sent and received
|
|
272
|
+
bothSentAndReceived.forEach((message) => {
|
|
273
|
+
if (message) {
|
|
274
|
+
edges.push({
|
|
275
|
+
id: generatedIdForEdge(service, message) + '-both',
|
|
276
|
+
source: generateIdForNode(service),
|
|
277
|
+
target: generateIdForNode(message),
|
|
278
|
+
label: `${getSendsMessageByMessageType(message?.collection)} & ${getReceivesMessageByMessageType(message?.collection)}`,
|
|
279
|
+
animated: false,
|
|
280
|
+
data: { message: { ...message.data } },
|
|
281
|
+
markerEnd: {
|
|
282
|
+
type: MarkerType.ArrowClosed,
|
|
283
|
+
width: 40,
|
|
284
|
+
height: 40,
|
|
285
|
+
},
|
|
286
|
+
style: {
|
|
287
|
+
strokeWidth: 1,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
bothReadsAndWrites.forEach((container) => {
|
|
295
|
+
edges.push({
|
|
296
|
+
id: generatedIdForEdge(service, container) + '-both',
|
|
297
|
+
source: generateIdForNode(service),
|
|
298
|
+
target: generateIdForNode(container),
|
|
299
|
+
type: 'multiline',
|
|
300
|
+
// @ts-ignore
|
|
301
|
+
label: `reads from \n and writes to \n (${container.data.technology})`,
|
|
302
|
+
markerStart: {
|
|
303
|
+
type: MarkerType.ArrowClosed,
|
|
304
|
+
color: '#666',
|
|
305
|
+
width: 40,
|
|
306
|
+
height: 40,
|
|
307
|
+
},
|
|
308
|
+
markerEnd: {
|
|
309
|
+
type: MarkerType.ArrowClosed,
|
|
310
|
+
color: '#666',
|
|
311
|
+
width: 40,
|
|
312
|
+
height: 40,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
211
317
|
nodes.forEach((node: any) => {
|
|
212
318
|
flow.setNode(node.id, { width: 150, height: 100 });
|
|
213
319
|
});
|