From 04ab91d9055f37ff5f4f1c4f147d1ed679b0cbab Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Fri, 10 Jan 2025 08:26:23 -0300 Subject: [PATCH] fix/ownerReference (#1078) * fix: change ownerReference to union of string and object It replaces the `reference` from the ownerReference to a union of string and an object with id and collection. The string refers to the owner id. The object with id and collection keep compatibility with `reference` in case someone use like that. It adds a collection utility to get the owner from the onwerReference. Fix #1034 * Create wild-items-shout.md --------- Co-authored-by: David Boyne --- .changeset/wild-items-shout.md | 5 +++ .../components/SideBars/ChannelSideBar.astro | 5 ++- .../components/SideBars/DomainSideBar.astro | 6 ++- .../components/SideBars/MessageSideBar.astro | 6 ++- .../components/SideBars/ServiceSideBar.astro | 6 ++- eventcatalog/src/content/config.ts | 16 ++++++- eventcatalog/src/utils/collections/owners.ts | 43 +++++++++++++++++++ 7 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 .changeset/wild-items-shout.md create mode 100644 eventcatalog/src/utils/collections/owners.ts diff --git a/.changeset/wild-items-shout.md b/.changeset/wild-items-shout.md new file mode 100644 index 00000000..073a5cad --- /dev/null +++ b/.changeset/wild-items-shout.md @@ -0,0 +1,5 @@ +--- +"@eventcatalog/core": patch +--- + +fix(core): fixed issue with teams/owners not showing in the UI diff --git a/eventcatalog/src/components/SideBars/ChannelSideBar.astro b/eventcatalog/src/components/SideBars/ChannelSideBar.astro index c5768eda..e65e3f4a 100644 --- a/eventcatalog/src/components/SideBars/ChannelSideBar.astro +++ b/eventcatalog/src/components/SideBars/ChannelSideBar.astro @@ -1,5 +1,5 @@ --- -import { getEntry, type CollectionEntry } from 'astro:content'; +import type { CollectionEntry } from 'astro:content'; import PillListFlat from '@components/Lists/PillListFlat'; import ProtocolList from '@components/Lists/ProtocolList'; import OwnersList from '@components/Lists/OwnersList'; @@ -7,6 +7,7 @@ import VersionList from '@components/Lists/VersionList.astro'; import { buildUrl } from '@utils/url-builder'; import { ScrollText } from 'lucide-react'; import RepositoryList from '@components/Lists/RepositoryList.astro'; +import { getOwner } from '@utils/collections/owners'; interface Props { channel: CollectionEntry<'channels'>; @@ -15,7 +16,7 @@ interface Props { const { channel } = Astro.props; const ownersRaw = channel.data?.owners || []; -const owners = await Promise.all(ownersRaw.map((o) => getEntry(o))); +const owners = await Promise.all>(ownersRaw.map(getOwner)); const filteredOwners = owners.filter((o) => o !== undefined); const channelParameters: Record = channel.data.parameters || {}; diff --git a/eventcatalog/src/components/SideBars/DomainSideBar.astro b/eventcatalog/src/components/SideBars/DomainSideBar.astro index b00e44a5..b64b0353 100644 --- a/eventcatalog/src/components/SideBars/DomainSideBar.astro +++ b/eventcatalog/src/components/SideBars/DomainSideBar.astro @@ -4,9 +4,11 @@ import PillListFlat from '@components/Lists/PillListFlat'; import RepositoryList from '@components/Lists/RepositoryList.astro'; import VersionList from '@components/Lists/VersionList.astro'; import { getUbiquitousLanguage } from '@utils/collections/domains'; +import { getOwner } from '@utils/collections/owners'; import { buildUrl } from '@utils/url-builder'; -import { getEntry, type CollectionEntry } from 'astro:content'; +import type { CollectionEntry } from 'astro:content'; import { ScrollText, Workflow } from 'lucide-react'; + interface Props { domain: CollectionEntry<'domains'>; } @@ -20,7 +22,7 @@ const hasUbiquitousLanguage = ubiquitousLanguage.length > 0; const ubiquitousLanguageDictionary = hasUbiquitousLanguage ? ubiquitousLanguage[0].data.dictionary : []; const ownersRaw = domain.data?.owners || []; -const owners = await Promise.all(ownersRaw.map((o) => getEntry(o))); +const owners = await Promise.all>(ownersRaw.map(getOwner)); const filteredOwners = owners.filter((o) => o !== undefined); const serviceList = services.map((p) => ({ diff --git a/eventcatalog/src/components/SideBars/MessageSideBar.astro b/eventcatalog/src/components/SideBars/MessageSideBar.astro index f91bd164..4b948598 100644 --- a/eventcatalog/src/components/SideBars/MessageSideBar.astro +++ b/eventcatalog/src/components/SideBars/MessageSideBar.astro @@ -1,5 +1,5 @@ --- -import { getEntry, type CollectionEntry } from 'astro:content'; +import type { CollectionEntry } from 'astro:content'; import PillListFlat from '@components/Lists/PillListFlat'; import OwnersList from '@components/Lists/OwnersList'; import type { CollectionMessageTypes } from '@types'; @@ -8,6 +8,8 @@ import VersionList from '@components/Lists/VersionList.astro'; import { buildUrl } from '@utils/url-builder'; import { FileDownIcon, ScrollText, Workflow } from 'lucide-react'; import RepositoryList from '@components/Lists/RepositoryList.astro'; +import { getOwner } from '@utils/collections/owners'; + interface Props { message: CollectionEntry; } @@ -19,7 +21,7 @@ const consumers = (message.data.consumers as CollectionEntry<'services'>[]) || [ const channels = (message.data.messageChannels as CollectionEntry<'channels'>[]) || []; const ownersRaw = message.data?.owners || []; -const owners = await Promise.all(ownersRaw.map((o) => getEntry(o))); +const owners = await Promise.all>(ownersRaw.map(getOwner)); const filteredOwners = owners.filter((o) => o !== undefined); const producerList = producers.map((p) => ({ diff --git a/eventcatalog/src/components/SideBars/ServiceSideBar.astro b/eventcatalog/src/components/SideBars/ServiceSideBar.astro index 28191a99..07480535 100644 --- a/eventcatalog/src/components/SideBars/ServiceSideBar.astro +++ b/eventcatalog/src/components/SideBars/ServiceSideBar.astro @@ -5,9 +5,11 @@ import RepositoryList from '@components/Lists/RepositoryList.astro'; import SpecificationsList from '@components/Lists/SpecificationsList.astro'; import VersionList from '@components/Lists/VersionList.astro'; import { buildUrl } from '@utils/url-builder'; -import { getEntry, type CollectionEntry } from 'astro:content'; +import { getOwner } from '@utils/collections/owners'; +import type { CollectionEntry } from 'astro:content'; import { ScrollText, Workflow, FileDownIcon, Code, Link } from 'lucide-react'; import { join } from 'node:path'; + interface Props { service: CollectionEntry<'services'>; } @@ -20,7 +22,7 @@ const sends = (service.data.sends as CollectionEntry<'events'>[]) || []; const receives = (service.data.receives as CollectionEntry<'events'>[]) || []; const ownersRaw = service.data?.owners || []; -const owners = await Promise.all(ownersRaw.map((o) => getEntry(o))); +const owners = await Promise.all>(ownersRaw.map(getOwner)); const filteredOwners = owners.filter((o) => o !== undefined); const sendsList = sends.map((p) => ({ diff --git a/eventcatalog/src/content/config.ts b/eventcatalog/src/content/config.ts index df3ab19b..bada0618 100644 --- a/eventcatalog/src/content/config.ts +++ b/eventcatalog/src/content/config.ts @@ -47,7 +47,21 @@ const changelogs = defineCollection({ }); // Create a union type for owners -const ownerReference = z.union([reference('users'), reference('teams')]); +const ownerReference = z + .union([ + // The ID of the user or team + z.string(), + // The full object with the ID and collection (keep compatibility with `reference`) + z.object({ + id: z.string(), + collection: z.enum(['users', 'teams']), + }), + ]) + .transform( + // This transformation is needed to keep compatibility with `reference`. + // The utilities `getTeams` and `getUsers` rely on this transformation. + (lookup) => ({ id: typeof lookup === 'string' ? lookup : lookup.id }) + ); const baseSchema = z.object({ id: z.string(), diff --git a/eventcatalog/src/utils/collections/owners.ts b/eventcatalog/src/utils/collections/owners.ts new file mode 100644 index 00000000..7b88f8ff --- /dev/null +++ b/eventcatalog/src/utils/collections/owners.ts @@ -0,0 +1,43 @@ +import { getCollection, type CollectionEntry } from 'astro:content'; + +const getOwners = (function () { + type Owners = CollectionEntry<'users' | 'teams'>; + let cachedOwners: Map | null = null; + let initializingPromise: Promise> | null = null; + + /** + * Initializes and caches the owners by fetching from the 'users' and 'teams' collections. + */ + async function init() { + const ownersMap = new Map>(); + + const owners = await Promise.all([ + getCollection('users', (entry) => entry.data.hidden !== true), + getCollection('teams', (entry) => entry.data.hidden !== true), + ]); + + for (const owner of owners.flat()) { + ownersMap.set(owner.data.id, owner); + } + + cachedOwners = ownersMap; + initializingPromise = null; + + return cachedOwners; + } + + return () => + cachedOwners || // Return cached owners if already initialized + initializingPromise || // Return the promise if initialization is in progress + (initializingPromise = init()); // Initialize if neither cache nor promise exists +})(); + +export async function getOwner(lookup: { id: string }): Promise | undefined> { + const lookupId = typeof lookup === 'string' ? lookup : lookup.id; + + const owner = (await getOwners()).get(lookupId); + + if (!owner) console.warn(`Entry ${lookupId} not found in "teams"/"users" collections.`); + + return owner; +}