Родитель
c5ca0897d8
Коммит
295e68a698
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче