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:
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user