@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.
Files changed (52) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-PV4MP6U4.js → chunk-F5WMB6Q5.js} +1 -1
  6. package/dist/{chunk-ZBPULBAC.js → chunk-SCDNNFXH.js} +1 -1
  7. package/dist/{chunk-WK6GQM5P.js → chunk-T3QXQPTB.js} +1 -1
  8. package/dist/constants.cjs +1 -1
  9. package/dist/constants.js +1 -1
  10. package/dist/eventcatalog.cjs +1 -1
  11. package/dist/eventcatalog.js +3 -3
  12. package/eventcatalog/src/components/Grids/DomainGrid.tsx +67 -2
  13. package/eventcatalog/src/components/Grids/MessageGrid.tsx +157 -41
  14. package/eventcatalog/src/components/Grids/ServiceGrid.tsx +78 -14
  15. package/eventcatalog/src/components/MDX/NodeGraph/Edges/MultilineEdgeLabel.tsx +52 -0
  16. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +13 -0
  17. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +4 -1
  18. package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Data.tsx +55 -16
  19. package/eventcatalog/src/components/SideBars/ContainerSideBar.astro +180 -0
  20. package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +41 -0
  21. package/eventcatalog/src/components/SideNav/ListViewSideBar/components/MessageList.tsx +1 -1
  22. package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +250 -59
  23. package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +3 -0
  24. package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +35 -1
  25. package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +2 -2
  26. package/eventcatalog/src/components/Tables/Table.tsx +22 -2
  27. package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +152 -0
  28. package/eventcatalog/src/components/Tables/columns/index.tsx +3 -0
  29. package/eventcatalog/src/content.config.ts +57 -1
  30. package/eventcatalog/src/layouts/DiscoverLayout.astro +11 -1
  31. package/eventcatalog/src/pages/architecture/architecture.astro +9 -1
  32. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +1 -1
  33. package/eventcatalog/src/pages/discover/[type]/index.astro +11 -1
  34. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +11 -1
  35. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +50 -1
  36. package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +2 -0
  37. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +4 -1
  38. package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +19 -1
  39. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +3 -0
  40. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +1 -1
  41. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +80 -0
  42. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +52 -0
  43. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +9 -2
  44. package/eventcatalog/src/types/index.ts +20 -2
  45. package/eventcatalog/src/utils/collections/containers.ts +94 -0
  46. package/eventcatalog/src/utils/collections/icons.ts +3 -1
  47. package/eventcatalog/src/utils/collections/services.ts +15 -1
  48. package/eventcatalog/src/utils/collections/util.ts +4 -2
  49. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +155 -0
  50. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +188 -82
  51. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  52. 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
- const nodes = [] as any,
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
- // All the messages the service receives
102
- receives.forEach((receive) => {
103
- // Create the node for the message
104
- nodes.push({
105
- id: generateIdForNode(receive),
106
- type: receive?.collection,
107
- sourcePosition: 'right',
108
- targetPosition: 'left',
109
- data: { mode, message: { ...receive.data } },
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
- nodes.push(...channelNodes);
126
- edges.push(...channelEdges);
127
- } else {
128
- // No channels, just link the message to the service
129
- edges.push(
130
- createEdge({
131
- id: generatedIdForEdge(receive, service),
132
- source: generateIdForNode(receive),
133
- target: generateIdForNode(service),
134
- label: getReceivesMessageByMessageType(receive?.collection),
135
- data: { message: { ...receive.data } },
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
- // The messages the service sends
152
- sends.forEach((send, index) => {
171
+ // Any containers the service writes to
172
+ writesTo.forEach((writeTo) => {
153
173
  nodes.push({
154
- id: generateIdForNode(send),
174
+ id: generateIdForNode(writeTo),
155
175
  sourcePosition: 'right',
156
176
  targetPosition: 'left',
157
- data: { mode, message: { ...send.data } },
158
- type: send?.collection,
177
+ data: { mode, data: { ...writeTo.data } },
178
+ type: 'data',
159
179
  });
160
180
 
161
- if (send.data.channels) {
162
- const { nodes: channelNodes, edges: channelEdges } = getChannelNodesAndEdges({
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, send),
185
+ id: generatedIdForEdge(service, writeTo),
180
186
  source: generateIdForNode(service),
181
- target: generateIdForNode(send),
182
- label: getSendsMessageByMessageType(send?.collection),
183
- data: { message: { ...send.data } },
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
- // Handle messages that are both sent and received
190
- bothSentAndReceived.forEach((message) => {
191
- if (message) {
192
- edges.push({
193
- id: generatedIdForEdge(service, message) + '-both',
194
- source: generateIdForNode(service),
195
- target: generateIdForNode(message),
196
- label: `${getSendsMessageByMessageType(message?.collection)} & ${getReceivesMessageByMessageType(message?.collection)}`,
197
- animated: false,
198
- data: { message: { ...message.data } },
199
- markerEnd: {
200
- type: MarkerType.ArrowClosed,
201
- width: 40,
202
- height: 40,
203
- },
204
- style: {
205
- strokeWidth: 1,
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
  });