Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Support null 3.1 type in Schema Object #597

Merged
merged 2 commits into from
Feb 22, 2021
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
49 changes: 33 additions & 16 deletions packages/openapi3-parser/lib/parser/oas/parseSchemaObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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',
Expand All @@ -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];
Expand Down Expand Up @@ -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));

Expand All @@ -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)],
Expand Down Expand Up @@ -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 = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down