-
Notifications
You must be signed in to change notification settings - Fork 18
Support array of type in Schema Object #600
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ const { | |
} = require('../annotations'); | ||
const pipeParseResult = require('../../pipeParseResult'); | ||
const { | ||
isString, hasKey, getValue, | ||
isArray, isString, hasKey, getValue, | ||
} = require('../../predicates'); | ||
const parseObject = require('../parseObject'); | ||
const parseArray = require('../parseArray'); | ||
|
@@ -128,10 +128,14 @@ const typeToElementNameMap = { | |
* Normalises result into an ArrayElement of StringElement | ||
*/ | ||
function parseType(context) { | ||
const { namespace } = context; | ||
|
||
let types; | ||
let isValidType; | ||
|
||
if (context.isOpenAPIVersionMoreThanOrEqual(3, 1)) { | ||
const isOpenAPI31OrHigher = R.always(context.isOpenAPIVersionMoreThanOrEqual(3, 1)); | ||
|
||
if (isOpenAPI31OrHigher()) { | ||
types = openapi31Types; | ||
isValidType = isValidOpenAPI31Type; | ||
} else { | ||
|
@@ -141,13 +145,71 @@ function parseType(context) { | |
|
||
const ensureValidType = R.unless( | ||
element => isValidType(element.toValue()), | ||
createWarning(context.namespace, `'${name}' 'type' must be either ${types.join(', ')}`) | ||
createWarning(namespace, `'${name}' 'type' must be either ${types.join(', ')}`) | ||
); | ||
|
||
return pipeParseResult(context.namespace, | ||
R.unless(isString, value => createWarning(context.namespace, `'${name}' 'type' is not a string`, value)), | ||
const parseStringType = pipeParseResult(namespace, | ||
ensureValidType, | ||
type => new context.namespace.elements.Array([type])); | ||
type => new namespace.elements.Array([type])); | ||
|
||
const ensureValidTypeInArray = R.unless( | ||
element => isValidType(element.toValue()), | ||
createWarning(namespace, `'${name}' 'type' array must only contain values: ${types.join(', ')}`) | ||
); | ||
|
||
const parseArrayTypeItem = pipeParseResult(namespace, | ||
R.unless(isString, createWarning(namespace, `'${name}' 'type' array value is not a string`)), | ||
ensureValidTypeInArray); | ||
|
||
const isEmpty = arrayElement => arrayElement.isEmpty; | ||
|
||
// ArrayElement -> ParseResult<ArrayElement, Annotation> | ||
const ensureTypesAreUnique = (types) => { | ||
Comment on lines
+166
to
+167
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know this function is a bit complicated due to its state, I am pretty sure I can make this function a bit simpler tomorrow with fresh eyes. This is a pattern we've done elsewhere too, but with const ensureTypesAreUnique = (types) => {
const inspectedTypes = [];
const isDuplicate = type => inspectedTypes.includes(type.toValue());
// allow, like R.T but also with side-effects
const allow = (type) => { inspectedTypes.push(type.toValue()); return type };
const validateType = R.ifElse(isDuplicate, createWarning(...), allow);
return new namespace.elements.ParseResult(R.map(validateType, types));
/* ^ but above isn't good, because:
ParseResult(["string", "number", Annotation()]) instead of:
ParseResult([["string", "number"], Annotation()])
*/
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. const types = ['string', 'boolean', 'boolean', 'annotation'].map(t => ({ toValue: () => t }));
const toValue = R.invoker(0, 'toValue');
R.pipe(
R.uniqBy(toValue),
R.map(toValue)
)(types) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Obviously the above doesn't help collecting the Annotations separately. Perhaps you could get that single list along with annotations and then filter them out in the last step: const validated = R.map(validateType, types);
const annotation = R.find(is(Annotation), validated);
const validatedTypes = R.filter(complement(equals(annotation)), validated);
return new namespace.elements.ParseResult(validatedTypes, annotation); |
||
const inspectedTypes = []; | ||
const warnings = []; | ||
const permittedTypes = R.filter((type) => { | ||
if (inspectedTypes.includes(type.toValue())) { | ||
warnings.push(createWarning(namespace, `'${name}' 'type' array must contain unique items, ${type.toValue()} is already present`, type)); | ||
return false; | ||
} | ||
|
||
inspectedTypes.push(type.toValue()); | ||
return true; | ||
}, types); | ||
|
||
const parseResult = new namespace.elements.ParseResult(); | ||
parseResult.push(permittedTypes.elements); | ||
|
||
if (warnings.length > 0) { | ||
parseResult.push(...warnings); | ||
} | ||
return parseResult; | ||
}; | ||
Comment on lines
+168
to
+187
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After the attempts I suggested above I think this way the implementation shows what you meant by the comment clearly so that I'd leave as is... |
||
|
||
const parseArrayType = pipeParseResult(namespace, | ||
R.when(isEmpty, createWarning(namespace, `'${name}' 'type' array must contain at least one type`)), | ||
parseArray(context, `${name}' 'type`, parseArrayTypeItem), | ||
ensureTypesAreUnique, | ||
|
||
// FIXME support >1 type | ||
R.when(e => e.length > 1, createWarning(namespace, `'${name}' 'type' more than one type is current unsupported`))); | ||
Comment on lines
+194
to
+195
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two lines will be removed to allow multiple types with the type parser in the future. |
||
|
||
return R.cond([ | ||
[isString, parseStringType], | ||
[ | ||
R.both(isArray, isOpenAPI31OrHigher), | ||
parseArrayType, | ||
], | ||
|
||
[ | ||
isOpenAPI31OrHigher, | ||
value => createWarning(namespace, `'${name}' 'type' is not a string or an array`, value), | ||
], | ||
[ | ||
R.T, | ||
value => createWarning(namespace, `'${name}' 'type' is not a string`, value), | ||
], | ||
]); | ||
} | ||
|
||
// Returns whether the given element value matches the provided schema type | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a suggestion for another PR
A
context.isOpenAPI31OrHigher
could be added as that is something we are using theisOpenAPIVersionMoreThanOrEqual
almost always for IIRC. Then it could be destructured nicely in