Skip to content

Commit

Permalink
Merge fix-742-responses
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Feb 3, 2020
2 parents 3c39233 + c57b51a commit 47399f4
Show file tree
Hide file tree
Showing 60 changed files with 1,355 additions and 1,344 deletions.
17 changes: 5 additions & 12 deletions docs/docs/snippets/middlewares/custom-endpoint-decorator-status.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
import {applyDecorators, StoreSet} from "@tsed/core";
import {IResponseOptions, UseAfter, mapReturnedResponse} from "@tsed/common";

export function Status(code: number, options: IResponseOptions = {}) {
const response = mapReturnedResponse(options);
import {UseAfter} from "@tsed/common";
import {applyDecorators} from "@tsed/core";

export function CustomStatus(code: number) {
return applyDecorators(
StoreSet("statusCode", code),
StoreSet("response", response),
StoreSet("responses", {[code]: response}),
UseAfter((request: any, response: any, next: any) => {
if (response.statusCode === 200) {
response.status(code);
}
UseAfter((req: any, res: any, next: any) => {
res.status(code);
next();
})
);
Expand Down
8 changes: 2 additions & 6 deletions packages/common/src/converters/interfaces/IConverter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
/**
*
*/
import {Type} from "@tsed/core";
import {IMetadataType} from "@tsed/core";

export interface IDeserializer {
(obj: any, targetType: any, baseType?: any): any;
Expand All @@ -24,8 +21,7 @@ export interface ISerializer {
/**
*
*/
export interface IConverterOptions {
type?: Type<any>;
export interface IConverterOptions extends IMetadataType {
ignoreCallback?: IConverterIgnoreCB;
checkRequiredValue?: boolean;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/mvc/builders/ControllerBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {InjectorService} from "@tsed/di";
import * as Express from "express";
import {bindEndpointMiddleware} from "../components/bindEndpointMiddleware";
import {SendResponseMiddleware} from "../components/SendResponseMiddleware";
import {statusAndHeadersMiddleware} from "../components/statusAndHeadersMiddleware";
import {IPathMethod} from "../interfaces/IPathMethod";
import {ControllerProvider} from "../models/ControllerProvider";
import {EndpointMetadata} from "../models/EndpointMetadata";
Expand Down Expand Up @@ -86,6 +87,7 @@ export class ControllerBuilder {
.concat(beforeMiddlewares) // Endpoint before-middlewares
.concat(mldwrs) // Endpoint middlewares
.concat(endpoint) // Endpoint handler
.concat(statusAndHeadersMiddleware)
.concat(afterMiddlewares) // Endpoint after-middlewares
.filter((item: any) => !!item)
.map((middleware: any) => HandlerBuilder.from(middleware).build(injector));
Expand Down
19 changes: 19 additions & 0 deletions packages/common/src/mvc/components/statusAndHeadersMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as Express from "express";

export function statusAndHeadersMiddleware(request: Express.Request, response: Express.Response, next: any) {
const {
statusCode,
response: {headers = {}}
} = request.ctx.endpoint;

if (response.statusCode === 200) {
// apply status only if the isn't already modified
response.status(statusCode);
}

// apply headers
Object.entries(headers).forEach(([key, schema]) => {
schema.value !== undefined && response.set(key, String(schema.value));
});
next();
}
4 changes: 4 additions & 0 deletions packages/common/src/mvc/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ export * from "./params/request";
export * from "./params/next";
export * from "./params/error";
export * from "./params/endpointInfo";

// utils
export * from "./utils/mapReturnedResponse";
export * from "./utils/getStorableMetadata";
18 changes: 18 additions & 0 deletions packages/common/src/mvc/decorators/method/endpointFn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {DecoratorParameters, getDecoratorType, Type} from "@tsed/core";
import {EndpointMetadata} from "../../models/EndpointMetadata";
import {EndpointRegistry} from "../../registries/EndpointRegistry";

/**
*
* @param fn
* @decorator
*/
export function EndpointFn(fn: (endpoint: EndpointMetadata, parameters: DecoratorParameters) => void) {
return <T>(target: Type<any>, property: string, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void => {
if (getDecoratorType([target, property, descriptor]) === "method") {
fn(EndpointRegistry.get(target, property!), [target, property, descriptor]);

return descriptor;
}
};
}
51 changes: 36 additions & 15 deletions packages/common/src/mvc/decorators/method/header.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
import {applyDecorators, StoreMerge} from "@tsed/core";
import {IResponseHeader} from "../../interfaces/IResponseHeader";
import {IHeadersOptions, IResponseHeaders} from "../../interfaces/IResponseHeaders";
import {mapHeaders} from "../../utils/mapHeaders";
import {UseAfter} from "./useAfter";
import {deepMerge} from "@tsed/core";
import {IResponseHeader, IResponseHeaders} from "../../interfaces";
import {EndpointFn} from "./endpointFn";

export type IHeaderOptions = string | number | IResponseHeader;

export interface IHeadersOptions {
[key: string]: IHeaderOptions;
}

export function mapHeaders(headers: IHeadersOptions): IResponseHeaders {
return Object.keys(headers).reduce<IResponseHeaders>((newHeaders: IResponseHeaders, key: string, index: number, array: string[]) => {
const value: any = headers[key];
let type = typeof value;
let options: any = {
value
};

if (type === "object") {
options = value;
type = typeof options.value;
}

options.type = options.type || type;

newHeaders[key] = options;

return newHeaders;
}, {});
}

/**
* Sets the response’s HTTP header field to value. To set multiple fields at once, pass an object as the parameter.
Expand Down Expand Up @@ -53,19 +78,15 @@ import {UseAfter} from "./useAfter";
* @decorator
* @endpoint
*/
export function Header(headerName: string | number | IHeadersOptions, headerValue?: string | number | IResponseHeader): Function {
export function Header(headerName: string | number | IHeadersOptions, headerValue?: IHeaderOptions): Function {
if (headerValue !== undefined) {
headerName = {[headerName as string]: headerValue};
}
const headers: IResponseHeaders = mapHeaders(headerName as IHeadersOptions);

return applyDecorators(
StoreMerge("response", {headers}),
UseAfter((request: any, response: any, next: any) => {
Object.keys(headers).forEach(key => {
response.set(key, headers[key].value);
});
next();
})
);
return EndpointFn(endpoint => {
const {response} = endpoint;

response.headers = deepMerge(response.headers || {}, headers);
});
}
38 changes: 26 additions & 12 deletions packages/common/src/mvc/decorators/method/returnType.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import {getDecoratorType, Type} from "@tsed/core";
import {EndpointRegistry} from "../../registries/EndpointRegistry";
import {cleanObject, deepMerge} from "@tsed/core";
import {IResponseOptions} from "../../interfaces/IResponseOptions";
import {EndpointFn} from "./endpointFn";

const isSuccessStatus = (code: number | undefined) => code && 200 <= code && code < 300;

/**
* Define the returned type for the serialization.
*
* ```typescript
* @Controller('/')
* export class Ctrl {
*
* @Get('/')
* @ReturnType(User)
* get(): Promise<User> { }
* @ReturnType(200, {type: User, collectionType: Map})
* get(): Promise<Map<User>> { }
* }
*
* ```
*
* @returns {Function}
* @param type
* @param response
* @decorator
* @endpoint
*/
export function ReturnType(type: Type<any> | any): Function {
return <T>(target: Type<any>, targetKey?: string, descriptor?: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void => {
if (getDecoratorType([target, targetKey, descriptor]) === "method") {
EndpointRegistry.get(target, targetKey!).type = type;
export function ReturnType(response: Partial<IResponseOptions> = {}): Function {
return EndpointFn(endpoint => {
const {responses, statusCode} = endpoint;
const code = response.code || statusCode; // implicit

return descriptor;
if (isSuccessStatus(response.code)) {
const {response} = endpoint;
responses.delete(statusCode);
endpoint.statusCode = code;
endpoint.responses.set(code, response);
}
};

response = {
code,
description: "",
...deepMerge(endpoint.get(code), cleanObject(response))
};

endpoint.responses.set(response.code!, response as IResponseOptions);
});
}
27 changes: 11 additions & 16 deletions packages/common/src/mvc/decorators/method/status.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {applyDecorators, StoreSet, StoreMerge} from "@tsed/core";
import {IResponseOptions} from "../../interfaces/IResponseOptions";
import {mapReturnedResponse} from "../../utils/mapReturnedResponse";
import {applyDecorators, StoreMerge, StoreSet} from "@tsed/core";
import {mapReturnedResponse} from "../utils/mapReturnedResponse";
import {ReturnType} from "./returnType";
import {UseAfter} from "./useAfter";

/**
Expand Down Expand Up @@ -46,18 +46,13 @@ import {UseAfter} from "./useAfter";
* @decorator
* @endpoint
*/
export function Status(code: number, options: IResponseOptions = {}) {
const response = mapReturnedResponse(options);
export function Status(code: number, options: TsED.ResponseOptions = {description: ""}) {
const {use, collection} = options as any;

return applyDecorators(
StoreSet("statusCode", code),
StoreMerge("response", response),
StoreMerge("responses", {[code]: response}),
UseAfter((request: any, response: any, next: any) => {
if (response.statusCode === 200) {
response.status(code);
}
next();
})
);
return ReturnType({
...options,
code,
type: options.type || use,
collectionType: options.collectionType || collection
});
}
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/bodyParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Type} from "@tsed/core";
import {IParamOptions} from "../../interfaces/IParamOptions";
import {ParamTypes} from "../../models/ParamTypes";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";

/**
* BodyParams return the value from [request.body](http://expressjs.com/en/4x/api.html#req.body) object.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {ParamTypes} from "../../models/ParamTypes";
import {RequestContext} from "../../models/RequestContext";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";

/**
* Context decorator return the @@RequestContext@@ created by Ts.ED when request is handled by the server.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/cookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Type} from "@tsed/core";
import {IParamOptions} from "../../interfaces/IParamOptions";
import {ParamTypes} from "../../models/ParamTypes";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";

/**
* Cookies or CookiesParams return the value from [request.cookies](http://expressjs.com/en/4x/api.html#req.cookies) object.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/headerParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Type} from "@tsed/core";
import {IParamOptions} from "../../interfaces/IParamOptions";
import {ParamTypes} from "../../models/ParamTypes";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";

/**
* HeaderParams return the value from [request.params](http://expressjs.com/en/4x/api.html#req.params) object.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/locals.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {ParamTypes} from "../../models/ParamTypes";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";

/**
* Locals return the value from [response.locals](http://expressjs.com/en/4x/api.html#res.locals) object.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/pathParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Type} from "@tsed/core";
import {IParamOptions} from "../../interfaces/IParamOptions";
import {ParamTypes} from "../../models/ParamTypes";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";

/**
* PathParams return the value from [request.params](http://expressjs.com/en/4x/api.html#req.params) object.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/queryParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Type} from "@tsed/core";
import {IParamOptions} from "../../interfaces/IParamOptions";
import {ParamTypes} from "../../models/ParamTypes";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";

/**
* QueryParams return the value from [request.query](http://expressjs.com/en/4x/api.html#req.query) object.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Type} from "@tsed/core";
import * as Express from "express";
import {ParamTypes} from "../../models/ParamTypes";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";
import {IParamOptions} from "../../interfaces/IParamOptions";

export type Request = Express.Request;
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/mvc/decorators/params/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Type} from "@tsed/core";
import {IParamOptions} from "../../interfaces/IParamOptions";
import {ParamTypes} from "../../models/ParamTypes";
import {UseFilter} from "./useFilter";
import {mapParamsOptions} from "./utils/mapParamsOptions";
import {mapParamsOptions} from "../utils/mapParamsOptions";

/**
* Session return the value from [request.session](http://expressjs.com/en/4x/api.html#req.session) object.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {isObject, isPrimitive} from "@tsed/core";
import {IParamOptions} from "../../../interfaces/IParamOptions";
import {IParamOptions} from "../../interfaces/IParamOptions";

export function mapParamsOptions(args: any[]): IParamOptions<any> {
if (args.length === 1) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function mapReturnedResponse({use, collection, ...options}: any): any {
return {
...options,
type: options.type || use,
collectionType: options.collectionType || collection
};
}
5 changes: 1 addition & 4 deletions packages/common/src/mvc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,5 @@ export * from "./errors/UnknowFilterError";
// decorators
export * from "./decorators";

// utils
export * from "./utils/mapHeaders";
export * from "./utils/mapReturnedResponse";

// Module
export * from "./MvcModule";
4 changes: 1 addition & 3 deletions packages/common/src/mvc/interfaces/IResponseError.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {IResponseHeaders} from "./IResponseHeaders";

/**
* Interface can be implemented to customize the error sent to the client.
*/
export interface IResponseError extends Error {
errors?: any[];
origin?: Error;
headers?: IResponseHeaders;
headers?: {};
}
5 changes: 0 additions & 5 deletions packages/common/src/mvc/interfaces/IResponseHeader.ts

This file was deleted.

9 changes: 0 additions & 9 deletions packages/common/src/mvc/interfaces/IResponseHeaders.ts

This file was deleted.

Loading

0 comments on commit 47399f4

Please sign in to comment.