Fix #3544.
This commit is contained in:
Brian Terlson 2024-06-11 10:11:53 -07:00 коммит произвёл GitHub
Родитель c5ca0897d8
Коммит 295e68a698
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
10 изменённых файлов: 98 добавлений и 2 удалений

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

@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/json-schema"
---
Add support for @oneOf decorator.

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

@ -258,6 +258,22 @@ Specify that the numeric type must be a multiple of some numeric value.
| ----- | ----------------- | -------------------------------------------------- |
| value | `valueof numeric` | The numeric type must be a multiple of this value. |
### `@oneOf` {#@TypeSpec.JsonSchema.oneOf}
Specify that `oneOf` should be used instead of `anyOf` for that union.
```typespec
@TypeSpec.JsonSchema.oneOf
```
#### Target
`Union | ModelProperty`
#### Parameters
None
### `@prefixItems` {#@TypeSpec.JsonSchema.prefixItems}
Specify that the target array must begin with the provided types.

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

@ -52,6 +52,7 @@ npm install --save-peer @typespec/json-schema
- [`@minContains`](./decorators.md#@TypeSpec.JsonSchema.minContains)
- [`@minProperties`](./decorators.md#@TypeSpec.JsonSchema.minProperties)
- [`@multipleOf`](./decorators.md#@TypeSpec.JsonSchema.multipleOf)
- [`@oneOf`](./decorators.md#@TypeSpec.JsonSchema.oneOf)
- [`@prefixItems`](./decorators.md#@TypeSpec.JsonSchema.prefixItems)
- [`@uniqueItems`](./decorators.md#@TypeSpec.JsonSchema.uniqueItems)

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

@ -95,6 +95,7 @@ When true, emit all references as json schema files, even if the referenced type
- [`@minContains`](#@mincontains)
- [`@minProperties`](#@minproperties)
- [`@multipleOf`](#@multipleof)
- [`@oneOf`](#@oneof)
- [`@prefixItems`](#@prefixitems)
- [`@uniqueItems`](#@uniqueitems)
@ -348,6 +349,22 @@ Specify that the numeric type must be a multiple of some numeric value.
| ----- | ----------------- | -------------------------------------------------- |
| value | `valueof numeric` | The numeric type must be a multiple of this value. |
#### `@oneOf`
Specify that `oneOf` should be used instead of `anyOf` for that union.
```typespec
@TypeSpec.JsonSchema.oneOf
```
##### Target
`Union | ModelProperty`
##### Parameters
None
#### `@prefixItems`
Specify that the target array must begin with the provided types.

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

@ -5,6 +5,7 @@ import type {
Numeric,
Scalar,
Type,
Union,
} from "@typespec/compiler";
/**
@ -43,6 +44,11 @@ export type BaseUriDecorator = (
*/
export type IdDecorator = (context: DecoratorContext, target: Type, id: string) => void;
/**
* Specify that `oneOf` should be used instead of `anyOf` for that union.
*/
export type OneOfDecorator = (context: DecoratorContext, target: Union | ModelProperty) => void;
/**
* Specify that the numeric type must be a multiple of some numeric value.
*

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

@ -13,6 +13,7 @@ import {
$minContains,
$minProperties,
$multipleOf,
$oneOf,
$prefixItems,
$uniqueItems,
} from "@typespec/json-schema";
@ -30,6 +31,7 @@ import type {
MinContainsDecorator,
MinPropertiesDecorator,
MultipleOfDecorator,
OneOfDecorator,
PrefixItemsDecorator,
UniqueItemsDecorator,
} from "./TypeSpec.JsonSchema.js";
@ -38,6 +40,7 @@ type Decorators = {
$jsonSchema: JsonSchemaDecorator;
$baseUri: BaseUriDecorator;
$id: IdDecorator;
$oneOf: OneOfDecorator;
$multipleOf: MultipleOfDecorator;
$contains: ContainsDecorator;
$minContains: MinContainsDecorator;
@ -57,6 +60,7 @@ const _: Decorators = {
$jsonSchema,
$baseUri,
$id,
$oneOf,
$multipleOf,
$contains,
$minContains,

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

@ -30,6 +30,11 @@ extern dec baseUri(target: Reflection.Namespace, baseUri: valueof string);
*/
extern dec id(target: unknown, id: valueof string);
/**
* Specify that `oneOf` should be used instead of `anyOf` for that union.
*/
extern dec oneOf(target: Reflection.Union | Reflection.ModelProperty);
/**
* Specify that the numeric type must be a multiple of some numeric value.
*

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

@ -29,6 +29,7 @@ import {
MinContainsDecorator,
MinPropertiesDecorator,
MultipleOfDecorator,
OneOfDecorator,
PrefixItemsDecorator,
UniqueItemsDecorator,
} from "../generated-defs/TypeSpec.JsonSchema.js";
@ -145,6 +146,15 @@ export function getId(program: Program, target: Type) {
return program.stateMap(idKey).get(target);
}
const oneOfKey = createStateSymbol("JsonSchema.oneOf");
export const $oneOf: OneOfDecorator = (context: DecoratorContext, target: Type) => {
context.program.stateMap(oneOfKey).set(target, true);
};
export function isOneOf(program: Program, target: Type) {
return program.stateMap(oneOfKey).has(target);
}
const containsKey = createStateSymbol("JsonSchema.contains");
export const $contains: ContainsDecorator = (
context: DecoratorContext,

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

@ -71,6 +71,7 @@ import {
getPrefixItems,
getUniqueItems,
isJsonSchemaDeclaration,
isOneOf,
} from "./index.js";
import { JSONSchemaEmitterOptions, reportDiagnostic } from "./lib.js";
export class JsonSchemaEmitter extends TypeEmitter<Record<string, any>, JSONSchemaEmitterOptions> {
@ -174,6 +175,11 @@ export class JsonSchemaEmitter extends TypeEmitter<Record<string, any>, JSONSche
result.default = this.#getDefaultValue(property.type, property.default);
}
if (result.anyOf && isOneOf(this.emitter.getProgram(), property)) {
result.oneOf = result.anyOf;
delete result.anyOf;
}
this.#applyConstraints(property, result);
return result;
@ -296,8 +302,10 @@ export class JsonSchemaEmitter extends TypeEmitter<Record<string, any>, JSONSche
}
unionDeclaration(union: Union, name: string): EmitterOutput<object> {
const key = isOneOf(this.emitter.getProgram(), union) ? "oneOf" : "anyOf";
const withConstraints = this.#initializeSchema(union, name, {
anyOf: this.emitter.emitUnionVariants(union),
[key]: this.emitter.emitUnionVariants(union),
});
this.#applyConstraints(union, withConstraints);
@ -305,8 +313,10 @@ export class JsonSchemaEmitter extends TypeEmitter<Record<string, any>, JSONSche
}
unionLiteral(union: Union): EmitterOutput<object> {
const key = isOneOf(this.emitter.getProgram(), union) ? "oneOf" : "anyOf";
return new ObjectBuilder({
anyOf: this.emitter.emitUnionVariants(union),
[key]: this.emitter.emitUnionVariants(union),
});
}

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

@ -88,6 +88,26 @@ describe("emitting unions", () => {
assert.strictEqual(Foo["x-foo"], true);
});
it("handles oneOf decorator", async () => {
const schemas = await emitSchema(`
@oneOf
union Foo {
"a",
"b"
}
model Bar {
@oneOf
prop: "a" | "b"
}
`);
const Foo = schemas["Foo.json"];
const Bar = schemas["Bar.json"];
assert.ok(Foo.oneOf, "Foo uses oneOf");
assert.ok(Bar.properties.prop.oneOf, "Bar.prop uses oneOf");
});
it("handles decorators on variants", async () => {
const schemas = await emitSchema(`
union Foo {