Skip to content

Commit

Permalink
fix(cli): Handle OpenAPI type arrays better (#5499)
Browse files Browse the repository at this point in the history
Handle OpenAPI type arrays better
---------

Co-authored-by: Swimburger <[email protected]>
  • Loading branch information
Swimburger and Swimburger authored Dec 28, 2024
1 parent b679c79 commit e8900d0
Show file tree
Hide file tree
Showing 12 changed files with 3,428 additions and 200 deletions.
12 changes: 6 additions & 6 deletions fern/pages/changelogs/cli/2024-12-14.mdx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
## 0.46.2
**`(feat):`** The Fern CLI now supports parsing a `logo` option from your frontmatter. If
you would like to override logo on a specific page you can do so by adding
the following:
**`(feat):`** The Fern CLI now supports parsing a `logo` option from your frontmatter. If
you would like to override logo on a specific page you can do so by adding
the following:

```markdown intro.mdx
---
logo: /path/to/my/logo
---
```

or
or

```markdown intro.mdx
---
logo:
light: /path/to/my/light/logo
logo:
light: /path/to/my/light/logo
dark: /path/to/my/dark/logo
---
```
Expand Down
2 changes: 1 addition & 1 deletion fern/pages/changelogs/cli/2024-12-15.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## 0.46.3
**`(fix):`** The Fern CLI now supports generating examples for streaming SSE (server-sent-event)
endpoints.
endpoints.


2 changes: 1 addition & 1 deletion fern/pages/changelogs/cli/2024-12-17.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## 0.46.5
**`(fix):`** The OpenAPI parser now deduplicates headers that appear in both security schemes and
**`(fix):`** The OpenAPI parser now deduplicates headers that appear in both security schemes and
operation-level headers to avoid duplicate header declarations.


2 changes: 1 addition & 1 deletion fern/pages/changelogs/cli/2024-12-19.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## 0.46.6
**`(fix):`** The audiences property on WebSocket channels is now respected when filtering
**`(fix):`** The audiences property on WebSocket channels is now respected when filtering
the IR graph based on configured audiences.


2 changes: 1 addition & 1 deletion fern/pages/changelogs/cli/2024-12-20.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## 0.46.8
**`(fix):`** The CLI now auto generates SSE and JSON Streaming examples even if those are
**`(fix):`** The CLI now auto generates SSE and JSON Streaming examples even if those are
not provided in the OpenAPI Spec or Fern Definition.


12 changes: 6 additions & 6 deletions fern/pages/changelogs/cli/2024-12-23.mdx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
## 0.46.11
**`(fix):`** Allow for configuring the depth of example generation in API Docs. For example,
if you want to generate optional properties that are 5 levels deep, you can add
**`(fix):`** Allow for configuring the depth of example generation in API Docs. For example,
if you want to generate optional properties that are 5 levels deep, you can add
the following configuration in your `generators.yml`

```yml generators.yml
api:
specs:
specs:
- openapi: ./openapi.json
settings:
example-generation:
response:
settings:
example-generation:
response:
max-depth: 10
```
Expand Down
6 changes: 6 additions & 0 deletions fern/pages/changelogs/cli/2024-12-28.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## 0.46.16
**`(fix):`** Improve parsing of OpenAPI schemas with an array in the `type` property.
* If the array contains `"null"`, it is interpreted as nullable, and removed from the array.
* If there is only a single item in the array (after removing "null"), it previously defaulted to `unknown`, but now the specified type is used.


Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { SchemaParserContext } from "./SchemaParserContext";
import { getBreadcrumbsFromReference } from "./utils/getBreadcrumbsFromReference";
import { getGeneratedTypeName } from "./utils/getSchemaName";
import { isReferenceObject } from "./utils/isReferenceObject";
import { size } from "lodash-es";

export const SCHEMA_REFERENCE_PREFIX = "#/components/schemas/";
export const SCHEMA_INLINE_REFERENCE_PREFIX = "#/components/responses/";
Expand Down Expand Up @@ -149,7 +150,7 @@ function getTitleAsName(title: string | undefined): string | undefined {
}

export function convertSchemaObject(
schema: OpenAPIV3.SchemaObject,
schema: OpenAPIV3.SchemaObject | string,
wrapAsNullable: boolean,
context: SchemaParserContext,
breadcrumbs: string[],
Expand All @@ -160,6 +161,9 @@ export function convertSchemaObject(
referencedAsRequest = false,
fallback?: string | number | boolean | unknown[]
): SchemaWithExample {
if (typeof schema === "string") {
schema = { type: schema } as OpenAPIV3.SchemaObject;
}
const nameOverride =
getExtension<string>(schema, FernOpenAPIExtension.TYPE_NAME) ??
(context.options.useTitlesAsName ? getTitleAsName(schema.title) : undefined);
Expand Down Expand Up @@ -202,6 +206,29 @@ export function convertSchemaObject(
return fernSchema;
}

// handle type array
if (Array.isArray(schema.type)) {
const nullIndex = schema.type.indexOf("null");
const hasNull = nullIndex !== -1;
if (schema.type.length === 1) {
schema.type = schema.type[0];
} else if (schema.type.length === 2 && hasNull) {
schema.type.splice(nullIndex, 1);
schema.type = schema.type[0];
schema.nullable = true;
} else {
if (hasNull) {
schema.type.splice(nullIndex, 1);
schema.nullable = true;
}
if (schema.oneOf == null) {
schema.oneOf = schema.type;
} else {
schema.oneOf.push(...schema.type);
}
}
}

// if a schema is null then we should wrap it as nullable
if (!wrapAsNullable && schema.nullable === true) {
return convertSchemaObject(
Expand Down Expand Up @@ -275,63 +302,6 @@ export function convertSchemaObject(
});
}

// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
if (isListOfStrings(schema.type) && schema.type[1] != null && schema.type[0] != null) {
const firstElement = schema.type[0];
const secondElement = schema.type[1];
if (firstElement === "null") {
return SchemaWithExample.nullable({
nameOverride,
generatedName,
title,
value: convertSchemaObject(
{
...schema,
type: secondElement as OpenAPIV3.NonArraySchemaObjectType
},
wrapAsNullable,
context,
breadcrumbs,
encoding,
source,
namespace,
propertiesToExclude,
referencedAsRequest,
fallback
),
groupName,
description: schema.description,
availability,
inline: undefined
});
} else if (secondElement === "null") {
return SchemaWithExample.nullable({
nameOverride,
generatedName,
title,
value: convertSchemaObject(
{
...schema,
type: firstElement as OpenAPIV3.NonArraySchemaObjectType
},
wrapAsNullable,
context,
breadcrumbs,
encoding,
source,
namespace,
propertiesToExclude,
referencedAsRequest,
fallback
),
groupName,
description: schema.description,
availability,
inline: undefined
});
}
}

// List of types that is undiscriminated union
if (isListOfStrings(schema.type) && schema.type.length > 1) {
const wrapVariantAsNullable = schema.type.includes("null");
Expand Down Expand Up @@ -582,15 +552,6 @@ export function convertSchemaObject(
}
}

// handle type array
if (Array.isArray(schema.type)) {
if (schema.oneOf == null) {
schema.oneOf = schema.type;
} else {
schema.oneOf.push(...schema.type);
}
}

// handle oneOf
if (schema.oneOf != null && schema.oneOf.length > 0) {
const isUndiscriminated = getExtension(schema, FernOpenAPIExtension.IS_UNDISCRIMINATED);
Expand Down Expand Up @@ -806,7 +767,7 @@ export function convertSchemaObject(
}
}
if (
(schema.properties == null || schema.properties.length === 0) &&
(schema.properties == null || hasNoProperties(schema)) &&
filteredAllOfs.length === 1 &&
filteredAllOfs[0] != null
) {
Expand Down Expand Up @@ -835,7 +796,7 @@ export function convertSchemaObject(
});

if (
(schema.properties == null || schema.properties.length === 0) &&
(schema.properties == null || hasNoProperties(schema)) &&
filteredAllOfObjects.length === 1 &&
filteredAllOfObjects[0] != null
) {
Expand Down Expand Up @@ -978,7 +939,7 @@ function hasNoAllOf(schema: OpenAPIV3.SchemaObject): boolean {
}

function hasNoProperties(schema: OpenAPIV3.SchemaObject): boolean {
return schema.properties == null || Object.keys(schema.properties).length === 0;
return schema.properties == null || size(schema.properties) === 0;
}

function isListOfStrings(x: unknown): x is string[] {
Expand Down
Loading

0 comments on commit e8900d0

Please sign in to comment.