diff --git a/README.md b/README.md index 0d201eb88e9..a61a4d0d182 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@
- header + + + + logo +
diff --git a/fern/images/logo-white.svg b/fern/images/logo-white.svg index 8f781d471d6..c409709eca2 100644 --- a/fern/images/logo-white.svg +++ b/fern/images/logo-white.svg @@ -1,4 +1,4 @@ - + diff --git a/fern/pages/changelogs/cli/2024-12-05.mdx b/fern/pages/changelogs/cli/2024-12-05.mdx new file mode 100644 index 00000000000..8dbf596d6c6 --- /dev/null +++ b/fern/pages/changelogs/cli/2024-12-05.mdx @@ -0,0 +1,4 @@ +## 0.45.3 +**`(fix):`** Unknown schemas are no longer incorrectly marked as `additionalProperties: true`. + + diff --git a/packages/cli/api-importers/openapi/openapi-ir-parser/src/index.ts b/packages/cli/api-importers/openapi/openapi-ir-parser/src/index.ts index a8be3e8bc17..cd5da9571d7 100644 --- a/packages/cli/api-importers/openapi/openapi-ir-parser/src/index.ts +++ b/packages/cli/api-importers/openapi/openapi-ir-parser/src/index.ts @@ -1,4 +1,4 @@ -export { type ParseOpenAPIOptions } from "./options"; -export { parse, type Document, type OpenAPIDocument, type SpecImportSettings } from "./parse"; +export { type ParseOpenAPIOptions, getParseOptions } from "./options"; +export { parse, type Document, type OpenAPIDocument } from "./parse"; export { FernOpenAPIExtension, XFernStreaming, FERN_TYPE_EXTENSIONS } from "./openapi/v3/extensions/fernExtensions"; export * from "./asyncapi/v2"; diff --git a/packages/cli/api-importers/openapi/openapi-ir-parser/src/openapi/v3/AbstractOpenAPIV3ParserContext.ts b/packages/cli/api-importers/openapi/openapi-ir-parser/src/openapi/v3/AbstractOpenAPIV3ParserContext.ts index 3af2189c53f..dea8ec2eb5a 100644 --- a/packages/cli/api-importers/openapi/openapi-ir-parser/src/openapi/v3/AbstractOpenAPIV3ParserContext.ts +++ b/packages/cli/api-importers/openapi/openapi-ir-parser/src/openapi/v3/AbstractOpenAPIV3ParserContext.ts @@ -106,17 +106,16 @@ export abstract class AbstractOpenAPIV3ParserContext implements SchemaParserCont for (const key of keys) { if (typeof resolvedSchema !== "object" || resolvedSchema == null) { return { - "x-fern-type": "unknown", - additionalProperties: true + "x-fern-type": "unknown" // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any as OpenAPIV3.SchemaObject; } resolvedSchema = resolvedSchema[key]; } if (resolvedSchema == null) { + this.logger.warn(`Encountered undefined reference: ${schema.$ref}`); return { - "x-fern-type": "unknown", - additionalProperties: true + "x-fern-type": "unknown" // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any as OpenAPIV3.SchemaObject; } diff --git a/packages/cli/api-importers/openapi/openapi-ir-parser/src/options.ts b/packages/cli/api-importers/openapi/openapi-ir-parser/src/options.ts index 4d35dc3127c..5a354e73828 100644 --- a/packages/cli/api-importers/openapi/openapi-ir-parser/src/options.ts +++ b/packages/cli/api-importers/openapi/openapi-ir-parser/src/options.ts @@ -1,4 +1,5 @@ export interface ParseOpenAPIOptions { + /* Whether or not to disable OpenAPI example generation */ disableExamples: boolean; /* * Parses discriminated unions as undiscriminated unions with literals. @@ -21,6 +22,14 @@ export interface ParseOpenAPIOptions { inlinePathParameters: boolean; /* Whether or not to preserve original schema Ids in the IR */ preserveSchemaIds: boolean; + /* Whether or not to parse object query parameters. */ + objectQueryParameters: boolean; + /* Whether or not to use undiscriminated unions with literals. */ + shouldUseUndiscriminatedUnionsWithLiterals: boolean; + + // For now, we include an AsyncAPI-specific option here, but this is better + // handled with a discriminated union. + asyncApiNaming: "v1" | "v2"; } export const DEFAULT_PARSE_OPENAPI_SETTINGS: ParseOpenAPIOptions = { @@ -33,5 +42,58 @@ export const DEFAULT_PARSE_OPENAPI_SETTINGS: ParseOpenAPIOptions = { respectReadonlySchemas: false, onlyIncludeReferencedSchemas: false, inlinePathParameters: false, - preserveSchemaIds: false + preserveSchemaIds: false, + objectQueryParameters: false, + shouldUseUndiscriminatedUnionsWithLiterals: false, + asyncApiNaming: "v1" }; + +export function getParseOptions({ + options, + overrides +}: { + options?: ParseOpenAPIOptions; + overrides?: Partial; +}): ParseOpenAPIOptions { + return { + disableExamples: overrides?.disableExamples ?? DEFAULT_PARSE_OPENAPI_SETTINGS.disableExamples, + discriminatedUnionV2: + overrides?.discriminatedUnionV2 ?? + options?.discriminatedUnionV2 ?? + DEFAULT_PARSE_OPENAPI_SETTINGS.discriminatedUnionV2, + useTitlesAsName: + overrides?.useTitlesAsName ?? options?.useTitlesAsName ?? DEFAULT_PARSE_OPENAPI_SETTINGS.useTitlesAsName, + audiences: overrides?.audiences ?? options?.audiences ?? DEFAULT_PARSE_OPENAPI_SETTINGS.audiences, + optionalAdditionalProperties: + overrides?.optionalAdditionalProperties ?? + options?.optionalAdditionalProperties ?? + DEFAULT_PARSE_OPENAPI_SETTINGS.optionalAdditionalProperties, + cooerceEnumsToLiterals: + overrides?.cooerceEnumsToLiterals ?? + options?.cooerceEnumsToLiterals ?? + DEFAULT_PARSE_OPENAPI_SETTINGS.cooerceEnumsToLiterals, + respectReadonlySchemas: + overrides?.respectReadonlySchemas ?? + options?.respectReadonlySchemas ?? + DEFAULT_PARSE_OPENAPI_SETTINGS.respectReadonlySchemas, + onlyIncludeReferencedSchemas: + overrides?.onlyIncludeReferencedSchemas ?? + options?.onlyIncludeReferencedSchemas ?? + DEFAULT_PARSE_OPENAPI_SETTINGS.onlyIncludeReferencedSchemas, + inlinePathParameters: + overrides?.inlinePathParameters ?? + options?.inlinePathParameters ?? + DEFAULT_PARSE_OPENAPI_SETTINGS.inlinePathParameters, + preserveSchemaIds: overrides?.preserveSchemaIds ?? DEFAULT_PARSE_OPENAPI_SETTINGS.preserveSchemaIds, + shouldUseUndiscriminatedUnionsWithLiterals: + overrides?.shouldUseUndiscriminatedUnionsWithLiterals ?? + options?.shouldUseUndiscriminatedUnionsWithLiterals ?? + DEFAULT_PARSE_OPENAPI_SETTINGS.shouldUseUndiscriminatedUnionsWithLiterals, + objectQueryParameters: + overrides?.objectQueryParameters ?? + options?.objectQueryParameters ?? + DEFAULT_PARSE_OPENAPI_SETTINGS.objectQueryParameters, + asyncApiNaming: + overrides?.asyncApiNaming ?? options?.asyncApiNaming ?? DEFAULT_PARSE_OPENAPI_SETTINGS.asyncApiNaming + }; +} diff --git a/packages/cli/api-importers/openapi/openapi-ir-parser/src/parse.ts b/packages/cli/api-importers/openapi/openapi-ir-parser/src/parse.ts index 1001535898b..54b5ffac3e7 100644 --- a/packages/cli/api-importers/openapi/openapi-ir-parser/src/parse.ts +++ b/packages/cli/api-importers/openapi/openapi-ir-parser/src/parse.ts @@ -6,7 +6,7 @@ import { DEFAULT_PARSE_ASYNCAPI_SETTINGS, ParseAsyncAPIOptions } from "./asyncap import { parseAsyncAPI } from "./asyncapi/parse"; import { AsyncAPIV2 } from "./asyncapi/v2"; import { generateIr as generateIrFromV3 } from "./openapi/v3/generateIr"; -import { DEFAULT_PARSE_OPENAPI_SETTINGS, ParseOpenAPIOptions } from "./options"; +import { getParseOptions, ParseOpenAPIOptions } from "./options"; export type Document = OpenAPIDocument | AsyncAPIDocument; @@ -15,7 +15,7 @@ export interface OpenAPIDocument { value: OpenAPIV3.Document; source?: OpenApiIrSource; namespace?: string; - settings?: SpecImportSettings; + settings?: ParseOpenAPIOptions; } export interface AsyncAPIDocument { @@ -23,20 +23,7 @@ export interface AsyncAPIDocument { value: AsyncAPIV2.Document; source?: OpenApiIrSource; namespace?: string; - settings?: SpecImportSettings; -} - -export interface SpecImportSettings { - audiences: string[]; - shouldUseTitleAsName: boolean; - shouldUseUndiscriminatedUnionsWithLiterals: boolean; - asyncApiNaming?: "v1" | "v2"; - optionalAdditionalProperties: boolean; - cooerceEnumsToLiterals: boolean; - objectQueryParameters: boolean; - respectReadonlySchemas: boolean; - onlyIncludeReferencedSchemas: boolean; - inlinePathParameters: boolean; + settings?: ParseOpenAPIOptions; } export async function parse({ @@ -80,7 +67,7 @@ export async function parse({ const openapiIr = generateIrFromV3({ taskContext: context, openApi: document.value, - options: getParseOptions({ specSettings: document.settings, overrides: options }), + options: getParseOptions({ options: document.settings, overrides: options }), source, namespace: document.namespace }); @@ -91,9 +78,9 @@ export async function parse({ const parsedAsyncAPI = parseAsyncAPI({ document: document.value, taskContext: context, - options: getParseOptions({ specSettings: document.settings }), + options: getParseOptions({ options: document.settings, overrides: options }), source, - asyncApiOptions: getParseAsyncOptions({ specSettings: document.settings }), + asyncApiOptions: getParseAsyncOptions({ options: document.settings }), namespace: document.namespace }); if (parsedAsyncAPI.channel != null) { @@ -114,57 +101,15 @@ export async function parse({ return ir; } -export function getParseOptions({ - specSettings, - overrides -}: { - specSettings?: SpecImportSettings; - overrides?: Partial; -}): ParseOpenAPIOptions { - return { - disableExamples: overrides?.disableExamples ?? DEFAULT_PARSE_OPENAPI_SETTINGS.disableExamples, - discriminatedUnionV2: - overrides?.discriminatedUnionV2 ?? - specSettings?.shouldUseUndiscriminatedUnionsWithLiterals ?? - DEFAULT_PARSE_OPENAPI_SETTINGS.discriminatedUnionV2, - useTitlesAsName: - overrides?.useTitlesAsName ?? - specSettings?.shouldUseTitleAsName ?? - DEFAULT_PARSE_OPENAPI_SETTINGS.useTitlesAsName, - audiences: overrides?.audiences ?? specSettings?.audiences ?? DEFAULT_PARSE_OPENAPI_SETTINGS.audiences, - optionalAdditionalProperties: - overrides?.optionalAdditionalProperties ?? - specSettings?.optionalAdditionalProperties ?? - DEFAULT_PARSE_OPENAPI_SETTINGS.optionalAdditionalProperties, - cooerceEnumsToLiterals: - overrides?.cooerceEnumsToLiterals ?? - specSettings?.cooerceEnumsToLiterals ?? - DEFAULT_PARSE_OPENAPI_SETTINGS.cooerceEnumsToLiterals, - respectReadonlySchemas: - overrides?.respectReadonlySchemas ?? - specSettings?.respectReadonlySchemas ?? - DEFAULT_PARSE_OPENAPI_SETTINGS.respectReadonlySchemas, - onlyIncludeReferencedSchemas: - overrides?.onlyIncludeReferencedSchemas ?? - specSettings?.onlyIncludeReferencedSchemas ?? - DEFAULT_PARSE_OPENAPI_SETTINGS.onlyIncludeReferencedSchemas, - inlinePathParameters: - overrides?.inlinePathParameters ?? - specSettings?.inlinePathParameters ?? - DEFAULT_PARSE_OPENAPI_SETTINGS.inlinePathParameters, - preserveSchemaIds: overrides?.preserveSchemaIds ?? DEFAULT_PARSE_OPENAPI_SETTINGS.preserveSchemaIds - }; -} - function getParseAsyncOptions({ - specSettings, + options, overrides }: { - specSettings?: SpecImportSettings; + options?: ParseOpenAPIOptions; overrides?: Partial; }): ParseAsyncAPIOptions { return { - naming: overrides?.naming ?? specSettings?.asyncApiNaming ?? DEFAULT_PARSE_ASYNCAPI_SETTINGS.naming + naming: overrides?.naming ?? options?.asyncApiNaming ?? DEFAULT_PARSE_ASYNCAPI_SETTINGS.naming }; } diff --git a/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/ConvertOpenAPIOptions.ts b/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/ConvertOpenAPIOptions.ts new file mode 100644 index 00000000000..71eca71f9ba --- /dev/null +++ b/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/ConvertOpenAPIOptions.ts @@ -0,0 +1,77 @@ +export interface ConvertOpenAPIOptions { + /** + * If true, each error will be made unique per endpoint. This is the preferred behavior for Docs. + * If false, error codes will be shared across endpoints. The side effect is that if more than one error schema is detected for each error code, then the error schema will default to unknown. This is the preferred behavior for SDKs. + */ + enableUniqueErrorsPerEndpoint: boolean; + + /** + * If true, the converter will detect frequently headers and add extract them as global headers within + * the IR. This is primarily used for generating SDKs, but disabled for docs as it allows the documentation + */ + detectGlobalHeaders: boolean; + + /** + * If true, the converter will generate complex query parameters in the generated Fern Definition. + */ + objectQueryParameters: boolean; + + /** + * If true, the converter will respect readonly properties in OpenAPI schemas. + */ + respectReadonlySchemas: boolean; + + /** + * If true, the converter will only include schemas referenced by endpoints. + */ + onlyIncludeReferencedSchemas: boolean; + + /** + * If true, the converter will include path parameters in the in-lined request. + */ + inlinePathParameters: boolean; +} + +export const DEFAULT_CONVERT_OPENAPI_OPTIONS: ConvertOpenAPIOptions = { + enableUniqueErrorsPerEndpoint: false, + detectGlobalHeaders: true, + objectQueryParameters: false, + respectReadonlySchemas: false, + onlyIncludeReferencedSchemas: false, + inlinePathParameters: false +}; + +export function getConvertOptions({ + options, + overrides +}: { + options?: ConvertOpenAPIOptions; + overrides?: Partial; +}): ConvertOpenAPIOptions { + return { + enableUniqueErrorsPerEndpoint: + overrides?.enableUniqueErrorsPerEndpoint ?? + options?.enableUniqueErrorsPerEndpoint ?? + DEFAULT_CONVERT_OPENAPI_OPTIONS.enableUniqueErrorsPerEndpoint, + detectGlobalHeaders: + overrides?.detectGlobalHeaders ?? + options?.detectGlobalHeaders ?? + DEFAULT_CONVERT_OPENAPI_OPTIONS.detectGlobalHeaders, + objectQueryParameters: + overrides?.objectQueryParameters ?? + options?.objectQueryParameters ?? + DEFAULT_CONVERT_OPENAPI_OPTIONS.objectQueryParameters, + respectReadonlySchemas: + overrides?.respectReadonlySchemas ?? + options?.respectReadonlySchemas ?? + DEFAULT_CONVERT_OPENAPI_OPTIONS.respectReadonlySchemas, + onlyIncludeReferencedSchemas: + overrides?.onlyIncludeReferencedSchemas ?? + options?.onlyIncludeReferencedSchemas ?? + DEFAULT_CONVERT_OPENAPI_OPTIONS.onlyIncludeReferencedSchemas, + inlinePathParameters: + overrides?.inlinePathParameters ?? + options?.inlinePathParameters ?? + DEFAULT_CONVERT_OPENAPI_OPTIONS.inlinePathParameters + }; +} diff --git a/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/OpenApiIrConverterContext.ts b/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/OpenApiIrConverterContext.ts index 8bcd1a6ed77..632b1228819 100644 --- a/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/OpenApiIrConverterContext.ts +++ b/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/OpenApiIrConverterContext.ts @@ -13,48 +13,16 @@ import { TaskContext } from "@fern-api/task-context"; import { FernDefinitionBuilder, FernDefinitionBuilderImpl } from "@fern-api/importer-commons"; import { isSchemaEqual } from "@fern-api/openapi-ir"; import { State } from "./State"; +import { ConvertOpenAPIOptions } from "./ConvertOpenAPIOptions"; export interface OpenApiIrConverterContextOpts { taskContext: TaskContext; ir: OpenApiIntermediateRepresentation; - /** - * If true, each error will be made unique per endpoint. This is the preferred behavior for Docs. - * If false, error codes will be shared across endpoints. The side effect is that if more than one error schema is detected for each error code, then the error schema will default to unknown. This is the preferred behavior for SDKs. - */ - enableUniqueErrorsPerEndpoint: boolean; - - /** - * If true, the converter will detect frequently headers and add extract them as global headers within - * the IR. This is primarily used for generating SDKs, but disabled for docs as it allows the documentation - */ - detectGlobalHeaders: boolean; - + options?: ConvertOpenAPIOptions; authOverrides?: RawSchemas.WithAuthSchema; - environmentOverrides?: RawSchemas.WithEnvironmentsSchema; - globalHeaderOverrides?: RawSchemas.WithHeadersSchema; - - /** - * If true, the converter will generate complex query parameters in the generated Fern Definition. - */ - objectQueryParameters: boolean; - - /** - * If true, the converter will respect readonly properties in OpenAPI schemas. - */ - respectReadonlySchemas: boolean; - - /** - * If true, the converter will only include schemas referenced by endpoints. - */ - onlyIncludeReferencedSchemas: boolean; - - /** - * If true, the converter will include path parameters in the in-lined request. - */ - inlinePathParameters: boolean; } export class OpenApiIrConverterContext { @@ -99,33 +67,28 @@ export class OpenApiIrConverterContext { constructor({ taskContext, ir, - enableUniqueErrorsPerEndpoint, - detectGlobalHeaders, + options, environmentOverrides, globalHeaderOverrides, - authOverrides, - objectQueryParameters, - respectReadonlySchemas, - onlyIncludeReferencedSchemas, - inlinePathParameters + authOverrides }: OpenApiIrConverterContextOpts) { this.logger = taskContext.logger; this.taskContext = taskContext; this.ir = ir; - this.enableUniqueErrorsPerEndpoint = enableUniqueErrorsPerEndpoint; - this.builder = new FernDefinitionBuilderImpl(enableUniqueErrorsPerEndpoint); - if (ir.title != null) { - this.builder.setDisplayName({ displayName: ir.title }); - } - this.detectGlobalHeaders = detectGlobalHeaders; this.environmentOverrides = environmentOverrides; this.authOverrides = authOverrides; this.globalHeaderOverrides = globalHeaderOverrides; - this.objectQueryParameters = objectQueryParameters; - this.respectReadonlySchemas = respectReadonlySchemas; - this.onlyIncludeReferencedSchemas = onlyIncludeReferencedSchemas; - this.inlinePathParameters = inlinePathParameters; - this.referencedSchemaIds = onlyIncludeReferencedSchemas ? new Set() : undefined; + this.detectGlobalHeaders = options?.detectGlobalHeaders ?? true; + this.objectQueryParameters = options?.objectQueryParameters ?? false; + this.respectReadonlySchemas = options?.respectReadonlySchemas ?? false; + this.onlyIncludeReferencedSchemas = options?.onlyIncludeReferencedSchemas ?? false; + this.inlinePathParameters = options?.inlinePathParameters ?? false; + this.referencedSchemaIds = options?.onlyIncludeReferencedSchemas ? new Set() : undefined; + this.enableUniqueErrorsPerEndpoint = options?.enableUniqueErrorsPerEndpoint ?? false; + this.builder = new FernDefinitionBuilderImpl(this.enableUniqueErrorsPerEndpoint); + if (ir.title != null) { + this.builder.setDisplayName({ displayName: ir.title }); + } const schemaByStatusCode: Record = {}; if (!this.enableUniqueErrorsPerEndpoint) { diff --git a/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/index.ts b/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/index.ts index 8dd87db9b7f..e163c194bb1 100644 --- a/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/index.ts +++ b/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/index.ts @@ -1,2 +1,3 @@ export { convert, type OpenApiConvertedFernDefinition } from "./convert"; export { getEndpointLocation } from "./utils/getEndpointLocation"; +export { getConvertOptions, type ConvertOpenAPIOptions } from "./ConvertOpenAPIOptions"; diff --git a/packages/cli/cli/versions.yml b/packages/cli/cli/versions.yml index eb8fa2f0699..b5799dbfe10 100644 --- a/packages/cli/cli/versions.yml +++ b/packages/cli/cli/versions.yml @@ -1,3 +1,10 @@ +- changelogEntry: + - summary: | + Unknown schemas are no longer incorrectly marked as `additionalProperties: true`. + type: fix + irVersion: 53 + version: 0.45.3 + - changelogEntry: - summary: | Example generation now respects read-only schemas when generating request examples. diff --git a/packages/cli/workspace/browser-compatible-fern-workspace/src/InMemoryOpenAPILoader.ts b/packages/cli/workspace/browser-compatible-fern-workspace/src/InMemoryOpenAPILoader.ts index 34a554a1698..297636b0511 100644 --- a/packages/cli/workspace/browser-compatible-fern-workspace/src/InMemoryOpenAPILoader.ts +++ b/packages/cli/workspace/browser-compatible-fern-workspace/src/InMemoryOpenAPILoader.ts @@ -1,24 +1,19 @@ -import { OpenAPIDocument, SpecImportSettings } from "@fern-api/openapi-ir-parser"; +import { getParseOptions, OpenAPIDocument } from "@fern-api/openapi-ir-parser"; +import { OpenAPIWorkspace } from "./OpenAPIWorkspace"; import { OpenAPI, OpenAPIV3 } from "openapi-types"; import { bundle, Source } from "@redocly/openapi-core"; import { DEFAULT_OPENAPI_BUNDLE_OPTIONS, isOpenAPIV2 } from "@fern-api/api-workspace-commons"; import { mergeWithOverrides as coreMergeWithOverrides } from "@fern-api/core-utils"; -export interface Spec { - parsed: OpenAPI.Document; - overrides?: OpenAPI.Document; - settings?: SpecImportSettings; -} - export class InMemoryOpenAPILoader { - public async loadDocument(spec: Spec): Promise { + public async loadDocument(spec: OpenAPIWorkspace.Spec): Promise { return { type: "openapi", value: await this.loadParsedOpenAPI({ openapi: spec.parsed, overrides: spec.overrides }), - settings: spec.settings + settings: spec.settings != null ? getParseOptions({ overrides: spec.settings }) : undefined }; } diff --git a/packages/cli/workspace/browser-compatible-fern-workspace/src/OpenAPIWorkspace.ts b/packages/cli/workspace/browser-compatible-fern-workspace/src/OpenAPIWorkspace.ts index d114c79683f..c7cd09e5324 100644 --- a/packages/cli/workspace/browser-compatible-fern-workspace/src/OpenAPIWorkspace.ts +++ b/packages/cli/workspace/browser-compatible-fern-workspace/src/OpenAPIWorkspace.ts @@ -1,9 +1,4 @@ -import { - AbstractAPIWorkspace, - BaseOpenAPIWorkspace, - getOptionsOverridesFromSettings -} from "@fern-api/api-workspace-commons"; -import { SpecImportSettings } from "@fern-api/openapi-ir-parser"; +import { BaseOpenAPIWorkspace } from "@fern-api/api-workspace-commons"; import { OpenAPI } from "openapi-types"; import { AbsoluteFilePath } from "@fern-api/path-utils"; import { TaskContext } from "@fern-api/task-context"; @@ -29,7 +24,7 @@ export declare namespace OpenAPIWorkspace { export interface Spec { parsed: OpenAPI.Document; overrides?: OpenAPI.Document; - settings?: SpecImportSettings; + settings?: Settings; } export type Settings = BaseOpenAPIWorkspace.Settings; @@ -43,10 +38,10 @@ export class OpenAPIWorkspace extends BaseOpenAPIWorkspace { super({ ...DEFAULT_WORKSPACE_ARGS, generatorsConfiguration, - respectReadonlySchemas: spec.settings?.respectReadonlySchemas ?? false, - onlyIncludeReferencedSchemas: spec.settings?.onlyIncludeReferencedSchemas ?? false, - inlinePathParameters: spec.settings?.inlinePathParameters ?? false, - objectQueryParameters: spec.settings?.objectQueryParameters ?? false + respectReadonlySchemas: spec.settings?.respectReadonlySchemas, + onlyIncludeReferencedSchemas: spec.settings?.onlyIncludeReferencedSchemas, + inlinePathParameters: spec.settings?.inlinePathParameters, + objectQueryParameters: spec.settings?.objectQueryParameters }); this.spec = spec; this.loader = new InMemoryOpenAPILoader(); @@ -58,17 +53,19 @@ export class OpenAPIWorkspace extends BaseOpenAPIWorkspace { }: { context: TaskContext; }, - settings?: OpenAPIWorkspace.Settings + options?: OpenAPIWorkspace.Settings ): Promise { - const optionOverrides = getOptionsOverridesFromSettings(settings); const document = await this.loader.loadDocument(this.spec); return await parse({ context, documents: [document], options: { - ...optionOverrides, - respectReadonlySchemas: this.respectReadonlySchemas, - onlyIncludeReferencedSchemas: this.onlyIncludeReferencedSchemas + ...options, + onlyIncludeReferencedSchemas: + options?.onlyIncludeReferencedSchemas ?? this.onlyIncludeReferencedSchemas, + respectReadonlySchemas: options?.respectReadonlySchemas ?? this.respectReadonlySchemas, + inlinePathParameters: options?.inlinePathParameters ?? this.inlinePathParameters, + objectQueryParameters: options?.objectQueryParameters ?? this.objectQueryParameters } }); } diff --git a/packages/cli/workspace/commons/src/Spec.ts b/packages/cli/workspace/commons/src/Spec.ts index cf355538336..cec86c94a72 100644 --- a/packages/cli/workspace/commons/src/Spec.ts +++ b/packages/cli/workspace/commons/src/Spec.ts @@ -1,6 +1,6 @@ +import { ParseOpenAPIOptions } from "@fern-api/openapi-ir-parser"; import { AbsoluteFilePath } from "@fern-api/path-utils"; import { Source } from "./Source"; -import { SpecImportSettings } from "@fern-api/openapi-ir-parser"; export type Spec = OpenAPISpec | ProtobufSpec; @@ -10,7 +10,7 @@ export interface OpenAPISpec { absoluteFilepathToOverrides: AbsoluteFilePath | undefined; source: Source; namespace?: string; - settings?: SpecImportSettings; + settings?: ParseOpenAPIOptions; } export interface ProtobufSpec { @@ -19,5 +19,5 @@ export interface ProtobufSpec { absoluteFilepathToProtobufTarget: AbsoluteFilePath; absoluteFilepathToOverrides: AbsoluteFilePath | undefined; generateLocally: boolean; - settings?: SpecImportSettings; + settings?: ParseOpenAPIOptions; } diff --git a/packages/cli/workspace/commons/src/openapi/BaseOpenAPIWorkspace.ts b/packages/cli/workspace/commons/src/openapi/BaseOpenAPIWorkspace.ts index a0c923eb0b4..22073077166 100644 --- a/packages/cli/workspace/commons/src/openapi/BaseOpenAPIWorkspace.ts +++ b/packages/cli/workspace/commons/src/openapi/BaseOpenAPIWorkspace.ts @@ -3,27 +3,27 @@ import { AbsoluteFilePath, RelativeFilePath } from "@fern-api/path-utils"; import { TaskContext } from "@fern-api/task-context"; import { OpenApiIntermediateRepresentation } from "@fern-api/openapi-ir"; import { mapValues } from "lodash-es"; -import { convert } from "@fern-api/openapi-ir-to-fern"; +import { convert, getConvertOptions } from "@fern-api/openapi-ir-to-fern"; import { FERN_PACKAGE_MARKER_FILENAME } from "@fern-api/configuration"; import { OpenAPISettings } from "./OpenAPISettings"; import yaml from "js-yaml"; export declare namespace BaseOpenAPIWorkspace { export interface Args extends AbstractAPIWorkspace.Args { - inlinePathParameters: boolean; - objectQueryParameters: boolean; - onlyIncludeReferencedSchemas: boolean; - respectReadonlySchemas: boolean; + inlinePathParameters: boolean | undefined; + objectQueryParameters: boolean | undefined; + onlyIncludeReferencedSchemas: boolean | undefined; + respectReadonlySchemas: boolean | undefined; } - export type Settings = OpenAPISettings; + export type Settings = Partial; } export abstract class BaseOpenAPIWorkspace extends AbstractAPIWorkspace { - public inlinePathParameters: boolean; - public objectQueryParameters: boolean; - public onlyIncludeReferencedSchemas: boolean; - public respectReadonlySchemas: boolean; + public inlinePathParameters: boolean | undefined; + public objectQueryParameters: boolean | undefined; + public onlyIncludeReferencedSchemas: boolean | undefined; + public respectReadonlySchemas: boolean | undefined; constructor(args: BaseOpenAPIWorkspace.Args) { super(args); @@ -47,6 +47,18 @@ export abstract class BaseOpenAPIWorkspace extends AbstractAPIWorkspace { const openApiIr = await this.getOpenAPIIr({ context, relativePathToDependency }, settings); const definition = convert({ + taskContext: context, + ir: openApiIr, + options: getConvertOptions({ + overrides: { + ...settings, + respectReadonlySchemas: settings?.respectReadonlySchemas ?? this.respectReadonlySchemas, + onlyIncludeReferencedSchemas: + settings?.onlyIncludeReferencedSchemas ?? this.onlyIncludeReferencedSchemas, + inlinePathParameters: settings?.inlinePathParameters ?? this.inlinePathParameters, + objectQueryParameters: settings?.objectQueryParameters ?? this.objectQueryParameters + } + }), authOverrides: this.generatorsConfiguration?.api?.auth != null ? { ...this.generatorsConfiguration?.api } : undefined, environmentOverrides: @@ -56,15 +68,7 @@ export abstract class BaseOpenAPIWorkspace extends AbstractAPIWorkspace | undefined { if (generatorInvocation.settings == null) { return undefined; } - const result: BaseOpenAPIWorkspace.Settings = { + const result: Partial = { detectGlobalHeaders: true }; if (generatorInvocation.settings.unions === "v1") { - result.enableDiscriminatedUnionV2 = true; + result.discriminatedUnionV2 = true; } return result; diff --git a/packages/cli/workspace/commons/src/openapi/getOptionsOverridesFromSettings.ts b/packages/cli/workspace/commons/src/openapi/getOptionsOverridesFromSettings.ts deleted file mode 100644 index c423cc1196d..00000000000 --- a/packages/cli/workspace/commons/src/openapi/getOptionsOverridesFromSettings.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { OpenAPISettings } from "./OpenAPISettings"; -import { ParseOpenAPIOptions } from "@fern-api/openapi-ir-parser"; - -export function getOptionsOverridesFromSettings(settings?: OpenAPISettings): Partial | undefined { - if (settings == null) { - return undefined; - } - const result: Partial = {}; - if (settings.enableDiscriminatedUnionV2) { - result.discriminatedUnionV2 = true; - } - if (settings.optionalAdditionalProperties) { - result.optionalAdditionalProperties = true; - } - if (settings.cooerceEnumsToLiterals) { - result.cooerceEnumsToLiterals = true; - } - if (settings.preserveSchemaIds) { - result.preserveSchemaIds = true; - } - return result; -} diff --git a/packages/cli/workspace/commons/src/openapi/index.ts b/packages/cli/workspace/commons/src/openapi/index.ts index f8077b674e3..62ee2901392 100644 --- a/packages/cli/workspace/commons/src/openapi/index.ts +++ b/packages/cli/workspace/commons/src/openapi/index.ts @@ -1,6 +1,5 @@ export { BaseOpenAPIWorkspace } from "./BaseOpenAPIWorkspace"; export { getBaseOpenAPIWorkspaceSettingsFromGeneratorInvocation } from "./getBaseOpenAPIWorkspaceSettingsFromGeneratorInvocation"; -export { getOptionsOverridesFromSettings } from "./getOptionsOverridesFromSettings"; export { type OpenAPISettings } from "./OpenAPISettings"; export { DEFAULT_OPENAPI_BUNDLE_OPTIONS } from "./constants"; export { isOpenAPIV2 } from "./isOpenAPIV2"; diff --git a/packages/cli/workspace/lazy-fern-workspace/src/OSSWorkspace.ts b/packages/cli/workspace/lazy-fern-workspace/src/OSSWorkspace.ts index 218dec1269d..f0e1839cea3 100644 --- a/packages/cli/workspace/lazy-fern-workspace/src/OSSWorkspace.ts +++ b/packages/cli/workspace/lazy-fern-workspace/src/OSSWorkspace.ts @@ -1,6 +1,6 @@ import { isNonNullish } from "@fern-api/core-utils"; import { AbsoluteFilePath, RelativeFilePath } from "@fern-api/fs-utils"; -import { parse, SpecImportSettings } from "@fern-api/openapi-ir-parser"; +import { parse } from "@fern-api/openapi-ir-parser"; import { TaskContext } from "@fern-api/task-context"; import { v4 as uuidv4 } from "uuid"; import { getAllOpenAPISpecs } from "./utils/getAllOpenAPISpecs"; @@ -8,7 +8,6 @@ import { AbstractAPIWorkspace, BaseOpenAPIWorkspace, FernWorkspace, - getOptionsOverridesFromSettings, IdentifiableSource, Spec } from "@fern-api/api-workspace-commons"; @@ -32,10 +31,10 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { constructor({ specs, ...superArgs }: OSSWorkspace.Args) { super({ ...superArgs, - respectReadonlySchemas: specs.every((spec) => spec.settings?.respectReadonlySchemas ?? false), - onlyIncludeReferencedSchemas: specs.every((spec) => spec.settings?.onlyIncludeReferencedSchemas ?? false), - inlinePathParameters: specs.every((spec) => spec.settings?.inlinePathParameters ?? false), - objectQueryParameters: specs.every((spec) => spec.settings?.objectQueryParameters ?? false) + respectReadonlySchemas: specs.every((spec) => spec.settings?.respectReadonlySchemas), + onlyIncludeReferencedSchemas: specs.every((spec) => spec.settings?.onlyIncludeReferencedSchemas), + inlinePathParameters: specs.every((spec) => spec.settings?.inlinePathParameters), + objectQueryParameters: specs.every((spec) => spec.settings?.objectQueryParameters) }); this.specs = specs; this.sources = this.convertSpecsToIdentifiableSources(specs); @@ -53,7 +52,6 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { settings?: OSSWorkspace.Settings ): Promise { const openApiSpecs = await getAllOpenAPISpecs({ context, specs: this.specs, relativePathToDependency }); - const optionOverrides = getOptionsOverridesFromSettings(settings); return await parse({ context, documents: await this.loader.loadDocuments({ @@ -61,9 +59,12 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { specs: openApiSpecs }), options: { - ...optionOverrides, - respectReadonlySchemas: this.respectReadonlySchemas, - onlyIncludeReferencedSchemas: this.onlyIncludeReferencedSchemas + ...settings, + respectReadonlySchemas: settings?.respectReadonlySchemas ?? this.respectReadonlySchemas, + onlyIncludeReferencedSchemas: + settings?.onlyIncludeReferencedSchemas ?? this.onlyIncludeReferencedSchemas, + inlinePathParameters: settings?.inlinePathParameters ?? this.inlinePathParameters, + objectQueryParameters: settings?.objectQueryParameters ?? this.objectQueryParameters } }); } diff --git a/packages/cli/workspace/loader/src/loadAPIWorkspace.ts b/packages/cli/workspace/loader/src/loadAPIWorkspace.ts index 12898a7d96b..805b81591f0 100644 --- a/packages/cli/workspace/loader/src/loadAPIWorkspace.ts +++ b/packages/cli/workspace/loader/src/loadAPIWorkspace.ts @@ -71,7 +71,7 @@ export async function loadSingleNamespaceAPIWorkspace({ generateLocally: definition.schema.localGeneration, settings: { audiences: definition.audiences ?? [], - shouldUseTitleAsName: definition.settings?.shouldUseTitleAsName ?? true, + useTitlesAsName: definition.settings?.shouldUseTitleAsName ?? true, shouldUseUndiscriminatedUnionsWithLiterals: definition.settings?.shouldUseUndiscriminatedUnionsWithLiterals ?? false, optionalAdditionalProperties: definition.settings?.shouldUseOptionalAdditionalProperties ?? true, @@ -79,7 +79,11 @@ export async function loadSingleNamespaceAPIWorkspace({ objectQueryParameters: definition.settings?.objectQueryParameters ?? false, respectReadonlySchemas: definition.settings?.respectReadonlySchemas ?? false, onlyIncludeReferencedSchemas: definition.settings?.onlyIncludeReferencedSchemas ?? false, - inlinePathParameters: definition.settings?.inlinePathParameters ?? false + inlinePathParameters: definition.settings?.inlinePathParameters ?? false, + disableExamples: false, + discriminatedUnionV2: false, + preserveSchemaIds: false, + asyncApiNaming: definition.settings?.asyncApiMessageNaming ?? "v1" } }); continue; @@ -115,16 +119,19 @@ export async function loadSingleNamespaceAPIWorkspace({ absoluteFilepathToOverrides, settings: { audiences: definition.audiences ?? [], - shouldUseTitleAsName: definition.settings?.shouldUseTitleAsName ?? true, + useTitlesAsName: definition.settings?.shouldUseTitleAsName ?? true, shouldUseUndiscriminatedUnionsWithLiterals: definition.settings?.shouldUseUndiscriminatedUnionsWithLiterals ?? false, - asyncApiNaming: definition.settings?.asyncApiMessageNaming, optionalAdditionalProperties: definition.settings?.shouldUseOptionalAdditionalProperties ?? true, cooerceEnumsToLiterals: definition.settings?.coerceEnumsToLiterals ?? true, objectQueryParameters: definition.settings?.objectQueryParameters ?? false, respectReadonlySchemas: definition.settings?.respectReadonlySchemas ?? false, onlyIncludeReferencedSchemas: definition.settings?.onlyIncludeReferencedSchemas ?? false, - inlinePathParameters: definition.settings?.inlinePathParameters ?? false + inlinePathParameters: definition.settings?.inlinePathParameters ?? false, + disableExamples: false, + discriminatedUnionV2: false, + preserveSchemaIds: false, + asyncApiNaming: definition.settings?.asyncApiMessageNaming ?? "v1" }, source: { type: "openapi", diff --git a/packages/snippets/core/src/API.ts b/packages/snippets/core/src/API.ts deleted file mode 100644 index bc63fe029f4..00000000000 --- a/packages/snippets/core/src/API.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { OpenAPI as OpenAPITypes } from "openapi-types"; - -export type API = OpenAPI; - -export interface OpenAPI { - type: "openapi"; - openapi: OpenAPITypes.Document; - overrides?: OpenAPITypes.Document; -} diff --git a/packages/snippets/core/src/Spec.ts b/packages/snippets/core/src/Spec.ts new file mode 100644 index 00000000000..57be9954ec8 --- /dev/null +++ b/packages/snippets/core/src/Spec.ts @@ -0,0 +1,11 @@ +import { OpenAPI as OpenAPITypes } from "openapi-types"; +import { OpenAPIWorkspace } from "@fern-api/browser-compatible-fern-workspace"; + +export type Spec = OpenAPISpec; + +export interface OpenAPISpec { + type: "openapi"; + openapi: OpenAPITypes.Document; + overrides?: OpenAPITypes.Document; + settings?: OpenAPIWorkspace.Settings; +} diff --git a/packages/snippets/core/src/__test__/generateDynamicIR.test.ts b/packages/snippets/core/src/__test__/generateDynamicIR.test.ts index 669f81df777..416e3188a49 100644 --- a/packages/snippets/core/src/__test__/generateDynamicIR.test.ts +++ b/packages/snippets/core/src/__test__/generateDynamicIR.test.ts @@ -34,7 +34,7 @@ describe("generateDynamicIR", () => { } }; const ir = await generateDynamicIR({ - api: { + spec: { type: "openapi", openapi }, diff --git a/packages/snippets/core/src/generateDynamicIR.ts b/packages/snippets/core/src/generateDynamicIR.ts index 5df278a392d..1ed4f6ee2de 100644 --- a/packages/snippets/core/src/generateDynamicIR.ts +++ b/packages/snippets/core/src/generateDynamicIR.ts @@ -4,28 +4,26 @@ import { NopSourceResolver } from "@fern-api/source-resolver"; import { createTaskContext } from "./utils/createTaskContext"; import { Audiences, generatorsYml } from "@fern-api/configuration"; import { dynamic } from "@fern-api/ir-sdk"; -import { API } from "./API"; -import { convertAPIToWorkspace } from "./utils/convertAPIToWorkspace"; +import { Spec } from "./Spec"; +import { convertSpecToWorkspace } from "./utils/convertSpecToWorkspace"; export async function generateDynamicIR({ - api, + spec, language, generatorsConfiguration, - settings, audiences, keywords, smartCasing }: { - api: API; + spec: Spec; language: generatorsYml.GenerationLanguage; generatorsConfiguration?: generatorsYml.GeneratorsConfiguration; - settings?: OpenAPIWorkspace.Settings; audiences?: Audiences; keywords?: string[]; smartCasing?: boolean; }): Promise { const context = createTaskContext(); - const workspace = await convertAPIToWorkspace({ context, api, generatorsConfiguration, settings }); + const workspace = await convertSpecToWorkspace({ context, spec, generatorsConfiguration }); const ir = await generateIntermediateRepresentation({ context, workspace, diff --git a/packages/snippets/core/src/index.ts b/packages/snippets/core/src/index.ts index a2253ae9d40..24d6675fc7c 100644 --- a/packages/snippets/core/src/index.ts +++ b/packages/snippets/core/src/index.ts @@ -1,2 +1,2 @@ -export { type API } from "./API"; +export { type Spec } from "./Spec"; export { generateDynamicIR } from "./generateDynamicIR"; diff --git a/packages/snippets/core/src/utils/convertAPIToWorkspace.ts b/packages/snippets/core/src/utils/convertSpecToWorkspace.ts similarity index 59% rename from packages/snippets/core/src/utils/convertAPIToWorkspace.ts rename to packages/snippets/core/src/utils/convertSpecToWorkspace.ts index a807100a880..520c704d952 100644 --- a/packages/snippets/core/src/utils/convertAPIToWorkspace.ts +++ b/packages/snippets/core/src/utils/convertSpecToWorkspace.ts @@ -1,30 +1,33 @@ import { FernWorkspace } from "@fern-api/api-workspace-commons"; import { OpenAPIWorkspace } from "@fern-api/browser-compatible-fern-workspace"; -import { API } from "../API"; +import { Spec } from "../Spec"; import { generatorsYml } from "@fern-api/configuration"; import { TaskContext } from "@fern-api/task-context"; -export async function convertAPIToWorkspace({ +export async function convertSpecToWorkspace({ context, - api, - generatorsConfiguration, - settings + spec, + generatorsConfiguration }: { context: TaskContext; - api: API; + spec: Spec; generatorsConfiguration: generatorsYml.GeneratorsConfiguration | undefined; - settings: OpenAPIWorkspace.Settings | undefined; }): Promise { - switch (api.type) { + switch (spec.type) { case "openapi": { const openapi = new OpenAPIWorkspace({ spec: { - parsed: api.openapi, - overrides: api.overrides + parsed: spec.openapi, + overrides: spec.overrides }, generatorsConfiguration }); - return await openapi.toFernWorkspace({ context }, settings); + return await openapi.toFernWorkspace( + { + context + }, + spec.settings + ); } } }