Skip to content

OpenAPI/JsonSchema - use unevaluatedProperties instead of additionalProperties and support setting to false - #5961

Merged
Christopher Radek (chrisradek) merged 6 commits into
microsoft:mainfrom
chrisradek:schemas-unevaluated-props
Feb 12, 2025
Merged

OpenAPI/JsonSchema - use unevaluatedProperties instead of additionalProperties and support setting to false#5961
Christopher Radek (chrisradek) merged 6 commits into
microsoft:mainfrom
chrisradek:schemas-unevaluated-props

Conversation

@chrisradek

@chrisradek Christopher Radek (chrisradek) commented Feb 11, 2025

Copy link
Copy Markdown
Member

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

model Widget {
  id: string;
  ...Record<never>;
}
{
  "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.

@azure-sdk

Azure SDK Bot (azure-sdk) commented Feb 11, 2025

Copy link
Copy Markdown
Collaborator

All changed packages have been documented.

  • @typespec/json-schema
  • @typespec/openapi3
Show changes

@typespec/json-schema - feature ✏️

Updates JsonSchema and Open API 3.1 emitters to use unevaluatedProperties instead of additionalProperties, and updates Open API 3 emitters to match JsonSchema behavior of treating Record<never> as setting additionalProperties: { not: {} }

@typespec/openapi3 - feature ✏️

Updates JsonSchema and Open API 3.1 emitters to use unevaluatedProperties instead of additionalProperties, and updates Open API 3 emitters to match JsonSchema behavior of treating Record<never> as setting additionalProperties: { not: {} }

Comment thread .chronus/changes/schemas-unevaluated-props-2025-1-11-14-31-24.md Outdated
@azure-sdk

Azure SDK Bot (azure-sdk) commented Feb 11, 2025

Copy link
Copy Markdown
Collaborator

You can try these changes here

🛝 Playground 🌐 Website 📚 Next docs 🛝 VSCode Extension

Merged via the queue into microsoft:main with commit 9bd1e82 Feb 12, 2025
@chrisradek
Christopher Radek (chrisradek) deleted the schemas-unevaluated-props branch February 12, 2025 23:09
Dmitrii Sedelnikov (dmnorc) pushed a commit to dmnorc/typespec that referenced this pull request Feb 18, 2025
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants