From fac4204a71d280ad9c1364452657bb3c1cd4e497 Mon Sep 17 00:00:00 2001 From: JonLuca De Caro Date: Tue, 11 Jul 2023 15:18:10 -0700 Subject: [PATCH] feat: add nested dot notation in definitionkeywords, bring in lodash again (#69) --- README.md | 2 +- package.json | 2 + src/index.ts | 2 +- src/lib/converters/schema.ts | 11 ++-- src/lib/utils/cloneDeep.ts | 47 ----------------- test/definition_keyworks.test.ts | 88 +++++++++++++++++++++++++++----- test/tsconfig.json | 4 +- yarn.lock | 5 ++ 8 files changed, 92 insertions(+), 69 deletions(-) delete mode 100644 src/lib/utils/cloneDeep.ts diff --git a/README.md b/README.md index 8e74e54..4803792 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ npm install --save @openapi-contrib/openapi-schema-to-json-schema ### CLI ```bash -npx openapi-schema-to-json-schema --input openapi.json --output json-schema.json +npx "@openapi-contrib/openapi-schema-to-json-schema" --input openapi.json --output json-schema.json ``` ## Converting OpenAPI schema diff --git a/package.json b/package.json index 9fcf068..f211491 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,10 @@ "bin": "dist/bin.js", "dependencies": { "@types/json-schema": "^7.0.12", + "@types/lodash": "^4.14.195", "@types/node": "^20.4.1", "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21", "openapi-typescript": "^5.4.1", "yargs": "^17.7.2" }, diff --git a/src/index.ts b/src/index.ts index 631fad7..2d91581 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import type { Options, OptionsInternal } from "./openapi-schema-types"; import { NOT_SUPPORTED, STRUCTS } from "./consts"; import type { JSONSchema4 } from "json-schema"; import type { ParameterObject, ResponseObject } from "openapi-typescript/src/types"; -import { cloneDeep } from "./lib/utils/cloneDeep"; +import cloneDeep from "lodash/cloneDeep"; import type { AcceptibleInputSchema } from "./openapi-schema-types"; const patternPropertiesHandler = (schema) => { diff --git a/src/lib/converters/schema.ts b/src/lib/converters/schema.ts index bb8b5bf..b8cf126 100644 --- a/src/lib/converters/schema.ts +++ b/src/lib/converters/schema.ts @@ -7,9 +7,10 @@ import type { SchemaObject } from "openapi-typescript/src/types"; import type { PatternPropertiesHandler } from "../../openapi-schema-types"; import type { OpenAPI3 } from "openapi-typescript"; import type { ReferenceObject } from "openapi-typescript/src/types"; -import { cloneDeep } from "../utils/cloneDeep"; import type { AcceptibleInputSchema } from "../../openapi-schema-types"; - +import cloneDeep from "lodash/cloneDeep"; +import get from "lodash/get"; +import set from "lodash/set"; // Convert from OpenAPI 3.0 `SchemaObject` to JSON schema v4 function convertFromSchema( schema: T, @@ -63,8 +64,10 @@ function convertSchema(schema: OpenAPI3 | SchemaObject | ReferenceObject, option let convertedSchema = schema as SchemaObject; for (const def of definitionKeywords) { - if (typeof schema[def] === "object") { - schema[def] = convertProperties(schema[def], options); + const innerDef = get(schema, def); + if (typeof innerDef === "object") { + const convertedInnerDef = convertProperties(innerDef, options); + set(schema, def, convertedInnerDef); } } diff --git a/src/lib/utils/cloneDeep.ts b/src/lib/utils/cloneDeep.ts deleted file mode 100644 index 3e928a4..0000000 --- a/src/lib/utils/cloneDeep.ts +++ /dev/null @@ -1,47 +0,0 @@ -// From https://dev.to/salyadav/deep-clone-of-js-objects-with-circular-dependency-4if7 -const isArray = (val: unknown) => { - return Array.isArray(val); -}; - -const isObject = (val: unknown) => { - return {}.toString.call(val) === "[object Object]" && !isArray(val); -}; - -export const cloneDeep = (val: unknown, history = new Set()) => { - const stack = history || new Set(); - - if (stack.has(val)) { - return val; - } - - stack.add(val); - - const copyObject = (o) => { - const oo = {}; - for (const k in o) { - oo[k] = cloneDeep(o[k], stack); - } - return oo; - }; - - const copyArray = (a) => { - return [...a].map((e) => { - if (isArray(e)) { - return copyArray(e); - } else if (isObject(e)) { - return copyObject(e); - } - return cloneDeep(e, stack); - }); - }; - - if (isArray(val)) { - return copyArray(val); - } - - if (isObject(val)) { - return copyObject(val); - } - - return val; -}; diff --git a/test/definition_keyworks.test.ts b/test/definition_keyworks.test.ts index 90205f7..9a8f619 100644 --- a/test/definition_keyworks.test.ts +++ b/test/definition_keyworks.test.ts @@ -1,6 +1,6 @@ import convert from "../src"; -it("handles conversion in keywords specified in additionalKeywords", function ({ expect }) { +describe("handles conversion in keywords specified in additionalKeywords", function () { const schema = { definitions: { sharedDefinition: { @@ -15,23 +15,83 @@ it("handles conversion in keywords specified in additionalKeywords", function ({ }, }; - const result = convert(schema, { - definitionKeywords: ["definitions"], + it("handles conversion in keywords specified in additionalKeywords", function ({ expect }) { + const result = convert(schema, { + definitionKeywords: ["definitions"], + }); + + const expected = { + $schema: "http://json-schema.org/draft-04/schema#", + definitions: { + sharedDefinition: { + type: "object", + properties: { + foo: { + type: ["string", "null"], + }, + }, + }, + }, + }; + + expect(result).toEqual(expected); }); - const expected = { - $schema: "http://json-schema.org/draft-04/schema#", - definitions: { - sharedDefinition: { - type: "object", - properties: { - foo: { - type: ["string", "null"], + it("does not convert when no definition keywords are included", function ({ expect }) { + const result = convert(schema); + + const expected = { + $schema: "http://json-schema.org/draft-04/schema#", + definitions: { + sharedDefinition: { + properties: { + foo: { + nullable: true, + type: "string", + }, }, + type: "object", }, }, - }, - }; + }; + + expect(result).toEqual(expected); + }); + + it("handles nested definition keywords", function ({ expect }) { + const nestedSchema = { + schema: { + definitions: { + sharedDefinition: { + type: "object", + properties: { + foo: { + type: "string", + nullable: true, + }, + }, + }, + }, + }, + }; + const result = convert(nestedSchema, { definitionKeywords: ["schema.definitions"] }); + + const expected = { + $schema: "http://json-schema.org/draft-04/schema#", + schema: { + definitions: { + sharedDefinition: { + type: "object", + properties: { + foo: { + type: ["string", "null"], + }, + }, + }, + }, + }, + }; - expect(result).toEqual(expected); + expect(result).toEqual(expected); + }); }); diff --git a/test/tsconfig.json b/test/tsconfig.json index 350a786..cec73cb 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,8 +1,8 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "target": "ES2015", - "lib": ["es2015", "dom"], + "target": "ESNext", + "lib": ["ESNext", "dom"], "types": ["vitest/globals"] }, "include": ["."] diff --git a/yarn.lock b/yarn.lock index 4abe2b4..d3dc65b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -714,6 +714,11 @@ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== +"@types/lodash@^4.14.195": + version "4.14.195" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" + integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== + "@types/minimist@^1.2.0": version "1.2.2" resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"