@eventcatalog/core 3.7.1 → 3.8.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-44EDP7IH.js → chunk-4EJDLNIX.js} +1 -1
- package/dist/{chunk-MIHGWXCX.js → chunk-EG36OTR7.js} +1 -1
- package/dist/{chunk-BVJJ3COQ.js → chunk-GITARDPK.js} +1 -1
- package/dist/{chunk-PV5ER42D.js → chunk-IEEU454Z.js} +1 -1
- package/dist/{chunk-B6NBNZGS.js → chunk-ZIG6J4R2.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 +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +13 -1
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +109 -6
- package/eventcatalog/src/components/Grids/utils.tsx +10 -1
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +2 -0
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +4 -0
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/DataProduct.tsx +132 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +29 -2
- package/eventcatalog/src/components/SchemaExplorer/types.ts +5 -1
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +3 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +1 -0
- package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +23 -1
- package/eventcatalog/src/components/Tables/Discover/columns.tsx +62 -0
- package/eventcatalog/src/content.config.ts +34 -0
- package/eventcatalog/src/enterprise/ai/chat-api.ts +26 -0
- package/eventcatalog/src/enterprise/custom-documentation/utils/custom-docs.ts +1 -1
- package/eventcatalog/src/enterprise/tools/catalog-tools.ts +169 -2
- package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -1
- package/eventcatalog/src/pages/discover/[type]/index.astro +57 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +5 -1
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +27 -3
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +74 -25
- package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +107 -1
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +10 -1
- package/eventcatalog/src/stores/sidebar-store/builders/container.ts +23 -16
- package/eventcatalog/src/stores/sidebar-store/builders/data-product.ts +130 -0
- package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +51 -0
- package/eventcatalog/src/stores/sidebar-store/state.ts +68 -13
- package/eventcatalog/src/styles/theme.css +4 -0
- package/eventcatalog/src/styles/themes/forest.css +4 -0
- package/eventcatalog/src/styles/themes/ocean.css +4 -0
- package/eventcatalog/src/styles/themes/sapphire.css +4 -0
- package/eventcatalog/src/styles/themes/sunset.css +4 -0
- package/eventcatalog/src/types/index.ts +4 -2
- package/eventcatalog/src/utils/collections/commands.ts +11 -29
- package/eventcatalog/src/utils/collections/containers.ts +25 -1
- package/eventcatalog/src/utils/collections/data-products.ts +85 -0
- package/eventcatalog/src/utils/collections/domains.ts +33 -11
- package/eventcatalog/src/utils/collections/events.ts +11 -29
- package/eventcatalog/src/utils/collections/icons.ts +5 -0
- package/eventcatalog/src/utils/collections/messages.ts +68 -0
- package/eventcatalog/src/utils/collections/queries.ts +11 -29
- package/eventcatalog/src/utils/collections/services.ts +2 -26
- package/eventcatalog/src/utils/collections/util.ts +75 -2
- package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +91 -3
- package/eventcatalog/src/utils/node-graphs/data-products-node-graph.ts +225 -0
- package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +28 -2
- package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +74 -20
- package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
- package/package.json +2 -2
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
|
+
import dagre from 'dagre';
|
|
3
|
+
import {
|
|
4
|
+
createDagreGraph,
|
|
5
|
+
generateIdForNode,
|
|
6
|
+
generatedIdForEdge,
|
|
7
|
+
calculatedNodes,
|
|
8
|
+
createEdge,
|
|
9
|
+
getColorFromString,
|
|
10
|
+
} from '@utils/node-graphs/utils/utils';
|
|
11
|
+
|
|
12
|
+
import { findInMap, createVersionedMap, mergeMaps, collectionToResourceMap } from '@utils/collections/util';
|
|
13
|
+
import { MarkerType } from '@xyflow/react';
|
|
14
|
+
import { getMessages, isCollectionAMessage } from '@utils/collections/messages';
|
|
15
|
+
import { getProducersOfMessage } from '@utils/collections/services';
|
|
16
|
+
import type { CollectionMessageTypes } from '@types';
|
|
17
|
+
import { getNodesAndEdgesForProducedMessage } from './message-node-graph';
|
|
18
|
+
|
|
19
|
+
type DagreGraph = any;
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
id: string;
|
|
23
|
+
version: string;
|
|
24
|
+
defaultFlow?: DagreGraph;
|
|
25
|
+
mode?: 'simple' | 'full';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const getNodePropertyFromCollectionType = (type: string) => {
|
|
29
|
+
if (isCollectionAMessage(type)) return 'message';
|
|
30
|
+
if (type === 'containers') return 'data';
|
|
31
|
+
return collectionToResourceMap[type as keyof typeof collectionToResourceMap];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simple' }: Props) => {
|
|
35
|
+
const flow = defaultFlow || createDagreGraph({ ranksep: 300, nodesep: 50 });
|
|
36
|
+
let nodes = [] as any,
|
|
37
|
+
edges = [] as any;
|
|
38
|
+
|
|
39
|
+
const [dataProducts, containers, services, events, queries, commands, channels] = await Promise.all([
|
|
40
|
+
getCollection('data-products'),
|
|
41
|
+
getCollection('containers'),
|
|
42
|
+
getCollection('services'),
|
|
43
|
+
getCollection('events'),
|
|
44
|
+
getCollection('queries'),
|
|
45
|
+
getCollection('commands'),
|
|
46
|
+
getCollection('channels'),
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const dataProduct = dataProducts.find((dp) => dp.data.id === id && dp.data.version === version);
|
|
50
|
+
|
|
51
|
+
// Nothing found...
|
|
52
|
+
if (!dataProduct) {
|
|
53
|
+
return {
|
|
54
|
+
nodes: [],
|
|
55
|
+
edges: [],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Build maps for O(1) lookups
|
|
60
|
+
const messages = [...events, ...commands, ...queries];
|
|
61
|
+
|
|
62
|
+
const messageMap = createVersionedMap(messages);
|
|
63
|
+
const containerMap = createVersionedMap(containers);
|
|
64
|
+
const serviceMap = createVersionedMap(services);
|
|
65
|
+
const channelMap = createVersionedMap(channels);
|
|
66
|
+
|
|
67
|
+
const inputsRaw = dataProduct?.data.inputs || [];
|
|
68
|
+
const outputsRaw = dataProduct?.data.outputs || [];
|
|
69
|
+
|
|
70
|
+
const resourceMap = mergeMaps<
|
|
71
|
+
| CollectionEntry<CollectionMessageTypes>
|
|
72
|
+
| CollectionEntry<'services'>
|
|
73
|
+
| CollectionEntry<'containers'>
|
|
74
|
+
| CollectionEntry<'channels'>
|
|
75
|
+
>(messageMap, serviceMap, containerMap, channelMap);
|
|
76
|
+
|
|
77
|
+
// Process inputs - messages, containers, services, channels (etc)
|
|
78
|
+
inputsRaw.forEach((inputConfig) => {
|
|
79
|
+
let inputResource = findInMap(resourceMap, inputConfig.id, inputConfig.version) as
|
|
80
|
+
| CollectionEntry<CollectionMessageTypes>
|
|
81
|
+
| CollectionEntry<'services'>
|
|
82
|
+
| CollectionEntry<'containers'>
|
|
83
|
+
| CollectionEntry<'channels'>;
|
|
84
|
+
|
|
85
|
+
const existingNode = nodes.find((n: any) => n.id === generateIdForNode(inputResource));
|
|
86
|
+
|
|
87
|
+
if (!existingNode) {
|
|
88
|
+
const nodeDataKey = getNodePropertyFromCollectionType(inputResource?.collection);
|
|
89
|
+
|
|
90
|
+
nodes.push({
|
|
91
|
+
id: generateIdForNode(inputResource),
|
|
92
|
+
sourcePosition: 'right',
|
|
93
|
+
targetPosition: 'left',
|
|
94
|
+
data: { mode, [nodeDataKey]: { ...inputResource?.data } },
|
|
95
|
+
type: (inputResource?.collection as any) === 'containers' ? 'data' : inputResource?.collection,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// If collection is a message we render the producers of the message
|
|
100
|
+
if (isCollectionAMessage(inputResource?.collection)) {
|
|
101
|
+
const producersOfMessage = getProducersOfMessage(
|
|
102
|
+
services as CollectionEntry<'services'>[],
|
|
103
|
+
inputResource as CollectionEntry<CollectionMessageTypes>
|
|
104
|
+
) as CollectionEntry<'services'>[];
|
|
105
|
+
for (const producer of producersOfMessage) {
|
|
106
|
+
const { nodes: producerNodes, edges: producerEdges } = getNodesAndEdgesForProducedMessage({
|
|
107
|
+
message: inputResource as CollectionEntry<CollectionMessageTypes>,
|
|
108
|
+
// We dont render any other services that consume this event for now
|
|
109
|
+
services: [],
|
|
110
|
+
// We dont render channels on this view for now...
|
|
111
|
+
channels: [],
|
|
112
|
+
currentNodes: nodes,
|
|
113
|
+
currentEdges: edges,
|
|
114
|
+
source: producer,
|
|
115
|
+
mode,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
nodes.push(...producerNodes);
|
|
119
|
+
edges.push(...producerEdges);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Add edge from resource to data product
|
|
124
|
+
edges.push(
|
|
125
|
+
createEdge({
|
|
126
|
+
id: generatedIdForEdge(inputResource, dataProduct),
|
|
127
|
+
source: generateIdForNode(inputResource),
|
|
128
|
+
target: generateIdForNode(dataProduct),
|
|
129
|
+
label: 'input',
|
|
130
|
+
type: 'animated',
|
|
131
|
+
data: {
|
|
132
|
+
customColor: getColorFromString(inputResource.data.id),
|
|
133
|
+
rootSourceAndTarget: { source: inputResource, target: dataProduct },
|
|
134
|
+
},
|
|
135
|
+
markerEnd: {
|
|
136
|
+
type: MarkerType.ArrowClosed,
|
|
137
|
+
color: '#666',
|
|
138
|
+
width: 40,
|
|
139
|
+
height: 40,
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// The data product itself
|
|
146
|
+
nodes.push({
|
|
147
|
+
id: generateIdForNode(dataProduct),
|
|
148
|
+
sourcePosition: 'right',
|
|
149
|
+
targetPosition: 'left',
|
|
150
|
+
data: { mode, dataProduct: { ...dataProduct.data } },
|
|
151
|
+
type: 'data-products',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Process outputs - messages, services, containers, channels that the data product produces
|
|
155
|
+
outputsRaw.forEach((outputConfig) => {
|
|
156
|
+
// Find the output resource (can be message, service, container, or channel)
|
|
157
|
+
const outputResource = findInMap(resourceMap, outputConfig.id, outputConfig.version) as
|
|
158
|
+
| CollectionEntry<CollectionMessageTypes>
|
|
159
|
+
| CollectionEntry<'services'>
|
|
160
|
+
| CollectionEntry<'containers'>
|
|
161
|
+
| CollectionEntry<'channels'>;
|
|
162
|
+
|
|
163
|
+
if (!outputResource) return;
|
|
164
|
+
|
|
165
|
+
// Add the node if it doesn't exist
|
|
166
|
+
const existingNode = nodes.find((n: any) => n.id === generateIdForNode(outputResource));
|
|
167
|
+
if (!existingNode) {
|
|
168
|
+
const nodeDataKey = getNodePropertyFromCollectionType(outputResource?.collection);
|
|
169
|
+
|
|
170
|
+
nodes.push({
|
|
171
|
+
id: generateIdForNode(outputResource),
|
|
172
|
+
sourcePosition: 'right',
|
|
173
|
+
targetPosition: 'left',
|
|
174
|
+
data: { mode, [nodeDataKey]: { ...outputResource?.data } },
|
|
175
|
+
type: (outputResource?.collection as any) === 'containers' ? 'data' : outputResource?.collection,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Add edge from data product to the output resource
|
|
180
|
+
edges.push(
|
|
181
|
+
createEdge({
|
|
182
|
+
id: generatedIdForEdge(dataProduct, outputResource),
|
|
183
|
+
source: generateIdForNode(dataProduct),
|
|
184
|
+
target: generateIdForNode(outputResource),
|
|
185
|
+
label: 'output',
|
|
186
|
+
type: 'animated',
|
|
187
|
+
data: {
|
|
188
|
+
customColor: getColorFromString(outputResource.data.id),
|
|
189
|
+
rootSourceAndTarget: { source: dataProduct, target: outputResource },
|
|
190
|
+
},
|
|
191
|
+
markerEnd: {
|
|
192
|
+
type: MarkerType.ArrowClosed,
|
|
193
|
+
color: '#666',
|
|
194
|
+
width: 40,
|
|
195
|
+
height: 40,
|
|
196
|
+
},
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
nodes.forEach((node: any) => {
|
|
202
|
+
flow.setNode(node.id, { width: 150, height: 100 });
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
edges.forEach((edge: any) => {
|
|
206
|
+
flow.setEdge(edge.source, edge.target);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Render the diagram in memory getting the X and Y
|
|
210
|
+
dagre.layout(flow);
|
|
211
|
+
|
|
212
|
+
// Find any duplicated edges, and merge them into one edge
|
|
213
|
+
const uniqueEdges = edges.reduce((acc: any[], edge: any) => {
|
|
214
|
+
const existingEdge = acc.find((e: any) => e.id === edge.id);
|
|
215
|
+
if (!existingEdge) {
|
|
216
|
+
acc.push(edge);
|
|
217
|
+
}
|
|
218
|
+
return acc;
|
|
219
|
+
}, []);
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
nodes: calculatedNodes(flow, nodes),
|
|
223
|
+
edges: uniqueEdges,
|
|
224
|
+
};
|
|
225
|
+
};
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
createEdge,
|
|
9
9
|
} from '@utils/node-graphs/utils/utils';
|
|
10
10
|
import { getNodesAndEdges as getServicesNodeAndEdges } from './services-node-graph';
|
|
11
|
+
import { getNodesAndEdges as getDataProductsNodeAndEdges } from './data-products-node-graph';
|
|
11
12
|
import merge from 'lodash.merge';
|
|
12
13
|
import { createVersionedMap, findInMap } from '@utils/collections/util';
|
|
13
14
|
import type { Node } from '@xyflow/react';
|
|
@@ -199,7 +200,11 @@ export const getNodesAndEdges = async ({
|
|
|
199
200
|
edges = new Map();
|
|
200
201
|
|
|
201
202
|
// 1. Parallel Fetching
|
|
202
|
-
const [domains, services] = await Promise.all([
|
|
203
|
+
const [domains, services, dataProducts] = await Promise.all([
|
|
204
|
+
getCollection('domains'),
|
|
205
|
+
getCollection('services'),
|
|
206
|
+
getCollection('data-products'),
|
|
207
|
+
]);
|
|
203
208
|
|
|
204
209
|
const domain = domains.find((service) => service.data.id === id && service.data.version === version);
|
|
205
210
|
|
|
@@ -214,9 +219,11 @@ export const getNodesAndEdges = async ({
|
|
|
214
219
|
// 2. Build optimized maps
|
|
215
220
|
const serviceMap = createVersionedMap(services);
|
|
216
221
|
const domainMap = createVersionedMap(domains);
|
|
222
|
+
const dataProductMap = createVersionedMap(dataProducts);
|
|
217
223
|
|
|
218
224
|
const rawServices = domain?.data.services || [];
|
|
219
225
|
const rawSubDomains = domain?.data.domains || [];
|
|
226
|
+
const rawDataProducts = (domain?.data as any)['data-products'] || [];
|
|
220
227
|
|
|
221
228
|
// Optimized hydration
|
|
222
229
|
const domainServicesWithVersion = rawServices
|
|
@@ -229,7 +236,12 @@ export const getNodesAndEdges = async ({
|
|
|
229
236
|
.filter((d): d is any => !!d)
|
|
230
237
|
.map((svc) => ({ id: svc.data.id, version: svc.data.version }));
|
|
231
238
|
|
|
232
|
-
|
|
239
|
+
const domainDataProductsWithVersion = rawDataProducts
|
|
240
|
+
.map((dataProduct: any) => findInMap(dataProductMap, dataProduct.id, dataProduct.version))
|
|
241
|
+
.filter((dp: any): dp is any => !!dp)
|
|
242
|
+
.map((dp: any) => ({ id: dp.data.id, version: dp.data.version }));
|
|
243
|
+
|
|
244
|
+
// Get all the nodes for everything
|
|
233
245
|
|
|
234
246
|
for (const service of domainServicesWithVersion) {
|
|
235
247
|
const { nodes: serviceNodes, edges: serviceEdges } = await getServicesNodeAndEdges({
|
|
@@ -255,6 +267,20 @@ export const getNodesAndEdges = async ({
|
|
|
255
267
|
serviceEdges.forEach((e) => edges.set(e.id, e));
|
|
256
268
|
}
|
|
257
269
|
|
|
270
|
+
for (const dataProduct of domainDataProductsWithVersion) {
|
|
271
|
+
const { nodes: dataProductNodes, edges: dataProductEdges } = await getDataProductsNodeAndEdges({
|
|
272
|
+
id: dataProduct.id,
|
|
273
|
+
version: dataProduct.version,
|
|
274
|
+
defaultFlow: flow,
|
|
275
|
+
mode,
|
|
276
|
+
});
|
|
277
|
+
dataProductNodes.forEach((n: any) => {
|
|
278
|
+
nodes.set(n.id, nodes.has(n.id) ? merge(nodes.get(n.id), n) : n);
|
|
279
|
+
});
|
|
280
|
+
// @ts-ignore
|
|
281
|
+
dataProductEdges.forEach((e) => edges.set(e.id, e));
|
|
282
|
+
}
|
|
283
|
+
|
|
258
284
|
for (const subDomain of domainSubDomainsWithVersion) {
|
|
259
285
|
const { nodes: subDomainNodes, edges: subDomainEdges } = await getNodesAndEdges({
|
|
260
286
|
id: subDomain.id,
|
|
@@ -84,25 +84,55 @@ const getNodesAndEdges = async ({
|
|
|
84
84
|
type: message.collection,
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
const producers = (message.data.producers as CollectionEntry<'services'>[]) || [];
|
|
88
|
-
const consumers = (message.data.consumers as CollectionEntry<'services'>[]) || [];
|
|
87
|
+
const producers = (message.data.producers as (CollectionEntry<'services'> | CollectionEntry<'data-products'>)[]) || [];
|
|
88
|
+
const consumers = (message.data.consumers as (CollectionEntry<'services'> | CollectionEntry<'data-products'>)[]) || [];
|
|
89
89
|
|
|
90
|
-
// Track nodes that are both sent and received
|
|
91
|
-
const
|
|
90
|
+
// Track nodes that are both sent and received (only for services)
|
|
91
|
+
const serviceProducers = producers.filter((p) => p.collection === 'services') as CollectionEntry<'services'>[];
|
|
92
|
+
const serviceConsumers = consumers.filter((c) => c.collection === 'services') as CollectionEntry<'services'>[];
|
|
93
|
+
const bothSentAndReceived = findMatchingNodes(serviceProducers, serviceConsumers);
|
|
92
94
|
|
|
93
95
|
for (const producer of producers) {
|
|
94
|
-
|
|
96
|
+
const isDataProduct = producer.collection === 'data-products';
|
|
97
|
+
|
|
98
|
+
// Create the producer node with appropriate data structure
|
|
95
99
|
nodes.push({
|
|
96
100
|
id: generateIdForNode(producer),
|
|
97
|
-
type: producer?.collection,
|
|
101
|
+
type: isDataProduct ? 'data-products' : producer?.collection,
|
|
98
102
|
sourcePosition: 'right',
|
|
99
103
|
targetPosition: 'left',
|
|
100
|
-
data: { mode, service: { ...producer.data } },
|
|
104
|
+
data: isDataProduct ? { mode, dataProduct: { ...producer.data } } : { mode, service: { ...producer.data } },
|
|
101
105
|
position: { x: 250, y: 0 },
|
|
102
106
|
});
|
|
103
107
|
|
|
108
|
+
// Data products don't have channel configuration, so connect directly to the message
|
|
109
|
+
if (isDataProduct) {
|
|
110
|
+
const rootSourceAndTarget = {
|
|
111
|
+
source: { id: generateIdForNode(producer), collection: producer.collection },
|
|
112
|
+
target: { id: generateIdForNode(message), collection: message.collection },
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
edges.push({
|
|
116
|
+
id: generatedIdForEdge(producer, message),
|
|
117
|
+
source: generateIdForNode(producer),
|
|
118
|
+
target: generateIdForNode(message),
|
|
119
|
+
label: 'produces',
|
|
120
|
+
data: { customColor: getColorFromString(message.data.id), rootSourceAndTarget },
|
|
121
|
+
animated: false,
|
|
122
|
+
markerEnd: {
|
|
123
|
+
type: MarkerType.ArrowClosed,
|
|
124
|
+
width: 40,
|
|
125
|
+
height: 40,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Service-specific channel handling
|
|
132
|
+
const serviceProducer = producer as CollectionEntry<'services'>;
|
|
133
|
+
|
|
104
134
|
// Is the producer sending this message to a channel?
|
|
105
|
-
const producerConfigurationForMessage =
|
|
135
|
+
const producerConfigurationForMessage = serviceProducer.data.sends?.find((send) => send.id === message.data.id);
|
|
106
136
|
const producerChannelConfiguration = producerConfigurationForMessage?.to ?? [];
|
|
107
137
|
|
|
108
138
|
const producerHasChannels = producerChannelConfiguration?.length > 0;
|
|
@@ -184,18 +214,44 @@ const getNodesAndEdges = async ({
|
|
|
184
214
|
|
|
185
215
|
// The messages the service sends
|
|
186
216
|
for (const consumer of consumers) {
|
|
187
|
-
|
|
217
|
+
const isDataProduct = consumer.collection === 'data-products';
|
|
218
|
+
|
|
219
|
+
// Render the consumer node with appropriate data structure
|
|
188
220
|
nodes.push({
|
|
189
221
|
id: generateIdForNode(consumer),
|
|
190
222
|
sourcePosition: 'right',
|
|
191
223
|
targetPosition: 'left',
|
|
192
|
-
data:
|
|
224
|
+
data: isDataProduct
|
|
225
|
+
? { title: consumer?.data.id, mode, dataProduct: { ...consumer.data } }
|
|
226
|
+
: { title: consumer?.data.id, mode, service: { ...consumer.data } },
|
|
193
227
|
position: { x: 0, y: 0 },
|
|
194
|
-
type: consumer?.collection,
|
|
228
|
+
type: isDataProduct ? 'data-products' : consumer?.collection,
|
|
195
229
|
});
|
|
196
230
|
|
|
231
|
+
// Data products don't have channel configuration, so connect directly from the message
|
|
232
|
+
if (isDataProduct) {
|
|
233
|
+
const rootSourceAndTarget = {
|
|
234
|
+
source: { id: generateIdForNode(message), collection: message.collection },
|
|
235
|
+
target: { id: generateIdForNode(consumer), collection: consumer.collection },
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
edges.push(
|
|
239
|
+
createEdge({
|
|
240
|
+
id: generatedIdForEdge(message, consumer),
|
|
241
|
+
source: generateIdForNode(message),
|
|
242
|
+
target: generateIdForNode(consumer),
|
|
243
|
+
label: 'consumed by',
|
|
244
|
+
data: { customColor: getColorFromString(message.data.id), rootSourceAndTarget },
|
|
245
|
+
})
|
|
246
|
+
);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Service-specific channel handling
|
|
251
|
+
const serviceConsumer = consumer as CollectionEntry<'services'>;
|
|
252
|
+
|
|
197
253
|
// Is the consumer receiving this message from a channel?
|
|
198
|
-
const consumerConfigurationForMessage =
|
|
254
|
+
const consumerConfigurationForMessage = serviceConsumer.data.receives?.find((receive) => receive.id === message.data.id);
|
|
199
255
|
const consumerChannelConfiguration = consumerConfigurationForMessage?.from ?? [];
|
|
200
256
|
|
|
201
257
|
const consumerHasChannels = consumerChannelConfiguration.length > 0;
|
|
@@ -237,10 +293,11 @@ const getNodesAndEdges = async ({
|
|
|
237
293
|
}
|
|
238
294
|
|
|
239
295
|
// Can any of the consumer channels be linked to any of the producer channels?
|
|
240
|
-
|
|
296
|
+
// Only consider service producers for channel linking (data products don't have sends/receives)
|
|
297
|
+
const producerChannels = serviceProducers
|
|
241
298
|
.map((producer) => producer.data.sends?.find((send) => send.id === message.data.id)?.to ?? [])
|
|
242
299
|
.flat();
|
|
243
|
-
const consumerChannels =
|
|
300
|
+
const consumerChannels = serviceConsumer.data.receives?.find((receive) => receive.id === message.data.id)?.from ?? [];
|
|
244
301
|
|
|
245
302
|
for (const producerChannel of producerChannels) {
|
|
246
303
|
const producerChannelValue = findInMap(
|
|
@@ -376,8 +433,7 @@ export const getNodesAndEdgesForQueries = async ({
|
|
|
376
433
|
mode = 'simple',
|
|
377
434
|
channelRenderMode = 'flat',
|
|
378
435
|
}: Props) => {
|
|
379
|
-
const queries = await getQueries();
|
|
380
|
-
const channels = await getChannels();
|
|
436
|
+
const [queries, channels] = await Promise.all([getQueries(), getChannels()]);
|
|
381
437
|
return getNodesAndEdges({ id, version, defaultFlow, mode, channelRenderMode, collection: queries, channels });
|
|
382
438
|
};
|
|
383
439
|
|
|
@@ -388,8 +444,7 @@ export const getNodesAndEdgesForCommands = async ({
|
|
|
388
444
|
mode = 'simple',
|
|
389
445
|
channelRenderMode = 'flat',
|
|
390
446
|
}: Props) => {
|
|
391
|
-
const commands = await getCommands();
|
|
392
|
-
const channels = await getChannels();
|
|
447
|
+
const [commands, channels] = await Promise.all([getCommands(), getChannels()]);
|
|
393
448
|
return getNodesAndEdges({ id, version, defaultFlow, mode, channelRenderMode, collection: commands, channels });
|
|
394
449
|
};
|
|
395
450
|
|
|
@@ -400,8 +455,7 @@ export const getNodesAndEdgesForEvents = async ({
|
|
|
400
455
|
mode = 'simple',
|
|
401
456
|
channelRenderMode = 'flat',
|
|
402
457
|
}: Props) => {
|
|
403
|
-
const events = await getEvents();
|
|
404
|
-
const channels = await getChannels();
|
|
458
|
+
const [events, channels] = await Promise.all([getEvents(), getChannels()]);
|
|
405
459
|
return getNodesAndEdges({ id, version, defaultFlow, mode, channelRenderMode, collection: events, channels });
|
|
406
460
|
};
|
|
407
461
|
|
|
@@ -9,6 +9,7 @@ import { getEntities } from '@utils/collections/entities';
|
|
|
9
9
|
import { getContainers } from '@utils/collections/containers';
|
|
10
10
|
import { getDiagrams } from '@utils/collections/diagrams';
|
|
11
11
|
import type { CollectionEntry } from 'astro:content';
|
|
12
|
+
import { getDataProducts } from '@utils/collections/data-products';
|
|
12
13
|
|
|
13
14
|
export const pageDataLoader: Record<PageTypes, () => Promise<CollectionEntry<CollectionTypes>[]>> = {
|
|
14
15
|
events: getEvents,
|
|
@@ -21,4 +22,5 @@ export const pageDataLoader: Record<PageTypes, () => Promise<CollectionEntry<Col
|
|
|
21
22
|
entities: getEntities,
|
|
22
23
|
containers: getContainers,
|
|
23
24
|
diagrams: getDiagrams,
|
|
25
|
+
'data-products': getDataProducts,
|
|
24
26
|
};
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "https://github.com/event-catalog/eventcatalog.git"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
-
"version": "3.
|
|
9
|
+
"version": "3.8.0",
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@eventcatalog/generator-ai": "^1.1.0",
|
|
35
35
|
"@eventcatalog/license": "^0.0.7",
|
|
36
36
|
"@eventcatalog/linter": "^0.0.2",
|
|
37
|
-
"@eventcatalog/sdk": "^2.
|
|
37
|
+
"@eventcatalog/sdk": "^2.11.0",
|
|
38
38
|
"@eventcatalog/visualizer": "^0.0.6",
|
|
39
39
|
"@fontsource/inter": "^5.2.5",
|
|
40
40
|
"@headlessui/react": "^2.0.3",
|