From 89dc1250d86138f73b4b7e927e55c56b326291bc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Oct 2023 19:18:10 +0200 Subject: [PATCH] frontend-app-api: make it possible to configure which extensions are forbidden Signed-off-by: Patrik Oldsberg --- .../src/graph/createAppGraph.ts | 2 + .../src/graph/resolveAppNodeSpecs.test.ts | 41 +++++++++++++++++++ .../src/graph/resolveAppNodeSpecs.ts | 28 ++++++++----- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/packages/frontend-app-api/src/graph/createAppGraph.ts b/packages/frontend-app-api/src/graph/createAppGraph.ts index 4c08b7c5b4..cb72fa3cd1 100644 --- a/packages/frontend-app-api/src/graph/createAppGraph.ts +++ b/packages/frontend-app-api/src/graph/createAppGraph.ts @@ -40,7 +40,9 @@ export function createAppGraph(options: CreateAppGraphOptions): AppGraph { features: options.features, builtinExtensions: options.builtinExtensions, parameters: readAppExtensionsConfig(options.config), + forbidden: new Set(['core']), }), + 'core', ); instantiateAppNodeTree(appGraph.root); return appGraph; diff --git a/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.test.ts b/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.test.ts index a3cda23113..59930968c6 100644 --- a/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.test.ts +++ b/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.test.ts @@ -234,4 +234,45 @@ describe('resolveAppNodeSpecs', () => { expect(result.map(r => r.extension.id)).toEqual(['b', 'c', 'a']); }); + + it('throws an error when a forbidden extension is overridden by a plugin', () => { + expect(() => + resolveAppNodeSpecs({ + features: [ + createPlugin({ id: 'test', extensions: [makeExt('forbidden')] }), + ], + builtinExtensions: [], + parameters: [], + forbidden: new Set(['forbidden']), + }), + ).toThrow( + "It is forbidden to override the following extension(s): 'forbidden', which is done by the following plugin(s): 'test'", + ); + }); + + it('throws an error when a forbidden extension is overridden by overrides', () => { + expect(() => + resolveAppNodeSpecs({ + features: [ + createExtensionOverrides({ extensions: [makeExt('forbidden')] }), + ], + builtinExtensions: [], + parameters: [], + forbidden: new Set(['forbidden']), + }), + ).toThrow( + "It is forbidden to override the following extension(s): 'forbidden', which is done by one or more extension overrides", + ); + }); + + it('throws an error when a forbidden extension is parametrized', () => { + expect(() => + resolveAppNodeSpecs({ + features: [], + builtinExtensions: [], + parameters: [{ id: 'forbidden', disabled: false }], + forbidden: new Set(['forbidden']), + }), + ).toThrow("Configuration of the 'forbidden' extension is forbidden"); + }); }); diff --git a/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.ts index d6cfcaede2..ca3c4fd1ea 100644 --- a/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.ts @@ -29,8 +29,9 @@ export function resolveAppNodeSpecs(options: { features: (BackstagePlugin | ExtensionOverrides)[]; builtinExtensions: Extension[]; parameters: Array; + forbidden?: Set; }): AppNodeSpec[] { - const { builtinExtensions, parameters } = options; + const { builtinExtensions, parameters, forbidden = new Set() } = options; const plugins = options.features.filter( (f): f is BackstagePlugin => f.$$type === '@backstage/BackstagePlugin', @@ -48,20 +49,21 @@ export function resolveAppNodeSpecs(options: { ); // Prevent core override - if (pluginExtensions.some(({ id }) => id === 'core')) { - const pluginIds = pluginExtensions - .filter(({ id }) => id === 'core') - .map(({ source }) => source.id); + if (pluginExtensions.some(({ id }) => forbidden.has(id))) { + const pluginsStr = pluginExtensions + .filter(({ id }) => forbidden.has(id)) + .map(({ source }) => `'${source.id}'`) + .join(', '); + const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', '); throw new Error( - `The following plugin(s) are overriding the 'core' extension which is forbidden: ${pluginIds.join( - ',', - )}`, + `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by the following plugin(s): ${pluginsStr}`, ); } - if (overrideExtensions.some(({ id }) => id === 'root')) { + if (overrideExtensions.some(({ id }) => forbidden.has(id))) { + const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', '); throw new Error( - `An extension override is overriding the 'root' extension which is forbidden`, + `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by one or more extension overrides`, ); } const overrideExtensionIds = overrideExtensions.map(({ id }) => id); @@ -164,6 +166,12 @@ export function resolveAppNodeSpecs(options: { for (const overrideParam of parameters) { const extensionId = overrideParam.id; + if (forbidden.has(extensionId)) { + throw new Error( + `Configuration of the '${extensionId}' extension is forbidden`, + ); + } + const existingIndex = configuredExtensions.findIndex( e => e.extension.id === extensionId, );