Skip to content

Commit 65def23

Browse files
committed
Added support for nullable values (fixes fastify#152)
1 parent 5160599 commit 65def23

File tree

2 files changed

+163
-12
lines changed

2 files changed

+163
-12
lines changed

index.js

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,40 @@ function build (schema, options) {
7070

7171
var main
7272

73+
// wrapper function for processing nullable values
74+
var processValue = (schema, code, name, nonNullHandler) => {
75+
code += `
76+
function ${name} (input) {
77+
if(input === null && ${schema.nullable} === true) {
78+
return null
79+
} else {
80+
return ${nonNullHandler}(input)
81+
}
82+
}
83+
`
84+
return code
85+
}
86+
7387
switch (schema.type) {
7488
case 'object':
7589
main = '$main'
7690
code = buildObject(schema, code, main, options.schema, schema)
7791
break
7892
case 'string':
79-
main = $asString.name
93+
main = '$main'
94+
code = processValue(schema, code, main, $asString.name)
8095
break
8196
case 'integer':
82-
main = $asInteger.name
97+
main = '$main'
98+
code = processValue(schema, code, main, $asInteger.name)
8399
break
84100
case 'number':
85-
main = $asNumber.name
101+
main = '$main'
102+
code = processValue(schema, code, main, $asNumber.name)
86103
break
87104
case 'boolean':
88-
main = $asBoolean.name
105+
main = '$main'
106+
code = processValue(schema, code, main, $asBoolean.name)
89107
break
90108
case 'null':
91109
main = $asNull.name
@@ -100,7 +118,7 @@ function build (schema, options) {
100118

101119
code += `
102120
;
103-
return ${main}
121+
return ${main}
104122
`
105123

106124
if (options.uglify) {
@@ -481,6 +499,16 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
481499
// see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
482500

483501
var type = schema.properties[key].type
502+
var nullable = schema.properties[key].nullable
503+
504+
code += `
505+
if (obj['${key}'] === null && ${nullable}) {
506+
${addComma}
507+
json += '${$asString(key)}:null'
508+
var rendered = true
509+
} else {
510+
`
511+
484512
if (type === 'number') {
485513
code += `
486514
var t = Number(obj['${key}'])
@@ -547,10 +575,9 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
547575
}
548576

549577
code += `
550-
}
578+
}}
551579
`
552580
})
553-
554581
return { code: code, laterCode: laterCode }
555582
}
556583

@@ -692,9 +719,19 @@ function buildObject (schema, code, name, externalSchema, fullSchema) {
692719
function buildArray (schema, code, name, externalSchema, fullSchema) {
693720
code += `
694721
function ${name} (obj) {
722+
`
723+
if (schema.nullable) {
724+
code += `
725+
if(obj === null) {
726+
return '${$asNull()}';
727+
}
728+
// }
729+
`
730+
// return code
731+
}
732+
code += `
695733
var json = '['
696734
`
697-
698735
var laterCode = ''
699736

700737
if (schema.items['$ref']) {
@@ -798,6 +835,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
798835
}
799836

800837
var type = schema.type
838+
var nullable = schema.nullable === true
801839

802840
var accessor = key.indexOf('[') === 0 ? key : `['${key}']`
803841
switch (type) {
@@ -808,22 +846,22 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
808846
break
809847
case 'string':
810848
code += `
811-
json += $asString(obj${accessor})
849+
json += ${nullable} && obj${accessor} === null ? null : $asString(obj${accessor})
812850
`
813851
break
814852
case 'integer':
815853
code += `
816-
json += $asInteger(obj${accessor})
854+
json += ${nullable} && obj${accessor} === null ? null : $asInteger(obj${accessor})
817855
`
818856
break
819857
case 'number':
820858
code += `
821-
json += $asNumber(obj${accessor})
859+
json += ${nullable} && obj${accessor} === null ? null : $asNumber(obj${accessor})
822860
`
823861
break
824862
case 'boolean':
825863
code += `
826-
json += $asBoolean(obj${accessor})
864+
json += ${nullable} && obj${accessor} === null ? null : $asBoolean(obj${accessor})
827865
`
828866
break
829867
case 'object':

test/nullable.test.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
'use strict'
2+
3+
const test = require('tap').test
4+
5+
const build = require('..')
6+
7+
const nullable = true
8+
9+
let complexObject = {
10+
type: 'object',
11+
properties: {
12+
nullableString: { type: 'string', nullable: nullable },
13+
nullableNumber: { type: 'number', nullable: nullable },
14+
nullableInteger: { type: 'integer', nullable: nullable },
15+
nullableBoolean: { type: 'boolean', nullable: nullable },
16+
nullableNull: { type: 'null', nullable: nullable },
17+
nullableArray: {
18+
type: 'array',
19+
nullable: true,
20+
items: {}
21+
},
22+
nullableObject: { type: 'object', nullable: true },
23+
objectWithNullableProps: {
24+
type: 'object',
25+
nullable: false,
26+
additionalProperties: true,
27+
properties: {
28+
nullableString: { type: 'string', nullable: nullable },
29+
nullableNumber: { type: 'number', nullable: nullable },
30+
nullableInteger: { type: 'integer', nullable: nullable },
31+
nullableBoolean: { type: 'boolean', nullable: nullable },
32+
nullableNull: { type: 'null', nullable: nullable },
33+
nullableArray: {
34+
type: 'array',
35+
nullable: true,
36+
items: {}
37+
}
38+
}
39+
},
40+
arrayWithNullableItems: {
41+
type: 'array',
42+
nullable: true,
43+
items: { type: ['integer', 'string'], nullable: true }
44+
}
45+
}
46+
}
47+
48+
let complexData = {
49+
nullableString: null,
50+
nullableNumber: null,
51+
nullableInteger: null,
52+
nullableBoolean: null,
53+
nullableNull: null,
54+
nullableArray: null,
55+
nullableObject: null,
56+
objectWithNullableProps: {
57+
additionalProp: null,
58+
nullableString: null,
59+
nullableNumber: null,
60+
nullableInteger: null,
61+
nullableBoolean: null,
62+
nullableNull: null,
63+
nullableArray: null
64+
},
65+
arrayWithNullableItems: [ 1, 2, null ]
66+
}
67+
68+
let complexExpectedResult = {
69+
nullableString: null,
70+
nullableNumber: null,
71+
nullableInteger: null,
72+
nullableBoolean: null,
73+
nullableNull: null,
74+
nullableArray: null,
75+
nullableObject: null,
76+
objectWithNullableProps: {
77+
additionalProp: null,
78+
nullableString: null,
79+
nullableNumber: null,
80+
nullableInteger: null,
81+
nullableBoolean: null,
82+
nullableNull: null,
83+
nullableArray: null
84+
},
85+
arrayWithNullableItems: [ 1, 2, null ]
86+
}
87+
88+
let testSet = {
89+
nullableString: [ { type: 'string', nullable: nullable }, null, null ],
90+
nullableNumber: [ { type: 'number', nullable: nullable }, null, null ],
91+
nullableInteger: [ { type: 'integer', nullable: nullable }, null, null ],
92+
nullableBoolean: [ { type: 'boolean', nullable: nullable }, null, null ],
93+
nullableNull: [ { type: 'null', nullable: nullable }, null, null ],
94+
nullableArray: [ {
95+
type: 'array',
96+
nullable: true,
97+
items: {}
98+
}, null, null ],
99+
nullableObject: [ { type: 'object', nullable: true }, null, null ],
100+
complexObject: [ complexObject, complexData, complexExpectedResult ]
101+
}
102+
103+
Object.keys(testSet).forEach(key => {
104+
test(`handle nullable:true in ${key} correctly`, (t) => {
105+
t.plan(1)
106+
107+
let stringifier = build(testSet[key][0])
108+
let data = testSet[key][1]
109+
let expected = testSet[key][2]
110+
let result = stringifier(data)
111+
t.deepEqual(JSON.parse(result), expected)
112+
})
113+
})

0 commit comments

Comments
 (0)