-
Notifications
You must be signed in to change notification settings - Fork 245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow configuration of unevaluatedProperties
in JSON Schema emitter, and possibly change the default behaviour of all models.
#3549
Comments
unevaluatedProperties
in JSON Schema emitter, and maybe additionalProperties
unevaluatedProperties
& additionalProperties
in JSON Schema emitter, and possibly change the default behavior
Some other epiphanies about this.
In the context of typespec, (2) completely messes with the expected semantics of However
You can imagine possibly this should be an emitter-level option that can override this default. As well as decorator overrides. But I think it makes sense to not have some implicit undeclared behaviour by default. |
unevaluatedProperties
& additionalProperties
in JSON Schema emitter, and possibly change the default behaviorunevaluatedProperties
in JSON Schema emitter, and possibly change the default behaviour
unevaluatedProperties
in JSON Schema emitter, and possibly change the default behaviourunevaluatedProperties
in JSON Schema emitter, and possibly change the default behaviour
unevaluatedProperties
in JSON Schema emitter, and possibly change the default behaviourunevaluatedProperties
in JSON Schema emitter, and possibly change the default behaviour of all models.
Indeed it would. It's kind of like a But why not allow models to opt-in to using it with a decorator if the OpenAPI output requires its use, taking responsibility for the potential consequences? Perhaps the In my situation, we have an API standard that requires It is very unfortunate that I cannot satisfy this requirement with Typespec. I can work around it by patching It sounds like motivation behind #5393 may be similar. |
This ticket was originally for the JSON schema emitter. For that emitter, Possibly, I should extend the title of this ticket since I do think it makes sense to think about this problem holistically across the targets instead of different tickets for each -- in the end solution could be per-emitter solutions, but we don't know that yet (I think...). And for sure part of the problem that is universal is around semantics in the language like I'm going to ping @wanlwanl and @chrisradek just because you interacted on related tickets (sorry for the annoying ping!) just in case they think the above is a good idea re: merging the tickets/expanding this one. Anyway, back to it. With other targets I think this kind of thing now comes down to a philosophical design decision on the part of TypeSpec. Are we trying to achieve "write it one consistent way and get a working solution regardless of target(s) schemas and their versions", or "you need to have the target in mind when writing your typespec". I like TypeSpec, but I feel it already mixes both of these ideas, and it's sometimes unclear where the line is drawn. Yes -- emitters have their own decorators, and you have to explicitly commit to them. But also, many of the core decorators as well as the language constructs themselves, just by existing, amount to assumptions about what is supported and makes sense across multiple emitters. But the reality is you've then got people on different versions of those targets as well. And the standards landscape is not just already-varied but also changing; and those changes could end up invalidating previous compromises on decisions like what is a global decorator and what is emitter level etc. So should there be multiple emitters for each version of OpenAPI? Or should the versions of each target schema type be restricted such that it is with those that work with TypeSpec? Or add userland escape hatches? It's a complicated situation and, ultimately, the headache that TypeSpec aims to solve. I've actually drove myself insane thinking about this problem and what the canonical answer is whilst working with TypeSpec, so I don't envy their position. Looking at it another way -- much of the compromises and decisions made around these things are actually in themselves, part of the inherent value of the TypeSpec project and their careful consideration of them is why we have TypeSpec today -- and its cool! Personally, in this case, I don't think it would be wholly unreasonable (though maybe unpopular) to:
In the short term, this allows everyone to do what they need without committing to potentially impactful and difficult-to-reverse decisions around |
We discussed this as a team and arrived at roughly this approach:
For item 2 - in order to handle the For Item 3 - we haven't found a good way to set
|
Thank you so much for the thoughtful consideration of these use cases! I've not used the |
…roperties and support setting to false (#5961) Related to #3549 This PR does a couple things: ### Json Schema and Open API 3.1 use unevaluatedProperties instead of additionalProperties `unevaluatedProperties` is similar to `additionalProperties` in that it can specify what extra properties are allowed on an object. One key difference from additionalProperties though is that it evaluates properties after any in-place applicators. Practically speaking, this means that it will take into account any properties defined in `allOf` subschemas when validating an object instance, whereas additionalProperties only takes into account properties defined in its containing schema. This is particularly useful when trying to set `additionalProperties` to false on a schema that has sub-schemas. #### Risks Functionally, I don't believe this is a breaking change. Where this _might_ cause problems though is if someone is doing their own processing of the Json Schema or Open API 3.1 output to add `additionalProperties: false` if that field isn't present, since those would now be called `unevaluatedProperties`. ### Open API 3 - support Record\<never\> for additionalProperties: { not: {} } This change brings the Open API 3 (3.0 and 3.1) emitter in line with the Json Schema emitter, which already supports treating `Record<never>` as `additionalProperties: { not: {} }`. `{ not: {} }` is equivalent to the boolean `false` for schemas, so this is the same as supporting `additionalProperties: false`. _Note_: For Open API 3.1 output, `unevaluatedProperties` is emitted instead of `additionalProperties`. For Open API 3.0 output that still relies on `additionalProperties`, there's some additional handling of `model extends` so that any properties that exist on a base model, but not the derived model, are redeclared as `propertyName: {}` in the derived model. Without this, if the base model contains any properties that aren't in the derived model, and the derived model spreads `Record<never>`, that emitted schema will never pass validation on an input. #### Example ```tsp model Widget { id: string; ...Record<never>; } ``` ```json { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "string" } }, "unevaluatedProperties": { "not": {} } } ``` ### Followups A separate PR will be created to add an emitter option to JsonSchema/OpenAPI emitters to automatically set `additionalProperties: { not: {} }` on at least leaf schemas. --------- Co-authored-by: Christopher Radek <Christopher.Radek@microsoft.com> Co-authored-by: Timothee Guerin <timothee.guerin@outlook.com>
…roperties and support setting to false (microsoft#5961) Related to microsoft#3549 This PR does a couple things: ### Json Schema and Open API 3.1 use unevaluatedProperties instead of additionalProperties `unevaluatedProperties` is similar to `additionalProperties` in that it can specify what extra properties are allowed on an object. One key difference from additionalProperties though is that it evaluates properties after any in-place applicators. Practically speaking, this means that it will take into account any properties defined in `allOf` subschemas when validating an object instance, whereas additionalProperties only takes into account properties defined in its containing schema. This is particularly useful when trying to set `additionalProperties` to false on a schema that has sub-schemas. #### Risks Functionally, I don't believe this is a breaking change. Where this _might_ cause problems though is if someone is doing their own processing of the Json Schema or Open API 3.1 output to add `additionalProperties: false` if that field isn't present, since those would now be called `unevaluatedProperties`. ### Open API 3 - support Record\<never\> for additionalProperties: { not: {} } This change brings the Open API 3 (3.0 and 3.1) emitter in line with the Json Schema emitter, which already supports treating `Record<never>` as `additionalProperties: { not: {} }`. `{ not: {} }` is equivalent to the boolean `false` for schemas, so this is the same as supporting `additionalProperties: false`. _Note_: For Open API 3.1 output, `unevaluatedProperties` is emitted instead of `additionalProperties`. For Open API 3.0 output that still relies on `additionalProperties`, there's some additional handling of `model extends` so that any properties that exist on a base model, but not the derived model, are redeclared as `propertyName: {}` in the derived model. Without this, if the base model contains any properties that aren't in the derived model, and the derived model spreads `Record<never>`, that emitted schema will never pass validation on an input. #### Example ```tsp model Widget { id: string; ...Record<never>; } ``` ```json { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "string" } }, "unevaluatedProperties": { "not": {} } } ``` ### Followups A separate PR will be created to add an emitter option to JsonSchema/OpenAPI emitters to automatically set `additionalProperties: { not: {} }` on at least leaf schemas. --------- Co-authored-by: Christopher Radek <Christopher.Radek@microsoft.com> Co-authored-by: Timothee Guerin <timothee.guerin@outlook.com>
Clear and concise description of the problem
Currently, there exists no (non-workaround) decorator in order to configure
unevaluatedProperties
on a schema. If looking to restrict unknown properties, I would hazardunevaluatedProperties: false
is usually the tool that you should reach for as opposed toadditionalProperties: false
-- especially where you have TypeSpec in play which induces dev expectations/assumptions about semantics of things likeextends
.That's because
additionalProperties: false
will restrict properties to those defined on the specific schema, and so prevent the use of properties on related schemas (viaallOf
for example).In that sense
additionalProperties: false
seals the definition and would not actually work with any properties coming from some other model viaextends
, which is weird semantics.Incidentally, there is no way to set
additionalProperties: false
either unless you override it with@extension("additionalProperties", Json<false>)
. But perhaps that should be added as a sort of@sealed
decorator.We could add a
@noUnknown
decorator or something that controlsunevaluatedProperties
. At the moment you have to do@extension("unevaluatedProperties", Json<false>)
which feels like a hack.Perhaps it could be put into one like
@strictProperties(preventExtension: boolean)
wherepreventExtension
would befalse
by default. iffalse
, it would addunevaluatedProperties: false
to the schema. Iftrue
, it would instead useadditionalProperties: false
, which would ideally a throw an error if used when the schema attempts to use index types (includingRecord
), or if that schema is extended by another.Note, not being able to define these properties using a first class API is unusual as the default behaviour of JSON Schema is to allow additional properties which is behavior the user has not defined in their schema specifically using Record or index types. Therefore there is probably a debate to be had about if
unevaluatedProperties: false
should actually be added to schemas by default.Checklist
The text was updated successfully, but these errors were encountered: