feat: add support for OpenAPI 3.1 (#32300)

* breaking: add support for OpenAPI 3.1

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>

* add changeset

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>

* update nullable prop

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>

* remove more allowReserved usages

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>

* make changes less breaking

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>

* Apply suggestion from @aramissennyeydd

Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com>

---------

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com>
This commit is contained in:
Aramis Sennyey
2026-02-28 09:04:02 -05:00
committed by GitHub
parent fbcfb392a2
commit 0fbcf23714
239 changed files with 360 additions and 673 deletions
+1 -1
View File
@@ -2,6 +2,6 @@
"$schema": "../../node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "6.5.0"
"version": "7.18.0"
}
}
@@ -26,6 +26,7 @@ import {
import { deduplicateImports } from '../../../../../lib/openapi/dedupe-imports';
import { targetPaths } from '@backstage/cli-common';
import {
getOpenApiGeneratorKey,
getPathToCurrentOpenApiSpec,
toGeneratorAdditionalProperties,
} from '../../../../../lib/openapi/helpers';
@@ -43,6 +44,7 @@ async function generate(
const additionalProperties = toGeneratorAdditionalProperties({
initialValue: clientAdditionalProperties,
});
const generatorKey = await getOpenApiGeneratorKey(resolvedOpenapiPath);
await fs.emptyDir(resolvedOutputDirectory);
@@ -68,7 +70,7 @@ async function generate(
'templates/typescript-backstage-client.yaml',
),
'--generator-key',
'v3.0',
generatorKey,
additionalProperties
? `--additional-properties=${additionalProperties}`
: '',
@@ -111,7 +113,12 @@ async function generate(
}
fs.removeSync(resolve(resolvedOutputDirectory, '.openapi-generator-ignore'));
fs.removeSync(resolve(resolvedOutputDirectory, '.gitattributes'));
fs.rmSync(resolve(resolvedOutputDirectory, 'docs'), {
recursive: true,
force: true,
});
fs.rmSync(resolve(resolvedOutputDirectory, '.openapi-generator'), {
recursive: true,
force: true,
@@ -29,6 +29,7 @@ import {
import { deduplicateImports } from '../../../../../lib/openapi/dedupe-imports';
import { targetPaths } from '@backstage/cli-common';
import {
getOpenApiGeneratorKey,
getPathToCurrentOpenApiSpec,
getRelativePathToFile,
toGeneratorAdditionalProperties,
@@ -103,6 +104,7 @@ async function generate(
const additionalProperties = toGeneratorAdditionalProperties({
initialValue: serverAdditionalProperties,
});
const generatorKey = await getOpenApiGeneratorKey(resolvedOpenapiPath);
await exec(
'node',
@@ -121,7 +123,7 @@ async function generate(
'templates/typescript-backstage-server.yaml',
),
'--generator-key',
'v3.0',
generatorKey,
additionalProperties
? `--additional-properties=${additionalProperties}`
: '',
@@ -160,7 +162,12 @@ async function generate(
}
fs.removeSync(resolve(resolvedOutputDirectory, '.openapi-generator-ignore'));
fs.removeSync(resolve(resolvedOutputDirectory, '.gitattributes'));
fs.rmSync(resolve(resolvedOutputDirectory, 'docs'), {
recursive: true,
force: true,
});
fs.rmSync(resolve(resolvedOutputDirectory, '.openapi-generator'), {
recursive: true,
force: true,
@@ -49,13 +49,15 @@ async function lint(directoryPath: string, config?: { strict: boolean }) {
{
extends: [oas, ruleset],
rules: {
'allow-reserved-in-params': {
given: '$.paths..parameters[*]',
'allow-reserved-in-query-params': {
given: '$.paths..parameters[?(@.in == "query")]',
then: {
field: 'allowReserved',
function: truthy,
},
severity: 'error',
message:
'Query parameters must specify allowReserved (true or false)',
},
},
overrides: [
@@ -70,3 +70,32 @@ export function toGeneratorAdditionalProperties({
.map(([key, value]) => `${key}=${value}`)
.join(',');
}
export async function getOpenApiGeneratorKey(
specPath: string,
): Promise<string> {
const yaml = (await loadAndValidateOpenApiYaml(specPath)) as any;
const version = yaml.openapi;
if (!version) {
throw new Error(`Could not determine OpenAPI version from ${specPath}`);
}
const semver = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(version);
if (!semver) {
throw new Error(`Invalid OpenAPI version format ${version} in ${specPath}`);
}
const [, major, minor] = semver;
const supportedVersions = ['3.0', '3.1'];
const majorMinor = `${major}.${minor}`;
if (!supportedVersions.includes(majorMinor)) {
throw new Error(
`Unsupported OpenAPI version ${version} in ${specPath}. Supported versions are: ${supportedVersions.join(
', ',
)}`,
);
}
return `v${majorMinor}`;
}