Skip to content

Commit

Permalink
fix(cli): openapi importer supports a setting for making additional p…
Browse files Browse the repository at this point in the history
…roperties optional (#4667)
  • Loading branch information
dsinghvi authored Sep 17, 2024
1 parent d97a98a commit 0d037c2
Show file tree
Hide file tree
Showing 16 changed files with 303 additions and 20 deletions.
18 changes: 18 additions & 0 deletions packages/cli/cli/versions.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
- changelogEntry:
- summary: |
Previously the OpenAPI converter would incorrectly mark
the values of `additionalProperties` as optional. Now, we have
introduced a feature flag to turn this behavior off.
The feature flag can be configured in generators.yml:
```yml
api:
specs:
- openapi: /path/to/openapi
settings:
optional-additional-properties: false
```
type: fix
irVersion: 53
version: 0.41.16

- changelogEntry:
- summary: |
Performance improvements for stringifiying large Intermediate Representations. If
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface APIDefinitionSettings {
shouldUseTitleAsName: boolean | undefined;
shouldUseUndiscriminatedUnionsWithLiterals: boolean | undefined;
asyncApiMessageNaming: "v1" | "v2" | undefined;
shouldUseOptionalAdditionalProperties: boolean | undefined;
}

export interface APIDefinitionLocation {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: undefined,
shouldUseUndiscriminatedUnionsWithLiterals: undefined,
asyncApiMessageNaming: undefined
asyncApiMessageNaming: undefined,
shouldUseOptionalAdditionalProperties: undefined
}
});
} else if (isRawProtobufAPIDefinitionSchema(apiConfiguration)) {
Expand All @@ -111,7 +112,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: undefined,
shouldUseUndiscriminatedUnionsWithLiterals: undefined,
asyncApiMessageNaming: undefined
asyncApiMessageNaming: undefined,
shouldUseOptionalAdditionalProperties: undefined
}
});
} else if (Array.isArray(apiConfiguration)) {
Expand All @@ -128,7 +130,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: undefined,
shouldUseUndiscriminatedUnionsWithLiterals: undefined,
asyncApiMessageNaming: undefined
asyncApiMessageNaming: undefined,
shouldUseOptionalAdditionalProperties: undefined
}
});
} else if (isRawProtobufAPIDefinitionSchema(definition)) {
Expand All @@ -145,7 +148,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: undefined,
shouldUseUndiscriminatedUnionsWithLiterals: undefined,
asyncApiMessageNaming: undefined
asyncApiMessageNaming: undefined,
shouldUseOptionalAdditionalProperties: undefined
}
});
} else {
Expand All @@ -160,7 +164,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: definition.settings?.["use-title"],
shouldUseUndiscriminatedUnionsWithLiterals: definition.settings?.unions === "v1",
asyncApiMessageNaming: definition.settings?.["message-naming"]
asyncApiMessageNaming: definition.settings?.["message-naming"],
shouldUseOptionalAdditionalProperties: undefined
}
});
}
Expand All @@ -177,7 +182,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: apiConfiguration.settings?.["use-title"],
shouldUseUndiscriminatedUnionsWithLiterals: apiConfiguration.settings?.unions === "v1",
asyncApiMessageNaming: apiConfiguration.settings?.["message-naming"]
asyncApiMessageNaming: apiConfiguration.settings?.["message-naming"],
shouldUseOptionalAdditionalProperties: undefined
}
});
}
Expand All @@ -199,7 +205,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: settings?.["use-title"],
shouldUseUndiscriminatedUnionsWithLiterals: settings?.unions === "v1",
asyncApiMessageNaming: undefined
asyncApiMessageNaming: undefined,
shouldUseOptionalAdditionalProperties: undefined
}
});
} else if (openapi != null) {
Expand All @@ -214,7 +221,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: openapi.settings?.["use-title"],
shouldUseUndiscriminatedUnionsWithLiterals: openapi.settings?.unions === "v1",
asyncApiMessageNaming: undefined
asyncApiMessageNaming: undefined,
shouldUseOptionalAdditionalProperties: undefined
}
});
}
Expand All @@ -231,7 +239,8 @@ async function parseAPIConfigurationToApiLocations(
settings: {
shouldUseTitleAsName: settings?.["use-title"],
shouldUseUndiscriminatedUnionsWithLiterals: settings?.unions === "v1",
asyncApiMessageNaming: settings?.["message-naming"]
asyncApiMessageNaming: settings?.["message-naming"],
shouldUseOptionalAdditionalProperties: undefined
}
});
}
Expand Down Expand Up @@ -263,7 +272,8 @@ async function parseApiConfigurationV2Schema({
settings: {
shouldUseTitleAsName: undefined,
shouldUseUndiscriminatedUnionsWithLiterals: undefined,
asyncApiMessageNaming: undefined
asyncApiMessageNaming: undefined,
shouldUseOptionalAdditionalProperties: spec.settings?.["optional-additional-properties"] ?? true
}
};
if (spec.namespace == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import { z } from "zod";
import { RawSchemas } from "@fern-api/fern-definition-schema";

/*********** OpenAPI Spec ***********/

export const OpenAPISettingsSchema = z.strictObject({
"optional-additional-properties": z.optional(z.boolean())
});

export type OpenAPISettingsSchema = z.infer<typeof OpenAPISettingsSchema>;

export const OpenAPISpecSchema = z.strictObject({
openapi: z.string(),
overrides: z.string().optional(),
namespace: z.string().optional()
namespace: z.string().optional(),
settings: z.optional(OpenAPISettingsSchema)
});

export type OpenAPISpecSchema = z.infer<typeof OpenAPISpecSchema>;

/*********** AsyncAPI Spec ***********/

export const AsyncAPISettingsSchema = z.strictObject({
"optional-additional-properties": z.optional(z.boolean())
});

export type AsyncAPISettingsSchema = z.infer<typeof AsyncAPISettingsSchema>;

export const AsyncAPISchema = z.strictObject({
asyncapi: z.string(),
overrides: z.string().optional(),
namespace: z.string().optional()
namespace: z.string().optional(),
settings: z.optional(AsyncAPISettingsSchema)
});

export type AsyncAPISchema = z.infer<typeof AsyncAPISchema>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`anyOf > additional-properties > parse open api 1`] = `
{
"apiVersion": null,
"basePath": null,
"channel": [],
"description": null,
"endpoints": [],
"globalHeaders": [],
"groups": {},
"hasEndpointsMarkedInternal": false,
"idempotencyHeaders": [],
"nonRequestReferencedSchemas": [],
"schemas": {
"CheckFlagRequestBody": {
"additionalProperties": false,
"allOf": [],
"allOfPropertyConflicts": [],
"availability": null,
"description": null,
"generatedName": "CheckFlagRequestBody",
"groupName": [],
"nameOverride": null,
"properties": [
{
"audiences": [],
"availability": null,
"conflict": {},
"generatedName": "checkFlagRequestBodyCompany",
"key": "company",
"nameOverride": null,
"schema": {
"availability": null,
"description": null,
"generatedName": "checkFlagRequestBodyCompany",
"groupName": [],
"nameOverride": null,
"title": null,
"type": "optional",
"value": {
"availability": null,
"description": null,
"generatedName": "CheckFlagRequestBodyCompany",
"groupName": [],
"nameOverride": null,
"title": null,
"type": "nullable",
"value": {
"availability": null,
"description": null,
"encoding": null,
"generatedName": "CheckFlagRequestBodyCompany",
"groupName": [],
"key": {
"availability": null,
"description": null,
"generatedName": "CheckFlagRequestBodyCompanyKey",
"groupName": [],
"nameOverride": null,
"schema": {
"default": null,
"format": null,
"maxLength": null,
"minLength": null,
"pattern": null,
"type": "string",
},
"title": null,
},
"nameOverride": null,
"title": null,
"type": "map",
"value": {
"availability": null,
"description": null,
"generatedName": "CheckFlagRequestBodyCompanyValue",
"groupName": [],
"nameOverride": null,
"schema": {
"default": null,
"format": null,
"maxLength": null,
"minLength": null,
"pattern": null,
"type": "string",
},
"title": null,
"type": "primitive",
},
},
},
},
},
{
"audiences": [],
"availability": null,
"conflict": {},
"generatedName": "checkFlagRequestBodyUser",
"key": "user",
"nameOverride": null,
"schema": {
"availability": null,
"description": null,
"generatedName": "checkFlagRequestBodyUser",
"groupName": [],
"nameOverride": null,
"title": null,
"type": "optional",
"value": {
"availability": null,
"description": null,
"generatedName": "CheckFlagRequestBodyUser",
"groupName": [],
"nameOverride": null,
"title": null,
"type": "nullable",
"value": {
"availability": null,
"description": null,
"encoding": null,
"generatedName": "CheckFlagRequestBodyUser",
"groupName": [],
"key": {
"availability": null,
"description": null,
"generatedName": "CheckFlagRequestBodyUserKey",
"groupName": [],
"nameOverride": null,
"schema": {
"default": null,
"format": null,
"maxLength": null,
"minLength": null,
"pattern": null,
"type": "string",
},
"title": null,
},
"nameOverride": null,
"title": null,
"type": "map",
"value": {
"availability": null,
"description": null,
"generatedName": "CheckFlagRequestBodyUserValue",
"groupName": [],
"nameOverride": null,
"schema": {
"default": null,
"format": null,
"maxLength": null,
"minLength": null,
"pattern": null,
"type": "string",
},
"title": null,
"type": "primitive",
},
},
},
},
},
],
"source": {
"file": "additional-properties/openapi.yml",
"type": "openapi",
},
"title": null,
"type": "object",
},
},
"securitySchemes": {},
"servers": [],
"tags": {
"orderedTagIds": null,
"tagsById": {},
},
"title": "Additional Properties",
"variables": {},
"webhooks": [],
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { testParseOpenAPI } from "./testParseOpenApi";

describe("anyOf", () => {
testParseOpenAPI("additional-properties", "openapi.yml", undefined, {
audiences: [],
shouldUseTitleAsName: true,
shouldUseUndiscriminatedUnionsWithLiterals: true,
optionalAdditionalProperties: false
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
openapi: 3.1.0
info:
title: Additional Properties
version: "0"
paths: {}
components:
schemas:
CheckFlagRequestBody:
type: object
properties:
company:
additionalProperties:
type: string
type: object
nullable: true
user:
additionalProperties:
type: string
type: object
nullable: true

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ describe("open api parser", () => {
testParseOpenAPI("gen-yml-make-undisc-unions", "openapi.json", undefined, {
audiences: [],
shouldUseTitleAsName: true,
shouldUseUndiscriminatedUnionsWithLiterals: true
shouldUseUndiscriminatedUnionsWithLiterals: true,
optionalAdditionalProperties: true
});
});
Loading

0 comments on commit 0d037c2

Please sign in to comment.