Search in sources :

Example 1 with SchemaKeyword

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);
}
Also used : Set(java.util.Set) Collectors(java.util.stream.Collectors) ObjectNode(com.fasterxml.jackson.databind.node.ObjectNode) SchemaVersion(com.github.victools.jsonschema.generator.SchemaVersion) ArrayList(java.util.ArrayList) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode) Consumer(java.util.function.Consumer) List(java.util.List) Stream(java.util.stream.Stream) SchemaGeneratorConfig(com.github.victools.jsonschema.generator.SchemaGeneratorConfig) Map(java.util.Map) JsonNode(com.fasterxml.jackson.databind.JsonNode) StreamSupport(java.util.stream.StreamSupport) SchemaKeyword(com.github.victools.jsonschema.generator.SchemaKeyword) ObjectNode(com.fasterxml.jackson.databind.node.ObjectNode) ArrayList(java.util.ArrayList) JsonNode(com.fasterxml.jackson.databind.JsonNode) ArrayList(java.util.ArrayList) List(java.util.List) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode) Map(java.util.Map)

Example 2 with SchemaKeyword

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());
}
Also used : TypeContext(com.github.victools.jsonschema.generator.TypeContext) FieldScope(com.github.victools.jsonschema.generator.FieldScope) MethodScope(com.github.victools.jsonschema.generator.MethodScope) TypeContextFactory(com.github.victools.jsonschema.generator.impl.TypeContextFactory) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) SchemaGenerationContext(com.github.victools.jsonschema.generator.SchemaGenerationContext) ResolvedTypeWithMembers(com.fasterxml.classmate.ResolvedTypeWithMembers) ResolvedType(com.fasterxml.classmate.ResolvedType) SchemaVersion(com.github.victools.jsonschema.generator.SchemaVersion) ResolvedMethod(com.fasterxml.classmate.members.ResolvedMethod) Mockito(org.mockito.Mockito) Answer(org.mockito.stubbing.Answer) Stream(java.util.stream.Stream) SchemaGeneratorConfig(com.github.victools.jsonschema.generator.SchemaGeneratorConfig) ResolvedField(com.fasterxml.classmate.members.ResolvedField) SchemaKeyword(com.github.victools.jsonschema.generator.SchemaKeyword) SchemaGeneratorConfig(com.github.victools.jsonschema.generator.SchemaGeneratorConfig) SchemaGenerationContext(com.github.victools.jsonschema.generator.SchemaGenerationContext) FieldScope(com.github.victools.jsonschema.generator.FieldScope) MethodScope(com.github.victools.jsonschema.generator.MethodScope) SchemaKeyword(com.github.victools.jsonschema.generator.SchemaKeyword) TypeContext(com.github.victools.jsonschema.generator.TypeContext) ResolvedType(com.fasterxml.classmate.ResolvedType) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper)

Example 3 with SchemaKeyword

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());
}
Also used : Set(java.util.Set) Collectors(java.util.stream.Collectors) ObjectNode(com.fasterxml.jackson.databind.node.ObjectNode) SchemaVersion(com.github.victools.jsonschema.generator.SchemaVersion) ArrayList(java.util.ArrayList) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode) Consumer(java.util.function.Consumer) List(java.util.List) Stream(java.util.stream.Stream) SchemaGeneratorConfig(com.github.victools.jsonschema.generator.SchemaGeneratorConfig) Map(java.util.Map) JsonNode(com.fasterxml.jackson.databind.JsonNode) StreamSupport(java.util.stream.StreamSupport) SchemaKeyword(com.github.victools.jsonschema.generator.SchemaKeyword) ObjectNode(com.fasterxml.jackson.databind.node.ObjectNode) ArrayList(java.util.ArrayList) JsonNode(com.fasterxml.jackson.databind.JsonNode) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode)

Aggregations

SchemaGeneratorConfig (com.github.victools.jsonschema.generator.SchemaGeneratorConfig)3 SchemaKeyword (com.github.victools.jsonschema.generator.SchemaKeyword)3 SchemaVersion (com.github.victools.jsonschema.generator.SchemaVersion)3 Stream (java.util.stream.Stream)3 JsonNode (com.fasterxml.jackson.databind.JsonNode)2 ArrayNode (com.fasterxml.jackson.databind.node.ArrayNode)2 ObjectNode (com.fasterxml.jackson.databind.node.ObjectNode)2 ArrayList (java.util.ArrayList)2 List (java.util.List)2 Map (java.util.Map)2 Set (java.util.Set)2 Consumer (java.util.function.Consumer)2 Collectors (java.util.stream.Collectors)2 StreamSupport (java.util.stream.StreamSupport)2 ResolvedType (com.fasterxml.classmate.ResolvedType)1 ResolvedTypeWithMembers (com.fasterxml.classmate.ResolvedTypeWithMembers)1 ResolvedField (com.fasterxml.classmate.members.ResolvedField)1 ResolvedMethod (com.fasterxml.classmate.members.ResolvedMethod)1 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)1 FieldScope (com.github.victools.jsonschema.generator.FieldScope)1