Merge pull request #15743 from backstage/mob/environments

backend-app-api: add shared environments
This commit is contained in:
Patrik Oldsberg
2023-01-16 15:57:08 +01:00
committed by GitHub
11 changed files with 362 additions and 8 deletions
@@ -16,10 +16,17 @@
import {
coreServices,
createBackendPlugin,
createServiceFactory,
createServiceRef,
createSharedEnvironment,
} from '@backstage/backend-plugin-api';
import { mockConfigFactory } from '@backstage/backend-test-utils';
import { createBackend } from './CreateBackend';
const fooServiceRef = createServiceRef<string>({ id: 'foo', scope: 'root' });
const barServiceRef = createServiceRef<string>({ id: 'bar', scope: 'root' });
describe('createBackend', () => {
it('should not throw when overriding a default service implementation', () => {
expect(() =>
@@ -69,4 +76,91 @@ describe('createBackend', () => {
}),
).toThrow('The core.pluginMetadata service cannot be overridden');
});
it('should throw if an unsupported InternalSharedEnvironment version is passed in', () => {
expect(() =>
createBackend({
env: {} as any,
}),
).toThrow(
"Shared environment version 'undefined' is invalid or not supported",
);
expect(() =>
createBackend({
env: { version: {} } as any,
}),
).toThrow(
"Shared environment version '[object Object]' is invalid or not supported",
);
expect(() =>
createBackend({
env: { version: 'v2' } as any,
}),
).toThrow("Shared environment version 'v2' is invalid or not supported");
});
it('should prioritize services correctly', async () => {
const backend = createBackend({
env: createSharedEnvironment({
services: [
createServiceFactory({
service: coreServices.rootHttpRouter,
deps: {},
async factory() {
return {
use() {},
};
},
}),
mockConfigFactory({
data: { root: 'root-env' },
}),
createServiceFactory({
service: fooServiceRef,
deps: {},
async factory() {
return 'foo-env';
},
}),
createServiceFactory({
service: barServiceRef,
deps: {},
async factory() {
return 'bar-env';
},
}),
],
})(),
services: [
createServiceFactory({
service: fooServiceRef,
deps: {},
factory: async () => 'foo-backend',
}),
],
});
expect.assertions(3);
backend.add(
createBackendPlugin({
id: 'test',
register(reg) {
reg.registerInit({
deps: {
config: coreServices.config,
foo: fooServiceRef,
bar: barServiceRef,
},
async init({ config, foo, bar }) {
expect(config.get('root')).toBe('root-env');
expect(foo).toBe('foo-backend');
expect(bar).toBe('bar-env');
},
});
},
})(),
);
await backend.start();
});
});
+37 -8
View File
@@ -33,7 +33,15 @@ import {
urlReaderFactory,
identityFactory,
} from '@backstage/backend-app-api';
import { ServiceFactoryOrFunction } from '@backstage/backend-plugin-api';
import {
ServiceFactory,
ServiceFactoryOrFunction,
SharedBackendEnvironment,
} from '@backstage/backend-plugin-api';
// Internal import of the type to avoid needing to export this.
// eslint-disable-next-line monorepo/no-internal-import
import type { InternalSharedBackendEnvironment } from '@backstage/backend-plugin-api/src/wiring/createSharedEnvironment';
export const defaultServiceFactories = [
cacheFactory(),
@@ -57,6 +65,7 @@ export const defaultServiceFactories = [
* @public
*/
export interface CreateBackendOptions {
env?: SharedBackendEnvironment;
services?: ServiceFactoryOrFunction[];
}
@@ -64,15 +73,35 @@ export interface CreateBackendOptions {
* @public
*/
export function createBackend(options?: CreateBackendOptions): Backend {
const services = new Array<ServiceFactory>();
// Highest priority: Services passed directly to createBackend
const providedServices = (options?.services ?? []).map(sf =>
typeof sf === 'function' ? sf() : sf,
);
const providedIds = new Set(providedServices.map(sf => sf.service.id));
const neededDefaultFactories = defaultServiceFactories.filter(
sf => !providedIds.has(sf.service.id),
);
services.push(...providedServices);
return createSpecializedBackend({
services: [...neededDefaultFactories, ...providedServices],
});
// Middle priority: Services from the shared environment
if (options?.env) {
const env = options.env as unknown as InternalSharedBackendEnvironment;
if (env.version !== 'v1') {
throw new Error(
`Shared environment version '${env.version}' is invalid or not supported`,
);
}
const environmentServices =
env.services?.filter(
sf => !services.some(({ service }) => sf.service.id === service.id),
) ?? [];
services.push(...environmentServices);
}
// Lowest priority: Default services that are not already provided by environment or directly to createBackend
const defaultServices = defaultServiceFactories.filter(
sf => !services.some(({ service }) => service.id === sf.service.id),
);
services.push(...defaultServices);
return createSpecializedBackend({ services });
}