From 5be41cdb9196ea7de9e25c19739eb1f780b64a23 Mon Sep 17 00:00:00 2001 From: Anton Piliugin <87300344+piliugin-anton@users.noreply.github.com> Date: Thu, 7 Jul 2022 16:02:19 +0500 Subject: [PATCH 01/10] fixes #2001 fixes #2001 --- lib/compile/jtd/serialize.ts | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/compile/jtd/serialize.ts b/lib/compile/jtd/serialize.ts index 7ebd26acb..e2e465c95 100644 --- a/lib/compile/jtd/serialize.ts +++ b/lib/compile/jtd/serialize.ts @@ -156,20 +156,20 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v const optProps = keys(optionalProperties) const allProps = allProperties(props.concat(optProps)) let first = !discriminator + const firstProp = gen.let("first", props.length === 0) + for (const key of props) { - serializeProperty(key, properties[key], keyValue(key)) + serializeProperty(key, properties[key], keyValue(key), false) } for (const key of optProps) { const value = keyValue(key) gen.if(and(_`${value} !== undefined`, isOwnProperty(gen, data, key)), () => - serializeProperty(key, optionalProperties[key], value) + serializeProperty(key, optionalProperties[key], value, true) ) } if (schema.additionalProperties) { gen.forIn("key", data, (key) => - gen.if(isAdditional(key, allProps), () => - serializeKeyValue(cxt, key, {}, gen.let("first", first)) - ) + gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp)) ) } @@ -189,9 +189,24 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v return gen.const("value", _`${data}${getProperty(key)}`) } - function serializeProperty(key: string, propSchema: SchemaObject, value: Name): void { - if (first) first = false - else gen.add(N.json, str`,`) + function serializeProperty( + key: string, + propSchema: SchemaObject, + value: Name, + isOptional: boolean + ): void { + if (first) { + first = false + if (isOptional) gen.assign(firstProp, false) + } else if (isOptional) { + gen.if( + firstProp, + () => gen.assign(firstProp, false), + () => gen.add(N.json, str`,`) + ) + } else { + gen.add(N.json, str`,`) + } gen.add(N.json, str`${JSON.stringify(key)}:`) serializeCode({...cxt, schema: propSchema, data: value}) } From 4ba369132c78d455820ae9f6eb04d53972d66f42 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 9 Jul 2022 19:48:14 +0100 Subject: [PATCH 02/10] reduce diff --- lib/compile/jtd/serialize.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/compile/jtd/serialize.ts b/lib/compile/jtd/serialize.ts index e2e465c95..02f947e12 100644 --- a/lib/compile/jtd/serialize.ts +++ b/lib/compile/jtd/serialize.ts @@ -169,7 +169,8 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v } if (schema.additionalProperties) { gen.forIn("key", data, (key) => - gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp)) + gen.if(isAdditional(key, allProps), () => + serializeKeyValue(cxt, key, {}, firstProp)) ) } @@ -189,12 +190,7 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v return gen.const("value", _`${data}${getProperty(key)}`) } - function serializeProperty( - key: string, - propSchema: SchemaObject, - value: Name, - isOptional: boolean - ): void { + function serializeProperty(key: string, propSchema: SchemaObject, value: Name, isOptional: boolean): void { if (first) { first = false if (isOptional) gen.assign(firstProp, false) From 5119767b01beb7a52e52ba0bcdd70dfc3194ffae Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 9 Jul 2022 20:10:28 +0100 Subject: [PATCH 03/10] prettier --- lib/compile/jtd/serialize.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/compile/jtd/serialize.ts b/lib/compile/jtd/serialize.ts index 02f947e12..acdf3c0d8 100644 --- a/lib/compile/jtd/serialize.ts +++ b/lib/compile/jtd/serialize.ts @@ -190,7 +190,12 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v return gen.const("value", _`${data}${getProperty(key)}`) } - function serializeProperty(key: string, propSchema: SchemaObject, value: Name, isOptional: boolean): void { + function serializeProperty( + key: string, + propSchema: SchemaObject, + value: Name, + isOptional: boolean + ): void { if (first) { first = false if (isOptional) gen.assign(firstProp, false) From 2f4939e0eb39b994f7bf50b5636fa2b2fe3e998c Mon Sep 17 00:00:00 2001 From: Anton Piliugin Date: Sun, 10 Jul 2022 00:29:37 +0500 Subject: [PATCH 04/10] JTD only optional properties test --- .../2001_jtd_only_optional_properties.spec.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 spec/issues/2001_jtd_only_optional_properties.spec.ts diff --git a/spec/issues/2001_jtd_only_optional_properties.spec.ts b/spec/issues/2001_jtd_only_optional_properties.spec.ts new file mode 100644 index 000000000..f49f28129 --- /dev/null +++ b/spec/issues/2001_jtd_only_optional_properties.spec.ts @@ -0,0 +1,27 @@ +import type Ajv from "../.." +import _Ajv from "../ajv_jtd" +import * as assert from "assert" + +const PROP_COUNT = 3 + +describe("schema with only optional properties", () => { + let ajv: Ajv + const schema = {optionalProperties: {}} + const data = {} + const invalidData = {} + + before(() => { + ajv = new _Ajv() + for (let i = 0; i < PROP_COUNT; i++) { + const prop = `prop${i}` + schema.optionalProperties[prop] = {type: "uint16"} + data[prop] = i + if (i !== 0) invalidData[prop] = i + } + }) + + it("should correctly compile reference to schema", () => { + assert.strictEqual(ajv.validate(schema, data), true) + assert.strictEqual(ajv.validate(schema, invalidData), false) + }) +}) \ No newline at end of file From 2b6f26f91efe4790b366696e1836abbbd37e93a7 Mon Sep 17 00:00:00 2001 From: Anton Piliugin Date: Sun, 17 Jul 2022 00:01:12 +0500 Subject: [PATCH 05/10] Test case --- lib/compile/jtd/serialize.ts | 3 +-- spec/issues/2001_jtd_only_optional_properties.spec.ts | 11 ++++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compile/jtd/serialize.ts b/lib/compile/jtd/serialize.ts index acdf3c0d8..e2e465c95 100644 --- a/lib/compile/jtd/serialize.ts +++ b/lib/compile/jtd/serialize.ts @@ -169,8 +169,7 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v } if (schema.additionalProperties) { gen.forIn("key", data, (key) => - gen.if(isAdditional(key, allProps), () => - serializeKeyValue(cxt, key, {}, firstProp)) + gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp)) ) } diff --git a/spec/issues/2001_jtd_only_optional_properties.spec.ts b/spec/issues/2001_jtd_only_optional_properties.spec.ts index f49f28129..99cf4d5b1 100644 --- a/spec/issues/2001_jtd_only_optional_properties.spec.ts +++ b/spec/issues/2001_jtd_only_optional_properties.spec.ts @@ -1,14 +1,14 @@ -import type Ajv from "../.." import _Ajv from "../ajv_jtd" import * as assert from "assert" const PROP_COUNT = 3 describe("schema with only optional properties", () => { - let ajv: Ajv + let ajv: InstanceType const schema = {optionalProperties: {}} const data = {} const invalidData = {} + let serialize before(() => { ajv = new _Ajv() @@ -18,10 +18,11 @@ describe("schema with only optional properties", () => { data[prop] = i if (i !== 0) invalidData[prop] = i } + serialize = ajv.compileSerializer(schema) }) it("should correctly compile reference to schema", () => { - assert.strictEqual(ajv.validate(schema, data), true) - assert.strictEqual(ajv.validate(schema, invalidData), false) + assert.strictEqual(serialize(data), '{"prop0":0,"prop1":1,"prop2":2}') + assert.strictEqual(serialize(invalidData), '{"prop1":1,"prop2":2}') }) -}) \ No newline at end of file +}) From cd04ec8f0455472bce8480380efe36040db4e25e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 2 Jan 2023 21:38:38 +0000 Subject: [PATCH 06/10] simplify, avoid run time code changes when possible --- lib/compile/jtd/serialize.ts | 51 ++++++++----------- .../2001_jtd_only_optional_properties.spec.ts | 39 +++++++------- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/lib/compile/jtd/serialize.ts b/lib/compile/jtd/serialize.ts index e2e465c95..1d228826d 100644 --- a/lib/compile/jtd/serialize.ts +++ b/lib/compile/jtd/serialize.ts @@ -117,7 +117,7 @@ function serializeValues(cxt: SerializeCxt): void { gen.add(N.json, str`}`) } -function serializeKeyValue(cxt: SerializeCxt, key: Name, schema: SchemaObject, first: Name): void { +function serializeKeyValue(cxt: SerializeCxt, key: Name, schema: SchemaObject, first?: Name): void { const {gen, data} = cxt addComma(cxt, first) serializeString({...cxt, data: key}) @@ -156,16 +156,20 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v const optProps = keys(optionalProperties) const allProps = allProperties(props.concat(optProps)) let first = !discriminator - const firstProp = gen.let("first", props.length === 0) + let firstProp: Name | undefined for (const key of props) { - serializeProperty(key, properties[key], keyValue(key), false) + if (first) first = false + else gen.add(N.json, str`,`) + serializeProperty(key, properties[key], keyValue(key)) } + if (first) firstProp = gen.let("first", true) for (const key of optProps) { const value = keyValue(key) - gen.if(and(_`${value} !== undefined`, isOwnProperty(gen, data, key)), () => - serializeProperty(key, optionalProperties[key], value, true) - ) + gen.if(and(_`${value} !== undefined`, isOwnProperty(gen, data, key)), () => { + addComma(cxt, firstProp) + serializeProperty(key, optionalProperties[key], value) + }) } if (schema.additionalProperties) { gen.forIn("key", data, (key) => @@ -189,24 +193,7 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v return gen.const("value", _`${data}${getProperty(key)}`) } - function serializeProperty( - key: string, - propSchema: SchemaObject, - value: Name, - isOptional: boolean - ): void { - if (first) { - first = false - if (isOptional) gen.assign(firstProp, false) - } else if (isOptional) { - gen.if( - firstProp, - () => gen.assign(firstProp, false), - () => gen.add(N.json, str`,`) - ) - } else { - gen.add(N.json, str`,`) - } + function serializeProperty(key: string, propSchema: SchemaObject, value: Name): void { gen.add(N.json, str`${JSON.stringify(key)}:`) serializeCode({...cxt, schema: propSchema, data: value}) } @@ -266,10 +253,14 @@ function serializeEmpty({gen, data}: SerializeCxt): void { gen.add(N.json, _`JSON.stringify(${data})`) } -function addComma({gen}: SerializeCxt, first: Name): void { - gen.if( - first, - () => gen.assign(first, false), - () => gen.add(N.json, str`,`) - ) +function addComma({gen}: SerializeCxt, first?: Name): void { + if (first) { + gen.if( + first, + () => gen.assign(first, false), + () => gen.add(N.json, str`,`) + ) + } else { + gen.add(N.json, str`,`) + } } diff --git a/spec/issues/2001_jtd_only_optional_properties.spec.ts b/spec/issues/2001_jtd_only_optional_properties.spec.ts index 99cf4d5b1..20811247f 100644 --- a/spec/issues/2001_jtd_only_optional_properties.spec.ts +++ b/spec/issues/2001_jtd_only_optional_properties.spec.ts @@ -1,28 +1,27 @@ import _Ajv from "../ajv_jtd" import * as assert from "assert" -const PROP_COUNT = 3 +describe("schema with optional/additional properties only", () => { + const ajv = new _Ajv() -describe("schema with only optional properties", () => { - let ajv: InstanceType - const schema = {optionalProperties: {}} - const data = {} - const invalidData = {} - let serialize - - before(() => { - ajv = new _Ajv() - for (let i = 0; i < PROP_COUNT; i++) { - const prop = `prop${i}` - schema.optionalProperties[prop] = {type: "uint16"} - data[prop] = i - if (i !== 0) invalidData[prop] = i + it("should correctly serialize optional properties", () => { + const schema = { + optionalProperties: { + prop0: {type: "uint16"}, + prop1: {type: "uint16"}, + prop2: {type: "uint16"}, + }, + additionalProperties: true, } - serialize = ajv.compileSerializer(schema) - }) + const serialize = ajv.compileSerializer(schema) + test({prop0: 0, prop1: 1, prop2: 2}, '{"prop0":0,"prop1":1,"prop2":2}') + test({prop1: 1, prop2: 2}, '{"prop1":1,"prop2":2}') + test({prop0: 0, prop1: 1, prop2: 2, foo: "bar"}, '{"prop0":0,"prop1":1,"prop2":2,"foo":"bar"}') + test({prop1: 1, prop2: 2, foo: "bar"}, '{"prop1":1,"prop2":2,"foo":"bar"}') + test({foo: "bar"}, '{"foo":"bar"}') - it("should correctly compile reference to schema", () => { - assert.strictEqual(serialize(data), '{"prop0":0,"prop1":1,"prop2":2}') - assert.strictEqual(serialize(invalidData), '{"prop1":1,"prop2":2}') + function test(data: object, json: string) { + assert.strictEqual(serialize(data), json) + } }) }) From 1751b923ca4caf0744812a91cde55b8e6ac916c6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 2 Jan 2023 21:46:30 +0000 Subject: [PATCH 07/10] fix test --- spec/issues/2001_jtd_only_optional_properties.spec.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/issues/2001_jtd_only_optional_properties.spec.ts b/spec/issues/2001_jtd_only_optional_properties.spec.ts index 20811247f..55efcc562 100644 --- a/spec/issues/2001_jtd_only_optional_properties.spec.ts +++ b/spec/issues/2001_jtd_only_optional_properties.spec.ts @@ -14,14 +14,11 @@ describe("schema with optional/additional properties only", () => { additionalProperties: true, } const serialize = ajv.compileSerializer(schema) + const test = (data, json) => assert.strictEqual(serialize(data), json) test({prop0: 0, prop1: 1, prop2: 2}, '{"prop0":0,"prop1":1,"prop2":2}') test({prop1: 1, prop2: 2}, '{"prop1":1,"prop2":2}') test({prop0: 0, prop1: 1, prop2: 2, foo: "bar"}, '{"prop0":0,"prop1":1,"prop2":2,"foo":"bar"}') test({prop1: 1, prop2: 2, foo: "bar"}, '{"prop1":1,"prop2":2,"foo":"bar"}') test({foo: "bar"}, '{"foo":"bar"}') - - function test(data: object, json: string) { - assert.strictEqual(serialize(data), json) - } }) }) From 916baabd3b5f9cba25120b396edef56cedab6614 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 2 Jan 2023 21:49:25 +0000 Subject: [PATCH 08/10] style --- lib/compile/jtd/serialize.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/compile/jtd/serialize.ts b/lib/compile/jtd/serialize.ts index 1d228826d..ea2bc243f 100644 --- a/lib/compile/jtd/serialize.ts +++ b/lib/compile/jtd/serialize.ts @@ -173,7 +173,8 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v } if (schema.additionalProperties) { gen.forIn("key", data, (key) => - gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp)) + gen.if(isAdditional(key, allProps), () => + serializeKeyValue(cxt, key, {}, firstProp)) ) } From 155e6501e36b7045eabda5a6b79c071d705a51d8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 2 Jan 2023 21:51:00 +0000 Subject: [PATCH 09/10] style --- lib/compile/jtd/serialize.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/compile/jtd/serialize.ts b/lib/compile/jtd/serialize.ts index ea2bc243f..169c35b44 100644 --- a/lib/compile/jtd/serialize.ts +++ b/lib/compile/jtd/serialize.ts @@ -174,7 +174,8 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v if (schema.additionalProperties) { gen.forIn("key", data, (key) => gen.if(isAdditional(key, allProps), () => - serializeKeyValue(cxt, key, {}, firstProp)) + serializeKeyValue(cxt, key, {}, firstProp) + ) ) } From dcbfb72c3ae63d537c30dedad83a29278da967de Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 2 Jan 2023 21:53:22 +0000 Subject: [PATCH 10/10] prettier --- lib/compile/jtd/serialize.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/compile/jtd/serialize.ts b/lib/compile/jtd/serialize.ts index 169c35b44..1d228826d 100644 --- a/lib/compile/jtd/serialize.ts +++ b/lib/compile/jtd/serialize.ts @@ -173,9 +173,7 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v } if (schema.additionalProperties) { gen.forIn("key", data, (key) => - gen.if(isAdditional(key, allProps), () => - serializeKeyValue(cxt, key, {}, firstProp) - ) + gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp)) ) }