plugin/frontend-*: switch extension namespace from core to app

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-12-18 00:47:10 +01:00
parent 199e660972
commit af7bc3eecb
35 changed files with 180 additions and 172 deletions
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/core-compat-api': minor
'@backstage/frontend-plugin-api': minor
'@backstage/frontend-test-utils': minor
'@backstage/frontend-app-api': minor
---
Switched all core extensions to instead use the namespace `'app'`.
@@ -60,13 +60,13 @@ describe('collectLegacyRoutes', () => {
extensions: [
{
id: 'page:score-card',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'score-board' },
},
{
id: 'api:plugin.scoringdata.service',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
},
],
@@ -76,13 +76,13 @@ describe('collectLegacyRoutes', () => {
extensions: [
{
id: 'page:stackstorm',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'stackstorm' },
},
{
id: 'api:plugin.stackstorm.service',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
},
],
@@ -92,19 +92,19 @@ describe('collectLegacyRoutes', () => {
extensions: [
{
id: 'page:puppetDb',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'puppetdb' },
},
{
id: 'page:puppetDb/1',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'puppetdb' },
},
{
id: 'api:plugin.puppetdb.service',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
},
],
@@ -163,13 +163,13 @@ describe('collectLegacyRoutes', () => {
extensions: [
{
id: 'page:catalog',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'catalog' },
},
{
id: 'page:catalog/1',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
defaultConfig: { path: 'catalog/:namespace/:kind/:name' },
disabled: false,
},
@@ -203,7 +203,7 @@ describe('collectLegacyRoutes', () => {
{
id: 'api:plugin.catalog.service',
attachTo: {
id: 'core',
id: 'app',
input: 'apis',
},
defaultConfig: undefined,
@@ -212,7 +212,7 @@ describe('collectLegacyRoutes', () => {
{
id: 'api:catalog-react.starred-entities',
attachTo: {
id: 'core',
id: 'app',
input: 'apis',
},
defaultConfig: undefined,
@@ -221,7 +221,7 @@ describe('collectLegacyRoutes', () => {
{
id: 'api:plugin.catalog.entity-presentation',
attachTo: {
id: 'core',
id: 'app',
input: 'apis',
},
defaultConfig: undefined,
@@ -234,7 +234,7 @@ describe('collectLegacyRoutes', () => {
extensions: [
{
id: 'api:plugin.scoringdata.service',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
},
],
@@ -60,13 +60,13 @@ describe('convertLegacyApp', () => {
extensions: [
{
id: 'page:score-card',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'score-board' },
},
{
id: 'api:plugin.scoringdata.service',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
},
],
@@ -76,13 +76,13 @@ describe('convertLegacyApp', () => {
extensions: [
{
id: 'page:stackstorm',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'stackstorm' },
},
{
id: 'api:plugin.stackstorm.service',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
},
],
@@ -92,19 +92,19 @@ describe('convertLegacyApp', () => {
extensions: [
{
id: 'page:puppetDb',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'puppetdb' },
},
{
id: 'page:puppetDb/1',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: { path: 'puppetdb' },
},
{
id: 'api:plugin.puppetdb.service',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
},
],
@@ -113,13 +113,13 @@ describe('convertLegacyApp', () => {
id: undefined,
extensions: [
{
id: 'core/layout',
attachTo: { id: 'core', input: 'root' },
id: 'app/layout',
attachTo: { id: 'app', input: 'root' },
disabled: false,
},
{
id: 'core/nav',
attachTo: { id: 'core/layout', input: 'nav' },
id: 'app/nav',
attachTo: { id: 'app/layout', input: 'nav' },
disabled: true,
},
],
@@ -103,9 +103,9 @@ export function convertLegacyApp(
const [routesEl] = routesEls;
const CoreLayoutOverride = createExtension({
namespace: 'core',
namespace: 'app',
name: 'layout',
attachTo: { id: 'core', input: 'root' },
attachTo: { id: 'app', input: 'root' },
inputs: {
content: createExtensionInput(
{
@@ -129,9 +129,9 @@ export function convertLegacyApp(
},
});
const CoreNavOverride = createExtension({
namespace: 'core',
namespace: 'app',
name: 'nav',
attachTo: { id: 'core/layout', input: 'nav' },
attachTo: { id: 'app/layout', input: 'nav' },
output: {},
factory: () => ({}),
disabled: true,
@@ -25,7 +25,7 @@ import {
} from '@backstage/frontend-plugin-api';
export const Core = createExtension({
namespace: 'core',
namespace: 'app',
attachTo: { id: 'root', input: 'default' }, // ignored
inputs: {
apis: createExtensionInput({
@@ -23,9 +23,9 @@ import {
import { SidebarPage } from '@backstage/core-components';
export const CoreLayout = createExtension({
namespace: 'core',
namespace: 'app',
name: 'layout',
attachTo: { id: 'core/router', input: 'children' },
attachTo: { id: 'app/router', input: 'children' },
inputs: {
nav: createExtensionInput(
{
@@ -79,9 +79,9 @@ const SidebarNavItem = (
};
export const CoreNav = createExtension({
namespace: 'core',
namespace: 'app',
name: 'nav',
attachTo: { id: 'core/layout', input: 'nav' },
attachTo: { id: 'app/layout', input: 'nav' },
inputs: {
items: createExtensionInput({
target: createNavItemExtension.targetDataRef,
@@ -35,9 +35,9 @@ import { BrowserRouter } from 'react-router-dom';
import { RouteTracker } from '../routing/RouteTracker';
export const CoreRouter = createExtension({
namespace: 'core',
namespace: 'app',
name: 'router',
attachTo: { id: 'core', input: 'root' },
attachTo: { id: 'app', input: 'root' },
inputs: {
signInPage: createExtensionInput(
{
@@ -25,9 +25,9 @@ import {
import { useRoutes } from 'react-router-dom';
export const CoreRoutes = createExtension({
namespace: 'core',
namespace: 'app',
name: 'routes',
attachTo: { id: 'core/layout', input: 'content' },
attachTo: { id: 'app/layout', input: 'content' },
inputs: {
routes: createExtensionInput({
path: coreExtensionData.routePath,
@@ -49,7 +49,7 @@ function createTestExtension(options: {
name: options.name,
attachTo: options.parent
? { id: `test/${options.parent}`, input: 'children' }
: { id: 'core/routes', input: 'routes' },
: { id: 'app/routes', input: 'routes' },
output: {
element: coreExtensionData.reactElement,
path: coreExtensionData.routePath.optional(),
@@ -24,18 +24,18 @@ import { createAppTree } from './createAppTree';
const extBase = {
id: 'test',
attachTo: { id: 'core', input: 'root' },
attachTo: { id: 'app', input: 'root' },
output: {},
factory: () => ({}),
};
describe('createAppTree', () => {
it('throws an error when a core extension is parametrized', () => {
it('throws an error when a app extension is parametrized', () => {
const config = new MockConfigApi({
app: {
extensions: [
{
core: {},
app: {},
},
],
},
@@ -48,17 +48,17 @@ describe('createAppTree', () => {
];
expect(() =>
createAppTree({ features, config, builtinExtensions: [] }),
).toThrow("Configuration of the 'core' extension is forbidden");
).toThrow("Configuration of the 'app' extension is forbidden");
});
it('throws an error when a core extension is overridden', () => {
it('throws an error when a app extension is overridden', () => {
const config = new MockConfigApi({});
const features = [
createExtensionOverrides({
extensions: [
createExtension({
name: 'core',
attachTo: { id: 'core/routes', input: 'route' },
name: 'app',
attachTo: { id: 'app/routes', input: 'route' },
inputs: {},
output: {},
factory: () => ({}),
@@ -69,7 +69,7 @@ describe('createAppTree', () => {
expect(() =>
createAppTree({ features, config, builtinExtensions: [] }),
).toThrow(
"It is forbidden to override the following extension(s): 'core', which is done by one or more extension overrides",
"It is forbidden to override the following extension(s): 'app', which is done by one or more extension overrides",
);
});
@@ -32,12 +32,12 @@ export interface CreateAppTreeOptions {
/** @internal */
export function createAppTree(options: CreateAppTreeOptions): AppTree {
const tree = resolveAppTree(
'core',
'app',
resolveAppNodeSpecs({
features: options.features,
builtinExtensions: options.builtinExtensions,
parameters: readAppExtensionsConfig(options.config),
forbidden: new Set(['core']),
forbidden: new Set(['app']),
}),
);
instantiateAppNodeTree(tree.root);
@@ -37,7 +37,7 @@ const inputMirrorDataRef = createExtensionDataRef<unknown>('mirror');
const simpleExtension = resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
output: {
@@ -256,7 +256,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
@@ -300,17 +300,17 @@ describe('createAppNodeInstance', () => {
expect(Array.from(instance.getDataRefs())).toEqual([inputMirrorDataRef]);
expect(instance.getData(inputMirrorDataRef)).toMatchObject({
optionalSingletonPresent: {
node: { spec: { id: 'core/test' } },
node: { spec: { id: 'app/test' } },
output: { test: 'optionalSingletonPresent' },
},
singleton: {
node: { spec: { id: 'core/test' } },
node: { spec: { id: 'app/test' } },
output: { test: 'singleton', other: 2 },
},
many: [
{ node: { spec: { id: 'core/test' } }, output: { test: 'many1' } },
{ node: { spec: { id: 'app/test' } }, output: { test: 'many1' } },
{
node: { spec: { id: 'core/test' } },
node: { spec: { id: 'app/test' } },
output: { test: 'many2', other: 3 },
},
],
@@ -324,7 +324,7 @@ describe('createAppNodeInstance', () => {
attachments: new Map(),
}),
).toThrow(
"Invalid configuration for extension 'core/test'; caused by Error: Expected number, received string at 'other'",
"Invalid configuration for extension 'app/test'; caused by Error: Expected number, received string at 'other'",
);
});
@@ -334,7 +334,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
output: {},
@@ -349,7 +349,7 @@ describe('createAppNodeInstance', () => {
attachments: new Map(),
}),
).toThrow(
"Failed to instantiate extension 'core/test'; caused by NopeError: NOPE",
"Failed to instantiate extension 'app/test'; caused by NopeError: NOPE",
);
});
@@ -359,7 +359,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
output: {
@@ -375,7 +375,7 @@ describe('createAppNodeInstance', () => {
attachments: new Map(),
}),
).toThrow(
"Failed to instantiate extension 'core/test', duplicate extension data 'test' received via output 'test2'",
"Failed to instantiate extension 'app/test', duplicate extension data 'test' received via output 'test2'",
);
});
@@ -385,7 +385,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
output: {
@@ -400,7 +400,7 @@ describe('createAppNodeInstance', () => {
attachments: new Map(),
}),
).toThrow(
"Failed to instantiate extension 'core/test', unknown output provided via 'nonexistent'",
"Failed to instantiate extension 'app/test', unknown output provided via 'nonexistent'",
);
});
@@ -410,7 +410,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
@@ -429,7 +429,7 @@ describe('createAppNodeInstance', () => {
attachments: new Map(),
}),
).toThrow(
"Failed to instantiate extension 'core/test', input 'singleton' is required but was not received",
"Failed to instantiate extension 'app/test', input 'singleton' is required but was not received",
);
});
@@ -457,7 +457,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
@@ -472,7 +472,7 @@ describe('createAppNodeInstance', () => {
),
}),
).toThrow(
"Failed to instantiate extension 'core/test', received undeclared input 'undeclared' from extension 'core/test'",
"Failed to instantiate extension 'app/test', received undeclared input 'undeclared' from extension 'app/test'",
);
});
@@ -495,7 +495,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
output: {},
@@ -505,7 +505,7 @@ describe('createAppNodeInstance', () => {
),
}),
).toThrow(
"Failed to instantiate extension 'core/test', received undeclared inputs 'undeclared1' from extension 'core/test' and 'undeclared2' from extensions 'core/test', 'core/test'",
"Failed to instantiate extension 'app/test', received undeclared inputs 'undeclared1' from extension 'app/test' and 'undeclared2' from extensions 'app/test', 'app/test'",
);
});
@@ -524,7 +524,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
@@ -542,7 +542,7 @@ describe('createAppNodeInstance', () => {
),
}),
).toThrow(
"Failed to instantiate extension 'core/test', expected exactly one 'singleton' input but received multiple: 'core/test', 'core/test'",
"Failed to instantiate extension 'app/test', expected exactly one 'singleton' input but received multiple: 'app/test', 'app/test'",
);
});
@@ -561,7 +561,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
@@ -579,7 +579,7 @@ describe('createAppNodeInstance', () => {
),
}),
).toThrow(
"Failed to instantiate extension 'core/test', expected at most one 'singleton' input but received multiple: 'core/test', 'core/test'",
"Failed to instantiate extension 'app/test', expected at most one 'singleton' input but received multiple: 'app/test', 'app/test'",
);
});
@@ -592,7 +592,7 @@ describe('createAppNodeInstance', () => {
node: makeNode(
resolveExtensionDefinition(
createExtension({
namespace: 'core',
namespace: 'app',
name: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
@@ -610,7 +610,7 @@ describe('createAppNodeInstance', () => {
),
}),
).toThrow(
"Failed to instantiate extension 'core/test', input 'singleton' did not receive required extension data 'other' from extension 'core/test'",
"Failed to instantiate extension 'app/test', input 'singleton' did not receive required extension data 'other' from extension 'app/test'",
);
});
});
@@ -25,18 +25,18 @@ describe('readAppExtensionsConfig', () => {
it('should disable extension with shorthand notation', () => {
expect(
readAppExtensionsConfig(
new ConfigReader({ app: { extensions: [{ 'core/router': false }] } }),
new ConfigReader({ app: { extensions: [{ 'app/router': false }] } }),
),
).toEqual([
{
id: 'core/router',
id: 'app/router',
disabled: true,
},
]);
expect(
readAppExtensionsConfig(
new ConfigReader({
app: { extensions: [{ 'core/router': { disabled: true } }] },
app: { extensions: [{ 'app/router': { disabled: true } }] },
}),
),
).toEqual([
@@ -44,7 +44,7 @@ describe('readAppExtensionsConfig', () => {
at: undefined,
config: undefined,
disabled: true,
id: 'core/router',
id: 'app/router',
},
]);
});
@@ -52,33 +52,33 @@ describe('readAppExtensionsConfig', () => {
it('should enable extension with shorthand notation', () => {
expect(
readAppExtensionsConfig(
new ConfigReader({ app: { extensions: ['core/router'] } }),
new ConfigReader({ app: { extensions: ['app/router'] } }),
),
).toEqual([
{
id: 'core/router',
id: 'app/router',
disabled: false,
},
]);
expect(
readAppExtensionsConfig(
new ConfigReader({ app: { extensions: [{ 'core/router': true }] } }),
new ConfigReader({ app: { extensions: [{ 'app/router': true }] } }),
),
).toEqual([
{
id: 'core/router',
id: 'app/router',
disabled: false,
},
]);
expect(
readAppExtensionsConfig(
new ConfigReader({
app: { extensions: [{ 'core/router': { disabled: false } }] },
app: { extensions: [{ 'app/router': { disabled: false } }] },
}),
),
).toEqual([
{
id: 'core/router',
id: 'app/router',
disabled: false,
},
]);
@@ -89,12 +89,12 @@ describe('readAppExtensionsConfig', () => {
readAppExtensionsConfig(
new ConfigReader({
app: {
extensions: [{ 'core/router': 'some-string' }],
extensions: [{ 'app/router': 'some-string' }],
},
}),
),
).toThrow(
'Invalid extension configuration at app.extensions[0][core/router], value must be a boolean or object',
'Invalid extension configuration at app.extensions[0][app/router], value must be a boolean or object',
);
});
@@ -156,8 +156,8 @@ describe('expandShorthandExtensionParameters', () => {
});
it('supports string key', () => {
expect(run('core/router')).toEqual({
id: 'core/router',
expect(run('app/router')).toEqual({
id: 'app/router',
disabled: false,
});
expect(() => run('')).toThrowErrorMatchingInlineSnapshot(
@@ -170,96 +170,96 @@ describe('expandShorthandExtensionParameters', () => {
it('supports null value', () => {
// this is the result of typing:
// - core/router:
// - app/router:
// The missing value is interpreted as null by the yaml parser so we deal with that
expect(run({ 'core/router': null })).toEqual({
id: 'core/router',
expect(run({ 'app/router': null })).toEqual({
id: 'app/router',
disabled: false,
});
});
it('supports boolean value', () => {
expect(run({ 'core/router': true })).toEqual({
id: 'core/router',
expect(run({ 'app/router': true })).toEqual({
id: 'app/router',
disabled: false,
});
expect(run({ 'core/router': false })).toEqual({
id: 'core/router',
expect(run({ 'app/router': false })).toEqual({
id: 'app/router',
disabled: true,
});
});
it('should not support string values', () => {
expect(() =>
run({ 'core/router': 'example-package#MyRouter' }),
run({ 'app/router': 'example-package#MyRouter' }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core/router], value must be a boolean or object"`,
`"Invalid extension configuration at app.extensions[1][app/router], value must be a boolean or object"`,
);
});
it('supports object id only in the key', () => {
expect(() =>
run({ 'core/router': { id: 'some.id' } }),
run({ 'app/router': { id: 'some.id' } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core/router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][app/router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
it('supports object attachTo', () => {
expect(
run({
'core/router': { attachTo: { id: 'other.root', input: 'inputs' } },
'app/router': { attachTo: { id: 'other.root', input: 'inputs' } },
}),
).toEqual({
id: 'core/router',
id: 'app/router',
attachTo: { id: 'other.root', input: 'inputs' },
});
expect(() =>
run({
'core/router': {
'app/router': {
id: 'other-id',
},
}),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core/router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][app/router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
it('supports object disabled', () => {
expect(run({ 'core/router': { disabled: true } })).toEqual({
id: 'core/router',
expect(run({ 'app/router': { disabled: true } })).toEqual({
id: 'app/router',
disabled: true,
});
expect(run({ 'core/router': { disabled: false } })).toEqual({
id: 'core/router',
expect(run({ 'app/router': { disabled: false } })).toEqual({
id: 'app/router',
disabled: false,
});
expect(() =>
run({ 'core/router': { disabled: 0 } }),
run({ 'app/router': { disabled: 0 } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core/router].disabled, must be a boolean"`,
`"Invalid extension configuration at app.extensions[1][app/router].disabled, must be a boolean"`,
);
});
it('supports object config', () => {
expect(
run({ 'core/router': { config: { disableRedirects: true } } }),
run({ 'app/router': { config: { disableRedirects: true } } }),
).toEqual({
id: 'core/router',
id: 'app/router',
config: { disableRedirects: true },
});
expect(() =>
run({ 'core/router': { config: 0 } }),
run({ 'app/router': { config: 0 } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core/router].config, must be an object"`,
`"Invalid extension configuration at app.extensions[1][app/router].config, must be an object"`,
);
});
it('rejects unknown object keys', () => {
expect(() =>
run({ 'core/router': { foo: { settings: true } } }),
run({ 'app/router': { foo: { settings: true } } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core/router].foo, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][app/router].foo, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
});
@@ -36,19 +36,19 @@ const baseSpec = {
describe('buildAppTree', () => {
it('should fail to create an empty tree', () => {
expect(() => resolveAppTree('core', [])).toThrow(
"No root node with id 'core' found in app tree",
expect(() => resolveAppTree('app', [])).toThrow(
"No root node with id 'app' found in app tree",
);
});
it('should create a tree with only one node', () => {
const tree = resolveAppTree('core', [{ ...baseSpec, id: 'core' }]);
const tree = resolveAppTree('app', [{ ...baseSpec, id: 'app' }]);
expect(tree.root).toEqual({
spec: { ...baseSpec, id: 'core' },
spec: { ...baseSpec, id: 'app' },
edges: { attachments: new Map() },
});
expect(Array.from(tree.orphans)).toEqual([]);
expect(Array.from(tree.nodes.keys())).toEqual(['core']);
expect(Array.from(tree.nodes.keys())).toEqual(['app']);
});
it('should create a tree', () => {
@@ -159,7 +159,7 @@ describe('buildAppTree', () => {
it('throws an error when duplicated extensions are detected', () => {
expect(() =>
resolveAppTree('core', [
resolveAppTree('app', [
{ ...baseSpec, id: 'a' },
{ ...baseSpec, id: 'a' },
]),
@@ -169,7 +169,7 @@ describe('createApp', () => {
extensions: [
createExtension({
name: 'first',
attachTo: { id: 'core', input: 'root' },
attachTo: { id: 'app', input: 'root' },
output: { element: coreExtensionData.reactElement },
factory() {
const Component = () => {
@@ -193,9 +193,9 @@ describe('createApp', () => {
featureFlags: [{ name: 'test-2' }],
extensions: [
createExtension({
namespace: 'core',
namespace: 'app',
name: 'router',
attachTo: { id: 'core', input: 'root' },
attachTo: { id: 'app', input: 'root' },
disabled: true,
output: {},
factory: () => ({}),
@@ -242,24 +242,24 @@ describe('createApp', () => {
const { tree } = appTreeApi!.getTree();
expect(String(tree.root)).toMatchInlineSnapshot(`
"<core out=[core.reactElement]>
"<app out=[core.reactElement]>
root [
<core/router out=[core.reactElement]>
<app/router out=[core.reactElement]>
children [
<core/layout out=[core.reactElement]>
<app/layout out=[core.reactElement]>
content [
<core/routes out=[core.reactElement]>
<app/routes out=[core.reactElement]>
routes [
<page:my-plugin out=[core.routing.path, core.routing.ref, core.reactElement] />
]
</core/routes>
</app/routes>
]
nav [
<core/nav out=[core.reactElement] />
<app/nav out=[core.reactElement] />
]
</core/layout>
</app/layout>
]
</core/router>
</app/router>
]
components [
<component:core.components.progress out=[core.component.component] />
@@ -289,7 +289,7 @@ describe('createApp', () => {
<api:core.auth.atlassian out=[core.api.factory] />
<api:plugin.permission.api out=[core.api.factory] />
]
</core>"
</app>"
`);
});
});
@@ -172,7 +172,7 @@ export function createExtensionTree(options: {
);
},
getRootRoutes(): JSX.Element[] {
return this.getExtensionAttachments('core/routes', 'routes').map(node => {
return this.getExtensionAttachments('app/routes', 'routes').map(node => {
const path = node.getData(coreExtensionData.routePath);
const element = node.getData(coreExtensionData.reactElement);
const routeRef = node.getData(coreExtensionData.routeRef);
@@ -199,7 +199,7 @@ export function createExtensionTree(options: {
);
};
return this.getExtensionAttachments('core/nav', 'items')
return this.getExtensionAttachments('app/nav', 'items')
.map((node, index) => {
const target = node.getData(createNavItemExtension.targetDataRef);
if (!target) {
@@ -27,7 +27,7 @@ const wrapInBoundaryExtension = (element: JSX.Element) => {
const routeRef = createRouteRef();
return createExtension({
name: 'test',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
output: {
element: coreExtensionData.reactElement,
path: coreExtensionData.routePath,
@@ -35,7 +35,7 @@ describe('createApiExtension', () => {
version: 'v1',
kind: 'api',
namespace: 'test',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
configSchema: undefined,
inputs: {},
@@ -71,7 +71,7 @@ describe('createApiExtension', () => {
version: 'v1',
kind: 'api',
namespace: 'test',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
disabled: false,
configSchema: undefined,
inputs: {},
@@ -55,7 +55,7 @@ export function createApiExtension<
// Since ApiRef IDs use a global namespace we use the namespace here in order to override
// potential plugin IDs and always end up with the format `api:<api-ref-id>`
namespace: apiRef.id,
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
inputs: extensionInputs,
configSchema,
output: {
@@ -54,7 +54,7 @@ export function createComponentExtension<
kind: 'component',
namespace: options.ref.id,
name: options.name,
attachTo: { id: 'core', input: 'components' },
attachTo: { id: 'app', input: 'components' },
inputs: options.inputs,
disabled: options.disabled,
configSchema: options.configSchema,
@@ -35,7 +35,7 @@ export function createNavItemExtension(options: {
namespace,
name,
kind: 'nav-item',
attachTo: { id: 'core/nav', input: 'items' },
attachTo: { id: 'app/nav', input: 'items' },
configSchema: createSchemaFromZod(z =>
z.object({
title: z.string().default(title),
@@ -34,7 +34,7 @@ describe('createNavLogoExtension', () => {
version: 'v1',
kind: 'nav-logo',
name: 'test',
attachTo: { id: 'core/nav', input: 'logos' },
attachTo: { id: 'app/nav', input: 'logos' },
disabled: false,
inputs: {},
output: {
@@ -31,7 +31,7 @@ export function createNavLogoExtension(options: {
kind: 'nav-logo',
name: options?.name,
namespace: options?.namespace,
attachTo: { id: 'core/nav', input: 'logos' },
attachTo: { id: 'app/nav', input: 'logos' },
output: {
logos: createNavLogoExtension.logoElementsDataRef,
},
@@ -45,7 +45,7 @@ describe('createPageExtension', () => {
version: 'v1',
name: 'test',
kind: 'page',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
configSchema: expect.anything(),
disabled: false,
inputs: {},
@@ -102,7 +102,7 @@ describe('createPageExtension', () => {
version: 'v1',
name: 'test',
kind: 'page',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
configSchema: expect.anything(),
disabled: false,
inputs: {},
@@ -67,7 +67,7 @@ export function createPageExtension<
kind: 'page',
namespace: options.namespace,
name: options.name,
attachTo: options.attachTo ?? { id: 'core/routes', input: 'routes' },
attachTo: options.attachTo ?? { id: 'app/routes', input: 'routes' },
configSchema,
inputs: options.inputs,
disabled: options.disabled,
@@ -50,7 +50,7 @@ export function createSignInPageExtension<
kind: 'sign-in-page',
namespace: options?.namespace,
name: options?.name,
attachTo: options.attachTo ?? { id: 'core/router', input: 'signInPage' },
attachTo: options.attachTo ?? { id: 'app/router', input: 'signInPage' },
configSchema: options.configSchema,
inputs: options.inputs,
disabled: options.disabled,
@@ -23,7 +23,7 @@ export function createThemeExtension(theme: AppTheme) {
kind: 'theme',
namespace: 'app',
name: theme.id,
attachTo: { id: 'core', input: 'themes' },
attachTo: { id: 'app', input: 'themes' },
output: {
theme: createThemeExtension.themeDataRef,
},
@@ -44,7 +44,7 @@ describe('createTranslationExtension', () => {
version: 'v1',
kind: 'translation',
namespace: 'test',
attachTo: { id: 'core', input: 'translations' },
attachTo: { id: 'app', input: 'translations' },
disabled: false,
inputs: {},
output: {
@@ -81,7 +81,7 @@ describe('createTranslationExtension', () => {
version: 'v1',
kind: 'translation',
namespace: 'test',
attachTo: { id: 'core', input: 'translations' },
attachTo: { id: 'app', input: 'translations' },
disabled: false,
inputs: {},
output: {
@@ -119,7 +119,7 @@ describe('createTranslationExtension', () => {
kind: 'translation',
namespace: 'test',
name: 'sv',
attachTo: { id: 'core', input: 'translations' },
attachTo: { id: 'app', input: 'translations' },
disabled: false,
inputs: {},
output: {
@@ -26,7 +26,7 @@ export function createTranslationExtension(options: {
kind: 'translation',
namespace: options.resource.id,
name: options.name,
attachTo: { id: 'core', input: 'translations' },
attachTo: { id: 'app', input: 'translations' },
output: {
resource: createTranslationExtension.translationDataRef,
},
@@ -38,13 +38,13 @@ describe('createExtensionOverrides', () => {
extensions: [
createExtension({
name: 'a',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
output: {},
factory: () => ({}),
}),
createExtension({
namespace: 'b',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
output: {},
factory: () => ({}),
}),
@@ -52,7 +52,7 @@ describe('createExtensionOverrides', () => {
kind: 'k',
namespace: 'c',
name: 'n',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
output: {},
factory: () => ({}),
}),
@@ -65,7 +65,7 @@ describe('createExtensionOverrides', () => {
{
"$$type": "@backstage/Extension",
"attachTo": {
"id": "core",
"id": "app",
"input": "apis",
},
"configSchema": undefined,
@@ -79,7 +79,7 @@ describe('createExtensionOverrides', () => {
{
"$$type": "@backstage/Extension",
"attachTo": {
"id": "core",
"id": "app",
"input": "apis",
},
"configSchema": undefined,
@@ -93,7 +93,7 @@ describe('createExtensionOverrides', () => {
{
"$$type": "@backstage/Extension",
"attachTo": {
"id": "core",
"id": "app",
"input": "apis",
},
"configSchema": undefined,
@@ -116,7 +116,7 @@ describe('createExtensionOverrides', () => {
extensions: [
createExtension({
namespace: 'a',
attachTo: { id: 'core', input: 'apis' },
attachTo: { id: 'app', input: 'apis' },
output: {},
factory: () => ({}),
}),
@@ -102,7 +102,7 @@ const Child2 = createExtension({
const outputExtension = createExtension({
name: 'output',
attachTo: { id: 'core', input: 'root' },
attachTo: { id: 'app', input: 'root' },
inputs: {
names: createExtensionInput({
name: nameExtensionDataRef,
@@ -150,7 +150,7 @@ describe('createPlugin', () => {
await renderWithEffects(
createTestAppRoot({
features: [plugin],
config: { app: { extensions: [{ 'core/router': false }] } },
config: { app: { extensions: [{ 'app/router': false }] } },
}),
);
@@ -172,7 +172,7 @@ describe('createPlugin', () => {
config: {
app: {
extensions: [
{ 'core/router': false },
{ 'app/router': false },
{
'test/2': {
config: { name: 'extension-2-renamed' },
@@ -210,7 +210,7 @@ describe('createPlugin', () => {
features: [plugin],
config: {
app: {
extensions: [{ 'core/router': false }],
extensions: [{ 'app/router': false }],
},
},
}),
@@ -64,7 +64,7 @@ describe('createExtensionTester', () => {
});
const tester = createExtensionTester(extension);
expect(() => tester.render()).toThrow(
"Failed to instantiate extension 'core/routes', input 'routes' did not receive required extension data 'core.reactElement' from extension 'test'",
"Failed to instantiate extension 'app/routes', input 'routes' did not receive required extension data 'core.reactElement' from extension 'test'",
);
});
@@ -82,7 +82,7 @@ describe('createExtensionTester', () => {
const detailsPageExtension = createExtension({
...defaultDefinition,
name: 'details',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
output: {
path: coreExtensionData.routePath,
element: coreExtensionData.reactElement,
@@ -130,7 +130,7 @@ describe('createExtensionTester', () => {
const detailsPageExtension = createExtension({
...defaultDefinition,
name: 'details',
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
configSchema: createSchemaFromZod(z =>
z.object({ title: z.string().optional() }),
),
@@ -64,9 +64,9 @@ const NavItem = (props: {
};
const TestCoreNavExtension = createExtension({
namespace: 'core',
namespace: 'app',
name: 'nav',
attachTo: { id: 'core/layout', input: 'nav' },
attachTo: { id: 'app/layout', input: 'nav' },
inputs: {
items: createExtensionInput({
target: createNavItemExtension.targetDataRef,
@@ -150,9 +150,9 @@ const AuthenticationProvider = (props: {
};
const TestCoreRouterExtension = createExtension({
namespace: 'core',
namespace: 'app',
name: 'router',
attachTo: { id: 'core', input: 'root' },
attachTo: { id: 'app', input: 'root' },
inputs: {
signInPage: createExtensionInput(
{
@@ -195,10 +195,10 @@ export class ExtensionTester {
): ExtensionTester {
const tester = new ExtensionTester();
const { output, factory, ...rest } = toInternalExtensionDefinition(subject);
// attaching to core/routes to render as index route
// attaching to app/routes to render as index route
const extension = createExtension({
...rest,
attachTo: { id: 'core/routes', input: 'routes' },
attachTo: { id: 'app/routes', input: 'routes' },
output: {
...output,
path: coreExtensionData.routePath,
@@ -27,7 +27,7 @@ import { createExtensionTester } from './createExtensionTester';
export function renderInTestApp(element: JSX.Element) {
const extension = createExtension({
namespace: 'test',
attachTo: { id: 'core', input: 'root' },
attachTo: { id: 'app', input: 'root' },
output: {
element: coreExtensionData.reactElement,
},