Skip to content

Commit 15bdbac

Browse files
Merge pull request nestjs#1328 from nestjs/bugfix/interceptor-flow
bugfix(core) fix interceptors execution flow
2 parents f96b2a8 + e3cbab7 commit 15bdbac

20 files changed

Lines changed: 168 additions & 122 deletions

File tree

packages/common/cache/interceptors/cache.interceptor.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Observable, of } from 'rxjs';
22
import { tap } from 'rxjs/operators';
33
import { Inject, Injectable, Optional } from '../../decorators';
44
import {
5+
CallHandler,
56
ExecutionContext,
67
HttpServer,
78
NestInterceptor,
@@ -28,22 +29,22 @@ export class CacheInterceptor implements NestInterceptor {
2829

2930
async intercept(
3031
context: ExecutionContext,
31-
call$: Observable<any>,
32+
next: CallHandler,
3233
): Promise<Observable<any>> {
3334
const key = this.trackBy(context);
3435
if (!key) {
35-
return call$;
36+
return next.handle();
3637
}
3738
try {
3839
const value = await this.cacheManager.get(key);
3940
if (value) {
4041
return of(value);
4142
}
42-
return call$.pipe(
43-
tap((response: any) => this.cacheManager.set(key, response)),
44-
);
43+
return next
44+
.handle()
45+
.pipe(tap(response => this.cacheManager.set(key, response)));
4546
} catch {
46-
return call$;
47+
return next.handle();
4748
}
4849
}
4950

packages/common/files/interceptors/file-fields.interceptor.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
MulterField,
88
MulterOptions,
99
} from '../../interfaces/external/multer-options.interface';
10-
import { NestInterceptor } from '../../interfaces/features/nest-interceptor.interface';
10+
import {
11+
CallHandler,
12+
NestInterceptor,
13+
} from '../../interfaces/features/nest-interceptor.interface';
1114
import { MULTER_MODULE_OPTIONS } from '../files.constants';
1215
import { MulterModuleOptions } from '../interfaces';
1316
import { transformException } from '../multer/multer.utils';
@@ -34,7 +37,7 @@ export function FileFieldsInterceptor(
3437

3538
async intercept(
3639
context: ExecutionContext,
37-
call$: Observable<any>,
40+
next: CallHandler,
3841
): Promise<Observable<any>> {
3942
const ctx = context.switchToHttp();
4043

@@ -51,7 +54,7 @@ export function FileFieldsInterceptor(
5154
},
5255
),
5356
);
54-
return call$;
57+
return next.handle();
5558
}
5659
}
5760
const Interceptor = mixin(MixinInterceptor);

packages/common/files/interceptors/file.interceptor.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { Inject, Optional } from '../../decorators';
44
import { mixin } from '../../decorators/core/injectable.decorator';
55
import { ExecutionContext, Type } from '../../interfaces';
66
import { MulterOptions } from '../../interfaces/external/multer-options.interface';
7-
import { NestInterceptor } from '../../interfaces/features/nest-interceptor.interface';
7+
import {
8+
CallHandler,
9+
NestInterceptor,
10+
} from '../../interfaces/features/nest-interceptor.interface';
811
import { MULTER_MODULE_OPTIONS } from '../files.constants';
912
import { MulterModuleOptions } from '../interfaces';
1013
import { transformException } from '../multer/multer.utils';
@@ -31,7 +34,7 @@ export function FileInterceptor(
3134

3235
async intercept(
3336
context: ExecutionContext,
34-
call$: Observable<any>,
37+
next: CallHandler,
3538
): Promise<Observable<any>> {
3639
const ctx = context.switchToHttp();
3740

@@ -48,7 +51,7 @@ export function FileInterceptor(
4851
},
4952
),
5053
);
51-
return call$;
54+
return next.handle();
5255
}
5356
}
5457
const Interceptor = mixin(MixinInterceptor);

packages/common/files/interceptors/files.interceptor.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { Inject, Optional } from '../../decorators';
44
import { mixin } from '../../decorators/core/injectable.decorator';
55
import { ExecutionContext, Type } from '../../interfaces';
66
import { MulterOptions } from '../../interfaces/external/multer-options.interface';
7-
import { NestInterceptor } from '../../interfaces/features/nest-interceptor.interface';
7+
import {
8+
CallHandler,
9+
NestInterceptor,
10+
} from '../../interfaces/features/nest-interceptor.interface';
811
import { MULTER_MODULE_OPTIONS } from '../files.constants';
912
import { MulterModuleOptions } from '../interfaces';
1013
import { transformException } from '../multer/multer.utils';
@@ -32,7 +35,7 @@ export function FilesInterceptor(
3235

3336
async intercept(
3437
context: ExecutionContext,
35-
call$: Observable<any>,
38+
next: CallHandler,
3639
): Promise<Observable<any>> {
3740
const ctx = context.switchToHttp();
3841

@@ -49,7 +52,7 @@ export function FilesInterceptor(
4952
},
5053
),
5154
);
52-
return call$;
55+
return next.handle();
5356
}
5457
}
5558
const Interceptor = mixin(MixinInterceptor);

packages/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export * from './http';
1515
export {
1616
ArgumentMetadata,
1717
ArgumentsHost,
18+
CallHandler,
1819
CanActivate,
1920
DynamicModule,
2021
ExceptionFilter,
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { Observable } from 'rxjs';
22
import { ExecutionContext } from './execution-context.interface';
33

4+
export interface CallHandler<T = any> {
5+
handle(): Observable<T>;
6+
}
7+
48
export interface NestInterceptor<T = any, R = any> {
59
intercept(
610
context: ExecutionContext,
7-
call$: Observable<T>,
11+
next: CallHandler<T>,
812
): Observable<R> | Promise<Observable<R>>;
913
}

packages/common/serializer/class-serializer.interceptor.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { Inject, Injectable } from '../decorators/core';
44
import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface';
55
import { loadPackage } from '../utils/load-package.util';
66
import { isObject } from '../utils/shared.utils';
7-
import { ExecutionContext, NestInterceptor } from './../interfaces';
7+
import {
8+
CallHandler,
9+
ExecutionContext,
10+
NestInterceptor,
11+
} from './../interfaces';
812
import { CLASS_SERIALIZER_OPTIONS } from './class-serializer.constants';
913

1014
let classTransformer: any = {};
@@ -26,16 +30,15 @@ export class ClassSerializerInterceptor implements NestInterceptor {
2630
classTransformer = loadPkg('class-transformer');
2731
}
2832

29-
intercept(
30-
context: ExecutionContext,
31-
call$: Observable<any>,
32-
): Observable<any> {
33+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
3334
const options = this.getContextOptions(context);
34-
return call$.pipe(
35-
map((res: PlainLiteralObject | Array<PlainLiteralObject>) =>
36-
this.serialize(res, options),
37-
),
38-
);
35+
return next
36+
.handle()
37+
.pipe(
38+
map((res: PlainLiteralObject | Array<PlainLiteralObject>) =>
39+
this.serialize(res, options),
40+
),
41+
);
3942
}
4043

4144
serialize(

packages/common/test/files/interceptors/file-fields.interceptor.spec.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { expect } from 'chai';
33
import { of } from 'rxjs';
44
import * as sinon from 'sinon';
55
import { FileFieldsInterceptor } from '../../../files/interceptors/file-fields.interceptor';
6+
import { CallHandler } from '../../../interfaces/features/nest-interceptor.interface';
67

78
describe('FileFieldsInterceptor', () => {
89
it('should return metatype with expected structure', async () => {
@@ -13,9 +14,11 @@ describe('FileFieldsInterceptor', () => {
1314
expect(targetClass.prototype.intercept).to.not.be.undefined;
1415
});
1516
describe('intercept', () => {
16-
let stream$;
17+
let handler: CallHandler;
1718
beforeEach(() => {
18-
stream$ = of('test');
19+
handler = {
20+
handle: () => of('test'),
21+
};
1922
});
2023
it('should call object with expected params', async () => {
2124
const fieldName1 = 'file';
@@ -33,7 +36,7 @@ describe('FileFieldsInterceptor', () => {
3336
.stub((target as any).multer, 'fields')
3437
.returns(callback);
3538

36-
await target.intercept(new ExecutionContextHost([]), stream$);
39+
await target.intercept(new ExecutionContextHost([]), handler);
3740

3841
expect(fieldsSpy.called).to.be.true;
3942
expect(fieldsSpy.calledWith(argument)).to.be.true;
@@ -55,7 +58,7 @@ describe('FileFieldsInterceptor', () => {
5558
array: () => callback,
5659
};
5760
expect(
58-
target.intercept(new ExecutionContextHost([]), stream$),
61+
target.intercept(new ExecutionContextHost([]), handler),
5962
).to.eventually.throw();
6063
});
6164
});

packages/common/test/files/interceptors/file.interceptor.spec.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ import { expect } from 'chai';
33
import { of } from 'rxjs';
44
import * as sinon from 'sinon';
55
import { FileInterceptor } from '../../../files/interceptors/file.interceptor';
6+
import { CallHandler } from '../../../interfaces/features/nest-interceptor.interface';
67

78
describe('FileInterceptor', () => {
89
it('should return metatype with expected structure', async () => {
910
const targetClass = FileInterceptor('file');
1011
expect(targetClass.prototype.intercept).to.not.be.undefined;
1112
});
1213
describe('intercept', () => {
13-
let stream$;
14+
let handler: CallHandler;
1415
beforeEach(() => {
15-
stream$ = of('test');
16+
handler = {
17+
handle: () => of('test'),
18+
};
1619
});
1720
it('should call single() with expected params', async () => {
1821
const fieldName = 'file';
@@ -22,7 +25,7 @@ describe('FileInterceptor', () => {
2225
.stub((target as any).multer, 'single')
2326
.returns(callback);
2427

25-
await target.intercept(new ExecutionContextHost([]), stream$);
28+
await target.intercept(new ExecutionContextHost([]), handler);
2629

2730
expect(singleSpy.called).to.be.true;
2831
expect(singleSpy.calledWith(fieldName)).to.be.true;
@@ -37,7 +40,7 @@ describe('FileInterceptor', () => {
3740
single: () => callback,
3841
};
3942
expect(
40-
target.intercept(new ExecutionContextHost([]), stream$),
43+
target.intercept(new ExecutionContextHost([]), handler),
4144
).to.eventually.throw();
4245
});
4346
});

packages/common/test/files/interceptors/files.interceptor.spec.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ import { expect } from 'chai';
33
import { of } from 'rxjs';
44
import * as sinon from 'sinon';
55
import { FilesInterceptor } from '../../../files/interceptors/files.interceptor';
6+
import { CallHandler } from '../../../interfaces/features/nest-interceptor.interface';
67

78
describe('FilesInterceptor', () => {
89
it('should return metatype with expected structure', async () => {
910
const targetClass = FilesInterceptor('file');
1011
expect(targetClass.prototype.intercept).to.not.be.undefined;
1112
});
1213
describe('intercept', () => {
13-
let stream$;
14+
let handler: CallHandler;
1415
beforeEach(() => {
15-
stream$ = of('test');
16+
handler = {
17+
handle: () => of('test'),
18+
};
1619
});
1720
it('should call array() with expected params', async () => {
1821
const fieldName = 'file';
@@ -24,7 +27,7 @@ describe('FilesInterceptor', () => {
2427
.stub((target as any).multer, 'array')
2528
.returns(callback);
2629

27-
await target.intercept(new ExecutionContextHost([]), stream$);
30+
await target.intercept(new ExecutionContextHost([]), handler);
2831

2932
expect(arraySpy.called).to.be.true;
3033
expect(arraySpy.calledWith(fieldName, maxCount)).to.be.true;
@@ -39,7 +42,7 @@ describe('FilesInterceptor', () => {
3942
array: () => callback,
4043
};
4144
expect(
42-
target.intercept(new ExecutionContextHost([]), stream$),
45+
target.intercept(new ExecutionContextHost([]), handler),
4346
).to.eventually.throw();
4447
});
4548
});

0 commit comments

Comments
 (0)