Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
@@ -0,0 +1,77 @@
import { CliError } from "@fern-api/task-context";
import { OpenAPIV2 } from "openapi-types";
import { vi } from "vitest";

import { convertOpenAPIV2ToV3 } from "../utils/convertOpenAPIV2ToV3.js";
import { createMockTaskContext } from "./helpers/createMockTaskContext.js";

function createSwaggerSpec(): OpenAPIV2.Document {
return {
swagger: "2.0",
info: {
title: "Pet Store",
version: "1.0.0"
},
host: "example.com",
schemes: ["https"],
paths: {
"/pets": {
get: {
operationId: "listPets",
responses: {
"200": {
description: "OK"
}
}
}
}
},
definitions: {
Pet: {
type: "object",
properties: {
name: {
type: "string"
}
}
}
}
};
}

describe("convertOpenAPIV2ToV3", () => {
it("converts a valid Swagger 2.0 document", async () => {
const result = await convertOpenAPIV2ToV3(createSwaggerSpec());

expect(result.openapi).toMatch(/^3\.0\./);
expect(result.info.title).toBe("Pet Store");
});

it("converts a Swagger 2.0 document with a nullable type array in patch mode", async () => {
const spec = createSwaggerSpec();
const property = spec.definitions?.Pet?.properties?.name;
if (property == null || Array.isArray(property) || "$ref" in property) {
throw new Error("Expected Pet.name to be a schema object");
}
Object.assign(property, { type: ["null", "string"] });

const context = createMockTaskContext();
const warn = vi.spyOn(context.logger, "warn");
const result = await convertOpenAPIV2ToV3(spec, { context });

expect(result.components?.schemas?.Pet).toMatchObject({
type: "object",
properties: {
name: {
type: "string",
nullable: true
}
}
});
expect(warn).toHaveBeenCalledWith(expect.stringContaining("lenient (patch) mode"));
});

it("throws a CliError when the document cannot be converted", async () => {
await expect(convertOpenAPIV2ToV3({} as OpenAPIV2.Document)).rejects.toBeInstanceOf(CliError);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class OpenAPILoader {
if (!openAPI.schemes || openAPI.schemes.length === 0) {
openAPI.schemes = ["https"];
}
const convertedOpenAPI = await convertOpenAPIV2ToV3(openAPI);
const convertedOpenAPI = await convertOpenAPIV2ToV3(openAPI, { context });
return {
type: "openapi",
value: convertedOpenAPI,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
import { CliError } from "@fern-api/task-context";
import { CliError, TaskContext } from "@fern-api/task-context";
import { OpenAPIV2, OpenAPIV3 } from "openapi-types";
import { convertObj } from "swagger2openapi";

export async function convertOpenAPIV2ToV3(openAPI: OpenAPIV2.Document): Promise<OpenAPIV3.Document> {
export async function convertOpenAPIV2ToV3(
openAPI: OpenAPIV2.Document,
options?: { context?: TaskContext }
): Promise<OpenAPIV3.Document> {
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
let strictError: unknown;

try {
const conversionResult = await convertObj(openAPI, {});
return conversionResult.openapi;
} catch (e) {
} catch (error) {
strictError = error;
}

try {
const conversionResult = await convertObj(openAPI, { patch: true });
options?.context?.logger.warn(
`OpenAPI v2 (Swagger) document is not strictly valid and was converted in lenient (patch) mode: ${
strictError instanceof Error ? strictError.message : String(strictError)
}`
);
return conversionResult.openapi;
} catch {
throw new CliError({
message: `Failed to convert OpenAPI v2 (Swagger) spec to OpenAPI v3: ${
e instanceof Error ? e.message : String(e)
strictError instanceof Error ? strictError.message : String(strictError)
}`,
code: CliError.Code.ParseError
});
Expand Down
Loading