Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP): feat/explorer-filter-by-domain #1103

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ServerIcon, BoltIcon, ChatBubbleLeftIcon, MagnifyingGlassIcon } from '@heroicons/react/24/solid';
import { RectangleGroupIcon } from '@heroicons/react/20/solid';
import { createColumnHelper } from '@tanstack/react-table';
import type { CollectionMessageTypes } from '@types';
import type { CollectionEntry } from 'astro:content';
Expand All @@ -7,7 +8,9 @@ import { filterByName, filterCollectionByName, filterByBadge } from '../filters/
import { buildUrl } from '@utils/url-builder';
import { createBadgesColumn } from './SharedColumns';

const columnHelper = createColumnHelper<CollectionEntry<CollectionMessageTypes>>();
const columnHelper = createColumnHelper<
CollectionEntry<CollectionMessageTypes> & { data: { domain: { id: string; name: string; version: string } | null } }
>();

export const getColorAndIconForMessageType = (type: string) => {
switch (type) {
Expand Down Expand Up @@ -68,6 +71,35 @@ export const columns = () => [
},
}),

columnHelper.accessor((originalRow) => originalRow.data.domain?.name, {
id: 'domain',
header: () => <span>Domain</span>,
cell: (info) => {
const color = 'yellow';
const domain = info.row.original.data.domain;

return domain ? (
<div className="group">
<a
href={buildUrl(`/docs/domains/${domain.id}/${domain.version}`)}
className={`group-hover:text-${color}-500 flex space-x-1 items-center`}
>
<div className={`flex items-center border border-gray-300 shadow-sm rounded-md group-hover:border-${color}-400`}>
<span className="flex items-center">
<span className={`bg-${color}-500 group-hover:bg-${color}-600 h-full rounded-tl rounded-bl p-1`}>
<RectangleGroupIcon className="h-4 w-4 text-white" />
</span>
<span className="leading-none px-2 group-hover:underline group-hover:text-primary font-light">
{domain.name} (v{domain.version})
</span>
</span>
</div>
</a>
</div>
) : null;
},
}),

columnHelper.accessor('data.producers', {
header: () => <span>Producers</span>,
meta: {
Expand Down
29 changes: 28 additions & 1 deletion eventcatalog/src/pages/discover/[type]/index.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
---
import * as path from 'node:path';
import DiscoverLayout from '@layouts/DiscoverLayout.astro';
import type { PageTypes } from '@types';
import { getFlows } from '@utils/collections/flows';

import { pageDataLoader } from '@utils/page-loaders/page-data-loader';
import { getEntry } from 'astro:content';
import { getDomainRefIdFromPathToFile, getDomains } from '@utils/collections/domains';
type PageTypesWithFlows = PageTypes | 'flows';

export async function getStaticPaths() {
Expand All @@ -27,11 +30,35 @@ export async function getStaticPaths() {
}

const { type, data } = Astro.props;

let dataWithDomain;

if (['commands', 'events', 'queries'].includes(type)) {
const domains = await getDomains();

dataWithDomain = data.map((item) => {
const domainRefId = item.data.pathToFile ? getDomainRefIdFromPathToFile(item.data.pathToFile) : null;
const domain = domainRefId ? domains.find((d) => d.id === domainRefId) : null;
return {
...item,
data: {
...item.data,
domain: domain
? {
id: domain.data.id,
name: domain.data.name,
version: domain.data.version,
}
: null,
},
};
});
}
---

<DiscoverLayout
title={`${type} (${data.length})`}
subtitle={`Find, filter and search for any ${type} in your system.`}
data={data}
data={dataWithDomain ?? data}
type={type}
/>
46 changes: 45 additions & 1 deletion eventcatalog/src/utils/collections/domains.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getItemsFromCollectionByIdAndSemverOrLatest, getVersionForCollectionItem } from '@utils/collections/util';
import { posixifyPath, removeBase, removeLeadingForwardSlash } from '@utils/path';
import { getCollection } from 'astro:content';
import type { CollectionEntry } from 'astro:content';
import path from 'path';
import path from 'node:path';

const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();

Expand Down Expand Up @@ -80,3 +81,46 @@ export const getUbiquitousLanguage = async (domain: Domain): Promise<UbiquitousL

return ubiquitousLanguages;
};

/**
* Extracts the domain reference ID from a given file path.
*
* This function processes a file path to identify the domain and versioning structure
* of the file within the project directory. It generates a reference ID for the domain,
* which points to the corresponding `index.mdx` file.
*
* ### Examples:
* - Input: `/domains/Orders/services/InventoryService/events/PaymentProcessed/index.md`
* Output: `Orders/index.mdx`
*
* - Input: `/domains/Orders/versioned/0.1.0/services/InventoryService/events/PaymentProcessed/index.md`
* Output: `Orders/versioned/0.1.0/index.mdx`
*
* @param pathToFile - The file path to process. This can be absolute or relative.
* @returns A string representing the domain reference ID (`<domain>/index.mdx`
* or `<domain>/versioned/<version>/index.mdx`), or `null` if the path
* does not belong to a valid domain structure.
*/
export function getDomainRefIdFromPathToFile(pathToFile: string) {
const projectDir = path.resolve(PROJECT_DIR);
const absolutePathToFile = path.isAbsolute(pathToFile) ? pathToFile : path.resolve(pathToFile);

const filePath = removeBase(posixifyPath(absolutePathToFile), posixifyPath(projectDir));

const parts = removeLeadingForwardSlash(filePath).split('/');

if (parts[0] !== 'domains') {
// Not in nested folders; Unable to identify the domain
return null;
}

const domain = parts[1]; // The domain is always the second part.
const isDomainVersioned = parts[2] === 'versioned';

if (isDomainVersioned) {
const version = parts[3];
return `${domain}/versioned/${version}/index.mdx`;
}

return `${domain}/index.mdx`;
}
23 changes: 23 additions & 0 deletions eventcatalog/src/utils/path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as path from 'node:path';

/**
* Convert a platform path to a posix path.
*/
export function posixifyPath(filePath: string) {
return filePath.split(path.sep).join('/');
}

export function removeLeadingForwardSlash(filePath: string) {
return filePath.startsWith('/') ? filePath.substring(1) : filePath;
}

export function removeTrailingForwardSlash(filePath: string) {
return filePath.endsWith('/') ? filePath.slice(0, -1) : filePath;
}

export function removeBase(filePath: string, base: string) {
if (filePath.startsWith(base)) {
return filePath.slice(removeTrailingForwardSlash(base).length);
}
return filePath;
}
Loading