diff --git a/functions/is-problem-json-schema.js b/functions/is-problem-json-schema.js index b42804f..4d71cd4 100644 --- a/functions/is-problem-json-schema.js +++ b/functions/is-problem-json-schema.js @@ -45,8 +45,33 @@ const assertProblemSchema = (schema) => { } }; +/* + * Merge list of schema definitions of type = 'object'. + * Return object will have a super set of attributes 'properties' and 'required'. + */ +const mergeObjectDefinitions = (allOfTypes) => { + if (allOfTypes.filter((item) => item.type !== 'object').length !== 0) { + throw "All schema definitions must be of type 'object'"; + } + + return allOfTypes.reduce((acc, item) => { + return { + type: 'object', + properties: { ...(acc.properties || {}), ...(item.properties || {}) }, + required: [...(acc.required || []), ...(item.required || [])], + }; + }, {}); +}; + const check = (schema) => { - const combinedSchemas = [...(schema.anyOf || []), ...(schema.oneOf || []), ...(schema.allOf || [])]; + const combinedSchemas = [...(schema.anyOf || []), ...(schema.oneOf || [])]; + if (schema.allOf) { + const mergedAllOf = mergeObjectDefinitions(schema.allOf); + if (mergedAllOf) { + combinedSchemas.push(mergedAllOf); + } + } + if (combinedSchemas.length > 0) { combinedSchemas.forEach(check); } else { diff --git a/tests/176-MUST-support-problem-JSON.test.ts b/tests/176-MUST-support-problem-JSON.test.ts index 97f03f7..0384b03 100644 --- a/tests/176-MUST-support-problem-JSON.test.ts +++ b/tests/176-MUST-support-problem-JSON.test.ts @@ -44,4 +44,36 @@ describe('MUST support problem JSON [176]', () => { }), ]); }); + + test('Verify custom problem extending valid problem object does not cause error', async () => { + const openApi = await loadOpenApiSpec('base-openapi.yml'); + + // define custom problem extending problem json + openApi.components.schemas['CustomValidationProblem'] = { + allOf: [ + { $ref: '#/components/schemas/Problem' }, + { + type: 'object', + required: ['title', 'status', 'detail', 'validation_error'], + properties: { + validation_error: { type: 'string' }, + }, + }, + ], + }; + + openApi.paths['/example'].get.responses['400'] = { + description: 'bad request', + content: { + 'application/problem+json': { + schema: { + $ref: '#/components/schemas/CustomValidationProblem', + }, + }, + }, + }; + + const result = await lint(openApi); + expect(result).toEqual([]); + }); });