Skip to content

Commit 25ae852

Browse files
committed
Add initialValues option to turn on/off value generation
1 parent e70df22 commit 25ae852

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.initialValues", () => {
1704+
it("should omit properties without default values when 'initialValues: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, initialValues: false
1717+
});
1718+
1719+
assert.deepEqual(res, { name: "John Doe", country: "USA" });
1720+
});
1721+
1722+
it("should omit properties without default values when 'initialValues: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, initialValues: 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 'initialValues:false'", () => {
1759+
const node = compileSchema({
1760+
type: "string",
1761+
default: "default value"
1762+
});
1763+
const res = node.getData(undefined, {
1764+
initialValues: false
1765+
});
1766+
assert.deepEqual(res, "default value");
1767+
});
1768+
1769+
it("should handle type string without default value and 'initialValues:false'", () => {
1770+
const node = compileSchema({
1771+
type: "string",
1772+
});
1773+
const res = node.getData(undefined, {initialValues: false});
1774+
assert.deepEqual(res, undefined);
1775+
});
1776+
1777+
it("should handle array without default value and 'initialValues: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, {initialValues: 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+
initialValues?: 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.initialValues),
181+
string: (node, data,opts) => getDefault(node, data, "", opts.initialValues),
182+
number: (node, data,opts) => getDefault(node, data, 0, opts.initialValues),
183+
integer: (node, data,opts) => getDefault(node, data, 0, opts.initialValues),
184+
boolean: (node, data,opts) => getDefault(node, data, false, opts.initialValues),
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){
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, initialValues: 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 && initialValues !== 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
@@ -1614,4 +1614,91 @@ describe("getData", () => {
16141614
assert.deepEqual(res, { valid: "stays", invalid: "not removed" });
16151615
});
16161616
});
1617+
describe("defaultTemplateOptions.initialValues", () => {
1618+
it("should omit properties without default values when 'initialValues:false' even if required", () => {
1619+
const node = compileSchema({
1620+
type: "object",
1621+
required: ["name", "age", "country"],
1622+
properties: {
1623+
name: { type: "string", default: "John Doe" },
1624+
age: { type: "number" },
1625+
country: { type: "string", default: "USA" }
1626+
}
1627+
});
1628+
const res = node.getData(undefined, {
1629+
extendDefaults: false, initialValues: false
1630+
});
1631+
1632+
assert.deepEqual(res, { name: "John Doe", country: "USA" });
1633+
});
1634+
1635+
it("should omit properties without default values when 'initialValues:false' even if required in nested", () => {
1636+
const node = compileSchema({
1637+
type: "object",
1638+
properties: {
1639+
user: {
1640+
type: "object",
1641+
required: ["id", "username"],
1642+
properties: {
1643+
id: { type: "string" },
1644+
username: { type: "string", default: "guest" },
1645+
profile: {
1646+
type: "object",
1647+
properties: {
1648+
bio: { type: "string" },
1649+
theme: { type: "string", default: "light" }
1650+
}
1651+
}
1652+
}
1653+
},
1654+
active: { type: "boolean", default: true }
1655+
}
1656+
});
1657+
const res = node.getData({}, { addOptionalProps: true, initialValues: false});
1658+
1659+
assert.deepEqual(JSON.stringify(res), JSON.stringify({
1660+
user: {
1661+
username: "guest",
1662+
profile: {
1663+
theme: "light"
1664+
}
1665+
},
1666+
active: true
1667+
}));
1668+
});
1669+
1670+
it("should handle type string with default value and 'initialValues:false'", () => {
1671+
const node = compileSchema({
1672+
type: "string",
1673+
default: "default value"
1674+
});
1675+
const res = node.getData(undefined, {
1676+
initialValues: false
1677+
});
1678+
assert.deepEqual(res, "default value");
1679+
});
1680+
1681+
it("should handle type string without default value and 'initialValues:false'", () => {
1682+
const node = compileSchema({
1683+
type: "string",
1684+
});
1685+
const res = node.getData(undefined, {initialValues: false});
1686+
assert.deepEqual(res, undefined);
1687+
});
1688+
1689+
it("should handle array without default value and 'initialValues:false'", () => {
1690+
const node = compileSchema({
1691+
type: "object",
1692+
required: ["title"],
1693+
properties: {
1694+
title: {
1695+
type: "array",
1696+
items: { type: "string" }
1697+
}
1698+
}
1699+
});
1700+
const res = node.getData(undefined, {initialValues: false});
1701+
assert.deepEqual(res, { title: [] });
1702+
});
1703+
});
16171704
});

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+
initialValues?: 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.initialValues),
172+
string: (node, data,opts) => getDefault(node, data, "", opts.initialValues),
173+
number: (node, data,opts) => getDefault(node, data, 0, opts.initialValues),
174+
integer: (node, data,opts) => getDefault(node, data, 0, opts.initialValues),
175+
boolean: (node, data,opts) => getDefault(node, data, false, opts.initialValues),
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){
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, initialValues: 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 && initialValues !== false) {
335342
return initValue;
336343
}
337344
return schema.default;
338-
}
345+
}

0 commit comments

Comments
 (0)