frontend-*-api: introduce ExtensionBoundary and pass source to extension factories
Co-authored-by: Fredrik Adelöw <freben@gmail.com> Co-authored-by: Johan Haals <johan.haals@gmail.com> Co-authored-by: Camila Belo <camilaibs@gmail.com> Co-authored-by: Philipp Hugenroth <philipph@spotify.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -41,13 +41,11 @@ export function createApp(options: { plugins: BackstagePlugin[] }): {
|
||||
|
||||
// pull in default extension instance from discovered packages
|
||||
// apply config to adjust default extension instances and add more
|
||||
const extensionParams = mergeExtensionParameters(
|
||||
[
|
||||
...options.plugins.flatMap(plugin => plugin.extensions),
|
||||
...builtinExtensions,
|
||||
],
|
||||
readAppExtensionParameters(appConfig),
|
||||
);
|
||||
const extensionParams = mergeExtensionParameters({
|
||||
sources: options.plugins,
|
||||
builtinExtensions,
|
||||
parameters: readAppExtensionParameters(appConfig),
|
||||
});
|
||||
|
||||
// TODO: validate the config of all extension instances
|
||||
// We do it at this point to ensure that merging (if any) of config has already happened
|
||||
@@ -96,6 +94,7 @@ export function createApp(options: { plugins: BackstagePlugin[] }): {
|
||||
|
||||
return createExtensionInstance({
|
||||
extension: instanceParams.extension,
|
||||
source: instanceParams.source,
|
||||
config: instanceParams.config,
|
||||
attachments,
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Extension } from '@backstage/frontend-plugin-api';
|
||||
import { BackstagePlugin, Extension } from '@backstage/frontend-plugin-api';
|
||||
import mapValues from 'lodash/mapValues';
|
||||
|
||||
/** @internal */
|
||||
@@ -28,9 +28,10 @@ export interface ExtensionInstance {
|
||||
export function createExtensionInstance(options: {
|
||||
extension: Extension<unknown>;
|
||||
config: unknown;
|
||||
source?: BackstagePlugin;
|
||||
attachments: Record<string, ExtensionInstance[]>;
|
||||
}): ExtensionInstance {
|
||||
const { extension, config, attachments } = options;
|
||||
const { extension, config, source, attachments } = options;
|
||||
const extensionData = new Map<string, unknown>();
|
||||
|
||||
let parsedConfig: unknown;
|
||||
@@ -44,6 +45,7 @@ export function createExtensionInstance(options: {
|
||||
|
||||
try {
|
||||
extension.factory({
|
||||
source,
|
||||
config: parsedConfig,
|
||||
bind: mapValues(extension.output, ref => {
|
||||
return (value: unknown) => extensionData.set(ref.id, value);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { Extension } from '@backstage/frontend-plugin-api';
|
||||
import { createPlugin, Extension } from '@backstage/frontend-plugin-api';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import {
|
||||
expandShorthandExtensionParameters,
|
||||
@@ -33,15 +33,25 @@ function makeExt(id: string, status: 'disabled' | 'enabled' = 'enabled') {
|
||||
|
||||
describe('mergeExtensionParameters', () => {
|
||||
it('should filter out disabled extension instances', () => {
|
||||
expect(mergeExtensionParameters([makeExt('a', 'disabled')], [])).toEqual(
|
||||
[],
|
||||
);
|
||||
expect(
|
||||
mergeExtensionParameters({
|
||||
sources: [],
|
||||
builtinExtensions: [makeExt('a', 'disabled')],
|
||||
parameters: [],
|
||||
}),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('should pass through extension instances', () => {
|
||||
const a = makeExt('a');
|
||||
const b = makeExt('b');
|
||||
expect(mergeExtensionParameters([a, b], [])).toEqual([
|
||||
expect(
|
||||
mergeExtensionParameters({
|
||||
sources: [],
|
||||
builtinExtensions: [a, b],
|
||||
parameters: [],
|
||||
}),
|
||||
).toEqual([
|
||||
{ extension: a, at: 'root' },
|
||||
{ extension: b, at: 'root' },
|
||||
]);
|
||||
@@ -50,18 +60,20 @@ describe('mergeExtensionParameters', () => {
|
||||
it('should override attachment points', () => {
|
||||
const a = makeExt('a');
|
||||
const b = makeExt('b');
|
||||
const pluginA = createPlugin({ id: 'test', extensions: [a] });
|
||||
expect(
|
||||
mergeExtensionParameters(
|
||||
[a, b],
|
||||
[
|
||||
mergeExtensionParameters({
|
||||
sources: [pluginA],
|
||||
builtinExtensions: [b],
|
||||
parameters: [
|
||||
{
|
||||
id: 'b',
|
||||
at: 'derp',
|
||||
},
|
||||
],
|
||||
),
|
||||
}),
|
||||
).toEqual([
|
||||
{ extension: a, at: 'root' },
|
||||
{ extension: a, at: 'root', source: pluginA },
|
||||
{ extension: b, at: 'derp' },
|
||||
]);
|
||||
});
|
||||
@@ -69,10 +81,12 @@ describe('mergeExtensionParameters', () => {
|
||||
it('should fully override configuration and duplicate', () => {
|
||||
const a = makeExt('a');
|
||||
const b = makeExt('b');
|
||||
const plugin = createPlugin({ id: 'test', extensions: [a, b] });
|
||||
expect(
|
||||
mergeExtensionParameters(
|
||||
[a, b],
|
||||
[
|
||||
mergeExtensionParameters({
|
||||
sources: [plugin],
|
||||
builtinExtensions: [],
|
||||
parameters: [
|
||||
{
|
||||
id: 'a',
|
||||
config: { foo: { bar: 1 } },
|
||||
@@ -86,10 +100,10 @@ describe('mergeExtensionParameters', () => {
|
||||
config: { foo: { qux: 3 } },
|
||||
},
|
||||
],
|
||||
),
|
||||
}),
|
||||
).toEqual([
|
||||
{ extension: a, at: 'root', config: { foo: { bar: 1 } } },
|
||||
{ extension: b, at: 'root', config: { foo: { qux: 3 } } },
|
||||
{ extension: a, at: 'root', source: plugin, config: { foo: { bar: 1 } } },
|
||||
{ extension: b, at: 'root', source: plugin, config: { foo: { qux: 3 } } },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -97,9 +111,10 @@ describe('mergeExtensionParameters', () => {
|
||||
const a = makeExt('a', 'disabled');
|
||||
const b = makeExt('b', 'disabled');
|
||||
expect(
|
||||
mergeExtensionParameters(
|
||||
[a, b],
|
||||
[
|
||||
mergeExtensionParameters({
|
||||
sources: [createPlugin({ id: 'empty', extensions: [] })],
|
||||
builtinExtensions: [a, b],
|
||||
parameters: [
|
||||
{
|
||||
id: 'b',
|
||||
disabled: false,
|
||||
@@ -109,7 +124,7 @@ describe('mergeExtensionParameters', () => {
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
),
|
||||
}),
|
||||
).toEqual([
|
||||
{ extension: b, at: 'root' },
|
||||
{ extension: a, at: 'root' },
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import { Extension } from '@backstage/frontend-plugin-api';
|
||||
import { BackstagePlugin, Extension } from '@backstage/frontend-plugin-api';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
|
||||
export interface ExtensionParameters {
|
||||
@@ -183,23 +183,41 @@ export function expandShorthandExtensionParameters(
|
||||
|
||||
export interface ExtensionInstanceParameters {
|
||||
extension: Extension<unknown>;
|
||||
source?: BackstagePlugin;
|
||||
at: string;
|
||||
config?: unknown;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function mergeExtensionParameters(
|
||||
base: Extension<unknown>[],
|
||||
parameters: Array<ExtensionParameters>,
|
||||
): ExtensionInstanceParameters[] {
|
||||
const overrides = base.map(extension => ({
|
||||
extension,
|
||||
params: {
|
||||
at: extension.at,
|
||||
disabled: extension.disabled,
|
||||
config: undefined as unknown,
|
||||
},
|
||||
}));
|
||||
export function mergeExtensionParameters(options: {
|
||||
sources: BackstagePlugin[];
|
||||
builtinExtensions: Extension<unknown>[];
|
||||
parameters: Array<ExtensionParameters>;
|
||||
}): ExtensionInstanceParameters[] {
|
||||
const { sources, builtinExtensions, parameters } = options;
|
||||
|
||||
const overrides = [
|
||||
...sources.flatMap(plugin =>
|
||||
plugin.extensions.map(extension => ({
|
||||
extension,
|
||||
params: {
|
||||
source: plugin,
|
||||
at: extension.at,
|
||||
disabled: extension.disabled,
|
||||
config: undefined as unknown,
|
||||
},
|
||||
})),
|
||||
),
|
||||
...builtinExtensions.map(extension => ({
|
||||
extension,
|
||||
params: {
|
||||
source: undefined,
|
||||
at: extension.at,
|
||||
disabled: extension.disabled,
|
||||
config: undefined as unknown,
|
||||
},
|
||||
})),
|
||||
];
|
||||
|
||||
for (const overrideParam of parameters) {
|
||||
const existingIndex = overrides.findIndex(
|
||||
@@ -234,6 +252,7 @@ export function mergeExtensionParameters(
|
||||
.map(param => ({
|
||||
extension: param.extension,
|
||||
at: param.params.at,
|
||||
source: param.params.source,
|
||||
config: param.params.config,
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user