Skip to content

Commit 19ad300

Browse files
authored
Merge pull request #75 from manisatwick/feature/initialValues
Add useTypeDefaults option to toggle use of type specific initial values
2 parents 4205dcf + c5c0784 commit 19ad300

File tree

4 files changed

+209
-18
lines changed

4 files changed

+209
-18
lines changed

src/draft2019-09/methods/getData.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,4 +1700,94 @@ describe("getData (2019)", () => {
17001700
assert.deepEqual(res, { valid: "stays", invalid: "not removed" });
17011701
});
17021702
});
1703+
describe("defaultTemplateOptions.useTypeDefaults", () => {
1704+
it("should omit properties without default values when 'useTypeDefaults:false' even if required", () => {
1705+
const node = compileSchema({
1706+
$schema: "draft-2019-09",
1707+
type: "object",
1708+
required: ["name", "age", "country"],
1709+
properties: {
1710+
name: { type: "string", default: "John Doe" },
1711+
age: { type: "number" },
1712+
country: { type: "string", default: "USA" }
1713+
}
1714+
});
1715+
const res = node.getData(undefined, {
1716+
extendDefaults: false, useTypeDefaults: false
1717+
});
1718+
1719+
assert.deepEqual(res, { name: "John Doe", country: "USA" });
1720+
});
1721+
1722+
it("should omit properties without default values when 'useTypeDefaults:false' even if required in nested", () => {
1723+
const node = compileSchema({
1724+
$schema: "draft-2019-09",
1725+
type: "object",
1726+
properties: {
1727+
user: {
1728+
type: "object",
1729+
required: ["id", "username"],
1730+
properties: {
1731+
id: { type: "string" },
1732+
username: { type: "string", default: "guest" },
1733+
profile: {
1734+
type: "object",
1735+
properties: {
1736+
bio: { type: "string" },
1737+
theme: { type: "string", default: "light" }
1738+
}
1739+
}
1740+
}
1741+
},
1742+
active: { type: "boolean", default: true }
1743+
}
1744+
});
1745+
const res = node.getData({}, { addOptionalProps: true, useTypeDefaults: false});
1746+
1747+
assert.deepEqual(JSON.stringify(res), JSON.stringify({
1748+
user: {
1749+
username: "guest",
1750+
profile: {
1751+
theme: "light"
1752+
}
1753+
},
1754+
active: true
1755+
}));
1756+
});
1757+
1758+
it("should handle type string with default value and 'useTypeDefaults:false'", () => {
1759+
const node = compileSchema({
1760+
type: "string",
1761+
default: "default value"
1762+
});
1763+
const res = node.getData(undefined, {
1764+
useTypeDefaults: false
1765+
});
1766+
assert.deepEqual(res, "default value");
1767+
});
1768+
1769+
it("should handle type string without default value and 'useTypeDefaults:false'", () => {
1770+
const node = compileSchema({
1771+
type: "string",
1772+
});
1773+
const res = node.getData(undefined, {useTypeDefaults: false});
1774+
assert.deepEqual(res, undefined);
1775+
});
1776+
1777+
it("should handle array without default value and 'useTypeDefaults:false'", () => {
1778+
const node = compileSchema({
1779+
$schema: "draft-2019-09",
1780+
type: "object",
1781+
required: ["title"],
1782+
properties: {
1783+
title: {
1784+
type: "array",
1785+
items: { type: "string" }
1786+
}
1787+
}
1788+
});
1789+
const res = node.getData(undefined, {useTypeDefaults: false});
1790+
assert.deepEqual(res, { title: [] });
1791+
});
1792+
});
17031793
});

src/draft2019-09/methods/getData.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export type TemplateOptions = {
2121
* regardless of minItems settings.
2222
*/
2323
extendDefaults?: boolean;
24+
/**
25+
* Set to false to not use type specific initial values.Defaults to true
26+
*/
27+
useTypeDefaults?: boolean;
2428
/**
2529
* Limits how often a $ref should be followed before aborting. Prevents infinite data-structure.
2630
* Defaults to 1
@@ -173,11 +177,11 @@ export function getData(node: SchemaNode, data?: unknown, opts?: TemplateOptions
173177
}
174178

175179
const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptions) => unknown> = {
176-
null: (node, data) => getDefault(node, data, null),
177-
string: (node, data) => getDefault(node, data, ""),
178-
number: (node, data) => getDefault(node, data, 0),
179-
integer: (node, data) => getDefault(node, data, 0),
180-
boolean: (node, data) => getDefault(node, data, false),
180+
null: (node, data, opts) => getDefault(node, data, null, opts.useTypeDefaults),
181+
string: (node, data,opts) => getDefault(node, data, "", opts.useTypeDefaults),
182+
number: (node, data,opts) => getDefault(node, data, 0, opts.useTypeDefaults),
183+
integer: (node, data,opts) => getDefault(node, data, 0, opts.useTypeDefaults),
184+
boolean: (node, data,opts) => getDefault(node, data, false, opts.useTypeDefaults),
181185
// object: (draft, schema, data: Record<string, unknown> | undefined, pointer: JsonPointer, opts: TemplateOptions) => {
182186
object: (node, data, opts) => {
183187
const schema = node.schema;
@@ -193,7 +197,10 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
193197
const value = data === undefined || input === undefined ? getValue(template, propertyName) : input;
194198
// Omit adding a property if it is not required or optional props should be added
195199
if (value != null || isRequired || opts.addOptionalProps) {
196-
d[propertyName] = propertyNode.getData(value, opts);
200+
const propertyValue = propertyNode.getData(value, opts);
201+
if (propertyValue !== undefined || opts.useTypeDefaults !== false) {
202+
d[propertyName] = propertyValue;
203+
}
197204
}
198205
});
199206
}
@@ -328,15 +335,15 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
328335
}
329336
};
330337

331-
function getDefault({ schema }: SchemaNode, templateValue: any, initValue: any) {
338+
function getDefault({ schema }: SchemaNode, templateValue: any, initValue: any, useTypeDefaults: boolean) {
332339
if (templateValue !== undefined) {
333340
return convertValue(schema.type, templateValue);
334341
} else if (schema.const) {
335342
return schema.const;
336343
} else if (schema.default === undefined && Array.isArray(schema.enum)) {
337344
return schema.enum[0];
338-
} else if (schema.default === undefined) {
345+
} else if (schema.default === undefined && useTypeDefaults !== false) {
339346
return initValue;
340347
}
341348
return schema.default;
342-
}
349+
}

src/methods/getData.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,4 +1656,91 @@ describe("getData", () => {
16561656
assert.deepEqual(res, { valid: "stays", invalid: "not removed" });
16571657
});
16581658
});
1659+
describe("defaultTemplateOptions.useTypeDefaults", () => {
1660+
it("should omit properties without default values when 'useTypeDefaults:false' even if required", () => {
1661+
const node = compileSchema({
1662+
type: "object",
1663+
required: ["name", "age", "country"],
1664+
properties: {
1665+
name: { type: "string", default: "John Doe" },
1666+
age: { type: "number" },
1667+
country: { type: "string", default: "USA" }
1668+
}
1669+
});
1670+
const res = node.getData(undefined, {
1671+
extendDefaults: false, useTypeDefaults: false
1672+
});
1673+
1674+
assert.deepEqual(res, { name: "John Doe", country: "USA" });
1675+
});
1676+
1677+
it("should omit properties without default values when 'useTypeDefaults:false' even if required in nested", () => {
1678+
const node = compileSchema({
1679+
type: "object",
1680+
properties: {
1681+
user: {
1682+
type: "object",
1683+
required: ["id", "username"],
1684+
properties: {
1685+
id: { type: "string" },
1686+
username: { type: "string", default: "guest" },
1687+
profile: {
1688+
type: "object",
1689+
properties: {
1690+
bio: { type: "string" },
1691+
theme: { type: "string", default: "light" }
1692+
}
1693+
}
1694+
}
1695+
},
1696+
active: { type: "boolean", default: true }
1697+
}
1698+
});
1699+
const res = node.getData({}, { addOptionalProps: true, useTypeDefaults: false});
1700+
1701+
assert.deepEqual(JSON.stringify(res), JSON.stringify({
1702+
user: {
1703+
username: "guest",
1704+
profile: {
1705+
theme: "light"
1706+
}
1707+
},
1708+
active: true
1709+
}));
1710+
});
1711+
1712+
it("should handle type string with default value and 'useTypeDefaults:false'", () => {
1713+
const node = compileSchema({
1714+
type: "string",
1715+
default: "default value"
1716+
});
1717+
const res = node.getData(undefined, {
1718+
useTypeDefaults: false
1719+
});
1720+
assert.deepEqual(res, "default value");
1721+
});
1722+
1723+
it("should handle type string without default value and 'useTypeDefaults:false'", () => {
1724+
const node = compileSchema({
1725+
type: "string",
1726+
});
1727+
const res = node.getData(undefined, {useTypeDefaults: false});
1728+
assert.deepEqual(res, undefined);
1729+
});
1730+
1731+
it("should handle array without default value and 'useTypeDefaults:false'", () => {
1732+
const node = compileSchema({
1733+
type: "object",
1734+
required: ["title"],
1735+
properties: {
1736+
title: {
1737+
type: "array",
1738+
items: { type: "string" }
1739+
}
1740+
}
1741+
});
1742+
const res = node.getData(undefined, {useTypeDefaults: false});
1743+
assert.deepEqual(res, { title: [] });
1744+
});
1745+
});
16591746
});

src/methods/getData.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export type TemplateOptions = {
2121
* regardless of minItems settings.
2222
*/
2323
extendDefaults?: boolean;
24+
/**
25+
* Set to false to not use type specific initial values.Defaults to true
26+
*/
27+
useTypeDefaults?: boolean;
2428
/**
2529
* Limits how often a $ref should be followed before aborting. Prevents infinite data-structure.
2630
* Defaults to 1
@@ -164,11 +168,11 @@ export function getData(node: SchemaNode, data?: unknown, opts?: TemplateOptions
164168
}
165169

166170
const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptions) => unknown> = {
167-
null: (node, data) => getDefault(node, data, null),
168-
string: (node, data) => getDefault(node, data, ""),
169-
number: (node, data) => getDefault(node, data, 0),
170-
integer: (node, data) => getDefault(node, data, 0),
171-
boolean: (node, data) => getDefault(node, data, false),
171+
null: (node, data, opts) => getDefault(node, data, null, opts.useTypeDefaults),
172+
string: (node, data,opts) => getDefault(node, data, "", opts.useTypeDefaults),
173+
number: (node, data,opts) => getDefault(node, data, 0, opts.useTypeDefaults),
174+
integer: (node, data,opts) => getDefault(node, data, 0, opts.useTypeDefaults),
175+
boolean: (node, data,opts) => getDefault(node, data, false, opts.useTypeDefaults),
172176
// object: (draft, schema, data: Record<string, unknown> | undefined, pointer: JsonPointer, opts: TemplateOptions) => {
173177
object: (node, data, opts) => {
174178
const schema = node.schema;
@@ -184,7 +188,10 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
184188
const value = data === undefined || input === undefined ? getValue(template, propertyName) : input;
185189
// Omit adding a property if it is not required or optional props should be added
186190
if (value != null || isRequired || opts.addOptionalProps) {
187-
d[propertyName] = propertyNode.getData(value, opts);
191+
const propertyValue = propertyNode.getData(value, opts);
192+
if(propertyValue !== undefined || opts.useTypeDefaults !== false) {
193+
d[propertyName] = propertyValue;
194+
}
188195
}
189196
});
190197
}
@@ -324,15 +331,15 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
324331
}
325332
};
326333

327-
function getDefault({ schema }: SchemaNode, templateValue: any, initValue: any) {
334+
function getDefault({ schema }: SchemaNode, templateValue: any, initValue: any, useTypeDefaults: boolean) {
328335
if (templateValue !== undefined) {
329336
return convertValue(schema.type, templateValue);
330337
} else if (schema.const) {
331338
return schema.const;
332339
} else if (schema.default === undefined && Array.isArray(schema.enum)) {
333340
return schema.enum[0];
334-
} else if (schema.default === undefined) {
341+
} else if (schema.default === undefined && useTypeDefaults !== false) {
335342
return initValue;
336343
}
337344
return schema.default;
338-
}
345+
}

0 commit comments

Comments
 (0)