Skip to content

Commit 70118b1

Browse files
authored
Refactor/len remove route array (#15)
* feat: add controller and request mappers decorators * refactor: remove route array from Http class * refactor: create handler to hook preHandler
1 parent 76efc5f commit 70118b1

File tree

9 files changed

+322
-50
lines changed

9 files changed

+322
-50
lines changed

package-lock.json

Lines changed: 22 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@secjs/http",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "",
55
"license": "MIT",
66
"author": "João Lenon",
@@ -21,7 +21,7 @@
2121
"devDependencies": {
2222
"@secjs/exceptions": "1.0.4",
2323
"@secjs/ioc": "1.0.8",
24-
"@secjs/utils": "1.6.2",
24+
"@secjs/utils": "1.6.3",
2525
"@types/jest": "27.0.1",
2626
"@types/node": "14.14.37",
2727
"@types/supertest": "^2.0.11",
@@ -42,6 +42,7 @@
4242
"jest": "27.1.0",
4343
"lint-staged": "11.1.2",
4444
"prettier": "2.3.2",
45+
"reflect-metadata": "0.1.13",
4546
"supertest": "6.1.6",
4647
"ts-jest": "27.0.5",
4748
"ts-loader": "9.2.3",

src/Decorators/Controller.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @secjs/http
3+
*
4+
* (c) João Lenon <lenon@secjs.com.br>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
import 'reflect-metadata'
11+
import { removeSlash } from '../Utils/removeSlash'
12+
13+
export function Controller(path?: string | string[]): ClassDecorator {
14+
if (!path) path = '/'
15+
16+
path = removeSlash(path)
17+
18+
return (target: any) => {
19+
const routes: any[] = Reflect.getMetadata('controller:routes', target)
20+
21+
if (routes && routes.length) {
22+
if (Array.isArray(path)) {
23+
const routesPrefixed = []
24+
25+
path.forEach(p => {
26+
if (p === '/') return
27+
28+
routes.forEach(route =>
29+
routesPrefixed.push({
30+
...route,
31+
path: `${p}${route.path}`,
32+
}),
33+
)
34+
})
35+
36+
Reflect.defineMetadata('controller:routes', routesPrefixed, target)
37+
} else {
38+
const routesPrefixed =
39+
path === '/'
40+
? routes
41+
: routes.filter(route => (route.path = `${path}${route.path}`))
42+
43+
Reflect.defineMetadata('controller:routes', routesPrefixed, target)
44+
}
45+
}
46+
47+
Reflect.defineMetadata(
48+
'controller:path',
49+
typeof path === 'string' ? [path] : path,
50+
target,
51+
)
52+
}
53+
}

src/Decorators/RequestMapping.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* @secjs/http
3+
*
4+
* (c) João Lenon <lenon@secjs.com.br>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
import 'reflect-metadata'
11+
import { removeSlash } from '../Utils/removeSlash'
12+
13+
export enum RequestMethod {
14+
GET,
15+
POST,
16+
PUT,
17+
DELETE,
18+
PATCH,
19+
ALL,
20+
OPTIONS,
21+
HEAD,
22+
}
23+
24+
export function RequestMapper(
25+
path: string | string[] = '/',
26+
method: RequestMethod = RequestMethod.GET,
27+
): MethodDecorator {
28+
if (path.length && !path[0]) path = '/'
29+
30+
path = removeSlash(path)
31+
32+
return (
33+
target: any,
34+
key: string | symbol,
35+
descriptor: TypedPropertyDescriptor<any>,
36+
) => {
37+
let routes = Reflect.getMetadata('controller:routes', target.constructor)
38+
39+
if (!routes) {
40+
Reflect.defineMetadata(
41+
'controller:routes',
42+
(routes = []),
43+
target.constructor,
44+
)
45+
}
46+
47+
typeof path === 'string'
48+
? routes.push({ path, method, handler: target[key] })
49+
: path.forEach(p =>
50+
routes.push({ path: p, method, handler: target[key] }),
51+
)
52+
53+
Reflect.defineMetadata('controller:routes', routes, target.constructor)
54+
55+
return descriptor
56+
}
57+
}
58+
59+
export const createRequestDecorator =
60+
(method: RequestMethod) =>
61+
(path?: string | string[]): MethodDecorator =>
62+
RequestMapper(path, method)
63+
64+
export const Get = createRequestDecorator(RequestMethod.GET)
65+
export const Post = createRequestDecorator(RequestMethod.POST)
66+
export const Put = createRequestDecorator(RequestMethod.PUT)
67+
export const Patch = createRequestDecorator(RequestMethod.PATCH)
68+
export const Delete = createRequestDecorator(RequestMethod.DELETE)
69+
export const All = createRequestDecorator(RequestMethod.ALL)
70+
export const Options = createRequestDecorator(RequestMethod.OPTIONS)
71+
export const Head = createRequestDecorator(RequestMethod.HEAD)

src/Http.ts

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import fastify, {
1717
import { String } from '@secjs/utils'
1818
import { Request } from './Context/Request'
1919
import { Response } from './Context/Response'
20-
import { RouteContract } from './Contracts/RouteContract'
2120
import { HttpMethodTypes } from './Contracts/HttpMethodTypes'
2221
import { HandlerContract } from './Contracts/Context/HandlerContract'
2322
import { FastifyHandlerContract } from './Contracts/FastifyHandlerContract'
@@ -29,14 +28,9 @@ declare module 'fastify' {
2928
}
3029

3130
export class Http {
32-
private readonly routes: RouteContract[]
3331
private readonly server: FastifyInstance
34-
private readonly middlewares: HandlerContract[]
3532

3633
constructor() {
37-
this.routes = []
38-
this.middlewares = []
39-
4034
this.server = fastify()
4135
this.server.setErrorHandler(Http.defaultErrorHandler)
4236
}
@@ -68,23 +62,22 @@ export class Http {
6862
private createFastifyHandler(
6963
handler: (ctx) => Promise<void> | void,
7064
): FastifyHandlerContract {
71-
return async (req: FastifyRequest, res: FastifyReply, next?: any) => {
65+
return async (req: FastifyRequest, res: FastifyReply) => {
7266
const request = new Request(req)
7367
const response = new Response(res)
7468

7569
if (!req.data) req.data = {}
7670
if (!req.query) req.query = {}
7771
if (!req.params) req.params = {}
78-
// eslint-disable-next-line @typescript-eslint/no-empty-function
79-
if (!next) next = () => {}
8072

8173
return handler({
8274
request,
8375
response,
8476
params: req.params,
8577
queries: req.query,
8678
data: req.data,
87-
next,
79+
// eslint-disable-next-line @typescript-eslint/no-empty-function
80+
next: () => {},
8881
})
8982
}
9083
}
@@ -93,24 +86,38 @@ export class Http {
9386
return this.server
9487
}
9588

96-
use(handler: HandlerContract) {
97-
this.middlewares.push(handler)
98-
}
99-
10089
getRoutes(options?: PrintRoutesOptions) {
10190
return this.server.printRoutes(options)
10291
}
10392

104-
async listen(
93+
use(handler: HandlerContract) {
94+
this.server.addHook('preHandler', (req, res, done) => {
95+
const request = new Request(req)
96+
const response = new Response(res)
97+
98+
if (!req.data) req.data = {}
99+
if (!req.query) req.query = {}
100+
if (!req.params) req.params = {}
101+
102+
return handler({
103+
request,
104+
response,
105+
params: req.params as Record<string, string>,
106+
queries: req.query as Record<string, string>,
107+
data: req.data,
108+
next: done,
109+
})
110+
})
111+
}
112+
113+
listen(
105114
port?: number,
106115
cb?: (err: Error | null, address: string) => void,
107-
): Promise<void> {
108-
this.routes.forEach(route => this.server.route(route))
109-
116+
): void | string {
110117
return this.server.listen(port || 1335, cb)
111118
}
112119

113-
async close(cb?: () => void): Promise<void> {
120+
close(cb?: () => void): void {
114121
return this.server.close(cb)
115122
}
116123

@@ -120,23 +127,12 @@ export class Http {
120127
handler: HandlerContract,
121128
middlewares?: HandlerContract[],
122129
) {
123-
let allMiddlewares = this.middlewares
124-
125-
if (middlewares && middlewares.length) {
126-
allMiddlewares = allMiddlewares.concat(middlewares)
127-
}
128-
129-
const preHandlers: FastifyHandlerContract[] = allMiddlewares.map(mid =>
130-
this.createFastifyHandler(mid),
131-
)
132-
133-
methods.forEach(method => {
134-
this.routes.push({
135-
url,
136-
method,
137-
preHandler: preHandlers,
138-
handler: this.createFastifyHandler(handler),
139-
})
130+
this.server.route({
131+
url,
132+
method: methods,
133+
handler: this.createFastifyHandler(handler),
134+
preHandler:
135+
middlewares && middlewares.map(mid => this.createFastifyHandler(mid)),
140136
})
141137
}
142138

src/Router/Route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class Route {
4949
}
5050

5151
private getUrl(): string {
52-
const url = removeSlash(this.url)
52+
const url = removeSlash(this.url) as string
5353

5454
const prefix = this.prefixes
5555
.slice()

src/Utils/removeSlash.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,18 @@
77
* file that was distributed with this source code.
88
*/
99

10-
export function removeSlash(url: string): string {
10+
import { Is } from '@secjs/utils'
11+
12+
export function removeSlash(url: string | string[]): string | string[] {
1113
if (url === '/') {
1214
return url
1315
}
1416

15-
return `/${url.replace(/^\//, '').replace(/\/$/, '')}`
17+
const matcher = url => `/${url.replace(/^\//, '').replace(/\/$/, '')}`
18+
19+
if (Is.Array(url)) {
20+
return url.map(u => matcher(u))
21+
}
22+
23+
return matcher(url)
1624
}

0 commit comments

Comments
 (0)