diff --git a/packages/di/src/common/fn/refValue.spec.ts b/packages/di/src/common/fn/refValue.spec.ts new file mode 100644 index 00000000000..71f6faf3e09 --- /dev/null +++ b/packages/di/src/common/fn/refValue.spec.ts @@ -0,0 +1,42 @@ +import {DITest} from "../../node/index.js"; +import {refValue} from "./refValue.js"; + +describe("refValue()", () => { + beforeEach(() => + DITest.create({ + logger: { + level: "off" + } + }) + ); + afterEach(() => DITest.reset()); + describe("when decorator is used as property decorator", () => { + it("should create a getter", async () => { + // WHEN + class Test { + test = refValue("logger.level", "default value"); + } + + // THEN + + const test = await DITest.invoke(Test); + + expect(test.test.value).toEqual("off"); + }); + it("should create a getter with default value", async () => { + expect(DITest.injector.settings.get("logger.test")).toEqual(undefined); + + // WHEN + class Test { + test = refValue("logger.test", "default value"); + } + + // THEN + + const test = await DITest.invoke(Test); + + expect(test.test.value).toEqual("default value"); + expect(DITest.injector.settings.get("logger.test")).toEqual(undefined); + }); + }); +}); diff --git a/packages/di/src/common/fn/refValue.ts b/packages/di/src/common/fn/refValue.ts new file mode 100644 index 00000000000..63dd1091f34 --- /dev/null +++ b/packages/di/src/common/fn/refValue.ts @@ -0,0 +1,33 @@ +import {injector} from "./injector.js"; + +/** + * Get a value from the `injector.settings`. + * + * ## Example + * + * ```ts + * import {refValue, Injectable} from "@tsed/di"; + * + * @Injectable() + * class Test { + * test = refValue("logger.level", "default value"); + * + * constructor() { + * console.log(this.test.value); // "off" + * } + * } + * + * @param expression The expression to get the value from the `injector.settings`. + */ +export function refValue(expression: string): {value: Type | undefined}; +export function refValue(expression: string, defaultValue: Type | undefined): {value: Type}; +export function refValue(expression: string, defaultValue?: Type | undefined): {value: Type | undefined} { + return Object.freeze({ + get value() { + return injector().settings.get(expression, defaultValue); + }, + set value(value: Type) { + injector().settings.set(expression, value); + } + }); +} diff --git a/packages/di/src/common/index.ts b/packages/di/src/common/index.ts index b198bdbd67a..ab620d5c40f 100644 --- a/packages/di/src/common/index.ts +++ b/packages/di/src/common/index.ts @@ -33,6 +33,7 @@ export * from "./fn/inject.js"; export * from "./fn/injectable.js"; export * from "./fn/injectMany.js"; export * from "./fn/injector.js"; +export * from "./fn/refValue.js"; export * from "./interfaces/DIConfigurationOptions.js"; export * from "./interfaces/DILogger.js"; export * from "./interfaces/DILoggerOptions.js"; diff --git a/packages/platform/platform-params/vitest.config.mts b/packages/platform/platform-params/vitest.config.mts index f9d941dcb43..947a6a0a2a9 100644 --- a/packages/platform/platform-params/vitest.config.mts +++ b/packages/platform/platform-params/vitest.config.mts @@ -11,11 +11,11 @@ export default defineConfig( ...presets.test.coverage, thresholds: { statements: 99.2, - branches: 90.69, + branches: 90.62, functions: 100, lines: 99.2 } } } } -); \ No newline at end of file +); diff --git a/packages/security/oidc-provider-plugin-wildcard-redirect-uri/src/OidcWildcardRedirectUriModule.ts b/packages/security/oidc-provider-plugin-wildcard-redirect-uri/src/OidcWildcardRedirectUriModule.ts index 5b803fed4f6..8d92547e247 100644 --- a/packages/security/oidc-provider-plugin-wildcard-redirect-uri/src/OidcWildcardRedirectUriModule.ts +++ b/packages/security/oidc-provider-plugin-wildcard-redirect-uri/src/OidcWildcardRedirectUriModule.ts @@ -1,5 +1,4 @@ -import {Constant, Inject, Module} from "@tsed/di"; -import {Logger} from "@tsed/logger"; +import {constant, logger, Module} from "@tsed/di"; import {OidcSettings} from "@tsed/oidc-provider"; import Provider, {errors, type KoaContextWithOIDC} from "oidc-provider"; // @ts-ignore @@ -19,11 +18,7 @@ declare global { @Module() export class OidcWildcardRedirectUriModule { - @Constant("oidc.plugins.wildcard.enabled", false) - readonly enabled: boolean; - - @Inject(Logger) - protected logger: Logger; + readonly enabled = constant("oidc.plugins.wildcard.enabled", false); $onCreateOIDC(provider: Provider) { if (this.enabled) { @@ -34,7 +29,7 @@ export class OidcWildcardRedirectUriModule { "postLogoutRedirectUris" ); - this.logger.warn("⚠️⚠️⚠️ OIDC Wildcard Uris plugin is ENABLED ⚠️⚠️⚠️"); + logger().warn("⚠️⚠️⚠️ OIDC Wildcard Uris plugin is ENABLED ⚠️⚠️⚠️"); } } diff --git a/packages/security/oidc-provider/src/OidcModule.ts b/packages/security/oidc-provider/src/OidcModule.ts index a6510617980..2f79f2c7075 100644 --- a/packages/security/oidc-provider/src/OidcModule.ts +++ b/packages/security/oidc-provider/src/OidcModule.ts @@ -1,5 +1,5 @@ -import {Inject, InjectorService, PlatformApplication} from "@tsed/common"; -import {Constant, Module} from "@tsed/di"; +import {PlatformApplication} from "@tsed/common"; +import {$injector, constant, inject, Module} from "@tsed/di"; import koaMount from "koa-mount"; import {OidcAdapters} from "./services/OidcAdapters.js"; @@ -10,20 +10,10 @@ import {OidcProvider} from "./services/OidcProvider.js"; imports: [OidcProvider, OidcAdapters, OidcJwks] }) export class OidcModule { - @Inject() - protected app: PlatformApplication; - - @Constant("PLATFORM_NAME") - protected platformName: string; - - @Constant("oidc.path", "/oidc") - protected basePath: string; - - @Inject() - protected oidcProvider: OidcProvider; - - @Inject() - protected injector: InjectorService; + protected app: PlatformApplication = inject(PlatformApplication); + protected platformName = constant("PLATFORM_NAME"); + protected basePath = constant("oidc.path", "/oidc"); + protected oidcProvider = inject(OidcProvider); async $onInit() { if (this.oidcProvider.hasConfiguration()) { @@ -54,8 +44,9 @@ export class OidcModule { } $onReady() { - if (this.oidcProvider.hasConfiguration() && "getBestHost" in this.injector.settings) { - const {injector} = this; + const injector = $injector(); + + if (this.oidcProvider.hasConfiguration() && "getBestHost" in injector.settings) { // @ts-ignore const host = injector.settings.getBestHost(); const url = host.toString(); diff --git a/packages/security/oidc-provider/src/services/OidcAdapters.ts b/packages/security/oidc-provider/src/services/OidcAdapters.ts index de84bbb7b96..d811c6eefa7 100644 --- a/packages/security/oidc-provider/src/services/OidcAdapters.ts +++ b/packages/security/oidc-provider/src/services/OidcAdapters.ts @@ -1,21 +1,17 @@ import {Adapter, Adapters} from "@tsed/adapters"; -import {Configuration, Inject, Injectable} from "@tsed/di"; +import {constant, inject, Injectable} from "@tsed/di"; import type {Adapter as OidcAdapter, AdapterConstructor} from "oidc-provider"; export type OidcAdapterMethods = Adapter & Partial>; @Injectable() export class OidcAdapters { - @Inject() - protected adapters: Adapters; - - @Configuration() - protected settings: Configuration; + protected adapters = inject(Adapters); createAdapterClass(): AdapterConstructor { const self = this; - const adapterBase = this.settings.get("oidc.Adapter", this.settings.get("adapters.Adapter")); - const connectionName = this.settings.get("oidc.connectionName", "default"); + const adapterBase = constant("oidc.Adapter", constant("adapters.Adapter")); + const connectionName = constant("oidc.connectionName", "default"); return class CustomAdapter implements OidcAdapter { adapter: OidcAdapterMethods; diff --git a/packages/security/oidc-provider/src/services/OidcInteractionContext.ts b/packages/security/oidc-provider/src/services/OidcInteractionContext.ts index 8aaf2c72440..b38e4adb7bf 100644 --- a/packages/security/oidc-provider/src/services/OidcInteractionContext.ts +++ b/packages/security/oidc-provider/src/services/OidcInteractionContext.ts @@ -1,6 +1,6 @@ -import {Constant, InjectContext, PlatformContext} from "@tsed/common"; +import {PlatformContext} from "@tsed/common"; import {Env} from "@tsed/core"; -import {Inject, Injectable} from "@tsed/di"; +import {constant, context, inject, Injectable} from "@tsed/di"; import {Unauthorized} from "@tsed/exceptions"; import omit from "lodash/omit.js"; import type {Account, default as Provider, InteractionResults, PromptDetail} from "oidc-provider"; @@ -24,20 +24,13 @@ import {OidcProvider} from "./OidcProvider.js"; @Injectable() export class OidcInteractionContext { - @Constant("env") - protected env: Env; + protected env = constant("env"); + protected oidcProvider = inject(OidcProvider); + protected oidcInteractions = inject(OidcInteractions); - @Constant("oidc.render.omitClientProps", []) - protected omitClientProps: string[]; - - @Inject() - protected oidcProvider: OidcProvider; - - @Inject() - protected oidcInteractions: OidcInteractions; - - @InjectContext() - protected $ctx: PlatformContext; + get $ctx() { + return context(); + } get raw(): OidcInteraction { return this.$ctx.get(INTERACTION_DETAILS)!; @@ -115,8 +108,10 @@ export class OidcInteractionContext { async interactionPrompt({client, ...options}: Record): Promise { client = client || (await this.findClient()); + const omitClientProps = constant("oidc.render.omitClientProps", []); + return { - client: omit(client, ["clientSecret", ...this.omitClientProps]), + client: omit(client, ["clientSecret", ...omitClientProps]), uid: this.uid, grantId: this.grantId, details: this.prompt.details, diff --git a/packages/security/oidc-provider/src/services/OidcInteractions.ts b/packages/security/oidc-provider/src/services/OidcInteractions.ts index 47cd2ca8084..1c7aba354ce 100644 --- a/packages/security/oidc-provider/src/services/OidcInteractions.ts +++ b/packages/security/oidc-provider/src/services/OidcInteractions.ts @@ -1,22 +1,15 @@ -import {Constant, EndpointMetadata, PlatformContext, PlatformHandler, Provider, TokenProvider} from "@tsed/common"; +import {PlatformContext, PlatformHandler} from "@tsed/common"; import {Env} from "@tsed/core"; -import {Inject, Injectable, InjectorService} from "@tsed/di"; +import {constant, Injectable, injector, Provider, TokenProvider} from "@tsed/di"; +import {EndpointMetadata} from "@tsed/schema"; import {INTERACTION, INTERACTION_OPTIONS, INTERACTIONS} from "../constants/constants.js"; import {OidcInteractionOptions} from "../domain/OidcInteractionOptions.js"; -import {OidcSettings} from "../domain/OidcSettings.js"; @Injectable() export class OidcInteractions { - @Inject() - protected injector: InjectorService; - - @Constant("env") - protected env: Env; - - @Constant("oidc") - protected oidcSettings: OidcSettings; - + protected injector = injector(); + protected env = constant("env"); protected interactions: Map = new Map(); $onInit(): void { diff --git a/packages/security/oidc-provider/src/services/OidcJwks.ts b/packages/security/oidc-provider/src/services/OidcJwks.ts index 270b5d37037..5f9f9f0c648 100644 --- a/packages/security/oidc-provider/src/services/OidcJwks.ts +++ b/packages/security/oidc-provider/src/services/OidcJwks.ts @@ -1,16 +1,12 @@ -import {Constant, Injectable} from "@tsed/di"; +import {constant, Injectable} from "@tsed/di"; import {getJwks, JwksKeyParameters} from "@tsed/jwks"; import {join} from "path"; @Injectable() export class OidcJwks { - @Constant("oidc.jwksPath", join(process.cwd(), "keys", "jwks.json")) - jwksPath: string; - - @Constant("oidc.certificates") - certificates?: JwksKeyParameters[]; - - keys: string; + public jwksPath: string = constant("oidc.jwksPath", join(process.cwd(), "keys", "jwks.json")); + public certificates?: JwksKeyParameters[] = constant("oidc.certificates"); + public keys: string; $onInit() { return this.getJwks(); diff --git a/packages/security/oidc-provider/src/services/OidcPolicy.ts b/packages/security/oidc-provider/src/services/OidcPolicy.ts index 8ebe88779e2..e7e385fcc9e 100644 --- a/packages/security/oidc-provider/src/services/OidcPolicy.ts +++ b/packages/security/oidc-provider/src/services/OidcPolicy.ts @@ -1,4 +1,4 @@ -import {Inject, Injectable, InjectorService, Provider} from "@tsed/di"; +import {inject, Injectable, injector, Provider} from "@tsed/di"; import {interactionPolicy} from "oidc-provider"; import {InteractionMethods} from "../domain/InteractionMethods.js"; @@ -8,11 +8,8 @@ import Prompt = interactionPolicy.Prompt; @Injectable() export class OidcPolicy { - @Inject() - protected injector: InjectorService; - - @Inject() - protected oidcInteractions: OidcInteractions; + protected injector = injector(); + protected oidcInteractions = inject(OidcInteractions); public getPolicy() { let policy = interactionPolicy.base(); diff --git a/packages/security/oidc-provider/src/services/OidcProvider.spec.ts b/packages/security/oidc-provider/src/services/OidcProvider.spec.ts index 5857c267bff..6da709775be 100644 --- a/packages/security/oidc-provider/src/services/OidcProvider.spec.ts +++ b/packages/security/oidc-provider/src/services/OidcProvider.spec.ts @@ -60,7 +60,7 @@ describe("OidcProvider", () => { expect((oidcProvider as any).injector.logger.error).toHaveBeenCalledWith({ duration: expect.any(Number), - reqId: "", + reqId: expect.any(String), account_id: "account_id", error: {error_description: "error_description", error_detail: "error_detail", error: "error"}, event: "OIDC_ERROR", diff --git a/packages/security/oidc-provider/src/services/OidcProvider.ts b/packages/security/oidc-provider/src/services/OidcProvider.ts index 4a151ec64d5..bc66ba3ef88 100644 --- a/packages/security/oidc-provider/src/services/OidcProvider.ts +++ b/packages/security/oidc-provider/src/services/OidcProvider.ts @@ -1,6 +1,6 @@ -import {InjectContext, PlatformApplication, PlatformContext} from "@tsed/common"; +import {PlatformApplication, PlatformContext} from "@tsed/common"; import {Env, setValue} from "@tsed/core"; -import {Constant, Inject, Injectable, InjectorService} from "@tsed/di"; +import {constant, context, inject, Injectable, InjectorService} from "@tsed/di"; import Provider, {type Configuration, type KoaContextWithOIDC} from "oidc-provider"; import {INTERACTIONS} from "../constants/constants.js"; @@ -8,7 +8,6 @@ import {OidcAccountsMethods} from "../domain/OidcAccountsMethods.js"; import {OidcSettings} from "../domain/OidcSettings.js"; import {OIDC_ERROR_EVENTS} from "../utils/events.js"; import {OidcAdapters} from "./OidcAdapters.js"; -import {OidcInteractions} from "./OidcInteractions.js"; import {OidcJwks} from "./OidcJwks.js"; import {OidcPolicy} from "./OidcPolicy.js"; @@ -25,47 +24,24 @@ function mapError(error: any) { export class OidcProvider { raw: Provider; - @Constant("env") - protected env: Env; - - @Constant("httpPort") - protected httpPort: number | string; - - @Constant("httpsPort") - protected httpsPort: number | string; - - @Constant("oidc.issuer", "") - protected issuer: string; - - @Constant("oidc") - protected oidc: OidcSettings; - - @Constant("PLATFORM_NAME") - protected platformName: string; - - @Inject() - protected oidcJwks: OidcJwks; - - @Inject() - protected oidcInteractions: OidcInteractions; - - @Inject() - protected oidcPolicy: OidcPolicy; - - @Inject() - protected adapters: OidcAdapters; - - @Inject() - protected injector: InjectorService; - - @Inject() - protected app: PlatformApplication; - - @InjectContext() - protected $ctx?: PlatformContext; + protected env = constant("env"); + protected httpPort = constant("httpPort"); + protected httpsPort = constant("httpsPort"); + protected issuer = constant("oidc.issuer", ""); + protected oidc = constant("oidc")!; + protected platformName = constant("PLATFORM_NAME"); + protected oidcJwks = inject(OidcJwks); + protected oidcPolicy = inject(OidcPolicy); + protected adapters = inject(OidcAdapters); + protected injector = inject(InjectorService); + protected app = inject(PlatformApplication); get logger() { - return this.$ctx?.logger || this.injector.logger; + return this.$ctx.logger; + } + + protected get $ctx() { + return context(); } hasConfiguration() {