use of com.github.victools.jsonschema.generator.SchemaKeyword in project jsonschema-generator by victools.
the class SchemaCleanUpUtils method mergeAllOfPartsIfPossible.
/**
* Check whether the given schema node and its {@link SchemaKeyword#TAG_ALLOF} elements (if there are any) are distinct. If yes, remove the
* {@link SchemaKeyword#TAG_ALLOF} node and merge all its elements with the given schema node instead.
* <br>
* This makes for more readable schemas being generated but has the side-effect that manually added {@link SchemaKeyword#TAG_ALLOF} (e.g. from a
* custom definition or attribute overrides) may be removed as well if it isn't strictly speaking necessary.
*
* @param schemaNode single node representing a sub-schema to consolidate contained {@link SchemaKeyword#TAG_ALLOF} for (if present)
* @param allOfTagName name of the {@link SchemaKeyword#TAG_ALLOF} in the designated JSON Schema version
*/
private void mergeAllOfPartsIfPossible(JsonNode schemaNode, String allOfTagName) {
if (!(schemaNode instanceof ObjectNode)) {
return;
}
JsonNode allOfTag = schemaNode.get(allOfTagName);
if (!(allOfTag instanceof ArrayNode)) {
return;
}
allOfTag.forEach(part -> this.mergeAllOfPartsIfPossible(part, allOfTagName));
List<JsonNode> allOfElements = new ArrayList<>();
allOfTag.forEach(allOfElements::add);
if (allOfElements.stream().anyMatch(part -> !(part instanceof ObjectNode) && !part.asBoolean())) {
return;
}
List<ObjectNode> parts = allOfElements.stream().filter(part -> part instanceof ObjectNode).map(part -> (ObjectNode) part).collect(Collectors.toList());
// collect all defined attributes from the separate parts and check whether there are incompatible differences
Map<String, List<JsonNode>> fieldsFromAllParts = Stream.concat(Stream.of(schemaNode), parts.stream()).flatMap(part -> StreamSupport.stream(((Iterable<Map.Entry<String, JsonNode>>) () -> part.fields()).spliterator(), false)).collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
if ((this.config.getSchemaVersion() == SchemaVersion.DRAFT_6 || this.config.getSchemaVersion() == SchemaVersion.DRAFT_7) && fieldsFromAllParts.containsKey(this.config.getKeyword(SchemaKeyword.TAG_REF))) {
// in Draft 7, any other attributes besides the $ref keyword were ignored
return;
}
String ifTagName = this.config.getKeyword(SchemaKeyword.TAG_IF);
for (Map.Entry<String, List<JsonNode>> fieldEntries : fieldsFromAllParts.entrySet()) {
if (fieldEntries.getValue().size() == 1) {
// no conflicts, no further checks
continue;
}
if (ifTagName.equals(fieldEntries.getKey())) {
// "if"/"then"/"else" tags should remain isolated in their sub-schemas
return;
}
int offset;
if (!allOfTagName.equals(fieldEntries.getKey())) {
offset = 0;
} else if (fieldEntries.getValue().size() == 2) {
// we can ignore the "allOf" tag in the target node (the one we are trying to remove here)
continue;
} else {
offset = 1;
}
if (!fieldEntries.getValue().stream().skip(offset + 1).allMatch(fieldEntries.getValue().get(offset)::equals)) {
// later, we may want to decide based on the tag how to merge them (e.g. take the highest "minLength" and the lowest "maximum")
return;
}
}
// all attributes are either distinct or have equal values in all occurrences
ObjectNode schemaObjectNode = (ObjectNode) schemaNode;
schemaObjectNode.remove(allOfTagName);
parts.forEach(schemaObjectNode::setAll);
}
use of com.github.victools.jsonschema.generator.SchemaKeyword in project jsonschema-generator by victools.
the class AbstractTypeAwareTest method prepareContextForVersion.
/**
* Override generation context mock methods that are version dependent.
*
* @param schemaVersion designated JSON Schema version
*/
protected void prepareContextForVersion(SchemaVersion schemaVersion) {
TypeContext typeContext = TypeContextFactory.createDefaultTypeContext();
ResolvedType resolvedTestClass = typeContext.resolve(this.testClass);
this.testClassMembers = typeContext.resolveWithMembers(resolvedTestClass);
this.context = Mockito.mock(SchemaGenerationContext.class, Mockito.RETURNS_DEEP_STUBS);
Mockito.when(this.context.getTypeContext()).thenReturn(typeContext);
SchemaGeneratorConfig config = Mockito.mock(SchemaGeneratorConfig.class);
Mockito.when(this.context.getGeneratorConfig()).thenReturn(config);
Mockito.when(config.resolveArrayMaxItems(Mockito.any(FieldScope.class))).thenReturn(null);
Mockito.when(config.resolveArrayMaxItems(Mockito.any(MethodScope.class))).thenReturn(null);
Mockito.when(config.resolveArrayMaxItemsForType(Mockito.any())).thenReturn(null);
Mockito.when(config.resolveArrayMinItems(Mockito.any(FieldScope.class))).thenReturn(null);
Mockito.when(config.resolveArrayMinItems(Mockito.any(MethodScope.class))).thenReturn(null);
Mockito.when(config.resolveArrayMinItemsForType(Mockito.any())).thenReturn(null);
Mockito.when(config.resolveArrayUniqueItems(Mockito.any(FieldScope.class))).thenReturn(null);
Mockito.when(config.resolveArrayUniqueItems(Mockito.any(MethodScope.class))).thenReturn(null);
Mockito.when(config.resolveArrayUniqueItemsForType(Mockito.any())).thenReturn(null);
Mockito.when(config.resolveStringMaxLength(Mockito.any(FieldScope.class))).thenReturn(null);
Mockito.when(config.resolveStringMaxLength(Mockito.any(MethodScope.class))).thenReturn(null);
Mockito.when(config.resolveStringMaxLengthForType(Mockito.any())).thenReturn(null);
Mockito.when(config.resolveStringMinLength(Mockito.any(FieldScope.class))).thenReturn(null);
Mockito.when(config.resolveStringMinLength(Mockito.any(MethodScope.class))).thenReturn(null);
Mockito.when(config.resolveStringMinLengthForType(Mockito.any())).thenReturn(null);
Mockito.when(config.getSchemaVersion()).thenReturn(schemaVersion);
Answer<String> keywordLookup = invocation -> ((SchemaKeyword) invocation.getArgument(0)).forVersion(schemaVersion);
Mockito.when(config.getKeyword(Mockito.any())).thenAnswer(keywordLookup);
Mockito.when(this.context.getKeyword(Mockito.any())).thenAnswer(keywordLookup);
ObjectMapper objectMapper = new ObjectMapper();
Mockito.when(config.getObjectMapper()).thenReturn(objectMapper);
Mockito.when(config.createArrayNode()).thenAnswer((_invocation) -> objectMapper.createArrayNode());
Mockito.when(config.createObjectNode()).thenAnswer((_invocation) -> objectMapper.createObjectNode());
Mockito.when(this.context.createStandardDefinitionReference(Mockito.any(ResolvedType.class), Mockito.any())).thenAnswer((_invocation) -> objectMapper.createObjectNode());
}
use of com.github.victools.jsonschema.generator.SchemaKeyword in project jsonschema-generator by victools.
the class SchemaCleanUpUtils method finaliseSchemaParts.
/**
* Iterate through a generated and fully populated schema and remove extraneous {@link SchemaKeyword#TAG_ANYOF} nodes, where one entry of the
* array is again a {@link SchemaKeyword#TAG_ANYOF} wrapper and nothing else. This makes for more readable schemas being generated but has the
* side-effect that any manually added {@link SchemaKeyword#TAG_ANYOF} (e.g. through a custom definition of attribute overrides) may be removed as
* well if it isn't strictly speaking necessary.
*
* @param schemaNodes generated schemas to clean-up
* @param performCleanUpOnSingleSchemaNode clean up task to execute before looking for deeper nested sub-schemas for which to apply the same
*/
private void finaliseSchemaParts(List<ObjectNode> schemaNodes, Consumer<ObjectNode> performCleanUpOnSingleSchemaNode) {
List<ObjectNode> nextNodesToCheck = new ArrayList<>(schemaNodes);
Consumer<JsonNode> addNodeToCheck = node -> {
if (node instanceof ObjectNode) {
nextNodesToCheck.add((ObjectNode) node);
}
};
Set<String> tagsWithSchemas = this.getTagNamesContainingSchema();
Set<String> tagsWithSchemaArrays = this.getTagNamesContainingSchemaArray();
Set<String> tagsWithSchemaObjects = this.getTagNamesContainingSchemaObject();
do {
List<ObjectNode> currentNodesToCheck = new ArrayList<>(nextNodesToCheck);
nextNodesToCheck.clear();
for (ObjectNode nodeToCheck : currentNodesToCheck) {
performCleanUpOnSingleSchemaNode.accept(nodeToCheck);
tagsWithSchemas.stream().map(nodeToCheck::get).forEach(addNodeToCheck);
tagsWithSchemaArrays.stream().map(nodeToCheck::get).filter(possibleArrayNode -> possibleArrayNode instanceof ArrayNode).forEach(arrayNode -> arrayNode.forEach(addNodeToCheck));
tagsWithSchemaObjects.stream().map(nodeToCheck::get).filter(possibleObjectNode -> possibleObjectNode instanceof ObjectNode).forEach(objectNode -> objectNode.forEach(addNodeToCheck));
}
} while (!nextNodesToCheck.isEmpty());
}
Aggregations