Merge pull request #13116 from backstage/rugvip/options

backend-plugin-api: handle optional plugin and module options
This commit is contained in:
Patrik Oldsberg
2022-08-12 13:21:30 +02:00
committed by GitHub
8 changed files with 195 additions and 65 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-plugin-api': patch
---
The factory returned by `createBackendPlugin` and `createBackendModule` no longer require a parameter to be passed if the options are optional.
+6 -2
View File
@@ -83,12 +83,16 @@ export const configServiceRef: ServiceRef<Config>;
// @public (undocumented)
export function createBackendModule<TOptions>(
config: BackendModuleConfig<TOptions>,
): (option: TOptions) => BackendRegistrable;
): undefined extends TOptions
? (options?: TOptions) => BackendRegistrable
: (options: TOptions) => BackendRegistrable;
// @public (undocumented)
export function createBackendPlugin<TOptions>(
config: BackendPluginConfig<TOptions>,
): (option: TOptions) => BackendRegistrable;
): undefined extends TOptions
? (options?: TOptions) => BackendRegistrable
: (options: TOptions) => BackendRegistrable;
// @public (undocumented)
export function createExtensionPoint<T>(options: {
@@ -0,0 +1,89 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
createBackendModule,
createBackendPlugin,
createExtensionPoint,
} from './factories';
describe('createExtensionPoint', () => {
it('should create an ExtensionPoint', () => {
const extensionPoint = createExtensionPoint({ id: 'x' });
expect(extensionPoint).toBeDefined();
expect(extensionPoint.id).toBe('x');
expect(() => extensionPoint.T).toThrow();
expect(String(extensionPoint)).toBe('extensionPoint{x}');
});
});
describe('createBackendPlugin', () => {
it('should create an BackendPlugin', () => {
const plugin = createBackendPlugin({
id: 'x',
register(_reg, _options: { a: string }) {},
});
expect(plugin).toBeDefined();
expect(plugin({ a: 'a' })).toBeDefined();
expect(plugin({ a: 'a' }).id).toBe('x');
// @ts-expect-error
expect(plugin()).toBeDefined();
// @ts-expect-error
expect(plugin({ b: 'b' })).toBeDefined();
});
it('should create plugins with optional options', () => {
const plugin = createBackendPlugin({
id: 'x',
register(_reg, _options?: { a: string }) {},
});
expect(plugin).toBeDefined();
expect(plugin({ a: 'a' })).toBeDefined();
expect(plugin()).toBeDefined();
// @ts-expect-error
expect(plugin({ b: 'b' })).toBeDefined();
});
});
describe('createBackendModule', () => {
it('should create an BackendModule', () => {
const mod = createBackendModule({
pluginId: 'x',
moduleId: 'y',
register(_reg, _options: { a: string }) {},
});
expect(mod).toBeDefined();
expect(mod({ a: 'a' })).toBeDefined();
expect(mod({ a: 'a' }).id).toBe('x.y');
// @ts-expect-error
expect(mod()).toBeDefined();
// @ts-expect-error
expect(mod({ b: 'b' })).toBeDefined();
});
it('should create modules with optional options', () => {
const mod = createBackendModule({
pluginId: 'x',
moduleId: 'y',
register(_reg, _options?: { a: string }) {},
});
expect(mod).toBeDefined();
expect(mod({ a: 'a' })).toBeDefined();
expect(mod()).toBeDefined();
// @ts-expect-error
expect(mod({ b: 'b' })).toBeDefined();
});
});
@@ -0,0 +1,82 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
BackendInitRegistry,
BackendRegistrable,
ExtensionPoint,
} from './types';
/** @public */
export function createExtensionPoint<T>(options: {
id: string;
}): ExtensionPoint<T> {
return {
id: options.id,
get T(): T {
throw new Error(`tried to read ExtensionPoint.T of ${this}`);
},
toString() {
return `extensionPoint{${options.id}}`;
},
$$ref: 'extension-point', // TODO: declare
};
}
/** @public */
export interface BackendPluginConfig<TOptions> {
id: string;
register(reg: BackendInitRegistry, options: TOptions): void;
}
/** @public */
export function createBackendPlugin<TOptions>(
config: BackendPluginConfig<TOptions>,
): undefined extends TOptions
? (options?: TOptions) => BackendRegistrable
: (options: TOptions) => BackendRegistrable {
return (options?: TOptions) => ({
id: config.id,
register(register: BackendInitRegistry) {
return config.register(register, options!);
},
});
}
/** @public */
export interface BackendModuleConfig<TOptions> {
pluginId: string;
moduleId: string;
register(
reg: Omit<BackendInitRegistry, 'registerExtensionPoint'>,
options: TOptions,
): void;
}
/** @public */
export function createBackendModule<TOptions>(
config: BackendModuleConfig<TOptions>,
): undefined extends TOptions
? (options?: TOptions) => BackendRegistrable
: (options: TOptions) => BackendRegistrable {
return (options?: TOptions) => ({
id: `${config.pluginId}.${config.moduleId}`,
register(register: BackendInitRegistry) {
// TODO: Hide registerExtensionPoint
return config.register(register, options!);
},
});
}
@@ -14,5 +14,14 @@
* limitations under the License.
*/
export type { BackendRegistrable } from './types';
export * from './types';
export type { BackendModuleConfig, BackendPluginConfig } from './factories';
export {
createBackendModule,
createBackendPlugin,
createExtensionPoint,
} from './factories';
export type {
BackendInitRegistry,
BackendRegistrable,
ExtensionPoint,
} from './types';
@@ -35,22 +35,6 @@ export type ExtensionPoint<T> = {
$$ref: 'extension-point';
};
/** @public */
export function createExtensionPoint<T>(options: {
id: string;
}): ExtensionPoint<T> {
return {
id: options.id,
get T(): T {
throw new Error(`tried to read ExtensionPoint.T of ${this}`);
},
toString() {
return `extensionPoint{${options.id}}`;
},
$$ref: 'extension-point', // TODO: declare
};
}
/** @public */
export interface BackendInitRegistry {
registerExtensionPoint<TExtensionPoint>(
@@ -68,46 +52,3 @@ export interface BackendRegistrable {
id: string;
register(reg: BackendInitRegistry): void;
}
/** @public */
export interface BackendPluginConfig<TOptions> {
id: string;
register(reg: BackendInitRegistry, options: TOptions): void;
}
// TODO: Make option optional in the returned factory if they are indeed optional
/** @public */
export function createBackendPlugin<TOptions>(
config: BackendPluginConfig<TOptions>,
): (option: TOptions) => BackendRegistrable {
return options => ({
id: config.id,
register(register) {
return config.register(register, options);
},
});
}
/** @public */
export interface BackendModuleConfig<TOptions> {
pluginId: string;
moduleId: string;
register(
reg: Omit<BackendInitRegistry, 'registerExtensionPoint'>,
options: TOptions,
): void;
}
// TODO: Make option optional in the returned factory if they are indeed optional
/** @public */
export function createBackendModule<TOptions>(
config: BackendModuleConfig<TOptions>,
): (option: TOptions) => BackendRegistrable {
return options => ({
id: `${config.pluginId}.${config.moduleId}`,
register(register) {
// TODO: Hide registerExtensionPoint
return config.register(register, options);
},
});
}
+1 -1
View File
@@ -224,7 +224,7 @@ export type CatalogPermissionRule<TParams extends unknown[] = unknown[]> =
PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>;
// @alpha
export const catalogPlugin: (option: unknown) => BackendRegistrable;
export const catalogPlugin: (options?: unknown) => BackendRegistrable;
// @public (undocumented)
export interface CatalogProcessingEngine {
+1 -1
View File
@@ -566,7 +566,7 @@ export type RunCommandOptions = {
};
// @alpha
export const scaffolderCatalogModule: (option: unknown) => BackendRegistrable;
export const scaffolderCatalogModule: (options?: unknown) => BackendRegistrable;
// @public (undocumented)
export class ScaffolderEntitiesProcessor implements CatalogProcessor {