Merge pull request #13510 from backstage/mob/factories

backend-next: incorporate options into createServiceFactory
This commit is contained in:
Patrik Oldsberg
2022-09-09 15:17:14 +02:00
committed by GitHub
12 changed files with 193 additions and 42 deletions
+15 -3
View File
@@ -109,16 +109,28 @@ export function createServiceFactory<
TDeps extends {
[name in string]: unknown;
},
TOpts extends
| {
[name in string]: unknown;
}
| undefined = undefined,
>(factory: {
service: ServiceRef<TService>;
deps: TypesToServiceRef<TDeps>;
factory(deps: DepsToDepFactories<TDeps>): Promise<FactoryFunc<TImpl>>;
}): ServiceFactory<TService>;
factory(
deps: DepsToDepFactories<TDeps>,
options: TOpts,
): Promise<FactoryFunc<TImpl>>;
}): undefined extends TOpts
? (options?: TOpts) => ServiceFactory<TService>
: (options: TOpts) => ServiceFactory<TService>;
// @public (undocumented)
export function createServiceRef<T>(options: {
id: string;
defaultFactory?: (service: ServiceRef<T>) => Promise<ServiceFactory<T>>;
defaultFactory?: (
service: ServiceRef<T>,
) => Promise<ServiceFactory<T> | (() => ServiceFactory<T>)>;
}): ServiceRef<T>;
// @public (undocumented)
@@ -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 { createServiceFactory, createServiceRef } from './types';
describe('createServiceFactory', () => {
it('should create a meta factory with no options', () => {
const ref = createServiceRef<string>({ id: 'x' });
const metaFactory = createServiceFactory({
service: ref,
deps: {},
async factory(_deps) {
return async () => '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);
metaFactory(undefined);
metaFactory();
});
it('should create a meta factory with optional options', () => {
const ref = createServiceRef<string>({ id: 'x' });
const metaFactory = createServiceFactory({
service: ref,
deps: {},
async factory(_deps, _opts?: { x: number }) {
return async () => 'x';
},
});
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory(null);
metaFactory(undefined);
metaFactory();
});
it('should create a meta factory with required options', () => {
const ref = createServiceRef<string>({ id: 'x' });
const metaFactory = createServiceFactory({
service: ref,
deps: {},
async factory(_deps, _opts: { x: number }) {
return async () => 'x';
},
});
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
// @ts-expect-error
metaFactory();
});
});
@@ -41,7 +41,9 @@ export type InternalServiceRef<T> = ServiceRef<T> & {
* The default factory that will be used to create service
* instances if no other factory is provided.
*/
__defaultFactory?: (service: ServiceRef<T>) => Promise<ServiceFactory<T>>;
__defaultFactory?: (
service: ServiceRef<T>,
) => Promise<ServiceFactory<T> | (() => ServiceFactory<T>)>;
};
/** @public */
@@ -67,7 +69,9 @@ export type ServiceFactory<TService = unknown> = {
*/
export function createServiceRef<T>(options: {
id: string;
defaultFactory?: (service: ServiceRef<T>) => Promise<ServiceFactory<T>>;
defaultFactory?: (
service: ServiceRef<T>,
) => Promise<ServiceFactory<T> | (() => ServiceFactory<T>)>;
}): ServiceRef<T> {
const { id, defaultFactory } = options;
return {
@@ -90,10 +94,22 @@ export function createServiceFactory<
TService,
TImpl extends TService,
TDeps extends { [name in string]: unknown },
TOpts extends { [name in string]: unknown } | undefined = undefined,
>(factory: {
service: ServiceRef<TService>;
deps: TypesToServiceRef<TDeps>;
factory(deps: DepsToDepFactories<TDeps>): Promise<FactoryFunc<TImpl>>;
}): ServiceFactory<TService> {
return factory as ServiceFactory<TService>;
factory(
deps: DepsToDepFactories<TDeps>,
options: TOpts,
): Promise<FactoryFunc<TImpl>>;
}): undefined extends TOpts
? (options?: TOpts) => ServiceFactory<TService>
: (options: TOpts) => ServiceFactory<TService> {
return (options?: TOpts) => ({
service: factory.service,
deps: factory.deps,
factory(deps: DepsToDepFactories<TDeps>) {
return factory.factory(deps, options!);
},
});
}