Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"),
Expand Down
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -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 '.+'/,
Expand Down
31 changes: 30 additions & 1 deletion packages/openapi-code-generator/src/core/openapi-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand All @@ -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", () => {
Expand All @@ -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
})
})
4 changes: 0 additions & 4 deletions packages/openapi-code-generator/src/core/openapi-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,3 @@ export function extractPlaceholders(
placeholder: match[1],
}))
}

export function getSchemaNameFromRef(reference: Reference) {
return getNameFromRef(reference, "s_")
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions packages/openapi-code-generator/src/core/openapi-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export class OpenapiValidator {
return this.validate3_1
}

// todo: openapi 3.2: add validator

throw new Error(`unsupported openapi version '${version}'`)
}

Expand Down
16 changes: 16 additions & 0 deletions packages/openapi-code-generator/src/core/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {describe, expect, it} from "@jest/globals"
import {
camelCase,
coalesce,
deepEqual,
hasSingleElement,
isDefined,
isHttpMethod,
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down