[OpenAPI3 Emitter] Add option to map `safeint` to `double-int` (#2933)

fix  #2367 
Format was added to the schema registry so we are safe to use that now
https://spec.openapis.org/registry/format/double-int.html

---------

Co-authored-by: Brian Terlson <brian.terlson@microsoft.com>
This commit is contained in:
Timothee Guerin 2024-02-29 16:05:41 -08:00 коммит произвёл GitHub
Родитель 02ed01e79b
Коммит 9654dd836b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 90 добавлений и 2 удалений

Просмотреть файл

@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: feature
packages:
- "@typespec/openapi3"
---
Add a new option `safeint-strategy` that can be set to `double-int` to emit `type: integer, format: double-int` instead of `type: integer, format: int64` when using the `safeint` scalar.

Просмотреть файл

@ -81,3 +81,14 @@ By default all types declared under the service namespace will be included. With
If the generated openapi types should have the `x-typespec-name` extension set with the name of the TypeSpec type that created it.
This extension is meant for debugging and should not be depended on.
### `safeint-strategy`
**Type:** `"double-int" | "int64"`
How to handle safeint type. Options are:
- `double-int`: Will produce `type: integer, format: double-int`
- `int64`: Will produce `type: integer, format: int64`
Default: `int64`

Просмотреть файл

@ -86,6 +86,17 @@ By default all types declared under the service namespace will be included. With
If the generated openapi types should have the `x-typespec-name` extension set with the name of the TypeSpec type that created it.
This extension is meant for debugging and should not be depended on.
#### `safeint-strategy`
**Type:** `"double-int" | "int64"`
How to handle safeint type. Options are:
- `double-int`: Will produce `type: integer, format: double-int`
- `int64`: Will produce `type: integer, format: int64`
Default: `int64`
## Decorators
### TypeSpec.OpenAPI

Просмотреть файл

@ -54,6 +54,14 @@ export interface OpenAPI3EmitterOptions {
* @default "never"
*/
"include-x-typespec-name"?: "inline-only" | "never";
/**
* How to handle safeint type. Options are:
* - `double-int`: Will produce `type: integer, format: double-int`
* - `int64`: Will produce `type: integer, format: int64`
* @default "int64"
*/
"safeint-strategy"?: "double-int" | "int64";
}
const EmitterOptionsSchema: JSONSchemaType<OpenAPI3EmitterOptions> = {
@ -117,6 +125,19 @@ const EmitterOptionsSchema: JSONSchemaType<OpenAPI3EmitterOptions> = {
description:
"If the generated openapi types should have the `x-typespec-name` extension set with the name of the TypeSpec type that created it.\nThis extension is meant for debugging and should not be depended on.",
},
"safeint-strategy": {
type: "string",
enum: ["double-int", "int64"],
nullable: true,
default: "int64",
description: [
"How to handle safeint type. Options are:",
" - `double-int`: Will produce `type: integer, format: double-int`",
" - `int64`: Will produce `type: integer, format: int64`",
"",
"Default: `int64`",
].join("\n"),
},
},
required: [],
};

Просмотреть файл

@ -118,6 +118,7 @@ const defaultOptions = {
"new-line": "lf",
"omit-unreachable-types": false,
"include-x-typespec-name": "never",
"safeint-strategy": "int64",
} as const;
export async function $onEmit(context: EmitContext<OpenAPI3EmitterOptions>) {
@ -186,6 +187,7 @@ export function resolveOptions(
newLine: resolvedOptions["new-line"],
omitUnreachableTypes: resolvedOptions["omit-unreachable-types"],
includeXTypeSpecName: resolvedOptions["include-x-typespec-name"],
safeintStrategy: resolvedOptions["safeint-strategy"],
outputFile: resolvePath(context.emitterOutputDir, outputFile),
};
}
@ -196,6 +198,7 @@ export interface ResolvedOpenAPI3EmitterOptions {
newLine: NewLine;
omitUnreachableTypes: boolean;
includeXTypeSpecName: "inline-only" | "never";
safeintStrategy: "double-int" | "int64";
}
function createOAPIEmitter(

Просмотреть файл

@ -699,7 +699,13 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter<
case "int64":
return { type: "integer", format: "int64" };
case "safeint":
return { type: "integer", format: "int64" };
switch (this.#options.safeintStrategy) {
case "double-int":
return { type: "integer", format: "double-int" };
case "int64":
default:
return { type: "integer", format: "int64" };
}
case "uint8":
return { type: "integer", format: "uint8" };
case "uint16":

Просмотреть файл

@ -1,7 +1,7 @@
import { deepStrictEqual, ok, strictEqual } from "assert";
import { describe, it } from "vitest";
import { OpenAPI3Schema } from "../src/types.js";
import { oapiForModel } from "./test-host.js";
import { oapiForModel, openApiFor } from "./test-host.js";
describe("openapi3: primitives", () => {
describe("handle TypeSpec intrinsic types", () => {
@ -48,6 +48,34 @@ describe("openapi3: primitives", () => {
}
});
describe("safeint-strategy", () => {
it("produce type: integer, format: double-int for safeint when safeint-strategy is double-int", async () => {
const res = await openApiFor(
`
model Pet { name: safeint };
`,
undefined,
{ "safeint-strategy": "double-int" }
);
const schema = res.components.schemas.Pet.properties.name;
deepStrictEqual(schema, { type: "integer", format: "double-int" });
});
it("produce type: integer, format: int64 for safeint when safeint-strategy is int64", async () => {
const res = await openApiFor(
`
model Pet { name: safeint };
`,
undefined,
{ "safeint-strategy": "int64" }
);
const schema = res.components.schemas.Pet.properties.name;
deepStrictEqual(schema, { type: "integer", format: "int64" });
});
});
it("defines models extended from primitives", async () => {
const res = await oapiForModel(
"Pet",