Skip to content

Commit

Permalink
fix(common): Move Render decorator inside PlatformExpress. Refactorin…
Browse files Browse the repository at this point in the history
…g Platform Express
  • Loading branch information
Romain Lenzotti committed Jun 14, 2020
1 parent 367c0b7 commit 4644f15
Show file tree
Hide file tree
Showing 42 changed files with 481 additions and 349 deletions.
2 changes: 1 addition & 1 deletion .nycrc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
],
"check-coverage": true,
"lines": 99.93,
"statements": 99.93,
"statements": 99.92,
"functions": 99.43,
"branches": 83.94
}
3 changes: 2 additions & 1 deletion docs/docs/snippets/controllers/merge-params-2.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Controller, Get, PathParams, MergeParams} from "@tsed/common";
import {Controller, Get, PathParams} from "@tsed/common";
import {MergeParams} from "@tsed/platform-express";

@Controller("/:calendarId/events")
@MergeParams()
Expand Down
1 change: 0 additions & 1 deletion packages/common/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export * from "./interfaces/IServerSettings";
export * from "./interfaces/IServerMountDirectories";
export * from "./interfaces/ILoggerSettings";
export * from "./interfaces/IRouterSettings";
export * from "./interfaces/IErrorSettings";
export * from "./services/ServerSettingsService";
5 changes: 0 additions & 5 deletions packages/common/src/config/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {ProviderScope} from "@tsed/di";
import * as Https from "https";
import {IErrorsSettings} from "./IErrorSettings";
import {ILoggerSettings} from "./ILoggerSettings";
import {IRouterSettings} from "./IRouterSettings";
import {IServerMountDirectories} from "./IServerMountDirectories";
import {IConverterSettings} from "./IConverterSettings";

Expand Down Expand Up @@ -95,10 +94,6 @@ declare global {
* @deprecated Use scopes["CONTROLLER"] instead.
*/
controllerScope: ProviderScope;
/**
* Global configuration for the Express.Router. See express [documentation](http://expressjs.com/en/api.html#express.router).
*/
routers: IRouterSettings;
/**
* Object to mount all directories under to his endpoints. See more on [Serve Static](/tutorials/serve-static-files.md).
*/
Expand Down
6 changes: 1 addition & 5 deletions packages/common/src/mvc/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export * from "./method/useBeforeEach";
export * from "./method/useAfter";
export * from "./method/useAuth";
export * from "./method/route";
export * from "./method/responseView";
export * from "./method/acceptMime";
export * from "./method/location";
export * from "./method/redirect";
export * from "./method/status";
Expand All @@ -25,10 +25,6 @@ export * from "./class/filter";
export * from "./class/middleware";
export * from "./class/middlewareError";
export * from "./class/overrideMiddleware";
export * from "./class/routerSettings";
export * from "./class/mergeParams";
export * from "./class/strict";
export * from "./class/caseSensitive";

// Params
export * from "./params/usePipe";
Expand Down
26 changes: 8 additions & 18 deletions packages/common/src/mvc/decorators/method/acceptMime.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
import {AcceptMime, AcceptMimesMiddleware} from "@tsed/common";
import {Store} from "@tsed/core";
import {expect} from "chai";
import * as Proxyquire from "proxyquire";
import * as Sinon from "sinon";
import {AcceptMimesMiddleware} from "../../../../src/mvc";

const middleware: any = Sinon.stub();
const useBeforeStub: any = Sinon.stub().returns(middleware);

const {AcceptMime} = Proxyquire.load("../../../../src/mvc/decorators/method/acceptMime", {
"./useBefore": {UseBefore: useBeforeStub}
});

class Test {}

describe("AcceptMime", () => {
it("should set metadata", () => {
const descriptor = {};
const options = "application/json";
AcceptMime("application/json")(Test, "test", descriptor);
const store = Store.from(Test, "test", descriptor);
expect(store.get(AcceptMimesMiddleware)).to.deep.eq([options]);
useBeforeStub.should.be.calledWith(AcceptMimesMiddleware);
middleware.should.be.calledWith(Test, "test", descriptor);
class Test {
@AcceptMime("application/json")
test() {}
}

const store = Store.fromMethod(Test, "test");
expect(store.get(AcceptMimesMiddleware)).to.deep.eq(["application/json"]);
});
});
40 changes: 11 additions & 29 deletions packages/common/src/mvc/decorators/method/contentType.spec.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,22 @@
import {assert, expect} from "chai";
import * as Proxyquire from "proxyquire";
import {ContentType, EndpointRegistry} from "@tsed/common";
import {Store} from "@tsed/core";
import {expect} from "chai";
import * as Sinon from "sinon";
import {FakeResponse} from "../../../../../../test/helper";

const middleware: any = Sinon.stub();
const useAfterStub: any = Sinon.stub().returns(middleware);

const {ContentType} = Proxyquire.load("../../../../src/mvc/decorators/method/contentType", {
"./useAfter": {UseAfter: useAfterStub}
});

class Test {}

describe("ContentType", () => {
const descriptor = {};
const options = "application/json";

it("should create middleware", () => {
ContentType(options)(Test, "test", descriptor);

expect(useAfterStub.args[0][0]).to.be.a("function");
assert(middleware.calledWith(Test, "test", descriptor));
});

it("should call response method", () => {
const nextSpy = Sinon.stub();
const response = new FakeResponse();
Sinon.stub(response, "type");

ContentType(options)(Test, "test", descriptor);

const middleware = useAfterStub.args[0][0];
middleware({}, response, nextSpy);

// @ts-ignore
assert(response.type.calledWith(options), "method not called");
assert(nextSpy.called, "function not called");
class Test {
@ContentType("application/json")
test() {}
}

const store = Store.fromMethod(Test, "test");
expect(store.get("produces")).to.deep.eq(["application/json"]);
EndpointRegistry.get(Test, "test").afterMiddlewares.length.should.eq(1);
});
});
11 changes: 5 additions & 6 deletions packages/common/src/mvc/decorators/method/contentType.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {applyDecorators, StoreMerge} from "@tsed/core";
/**
* @module common/mvc
*/
/** */
import {Next} from "../params/next";
import {Req} from "../params/request";
import {Res} from "../params/response";
import {UseAfter} from "./useAfter";

/**
Expand All @@ -26,8 +25,8 @@ import {UseAfter} from "./useAfter";
export function ContentType(type: string) {
return applyDecorators(
StoreMerge("produces", [type]),
UseAfter((request: any, response: any, next: any) => {
response.type(type);
UseAfter((request: Req, response: Res, next: Next) => {
response.contentType(type);
next();
})
);
Expand Down
6 changes: 4 additions & 2 deletions packages/common/src/mvc/decorators/method/location.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {UseAfter} from "./useAfter";

import {Next} from "../params/next";
import {Req} from "../params/request";
import {Res} from "../params/response";
/**
* Sets the response Location HTTP header to the specified path parameter.
*
Expand All @@ -20,7 +22,7 @@ import {UseAfter} from "./useAfter";
* @endpoint
*/
export function Location(location: string): Function {
return UseAfter((request: any, response: any, next: any) => {
return UseAfter((request: Req, response: Res, next: Next) => {
response.location(location);

next();
Expand Down
8 changes: 5 additions & 3 deletions packages/common/src/mvc/decorators/method/redirect.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {UseAfter} from "./useAfter";

import {Next} from "../params/next";
import {Req} from "../params/request";
import {Res} from "../params/response";
/**
* Redirects to the URL derived from the specified path, with specified status, a positive integer that corresponds to an HTTP status code . If not specified, status defaults to “302 “Found”.
*
Expand Down Expand Up @@ -49,12 +51,12 @@ import {UseAfter} from "./useAfter";
* @endpoint
*/
export function Redirect(status: string | number, location?: string): Function {
return UseAfter((request: any, response: any, next: any) => {
return UseAfter((request: Req, response: Res, next: Next) => {
/* istanbul ignore else */
if (typeof status === "string") {
response.redirect(status);
} else {
response.redirect(status, location);
response.redirect(status as number, location!);
}
next();
});
Expand Down
28 changes: 0 additions & 28 deletions packages/common/src/mvc/decorators/method/responseView.spec.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/common/src/mvc/decorators/method/returnType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export function ReturnType(response: Partial<IResponseOptions> = {}): Function {
}

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

endpoint.responses.set(response.code!, response as IResponseOptions);
Expand Down
97 changes: 96 additions & 1 deletion packages/common/src/mvc/decorators/params/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,102 @@ import {UseParam} from "./useParam";

declare global {
namespace TsED {
export interface Response {}
export interface Response {
headersSent: boolean;
writableEnded: boolean;
writableFinished: boolean;
statusCode: number;
/**
* Set header `field` to `val`, or pass
* an object of header fields.
*
* Examples:
*
* res.set('Foo', ['bar', 'baz']);
* res.set('Accept', 'application/json');
* res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
*
* Aliased as `res.header()`.
*/
set(key: string, value: any): any;
set(headers: {[key: string]: any}): any;
/**
* Set status `code`.
*/
status(code: number): any;
pipe(pipe: any): any;
send(body?: any): any;
/**
* Send JSON response.
*
* Examples:
*
* res.json(null);
* res.json({ user: 'tj' });
* res.status(500).json('oh noes!');
* res.status(404).json('I dont have that');
*/
json(obj: any): any;
/**
* Set _Content-Type_ response header with `type` through `mime.lookup()`
* when it does not contain "/", or set the Content-Type to `type` otherwise.
*
* Examples:
*
* res.type('.html');
* res.type('html');
* res.type('json');
* res.type('application/json');
* res.type('png');
*/
contentType(type: string): this;
/**
* Set the location header to `url`.
*
* The given `url` can also be the name of a mapped url, for
* example by default express supports "back" which redirects
* to the _Referrer_ or _Referer_ headers or "/".
*
* Examples:
*
* res.location('/foo/bar').;
* res.location('http://example.com');
* res.location('../login'); // /blog/post/1 -> /blog/login
*
* Mounting:
*
* When an application is mounted and `res.location()`
* is given a path that does _not_ lead with "/" it becomes
* relative to the mount-point. For example if the application
* is mounted at "/blog", the following would become "/blog/login".
*
* res.location('login');
*
* While the leading slash would result in a location of "/login":
*
* res.location('/login');
*/
location(url: string): this;
/**
* Redirect to the given `url` with optional response `status`
* defaulting to 302.
*
* The resulting `url` is determined by `res.location()`, so
* it will play nicely with mounted apps, relative paths,
* `"back"` etc.
*
* Examples:
*
* res.redirect('/foo/bar');
* res.redirect('http://example.com');
* res.redirect(301, 'http://example.com');
* res.redirect('http://example.com', 301);
* res.redirect('../login'); // /blog/post/1 -> /blog/login
*/
redirect(url: string): void;
redirect(status: number, url: string): void;
redirect(url: string, status: number): void;
}
}
}

Expand Down
1 change: 0 additions & 1 deletion packages/common/src/mvc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export * from "./registries/FilterRegistry";
// components
export * from "./middlewares/AuthenticatedMiddleware";
export * from "./middlewares/AcceptMimesMiddleware";
export * from "./middlewares/ResponseViewMiddleware";

// pipes
export * from "./pipes/ValidationPipe";
Expand Down

0 comments on commit 4644f15

Please sign in to comment.