json-schema-library 11.6.0 → 11.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -191,8 +191,19 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
191
191
  const input = getValue(data, propertyName);
192
192
  const value = data === undefined || input === undefined ? getValue(template, propertyName) : input;
193
193
  // Omit adding a property if it is not required or optional props should be added
194
- if (value != null || isRequired || opts.addOptionalProps) {
194
+ if (value !== undefined || isRequired || opts.addOptionalProps) {
195
195
  const propertyValue = propertyNode.getData(value, opts);
196
+ const dataType = getTypeOf(propertyValue);
197
+ if (
198
+ opts.removeInvalidData &&
199
+ // @attention we do not remove values that might have valid nested values
200
+ dataType !== "object" &&
201
+ dataType !== "array" &&
202
+ !propertyNode.validate(propertyValue).valid
203
+ ) {
204
+ return;
205
+ }
206
+
196
207
  if (propertyValue !== undefined || opts.useTypeDefaults !== false) {
197
208
  d[propertyName] = propertyValue;
198
209
  }
@@ -250,7 +261,28 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
250
261
  }
251
262
  } else {
252
263
  // merge any missing data (additionals) to resulting object
253
- Object.keys(data).forEach((key) => d[key] == null && (d[key] = getValue(data, key)));
264
+ Object.keys(data).forEach((key) => {
265
+ // if we allow additionalProperties, but the current value has a schema that invalidates
266
+ // do not add this value
267
+ const dataType = getTypeOf(key);
268
+ if (
269
+ opts.removeInvalidData &&
270
+ node.properties &&
271
+ node.properties[key] &&
272
+ dataType !== "object" &&
273
+ dataType !== "array"
274
+ ) {
275
+ const propertyNode = node.properties[key];
276
+ const propertyValue = propertyNode.getData(getValue(data, key), opts);
277
+ if (!propertyNode.validate(propertyValue).valid) {
278
+ return;
279
+ }
280
+ }
281
+
282
+ if (d[key] == null) {
283
+ d[key] = getValue(data, key);
284
+ }
285
+ });
254
286
  }
255
287
  }
256
288
 
@@ -1450,6 +1450,79 @@ describe("getData", () => {
1450
1450
  assert.deepEqual(res, {});
1451
1451
  });
1452
1452
 
1453
+ it("should not remove 'null' values if specified in enum", () => {
1454
+ const node = compileSchema({
1455
+ type: "object",
1456
+ additionalProperties: false,
1457
+ properties: {
1458
+ keepA: {
1459
+ type: ["string", "null"],
1460
+ enum: ["Chicago", "Rome", null]
1461
+ },
1462
+ keepB: {
1463
+ type: ["null", "string"],
1464
+ enum: ["Chicago", null, "Rome"]
1465
+ },
1466
+ removeA: {
1467
+ type: ["string"],
1468
+ enum: ["Chicago", "Rome", null]
1469
+ },
1470
+ removeB: {
1471
+ type: ["string", "null"],
1472
+ enum: ["Chicago", "Rome"]
1473
+ }
1474
+ }
1475
+ });
1476
+
1477
+ const res = node.getData(
1478
+ { keepA: null, keepB: null, removeA: null, removeB: null },
1479
+ { removeInvalidData: true }
1480
+ );
1481
+
1482
+ assert.deepEqual(res, { keepA: null, keepB: null });
1483
+ });
1484
+
1485
+ it("should not remove full object when a nested value fails validation", () => {
1486
+ const node = compileSchema({
1487
+ type: "object",
1488
+ additionalProperties: false,
1489
+ properties: {
1490
+ keep: {
1491
+ type: "object",
1492
+ additionalProperties: false,
1493
+ properties: {
1494
+ keep: { type: "number" },
1495
+ remove: { type: "number" }
1496
+ }
1497
+ }
1498
+ }
1499
+ });
1500
+
1501
+ const res = node.getData({ keep: { keep: 9, remove: "a" } }, { removeInvalidData: true });
1502
+
1503
+ assert.deepEqual(res, { keep: { keep: 9 } });
1504
+ });
1505
+
1506
+ it("should remove nested properties that fail validation", () => {
1507
+ const node = compileSchema({
1508
+ type: "object",
1509
+ additionalProperties: false,
1510
+ properties: {
1511
+ keep: {
1512
+ type: "object",
1513
+ properties: {
1514
+ keep: { type: "number" },
1515
+ remove: { type: "number" }
1516
+ }
1517
+ }
1518
+ }
1519
+ });
1520
+
1521
+ const res = node.getData({ keep: { keep: 9, remove: "a" } }, { removeInvalidData: true });
1522
+
1523
+ assert.deepEqual(res, { keep: { keep: 9 } });
1524
+ });
1525
+
1453
1526
  it("should not add optional properties", () => {
1454
1527
  const schema = {
1455
1528
  type: "object",
@@ -190,8 +190,19 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
190
190
  const input = getValue(data, propertyName);
191
191
  const value = data === undefined || input === undefined ? getValue(template, propertyName) : input;
192
192
  // Omit adding a property if it is not required or optional props should be added
193
- if (value != null || isRequired || opts.addOptionalProps) {
193
+ if (value !== undefined || isRequired || opts.addOptionalProps) {
194
194
  const propertyValue = propertyNode.getData(value, opts);
195
+ const dataType = getTypeOf(propertyValue);
196
+ if (
197
+ opts.removeInvalidData &&
198
+ // @attention we do not remove values that might have valid nested values
199
+ dataType !== "object" &&
200
+ dataType !== "array" &&
201
+ !propertyNode.validate(propertyValue).valid
202
+ ) {
203
+ return;
204
+ }
205
+
195
206
  if (propertyValue !== undefined || opts.useTypeDefaults !== false) {
196
207
  d[propertyName] = propertyValue;
197
208
  }
@@ -229,7 +240,6 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
229
240
  });
230
241
  }
231
242
 
232
- // console.log("getData object", data, opts);
233
243
  if (data) {
234
244
  if (
235
245
  opts.removeInvalidData === true &&
@@ -248,7 +258,28 @@ const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptio
248
258
  }
249
259
  } else {
250
260
  // merge any missing data (additionals) to resulting object
251
- Object.keys(data).forEach((key) => d[key] == null && (d[key] = getValue(data, key)));
261
+ Object.keys(data).forEach((key) => {
262
+ // if we allow additionalProperties, but the current value has a schema that invalidates
263
+ // do not add this value
264
+ const dataType = getTypeOf(key);
265
+ if (
266
+ opts.removeInvalidData &&
267
+ node.properties &&
268
+ node.properties[key] &&
269
+ dataType !== "object" &&
270
+ dataType !== "array"
271
+ ) {
272
+ const propertyNode = node.properties[key];
273
+ const propertyValue = propertyNode.getData(getValue(data, key), opts);
274
+ if (!propertyNode.validate(propertyValue).valid) {
275
+ return;
276
+ }
277
+ }
278
+
279
+ if (d[key] == null) {
280
+ d[key] = getValue(data, key);
281
+ }
282
+ });
252
283
  }
253
284
  }
254
285
 
@@ -1,7 +1,8 @@
1
1
  import { isObject } from "../utils/isObject";
2
+ import { hasProperty } from "./hasProperty";
2
3
 
3
4
  export function getValue(data: unknown, key: string | number) {
4
- if (isObject(data)) {
5
+ if (isObject(data) && hasProperty(data, `${key}`)) {
5
6
  return data[key];
6
7
  } else if (Array.isArray(data)) {
7
8
  return data[key as number];