diff --git a/src/plugins/validation/2and3/semantic-validators/responses.js b/src/plugins/validation/2and3/semantic-validators/responses.js index 8095b931c..09b9c1743 100644 --- a/src/plugins/validation/2and3/semantic-validators/responses.js +++ b/src/plugins/validation/2and3/semantic-validators/responses.js @@ -19,23 +19,61 @@ module.exports.validate = function({ jsSpec, isOAS3 }, config) { each(obj, (response, responseKey) => { if (isOAS3) { each(response.content, (mediaType, mediaTypeKey) => { - const hasInlineSchema = mediaType.schema && !mediaType.schema.$ref; + const combinedSchemaTypes = ['allOf', 'oneOf', 'anyOf']; + if ( - hasInlineSchema && + mediaType.schema && mediaTypeKey.startsWith('application/json') ) { - const checkStatus = config.inline_response_schema; - if (checkStatus !== 'off') { - result[checkStatus].push({ - path: [ - ...path, - responseKey, - 'content', - mediaTypeKey, - 'schema' - ], - message: INLINE_SCHEMA_MESSAGE + const hasCombinedSchema = + mediaType.schema.allOf || + mediaType.schema.anyOf || + mediaType.schema.oneOf; + + if (hasCombinedSchema) { + combinedSchemaTypes.forEach(schemaType => { + if (mediaType.schema[schemaType]) { + for ( + let i = 0; + i < mediaType.schema[schemaType].length; + i++ + ) { + const hasInlineSchema = !mediaType.schema[schemaType][i] + .$ref; + if (hasInlineSchema) { + const checkStatus = config.inline_response_schema; + if (checkStatus !== 'off') { + result[checkStatus].push({ + path: [ + ...path, + responseKey, + 'content', + mediaTypeKey, + 'schema', + schemaType, + i + ], + message: INLINE_SCHEMA_MESSAGE + }); + } + } + } + } }); + } else if (!mediaType.schema.$ref) { + const checkStatus = config.inline_response_schema; + if (checkStatus !== 'off') { + result[checkStatus].push({ + path: [ + ...path, + responseKey, + 'content', + mediaTypeKey, + 'schema' + ], + message: INLINE_SCHEMA_MESSAGE + }); + } } } }); diff --git a/test/plugins/validation/2and3/responses.js b/test/plugins/validation/2and3/responses.js index 3388a5e7f..9d9e251c4 100644 --- a/test/plugins/validation/2and3/responses.js +++ b/test/plugins/validation/2and3/responses.js @@ -161,6 +161,116 @@ describe('validation plugin - semantic - responses', function() { expect(res.errors.length).toEqual(0); }); + it('should not complain for a valid response in oneOf', function() { + const spec = { + paths: { + '/stuff': { + get: { + summary: 'list stuff', + operationId: 'listStuff', + responses: { + 200: { + description: 'successful operation', + content: { + 'application/json': { + schema: { + oneOf: [ + { + $ref: + '#/components/schemas/ListStuffResponseModel' + }, + { + $ref: '#/components/schemas/ListStuffSecondModel' + } + ] + } + } + } + } + } + } + } + } + }; + + const res = validate({ jsSpec: spec, isOAS3: true }, config); + expect(res.warnings.length).toEqual(0); + expect(res.errors.length).toEqual(0); + }); + + it('should not complain for a valid response in allOf', function() { + const spec = { + paths: { + '/stuff': { + get: { + summary: 'list stuff', + operationId: 'listStuff', + responses: { + 200: { + description: 'successful operation', + content: { + 'application/json': { + schema: { + allOf: [ + { + $ref: + '#/components/schemas/ListStuffResponseModel' + }, + { + $ref: '#/components/schemas/ListStuffSecondModel' + } + ] + } + } + } + } + } + } + } + } + }; + + const res = validate({ jsSpec: spec, isOAS3: true }, config); + expect(res.warnings.length).toEqual(0); + expect(res.errors.length).toEqual(0); + }); + + it('should not complain for a valid response in anyOf', function() { + const spec = { + paths: { + '/stuff': { + get: { + summary: 'list stuff', + operationId: 'listStuff', + responses: { + 200: { + description: 'successful operation', + content: { + 'application/json': { + schema: { + anyOf: [ + { + $ref: + '#/components/schemas/ListStuffResponseModel' + }, + { + $ref: '#/components/schemas/ListStuffSecondModel' + } + ] + } + } + } + } + } + } + } + } + }; + + const res = validate({ jsSpec: spec, isOAS3: true }, config); + expect(res.warnings.length).toEqual(0); + expect(res.errors.length).toEqual(0); + }); it('should complain about an inline schema', function() { const spec = { paths: { @@ -208,6 +318,159 @@ describe('validation plugin - semantic - responses', function() { expect(res.errors.length).toEqual(0); }); + it('should complain about an inline schema when using oneOf', function() { + const spec = { + paths: { + '/stuff': { + get: { + summary: 'list stuff', + operationId: 'listStuff', + responses: { + 200: { + description: 'successful operation', + content: { + 'application/json': { + schema: { + oneOf: [ + { + type: 'object' + }, + { + $ref: 'ref1' + } + ] + } + } + } + } + } + } + } + } + }; + + const res = validate({ jsSpec: spec, isOAS3: true }, config); + expect(res.warnings.length).toEqual(1); + expect(res.warnings[0].path).toEqual([ + 'paths', + '/stuff', + 'get', + 'responses', + '200', + 'content', + 'application/json', + 'schema', + 'oneOf', + 0 + ]); + expect(res.warnings[0].message).toEqual( + 'Response schemas should be defined with a named ref.' + ); + expect(res.errors.length).toEqual(0); + }); + + it('should complain about an inline schema when using allOf', function() { + const spec = { + paths: { + '/stuff': { + get: { + summary: 'list stuff', + operationId: 'listStuff', + responses: { + 200: { + description: 'successful operation', + content: { + 'application/json': { + schema: { + allOf: [ + { + type: 'object' + }, + { + $ref: 'ref1' + } + ] + } + } + } + } + } + } + } + } + }; + + const res = validate({ jsSpec: spec, isOAS3: true }, config); + expect(res.warnings.length).toEqual(1); + expect(res.warnings[0].path).toEqual([ + 'paths', + '/stuff', + 'get', + 'responses', + '200', + 'content', + 'application/json', + 'schema', + 'allOf', + 0 + ]); + expect(res.warnings[0].message).toEqual( + 'Response schemas should be defined with a named ref.' + ); + expect(res.errors.length).toEqual(0); + }); + + it('should complain about an inline schema when using anyOf', function() { + const spec = { + paths: { + '/stuff': { + get: { + summary: 'list stuff', + operationId: 'listStuff', + responses: { + 200: { + description: 'successful operation', + content: { + 'application/json': { + schema: { + anyOf: [ + { + type: 'object' + }, + { + $ref: 'ref1' + } + ] + } + } + } + } + } + } + } + } + }; + + const res = validate({ jsSpec: spec, isOAS3: true }, config); + expect(res.warnings.length).toEqual(1); + expect(res.warnings[0].path).toEqual([ + 'paths', + '/stuff', + 'get', + 'responses', + '200', + 'content', + 'application/json', + 'schema', + 'anyOf', + 0 + ]); + expect(res.warnings[0].message).toEqual( + 'Response schemas should be defined with a named ref.' + ); + expect(res.errors.length).toEqual(0); + }); + it('should not complain for a response with no schema', function() { const spec = { paths: {