diff --git a/packages/openapi-code-generator/src/core/dependency-graph.spec.ts b/packages/openapi-code-generator/src/core/dependency-graph.spec.ts index 2489a2f4..8292e5ee 100644 --- a/packages/openapi-code-generator/src/core/dependency-graph.spec.ts +++ b/packages/openapi-code-generator/src/core/dependency-graph.spec.ts @@ -1,13 +1,13 @@ import {describe, expect, it} from "@jest/globals" import {testVersions, unitTestInput} from "../test/input.test-utils" import {buildDependencyGraph} from "./dependency-graph" -import {getSchemaNameFromRef} from "./openapi-utils" +import {getNameFromRef} from "./openapi-utils" describe.each(testVersions)("%s - core/dependency-graph", (version) => { it("works", async () => { const {input} = await unitTestInput(version) - const graph = buildDependencyGraph(input, getSchemaNameFromRef) + const graph = buildDependencyGraph(input, (it) => getNameFromRef(it, "s_")) expect(graph.order.indexOf("s_Ordering")).toBeGreaterThan( graph.order.indexOf("s_AOrdering"), diff --git a/packages/openapi-code-generator/src/core/loaders/tsconfig.loader.spec.ts b/packages/openapi-code-generator/src/core/loaders/tsconfig.loader.spec.ts index 09577fb6..2ec9f81f 100644 --- a/packages/openapi-code-generator/src/core/loaders/tsconfig.loader.spec.ts +++ b/packages/openapi-code-generator/src/core/loaders/tsconfig.loader.spec.ts @@ -1,4 +1,4 @@ -import {describe, expect, it} from "@jest/globals" +import {describe, expect, it, jest} from "@jest/globals" import {WebFsAdaptor} from "../file-system/web-fs-adaptor" import {loadTsConfigCompilerOptions} from "./tsconfig.loader" @@ -98,4 +98,30 @@ describe("core/loaders/tsconfig.loader", () => { rewriteRelativeImportExtensions: true, }) }) + + it("falls back to defaults if an exception occurs", async () => { + const fs = fsAdaptor({ + "/virtual/ws/tsconfig.json": JSON.stringify({ + compilerOptions: { + exactOptionalPropertyTypes: true, + }, + }), + }) + + const spy = jest + .spyOn(fs, "readFile") + .mockRejectedValue(new Error("EACCES: permission denied")) + + const actual = await loadTsConfigCompilerOptions( + "/virtual/ws/packages/pkg", + fs, + ) + + expect(actual).toEqual({ + exactOptionalPropertyTypes: false, + rewriteRelativeImportExtensions: false, + }) + + expect(spy).toHaveBeenCalled() + }) }) diff --git a/packages/openapi-code-generator/src/core/openapi-loader.spec.ts b/packages/openapi-code-generator/src/core/openapi-loader.spec.ts index 6875ee19..616634f8 100644 --- a/packages/openapi-code-generator/src/core/openapi-loader.spec.ts +++ b/packages/openapi-code-generator/src/core/openapi-loader.spec.ts @@ -2,7 +2,7 @@ import {describe, expect, it} from "@jest/globals" import {normalizeRef, pathFromRef} from "./openapi-loader" describe("core/openapi-loader", () => { - describe("normalizeRef", () => { + describe("#normalizeRef", () => { it("rejects invalid $refs", () => { expect(() => normalizeRef("not a ref", "/some/path")).toThrow( /invalid \$ref '.+'/, diff --git a/packages/openapi-code-generator/src/core/openapi-utils.spec.ts b/packages/openapi-code-generator/src/core/openapi-utils.spec.ts index 0225bb56..3abb08ed 100644 --- a/packages/openapi-code-generator/src/core/openapi-utils.spec.ts +++ b/packages/openapi-code-generator/src/core/openapi-utils.spec.ts @@ -1,5 +1,5 @@ import {describe, expect, it} from "@jest/globals" -import {getNameFromRef, isRef} from "./openapi-utils" +import {extractPlaceholders, getNameFromRef, isRef} from "./openapi-utils" describe("core/openapi-utils", () => { describe("#isRef", () => { @@ -10,6 +10,11 @@ describe("core/openapi-utils", () => { it("returns false if $ref is not defined", () => { expect(isRef({type: "number"})).toBe(false) }) + + it("returns false if not passed an object", () => { + expect(isRef(null)).toBe(false) + expect(isRef(123)).toBe(false) + }) }) describe("#getNameFromRef", () => { @@ -30,5 +35,29 @@ describe("core/openapi-utils", () => { "t_Foo_Bar", ) }) + + it("throws on an invalid $ref", () => { + expect(() => getNameFromRef({$ref: "#/"}, "t_")).toThrow( + "no name found in $ref: '#/'", + ) + }) + }) + + describe("#extractPlaceholders", () => { + it("returns an empty array if no placeholders in input", () => { + expect(extractPlaceholders("/foo/bar")).toStrictEqual([]) + }) + + it("extracts valid placeholders", () => { + expect(extractPlaceholders("/{foo/{id}/bar}/{type}")).toStrictEqual([ + { + placeholder: "id", + wholeString: "{id}", + }, + {placeholder: "type", wholeString: "{type}"}, + ]) + }) + + // todo: expand tests for special characters / escaping }) }) diff --git a/packages/openapi-code-generator/src/core/openapi-utils.ts b/packages/openapi-code-generator/src/core/openapi-utils.ts index 3eb7f458..6d10e2ef 100644 --- a/packages/openapi-code-generator/src/core/openapi-utils.ts +++ b/packages/openapi-code-generator/src/core/openapi-utils.ts @@ -31,7 +31,3 @@ export function extractPlaceholders( placeholder: match[1], })) } - -export function getSchemaNameFromRef(reference: Reference) { - return getNameFromRef(reference, "s_") -} diff --git a/packages/openapi-code-generator/src/core/openapi-validator.spec.ts b/packages/openapi-code-generator/src/core/openapi-validator.spec.ts index 25ea6b5c..eb65e5dd 100644 --- a/packages/openapi-code-generator/src/core/openapi-validator.spec.ts +++ b/packages/openapi-code-generator/src/core/openapi-validator.spec.ts @@ -54,7 +54,7 @@ describe("core/openapi-validator", () => { }) }) - describe.skip("openapi 3.1", () => { + describe("openapi 3.1", () => { it("should accept a valid specification", async () => { const validator = await OpenapiValidator.create() await expect( @@ -89,7 +89,7 @@ describe("core/openapi-validator", () => { ).resolves.toBeUndefined() }) - it("should reject an invalid specification", async () => { + it.skip("should reject an invalid specification", async () => { const validator = await OpenapiValidator.create() await expect( validator.validate( diff --git a/packages/openapi-code-generator/src/core/openapi-validator.ts b/packages/openapi-code-generator/src/core/openapi-validator.ts index e83691af..decf4126 100644 --- a/packages/openapi-code-generator/src/core/openapi-validator.ts +++ b/packages/openapi-code-generator/src/core/openapi-validator.ts @@ -22,6 +22,8 @@ export class OpenapiValidator { return this.validate3_1 } + // todo: openapi 3.2: add validator + throw new Error(`unsupported openapi version '${version}'`) } diff --git a/packages/openapi-code-generator/src/core/utils.spec.ts b/packages/openapi-code-generator/src/core/utils.spec.ts index a4e13628..17b4b701 100644 --- a/packages/openapi-code-generator/src/core/utils.spec.ts +++ b/packages/openapi-code-generator/src/core/utils.spec.ts @@ -2,6 +2,7 @@ import {describe, expect, it} from "@jest/globals" import { camelCase, coalesce, + deepEqual, hasSingleElement, isDefined, isHttpMethod, @@ -46,6 +47,21 @@ describe("core/utils", () => { }) }) + describe("#deepEqual", () => { + it("works for primitives", () => { + expect(deepEqual(2, 2)).toBe(true) + expect(deepEqual(1, 2)).toBe(false) + }) + it("works for array", () => { + expect(deepEqual([1], [1])).toBe(true) + expect(deepEqual([1], [2])).toBe(false) + }) + it("works for object", () => { + expect(deepEqual({foo: "bar"}, {foo: "bar"})).toBe(true) + expect(deepEqual({foo: "bar"}, {foo: "baz"})).toBe(false) + }) + }) + describe("#coalesce", () => { it("returns the first defined parameter", () => { expect(coalesce(null, undefined, 1, 2)).toBe(1) diff --git a/packages/openapi-code-generator/src/typescript/common/schema-builders/abstract-schema-builder.ts b/packages/openapi-code-generator/src/typescript/common/schema-builders/abstract-schema-builder.ts index 60737f59..a87b44d1 100644 --- a/packages/openapi-code-generator/src/typescript/common/schema-builders/abstract-schema-builder.ts +++ b/packages/openapi-code-generator/src/typescript/common/schema-builders/abstract-schema-builder.ts @@ -15,7 +15,7 @@ import type { IRParameter, MaybeIRModel, } from "../../../core/openapi-types-normalized" -import {getSchemaNameFromRef, isRef} from "../../../core/openapi-utils" +import {getNameFromRef, isRef} from "../../../core/openapi-utils" import {hasSingleElement} from "../../../core/utils" import {CompilationUnit, type ICompilable} from "../compilation-units" import type {ImportBuilder} from "../import-builder" @@ -51,7 +51,8 @@ export abstract class AbstractSchemaBuilder< private readonly parent?: SubClass, ) { this.graph = - parent?.graph ?? buildDependencyGraph(this.input, getSchemaNameFromRef) + parent?.graph ?? + buildDependencyGraph(this.input, (it) => this.getSchemaNameFromRef(it)) this.importHelpers(this.schemaBuilderImports) this.typeBuilder = typeBuilder.withImports(this.schemaBuilderImports) } @@ -61,7 +62,7 @@ export abstract class AbstractSchemaBuilder< private add(reference: Reference): string { this.parent?.add(reference) - const name = getSchemaNameFromRef(reference) + const name = this.getSchemaNameFromRef(reference) this.referenced[name] = reference if (this.imports) { @@ -148,6 +149,10 @@ export abstract class AbstractSchemaBuilder< protected abstract importHelpers(importBuilder: ImportBuilder): void + public getSchemaNameFromRef(reference: Reference) { + return getNameFromRef(reference, "s_") + } + public abstract schemaTypeForType(type: string): string protected abstract schemaFromRef(reference: Reference): ExportDefinition diff --git a/packages/openapi-code-generator/src/typescript/common/schema-builders/joi-schema-builder.ts b/packages/openapi-code-generator/src/typescript/common/schema-builders/joi-schema-builder.ts index e715eeb6..49639bff 100644 --- a/packages/openapi-code-generator/src/typescript/common/schema-builders/joi-schema-builder.ts +++ b/packages/openapi-code-generator/src/typescript/common/schema-builders/joi-schema-builder.ts @@ -8,7 +8,6 @@ import type { IRModelString, MaybeIRModel, } from "../../../core/openapi-types-normalized" -import {getSchemaNameFromRef} from "../../../core/openapi-utils" import {hasSingleElement, isDefined} from "../../../core/utils" import type {ImportBuilder} from "../import-builder" import type {TypeBuilder} from "../type-builder" @@ -75,7 +74,7 @@ export class JoiBuilder extends AbstractSchemaBuilder< } protected schemaFromRef(reference: Reference): ExportDefinition { - const name = getSchemaNameFromRef(reference) + const name = this.getSchemaNameFromRef(reference) const schemaObject = this.input.schema(reference) const value = this.fromModel(schemaObject, true) diff --git a/packages/openapi-code-generator/src/typescript/common/schema-builders/zod-v3-schema-builder.ts b/packages/openapi-code-generator/src/typescript/common/schema-builders/zod-v3-schema-builder.ts index bae2b203..8320e2b8 100644 --- a/packages/openapi-code-generator/src/typescript/common/schema-builders/zod-v3-schema-builder.ts +++ b/packages/openapi-code-generator/src/typescript/common/schema-builders/zod-v3-schema-builder.ts @@ -8,7 +8,6 @@ import type { IRModelString, MaybeIRModel, } from "../../../core/openapi-types-normalized" -import {getSchemaNameFromRef} from "../../../core/openapi-utils" import {hasSingleElement, isDefined} from "../../../core/utils" import type {ImportBuilder} from "../import-builder" import type {TypeBuilder} from "../type-builder" @@ -90,7 +89,7 @@ export class ZodV3Builder extends AbstractSchemaBuilder< } protected schemaFromRef(reference: Reference): ExportDefinition { - const name = getSchemaNameFromRef(reference) + const name = this.getSchemaNameFromRef(reference) const schemaObject = this.input.schema(reference) const value = this.fromModel(schemaObject, true) diff --git a/packages/openapi-code-generator/src/typescript/common/schema-builders/zod-v4-schema-builder.ts b/packages/openapi-code-generator/src/typescript/common/schema-builders/zod-v4-schema-builder.ts index ae0f9d54..ffa0341b 100644 --- a/packages/openapi-code-generator/src/typescript/common/schema-builders/zod-v4-schema-builder.ts +++ b/packages/openapi-code-generator/src/typescript/common/schema-builders/zod-v4-schema-builder.ts @@ -8,7 +8,6 @@ import type { IRModelString, MaybeIRModel, } from "../../../core/openapi-types-normalized" -import {getSchemaNameFromRef} from "../../../core/openapi-utils" import {hasSingleElement, isDefined} from "../../../core/utils" import type {ImportBuilder} from "../import-builder" import type {TypeBuilder} from "../type-builder" @@ -91,7 +90,7 @@ export class ZodV4Builder extends AbstractSchemaBuilder< } protected schemaFromRef(reference: Reference): ExportDefinition { - const name = getSchemaNameFromRef(reference) + const name = this.getSchemaNameFromRef(reference) const schemaObject = this.input.schema(reference) const value = this.fromModel(schemaObject, true)