From 60f606736a0208dc0a6eb88964ec1c7be5b69880 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 15:18:38 +0100 Subject: [PATCH 01/13] backend-plugin-api: refactor service factory config to support TS 4.9 Co-authored-by: blam Signed-off-by: Patrik Oldsberg --- .../src/services/system/types.test.ts | 200 ++++++++++++++---- .../src/services/system/types.ts | 141 +++++++++--- 2 files changed, 276 insertions(+), 65 deletions(-) diff --git a/packages/backend-plugin-api/src/services/system/types.test.ts b/packages/backend-plugin-api/src/services/system/types.test.ts index 9cbc45151d..7c0942a4c3 100644 --- a/packages/backend-plugin-api/src/services/system/types.test.ts +++ b/packages/backend-plugin-api/src/services/system/types.test.ts @@ -20,13 +20,19 @@ const ref = createServiceRef({ id: 'x' }); const rootDep = createServiceRef({ id: 'y', scope: 'root' }); const pluginDep = createServiceRef({ id: 'z' }); +interface TestOptions { + x: number; +} +function unused(..._any: any[]) {} + describe('createServiceFactory', () => { it('should create a meta factory with no options', () => { const metaFactory = createServiceFactory({ service: ref, deps: {}, + async rootFactory() {}, async factory(_deps) { - return async () => 'x'; + return 'x'; }, }); expect(metaFactory).toEqual(expect.any(Function)); @@ -49,8 +55,9 @@ describe('createServiceFactory', () => { const metaFactory = createServiceFactory((_opts?: { x: number }) => ({ service: ref, deps: {}, + async rootFactory() {}, async factory() { - return async () => 'x'; + return 'x'; }, })); expect(metaFactory).toEqual(expect.any(Function)); @@ -72,8 +79,9 @@ describe('createServiceFactory', () => { const metaFactory = createServiceFactory((_opts: { x: number }) => ({ service: ref, deps: {}, + async rootFactory() {}, async factory() { - return async () => 'x'; + return 'x'; }, })); expect(metaFactory).toEqual(expect.any(Function)); @@ -94,14 +102,12 @@ describe('createServiceFactory', () => { }); it('should create a meta factory with optional options as interface', () => { - interface TestOptions { - x: number; - } const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ service: ref, deps: {}, + async rootFactory() {}, async factory() { - return async () => 'x'; + return 'x'; }, })); expect(metaFactory).toEqual(expect.any(Function)); @@ -120,14 +126,12 @@ describe('createServiceFactory', () => { }); it('should create a meta factory with required options as interface', () => { - interface TestOptions { - x: number; - } const metaFactory = createServiceFactory((_opts: TestOptions) => ({ service: ref, deps: {}, + async rootFactory() {}, async factory() { - return async () => 'x'; + return 'x'; }, })); expect(metaFactory).toEqual(expect.any(Function)); @@ -147,15 +151,9 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create factory with required options and dependencies', () => { - interface TestOptions { - x: number; - } - - function unused(..._any: any[]) {} - - const metaFactory = createServiceFactory((_opts: TestOptions) => ({ - service: ref, + it('should create root scoped factory with dependencies', () => { + const metaFactory = createServiceFactory({ + service: createServiceRef({ id: 'foo', scope: 'root' }), deps: { root: rootDep, plugin: pluginDep, @@ -164,13 +162,134 @@ describe('createServiceFactory', () => { const root1: number = root; // @ts-expect-error const root2: string = root; - return async ({ plugin }) => { - const plugin3: boolean = plugin; - // @ts-expect-error - const plugin4: number = plugin; - unused(root1, root2, plugin3, plugin4); - return 'x'; - }; + unused(root1, root2); + return 0; + }, + }); + expect(metaFactory).toEqual(expect.any(Function)); + }); + + it('should create root scoped factory with dependencies and optional options', () => { + const metaFactory = createServiceFactory((_options?: TestOptions) => ({ + service: createServiceRef({ id: 'foo', scope: 'root' }), + deps: { + root: rootDep, + plugin: pluginDep, + }, + 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 root scoped factory with dependencies and required options', () => { + const metaFactory = createServiceFactory((_options: TestOptions) => ({ + service: createServiceRef({ id: 'foo', scope: 'root' }), + deps: { + root: rootDep, + plugin: pluginDep, + }, + 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); + // @ts-expect-error + metaFactory(undefined); + // @ts-expect-error + metaFactory(); + }); + + it('should create factory with dependencies', () => { + const metaFactory = createServiceFactory({ + service: createServiceRef({ id: 'derp' }), + deps: { + root: rootDep, + plugin: pluginDep, + }, + async rootFactory({ 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({}); + // @ts-expect-error + metaFactory(null); + // @ts-expect-error + metaFactory(undefined); + metaFactory(); + }); + + it('should create factory with required options and dependencies', () => { + const metaFactory = createServiceFactory((_opts: TestOptions) => ({ + service: ref, + deps: { + root: rootDep, + plugin: pluginDep, + }, + async rootFactory({ 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)); @@ -191,29 +310,28 @@ describe('createServiceFactory', () => { }); it('should create factory with optional options and dependencies', () => { - interface TestOptions { - x: number; - } - - function unused(..._any: any[]) {} - const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ service: ref, deps: { root: rootDep, plugin: pluginDep, }, - async factory({ root }) { + async rootFactory({ root }) { const root1: number = root; // @ts-expect-error const root2: string = root; - return async ({ plugin }) => { - const plugin3: boolean = plugin; - // @ts-expect-error - const plugin4: number = plugin; - unused(root1, root2, plugin3, plugin4); - return 'x'; - }; + 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)); diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 34c0a50005..71359bad68 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -135,46 +135,139 @@ type ServiceRefsToInstances< }; /** @public */ -export interface ServiceFactoryConfig< +export interface RootServiceFactoryConfig< TService, - TScope extends 'root' | 'plugin', TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, > { - service: ServiceRef; + service: ServiceRef; deps: TDeps; + factory(deps: ServiceRefsToInstances): Promise; +} + +/** @public */ +export interface PluginServiceFactoryConfig< + TService, + TContext, + TImpl extends TService, + TDeps extends { [name in string]: ServiceRef }, +> { + service: ServiceRef; + deps: TDeps; + rootFactory(deps: ServiceRefsToInstances): Promise; factory( - deps: ServiceRefsToInstances, - ): TScope extends 'root' - ? Promise - : Promise<(deps: ServiceRefsToInstances) => Promise>; + deps: ServiceRefsToInstances, + context: TContext, + ): Promise; } /** + * Creates a root scoped service factory. + * * @public + * @param config */ export function createServiceFactory< TService, - TScope extends 'root' | 'plugin', TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, - TOpts extends [options?: object] = [], + TOpts extends object | undefined = undefined, +>( + config: RootServiceFactoryConfig, +): (params: TOpts) => ServiceFactory; +/** + * Creates a root scoped service factory with optional options. + * + * @public + * @param config + */ +export function createServiceFactory< + TService, + TImpl extends TService, + TDeps extends { [name in string]: ServiceRef }, + TOpts extends object | undefined = undefined, +>( + config: (options?: TOpts) => RootServiceFactoryConfig, +): (options?: TOpts) => ServiceFactory; +/** + * Creates a plugin scoped service factory with required options. + * + * @public + * @param config + */ +export function createServiceFactory< + TService, + TImpl extends TService, + TDeps extends { [name in string]: ServiceRef }, + TOpts extends object | undefined = undefined, +>( + config: (options: TOpts) => RootServiceFactoryConfig, +): (options: TOpts) => ServiceFactory; +/** + * Creates a plugin scoped service factory with optional options. + * + * @public + * @param config + */ +export function createServiceFactory< + TService, + TContext, + TImpl extends TService, + TDeps extends { [name in string]: ServiceRef }, + TOpts extends object | undefined = undefined, +>( + config: PluginServiceFactoryConfig, +): () => ServiceFactory; +/** + * Creates a plugin scoped service factory with required options. + * + * @public + * @param config + */ +export function createServiceFactory< + TService, + TContext, + TImpl extends TService, + TDeps extends { [name in string]: ServiceRef }, + TOpts extends object | undefined = undefined, +>( + config: ( + options?: TOpts, + ) => PluginServiceFactoryConfig, +): (options?: TOpts) => ServiceFactory; +export function createServiceFactory< + TService, + TContext, + TImpl extends TService, + TDeps extends { [name in string]: ServiceRef }, + TOpts extends object | undefined = undefined, >( config: - | ServiceFactoryConfig + | PluginServiceFactoryConfig | (( - ...options: TOpts - ) => ServiceFactoryConfig), -): (...params: TOpts) => ServiceFactory { - if (typeof config === 'function') { - return (...opts: TOpts) => { - const c = config(...opts); - return { ...c, scope: c.service.scope } as ServiceFactory; - }; - } - return () => - ({ - ...config, - scope: config.service.scope, - } as ServiceFactory); + options: TOpts, + ) => PluginServiceFactoryConfig), +): (options: TOpts) => ServiceFactory; +export function createServiceFactory< + TService, + TContext, + TImpl extends TService, + TDeps extends { [name in string]: ServiceRef }, + TOpts extends object | undefined = undefined, +>( + config: + | RootServiceFactoryConfig + | PluginServiceFactoryConfig + | ((options: TOpts) => RootServiceFactoryConfig) + | (( + options: TOpts, + ) => PluginServiceFactoryConfig) + | (() => RootServiceFactoryConfig) + | (() => PluginServiceFactoryConfig), +): (options: TOpts) => ServiceFactory { + const configCallback = typeof config === 'function' ? config : () => config; + return (options: TOpts) => { + const c = configCallback(options); + return { ...c, scope: c.service.scope } as ServiceFactory; + }; } From a758c26f4883b4f6f4d939647594abceb26ed8ee Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 15:27:46 +0100 Subject: [PATCH 02/13] backend-plugin-api: make rootFactory optional Co-authored-by: blam Signed-off-by: Patrik Oldsberg --- .../src/services/system/types.test.ts | 62 +++++++++---------- .../src/services/system/types.ts | 2 +- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/packages/backend-plugin-api/src/services/system/types.test.ts b/packages/backend-plugin-api/src/services/system/types.test.ts index 7c0942a4c3..046c711ee7 100644 --- a/packages/backend-plugin-api/src/services/system/types.test.ts +++ b/packages/backend-plugin-api/src/services/system/types.test.ts @@ -199,38 +199,6 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create root scoped factory with dependencies and required options', () => { - const metaFactory = createServiceFactory((_options: TestOptions) => ({ - service: createServiceRef({ id: 'foo', scope: 'root' }), - deps: { - root: rootDep, - plugin: pluginDep, - }, - 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); - // @ts-expect-error - metaFactory(undefined); - // @ts-expect-error - metaFactory(); - }); - it('should create factory with dependencies', () => { const metaFactory = createServiceFactory({ service: createServiceRef({ id: 'derp' }), @@ -267,6 +235,36 @@ describe('createServiceFactory', () => { metaFactory(); }); + it('should create factory with dependencies with optional derpFactory', () => { + const metaFactory = createServiceFactory({ + service: createServiceRef({ id: 'derp' }), + deps: { + root: rootDep, + plugin: pluginDep, + }, + async factory({ root, plugin }) { + 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({}); + // @ts-expect-error + metaFactory(null); + // @ts-expect-error + metaFactory(undefined); + metaFactory(); + }); + it('should create factory with required options and dependencies', () => { const metaFactory = createServiceFactory((_opts: TestOptions) => ({ service: ref, diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 71359bad68..9d57d19e49 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -154,7 +154,7 @@ export interface PluginServiceFactoryConfig< > { service: ServiceRef; deps: TDeps; - rootFactory(deps: ServiceRefsToInstances): Promise; + rootFactory?(deps: ServiceRefsToInstances): Promise; factory( deps: ServiceRefsToInstances, context: TContext, From b3a75d7664efd9a01a31383fed866046b8dbd1cc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 15:31:56 +0100 Subject: [PATCH 03/13] backend-plugin-api: rename rootFactory to createRootContext + default context to undefined Co-authored-by: blam Signed-off-by: Patrik Oldsberg --- .../src/services/system/types.test.ts | 16 ++++++++-------- .../src/services/system/types.ts | 12 +++++++----- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/backend-plugin-api/src/services/system/types.test.ts b/packages/backend-plugin-api/src/services/system/types.test.ts index 046c711ee7..43272a52bf 100644 --- a/packages/backend-plugin-api/src/services/system/types.test.ts +++ b/packages/backend-plugin-api/src/services/system/types.test.ts @@ -30,7 +30,7 @@ describe('createServiceFactory', () => { const metaFactory = createServiceFactory({ service: ref, deps: {}, - async rootFactory() {}, + async createRootContext() {}, async factory(_deps) { return 'x'; }, @@ -55,7 +55,7 @@ describe('createServiceFactory', () => { const metaFactory = createServiceFactory((_opts?: { x: number }) => ({ service: ref, deps: {}, - async rootFactory() {}, + async createRootContext() {}, async factory() { return 'x'; }, @@ -79,7 +79,7 @@ describe('createServiceFactory', () => { const metaFactory = createServiceFactory((_opts: { x: number }) => ({ service: ref, deps: {}, - async rootFactory() {}, + async createRootContext() {}, async factory() { return 'x'; }, @@ -105,7 +105,7 @@ describe('createServiceFactory', () => { const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ service: ref, deps: {}, - async rootFactory() {}, + async createRootContext() {}, async factory() { return 'x'; }, @@ -129,7 +129,7 @@ describe('createServiceFactory', () => { const metaFactory = createServiceFactory((_opts: TestOptions) => ({ service: ref, deps: {}, - async rootFactory() {}, + async createRootContext() {}, async factory() { return 'x'; }, @@ -206,7 +206,7 @@ describe('createServiceFactory', () => { root: rootDep, plugin: pluginDep, }, - async rootFactory({ root }) { + async createRootContext({ root }) { const root1: number = root; // @ts-expect-error const root2: string = root; @@ -272,7 +272,7 @@ describe('createServiceFactory', () => { root: rootDep, plugin: pluginDep, }, - async rootFactory({ root }) { + async createRootContext({ root }) { const root1: number = root; // @ts-expect-error const root2: string = root; @@ -314,7 +314,7 @@ describe('createServiceFactory', () => { root: rootDep, plugin: pluginDep, }, - async rootFactory({ root }) { + async createRootContext({ root }) { const root1: number = root; // @ts-expect-error const root2: string = root; diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 9d57d19e49..5da6dc990a 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -154,7 +154,9 @@ export interface PluginServiceFactoryConfig< > { service: ServiceRef; deps: TDeps; - rootFactory?(deps: ServiceRefsToInstances): Promise; + createRootContext?( + deps: ServiceRefsToInstances, + ): Promise; factory( deps: ServiceRefsToInstances, context: TContext, @@ -211,9 +213,9 @@ export function createServiceFactory< */ export function createServiceFactory< TService, - TContext, TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, + TContext = undefined, TOpts extends object | undefined = undefined, >( config: PluginServiceFactoryConfig, @@ -226,9 +228,9 @@ export function createServiceFactory< */ export function createServiceFactory< TService, - TContext, TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, + TContext = undefined, TOpts extends object | undefined = undefined, >( config: ( @@ -237,9 +239,9 @@ export function createServiceFactory< ): (options?: TOpts) => ServiceFactory; export function createServiceFactory< TService, - TContext, TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, + TContext = undefined, TOpts extends object | undefined = undefined, >( config: @@ -250,9 +252,9 @@ export function createServiceFactory< ): (options: TOpts) => ServiceFactory; export function createServiceFactory< TService, - TContext, TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, + TContext, TOpts extends object | undefined = undefined, >( config: From de42fa4ef492fefab5223627cd88e87473431f50 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 15:41:38 +0100 Subject: [PATCH 04/13] backend-plugin-api: fix unexpected required options for root factories Co-authored-by: blam Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- .../src/services/system/types.test.ts | 15 +++++++++++++-- .../src/services/system/types.ts | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/backend-plugin-api/src/services/system/types.test.ts b/packages/backend-plugin-api/src/services/system/types.test.ts index 43272a52bf..fab0232380 100644 --- a/packages/backend-plugin-api/src/services/system/types.test.ts +++ b/packages/backend-plugin-api/src/services/system/types.test.ts @@ -167,6 +167,14 @@ describe('createServiceFactory', () => { }, }); expect(metaFactory).toEqual(expect.any(Function)); + + // @ts-expect-error + metaFactory({}); + // @ts-expect-error + metaFactory(null); + // @ts-expect-error + metaFactory(undefined); + metaFactory(); }); it('should create root scoped factory with dependencies and optional options', () => { @@ -213,14 +221,17 @@ describe('createServiceFactory', () => { unused(root1, root2); return { root }; }, - async factory({ plugin }, { root }) { + async factory({ plugin, root: rootB }, { root }) { const root1: number = root; // @ts-expect-error const root2: string = root; + const root3: number = rootB; + // @ts-expect-error + const root4: string = rootB; const plugin3: boolean = plugin; // @ts-expect-error const plugin4: number = plugin; - unused(root1, root2, plugin3, plugin4); + unused(root1, root2, root3, root4, plugin3, plugin4); return 'x'; }, }); diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 5da6dc990a..a482d10dfb 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -176,7 +176,7 @@ export function createServiceFactory< TOpts extends object | undefined = undefined, >( config: RootServiceFactoryConfig, -): (params: TOpts) => ServiceFactory; +): () => ServiceFactory; /** * Creates a root scoped service factory with optional options. * From d6f4b92097a685e78c257cf743d878ad06ac3442 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 16:06:20 +0100 Subject: [PATCH 05/13] backend-plugin-api: update ServiceFactory type to match new implementation Co-authored-by: blam Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- packages/backend-plugin-api/src/services/system/types.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index a482d10dfb..618ef1b7b6 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -64,9 +64,11 @@ export type ServiceFactory = scope: 'plugin'; service: ServiceRef; deps: { [key in string]: ServiceRef }; - factory(deps: { [key in string]: unknown }): Promise< - (deps: { [key in string]: unknown }) => Promise - >; + createRootContext?(deps: { [key in string]: unknown }): Promise; + factory( + deps: { [key in string]: unknown }, + context: unknown, + ): Promise; }; /** From eadc8f7df8ba7d8e00d988218b49e8f44a0aa89b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 16:07:03 +0100 Subject: [PATCH 06/13] backend-app-api: update ServiceRegistry for new factory definition Co-authored-by: blam Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- .../src/wiring/ServiceRegistry.test.ts | 67 ++++++++++++------- .../src/wiring/ServiceRegistry.ts | 38 +++++------ 2 files changed, 60 insertions(+), 45 deletions(-) diff --git a/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts b/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts index 405a2fc630..e698ade348 100644 --- a/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts +++ b/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts @@ -29,9 +29,7 @@ const sf1 = createServiceFactory({ service: ref1, deps: {}, async factory() { - return async () => { - return { x: 1 }; - }; + return { x: 1 }; }, })(); @@ -61,7 +59,7 @@ const refDefault1 = createServiceRef<{ x: number }>({ service, deps: {}, async factory() { - return async () => ({ x: 10 }); + return { x: 10 }; }, })(), }); @@ -73,7 +71,7 @@ const refDefault2a = createServiceRef<{ x: number }>({ service, deps: {}, async factory() { - return async () => ({ x: 20 }); + return { x: 20 }; }, }), }); @@ -85,7 +83,7 @@ const refDefault2b = createServiceRef<{ x: number }>({ service, deps: {}, async factory() { - return async () => ({ x: 220 }); + return { x: 220 }; }, }), }); @@ -144,7 +142,7 @@ describe('ServiceRegistry', () => { service: ref1, deps: { rootDep: ref2 }, factory: async ({ rootDep }) => { - return async () => ({ x: rootDep.x }); + return { x: rootDep.x }; }, }); const registry = new ServiceRegistry([factory(), sf2]); @@ -173,8 +171,8 @@ describe('ServiceRegistry', () => { const factory = createServiceFactory({ service: ref, deps: { meta: coreServices.pluginMetadata }, - async factory() { - return async ({ meta }) => ({ pluginId: meta.getId() }); + async factory({ meta }) { + return { pluginId: meta.getId() }; }, }); const registry = new ServiceRegistry([factory()]); @@ -224,13 +222,11 @@ describe('ServiceRegistry', () => { }); it('should only call each default factory loader once', async () => { - const factoryLoader = jest.fn(async (service: ServiceRef) => + const factoryLoader = jest.fn(async (service: ServiceRef) => createServiceFactory({ service, deps: {}, - async factory() { - return async () => {}; - }, + async factory() {}, }), ); const ref = createServiceRef({ @@ -247,10 +243,31 @@ describe('ServiceRegistry', () => { }); it('should not call factory functions more than once', async () => { - const innerFactory = jest.fn(async () => { - return { x: 1 }; + const createRootContext = jest.fn(async () => ({ x: 1 })); + const factory = jest.fn(async () => ({ x: 1 })); + const myFactory = createServiceFactory({ + service: ref1, + deps: {}, + createRootContext, + factory, }); - const factory = jest.fn(async () => innerFactory); + + const registry = new ServiceRegistry([myFactory()]); + + await Promise.all([ + registry.get(ref1, 'catalog')!, + registry.get(ref1, 'catalog')!, + registry.get(ref1, 'catalog')!, + registry.get(ref1, 'scaffolder')!, + registry.get(ref1, 'scaffolder')!, + ]); + + expect(createRootContext).toHaveBeenCalledTimes(1); + expect(factory).toHaveBeenCalledTimes(2); + }); + + it('should not call factory functions more than once without root context', async () => { + const factory = jest.fn(async () => ({ x: 1 })); const myFactory = createServiceFactory({ service: ref1, deps: {}, @@ -267,8 +284,7 @@ describe('ServiceRegistry', () => { registry.get(ref1, 'scaffolder')!, ]); - expect(factory).toHaveBeenCalledTimes(1); - expect(innerFactory).toHaveBeenCalledTimes(2); + expect(factory).toHaveBeenCalledTimes(2); }); it('should throw if dependencies are not available', async () => { @@ -296,9 +312,7 @@ describe('ServiceRegistry', () => { const factoryA = createServiceFactory({ service: refA, deps: { b: refB }, - async factory() { - return async ({ b }) => b; - }, + factory: async ({ b }) => b, }); const factoryB = createServiceFactory({ @@ -320,15 +334,18 @@ describe('ServiceRegistry', () => { const myFactory = createServiceFactory({ service: ref1, deps: {}, - factory() { + createRootContext() { throw new Error('top-level error'); }, + factory() { + throw new Error(`error in plugin`); + }, }); const registry = new ServiceRegistry([myFactory()]); await expect(registry.get(ref1, 'catalog')).rejects.toThrow( - "Failed to instantiate service '1' because the top-level factory function threw an error, Error: top-level error", + "Failed to instantiate service '1' because createRootContext threw an error, Error: top-level error", ); }); @@ -337,9 +354,7 @@ describe('ServiceRegistry', () => { service: ref1, deps: {}, async factory() { - return () => { - throw new Error(`error in plugin`); - }; + throw new Error(`error in plugin`); }, }); diff --git a/packages/backend-app-api/src/wiring/ServiceRegistry.ts b/packages/backend-app-api/src/wiring/ServiceRegistry.ts index b4d75dac1a..68dff3f7c3 100644 --- a/packages/backend-app-api/src/wiring/ServiceRegistry.ts +++ b/packages/backend-app-api/src/wiring/ServiceRegistry.ts @@ -37,12 +37,14 @@ export class ServiceRegistry implements EnumerableServiceHolder { readonly #implementations: Map< ServiceFactory, { - factoryFunc: Promise< - (deps: { [name in string]: unknown }) => Promise - >; + context: Promise; byPlugin: Map>; } >; + readonly #rootServiceImplementations = new Map< + ServiceFactory, + Promise + >(); constructor(factories: Array>) { this.#providedFactories = new Map(factories.map(f => [f.service.id, f])); @@ -56,15 +58,13 @@ export class ServiceRegistry implements EnumerableServiceHolder { ): Promise | undefined { // Special case handling of the plugin metadata service, generating a custom factory for it each time if (ref.id === coreServices.pluginMetadata.id) { - return Promise.resolve({ + return Promise.resolve< + ServiceFactory + >({ scope: 'plugin', service: coreServices.pluginMetadata, deps: {}, - factory: async () => async () => ({ - getId() { - return pluginId; - }, - }), + factory: async () => ({ getId: () => pluginId }), }); } @@ -100,8 +100,6 @@ export class ServiceRegistry implements EnumerableServiceHolder { return Promise.resolve(resolvedFactory); } - #separateMapForTheRootService = new Map>(); - #checkForMissingDeps(factory: ServiceFactory, pluginId: string) { const missingDeps = Object.values(factory.deps).filter(ref => { if (ref.id === coreServices.pluginMetadata.id) { @@ -129,7 +127,7 @@ export class ServiceRegistry implements EnumerableServiceHolder { get(ref: ServiceRef, pluginId: string): Promise | undefined { return this.#resolveFactory(ref, pluginId)?.then(factory => { if (factory.scope === 'root') { - let existing = this.#separateMapForTheRootService.get(factory); + let existing = this.#rootServiceImplementations.get(factory); if (!existing) { this.#checkForMissingDeps(factory, pluginId); const rootDeps = new Array>(); @@ -147,7 +145,7 @@ export class ServiceRegistry implements EnumerableServiceHolder { existing = Promise.all(rootDeps).then(entries => factory.factory(Object.fromEntries(entries)), ); - this.#separateMapForTheRootService.set(factory, existing); + this.#rootServiceImplementations.set(factory, existing); } return existing as Promise; } @@ -165,12 +163,14 @@ export class ServiceRegistry implements EnumerableServiceHolder { } implementation = { - factoryFunc: Promise.all(rootDeps) - .then(entries => factory.factory(Object.fromEntries(entries))) + context: Promise.all(rootDeps) + .then(entries => + factory.createRootContext?.(Object.fromEntries(entries)), + ) .catch(error => { const cause = stringifyError(error); throw new Error( - `Failed to instantiate service '${ref.id}' because the top-level factory function threw an error, ${cause}`, + `Failed to instantiate service '${ref.id}' because createRootContext threw an error, ${cause}`, ); }), byPlugin: new Map(), @@ -188,10 +188,10 @@ export class ServiceRegistry implements EnumerableServiceHolder { allDeps.push(target.then(impl => [name, impl])); } - result = implementation.factoryFunc - .then(func => + result = implementation.context + .then(context => Promise.all(allDeps).then(entries => - func(Object.fromEntries(entries)), + factory.factory(Object.fromEntries(entries), context), ), ) .catch(error => { From b8a14b3023f865266648a69759b68706e09edd78 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 16:16:26 +0100 Subject: [PATCH 07/13] update usages of createServiceFactory Co-authored-by: blam Signed-off-by: Patrik Oldsberg --- .../implementations/cache/cacheFactory.ts | 10 +++--- .../database/databaseFactory.ts | 11 +++--- .../discovery/discoveryFactory.ts | 10 +++--- .../httpRouter/httpRouterFactory.test.ts | 34 +++++++++++++++---- .../httpRouter/httpRouterFactory.ts | 15 ++++---- .../identity/identityFactory.ts | 24 ++++++------- .../lifecycle/lifecycleFactory.ts | 20 +++++------ .../implementations/logger/loggerFactory.ts | 6 ++-- .../permissions/permissionsFactory.ts | 12 +++---- .../scheduler/schedulerFactory.ts | 14 ++++---- .../tokenManager/tokenManagerFactory.ts | 2 +- .../urlReader/urlReaderFactory.ts | 12 +++---- .../wiring/createSpecializedBackend.test.ts | 2 +- .../src/CreateBackend.test.ts | 2 +- .../src/services/system/index.ts | 3 +- .../mockTokenManagerService.ts | 4 +-- .../src/next/wiring/TestBackend.test.ts | 2 +- .../src/next/wiring/TestBackend.ts | 8 ++--- plugins/catalog-node/src/catalogService.ts | 6 ++-- 19 files changed, 101 insertions(+), 96 deletions(-) diff --git a/packages/backend-app-api/src/services/implementations/cache/cacheFactory.ts b/packages/backend-app-api/src/services/implementations/cache/cacheFactory.ts index df4a782a46..426a890c00 100644 --- a/packages/backend-app-api/src/services/implementations/cache/cacheFactory.ts +++ b/packages/backend-app-api/src/services/implementations/cache/cacheFactory.ts @@ -27,10 +27,10 @@ export const cacheFactory = createServiceFactory({ config: coreServices.config, plugin: coreServices.pluginMetadata, }, - async factory({ config }) { - const cacheManager = CacheManager.fromConfig(config); - return async ({ plugin }) => { - return cacheManager.forPlugin(plugin.getId()); - }; + async createRootContext({ config }) { + return CacheManager.fromConfig(config); + }, + async factory({ plugin }, manager) { + return manager.forPlugin(plugin.getId()); }, }); diff --git a/packages/backend-app-api/src/services/implementations/database/databaseFactory.ts b/packages/backend-app-api/src/services/implementations/database/databaseFactory.ts index 52cecdbc4d..8baf84cb69 100644 --- a/packages/backend-app-api/src/services/implementations/database/databaseFactory.ts +++ b/packages/backend-app-api/src/services/implementations/database/databaseFactory.ts @@ -28,8 +28,8 @@ export const databaseFactory = createServiceFactory({ config: coreServices.config, plugin: coreServices.pluginMetadata, }, - async factory({ config }) { - const databaseManager = config.getOptional('backend.database') + async createRootContext({ config }) { + return config.getOptional('backend.database') ? DatabaseManager.fromConfig(config) : DatabaseManager.fromConfig( new ConfigReader({ @@ -38,9 +38,8 @@ export const databaseFactory = createServiceFactory({ }, }), ); - - return async ({ plugin }) => { - return databaseManager.forPlugin(plugin.getId()); - }; + }, + async factory({ plugin }, databaseManager) { + return databaseManager.forPlugin(plugin.getId()); }, }); diff --git a/packages/backend-app-api/src/services/implementations/discovery/discoveryFactory.ts b/packages/backend-app-api/src/services/implementations/discovery/discoveryFactory.ts index 0c04ec6bf0..131bf1d697 100644 --- a/packages/backend-app-api/src/services/implementations/discovery/discoveryFactory.ts +++ b/packages/backend-app-api/src/services/implementations/discovery/discoveryFactory.ts @@ -26,10 +26,10 @@ export const discoveryFactory = createServiceFactory({ deps: { config: coreServices.config, }, - async factory({ config }) { - const discovery = SingleHostDiscovery.fromConfig(config); - return async () => { - return discovery; - }; + async createRootContext({ config }) { + return SingleHostDiscovery.fromConfig(config); + }, + async factory(_, discovery) { + return discovery; }, }); diff --git a/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterFactory.test.ts b/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterFactory.test.ts index 7717f4116e..150bd628f5 100644 --- a/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterFactory.test.ts +++ b/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterFactory.test.ts @@ -27,16 +27,27 @@ describe('httpRouterFactory', () => { ServiceFactory, { scope: 'root' } >; - const innerFactory = await factory.factory({ rootHttpRouter }); const handler1 = () => {}; - const router1 = await innerFactory({ plugin: { getId: () => 'test1' } }); + const router1 = await factory.factory( + { + rootHttpRouter, + plugin: { getId: () => 'test1' }, + }, + undefined, + ); router1.use(handler1); expect(rootHttpRouter.use).toHaveBeenCalledTimes(1); expect(rootHttpRouter.use).toHaveBeenCalledWith('/api/test1', handler1); const handler2 = () => {}; - const router2 = await innerFactory({ plugin: { getId: () => 'test2' } }); + const router2 = await factory.factory( + { + rootHttpRouter, + plugin: { getId: () => 'test2' }, + }, + undefined, + ); router2.use(handler2); expect(rootHttpRouter.use).toHaveBeenCalledTimes(2); expect(rootHttpRouter.use).toHaveBeenCalledWith('/api/test2', handler2); @@ -47,10 +58,15 @@ describe('httpRouterFactory', () => { const factory = httpRouterFactory({ getPath: id => `/some/${id}/path`, }) as Exclude, { scope: 'root' }>; - const innerFactory = await factory.factory({ rootHttpRouter }); const handler1 = () => {}; - const router1 = await innerFactory({ plugin: { getId: () => 'test1' } }); + const router1 = await factory.factory( + { + rootHttpRouter, + plugin: { getId: () => 'test1' }, + }, + undefined, + ); router1.use(handler1); expect(rootHttpRouter.use).toHaveBeenCalledTimes(1); expect(rootHttpRouter.use).toHaveBeenCalledWith( @@ -59,7 +75,13 @@ describe('httpRouterFactory', () => { ); const handler2 = () => {}; - const router2 = await innerFactory({ plugin: { getId: () => 'test2' } }); + const router2 = await factory.factory( + { + rootHttpRouter, + plugin: { getId: () => 'test2' }, + }, + undefined, + ); router2.use(handler2); expect(rootHttpRouter.use).toHaveBeenCalledTimes(2); expect(rootHttpRouter.use).toHaveBeenCalledWith( diff --git a/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterFactory.ts b/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterFactory.ts index 8787b1ad9e..1d52af3b76 100644 --- a/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterFactory.ts +++ b/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterFactory.ts @@ -38,16 +38,13 @@ export const httpRouterFactory = createServiceFactory( plugin: coreServices.pluginMetadata, rootHttpRouter: coreServices.rootHttpRouter, }, - async factory({ rootHttpRouter }) { + async factory({ plugin, rootHttpRouter }) { const getPath = options?.getPath ?? (id => `/api/${id}`); - - return async ({ plugin }) => { - const path = getPath(plugin.getId()); - return { - use(handler: Handler) { - rootHttpRouter.use(path, handler); - }, - }; + const path = getPath(plugin.getId()); + return { + use(handler: Handler) { + rootHttpRouter.use(path, handler); + }, }; }, }), diff --git a/packages/backend-app-api/src/services/implementations/identity/identityFactory.ts b/packages/backend-app-api/src/services/implementations/identity/identityFactory.ts index f554927930..0531c35480 100644 --- a/packages/backend-app-api/src/services/implementations/identity/identityFactory.ts +++ b/packages/backend-app-api/src/services/implementations/identity/identityFactory.ts @@ -34,17 +34,17 @@ export type IdentityFactoryOptions = { }; /** @public */ -export const identityFactory = createServiceFactory({ - service: coreServices.identity, - deps: { - config: coreServices.config, - discovery: coreServices.discovery, - tokenManager: coreServices.tokenManager, - }, +export const identityFactory = createServiceFactory( + (options?: IdentityFactoryOptions) => ({ + service: coreServices.identity, + deps: { + config: coreServices.config, + discovery: coreServices.discovery, + tokenManager: coreServices.tokenManager, + }, - async factory({}, options?: IdentityFactoryOptions) { - return async ({ discovery }) => { + async factory({ discovery }) { return DefaultIdentityClient.create({ discovery, ...options }); - }; - }, -}); + }, + }), +); diff --git a/packages/backend-app-api/src/services/implementations/lifecycle/lifecycleFactory.ts b/packages/backend-app-api/src/services/implementations/lifecycle/lifecycleFactory.ts index 632abd238c..b2f3a9f70b 100644 --- a/packages/backend-app-api/src/services/implementations/lifecycle/lifecycleFactory.ts +++ b/packages/backend-app-api/src/services/implementations/lifecycle/lifecycleFactory.ts @@ -29,18 +29,16 @@ export const lifecycleFactory = createServiceFactory({ rootLifecycle: coreServices.rootLifecycle, pluginMetadata: coreServices.pluginMetadata, }, - async factory({ rootLifecycle }) { - return async ({ logger, pluginMetadata }) => { - const plugin = pluginMetadata.getId(); - return { - addShutdownHook(options: LifecycleServiceShutdownHook): void { - rootLifecycle.addShutdownHook({ - ...options, + async factory({ rootLifecycle, logger, pluginMetadata }) { + const plugin = pluginMetadata.getId(); + return { + addShutdownHook(options: LifecycleServiceShutdownHook): void { + rootLifecycle.addShutdownHook({ + ...options, - logger: options.logger?.child({ plugin }) ?? logger, - }); - }, - }; + logger: options.logger?.child({ plugin }) ?? logger, + }); + }, }; }, }); diff --git a/packages/backend-app-api/src/services/implementations/logger/loggerFactory.ts b/packages/backend-app-api/src/services/implementations/logger/loggerFactory.ts index 5cefe808f8..14b6cc5f35 100644 --- a/packages/backend-app-api/src/services/implementations/logger/loggerFactory.ts +++ b/packages/backend-app-api/src/services/implementations/logger/loggerFactory.ts @@ -26,9 +26,7 @@ export const loggerFactory = createServiceFactory({ rootLogger: coreServices.rootLogger, plugin: coreServices.pluginMetadata, }, - async factory({ rootLogger }) { - return async ({ plugin }) => { - return rootLogger.child({ plugin: plugin.getId() }); - }; + async factory({ rootLogger, plugin }) { + return rootLogger.child({ plugin: plugin.getId() }); }, }); diff --git a/packages/backend-app-api/src/services/implementations/permissions/permissionsFactory.ts b/packages/backend-app-api/src/services/implementations/permissions/permissionsFactory.ts index d3f8d5783c..4eb05e3e4c 100644 --- a/packages/backend-app-api/src/services/implementations/permissions/permissionsFactory.ts +++ b/packages/backend-app-api/src/services/implementations/permissions/permissionsFactory.ts @@ -28,12 +28,10 @@ export const permissionsFactory = createServiceFactory({ discovery: coreServices.discovery, tokenManager: coreServices.tokenManager, }, - async factory({ config }) { - return async ({ discovery, tokenManager }) => { - return ServerPermissionClient.fromConfig(config, { - discovery, - tokenManager, - }); - }; + async factory({ config, discovery, tokenManager }) { + return ServerPermissionClient.fromConfig(config, { + discovery, + tokenManager, + }); }, }); diff --git a/packages/backend-app-api/src/services/implementations/scheduler/schedulerFactory.ts b/packages/backend-app-api/src/services/implementations/scheduler/schedulerFactory.ts index 4e472b78f4..458c5acfd6 100644 --- a/packages/backend-app-api/src/services/implementations/scheduler/schedulerFactory.ts +++ b/packages/backend-app-api/src/services/implementations/scheduler/schedulerFactory.ts @@ -29,13 +29,11 @@ export const schedulerFactory = createServiceFactory({ databaseManager: coreServices.database, logger: coreServices.logger, }, - async factory() { - return async ({ plugin, databaseManager, logger }) => { - return TaskScheduler.forPlugin({ - pluginId: plugin.getId(), - databaseManager, - logger: loggerToWinstonLogger(logger), - }); - }; + async factory({ plugin, databaseManager, logger }) { + return TaskScheduler.forPlugin({ + pluginId: plugin.getId(), + databaseManager, + logger: loggerToWinstonLogger(logger), + }); }, }); diff --git a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.ts b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.ts index cbd9e32d20..bb45f1f92c 100644 --- a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.ts +++ b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.ts @@ -31,6 +31,6 @@ export const tokenManagerFactory = createServiceFactory({ const tokenManager = ServerTokenManager.fromConfig(config, { logger, }); - return async () => tokenManager; + return tokenManager; }, }); diff --git a/packages/backend-app-api/src/services/implementations/urlReader/urlReaderFactory.ts b/packages/backend-app-api/src/services/implementations/urlReader/urlReaderFactory.ts index 758b555d66..1b52ce9604 100644 --- a/packages/backend-app-api/src/services/implementations/urlReader/urlReaderFactory.ts +++ b/packages/backend-app-api/src/services/implementations/urlReader/urlReaderFactory.ts @@ -27,12 +27,10 @@ export const urlReaderFactory = createServiceFactory({ config: coreServices.config, logger: coreServices.logger, }, - async factory() { - return async ({ config, logger }) => { - return UrlReaders.default({ - config, - logger: loggerToWinstonLogger(logger), - }); - }; + async factory({ config, logger }) { + return UrlReaders.default({ + config, + logger: loggerToWinstonLogger(logger), + }); }, }); diff --git a/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts b/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts index c670613657..ad50455e5f 100644 --- a/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts +++ b/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts @@ -53,7 +53,7 @@ describe('createSpecializedBackend', () => { createServiceFactory({ service: coreServices.pluginMetadata, deps: {}, - factory: async () => async () => ({ getId: () => 'test' }), + factory: async () => ({ getId: () => 'test' }), }), ], }), diff --git a/packages/backend-defaults/src/CreateBackend.test.ts b/packages/backend-defaults/src/CreateBackend.test.ts index 89a8e96b28..971ddc67ff 100644 --- a/packages/backend-defaults/src/CreateBackend.test.ts +++ b/packages/backend-defaults/src/CreateBackend.test.ts @@ -63,7 +63,7 @@ describe('createBackend', () => { createServiceFactory({ service: coreServices.pluginMetadata, deps: {}, - factory: async () => async () => ({ getId: () => 'test' }), + factory: async () => ({ getId: () => 'test' }), }), ], }), diff --git a/packages/backend-plugin-api/src/services/system/index.ts b/packages/backend-plugin-api/src/services/system/index.ts index cd15d8127d..74be9f8b11 100644 --- a/packages/backend-plugin-api/src/services/system/index.ts +++ b/packages/backend-plugin-api/src/services/system/index.ts @@ -19,7 +19,8 @@ export type { ServiceRefConfig, TypesToServiceRef, ServiceFactory, - ServiceFactoryConfig, + PluginServiceFactoryConfig, + RootServiceFactoryConfig, ServiceFactoryOrFunction, } from './types'; export { createServiceRef, createServiceFactory } from './types'; diff --git a/packages/backend-test-utils/src/next/implementations/mockTokenManagerService.ts b/packages/backend-test-utils/src/next/implementations/mockTokenManagerService.ts index dd1d6b16f7..9b1fc42f39 100644 --- a/packages/backend-test-utils/src/next/implementations/mockTokenManagerService.ts +++ b/packages/backend-test-utils/src/next/implementations/mockTokenManagerService.ts @@ -34,8 +34,6 @@ export const mockTokenManagerFactory = createServiceFactory({ service: coreServices.tokenManager, deps: {}, async factory() { - return async () => { - return new TokenManagerMock(); - }; + return new TokenManagerMock(); }, }); diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts index 6358051c34..dca463a9ae 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts @@ -106,7 +106,7 @@ describe('TestBackend', () => { deps: {}, service: testRef, factory: async () => { - return async () => testFn; + return testFn; }, }); diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.ts index 21650125d3..31bb7f025d 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.ts @@ -168,7 +168,7 @@ export async function startTestBackend< backend: { baseUrl: `http://localhost:${port}`, listen: { port } }, }), ); - return async () => discovery; + return discovery; }, }); @@ -179,13 +179,13 @@ export async function startTestBackend< const [ref, impl] = serviceDef; if (ref.scope === 'plugin') { return createServiceFactory({ - service: ref, + service: ref as ServiceRef, deps: {}, - factory: async () => async () => impl, + factory: async () => impl, })(); } return createServiceFactory({ - service: ref, + service: ref as ServiceRef, deps: {}, factory: async () => impl, })(); diff --git a/plugins/catalog-node/src/catalogService.ts b/plugins/catalog-node/src/catalogService.ts index 0c54bd2d9d..9a8ff55e1a 100644 --- a/plugins/catalog-node/src/catalogService.ts +++ b/plugins/catalog-node/src/catalogService.ts @@ -33,10 +33,8 @@ export const catalogServiceRef = createServiceRef({ deps: { discoveryApi: coreServices.discovery, }, - async factory() { - return async ({ discoveryApi }) => { - return new CatalogClient({ discoveryApi }); - }; + async factory({ discoveryApi }) { + return new CatalogClient({ discoveryApi }); }, }), }); From 483e907eafc68d57197694b3a6aa76fd4b585d8a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 17:37:08 +0100 Subject: [PATCH 08/13] changesets: added changeset for createRootContext Signed-off-by: Patrik Oldsberg --- .changeset/nice-jars-talk.md | 7 +++++ .changeset/real-poets-run.md | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 .changeset/nice-jars-talk.md create mode 100644 .changeset/real-poets-run.md diff --git a/.changeset/nice-jars-talk.md b/.changeset/nice-jars-talk.md new file mode 100644 index 0000000000..9b92f67042 --- /dev/null +++ b/.changeset/nice-jars-talk.md @@ -0,0 +1,7 @@ +--- +'@backstage/backend-test-utils': patch +'@backstage/backend-app-api': patch +'@backstage/plugin-catalog-node': patch +--- + +Internal updates of `createServiceFactory` from `@backstage/backend-plugin-api`. diff --git a/.changeset/real-poets-run.md b/.changeset/real-poets-run.md new file mode 100644 index 0000000000..f1bef6ed68 --- /dev/null +++ b/.changeset/real-poets-run.md @@ -0,0 +1,56 @@ +--- +'@backstage/backend-plugin-api': patch +--- + +The `createServiceFactory` function has been updated to no longer use a duplicate callback pattern for plugin scoped services. The outer callback is now replaced by an optional `createRootContext` method. This change was made in order to support TypeScript 4.9, but it also simplifies the API surface a bit, especially for plugin scoped service factories that don't need to create a root context. + +A factory that previously would have looked like this: + +```ts +createServiceFactory({ + service: coreServices.cache, + deps: { + config: coreServices.config, + plugin: coreServices.pluginMetadata, + }, + async factory({ config }) { + const cacheManager = CacheManager.fromConfig(config); + return async ({ plugin }) => { + return cacheManager.forPlugin(plugin.getId()); + }; + }, +}); +``` + +Now instead looks like this: + +```ts +createServiceFactory({ + service: coreServices.cache, + deps: { + config: coreServices.config, + plugin: coreServices.pluginMetadata, + }, + async createRootContext({ config }) { + return CacheManager.fromConfig(config); + }, + async factory({ plugin }, manager) { + return manager.forPlugin(plugin.getId()); + }, +}); +``` + +Although in many cases the `createRootContext` isn't needed, for example: + +```ts +createServiceFactory({ + service: coreServices.logger, + deps: { + rootLogger: coreServices.rootLogger, + plugin: coreServices.pluginMetadata, + }, + async factory({ rootLogger, plugin }) { + return rootLogger.child({ plugin: plugin.getId() }); + }, +}); +``` From d08a7251fec31f8673fbb589f7a198ccdcbdeac6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 17:37:50 +0100 Subject: [PATCH 09/13] backend-app-api: fixups from service factory refactor Signed-off-by: Patrik Oldsberg --- .../services/implementations/discovery/discoveryFactory.ts | 5 +---- .../src/services/implementations/identity/identityFactory.ts | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/backend-app-api/src/services/implementations/discovery/discoveryFactory.ts b/packages/backend-app-api/src/services/implementations/discovery/discoveryFactory.ts index 131bf1d697..51ea3c881d 100644 --- a/packages/backend-app-api/src/services/implementations/discovery/discoveryFactory.ts +++ b/packages/backend-app-api/src/services/implementations/discovery/discoveryFactory.ts @@ -26,10 +26,7 @@ export const discoveryFactory = createServiceFactory({ deps: { config: coreServices.config, }, - async createRootContext({ config }) { + async factory({ config }) { return SingleHostDiscovery.fromConfig(config); }, - async factory(_, discovery) { - return discovery; - }, }); diff --git a/packages/backend-app-api/src/services/implementations/identity/identityFactory.ts b/packages/backend-app-api/src/services/implementations/identity/identityFactory.ts index 0531c35480..0031979390 100644 --- a/packages/backend-app-api/src/services/implementations/identity/identityFactory.ts +++ b/packages/backend-app-api/src/services/implementations/identity/identityFactory.ts @@ -38,11 +38,8 @@ export const identityFactory = createServiceFactory( (options?: IdentityFactoryOptions) => ({ service: coreServices.identity, deps: { - config: coreServices.config, discovery: coreServices.discovery, - tokenManager: coreServices.tokenManager, }, - async factory({ discovery }) { return DefaultIdentityClient.create({ discovery, ...options }); }, From c1369c2cf457c1230cb6fc336cb37de8a5ba4f0d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Jan 2023 17:40:46 +0100 Subject: [PATCH 10/13] backend-plugin-api: update API report + fixes Signed-off-by: Patrik Oldsberg --- packages/backend-plugin-api/api-report.md | 151 ++++++++++++++---- .../src/services/system/types.ts | 24 +-- 2 files changed, 133 insertions(+), 42 deletions(-) diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index 33190277c9..59338fdf42 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -126,22 +126,86 @@ export function createExtensionPoint( config: ExtensionPointConfig, ): ExtensionPoint; -// @public (undocumented) +// @public export function createServiceFactory< TService, - TScope extends 'root' | 'plugin', TImpl extends TService, TDeps extends { [name in string]: ServiceRef; }, - TOpts extends [options?: object] = [], + TOpts extends object | undefined = undefined, +>( + config: RootServiceFactoryConfig, +): () => ServiceFactory; + +// @public +export function createServiceFactory< + TService, + TImpl extends TService, + TDeps extends { + [name in string]: ServiceRef; + }, + TOpts extends object | undefined = undefined, +>( + config: (options?: TOpts) => RootServiceFactoryConfig, +): (options?: TOpts) => ServiceFactory; + +// @public +export function createServiceFactory< + TService, + TImpl extends TService, + TDeps extends { + [name in string]: ServiceRef; + }, + TOpts extends object | undefined = undefined, +>( + config: (options: TOpts) => RootServiceFactoryConfig, +): (options: TOpts) => ServiceFactory; + +// @public +export function createServiceFactory< + TService, + TImpl extends TService, + TDeps extends { + [name in string]: ServiceRef; + }, + TContext = undefined, + TOpts extends object | undefined = undefined, +>( + config: PluginServiceFactoryConfig, +): () => ServiceFactory; + +// @public +export function createServiceFactory< + TService, + TImpl extends TService, + TDeps extends { + [name in string]: ServiceRef; + }, + TContext = undefined, + TOpts extends object | undefined = undefined, +>( + config: ( + options?: TOpts, + ) => PluginServiceFactoryConfig, +): (options?: TOpts) => ServiceFactory; + +// @public +export function createServiceFactory< + TService, + TImpl extends TService, + TDeps extends { + [name in string]: ServiceRef; + }, + TContext = undefined, + TOpts extends object | undefined = undefined, >( config: - | ServiceFactoryConfig + | PluginServiceFactoryConfig | (( - ...options: TOpts - ) => ServiceFactoryConfig), -): (...params: TOpts) => ServiceFactory; + options: TOpts, + ) => PluginServiceFactoryConfig), +): (options: TOpts) => ServiceFactory; // @public export function createServiceRef( @@ -229,6 +293,30 @@ export interface PluginMetadataService { getId(): string; } +// @public (undocumented) +export interface PluginServiceFactoryConfig< + TService, + TContext, + TImpl extends TService, + TDeps extends { + [name in string]: ServiceRef; + }, +> { + // (undocumented) + createRootContext?( + deps: ServiceRefsToInstances, + ): Promise; + // (undocumented) + deps: TDeps; + // (undocumented) + factory( + deps: ServiceRefsToInstances, + context: TContext, + ): Promise; + // (undocumented) + service: ServiceRef; +} + // @public export type ReadTreeOptions = { filter?( @@ -284,6 +372,22 @@ export interface RootLifecycleService extends LifecycleService {} // @public (undocumented) export interface RootLoggerService extends LoggerService {} +// @public (undocumented) +export interface RootServiceFactoryConfig< + TService, + TImpl extends TService, + TDeps extends { + [name in string]: ServiceRef; + }, +> { + // (undocumented) + deps: TDeps; + // (undocumented) + factory(deps: ServiceRefsToInstances): Promise; + // (undocumented) + service: ServiceRef; +} + // @public (undocumented) export interface SchedulerService extends PluginTaskScheduler {} @@ -323,36 +427,17 @@ export type ServiceFactory = deps: { [key in string]: ServiceRef; }; - factory(deps: { + createRootContext?(deps: { [key in string]: unknown; - }): Promise< - (deps: { + }): Promise; + factory( + deps: { [key in string]: unknown; - }) => Promise - >; + }, + context: unknown, + ): Promise; }; -// @public (undocumented) -export interface ServiceFactoryConfig< - TService, - TScope extends 'root' | 'plugin', - TImpl extends TService, - TDeps extends { - [name in string]: ServiceRef; - }, -> { - // (undocumented) - deps: TDeps; - // (undocumented) - factory( - deps: ServiceRefsToInstances, - ): TScope extends 'root' - ? Promise - : Promise<(deps: ServiceRefsToInstances) => Promise>; - // (undocumented) - service: ServiceRef; -} - // @public export type ServiceFactoryOrFunction = | ServiceFactory diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 618ef1b7b6..f4489b168b 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -166,10 +166,10 @@ export interface PluginServiceFactoryConfig< } /** - * Creates a root scoped service factory. + * Creates a root scoped service factory without options. * * @public - * @param config + * @param config - The service factory configuration. */ export function createServiceFactory< TService, @@ -183,7 +183,7 @@ export function createServiceFactory< * Creates a root scoped service factory with optional options. * * @public - * @param config + * @param config - The service factory configuration. */ export function createServiceFactory< TService, @@ -194,10 +194,10 @@ export function createServiceFactory< config: (options?: TOpts) => RootServiceFactoryConfig, ): (options?: TOpts) => ServiceFactory; /** - * Creates a plugin scoped service factory with required options. + * Creates a root scoped service factory with required options. * * @public - * @param config + * @param config - The service factory configuration. */ export function createServiceFactory< TService, @@ -208,10 +208,10 @@ export function createServiceFactory< config: (options: TOpts) => RootServiceFactoryConfig, ): (options: TOpts) => ServiceFactory; /** - * Creates a plugin scoped service factory with optional options. + * Creates a plugin scoped service factory without options. * * @public - * @param config + * @param config - The service factory configuration. */ export function createServiceFactory< TService, @@ -223,10 +223,10 @@ export function createServiceFactory< config: PluginServiceFactoryConfig, ): () => ServiceFactory; /** - * Creates a plugin scoped service factory with required options. + * Creates a plugin scoped service factory with optional options. * * @public - * @param config + * @param config - The service factory configuration. */ export function createServiceFactory< TService, @@ -239,6 +239,12 @@ export function createServiceFactory< options?: TOpts, ) => PluginServiceFactoryConfig, ): (options?: TOpts) => ServiceFactory; +/** + * Creates a plugin scoped service factory with required options. + * + * @public + * @param config - The service factory configuration. + */ export function createServiceFactory< TService, TImpl extends TService, From f8c313f80f72f0980748155a866fea2f01c47668 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 14 Jan 2023 12:46:29 +0100 Subject: [PATCH 11/13] backend-plugin-api: allow service factories to be sync Signed-off-by: Patrik Oldsberg --- .changeset/real-poets-run.md | 4 +- .../implementations/logger/loggerFactory.ts | 2 +- packages/backend-plugin-api/api-report.md | 6 +- .../src/services/system/types.test.ts | 59 +++++++++++++++++-- .../src/services/system/types.ts | 18 ++++-- 5 files changed, 74 insertions(+), 15 deletions(-) diff --git a/.changeset/real-poets-run.md b/.changeset/real-poets-run.md index f1bef6ed68..d626827b96 100644 --- a/.changeset/real-poets-run.md +++ b/.changeset/real-poets-run.md @@ -2,7 +2,7 @@ '@backstage/backend-plugin-api': patch --- -The `createServiceFactory` function has been updated to no longer use a duplicate callback pattern for plugin scoped services. The outer callback is now replaced by an optional `createRootContext` method. This change was made in order to support TypeScript 4.9, but it also simplifies the API surface a bit, especially for plugin scoped service factories that don't need to create a root context. +The `createServiceFactory` function has been updated to no longer use a duplicate callback pattern for plugin scoped services. The outer callback is now replaced by an optional `createRootContext` method. This change was made in order to support TypeScript 4.9, but it also simplifies the API surface a bit, especially for plugin scoped service factories that don't need to create a root context. In addition, the factory and root context functions can now be synchronous. A factory that previously would have looked like this: @@ -49,7 +49,7 @@ createServiceFactory({ rootLogger: coreServices.rootLogger, plugin: coreServices.pluginMetadata, }, - async factory({ rootLogger, plugin }) { + factory({ rootLogger, plugin }) { return rootLogger.child({ plugin: plugin.getId() }); }, }); diff --git a/packages/backend-app-api/src/services/implementations/logger/loggerFactory.ts b/packages/backend-app-api/src/services/implementations/logger/loggerFactory.ts index 14b6cc5f35..60c91d0ee6 100644 --- a/packages/backend-app-api/src/services/implementations/logger/loggerFactory.ts +++ b/packages/backend-app-api/src/services/implementations/logger/loggerFactory.ts @@ -26,7 +26,7 @@ export const loggerFactory = createServiceFactory({ rootLogger: coreServices.rootLogger, plugin: coreServices.pluginMetadata, }, - async factory({ rootLogger, plugin }) { + factory({ rootLogger, plugin }) { return rootLogger.child({ plugin: plugin.getId() }); }, }); diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index 59338fdf42..931aba62a6 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -305,14 +305,14 @@ export interface PluginServiceFactoryConfig< // (undocumented) createRootContext?( deps: ServiceRefsToInstances, - ): Promise; + ): TContext | Promise; // (undocumented) deps: TDeps; // (undocumented) factory( deps: ServiceRefsToInstances, context: TContext, - ): Promise; + ): TImpl | Promise; // (undocumented) service: ServiceRef; } @@ -383,7 +383,7 @@ export interface RootServiceFactoryConfig< // (undocumented) deps: TDeps; // (undocumented) - factory(deps: ServiceRefsToInstances): Promise; + factory(deps: ServiceRefsToInstances): TImpl | Promise; // (undocumented) service: ServiceRef; } diff --git a/packages/backend-plugin-api/src/services/system/types.test.ts b/packages/backend-plugin-api/src/services/system/types.test.ts index fab0232380..d3e9b9dd4e 100644 --- a/packages/backend-plugin-api/src/services/system/types.test.ts +++ b/packages/backend-plugin-api/src/services/system/types.test.ts @@ -26,7 +26,56 @@ interface TestOptions { function unused(..._any: any[]) {} describe('createServiceFactory', () => { - it('should create a meta factory with no options', () => { + it('should create a sync factory with no options', () => { + const metaFactory = createServiceFactory({ + service: ref, + deps: {}, + createRootContext() {}, + factory(_deps) { + 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 sync root factory with no options', () => { + const metaFactory = 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(); + }); + + it('should create a factory with no options', () => { const metaFactory = createServiceFactory({ service: ref, deps: {}, @@ -51,7 +100,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a meta factory with optional options', () => { + it('should create a factory with optional options', () => { const metaFactory = createServiceFactory((_opts?: { x: number }) => ({ service: ref, deps: {}, @@ -75,7 +124,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a meta factory with required options', () => { + it('should create a factory with required options', () => { const metaFactory = createServiceFactory((_opts: { x: number }) => ({ service: ref, deps: {}, @@ -101,7 +150,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a meta factory with optional options as interface', () => { + it('should create a factory with optional options as interface', () => { const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ service: ref, deps: {}, @@ -125,7 +174,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a meta factory with required options as interface', () => { + it('should create a factory with required options as interface', () => { const metaFactory = createServiceFactory((_opts: TestOptions) => ({ service: ref, deps: {}, diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index f4489b168b..2615c5e3a3 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -144,7 +144,7 @@ export interface RootServiceFactoryConfig< > { service: ServiceRef; deps: TDeps; - factory(deps: ServiceRefsToInstances): Promise; + factory(deps: ServiceRefsToInstances): TImpl | Promise; } /** @public */ @@ -158,11 +158,11 @@ export interface PluginServiceFactoryConfig< deps: TDeps; createRootContext?( deps: ServiceRefsToInstances, - ): Promise; + ): TContext | Promise; factory( deps: ServiceRefsToInstances, context: TContext, - ): Promise; + ): TImpl | Promise; } /** @@ -278,6 +278,16 @@ export function createServiceFactory< const configCallback = typeof config === 'function' ? config : () => config; return (options: TOpts) => { const c = configCallback(options); - return { ...c, scope: c.service.scope } as ServiceFactory; + return { + ...c, + ...('createRootContext' in c + ? { + createRootContext: async (deps: TDeps) => + c?.createRootContext?.(deps), + } + : {}), + factory: async (deps: TDeps, ctx: TContext) => c.factory(deps, ctx), + scope: c.service.scope, + } as ServiceFactory; }; } From e259af4c682487a6b69f73ec671e4d53c34d2bc3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 14 Jan 2023 12:46:53 +0100 Subject: [PATCH 12/13] backend-app-api: update API report Signed-off-by: Patrik Oldsberg --- packages/backend-app-api/api-report.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend-app-api/api-report.md b/packages/backend-app-api/api-report.md index bcf746aa5a..dcdc105b17 100644 --- a/packages/backend-app-api/api-report.md +++ b/packages/backend-app-api/api-report.md @@ -152,7 +152,9 @@ export type HttpServerOptions = { }; // @public (undocumented) -export const identityFactory: () => ServiceFactory; +export const identityFactory: ( + options?: IdentityFactoryOptions | undefined, +) => ServiceFactory; // @public export type IdentityFactoryOptions = { From ab3b80ccde1dced64144ee41645e5549c92c1709 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Jan 2023 14:41:16 +0100 Subject: [PATCH 13/13] backend-app-api: fix tokenManagerFactory regression + add test Signed-off-by: Patrik Oldsberg --- .../tokenManager/tokenManagerFactory.test.ts | 45 +++++++++++++++++++ .../tokenManager/tokenManagerFactory.ts | 6 ++- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.test.ts diff --git a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.test.ts b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.test.ts new file mode 100644 index 0000000000..abc62fff69 --- /dev/null +++ b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.test.ts @@ -0,0 +1,45 @@ +/* + * 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 { + LoggerService, + ServiceFactory, + TokenManagerService, +} from '@backstage/backend-plugin-api'; +import { ConfigReader } from '@backstage/config'; +import { tokenManagerFactory } from './tokenManagerFactory'; + +describe('tokenManagerFactory', () => { + it('should create managers that can share tokens in development', async () => { + (process.env as { NODE_ENV?: string }).NODE_ENV = 'development'; + + const factory = tokenManagerFactory() as Exclude< + ServiceFactory, + { scope: 'root' } + >; + const deps = { + config: new ConfigReader({}), + logger: { warn() {} } as unknown as LoggerService, + }; + + const ctx = await factory.createRootContext?.(deps); + const manager1 = await factory.factory!(deps, ctx); + const manager2 = await factory.factory!(deps, ctx); + + const { token } = await manager1.getToken(); + await expect(manager2.authenticate(token)).resolves.toBeUndefined(); + }); +}); diff --git a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.ts b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.ts index bb45f1f92c..ae0b402eec 100644 --- a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.ts +++ b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerFactory.ts @@ -27,10 +27,12 @@ export const tokenManagerFactory = createServiceFactory({ config: coreServices.config, logger: coreServices.rootLogger, }, - async factory({ config, logger }) { - const tokenManager = ServerTokenManager.fromConfig(config, { + createRootContext({ config, logger }) { + return ServerTokenManager.fromConfig(config, { logger, }); + }, + async factory(_deps, tokenManager) { return tokenManager; }, });