Skip to content
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
57 changes: 57 additions & 0 deletions integration/hello-world/e2e/interceptors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,36 @@ export class TransformInterceptor {
}
}

@Injectable()
export class StatusInterceptor {

constructor(private statusCode: number){}

intercept(context: ExecutionContext, next: CallHandler) {
const ctx = context.switchToHttp();
const res = ctx.getResponse();
res.status(this.statusCode);
return next.handle().pipe(map(data => ({ data })));
}
}

@Injectable()
export class HeaderInterceptor {

constructor(private headers: object){}

intercept(context: ExecutionContext, next: CallHandler) {
const ctx = context.switchToHttp();
const res = ctx.getResponse();
for (const key in this.headers) {
if (this.headers.hasOwnProperty(key)) {
res.header(key, this.headers[key]);
}
}
return next.handle().pipe(map(data => ({ data })));
}
}

function createTestModule(interceptor) {
return Test.createTestingModule({
imports: [ApplicationModule],
Expand Down Expand Up @@ -87,6 +117,33 @@ describe('Interceptors', () => {
.expect(200, { data: 'Hello world!' });
});

it(`should modify response status`, async () => {
app = (await createTestModule(
new StatusInterceptor(400),
)).createNestApplication();

await app.init();
return request(app.getHttpServer())
.get('/hello')
.expect(400, { data: 'Hello world!' });
});

it(`should modify Authorization header`, async () => {
const customHeaders = {
Authorization: 'jwt',
};

app = (await createTestModule(
new HeaderInterceptor(customHeaders),
)).createNestApplication();

await app.init();
return request(app.getHttpServer())
.get('/hello')
.expect(200)
.expect('Authorization', 'jwt');
});

afterEach(async () => {
await app.close();
});
Expand Down
2 changes: 1 addition & 1 deletion integration/hello-world/src/hello/hello.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HelloService } from './hello.service';
import { Controller, Get, Header, Param } from '@nestjs/common';
import { Controller, Get, Header, Param, Post } from '@nestjs/common';
import { Observable, of } from 'rxjs';
import { UserByIdPipe } from './users/user-by-id.pipe';

Expand Down
3 changes: 2 additions & 1 deletion packages/common/interfaces/http/http-server.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export interface HttpServer<TRequest = any, TResponse = any> {
options(path: string, handler: RequestHandler<TRequest, TResponse>): any;
listen(port: number | string, callback?: () => void): any;
listen(port: number | string, hostname: string, callback?: () => void): any;
reply(response: any, body: any, statusCode: number): any;
reply(response: any, body: any, statusCode?: number): any;
status(response: any, statusCode: number): any;
render(response: any, view: string, options: any): any;
setHeader(response: any, name: string, value: string): any;
setErrorHandler?(handler: Function): any;
Expand Down
3 changes: 2 additions & 1 deletion packages/core/adapters/http-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export abstract class AbstractHttpAdapter<
abstract setViewEngine(engine: string);
abstract getRequestMethod(request);
abstract getRequestUrl(request);
abstract reply(response, body: any, statusCode: number);
abstract status(response, statusCode: number);
abstract reply(response, body: any, statusCode?: number);
abstract render(response, view: string, options: any);
abstract setErrorHandler(handler: Function);
abstract setNotFoundHandler(handler: Function);
Expand Down
35 changes: 18 additions & 17 deletions packages/core/router/router-execution-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class RouterExecutionContext {
fnHandleResponse,
paramtypes,
getParamsMetadata,
} = this.getMetadata(instance, callback, methodName, module, requestMethod);
} = this.getMetadata(instance, callback, methodName, module);
const paramsOptions = this.contextUtils.mergeParamsMetatypes(
getParamsMetadata(module, contextId, inquirerId),
paramtypes,
Expand Down Expand Up @@ -131,6 +131,20 @@ export class RouterExecutionContext {
const args = this.contextUtils.createNullArray(argsLength);
fnCanActivate && (await fnCanActivate([req, res]));

const httpCode = this.reflectHttpStatusCode(callback);

const httpStatusCode = httpCode
? httpCode
: this.responseController.getStatusByMethod(requestMethod);

this.responseController.status(res, httpStatusCode);

const responseHeaders = this.reflectResponseHeaders(callback);
const hasCustomHeaders = !isEmpty(responseHeaders);

hasCustomHeaders &&
this.responseController.setHeaders(res, responseHeaders);

const result = await this.interceptorsConsumer.intercept(
interceptors,
[req, res],
Expand All @@ -146,8 +160,7 @@ export class RouterExecutionContext {
instance: Controller,
callback: (...args: any[]) => any,
methodName: string,
module: string,
requestMethod: RequestMethod,
module: string
): HandlerMetadata {
const cacheMetadata = this.handlerMetadataStorage.get(instance, methodName);
if (cacheMetadata) {
Expand All @@ -165,7 +178,6 @@ export class RouterExecutionContext {
instance,
methodName,
);
const httpCode = this.reflectHttpStatusCode(callback);
const getParamsMetadata = (
moduleKey: string,
contextId = STATIC_CONTEXT,
Expand All @@ -184,14 +196,10 @@ export class RouterExecutionContext {
({ type }) =>
type === RouteParamtypes.RESPONSE || type === RouteParamtypes.NEXT,
);
const httpStatusCode = httpCode
? httpCode
: this.responseController.getStatusByMethod(requestMethod);

const fnHandleResponse = this.createHandleResponseFn(
callback,
isResponseHandled,
httpStatusCode,
isResponseHandled
);
const handlerMetadata: HandlerMetadata = {
argsLength,
Expand Down Expand Up @@ -342,23 +350,16 @@ export class RouterExecutionContext {
public createHandleResponseFn(
callback: (...args: any[]) => any,
isResponseHandled: boolean,
httpStatusCode: number,
httpStatusCode?: number
) {
const renderTemplate = this.reflectRenderTemplate(callback);
const responseHeaders = this.reflectResponseHeaders(callback);
const hasCustomHeaders = !isEmpty(responseHeaders);

if (renderTemplate) {
return async <TResult, TResponse>(result: TResult, res: TResponse) => {
hasCustomHeaders &&
this.responseController.setHeaders(res, responseHeaders);
await this.responseController.render(result, res, renderTemplate);
};
}
return async <TResult, TResponse>(result: TResult, res: TResponse) => {
hasCustomHeaders &&
this.responseController.setHeaders(res, responseHeaders);

result = await this.responseController.transformToResult(result);
!isResponseHandled &&
(await this.responseController.apply(result, res, httpStatusCode));
Expand Down
9 changes: 8 additions & 1 deletion packages/core/router/router-response-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class RouterResponseController {
public async apply<TInput = any, TResponse = any>(
result: TInput,
response: TResponse,
httpStatusCode: number,
httpStatusCode?: number,
) {
return this.applicationRef.reply(response, result, httpStatusCode);
}
Expand Down Expand Up @@ -50,4 +50,11 @@ export class RouterResponseController {
this.applicationRef.setHeader(response, name, value),
);
}

public status<TResponse = any>(
response: TResponse,
statusCode: number
) {
this.applicationRef.status(response, statusCode);
}
}
10 changes: 6 additions & 4 deletions packages/core/test/exceptions/exceptions-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ describe('ExceptionsHandler', () => {
beforeEach(() => {
sinon
.stub(adapter, 'reply')
.callsFake((responseRef: any, body: any, statusCode: number) => {
const res = responseRef.status(statusCode);
.callsFake((responseRef: any, body: any, statusCode?: number) => {
if (statusCode) {
responseRef.status(statusCode);
}
if (isNil(body)) {
return res.send();
return responseRef.send();
}
return isObject(body) ? res.json(body) : res.send(String(body));
return isObject(body) ? responseRef.json(body) : responseRef.send(String(body));
});
});
it('should method send expected response status code and message when exception is unknown', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/test/router/router-execution-context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ describe('RouterExecutionContext', () => {
sinon.stub(contextCreator, 'reflectResponseHeaders').returns([]);
sinon.stub(contextCreator, 'reflectRenderTemplate').returns(template);

const handler = contextCreator.createHandleResponseFn(null, true, 100);
const handler = contextCreator.createHandleResponseFn(null, true, 200);
await handler(value, response);

expect(response.render.calledWith(template, value)).to.be.true;
Expand All @@ -295,7 +295,7 @@ describe('RouterExecutionContext', () => {
sinon.stub(contextCreator, 'reflectResponseHeaders').returns([]);
sinon.stub(contextCreator, 'reflectRenderTemplate').returns(undefined);

const handler = contextCreator.createHandleResponseFn(null, true, 100);
const handler = contextCreator.createHandleResponseFn(null, true, 200);
handler(result, response);

expect(response.render.called).to.be.false;
Expand Down
31 changes: 25 additions & 6 deletions packages/core/test/router/router-response-controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,20 @@ describe('RouterResponseController', () => {
json: sinon.SinonSpy;
};
beforeEach(() => {
response = { send: sinon.spy(), json: sinon.spy() };
response.status = sinon.stub().returns(response);
response = { send: sinon.spy(), json: sinon.spy(), status: sinon.spy() };
});
describe('when result is', () => {
beforeEach(() => {
sinon
.stub(adapter, 'reply')
.callsFake((responseRef: any, body: any, statusCode: number) => {
const res = responseRef.status(statusCode);
.callsFake((responseRef: any, body: any, statusCode?: number) => {
if (statusCode) {
responseRef.status(statusCode);
}
if (isNil(body)) {
return res.send();
return responseRef.send();
}
return isObject(body) ? res.json(body) : res.send(String(body));
return isObject(body) ? responseRef.json(body) : responseRef.send(String(body));
});
});
describe('nil', () => {
Expand Down Expand Up @@ -149,4 +150,22 @@ describe('RouterResponseController', () => {
).to.be.true;
});
});

describe('status', () => {
let statusStub: sinon.SinonStub;

beforeEach(() => {
statusStub = sinon.stub(adapter, 'status').callsFake(() => ({}));
});

it('should set status', () => {
const response = {};
const statusCode = 400;

routerResponseController.status(response, statusCode);
expect(
statusStub.calledWith(response, statusCode),
).to.be.true;
});
});
});
3 changes: 2 additions & 1 deletion packages/core/test/utils/noop-adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export class NoopHttpAdapter extends AbstractHttpAdapter {
setViewEngine(engine: string): any {}
getRequestMethod(request: any): any {}
getRequestUrl(request: any): any {}
reply(response: any, body: any, statusCode: number): any {}
reply(response: any, body: any): any {}
status(response: any, statusCode: number): any {}
render(response: any, view: string, options: any): any {}
setErrorHandler(handler: Function): any {}
setNotFoundHandler(handler: Function): any {}
Expand Down
14 changes: 10 additions & 4 deletions packages/platform-express/adapters/express-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ export class ExpressAdapter extends AbstractHttpAdapter {
super(instance || express());
}

public reply(response, body: any, statusCode: number) {
const res = response.status(statusCode);
public reply(response, body: any, statusCode?: number) {
if (statusCode) {
response.status(statusCode);
}
if (isNil(body)) {
return res.send();
return response.send();
}
return isObject(body) ? res.json(body) : res.send(String(body));
return isObject(body) ? response.json(body) : response.send(String(body));
}

public status(response: any, statusCode: number) {
return response.status(statusCode);
}

public render(response: any, view: string, options: any) {
Expand Down
11 changes: 9 additions & 2 deletions packages/platform-fastify/adapters/fastify-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,15 @@ export class FastifyAdapter extends AbstractHttpAdapter {
return this.instance.listen(port, ...args);
}

public reply(response: any, body: any, statusCode: number) {
return response.code(statusCode).send(body);
public reply(response: any, body: any, statusCode?: number) {
if (statusCode) {
response.status(statusCode);
}
return response.send(body);
}

public status(response: any, statusCode: number) {
return response.code(statusCode);
}

public render(response: any, view: string, options: any) {
Expand Down
Empty file modified scripts/test.sh
100644 → 100755
Empty file.