@@ -233,6 +233,29 @@ function build (schema, options) {
233
233
ajvInstance = new Ajv ( { ...options . ajv , strictSchema : false , uriResolver : fastUri } )
234
234
ajvFormats ( ajvInstance )
235
235
236
+ const validateDateTimeFormat = ajvFormats . get ( 'date-time' ) . validate
237
+ const validateDateFormat = ajvFormats . get ( 'date' ) . validate
238
+ const validateTimeFormat = ajvFormats . get ( 'time' ) . validate
239
+
240
+ ajvInstance . addKeyword ( {
241
+ keyword : 'fjs_date_type' ,
242
+ validate : ( schema , date ) => {
243
+ if ( date instanceof Date ) {
244
+ return true
245
+ }
246
+ if ( schema === 'date-time' ) {
247
+ return validateDateTimeFormat ( date )
248
+ }
249
+ if ( schema === 'date' ) {
250
+ return validateDateFormat ( date )
251
+ }
252
+ if ( schema === 'time' ) {
253
+ return validateTimeFormat ( date )
254
+ }
255
+ return false
256
+ }
257
+ } )
258
+
236
259
isValidSchema ( schema )
237
260
if ( options . schema ) {
238
261
// eslint-disable-next-line
@@ -367,12 +390,6 @@ function inferTypeByKeyword (schema) {
367
390
return schema . type
368
391
}
369
392
370
- const stringSerializerMap = {
371
- 'date-time' : 'serializer.asDatetime.bind(serializer)' ,
372
- date : 'serializer.asDate.bind(serializer)' ,
373
- time : 'serializer.asTime.bind(serializer)'
374
- }
375
-
376
393
function getStringSerializer ( format , nullable ) {
377
394
switch ( format ) {
378
395
case 'date-time' : return nullable ? 'serializer.asDatetimeNullable.bind(serializer)' : 'serializer.asDatetime.bind(serializer)'
@@ -382,10 +399,6 @@ function getStringSerializer (format, nullable) {
382
399
}
383
400
}
384
401
385
- function getTestSerializer ( format ) {
386
- return stringSerializerMap [ format ]
387
- }
388
-
389
402
function addPatternProperties ( location ) {
390
403
const schema = location . schema
391
404
const pp = schema . patternProperties
@@ -1150,25 +1163,25 @@ function buildValue (laterCode, locationPath, input, location, isArray) {
1150
1163
const locations = dereferenceOfRefs ( location , schema . anyOf ? 'anyOf' : 'oneOf' )
1151
1164
locations . forEach ( ( location , index ) => {
1152
1165
const nestedResult = buildValue ( laterCode , locationPath + 'i' + index , input , location , isArray )
1153
- // We need a test serializer as the String serializer will not work with
1154
- // date/time ajv validations
1155
- // see: https://github.com/fastify/fast-json-stringify/issues/325
1156
- const testSerializer = getTestSerializer ( location . schema . format )
1157
- const testValue = testSerializer !== undefined ? `${ testSerializer } (${ input } , true)` : `${ input } `
1158
-
1159
1166
// Since we are only passing the relevant schema to ajv.validate, it needs to be full dereferenced
1160
1167
// otherwise any $ref pointing to an external schema would result in an error.
1161
1168
// Full dereference of the schema happens as side effect of two functions:
1162
1169
// 1. `dereferenceOfRefs` loops through the `schema.anyOf`` array and replaces any top level reference
1163
1170
// with the actual schema
1164
- // 2. `nested `, through `buildCode`, replaces any reference in object properties with the actual schema
1171
+ // 2. `buildValue `, through `buildCode`, replaces any reference in object properties with the actual schema
1165
1172
// (see https://github.com/fastify/fast-json-stringify/blob/6da3b3e8ac24b1ca5578223adedb4083b7adf8db/index.js#L631)
1166
1173
1174
+ // Ajv does not support js date format. In order to properly validate objects containing a date,
1175
+ // it needs to replace all occurrences of the string date format with a custom keyword fjs_date_type.
1176
+ // (see https://github.com/fastify/fast-json-stringify/pull/441)
1177
+ const extendedSchema = clone ( location . schema )
1178
+ extendDateTimeType ( extendedSchema )
1179
+
1167
1180
const schemaKey = location . schema . $id || randomUUID ( )
1168
- ajvInstance . addSchema ( location . schema , schemaKey )
1181
+ ajvInstance . addSchema ( extendedSchema , schemaKey )
1169
1182
1170
1183
code += `
1171
- ${ index === 0 ? 'if' : 'else if' } (ajv.validate("${ schemaKey } ", ${ testValue } ))
1184
+ ${ index === 0 ? 'if' : 'else if' } (ajv.validate("${ schemaKey } ", ${ input } ))
1172
1185
${ nestedResult . code }
1173
1186
`
1174
1187
laterCode = nestedResult . laterCode
@@ -1261,6 +1274,19 @@ function buildValue (laterCode, locationPath, input, location, isArray) {
1261
1274
return { code, laterCode }
1262
1275
}
1263
1276
1277
+ function extendDateTimeType ( schema ) {
1278
+ if ( schema . type === 'string' && [ 'date-time' , 'date' , 'time' ] . includes ( schema . format ) ) {
1279
+ schema . fjs_date_type = schema . format
1280
+ delete schema . type
1281
+ delete schema . format
1282
+ }
1283
+ for ( const property in schema ) {
1284
+ if ( typeof schema [ property ] === 'object' ) {
1285
+ extendDateTimeType ( schema [ property ] )
1286
+ }
1287
+ }
1288
+ }
1289
+
1264
1290
function isEmpty ( schema ) {
1265
1291
// eslint-disable-next-line
1266
1292
for ( var key in schema ) {
0 commit comments