diff --git a/navnotes.md b/navnotes.md index 407a89a71..289e27af0 100644 --- a/navnotes.md +++ b/navnotes.md @@ -28,3 +28,21 @@ Same as `bundleSegmentRef`, but for global segments. ### frontendRef Required to match nav item in bundle to current app + +# Search interceptor notes + +## frontendRef + +search entries need a `frontendRef` attribute. Without the attribute, we can modify/add frontend entries, but we can't remove them + +# Service tiles interceptor + +## frontendRef + +Service tile entries need a `frontendRef` attribute. Without the attribute, we can modify/add frontend entries, but we can't remove them + +# Widget registry interceptor + +## frontendRef + +Widget registry entries need a `frontendRef` attribute. Without the attribute, we can modify/add frontend entries, but we can't remove them diff --git a/packages/config-utils/src/feo/feo-types.ts b/packages/config-utils/src/feo/feo-types.ts new file mode 100644 index 000000000..3cac5f580 --- /dev/null +++ b/packages/config-utils/src/feo/feo-types.ts @@ -0,0 +1,139 @@ +export type SupportCaseData = { + version: string; + product: string; +}; + +export type ChromeGlobalModuleConfig = { + supportCaseData?: SupportCaseData; + ssoScopes?: string[]; +}; + +export type ChromePermissions = { + method: string; + apps?: string[]; + args?: unknown[]; +}; + +export type ChromeEntryModuleRoute = { + pathname: string; + exact?: boolean; + props?: object; + supportCaseData?: SupportCaseData; + permissions?: ChromePermissions; +}; + +export type ChromeEntryModule = { + id: string; + module: string; + routes: ChromeEntryModuleRoute[]; +}; + +type ChromeModuleAnalytics = { + APIKey: string; +}; + +export type ChromeModule = { + manifestLocation: string; + defaultDocumentTitle?: string; + /** + * @deprecated + * use `moduleConfig` instead + */ + config?: object; + moduleConfig?: ChromeGlobalModuleConfig; + modules?: ChromeEntryModule[]; + /** + * @deprecated + * Use feo generated resources to get permitted modules + */ + isFedramp?: boolean; + analytics?: ChromeModuleAnalytics; +}; + +export type ChromeModuleRegistry = { + [moduleName: string]: ChromeModule; +}; + +export type ChromeStaticSearchEntry = { + frontendRef: string; + id: string; + href: string; + title: string; + description: string; + alt_title?: string[]; + isExternal?: boolean; +}; + +export type SegmentRef = { + segmentId: string; + frontendName: string; +}; + +export type DirectNavItem = { + id?: string; + frontendRef?: string; + href?: string; + title?: string; + expandable?: boolean; + // should be removed + appId?: string; + routes?: DirectNavItem[]; + navItems?: DirectNavItem[]; + bundleSegmentRef?: string; + segmentRef?: SegmentRef; + segmentId?: string; +}; + +export type Nav = { + title?: string; + id: string; + navItems: DirectNavItem[]; +}; + +export type BundleSegment = { + segmentId: string; + bundleId: string; + position: number; + navItems: DirectNavItem[]; +}; + +export type ServiceTile = { + section: string; + group: string; + id: string; + frontendRef: string; +}; + +export type ServiceGroup = { + id: string; + tiles: ServiceTile[]; +}; + +export type ServiceCategory = { + id: string; + groups: ServiceGroup[]; +}; + +export type ChromeWidgetEntry = { + scope: string; + module: string; + frontendRef: string; +}; + +export type CRDObject = { + metadata: { + name: string; + }; + spec: { + bundleSegments?: BundleSegment[]; + navigationSegments?: DirectNavItem[]; + module: ChromeModule; + searchEntries?: ChromeStaticSearchEntry[]; + serviceTiles?: ServiceTile[]; + widgetRegistry?: ChromeWidgetEntry[]; + }; +}; + +export type FrontendCRD = { + objects: CRDObject[]; +}; diff --git a/packages/config-utils/src/feo/module-interceptor.test.ts b/packages/config-utils/src/feo/module-interceptor.test.ts new file mode 100644 index 000000000..9b5cc131e --- /dev/null +++ b/packages/config-utils/src/feo/module-interceptor.test.ts @@ -0,0 +1,61 @@ +import { ChromeModule, ChromeModuleRegistry, FrontendCRD } from './feo-types'; +import moduleInterceptor from './module-interceptor'; + +describe('module-interceptor', () => { + it('should replace existing entry in moduleRegistry with new entry', () => { + const moduleName = 'module-name'; + const newEntry: ChromeModule = { + manifestLocation: 'new-location', + }; + const frontendCRD: FrontendCRD = { + objects: [ + { + metadata: { + name: moduleName, + }, + spec: { + module: newEntry, + }, + }, + ], + }; + const remoteModuleRegistry: ChromeModuleRegistry = { + [moduleName]: { + manifestLocation: 'old-location', + }, + }; + const expectedResult: ChromeModuleRegistry = { + [moduleName]: newEntry, + }; + + const result = moduleInterceptor(remoteModuleRegistry, frontendCRD); + expect(result).toEqual(expectedResult); + }); + + it('should add new entry to moduleRegistry', () => { + const moduleName = 'module-name'; + const newEntry: ChromeModule = { + manifestLocation: 'new-location', + }; + const frontendCRD: FrontendCRD = { + objects: [ + { + metadata: { + name: moduleName, + }, + spec: { + module: newEntry, + }, + }, + ], + }; + const remoteModuleRegistry: ChromeModuleRegistry = {}; + + const expectedResult: ChromeModuleRegistry = { + [moduleName]: newEntry, + }; + + const result = moduleInterceptor(remoteModuleRegistry, frontendCRD); + expect(result).toEqual(expectedResult); + }); +}); diff --git a/packages/config-utils/src/feo/module-interceptor.ts b/packages/config-utils/src/feo/module-interceptor.ts new file mode 100644 index 000000000..49b801a04 --- /dev/null +++ b/packages/config-utils/src/feo/module-interceptor.ts @@ -0,0 +1,11 @@ +import { ChromeModuleRegistry, FrontendCRD } from './feo-types'; + +function moduleInterceptor(moduleRegistry: ChromeModuleRegistry, frontendCRD: FrontendCRD): ChromeModuleRegistry { + const moduleName = frontendCRD.objects[0].metadata.name; + return { + ...moduleRegistry, + [moduleName]: frontendCRD.objects[0].spec.module, + }; +} + +export default moduleInterceptor; diff --git a/packages/config-utils/src/navigation-interceptor.test.ts b/packages/config-utils/src/feo/navigation-interceptor.test.ts similarity index 92% rename from packages/config-utils/src/navigation-interceptor.test.ts rename to packages/config-utils/src/feo/navigation-interceptor.test.ts index f24b60a63..e022e7e26 100644 --- a/packages/config-utils/src/navigation-interceptor.test.ts +++ b/packages/config-utils/src/feo/navigation-interceptor.test.ts @@ -1,16 +1,17 @@ -import navigationInterceptor, { FrontendCRD, Nav, NavItem, SegmentRef } from './navigation-interceptor'; +import { DirectNavItem, FrontendCRD, Nav, SegmentRef } from './feo-types'; +import navigationInterceptor from './navigation-interceptor'; describe('NavigationInterceptor', () => { describe('bundle segments', () => { const bundleName = 'testing-bundle'; const defaultFrontendName = 'testing-frontend'; const bundleSegmentName = 'testing-bundle-segment'; - const baseNavItem: NavItem = { + const baseNavItem: DirectNavItem = { id: 'link-one', href: '/link-one', title: 'Link one', }; - function createLocalCRD({ bundleSegmentRef, frontendRef, ...navItem }: NavItem, frontendName: string): FrontendCRD { + function createLocalCRD({ bundleSegmentRef, frontendRef, ...navItem }: DirectNavItem, frontendName: string): FrontendCRD { return { objects: [ { @@ -18,6 +19,9 @@ describe('NavigationInterceptor', () => { name: frontendName, }, spec: { + module: { + manifestLocation: 'http://localhost:3000/manifest.json', + }, bundleSegments: [ { bundleId: bundleName, @@ -31,18 +35,18 @@ describe('NavigationInterceptor', () => { ], }; } - function createRemoteNav(navItem: NavItem): Nav { + function createRemoteNav(navItem: DirectNavItem): Nav { return { id: bundleName, title: bundleName, navItems: [navItem], }; } - function createExpectedNavItems(navItem: NavItem): NavItem[] { + function createExpectedNavItems(navItem: DirectNavItem): DirectNavItem[] { return [navItem]; } function crateTestData( - navItem: NavItem, + navItem: DirectNavItem, { shouldChange, isNestedRoute, @@ -51,7 +55,7 @@ describe('NavigationInterceptor', () => { }: { shouldChange?: boolean; isNestedRoute?: boolean; isNestedNav?: boolean; frontendName?: string } = {} ) { const internalFrontendName = frontendName ?? defaultFrontendName; - let internalNavItem: NavItem = { ...navItem }; + let internalNavItem: DirectNavItem = { ...navItem }; internalNavItem.bundleSegmentRef = bundleSegmentName; internalNavItem.frontendRef = internalFrontendName; if (isNestedRoute) { @@ -88,7 +92,7 @@ describe('NavigationInterceptor', () => { ], }; } - let changedNavItem: NavItem; + let changedNavItem: DirectNavItem; if (shouldChange) { if (isNestedRoute) { changedNavItem = { @@ -180,7 +184,7 @@ describe('NavigationInterceptor', () => { frontendName: defaultFrontendName, segmentId: navSegmentId, }; - const baseNavItem: NavItem = { + const baseNavItem: DirectNavItem = { id: 'link-one', href: '/link-one', title: 'Link one', @@ -194,6 +198,9 @@ describe('NavigationInterceptor', () => { name: defaultFrontendName, }, spec: { + module: { + manifestLocation: 'http://localhost:3000/manifest.json', + }, bundleSegments: [ { bundleId: bundleName, @@ -219,7 +226,7 @@ describe('NavigationInterceptor', () => { navItems: [{ ...baseNavItem, segmentRef: baseSegmentRef, frontendRef: defaultFrontendName, bundleSegmentRef: bundleSegmentName }], }; - const expectedResult: NavItem[] = [ + const expectedResult: DirectNavItem[] = [ { ...baseNavItem, title: 'Link one changed', @@ -239,7 +246,7 @@ describe('NavigationInterceptor', () => { frontendName: defaultFrontendName, segmentId: navSegmentId, }; - const baseNavItems: NavItem[] = [ + const baseNavItems: DirectNavItem[] = [ { id: 'link-one', href: '/link-one', @@ -259,6 +266,9 @@ describe('NavigationInterceptor', () => { name: defaultFrontendName, }, spec: { + module: { + manifestLocation: 'http://localhost:3000/manifest.json', + }, bundleSegments: [ { bundleId: bundleName, @@ -303,7 +313,7 @@ describe('NavigationInterceptor', () => { ], }; - const expectedResult: NavItem[] = [ + const expectedResult: DirectNavItem[] = [ { title: 'persistent item', href: '/persistent', @@ -328,7 +338,7 @@ describe('NavigationInterceptor', () => { frontendName: defaultFrontendName, segmentId: navSegmentId, }; - const baseNavItems: NavItem[] = [ + const baseNavItems: DirectNavItem[] = [ { id: 'link-one', href: '/link-one', @@ -348,6 +358,9 @@ describe('NavigationInterceptor', () => { name: defaultFrontendName, }, spec: { + module: { + manifestLocation: 'http://localhost:3000/manifest.json', + }, bundleSegments: [ { bundleId: bundleName, @@ -373,7 +386,7 @@ describe('NavigationInterceptor', () => { navItems: [{ ...baseNavItems[0], segmentRef: baseSegmentRef, frontendRef: defaultFrontendName, bundleSegmentRef: bundleSegmentName }], }; - const expectedResult: NavItem[] = baseNavItems.map(({ title, ...navItem }) => ({ + const expectedResult: DirectNavItem[] = baseNavItems.map(({ title, ...navItem }) => ({ ...navItem, title: `${title} changed`, })); @@ -391,7 +404,7 @@ describe('NavigationInterceptor', () => { frontendName: defaultFrontendName, segmentId: navSegmentId, }; - const baseNavItems: NavItem[] = [ + const baseNavItems: DirectNavItem[] = [ { id: 'link-one', href: '/link-one', @@ -411,6 +424,9 @@ describe('NavigationInterceptor', () => { name: defaultFrontendName, }, spec: { + module: { + manifestLocation: 'http://localhost:3000/manifest.json', + }, bundleSegments: [ { bundleId: bundleName, @@ -441,7 +457,7 @@ describe('NavigationInterceptor', () => { })), }; - const expectedResult: NavItem[] = [{ ...baseNavItems[0], title: `${baseNavItems[0].title} changed` }]; + const expectedResult: DirectNavItem[] = [{ ...baseNavItems[0], title: `${baseNavItems[0].title} changed` }]; const result = navigationInterceptor(frontendCRD, remoteNav, bundleName); expect(result).toEqual(expectedResult); @@ -475,6 +491,9 @@ describe('NavigationInterceptor', () => { name: frontendName, }, spec: { + module: { + manifestLocation: 'http://localhost:3000/manifest.json', + }, navigationSegments: [ { segmentId: segmentOneId, @@ -601,7 +620,7 @@ describe('NavigationInterceptor', () => { ], }; - const expectedResult: NavItem[] = [ + const expectedResult: DirectNavItem[] = [ { title: 'Link one', href: '/link-one', diff --git a/packages/config-utils/src/navigation-interceptor.ts b/packages/config-utils/src/feo/navigation-interceptor.ts similarity index 81% rename from packages/config-utils/src/navigation-interceptor.ts rename to packages/config-utils/src/feo/navigation-interceptor.ts index 916bc739d..c31ad9ccc 100644 --- a/packages/config-utils/src/navigation-interceptor.ts +++ b/packages/config-utils/src/feo/navigation-interceptor.ts @@ -1,53 +1,6 @@ -export type SegmentRef = { - segmentId: string; - frontendName: string; -}; - -type DirectNavItem = { - id?: string; - frontendRef?: string; - href?: string; - title?: string; - expandable?: boolean; - // should be removed - appId?: string; - routes?: NavItem[]; - navItems?: NavItem[]; - bundleSegmentRef?: string; - segmentRef?: SegmentRef; - segmentId?: string; -}; - -export type NavItem = DirectNavItem; - -export type Nav = { - title?: string; - id: string; - navItems: NavItem[]; -}; - -type BundleSegment = { - segmentId: string; - bundleId: string; - position: number; - navItems: NavItem[]; -}; - -type CRDObject = { - metadata: { - name: string; - }; - spec: { - bundleSegments?: BundleSegment[]; - navigationSegments?: DirectNavItem[]; - }; -}; - -export type FrontendCRD = { - objects: CRDObject[]; -}; +import { BundleSegment, DirectNavItem, FrontendCRD, Nav, SegmentRef } from './feo-types'; -function hasSegmentRef(item: NavItem): item is Omit & { segmentRef: SegmentRef } { +function hasSegmentRef(item: DirectNavItem): item is Omit & { segmentRef: SegmentRef } { return typeof item?.segmentRef?.segmentId === 'string' && typeof item?.segmentRef?.frontendName === 'string'; } @@ -63,7 +16,7 @@ const getBundleSegments = (segmentCache: typeof bundleSegmentsCache, bundleId: s }, {}); }; -function findMatchingSegmentItem(navItems: NavItem[], matchId: string): NavItem | undefined { +function findMatchingSegmentItem(navItems: DirectNavItem[], matchId: string): DirectNavItem | undefined { let match = navItems.find((item) => { if (!hasSegmentRef(item)) { return item.id === matchId; @@ -92,7 +45,7 @@ function handleNestedNav( nSegmentCache: typeof navSegmentCache, bundleId: string, currentFrontendName: string -): NavItem { +): DirectNavItem { const { routes, navItems, ...segmentItem } = segmentMatch; let parsedRoutes = originalNavItem.routes; let parsedNavItems = originalNavItem.navItems; @@ -112,13 +65,13 @@ function handleNestedNav( }; } -function findNavItemsFirstSegmentIndex(navItems: NavItem[], frontendName: string) { +function findNavItemsFirstSegmentIndex(navItems: DirectNavItem[], frontendName: string) { return navItems.findIndex((item) => { return hasSegmentRef(item) && item.segmentRef.frontendName === frontendName; }); } -function findSegmentSequenceLength(navItems: NavItem[], sequenceStartIndex: number, sementId: string, frontendName: string) { +function findSegmentSequenceLength(navItems: DirectNavItem[], sequenceStartIndex: number, sementId: string, frontendName: string) { let finalIndex = sequenceStartIndex; for (let i = sequenceStartIndex; i < navItems.length; i += 1) { const item = navItems[i]; @@ -138,12 +91,12 @@ function findSegmentSequenceLength(navItems: NavItem[], sequenceStartIndex: numb } function parseNavItems( - navItems: NavItem[], + navItems: DirectNavItem[], bSegmentCache: typeof bundleSegmentsCache, nSegmentCache: typeof navSegmentCache, bundleId: string, currentFrontendName: string -): NavItem[] { +): DirectNavItem[] { const relevantSegments = getBundleSegments(bSegmentCache, bundleId); const res = navItems.map((navItem) => { if (!hasSegmentRef(navItem) && navItem.id) { @@ -195,7 +148,7 @@ function parseNavItems( // replaces changed nav items, local data overrides the remote data const substituteLocalNav = (frontendCRD: FrontendCRD, nav: Nav, bundleName: string) => { - let res: NavItem[] = []; + let res: DirectNavItem[] = []; const bundleSegmentsCache: { [bundleSegmentId: string]: BundleSegment } = {}; const navSegmentCache: { [navSegmentId: string]: DirectNavItem } = {}; frontendCRD.objects.forEach((obj) => { diff --git a/packages/config-utils/src/feo/search-interceptor.test.ts b/packages/config-utils/src/feo/search-interceptor.test.ts new file mode 100644 index 000000000..fb69a5d46 --- /dev/null +++ b/packages/config-utils/src/feo/search-interceptor.test.ts @@ -0,0 +1,58 @@ +import { ChromeStaticSearchEntry, FrontendCRD } from './feo-types'; +import searchInterceptor from './search-interceptor'; + +describe('SearchInterceptor', () => { + it('should replace search entries with the ones from the frontendCRD', () => { + const frontendName = 'frontendName'; + const frontendCRD: FrontendCRD = { + objects: [ + { + metadata: { + name: frontendName, + }, + spec: { + module: { + manifestLocation: 'location', + }, + searchEntries: [ + { + frontendRef: frontendName, + id: 'id-1', + href: 'href-1', + title: 'title-1', + description: 'description-1', + }, + { + frontendRef: frontendName, + id: 'id-1', + href: 'href-1', + title: 'title-1', + description: 'description-1', + }, + ], + }, + }, + ], + }; + const remoteSearchEntries: ChromeStaticSearchEntry[] = [ + { + frontendRef: 'otherFrontend', + id: 'otherFrontend', + href: 'otherFrontend', + title: 'otherFrontend', + description: 'otherFrontend', + }, + { + frontendRef: frontendName, + id: frontendName, + href: frontendName, + title: frontendName, + description: frontendName, + }, + ]; + + const expectedSearchEntries: ChromeStaticSearchEntry[] = [remoteSearchEntries[0], ...(frontendCRD.objects[0].spec.searchEntries ?? [])]; + const result = searchInterceptor(remoteSearchEntries, frontendCRD); + expect(result).toEqual(expectedSearchEntries); + }); +}); diff --git a/packages/config-utils/src/feo/search-interceptor.ts b/packages/config-utils/src/feo/search-interceptor.ts new file mode 100644 index 000000000..fd7b4a774 --- /dev/null +++ b/packages/config-utils/src/feo/search-interceptor.ts @@ -0,0 +1,9 @@ +import { ChromeStaticSearchEntry, FrontendCRD } from './feo-types'; + +function searchInterceptor(staticSearchIndex: ChromeStaticSearchEntry[], frontendCRD: FrontendCRD): ChromeStaticSearchEntry[] { + const frontendRef = frontendCRD.objects[0].metadata.name; + const result = staticSearchIndex.filter((entry) => entry.frontendRef !== frontendRef); + return [...result, ...(frontendCRD.objects[0].spec.searchEntries ?? [])]; +} + +export default searchInterceptor; diff --git a/packages/config-utils/src/feo/service-tiles-interceptor.test.ts b/packages/config-utils/src/feo/service-tiles-interceptor.test.ts new file mode 100644 index 000000000..95baf053a --- /dev/null +++ b/packages/config-utils/src/feo/service-tiles-interceptor.test.ts @@ -0,0 +1,127 @@ +import { FrontendCRD, ServiceCategory } from './feo-types'; +import serviceTilesInterceptor from './service-tiles-interceptor'; + +describe('Service tiles interceptor', () => { + it('should replace service tiles with the ones from the frontendCRD', () => { + const frontendName = 'frontendName'; + const frontendCrd: FrontendCRD = { + objects: [ + { + metadata: { + name: frontendName, + }, + spec: { + module: { + manifestLocation: 'location', + }, + serviceTiles: [ + { + section: 'section-1', + group: 'group-1', + id: 'id-1', + frontendRef: frontendName, + }, + { + section: 'section-1', + group: 'group-1', + id: 'id-2', + frontendRef: frontendName, + }, + { + section: 'section-2', + group: 'group-1', + id: 'id-3', + frontendRef: frontendName, + }, + ], + }, + }, + ], + }; + const remoteServiceTiles: ServiceCategory[] = [ + { + id: 'section-1', + groups: [ + { + id: 'group-1', + tiles: [ + { + section: 'section-1', + group: 'group-1', + id: 'otherFrontend', + frontendRef: 'otherFrontend', + }, + { + section: 'section-1', + group: 'group-1', + id: 'id-2', + frontendRef: frontendName, + }, + ], + }, + ], + }, + { + id: 'section-2', + groups: [ + { + id: 'group-1', + tiles: [ + { + section: 'section-2', + group: 'group-1', + id: 'otherFrontend', + frontendRef: 'otherFrontend', + }, + ], + }, + ], + }, + ]; + const expectedServiceTiles: ServiceCategory[] = [ + { + id: 'section-1', + groups: [ + { + id: 'group-1', + tiles: [ + remoteServiceTiles[0].groups[0].tiles[0], + { + section: 'section-1', + group: 'group-1', + id: 'id-1', + frontendRef: frontendName, + }, + { + section: 'section-1', + group: 'group-1', + id: 'id-2', + frontendRef: frontendName, + }, + ], + }, + ], + }, + { + id: 'section-2', + groups: [ + { + id: 'group-1', + tiles: [ + remoteServiceTiles[1].groups[0].tiles[0], + { + section: 'section-2', + group: 'group-1', + id: 'id-3', + frontendRef: frontendName, + }, + ], + }, + ], + }, + ]; + + const result = serviceTilesInterceptor(remoteServiceTiles, frontendCrd); + expect(result).toEqual(expectedServiceTiles); + }); +}); diff --git a/packages/config-utils/src/feo/service-tiles-interceptor.ts b/packages/config-utils/src/feo/service-tiles-interceptor.ts new file mode 100644 index 000000000..c2793cee3 --- /dev/null +++ b/packages/config-utils/src/feo/service-tiles-interceptor.ts @@ -0,0 +1,42 @@ +import { FrontendCRD, ServiceCategory, ServiceTile } from './feo-types'; + +function serviceTilesInterceptor(serviceCategories: ServiceCategory[], frontendCrd: FrontendCRD): ServiceCategory[] { + const frontendRef = frontendCrd.objects[0].metadata.name; + let result = [...serviceCategories]; + + const frontendCategories = + frontendCrd.objects[0].spec.serviceTiles?.reduce<{ + [section: string]: { [group: string]: ServiceTile[] }; + }>((acc, tile) => { + const section = tile.section; + const group = tile.group; + if (!acc[section]) { + acc[section] = {}; + } + + if (!acc[section][group]) { + acc[section][group] = []; + } + + acc[section][group].push({ ...tile }); + return acc; + }, {}) ?? {}; + + result = result.map((category) => { + const newGroups = category.groups.map((group) => { + const newTiles = group.tiles.filter((tile) => tile.frontendRef !== frontendRef); + return { + ...group, + tiles: [...newTiles, ...(frontendCategories[category.id]?.[group.id] ?? [])], + }; + }); + return { + ...category, + groups: newGroups, + }; + }); + + return result; +} + +export default serviceTilesInterceptor; diff --git a/packages/config-utils/src/feo/widget-registry-interceptor.test.ts b/packages/config-utils/src/feo/widget-registry-interceptor.test.ts new file mode 100644 index 000000000..97120ee8b --- /dev/null +++ b/packages/config-utils/src/feo/widget-registry-interceptor.test.ts @@ -0,0 +1,35 @@ +import { FrontendCRD } from './feo-types'; +import widgetRegistryInterceptor from './widget-registry-interceptor'; + +describe('Widget registry interceptor', () => { + it('should replace the widget registry with the one from the server', () => { + const frontendName = 'name'; + const widgetEntries = [ + { module: 'module1', scope: 'scope1', frontendRef: frontendName }, + { module: 'module1', scope: 'scope2', frontendRef: frontendName }, + { module: 'module2', scope: 'scope1', frontendRef: 'foo' }, + ]; + const frontendCrd: FrontendCRD = { + objects: [ + { + metadata: { + name: 'name', + }, + spec: { + module: { + manifestLocation: 'location', + }, + widgetRegistry: [{ module: 'module1', scope: 'scope1', frontendRef: frontendName }], + }, + }, + ], + }; + + const result = widgetRegistryInterceptor(widgetEntries, frontendCrd); + + expect(result).toEqual([ + { module: 'module2', scope: 'scope1', frontendRef: 'foo' }, + { module: 'module1', scope: 'scope1', frontendRef: frontendName }, + ]); + }); +}); diff --git a/packages/config-utils/src/feo/widget-registry-interceptor.ts b/packages/config-utils/src/feo/widget-registry-interceptor.ts new file mode 100644 index 000000000..fc62c2917 --- /dev/null +++ b/packages/config-utils/src/feo/widget-registry-interceptor.ts @@ -0,0 +1,10 @@ +import { ChromeWidgetEntry, FrontendCRD } from './feo-types'; + +function widgetRegistryInterceptor(widgetEntries: ChromeWidgetEntry[], frontendCrd: FrontendCRD): ChromeWidgetEntry[] { + const frontendName = frontendCrd.objects[0].metadata.name; + const result = widgetEntries.filter((entry) => entry.frontendRef !== frontendName); + + return [...result, ...(frontendCrd.objects[0].spec.widgetRegistry ?? [])]; +} + +export default widgetRegistryInterceptor;