Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/downgrader/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ Accepts the default OAS dialect, which adds `discriminator`, `xml`, `externalDoc
| A [`$ref`][js-ref] with siblings | `allOf: [{ $ref }]` beside the siblings, which 3.0 would ignore. |
| [`$defs`][js-defs], and `definitions`, its draft-07 name | Removed. Each `$ref` into it is replaced by its converted target, where a reference back into a target being inlined becomes `{}`. |
| Boolean subschemas | `{}` for `true` and `{ not: {} }` for `false`, except in `additionalProperties`, which allows booleans. |
| [`patternProperties`][js-pattern-properties] | Removed, with the `additionalProperties` beside it, which would reject the properties it allowed. |
| [`patternProperties`][js-pattern-properties] | Removed. `additionalProperties` becomes `true`, as a schema or `false` there would reject the properties `patternProperties` allowed, and code generators read a missing one as closed. |
| [`$schema`][js-schema], [`$vocabulary`][js-vocabulary], [`$id`][js-id], [`$anchor`][js-anchor], [`$dynamicRef`, `$dynamicAnchor`][js-dynamic], [`$comment`][js-comment], and [`contentSchema`][js-content-schema] | Removed. |
| [`if`][js-if], [`then`][js-then], [`else`][js-else], [`dependentSchemas`][js-dependent-schemas], and [`dependentRequired`][js-dependent-required] | Removed. |
| [`contains`][js-contains], [`minContains`][js-min-contains], [`maxContains`][js-max-contains], and [`propertyNames`][js-property-names] | Removed. |
Expand Down Expand Up @@ -246,7 +246,7 @@ Accepts the default OAS dialect, which adds `discriminator`, `xml`, `externalDoc
- `$ref`s into a keyword 3.0 removes, such as `prefixItems/0` or `propertyNames`, or into a `oneOf` that becomes `anyOf`, are left as written and dangle.
- Recursion through `$defs` is cut to `{}` after one level. Where several `$ref`s enter the same cycle, the first copy made is reused, so a later one can be cut sooner.
- Older drafts are not supported. `definitions` is read as `$defs`, but other keywords that only draft-07 or 2019-09 define, such as `dependencies` and `additionalItems`, become extensions unconverted, so `$ref`s into them dangle, and array-form `items` passes through. Convert such schemas to 2020-12 first.
- `patternProperties` that matches every name, as TypeBox emits for a record, is removed with its value schema rather than turned into `additionalProperties`.
- `patternProperties` that matches every name, as TypeBox emits for a record, gives `additionalProperties: true`, so its value schema is lost.
- An untyped schema that gains a `format` from `contentEncoding` or `contentMediaType` also gains `type: "string"`, as 3.0 binary strings need, so it rejects non-string values it accepted. 3.0 tools find file uploads only by `type: "string"` with `format: "binary"`.
- OpenAPI Generator merges the `"null"` branch beside a `$ref` to a string enum into a new enum, so the reference is lost. Pass `--openapi-normalizer SIMPLIFY_ONEOF_ANYOF_ENUM=false` to keep it.
- datamodel-code-generator reads a required property with `nullable: true` as never null. Pass `--strict-nullable` to keep null.
Expand Down
6 changes: 6 additions & 0 deletions packages/downgrader/src/v3.1-to-v3.0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,12 @@ function finishSchema(
if (Array.isArray(schema.examples) && schema.examples.length > 0 && out.example === undefined) {
out.example = clone(schema.examples[0])
}
// 3.0 lacks `patternProperties`, and `additionalProperties` beside it is
// dropped, as it would reject the properties `patternProperties` allowed.
// Code generators read a missing one as allowing no other properties.
if (schema.patternProperties !== undefined) {
out.additionalProperties = true
}
const format = binaryFormat(schema)
if (format !== undefined && (schema.type === undefined || hasType(schema.type, 'string'))) {
// A `format` that only repeats the encoding, as Zod writes, gives way.
Expand Down
17 changes: 10 additions & 7 deletions packages/downgrader/tests/v3.1-to-v3.0.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1100,14 +1100,17 @@ describe('downgradeSchemaV31ToV30', () => {
})
})

it('drops additionalProperties beside patternProperties, which it would contradict', () => {
it('makes additionalProperties beside patternProperties true, which allows what patternProperties did', () => {
const patternProperties = { '^x-': { type: 'string' } } as const
// As Zod writes a loose record, and Effect a record with number keys.
for (const additionalProperties of [undefined, true, false, { type: 'number' }] as const) {
expect(
downgradeSchemaV31ToV30({ type: 'object', patternProperties, additionalProperties }),
).toEqual({ type: 'object', additionalProperties: true })
}
expect(
downgradeSchemaV31ToV30({
type: 'object',
patternProperties: { '^x-': { type: 'string' } },
additionalProperties: false,
}),
).toEqual({ type: 'object' })
downgradeSchemaV31ToV30({ properties: { a: { type: 'string' } }, patternProperties }),
).toEqual({ properties: { a: { type: 'string' } }, additionalProperties: true })
})

describe('a schema that lost a restriction', () => {
Expand Down
Loading