Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(openapi): x-fern-base-path #1862

Merged
merged 13 commits into from
Dec 6, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
BaseOpenApiV3_1ConverterNode,
BaseOpenApiV3_1ConverterNodeConstructorArgs,
} from "../BaseOpenApiV3_1Converter.node";
import { basePathExtensionKey } from "../types/extension.types";
import { coalesceServers } from "../utils/3.1/coalesceServers";
import { XFernBasePathConverterNode } from "./extensions/XFernBasePathConverter.node";
import { PathsObjectConverterNode } from "./paths/PathsObjectConverter.node";
import { ServerObjectConverterNode } from "./paths/ServerObjectConverter.node";
import { ComponentsConverterNode } from "./schemas/ComponentsConverter.node";
Expand All @@ -18,6 +20,7 @@ export class OpenApiDocumentConverterNode extends BaseOpenApiV3_1ConverterNode<
// webhooks: WebhooksObjectConverterNode | undefined;
components: ComponentsConverterNode | undefined;
servers: ServerObjectConverterNode[] | undefined;
basePath: XFernBasePathConverterNode | undefined;

constructor(args: BaseOpenApiV3_1ConverterNodeConstructorArgs<OpenAPIV3_1.Document>) {
super(args);
Expand All @@ -26,6 +29,12 @@ export class OpenApiDocumentConverterNode extends BaseOpenApiV3_1ConverterNode<

parse(): void {
this.servers = coalesceServers(this.servers, this.input.servers, this.context, this.accessPath);
this.basePath = new XFernBasePathConverterNode({
input: this.input,
context: this.context,
accessPath: this.accessPath,
pathId: basePathExtensionKey,
});

if (this.input.paths == null) {
this.context.errors.warning({
Expand All @@ -41,6 +50,7 @@ export class OpenApiDocumentConverterNode extends BaseOpenApiV3_1ConverterNode<
pathId: "paths",
},
this.servers,
this.basePath,
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { OpenAPIV3_1 } from "openapi-types";
import {
BaseOpenApiV3_1ConverterNode,
BaseOpenApiV3_1ConverterNodeConstructorArgs,
} from "../../BaseOpenApiV3_1Converter.node";
import { basePathExtensionKey } from "../../types/extension.types";
import { extendType } from "../../utils/extendType";

export class XFernBasePathConverterNode extends BaseOpenApiV3_1ConverterNode<OpenAPIV3_1.Document, string | undefined> {
basePath?: string;

constructor(args: BaseOpenApiV3_1ConverterNodeConstructorArgs<OpenAPIV3_1.Document>) {
super(args);
this.safeParse();
}

parse(): void {
this.basePath = extendType<{ [key in typeof basePathExtensionKey]?: string }>(this.input)[basePathExtensionKey];

if (this.basePath != null) {
if (this.basePath.startsWith("/")) {
this.basePath = this.basePath.slice(1);
}

if (this.basePath.endsWith("/")) {
this.basePath = this.basePath.slice(0, -1);
}
} else {
this.basePath = undefined;
}
}

convert(): string | undefined {
return this.basePath;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { OpenAPIV3_1 } from "openapi-types";
import { createMockContext } from "../../../../__test__/createMockContext.util";
import { basePathExtensionKey } from "../../../types/extension.types";
import { XFernBasePathConverterNode } from "../XFernBasePathConverter.node";

describe("XFernGroupNameConverterNode", () => {
const mockContext = createMockContext();

describe("parse", () => {
it(`sets basePath from ${basePathExtensionKey} when present`, () => {
const converter = new XFernBasePathConverterNode({
input: { [basePathExtensionKey]: "/v1" } as unknown as OpenAPIV3_1.Document,
context: mockContext,
accessPath: [],
pathId: "",
});
expect(converter.basePath).toBe("v1");
});

it(`properly formats ${basePathExtensionKey} with slashes`, () => {
const converter = new XFernBasePathConverterNode({
input: { [basePathExtensionKey]: "/v1/" } as unknown as OpenAPIV3_1.Document,
context: mockContext,
accessPath: [],
pathId: "",
});
expect(converter.basePath).toBe("v1");
});

it(`sets basePath to undefined when ${basePathExtensionKey} is not present`, () => {
const converter = new XFernBasePathConverterNode({
input: {} as unknown as OpenAPIV3_1.Document,
context: mockContext,
accessPath: [],
pathId: "",
});
expect(converter.basePath).toBeUndefined();
});

it(`sets basePath to undefined when ${basePathExtensionKey} is explicitly null`, () => {
const converter = new XFernBasePathConverterNode({
input: { [basePathExtensionKey]: null } as unknown as OpenAPIV3_1.Document,
context: mockContext,
accessPath: [],
pathId: "",
});
expect(converter.basePath).toBeUndefined();
});
});

describe("convert", () => {
it("returns the basePath value", () => {
const converter = new XFernBasePathConverterNode({
input: { [basePathExtensionKey]: "/v1" } as unknown as OpenAPIV3_1.Document,
context: mockContext,
accessPath: [],
pathId: "",
});
expect(converter.convert()).toBe("v1");
});

it("returns undefined when basePath is not set", () => {
const converter = new XFernBasePathConverterNode({
input: {} as unknown as OpenAPIV3_1.Document,
context: mockContext,
accessPath: [],
pathId: "",
});
expect(converter.convert()).toBeUndefined();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { coalesceServers } from "../../utils/3.1/coalesceServers";
import { convertToObjectProperties } from "../../utils/3.1/convertToObjectProperties";
import { resolveParameterReference } from "../../utils/3.1/resolveParameterReference";
import { AvailabilityConverterNode } from "../extensions/AvailabilityConverter.node";
import { XFernBasePathConverterNode } from "../extensions/XFernBasePathConverter.node";
import { isReferenceObject } from "../guards/isReferenceObject";
import { ServerObjectConverterNode } from "./ServerObjectConverter.node";
import { ParameterBaseObjectConverterNode } from "./parameters/ParameterBaseObjectConverter.node";
Expand All @@ -32,6 +33,7 @@ export class OperationObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
protected servers: ServerObjectConverterNode[] | undefined,
protected path: string | undefined,
protected method: "GET" | "POST" | "PUT" | "DELETE",
protected basePath: XFernBasePathConverterNode | undefined,
) {
super(args);
this.safeParse();
Expand Down Expand Up @@ -122,8 +124,10 @@ export class OperationObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
}

const path = this.path.startsWith("/") ? this.path.slice(1) : this.path;
const basePath = this.basePath?.convert();
const pathParts = basePath ? [basePath, ...path.split("/")] : path.split("/");

return path.split("/").map((part) => {
return pathParts.map((part) => {
if (part.startsWith("{") && part.endsWith("}")) {
return {
type: "pathParameter" as const,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
BaseOpenApiV3_1ConverterNodeConstructorArgs,
} from "../../BaseOpenApiV3_1Converter.node";
import { coalesceServers } from "../../utils/3.1/coalesceServers";
import { XFernBasePathConverterNode } from "../extensions/XFernBasePathConverter.node";
import { OperationObjectConverterNode } from "./OperationObjectConverter.node";
import { ServerObjectConverterNode } from "./ServerObjectConverter.node";

Expand All @@ -21,12 +22,12 @@ export class PathItemObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
put: OperationObjectConverterNode | undefined;
patch: OperationObjectConverterNode | undefined;
delete: OperationObjectConverterNode | undefined;

path: string | undefined;

constructor(
args: BaseOpenApiV3_1ConverterNodeConstructorArgs<OpenAPIV3_1.PathItemObject>,
protected servers: ServerObjectConverterNode[] | undefined,
protected basePath: XFernBasePathConverterNode | undefined,
) {
super(args);
this.safeParse();
Expand All @@ -47,6 +48,7 @@ export class PathItemObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
this.servers,
this.pathId,
"GET",
this.basePath,
);
}
if (this.input.post != null) {
Expand All @@ -60,6 +62,7 @@ export class PathItemObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
this.servers,
this.pathId,
"POST",
this.basePath,
);
}
if (this.input.put != null) {
Expand All @@ -73,6 +76,7 @@ export class PathItemObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
this.servers,
this.pathId,
"PUT",
this.basePath,
);
}
if (this.input.delete != null) {
Expand All @@ -86,6 +90,7 @@ export class PathItemObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
this.servers,
this.pathId,
"DELETE",
this.basePath,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
BaseOpenApiV3_1ConverterNodeConstructorArgs,
} from "../../BaseOpenApiV3_1Converter.node";
import { coalesceServers } from "../../utils/3.1/coalesceServers";
import { XFernBasePathConverterNode } from "../extensions/XFernBasePathConverter.node";
import { PathItemObjectConverterNode } from "./PathItemObjectConverter.node";
import { ServerObjectConverterNode } from "./ServerObjectConverter.node";

Expand All @@ -19,6 +20,7 @@ export class PathsObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
constructor(
args: BaseOpenApiV3_1ConverterNodeConstructorArgs<OpenAPIV3_1.PathsObject>,
protected readonly servers: ServerObjectConverterNode[] | undefined,
protected readonly basePath: XFernBasePathConverterNode | undefined,
) {
super(args);
this.safeParse();
Expand All @@ -37,6 +39,7 @@ export class PathsObjectConverterNode extends BaseOpenApiV3_1ConverterNode<
pathId: path,
},
coalesceServers(this.servers, pathItem.servers, this.context, this.accessPath),
this.basePath,
);
})
.filter(isNonNullish);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe("OperationObjectConverterNode", () => {
undefined,
"/pets/{petId}",
"GET",
undefined,
);

const result = node.convert();
Expand Down Expand Up @@ -75,6 +76,7 @@ describe("OperationObjectConverterNode", () => {
undefined,
undefined,
"GET",
undefined,
);

const result = node.convert();
Expand All @@ -94,6 +96,7 @@ describe("OperationObjectConverterNode", () => {
undefined,
"/users/{userId}/posts/{postId}",
"GET",
undefined,
);

const result = node.convertPathToPathParts();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ describe("PathItemObjectConverterNode", () => {
pathId: "/pets/{petId}",
},
undefined,
undefined,
);

const result = node.convert();
Expand Down Expand Up @@ -112,6 +113,7 @@ describe("PathItemObjectConverterNode", () => {
pathId: "/empty",
},
undefined,
undefined,
);

const result = node.convert();
Expand Down Expand Up @@ -143,6 +145,7 @@ describe("PathItemObjectConverterNode", () => {
pathId: "/test",
},
undefined,
undefined,
);

const result = node.convert();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describe("PathsObjectConverterNode", () => {
pathId: "test",
},
undefined,
undefined,
);

const result = node.convert() ?? {};
Expand All @@ -71,6 +72,7 @@ describe("PathsObjectConverterNode", () => {
pathId: "test",
},
undefined,
undefined,
);

const result = node.convert();
Expand All @@ -90,6 +92,7 @@ describe("PathsObjectConverterNode", () => {
pathId: "test",
},
undefined,
undefined,
);

const result = node.convert();
Expand Down Expand Up @@ -123,6 +126,7 @@ describe("PathsObjectConverterNode", () => {
pathId: "test",
},
undefined,
undefined,
);

const result = node.convert() ?? {};
Expand Down
1 change: 1 addition & 0 deletions packages/parsers/src/openapi/types/extension.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const basePathExtensionKey: string = "x-fern-base-path";
Loading