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

Support array of type in Schema Object #600

Merged
merged 1 commit into from
Feb 23, 2021
Merged

Support array of type in Schema Object #600

merged 1 commit into from
Feb 23, 2021

Conversation

kylef
Copy link
Member

@kylef kylef commented Feb 22, 2021

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.

@kylef kylef added the openapi3 label Feb 22, 2021
@kylef kylef requested a review from opichals February 22, 2021 20:49
Comment on lines +146 to +167
// ArrayElement -> ParseResult<ArrayElement, Annotation>
const ensureTypesAreUnique = (types) => {
Copy link
Member Author

@kylef kylef Feb 22, 2021

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()])
  */
}

Copy link
Contributor

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?

Copy link
Contributor

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)

Copy link
Contributor

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);

Comment on lines +174 to +195
// FIXME support >1 type
R.when(e => e.length > 1, createWarning(namespace, `'${name}' 'type' more than one type is current unsupported`)));
Copy link
Member Author

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.

Comment on lines +248 to +271
it('warns when array items contain more than 1 entry', () => {
// FIXME support more than one entry :)
Copy link
Member Author

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));
Copy link
Contributor

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;

Comment on lines +148 to +187
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;
};
Copy link
Contributor

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.
@kylef kylef merged commit 7f8206d into master Feb 23, 2021
@kylef kylef deleted the kylef/type-arr branch February 23, 2021 10:52
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants