-
Notifications
You must be signed in to change notification settings - Fork 99
/
decorator.ts
106 lines (92 loc) · 3.61 KB
/
decorator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { createParamDecorator, ExecutionContext } from '@nestjs/common'
import type { Request as ExpressRequest } from 'express'
import type { FastifyRequest } from 'fastify'
import { Dictionary, isString, mapKeys, pickBy } from 'lodash'
function isRecord(data: unknown): data is Record<string, unknown> {
return data !== null && typeof data === 'object' && !Array.isArray(data)
}
function isExpressRequest(request: unknown): request is ExpressRequest {
return isRecord(request) && typeof request.get === 'function'
}
export interface PaginateQuery {
page?: number
limit?: number
sortBy?: [string, string][]
searchBy?: string[]
search?: string
filter?: { [column: string]: string | string[] }
select?: string[]
path: string
}
const singleSplit = (param: string, res: any[]) => res.push(param)
const multipleSplit = (param: string, res: any[]) => {
const items = param.split(':')
if (items.length === 2) {
res.push(items as [string, string])
}
}
const multipleAndCommaSplit = (param: string, res: any[]) => {
const set = new Set<string>(param.split(','))
set.forEach((item) => res.push(item))
}
function parseParam<T>(queryParam: unknown, parserLogic: (param: string, res: any[]) => void): T[] | undefined {
const res = []
if (queryParam) {
const params = !Array.isArray(queryParam) ? [queryParam] : queryParam
for (const param of params) {
if (isString(param)) {
parserLogic(param, res)
}
}
}
return res.length ? res : undefined
}
export const Paginate = createParamDecorator((_data: unknown, ctx: ExecutionContext): PaginateQuery => {
let path: string
let query: Record<string, unknown>
switch (ctx.getType()) {
case 'http':
const request: ExpressRequest | FastifyRequest = ctx.switchToHttp().getRequest()
query = request.query as Record<string, unknown>
// Determine if Express or Fastify to rebuild the original url and reduce down to protocol, host and base url
let originalUrl: string
if (isExpressRequest(request)) {
originalUrl = request.protocol + '://' + request.get('host') + request.originalUrl
} else {
originalUrl = request.protocol + '://' + request.hostname + request.url
}
const urlParts = new URL(originalUrl)
path = urlParts.protocol + '//' + urlParts.host + urlParts.pathname
break
case 'ws':
query = ctx.switchToWs().getData()
path = null
break
case 'rpc':
query = ctx.switchToRpc().getData()
path = null
break
}
const searchBy = parseParam<string>(query.searchBy, singleSplit)
const sortBy = parseParam<[string, string]>(query.sortBy, multipleSplit)
const select = parseParam<string>(query.select, multipleAndCommaSplit)
const filter = mapKeys(
pickBy(
query,
(param, name) =>
name.includes('filter.') &&
(isString(param) || (Array.isArray(param) && (param as any[]).every((p) => isString(p))))
) as Dictionary<string | string[]>,
(_param, name) => name.replace('filter.', '')
)
return {
page: query.page ? parseInt(query.page.toString(), 10) : undefined,
limit: query.limit ? parseInt(query.limit.toString(), 10) : undefined,
sortBy,
search: query.search ? query.search.toString() : undefined,
searchBy,
filter: Object.keys(filter).length ? filter : undefined,
select,
path,
}
})