@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
package/lib/events.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { serialize } from 'next-mdx-remote/serialize';
|
|
4
|
+
import type { Service, Event } from '@eventcatalog/types';
|
|
5
|
+
import compareVersions from 'compare-versions';
|
|
6
|
+
import * as Diff from 'diff';
|
|
7
|
+
import { MarkdownFile } from '@/types/index';
|
|
8
|
+
|
|
9
|
+
import { extentionToLanguageMap } from './file-reader';
|
|
10
|
+
|
|
11
|
+
import { getLastModifiedDateOfFile, getSchemaFromDir, readMarkdownFile } from '@/lib/file-reader';
|
|
12
|
+
|
|
13
|
+
const parseEventFrontMatterIntoEvent = (eventFrontMatter: any): Event => {
|
|
14
|
+
const { name, version, summary, producers = [], consumers = [], owners = [] } = eventFrontMatter;
|
|
15
|
+
return { name, version, summary, producers, consumers, owners };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const versionsForEvents = (pathToEvent) => {
|
|
19
|
+
const versionsDir = path.join(pathToEvent, 'versioned');
|
|
20
|
+
|
|
21
|
+
if (fs.existsSync(versionsDir)) {
|
|
22
|
+
const files = fs.readdirSync(versionsDir);
|
|
23
|
+
return files.sort(compareVersions).reverse();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return [];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const getLogsForEvent = (eventName) => {
|
|
30
|
+
const eventsDir = path.join(process.env.PROJECT_DIR, 'events');
|
|
31
|
+
const historicVersions = versionsForEvents(path.join(eventsDir, eventName));
|
|
32
|
+
|
|
33
|
+
const allVersions = historicVersions.map((version) => ({
|
|
34
|
+
version,
|
|
35
|
+
pathToDir: path.join(eventsDir, eventName, 'versioned', version),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
// Get the latest version
|
|
39
|
+
const { data: { version: latestVersion } = {} } = readMarkdownFile(
|
|
40
|
+
path.join(eventsDir, eventName, 'index.md')
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Add the current version to the list
|
|
44
|
+
allVersions.unshift({ version: latestVersion, pathToDir: path.join(eventsDir, eventName) });
|
|
45
|
+
|
|
46
|
+
const versions = allVersions.reduce((diffs, versionData, index) => {
|
|
47
|
+
const hasVersionToCompareToo = !!allVersions[index + 1];
|
|
48
|
+
const previousVersionData = allVersions[index + 1];
|
|
49
|
+
|
|
50
|
+
// Check if both files have the schema to compare against...
|
|
51
|
+
if (hasVersionToCompareToo) {
|
|
52
|
+
const { version, pathToDir } = versionData;
|
|
53
|
+
const { version: previousVersion, pathToDir: previousVersionPathToDir } = previousVersionData;
|
|
54
|
+
const schema = getSchemaFromDir(pathToDir);
|
|
55
|
+
const previousSchema = getSchemaFromDir(previousVersionPathToDir);
|
|
56
|
+
let changelog = null;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const { content } = readMarkdownFile(path.join(pathToDir, 'changelog.md'));
|
|
60
|
+
changelog = content;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
// nothing found it's OK.
|
|
63
|
+
console.log('No changelog found');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const comparision = {
|
|
67
|
+
versions: [previousVersion, version],
|
|
68
|
+
changelog: {
|
|
69
|
+
content: changelog,
|
|
70
|
+
},
|
|
71
|
+
value:
|
|
72
|
+
schema && previousSchema
|
|
73
|
+
? Diff.createTwoFilesPatch(
|
|
74
|
+
`schema.${schema.extension} (${previousVersion})`,
|
|
75
|
+
`schema.${previousSchema.extension} (${version})`,
|
|
76
|
+
previousSchema.snippet,
|
|
77
|
+
schema.snippet
|
|
78
|
+
)
|
|
79
|
+
: null,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
diffs.push(comparision);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return diffs;
|
|
86
|
+
}, []);
|
|
87
|
+
|
|
88
|
+
const parseChangeLogs = versions.map(async (version) => {
|
|
89
|
+
if (version.changelog.content) {
|
|
90
|
+
const mdxSource = await serialize(version.changelog.content);
|
|
91
|
+
return {
|
|
92
|
+
...version,
|
|
93
|
+
changelog: {
|
|
94
|
+
...version.changelog,
|
|
95
|
+
source: mdxSource,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return version;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const values = Promise.all(parseChangeLogs);
|
|
103
|
+
|
|
104
|
+
return values;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const getEventExamplesFromDir = (pathToExamples) => {
|
|
108
|
+
let examples;
|
|
109
|
+
|
|
110
|
+
// Get examples for directory
|
|
111
|
+
try {
|
|
112
|
+
const files = fs.readdirSync(pathToExamples);
|
|
113
|
+
examples = files.map((filename) => {
|
|
114
|
+
const content = fs.readFileSync(path.join(pathToExamples, filename), {
|
|
115
|
+
encoding: 'utf-8',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const extension = filename.split('.').pop();
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
name: filename,
|
|
122
|
+
snippet: content,
|
|
123
|
+
langugage: extentionToLanguageMap[extension],
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
} catch (error) {
|
|
127
|
+
examples = [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return examples;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const getAllEvents = (): Event[] => {
|
|
134
|
+
const eventsDir = path.join(process.env.PROJECT_DIR, 'events');
|
|
135
|
+
const folders = fs.readdirSync(eventsDir);
|
|
136
|
+
return folders.map((folder) => {
|
|
137
|
+
const { data } = readMarkdownFile(path.join(eventsDir, folder, 'index.md'));
|
|
138
|
+
const historicVersions = versionsForEvents(path.join(eventsDir, folder));
|
|
139
|
+
return {
|
|
140
|
+
...parseEventFrontMatterIntoEvent(data),
|
|
141
|
+
historicVersions,
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const getEventByName = async (
|
|
147
|
+
eventName: string,
|
|
148
|
+
version?: string
|
|
149
|
+
): Promise<{ event: Event; markdown: MarkdownFile }> => {
|
|
150
|
+
const eventsDir = path.join(process.env.PROJECT_DIR, 'events');
|
|
151
|
+
const eventDirectory = path.join(eventsDir, eventName);
|
|
152
|
+
let versionDirectory = null;
|
|
153
|
+
|
|
154
|
+
if (version) {
|
|
155
|
+
versionDirectory = path.join(eventsDir, eventName, 'versioned', version);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const directoryToLoadForEvent = version ? versionDirectory : eventDirectory;
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const { data, content } = readMarkdownFile(path.join(directoryToLoadForEvent, `index.md`));
|
|
162
|
+
const event = parseEventFrontMatterIntoEvent(data);
|
|
163
|
+
|
|
164
|
+
const mdxSource = await serialize(content);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
event: {
|
|
168
|
+
...event,
|
|
169
|
+
historicVersions: versionsForEvents(eventDirectory),
|
|
170
|
+
schema: getSchemaFromDir(directoryToLoadForEvent),
|
|
171
|
+
examples: getEventExamplesFromDir(path.join(directoryToLoadForEvent, `examples`)),
|
|
172
|
+
},
|
|
173
|
+
markdown: {
|
|
174
|
+
content,
|
|
175
|
+
source: mdxSource,
|
|
176
|
+
lastModifiedDate: getLastModifiedDateOfFile(path.join(directoryToLoadForEvent, `index.md`)),
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.log(error);
|
|
181
|
+
console.log('Failed to get event by name', eventName);
|
|
182
|
+
return Promise.reject();
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export const getUniqueServicesNamesFromEvents = (events: Event[]) => {
|
|
187
|
+
const allConsumersAndProducers = events.reduce((domains, event) => {
|
|
188
|
+
const { consumers = [], producers = [] } = event;
|
|
189
|
+
return domains.concat(consumers).concat(producers);
|
|
190
|
+
}, []);
|
|
191
|
+
|
|
192
|
+
const data = allConsumersAndProducers.map((service) => service);
|
|
193
|
+
|
|
194
|
+
// @ts-ignore
|
|
195
|
+
return [...new Set(data)];
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export const getAllEventsByOwnerId = async (ownerId) => {
|
|
199
|
+
const events = await getAllEvents();
|
|
200
|
+
return events.filter(({ owners = [] }) => owners.some((id) => id === ownerId));
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const getAllEventsThatHaveRelationshipWithService = (
|
|
204
|
+
service: Service,
|
|
205
|
+
events: Event[]
|
|
206
|
+
): { publishes: Event[]; subscribes: Event[] } => {
|
|
207
|
+
const relationshipsBetweenEvents = events.reduce(
|
|
208
|
+
(data, event) => {
|
|
209
|
+
const serviceSubscribesToEvent = event.consumers.some((id) => id === service.id);
|
|
210
|
+
const servicePublishesEvent = event.producers.some((id) => id === service.id);
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
publishes: servicePublishesEvent ? data.publishes.concat(event) : data.publishes,
|
|
214
|
+
subscribes: serviceSubscribesToEvent ? data.subscribes.concat(event) : data.subscribes,
|
|
215
|
+
};
|
|
216
|
+
},
|
|
217
|
+
{ publishes: [], subscribes: [] }
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
return relationshipsBetweenEvents;
|
|
221
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import type { Schema } from '@eventcatalog/types';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
|
|
6
|
+
// https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD
|
|
7
|
+
export const extentionToLanguageMap = {
|
|
8
|
+
cs: 'csharp',
|
|
9
|
+
js: 'javascript',
|
|
10
|
+
json: 'json',
|
|
11
|
+
yml: 'yml',
|
|
12
|
+
java: 'java',
|
|
13
|
+
pb: 'protobuf',
|
|
14
|
+
proto: 'protobuf',
|
|
15
|
+
thrift: 'thrift',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const readMarkdownFile = (pathToFile: string) => {
|
|
19
|
+
const file = fs.readFileSync(pathToFile, {
|
|
20
|
+
encoding: 'utf-8',
|
|
21
|
+
});
|
|
22
|
+
return matter(file);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const getSchemaFromDir = (pathToSchemaDir: string): Schema => {
|
|
26
|
+
try {
|
|
27
|
+
const files = fs.readdirSync(pathToSchemaDir);
|
|
28
|
+
|
|
29
|
+
// See if any schemas are in there, ignoring extension
|
|
30
|
+
const schemaFileName = files.find((fileName) => fileName.includes('schema'));
|
|
31
|
+
if (!schemaFileName) throw new Error('No schema found');
|
|
32
|
+
|
|
33
|
+
const schemaFile = fs.readFileSync(path.join(pathToSchemaDir, schemaFileName), 'utf-8');
|
|
34
|
+
const extension = schemaFileName.split('.').pop();
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
snippet: `${schemaFile}`,
|
|
38
|
+
language: extentionToLanguageMap[extension] || extension,
|
|
39
|
+
extension,
|
|
40
|
+
};
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const getLastModifiedDateOfFile = (filePath) => {
|
|
47
|
+
const stats = fs.statSync(filePath);
|
|
48
|
+
const lastModifiedDate = new Date(stats.mtime);
|
|
49
|
+
return `${lastModifiedDate.getFullYear()}/${
|
|
50
|
+
lastModifiedDate.getMonth() + 1
|
|
51
|
+
}/${lastModifiedDate.getDate()}`;
|
|
52
|
+
};
|
package/lib/graphs.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Event, Service } from '@eventcatalog/types';
|
|
2
|
+
|
|
3
|
+
const buildMermaid = (centerNode, leftNodes, rightNodes, rootNodeColor) => {
|
|
4
|
+
// mermaid does not work with spaces in nodes
|
|
5
|
+
const removeSpacesInNames = (nodes) => nodes.map((node) => node.replace(/ /g, '_'));
|
|
6
|
+
const lNodes = removeSpacesInNames(leftNodes);
|
|
7
|
+
const rNodes = removeSpacesInNames(rightNodes);
|
|
8
|
+
const nodeValue = centerNode.replace(/ /g, '_');
|
|
9
|
+
|
|
10
|
+
return `flowchart LR
|
|
11
|
+
${lNodes.map((node) => `${node}:::producer-->${nodeValue}:::event\n`).join('')}
|
|
12
|
+
classDef event stroke:${rootNodeColor},stroke-width: 4px;
|
|
13
|
+
classDef producer stroke:#75d7b6,stroke-width: 2px;
|
|
14
|
+
classDef consumer stroke:#818cf8,stroke-width: 2px;
|
|
15
|
+
${rNodes.map((node) => `${nodeValue}:::event-->${node}:::consumer\n`).join('')}
|
|
16
|
+
`;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const buildMermaidFlowChartForEvent = (
|
|
20
|
+
{ name: eventName, producers, consumers }: Event,
|
|
21
|
+
rootNodeColor = '#2563eb'
|
|
22
|
+
) => buildMermaid(eventName, producers, consumers, rootNodeColor);
|
|
23
|
+
|
|
24
|
+
export const buildMermaidFlowChartForService = (
|
|
25
|
+
{ publishes, subscribes, name: serviceName }: Service,
|
|
26
|
+
rootNodeColor = '#2563eb'
|
|
27
|
+
) =>
|
|
28
|
+
buildMermaid(
|
|
29
|
+
serviceName,
|
|
30
|
+
subscribes.map((s) => s.name),
|
|
31
|
+
publishes.map((p) => p.name),
|
|
32
|
+
rootNodeColor
|
|
33
|
+
);
|
package/lib/services.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { serialize } from 'next-mdx-remote/serialize';
|
|
4
|
+
import { Service } from '@eventcatalog/types';
|
|
5
|
+
import { readMarkdownFile, getLastModifiedDateOfFile } from '@/lib/file-reader';
|
|
6
|
+
import { MarkdownFile } from '../types/index';
|
|
7
|
+
|
|
8
|
+
import { getAllEvents, getAllEventsThatHaveRelationshipWithService } from '@/lib/events';
|
|
9
|
+
|
|
10
|
+
const buildService = (eventFrontMatter: any): Service => {
|
|
11
|
+
const { id, name, summary, owners = [], repository = {}, tags = [] } = eventFrontMatter;
|
|
12
|
+
return { id, name, summary, owners, repository, tags };
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const getAllServices = (): Service[] => {
|
|
16
|
+
const servicesDir = path.join(process.env.PROJECT_DIR, 'services');
|
|
17
|
+
|
|
18
|
+
const folders = fs.readdirSync(servicesDir);
|
|
19
|
+
const services = folders.map((folder) =>
|
|
20
|
+
readMarkdownFile(path.join(servicesDir, folder, 'index.md'))
|
|
21
|
+
);
|
|
22
|
+
const events = getAllEvents();
|
|
23
|
+
|
|
24
|
+
const parsedServices = services.map((frontMatter) => buildService(frontMatter.data));
|
|
25
|
+
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
return parsedServices.map((service) => ({
|
|
28
|
+
...service,
|
|
29
|
+
...getAllEventsThatHaveRelationshipWithService(service, events),
|
|
30
|
+
}));
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const getAllServicesByOwnerId = async (ownerId): Promise<Service[]> => {
|
|
34
|
+
const services = await getAllServices();
|
|
35
|
+
const servicesOwnedByUser = services.filter((service) =>
|
|
36
|
+
service.owners.some((id) => id === ownerId)
|
|
37
|
+
);
|
|
38
|
+
return servicesOwnedByUser.map((service) => ({
|
|
39
|
+
...service,
|
|
40
|
+
}));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const getServiceByName = async (
|
|
44
|
+
serviceName
|
|
45
|
+
): Promise<{ service: Service; markdown: MarkdownFile }> => {
|
|
46
|
+
try {
|
|
47
|
+
const servicesDir = path.join(process.env.PROJECT_DIR, 'services');
|
|
48
|
+
const serviceDirectory = path.join(servicesDir, serviceName);
|
|
49
|
+
const { data, content } = readMarkdownFile(path.join(serviceDirectory, `index.md`));
|
|
50
|
+
const service = buildService(data);
|
|
51
|
+
|
|
52
|
+
const events = getAllEvents();
|
|
53
|
+
|
|
54
|
+
const mdxSource = await serialize(content);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
// @ts-ignore
|
|
58
|
+
service: {
|
|
59
|
+
...service,
|
|
60
|
+
...getAllEventsThatHaveRelationshipWithService(service, events),
|
|
61
|
+
},
|
|
62
|
+
markdown: {
|
|
63
|
+
content,
|
|
64
|
+
lastModifiedDate: getLastModifiedDateOfFile(path.join(serviceDirectory, `index.md`)),
|
|
65
|
+
source: mdxSource,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.log('Failed to get service by name', serviceName);
|
|
70
|
+
return Promise.reject();
|
|
71
|
+
}
|
|
72
|
+
};
|
package/next-env.d.ts
ADDED
package/next.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eventcatalog/core",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"bin": {
|
|
8
|
+
"eventcatalog": "bin/eventcatalog.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "next dev",
|
|
12
|
+
"generate": "node scripts/generate.js",
|
|
13
|
+
"build": "next build",
|
|
14
|
+
"start": "next start",
|
|
15
|
+
"lint": "next lint"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@headlessui/react": "^1.4.2",
|
|
19
|
+
"@heroicons/react": "^1.0.5",
|
|
20
|
+
"@mdx-js/react": "^1.6.22",
|
|
21
|
+
"@tailwindcss/forms": "^0.3.4",
|
|
22
|
+
"@tailwindcss/line-clamp": "^0.2.2",
|
|
23
|
+
"@tailwindcss/typography": "^0.4.1",
|
|
24
|
+
"compare-versions": "^4.1.2",
|
|
25
|
+
"copy-text-to-clipboard": "^3.0.1",
|
|
26
|
+
"diff": "^5.0.0",
|
|
27
|
+
"diff2html": "^3.4.13",
|
|
28
|
+
"fs-extra": "^10.0.0",
|
|
29
|
+
"gray-matter": "^4.0.3",
|
|
30
|
+
"js-file-download": "^0.4.12",
|
|
31
|
+
"mermaid": "^8.13.4",
|
|
32
|
+
"next": "^12.0.5",
|
|
33
|
+
"next-mdx-remote": "^3.0.8",
|
|
34
|
+
"react": "17.0.2",
|
|
35
|
+
"react-dom": "17.0.2",
|
|
36
|
+
"react-force-graph-3d": "^1.21.10",
|
|
37
|
+
"react-syntax-highlighter": "^15.4.5"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@eventcatalog/types": "0.0.0",
|
|
41
|
+
"@types/react": "^17.0.36",
|
|
42
|
+
"autoprefixer": "^10.4.0",
|
|
43
|
+
"commander": "^8.3.0",
|
|
44
|
+
"eslint": "7.32.0",
|
|
45
|
+
"eslint-config-next": "12.0.4",
|
|
46
|
+
"postcss": "^8.3.11",
|
|
47
|
+
"tailwindcss": "^2.2.19",
|
|
48
|
+
"trim": "0.0.3",
|
|
49
|
+
"typescript": "^4.4.4"
|
|
50
|
+
},
|
|
51
|
+
"gitHead": "5f82ce7f267e9b3ec771bbd3cc81359ed0a8fd18"
|
|
52
|
+
}
|
package/pages/_app.tsx
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import '../styles/globals.css';
|
|
2
|
+
import { AppProps } from 'next/app';
|
|
3
|
+
import Head from 'next/head';
|
|
4
|
+
import Header from '@/components/Header';
|
|
5
|
+
import Footer from '@/components/Footer';
|
|
6
|
+
import { EventCatalogContextProvider } from '@/hooks/EventCatalog';
|
|
7
|
+
|
|
8
|
+
export default ({ Component, pageProps }: AppProps) => (
|
|
9
|
+
<EventCatalogContextProvider>
|
|
10
|
+
<Head>
|
|
11
|
+
<title>EventCatalog</title>
|
|
12
|
+
<script src="//unpkg.com/three" />
|
|
13
|
+
<script src="//unpkg.com/three/examples/js/renderers/CSS2DRenderer.js" />
|
|
14
|
+
|
|
15
|
+
<meta
|
|
16
|
+
name="description"
|
|
17
|
+
content="An open source project to Discover, Explore and Document your Event Driven Architectures."
|
|
18
|
+
/>
|
|
19
|
+
<meta property="og:url" content="https://demo.eventcatalog.dev/" />
|
|
20
|
+
<meta property="og:type" content="website" />
|
|
21
|
+
<meta
|
|
22
|
+
property="og:title"
|
|
23
|
+
content="EventCatalog | Discover, Explore and Document your Event Driven Architectures."
|
|
24
|
+
/>
|
|
25
|
+
<meta
|
|
26
|
+
property="og:description"
|
|
27
|
+
content="An open source tool powered by markdown to document your Event Driven Architecture."
|
|
28
|
+
/>
|
|
29
|
+
<meta property="og:image" content="https://demo.eventcatalog.dev/opengraph.png" />
|
|
30
|
+
<meta
|
|
31
|
+
property="og:image:alt"
|
|
32
|
+
content="EventCatalog | Discover, Explore and Document your Event Driven Architectures."
|
|
33
|
+
/>
|
|
34
|
+
<meta property="og:image:width" content="1200" />
|
|
35
|
+
<meta property="og:image:height" content="600" />
|
|
36
|
+
<meta property="og:locale" content="en-GB" />
|
|
37
|
+
<meta name="author" content="David Boyne" />
|
|
38
|
+
|
|
39
|
+
{/* Need to load this before any of the Html2Diff Code */}
|
|
40
|
+
<link
|
|
41
|
+
rel="stylesheet"
|
|
42
|
+
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/atom-one-light.min.css"
|
|
43
|
+
/>
|
|
44
|
+
</Head>
|
|
45
|
+
<Header />
|
|
46
|
+
<Component {...pageProps} />
|
|
47
|
+
<Footer />
|
|
48
|
+
</EventCatalogContextProvider>
|
|
49
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line func-names
|
|
5
|
+
export default function (req, res) {
|
|
6
|
+
const { name: eventName } = req.query;
|
|
7
|
+
|
|
8
|
+
const eventDir = path.join(process.env.PROJECT_DIR, 'events', eventName);
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
// Find the schema file
|
|
12
|
+
const filesInEventDir = fs.readdirSync(eventDir);
|
|
13
|
+
const schemaFileName = filesInEventDir.find((fileName) => fileName.indexOf('schema.') > -1);
|
|
14
|
+
|
|
15
|
+
if (schemaFileName) {
|
|
16
|
+
const schemaFile = fs.readFileSync(path.join(eventDir, schemaFileName));
|
|
17
|
+
res.send(schemaFile).end();
|
|
18
|
+
} else {
|
|
19
|
+
res.status(404).end();
|
|
20
|
+
}
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.log(error);
|
|
23
|
+
res.status(404).end();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { MDXRemote } from 'next-mdx-remote';
|
|
2
|
+
import React, { useEffect } from 'react';
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
|
|
5
|
+
import * as Diff2Html from 'diff2html/lib/ui/js/diff2html-ui-slim';
|
|
6
|
+
import 'diff2html/bundles/css/diff2html.min.css';
|
|
7
|
+
|
|
8
|
+
import { CodeIcon } from '@heroicons/react/solid';
|
|
9
|
+
import BreadCrumbs from '@/components/BreadCrumbs';
|
|
10
|
+
|
|
11
|
+
import { getLogsForEvent, getEventByName } from '@/lib/events';
|
|
12
|
+
|
|
13
|
+
function classNames(...classes) {
|
|
14
|
+
return classes.filter(Boolean).join(' ');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface LogsProps {
|
|
18
|
+
name: string;
|
|
19
|
+
currentVersion: string;
|
|
20
|
+
changes: any;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function Logs({ changes, name: eventName, currentVersion }: LogsProps) {
|
|
24
|
+
const pages = [
|
|
25
|
+
{ name: 'Events', href: '/events', current: false },
|
|
26
|
+
{ name: eventName, href: `/events/${eventName}`, current: false },
|
|
27
|
+
{ name: 'Logs', href: `/events/${eventName}/history`, current: true },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const configuration = {
|
|
32
|
+
drawFileList: false,
|
|
33
|
+
matching: 'lines',
|
|
34
|
+
highlight: true,
|
|
35
|
+
fileListToggle: false,
|
|
36
|
+
outputFormat: 'side-by-side',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
changes.forEach((diff, index) => {
|
|
40
|
+
if (diff.value) {
|
|
41
|
+
const targetElement = document.getElementById(`code-diff-${index}`);
|
|
42
|
+
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
const diff2htmlUi = new Diff2Html.Diff2HtmlUI(targetElement, diff.value, configuration);
|
|
45
|
+
diff2htmlUi.draw();
|
|
46
|
+
diff2htmlUi.highlightCode();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}, [changes]);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="flex relative min-h-screen">
|
|
53
|
+
<div className=" flex flex-col w-0 flex-1 ">
|
|
54
|
+
<main className="flex-1 ">
|
|
55
|
+
<div className="py-8 xl:py-10">
|
|
56
|
+
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 xl:max-w-7xl xl:grid xl:grid-cols-4">
|
|
57
|
+
<div className="xl:col-span-4 flex-col justify-between flex ">
|
|
58
|
+
<div className="mb-5 border-b border-gray-100 pb-4">
|
|
59
|
+
<BreadCrumbs pages={pages} />
|
|
60
|
+
</div>
|
|
61
|
+
<div>
|
|
62
|
+
<div>
|
|
63
|
+
<div className="border-b pb-4 flex justify-between mb-4">
|
|
64
|
+
<div className="space-y-2 w-full">
|
|
65
|
+
<h1 className="text-3xl font-bold text-gray-900 relative">EmailSent</h1>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
{changes.length === 0 && (
|
|
71
|
+
<div className="text-gray-400 text-xl">No versions for Event found.</div>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
<div className="flow-root mb-20">
|
|
75
|
+
<ul className="">
|
|
76
|
+
{changes.map((event, eventIdx) => (
|
|
77
|
+
<li key={eventIdx} className="">
|
|
78
|
+
<div className="relative pb-8">
|
|
79
|
+
{eventIdx !== changes.length - 1 ? (
|
|
80
|
+
<span
|
|
81
|
+
className="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-100"
|
|
82
|
+
aria-hidden="true"
|
|
83
|
+
/>
|
|
84
|
+
) : null}
|
|
85
|
+
<div className="relative flex space-x-3">
|
|
86
|
+
<div>
|
|
87
|
+
<span
|
|
88
|
+
className={classNames(
|
|
89
|
+
'h-8 text-white text-xs w-8 rounded-full flex items-center justify-center ring-8 ring-white bg-blue-500'
|
|
90
|
+
)}
|
|
91
|
+
>
|
|
92
|
+
<CodeIcon className="h-4 w-4" />
|
|
93
|
+
{/* {event.versions[0]} */}
|
|
94
|
+
</span>
|
|
95
|
+
</div>
|
|
96
|
+
<div>
|
|
97
|
+
<div>
|
|
98
|
+
<p className="font-bold text-gray-800 text-xl">
|
|
99
|
+
Schema version update
|
|
100
|
+
{event.versions.map((version, index) => {
|
|
101
|
+
const linkHref =
|
|
102
|
+
version === currentVersion
|
|
103
|
+
? `/events/${eventName}`
|
|
104
|
+
: `/events/${eventName}/v/${version}`;
|
|
105
|
+
return (
|
|
106
|
+
<Link key={version} href={linkHref}>
|
|
107
|
+
<a className="font-medium">
|
|
108
|
+
{index === 0 && ` from`}
|
|
109
|
+
<span className="text-blue-500 underline px-1">
|
|
110
|
+
{version}
|
|
111
|
+
{version === currentVersion ? '(latest)' : ''}
|
|
112
|
+
</span>
|
|
113
|
+
{index === 0 && `to`}
|
|
114
|
+
</a>
|
|
115
|
+
</Link>
|
|
116
|
+
);
|
|
117
|
+
})}
|
|
118
|
+
</p>
|
|
119
|
+
{event.changelog.source && (
|
|
120
|
+
<>
|
|
121
|
+
<h2 className="text-xl text-blue-500 font-bold mt-4 border-b border-gray-100 pb-2">
|
|
122
|
+
Changelog
|
|
123
|
+
</h2>
|
|
124
|
+
<div className="prose max-w-none mt-2">
|
|
125
|
+
<MDXRemote {...event.changelog.source} />
|
|
126
|
+
</div>
|
|
127
|
+
</>
|
|
128
|
+
)}
|
|
129
|
+
{!event.changelog.source && (
|
|
130
|
+
<h2 className="text-base text-gray-300 font-bold mt-4">
|
|
131
|
+
No changelog file found.
|
|
132
|
+
</h2>
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
<div className="text-right text-sm text-gray-500 py-4">
|
|
136
|
+
<div id={`code-diff-${eventIdx}`} />
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</li>
|
|
142
|
+
))}
|
|
143
|
+
</ul>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</main>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const getServerSideProps = async ({ query }) => {
|
|
156
|
+
const { name: eventName } = query;
|
|
157
|
+
|
|
158
|
+
const history = await getLogsForEvent(eventName);
|
|
159
|
+
const { event: { version } = {} } = await getEventByName(eventName);
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
props: {
|
|
163
|
+
changes: history,
|
|
164
|
+
name: eventName,
|
|
165
|
+
currentVersion: version,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export default Logs;
|