remove service compat too

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2024-08-15 16:46:19 +02:00
parent d425fc4dfd
commit 9080f57970
57 changed files with 381 additions and 946 deletions
+2 -58
View File
@@ -291,25 +291,9 @@ export function createServiceFactory<
TDeps extends {
[name in string]: ServiceRef<unknown, 'root'>;
},
TOpts extends object | undefined = undefined,
>(
options: RootServiceFactoryOptions<TService, TInstances, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TInstances>;
// @public @deprecated
export function createServiceFactory<
TService,
TInstances extends 'singleton' | 'multiton',
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown, 'root'>;
},
TOpts extends object | undefined = undefined,
>(
options: (
options?: TOpts,
) => RootServiceFactoryOptions<TService, TInstances, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TInstances, TOpts>;
): ServiceFactory<TService, 'root', TInstances>;
// @public
export function createServiceFactory<
@@ -320,7 +304,6 @@ export function createServiceFactory<
[name in string]: ServiceRef<unknown>;
},
TContext = undefined,
TOpts extends object | undefined = undefined,
>(
options: PluginServiceFactoryOptions<
TService,
@@ -329,29 +312,7 @@ export function createServiceFactory<
TImpl,
TDeps
>,
): ServiceFactoryCompat<TService, 'plugin', TInstances>;
// @public @deprecated
export function createServiceFactory<
TService,
TInstances extends 'singleton' | 'multiton',
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown>;
},
TContext = undefined,
TOpts extends object | undefined = undefined,
>(
options: (
options?: TOpts,
) => PluginServiceFactoryOptions<
TService,
TInstances,
TContext,
TImpl,
TDeps
>,
): ServiceFactoryCompat<TService, 'plugin', TInstances, TOpts>;
): ServiceFactory<TService, 'plugin', TInstances>;
// @public
export function createServiceRef<TService>(
@@ -671,19 +632,6 @@ export interface ServiceFactory<
service: ServiceRef<TService, TScope, TInstances>;
}
// @public @deprecated (undocumented)
export interface ServiceFactoryCompat<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
TInstances extends 'singleton' | 'multiton' = 'singleton' | 'multiton',
TOpts extends object | undefined = undefined,
> extends ServiceFactory<TService, TScope, TInstances> {
// @deprecated (undocumented)
(
...options: undefined extends TOpts ? [] : [options?: TOpts]
): ServiceFactory<TService, TScope, TInstances>;
}
// @public @deprecated
export type ServiceFactoryOrFunction = ServiceFactory | (() => ServiceFactory);
@@ -710,10 +658,6 @@ export interface ServiceRefOptions<
defaultFactory?(
service: ServiceRef<TService, TScope>,
): Promise<ServiceFactory>;
// @deprecated (undocumented)
defaultFactory?(
service: ServiceRef<TService, TScope>,
): Promise<() => ServiceFactory>;
// (undocumented)
id: string;
// (undocumented)
@@ -21,6 +21,5 @@ export type {
PluginServiceFactoryOptions,
RootServiceFactoryOptions,
ServiceFactoryOrFunction,
ServiceFactoryCompat,
} from './types';
export { createServiceRef, createServiceFactory } from './types';
@@ -23,15 +23,11 @@ import {
const ref = createServiceRef<string>({ id: 'x' });
const rootDep = createServiceRef<number>({ id: 'y', scope: 'root' });
const pluginDep = createServiceRef<boolean>({ id: 'z' });
interface TestOptions {
x: number;
}
function unused(..._any: any[]) {}
describe('createServiceFactory', () => {
it('should create a sync factory', () => {
const metaFactory = createServiceFactory({
it('should create a plugin scoped factory', () => {
const factory = createServiceFactory({
service: ref,
deps: {},
createRootContext() {},
@@ -39,52 +35,26 @@ describe('createServiceFactory', () => {
return 'x';
},
});
expect(metaFactory).toEqual(expect.any(Function));
expect(metaFactory().$$type).toBe('@backstage/BackendFeature');
expect((metaFactory() as InternalServiceFactory).featureType).toBe(
'service',
);
expect(metaFactory().service).toBe(ref);
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
// @ts-expect-error
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
metaFactory();
expect(factory.$$type).toBe('@backstage/BackendFeature');
expect((factory as InternalServiceFactory).featureType).toBe('service');
expect(factory.service).toBe(ref);
});
it('should create a sync root factory', () => {
const metaFactory = createServiceFactory({
it('should create a root scoped factory', () => {
const factory = createServiceFactory({
service: rootDep,
deps: {},
factory(_deps) {
return 0;
},
});
expect(metaFactory).toEqual(expect.any(Function));
expect(metaFactory().service).toBe(rootDep);
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
// @ts-expect-error
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
metaFactory();
expect(factory.$$type).toBe('@backstage/BackendFeature');
expect((factory as InternalServiceFactory).featureType).toBe('service');
expect(factory.service).toBe(rootDep);
});
it('should create a factory', () => {
const metaFactory = createServiceFactory({
it('should create a plugin scoped factory with a root context', () => {
const factory = createServiceFactory({
service: ref,
deps: {},
async createRootContext() {},
@@ -92,85 +62,13 @@ describe('createServiceFactory', () => {
return 'x';
},
});
expect(metaFactory).toEqual(expect.any(Function));
expect(metaFactory().service).toBe(ref);
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
// @ts-expect-error
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
metaFactory();
});
it('should create a factory with options', () => {
const metaFactory = createServiceFactory((_opts?: { x: number }) => ({
service: ref,
deps: {},
async createRootContext() {},
async factory() {
return 'x';
},
}));
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory({ x: 1, y: 2 });
// @ts-expect-error
metaFactory(null);
metaFactory(undefined);
metaFactory();
});
it('should not be allowed to require options', () => {
// @ts-expect-error
const metaFactory = createServiceFactory((_opts: { x: number }) => ({
service: ref,
deps: {},
async createRootContext() {},
async factory() {
return 'x';
},
}));
expect(metaFactory).toEqual(expect.any(Function));
});
it('should create a factory with options as interface', () => {
const metaFactory = createServiceFactory((_opts?: TestOptions) => ({
service: ref,
deps: {},
async createRootContext() {},
async factory() {
return 'x';
},
}));
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory({ x: 1, y: 2 });
// @ts-expect-error
metaFactory(null);
metaFactory(undefined);
metaFactory();
expect(factory.$$type).toBe('@backstage/BackendFeature');
expect((factory as InternalServiceFactory).featureType).toBe('service');
expect(factory.service).toBe(ref);
});
it('should create root scoped factory with dependencies', () => {
const metaFactory = createServiceFactory({
const factory = createServiceFactory({
service: createServiceRef({ id: 'foo', scope: 'root' }),
deps: {
root: rootDep,
@@ -183,48 +81,11 @@ describe('createServiceFactory', () => {
return 0;
},
});
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory({});
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
metaFactory();
expect(factory.$$type).toBe('@backstage/BackendFeature');
});
it('should create root scoped factory with dependencies and options', () => {
const metaFactory = createServiceFactory((_options?: TestOptions) => ({
service: createServiceRef({ id: 'foo', scope: 'root' }),
deps: {
root: rootDep,
},
async factory({ root }) {
const root1: number = root;
// @ts-expect-error
const root2: string = root;
unused(root1, root2);
return 0;
},
}));
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory({ x: 1, y: 2 });
// @ts-expect-error
metaFactory(null);
metaFactory(undefined);
metaFactory();
});
it('should create factory with dependencies', () => {
const metaFactory = createServiceFactory({
it('should create a plugin scoped factory with dependencies', () => {
const factory = createServiceFactory({
service: createServiceRef({ id: 'derp' }),
deps: {
root: rootDep,
@@ -251,19 +112,11 @@ describe('createServiceFactory', () => {
return 'x';
},
});
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory({});
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
metaFactory();
expect(factory.$$type).toBe('@backstage/BackendFeature');
});
it('should create factory with dependencies with optional derpFactory', () => {
const metaFactory = createServiceFactory({
const factory = createServiceFactory({
service: createServiceRef({ id: 'derp' }),
deps: {
root: rootDep,
@@ -280,61 +133,12 @@ describe('createServiceFactory', () => {
return 'x';
},
});
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory({});
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
metaFactory();
});
it('should create factory with options and dependencies', () => {
const metaFactory = createServiceFactory((_opts?: TestOptions) => ({
service: ref,
deps: {
root: rootDep,
plugin: pluginDep,
},
async createRootContext({ root }) {
const root1: number = root;
// @ts-expect-error
const root2: string = root;
unused(root1, root2);
return { root };
},
async factory({ plugin }, { root }) {
const root1: number = root;
// @ts-expect-error
const root2: string = root;
const plugin3: boolean = plugin;
// @ts-expect-error
const plugin4: number = plugin;
unused(root1, root2, plugin3, plugin4);
return 'x';
},
}));
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory({ x: 1, y: 2 });
// @ts-expect-error
metaFactory(null);
metaFactory(undefined);
metaFactory();
expect(factory.$$type).toBe('@backstage/BackendFeature');
});
it('should support old service refs without a multiton field', () => {
const oldPluginDep = pluginDep as Omit<typeof pluginDep, 'multiton'>; // Old refs don't have a multiton field
const metaFactory = createServiceFactory({
const factory = createServiceFactory({
service: ref,
deps: {
plugin: oldPluginDep,
@@ -347,82 +151,6 @@ describe('createServiceFactory', () => {
return 'x';
},
});
expect(metaFactory).toEqual(expect.any(Function));
});
it('should only allow objects as options', () => {
// @ts-expect-error
const metaFactory = createServiceFactory((_opts: string) => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
createServiceFactory((_opts: number) => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
// @ts-expect-error
createServiceFactory((_opts: symbol) => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
// @ts-expect-error
createServiceFactory((_opts: bigint) => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
// @ts-expect-error
createServiceFactory((_opts: 'string') => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
// @ts-expect-error
createServiceFactory((_opts: Array) => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
// @ts-expect-error
createServiceFactory((_opts: Map) => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
// @ts-expect-error
createServiceFactory((_opts: Set) => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
// @ts-expect-error
createServiceFactory((_opts: null) => ({
service: ref,
deps: {},
async factory() {
return async () => 'x';
},
}));
expect(factory.$$type).toBe('@backstage/BackendFeature');
});
});
@@ -59,24 +59,6 @@ export interface ServiceFactory<
service: ServiceRef<TService, TScope, TInstances>;
}
/**
* @public
* @deprecated This type exists only as a helper for old code that relied on `createServiceFactory` to return `() => ServiceFactory` instead of `ServiceFactory`. You should remove the `()` parentheses at the end of your usages. This type will be removed in a future release.
*/
export interface ServiceFactoryCompat<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
TInstances extends 'singleton' | 'multiton' = 'singleton' | 'multiton',
TOpts extends object | undefined = undefined,
> extends ServiceFactory<TService, TScope, TInstances> {
/**
* @deprecated Callable service factories will be removed in a future release, please re-implement the service factory using the available APIs instead. If no options are being passed, you can simply remove the trailing `()`.
*/
(
...options: undefined extends TOpts ? [] : [options?: TOpts]
): ServiceFactory<TService, TScope, TInstances>;
}
/** @internal */
export interface InternalServiceFactory<
TService = unknown,
@@ -114,12 +96,6 @@ export interface ServiceRefOptions<
defaultFactory?(
service: ServiceRef<TService, TScope>,
): Promise<ServiceFactory>;
/**
* @deprecated The defaultFactory must return a plain `ServiceFactory` object, support for returning a function will be removed.
*/
defaultFactory?(
service: ServiceRef<TService, TScope>,
): Promise<() => ServiceFactory>;
}
/**
@@ -179,7 +155,7 @@ export function createServiceRef<
} as ServiceRef<TService, typeof scope, TInstances> & {
__defaultFactory?: (
service: ServiceRef<TService>,
) => Promise<ServiceFactory<TService> | (() => ServiceFactory<TService>)>;
) => Promise<ServiceFactory<TService>>;
};
}
@@ -259,31 +235,9 @@ export function createServiceFactory<
TInstances extends 'singleton' | 'multiton',
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown, 'root'> },
TOpts extends object | undefined = undefined,
>(
options: RootServiceFactoryOptions<TService, TInstances, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TInstances>;
/**
* Creates a root scoped service factory with optional options.
*
* @deprecated The ability to define options for service factories is deprecated
* and will be removed. Please use the non-callback form of createServiceFactory
* and provide an API that allows for a simple re-implementation of the service
* factory instead.
* @public
* @param options - The service factory configuration.
*/
export function createServiceFactory<
TService,
TInstances extends 'singleton' | 'multiton',
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown, 'root'> },
TOpts extends object | undefined = undefined,
>(
options: (
options?: TOpts,
) => RootServiceFactoryOptions<TService, TInstances, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TInstances, TOpts>;
): ServiceFactory<TService, 'root', TInstances>;
/**
* Creates a plugin scoped service factory without options.
*
@@ -296,7 +250,6 @@ export function createServiceFactory<
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
TContext = undefined,
TOpts extends object | undefined = undefined,
>(
options: PluginServiceFactoryOptions<
TService,
@@ -305,100 +258,22 @@ export function createServiceFactory<
TImpl,
TDeps
>,
): ServiceFactoryCompat<TService, 'plugin', TInstances>;
/**
* Creates a plugin scoped service factory with optional options.
*
* @deprecated The ability to define options for service factories is deprecated
* and will be removed. Please use the non-callback form of createServiceFactory
* and provide an API that allows for a simple re-implementation of the service
* factory instead.
* @public
* @param options - The service factory configuration.
*/
export function createServiceFactory<
TService,
TInstances extends 'singleton' | 'multiton',
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
TContext = undefined,
TOpts extends object | undefined = undefined,
>(
options: (
options?: TOpts,
) => PluginServiceFactoryOptions<
TService,
TInstances,
TContext,
TImpl,
TDeps
>,
): ServiceFactoryCompat<TService, 'plugin', TInstances, TOpts>;
): ServiceFactory<TService, 'plugin', TInstances>;
export function createServiceFactory<
TService,
TInstances extends 'singleton' | 'multiton',
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
TContext,
TOpts extends object | undefined = undefined,
>(
options:
| RootServiceFactoryOptions<TService, TInstances, TImpl, TDeps>
| PluginServiceFactoryOptions<TService, TInstances, TContext, TImpl, TDeps>
| ((
options: TOpts,
) => RootServiceFactoryOptions<TService, TInstances, TImpl, TDeps>)
| ((
options: TOpts,
) => PluginServiceFactoryOptions<
TService,
TInstances,
TContext,
TImpl,
TDeps
>)
| (() => RootServiceFactoryOptions<TService, TInstances, TImpl, TDeps>)
| (() => PluginServiceFactoryOptions<
TService,
TInstances,
TContext,
TImpl,
TDeps
>),
): ServiceFactoryCompat<
TService,
'root' | 'plugin',
'singleton' | 'multiton',
TOpts
> {
const configCallback =
typeof options === 'function' ? options : () => options;
const factory = (
o?: TOpts,
): InternalServiceFactory<TService, 'plugin' | 'root'> => {
const anyConf = configCallback(o!);
if (anyConf.service.scope === 'root') {
const c = anyConf as RootServiceFactoryOptions<
TService,
TInstances,
TImpl,
TDeps
>;
return {
$$type: '@backstage/BackendFeature',
version: 'v1',
featureType: 'service',
service: c.service,
initialization: c.initialization,
deps: c.deps,
factory: async (deps: ServiceRefsToInstances<TDeps, 'root'>) =>
c.factory(deps),
};
}
const c = anyConf as PluginServiceFactoryOptions<
| PluginServiceFactoryOptions<TService, TInstances, TContext, TImpl, TDeps>,
): ServiceFactory<TService, 'root' | 'plugin', 'singleton' | 'multiton'> {
if (options.service.scope === 'root') {
const c = options as RootServiceFactoryOptions<
TService,
TInstances,
TContext,
TImpl,
TDeps
>;
@@ -408,23 +283,33 @@ export function createServiceFactory<
featureType: 'service',
service: c.service,
initialization: c.initialization,
...('createRootContext' in c
? {
createRootContext: async (
deps: ServiceRefsToInstances<TDeps, 'root'>,
) => c?.createRootContext?.(deps),
}
: {}),
deps: c.deps,
factory: async (deps: ServiceRefsToInstances<TDeps>, ctx: TContext) =>
c.factory(deps, ctx),
};
};
// This constructs the `ServiceFactoryCompat` type, which is both a plain
// factory object as well as a function that can be called to construct a
// factory, potentially with options. In the future only the plain factory
// form will be supported, but for now we need to allow callers to call the
// factory too.
return Object.assign(factory, factory(undefined as TOpts));
deps: options.deps,
factory: async (deps: ServiceRefsToInstances<TDeps, 'root'>) =>
c.factory(deps),
} as InternalServiceFactory<TService, 'root', TInstances>;
}
const c = options as PluginServiceFactoryOptions<
TService,
TInstances,
TContext,
TImpl,
TDeps
>;
return {
$$type: '@backstage/BackendFeature',
version: 'v1',
featureType: 'service',
service: c.service,
initialization: c.initialization,
...('createRootContext' in options
? {
createRootContext: async (
deps: ServiceRefsToInstances<TDeps, 'root'>,
) => c?.createRootContext?.(deps),
}
: {}),
deps: options.deps,
factory: async (deps: ServiceRefsToInstances<TDeps>, ctx: TContext) =>
c.factory(deps, ctx),
} as InternalServiceFactory<TService, 'plugin', TInstances>;
}