-
-
Notifications
You must be signed in to change notification settings - Fork 517
/
restful-params.interceptor.ts
118 lines (96 loc) · 3.23 KB
/
restful-params.interceptor.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
107
108
109
110
111
112
113
114
115
116
117
118
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
BadRequestException,
} from '@nestjs/common';
import { isObject } from '@nestjs/common/utils/shared.utils';
import { CrudOptions, FilterParamParsed, ObjectLiteral, RestfulOptions } from '../interfaces';
import { PARSED_OPTIONS_METADATA, PARSED_PARAMS_REQUEST_KEY } from '../constants';
let counter = 0;
export function RestfulParamsInterceptorFactory(crudOptions: CrudOptions): Function {
@Injectable()
class RestfulParamsInterceptor implements NestInterceptor {
async intercept(context: ExecutionContext, next: CallHandler) {
const req = context.switchToHttp().getRequest();
const { parsedParams, options } = await this.transform(req.params);
req[PARSED_PARAMS_REQUEST_KEY] = parsedParams;
req[PARSED_OPTIONS_METADATA] = options;
return next.handle();
}
private async transform(
params: ObjectLiteral,
): Promise<{ options: RestfulOptions; parsedParams: FilterParamParsed }> {
const transformed: any = {};
const keys = isObject(params) ? Object.keys(params) : [];
if (keys.length) {
// parse params
transformed.parsedParams = keys.map(
(key) =>
({
field: key,
operator: 'eq',
value: this.validate(key, crudOptions.params[key], params[key]),
} as FilterParamParsed),
);
} else {
transformed.parsedParams = [];
}
// parseOptions
transformed.options = this.parseOptions(transformed.parsedParams);
return transformed;
}
/**
* Validate params
* @param key
* @param type
* @param value
*/
private validate(key: string, type: 'number' | 'string' | 'uuid', value: string): any {
switch (type) {
// is number
case 'number':
const isNumeric =
'string' === typeof value && !isNaN(parseFloat(value)) && isFinite(value as any);
if (!isNumeric) {
throw new BadRequestException(
`Validation failed. Param '${key}': numeric string is expected`,
);
}
return parseInt(value, 10);
// is UUID
case 'uuid':
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!uuid.test(value)) {
throw new BadRequestException(
`Validation failed. Param '${key}': UUID string is expected`,
);
}
return value;
// is string
default:
return value;
}
}
/**
* Parse options
* @param parsedParams
*/
private parseOptions(parsedParams: FilterParamParsed[]): CrudOptions {
const options = Object.assign({}, crudOptions.options || {}) as RestfulOptions;
const optionsFilter = options.filter || [];
const filter = [...optionsFilter, ...parsedParams];
if (filter.length) {
options.filter = filter;
}
return { ...crudOptions, options };
}
}
// MUST change class name see #25
Object.defineProperty(RestfulParamsInterceptor, 'name', {
value: `RestfulParamsInterceptor${counter++}`,
writable: false,
});
return RestfulParamsInterceptor;
}