-
Notifications
You must be signed in to change notification settings - Fork 0
Support nullable #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,9 @@ | ||
| import {JSONSchemaTypeName, LinkedJSONSchema, NormalizedJSONSchema, Parent} from './types/JSONSchema' | ||
| import {appendToDescription, escapeBlockComment, isSchemaLike, justName, toSafeString, traverse} from './utils' | ||
| import {appendToDescription, escapeBlockComment, isSchemaLike, justName, log, toSafeString, traverse} from './utils' | ||
| import {Options} from './' | ||
| import {DereferencedPaths} from './resolver' | ||
| import {isDeepStrictEqual} from 'util' | ||
| import {link} from './linker' | ||
|
|
||
| type Rule = ( | ||
| schema: LinkedJSONSchema, | ||
|
|
@@ -222,6 +223,32 @@ rules.set('Transform const to singleton enum', schema => { | |
| } | ||
| }) | ||
|
|
||
| rules.set('Transform nullable to null type', schema => { | ||
| if (schema.nullable !== true) { | ||
| return | ||
| } | ||
|
|
||
| delete schema.nullable | ||
|
|
||
| if (schema.const !== undefined) { | ||
| if (schema.const !== null) { | ||
| schema.enum = [schema.const, null] | ||
| delete schema.const | ||
| } | ||
| } else if (schema.enum) { | ||
| if (!schema.enum.includes(null)) { | ||
| schema.enum.push(null) | ||
| log('yellow', 'normalizer', 'enum should include null when schema is nullable', schema) | ||
| } | ||
| } else if (schema.type) { | ||
| schema.type = [...[schema.type].flatMap(value => value), 'null'] | ||
| } else if (schema.anyOf) { | ||
| schema.anyOf.push(link({type: 'null'}, schema.anyOf)) | ||
| } else if (schema.oneOf) { | ||
| schema.oneOf.push(link({type: 'null'}, schema.oneOf)) | ||
|
||
| } | ||
| }) | ||
|
|
||
| export function normalize( | ||
| rootSchema: LinkedJSONSchema, | ||
| dereferencedPaths: DereferencedPaths, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| export const input = { | ||
| type: 'object', | ||
| properties: { | ||
| a: { | ||
| const: '', | ||
| nullable: true, | ||
| }, | ||
| b: { | ||
| const: null, | ||
| nullable: true, | ||
| }, | ||
| c: { | ||
| enum: ['a', 'b'], | ||
| nullable: true, | ||
| }, | ||
| d: { | ||
| enum: ['', null], | ||
| nullable: true, | ||
| }, | ||
| e: { | ||
| type: 'string', | ||
| nullable: true, | ||
| }, | ||
| f: { | ||
| type: 'null', | ||
| nullable: true, | ||
| }, | ||
| g: { | ||
| type: 'string', | ||
| const: '', | ||
| nullable: true, | ||
| }, | ||
| h: { | ||
| type: 'string', | ||
| const: null, | ||
| nullable: true, | ||
| }, | ||
|
Comment on lines
+33
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mdmower-csnw: This might not be directly related to the changes in this PR, but I was wondering if there's a warning for a schema like this where the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is not a warning output by this tool. You can test this. |
||
| i: { | ||
| type: 'string', | ||
| enum: ['a', 'b'], | ||
| nullable: true, | ||
| }, | ||
| j: { | ||
| type: 'string', | ||
| enum: ['', null], | ||
| nullable: true, | ||
| }, | ||
| k: { | ||
| type: ['string', 'integer'], | ||
| nullable: true, | ||
| }, | ||
| l: { | ||
| type: ['string', 'null'], | ||
| nullable: true, | ||
| }, | ||
| m: { | ||
| anyOf: [{type: 'string'}, {type: 'integer'}], | ||
| nullable: true, | ||
| }, | ||
| n: { | ||
| anyOf: [{type: 'string'}, {type: 'null'}], | ||
| nullable: true, | ||
| }, | ||
| o: { | ||
| oneOf: [{type: 'string'}, {type: 'integer'}], | ||
| nullable: true, | ||
| }, | ||
| p: { | ||
| oneOf: [{type: 'string'}, {type: 'null'}], | ||
| nullable: true, | ||
| }, | ||
| }, | ||
| additionalProperties: false, | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| { | ||
| "name": "Nullable adds null to type", | ||
| "in": { | ||
| "$id": "a", | ||
| "type": "object", | ||
| "properties": { | ||
| "a": { | ||
| "const": "", | ||
| "nullable": true | ||
| }, | ||
| "b": { | ||
| "const": null, | ||
| "nullable": true | ||
| }, | ||
| "c": { | ||
| "enum": ["a", "b"], | ||
| "nullable": true | ||
| }, | ||
| "d": { | ||
| "enum": ["", null], | ||
| "nullable": true | ||
| }, | ||
| "e": { | ||
| "type": "string", | ||
| "nullable": true | ||
| }, | ||
| "f": { | ||
| "type": "null", | ||
| "nullable": true | ||
| }, | ||
| "g": { | ||
| "type": "string", | ||
| "const": "", | ||
| "nullable": true | ||
| }, | ||
| "h": { | ||
| "type": "string", | ||
| "const": null, | ||
| "nullable": true | ||
| }, | ||
| "i": { | ||
| "type": "string", | ||
| "enum": ["a", "b"], | ||
| "nullable": true | ||
| }, | ||
| "j": { | ||
| "type": "string", | ||
| "enum": ["", null], | ||
| "nullable": true | ||
| }, | ||
| "k": { | ||
| "type": ["string", "integer"], | ||
| "nullable": true | ||
| }, | ||
| "l": { | ||
| "type": ["string", "null"], | ||
| "nullable": true | ||
| }, | ||
| "m": { | ||
| "anyOf": [{"type": "string"}, {"type": "integer"}], | ||
| "nullable": true | ||
| }, | ||
| "n": { | ||
| "anyOf": [{"type": "string"}, {"type": "null"}], | ||
| "nullable": true | ||
| }, | ||
| "o": { | ||
| "oneOf": [{"type": "string"}, {"type": "integer"}], | ||
| "nullable": true | ||
| }, | ||
| "p": { | ||
| "oneOf": [{"type": "string"}, {"type": "null"}], | ||
| "nullable": true | ||
| } | ||
| }, | ||
| "required": [], | ||
| "additionalProperties": false | ||
| }, | ||
| "out": { | ||
| "$id": "a", | ||
| "type": "object", | ||
| "properties": { | ||
| "a": { | ||
| "enum": ["", null] | ||
| }, | ||
| "b": { | ||
| "enum": [null] | ||
| }, | ||
| "c": { | ||
| "enum": ["a", "b", null] | ||
| }, | ||
| "d": { | ||
| "enum": ["", null] | ||
| }, | ||
| "e": { | ||
| "type": ["string", "null"] | ||
| }, | ||
| "f": { | ||
| "type": ["null", "null"] | ||
| }, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mdmower-csnw: The output here and cases
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. I don't think there's need for deduplication of types at this stage since the end user doesn't see it and the TypeScript conversion handles duplicates just fine.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deduplication was trivial so opted to handle it in a5db6da. |
||
| "g": { | ||
| "type": "string", | ||
| "enum": ["", null] | ||
| }, | ||
| "h": { | ||
| "type": "string", | ||
| "enum": [null] | ||
| }, | ||
| "i": { | ||
| "type": "string", | ||
| "enum": ["a", "b", null] | ||
| }, | ||
| "j": { | ||
| "type": "string", | ||
| "enum": ["", null] | ||
| }, | ||
| "k": { | ||
| "type": ["string", "integer", "null"] | ||
| }, | ||
| "l": { | ||
| "type": ["string", "null", "null"] | ||
| }, | ||
| "m": { | ||
| "anyOf": [{"type": "string"}, {"type": "integer"}, {"type": "null"}] | ||
| }, | ||
| "n": { | ||
| "anyOf": [{"type": "string"}, {"type": "null"}, {"type": "null"}] | ||
| }, | ||
| "o": { | ||
| "oneOf": [{"type": "string"}, {"type": "integer"}, {"type": "null"}] | ||
| }, | ||
| "p": { | ||
| "oneOf": [{"type": "string"}, {"type": "null"}, {"type": "null"}] | ||
| } | ||
| }, | ||
| "required": [], | ||
| "additionalProperties": false | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mdmower-csnw:
constcase, too? AJV docs say withnullable: true:I wouldn't be opposed to throwing instead of the warnings, because it's critical that the schema is updated or the type will be at odds with how AJV validates.
What if
constandenumare both defined? Is that case caught earlier by validation?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
constcase is mostly moot because normalizer Transform const to singleton enum runs just before Transform nullable to null type:I included
consthandling just in case normalizers are reordered in the future.schema.enumactually gets wiped out in this case due to the above normalizer. This is a great example of how this tool is not a validator. There's an expectation that the user has performed validation prior to using this tool.That's fine. Can do.
Like mentioned before, this is not a validator. It's a tool that seldom throws.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opted to reorder transforms so that Transform nullable to null type runs before Transform const to singleton enum in a5db6da. Also created a
warning()function that prints to the console even whenVERBOSEis not used.