From 44ba41ad14f5d9218b99280a3a18d6cc2e71ea5a Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Sun, 23 Jul 2023 17:53:54 +0300 Subject: [PATCH 1/4] fix: support anyOf inside allOf --- index.js | 22 ++++++++++++++++--- test/allof.test.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index a2d07dcf..52203c0f 100644 --- a/index.js +++ b/index.js @@ -76,6 +76,7 @@ function build (schema, options) { functionsCounter: 0, functionsNamesBySchema: new Map(), options, + wrapObjects: true, refResolver: new RefResolver(), rootSchemaId: schema.$id || randomUUID(), validatorSchemasIds: new Set() @@ -510,13 +511,13 @@ function buildObject (context, location) { functionCode += ` const obj = ${toJSON('input')} - let json = '{' + let json = '${context.wrapObjects ? '{' : ''}' let addComma = false ` functionCode += buildInnerObject(context, location) functionCode += ` - return json + '}' + return json${context.wrapObjects ? ' + \'}\'' : ''} } ` @@ -832,9 +833,19 @@ function buildValue (context, location, input) { let code = '' - if (type === undefined && (schema.anyOf || schema.oneOf)) { + if ((type === undefined || type === 'object') && (schema.anyOf || schema.oneOf)) { context.validatorSchemasIds.add(location.getSchemaId()) + if (schema.type === 'object') { + context.wrapObjects = false + const funcName = buildObject(context, location) + code += ` + json += '{' + json += ${funcName}(${input}) + json += ',' + ` + } + const type = schema.anyOf ? 'anyOf' : 'oneOf' const anyOfLocation = location.getPropertyLocation(type) @@ -856,6 +867,11 @@ function buildValue (context, location, input) { code += ` else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`) ` + if (schema.type === 'object') { + code += ` + json += '}' + ` + } return code } diff --git a/test/allof.test.js b/test/allof.test.js index 942725d5..abb4a9e4 100644 --- a/test/allof.test.js +++ b/test/allof.test.js @@ -220,6 +220,59 @@ test('object with nested allOfs', (t) => { t.equal(value, '{"id1":1,"id2":2,"id3":3}') }) +test('object with anyOf nested inside allOf', (t) => { + t.plan(1) + + const schema = { + title: 'object with anyOf nested inside allOf', + type: 'object', + allOf: [ + { + required: [ + 'id1' + ], + type: 'object', + properties: { + id1: { + type: 'integer' + } + } + }, + { + anyOf: [ + { + type: 'object', + properties: { + id2: { + type: 'integer' + } + }, + required: ['id2'] + }, + { + type: 'object', + properties: { + id3: { + type: 'integer' + } + }, + required: ['id3'] + } + ] + } + ] + } + + const stringify = build(schema) + const value = stringify({ + id1: 1, + id2: 2, + id3: 3, // anyOf should use its first match + id4: 4 // extra prop shouldn't be in result + }) + t.equal(value, '{"id1":1,"id2":2}') +}) + test('object with $ref in allOf', (t) => { t.plan(1) From d38c70cc4883c76d02bb8d79ed9b5c3d96c75d7c Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Mon, 24 Jul 2023 09:50:04 +0300 Subject: [PATCH 2/4] CR --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index 52203c0f..313cd0df 100644 --- a/index.js +++ b/index.js @@ -836,6 +836,7 @@ function buildValue (context, location, input) { if ((type === undefined || type === 'object') && (schema.anyOf || schema.oneOf)) { context.validatorSchemasIds.add(location.getSchemaId()) + const originalWrapObjects = context.wrapObjects if (schema.type === 'object') { context.wrapObjects = false const funcName = buildObject(context, location) @@ -871,6 +872,7 @@ function buildValue (context, location, input) { code += ` json += '}' ` + context.wrapObjects = originalWrapObjects } return code } From 3cfbbdce37c78a8514b5e5ca0aeb5cfe13b4057f Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Mon, 24 Jul 2023 10:43:25 +0300 Subject: [PATCH 3/4] fix nested objects --- index.js | 10 ++++++---- test/allof.test.js | 15 ++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index 313cd0df..430c60ea 100644 --- a/index.js +++ b/index.js @@ -509,15 +509,17 @@ function buildObject (context, location) { // ${schemaRef} ` + const wrapObjects = context.wrapObjects + context.wrapObjects = true functionCode += ` const obj = ${toJSON('input')} - let json = '${context.wrapObjects ? '{' : ''}' + let json = '${wrapObjects ? '{' : ''}' let addComma = false ` functionCode += buildInnerObject(context, location) functionCode += ` - return json${context.wrapObjects ? ' + \'}\'' : ''} + return json${wrapObjects ? ' + \'}\'' : ''} } ` @@ -836,7 +838,6 @@ function buildValue (context, location, input) { if ((type === undefined || type === 'object') && (schema.anyOf || schema.oneOf)) { context.validatorSchemasIds.add(location.getSchemaId()) - const originalWrapObjects = context.wrapObjects if (schema.type === 'object') { context.wrapObjects = false const funcName = buildObject(context, location) @@ -845,6 +846,7 @@ function buildValue (context, location, input) { json += ${funcName}(${input}) json += ',' ` + context.wrapObjects = false } const type = schema.anyOf ? 'anyOf' : 'oneOf' @@ -872,7 +874,7 @@ function buildValue (context, location, input) { code += ` json += '}' ` - context.wrapObjects = originalWrapObjects + context.wrapObjects = true } return code } diff --git a/test/allof.test.js b/test/allof.test.js index abb4a9e4..f87849e9 100644 --- a/test/allof.test.js +++ b/test/allof.test.js @@ -228,13 +228,17 @@ test('object with anyOf nested inside allOf', (t) => { type: 'object', allOf: [ { - required: [ - 'id1' - ], + required: ['id1', 'obj'], type: 'object', properties: { id1: { type: 'integer' + }, + obj: { + type: 'object', + properties: { + nested: { type: 'string' } + } } } }, @@ -268,9 +272,10 @@ test('object with anyOf nested inside allOf', (t) => { id1: 1, id2: 2, id3: 3, // anyOf should use its first match - id4: 4 // extra prop shouldn't be in result + id4: 4, // extra prop shouldn't be in result + obj: { nested: 'yes' } }) - t.equal(value, '{"id1":1,"id2":2}') + t.equal(value, '{"id1":1,"obj":{"nested":"yes"},"id2":2}') }) test('object with $ref in allOf', (t) => { From c9a51877851d76be9c996df01d9666b1f205a17e Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Mon, 24 Jul 2023 11:21:15 +0300 Subject: [PATCH 4/4] fix nested objects --- index.js | 10 +++++----- test/allof.test.js | 25 ++++++++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 430c60ea..b8a0d61e 100644 --- a/index.js +++ b/index.js @@ -509,17 +509,18 @@ function buildObject (context, location) { // ${schemaRef} ` - const wrapObjects = context.wrapObjects - context.wrapObjects = true functionCode += ` const obj = ${toJSON('input')} - let json = '${wrapObjects ? '{' : ''}' + let json = '${context.wrapObjects ? '{' : ''}' let addComma = false ` + const wrapObjects = context.wrapObjects + context.wrapObjects = true functionCode += buildInnerObject(context, location) + context.wrapObjects = wrapObjects functionCode += ` - return json${wrapObjects ? ' + \'}\'' : ''} + return json${context.wrapObjects ? ' + \'}\'' : ''} } ` @@ -846,7 +847,6 @@ function buildValue (context, location, input) { json += ${funcName}(${input}) json += ',' ` - context.wrapObjects = false } const type = schema.anyOf ? 'anyOf' : 'oneOf' diff --git a/test/allof.test.js b/test/allof.test.js index f87849e9..d0b8b74f 100644 --- a/test/allof.test.js +++ b/test/allof.test.js @@ -247,9 +247,7 @@ test('object with anyOf nested inside allOf', (t) => { { type: 'object', properties: { - id2: { - type: 'integer' - } + id2: { type: 'string' } }, required: ['id2'] }, @@ -258,9 +256,22 @@ test('object with anyOf nested inside allOf', (t) => { properties: { id3: { type: 'integer' + }, + nestedObj: { + type: 'object', + properties: { + nested: { type: 'string' } + } } }, required: ['id3'] + }, + { + type: 'object', + properties: { + id4: { type: 'integer' } + }, + required: ['id4'] } ] } @@ -270,12 +281,12 @@ test('object with anyOf nested inside allOf', (t) => { const stringify = build(schema) const value = stringify({ id1: 1, - id2: 2, - id3: 3, // anyOf should use its first match + id3: 3, id4: 4, // extra prop shouldn't be in result - obj: { nested: 'yes' } + obj: { nested: 'yes' }, + nestedObj: { nested: 'yes' } }) - t.equal(value, '{"id1":1,"obj":{"nested":"yes"},"id2":2}') + t.equal(value, '{"id1":1,"obj":{"nested":"yes"},"id3":3,"nestedObj":{"nested":"yes"}}') }) test('object with $ref in allOf', (t) => {