Created
April 18, 2025 00:58
-
-
Save jdesrosiers/dc7f6232d23b3dffdd253288384ebba8 to your computer and use it in GitHub Desktop.
fillDefaults -- Apply default values from a JSON Schema to a JSON instance.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"description": "Hierarchical ambiguous defaults", | |
"schema": { | |
"type": "object", | |
"properties": { | |
"foo": { | |
"$ref": "#/$defs/foo", | |
"default": true | |
} | |
}, | |
"$defs": { | |
"foo": { "default": 42 } | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "given an empty object, the property is added", | |
"instance": {}, | |
"expected": { "foo": true } | |
} | |
] | |
}, | |
{ | |
"description": "Hierarchical ambiguous defaults always chooses the top default regardless of evaluation order", | |
"schema": { | |
"type": "object", | |
"properties": { | |
"foo": { | |
"default": true, | |
"$ref": "#/$defs/foo" | |
} | |
}, | |
"$defs": { | |
"foo": { "default": true } | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "given an empty object, the property is added", | |
"instance": {}, | |
"expected": { "foo": true } | |
} | |
] | |
}, | |
{ | |
"description": "Non-hierarchical ambiguous defaults with allOf", | |
"schema": { | |
"allOf": [ | |
{ "default": 42 }, | |
{ "default": "foo" } | |
] | |
}, | |
"tests": [ | |
{ | |
"description": "it should use the first default encountered", | |
"expected": 42 | |
} | |
] | |
}, | |
{ | |
"description": "Non-hierarchical ambiguous defaults with anyOf", | |
"schema": { | |
"anyOf": [ | |
{ "default": 42 }, | |
{ "default": "foo" } | |
] | |
}, | |
"tests": [ | |
{ | |
"description": "it should use the first default encountered", | |
"expected": 42 | |
} | |
] | |
}, | |
{ | |
"description": "Non-hierarchical ambiguous defaults with anyOf", | |
"schema": { | |
"anyOf": [ | |
{ "default": 42 }, | |
{ "default": "foo" } | |
] | |
}, | |
"tests": [ | |
{ | |
"description": "it should use the first default encountered", | |
"expected": 42 | |
} | |
] | |
}, | |
{ | |
"description": "Non-hierarchical ambiguous defaults with oneOf", | |
"schema": { | |
"oneOf": [ | |
{ "default": 42 } | |
] | |
}, | |
"tests": [ | |
{ | |
"description": "it should use the first default encountered", | |
"expected": 42 | |
} | |
] | |
} | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"description": "Undefined item with a default gets added", | |
"schema": { | |
"type": "array", | |
"prefixItems": [{ "default": 42 }] | |
}, | |
"tests": [ | |
{ | |
"description": "given an empty array, the item is added", | |
"instance": [], | |
"expected": [42] | |
}, | |
{ | |
"description": "given an array with the item already set, the item is unchanged", | |
"instance": ["bar"], | |
"expected": ["bar"] | |
} | |
] | |
}, | |
{ | |
"description": "Undefined nested array with a default gets added", | |
"schema": { | |
"type": "array", | |
"prefixItems": [ | |
{ | |
"type": "array", | |
"prefixItems": [{ "default": 42 }] | |
} | |
] | |
}, | |
"tests": [ | |
{ | |
"description": "given an empty array, nothing is added", | |
"instance": [], | |
"expected": [] | |
}, | |
{ | |
"description": "given an item with an empty array, an item is added", | |
"instance": [[]], | |
"expected": [[42]] | |
}, | |
{ | |
"description": "given an array with the '0' index already set, the item is unchanged", | |
"instance": [[true]], | |
"expected": [[true]] | |
} | |
] | |
}, | |
{ | |
"description": "Add defaults to array items", | |
"schema": { | |
"type": "array", | |
"prefixItems": [true], | |
"items": { | |
"type": "object", | |
"properties": { | |
"foo": { "default": 42 } | |
} | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "Any items without 'foo' should have the default filled in", | |
"instance": [{}, {}, { "foo": true }, {}], | |
"expected": [ | |
{}, | |
{ "foo": 42 }, | |
{ "foo": true }, | |
{ "foo": 42 } | |
] | |
} | |
] | |
}, | |
{ | |
"description": "Add defaults to array additional properties", | |
"schema": { | |
"type": "object", | |
"additionalProperties": { | |
"type": "array", | |
"prefixItems": [{ "default": 42 }] | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "it should fill in the defaults in an additional property", | |
"instance": { "aaa": [] }, | |
"expected": { "aaa": [42] } | |
}, | |
{ | |
"description": "it should not apply the default in an additional property if the value is already present", | |
"instance": { "foo": [24] }, | |
"expected": { "foo": [24] } | |
} | |
] | |
}, | |
{ | |
"description": "Add defaults to array with contains", | |
"schema": { | |
"type": "array", | |
"contains": { | |
"type": "object", | |
"properties": { | |
"foo": { "const": 42 }, | |
"bar": { "default": 24 } | |
}, | |
"required": ["foo", "bar"] | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "it should apply the default to all items in the array", | |
"instance": [{}, { "foo": 42 }, {}], | |
"expected": [{ "bar": 24 }, { "foo": 42, "bar": 24 }, { "bar": 24 }] | |
}, | |
{ | |
"description": "it should fill in the defaults even when validation fails", | |
"instance": [{}, { "foo": true }, {}], | |
"expected": [{ "bar": 24 }, { "foo": true, "bar": 24 }, { "bar": 24 }] | |
}, | |
{ | |
"description": "it should not apply the default if the value is already present", | |
"instance": [{}, { "foo": 42, "bar": false }, {}], | |
"expected": [{ "bar": 24 }, { "foo": 42, "bar": false }, { "bar": 24 }] | |
} | |
] | |
} | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"description": "Conditionally add default property", | |
"schema": { | |
"if": { | |
"type": "object", | |
"properties": { | |
"aaa": { "const": 42 } | |
}, | |
"required": ["aaa"] | |
}, | |
"then": { | |
"properties": { | |
"bbb": { "default": "foo" } | |
} | |
}, | |
"else": { | |
"properties": { | |
"bbb": { "default": "bar" } | |
} | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "given 'aaa': 42, the then default applies", | |
"instance": { "aaa": 42 }, | |
"expected": { | |
"aaa": 42, | |
"bbb": "foo" | |
} | |
}, | |
{ | |
"description": "given no 'aaa' property, the else default applies", | |
"instance": {}, | |
"expected": { "bbb": "bar" } | |
}, | |
{ | |
"description": "given a 'aaa' value other than 42, the else default applies", | |
"instance": { "aaa": 24 }, | |
"expected": { | |
"aaa": 24, | |
"bbb": "bar" | |
} | |
} | |
] | |
}, | |
{ | |
"description": "If can add default property", | |
"schema": { | |
"if": { | |
"type": "object", | |
"properties": { | |
"aaa": { "const": 42 }, | |
"bbb": { "default": "foo" } | |
}, | |
"required": ["aaa"] | |
}, | |
"else": { | |
"properties": { | |
"bbb": { "default": "bar" } | |
} | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "given 'aaa': 42, the then default applies", | |
"instance": { "aaa": 42 }, | |
"expected": { | |
"aaa": 42, | |
"bbb": "foo" | |
} | |
}, | |
{ | |
"description": "given no 'aaa' property, the else default applies", | |
"instance": {}, | |
"expected": { "bbb": "bar" } | |
}, | |
{ | |
"description": "given no 'aaa' property, the else default applies", | |
"instance": { "aaa": 24 }, | |
"expected": { | |
"aaa": 24, | |
"bbb": "bar" | |
} | |
} | |
] | |
}, | |
{ | |
"description": "Conditional defaults with dependentSchemas", | |
"schema": { | |
"type": "object", | |
"properties": { | |
"foo": { "type": "null" }, | |
"bar": { "type": "number" } | |
}, | |
"dependentSchemas": { | |
"foo": { | |
"properties": { | |
"bar": { "default": 42 } | |
} | |
} | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "if the 'foo' property is present the 'bar' property is added", | |
"instance": { "foo": null }, | |
"expected": { | |
"foo": null, | |
"bar": 42 | |
} | |
}, | |
{ | |
"description": "if the 'foo' property is not present the 'bar' property is not added", | |
"instance": {}, | |
"expected": {} | |
} | |
] | |
}, | |
{ | |
"description": "Conditionally add default property using implication", | |
"schema": { | |
"oneOf": [ | |
{ | |
"not": { | |
"type": "object", | |
"properties": { | |
"aaa": { "const": 42 } | |
}, | |
"required": ["aaa"] | |
} | |
}, | |
{ | |
"properties": { | |
"ccc": { "default": "foo" } | |
} | |
} | |
] | |
}, | |
"tests": [ | |
{ | |
"description": "given 'aaa': 42, the default applies", | |
"instance": { | |
"aaa": 42, | |
"bbb": true | |
}, | |
"expected": { | |
"aaa": 42, | |
"bbb": true, | |
"ccc": "foo" | |
} | |
}, | |
{ | |
"description": "given no 'aaa' property, the default doesn't apply", | |
"instance": {}, | |
"expected": {} | |
}, | |
{ | |
"description": "given an 'aaa' value other than 42, the default doesn't apply", | |
"instance": { "aaa": 24 }, | |
"expected": { "aaa": 24 } | |
} | |
] | |
} | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"description": "Defaults can be applied through a dynamic reference", | |
"schema": { | |
"$ref": "main", | |
"$defs": { | |
"foo": { | |
"$dynamicAnchor": "default", | |
"default": 42 | |
}, | |
"main": { | |
"$id": "main", | |
"type": "object", | |
"properties": { | |
"foo": { "$dynamicRef": "default" } | |
} | |
} | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "given an empty object, the property is added", | |
"instance": {}, | |
"expected": { "foo": 42 } | |
}, | |
{ | |
"description": "given an object with property already set, the property is unchanged", | |
"instance": { "foo": "bar" }, | |
"expected": { "foo": "bar" } | |
} | |
] | |
} | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { FLAG } from "@hyperjump/json-schema"; | |
import { compile, getSchema, Validation } from "@hyperjump/json-schema/experimental"; | |
import * as Instance from "@hyperjump/json-schema/instance/experimental"; | |
import { toAbsoluteIri } from "@hyperjump/uri"; | |
/** | |
* @import { Json } from "@hyperjump/json-pointer" | |
* @import { AST, Anchors, Node } from "@hyperjump/json-schema/experimental" | |
*/ | |
/** @type (schemaId: string, instance: Json) => Promise<Json> */ | |
export const fillDefaults = async (schemaId, instance) => { | |
const schemaDocument = await getSchema(schemaId); | |
const { ast, schemaUri } = await compile(schemaDocument); | |
return evaluateSchema(schemaUri, instance, ast, {}); | |
}; | |
/** | |
* @typedef {(keywordValue: any, instance: Json, ast: AST, dynamicAnchors: Anchors) => Json} FillDefaultHandler | |
*/ | |
/** @type FillDefaultHandler */ | |
const evaluateSchema = (/** @type string */ schemaUri, instance, ast, dynamicAnchors) => { | |
if (typeof ast[schemaUri] !== "boolean") { | |
dynamicAnchors = { ...ast.metaData[toAbsoluteIri(schemaUri)].dynamicAnchors, ...dynamicAnchors }; | |
// Always process "default" first | |
for (const [keywordId, , keywordValue] of sortKeywords(ast[schemaUri])) { | |
const handler = getKeywordHandler(keywordId); | |
instance = handler(keywordValue, instance, ast, dynamicAnchors); | |
} | |
} | |
return instance; | |
}; | |
/** @type (nodes: boolean | Node<unknown>[]) => Node<unknown>[] */ | |
const sortKeywords = (nodes) => { | |
/** @type Node<unknown>[] */ | |
const result = []; | |
if (typeof nodes === "boolean") { | |
return result; | |
} | |
for (const node of nodes) { | |
if (node[0] === "https://json-schema.org/keyword/default") { | |
result.unshift(node); | |
} else { | |
result.push(node); | |
} | |
} | |
return result; | |
}; | |
/** @type (value: Json) => value is Record<string, Json> */ | |
const isObject = (value) => typeof value === "object" && !Array.isArray(value) && value !== null; | |
/** @type FillDefaultHandler */ | |
const noopKeywordHandler = (_keywordValue, instance) => instance; | |
/** @type (keywordId: string) => FillDefaultHandler */ | |
const getKeywordHandler = (keywordId) => { | |
const normalizedKeywordId = toAbsoluteIri(keywordId); | |
if (!(normalizedKeywordId in keywordHandlers)) { | |
throw Error(`No 'fillDefaults' handler found for Keyword: ${normalizedKeywordId}`); | |
} | |
return keywordHandlers[normalizedKeywordId]; | |
}; | |
/** @type Record<string, FillDefaultHandler> */ | |
const keywordHandlers = { | |
// Core | |
"https://json-schema.org/keyword/comment": noopKeywordHandler, | |
"https://json-schema.org/keyword/definitions": noopKeywordHandler, | |
"https://json-schema.org/keyword/dynamicRef": (/** @type string */ dynamicAnchor, instance, ast, dynamicAnchors) => { | |
if (!(dynamicAnchor in dynamicAnchors)) { | |
throw Error(`No dynamic anchor found for "${dynamicAnchor}"`); | |
} | |
return evaluateSchema(dynamicAnchors[dynamicAnchor], instance, ast, dynamicAnchors); | |
}, | |
"https://json-schema.org/keyword/ref": evaluateSchema, | |
// Applicators | |
"https://json-schema.org/keyword/additionalProperties": (/** @type [RegExp, string] */ [isDefinedProperty, additionalProperties], instance, ast, dynamicAnchors) => { | |
if (isObject(instance)) { | |
instance = structuredClone(instance); | |
for (const propertyName in instance) { | |
if (!isDefinedProperty.test(propertyName)) { | |
instance[propertyName] = evaluateSchema(additionalProperties, instance[propertyName], ast, dynamicAnchors); | |
} | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/allOf": (/** @type string[] */ allOf, instance, ast, dynamicAnchors) => { | |
return allOf.reduce((instance, schema) => evaluateSchema(schema, instance, ast, dynamicAnchors), instance); | |
}, | |
"https://json-schema.org/keyword/anyOf": (/** @type string[] */ anyOf, instance, ast, dynamicAnchors) => { | |
for (const schema of anyOf) { | |
const instanceWithDefaults = evaluateSchema(schema, instance, ast, dynamicAnchors); | |
if (Validation.interpret(schema, Instance.fromJs(instanceWithDefaults), { ast, dynamicAnchors, errors: [], annotations: [], outputFormat: FLAG })) { | |
instance = instanceWithDefaults; | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/contains": (/** @type {{ contains: string }} */ { contains }, instance, ast, dynamicAnchors) => { | |
if (Array.isArray(instance)) { | |
instance = structuredClone(instance); | |
for (let index = 0; index < instance.length; index++) { | |
const instanceWithDefaults = evaluateSchema(contains, instance[index], ast, dynamicAnchors); | |
instance[index] = instanceWithDefaults; | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/dependentSchemas": (dependentSchemas, instance, ast, dynamicAnchors) => { | |
if (isObject(instance)) { | |
for (const [propertyName, propertySchema] of dependentSchemas) { | |
if (propertyName in /** @type Record<string, Json> */ (instance)) { | |
instance = evaluateSchema(propertySchema, instance, ast, dynamicAnchors); | |
} | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/if": (/** @type string */ ifSchema, instance, ast, dynamicAnchors) => { | |
const instanceWithDefaults = evaluateSchema(ifSchema, instance, ast, dynamicAnchors); | |
return Validation.interpret(ifSchema, Instance.fromJs(instanceWithDefaults), { ast, dynamicAnchors, errors: [], annotations: [], outputFormat: FLAG }) | |
? instanceWithDefaults | |
: instance; | |
}, | |
"https://json-schema.org/keyword/then": (/** @type [string, string] */ [ifSchema, thenSchema], instance, ast, dynamicAnchors) => { | |
const instanceWithDefaults = evaluateSchema(ifSchema, instance, ast, dynamicAnchors); | |
return Validation.interpret(ifSchema, Instance.fromJs(instanceWithDefaults), { ast, dynamicAnchors, errors: [], annotations: [], outputFormat: FLAG }) | |
? evaluateSchema(thenSchema, instanceWithDefaults, ast, dynamicAnchors) | |
: instance; | |
}, | |
"https://json-schema.org/keyword/else": (/** @type [string, string] */ [ifSchema, elseSchema], instance, ast, dynamicAnchors) => { | |
const instanceWithDefaults = evaluateSchema(ifSchema, instance, ast, dynamicAnchors); | |
return !Validation.interpret(ifSchema, Instance.fromJs(instanceWithDefaults), { ast, dynamicAnchors, errors: [], annotations: [], outputFormat: FLAG }) | |
? evaluateSchema(elseSchema, instance, ast, dynamicAnchors) | |
: instance; | |
}, | |
"https://json-schema.org/keyword/items": (/** @type [number, string] */ [numberOfPrefixItems, items], instance, ast, dynamicAnchors) => { | |
if (Array.isArray(instance)) { | |
instance = structuredClone(instance); | |
for (let index = numberOfPrefixItems; index < instance.length; index++) { | |
const instanceWithDefaults = evaluateSchema(items, instance[index], ast, dynamicAnchors); | |
instance[index] = instanceWithDefaults; | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/not": noopKeywordHandler, | |
"https://json-schema.org/keyword/oneOf": (/** @type string[] */ oneOf, instance, ast, dynamicAnchors) => { | |
const originalInstance = instance; | |
let hasMatch = false; | |
for (const schema of oneOf) { | |
const instanceWithDefaults = evaluateSchema(schema, originalInstance, ast, dynamicAnchors); | |
if (Validation.interpret(schema, Instance.fromJs(instanceWithDefaults), { ast, dynamicAnchors, errors: [], annotations: [], outputFormat: FLAG })) { | |
if (hasMatch) { | |
return originalInstance; | |
} | |
hasMatch = true; | |
instance = instanceWithDefaults; | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/patternProperties": (/** @type [RegExp, string][] */ patternProperties, instance, ast, dynamicAnchors) => { | |
if (isObject(instance)) { | |
instance = structuredClone(instance); | |
for (const propertyName in instance) { | |
for (const [pattern, property] of patternProperties) { | |
if (pattern.test(propertyName)) { | |
instance[propertyName] = evaluateSchema(property, instance[propertyName], ast, dynamicAnchors); | |
} | |
} | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/prefixItems": (/** @type string[] */ prefixItems, instance, ast, dynamicAnchors) => { | |
if (Array.isArray(instance)) { | |
instance = structuredClone(instance); | |
for (let index = 0; index < prefixItems.length; index++) { | |
const value = evaluateSchema(prefixItems[index], instance[index], ast, dynamicAnchors); | |
if (value !== undefined) { | |
instance = structuredClone(instance); | |
instance[index] = value; | |
} | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/properties": (/** @type Record<string, string> */ properties, instance, ast, dynamicAnchors) => { | |
if (isObject(instance)) { | |
instance = structuredClone(instance); | |
for (const propertyName in properties) { | |
const value = evaluateSchema(properties[propertyName], instance[propertyName], ast, dynamicAnchors); | |
if (value !== undefined) { | |
instance = structuredClone(instance); | |
instance[propertyName] = value; | |
} | |
} | |
} | |
return instance; | |
}, | |
"https://json-schema.org/keyword/propertyNames": noopKeywordHandler, | |
// Validators | |
"https://json-schema.org/keyword/const": noopKeywordHandler, | |
"https://json-schema.org/keyword/dependentRequired": noopKeywordHandler, | |
"https://json-schema.org/keyword/enum": noopKeywordHandler, | |
"https://json-schema.org/keyword/exclusiveMaximum": noopKeywordHandler, | |
"https://json-schema.org/keyword/exclusiveMinimum": noopKeywordHandler, | |
"https://json-schema.org/keyword/maxItems": noopKeywordHandler, | |
"https://json-schema.org/keyword/maxLength": noopKeywordHandler, | |
"https://json-schema.org/keyword/maxProperties": noopKeywordHandler, | |
"https://json-schema.org/keyword/maximum": noopKeywordHandler, | |
"https://json-schema.org/keyword/minItems": noopKeywordHandler, | |
"https://json-schema.org/keyword/minLength": noopKeywordHandler, | |
"https://json-schema.org/keyword/minProperties": noopKeywordHandler, | |
"https://json-schema.org/keyword/minimum": noopKeywordHandler, | |
"https://json-schema.org/keyword/multipleOf": noopKeywordHandler, | |
"https://json-schema.org/keyword/pattern": noopKeywordHandler, | |
"https://json-schema.org/keyword/required": noopKeywordHandler, | |
"https://json-schema.org/keyword/type": noopKeywordHandler, | |
"https://json-schema.org/keyword/uniqueItems": noopKeywordHandler, | |
// Meta-data | |
"https://json-schema.org/keyword/default": (/** @type Json */ defaultValue, instance) => { | |
return instance === undefined ? defaultValue : instance; | |
}, | |
"https://json-schema.org/keyword/deprecated": noopKeywordHandler, | |
"https://json-schema.org/keyword/description": noopKeywordHandler, | |
"https://json-schema.org/keyword/examples": noopKeywordHandler, | |
"https://json-schema.org/keyword/readOnly": noopKeywordHandler, | |
"https://json-schema.org/keyword/title": noopKeywordHandler, | |
"https://json-schema.org/keyword/writeOnly": noopKeywordHandler, | |
// Format Annotation | |
"https://json-schema.org/keyword/format": noopKeywordHandler, | |
// Format Assertion | |
"https://json-schema.org/keyword/format-assertion": noopKeywordHandler, | |
// Content | |
"https://json-schema.org/keyword/contentEncoding": noopKeywordHandler, | |
"https://json-schema.org/keyword/contentMediaType": noopKeywordHandler, | |
"https://json-schema.org/keyword/contentSchema": noopKeywordHandler, | |
// Unevaluated | |
// "https://json-schema.org/keyword/unevaluatedItems" | |
// "https://json-schema.org/keyword/unevaluatedProperties" | |
// Unknown keywords | |
"https://json-schema.org/keyword/unknown": noopKeywordHandler | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"description": "Undefined property with a default gets added", | |
"schema": { | |
"type": "object", | |
"properties": { | |
"foo": { "default": 42 } | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "given an empty object, the property is added", | |
"instance": {}, | |
"expected": { "foo": 42 } | |
}, | |
{ | |
"description": "given an object with property already set, the property is unchanged", | |
"instance": { "foo": "bar" }, | |
"expected": { "foo": "bar" } | |
} | |
] | |
}, | |
{ | |
"description": "Undefined nested property with a default gets added", | |
"schema": { | |
"type": "object", | |
"properties": { | |
"foo": { | |
"type": "object", | |
"properties": { | |
"bar": { "default": 42 } | |
} | |
} | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "given an empty object, nothing is added", | |
"instance": {}, | |
"expected": {} | |
}, | |
{ | |
"description": "given 'foo' with an empty object, the 'bar' property is added", | |
"instance": { "foo": {} }, | |
"expected": { "foo": { "bar": 42 } } | |
}, | |
{ | |
"description": "given an object with the 'bar' property already set, the property is unchanged", | |
"instance": { "foo": { "bar": true } }, | |
"expected": { "foo": { "bar": true } } | |
} | |
] | |
}, | |
{ | |
"description": "Add defaults to patternProperties objects", | |
"schema": { | |
"type": "object", | |
"patternProperties": { | |
"^a": { | |
"type": "object", | |
"properties": { | |
"foo": { "default": 42 } | |
} | |
} | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "Any properties that match the pattern and don't have 'foo' should have the default filled in", | |
"instance": { | |
"abc": {}, | |
"def": { "foo": true } | |
}, | |
"expected": { | |
"abc": { "foo": 42 }, | |
"def": { "foo": true } | |
} | |
} | |
] | |
}, | |
{ | |
"description": "Add defaults to object additional properties", | |
"schema": { | |
"type": "object", | |
"additionalProperties": { | |
"type": "object", | |
"properties": { | |
"foo": { "default": 42 } | |
} | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "it should fill in the defaults in an additional property", | |
"instance": { | |
"aaa": {} | |
}, | |
"expected": { | |
"aaa": { "foo": 42 } | |
} | |
}, | |
{ | |
"description": "it should not apply the default in an additional property if the value is already present", | |
"instance": { | |
"aaa": { "foo": 24 } | |
}, | |
"expected": { | |
"aaa": { "foo": 24 } | |
} | |
} | |
] | |
} | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"description": "Undefined property with a referenced default gets added", | |
"schema": { | |
"type": "object", | |
"properties": { | |
"foo": { "$ref": "#/$defs/foo" } | |
}, | |
"$defs": { | |
"foo": { "default": 42 } | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "given an empty object, the property is added", | |
"instance": {}, | |
"expected": { "foo": 42 } | |
}, | |
{ | |
"description": "given an object with property already set, the property is unchanged", | |
"instance": { "foo": "bar" }, | |
"expected": { "foo": "bar" } | |
} | |
] | |
} | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"description": "Defaults with not are ignored", | |
"schema": { | |
"not": { | |
"not": { "default": 42 } | |
} | |
}, | |
"tests": [ | |
{ | |
"description": "it should not apply a default" | |
} | |
] | |
} | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"description": "Undefined gets default from the root", | |
"schema": { | |
"default": 42 | |
}, | |
"tests": [ | |
{ | |
"description": "instance is undefined, so default is applied", | |
"expected": 42 | |
}, | |
{ | |
"description": "instance is present, so default is not applied", | |
"instance": "foo", | |
"expected": "foo" | |
}, | |
{ | |
"description": "null is not undefined", | |
"instance": null, | |
"expected": null | |
}, | |
{ | |
"description": "0 is not undefined", | |
"instance": 0, | |
"expected": 0 | |
}, | |
{ | |
"description": "false is not undefined", | |
"instance": false, | |
"expected": false | |
}, | |
{ | |
"description": "empty string is not undefined", | |
"instance": "", | |
"expected": "" | |
}, | |
{ | |
"description": "empty array is not undefined", | |
"instance": [], | |
"expected": [] | |
}, | |
{ | |
"description": "empty object is not undefined", | |
"instance": {}, | |
"expected": {} | |
} | |
] | |
} | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment