Skip to content

Commit

Permalink
(feature): Add option to disable OpenAPI example generation (#3091)
Browse files Browse the repository at this point in the history
  • Loading branch information
amckinney authored Mar 1, 2024
1 parent 41d12bd commit f8206c8
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface GeneratorsConfiguration {
absolutePathToOpenAPI: AbsoluteFilePath | undefined;
absolutePathToOpenAPIOverrides: AbsoluteFilePath | undefined;
absolutePathToAsyncAPI: AbsoluteFilePath | undefined;
disableOpenAPIExamples: boolean | undefined;
rawConfiguration: GeneratorsConfigurationSchema;
defaultGroup: string | undefined;
groups: GeneratorGroup[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,31 @@ describe("convertGeneratorsConfiguration", () => {

expect(converted.groups[0]?.generators[0]?.outputMode?.type).toEqual("githubV2");
});

it("OpenAPI legacy", async () => {
const converted = await convertGeneratorsConfiguration({
absolutePathToGeneratorsConfiguration: AbsoluteFilePath.of(__filename),
rawGeneratorsConfiguration: {
openapi: "path/to/openapi.yml",
["openapi-overrides"]: "path/to/overrides.yml"
}
});

expect(converted.disableOpenAPIExamples).toEqual(undefined);
});

it("OpenAPI object", async () => {
const converted = await convertGeneratorsConfiguration({
absolutePathToGeneratorsConfiguration: AbsoluteFilePath.of(__filename),
rawGeneratorsConfiguration: {
openapi: {
path: "path/to/openapi.yml",
overrides: "path/to/overrides.yml",
["disable-examples"]: true
}
}
});

expect(converted.disableOpenAPIExamples).toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
OPENAPI_LOCATION_KEY,
OPENAPI_OVERRIDES_LOCATION_KEY
} from "./schemas/GeneratorsConfigurationSchema";
import { OPENAPI_DISABLE_EXAMPLES_KEY } from "./schemas/GeneratorsOpenAPIObjectSchema";
import { GithubLicenseSchema } from "./schemas/GithubLicenseSchema";

export async function convertGeneratorsConfiguration({
Expand All @@ -27,23 +28,23 @@ export async function convertGeneratorsConfiguration({
absolutePathToGeneratorsConfiguration: AbsoluteFilePath;
rawGeneratorsConfiguration: GeneratorsConfigurationSchema;
}): Promise<GeneratorsConfiguration> {
const pathToOpenAPI = rawGeneratorsConfiguration[OPENAPI_LOCATION_KEY];
const openAPI = await parseOpenAPIConfiguration(rawGeneratorsConfiguration);
const pathToAsyncAPI = rawGeneratorsConfiguration[ASYNC_API_LOCATION_KEY];
const pathToOpenAPIOverrides = rawGeneratorsConfiguration[OPENAPI_OVERRIDES_LOCATION_KEY];
return {
absolutePathToConfiguration: absolutePathToGeneratorsConfiguration,
absolutePathToAsyncAPI:
pathToAsyncAPI != null
? join(dirname(absolutePathToGeneratorsConfiguration), RelativeFilePath.of(pathToAsyncAPI))
: undefined,
absolutePathToOpenAPI:
pathToOpenAPI != null
? join(dirname(absolutePathToGeneratorsConfiguration), RelativeFilePath.of(pathToOpenAPI))
openAPI.path != null
? join(dirname(absolutePathToGeneratorsConfiguration), RelativeFilePath.of(openAPI.path))
: undefined,
absolutePathToOpenAPIOverrides:
pathToOpenAPIOverrides != null
? join(dirname(absolutePathToGeneratorsConfiguration), RelativeFilePath.of(pathToOpenAPIOverrides))
openAPI.overrides != null
? join(dirname(absolutePathToGeneratorsConfiguration), RelativeFilePath.of(openAPI.overrides))
: undefined,
disableOpenAPIExamples: openAPI.disableExamples,
rawConfiguration: rawGeneratorsConfiguration,
defaultGroup: rawGeneratorsConfiguration["default-group"],
groups:
Expand All @@ -67,6 +68,30 @@ export async function convertGeneratorsConfiguration({
};
}

interface OpenAPIConfiguration {
path: string | undefined;
overrides: string | undefined;
disableExamples: boolean | undefined;
}

async function parseOpenAPIConfiguration(
rawGeneratorsConfiguration: GeneratorsConfigurationSchema
): Promise<OpenAPIConfiguration> {
const openAPI = rawGeneratorsConfiguration[OPENAPI_LOCATION_KEY];
if (typeof openAPI === "string") {
return {
path: openAPI,
overrides: rawGeneratorsConfiguration[OPENAPI_OVERRIDES_LOCATION_KEY],
disableExamples: undefined
};
}
return {
path: openAPI?.path,
overrides: openAPI?.overrides ?? rawGeneratorsConfiguration[OPENAPI_OVERRIDES_LOCATION_KEY],
disableExamples: openAPI?.[OPENAPI_DISABLE_EXAMPLES_KEY]
};
}

async function convertGroup({
absolutePathToGeneratorsConfiguration,
groupName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { z } from "zod";
import { GeneratorGroupSchema } from "./GeneratorGroupSchema";
import { GeneratorsOpenAPISchema } from "./GeneratorsOpenAPISchema";
import { WhitelabelConfigurationSchema } from "./WhitelabelConfigurationSchema";

export const DEFAULT_GROUP_GENERATORS_CONFIG_KEY = "default-group";
Expand All @@ -9,8 +10,8 @@ export const ASYNC_API_LOCATION_KEY = "async-api";

export const GeneratorsConfigurationSchema = z.strictObject({
[DEFAULT_GROUP_GENERATORS_CONFIG_KEY]: z.optional(z.string()),
[OPENAPI_LOCATION_KEY]: z.optional(z.string()),
[OPENAPI_OVERRIDES_LOCATION_KEY]: z.optional(z.string()),
[OPENAPI_LOCATION_KEY]: z.optional(GeneratorsOpenAPISchema),
[OPENAPI_OVERRIDES_LOCATION_KEY]: z.optional(z.string()).describe("Deprecated; use openapi.overrides instead."),
[ASYNC_API_LOCATION_KEY]: z.optional(z.string()),
whitelabel: z.optional(WhitelabelConfigurationSchema),
groups: z.optional(z.record(GeneratorGroupSchema))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { z } from "zod";

export const OPENAPI_DISABLE_EXAMPLES_KEY = "disable-examples";

export const GeneratorsOpenAPIObjectSchema = z.strictObject({
path: z.optional(z.string()),
overrides: z.optional(z.string()),
[OPENAPI_DISABLE_EXAMPLES_KEY]: z.optional(z.boolean())
});

export type GeneratorsOpenAPIObjectSchema = z.infer<typeof GeneratorsOpenAPIObjectSchema>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { z } from "zod";
import { GeneratorsOpenAPIObjectSchema } from "./GeneratorsOpenAPIObjectSchema";

export const GeneratorsOpenAPISchema = z.union([GeneratorsOpenAPIObjectSchema, z.string()]);

export type GeneratorsOpenAPISchema = z.infer<typeof GeneratorsOpenAPISchema>;
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export function testConvertOpenAPI(fixtureName: string, filename: string, asyncA
absolutePathToOpenAPI: AbsoluteFilePath.of(openApiPath),
absolutePathToAsyncAPI,
absolutePathToOpenAPIOverrides: undefined,
taskContext: mockTaskContext
taskContext: mockTaskContext,
disableExamples: undefined
});
const fernDefinition = convert({ openApiIr, taskContext: mockTaskContext });
expect(fernDefinition).toMatchSnapshot();
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/openapi-parser/src/__test__/testParseOpenApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export function testParseOpenAPI(fixtureName: string, openApiFilename: string, a
absolutePathToAsyncAPI,
absolutePathToOpenAPI,
absolutePathToOpenAPIOverrides: undefined,
taskContext: createMockTaskContext({ logger: CONSOLE_LOGGER })
taskContext: createMockTaskContext({ logger: CONSOLE_LOGGER }),
disableExamples: undefined
});
const openApiIrJson = await serialization.OpenApiIntermediateRepresentation.jsonOrThrow(openApiIr, {
skipValidation: true
Expand Down
14 changes: 12 additions & 2 deletions packages/cli/openapi-parser/src/openapi/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ export async function parse({
absolutePathToAsyncAPI,
absolutePathToOpenAPI,
absolutePathToOpenAPIOverrides,
disableExamples,
taskContext
}: {
absolutePathToAsyncAPI: AbsoluteFilePath | undefined;
absolutePathToOpenAPI: AbsoluteFilePath;
absolutePathToOpenAPIOverrides: AbsoluteFilePath | undefined;
disableExamples: boolean | undefined;
taskContext: TaskContext;
}): Promise<OpenApiIntermediateRepresentation> {
let parsedAsyncAPI: AsyncAPIIntermediateRepresentation = {
Expand All @@ -47,9 +49,17 @@ export async function parse({
});
let openApiIr: OpenApiIntermediateRepresentation | undefined = undefined;
if (isOpenApiV3(openApiDocument)) {
openApiIr = generateIrFromV3(openApiDocument, taskContext);
openApiIr = generateIrFromV3({
openApi: openApiDocument,
taskContext,
disableExamples
});
} else if (isOpenApiV2(openApiDocument)) {
openApiIr = await generateIrFromV2(openApiDocument, taskContext);
openApiIr = await generateIrFromV2({
openApi: openApiDocument,
taskContext,
disableExamples
});
}

if (openApiIr != null) {
Expand Down
19 changes: 14 additions & 5 deletions packages/cli/openapi-parser/src/openapi/v2/generateIr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ import { OpenAPIV2 } from "openapi-types";
import { convertObj } from "swagger2openapi";
import { generateIr as generateIrFromV3 } from "../v3/generateIr";

export async function generateIr(
openApi: OpenAPIV2.Document,
taskContext: TaskContext
): Promise<OpenApiIntermediateRepresentation> {
export async function generateIr({
openApi,
taskContext,
disableExamples
}: {
openApi: OpenAPIV2.Document;
taskContext: TaskContext;
disableExamples: boolean | undefined;
}): Promise<OpenApiIntermediateRepresentation> {
const conversionResult = await convertObj(openApi, {});
return generateIrFromV3(conversionResult.openapi, taskContext);
return generateIrFromV3({
openApi: conversionResult.openapi,
taskContext,
disableExamples
});
}
12 changes: 10 additions & 2 deletions packages/cli/openapi-parser/src/openapi/v3/generateIr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ import { getVariableDefinitions } from "./extensions/getVariableDefinitions";
import { OpenAPIV3ParserContext } from "./OpenAPIV3ParserContext";
import { runResolutions } from "./runResolutions";

export function generateIr(openApi: OpenAPIV3.Document, taskContext: TaskContext): OpenApiIntermediateRepresentation {
export function generateIr({
openApi,
taskContext,
disableExamples
}: {
openApi: OpenAPIV3.Document;
taskContext: TaskContext;
disableExamples: boolean | undefined;
}): OpenApiIntermediateRepresentation {
openApi = runResolutions({ openapi: openApi });

const securitySchemes: Record<string, SecurityScheme> = Object.fromEntries(
Expand Down Expand Up @@ -118,7 +126,7 @@ export function generateIr(openApi: OpenAPIV3.Document, taskContext: TaskContext
const endpoints = endpointsWithExample.map((endpointWithExample): Endpoint => {
// if x-fern-examples is not present, generate an example
let examples = endpointWithExample.examples;
if (examples.length === 0 || examples.every(hasIncompleteExample)) {
if (!disableExamples && (examples.length === 0 || examples.every(hasIncompleteExample))) {
const endpointExample = exampleEndpointFactory.buildEndpointExample(endpointWithExample);
if (endpointExample != null) {
examples = [endpointExample, ...endpointWithExample.examples];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export async function getOpenAPIIRFromOpenAPIWorkspace(
absolutePathToAsyncAPI: openapiWorkspace.absolutePathToAsyncAPI,
absolutePathToOpenAPI: openapiWorkspace.absolutePathToOpenAPI,
absolutePathToOpenAPIOverrides: openapiWorkspace.generatorsConfiguration?.absolutePathToOpenAPIOverrides,
disableExamples: openapiWorkspace.generatorsConfiguration?.disableOpenAPIExamples,
taskContext: context
});
}
Expand Down

0 comments on commit f8206c8

Please sign in to comment.