@eventcatalog/core 5.0.0-beta.2 → 5.0.0-beta.21
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/LICENSE +234 -134
- package/README.md +5 -5
- package/dist/analytics/analytics.cjs +18 -3
- package/dist/analytics/analytics.d.cts +1 -1
- package/dist/analytics/analytics.d.ts +1 -1
- package/dist/analytics/analytics.js +3 -2
- package/dist/analytics/log-build.cjs +71 -35
- package/dist/analytics/log-build.js +5 -4
- package/dist/analytics/telemetry.cjs +42 -0
- package/dist/analytics/telemetry.d.cts +20 -0
- package/dist/analytics/telemetry.d.ts +20 -0
- package/dist/analytics/telemetry.js +8 -0
- package/dist/catalog-runtime.cjs +1 -1
- package/dist/{chunk-TQLRG35T.js → chunk-AWOO63ME.js} +71 -45
- package/dist/chunk-CPW6G62V.js +17 -0
- package/dist/{chunk-KIDISU6X.js → chunk-I7F7CPDC.js} +56 -33
- package/dist/{chunk-HGRFXTFC.js → chunk-OVWDUANF.js} +1 -1
- package/dist/{chunk-62EV4IJA.js → chunk-RIRRWE7D.js} +1 -1
- package/dist/chunk-S5GWAUG5.js +29 -0
- package/dist/{chunk-4J2ZM2HX.js → chunk-W6KDW6ZC.js} +1 -1
- package/dist/{chunk-XARPIXWF.js → chunk-WHYRRLHE.js} +9 -3
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +245 -131
- package/dist/eventcatalog.config.d.cts +59 -1
- package/dist/eventcatalog.config.d.ts +59 -1
- package/dist/eventcatalog.js +45 -15
- package/dist/federation/federate.js +2 -2
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
- package/dist/runtime-dependencies/@hocuspocus__provider.mjs +3 -0
- package/dist/runtime-dependencies/@hocuspocus__server.mjs +3 -0
- package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +3 -0
- package/dist/runtime-dependencies/@mcp-b__webmcp-polyfill.mjs +3 -0
- package/dist/runtime-dependencies/htmlparser2.mjs +3 -0
- package/dist/runtime-dependencies/manifest.json +13 -1
- package/dist/runtime-dependencies/react-hotkeys-hook.mjs +3 -0
- package/dist/runtime-dependencies/react-syntax-highlighter__dist__cjs__create-element.js.mjs +3 -0
- package/dist/runtime-dependencies/y-protocols.mjs +3 -0
- package/dist/runtime-dependencies/y-protocols__awareness.mjs +3 -0
- package/dist/runtime-dependencies/yjs.mjs +3 -0
- package/dist/utils/child-process-signals.cjs +53 -0
- package/dist/utils/child-process-signals.d.cts +19 -0
- package/dist/utils/child-process-signals.d.ts +19 -0
- package/dist/utils/child-process-signals.js +6 -0
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/dist/utils/license-status.cjs +71 -45
- package/dist/utils/license-status.d.cts +14 -5
- package/dist/utils/license-status.d.ts +14 -5
- package/dist/utils/license-status.js +1 -1
- package/eventcatalog/astro.config.mjs +23 -1
- package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
- package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
- package/eventcatalog/integrations/storage.mjs +20 -0
- package/eventcatalog/integrations/studio-server.mjs +32 -0
- package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
- package/eventcatalog/src/components/Grids/utils.tsx +0 -23
- package/eventcatalog/src/components/Header.astro +3 -3
- package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.tsx +6 -1
- package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +42 -3
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
- package/eventcatalog/src/components/NavigationProgress.astro +7 -0
- package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +12 -4
- package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
- package/eventcatalog/src/components/Search/SearchModal.tsx +2 -10
- package/eventcatalog/src/components/Search/search-utils.ts +26 -0
- package/eventcatalog/src/components/Seo.astro +5 -1
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +53 -83
- package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +131 -0
- package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
- package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +4 -62
- package/eventcatalog/src/components/Tables/Discover/columns.tsx +17 -150
- package/eventcatalog/src/components/Tables/Resources/ResourcesTable.tsx +425 -0
- package/eventcatalog/src/components/Tables/RowActionsMenu.tsx +157 -0
- package/eventcatalog/src/components/Tables/TablePagination.tsx +82 -0
- package/eventcatalog/src/features/ai/chat-api.ts +3 -3
- package/eventcatalog/src/features/auth/error.astro +4 -2
- package/eventcatalog/src/features/auth/login.astro +2 -1
- package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +10 -0
- package/eventcatalog/src/features/auth/unauthorized.astro +1 -1
- package/eventcatalog/src/features/fields/pages/fields.astro +1 -1
- package/eventcatalog/src/features/integrations/eventcatalog-features.ts +34 -1
- package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
- package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +47 -0
- package/eventcatalog/src/features/mcp/apps/canvas/styles.css +24 -0
- package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +278 -0
- package/eventcatalog/src/features/mcp/apps/generated/canvas.html +272 -0
- package/eventcatalog/src/features/mcp/apps/generated/viewer.html +77 -76
- package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +2 -1
- package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -3
- package/eventcatalog/src/features/mcp/mcp-server.ts +36 -5
- package/eventcatalog/src/features/server/start.ts +25 -0
- package/eventcatalog/src/features/storage/database.ts +88 -0
- package/eventcatalog/src/features/storage/migrations.ts +85 -0
- package/eventcatalog/src/features/storage/sql/database.sql +5 -0
- package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
- package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
- package/eventcatalog/src/features/storage/sql.ts +28 -0
- package/eventcatalog/src/features/studio/agent-choreography.ts +384 -0
- package/eventcatalog/src/features/studio/agents.ts +28 -0
- package/eventcatalog/src/features/studio/api/canvases-api.ts +219 -0
- package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
- package/eventcatalog/src/features/studio/api/client.ts +40 -0
- package/eventcatalog/src/features/studio/canvas-actions.ts +540 -0
- package/eventcatalog/src/features/studio/canvas-preview.ts +52 -0
- package/eventcatalog/src/features/studio/catalog-resources.ts +155 -0
- package/eventcatalog/src/features/studio/catalog.ts +173 -0
- package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +518 -0
- package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +118 -0
- package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +434 -0
- package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +143 -0
- package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +65 -0
- package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
- package/eventcatalog/src/features/studio/components/JoinForm.tsx +109 -0
- package/eventcatalog/src/features/studio/components/LeftPanel.tsx +203 -0
- package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
- package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
- package/eventcatalog/src/features/studio/components/Presence.tsx +293 -0
- package/eventcatalog/src/features/studio/components/ShareDialog.tsx +396 -0
- package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
- package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +2091 -0
- package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
- package/eventcatalog/src/features/studio/document-name.ts +7 -0
- package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
- package/eventcatalog/src/features/studio/hooks/presence-sender.ts +63 -0
- package/eventcatalog/src/features/studio/hooks/presence-store.ts +119 -0
- package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +353 -0
- package/eventcatalog/src/features/studio/hooks/use-comments.ts +54 -0
- package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +109 -0
- package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +782 -0
- package/eventcatalog/src/features/studio/identity.ts +59 -0
- package/eventcatalog/src/features/studio/limits.ts +2 -0
- package/eventcatalog/src/features/studio/pages/[id].astro +69 -0
- package/eventcatalog/src/features/studio/pages/index.astro +784 -0
- package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +27 -0
- package/eventcatalog/src/features/studio/server/canvas-mcp.ts +495 -0
- package/eventcatalog/src/features/studio/server/runtime.ts +498 -0
- package/eventcatalog/src/features/studio/server/sign-in.ts +18 -0
- package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
- package/eventcatalog/src/features/studio/server/storage.ts +90 -0
- package/eventcatalog/src/features/studio/server/tool-icons.ts +30 -0
- package/eventcatalog/src/features/studio/tool-sync.ts +75 -0
- package/eventcatalog/src/layouts/BaseLayout.astro +4 -4
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +13 -7
- package/eventcatalog/src/pages/_index.astro +1 -1
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +52 -63
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +34 -46
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +11 -29
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_ResourcesPage.astro +79 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +32 -14
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +63 -58
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +2 -62
- package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +1 -1
- package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +13 -0
- package/eventcatalog/src/pages/index.astro +1 -1
- package/eventcatalog/src/pages/schemas/explorer/index.astro +1 -1
- package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +1 -1
- package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +24 -42
- package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +2 -0
- package/eventcatalog/src/stores/sidebar-store/builders/message.ts +2 -0
- package/eventcatalog/src/stores/sidebar-store/builders/service.ts +23 -23
- package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +13 -0
- package/eventcatalog/src/stores/sidebar-store/builders/system.ts +18 -27
- package/eventcatalog/src/stores/sidebar-store/state.ts +5 -0
- package/eventcatalog/src/styles/tailwind.css +2 -2
- package/eventcatalog/src/types/react-syntax-highlighter.d.ts +10 -0
- package/eventcatalog/src/utils/collections/channels.ts +56 -1
- package/eventcatalog/src/utils/collections/flows.ts +50 -1
- package/eventcatalog/src/utils/collections/message-connections.ts +60 -0
- package/eventcatalog/src/utils/collections/message-triggers.ts +2 -1
- package/eventcatalog/src/utils/collections/resource-owners.ts +193 -0
- package/eventcatalog/src/utils/collections/resources.ts +310 -0
- package/eventcatalog/src/utils/collections/util.ts +31 -0
- package/eventcatalog/src/utils/feature.ts +8 -0
- package/eventcatalog/src/utils/git.ts +11 -5
- package/eventcatalog/src/utils/link-validation.ts +25 -19
- package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +3 -1
- package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
- package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +0 -2
- package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +20 -1
- package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
- package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
- package/eventcatalog/src/utils/page-title.ts +15 -0
- package/eventcatalog/src/utils/syntax-highlighter-renderer.ts +64 -0
- package/eventcatalog/src/utils/trial.ts +1 -1
- package/package.json +22 -16
- package/dist/runtime-dependencies/shelljs.mjs +0 -3
- package/eventcatalog/public/studio-bg.png +0 -0
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +0 -838
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +0 -344
- package/eventcatalog/src/components/Grids/SystemGrid.tsx +0 -185
- package/eventcatalog/src/components/Grids/components.tsx +0 -170
- package/eventcatalog/src/components/Grids/message-link.ts +0 -38
- package/eventcatalog/src/components/Studio/StudioPageModal.tsx +0 -150
- package/eventcatalog/src/components/Tables/SystemResources/SystemResourcesTable.tsx +0 -294
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_resources.spec.ts +0 -65
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_resources.ts +0 -46
- package/eventcatalog/src/pages/studio.astro +0 -316
- package/dist/{chunk-AYNZXUB5.js → chunk-KHACCTW2.js} +3 -3
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
import { applyAwarenessUpdate, encodeAwarenessUpdate, type Awareness } from 'y-protocols/awareness';
|
|
3
|
+
import type { Status } from './hooks/use-studio-flow';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Syncing a canvas through MCP tool calls, for when a view can't open the collaboration WebSocket
|
|
7
|
+
* (e.g. an MCP App inside a chat whose sandbox blocks it). Each call sends the server what it doesn't have
|
|
8
|
+
* yet (by its state vector) and our presence, and gets back what we don't have and everyone's presence.
|
|
9
|
+
* Slower than the WebSocket (it polls), but works wherever the chat can call the MCP server's tools.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type SyncRequest = {
|
|
13
|
+
canvasId: string;
|
|
14
|
+
clientId: number;
|
|
15
|
+
/** What we have, so the server sends only what we're missing */
|
|
16
|
+
stateVector: string;
|
|
17
|
+
/** What we have that the server doesn't (as of its last state vector) */
|
|
18
|
+
update: string;
|
|
19
|
+
/** Our presence (name, pointer, selection...) */
|
|
20
|
+
awareness: string;
|
|
21
|
+
};
|
|
22
|
+
export type SyncResponse = { update: string; stateVector: string; awareness?: string };
|
|
23
|
+
export type ToolSync = (request: SyncRequest) => Promise<SyncResponse>;
|
|
24
|
+
|
|
25
|
+
// Binary Yjs updates travel as base64 in tool arguments and results (atob/btoa exist in browsers and Node)
|
|
26
|
+
export const toBase64 = (bytes: Uint8Array) => {
|
|
27
|
+
let text = '';
|
|
28
|
+
for (let i = 0; i < bytes.length; i += 0x8000) text += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
29
|
+
return btoa(text);
|
|
30
|
+
};
|
|
31
|
+
export const fromBase64 = (text: string) => Uint8Array.from(atob(text), (char) => char.charCodeAt(0));
|
|
32
|
+
|
|
33
|
+
/** Applied with this origin, so undo (which tracks local changes) leaves other people's changes alone */
|
|
34
|
+
const REMOTE = Symbol('tool-sync');
|
|
35
|
+
|
|
36
|
+
export function startToolSync(
|
|
37
|
+
doc: Y.Doc,
|
|
38
|
+
awareness: Awareness,
|
|
39
|
+
{
|
|
40
|
+
canvasId,
|
|
41
|
+
sync,
|
|
42
|
+
onStatus,
|
|
43
|
+
intervalMs = 1000,
|
|
44
|
+
}: { canvasId: string; sync: ToolSync; onStatus: (status: Status) => void; intervalMs?: number }
|
|
45
|
+
) {
|
|
46
|
+
let serverVector: Uint8Array | undefined;
|
|
47
|
+
let stopped = false;
|
|
48
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
49
|
+
|
|
50
|
+
const tick = async () => {
|
|
51
|
+
try {
|
|
52
|
+
const response = await sync({
|
|
53
|
+
canvasId,
|
|
54
|
+
clientId: doc.clientID,
|
|
55
|
+
stateVector: toBase64(Y.encodeStateVector(doc)),
|
|
56
|
+
update: toBase64(Y.encodeStateAsUpdate(doc, serverVector)),
|
|
57
|
+
awareness: toBase64(encodeAwarenessUpdate(awareness, [doc.clientID])),
|
|
58
|
+
});
|
|
59
|
+
if (stopped) return;
|
|
60
|
+
Y.applyUpdate(doc, fromBase64(response.update), REMOTE);
|
|
61
|
+
serverVector = fromBase64(response.stateVector);
|
|
62
|
+
if (response.awareness) applyAwarenessUpdate(awareness, fromBase64(response.awareness), REMOTE);
|
|
63
|
+
onStatus('connected');
|
|
64
|
+
} catch {
|
|
65
|
+
if (!stopped) onStatus('disconnected');
|
|
66
|
+
}
|
|
67
|
+
if (!stopped) timer = setTimeout(tick, intervalMs);
|
|
68
|
+
};
|
|
69
|
+
void tick();
|
|
70
|
+
|
|
71
|
+
return () => {
|
|
72
|
+
stopped = true;
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -6,12 +6,12 @@ import NavigationProgress from '../components/NavigationProgress.astro';
|
|
|
6
6
|
import config from '@config';
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
|
-
title
|
|
9
|
+
/** The page's own title; the catalog's title follows it (the homepage has none) */
|
|
10
|
+
title?: string;
|
|
10
11
|
description?: string;
|
|
11
|
-
ogTitle?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const { title, description
|
|
14
|
+
const { title, description } = Astro.props;
|
|
15
15
|
const catalogTheme = config.theme || 'default';
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -50,7 +50,7 @@ const catalogTheme = config.theme || 'default';
|
|
|
50
50
|
document.addEventListener('astro:page-load', applyTheme);
|
|
51
51
|
})();
|
|
52
52
|
</script>
|
|
53
|
-
<SEO title={title} description={description}
|
|
53
|
+
<SEO title={title} description={description} />
|
|
54
54
|
<AnalyticsHead />
|
|
55
55
|
<slot name="head" />
|
|
56
56
|
</head>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
interface Props {
|
|
3
|
-
title
|
|
3
|
+
/** The page's own title; the catalog's title follows it (the homepage has none) */
|
|
4
|
+
title?: string;
|
|
4
5
|
sidebar?: boolean;
|
|
5
6
|
description?: string;
|
|
6
7
|
showNestedSideBar?: boolean;
|
|
@@ -52,7 +53,7 @@ import { buildUrl } from '@utils/url-builder';
|
|
|
52
53
|
import { getQueries } from '@utils/collections/queries';
|
|
53
54
|
import { getFirstNavigationPage } from '@features/custom-documentation/utils/custom-docs';
|
|
54
55
|
|
|
55
|
-
import { isSSR } from '@utils/feature';
|
|
56
|
+
import { isCanvasEnabled, isSSR } from '@utils/feature';
|
|
56
57
|
|
|
57
58
|
const customDocs = await getCollection('customPages');
|
|
58
59
|
const firstCustomDocsPage = customDocs.length > 0 ? await getFirstNavigationPage() : null;
|
|
@@ -241,7 +242,7 @@ const builtinNavigationItems: Record<string, Omit<VerticalNavigationItem, 'curre
|
|
|
241
242
|
'/studio': {
|
|
242
243
|
id: '/studio',
|
|
243
244
|
matchId: '/studio',
|
|
244
|
-
label: '
|
|
245
|
+
label: 'Studio',
|
|
245
246
|
icon: 'SquareDashedMousePointer',
|
|
246
247
|
href: buildUrl('/studio'),
|
|
247
248
|
},
|
|
@@ -292,7 +293,12 @@ const builtinNavigationItems: Record<string, Omit<VerticalNavigationItem, 'curre
|
|
|
292
293
|
const defaultNavigationGroups: NavigationGroupConfig[] = [
|
|
293
294
|
{
|
|
294
295
|
id: 'main',
|
|
295
|
-
items: [
|
|
296
|
+
items: [
|
|
297
|
+
{ id: '/' },
|
|
298
|
+
{ id: '/docs/custom', visible: customDocs.length > 0 },
|
|
299
|
+
// Collaborative canvases, where they're on
|
|
300
|
+
{ id: '/studio', visible: isCanvasEnabled() },
|
|
301
|
+
],
|
|
296
302
|
},
|
|
297
303
|
{
|
|
298
304
|
id: 'browse',
|
|
@@ -323,7 +329,6 @@ const isNavigationItemCurrent = (item: Pick<VerticalNavigationItem, 'href' | 'ma
|
|
|
323
329
|
return (
|
|
324
330
|
pathname === itemPath ||
|
|
325
331
|
(pathname.includes('/docs') && !pathname.includes('/docs/custom')) ||
|
|
326
|
-
pathname.includes('/architecture/') ||
|
|
327
332
|
pathname.includes('/visualiser') ||
|
|
328
333
|
(pathname.includes('/schemas') && !pathname.includes('/schemas/explorer') && !pathname.includes('/schemas/fields'))
|
|
329
334
|
);
|
|
@@ -372,6 +377,8 @@ const resolveNavigationItem = (item: NavigationItemConfig): VerticalNavigationIt
|
|
|
372
377
|
if (item.visible === false) return null;
|
|
373
378
|
|
|
374
379
|
const builtinId = navigationBuiltinAliases[item.id] || item.id;
|
|
380
|
+
// Studio is only there where canvases are on
|
|
381
|
+
if (builtinId === '/studio' && !item.href && !isCanvasEnabled()) return null;
|
|
375
382
|
const builtin = builtinNavigationItems[builtinId];
|
|
376
383
|
const href = item.href ? normalizeNavigationHref(item.href) : builtin?.href;
|
|
377
384
|
|
|
@@ -424,7 +431,7 @@ const { title, description, showNestedSideBar = true, showHeader = true, reduced
|
|
|
424
431
|
const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/docs', true)];
|
|
425
432
|
---
|
|
426
433
|
|
|
427
|
-
<BaseLayout title={
|
|
434
|
+
<BaseLayout title={title} description={description}>
|
|
428
435
|
<Fragment slot="head">
|
|
429
436
|
<script is:inline define:vars={{ verticalNavAutoCollapsePaths }}>
|
|
430
437
|
(() => {
|
|
@@ -889,7 +896,6 @@ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/do
|
|
|
889
896
|
return (
|
|
890
897
|
pathname === targetPath ||
|
|
891
898
|
(pathname.includes('/docs') && !pathname.includes('/docs/custom')) ||
|
|
892
|
-
pathname.includes('/architecture/') ||
|
|
893
899
|
pathname.includes('/visualiser') ||
|
|
894
900
|
(pathname.includes('/schemas') && !pathname.includes('/schemas/explorer') && !pathname.includes('/schemas/fields'))
|
|
895
901
|
);
|
|
@@ -134,7 +134,7 @@ const exploreCards: ExploreCard[] = [
|
|
|
134
134
|
];
|
|
135
135
|
---
|
|
136
136
|
|
|
137
|
-
<VerticalSideBarLayout
|
|
137
|
+
<VerticalSideBarLayout showNestedSideBar={showNestedSideBar}>
|
|
138
138
|
<div class="ec-home min-h-screen">
|
|
139
139
|
<!-- Hero -->
|
|
140
140
|
<header class="ec-hero">
|
|
@@ -1,88 +1,77 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { Page } from './_index.data';
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const mocks = vi.hoisted(() => ({
|
|
5
|
+
isSSR: vi.fn(() => false),
|
|
5
6
|
getDomains: vi.fn(),
|
|
6
7
|
getServices: vi.fn(),
|
|
7
8
|
getSystems: vi.fn(),
|
|
9
|
+
getOwnersWithResources: vi.fn(),
|
|
8
10
|
}));
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
const loaders: Record<string, () => Promise<unknown>> = {
|
|
13
|
+
domains: () => mocks.getDomains(),
|
|
14
|
+
services: () => mocks.getServices(),
|
|
15
|
+
systems: () => mocks.getSystems(),
|
|
16
|
+
};
|
|
13
17
|
|
|
14
|
-
vi.mock('@utils/
|
|
15
|
-
vi.mock('@utils/
|
|
16
|
-
vi.mock('@utils/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
systems: (...args: unknown[]) => collectionMocks.getSystems(...args),
|
|
21
|
-
},
|
|
18
|
+
vi.mock('@utils/feature', () => ({ isSSR: mocks.isSSR }));
|
|
19
|
+
vi.mock('@utils/url-builder', () => ({ buildUrl: (path: string) => path }));
|
|
20
|
+
vi.mock('@utils/collections/resource-owners', () => ({
|
|
21
|
+
createResourcesCatalog: () => ({}),
|
|
22
|
+
loadResourceOwners: (type: string) => loaders[type](),
|
|
23
|
+
getOwnersWithResources: mocks.getOwnersWithResources,
|
|
22
24
|
}));
|
|
23
25
|
|
|
24
|
-
const
|
|
25
|
-
collection: 'domains',
|
|
26
|
-
data: { id: 'ordering', name: 'Ordering', version: '1.0.0' },
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const service = {
|
|
30
|
-
collection: 'services',
|
|
31
|
-
data: { id: 'order-service', name: 'Order Service', version: '1.0.0' },
|
|
32
|
-
};
|
|
26
|
+
const entry = (collection: string, id: string, version = '1.0.0') => ({ collection, data: { id, name: id, version } });
|
|
33
27
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
const ordering = entry('domains', 'ordering');
|
|
29
|
+
const orderService = entry('services', 'order-service');
|
|
30
|
+
const orderServiceV0 = entry('services', 'order-service', '0.1.0');
|
|
31
|
+
const cartSystem = entry('systems', 'cart-system');
|
|
38
32
|
|
|
39
|
-
describe('architecture page
|
|
33
|
+
describe('architecture page redirects', () => {
|
|
40
34
|
beforeEach(() => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
mocks.isSSR.mockReturnValue(false);
|
|
36
|
+
mocks.getDomains.mockReset().mockResolvedValue([ordering]);
|
|
37
|
+
mocks.getServices.mockReset().mockResolvedValue([orderService, orderServiceV0]);
|
|
38
|
+
mocks.getSystems.mockReset().mockResolvedValue([cartSystem]);
|
|
39
|
+
// Only the latest order service and the cart system have a Resources page.
|
|
40
|
+
mocks.getOwnersWithResources.mockReset().mockImplementation(async (type: string) => {
|
|
41
|
+
if (type === 'services') return [orderService];
|
|
42
|
+
if (type === 'systems') return [cartSystem];
|
|
43
|
+
return [];
|
|
44
|
+
});
|
|
44
45
|
});
|
|
45
46
|
|
|
46
|
-
it('
|
|
47
|
+
it('redirects every architecture page to its Resources page, or the docs page when it has no Resources page', async () => {
|
|
47
48
|
const paths = await Page.getStaticPaths();
|
|
48
49
|
|
|
49
|
-
expect(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
params: { type: 'systems', id: 'order-management-system', version: '1.0.0' },
|
|
58
|
-
}),
|
|
59
|
-
expect.objectContaining({
|
|
60
|
-
params: { type: 'services', id: 'order-service', version: '1.0.0' },
|
|
61
|
-
}),
|
|
62
|
-
])
|
|
63
|
-
);
|
|
50
|
+
expect(
|
|
51
|
+
paths.map(({ params, props }) => [`/architecture/${params.type}/${params.id}/${params.version}`, props.redirectPath])
|
|
52
|
+
).toEqual([
|
|
53
|
+
['/architecture/services/order-service/1.0.0', '/docs/services/order-service/1.0.0/resources'],
|
|
54
|
+
['/architecture/services/order-service/0.1.0', '/docs/services/order-service/0.1.0'],
|
|
55
|
+
['/architecture/domains/ordering/1.0.0', '/docs/domains/ordering/1.0.0'],
|
|
56
|
+
['/architecture/systems/cart-system/1.0.0', '/docs/systems/cart-system/1.0.0/resources'],
|
|
57
|
+
]);
|
|
64
58
|
});
|
|
65
59
|
|
|
66
|
-
it('
|
|
67
|
-
|
|
68
|
-
props: {},
|
|
69
|
-
params: { type: 'domains', id: 'ordering', version: '1.0.0' },
|
|
70
|
-
} as any);
|
|
60
|
+
it('works out the redirect when the page is requested on a server', async () => {
|
|
61
|
+
mocks.isSSR.mockReturnValue(true);
|
|
71
62
|
|
|
72
|
-
expect(
|
|
63
|
+
await expect(
|
|
64
|
+
Page.getData({ params: { type: 'systems', id: 'cart-system', version: '1.0.0' }, props: {} } as any)
|
|
65
|
+
).resolves.toMatchObject({ redirectPath: '/docs/systems/cart-system/1.0.0/resources' });
|
|
73
66
|
});
|
|
74
67
|
|
|
75
|
-
it('
|
|
76
|
-
|
|
77
|
-
props: {},
|
|
78
|
-
params: { type: 'systems', id: 'order-management-system', version: '1.0.0' },
|
|
79
|
-
} as any);
|
|
80
|
-
|
|
81
|
-
expect(collectionMocks.getSystems).toHaveBeenCalledWith({ enrichServices: true });
|
|
82
|
-
});
|
|
68
|
+
it('returns not found for resources without an architecture page', async () => {
|
|
69
|
+
mocks.isSSR.mockReturnValue(true);
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
71
|
+
await expect(
|
|
72
|
+
Page.getData({ params: { type: 'events', id: 'order-created', version: '1.0.0' }, props: {} } as any)
|
|
73
|
+
).rejects.toMatchObject({
|
|
74
|
+
status: 404,
|
|
75
|
+
});
|
|
87
76
|
});
|
|
88
77
|
});
|
|
@@ -1,53 +1,48 @@
|
|
|
1
1
|
import { isSSR } from '@utils/feature';
|
|
2
2
|
import { HybridPage } from '@utils/page-loaders/hybrid-page';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import {
|
|
4
|
+
createResourcesCatalog,
|
|
5
|
+
getOwnersWithResources,
|
|
6
|
+
loadResourceOwners,
|
|
7
|
+
type ResourcesCatalog,
|
|
8
|
+
} from '@utils/collections/resource-owners';
|
|
9
|
+
import type { ResourceOwner } from '@utils/collections/resources';
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
// The architecture overview pages were replaced by the Resources pages. These routes stay so
|
|
12
|
+
// old links keep working: each one redirects to the resource's Resources page, or to its docs
|
|
13
|
+
// page when it has nothing to list.
|
|
14
|
+
const architecturePageTypes = ['services', 'domains', 'systems'] as const;
|
|
15
|
+
type ArchitecturePageType = (typeof architecturePageTypes)[number];
|
|
9
16
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* systems must hydrate those messages (collection + name). `pageDataLoader`
|
|
13
|
-
* uses the cheaper unenriched path used by docs/sidebar.
|
|
14
|
-
*/
|
|
15
|
-
export const loadArchitectureItems = (type: PageTypes) => {
|
|
16
|
-
if (type === 'domains') {
|
|
17
|
-
return getDomains({ enrichServices: true });
|
|
18
|
-
}
|
|
17
|
+
const isArchitecturePageType = (type: string): type is ArchitecturePageType =>
|
|
18
|
+
architecturePageTypes.includes(type as ArchitecturePageType);
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const key = (item: ResourceOwner) => `${item.data.id}:${item.data.version}`;
|
|
21
|
+
|
|
22
|
+
const getRedirectPaths = async (type: ArchitecturePageType, catalog: ResourcesCatalog = createResourcesCatalog()) => {
|
|
23
|
+
const [items, ownersWithResources] = await Promise.all([loadResourceOwners(type), getOwnersWithResources(type, catalog)]);
|
|
24
|
+
const hasResourcesPage = new Set(ownersWithResources.map(key));
|
|
23
25
|
|
|
24
|
-
return
|
|
26
|
+
return items.map((item) => {
|
|
27
|
+
const docsPath = `/docs/${type}/${item.data.id}/${item.data.version}`;
|
|
28
|
+
return { item, redirectPath: hasResourcesPage.has(key(item)) ? `${docsPath}/resources` : docsPath };
|
|
29
|
+
});
|
|
25
30
|
};
|
|
26
31
|
|
|
27
|
-
/**
|
|
28
|
-
* Documentation page class for all collection types with versioning
|
|
29
|
-
*/
|
|
30
32
|
export class Page extends HybridPage {
|
|
31
33
|
static async getStaticPaths() {
|
|
32
34
|
if (isSSR()) {
|
|
33
35
|
return [];
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
const
|
|
38
|
+
const catalog = createResourcesCatalog();
|
|
39
|
+
const redirects: Awaited<ReturnType<typeof getRedirectPaths>>[] = [];
|
|
40
|
+
for (const type of architecturePageTypes) redirects.push(await getRedirectPaths(type, catalog));
|
|
37
41
|
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
params: {
|
|
41
|
-
|
|
42
|
-
id: item.data.id,
|
|
43
|
-
version: item.data.version,
|
|
44
|
-
},
|
|
45
|
-
props: {
|
|
46
|
-
type: architecturePageTypes[index],
|
|
47
|
-
...item,
|
|
48
|
-
// Not everything needs the body of the page itself.
|
|
49
|
-
body: undefined,
|
|
50
|
-
},
|
|
42
|
+
return architecturePageTypes.flatMap((type, index) =>
|
|
43
|
+
redirects[index].map(({ item, redirectPath }) => ({
|
|
44
|
+
params: { type, id: item.data.id, version: item.data.version },
|
|
45
|
+
props: { type, data: { id: item.data.id, name: item.data.name, version: item.data.version }, redirectPath },
|
|
51
46
|
}))
|
|
52
47
|
);
|
|
53
48
|
}
|
|
@@ -55,23 +50,16 @@ export class Page extends HybridPage {
|
|
|
55
50
|
protected static async fetchData(params: any) {
|
|
56
51
|
const { type, id, version } = params;
|
|
57
52
|
|
|
58
|
-
if (!type || !id || !version || !
|
|
53
|
+
if (!type || !id || !version || !isArchitecturePageType(type)) {
|
|
59
54
|
return null;
|
|
60
55
|
}
|
|
61
56
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
// Find the specific item by id and version
|
|
65
|
-
const item = items.find((i) => i.data.id === id && i.data.version === version);
|
|
66
|
-
|
|
67
|
-
if (!item) {
|
|
57
|
+
const match = (await getRedirectPaths(type)).find(({ item }) => item.data.id === id && item.data.version === version);
|
|
58
|
+
if (!match) {
|
|
68
59
|
return null;
|
|
69
60
|
}
|
|
70
61
|
|
|
71
|
-
return {
|
|
72
|
-
type,
|
|
73
|
-
...item,
|
|
74
|
-
};
|
|
62
|
+
return { type, data: { id, name: match.item.data.name, version }, redirectPath: match.redirectPath };
|
|
75
63
|
}
|
|
76
64
|
|
|
77
65
|
protected static createNotFoundResponse(): Response {
|
|
@@ -1,38 +1,20 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
|
|
2
|
+
// The architecture overview pages were replaced by the Resources pages; this route only
|
|
3
|
+
// redirects so old links keep working.
|
|
4
|
+
import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
|
|
5
|
+
import { isSSR } from '@utils/feature';
|
|
6
|
+
import { buildUrl } from '@utils/url-builder';
|
|
8
7
|
import { Page } from './_index.data';
|
|
9
8
|
|
|
10
9
|
export const prerender = Page.prerender;
|
|
11
10
|
export const getStaticPaths = Page.getStaticPaths;
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
let domain = props;
|
|
16
|
-
|
|
17
|
-
const pageTitle = `${props.type} | ${props.data.name}`.replace(/^\w/, (c) => c.toUpperCase());
|
|
18
|
-
|
|
19
|
-
const type = props.type;
|
|
12
|
+
const { data, redirectPath } = await Page.getData(Astro);
|
|
13
|
+
const redirectUrl = buildUrl(redirectPath);
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
if (isSSR()) {
|
|
16
|
+
return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`, 301);
|
|
17
|
+
}
|
|
22
18
|
---
|
|
23
19
|
|
|
24
|
-
<
|
|
25
|
-
<main class="flex docs-layout min-h-full bg-[rgb(var(--ec-page-bg))]">
|
|
26
|
-
<div class="flex docs-layout w-full">
|
|
27
|
-
<div class="w-full min-w-0 lg:mr-2 xl:pr-12 py-8 bg-[rgb(var(--ec-page-bg))]">
|
|
28
|
-
{type === 'domains' && <DomainGrid domain={domain} client:load />}
|
|
29
|
-
{type === 'systems' && <SystemGrid system={props} client:load />}
|
|
30
|
-
{
|
|
31
|
-
(type === 'agents' || type === 'services') && (
|
|
32
|
-
<MessageGrid service={props} specifications={specifications} client:load />
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
</main>
|
|
38
|
-
</VerticalSideBarLayout>
|
|
20
|
+
<LatestVersionRedirect title={`${data.name} | Resources`} redirectUrl={redirectUrl} />
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
|
|
3
|
+
import { buildUrl } from '@utils/url-builder';
|
|
4
|
+
import { isVisualiserEnabled } from '@utils/feature';
|
|
5
|
+
import {
|
|
6
|
+
ResourcesTable,
|
|
7
|
+
type ResourceItem,
|
|
8
|
+
} from '@components/Tables/Resources/ResourcesTable';
|
|
9
|
+
import { getCollection } from 'astro:content';
|
|
10
|
+
import {
|
|
11
|
+
getDocsPath,
|
|
12
|
+
getFavorite,
|
|
13
|
+
getResourceGroups,
|
|
14
|
+
getResourceRelationship,
|
|
15
|
+
getSchemaPath,
|
|
16
|
+
getVisualiserPath,
|
|
17
|
+
type ResourceOwner,
|
|
18
|
+
type ResourceOwnerCollection,
|
|
19
|
+
} from '@utils/collections/resources';
|
|
20
|
+
|
|
21
|
+
// Shared by the generic Resources route and the services route.
|
|
22
|
+
interface Props {
|
|
23
|
+
resourceOwner: ResourceOwner;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { resourceOwner } = Astro.props;
|
|
27
|
+
const { data } = resourceOwner;
|
|
28
|
+
const ownerCollection = resourceOwner.collection as ResourceOwnerCollection;
|
|
29
|
+
|
|
30
|
+
// Only show resources directly attached to the owner. Domain child resources are not
|
|
31
|
+
// flattened, so services inside a child system remain on that system's page.
|
|
32
|
+
const resourceGroups = getResourceGroups(data, ownerCollection, resourceOwner);
|
|
33
|
+
|
|
34
|
+
const withBaseUrl = (path: string | undefined) => (path ? buildUrl(path) : undefined);
|
|
35
|
+
|
|
36
|
+
// Messages with an entry in the schemas collection have a schema page.
|
|
37
|
+
const schemaKeys = new Set(
|
|
38
|
+
(await getCollection('schemas')).map(
|
|
39
|
+
({ data: schema }) => `${schema.message.collectionName}:${schema.message.id}:${schema.message.version}`
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const resources: ResourceItem[] = resourceGroups.flatMap(({ collection, items }) =>
|
|
44
|
+
items.map((item) => ({
|
|
45
|
+
collection,
|
|
46
|
+
id: item.data.id,
|
|
47
|
+
name: item.data.name || item.data.id,
|
|
48
|
+
version: item.data.version,
|
|
49
|
+
summary: item.data.summary,
|
|
50
|
+
icon: item.data?.styles?.icon,
|
|
51
|
+
href: buildUrl(getDocsPath(collection, item)),
|
|
52
|
+
visualiserHref: withBaseUrl(getVisualiserPath(collection, item, isVisualiserEnabled())),
|
|
53
|
+
schemaHref: withBaseUrl(getSchemaPath(collection, item, schemaKeys)),
|
|
54
|
+
favorite: getFavorite(collection, item),
|
|
55
|
+
relationship: getResourceRelationship(data, ownerCollection, collection, item, resourceOwner),
|
|
56
|
+
}))
|
|
57
|
+
);
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
<VerticalSideBarLayout title={`${data.name} | Resources`}>
|
|
61
|
+
<style is:global>
|
|
62
|
+
:root[data-embedded='true'] #resources-page {
|
|
63
|
+
height: 100dvh !important;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
66
|
+
{/* Like the discover pages: pull the table out of the content gutter and fill the viewport so it can paginate. */}
|
|
67
|
+
<main
|
|
68
|
+
id="resources-page"
|
|
69
|
+
class="flex h-full min-h-0 flex-col overflow-hidden bg-[rgb(var(--ec-page-bg))]"
|
|
70
|
+
style="margin-left: calc(var(--ec-app-content-padding-left, 5rem) * -1); margin-right: calc(var(--ec-app-content-padding-right, 5rem) * -1); height: calc(100dvh - 60px);"
|
|
71
|
+
>
|
|
72
|
+
<ResourcesTable
|
|
73
|
+
resources={resources}
|
|
74
|
+
title={`${data.name} Resources`}
|
|
75
|
+
description={`${resources.length} ${resources.length === 1 ? 'resource' : 'resources'} mapped to ${data.name}.`}
|
|
76
|
+
client:only="react"
|
|
77
|
+
/>
|
|
78
|
+
</main>
|
|
79
|
+
</VerticalSideBarLayout>
|
|
@@ -1,28 +1,46 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { Page, ServicePage } from './_index.data';
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const mocks = vi.hoisted(() => ({
|
|
5
|
+
getOwnersWithResources: vi.fn(),
|
|
6
|
+
findOwnerWithResources: vi.fn(),
|
|
7
7
|
}));
|
|
8
8
|
|
|
9
|
-
vi.mock('@utils/
|
|
10
|
-
vi.mock('@utils/collections/
|
|
9
|
+
vi.mock('@utils/feature', () => ({ isSSR: () => false }));
|
|
10
|
+
vi.mock('@utils/collections/resource-owners', () => ({
|
|
11
|
+
RESOURCE_OWNER_COLLECTIONS: ['domains', 'systems', 'services', 'flows', 'channels', 'events', 'commands', 'queries'],
|
|
12
|
+
createResourcesCatalog: () => ({}),
|
|
13
|
+
getOwnersWithResources: mocks.getOwnersWithResources,
|
|
14
|
+
findOwnerWithResources: mocks.findOwnerWithResources,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const owner = (collection: string, id: string) => ({ collection, data: { id, version: '1.0.0' } });
|
|
11
18
|
|
|
12
|
-
describe('Resources
|
|
19
|
+
describe('Resources routes', () => {
|
|
13
20
|
beforeEach(() => {
|
|
14
|
-
|
|
21
|
+
mocks.getOwnersWithResources.mockReset().mockImplementation(async (type: string) => [owner(type, `${type}-owner`)]);
|
|
22
|
+
mocks.findOwnerWithResources.mockReset();
|
|
15
23
|
});
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
// /docs/services/[id]/[docType]/[docId] would otherwise catch /docs/services/{id}/{version}/resources,
|
|
26
|
+
// so services get their own route and the generic route leaves them out.
|
|
27
|
+
it('gives every owner type but services a path on the generic route', async () => {
|
|
28
|
+
const paths = await Page.getStaticPaths();
|
|
19
29
|
|
|
20
|
-
expect(
|
|
30
|
+
expect(paths.map(({ params }) => params.type)).toEqual(['domains', 'systems', 'flows', 'channels', 'events', 'commands', 'queries']);
|
|
31
|
+
expect(paths[0]).toEqual({ params: { type: 'domains', id: 'domains-owner', version: '1.0.0' }, props: owner('domains', 'domains-owner') });
|
|
21
32
|
});
|
|
22
33
|
|
|
23
|
-
it('
|
|
24
|
-
await
|
|
34
|
+
it('gives services their own route without a type param', async () => {
|
|
35
|
+
const paths = await ServicePage.getStaticPaths();
|
|
36
|
+
|
|
37
|
+
expect(paths).toEqual([{ params: { id: 'services-owner', version: '1.0.0' }, props: owner('services', 'services-owner') }]);
|
|
38
|
+
});
|
|
25
39
|
|
|
26
|
-
|
|
40
|
+
it('returns not found for types without a Resources page on a server request', async () => {
|
|
41
|
+
await expect(Page.getData({ params: { type: 'entities', id: 'Order', version: '1.0.0' }, props: {} } as any)).rejects.toMatchObject({
|
|
42
|
+
status: 404,
|
|
43
|
+
});
|
|
44
|
+
expect(mocks.findOwnerWithResources).not.toHaveBeenCalled();
|
|
27
45
|
});
|
|
28
46
|
});
|