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
2 changes: 1 addition & 1 deletion packages/downgrader/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Schema Objects:
| `true` / `false` boolean schemas | `{}` / `{ not: {} }` |
| `$ref` with sibling keywords | siblings kept, `$ref` moved into `allOf` |
| `type: ["T", "null"]` | `type: "T"` plus `nullable: true` |
| `type` with several non-null entries | `anyOf` of single-type schemas, each `nullable` when `null` was listed |
| `type` with several non-null entries | `anyOf` of single-type schemas, each `nullable` when `null` was listed. A sibling `items` moves into the `array` variant |
| `type: "null"` | `enum: [null]`, since 3.0 ignores `nullable` without a `type`. A sibling `enum` or `const` is intersected with the null type: an `enum` containing `null` collapses to `[null]`, and one excluding it yields `not: {}`, since the source accepted no value |
| `const` | single-value `enum` |
| numeric `exclusiveMinimum` / `exclusiveMaximum` | `minimum` / `maximum` plus the boolean flag; a tighter existing bound wins |
Expand Down
34 changes: 31 additions & 3 deletions packages/downgrader/src/v3.1-to-v3.0.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -898,13 +898,20 @@ describe('downgradeSchemaV31ToV30', () => {
{ anyOf: [{ items: {}, type: 'array' }, { type: 'string' }] },
],
[
'copies existing items into the synthesized array variant',
'moves existing items into the synthesized array variant',
{ items: { type: 'integer' }, type: ['array', 'string', 'null'] },
{
anyOf: [
{ items: { type: 'integer' }, nullable: true, type: 'array' },
{ nullable: true, type: 'string' },
],
},
],
[
'keeps items in place when the type union has no array variant',
{ items: { type: 'integer' }, type: ['object', 'string'] },
{
anyOf: [{ type: 'object' }, { type: 'string' }],
items: { type: 'integer' },
},
],
Expand Down Expand Up @@ -936,9 +943,10 @@ describe('downgradeSchemaV31ToV30', () => {
{
allOf: 'junk',
anyOf: [{ type: 'string' }],
type: ['integer', 'string'],
items: { type: 'integer' },
type: ['array', 'string'],
},
{ allOf: 'junk', anyOf: [{ type: 'string' }] },
{ allOf: 'junk', anyOf: [{ type: 'string' }], items: { type: 'integer' } },
],
[
'deduplicates type array entries',
Expand Down Expand Up @@ -1369,6 +1377,16 @@ describe('downgradeSchemaV31ToV30', () => {
expect(() => downgradeSchemaV31ToV30(deep)).not.toThrow()
})

it('keeps nested multi-type arrays linear instead of doubling per level', () => {
let input: OpenAPIV3_1.SchemaObject = { type: 'string' }
let expected: unknown = { type: 'string' }
for (let index = 0; index < 10; index += 1) {
input = { items: input, type: ['array', 'object'] }
expected = { anyOf: [{ items: expected, type: 'array' }, { type: 'object' }] }
}
expect(convertSchema(input)).toEqual(expected)
})

it('converts a dereferenced cyclic schema, pointing the cycle at the converted ancestor', () => {
const properties: Record<string, unknown> = {}
const node: Record<string, unknown> = {
Expand All @@ -1384,5 +1402,15 @@ describe('downgradeSchemaV31ToV30', () => {
expect(dig(result, 'properties', 'children', 'items')).toBe(result)
expect(node.type).toEqual(['object', 'null'])
})

it('points the array variant of a cyclic multi-type schema at the converted schema', () => {
const node: Record<string, unknown> = { type: ['array', 'object'] }
node.items = node
const result = convertSchema(node) as Record<string, unknown>
expect(result).not.toHaveProperty('items')
expect(dig(result, 'anyOf', '0', 'items')).toBe(result)
expect(dig(result, 'anyOf', '1')).toEqual({ type: 'object' })
expect(node.items).toBe(node)
})
})
})
8 changes: 6 additions & 2 deletions packages/downgrader/src/v3.1-to-v3.0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ function applyTypes(types: string[], schema: Record<string, unknown>, out: Recor
}
return
}
if (out.anyOf !== undefined && out.allOf !== undefined && !Array.isArray(out.allOf)) {
return
}
const variants = rest.map((item) => {
const variant: Record<string, unknown> = { type: item }
if (item === 'array') {
variant.items = out.items === undefined ? {} : deepClone(out.items)
variant.items = out.items === undefined ? {} : out.items
delete out.items
Comment thread
pullfrog[bot] marked this conversation as resolved.
}
if (nullable) {
variant.nullable = true
Expand All @@ -71,7 +75,7 @@ function applyTypes(types: string[], schema: Record<string, unknown>, out: Recor
if (out.anyOf === undefined) {
out.anyOf = variants
}
else if (out.allOf === undefined || Array.isArray(out.allOf)) {
else {
out.allOf = [...(Array.isArray(out.allOf) ? out.allOf : []), { anyOf: variants }]
}
}
Expand Down
Loading