Skip to content

Commit

Permalink
fix: subtype resolution with single result (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
CarstenWickner committed Jun 24, 2022
1 parent b5ab0e9 commit 7db0d1a
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- new `Option.FLATTENED_SUPPLIERS` to unwrap the supplied type; `Supplier<T>` would thus be a type `T`

#### Fixed
- when resolving subtypes with a single other type, under some circumstances the type definition gets lost
- set default `ObjectMapper` node factory to `JsonNodeFactory.withExactBigDecimals(true)` to avoid scientific notation for numbers

#### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,10 @@ private boolean addSubtypeReferencesInDefinition(ResolvedType targetType, Object
this.generateObjectDefinition(targetType, definition);
return false;
}
if (subtypes.size() == 1) {
// avoid unnecessary "anyOf" by making the definition a direct reference to the subtype's definition
this.traverseGenericType(subtypes.get(0), definition, false);
} else {
ArrayNode anyOfArrayNode = this.generatorConfig.createArrayNode();
subtypes.forEach(subtype -> this.traverseGenericType(subtype, anyOfArrayNode.addObject(), false));
definition.set(this.getKeyword(SchemaKeyword.TAG_ANYOF), anyOfArrayNode);
}
// always wrap subtype definitions, in order to avoid pointing at the same definition node as the super type
SchemaKeyword arrayNodeName = subtypes.size() == 1 ? SchemaKeyword.TAG_ALLOF : SchemaKeyword.TAG_ANYOF;
ArrayNode subtypeDefinitionArrayNode = definition.withArray(this.getKeyword(arrayNodeName));
subtypes.forEach(subtype -> this.traverseGenericType(subtype, subtypeDefinitionArrayNode.addObject(), false));
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,27 @@

package com.github.victools.jsonschema.generator.impl.module;

import com.fasterxml.jackson.databind.JsonNode;
import com.github.victools.jsonschema.generator.AbstractTypeAwareTest;
import com.github.victools.jsonschema.generator.ConfigFunction;
import com.github.victools.jsonschema.generator.Module;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.OptionPreset;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import com.github.victools.jsonschema.generator.SchemaGeneratorGeneralConfigPart;
import com.github.victools.jsonschema.generator.SchemaVersion;
import com.github.victools.jsonschema.generator.TestUtils;
import com.github.victools.jsonschema.generator.TypeContext;
import com.github.victools.jsonschema.generator.TypeScope;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.json.JSONException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -37,6 +45,8 @@
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;

/**
* Test for the {@link AdditionalPropertiesModule} class.
Expand Down Expand Up @@ -115,7 +125,46 @@ public void testResolveAdditionalProperties(Module moduleInstance, Type expected
}
}

@Test
public void testAdditionalPropertyWithSubtype() throws JSONException, IOException {
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
configBuilder.with(Option.MAP_VALUES_AS_ADDITIONAL_PROPERTIES)
.without(Option.SCHEMA_VERSION_INDICATOR)
.forTypesInGeneral()
.withSubtypeResolver((declaredType, context) -> declaredType.getErasedType() == B.class
? Collections.singletonList(context.getTypeContext().resolve(BImpl.class))
: null);
SchemaGenerator generator = new SchemaGenerator(configBuilder.build());
JsonNode result = generator.generateSchema(A.class);
JSONAssert.assertEquals(TestUtils.loadResource(AdditionalPropertiesModuleTest.class, "additional-property-with-subtype.json"),
result.toString(), JSONCompareMode.STRICT);
}

private static class TestMapSubType extends HashMap<Object, String> {
// no further fields
}

private static class A {

private Map<String, B> map;

Map<String, B> getMap() {
return this.map;
}
}

private interface B {

String getName();
}

private static class BImpl implements B {

private String name;

@Override
public String getName() {
return this.name;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"type" : "object",
"properties" : {
"map" : {
"type" : "object",
"additionalProperties" : {
"type" : "object",
"properties" : {
"name" : {
"type" : "string"
}
}
}
}
}
}

0 comments on commit 7db0d1a

Please sign in to comment.