diff --git a/packages/openapi3-parser/lib/parser/oas/parseSchemaObject.js b/packages/openapi3-parser/lib/parser/oas/parseSchemaObject.js index a76104ea4..b9879643e 100644 --- a/packages/openapi3-parser/lib/parser/oas/parseSchemaObject.js +++ b/packages/openapi3-parser/lib/parser/oas/parseSchemaObject.js @@ -30,9 +30,6 @@ const unsupportedKeys = [ ]; const isUnsupportedKey = R.anyPass(R.map(hasKey, unsupportedKeys)); -// purposely in the order defined in the JSON Schema spec, integer is an OAS 3 specific addition and thus is at the end -const types = ['boolean', 'object', 'array', 'number', 'string', 'integer']; -const isValidType = R.anyPass(R.map(hasValue, types)); function constructObjectStructure(namespace, schema) { const element = R.or(schema.get('properties'), new namespace.elements.Object()); @@ -69,6 +66,11 @@ function constructArrayStructure(namespace, schema) { return element; } +const openapi30Types = ['boolean', 'object', 'array', 'number', 'string', 'integer']; +const openapi31Types = openapi30Types.concat(['null']); +const isValidOpenAPI30Type = R.anyPass(R.map(hasValue, openapi30Types)); +const isValidOpenAPI31Type = R.anyPass(R.map(hasValue, openapi31Types)); + const typeToElementNameMap = { array: 'array', boolean: 'boolean', @@ -79,6 +81,31 @@ const typeToElementNameMap = { string: 'string', }; +function parseType(context) { + let types; + let isValidType; + + if (context.isOpenAPIVersionMoreThanOrEqual(3, 1)) { + types = openapi31Types; + isValidType = isValidOpenAPI31Type; + } else { + types = openapi30Types; + isValidType = isValidOpenAPI30Type; + } + + const ensureValidType = R.unless( + isValidType, + R.compose( + createWarning(context.namespace, `'${name}' 'type' must be either ${types.join(', ')}`), + getValue + ) + ); + + return pipeParseResult(context.namespace, + parseString(context, name, false), + ensureValidType); +} + // Returns whether the given element value matches the provided schema type const valueMatchesType = (type, value) => { const expectedElementType = typeToElementNameMap[type]; @@ -172,18 +199,6 @@ function validateOneOfIsNotUsedWithUnsupportedConstraints(context) { function parseSchema(context) { const { namespace } = context; - const ensureValidType = R.unless( - isValidType, - R.compose( - createWarning(namespace, `'Schema Object' 'type' must be either ${types.join(', ')}`), - getValue - ) - ); - - const parseType = pipeParseResult(namespace, - parseString(context, name, false), - ensureValidType); - const parseSubSchema = element => parseReference('schemas', R.uncurryN(2, parseSchema), context, element, true); const parseProperties = parseObject(context, `${name}' 'properties`, R.compose(parseSubSchema, getValue)); @@ -200,7 +215,7 @@ function parseSchema(context) { }); const parseMember = R.cond([ - [hasKey('type'), parseType], + [hasKey('type'), parseType(context)], [hasKey('enum'), R.compose(parseEnum(context, name), getValue)], [hasKey('properties'), R.compose(parseProperties, getValue)], [hasKey('items'), R.compose(parseSubSchema, getValue)], @@ -243,6 +258,8 @@ function parseSchema(context) { element = new namespace.elements.Number(); } else if (type === 'boolean') { element = new namespace.elements.Boolean(); + } else if (type === 'null') { + element = new namespace.elements.Null(); } else { element = new namespace.elements.Enum(); element.enumerations = [ diff --git a/packages/openapi3-parser/test/unit/parser/oas/parseSchemaObject-test.js b/packages/openapi3-parser/test/unit/parser/oas/parseSchemaObject-test.js index f417c337d..df68f1611 100644 --- a/packages/openapi3-parser/test/unit/parser/oas/parseSchemaObject-test.js +++ b/packages/openapi3-parser/test/unit/parser/oas/parseSchemaObject-test.js @@ -143,6 +143,32 @@ describe('Schema Object', () => { const string = parseResult.get(0).content; expect(string).to.be.instanceof(namespace.elements.Number); }); + + it('returns a warning for null type on OpenAPI prior 3.1', () => { + const schema = new namespace.elements.Object({ + type: 'null', + }); + const parseResult = parse(context, schema); + + expect(parseResult).to.contain.warning( + "'Schema Object' 'type' must be either boolean, object, array, number, string, integer" + ); + }); + + it('returns a null structure for null type on OpenAPI 3.1', () => { + context.openapiVersion = { major: 3, minor: 1 }; + const schema = new namespace.elements.Object({ + type: 'null', + }); + const parseResult = parse(context, schema); + + expect(parseResult.length).to.equal(1); + expect(parseResult.get(0)).to.be.instanceof(namespace.elements.DataStructure); + expect(parseResult).to.not.contain.annotations; + + const element = parseResult.get(0).content; + expect(element).to.be.instanceof(namespace.elements.Null); + }); }); describe('#enum', () => {