@eventcatalog/core 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/README.md +11 -0
- package/bin/eventcatalog.js +125 -0
- package/components/BreadCrumbs.tsx +50 -0
- package/components/ContentView.tsx +127 -0
- package/components/Footer.tsx +38 -0
- package/components/Grids/EventGrid.tsx +89 -0
- package/components/Grids/ServiceGrid.tsx +70 -0
- package/components/Header.tsx +59 -0
- package/components/Mdx/Admonition.tsx +33 -0
- package/components/Mdx/Examples.tsx +77 -0
- package/components/Mermaid/index.tsx +47 -0
- package/components/NotFound/index.tsx +44 -0
- package/components/Sidebars/EventSidebar.tsx +202 -0
- package/components/Sidebars/ServiceSidebar.tsx +198 -0
- package/components/SyntaxHighlighter.tsx +34 -0
- package/hooks/EventCatalog.tsx +35 -0
- package/lib/__tests__/assets/events/AddedItemToCart/index.md +19 -0
- package/lib/__tests__/assets/events/EmailSent/index.md +15 -0
- package/lib/__tests__/assets/events/EventWithSchemaAndExamples/examples/Basic.cs +31 -0
- package/lib/__tests__/assets/events/EventWithSchemaAndExamples/examples/Basic.js +1 -0
- package/lib/__tests__/assets/events/EventWithSchemaAndExamples/index.md +8 -0
- package/lib/__tests__/assets/events/EventWithSchemaAndExamples/schema.json +4 -0
- package/lib/__tests__/assets/events/EventWithVersions/index.md +10 -0
- package/lib/__tests__/assets/events/EventWithVersions/versioned/0.0.1/index.md +10 -0
- package/lib/__tests__/assets/services/Email Platform/index.md +17 -0
- package/lib/__tests__/events.spec.ts +294 -0
- package/lib/__tests__/file-reader.spec.ts +57 -0
- package/lib/__tests__/graphs.spec.ts +62 -0
- package/lib/__tests__/services.spec.ts +144 -0
- package/lib/events.ts +221 -0
- package/lib/file-reader.ts +52 -0
- package/lib/graphs.ts +33 -0
- package/lib/services.ts +72 -0
- package/next-env.d.ts +5 -0
- package/next.config.js +3 -0
- package/package.json +52 -0
- package/pages/_app.tsx +49 -0
- package/pages/api/event/[name]/download.js +25 -0
- package/pages/events/[name]/logs.tsx +170 -0
- package/pages/events/[name]/v/[version].tsx +19 -0
- package/pages/events/[name].tsx +139 -0
- package/pages/events.tsx +227 -0
- package/pages/index.tsx +47 -0
- package/pages/overview.tsx +80 -0
- package/pages/services/[name].tsx +102 -0
- package/pages/services.tsx +123 -0
- package/pages/users/[id].tsx +83 -0
- package/postcss.config.js +6 -0
- package/public/favicon.ico +0 -0
- package/public/logo-random.svg +114 -0
- package/public/logo.svg +44 -0
- package/public/opengraph.png +0 -0
- package/scripts/__tests__/assets/eventcatalog.config.js +33 -0
- package/scripts/__tests__/generate.spec.ts +39 -0
- package/scripts/generate.js +28 -0
- package/styles/Home.module.css +116 -0
- package/styles/globals.css +48 -0
- package/tailwind.config.js +16 -0
- package/tsconfig.json +38 -0
- package/types/index.ts +7 -0
- package/utils/random-bg.ts +13 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import SyntaxHighlighter from '@/components/SyntaxHighlighter';
|
|
3
|
+
|
|
4
|
+
function classNames(...classes) {
|
|
5
|
+
return classes.filter(Boolean).join(' ');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface ExampleProps {
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
examples: any;
|
|
12
|
+
showLineNumbers?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Examples({
|
|
16
|
+
title = 'Examples',
|
|
17
|
+
description,
|
|
18
|
+
examples = [],
|
|
19
|
+
showLineNumbers,
|
|
20
|
+
}: ExampleProps) {
|
|
21
|
+
const tabs = examples.map((example, index) => ({
|
|
22
|
+
name: example.name || `Example ${index + 1}`,
|
|
23
|
+
content: example.snippet,
|
|
24
|
+
description: example.description,
|
|
25
|
+
langugage: example.langugage,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
const [selectedTab, setSelectedTab] = useState(tabs[0]);
|
|
29
|
+
|
|
30
|
+
const handleTabSelection = (tab: string) => {
|
|
31
|
+
setSelectedTab(tab);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="my-5 examples">
|
|
36
|
+
<div className="">
|
|
37
|
+
<h2 className="text-lg font-medium text-gray-700 underline">{title}</h2>
|
|
38
|
+
{description && <p className="text-md font-medium text-gray-700">{description}</p>}
|
|
39
|
+
|
|
40
|
+
<div>
|
|
41
|
+
<nav className="-mb-px flex" aria-label="Tabs">
|
|
42
|
+
{tabs.map((tab) => {
|
|
43
|
+
const isSelected = tab.name === selectedTab.name;
|
|
44
|
+
return (
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
key={tab.name}
|
|
48
|
+
onClick={() => handleTabSelection(tab)}
|
|
49
|
+
className={classNames(
|
|
50
|
+
isSelected
|
|
51
|
+
? 'border-green-500 text-green-600 selected'
|
|
52
|
+
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300',
|
|
53
|
+
'whitespace-nowrap py-4 border-b-2 font-medium text-sm important:no-underline px-8 hover:bg-gray-50 transition'
|
|
54
|
+
)}
|
|
55
|
+
aria-current={isSelected ? 'page' : undefined}
|
|
56
|
+
>
|
|
57
|
+
{tab.name}
|
|
58
|
+
<span className="block text-xs mt-">({tab.langugage})</span>
|
|
59
|
+
</button>
|
|
60
|
+
);
|
|
61
|
+
})}
|
|
62
|
+
</nav>
|
|
63
|
+
<div className="my-4">
|
|
64
|
+
<SyntaxHighlighter language={selectedTab.langugage} showLineNumbers={showLineNumbers}>
|
|
65
|
+
{selectedTab.content}
|
|
66
|
+
</SyntaxHighlighter>
|
|
67
|
+
{selectedTab.langugage && (
|
|
68
|
+
<span className="-mb-2 block text-xs text-right font-bold">{selectedTab.name}</span>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default Examples;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import mermaid from 'mermaid';
|
|
3
|
+
import { Service, Event } from '@eventcatalog/types';
|
|
4
|
+
import { buildMermaidFlowChartForEvent, buildMermaidFlowChartForService } from '@/lib/graphs';
|
|
5
|
+
|
|
6
|
+
mermaid.initialize({
|
|
7
|
+
startOnLoad: true,
|
|
8
|
+
theme: 'forest',
|
|
9
|
+
securityLevel: 'loose',
|
|
10
|
+
flowchart: {
|
|
11
|
+
useMaxWidth: false,
|
|
12
|
+
width: '1000px',
|
|
13
|
+
},
|
|
14
|
+
themeCSS: `
|
|
15
|
+
.node {
|
|
16
|
+
filter: drop-shadow( 3px 3px 2px rgba(0, 0, 0, .2))
|
|
17
|
+
}
|
|
18
|
+
.mermaid svg {
|
|
19
|
+
width: 10000px
|
|
20
|
+
}
|
|
21
|
+
.node rect {
|
|
22
|
+
fill: white
|
|
23
|
+
}
|
|
24
|
+
`,
|
|
25
|
+
fontFamily: 'Fira Code',
|
|
26
|
+
width: '100%',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
interface MermaidProps {
|
|
30
|
+
data: Event | Service;
|
|
31
|
+
source: 'event' | 'service';
|
|
32
|
+
rootNodeColor?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Mermaid({ data, source = 'event', rootNodeColor }: MermaidProps) {
|
|
36
|
+
const mermaidChart =
|
|
37
|
+
source === 'event'
|
|
38
|
+
? buildMermaidFlowChartForEvent(data as Event, rootNodeColor)
|
|
39
|
+
: buildMermaidFlowChartForService(data as Service, rootNodeColor);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
mermaid.contentLoaded();
|
|
43
|
+
}, []);
|
|
44
|
+
return <div className="mermaid">{mermaidChart}</div>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default Mermaid;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DocumentAddIcon } from '@heroicons/react/solid';
|
|
2
|
+
import url from 'url';
|
|
3
|
+
|
|
4
|
+
interface NotFoundProps {
|
|
5
|
+
type: 'service' | 'event';
|
|
6
|
+
name: string;
|
|
7
|
+
editUrl: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function Example(props: NotFoundProps) {
|
|
11
|
+
const { type, name, editUrl } = props;
|
|
12
|
+
|
|
13
|
+
const urlToAddPage = url.resolve(editUrl, `${type}/${name}`);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<main className="min-h-full bg-cover bg-top sm:bg-top h-screen">
|
|
17
|
+
<div className="max-w-7xl mx-auto px-4 py-16 text-center sm:px-6 sm:py-24 lg:px-8 lg:py-48">
|
|
18
|
+
<p className="text-sm font-semibold text-gray-700 text-opacity-50 uppercase tracking-wide blur-xl">
|
|
19
|
+
Failed to find {type}
|
|
20
|
+
</p>
|
|
21
|
+
<h1 className="mt-2 text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl">
|
|
22
|
+
Missing Documentation
|
|
23
|
+
</h1>
|
|
24
|
+
<p className="mt-2 text-lg font-medium text-gray-700 text-opacity-50">
|
|
25
|
+
Documentation for {type} <span className="underline">{name}</span> is missing!
|
|
26
|
+
</p>
|
|
27
|
+
<p className="mt-4 text-xs text-gray-400">
|
|
28
|
+
Help the eco-system and add the documentation for others ❤️{' '}
|
|
29
|
+
</p>
|
|
30
|
+
<div className="mt-12">
|
|
31
|
+
<a
|
|
32
|
+
href={urlToAddPage}
|
|
33
|
+
target="_blank"
|
|
34
|
+
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-black bg-opacity-75 "
|
|
35
|
+
rel="noreferrer"
|
|
36
|
+
>
|
|
37
|
+
<DocumentAddIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
|
|
38
|
+
Add <span className="underline px-1">{name}</span> documentation
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</main>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Link from 'next/link';
|
|
3
|
+
import { CubeIcon, DownloadIcon } from '@heroicons/react/outline';
|
|
4
|
+
import type { Event } from '@eventcatalog/types';
|
|
5
|
+
import fileDownload from 'js-file-download';
|
|
6
|
+
import { useUser } from '@/hooks/EventCatalog';
|
|
7
|
+
|
|
8
|
+
interface EventSideBarProps {
|
|
9
|
+
event: Event;
|
|
10
|
+
loadedVersion?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function EventSideBar({ event, loadedVersion }: EventSideBarProps) {
|
|
14
|
+
const { getUserById } = useUser();
|
|
15
|
+
|
|
16
|
+
const { name: eventName, owners, producers, consumers, historicVersions } = event;
|
|
17
|
+
|
|
18
|
+
const handleDownload = async () => {
|
|
19
|
+
try {
|
|
20
|
+
const res = await fetch(`/api/event/${event.name}/download`);
|
|
21
|
+
if (res.status === 404) throw new Error('Failed to find file');
|
|
22
|
+
const data = await res.text();
|
|
23
|
+
fileDownload(data, event.name);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
// TODO: Maybe better error experince
|
|
26
|
+
console.error(error);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<aside className="hidden xl:block xl:pl-8">
|
|
32
|
+
<h2 className="sr-only">Details</h2>
|
|
33
|
+
|
|
34
|
+
<div className="pt-6 py-6 space-y-8">
|
|
35
|
+
<div>
|
|
36
|
+
<h2 className="text-sm font-medium text-gray-500">
|
|
37
|
+
<CubeIcon className="h-5 w-5 text-green-400 inline-block mr-2" aria-hidden="true" />
|
|
38
|
+
Producers
|
|
39
|
+
</h2>
|
|
40
|
+
<ul className="mt-2 leading-8">
|
|
41
|
+
{producers.map((producer) => (
|
|
42
|
+
<li className="inline mr-1" key={producer}>
|
|
43
|
+
<Link href={`/services/${producer}`}>
|
|
44
|
+
<a className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5">
|
|
45
|
+
<div className="absolute flex-shrink-0 flex items-center justify-center">
|
|
46
|
+
<span
|
|
47
|
+
className="h-1.5 w-1.5 rounded-full bg-green-500 animate animate-pulse"
|
|
48
|
+
aria-hidden="true"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
<div className="ml-3.5 text-sm font-medium text-gray-900">{producer}</div>
|
|
52
|
+
</a>
|
|
53
|
+
</Link>
|
|
54
|
+
</li>
|
|
55
|
+
))}
|
|
56
|
+
</ul>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<div className="border-t border-gray-200 py-6 space-y-8">
|
|
60
|
+
<div>
|
|
61
|
+
<h2 className="text-sm font-medium text-gray-500">
|
|
62
|
+
<CubeIcon className="h-5 w-5 text-indigo-400 inline-block mr-2" aria-hidden="true" />
|
|
63
|
+
Consumers
|
|
64
|
+
</h2>
|
|
65
|
+
<ul className="mt-2 leading-8">
|
|
66
|
+
{consumers.map((consumer) => (
|
|
67
|
+
<li className="inline" key={consumer}>
|
|
68
|
+
<Link href={`/services/${consumer}`}>
|
|
69
|
+
<a
|
|
70
|
+
href="#"
|
|
71
|
+
className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5"
|
|
72
|
+
>
|
|
73
|
+
<div className="absolute flex-shrink-0 flex items-center justify-center">
|
|
74
|
+
<span
|
|
75
|
+
className="h-1.5 w-1.5 rounded-full bg-indigo-500 animate animate-pulse"
|
|
76
|
+
aria-hidden="true"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
<div className="ml-3.5 text-sm font-medium text-gray-900">{consumer}</div>
|
|
80
|
+
</a>
|
|
81
|
+
</Link>
|
|
82
|
+
</li>
|
|
83
|
+
))}
|
|
84
|
+
</ul>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
{historicVersions.length > 0 && (
|
|
88
|
+
<div className="border-t border-gray-200 py-6">
|
|
89
|
+
<div>
|
|
90
|
+
<h2 className="text-sm font-medium text-gray-500">Event Versions</h2>
|
|
91
|
+
<ul className="mt-2 leading-8 text-left text-blue-500">
|
|
92
|
+
<li className="text-sm inline ">
|
|
93
|
+
<Link href={`/events/${eventName}`}>
|
|
94
|
+
<a>
|
|
95
|
+
<span
|
|
96
|
+
className={`inline-flex mr-2 items-center px-2.5 py-0.5 rounded-full text-xs font-medium -top-0.5 relative ${
|
|
97
|
+
loadedVersion === 'latest'
|
|
98
|
+
? 'bg-blue-400 text-white shadow-md font-bold underline'
|
|
99
|
+
: 'bg-blue-100 text-blue-800'
|
|
100
|
+
}`}
|
|
101
|
+
>
|
|
102
|
+
Latest
|
|
103
|
+
</span>
|
|
104
|
+
</a>
|
|
105
|
+
</Link>
|
|
106
|
+
</li>
|
|
107
|
+
|
|
108
|
+
{historicVersions.map((version) => {
|
|
109
|
+
const isLoadedVersion = loadedVersion === version;
|
|
110
|
+
const styles = isLoadedVersion
|
|
111
|
+
? 'bg-blue-400 text-white shadow-md font-bold underline'
|
|
112
|
+
: 'bg-blue-100 text-blue-800';
|
|
113
|
+
return (
|
|
114
|
+
<li className="text-sm inline ">
|
|
115
|
+
<Link href={`/events/${eventName}/v/${version}`}>
|
|
116
|
+
<a>
|
|
117
|
+
<span
|
|
118
|
+
className={`inline-flex mr-2 items-center px-2.5 py-0.5 rounded-full text-xs font-medium -top-0.5 relative ${styles}`}
|
|
119
|
+
>
|
|
120
|
+
v{version}
|
|
121
|
+
</span>
|
|
122
|
+
</a>
|
|
123
|
+
</Link>
|
|
124
|
+
</li>
|
|
125
|
+
);
|
|
126
|
+
})}
|
|
127
|
+
</ul>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
{/* <div className="border-t border-gray-200 py-6 space-y-8">
|
|
132
|
+
<div>
|
|
133
|
+
<h2 className="text-sm font-medium text-gray-500">
|
|
134
|
+
<MapIcon className="h-5 w-5 text-red-400 inline-block mr-2" aria-hidden="true" />
|
|
135
|
+
Domains
|
|
136
|
+
</h2>
|
|
137
|
+
<ul role="list" className="mt-2 leading-8">
|
|
138
|
+
{domains.map((domain) => {
|
|
139
|
+
return (
|
|
140
|
+
<li className="inline" key={domain}>
|
|
141
|
+
<a
|
|
142
|
+
href="#"
|
|
143
|
+
className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5"
|
|
144
|
+
>
|
|
145
|
+
<div className="absolute flex-shrink-0 flex items-center justify-center">
|
|
146
|
+
<span className="h-1.5 w-1.5 rounded-full bg-red-500" aria-hidden="true" />
|
|
147
|
+
</div>
|
|
148
|
+
<div className="ml-3.5 text-sm font-medium text-gray-900">{domain}</div>
|
|
149
|
+
</a>{' '}
|
|
150
|
+
</li>
|
|
151
|
+
)
|
|
152
|
+
})}
|
|
153
|
+
</ul>
|
|
154
|
+
</div>
|
|
155
|
+
</div> */}
|
|
156
|
+
<div className="border-t border-gray-200 py-6 space-y-8">
|
|
157
|
+
<div>
|
|
158
|
+
<h2 className="text-sm font-medium text-gray-500">Event Owners</h2>
|
|
159
|
+
<ul className="mt-4 leading-8 space-y-2">
|
|
160
|
+
{owners.map((id) => {
|
|
161
|
+
const user = getUserById(id);
|
|
162
|
+
|
|
163
|
+
if (!user) return null;
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<li className="flex justify-start" key={id}>
|
|
167
|
+
<Link href={`/users/${id}`}>
|
|
168
|
+
<a className="flex items-center space-x-3">
|
|
169
|
+
<div className="flex-shrink-0">
|
|
170
|
+
<img className="h-5 w-5 rounded-full" src={user.avatarUrl} alt="" />
|
|
171
|
+
</div>
|
|
172
|
+
<div className="text-sm font-medium text-gray-900">{user.name}</div>
|
|
173
|
+
</a>
|
|
174
|
+
</Link>
|
|
175
|
+
</li>
|
|
176
|
+
);
|
|
177
|
+
})}
|
|
178
|
+
</ul>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
<div className="border-t border-gray-200 py-6 space-y-1">
|
|
182
|
+
<button
|
|
183
|
+
type="button"
|
|
184
|
+
className="hidden w-full md:inline-flex h-10 justify-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-200 bg-gray-800 hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-900"
|
|
185
|
+
onClick={() => handleDownload()}
|
|
186
|
+
>
|
|
187
|
+
<DownloadIcon className="-ml-1 mr-2 h-5 w-5 text-gray-200" aria-hidden="true" />
|
|
188
|
+
<span>Download Schema</span>
|
|
189
|
+
</button>
|
|
190
|
+
{historicVersions.length > 0 && (
|
|
191
|
+
<Link href={`/events/${eventName}/logs`}>
|
|
192
|
+
<a className="hidden w-full md:inline-flex h-10 justify-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-800 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-200">
|
|
193
|
+
<span>View Changes</span>
|
|
194
|
+
</a>
|
|
195
|
+
</Link>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
</aside>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export default EventSideBar;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Link from 'next/link';
|
|
3
|
+
import type { Service } from '@eventcatalog/types';
|
|
4
|
+
|
|
5
|
+
import { CubeIcon, TagIcon } from '@heroicons/react/outline';
|
|
6
|
+
import { useUser } from '@/hooks/EventCatalog';
|
|
7
|
+
import getBackgroundColor from '@/utils/random-bg';
|
|
8
|
+
|
|
9
|
+
const tailwindBgs = ['purple', 'pink', 'green', 'yellow', 'blue', 'indigo'];
|
|
10
|
+
|
|
11
|
+
interface ServiceSideBarProps {
|
|
12
|
+
service: Service;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function ServiceSidebar({ service }: ServiceSideBarProps) {
|
|
16
|
+
const { getUserById } = useUser();
|
|
17
|
+
|
|
18
|
+
const { owners, subscribes, publishes, repository, tags = [] } = service;
|
|
19
|
+
const { language, url: repositoryUrl } = repository;
|
|
20
|
+
|
|
21
|
+
let languages = [];
|
|
22
|
+
|
|
23
|
+
if (language) {
|
|
24
|
+
languages = Array.isArray(language) ? language : [language];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let trimmedUrl = '';
|
|
28
|
+
|
|
29
|
+
if (repositoryUrl) {
|
|
30
|
+
trimmedUrl = repositoryUrl.replace(/(^\w+:|^)\/\//, '');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<aside className="hidden xl:block xl:pl-8">
|
|
35
|
+
<h2 className="sr-only">Details</h2>
|
|
36
|
+
|
|
37
|
+
<div className="pt-6 py-6 space-y-8">
|
|
38
|
+
<div>
|
|
39
|
+
<h2 className="text-sm font-medium text-gray-500">
|
|
40
|
+
<CubeIcon className="h-5 w-5 text-indigo-400 inline-block mr-2" aria-hidden="true" />
|
|
41
|
+
Publishes Events ({publishes.length})
|
|
42
|
+
</h2>
|
|
43
|
+
<ul className="mt-2 leading-8">
|
|
44
|
+
{publishes.map((event) => (
|
|
45
|
+
<li className="inline" key={event.name}>
|
|
46
|
+
<Link href={`/events/${event.name}`}>
|
|
47
|
+
<a
|
|
48
|
+
href="#"
|
|
49
|
+
className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5"
|
|
50
|
+
>
|
|
51
|
+
<div className="absolute flex-shrink-0 flex items-center justify-center">
|
|
52
|
+
<span
|
|
53
|
+
className="h-1.5 w-1.5 rounded-full bg-indigo-500 animate animate-pulse"
|
|
54
|
+
aria-hidden="true"
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="ml-3.5 text-sm font-medium text-gray-900">{event.name}</div>
|
|
58
|
+
</a>
|
|
59
|
+
</Link>
|
|
60
|
+
</li>
|
|
61
|
+
))}
|
|
62
|
+
</ul>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
<div className="border-t border-gray-200 py-6 space-y-8">
|
|
66
|
+
<div>
|
|
67
|
+
<h2 className="text-sm font-medium text-gray-500">
|
|
68
|
+
<CubeIcon className="h-5 w-5 text-green-400 inline-block mr-2" aria-hidden="true" />
|
|
69
|
+
Subscribes to Events ({subscribes.length})
|
|
70
|
+
</h2>
|
|
71
|
+
<ul className="mt-2 leading-8">
|
|
72
|
+
{subscribes.map((event) => (
|
|
73
|
+
<li className="inline" key={event.name}>
|
|
74
|
+
<Link href={`/events/${event.name}`}>
|
|
75
|
+
<a className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5">
|
|
76
|
+
<div className="absolute flex-shrink-0 flex items-center justify-center">
|
|
77
|
+
<span
|
|
78
|
+
className="h-1.5 w-1.5 rounded-full bg-green-500 animate animate-pulse"
|
|
79
|
+
aria-hidden="true"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
<div className="ml-3.5 text-sm font-medium text-gray-900">{event.name}</div>
|
|
83
|
+
</a>
|
|
84
|
+
</Link>
|
|
85
|
+
</li>
|
|
86
|
+
))}
|
|
87
|
+
</ul>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div className="border-t border-gray-200 py-6 space-y-8">
|
|
92
|
+
<div>
|
|
93
|
+
<h2 className="text-sm font-medium text-gray-500">Service Owners ({owners.length})</h2>
|
|
94
|
+
<ul className="mt-4 leading-8 space-y-2">
|
|
95
|
+
{owners.map((owner) => {
|
|
96
|
+
const user = getUserById(owner);
|
|
97
|
+
|
|
98
|
+
if (!user) return null;
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<li className="flex justify-start" key={user.id}>
|
|
102
|
+
<Link href={`/users/${user.id}`}>
|
|
103
|
+
<a className="flex items-center space-x-3">
|
|
104
|
+
<div className="flex-shrink-0">
|
|
105
|
+
<img className="h-5 w-5 rounded-full" src={user.avatarUrl} alt="" />
|
|
106
|
+
</div>
|
|
107
|
+
<div className="text-sm font-medium text-gray-900">{user.name}</div>
|
|
108
|
+
</a>
|
|
109
|
+
</Link>
|
|
110
|
+
</li>
|
|
111
|
+
);
|
|
112
|
+
})}
|
|
113
|
+
</ul>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
{repository?.url && (
|
|
117
|
+
<div className="border-t border-gray-200 py-6 space-y-8">
|
|
118
|
+
<div className="space-y-3">
|
|
119
|
+
<h2 className="text-sm font-medium text-gray-500">Repository</h2>
|
|
120
|
+
<ul className=" leading-8 space-y-2">
|
|
121
|
+
<li className="flex justify-start">
|
|
122
|
+
<a
|
|
123
|
+
href={repository?.url}
|
|
124
|
+
target="_blank"
|
|
125
|
+
className="flex items-center space-x-3 text-blue-600 underline text-sm"
|
|
126
|
+
rel="noreferrer"
|
|
127
|
+
>
|
|
128
|
+
{trimmedUrl}
|
|
129
|
+
</a>
|
|
130
|
+
</li>
|
|
131
|
+
</ul>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
)}
|
|
135
|
+
{languages.length > 0 && (
|
|
136
|
+
<div className="border-t border-gray-200 py-6 space-y-8">
|
|
137
|
+
<div className="space-y-3">
|
|
138
|
+
<h2 className="text-sm font-medium text-gray-500">Language</h2>
|
|
139
|
+
{languages.map((value) => (
|
|
140
|
+
<div className="relative flex items-center mt-2">
|
|
141
|
+
<div className="absolute flex-shrink-0 flex items-center justify-center">
|
|
142
|
+
<span
|
|
143
|
+
className="h-2 w-2 rounded-full"
|
|
144
|
+
aria-hidden="true"
|
|
145
|
+
style={{ background: getBackgroundColor(value) }}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
<div className="ml-3.5 text-sm font-medium text-gray-900">{value}</div>
|
|
149
|
+
</div>
|
|
150
|
+
))}
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
{tags.length > 0 && (
|
|
155
|
+
<div className="border-t border-gray-200 py-6 space-y-8">
|
|
156
|
+
<div>
|
|
157
|
+
<h2 className="text-sm font-medium text-gray-500">
|
|
158
|
+
<TagIcon className="h-5 w-5 text-gray-400 inline-block mr-2" aria-hidden="true" />
|
|
159
|
+
Tags
|
|
160
|
+
</h2>
|
|
161
|
+
<div className="mt-3 space-y-2">
|
|
162
|
+
{tags.map(({ label, url }, index) => {
|
|
163
|
+
const color = tailwindBgs[index % tailwindBgs.length];
|
|
164
|
+
|
|
165
|
+
if (url) {
|
|
166
|
+
return (
|
|
167
|
+
<a
|
|
168
|
+
href={url}
|
|
169
|
+
className="inline-block underline"
|
|
170
|
+
target="_blank"
|
|
171
|
+
rel="noreferrer"
|
|
172
|
+
>
|
|
173
|
+
<span
|
|
174
|
+
className={`underline inline-block mr-2 items-center px-2.5 py-0.5 rounded-full text-xs font-medium -top-0.5 relative bg-${color}-100 text-${color}-800`}
|
|
175
|
+
>
|
|
176
|
+
{label}
|
|
177
|
+
</span>
|
|
178
|
+
</a>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<span
|
|
184
|
+
className={`inline-block mr-2 items-center px-2.5 py-0.5 rounded-full text-xs font-medium -top-0.5 relative bg-${color}-100 text-${color}-800`}
|
|
185
|
+
>
|
|
186
|
+
{label}
|
|
187
|
+
</span>
|
|
188
|
+
);
|
|
189
|
+
})}
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
)}
|
|
194
|
+
</aside>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export default ServiceSidebar;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import codeStyle from 'react-syntax-highlighter/dist/cjs/styles/prism/dracula';
|
|
4
|
+
import { Prism as PrismSyntaxHighlighter } from 'react-syntax-highlighter';
|
|
5
|
+
import copy from 'copy-text-to-clipboard';
|
|
6
|
+
|
|
7
|
+
function SyntaxHighlighter({ language, name = '', ...props }: any) {
|
|
8
|
+
const [showCopied, setShowCopied] = useState(false);
|
|
9
|
+
|
|
10
|
+
const handleCopyCode = () => {
|
|
11
|
+
copy(props.children);
|
|
12
|
+
setShowCopied(true);
|
|
13
|
+
setTimeout(() => setShowCopied(false), 2000);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const regex = /\\n/g;
|
|
17
|
+
return (
|
|
18
|
+
<div className="relative group">
|
|
19
|
+
<button
|
|
20
|
+
type="button"
|
|
21
|
+
onClick={handleCopyCode}
|
|
22
|
+
className="absolute top-2 right-5 text-sm bg-gray-700 text-white rounded-md py-1 px-4 transform transition opacity-0 group-hover:opacity-100"
|
|
23
|
+
>
|
|
24
|
+
{showCopied ? 'Copied' : 'Copy'}
|
|
25
|
+
</button>
|
|
26
|
+
<PrismSyntaxHighlighter style={codeStyle} language={language} {...props} wrapLines>
|
|
27
|
+
{props.children.replace(regex, '\n')}
|
|
28
|
+
</PrismSyntaxHighlighter>
|
|
29
|
+
{name && <span className="-mb-2 block text-xs text-right font-bold">{name}</span>}
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default SyntaxHighlighter;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { useContext, ReactNode } from 'react';
|
|
2
|
+
import type { EventCataLogConfig, User } from '@eventcatalog/types';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import defaultConfig from '../eventcatalog.config';
|
|
5
|
+
|
|
6
|
+
export const Context = React.createContext<EventCataLogConfig | null>(defaultConfig);
|
|
7
|
+
|
|
8
|
+
export function EventCatalogContextProvider({ children }: { children: ReactNode }): JSX.Element {
|
|
9
|
+
return <Context.Provider value={defaultConfig}>{children}</Context.Provider>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const useConfig = () => useContext<EventCataLogConfig>(Context);
|
|
13
|
+
|
|
14
|
+
export const useUser = () => {
|
|
15
|
+
const config = useConfig();
|
|
16
|
+
|
|
17
|
+
const getUserById = (id): User => {
|
|
18
|
+
const users = config.users || [];
|
|
19
|
+
return users.find((user) => user.id === id);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
getUserById,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const useUrl = () => {
|
|
28
|
+
const config = useConfig();
|
|
29
|
+
|
|
30
|
+
const getEditUrl = (url: string) => path.join(config.editUrl, url);
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
getEditUrl,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: AddedItemToCart
|
|
3
|
+
version: 0.0.1
|
|
4
|
+
summary: |
|
|
5
|
+
Holds information about the cusomer and product when they add an item to the cart.
|
|
6
|
+
producers:
|
|
7
|
+
- Shopping API
|
|
8
|
+
- Application API
|
|
9
|
+
consumers:
|
|
10
|
+
- Customer Portal
|
|
11
|
+
domains:
|
|
12
|
+
- Shopping
|
|
13
|
+
owners:
|
|
14
|
+
- dboyne
|
|
15
|
+
- mSmith
|
|
16
|
+
draft: true
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Testing
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: EmailSent
|
|
3
|
+
version: 0.0.1
|
|
4
|
+
summary: |
|
|
5
|
+
Tells us when an email has been sent
|
|
6
|
+
producers:
|
|
7
|
+
- Email Platform
|
|
8
|
+
owners:
|
|
9
|
+
- dboyne
|
|
10
|
+
- mSmith
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Duis mollis quam enim, feugiat porta mi porta non. In lacus nulla, gravida nec sagittis vel, sagittis id tellus. Vestibulum maximus velit eget massa pulvinar ornare. In vel libero nulla. Aliquam a leo risus. Donec bibendum velit non nulla sollicitudin lacinia. Vestibulum imperdiet nunc eget neque sagittis, eget volutpat purus ornare. Mauris malesuada finibus pretium. Vestibulum suscipit tortor sit amet dolor tempor cursus. Nunc ac felis accumsan.
|
|
14
|
+
|
|
15
|
+
<Mermaid />
|