frontend-plugin-api: add new internal option for extension inputs
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -604,7 +604,7 @@ export function createExtensionDataRef<TData>(): {
|
||||
}): ConfigurableExtensionDataRef<TData, TId>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export function createExtensionInput<
|
||||
UExtensionData extends ExtensionDataRef<
|
||||
unknown,
|
||||
@@ -616,6 +616,7 @@ export function createExtensionInput<
|
||||
TConfig extends {
|
||||
singleton?: boolean;
|
||||
optional?: boolean;
|
||||
internal?: boolean;
|
||||
},
|
||||
>(
|
||||
extensionData: Array<UExtensionData>,
|
||||
@@ -630,6 +631,7 @@ export function createExtensionInput<
|
||||
{
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
internal: TConfig['internal'] extends true ? true : false;
|
||||
}
|
||||
>;
|
||||
|
||||
@@ -1288,9 +1290,11 @@ export interface ExtensionInput<
|
||||
TConfig extends {
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
} = {
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
},
|
||||
> {
|
||||
// (undocumented)
|
||||
|
||||
@@ -29,28 +29,28 @@ describe('createExtensionInput', () => {
|
||||
expect(input).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef, numberDataRef],
|
||||
config: { singleton: false, optional: false },
|
||||
config: { singleton: false, optional: false, internal: false },
|
||||
withContext: expect.any(Function),
|
||||
});
|
||||
|
||||
const x1: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: false }
|
||||
{ singleton: false; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x2: ExtensionInput<
|
||||
typeof stringDataRef,
|
||||
{ singleton: false; optional: false }
|
||||
{ singleton: false; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x3: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: true; optional: false }
|
||||
{ singleton: true; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x4: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: true }
|
||||
{ singleton: false; optional: true; internal: false }
|
||||
> = input;
|
||||
|
||||
unused(x1, x2, x3, x4);
|
||||
@@ -64,7 +64,7 @@ describe('createExtensionInput', () => {
|
||||
expect(inputWithContext).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef, numberDataRef],
|
||||
config: { singleton: false, optional: false },
|
||||
config: { singleton: false, optional: false, internal: false },
|
||||
withContext: expect.any(Function),
|
||||
context,
|
||||
});
|
||||
@@ -77,28 +77,28 @@ describe('createExtensionInput', () => {
|
||||
expect(input).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef, numberDataRef],
|
||||
config: { singleton: true, optional: false },
|
||||
config: { singleton: true, optional: false, internal: false },
|
||||
withContext: expect.any(Function),
|
||||
});
|
||||
|
||||
const x1: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: true; optional: false }
|
||||
{ singleton: true; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x2: ExtensionInput<
|
||||
typeof stringDataRef,
|
||||
{ singleton: true; optional: false }
|
||||
{ singleton: true; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x3: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: false }
|
||||
{ singleton: false; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x4: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: true }
|
||||
{ singleton: false; optional: true; internal: false }
|
||||
> = input;
|
||||
|
||||
unused(x1, x2, x3, x4);
|
||||
@@ -112,28 +112,28 @@ describe('createExtensionInput', () => {
|
||||
expect(input).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef, numberDataRef],
|
||||
config: { singleton: true, optional: true },
|
||||
config: { singleton: true, optional: true, internal: false },
|
||||
withContext: expect.any(Function),
|
||||
});
|
||||
|
||||
const x1: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: true; optional: true }
|
||||
{ singleton: true; optional: true; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x2: ExtensionInput<
|
||||
typeof stringDataRef,
|
||||
{ singleton: true; optional: true }
|
||||
{ singleton: true; optional: true; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x3: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: false }
|
||||
{ singleton: false; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x4: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: true }
|
||||
{ singleton: false; optional: true; internal: false }
|
||||
> = input;
|
||||
|
||||
unused(x1, x2, x3, x4);
|
||||
@@ -144,4 +144,14 @@ describe('createExtensionInput', () => {
|
||||
createExtensionInput([stringDataRef, stringDataRef], { singleton: true }),
|
||||
).toThrow("ExtensionInput may not have duplicate data refs: 'str'");
|
||||
});
|
||||
|
||||
it('should create an internal input', () => {
|
||||
const input = createExtensionInput([stringDataRef], { internal: true });
|
||||
expect(input).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef],
|
||||
config: { singleton: false, optional: false, internal: true },
|
||||
withContext: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,9 +27,14 @@ export interface ExtensionInput<
|
||||
string,
|
||||
{ optional?: true }
|
||||
> = ExtensionDataRef,
|
||||
TConfig extends { singleton: boolean; optional: boolean } = {
|
||||
TConfig extends {
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
} = {
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
},
|
||||
> {
|
||||
readonly $$type: '@backstage/ExtensionInput';
|
||||
@@ -38,10 +43,60 @@ export interface ExtensionInput<
|
||||
readonly replaces?: Array<{ id: string; input: string }>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Creates a new extension input to be passed to the input map of an extension.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Extension inputs created with this function can be passed to any `inputs` map
|
||||
* as part of creating or overriding an extension.
|
||||
*
|
||||
* The array of extension data references defines the data this input expects.
|
||||
* If the required data is not provided by the attached extension, the
|
||||
* attachment will fail.
|
||||
*
|
||||
* The `config` object can be used to restrict the behavior and shape of the
|
||||
* input. By default an input will accept zero or more extensions from any
|
||||
* plugin. The following options are available:
|
||||
*
|
||||
* - `singleton`: If set to `true`, only one extension can be attached to the
|
||||
* input at a time. Additional extension will trigger an app error and be
|
||||
* ignored.
|
||||
* - `optional`: If set to `true`, the input is optional and can be omitted,
|
||||
* this only has an effect if the `singleton` is set to `true`.
|
||||
* - `internal`: If set to `true`, only extensions from the same plugins will be
|
||||
* allowed to attach to this input. Other extensions will trigger an app error
|
||||
* and be ignored.
|
||||
*
|
||||
* @param extensionData - The array of extension data references that this input
|
||||
* expects.
|
||||
* @param config - The configuration object for the input.
|
||||
* @returns An extension input declaration.
|
||||
* @example
|
||||
* ```ts
|
||||
* const extension = createExtension({
|
||||
* attachTo: { id: 'example-parent', input: 'example-input' },
|
||||
* inputs: {
|
||||
* content: createExtensionInput([coreExtensionData.reactElement], {
|
||||
* singleton: true,
|
||||
* }),
|
||||
* },
|
||||
* output: [coreExtensionData.reactElement],
|
||||
* *factory({ inputs }) {
|
||||
* const content = inputs.content?.get(coreExtensionData.reactElement);
|
||||
* yield coreExtensionData.reactElement(<ContentWrapper>{content}</ContentWrapper>);
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export function createExtensionInput<
|
||||
UExtensionData extends ExtensionDataRef<unknown, string, { optional?: true }>,
|
||||
TConfig extends { singleton?: boolean; optional?: boolean },
|
||||
TConfig extends {
|
||||
singleton?: boolean;
|
||||
optional?: boolean;
|
||||
internal?: boolean;
|
||||
},
|
||||
>(
|
||||
extensionData: Array<UExtensionData>,
|
||||
config?: TConfig & { replaces?: Array<{ id: string; input: string }> },
|
||||
@@ -50,6 +105,7 @@ export function createExtensionInput<
|
||||
{
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
internal: TConfig['internal'] extends true ? true : false;
|
||||
}
|
||||
> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
@@ -81,6 +137,9 @@ export function createExtensionInput<
|
||||
optional: Boolean(config?.optional) as TConfig['optional'] extends true
|
||||
? true
|
||||
: false,
|
||||
internal: Boolean(config?.internal) as TConfig['internal'] extends true
|
||||
? true
|
||||
: false,
|
||||
},
|
||||
replaces: config?.replaces,
|
||||
};
|
||||
@@ -90,6 +149,7 @@ export function createExtensionInput<
|
||||
{
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
internal: TConfig['internal'] extends true ? true : false;
|
||||
}
|
||||
> {
|
||||
return OpaqueExtensionInput.createInstance(undefined, {
|
||||
|
||||
@@ -37,6 +37,7 @@ export type ResolvedInputValueOverrides<
|
||||
{
|
||||
optional: infer IOptional extends boolean;
|
||||
singleton: boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>
|
||||
? IOptional extends true
|
||||
@@ -44,7 +45,11 @@ export type ResolvedInputValueOverrides<
|
||||
: KName
|
||||
: never]: TInputs[KName] extends ExtensionInput<
|
||||
infer IDataRefs,
|
||||
{ optional: boolean; singleton: infer ISingleton extends boolean }
|
||||
{
|
||||
optional: boolean;
|
||||
singleton: infer ISingleton extends boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>
|
||||
? ISingleton extends true
|
||||
? Iterable<ExtensionDataRefToValue<IDataRefs>>
|
||||
@@ -56,6 +61,7 @@ export type ResolvedInputValueOverrides<
|
||||
{
|
||||
optional: infer IOptional extends boolean;
|
||||
singleton: boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>
|
||||
? IOptional extends true
|
||||
@@ -63,7 +69,11 @@ export type ResolvedInputValueOverrides<
|
||||
: never
|
||||
: never]?: TInputs[KName] extends ExtensionInput<
|
||||
infer IDataRefs,
|
||||
{ optional: boolean; singleton: infer ISingleton extends boolean }
|
||||
{
|
||||
optional: boolean;
|
||||
singleton: infer ISingleton extends boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>
|
||||
? ISingleton extends true
|
||||
? Iterable<ExtensionDataRefToValue<IDataRefs>>
|
||||
|
||||
Reference in New Issue
Block a user