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

Remove namespaces support #36

Merged
merged 2 commits into from
Jun 25, 2022
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
1 change: 1 addition & 0 deletions .github/workflows/run-tests.workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
pull_request:
branches:
- main
- release/*

concurrency:
cancel-in-progress: true
Expand Down
59 changes: 1 addition & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ Most of these are to some extent solvable using _request-scoped_ providers or pa
- [REST](#rest)
- [GraphQL](#graphql)
- [Others](#others)
- [~~Namespaces~~](#namespaces-deprecated) (deprecated)

> **Notice**: I have deprecated [Namespaces](#namespaces-deprecated) since version `2.1.1` and will be removing them in `3.0` to make room for new features ([#31](https://github.com/Papooch/nestjs-cls/issues/31)). Namespace support was experimental from the begining, and I havent seen any justifiable use case to keep it around.
> **Notice**: I have deprecated [Namespaces](#namespaces-deprecated) since version `2.1.1` and will be removing them in `3.0` to make room for new features ([#31](https://github.com/Papooch/nestjs-cls/issues/31)). Namespace support was experimental from the beginning, and I haven't seen any justifiable use case to keep it around.

# Install

Expand Down Expand Up @@ -569,62 +568,6 @@ Below are listed platforms with which it is confirmed to work.

_Websocket Gateways_ don't respect globally bound enhancers, therefore it is required to bind the `ClsGuard` or `ClsIntercetor` manually on the `WebscocketGateway`. (See [#8](https://github.com/Papooch/nestjs-cls/issues/8))

# ~~Namespaces~~ (deprecated)

> **Warning**: Namespace support will be dropped in v3.0

The default CLS namespace that the `ClsService` provides should be enough for most application, but should you need it, this package provides a way to use multiple CLS namespaces simultaneously.

To use custom namespace provider, use `ClsModule.forFeature('my-namespace')`.

```ts
@Module({
imports: [ClsModule.forFeature('hello-namespace')],
providers: [HelloService],
controllers: [HelloController],
})
export class HelloModule {}
```

This creates a namespaced `ClsService` provider that you can inject using `@InjectCls`

```ts
// hello.service.ts

@Injectable()
class HelloService {
constructor(
@InjectCls('hello-namespace')
private readonly myCls: ClsService,
) {}

sayHello() {
return this.myCls.run('hi');
}
}

// hello.controller.ts
@Injectable()
export class HelloController {
constructor(
@InjectCls('hello-namespace')
private readonly myCls: ClsService,
private readonly helloService: HelloService,
);

@Get('/hello')
hello2() {
// setting up cls context manually
return this.myCls.run(() => {
this.myCls.set('hi', 'Hello');
return this.helloService.sayHello();
});
}
}
```

> **Note**: `@InjectCls('x')` is equivalent to `@Inject(getClsServiceToken('x'))`. If you don't pass an argument to `@InjectCls()`, the default ClsService will be injected and is equivalent to omitting the decorator altogether.

# Contributing

Contributing to a community project is always welcome, please see the [Contributing guide](./CONTRIBUTING.md) :)
72 changes: 7 additions & 65 deletions src/lib/cls-service-manager.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,17 @@
import { ClassProvider, ValueProvider } from '@nestjs/common';
import { CLS_DEFAULT_NAMESPACE } from './cls.constants';
import { ClsService } from './cls.service';
import { AsyncLocalStorage } from 'async_hooks';

/**
* Get ClsService injection token (as a string)
*/
export function getClsServiceToken(): string;
/**
* Get namespaced ClsService injection token (as a string)
* @param namespace name of the namespace
* @deprecated Namespace support will be removed in v3.0
*/
export function getClsServiceToken(namespace: string): string;
export function getClsServiceToken(namespace = CLS_DEFAULT_NAMESPACE) {
return `ClsService-${namespace}`;
}
import { ClsStore } from './cls.interfaces';
import { ClsService } from './cls.service';

export class ClsServiceManager {
private static namespaces: Record<
string,
AsyncLocalStorage<any> & { name?: string }
> = {};

private static clsServices: Map<string | typeof ClsService, ClsService> =
new Map([
[
ClsService,
new ClsService(this.resolveNamespace(CLS_DEFAULT_NAMESPACE)),
],
]);

private static resolveNamespace(name: string) {
if (!this.namespaces[name]) {
this.namespaces[name] = new AsyncLocalStorage();
this.namespaces[name].name = name;
}
return this.namespaces[name];
}

static addClsService(name: string = CLS_DEFAULT_NAMESPACE) {
const service = new ClsService(this.resolveNamespace(name));
this.clsServices.set(
getClsServiceToken(name),
new ClsService(this.resolveNamespace(name)),
);
return service;
}
private static als = new AsyncLocalStorage();
private static clsService = new ClsService(this.als);

/**
* Retrieve a ClsService outside of Nest's DI.
* @param name namespace name, omit for default
* @returns the ClsService with the given namespace
* @returns the ClsService
*/
static getClsService(name?: string) {
const cls = this.clsServices.get(
name ? getClsServiceToken(name) : ClsService,
);
if (!cls)
throw new Error(`ClsService with namespace ${name} does not exist`);
static getClsService<T extends ClsStore = ClsStore>(): ClsService<T> {
const cls = this.clsService as ClsService<T>;
return cls;
}

static getClsServicesAsProviders(): Array<
ClassProvider<ClsService> | ValueProvider<ClsService>
> {
return Array.from(this.clsServices.entries()).map(
([provide, service]) => ({
provide,
useValue: service,
}),
);
}
}
1 change: 0 additions & 1 deletion src/lib/cls.constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export const CLS_REQ = Symbol('CLS_REQUEST');
export const CLS_RES = Symbol('CLS_RESPONSE');
export const CLS_ID = Symbol('CLS_ID');
export const CLS_DEFAULT_NAMESPACE = 'CLS_DEFAULT_NAMESPACE';
export const CLS_MODULE_OPTIONS = 'ClsModuleOptions';
export const CLS_MIDDLEWARE_OPTIONS = 'ClsMiddlewareOptions';
export const CLS_GUARD_OPTIONS = 'ClsGuardOptions';
Expand Down
15 changes: 3 additions & 12 deletions src/lib/cls.decorators.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import { Inject } from '@nestjs/common';
import { getClsServiceToken } from './cls-service-manager';
import { CLS_DEFAULT_NAMESPACE } from './cls.constants';
import { ClsService } from './cls.service';

/**
* Use to explicitly inject the ClsService
*/
export function InjectCls(): (target: any, key: string | symbol, index?: number) => void;

/**
* Use to inject a namespaced CLS service
* @param namespace name of the namespace
* @deprecated Namespace support will be removed in v3.0
*/
export function InjectCls(namespace: string): (target: any, key: string | symbol, index?: number) => void;
export function InjectCls(namespace = CLS_DEFAULT_NAMESPACE) {
return Inject(getClsServiceToken(namespace));
export function InjectCls() {
return Inject(ClsService);
}
2 changes: 1 addition & 1 deletion src/lib/cls.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class ClsGuard implements CanActivate {
}

async canActivate(context: ExecutionContext): Promise<boolean> {
const cls = ClsServiceManager.getClsService(this.options.namespaceName);
const cls = ClsServiceManager.getClsService();
return cls.exit(async () => {
cls.enter();
if (this.options.generateId) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/cls.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class ClsInterceptor implements NestInterceptor {
}

intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const cls = ClsServiceManager.getClsService(this.options.namespaceName);
const cls = ClsServiceManager.getClsService();
return new Observable((subscriber) => {
cls.run(async () => {
if (this.options.generateId) {
Expand Down
36 changes: 1 addition & 35 deletions src/lib/cls.interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ExecutionContext, ModuleMetadata } from '@nestjs/common';
import { CLS_DEFAULT_NAMESPACE } from './cls.constants';
import { ClsService } from './cls.service';

export class ClsModuleOptions {
Expand All @@ -23,20 +22,9 @@ export class ClsModuleOptions {
* An object with additional options for the `ClsInterceptor`
*/
interceptor?: ClsInterceptorOptions = null;

/**
* The namespace that will be set up. When used, `ClsService`
* must be injected using the `@InjectCls('name')` decorator.
* (most of the time you will not need to touch this setting)
* @deprecated Namespace support will be removed in v3.0
*/
namespaceName? = CLS_DEFAULT_NAMESPACE;
}

export type ClsModuleFactoryOptions = Omit<
ClsModuleOptions,
'global' | 'namespaceName'
>;
export type ClsModuleFactoryOptions = Omit<ClsModuleOptions, 'global'>;
export interface ClsModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
inject?: any[];
useFactory?: (
Expand All @@ -47,13 +35,6 @@ export interface ClsModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
* to import `ClsModule.forFeature()` in other modules
*/
global?: boolean;
/**
* The namespace that will be set up. When used, `ClsService`
* must be injected using the `@InjectCls('name')` decorator.
* (most of the time you will not need to touch this setting)
* @deprecated Namespace support will be removed in v3.0
*/
namespaceName?: string;
}

export class ClsMiddlewareOptions {
Expand Down Expand Up @@ -100,11 +81,6 @@ export class ClsMiddlewareOptions {
* some frameworks are known to lose the context wih `run`.
*/
useEnterWith? = false;

/**
* @deprecated Namespace support will be removed in v3.0
*/
readonly namespaceName?: string;
}

export class ClsGuardOptions {
Expand Down Expand Up @@ -132,11 +108,6 @@ export class ClsGuardOptions {
cls: ClsService,
context: ExecutionContext,
) => void | Promise<void>;

/**
* @deprecated Namespace support will be removed in v3.0
*/
readonly namespaceName?: string;
}

export class ClsInterceptorOptions {
Expand Down Expand Up @@ -164,11 +135,6 @@ export class ClsInterceptorOptions {
cls: ClsService,
context: ExecutionContext,
) => void | Promise<void>;

/**
* @deprecated Namespace support will be removed in v3.0
*/
readonly namespaceName?: string;
}

export interface ClsStore {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/cls.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class ClsMiddleware implements NestMiddleware {
this.options = { ...new ClsMiddlewareOptions(), ...options };
}
use = async (req: any, res: any, next: () => any) => {
const cls = ClsServiceManager.getClsService(this.options.namespaceName);
const cls = ClsServiceManager.getClsService();
const callback = async () => {
this.options.useEnterWith && cls.enter();
if (this.options.generateId) {
Expand Down
Loading