Describe the bug
For a model that uses extends, MergePatchUpdate transforms the model's own properties but emits allOf pointing at the unpatched base schema. The inherited properties never go through the transform, so they stay required and non-nullable.
The generated patch schema therefore demands every inherited required property on every request, including on the empty patch. The algorithm in RFC 7396, which this template's doc comment says the transform follows, iterates over the pairs present in the patch document, so {} makes no changes and is always a valid patch. A schema that rejects {} is not describing a merge-patch body.
Versions: @typespec/compiler 1.14.0, @typespec/http 1.14.0, @typespec/openapi3 1.14.0.
Reproduction
import "@typespec/http";
using Http;
@service(#{ title: "Extends repro" })
namespace ExtendsRepro;
model Base {
id: string;
}
model Widget extends Base {
label?: string;
}
model WidgetPatch is MergePatchUpdate<Widget>;
@route("/widgets/{id}")
@patch
op update(@path id: string, @body body: WidgetPatch): Widget;
Emitted schemas:
Base:
type: object
required: [id] # <- never transformed
properties:
id: { type: string }
WidgetPatch:
type: object
properties:
label: # <- own property: transformed correctly
anyOf:
- type: string
- type: 'null'
allOf:
- $ref: '#/components/schemas/Base' # <- unpatched base
Validating patch bodies against WidgetPatch:
INVALID {} <- must have required property 'id'
INVALID {"label":"x"} <- must have required property 'id'
INVALID {"label":null} <- must have required property 'id'
VALID {"id":"keep","label":"x"}
Expected: inherited properties go through the same transform as the model's own, so id becomes optional and nullable in the patch shape and {} validates.
The same happens when the extending model is used as a nested property rather than as the patch root; the generated *MergePatchUpdateOrCreate schema carries the same allOf to the unpatched base.
Describe the bug
For a model that uses
extends,MergePatchUpdatetransforms the model's own properties but emitsallOfpointing at the unpatched base schema. The inherited properties never go through the transform, so they stay required and non-nullable.The generated patch schema therefore demands every inherited required property on every request, including on the empty patch. The algorithm in RFC 7396, which this template's doc comment says the transform follows, iterates over the pairs present in the patch document, so
{}makes no changes and is always a valid patch. A schema that rejects{}is not describing a merge-patch body.Versions:
@typespec/compiler1.14.0,@typespec/http1.14.0,@typespec/openapi31.14.0.Reproduction
Emitted schemas:
Validating patch bodies against
WidgetPatch:Expected: inherited properties go through the same transform as the model's own, so
idbecomes optional and nullable in the patch shape and{}validates.The same happens when the extending model is used as a nested property rather than as the patch root; the generated
*MergePatchUpdateOrCreateschema carries the sameallOfto the unpatched base.