diff --git a/packages/cli/config-management/generators-configuration/src/GeneratorsConfiguration.ts b/packages/cli/config-management/generators-configuration/src/GeneratorsConfiguration.ts index 9b107acce2e..2b8e2c11cfd 100644 --- a/packages/cli/config-management/generators-configuration/src/GeneratorsConfiguration.ts +++ b/packages/cli/config-management/generators-configuration/src/GeneratorsConfiguration.ts @@ -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[]; diff --git a/packages/cli/config-management/generators-configuration/src/__test__/convertGeneratorsConfiguration.test.ts b/packages/cli/config-management/generators-configuration/src/__test__/convertGeneratorsConfiguration.test.ts index 67f2a46b27a..f3e7076dd1f 100644 --- a/packages/cli/config-management/generators-configuration/src/__test__/convertGeneratorsConfiguration.test.ts +++ b/packages/cli/config-management/generators-configuration/src/__test__/convertGeneratorsConfiguration.test.ts @@ -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); + }); }); diff --git a/packages/cli/config-management/generators-configuration/src/convertGeneratorsConfiguration.ts b/packages/cli/config-management/generators-configuration/src/convertGeneratorsConfiguration.ts index 528241e5e6d..7bccbb13ad5 100644 --- a/packages/cli/config-management/generators-configuration/src/convertGeneratorsConfiguration.ts +++ b/packages/cli/config-management/generators-configuration/src/convertGeneratorsConfiguration.ts @@ -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({ @@ -27,9 +28,8 @@ export async function convertGeneratorsConfiguration({ absolutePathToGeneratorsConfiguration: AbsoluteFilePath; rawGeneratorsConfiguration: GeneratorsConfigurationSchema; }): Promise { - 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: @@ -37,13 +37,14 @@ export async function convertGeneratorsConfiguration({ ? 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: @@ -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 { + 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, diff --git a/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsConfigurationSchema.ts b/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsConfigurationSchema.ts index a7d39da7d76..6dec9c63860 100644 --- a/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsConfigurationSchema.ts +++ b/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsConfigurationSchema.ts @@ -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"; @@ -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)) diff --git a/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsOpenAPIObjectSchema.ts b/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsOpenAPIObjectSchema.ts new file mode 100644 index 00000000000..db4c1380f31 --- /dev/null +++ b/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsOpenAPIObjectSchema.ts @@ -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; diff --git a/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsOpenAPISchema.ts b/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsOpenAPISchema.ts new file mode 100644 index 00000000000..d6dc84f646d --- /dev/null +++ b/packages/cli/config-management/generators-configuration/src/schemas/GeneratorsOpenAPISchema.ts @@ -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; diff --git a/packages/cli/openapi-ir-to-fern/src/__test__/testConvertOpenApi.ts b/packages/cli/openapi-ir-to-fern/src/__test__/testConvertOpenApi.ts index c0fbda91822..a43e6d663f3 100644 --- a/packages/cli/openapi-ir-to-fern/src/__test__/testConvertOpenApi.ts +++ b/packages/cli/openapi-ir-to-fern/src/__test__/testConvertOpenApi.ts @@ -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(); diff --git a/packages/cli/openapi-parser/src/__test__/testParseOpenApi.ts b/packages/cli/openapi-parser/src/__test__/testParseOpenApi.ts index 09f223b2d00..8f844761252 100644 --- a/packages/cli/openapi-parser/src/__test__/testParseOpenApi.ts +++ b/packages/cli/openapi-parser/src/__test__/testParseOpenApi.ts @@ -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 diff --git a/packages/cli/openapi-parser/src/openapi/parse.ts b/packages/cli/openapi-parser/src/openapi/parse.ts index 1df73e0c55b..67f91a6c1cf 100644 --- a/packages/cli/openapi-parser/src/openapi/parse.ts +++ b/packages/cli/openapi-parser/src/openapi/parse.ts @@ -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 { let parsedAsyncAPI: AsyncAPIIntermediateRepresentation = { @@ -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) { diff --git a/packages/cli/openapi-parser/src/openapi/v2/generateIr.ts b/packages/cli/openapi-parser/src/openapi/v2/generateIr.ts index b737d4542f8..1ad8423cbc1 100644 --- a/packages/cli/openapi-parser/src/openapi/v2/generateIr.ts +++ b/packages/cli/openapi-parser/src/openapi/v2/generateIr.ts @@ -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 { +export async function generateIr({ + openApi, + taskContext, + disableExamples +}: { + openApi: OpenAPIV2.Document; + taskContext: TaskContext; + disableExamples: boolean | undefined; +}): Promise { const conversionResult = await convertObj(openApi, {}); - return generateIrFromV3(conversionResult.openapi, taskContext); + return generateIrFromV3({ + openApi: conversionResult.openapi, + taskContext, + disableExamples + }); } diff --git a/packages/cli/openapi-parser/src/openapi/v3/generateIr.ts b/packages/cli/openapi-parser/src/openapi/v3/generateIr.ts index 0c2fcedcef0..0e75edbe90f 100644 --- a/packages/cli/openapi-parser/src/openapi/v3/generateIr.ts +++ b/packages/cli/openapi-parser/src/openapi/v3/generateIr.ts @@ -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 = Object.fromEntries( @@ -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]; diff --git a/packages/cli/workspace-loader/src/utils/convertOpenApiWorkspaceToFernWorkspace.ts b/packages/cli/workspace-loader/src/utils/convertOpenApiWorkspaceToFernWorkspace.ts index fc7ab1908f3..a39840805d7 100644 --- a/packages/cli/workspace-loader/src/utils/convertOpenApiWorkspaceToFernWorkspace.ts +++ b/packages/cli/workspace-loader/src/utils/convertOpenApiWorkspaceToFernWorkspace.ts @@ -16,6 +16,7 @@ export async function getOpenAPIIRFromOpenAPIWorkspace( absolutePathToAsyncAPI: openapiWorkspace.absolutePathToAsyncAPI, absolutePathToOpenAPI: openapiWorkspace.absolutePathToOpenAPI, absolutePathToOpenAPIOverrides: openapiWorkspace.generatorsConfiguration?.absolutePathToOpenAPIOverrides, + disableExamples: openapiWorkspace.generatorsConfiguration?.disableOpenAPIExamples, taskContext: context }); }