Merge pull request #25870 from backstage/blam/nfs/overrides
NFS: Support Extension Overriding
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
'@backstage/frontend-test-utils': patch
|
||||
---
|
||||
|
||||
Added support for being able to override extension definitions.
|
||||
|
||||
```tsx
|
||||
const TestCard = EntityCardBlueprint.make({
|
||||
...
|
||||
});
|
||||
|
||||
TestCard.override({
|
||||
// override attachment points
|
||||
attachTo: { id: 'something-else', input: 'overridden' },
|
||||
// extend the config schema
|
||||
config: {
|
||||
schema: {
|
||||
newConfig: z => z.string().optional(),
|
||||
}
|
||||
},
|
||||
// override factory
|
||||
*factory(originalFactory, { inputs, config }){
|
||||
const originalOutput = originalFactory();
|
||||
|
||||
yield coreExentsionData.reactElement(
|
||||
<Wrapping>
|
||||
{originalOutput.get(coreExentsionData.reactElement)}
|
||||
</Wrapping>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
@@ -68,7 +68,7 @@ function makeNode<TConfig, TConfigInput>(
|
||||
|
||||
function makeInstanceWithId<TConfig, TConfigInput>(
|
||||
extension: Extension<TConfig, TConfigInput>,
|
||||
config?: TConfig,
|
||||
config?: TConfigInput,
|
||||
): AppNode {
|
||||
const node = makeNode(extension, { config });
|
||||
return {
|
||||
@@ -640,12 +640,12 @@ describe('instantiateAppNodeTree', () => {
|
||||
name: 'test',
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
output: [testDataRef, otherDataRef.optional()],
|
||||
configSchema: createSchemaFromZod(z =>
|
||||
z.object({
|
||||
output: z.string().default('test'),
|
||||
other: z.number().optional(),
|
||||
}),
|
||||
),
|
||||
config: {
|
||||
schema: {
|
||||
output: z => z.string().default('test'),
|
||||
other: z => z.number().optional(),
|
||||
},
|
||||
},
|
||||
factory({ config }) {
|
||||
return [
|
||||
testDataRef(config.output),
|
||||
|
||||
@@ -48,6 +48,7 @@ function makeExtDef(
|
||||
name,
|
||||
attachTo: { id: attachId, input: 'default' },
|
||||
disabled: status === 'disabled',
|
||||
override: () => ({} as ExtensionDefinition<unknown>),
|
||||
} as ExtensionDefinition<unknown>;
|
||||
}
|
||||
|
||||
|
||||
@@ -396,7 +396,7 @@ export function createApiExtension<
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
inputs?: TInputs;
|
||||
},
|
||||
): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
): ExtensionDefinition<TConfig, TConfig, never, never>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createApiExtension {
|
||||
@@ -492,7 +492,7 @@ export function createComponentExtension<
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => ComponentType<TProps>;
|
||||
};
|
||||
}): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
}): ExtensionDefinition<TConfig, TConfig, never, never>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createComponentExtension {
|
||||
@@ -524,8 +524,6 @@ export function createExtension<
|
||||
}
|
||||
>;
|
||||
},
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends {
|
||||
[key: string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
@@ -534,26 +532,20 @@ export function createExtension<
|
||||
options: CreateExtensionOptions<
|
||||
UOutput,
|
||||
TInputs,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema,
|
||||
UFactoryOutput
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
}),
|
||||
TConfigInput &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>)
|
||||
{
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
},
|
||||
z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>,
|
||||
UOutput,
|
||||
TInputs
|
||||
>;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
@@ -562,33 +554,14 @@ export function createExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends {
|
||||
[key: string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
>(
|
||||
options: LegacyCreateExtensionOptions<
|
||||
TOutput,
|
||||
TInputs,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema
|
||||
TConfigInput
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
}),
|
||||
TConfigInput &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>)
|
||||
>;
|
||||
): ExtensionDefinition<TConfig, TConfigInput, never, never>;
|
||||
|
||||
// @public
|
||||
export function createExtensionBlueprint<
|
||||
@@ -751,8 +724,6 @@ export type CreateExtensionOptions<
|
||||
}
|
||||
>;
|
||||
},
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends {
|
||||
[key: string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
@@ -768,20 +739,14 @@ export type CreateExtensionOptions<
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
output: Array<UOutput>;
|
||||
configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
config?: {
|
||||
schema: TConfigSchema;
|
||||
};
|
||||
factory(context: {
|
||||
node: AppNode;
|
||||
config: TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
});
|
||||
config: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): Iterable<UFactoryOutput>;
|
||||
} & VerifyExtensionFactoryOutput<UOutput, UFactoryOutput>;
|
||||
@@ -827,7 +792,9 @@ export function createNavItemExtension(options: {
|
||||
},
|
||||
{
|
||||
title?: string | undefined;
|
||||
}
|
||||
},
|
||||
never,
|
||||
never
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -850,7 +817,7 @@ export function createNavLogoExtension(options: {
|
||||
namespace?: string;
|
||||
logoIcon: JSX.Element;
|
||||
logoFull: JSX.Element;
|
||||
}): ExtensionDefinition<{}, {}>;
|
||||
}): ExtensionDefinition<unknown, unknown, never, never>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createNavLogoExtension {
|
||||
@@ -1005,7 +972,7 @@ export function createSubRouteRef<
|
||||
// @public (undocumented)
|
||||
export function createThemeExtension(
|
||||
theme: AppTheme,
|
||||
): ExtensionDefinition<{}, {}>;
|
||||
): ExtensionDefinition<unknown, unknown, never, never>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createThemeExtension {
|
||||
@@ -1021,7 +988,7 @@ export namespace createThemeExtension {
|
||||
export function createTranslationExtension(options: {
|
||||
name?: string;
|
||||
resource: TranslationResource | TranslationMessages;
|
||||
}): ExtensionDefinition<{}, {}>;
|
||||
}): ExtensionDefinition<unknown, unknown, never, never>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createTranslationExtension {
|
||||
@@ -1259,7 +1226,20 @@ export type ExtensionDataValues<TExtensionData extends AnyExtensionDataMap> = {
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ExtensionDefinition<TConfig, TConfigInput = TConfig> {
|
||||
export interface ExtensionDefinition<
|
||||
TConfig,
|
||||
TConfigInput = TConfig,
|
||||
UOutput extends AnyExtensionDataRef = AnyExtensionDataRef,
|
||||
TInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{
|
||||
optional: boolean;
|
||||
singleton: boolean;
|
||||
}
|
||||
>;
|
||||
} = {},
|
||||
> {
|
||||
// (undocumented)
|
||||
$$type: '@backstage/ExtensionDefinition';
|
||||
// (undocumented)
|
||||
@@ -1277,6 +1257,76 @@ export interface ExtensionDefinition<TConfig, TConfigInput = TConfig> {
|
||||
readonly name?: string;
|
||||
// (undocumented)
|
||||
readonly namespace?: string;
|
||||
// (undocumented)
|
||||
override<
|
||||
TExtensionConfigSchema extends {
|
||||
[key in string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
UFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
UNewOutput extends AnyExtensionDataRef,
|
||||
TExtraInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{
|
||||
optional: boolean;
|
||||
singleton: boolean;
|
||||
}
|
||||
>;
|
||||
},
|
||||
>(
|
||||
args: {
|
||||
attachTo?: {
|
||||
id: string;
|
||||
input: string;
|
||||
};
|
||||
disabled?: boolean;
|
||||
inputs?: TExtraInputs & {
|
||||
[KName in keyof TInputs]?: `Error: Input '${KName &
|
||||
string}' is already defined in parent definition`;
|
||||
};
|
||||
output?: Array<UNewOutput>;
|
||||
config?: {
|
||||
schema: TExtensionConfigSchema & {
|
||||
[KName in keyof TConfig]?: `Error: Config key '${KName &
|
||||
string}' is already defined in parent schema`;
|
||||
};
|
||||
};
|
||||
factory(
|
||||
originalFactory: (context?: {
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => ExtensionDataContainer<UOutput>,
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig & {
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs & TExtraInputs>>;
|
||||
},
|
||||
): Iterable<UFactoryOutput>;
|
||||
} & VerifyExtensionFactoryOutput<
|
||||
AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput,
|
||||
UFactoryOutput
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & TConfig,
|
||||
z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TExtensionConfigSchema]: ReturnType<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
}>
|
||||
> &
|
||||
TConfigInput,
|
||||
AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput,
|
||||
TInputs & TExtraInputs
|
||||
>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -1418,9 +1468,6 @@ export interface LegacyCreateExtensionOptions<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends {
|
||||
[key: string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
> {
|
||||
// (undocumented)
|
||||
attachTo: {
|
||||
@@ -1428,24 +1475,13 @@ export interface LegacyCreateExtensionOptions<
|
||||
input: string;
|
||||
};
|
||||
// (undocumented)
|
||||
config?: {
|
||||
schema: TConfigSchema;
|
||||
};
|
||||
// @deprecated (undocumented)
|
||||
configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
factory(context: {
|
||||
node: AppNode;
|
||||
config: TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
});
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): Expand<ExtensionDataValues<TOutput>>;
|
||||
// (undocumented)
|
||||
|
||||
@@ -48,6 +48,7 @@ describe('createApiExtension', () => {
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,6 +86,7 @@ describe('createApiExtension', () => {
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ describe('createAppRootElementExtension', () => {
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
});
|
||||
|
||||
createExtensionTester(extension).render();
|
||||
@@ -88,6 +89,7 @@ describe('createAppRootElementExtension', () => {
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
});
|
||||
|
||||
createExtensionTester(extension, { config: { name: 'Robin' } })
|
||||
|
||||
@@ -42,6 +42,7 @@ describe('createAppRootWrapperExtension', () => {
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
});
|
||||
|
||||
createExtensionTester(
|
||||
@@ -94,6 +95,7 @@ describe('createAppRootWrapperExtension', () => {
|
||||
component: expect.anything(),
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
});
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ describe('createNavLogoExtension', () => {
|
||||
logos: expect.anything(),
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,6 +56,7 @@ describe('createPageExtension', () => {
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(
|
||||
@@ -78,6 +79,7 @@ describe('createPageExtension', () => {
|
||||
kind: 'page',
|
||||
attachTo: { id: 'other', input: 'place' },
|
||||
configSchema: expect.anything(),
|
||||
override: expect.any(Function),
|
||||
disabled: true,
|
||||
inputs: {
|
||||
first: createExtensionInput({
|
||||
@@ -115,6 +117,7 @@ describe('createPageExtension', () => {
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ describe('createRouterExtension', () => {
|
||||
component: expect.anything(),
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
});
|
||||
|
||||
@@ -119,6 +120,7 @@ describe('createRouterExtension', () => {
|
||||
component: expect.anything(),
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
});
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ describe('createTranslationExtension', () => {
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'app', input: 'translations' },
|
||||
disabled: false,
|
||||
override: expect.any(Function),
|
||||
inputs: {},
|
||||
output: {
|
||||
resource: createTranslationExtension.translationDataRef,
|
||||
@@ -84,6 +85,7 @@ describe('createTranslationExtension', () => {
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'app', input: 'translations' },
|
||||
disabled: false,
|
||||
override: expect.any(Function),
|
||||
inputs: {},
|
||||
output: {
|
||||
resource: createTranslationExtension.translationDataRef,
|
||||
@@ -120,6 +122,7 @@ describe('createTranslationExtension', () => {
|
||||
version: 'v1',
|
||||
kind: 'translation',
|
||||
namespace: 'test',
|
||||
override: expect.any(Function),
|
||||
name: 'sv',
|
||||
attachTo: { id: 'app', input: 'translations' },
|
||||
disabled: false,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { createExtension } from './createExtension';
|
||||
import { createExtensionDataRef } from './createExtensionDataRef';
|
||||
import { createExtensionInput } from './createExtensionInput';
|
||||
@@ -304,9 +305,7 @@ describe('createExtension', () => {
|
||||
baz: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
output: {
|
||||
foo: stringDataRef,
|
||||
},
|
||||
output: [stringDataRef],
|
||||
factory({ config }) {
|
||||
const a1: string = config.foo;
|
||||
const a2: string = config.bar;
|
||||
@@ -320,12 +319,10 @@ describe('createExtension', () => {
|
||||
const c3: number = config.baz;
|
||||
unused(a1, a2, a3, c1, c2, c3);
|
||||
|
||||
return {
|
||||
foo: 'bar',
|
||||
};
|
||||
return [stringDataRef('bar')];
|
||||
},
|
||||
});
|
||||
expect(extension).toMatchObject({ version: 'v1', namespace: 'test' });
|
||||
expect(extension).toMatchObject({ version: 'v2', namespace: 'test' });
|
||||
expect(String(extension)).toBe(
|
||||
'ExtensionDefinition{namespace=test,attachTo=root@default}',
|
||||
);
|
||||
@@ -561,4 +558,179 @@ describe('createExtension', () => {
|
||||
}),
|
||||
).toMatchObject({ version: 'v2' });
|
||||
});
|
||||
|
||||
describe('overrides', () => {
|
||||
it('should allow overriding of config and merging', () => {
|
||||
const testExtension = createExtension({
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'root', input: 'blob' },
|
||||
output: [stringDataRef],
|
||||
config: {
|
||||
schema: {
|
||||
foo: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
factory() {
|
||||
return [stringDataRef('default')];
|
||||
},
|
||||
});
|
||||
|
||||
testExtension.override({
|
||||
config: {
|
||||
schema: {
|
||||
bar: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
factory(_, { config }) {
|
||||
return [stringDataRef(config.foo ?? config.bar ?? 'default')];
|
||||
},
|
||||
});
|
||||
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow overriding of outputs', () => {
|
||||
const testExtension = createExtension({
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'root', input: 'blob' },
|
||||
output: [stringDataRef],
|
||||
inputs: {
|
||||
test: createExtensionInput([stringDataRef], {
|
||||
singleton: true,
|
||||
}),
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
foo: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
factory({ inputs }) {
|
||||
return [stringDataRef(inputs.test.get(stringDataRef))];
|
||||
},
|
||||
});
|
||||
|
||||
const override1 = testExtension.override({
|
||||
output: [numberDataRef],
|
||||
factory(_, { inputs }) {
|
||||
return [numberDataRef(inputs.test.get(stringDataRef).length)];
|
||||
},
|
||||
});
|
||||
|
||||
// @ts-expect-error - this should fail because string output should be merged?
|
||||
const override2 = testExtension.override({
|
||||
output: [numberDataRef],
|
||||
factory(_, { inputs }) {
|
||||
return [stringDataRef(inputs.test.get(stringDataRef))];
|
||||
},
|
||||
});
|
||||
|
||||
unused(override1, override2);
|
||||
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow overriding the factory function and calling the original factory', () => {
|
||||
const testExtension = createExtension({
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'root', input: 'blob' },
|
||||
output: [stringDataRef],
|
||||
config: {
|
||||
schema: {
|
||||
foo: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
factory() {
|
||||
return [stringDataRef('default')];
|
||||
},
|
||||
});
|
||||
|
||||
testExtension.override({
|
||||
factory(originalFactory) {
|
||||
const response = originalFactory();
|
||||
|
||||
const foo: string = response.get(stringDataRef);
|
||||
|
||||
// @ts-expect-error - fails because original factory does not return number
|
||||
const number: boolean = response.get(numberDataRef);
|
||||
|
||||
return [stringDataRef(`foo-${foo}-override`)];
|
||||
},
|
||||
});
|
||||
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow overriding the returned values from the parent factory', () => {
|
||||
const testExtension = createExtension({
|
||||
kind: 'thing',
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'root', input: 'default' },
|
||||
output: [stringDataRef, numberDataRef],
|
||||
config: {
|
||||
schema: {
|
||||
foo: z => z.string().default('boom'),
|
||||
},
|
||||
},
|
||||
factory({ config }) {
|
||||
return [stringDataRef(config.foo), numberDataRef(42)];
|
||||
},
|
||||
});
|
||||
|
||||
const overridden = testExtension.override({
|
||||
output: [numberDataRef, stringDataRef],
|
||||
*factory(originalFactory) {
|
||||
const output = originalFactory();
|
||||
yield* output;
|
||||
|
||||
yield numberDataRef(output.get(numberDataRef) + 1);
|
||||
},
|
||||
});
|
||||
|
||||
const tester = createExtensionTester(overridden);
|
||||
|
||||
expect(tester.data(numberDataRef)).toBe(43);
|
||||
});
|
||||
|
||||
it('should work functionally with overrides', () => {
|
||||
const testExtension = createExtension({
|
||||
kind: 'thing',
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'root', input: 'default' },
|
||||
output: [stringDataRef],
|
||||
config: {
|
||||
schema: {
|
||||
foo: z => z.string().default('boom'),
|
||||
},
|
||||
},
|
||||
factory({ config }) {
|
||||
return [stringDataRef(config.foo)];
|
||||
},
|
||||
});
|
||||
|
||||
const overriden = testExtension.override({
|
||||
config: {
|
||||
schema: {
|
||||
bar: z => z.string().default('hello'),
|
||||
},
|
||||
},
|
||||
factory(originalFactory, { config }) {
|
||||
const response = originalFactory();
|
||||
|
||||
const foo: string = response.get(stringDataRef);
|
||||
|
||||
return [stringDataRef(`foo-${foo}-override-${config.bar}`)];
|
||||
},
|
||||
});
|
||||
|
||||
expect(createExtensionTester(overriden).data(stringDataRef)).toBe(
|
||||
'foo-boom-override-hello',
|
||||
);
|
||||
|
||||
expect(
|
||||
createExtensionTester(overriden, {
|
||||
config: { foo: 'hello', bar: 'world' },
|
||||
}).data(stringDataRef),
|
||||
).toBe('foo-hello-override-world');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { AppNode } from '../apis';
|
||||
import { PortableSchema, createSchemaFromZod } from '../schema';
|
||||
import { Expand } from '../types';
|
||||
import { createDataContainer } from './createExtensionBlueprint';
|
||||
import {
|
||||
AnyExtensionDataRef,
|
||||
ExtensionDataRef,
|
||||
@@ -127,7 +128,6 @@ export interface LegacyCreateExtensionOptions<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
|
||||
> {
|
||||
kind?: string;
|
||||
namespace?: string;
|
||||
@@ -136,21 +136,10 @@ export interface LegacyCreateExtensionOptions<
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
output: TOutput;
|
||||
/** @deprecated - use `config.schema` instead */
|
||||
configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
config?: {
|
||||
schema: TConfigSchema;
|
||||
};
|
||||
factory(context: {
|
||||
node: AppNode;
|
||||
config: TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
});
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): Expand<ExtensionDataValues<TOutput>>;
|
||||
}
|
||||
@@ -192,8 +181,6 @@ export type CreateExtensionOptions<
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
},
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
|
||||
UFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
> = {
|
||||
@@ -204,27 +191,30 @@ export type CreateExtensionOptions<
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
output: Array<UOutput>;
|
||||
/** @deprecated - use `config.schema` instead */
|
||||
configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
config?: {
|
||||
schema: TConfigSchema;
|
||||
};
|
||||
factory(context: {
|
||||
node: AppNode;
|
||||
config: TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
});
|
||||
config: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): Iterable<UFactoryOutput>;
|
||||
} & VerifyExtensionFactoryOutput<UOutput, UFactoryOutput>;
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionDefinition<TConfig, TConfigInput = TConfig> {
|
||||
export interface ExtensionDefinition<
|
||||
TConfig,
|
||||
TConfigInput = TConfig,
|
||||
UOutput extends AnyExtensionDataRef = AnyExtensionDataRef,
|
||||
TInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
} = {},
|
||||
> {
|
||||
$$type: '@backstage/ExtensionDefinition';
|
||||
readonly kind?: string;
|
||||
readonly namespace?: string;
|
||||
@@ -232,6 +222,70 @@ export interface ExtensionDefinition<TConfig, TConfigInput = TConfig> {
|
||||
readonly attachTo: { id: string; input: string };
|
||||
readonly disabled: boolean;
|
||||
readonly configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
|
||||
override<
|
||||
TExtensionConfigSchema extends {
|
||||
[key in string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
UFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
UNewOutput extends AnyExtensionDataRef,
|
||||
TExtraInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
},
|
||||
>(
|
||||
args: {
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TExtraInputs & {
|
||||
[KName in keyof TInputs]?: `Error: Input '${KName &
|
||||
string}' is already defined in parent definition`;
|
||||
};
|
||||
output?: Array<UNewOutput>;
|
||||
config?: {
|
||||
schema: TExtensionConfigSchema & {
|
||||
[KName in keyof TConfig]?: `Error: Config key '${KName &
|
||||
string}' is already defined in parent schema`;
|
||||
};
|
||||
};
|
||||
factory(
|
||||
originalFactory: (context?: {
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => ExtensionDataContainer<UOutput>,
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig & {
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs & TExtraInputs>>;
|
||||
},
|
||||
): Iterable<UFactoryOutput>;
|
||||
} & VerifyExtensionFactoryOutput<
|
||||
AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput,
|
||||
UFactoryOutput
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & TConfig,
|
||||
z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TExtensionConfigSchema]: ReturnType<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
}>
|
||||
> &
|
||||
TConfigInput,
|
||||
AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput,
|
||||
TInputs & TExtraInputs
|
||||
>;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -301,34 +355,26 @@ export function createExtension<
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
},
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
|
||||
UFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
>(
|
||||
options: CreateExtensionOptions<
|
||||
UOutput,
|
||||
TInputs,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema,
|
||||
UFactoryOutput
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
}),
|
||||
TConfigInput &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>)
|
||||
{
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
},
|
||||
z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>,
|
||||
UOutput,
|
||||
TInputs
|
||||
>;
|
||||
/**
|
||||
* @public
|
||||
@@ -339,31 +385,14 @@ export function createExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
|
||||
>(
|
||||
options: LegacyCreateExtensionOptions<
|
||||
TOutput,
|
||||
TInputs,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema
|
||||
TConfigInput
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
}),
|
||||
TConfigInput &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>)
|
||||
>;
|
||||
): ExtensionDefinition<TConfig, TConfigInput, never, never>;
|
||||
export function createExtension<
|
||||
UOutput extends AnyExtensionDataRef,
|
||||
TInputs extends {
|
||||
@@ -379,20 +408,12 @@ export function createExtension<
|
||||
UFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
>(
|
||||
options:
|
||||
| CreateExtensionOptions<
|
||||
UOutput,
|
||||
TInputs,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema,
|
||||
UFactoryOutput
|
||||
>
|
||||
| CreateExtensionOptions<UOutput, TInputs, TConfigSchema, UFactoryOutput>
|
||||
| LegacyCreateExtensionOptions<
|
||||
AnyExtensionDataMap,
|
||||
TLegacyInputs,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema
|
||||
TConfigInput
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
TConfig &
|
||||
@@ -408,21 +429,29 @@ export function createExtension<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>)
|
||||
>),
|
||||
UOutput,
|
||||
TInputs
|
||||
> {
|
||||
const newConfigSchema = options.config?.schema;
|
||||
if (newConfigSchema && options.configSchema) {
|
||||
if ('configSchema' in options && 'config' in options) {
|
||||
throw new Error(`Cannot provide both configSchema and config.schema`);
|
||||
}
|
||||
const configSchema = newConfigSchema
|
||||
? createSchemaFromZod(innerZ =>
|
||||
let configSchema: PortableSchema<any, any> | undefined;
|
||||
if ('configSchema' in options) {
|
||||
configSchema = options.configSchema;
|
||||
}
|
||||
if ('config' in options) {
|
||||
const newConfigSchema = options.config?.schema;
|
||||
configSchema =
|
||||
newConfigSchema &&
|
||||
createSchemaFromZod(innerZ =>
|
||||
innerZ.object(
|
||||
Object.fromEntries(
|
||||
Object.entries(newConfigSchema).map(([k, v]) => [k, v(innerZ)]),
|
||||
),
|
||||
),
|
||||
)
|
||||
: options.configSchema;
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
$$type: '@backstage/ExtensionDefinition',
|
||||
@@ -450,6 +479,141 @@ export function createExtension<
|
||||
parts.push(`attachTo=${options.attachTo.id}@${options.attachTo.input}`);
|
||||
return `ExtensionDefinition{${parts.join(',')}}`;
|
||||
},
|
||||
override: <
|
||||
TExtensionConfigSchema extends {
|
||||
[key in string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
UOverrideFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
UNewOutput extends AnyExtensionDataRef,
|
||||
TExtraInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
},
|
||||
>(overrideOptions: {
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TExtraInputs;
|
||||
output?: Array<UNewOutput>;
|
||||
config?: {
|
||||
schema: TExtensionConfigSchema;
|
||||
};
|
||||
factory(
|
||||
originalFactory: (context?: {
|
||||
config?: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => ExtensionDataContainer<UOutput>,
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: {
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs & TExtraInputs>>;
|
||||
},
|
||||
): Iterable<UOverrideFactoryOutput>;
|
||||
}): ExtensionDefinition<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
},
|
||||
z.input<
|
||||
z.ZodObject<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: ReturnType<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}
|
||||
>
|
||||
>,
|
||||
AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput,
|
||||
TInputs & TExtraInputs
|
||||
> => {
|
||||
if (!Array.isArray(options.output)) {
|
||||
throw new Error(
|
||||
'Cannot override an extension that is not declared using the new format with outputs as an array',
|
||||
);
|
||||
}
|
||||
const newOptions = options as CreateExtensionOptions<
|
||||
UOutput,
|
||||
TInputs,
|
||||
TConfigSchema,
|
||||
UFactoryOutput
|
||||
>;
|
||||
const overrideNewConfigSchema = overrideOptions.config?.schema;
|
||||
|
||||
const schema = {
|
||||
...newOptions.config?.schema,
|
||||
...overrideNewConfigSchema,
|
||||
} as TConfigSchema & TExtensionConfigSchema;
|
||||
|
||||
return createExtension({
|
||||
kind: newOptions.kind,
|
||||
namespace: newOptions.namespace,
|
||||
name: newOptions.name,
|
||||
attachTo: overrideOptions.attachTo ?? newOptions.attachTo,
|
||||
disabled: overrideOptions.disabled ?? newOptions.disabled,
|
||||
inputs: { ...overrideOptions.inputs, ...newOptions.inputs },
|
||||
output: overrideOptions.output ?? newOptions.output,
|
||||
config: Object.keys(schema).length === 0 ? undefined : { schema },
|
||||
factory: ({ node, config, inputs }) => {
|
||||
if (!overrideOptions.factory) {
|
||||
return newOptions.factory({
|
||||
node,
|
||||
config,
|
||||
inputs: inputs as unknown as Expand<
|
||||
ResolvedExtensionInputs<TInputs>
|
||||
>,
|
||||
});
|
||||
}
|
||||
const parentResult = overrideOptions.factory(
|
||||
(innerContext?: {
|
||||
config?: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): ExtensionDataContainer<UOutput> => {
|
||||
return createDataContainer<UOutput>(
|
||||
newOptions.factory({
|
||||
node,
|
||||
config: innerContext?.config ?? config,
|
||||
inputs: (innerContext?.inputs ?? inputs) as any, // TODO: Fix the way input values are overridden
|
||||
}) as Iterable<any>,
|
||||
);
|
||||
},
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
},
|
||||
);
|
||||
|
||||
const deduplicatedResult = new Map<string, UOverrideFactoryOutput>();
|
||||
for (const item of parentResult) {
|
||||
deduplicatedResult.set(item.id, item);
|
||||
}
|
||||
|
||||
return deduplicatedResult.values() as Iterable<UOverrideFactoryOutput>;
|
||||
},
|
||||
} as CreateExtensionOptions<AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, TInputs & TExtraInputs, TConfigSchema & TExtensionConfigSchema, UOverrideFactoryOutput>);
|
||||
},
|
||||
} as InternalExtensionDefinition<
|
||||
TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
|
||||
@@ -69,6 +69,7 @@ describe('createExtensionBlueprint', () => {
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
version: 'v2',
|
||||
});
|
||||
|
||||
@@ -108,6 +109,7 @@ describe('createExtensionBlueprint', () => {
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
version: 'v2',
|
||||
});
|
||||
|
||||
@@ -389,6 +391,7 @@ describe('createExtensionBlueprint', () => {
|
||||
"output": [
|
||||
[Function],
|
||||
],
|
||||
"override": [Function],
|
||||
"toString": [Function],
|
||||
"version": "v2",
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ export interface ExtensionBlueprint<
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function createDataContainer<UData extends AnyExtensionDataRef>(
|
||||
export function createDataContainer<UData extends AnyExtensionDataRef>(
|
||||
values: Iterable<
|
||||
UData extends ExtensionDataRef<infer IData, infer IId>
|
||||
? ExtensionDataValue<IData, IId>
|
||||
@@ -241,10 +241,7 @@ class ExtensionBlueprintImpl<
|
||||
name?: string;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TExtraInputs & {
|
||||
[KName in keyof TInputs]?: `Error: Input '${KName &
|
||||
string}' is already defined in parent definition`;
|
||||
};
|
||||
inputs?: TExtraInputs;
|
||||
output?: Array<UNewOutput>;
|
||||
params?: TParams;
|
||||
config?: {
|
||||
@@ -363,9 +360,7 @@ class ExtensionBlueprintImpl<
|
||||
}
|
||||
throw new Error('Either params or factory must be provided');
|
||||
},
|
||||
} as CreateExtensionOptions<
|
||||
UOutput,
|
||||
TInputs & TExtraInputs,
|
||||
} as CreateExtensionOptions<UOutput, TInputs & TExtraInputs, TConfigSchema & TExtensionConfigSchema, UFactoryOutput>) as ExtensionDefinition<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
@@ -383,10 +378,8 @@ class ExtensionBlueprintImpl<
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}
|
||||
>
|
||||
>,
|
||||
TConfigSchema,
|
||||
UFactoryOutput
|
||||
>);
|
||||
>
|
||||
>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ describe('resolveExtensionDefinition', () => {
|
||||
version: 'v2',
|
||||
attachTo: { id: '', input: '' },
|
||||
disabled: false,
|
||||
override: () => ({} as ExtensionDefinition<unknown>),
|
||||
};
|
||||
|
||||
it.each([
|
||||
@@ -63,6 +64,7 @@ describe('old resolveExtensionDefinition', () => {
|
||||
version: 'v1',
|
||||
attachTo: { id: '', input: '' },
|
||||
disabled: false,
|
||||
override: () => ({} as ExtensionDefinition<unknown>),
|
||||
};
|
||||
|
||||
it.each([
|
||||
|
||||
@@ -100,7 +100,13 @@ export function resolveExtensionDefinition<TConfig, TConfigInput>(
|
||||
context?: { namespace?: string },
|
||||
): Extension<TConfig, TConfigInput> {
|
||||
const internalDefinition = toInternalExtensionDefinition(definition);
|
||||
const { name, kind, namespace: _, ...rest } = internalDefinition;
|
||||
const {
|
||||
name,
|
||||
kind,
|
||||
namespace: _skip1,
|
||||
override: _skip2,
|
||||
...rest
|
||||
} = internalDefinition;
|
||||
const namespace = internalDefinition.namespace ?? context?.namespace;
|
||||
|
||||
const namePart =
|
||||
|
||||
@@ -169,7 +169,7 @@ export class ExtensionTester {
|
||||
: [...internal.output, coreExtensionData.routePath],
|
||||
factory: params => {
|
||||
const parentOutput = Array.from(
|
||||
internal.factory(params) as Iterable<
|
||||
internal.factory(params as any) as Iterable<
|
||||
ExtensionDataValue<any, any>
|
||||
>,
|
||||
).filter(val => val.id !== coreExtensionData.routePath.id);
|
||||
|
||||
@@ -115,7 +115,7 @@ export function createEntityCardExtension<
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
}): ExtensionDefinition<TConfig, TConfig, never, never>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function createEntityContentExtension<
|
||||
@@ -148,7 +148,9 @@ export function createEntityContentExtension<
|
||||
filter?: string | undefined;
|
||||
title?: string | undefined;
|
||||
path?: string | undefined;
|
||||
}
|
||||
},
|
||||
never,
|
||||
never
|
||||
>;
|
||||
|
||||
// @alpha
|
||||
|
||||
@@ -108,7 +108,7 @@ export function createCatalogFilterExtension<
|
||||
inputs?: TInputs;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
loader: (options: { config: TConfig }) => Promise<JSX.Element>;
|
||||
}): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
}): ExtensionDefinition<TConfig, TConfig, never, never>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
const _default: BackstagePlugin<
|
||||
|
||||
@@ -25,7 +25,7 @@ export function createSearchResultListItemExtension<
|
||||
},
|
||||
>(
|
||||
options: SearchResultItemExtensionOptions<TConfig>,
|
||||
): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
): ExtensionDefinition<TConfig, TConfig, never, never>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export namespace createSearchResultListItemExtension {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AnyExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { BackstagePlugin } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
@@ -17,7 +18,7 @@ const _default: BackstagePlugin<
|
||||
export default _default;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const searchApi: ExtensionDefinition<{}, {}>;
|
||||
export const searchApi: ExtensionDefinition<{}, {}, never, never>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const searchNavItem: ExtensionDefinition<
|
||||
@@ -26,7 +27,9 @@ export const searchNavItem: ExtensionDefinition<
|
||||
},
|
||||
{
|
||||
title?: string | undefined;
|
||||
}
|
||||
},
|
||||
never,
|
||||
never
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
@@ -38,7 +41,9 @@ export const searchPage: ExtensionDefinition<
|
||||
{
|
||||
path: string;
|
||||
noTrack: boolean;
|
||||
}
|
||||
},
|
||||
AnyExtensionDataRef,
|
||||
{}
|
||||
>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
@@ -37,7 +37,9 @@ export const techDocsSearchResultListItemExtension: ExtensionDefinition<
|
||||
asListItem: boolean;
|
||||
asLink: boolean;
|
||||
title?: string | undefined;
|
||||
}
|
||||
},
|
||||
never,
|
||||
never
|
||||
>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
@@ -24,7 +24,9 @@ export const settingsNavItem: ExtensionDefinition<
|
||||
},
|
||||
{
|
||||
title?: string | undefined;
|
||||
}
|
||||
},
|
||||
never,
|
||||
never
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
|
||||
Reference in New Issue
Block a user