-
Notifications
You must be signed in to change notification settings - Fork 18
Support array of type in Schema Object #600
Conversation
// ArrayElement -> ParseResult<ArrayElement, Annotation> | ||
const ensureTypesAreUnique = (types) => { |
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.
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 map
(because we can return the value element or an annotation element into a parse result, but that's a bit tricky because we still need the "values" to be wrapped in an array element instead of being "loose" in a parse result.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
What about using R.uniqBy
?
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.
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 comment
The 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);
// FIXME support >1 type | ||
R.when(e => e.length > 1, createWarning(namespace, `'${name}' 'type' more than one type is current unsupported`))); |
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.
These two lines will be removed to allow multiple types with the type parser in the future.
it('warns when array items contain more than 1 entry', () => { | ||
// FIXME support more than one entry :) |
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.
This is the one test that is more temporary as it should go away once we do support multiple types.
let types; | ||
let isValidType; | ||
|
||
if (context.isOpenAPIVersionMoreThanOrEqual(3, 1)) { | ||
const isOpenAPI31OrHigher = R.always(context.isOpenAPIVersionMoreThanOrEqual(3, 1)); |
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 the isOpenAPIVersionMoreThanOrEqual
almost always for IIRC. Then it could be destructured nicely in
const { namespace, isOpenAPI31OrHigher } = context;
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; | ||
}; |
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.
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...
The caveat, is that the array can only be a single value.
54b6f7c
to
cfd36b3
Compare
The caveat is that the array can only be a single value. This brings the logic to parse and validate an array of types, but only allows one at the moment so it does not add additional functionality to the rest of the parser. This allows reviewing the array parsing, and all the validation cases independently of support for multiple types.
I'd perhaps suggest looking at the test cases first to understand all the desired behaviours.