From 27446d4cafe12720e7731218fa57002ee5533f00 Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Fri, 30 May 2025 11:26:52 +0200 Subject: [PATCH 1/6] fix(resolver): avoid duplicating enum values when nested in allOf --- package-lock.json | 140 +++++++++--------- package.json | 10 +- .../visitors/all-of.js | 19 ++- src/resolver/specmap/lib/index.js | 15 +- .../schema-object/all-of.js | 43 ++++++ test/resolver/strategies/generic/index.js | 34 +++++ 6 files changed, 183 insertions(+), 78 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0e76489e6..64817fc2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,11 @@ "dependencies": { "@babel/runtime-corejs3": "^7.22.15", "@scarf/scarf": "=1.4.0", - "@swagger-api/apidom-core": ">=1.0.0-beta.39 <1.0.0-rc.0", - "@swagger-api/apidom-error": ">=1.0.0-beta.39 <1.0.0-rc.0", - "@swagger-api/apidom-json-pointer": ">=1.0.0-beta.39 <1.0.0-rc.0", - "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-beta.39 <1.0.0-rc.0", - "@swagger-api/apidom-reference": ">=1.0.0-beta.39 <1.0.0-rc.0", + "@swagger-api/apidom-core": ">=1.0.0-beta.40 <1.0.0-rc.0", + "@swagger-api/apidom-error": ">=1.0.0-beta.40 <1.0.0-rc.0", + "@swagger-api/apidom-json-pointer": ">=1.0.0-beta.40 <1.0.0-rc.0", + "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-beta.40 <1.0.0-rc.0", + "@swagger-api/apidom-reference": ">=1.0.0-beta.40 <1.0.0-rc.0", "@swaggerexpert/cookie": "^2.0.2", "deepmerge": "~4.3.0", "fast-json-patch": "^3.0.0-1", @@ -3440,13 +3440,13 @@ } }, "node_modules/@swagger-api/apidom-ast": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-beta.39.tgz", - "integrity": "sha512-EWeSOtvI8XpbYMRkDyu4qAIlivhcplrskpau2cbrWfXGBjrqEtmHqWlbJ9xoXJbNshbIcZ0Z77QdxicimGjs0w==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-beta.40.tgz", + "integrity": "sha512-3TBsbdFqGmvsyj5JMDXQXiP5jvjZT3KUy+58Bz2ljiZ+P4XX2J9T/3Uj0LZe4/ekf2As1F88PucEhMSR/o9KZQ==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3454,14 +3454,14 @@ } }, "node_modules/@swagger-api/apidom-core": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-beta.39.tgz", - "integrity": "sha512-tYZSVA+uDFvBJmnP104d8Qb/mye8B6ykNviohHAngHsy8ElcOPzSi5GKwwmJgf3taWzipMqWNM0ch5KytbXTqw==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-beta.40.tgz", + "integrity": "sha512-WnC5H1u9D1HPG1PBfkR4A1J7Rv/+hzHY5Z5TjTpO54RxUTkhRCftZ36+OmdAL3rpPdBLgHCX5h8C9wty3TnJrA==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-ast": "^1.0.0-beta.39", - "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-ast": "^1.0.0-beta.40", + "@swagger-api/apidom-error": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "minim": "~0.23.8", "ramda": "~0.30.0", @@ -3471,23 +3471,23 @@ } }, "node_modules/@swagger-api/apidom-error": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-beta.39.tgz", - "integrity": "sha512-vQ3xQaRQGP9kNNBEDcFCmUd2PT9rCtYdkCyqYWZMxHBm5dXSBC/dQaC5VN1DbqQygE16fSQC+c5sqOrwg5d5WQ==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-beta.40.tgz", + "integrity": "sha512-9KHLLrNN/Qiqe/lkLVk0lKpuu4NWuU69NelaFQfrN7Sl8IsCTBlPbu4aGtQOGSM33rc0AclFywPdzngjgBO+0A==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.20.7" } }, "node_modules/@swagger-api/apidom-json-pointer": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-beta.39.tgz", - "integrity": "sha512-gPDNT+MCs/B1XYuNpmnz0rOHQ0ssN9YjVDqeGkX61v03BLJUF/JZKMo3J3FA2mgKb6ap+kRHzpzw5PpHLwRKAw==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-beta.40.tgz", + "integrity": "sha512-77GK2lP9OrYNr7u3hsPLkeReh5iAgxuZw/eboJd0QSX4Ff3Z4zYDrYjkRjv0hvO6e+AedL8JIyNFl5UYF2oh+A==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-core": "^1.0.0-beta.39", - "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.40", + "@swagger-api/apidom-error": "^1.0.0-beta.40", "@swaggerexpert/json-pointer": "^2.10.1" } }, @@ -3516,15 +3516,15 @@ "optional": true }, "node_modules/@swagger-api/apidom-ns-json-schema-2019-09": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2019-09/-/apidom-ns-json-schema-2019-09-1.0.0-beta.39.tgz", - "integrity": "sha512-I/XP4zbrWAmnq2KWPtbb9DKLWgzYFovIiSQOyh47bJqbYgz64/IhoZb/uGihZojVVHSqeeJH9o6JOahqHQzKOw==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2019-09/-/apidom-ns-json-schema-2019-09-1.0.0-beta.40.tgz", + "integrity": "sha512-v8A0YyiaGSTz8RxRbpEaP0+67jTLL4bWKBZXkLX8Z0yJYoyYKFrdYXJY4odYtSJ8I0Ilk0ksFpJYx+NQlZZC4Q==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-core": "^1.0.0-beta.39", - "@swagger-api/apidom-error": "^1.0.0-beta.39", - "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.40", + "@swagger-api/apidom-error": "^1.0.0-beta.40", + "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3532,15 +3532,15 @@ } }, "node_modules/@swagger-api/apidom-ns-json-schema-2020-12": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2020-12/-/apidom-ns-json-schema-2020-12-1.0.0-beta.39.tgz", - "integrity": "sha512-9bpMp96fb76lOqeggtyCU457K/XBLyw3O9fxdVS3Tevhf8P3SJ6QpabmweRb6kFt4vI3+DiBschJGn0iqmlcXw==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2020-12/-/apidom-ns-json-schema-2020-12-1.0.0-beta.40.tgz", + "integrity": "sha512-t7OI4zrNj2YKR++DTHimQ8+Gp7odavGgIMj+hdhP0flQ6OLCRIaTN+eKmlnbCjA5MQBEF4wzGe+JyRl1fZSFeg==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-core": "^1.0.0-beta.39", - "@swagger-api/apidom-error": "^1.0.0-beta.39", - "@swagger-api/apidom-ns-json-schema-2019-09": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.40", + "@swagger-api/apidom-error": "^1.0.0-beta.40", + "@swagger-api/apidom-ns-json-schema-2019-09": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3548,14 +3548,14 @@ } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-4": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-beta.39.tgz", - "integrity": "sha512-F25tm/nwPl1rRnUHzaVw4SAeASodO60oAtWX+GF3K61WEx/Aao4Maldv3CQtAoUk8L0Ml0l1KZL00sgfikwqlw==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-beta.40.tgz", + "integrity": "sha512-+INVqQ7YAH2LY8rpWnXOoXGk49E4UXufQyYumKnXyPaNsigZWkPyMXXx3xgzys+eyUnqwGtf1HrXuVqZalJl1g==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-ast": "^1.0.0-beta.39", - "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ast": "^1.0.0-beta.40", + "@swagger-api/apidom-core": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3563,15 +3563,15 @@ } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-6": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-beta.39.tgz", - "integrity": "sha512-E2fQQHWIRtbM5C1m1EL95MQNDPL98mlgYomPQDDUEFbYrH3u9BQGAgpIu4KuYasKquyuhx9YXqS/jLRhMCRfAQ==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-beta.40.tgz", + "integrity": "sha512-DiEigKi+q+n97KPK5Up2iDh6GT2p9fc0jw0CIf2JntXj6/1Bo0dyg224CRgh2HrnWixwsq9b3Lbwa9eQ0UhfVw==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-core": "^1.0.0-beta.39", - "@swagger-api/apidom-error": "^1.0.0-beta.39", - "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.40", + "@swagger-api/apidom-error": "^1.0.0-beta.40", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3579,15 +3579,15 @@ } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-7": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-beta.39.tgz", - "integrity": "sha512-mhzb7n3pm0yfYuM9bZowYMp6L61Cz+HrbjBowUIt5iOMMAATQd6x209pj81hnSmgHmEJCgv+8IO9dvweme698A==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-beta.40.tgz", + "integrity": "sha512-/n0HIlshe8CjDIxMt3hATmXGqMYTbTMfder7KUcdiAaO/CQ6fVs6Zr/LxlWRowlSzBSLxKjM++/YSaLmWg0x+g==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-core": "^1.0.0-beta.39", - "@swagger-api/apidom-error": "^1.0.0-beta.39", - "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.40", + "@swagger-api/apidom-error": "^1.0.0-beta.40", + "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3603,15 +3603,15 @@ "optional": true }, "node_modules/@swagger-api/apidom-ns-openapi-3-0": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-beta.39.tgz", - "integrity": "sha512-lvNlUtCmyHH8+52qOhgXXdzy4HEYA+t7xnFNvDb6dtP+epXCexux3uRs8+xEYBHo/WqUGzjdwd0qKFRgyP7Lrw==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-beta.40.tgz", + "integrity": "sha512-kVYTIAiifrmj9aMG2oRDjxPom/vFAYWbMPq5zETI53diEtLwY36pU+ST6/yhjmtnA3GDSSH/lAQjM7wIIpsmHg==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-core": "^1.0.0-beta.39", - "@swagger-api/apidom-error": "^1.0.0-beta.39", - "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.40", + "@swagger-api/apidom-error": "^1.0.0-beta.40", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3619,17 +3619,17 @@ } }, "node_modules/@swagger-api/apidom-ns-openapi-3-1": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-beta.39.tgz", - "integrity": "sha512-sXMJxTGL2F36Uyv9iqvPwvzsD5NJM/dJ52tUuiJP8h4RqXwjrOC86hqf1/Xk/rxgpZShhW4PNEqifvPq/Mto3w==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-beta.40.tgz", + "integrity": "sha512-uDaIijFgL5fkKoSNMAJQUweaHCidJGr3CUixX0d7gT0I13GIH3miRaa/ISm+siaUnBxqTfON9XtE1Krs7pRPCg==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-ast": "^1.0.0-beta.39", - "@swagger-api/apidom-core": "^1.0.0-beta.39", - "@swagger-api/apidom-json-pointer": "^1.0.0-beta.39", - "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.39", - "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.39", + "@swagger-api/apidom-ast": "^1.0.0-beta.40", + "@swagger-api/apidom-core": "^1.0.0-beta.40", + "@swagger-api/apidom-json-pointer": "^1.0.0-beta.40", + "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.40", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3749,14 +3749,14 @@ "optional": true }, "node_modules/@swagger-api/apidom-reference": { - "version": "1.0.0-beta.39", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-beta.39.tgz", - "integrity": "sha512-PrV2/3Z6XGJPj4fv1JazY1dKjlnAg/BN22UQdUOzA5/A0TkfbImt8uVQuVzQSL2P8RA6G9TDsdpOalj80N47rw==", + "version": "1.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-beta.40.tgz", + "integrity": "sha512-9EEFuKy0/yoiTWXvzAoKlDke3UydXqqaDuzIZnoaBPjE7R8REPCHvfSHKtz4b8U6eyhwntV90YBlDmbAtxhS9A==", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", - "@swagger-api/apidom-core": "^1.0.0-beta.39", - "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.40", + "@swagger-api/apidom-error": "^1.0.0-beta.40", "@types/ramda": "~0.30.0", "axios": "^1.9.0", "minimatch": "^7.4.3", diff --git a/package.json b/package.json index 641c51ddc..b97436d28 100644 --- a/package.json +++ b/package.json @@ -74,11 +74,11 @@ "dependencies": { "@babel/runtime-corejs3": "^7.22.15", "@scarf/scarf": "=1.4.0", - "@swagger-api/apidom-core": ">=1.0.0-beta.39 <1.0.0-rc.0", - "@swagger-api/apidom-error": ">=1.0.0-beta.39 <1.0.0-rc.0", - "@swagger-api/apidom-json-pointer": ">=1.0.0-beta.39 <1.0.0-rc.0", - "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-beta.39 <1.0.0-rc.0", - "@swagger-api/apidom-reference": ">=1.0.0-beta.39 <1.0.0-rc.0", + "@swagger-api/apidom-core": ">=1.0.0-beta.40 <1.0.0-rc.0", + "@swagger-api/apidom-error": ">=1.0.0-beta.40 <1.0.0-rc.0", + "@swagger-api/apidom-json-pointer": ">=1.0.0-beta.40 <1.0.0-rc.0", + "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-beta.40 <1.0.0-rc.0", + "@swagger-api/apidom-reference": ">=1.0.0-beta.40 <1.0.0-rc.0", "@swaggerexpert/cookie": "^2.0.2", "deepmerge": "~4.3.0", "fast-json-patch": "^3.0.0-1", diff --git a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js index f7782aeb1..a843119e8 100644 --- a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js +++ b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js @@ -1,5 +1,6 @@ -import { isArrayElement, deepmerge } from '@swagger-api/apidom-core'; +import { ArrayElement, isArrayElement, deepmerge, toValue } from '@swagger-api/apidom-core'; import { isSchemaElement } from '@swagger-api/apidom-ns-openapi-3-1'; +import { uniqWith, equals } from 'ramda'; import toPath from '../utils/to-path.js'; @@ -37,7 +38,21 @@ class AllOfVisitor { while (schemaElement.hasKey('allOf')) { const { allOf } = schemaElement; schemaElement.remove('allOf'); - const allOfMerged = deepmerge.all([...allOf.content, schemaElement]); + const allOfMerged = deepmerge.all([...allOf.content, schemaElement], { + customMerge: (keyElement) => { + if (toValue(keyElement) === 'enum') { + return (targetElement, sourceElement) => { + if (isArrayElement(targetElement) && isArrayElement(sourceElement)) { + return new ArrayElement( + uniqWith(equals)([...toValue(targetElement), ...toValue(sourceElement)]) + ); + } + return deepmerge(targetElement, sourceElement); + }; + } + return deepmerge; + }, + }); /** * If there was not an original $$ref value, make sure to remove diff --git a/src/resolver/specmap/lib/index.js b/src/resolver/specmap/lib/index.js index 159318864..3d67b74fa 100644 --- a/src/resolver/specmap/lib/index.js +++ b/src/resolver/specmap/lib/index.js @@ -1,5 +1,6 @@ import * as jsonPatch from 'fast-json-patch'; import deepmerge from 'deepmerge'; +import { uniqWith, equals } from 'ramda'; export default { add, @@ -39,7 +40,19 @@ function applyPatch(obj, patch, opts) { jsonPatch.applyPatch(obj, [replace(patch.path, newValue)]); } else if (patch.op === 'mergeDeep') { const currentValue = getInByJsonPath(obj, patch.path); - const newValue = deepmerge(currentValue, patch.value); + const newValue = deepmerge(currentValue, patch.value, { + customMerge: (key) => { + if (key === 'enum') { + return (targetElement, sourceElement) => { + if (Array.isArray(targetElement) && Array.isArray(sourceElement)) { + return uniqWith(equals)([...targetElement, ...sourceElement]); + } + return deepmerge(targetElement, sourceElement); + }; + } + return undefined; + }, + }); obj = jsonPatch.applyPatch(obj, [replace(patch.path, newValue)]).newDocument; } else if (patch.op === 'add' && patch.path === '' && isObject(patch.value)) { // { op: 'add', path: '', value: { a: 1, b: 2 }} diff --git a/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js b/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js index e4cedcb57..9321b7d53 100644 --- a/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js +++ b/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js @@ -1018,6 +1018,49 @@ describe('dereference', () => { }, }); }); + + test('should not duplicate `enum` values nested in `allOf`', async () => { + const spec = OpenApi3_1Element.refract({ + openapi: '3.1.0', + components: { + schemas: { + one: { + allOf: [ + { + properties: { + foo: { enum: [1, 2, 3, 4] }, + bar: { enum: [{ enum: [1, 2, 3] }, { enum: [1, 2, 3, 4] }] }, + }, + }, + { + properties: { + foo: { enum: [1, 2, 3, 5] }, + bar: { enum: [{ enum: [1, 2, 3, 4] }] }, + }, + }, + ], + }, + }, + }, + }); + const dereferenced = await dereferenceApiDOM(spec, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + + expect(toValue(dereferenced)).toEqual({ + openapi: '3.1.0', + components: { + schemas: { + one: { + properties: { + foo: { enum: [1, 2, 3, 4, 5] }, + bar: { enum: [{ enum: [1, 2, 3] }, { enum: [1, 2, 3, 4] }] }, + }, + }, + }, + }, + }); + }); }); }); }); diff --git a/test/resolver/strategies/generic/index.js b/test/resolver/strategies/generic/index.js index 6ce0f1721..e9547dd8d 100644 --- a/test/resolver/strategies/generic/index.js +++ b/test/resolver/strategies/generic/index.js @@ -890,6 +890,40 @@ describe('resolver', () => { }); }); + test('should not duplicate `enum` values nested in `allOf`', () => { + // Given + const spec = { + allOf: [ + { + properties: { + foo: { enum: [1, 2, 3, 4] }, + bar: { enum: [{ enum: [1, 2, 3] }, { enum: [1, 2, 3, 4] }] }, + }, + }, + { + properties: { + foo: { enum: [1, 2, 3, 5] }, + bar: { enum: [{ enum: [1, 2, 3, 4] }] }, + }, + }, + ], + }; + + // When + return Swagger.resolve({ spec }).then(handleResponse); + + // Then + function handleResponse(obj) { + expect(obj.errors).toEqual([]); + expect(obj.spec).toEqual({ + properties: { + foo: { enum: [1, 2, 3, 4, 5] }, + bar: { enum: [{ enum: [1, 2, 3] }, { enum: [1, 2, 3, 4] }] }, + }, + }); + } + }); + test('should not throw errors on resvered-keywords in freely-named-fields', () => { // Given const ReservedKeywordSpec = jsYaml.load( From d9697ce75b60b9dfc26e77cb88e1442eb086c198 Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Fri, 30 May 2025 15:52:30 +0200 Subject: [PATCH 2/6] fix: avoid processing complex enum values in OpenAPI 3.1.x --- .../visitors/all-of.js | 26 +++++++++++++++---- .../schema-object/all-of.js | 8 +++--- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js index a843119e8..8d1fde525 100644 --- a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js +++ b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js @@ -1,6 +1,11 @@ -import { ArrayElement, isArrayElement, deepmerge, toValue } from '@swagger-api/apidom-core'; +import { + ArrayElement, + isArrayElement, + isPrimitiveElement, + deepmerge, + toValue, +} from '@swagger-api/apidom-core'; import { isSchemaElement } from '@swagger-api/apidom-ns-openapi-3-1'; -import { uniqWith, equals } from 'ramda'; import toPath from '../utils/to-path.js'; @@ -43,9 +48,20 @@ class AllOfVisitor { if (toValue(keyElement) === 'enum') { return (targetElement, sourceElement) => { if (isArrayElement(targetElement) && isArrayElement(sourceElement)) { - return new ArrayElement( - uniqWith(equals)([...toValue(targetElement), ...toValue(sourceElement)]) - ); + const primitiveElements = new ArrayElement([ + ...targetElement.findElements(isPrimitiveElement), + ...sourceElement.findElements(isPrimitiveElement), + ]); + const nonPrimitiveElements = new ArrayElement([ + ...targetElement.findElements((element) => !isPrimitiveElement(element)), + ...sourceElement.findElements((element) => !isPrimitiveElement(element)), + ]); + + const uniquePrimitiveElements = new ArrayElement([ + ...new Set(toValue(primitiveElements)), + ]); + + return uniquePrimitiveElements.concat(nonPrimitiveElements); } return deepmerge(targetElement, sourceElement); }; diff --git a/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js b/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js index 9321b7d53..c3925c2bb 100644 --- a/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js +++ b/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js @@ -1034,7 +1034,7 @@ describe('dereference', () => { }, { properties: { - foo: { enum: [1, 2, 3, 5] }, + foo: { enum: [1, 2, 3, 5, { enum: [1, 2, 3] }] }, bar: { enum: [{ enum: [1, 2, 3, 4] }] }, }, }, @@ -1053,8 +1053,10 @@ describe('dereference', () => { schemas: { one: { properties: { - foo: { enum: [1, 2, 3, 4, 5] }, - bar: { enum: [{ enum: [1, 2, 3] }, { enum: [1, 2, 3, 4] }] }, + foo: { enum: [1, 2, 3, 4, 5, { enum: [1, 2, 3] }] }, + bar: { + enum: [{ enum: [1, 2, 3] }, { enum: [1, 2, 3, 4] }, { enum: [1, 2, 3, 4] }], + }, }, }, }, From 16926fdbc07bd3a1efa596b6310adb46e4600135 Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Mon, 2 Jun 2025 08:34:39 +0200 Subject: [PATCH 3/6] fix: align generic strategy with strategy for OpenAPI 3.1.x --- src/resolver/specmap/lib/index.js | 3 +-- .../openapi-3-1-swagger-client/schema-object/all-of.js | 2 +- test/resolver/strategies/generic/index.js | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/resolver/specmap/lib/index.js b/src/resolver/specmap/lib/index.js index 3d67b74fa..35718f8cd 100644 --- a/src/resolver/specmap/lib/index.js +++ b/src/resolver/specmap/lib/index.js @@ -1,6 +1,5 @@ import * as jsonPatch from 'fast-json-patch'; import deepmerge from 'deepmerge'; -import { uniqWith, equals } from 'ramda'; export default { add, @@ -45,7 +44,7 @@ function applyPatch(obj, patch, opts) { if (key === 'enum') { return (targetElement, sourceElement) => { if (Array.isArray(targetElement) && Array.isArray(sourceElement)) { - return uniqWith(equals)([...targetElement, ...sourceElement]); + return [...new Set([...targetElement, ...sourceElement])]; } return deepmerge(targetElement, sourceElement); }; diff --git a/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js b/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js index c3925c2bb..7b69d2154 100644 --- a/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js +++ b/test/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/all-of.js @@ -1019,7 +1019,7 @@ describe('dereference', () => { }); }); - test('should not duplicate `enum` values nested in `allOf`', async () => { + test('should not duplicate primitive `enum` values nested in `allOf`', async () => { const spec = OpenApi3_1Element.refract({ openapi: '3.1.0', components: { diff --git a/test/resolver/strategies/generic/index.js b/test/resolver/strategies/generic/index.js index e9547dd8d..6e9c1563b 100644 --- a/test/resolver/strategies/generic/index.js +++ b/test/resolver/strategies/generic/index.js @@ -890,7 +890,7 @@ describe('resolver', () => { }); }); - test('should not duplicate `enum` values nested in `allOf`', () => { + test('should not duplicate primitive `enum` values nested in `allOf`', () => { // Given const spec = { allOf: [ @@ -918,7 +918,7 @@ describe('resolver', () => { expect(obj.spec).toEqual({ properties: { foo: { enum: [1, 2, 3, 4, 5] }, - bar: { enum: [{ enum: [1, 2, 3] }, { enum: [1, 2, 3, 4] }] }, + bar: { enum: [{ enum: [1, 2, 3] }, { enum: [1, 2, 3, 4] }, { enum: [1, 2, 3, 4] }] }, }, }); } From 9ce8e41f16fd25a44cce6aec0583050dd580d42c Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Mon, 2 Jun 2025 14:30:01 +0200 Subject: [PATCH 4/6] fix: change comparison logic --- .../visitors/all-of.js | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js index 8d1fde525..aabeeb547 100644 --- a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js +++ b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js @@ -1,10 +1,5 @@ -import { - ArrayElement, - isArrayElement, - isPrimitiveElement, - deepmerge, - toValue, -} from '@swagger-api/apidom-core'; +import { uniqWith } from 'ramda'; +import { isArrayElement, isObjectElement, deepmerge, toValue } from '@swagger-api/apidom-core'; import { isSchemaElement } from '@swagger-api/apidom-ns-openapi-3-1'; import toPath from '../utils/to-path.js'; @@ -48,20 +43,23 @@ class AllOfVisitor { if (toValue(keyElement) === 'enum') { return (targetElement, sourceElement) => { if (isArrayElement(targetElement) && isArrayElement(sourceElement)) { - const primitiveElements = new ArrayElement([ - ...targetElement.findElements(isPrimitiveElement), - ...sourceElement.findElements(isPrimitiveElement), - ]); - const nonPrimitiveElements = new ArrayElement([ - ...targetElement.findElements((element) => !isPrimitiveElement(element)), - ...sourceElement.findElements((element) => !isPrimitiveElement(element)), - ]); - - const uniquePrimitiveElements = new ArrayElement([ - ...new Set(toValue(primitiveElements)), - ]); - - return uniquePrimitiveElements.concat(nonPrimitiveElements); + const areElementsEqual = (a, b) => { + if ( + isArrayElement(a) || + isArrayElement(b) || + isObjectElement(a) || + isObjectElement(b) + ) { + return false; + } + return a.equals(toValue(b)); + }; + const mergedElements = targetElement.concat(sourceElement); + const uniqueElements = uniqWith(areElementsEqual)(mergedElements.content); + + mergedElements.content = uniqueElements; + + return mergedElements; } return deepmerge(targetElement, sourceElement); }; From 610e457defbe4ab96443e899375163a4f5ca858d Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Tue, 3 Jun 2025 10:20:12 +0200 Subject: [PATCH 5/6] fix: apply code review changes --- .../visitors/all-of.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js index aabeeb547..f23f142dd 100644 --- a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js +++ b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js @@ -1,5 +1,11 @@ import { uniqWith } from 'ramda'; -import { isArrayElement, isObjectElement, deepmerge, toValue } from '@swagger-api/apidom-core'; +import { + isArrayElement, + isObjectElement, + deepmerge, + toValue, + cloneShallow, +} from '@swagger-api/apidom-core'; import { isSchemaElement } from '@swagger-api/apidom-ns-openapi-3-1'; import toPath from '../utils/to-path.js'; @@ -54,12 +60,13 @@ class AllOfVisitor { } return a.equals(toValue(b)); }; - const mergedElements = targetElement.concat(sourceElement); - const uniqueElements = uniqWith(areElementsEqual)(mergedElements.content); + const clone = cloneShallow(targetElement); + clone.content = uniqWith(areElementsEqual)([ + ...targetElement.content, + ...sourceElement.content, + ]); - mergedElements.content = uniqueElements; - - return mergedElements; + return clone; } return deepmerge(targetElement, sourceElement); }; From c053a0d6185a4d5787d551a3752769ea91a4ef91 Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Tue, 3 Jun 2025 10:23:27 +0200 Subject: [PATCH 6/6] fix: check metadata instead of array --- .../openapi-3-1-swagger-client/visitors/all-of.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js index f23f142dd..1c05ea101 100644 --- a/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js +++ b/src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/all-of.js @@ -5,6 +5,7 @@ import { deepmerge, toValue, cloneShallow, + includesClasses, } from '@swagger-api/apidom-core'; import { isSchemaElement } from '@swagger-api/apidom-ns-openapi-3-1'; @@ -48,7 +49,10 @@ class AllOfVisitor { customMerge: (keyElement) => { if (toValue(keyElement) === 'enum') { return (targetElement, sourceElement) => { - if (isArrayElement(targetElement) && isArrayElement(sourceElement)) { + if ( + includesClasses(['json-schema-enum'], targetElement) && + includesClasses(['json-schema-enum'], sourceElement) + ) { const areElementsEqual = (a, b) => { if ( isArrayElement(a) ||