Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose cls.run options on decorator, update defaults #105

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/docs/03_features-and-use-cases/06_proxy-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export class CronController {

#### With @UseCls()

The `resolveProxyProviders` is set to `false` by default on the `@UseCls` decorator. To achieve the same behavior using it, you must set it to `true`.
Since the `@UseCls()` decorator wraps the function body with `cls.run()` automatically, you can use the `setup` function to prepare the context.

The Proxy Providers will be resolved after the `setup` phase.

Expand All @@ -238,9 +238,8 @@ export class CronController {
@Cron('45 * * * * *')
@UseCls({
// highlight-start
resolveProxyProviders: true,
setup: (cls) => {
this.cls.set('some-key', 'some-value');
cls.set('some-key', 'some-value');
},
// highlight-end
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function UseCls<TArgs extends any[]>(
);
}
descriptor.value = function (...args: TArgs) {
return cls.run(async () => {
return cls.run(options.runOptions ?? {}, async () => {
if (options.generateId) {
const id = await options.idGenerator?.apply(this, args);
cls.set<string>(CLS_ID, id);
Expand Down
135 changes: 55 additions & 80 deletions packages/core/src/lib/cls.options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,28 +63,27 @@ export interface ClsModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
plugins?: ClsPlugin[];
}

export class ClsMiddlewareOptions {
export class ClsContextOptions {
/**
* whether to mount the middleware to every route
* Sets the behavior of nested CLS context creation. Has no effect if no parent context exists.
*
* `inherit` (default) - Run the callback with a shallow copy of the parent context.
* Assignments to top-level properties will not be reflected in the parent context.
*
* `reuse` - Reuse existing context without creating a new one.
*
* `override` - Run the callback with an new empty context.
* Warning: No values from the parent context will be accessible.
*/
mount?: boolean; // default false
ifNested?: 'inherit' | 'reuse' | 'override' = 'inherit';
}

export class ClsInitializerCommonOptions {
/**
* whether to automatically generate request ids
*/
generateId?: boolean; // default false

/**
* the function to generate request ids inside the middleware
*/
idGenerator?: (req: any) => string | Promise<string> = getRandomString;

/**
* Function that executes after the CLS context has been initialised.
* It can be used to put additional variables in the CLS context.
*/
setup?: (cls: ClsService, req: any, res: any) => void | Promise<void>;

/**
* Whether to resolve proxy providers as a part
* of the CLS context registration
Expand All @@ -100,6 +99,24 @@ export class ClsMiddlewareOptions {
* Default: `true`
*/
initializePlugins? = true;
}

export class ClsMiddlewareOptions extends ClsInitializerCommonOptions {
/**
* whether to mount the middleware to every route
*/
mount?: boolean; // default false

/**
* the function to generate request ids for the CLS context
*/
idGenerator?: (req: any) => string | Promise<string> = getRandomString;

/**
* Function that executes after the CLS context has been initialized.
* It can be used to put additional variables in the CLS context.
*/
setup?: (cls: ClsService, req: any, res: any) => void | Promise<void>;

/**
* Whether to store the Request object to the CLS
Expand All @@ -124,17 +141,12 @@ export class ClsMiddlewareOptions {
useEnterWith? = false;
}

export class ClsGuardOptions {
export class ClsGuardOptions extends ClsInitializerCommonOptions {
/**
* whether to mount the guard globally
*/
mount?: boolean; // default false

/**
* whether to automatically generate request ids
*/
generateId?: boolean; // default false

/**
* the function to generate request ids inside the guard
*/
Expand All @@ -149,35 +161,14 @@ export class ClsGuardOptions {
cls: ClsService,
context: ExecutionContext,
) => void | Promise<void>;

/**
* Whether to resolve proxy providers as a part
* of the CLS context registration
*
* Default: `true`
*/
resolveProxyProviders? = true;

/**
* Whether to run the onClsInit hook for plugins as a part
* of the CLS context registration (runs before `resolveProxyProviders` just after `setup`)
*
* Default: `true`
*/
initializePlugins? = true;
}

export class ClsInterceptorOptions {
export class ClsInterceptorOptions extends ClsInitializerCommonOptions {
/**
* whether to mount the interceptor globally
*/
mount?: boolean; // default false

/**
* whether to automatically generate request ids
*/
generateId?: boolean; // default false

/**
* the function to generate request ids inside the interceptor
*/
Expand All @@ -192,64 +183,48 @@ export class ClsInterceptorOptions {
cls: ClsService,
context: ExecutionContext,
) => void | Promise<void>;

/**
* Whether to resolve proxy providers as a part
* of the CLS context registration
*
* Default: `true`
*/
resolveProxyProviders? = true;

/**
* Whether to run the onClsInit hook for plugins as a part
* of the CLS context registration (runs before `resolveProxyProviders` just after `setup`)
*
* Default: `true`
*/
initializePlugins? = true;
}

export class ClsDecoratorOptions<T extends any[]> {
export class ClsDecoratorOptions<
T extends any[],
> extends ClsInitializerCommonOptions {
/**
* Whether to automatically generate request ids
* Additional options for the `ClsService#run` method.
*/
generateId?: boolean; // default false
runOptions?: ClsContextOptions;

/**
* The function to generate request ids inside the interceptor.
*
* Takes the same parameters in the same order as the decorated function.
*
* If you use a `function` expression, it will executed with the `this` context of the decorated class instance.
* to get type safety, use:
*
* `idGenerator: function (this: MyClass, ...args) { ... }`
*
* Note: To avoid type errors, you must list all parameters, even if they're not used,
* or type the decorator as `@UseCls<[arg1: Type1, arg2: Type2]>()`
* or type the decorator as:
*
* `@UseCls<[arg1: Type1, arg2: Type2]>()`
*/
idGenerator?: (...args: T) => string | Promise<string> = getRandomString;

/**
* Function that executes after the CLS context has been initialised.
* Function that executes after the CLS context has been initialized.
* Takes ClsService as the first parameter and then the same parameters in the same order as the decorated function.
*
* Note: To avoid type errors, you must list all parameters, even if they're not used,
* or type the decorator as `@UseCls<[arg1: Type1, arg2: Type2]>()`
*/
setup?: (cls: ClsService, ...args: T) => void | Promise<void>;

/**
* Whether to resolve proxy providers as a part
* of the CLS context registration
* If you use a `function` expression, it will executed with the `this` context of the decorated class instance.
* to get type safety, use:
*
* Default: `false`
*/
resolveProxyProviders? = false;

/**
* Whether to run the onClsInit hook for plugins as a part
* of the CLS context registration (runs before `resolveProxyProviders` just after `setup`)
* `setup: function (this: MyClass, cls: ClsService, ...args) { ... }`
*
* Default: `false`
* Note: To avoid type errors, you must list all parameters, even if they're not used,
* or type the decorator as:
*
* `@UseCls<[arg1: Type1, arg2: Type2]>()`
*/
initializePlugins? = false;
setup?: (cls: ClsService, ...args: T) => void | Promise<void>;
}

export interface ClsStore {
Expand Down
17 changes: 1 addition & 16 deletions packages/core/src/lib/cls.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,7 @@ import {
} from '../types/type-if-type.type';
import { getValueFromPath, setValueFromPath } from '../utils/value-from-path';
import { CLS_ID } from './cls.constants';
import type { ClsStore } from './cls.options';

export class ClsContextOptions {
/**
* Sets the behavior of nested CLS context creation. Has no effect if no parent context exists.
*
* `inherit` (default) - Run the callback with a shallow copy of the parent context.
* Assignments to top-level properties will not be reflected in the parent context.
*
* `reuse` - Reuse existing context without creating a new one.
*
* `override` - Run the callback with an new empty context.
* Warning: No values from the parent context will be accessible.
*/
ifNested?: 'inherit' | 'reuse' | 'override' = 'inherit';
}
import { ClsContextOptions, ClsStore } from './cls.options';

export class ClsService<S extends ClsStore = ClsStore> {
constructor(private readonly als: AsyncLocalStorage<any>) {}
Expand Down
Loading