Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .chronus/changes/safe-int-openapi3-2024-1-17-0-53-2.md
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 11 additions & 0 deletions docs/libraries/openapi3/reference/emitter.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
11 changes: 11 additions & 0 deletions packages/openapi3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions packages/openapi3/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> = {
Expand Down Expand Up @@ -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: [],
};
Expand Down
3 changes: 3 additions & 0 deletions packages/openapi3/src/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>) {
Expand Down Expand Up @@ -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),
};
}
Expand All @@ -196,6 +198,7 @@ export interface ResolvedOpenAPI3EmitterOptions {
newLine: NewLine;
omitUnreachableTypes: boolean;
includeXTypeSpecName: "inline-only" | "never";
safeintStrategy: "double-int" | "int64";
}

function createOAPIEmitter(
Expand Down
8 changes: 7 additions & 1 deletion packages/openapi3/src/schema-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down
30 changes: 29 additions & 1 deletion packages/openapi3/test/primitive-types.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand Down Expand Up @@ -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",
Expand Down