frontend-plugin-api: wrap resolved extension inputs in an object
Co-authored-by: Camila Belo <camilaibs@gmail.com> Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com> Co-authored-by: Fredrik Adelöw <freben@gmail.com> Co-authored-by: Philipp Hugenroth <philipph@spotify.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
'@backstage/core-compat-api': patch
|
||||
---
|
||||
|
||||
Updates to match the new extension input wrapping.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
'@backstage/plugin-user-settings': patch
|
||||
'@backstage/plugin-graphiql': patch
|
||||
'@backstage/plugin-catalog': patch
|
||||
'@backstage/plugin-search': patch
|
||||
'@backstage/plugin-home': patch
|
||||
---
|
||||
|
||||
Updates to the `/alpha` exports to match the extension input wrapping change.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': minor
|
||||
---
|
||||
|
||||
Extension inputs are now wrapped into an additional object when passed to the extension factory, with the previous values being available at the `output` property. The `ExtensionInputValues` type has also been replaced by `ResolvedExtensionInputs`.
|
||||
@@ -117,7 +117,11 @@ export function convertLegacyApp(
|
||||
factory({ inputs }) {
|
||||
// Clone the root element, this replaces the FlatRoutes declared in the app with out content input
|
||||
return {
|
||||
element: React.cloneElement(rootEl, undefined, inputs.content.element),
|
||||
element: React.cloneElement(
|
||||
rootEl,
|
||||
undefined,
|
||||
inputs.content.output.element,
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ export const Core = createExtension({
|
||||
},
|
||||
factory({ inputs }) {
|
||||
return {
|
||||
root: inputs.root.element,
|
||||
root: inputs.root.output.element,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -47,8 +47,8 @@ export const CoreLayout = createExtension({
|
||||
return {
|
||||
element: (
|
||||
<SidebarPage>
|
||||
{inputs.nav.element}
|
||||
{inputs.content.element}
|
||||
{inputs.nav.output.element}
|
||||
{inputs.content.output.element}
|
||||
</SidebarPage>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -99,10 +99,10 @@ export const CoreNav = createExtension({
|
||||
return {
|
||||
element: (
|
||||
<Sidebar>
|
||||
<SidebarLogo {...inputs.logos?.elements} />
|
||||
<SidebarLogo {...inputs.logos?.output.elements} />
|
||||
<SidebarDivider />
|
||||
{inputs.items.map((item, index) => (
|
||||
<SidebarNavItem {...item.target} key={index} />
|
||||
<SidebarNavItem {...item.output.target} key={index} />
|
||||
))}
|
||||
</Sidebar>
|
||||
),
|
||||
|
||||
@@ -59,8 +59,8 @@ export const CoreRouter = createExtension({
|
||||
factory({ inputs }) {
|
||||
return {
|
||||
element: (
|
||||
<AppRouter SignInPageComponent={inputs.signInPage?.component}>
|
||||
{inputs.children.element}
|
||||
<AppRouter SignInPageComponent={inputs.signInPage?.output.component}>
|
||||
{inputs.children.output.element}
|
||||
</AppRouter>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -48,8 +48,8 @@ export const CoreRoutes = createExtension({
|
||||
|
||||
const element = useRoutes([
|
||||
...inputs.routes.map(route => ({
|
||||
path: `${route.path}/*`,
|
||||
element: route.element,
|
||||
path: `${route.output.path}/*`,
|
||||
element: route.output.element,
|
||||
})),
|
||||
{
|
||||
path: '*',
|
||||
|
||||
@@ -153,7 +153,7 @@ describe('instantiateAppNodeTree', () => {
|
||||
expect(tree.root.instance).toBeDefined();
|
||||
expect(childNode?.instance).toBeDefined();
|
||||
expect(tree.root.instance?.getData(inputMirrorDataRef)).toEqual({
|
||||
test: [{ test: 'test' }],
|
||||
test: [{ extensionId: 'child-node', output: { test: 'test' } }],
|
||||
});
|
||||
|
||||
// Multiple calls should have no effect
|
||||
@@ -296,9 +296,18 @@ describe('createAppNodeInstance', () => {
|
||||
|
||||
expect(Array.from(instance.getDataRefs())).toEqual([inputMirrorDataRef]);
|
||||
expect(instance.getData(inputMirrorDataRef)).toEqual({
|
||||
optionalSingletonPresent: { test: 'optionalSingletonPresent' },
|
||||
singleton: { test: 'singleton', other: 2 },
|
||||
many: [{ test: 'many1' }, { test: 'many2', other: 3 }],
|
||||
optionalSingletonPresent: {
|
||||
extensionId: 'core/test',
|
||||
output: { test: 'optionalSingletonPresent' },
|
||||
},
|
||||
singleton: {
|
||||
extensionId: 'core/test',
|
||||
output: { test: 'singleton', other: 2 },
|
||||
},
|
||||
many: [
|
||||
{ extensionId: 'core/test', output: { test: 'many1' } },
|
||||
{ extensionId: 'core/test', output: { test: 'many2', other: 3 } },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
AnyExtensionDataMap,
|
||||
AnyExtensionInputMap,
|
||||
ExtensionDataRef,
|
||||
ResolvedExtensionInputs,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import mapValues from 'lodash/mapValues';
|
||||
import { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api';
|
||||
@@ -45,7 +46,7 @@ function resolveInputData(
|
||||
function resolveInputs(
|
||||
inputMap: AnyExtensionInputMap,
|
||||
attachments: ReadonlyMap<string, { id: string; instance: AppNodeInstance }[]>,
|
||||
) {
|
||||
): ResolvedExtensionInputs<AnyExtensionInputMap> {
|
||||
const undeclaredAttachments = Array.from(attachments.entries()).filter(
|
||||
([inputName]) => inputMap[inputName] === undefined,
|
||||
);
|
||||
@@ -84,13 +85,21 @@ function resolveInputs(
|
||||
}
|
||||
throw Error(`input '${inputName}' is required but was not received`);
|
||||
}
|
||||
return resolveInputData(input.extensionData, attachedNodes[0], inputName);
|
||||
return {
|
||||
extensionId: attachedNodes[0].id,
|
||||
output: resolveInputData(
|
||||
input.extensionData,
|
||||
attachedNodes[0],
|
||||
inputName,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return attachedNodes.map(attachment =>
|
||||
resolveInputData(input.extensionData, attachment, inputName),
|
||||
);
|
||||
});
|
||||
return attachedNodes.map(attachment => ({
|
||||
extensionId: attachment.id,
|
||||
output: resolveInputData(input.extensionData, attachment, inputName),
|
||||
}));
|
||||
}) as ResolvedExtensionInputs<AnyExtensionInputMap>;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -135,7 +144,7 @@ export function createAppNodeInstance(options: {
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to instantiate extension '${id}'${
|
||||
e.name === 'Error' ? `, ${e.message}` : `; caused by ${e}`
|
||||
e.name === 'Error' ? `, ${e.message}` : `; caused by ${e.stack}`
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ export function createApiExtension<
|
||||
api: AnyApiRef;
|
||||
factory: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => AnyApiFactory;
|
||||
}
|
||||
| {
|
||||
@@ -413,13 +413,13 @@ export function createComponentExtension<
|
||||
| {
|
||||
lazy: (values: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<TRef['T']>;
|
||||
}
|
||||
| {
|
||||
sync: (values: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => TRef['T'];
|
||||
};
|
||||
}): ExtensionDefinition<TConfig>;
|
||||
@@ -475,7 +475,7 @@ export interface CreateExtensionOptions<
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): Expand<ExtensionDataValues<TOutput>>;
|
||||
// (undocumented)
|
||||
inputs?: TInputs;
|
||||
@@ -556,7 +556,7 @@ export function createPageExtension<
|
||||
routeRef?: RouteRef;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
},
|
||||
): ExtensionDefinition<TConfig>;
|
||||
@@ -610,7 +610,7 @@ export function createSignInPageExtension<
|
||||
inputs?: TInputs;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<ComponentType<SignInPageProps>>;
|
||||
}): ExtensionDefinition<TConfig>;
|
||||
|
||||
@@ -657,10 +657,7 @@ export interface Extension<TConfig> {
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Record<
|
||||
string,
|
||||
undefined | Record<string, unknown> | Array<Record<string, unknown>>
|
||||
>;
|
||||
inputs: ResolvedExtensionInputs<any>;
|
||||
}): ExtensionDataValues<any>;
|
||||
// (undocumented)
|
||||
id: string;
|
||||
@@ -730,10 +727,7 @@ export interface ExtensionDefinition<TConfig> {
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Record<
|
||||
string,
|
||||
undefined | Record<string, unknown> | Array<Record<string, unknown>>
|
||||
>;
|
||||
inputs: ResolvedExtensionInputs<any>;
|
||||
}): ExtensionDataValues<any>;
|
||||
// (undocumented)
|
||||
inputs: AnyExtensionInputMap;
|
||||
@@ -763,21 +757,6 @@ export interface ExtensionInput<
|
||||
extensionData: TExtensionData;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type ExtensionInputValues<
|
||||
TInputs extends {
|
||||
[name in string]: ExtensionInput<any, any>;
|
||||
},
|
||||
> = {
|
||||
[InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton']
|
||||
? Array<Expand<ExtensionDataValues<TInputs[InputName]['extensionData']>>>
|
||||
: false extends TInputs[InputName]['config']['optional']
|
||||
? Expand<ExtensionDataValues<TInputs[InputName]['extensionData']>>
|
||||
: Expand<
|
||||
ExtensionDataValues<TInputs[InputName]['extensionData']> | undefined
|
||||
>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ExtensionOverrides {
|
||||
// (undocumented)
|
||||
@@ -906,6 +885,28 @@ export { ProfileInfo };
|
||||
|
||||
export { ProfileInfoApi };
|
||||
|
||||
// @public
|
||||
export type ResolvedExtensionInput<TExtensionData extends AnyExtensionDataMap> =
|
||||
{
|
||||
extensionId: string;
|
||||
output: ExtensionDataValues<TExtensionData>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type ResolvedExtensionInputs<
|
||||
TInputs extends {
|
||||
[name in string]: ExtensionInput<any, any>;
|
||||
},
|
||||
> = {
|
||||
[InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton']
|
||||
? Array<Expand<ResolvedExtensionInput<TInputs[InputName]['extensionData']>>>
|
||||
: false extends TInputs[InputName]['config']['optional']
|
||||
? Expand<ResolvedExtensionInput<TInputs[InputName]['extensionData']>>
|
||||
: Expand<
|
||||
ResolvedExtensionInput<TInputs[InputName]['extensionData']> | undefined
|
||||
>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type RouteFunc<TParams extends AnyRouteRefParams> = (
|
||||
...[params]: TParams extends undefined
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { AnyApiFactory, AnyApiRef } from '@backstage/core-plugin-api';
|
||||
import { PortableSchema } from '../schema';
|
||||
import {
|
||||
ExtensionInputValues,
|
||||
ResolvedExtensionInputs,
|
||||
createExtension,
|
||||
coreExtensionData,
|
||||
} from '../wiring';
|
||||
@@ -34,7 +34,7 @@ export function createApiExtension<
|
||||
api: AnyApiRef;
|
||||
factory: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => AnyApiFactory;
|
||||
}
|
||||
| {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import React, { lazy } from 'react';
|
||||
import {
|
||||
AnyExtensionInputMap,
|
||||
ExtensionInputValues,
|
||||
ResolvedExtensionInputs,
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
} from '../wiring';
|
||||
@@ -40,13 +40,13 @@ export function createComponentExtension<
|
||||
| {
|
||||
lazy: (values: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<TRef['T']>;
|
||||
}
|
||||
| {
|
||||
sync: (values: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => TRef['T'];
|
||||
};
|
||||
}) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { createSchemaFromZod, PortableSchema } from '../schema';
|
||||
import {
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
ExtensionInputValues,
|
||||
ResolvedExtensionInputs,
|
||||
AnyExtensionInputMap,
|
||||
} from '../wiring';
|
||||
import { RouteRef } from '../routing';
|
||||
@@ -52,7 +52,7 @@ export function createPageExtension<
|
||||
routeRef?: RouteRef;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
},
|
||||
): ExtensionDefinition<TConfig> {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ExtensionBoundary } from '../components';
|
||||
import { PortableSchema } from '../schema';
|
||||
import {
|
||||
createExtension,
|
||||
ExtensionInputValues,
|
||||
ResolvedExtensionInputs,
|
||||
AnyExtensionInputMap,
|
||||
createExtensionDataRef,
|
||||
ExtensionDefinition,
|
||||
@@ -47,7 +47,7 @@ export function createSignInPageExtension<
|
||||
inputs?: TInputs;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<ComponentType<SignInPageProps>>;
|
||||
}): ExtensionDefinition<TConfig> {
|
||||
return createExtension({
|
||||
|
||||
@@ -251,34 +251,34 @@ describe('createExtension', () => {
|
||||
foo: stringData,
|
||||
},
|
||||
factory({ inputs }) {
|
||||
const a1: string = inputs.mixed?.[0].required;
|
||||
const a1: string = inputs.mixed?.[0].output.required;
|
||||
// @ts-expect-error
|
||||
const a2: number = inputs.mixed?.[0].required;
|
||||
const a2: number = inputs.mixed?.[0].output.required;
|
||||
// @ts-expect-error
|
||||
const a3: any = inputs.mixed?.[0].nonExistent;
|
||||
const a3: any = inputs.mixed?.[0].output.nonExistent;
|
||||
unused(a1, a2, a3);
|
||||
|
||||
const b1: string | undefined = inputs.mixed?.[0].optional;
|
||||
const b1: string | undefined = inputs.mixed?.[0].output.optional;
|
||||
// @ts-expect-error
|
||||
const b2: string = inputs.mixed?.[0].optional;
|
||||
const b2: string = inputs.mixed?.[0].output.optional;
|
||||
// @ts-expect-error
|
||||
const b3: number = inputs.mixed?.[0].optional;
|
||||
const b3: number = inputs.mixed?.[0].output.optional;
|
||||
// @ts-expect-error
|
||||
const b4: number | undefined = inputs.mixed?.[0].optional;
|
||||
const b4: number | undefined = inputs.mixed?.[0].output.optional;
|
||||
unused(b1, b2, b3, b4);
|
||||
|
||||
const c1: string = inputs.onlyRequired?.[0].required;
|
||||
const c1: string = inputs.onlyRequired?.[0].output.required;
|
||||
// @ts-expect-error
|
||||
const c2: number = inputs.onlyRequired?.[0].required;
|
||||
const c2: number = inputs.onlyRequired?.[0].output.required;
|
||||
unused(c1, c2);
|
||||
|
||||
const d1: string | undefined = inputs.onlyOptional?.[0].optional;
|
||||
const d1: string | undefined = inputs.onlyOptional?.[0].output.optional;
|
||||
// @ts-expect-error
|
||||
const d2: string = inputs.onlyOptional?.[0].optional;
|
||||
const d2: string = inputs.onlyOptional?.[0].output.optional;
|
||||
// @ts-expect-error
|
||||
const d3: number = inputs.onlyOptional?.[0].optional;
|
||||
const d3: number = inputs.onlyOptional?.[0].output.optional;
|
||||
// @ts-expect-error
|
||||
const d4: number | undefined = inputs.onlyOptional?.[0].optional;
|
||||
const d4: number | undefined = inputs.onlyOptional?.[0].output.optional;
|
||||
unused(d1, d2, d3, d4);
|
||||
|
||||
return {
|
||||
|
||||
@@ -52,18 +52,28 @@ export type ExtensionDataValues<TExtensionData extends AnyExtensionDataMap> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an extension input map into the matching concrete input values type.
|
||||
* Convert a single extension input into a matching resolved input.
|
||||
* @public
|
||||
*/
|
||||
export type ExtensionInputValues<
|
||||
export type ResolvedExtensionInput<TExtensionData extends AnyExtensionDataMap> =
|
||||
{
|
||||
extensionId: string;
|
||||
output: ExtensionDataValues<TExtensionData>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an extension input map into a matching collection of resolved inputs.
|
||||
* @public
|
||||
*/
|
||||
export type ResolvedExtensionInputs<
|
||||
TInputs extends { [name in string]: ExtensionInput<any, any> },
|
||||
> = {
|
||||
[InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton']
|
||||
? Array<Expand<ExtensionDataValues<TInputs[InputName]['extensionData']>>>
|
||||
? Array<Expand<ResolvedExtensionInput<TInputs[InputName]['extensionData']>>>
|
||||
: false extends TInputs[InputName]['config']['optional']
|
||||
? Expand<ExtensionDataValues<TInputs[InputName]['extensionData']>>
|
||||
? Expand<ResolvedExtensionInput<TInputs[InputName]['extensionData']>>
|
||||
: Expand<
|
||||
ExtensionDataValues<TInputs[InputName]['extensionData']> | undefined
|
||||
ResolvedExtensionInput<TInputs[InputName]['extensionData']> | undefined
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -84,7 +94,7 @@ export interface CreateExtensionOptions<
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): Expand<ExtensionDataValues<TOutput>>;
|
||||
}
|
||||
|
||||
@@ -102,10 +112,7 @@ export interface ExtensionDefinition<TConfig> {
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Record<
|
||||
string,
|
||||
undefined | Record<string, unknown> | Array<Record<string, unknown>>
|
||||
>;
|
||||
inputs: ResolvedExtensionInputs<any>;
|
||||
}): ExtensionDataValues<any>;
|
||||
}
|
||||
|
||||
@@ -121,10 +128,7 @@ export interface Extension<TConfig> {
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Record<
|
||||
string,
|
||||
undefined | Record<string, unknown> | Array<Record<string, unknown>>
|
||||
>;
|
||||
inputs: ResolvedExtensionInputs<any>;
|
||||
}): ExtensionDataValues<any>;
|
||||
}
|
||||
|
||||
@@ -149,7 +153,7 @@ export function createExtension<
|
||||
factory({ inputs, ...rest }) {
|
||||
// TODO: Simplify this, but TS wouldn't infer the input type for some reason
|
||||
return options.factory({
|
||||
inputs: inputs as Expand<ExtensionInputValues<TInputs>>,
|
||||
inputs: inputs as Expand<ResolvedExtensionInputs<TInputs>>,
|
||||
...rest,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -65,7 +65,9 @@ const Extension3 = createExtension({
|
||||
name: nameExtensionDataRef,
|
||||
},
|
||||
factory({ inputs }) {
|
||||
return { name: `extension-3:${inputs.addons.map(n => n.name).join('-')}` };
|
||||
return {
|
||||
name: `extension-3:${inputs.addons.map(n => n.output.name).join('-')}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -111,7 +113,7 @@ const outputExtension = createExtension({
|
||||
factory({ inputs }) {
|
||||
return {
|
||||
element: React.createElement('span', {}, [
|
||||
`Names: ${inputs.names.map(n => n.name).join(', ')}`,
|
||||
`Names: ${inputs.names.map(n => n.output.name).join(', ')}`,
|
||||
]),
|
||||
};
|
||||
},
|
||||
|
||||
@@ -25,7 +25,8 @@ export {
|
||||
type ExtensionDefinition,
|
||||
type CreateExtensionOptions,
|
||||
type ExtensionDataValues,
|
||||
type ExtensionInputValues,
|
||||
type ResolvedExtensionInput,
|
||||
type ResolvedExtensionInputs,
|
||||
type AnyExtensionInputMap,
|
||||
type AnyExtensionDataMap,
|
||||
} from './createExtension';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { AnyExtensionInputMap } from '@backstage/frontend-plugin-api';
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInputValues } from '@backstage/frontend-plugin-api';
|
||||
import { ResolvedExtensionInputs } from '@backstage/frontend-plugin-api';
|
||||
import { ResourcePermission } from '@backstage/plugin-permission-common';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -29,7 +29,7 @@ export function createEntityCardExtension<
|
||||
| typeof entityFilterFunctionExtensionDataRef.T
|
||||
| typeof entityFilterExpressionExtensionDataRef.T;
|
||||
loader: (options: {
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}): ExtensionDefinition<{
|
||||
filter?: string | undefined;
|
||||
@@ -54,7 +54,7 @@ export function createEntityContentExtension<
|
||||
| typeof entityFilterFunctionExtensionDataRef.T
|
||||
| typeof entityFilterExpressionExtensionDataRef.T;
|
||||
loader: (options: {
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}): ExtensionDefinition<{
|
||||
title: string;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import {
|
||||
AnyExtensionInputMap,
|
||||
ExtensionBoundary,
|
||||
ExtensionInputValues,
|
||||
ResolvedExtensionInputs,
|
||||
RouteRef,
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
@@ -59,7 +59,7 @@ export function createEntityCardExtension<
|
||||
| typeof entityFilterFunctionExtensionDataRef.T
|
||||
| typeof entityFilterExpressionExtensionDataRef.T;
|
||||
loader: (options: {
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}) {
|
||||
return createExtension({
|
||||
@@ -117,7 +117,7 @@ export function createEntityContentExtension<
|
||||
| typeof entityFilterFunctionExtensionDataRef.T
|
||||
| typeof entityFilterExpressionExtensionDataRef.T;
|
||||
loader: (options: {
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}) {
|
||||
return createExtension({
|
||||
|
||||
@@ -39,7 +39,7 @@ export const OverviewEntityContent = createEntityContentExtension({
|
||||
},
|
||||
loader: async ({ inputs }) =>
|
||||
import('./EntityOverviewPage').then(m => (
|
||||
<m.EntityOverviewPage cards={inputs.cards} />
|
||||
<m.EntityOverviewPage cards={inputs.cards.map(c => c.output)} />
|
||||
)),
|
||||
});
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ export const CatalogIndexPage = createPageExtension({
|
||||
},
|
||||
loader: async ({ inputs }) => {
|
||||
const { BaseCatalogPage } = await import('../components/CatalogPage');
|
||||
const filters = inputs.filters.map(filter => filter.element);
|
||||
const filters = inputs.filters.map(filter => filter.output.element);
|
||||
return <BaseCatalogPage filters={<>{filters}</>} />;
|
||||
},
|
||||
});
|
||||
@@ -64,11 +64,11 @@ export const CatalogEntityPage = createPageExtension({
|
||||
<EntityLayout>
|
||||
{inputs.contents.map(content => (
|
||||
<EntityLayout.Route
|
||||
key={content.path}
|
||||
path={content.path}
|
||||
title={content.title}
|
||||
key={content.output.path}
|
||||
path={content.output.path}
|
||||
title={content.output.title}
|
||||
>
|
||||
{content.element}
|
||||
{content.output.element}
|
||||
</EntityLayout.Route>
|
||||
))}
|
||||
</EntityLayout>
|
||||
|
||||
@@ -66,7 +66,7 @@ export const graphiqlBrowseApi = createApiExtension({
|
||||
factory({ inputs }) {
|
||||
return createApiFactory(
|
||||
graphQlBrowseApiRef,
|
||||
GraphQLEndpoints.from(inputs.endpoints.map(i => i.endpoint)),
|
||||
GraphQLEndpoints.from(inputs.endpoints.map(i => i.output.endpoint)),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -51,8 +51,8 @@ const HomepageCompositionRootExtension = createPageExtension({
|
||||
loader: ({ inputs }) =>
|
||||
import('./components/').then(m => (
|
||||
<m.HomepageCompositionRoot
|
||||
children={inputs.props?.children}
|
||||
title={inputs.props?.title}
|
||||
children={inputs.props?.output.children}
|
||||
title={inputs.props?.output.title}
|
||||
/>
|
||||
)),
|
||||
});
|
||||
|
||||
@@ -115,10 +115,10 @@ describe('createSearchResultListItemExtension', () => {
|
||||
);
|
||||
|
||||
const getResultItemComponent = (result: SearchResult) => {
|
||||
const value = inputs.items.find(({ item }) =>
|
||||
item?.predicate?.(result),
|
||||
const value = inputs.items.find(item =>
|
||||
item?.output.item.predicate?.(result),
|
||||
);
|
||||
return value?.item.component ?? DefaultResultItem;
|
||||
return value?.output.item.component ?? DefaultResultItem;
|
||||
};
|
||||
|
||||
const Component = () => {
|
||||
|
||||
@@ -112,8 +112,10 @@ export const SearchPage = createPageExtension({
|
||||
},
|
||||
loader: async ({ config, inputs }) => {
|
||||
const getResultItemComponent = (result: SearchResult) => {
|
||||
const value = inputs.items.find(({ item }) => item?.predicate?.(result));
|
||||
return value?.item.component ?? DefaultResultListItem;
|
||||
const value = inputs.items.find(item =>
|
||||
item?.output.item.predicate?.(result),
|
||||
);
|
||||
return value?.output.item.component ?? DefaultResultListItem;
|
||||
};
|
||||
|
||||
const Component = () => {
|
||||
|
||||
@@ -39,7 +39,9 @@ const UserSettingsPage = createPageExtension({
|
||||
},
|
||||
loader: ({ inputs }) =>
|
||||
import('./components/SettingsPage').then(m => (
|
||||
<m.SettingsPage providerSettings={inputs.providerSettings?.element} />
|
||||
<m.SettingsPage
|
||||
providerSettings={inputs.providerSettings?.output.element}
|
||||
/>
|
||||
)),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user