diff --git a/.changeset/ref-sibling-regression.md b/.changeset/ref-sibling-regression.md new file mode 100644 index 0000000..1ef5ad6 --- /dev/null +++ b/.changeset/ref-sibling-regression.md @@ -0,0 +1,5 @@ +--- +"openapi-sampler": patch +--- + +Fixed a regression from 1.7.7: a `$ref` inside `allOf` was sampled as `null`, and `skipReadOnly`/`skipWriteOnly` ignored a `$ref` inside `oneOf` or `anyOf`. diff --git a/src/allOf.js b/src/allOf.js index f7d69da..a4d3d31 100644 --- a/src/allOf.js +++ b/src/allOf.js @@ -7,7 +7,8 @@ export function allOfSample(into, children, options, spec, context) { const subSamples = []; for (let subSchema of children) { - const { type, readOnly, writeOnly, value } = traverse({ type: res.type, ...subSchema }, options, spec, { + const childSchema = subSchema.$ref ? subSchema : { type: res.type, ...subSchema }; + const { type, readOnly, writeOnly, value } = traverse(childSchema, options, spec, { ...context, isAllOfChild: true, }); diff --git a/src/traverse.js b/src/traverse.js index b2632d1..fa4c73b 100644 --- a/src/traverse.js +++ b/src/traverse.js @@ -78,7 +78,9 @@ export function traverse(schema, options, spec, context) { ref = ref.substring(1); } - const { $ref: _, ...siblings } = schema; + const siblings = Object.fromEntries( + Object.entries(schema).filter(([key, value]) => key !== '$ref' && value !== undefined), + ); const referenced = Object.keys(siblings).length && !shouldIgnoreRefSiblings(spec) ? mergeRefSiblings(JsonPointer.get(spec, ref), siblings) : JsonPointer.get(spec, ref); diff --git a/test/integration.spec.js b/test/integration.spec.js index 4ee1a01..4e662a5 100644 --- a/test/integration.spec.js +++ b/test/integration.spec.js @@ -939,6 +939,54 @@ describe('Integration', () => { expect(spec.components.schemas.Num).toEqual({ type: 'number' }); } }); + + it('should apply a null sibling written in the document', () => { + result = sample({ $ref: '#/components/schemas/Num', default: null }, {}, refSiblingsSpec({ openapi: '3.1.0' })); + expect(result).toBeNull(); + }); + + it('should not apply the type that allOf passes to its items to a $ref', () => { + for (const spec of [refSiblingsSpec({ openapi: '3.1.0' }), refSiblingsSpec({})]) { + result = sample({ allOf: [{ $ref: '#/components/schemas/Pet' }] }, {}, spec); + expect(result).toEqual({ name: 'string' }); + + result = sample({ + allOf: [ + { $ref: '#/components/schemas/Pet' }, + { properties: { age: { type: 'integer' } } }, + ] + }, {}, spec); + expect(result).toEqual({ name: 'string', age: 0 }); + } + }); + + it('should keep readOnly and writeOnly of a $ref in oneOf and anyOf', () => { + for (const version of [{ openapi: '3.1.0' }, {}]) { + const spec = { + ...version, + components: { + schemas: { + Id: { type: 'string', readOnly: true }, + Password: { type: 'string', writeOnly: true }, + }, + }, + }; + const schema = { + type: 'object', + properties: { + id: { oneOf: [{ $ref: '#/components/schemas/Id' }] }, + password: { anyOf: [{ $ref: '#/components/schemas/Password' }] }, + name: { type: 'string' }, + }, + }; + + result = sample(schema, { skipReadOnly: true }, spec); + expect(result).toEqual({ password: 'string', name: 'string' }); + + result = sample(schema, { skipWriteOnly: true }, spec); + expect(result).toEqual({ id: 'string', name: 'string' }); + } + }); }); });