@@ -2,6 +2,59 @@ import { Request, Response } from 'express';
22import { isKeyInModel } from '../utils' ;
33import { queryTypes , queryTypeKeys } from './query-types' ;
44import { getReadableFields , getReadableRelations } from '../bodyguard' ;
5+ import {
6+ validateSync ,
7+ MetadataStorage ,
8+ getFromContainer
9+ } from 'class-validator' ;
10+
11+ /**
12+ * Get class-validator constraints for a class
13+ * @param someClass BaseModel class(e.g `request.payloadType`)
14+ * @param key (Optional) Key to get constraints for. If unset, constraints for
15+ * all keys will be returned.
16+ */
17+ function getValidationConstraints ( someClass : Function , key ?: string ) {
18+ const container = < MetadataStorage > getFromContainer ( MetadataStorage ) ;
19+ const metadata = container . getTargetValidationMetadatas (
20+ someClass ,
21+ JSON . stringify ( someClass )
22+ ) ;
23+ const properties = container . groupByPropertyName ( metadata ) ;
24+
25+ const validators = Object . keys ( properties ) . reduce ( ( schema , property ) => {
26+ schema [ property ] = properties [
27+ property
28+ ] . reduce ( ( propertySchema , { type, constraints } ) => {
29+ if ( Array . isArray ( constraints ) && constraints . length === 1 ) {
30+ constraints = constraints [ 0 ] ;
31+ }
32+
33+ if ( typeof constraints === 'undefined' ) {
34+ propertySchema [ type ] = true ;
35+ }
36+ else {
37+ propertySchema [ type ] = constraints ;
38+ }
39+
40+ return propertySchema ;
41+ } , { } ) ;
42+
43+ return schema ;
44+ } , { } ) ;
45+
46+ if ( key ) {
47+ if ( key in validators ) {
48+ return validators [ key ] ;
49+ }
50+ else {
51+ return false ;
52+ }
53+ }
54+ else {
55+ return validators ;
56+ }
57+ }
558
659/**
760 * Validate a GET query type
@@ -37,6 +90,10 @@ function queryFieldValidator(
3790 }
3891
3992 if ( ! isKeyInModel ( key , request . payload , response ) ) {
93+ response . validationResponder (
94+ 'Member "' + key + '" does not exist in model'
95+ ) ;
96+
4097 return false ;
4198 }
4299
@@ -55,14 +112,22 @@ function queryFieldValidator(
55112 }
56113 }
57114 else if ( request . query [ type ] instanceof Object ) {
115+ const validators = getValidationConstraints ( request . payloadType ) ;
116+
58117 for ( const key in request . query [ type ] ) {
59- if ( request . query [ type ] [ key ] === undefined ) {
118+ const value = request . query [ type ] [ key ] ;
119+
120+ if ( value === undefined ) {
60121 delete request . query [ type ] [ key ] ;
61122
62123 continue ;
63124 }
64125
65126 if ( ! isKeyInModel ( key , request . payload , response ) ) {
127+ response . validationResponder (
128+ 'Member "' + key + '" does not exist in model'
129+ ) ;
130+
66131 return false ;
67132 }
68133
@@ -72,12 +137,88 @@ function queryFieldValidator(
72137 key && key . indexOf ( '.' ) ? key . split ( '.' ) [ 0 ] : key
73138 )
74139 ) {
75- response . validationResponder (
140+ response . forbiddenResponder (
76141 'Cannot "' + type + '" by member "' + key + '"'
77142 ) ;
78143
79144 return false ;
80145 }
146+
147+ // Cast to int if need be
148+ if (
149+ key in validators &&
150+ 'isInt' in validators [ key ] &&
151+ validators [ key ] [ 'isInt' ] === true
152+ ) {
153+ if ( Array . isArray ( value ) ) {
154+ for ( let i = 0 ; i < value . length ; i ++ ) {
155+ const asInt = parseInt ( value [ i ] , 10 ) ;
156+
157+ if ( ! isNaN ( asInt ) ) {
158+ value [ i ] = asInt ;
159+ }
160+ }
161+ }
162+ else {
163+ const asInt = parseInt ( value , 10 ) ;
164+
165+ if ( ! isNaN ( asInt ) ) {
166+ request . query [ type ] [ key ] = asInt ;
167+ }
168+ }
169+ }
170+ }
171+
172+ if (
173+ type === 'where' ||
174+ type === 'whereAnyOf' ||
175+ type === 'not' ||
176+ type === 'lessThan' ||
177+ type === 'lessThanOrEqual' ||
178+ type === 'greaterThan' ||
179+ type === 'greaterThanOrEqual'
180+ ) {
181+ const testObject = Object . assign (
182+ new request . payloadType ( ) ,
183+ request . query [ type ]
184+ ) ;
185+
186+ const validationErrors = validateSync ( testObject , {
187+ skipMissingProperties : true
188+ } ) ;
189+ if ( validationErrors && validationErrors . length ) {
190+ response . validationResponder ( validationErrors ) ;
191+
192+ return false ;
193+ }
194+ }
195+ }
196+ else if ( type === 'id' ) {
197+ const validator = getValidationConstraints (
198+ request . payloadType ,
199+ 'id'
200+ ) ;
201+
202+ if ( validator && 'isInt' in validator && validator . isInt === true ) {
203+ const asInt = parseInt ( request . query . id , 10 ) ;
204+
205+ if ( ! isNaN ( asInt ) ) {
206+ request . query . id = asInt ;
207+ }
208+ }
209+
210+ const testObject = Object . assign ( new request . payloadType ( ) , {
211+ id : request . query [ type ]
212+ } ) ;
213+
214+ const validationErrors = validateSync ( testObject , {
215+ skipMissingProperties : true
216+ } ) ;
217+
218+ if ( validationErrors && validationErrors . length ) {
219+ response . validationResponder ( validationErrors ) ;
220+
221+ return false ;
81222 }
82223 }
83224 }
0 commit comments