diff --git a/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts b/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts new file mode 100644 index 0000000000..7561e229a2 --- /dev/null +++ b/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2023 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 { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; +import { createSpecializedBackend } from './createSpecializedBackend'; + +describe('createSpecializedBackend', () => { + it('should create a backend without services', () => { + expect(() => createSpecializedBackend({ services: [] })).not.toThrow(); + }); + + it('should throw on duplicate service implementations', () => { + expect(() => + createSpecializedBackend({ + services: [ + createServiceFactory({ + service: coreServices.rootLifecycle, + deps: {}, + factory: async () => ({ addShutdownHook: () => {} }), + }), + createServiceFactory({ + service: coreServices.rootLifecycle, + deps: {}, + factory: async () => ({ addShutdownHook: () => {} }), + }), + ], + }), + ).toThrow( + 'Duplicate service implementations provided for core.rootLifecycle', + ); + }); + + it('should throw when providing a plugin metadata service implementation', () => { + expect(() => + createSpecializedBackend({ + services: [ + createServiceFactory({ + service: coreServices.pluginMetadata, + deps: {}, + factory: async () => async () => ({ getId: () => 'test' }), + }), + ], + }), + ).toThrow('The core.plugin-metadata service cannot be overridden'); + }); +}); diff --git a/packages/backend-app-api/src/wiring/createSpecializedBackend.ts b/packages/backend-app-api/src/wiring/createSpecializedBackend.ts new file mode 100644 index 0000000000..15e51f7c4c --- /dev/null +++ b/packages/backend-app-api/src/wiring/createSpecializedBackend.ts @@ -0,0 +1,51 @@ +/* + * 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 { coreServices } from '@backstage/backend-plugin-api'; +import { BackstageBackend } from './BackstageBackend'; +import { Backend, CreateSpecializedBackendOptions } from './types'; + +/** + * @public + */ +export function createSpecializedBackend( + options: CreateSpecializedBackendOptions, +): Backend { + const services = options.services.map(sf => + typeof sf === 'function' ? sf() : sf, + ); + + const exists = new Set(); + const duplicates = new Set(); + for (const { service } of services) { + if (exists.has(service.id)) { + duplicates.add(service.id); + } else { + exists.add(service.id); + } + } + if (duplicates.size > 0) { + const ids = Array.from(duplicates).join(', '); + throw new Error(`Duplicate service implementations provided for ${ids}`); + } + if (exists.has(coreServices.pluginMetadata.id)) { + throw new Error( + `The ${coreServices.pluginMetadata.id} service cannot be overridden`, + ); + } + + return new BackstageBackend(services); +} diff --git a/packages/backend-app-api/src/wiring/index.ts b/packages/backend-app-api/src/wiring/index.ts index 2f55076922..6e0fb272ae 100644 --- a/packages/backend-app-api/src/wiring/index.ts +++ b/packages/backend-app-api/src/wiring/index.ts @@ -19,4 +19,4 @@ export type { CreateSpecializedBackendOptions, ServiceOrExtensionPoint, } from './types'; -export { createSpecializedBackend } from './types'; +export { createSpecializedBackend } from './createSpecializedBackend'; diff --git a/packages/backend-app-api/src/wiring/types.ts b/packages/backend-app-api/src/wiring/types.ts index d3ffef731c..115488d596 100644 --- a/packages/backend-app-api/src/wiring/types.ts +++ b/packages/backend-app-api/src/wiring/types.ts @@ -15,13 +15,11 @@ */ import { - ServiceFactory, BackendFeature, ExtensionPoint, + ServiceFactory, ServiceRef, - coreServices, } from '@backstage/backend-plugin-api'; -import { BackstageBackend } from './BackstageBackend'; /** * @public @@ -58,38 +56,6 @@ export interface EnumerableServiceHolder extends ServiceHolder { getServiceRefs(): ServiceRef[]; } -/** - * @public - */ -export function createSpecializedBackend( - options: CreateSpecializedBackendOptions, -): Backend { - const services = options.services.map(sf => - typeof sf === 'function' ? sf() : sf, - ); - - const exists = new Set(); - const duplicates = new Set(); - for (const { service } of services) { - if (exists.has(service.id)) { - duplicates.add(service.id); - } else { - exists.add(service.id); - } - } - if (duplicates.size > 0) { - const ids = Array.from(duplicates).join(', '); - throw new Error(`Duplicate service implementations provided for ${ids}`); - } - if (exists.has(coreServices.pluginMetadata.id)) { - throw new Error( - `The ${coreServices.pluginMetadata.id} service cannot be overridden`, - ); - } - - return new BackstageBackend(services); -} - /** * @public */