11"use strict" ;
2+ var __importDefault = ( this && this . __importDefault ) || function ( mod ) {
3+ return ( mod && mod . __esModule ) ? mod : { "default" : mod } ;
4+ } ;
25Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
6+ const http_errors_1 = __importDefault ( require ( "http-errors" ) ) ;
7+ const http_status_codes_1 = require ( "http-status-codes" ) ;
38const get = require ( "lodash.get" ) ;
49function niceJoin ( array , lastSeparator = ' and ' , separator = ', ' ) {
510 switch ( array . length ) {
@@ -15,6 +20,35 @@ function niceJoin(array, lastSeparator = ' and ', separator = ', ') {
1520}
1621exports . niceJoin = niceJoin ;
1722exports . validationMessagesFormatters = {
23+ contentType : ( ) => 'only JSON payloads are accepted. Please set the "Content-Type" header to start with "application/json"' ,
24+ json : ( ) => 'the body payload is not a valid JSON' ,
25+ jsonEmpty : ( ) => 'the JSON body payload cannot be empty if the "Content-Type" header is set' ,
26+ missing : ( ) => 'must be present' ,
27+ unknown : ( ) => 'is not a valid property' ,
28+ uuid : ( ) => 'must be a valid GUID (UUID v4)' ,
29+ timestamp : ( ) => 'must be a valid ISO 8601 / RFC 3339 timestamp (example: 2018-07-06T12:34:56Z)' ,
30+ date : ( ) => 'must be a valid ISO 8601 / RFC 3339 date (example: 2018-07-06)' ,
31+ time : ( ) => 'must be a valid ISO 8601 / RFC 3339 time (example: 12:34:56)' ,
32+ hostname : ( ) => 'must be a valid hostname' ,
33+ ipv4 : ( ) => 'must be a valid IPv4' ,
34+ ipv6 : ( ) => 'must be a valid IPv6' ,
35+ paramType : ( type ) => {
36+ switch ( type ) {
37+ case 'integer' :
38+ return 'must be a valid integer number' ;
39+ case 'number' :
40+ return 'must be a valid number' ;
41+ case 'boolean' :
42+ return 'must be a valid boolean (true or false)' ;
43+ case 'object' :
44+ return 'must be a object' ;
45+ case 'array' :
46+ return 'must be an array' ;
47+ default :
48+ return 'must be a string' ;
49+ }
50+ } ,
51+ presentString : ( ) => 'must be a non empty string' ,
1852 minimum : ( min ) => `must be a number greater than or equal to ${ min } ` ,
1953 maximum : ( max ) => `must be a number less than or equal to ${ max } ` ,
2054 minimumProperties ( min ) {
@@ -32,30 +66,8 @@ exports.validationMessagesFormatters = {
3266 enum : ( values ) => `must be one of the following values: ${ niceJoin ( values . map ( ( f ) => `"${ f } "` ) , ' or ' ) } ` ,
3367 pattern : ( pattern ) => `must match pattern "${ pattern . replace ( / \( \? \: / g, '(' ) } "` ,
3468 invalidResponseCode : ( code ) => `This endpoint cannot respond with HTTP status ${ code } .` ,
35- invalidResponse : ( code ) => `The response returned from the endpoint violates its specification for the HTTP status ${ code } .`
36- } ;
37- exports . validationMessages = {
38- contentType : 'only JSON payloads are accepted. Please set the "Content-Type" header to start with "application/json"' ,
39- json : 'the body payload is not a valid JSON' ,
40- jsonEmpty : 'the JSON body payload cannot be empty if the "Content-Type" header is set' ,
41- missing : 'must be present' ,
42- unknown : 'is not a valid property' ,
43- emptyObject : 'cannot be a empty object' ,
44- uuid : 'must be a valid GUID (UUID v4)' ,
45- timestamp : 'must be a valid ISO 8601 / RFC 3339 timestamp (example: 2018-07-06T12:34:56Z)' ,
46- date : 'must be a valid ISO 8601 / RFC 3339 date (example: 2018-07-06)' ,
47- time : 'must be a valid ISO 8601 / RFC 3339 time (example: 12:34:56)' ,
48- hostname : 'must be a valid hostname' ,
49- ip : 'must be a valid IPv4 or IPv6' ,
50- ipv4 : 'must be a valid IPv4' ,
51- ipv6 : 'must be a valid IPv6' ,
52- integer : 'must be a valid integer number' ,
53- number : 'must be a valid number' ,
54- boolean : 'must be a valid boolean (true or false)' ,
55- object : 'must be a object' ,
56- array : 'must be an array' ,
57- string : 'must be a string' ,
58- presentString : 'must be a non empty string'
69+ invalidResponse : ( code ) => `The response returned from the endpoint violates its specification for the HTTP status ${ code } .` ,
70+ invalidFormat : ( format ) => `must match format "${ format } " (format)`
5971} ;
6072function convertValidationErrors ( section , data , validationErrors ) {
6173 const errors = { } ;
@@ -78,14 +90,14 @@ function convertValidationErrors(section, data, validationErrors) {
7890 case 'required' :
7991 case 'dependencies' :
8092 key = e . params . missingProperty ;
81- message = exports . validationMessages . missing ;
93+ message = exports . validationMessagesFormatters . missing ( ) ;
8294 break ;
8395 case 'additionalProperties' :
8496 key = e . params . additionalProperty ;
85- message = exports . validationMessages . unknown ;
97+ message = exports . validationMessagesFormatters . unknown ( ) ;
8698 break ;
8799 case 'type' :
88- message = exports . validationMessages [ e . params . type ] ;
100+ message = exports . validationMessagesFormatters . paramType ( e . params . type ) ;
89101 break ;
90102 case 'minProperties' :
91103 message = exports . validationMessagesFormatters . minimumProperties ( e . params . limit ) ;
@@ -112,7 +124,7 @@ function convertValidationErrors(section, data, validationErrors) {
112124 const pattern = e . params . pattern ;
113125 const value = get ( data , key ) ;
114126 if ( pattern === '.+' && ! value ) {
115- message = exports . validationMessages . presentString ;
127+ message = exports . validationMessagesFormatters . presentString ( ) ;
116128 }
117129 else {
118130 message = exports . validationMessagesFormatters . pattern ( e . params . pattern ) ;
@@ -124,9 +136,7 @@ function convertValidationErrors(section, data, validationErrors) {
124136 if ( reason === 'date-time' ) {
125137 reason = 'timestamp' ;
126138 }
127- message = exports . validationMessagesFormatters [ reason ]
128- ? exports . validationMessagesFormatters [ reason ] ( reason )
129- : exports . validationMessages [ reason ] ;
139+ message = ( exports . validationMessagesFormatters [ reason ] || exports . validationMessagesFormatters . invalidFormat ) ( reason ) ;
130140 break ;
131141 }
132142 // No custom message was found, default to input one replacing the starting verb and adding some path info
@@ -137,14 +147,48 @@ function convertValidationErrors(section, data, validationErrors) {
137147 let property = key
138148 . replace ( / \[ ( \d + ) \] / g, '.$1' ) // Array path
139149 . replace ( / \[ ( [ ^ \] ] + ) \] / g, '.$1' ) ; // Object path
140- if ( ! property ) {
141- property = '$root' ;
142- }
150+ // Remove useless quotes
143151 if ( property . match ( / (?: ^ [ ' " ] ) (?: [ ^ \. ] + ) (?: [ ' " ] $ ) / ) ) {
144152 property = property . substring ( 1 , property . length - 1 ) ;
145153 }
154+ // Fix empty properties
155+ if ( ! property ) {
156+ property = '$root' ;
157+ }
146158 errors [ property ] = message ;
147159 }
148160 return { [ section ] : errors } ;
149161}
150162exports . convertValidationErrors = convertValidationErrors ;
163+ function addResponseValidation ( route ) {
164+ var _a ;
165+ if ( ! ( ( _a = route . schema ) === null || _a === void 0 ? void 0 : _a . response ) ) {
166+ return ;
167+ }
168+ const validators = Object . entries ( route . schema . response ) . reduce ( ( accu , [ code , schema ] ) => {
169+ accu [ code ] = this . responseValidatorSchemaCompiler . compile ( schema ) ;
170+ return accu ;
171+ } , { } ) ;
172+ // Note that this hook is not called for non JSON payloads therefore validation is not possible in such cases
173+ route . preSerialization = async function ( _request , reply , payload ) {
174+ const statusCode = reply . res . statusCode ;
175+ // Never validate error 500
176+ if ( statusCode === http_status_codes_1 . INTERNAL_SERVER_ERROR ) {
177+ return payload ;
178+ }
179+ // No validator, it means the HTTP status is not allowed
180+ const validator = validators [ statusCode ] ;
181+ if ( ! validator ) {
182+ throw http_errors_1 . default ( http_status_codes_1 . INTERNAL_SERVER_ERROR , exports . validationMessagesFormatters . invalidResponseCode ( statusCode ) ) ;
183+ }
184+ // Now validate the payload
185+ const valid = validator ( payload ) ;
186+ if ( ! valid ) {
187+ throw http_errors_1 . default ( http_status_codes_1 . INTERNAL_SERVER_ERROR , exports . validationMessagesFormatters . invalidResponse ( statusCode ) , {
188+ failedValidations : convertValidationErrors ( 'response' , payload , validator . errors )
189+ } ) ;
190+ }
191+ return payload ;
192+ } ;
193+ }
194+ exports . addResponseValidation = addResponseValidation ;
0 commit comments