@eventcatalog/core 3.7.0 → 3.7.2
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-TWL75YK3.js → chunk-7CTNGTBB.js} +1 -1
- package/dist/{chunk-6DINZHVK.js → chunk-GQZVIS3Z.js} +1 -1
- package/dist/{chunk-2CIYDOFG.js → chunk-M7EPRGHR.js} +1 -1
- package/dist/{chunk-VA6OWAKW.js → chunk-O6SRHGZ7.js} +1 -1
- package/dist/{chunk-WFIM5UXN.js → chunk-WAX3S32H.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/SchemaExplorer/JSONSchemaViewer.tsx +175 -45
- package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +53 -1
- package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +40 -0
- package/eventcatalog/src/utils/collections/domains.ts +5 -1
- package/eventcatalog/src/utils/collections/services.ts +2 -26
- package/eventcatalog/src/utils/collections/util.ts +64 -0
- package/package.json +4 -4
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-M7EPRGHR.js";
|
|
4
|
+
import "../chunk-GQZVIS3Z.js";
|
|
5
|
+
import "../chunk-WAX3S32H.js";
|
|
6
6
|
import "../chunk-UPONRQSN.js";
|
|
7
7
|
export {
|
|
8
8
|
log_build_default as default
|
package/dist/constants.cjs
CHANGED
package/dist/constants.js
CHANGED
package/dist/eventcatalog.cjs
CHANGED
|
@@ -109,7 +109,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
109
109
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
110
110
|
|
|
111
111
|
// package.json
|
|
112
|
-
var version = "3.7.
|
|
112
|
+
var version = "3.7.2";
|
|
113
113
|
|
|
114
114
|
// src/constants.ts
|
|
115
115
|
var VERSION = version;
|
package/dist/eventcatalog.js
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
} from "./chunk-PLNJC7NZ.js";
|
|
7
7
|
import {
|
|
8
8
|
log_build_default
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-M7EPRGHR.js";
|
|
10
|
+
import "./chunk-GQZVIS3Z.js";
|
|
11
11
|
import {
|
|
12
12
|
runMigrations
|
|
13
13
|
} from "./chunk-BH3JMNAV.js";
|
|
@@ -21,13 +21,13 @@ import {
|
|
|
21
21
|
} from "./chunk-5VBIXL6C.js";
|
|
22
22
|
import {
|
|
23
23
|
generate
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-O6SRHGZ7.js";
|
|
25
25
|
import {
|
|
26
26
|
logger
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-7CTNGTBB.js";
|
|
28
28
|
import {
|
|
29
29
|
VERSION
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-WAX3S32H.js";
|
|
31
31
|
import "./chunk-UPONRQSN.js";
|
|
32
32
|
|
|
33
33
|
// src/eventcatalog.ts
|
package/dist/generate.cjs
CHANGED
package/dist/generate.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generate
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-O6SRHGZ7.js";
|
|
4
|
+
import "./chunk-7CTNGTBB.js";
|
|
5
|
+
import "./chunk-WAX3S32H.js";
|
|
6
6
|
import "./chunk-UPONRQSN.js";
|
|
7
7
|
export {
|
|
8
8
|
generate
|
package/dist/utils/cli-logger.js
CHANGED
|
@@ -157,32 +157,32 @@ function processSchema(schema: any, rootSchema?: any): any {
|
|
|
157
157
|
return mergeAllOfSchemas({ ...schema, processSchema: (s: any) => processSchema(s, root) });
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
if (schema.oneOf) {
|
|
161
|
-
const
|
|
160
|
+
if (schema.oneOf || schema.anyOf) {
|
|
161
|
+
const variantsArray = schema.oneOf || schema.anyOf;
|
|
162
|
+
const variantType = schema.oneOf ? 'oneOf' : 'anyOf';
|
|
163
|
+
|
|
164
|
+
const processedVariants = variantsArray.map((variant: any, index: number) => {
|
|
162
165
|
const processedVariant = processSchema(variant, root);
|
|
163
166
|
return {
|
|
164
|
-
title: processedVariant.title || variant.title ||
|
|
167
|
+
title: processedVariant.title || variant.title || processedVariant.$id || `Option ${index + 1}`,
|
|
168
|
+
description: processedVariant.description || variant.description,
|
|
165
169
|
required: processedVariant.required || variant.required || [],
|
|
166
170
|
properties: processedVariant.properties || {},
|
|
171
|
+
type: processedVariant.type || variant.type || 'object',
|
|
167
172
|
...processedVariant,
|
|
168
173
|
};
|
|
169
174
|
});
|
|
170
175
|
|
|
171
|
-
const allProperties: Record<string, any> = {};
|
|
172
|
-
processedVariants.forEach((variant: any) => {
|
|
173
|
-
if (variant.properties) {
|
|
174
|
-
Object.assign(allProperties, variant.properties);
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
176
|
return {
|
|
179
177
|
...schema,
|
|
180
178
|
type: schema.type || 'object',
|
|
181
|
-
properties:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
properties: schema.properties
|
|
180
|
+
? Object.fromEntries(
|
|
181
|
+
Object.entries(schema.properties).map(([key, prop]: [string, any]) => [key, processSchema(prop, root)])
|
|
182
|
+
)
|
|
183
|
+
: {},
|
|
185
184
|
variants: processedVariants,
|
|
185
|
+
variantType,
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -203,9 +203,66 @@ function processSchema(schema: any, rootSchema?: any): any {
|
|
|
203
203
|
return schema;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
// NestedVariantSelector component for handling variants within array items
|
|
207
|
+
const NestedVariantSelector = ({
|
|
208
|
+
variants,
|
|
209
|
+
variantType,
|
|
210
|
+
level,
|
|
211
|
+
expand,
|
|
212
|
+
}: {
|
|
213
|
+
variants: any[];
|
|
214
|
+
variantType?: string;
|
|
215
|
+
level: number;
|
|
216
|
+
expand: boolean;
|
|
217
|
+
}) => {
|
|
218
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
219
|
+
const selectedVariant = variants[selectedIndex];
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<div className="mt-2">
|
|
223
|
+
<div className="p-2 bg-[rgb(var(--ec-accent-subtle,var(--ec-content-hover)))] border border-[rgb(var(--ec-page-border))] rounded-md">
|
|
224
|
+
<div className="flex flex-col sm:flex-row sm:items-center gap-2">
|
|
225
|
+
<span className="text-xs font-medium text-[rgb(var(--ec-page-text))]">
|
|
226
|
+
{variantType === 'anyOf' ? 'Any of' : 'One of'} {variants.length} options:
|
|
227
|
+
</span>
|
|
228
|
+
<select
|
|
229
|
+
value={selectedIndex}
|
|
230
|
+
onChange={(e) => setSelectedIndex(parseInt(e.target.value))}
|
|
231
|
+
className="form-select text-xs border-[rgb(var(--ec-input-border))] bg-[rgb(var(--ec-input-bg))] text-[rgb(var(--ec-input-text))] rounded-md shadow-sm focus:border-[rgb(var(--ec-accent))] focus:ring focus:ring-[rgb(var(--ec-accent)/0.2)] py-1"
|
|
232
|
+
>
|
|
233
|
+
{variants.map((variant: any, index: number) => (
|
|
234
|
+
<option key={index} value={index}>
|
|
235
|
+
{variant.title}
|
|
236
|
+
</option>
|
|
237
|
+
))}
|
|
238
|
+
</select>
|
|
239
|
+
</div>
|
|
240
|
+
{selectedVariant?.description && (
|
|
241
|
+
<p className="text-xs text-[rgb(var(--ec-page-text-muted))] mt-1">{selectedVariant.description}</p>
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
{selectedVariant?.properties && (
|
|
245
|
+
<div className="mt-2">
|
|
246
|
+
{Object.entries(selectedVariant.properties).map(([propName, propDetails]: [string, any]) => (
|
|
247
|
+
<SchemaProperty
|
|
248
|
+
key={`${selectedIndex}-${propName}`}
|
|
249
|
+
name={propName}
|
|
250
|
+
details={propDetails}
|
|
251
|
+
isRequired={selectedVariant.required?.includes(propName) ?? false}
|
|
252
|
+
level={level}
|
|
253
|
+
expand={expand}
|
|
254
|
+
/>
|
|
255
|
+
))}
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
|
|
206
262
|
// SchemaProperty component
|
|
207
263
|
const SchemaProperty = ({ name, details, isRequired, level, isListItem = false, expand }: SchemaPropertyProps) => {
|
|
208
264
|
const [isExpanded, setIsExpanded] = useState(expand);
|
|
265
|
+
const [selectedVariantIndex, setSelectedVariantIndex] = useState(0);
|
|
209
266
|
const contentId = useRef(`prop-${name}-${level}-${Math.random().toString(36).substring(2, 7)}`).current;
|
|
210
267
|
|
|
211
268
|
useEffect(() => {
|
|
@@ -219,8 +276,11 @@ const SchemaProperty = ({ name, details, isRequired, level, isListItem = false,
|
|
|
219
276
|
((details.items.type === 'object' && details.items.properties) ||
|
|
220
277
|
details.items.allOf ||
|
|
221
278
|
details.items.oneOf ||
|
|
279
|
+
details.items.anyOf ||
|
|
280
|
+
details.items.variants ||
|
|
222
281
|
details.items.$ref);
|
|
223
|
-
const
|
|
282
|
+
const hasVariants = details.variants && details.variants.length > 0;
|
|
283
|
+
const isCollapsible = hasNestedProperties || hasArrayItemProperties || hasVariants;
|
|
224
284
|
|
|
225
285
|
const indentationClass = `pl-${level * 3}`;
|
|
226
286
|
|
|
@@ -247,7 +307,7 @@ const SchemaProperty = ({ name, details, isRequired, level, isListItem = false,
|
|
|
247
307
|
<div>
|
|
248
308
|
<span className="font-semibold text-[rgb(var(--ec-page-text))] text-sm">{name}</span>
|
|
249
309
|
<span className="ml-1.5 text-[rgb(var(--ec-accent))] font-mono text-xs">
|
|
250
|
-
{details.type}
|
|
310
|
+
{hasVariants ? (details.variantType === 'anyOf' ? 'anyOf' : 'oneOf') : details.type}
|
|
251
311
|
{details.type === 'array' && details.items?.type ? `[${details.items.type}]` : ''}
|
|
252
312
|
{details.format ? `<${details.format}>` : ''}
|
|
253
313
|
{details._refPath && (
|
|
@@ -325,9 +385,53 @@ const SchemaProperty = ({ name, details, isRequired, level, isListItem = false,
|
|
|
325
385
|
)}
|
|
326
386
|
</div>
|
|
327
387
|
|
|
328
|
-
{(hasNestedProperties || hasArrayItems) && (
|
|
388
|
+
{(hasNestedProperties || hasArrayItems || hasVariants) && (
|
|
329
389
|
<div id={contentId} className={`nested-content mt-1 ${isCollapsible && !isExpanded ? 'hidden' : ''}`}>
|
|
390
|
+
{hasVariants && (
|
|
391
|
+
<div className="mt-2 mb-2 p-2 bg-[rgb(var(--ec-accent-subtle,var(--ec-content-hover)))] border border-[rgb(var(--ec-page-border))] rounded-md">
|
|
392
|
+
<div className="flex flex-col sm:flex-row sm:items-center gap-2">
|
|
393
|
+
<span className="text-xs font-medium text-[rgb(var(--ec-page-text))]">
|
|
394
|
+
{details.variantType === 'anyOf' ? 'Any of' : 'One of'} {details.variants.length} options:
|
|
395
|
+
</span>
|
|
396
|
+
<select
|
|
397
|
+
value={selectedVariantIndex}
|
|
398
|
+
onChange={(e) => setSelectedVariantIndex(parseInt(e.target.value))}
|
|
399
|
+
className="form-select text-xs border-[rgb(var(--ec-input-border))] bg-[rgb(var(--ec-input-bg))] text-[rgb(var(--ec-input-text))] rounded-md shadow-sm focus:border-[rgb(var(--ec-accent))] focus:ring focus:ring-[rgb(var(--ec-accent)/0.2)] py-1"
|
|
400
|
+
>
|
|
401
|
+
{details.variants.map((variant: any, index: number) => (
|
|
402
|
+
<option key={index} value={index}>
|
|
403
|
+
{variant.title}
|
|
404
|
+
</option>
|
|
405
|
+
))}
|
|
406
|
+
</select>
|
|
407
|
+
</div>
|
|
408
|
+
{details.variants[selectedVariantIndex]?.description && (
|
|
409
|
+
<p className="text-xs text-[rgb(var(--ec-page-text-muted))] mt-1">
|
|
410
|
+
{details.variants[selectedVariantIndex].description}
|
|
411
|
+
</p>
|
|
412
|
+
)}
|
|
413
|
+
</div>
|
|
414
|
+
)}
|
|
415
|
+
|
|
416
|
+
{hasVariants && details.variants[selectedVariantIndex]?.properties && (
|
|
417
|
+
<div className="mt-1">
|
|
418
|
+
{Object.entries(details.variants[selectedVariantIndex].properties).map(
|
|
419
|
+
([nestedName, nestedDetails]: [string, any]) => (
|
|
420
|
+
<SchemaProperty
|
|
421
|
+
key={`${selectedVariantIndex}-${nestedName}`}
|
|
422
|
+
name={nestedName}
|
|
423
|
+
details={nestedDetails}
|
|
424
|
+
isRequired={details.variants[selectedVariantIndex]?.required?.includes(nestedName) ?? false}
|
|
425
|
+
level={level + 1}
|
|
426
|
+
expand={expand}
|
|
427
|
+
/>
|
|
428
|
+
)
|
|
429
|
+
)}
|
|
430
|
+
</div>
|
|
431
|
+
)}
|
|
432
|
+
|
|
330
433
|
{hasNestedProperties &&
|
|
434
|
+
!hasVariants &&
|
|
331
435
|
details.properties &&
|
|
332
436
|
Object.entries(details.properties).map(([nestedName, nestedDetails]: [string, any]) => (
|
|
333
437
|
<SchemaProperty
|
|
@@ -355,11 +459,21 @@ const SchemaProperty = ({ name, details, isRequired, level, isListItem = false,
|
|
|
355
459
|
expand={expand}
|
|
356
460
|
/>
|
|
357
461
|
))}
|
|
358
|
-
{
|
|
359
|
-
<
|
|
360
|
-
|
|
361
|
-
|
|
462
|
+
{details.items.variants && details.items.variants.length > 0 && (
|
|
463
|
+
<NestedVariantSelector
|
|
464
|
+
variants={details.items.variants}
|
|
465
|
+
variantType={details.items.variantType}
|
|
466
|
+
level={level + 1}
|
|
467
|
+
expand={expand}
|
|
468
|
+
/>
|
|
362
469
|
)}
|
|
470
|
+
{(details.items.allOf || details.items.oneOf || details.items.anyOf || details.items.$ref) &&
|
|
471
|
+
!details.items.properties &&
|
|
472
|
+
!details.items.variants && (
|
|
473
|
+
<div className="text-xs text-[rgb(var(--ec-page-text-muted))] mt-1">
|
|
474
|
+
Complex array item schema detected. The properties should be processed by the parent SchemaViewer.
|
|
475
|
+
</div>
|
|
476
|
+
)}
|
|
363
477
|
</div>
|
|
364
478
|
)}
|
|
365
479
|
</div>
|
|
@@ -421,7 +535,7 @@ export default function JSONSchemaViewer({
|
|
|
421
535
|
return { displaySchema: display, isRootArray: isArray };
|
|
422
536
|
}, [processedSchema]);
|
|
423
537
|
|
|
424
|
-
const { description, properties, required = [], variants } = displaySchema;
|
|
538
|
+
const { description, properties, required = [], variants, variantType } = displaySchema;
|
|
425
539
|
const totalProperties = useMemo(() => countProperties(displaySchema), [displaySchema]);
|
|
426
540
|
|
|
427
541
|
// Search functionality
|
|
@@ -667,14 +781,16 @@ export default function JSONSchemaViewer({
|
|
|
667
781
|
)}
|
|
668
782
|
{description && <p className="text-[rgb(var(--ec-page-text-muted))] text-xs mb-5">{description}</p>}
|
|
669
783
|
|
|
670
|
-
{variants && (
|
|
671
|
-
<div className="mb-4">
|
|
672
|
-
<div className="flex items-center
|
|
673
|
-
<span className="text-sm text-[rgb(var(--ec-page-text
|
|
784
|
+
{variants && variants.length > 0 && (
|
|
785
|
+
<div className="mb-4 p-3 bg-[rgb(var(--ec-accent-subtle,var(--ec-content-hover)))] border border-[rgb(var(--ec-page-border))] rounded-md">
|
|
786
|
+
<div className="flex flex-col sm:flex-row sm:items-center gap-2">
|
|
787
|
+
<span className="text-sm font-medium text-[rgb(var(--ec-page-text))]">
|
|
788
|
+
{variantType === 'anyOf' ? 'Any of' : 'One of'} {variants.length} options:
|
|
789
|
+
</span>
|
|
674
790
|
<select
|
|
675
791
|
value={selectedVariantIndex}
|
|
676
792
|
onChange={(e) => setSelectedVariantIndex(parseInt(e.target.value))}
|
|
677
|
-
className="form-select text-sm border-[rgb(var(--ec-input-border))] bg-[rgb(var(--ec-input-bg))] text-[rgb(var(--ec-input-text))] rounded-md shadow-sm focus:border-[rgb(var(--ec-accent))] focus:ring focus:ring-[rgb(var(--ec-accent)/0.2)]"
|
|
793
|
+
className="form-select text-sm border-[rgb(var(--ec-input-border))] bg-[rgb(var(--ec-input-bg))] text-[rgb(var(--ec-input-text))] rounded-md shadow-sm focus:border-[rgb(var(--ec-accent))] focus:ring focus:ring-[rgb(var(--ec-accent)/0.2)] flex-1 sm:flex-initial"
|
|
678
794
|
>
|
|
679
795
|
{variants.map((variant: any, index: number) => (
|
|
680
796
|
<option key={index} value={index}>
|
|
@@ -683,27 +799,41 @@ export default function JSONSchemaViewer({
|
|
|
683
799
|
))}
|
|
684
800
|
</select>
|
|
685
801
|
</div>
|
|
802
|
+
{variants[selectedVariantIndex]?.description && (
|
|
803
|
+
<p className="text-xs text-[rgb(var(--ec-page-text-muted))] mt-2">{variants[selectedVariantIndex].description}</p>
|
|
804
|
+
)}
|
|
686
805
|
</div>
|
|
687
806
|
)}
|
|
688
807
|
|
|
689
|
-
{
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
808
|
+
{(() => {
|
|
809
|
+
// Determine which properties to display
|
|
810
|
+
const propsToDisplay =
|
|
811
|
+
variants && variants.length > 0 && variants[selectedVariantIndex]?.properties
|
|
812
|
+
? { ...properties, ...variants[selectedVariantIndex].properties }
|
|
813
|
+
: properties;
|
|
814
|
+
const requiredProps = variants && variants.length > 0 ? variants[selectedVariantIndex]?.required || [] : required;
|
|
815
|
+
|
|
816
|
+
if (propsToDisplay && Object.keys(propsToDisplay).length > 0) {
|
|
817
|
+
return (
|
|
818
|
+
<div ref={propertiesContainerRef}>
|
|
819
|
+
{Object.entries(propsToDisplay).map(([name, details]: [string, any]) => (
|
|
820
|
+
<SchemaProperty
|
|
821
|
+
key={`${selectedVariantIndex}-${name}`}
|
|
822
|
+
name={name}
|
|
823
|
+
details={details}
|
|
824
|
+
isRequired={requiredProps.includes(name)}
|
|
825
|
+
level={0}
|
|
826
|
+
expand={expandAll}
|
|
827
|
+
/>
|
|
828
|
+
))}
|
|
829
|
+
</div>
|
|
830
|
+
);
|
|
831
|
+
} else if (!isRootArray) {
|
|
832
|
+
return <p className="text-[rgb(var(--ec-page-text-muted))] text-sm">Schema does not contain any properties.</p>;
|
|
833
|
+
}
|
|
834
|
+
return null;
|
|
835
|
+
})()}
|
|
836
|
+
{!properties && !variants && isRootArray && (
|
|
707
837
|
<div className="text-center py-8">
|
|
708
838
|
<div className="text-[rgb(var(--ec-page-text-muted))] text-sm">
|
|
709
839
|
<p>
|
|
@@ -4,6 +4,7 @@ import { getEvents } from '@utils/collections/events';
|
|
|
4
4
|
import { getCommands } from '@utils/collections/commands';
|
|
5
5
|
import { getQueries } from '@utils/collections/queries';
|
|
6
6
|
import { getServices, getSpecificationsForService } from '@utils/collections/services';
|
|
7
|
+
import { getDomains, getSpecificationsForDomain } from '@utils/collections/domains';
|
|
7
8
|
import { getOwner } from '@utils/collections/owners';
|
|
8
9
|
import { buildUrl } from '@utils/url-builder';
|
|
9
10
|
import { resourceFileExists, readResourceFile } from '@utils/resource-files';
|
|
@@ -133,7 +134,58 @@ async function fetchAllSchemas() {
|
|
|
133
134
|
// Flatten and filter out null values
|
|
134
135
|
const flatServicesWithSpecs = servicesWithSpecs.flat().filter((service) => service !== null);
|
|
135
136
|
|
|
136
|
-
|
|
137
|
+
// Fetch all domains
|
|
138
|
+
const domains = await getDomains({ getAllVersions: true });
|
|
139
|
+
|
|
140
|
+
// Filter domains with specifications and read spec content - only keep essential data
|
|
141
|
+
const domainsWithSpecs = await Promise.all(
|
|
142
|
+
domains.map(async (domain) => {
|
|
143
|
+
try {
|
|
144
|
+
const specifications = getSpecificationsForDomain(domain);
|
|
145
|
+
|
|
146
|
+
if (specifications.length === 0) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return await Promise.all(
|
|
151
|
+
specifications.map(async (spec) => {
|
|
152
|
+
if (!resourceFileExists(domain, spec.path)) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const schemaContent = readResourceFile(domain, spec.path) ?? '';
|
|
157
|
+
const schemaExtension = spec.type;
|
|
158
|
+
const enrichedOwners = await enrichOwners(domain.data.owners || []);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
collection: 'domains',
|
|
162
|
+
data: {
|
|
163
|
+
id: `${domain.data.id}`,
|
|
164
|
+
name: `${domain.data.name} - ${spec.name}`,
|
|
165
|
+
version: domain.data.version,
|
|
166
|
+
summary: domain.data.summary,
|
|
167
|
+
schemaPath: spec.path,
|
|
168
|
+
owners: enrichedOwners,
|
|
169
|
+
},
|
|
170
|
+
schemaContent,
|
|
171
|
+
schemaExtension,
|
|
172
|
+
specType: spec.type,
|
|
173
|
+
specName: spec.name,
|
|
174
|
+
specFilenameWithoutExtension: spec.filenameWithoutExtension,
|
|
175
|
+
};
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error(`Error reading specifications for domain ${domain.data.id}:`, error);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
// Flatten and filter out null values for domains
|
|
186
|
+
const flatDomainsWithSpecs = domainsWithSpecs.flat().filter((domain) => domain !== null);
|
|
187
|
+
|
|
188
|
+
return [...messagesWithSchemas, ...flatServicesWithSpecs, ...flatDomainsWithSpecs];
|
|
137
189
|
}
|
|
138
190
|
|
|
139
191
|
export class Page extends HybridPage {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from './shared';
|
|
13
13
|
import { isVisualiserEnabled } from '@utils/feature';
|
|
14
14
|
import { pluralizeMessageType } from '@utils/collections/messages';
|
|
15
|
+
import { getSpecificationsForDomain } from '@utils/collections/domains';
|
|
15
16
|
|
|
16
17
|
export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[], context: ResourceGroupContext): NavNode => {
|
|
17
18
|
const servicesInDomain = domain.data.services || [];
|
|
@@ -48,6 +49,14 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
|
|
|
48
49
|
const diagramNavItems = buildDiagramNavItems(domainDiagrams, context.diagrams);
|
|
49
50
|
const hasDiagrams = diagramNavItems.length > 0;
|
|
50
51
|
|
|
52
|
+
// Specifications
|
|
53
|
+
const specifications = getSpecificationsForDomain(domain);
|
|
54
|
+
const hasSpecifications = specifications.length > 0;
|
|
55
|
+
const openAPISpecifications = specifications.filter((specification) => specification.type === 'openapi');
|
|
56
|
+
const asyncAPISpecifications = specifications.filter((specification) => specification.type === 'asyncapi');
|
|
57
|
+
const graphQLSpecifications = specifications.filter((specification) => specification.type === 'graphql');
|
|
58
|
+
const renderSpecifications = hasSpecifications && shouldRenderSideBarSection(domain, 'specifications');
|
|
59
|
+
|
|
51
60
|
return {
|
|
52
61
|
type: 'item',
|
|
53
62
|
title: domain.data.name,
|
|
@@ -87,6 +96,37 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
|
|
|
87
96
|
icon: 'FileImage',
|
|
88
97
|
pages: diagramNavItems,
|
|
89
98
|
},
|
|
99
|
+
renderSpecifications && {
|
|
100
|
+
type: 'group',
|
|
101
|
+
title: 'API & Contracts',
|
|
102
|
+
icon: 'FileCode',
|
|
103
|
+
pages: [
|
|
104
|
+
...openAPISpecifications.map((specification) => ({
|
|
105
|
+
type: 'item',
|
|
106
|
+
title: specification.name,
|
|
107
|
+
leftIcon: '/icons/openapi-black.svg',
|
|
108
|
+
href: buildUrl(
|
|
109
|
+
`/docs/domains/${domain.data.id}/${domain.data.version}/spec/${specification.filenameWithoutExtension}`
|
|
110
|
+
),
|
|
111
|
+
})),
|
|
112
|
+
...asyncAPISpecifications.map((specification) => ({
|
|
113
|
+
type: 'item',
|
|
114
|
+
title: specification.name,
|
|
115
|
+
leftIcon: '/icons/asyncapi-black.svg',
|
|
116
|
+
href: buildUrl(
|
|
117
|
+
`/docs/domains/${domain.data.id}/${domain.data.version}/asyncapi/${specification.filenameWithoutExtension}`
|
|
118
|
+
),
|
|
119
|
+
})),
|
|
120
|
+
...graphQLSpecifications.map((specification) => ({
|
|
121
|
+
type: 'item',
|
|
122
|
+
title: specification.name,
|
|
123
|
+
leftIcon: '/icons/graphql-black.svg',
|
|
124
|
+
href: buildUrl(
|
|
125
|
+
`/docs/domains/${domain.data.id}/${domain.data.version}/graphql/${specification.filenameWithoutExtension}`
|
|
126
|
+
),
|
|
127
|
+
})),
|
|
128
|
+
],
|
|
129
|
+
},
|
|
90
130
|
renderSubDomains && {
|
|
91
131
|
type: 'group',
|
|
92
132
|
title: 'Subdomains',
|
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import type { CollectionMessageTypes } from '@types';
|
|
5
5
|
import type { Service } from './types';
|
|
6
6
|
import utils from '@eventcatalog/sdk';
|
|
7
|
-
import { createVersionedMap, findInMap } from '@utils/collections/util';
|
|
7
|
+
import { createVersionedMap, findInMap, processSpecifications } from '@utils/collections/util';
|
|
8
8
|
|
|
9
9
|
const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
|
|
10
10
|
const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
|
|
@@ -383,3 +383,7 @@ export const getDomainsForService = async (service: Service): Promise<Domain[]>
|
|
|
383
383
|
export const domainHasEntities = (domain: Domain): boolean => {
|
|
384
384
|
return (domain.data.entities && domain.data.entities.length > 0) || false;
|
|
385
385
|
};
|
|
386
|
+
|
|
387
|
+
export const getSpecificationsForDomain = (domain: Domain) => {
|
|
388
|
+
return processSpecifications(domain.data.specifications as any);
|
|
389
|
+
};
|
|
@@ -6,7 +6,7 @@ import type { CollectionMessageTypes, CollectionTypes } from '@types';
|
|
|
6
6
|
const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
|
|
7
7
|
import utils, { type Domain } from '@eventcatalog/sdk';
|
|
8
8
|
import { getDomains, getDomainsForService } from './domains';
|
|
9
|
-
import { createVersionedMap, findInMap } from '@utils/collections/util';
|
|
9
|
+
import { createVersionedMap, findInMap, processSpecifications } from '@utils/collections/util';
|
|
10
10
|
|
|
11
11
|
export type Service = CollectionEntry<'services'>;
|
|
12
12
|
|
|
@@ -174,31 +174,7 @@ export const getConsumersOfMessage = (services: Service[], message: CollectionEn
|
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
export const getSpecificationsForService = (service: CollectionEntry<CollectionTypes>) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (service.data.specifications && !Array.isArray(service.data.specifications)) {
|
|
180
|
-
if (service.data.specifications.asyncapiPath) {
|
|
181
|
-
specifications.push({
|
|
182
|
-
type: 'asyncapi',
|
|
183
|
-
path: service.data.specifications.asyncapiPath,
|
|
184
|
-
name: 'AsyncAPI',
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
if (service.data.specifications.openapiPath) {
|
|
188
|
-
specifications.push({
|
|
189
|
-
type: 'openapi',
|
|
190
|
-
path: service.data.specifications.openapiPath,
|
|
191
|
-
name: 'OpenAPI',
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return specifications.map((spec) => ({
|
|
197
|
-
...spec,
|
|
198
|
-
name: spec.name || (spec.type === 'asyncapi' ? 'AsyncAPI' : 'OpenAPI'),
|
|
199
|
-
filename: path.basename(spec.path),
|
|
200
|
-
filenameWithoutExtension: path.basename(spec.path, path.extname(spec.path)),
|
|
201
|
-
}));
|
|
177
|
+
return processSpecifications(service.data.specifications as any);
|
|
202
178
|
};
|
|
203
179
|
// Get services for channel
|
|
204
180
|
export const getProducersAndConsumersForChannel = async (channel: CollectionEntry<'channels'>) => {
|
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
import type { CollectionTypes } from '@types';
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
3
|
import semver, { coerce, compare, eq, satisfies as satisfiesRange } from 'semver';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
// --- SPECIFICATION HELPERS ---
|
|
7
|
+
|
|
8
|
+
export type SpecificationType = 'asyncapi' | 'openapi' | 'graphql';
|
|
9
|
+
|
|
10
|
+
export interface SpecificationInput {
|
|
11
|
+
type: SpecificationType;
|
|
12
|
+
path: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ProcessedSpecification {
|
|
17
|
+
type: SpecificationType;
|
|
18
|
+
path: string;
|
|
19
|
+
name: string;
|
|
20
|
+
filename: string;
|
|
21
|
+
filenameWithoutExtension: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const getDefaultSpecificationName = (type: string): string => {
|
|
25
|
+
switch (type) {
|
|
26
|
+
case 'asyncapi':
|
|
27
|
+
return 'AsyncAPI';
|
|
28
|
+
case 'openapi':
|
|
29
|
+
return 'OpenAPI';
|
|
30
|
+
case 'graphql':
|
|
31
|
+
return 'GraphQL';
|
|
32
|
+
default:
|
|
33
|
+
return 'Specification';
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
interface LegacySpecificationFormat {
|
|
38
|
+
asyncapiPath?: string;
|
|
39
|
+
openapiPath?: string;
|
|
40
|
+
graphqlPath?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const processSpecifications = (
|
|
44
|
+
specifications: SpecificationInput[] | LegacySpecificationFormat | undefined
|
|
45
|
+
): ProcessedSpecification[] => {
|
|
46
|
+
const specs: SpecificationInput[] = Array.isArray(specifications) ? [...specifications] : [];
|
|
47
|
+
|
|
48
|
+
// Handle legacy object format
|
|
49
|
+
if (specifications && !Array.isArray(specifications)) {
|
|
50
|
+
if (specifications.asyncapiPath) {
|
|
51
|
+
specs.push({ type: 'asyncapi', path: specifications.asyncapiPath, name: 'AsyncAPI' });
|
|
52
|
+
}
|
|
53
|
+
if (specifications.openapiPath) {
|
|
54
|
+
specs.push({ type: 'openapi', path: specifications.openapiPath, name: 'OpenAPI' });
|
|
55
|
+
}
|
|
56
|
+
if (specifications.graphqlPath) {
|
|
57
|
+
specs.push({ type: 'graphql', path: specifications.graphqlPath, name: 'GraphQL' });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return specs.map((spec) => ({
|
|
62
|
+
...spec,
|
|
63
|
+
name: spec.name || getDefaultSpecificationName(spec.type),
|
|
64
|
+
filename: path.basename(spec.path),
|
|
65
|
+
filenameWithoutExtension: path.basename(spec.path, path.extname(spec.path)),
|
|
66
|
+
}));
|
|
67
|
+
};
|
|
4
68
|
|
|
5
69
|
export const getPreviousVersion = (version: string, versions: string[]) => {
|
|
6
70
|
const index = versions.indexOf(version);
|
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.7.
|
|
9
|
+
"version": "3.7.2",
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"@ai-sdk/react": "^3.0.17",
|
|
24
24
|
"@astrojs/markdown-remark": "^6.3.10",
|
|
25
25
|
"@astrojs/mdx": "^4.3.13",
|
|
26
|
-
"@astrojs/node": "^9.5.
|
|
26
|
+
"@astrojs/node": "^9.5.2",
|
|
27
27
|
"@astrojs/react": "^4.4.2",
|
|
28
|
-
"@astrojs/rss": "^4.0.
|
|
28
|
+
"@astrojs/rss": "^4.0.15",
|
|
29
29
|
"@astrojs/tailwind": "^6.0.2",
|
|
30
30
|
"@asyncapi/avro-schema-parser": "3.0.24",
|
|
31
31
|
"@asyncapi/parser": "3.4.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@tanstack/react-table": "^8.17.3",
|
|
56
56
|
"@xyflow/react": "^12.3.6",
|
|
57
57
|
"ai": "^6.0.17",
|
|
58
|
-
"astro": "^5.16.
|
|
58
|
+
"astro": "^5.16.10",
|
|
59
59
|
"astro-compress": "^2.3.8",
|
|
60
60
|
"astro-expressive-code": "^0.41.3",
|
|
61
61
|
"astro-seo": "^0.8.4",
|