Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): handle example root schema lazily #968

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,7 @@ public R fromSchema(Schema schema, Map<String, Schema> definitions) {
exampleValueGenerator.initialize();

try {
String schemaName = exampleValueGenerator
.lookupSchemaName(schema)
.orElseThrow(() ->
new ExampleGeneratingException("There is no name set for Schema: " + schema.toString()));
Optional<String> schemaName = exampleValueGenerator.lookupSchemaName(schema);

T generatedExample = buildExample(schemaName, schema, definitions, new HashSet<>())
.orElseThrow(() -> new ExampleGeneratingException("Something went wrong"));
Expand All @@ -71,7 +68,8 @@ public R fromSchema(Schema schema, Map<String, Schema> definitions) {
return null;
}

private Optional<T> buildExample(String name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
private Optional<T> buildExample(
Optional<String> name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
log.debug("Building example for schema {}", schema);

Optional<T> exampleValue = getExampleFromSchemaAnnotation(name, schema);
Expand All @@ -88,12 +86,13 @@ private Optional<T> buildExample(String name, Schema schema, Map<String, Schema>
return example;
}

private Optional<T> getExampleFromSchemaAnnotation(String fieldName, Schema schema) {
private Optional<T> getExampleFromSchemaAnnotation(Optional<String> fieldName, Schema schema) {
return getExampleValueFromSchemaAnnotation(fieldName, schema, schema.getExample())
.or(() -> getExampleValueFromSchemaAnnotation(fieldName, schema, schema.getDefault()));
}

private Optional<T> getExampleValueFromSchemaAnnotation(String fieldName, Schema schema, Object exampleValue) {
private Optional<T> getExampleValueFromSchemaAnnotation(
Optional<String> fieldName, Schema schema, Object exampleValue) {
// schema is a map of properties from a nested object, whose example cannot be inferred
if (exampleValue == null) {
return Optional.empty();
Expand Down Expand Up @@ -159,7 +158,7 @@ private Optional<T> getExampleValueFromSchemaAnnotation(String fieldName, Schema
* The caller must ensure that the schema has not been visited before to avoid infinite recursion
*/
private Optional<T> buildExampleFromUnvisitedSchema(
String name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
Optional<String> name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
Optional<Schema<?>> resolvedSchema = resolveSchemaFromRef(schema, definitions);
if (resolvedSchema.isPresent()) {
return buildExample(name, resolvedSchema.get(), definitions, visited);
Expand Down Expand Up @@ -192,7 +191,8 @@ private Optional<T> buildArrayExample(Schema schema, Map<String, Schema> definit
return exampleValueGenerator
.lookupSchemaName(arrayItemSchema)
.or(() -> arrayName)
.flatMap(arrayItemName -> buildExample(arrayItemName, arrayItemSchema, definitions, visited))
.flatMap(arrayItemName ->
buildExample(Optional.of(arrayItemName), arrayItemSchema, definitions, visited))
.map(arrayItem -> exampleValueGenerator.createArrayExample(arrayName, arrayItem));
}

Expand Down Expand Up @@ -231,7 +231,7 @@ private String getFirstEnumValue(Schema schema) {
}

private Optional<T> buildFromComposedSchema(
String name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
Optional<String> name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
final List<Schema> schemasAllOf = schema.getAllOf();
final List<Schema> schemasAnyOf = schema.getAnyOf();
final List<Schema> schemasOneOf = schema.getOneOf();
Expand All @@ -247,7 +247,7 @@ private Optional<T> buildFromComposedSchema(
}

private Optional<T> buildFromObjectSchema(
String name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
Optional<String> name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
final Optional<T> exampleValue;

final Map<String, Schema> properties = schema.getProperties();
Expand All @@ -264,7 +264,7 @@ private Optional<T> buildFromObjectSchema(
}

private Optional<T> buildMapExample(
String name, Schema additionalProperties, Map<String, Schema> definitions, Set<Schema> visited) {
Optional<String> name, Schema additionalProperties, Map<String, Schema> definitions, Set<Schema> visited) {
T object = exampleValueGenerator.startObject(name);
Map<String, Schema> mapProperties = Map.of(DEFAULT_MAP_KEY_EXAMPLE, additionalProperties);
exampleValueGenerator.addPropertyExamples(
Expand All @@ -275,7 +275,10 @@ private Optional<T> buildMapExample(
}

private Optional<T> buildFromObjectSchemaWithProperties(
String name, Map<String, Schema> properties, Map<String, Schema> definitions, Set<Schema> visited) {
Optional<String> name,
Map<String, Schema> properties,
Map<String, Schema> definitions,
Set<Schema> visited) {
T object = exampleValueGenerator.startObject(name);
exampleValueGenerator.addPropertyExamples(
object, buildPropertyExampleListFromSchema(properties, definitions, visited));
Expand All @@ -285,7 +288,7 @@ private Optional<T> buildFromObjectSchemaWithProperties(
}

private Optional<T> buildFromObjectSchemaWithAllOf(
String name, List<Schema> schemasAllOf, Map<String, Schema> definitions, Set<Schema> visited) {
Optional<String> name, List<Schema> schemasAllOf, Map<String, Schema> definitions, Set<Schema> visited) {
T object = exampleValueGenerator.startObject(name);
exampleValueGenerator.addPropertyExamples(
object, buildPropertyExampleListFromSchemas(schemasAllOf, definitions, visited));
Expand All @@ -305,7 +308,7 @@ private List<PropertyExample<T>> buildPropertyExampleListFromSchema(
.orElse(propertySchema.getKey());

Optional<T> propertyValue =
buildExample(propertyKey, propertySchema.getValue(), definitions, visited);
buildExample(Optional.of(propertyKey), propertySchema.getValue(), definitions, visited);

return propertyValue
.map(optionalElem -> new PropertyExample<>(propertyKey, optionalElem))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ default void initialize() {}

Optional<T> createUnknownSchemaStringFormatExample(String schemaFormat);

T startObject(String name);
T startObject(Optional<String> name);

default void endObject() {}

Expand All @@ -54,5 +54,5 @@ default void endObject() {}

T createRaw(Object exampleValueString);

T getExampleOrNull(String fieldName, Schema schema, Object example);
T getExampleOrNull(Optional<String> fieldName, Schema schema, Object example);
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public JsonNode createRaw(Object exampleValue) {
}

@Override
public JsonNode getExampleOrNull(String fieldName, Schema schema, Object example) {
public JsonNode getExampleOrNull(Optional<String> fieldName, Schema schema, Object example) {
if (example instanceof JsonNode) {
return (JsonNode) example;
}
Expand All @@ -106,7 +106,7 @@ public JsonNode getExampleOrNull(String fieldName, Schema schema, Object example
}

@Override
public JsonNode startObject(String name) {
public JsonNode startObject(Optional<String> name) {
return objectMapper.createObjectNode();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,9 @@ public Optional<Node> createBooleanExample(Boolean value, Schema schema) {
}

@Override
public Element startObject(String name) {
if (name == null) {
throw new IllegalArgumentException("Object name must not be empty");
}

return nodeStack.push(document.createElement(name));
public Element startObject(Optional<String> name) {
return nodeStack.push(document.createElement(name.orElseThrow(
() -> new SchemaWalker.ExampleGeneratingException("There is no name set for Schema"))));
}

@Override
Expand Down Expand Up @@ -198,7 +195,7 @@ public Node createRaw(Object exampleValue) {
}

@Override
public Node getExampleOrNull(String fieldName, Schema schema, Object example) {
public Node getExampleOrNull(Optional<String> fieldName, Schema schema, Object example) {
String name = getCacheKey(schema);

if (example instanceof Node) {
Expand All @@ -207,7 +204,10 @@ public Node getExampleOrNull(String fieldName, Schema schema, Object example) {

if (exampleCache.containsKey(name)) {
Node oldElement = exampleCache.get(name);
Node newElement = modifyElementFromCacheIfNeeded(oldElement, fieldName);
Node newElement = modifyElementFromCacheIfNeeded(
oldElement,
fieldName.orElseThrow(
() -> new SchemaWalker.ExampleGeneratingException("There is no name set for Schema")));
return this.document.importNode(newElement, true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public Optional<JsonNode> createBooleanExample(Boolean value, Schema schema) {
}

@Override
public JsonNode startObject(String name) {
public JsonNode startObject(Optional<String> name) {
return this.exampleJsonValueGenerator.startObject(name);
}

Expand Down Expand Up @@ -116,7 +116,7 @@ public JsonNode createRaw(Object exampleValueString) {
}

@Override
public JsonNode getExampleOrNull(String fieldName, Schema schema, Object example) {
public JsonNode getExampleOrNull(Optional<String> fieldName, Schema schema, Object example) {
return this.exampleJsonValueGenerator.getExampleOrNull(fieldName, schema, example);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;

Expand All @@ -33,11 +35,13 @@ void cacheShouldResolveBySchemaName() {
.get();
generator.prepareForSerialization(schema1, example1);

Node cachedExample1 = generator.getExampleOrNull("fieldName1", schema1, "does-not-matter-for-test-1");
Node cachedExample1 =
generator.getExampleOrNull(Optional.of("fieldName1"), schema1, "does-not-matter-for-test-1");

// when
generator.initialize();
Node exampleFromCache = generator.getExampleOrNull("fieldName2", schema2, "does-not-matter-for-test-2");
Node exampleFromCache =
generator.getExampleOrNull(Optional.of("fieldName2"), schema2, "does-not-matter-for-test-2");

// then
assertThat(exampleFromCache).isNotEqualTo(cachedExample1);
Expand All @@ -62,11 +66,13 @@ void cacheShouldResolveBySchemaNameAndRenameToWrappingField() {
Node example1 = generator.createRaw("<xml><value>aValue</value></xml>");
generator.prepareForSerialization(schema1, example1);

Node cachedExample1 = generator.getExampleOrNull("fieldName1", schema1, "does-not-matter-for-test-1");
Node cachedExample1 =
generator.getExampleOrNull(Optional.of("fieldName1"), schema1, "does-not-matter-for-test-1");

// when
generator.initialize();
Node exampleFromCache = generator.getExampleOrNull("fieldName2", schema2, "does-not-matter-for-test-2");
Node exampleFromCache =
generator.getExampleOrNull(Optional.of("fieldName2"), schema2, "does-not-matter-for-test-2");

// then
assertThat(((Element) cachedExample1).getTagName()).isEqualTo("fieldName1");
Expand All @@ -90,7 +96,7 @@ void cacheShouldStoreExampleBySchemaName() {
generator.prepareForSerialization(schema1, example1);

generator.initialize();
Node exampleFromCache = generator.getExampleOrNull("fieldName", schema2, "example-string");
Node exampleFromCache = generator.getExampleOrNull(Optional.of("fieldName"), schema2, "example-string");

assertThat(exampleFromCache).isNull();
}
Expand Down
Loading