Skip to content

Commit

Permalink
refactor(core): moves methods and add class comments
Browse files Browse the repository at this point in the history
  • Loading branch information
timonback committed Feb 20, 2024
1 parent 9fe3eca commit 8ffbb0b
Show file tree
Hide file tree
Showing 13 changed files with 124 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,5 @@ public class SpringwolfConfigConstants {
public static final String SPRINGWOLF_SCANNER_PRODUCER_DATA_ENABLED =
SPRINGWOLF_SCANNER_PREFIX + ".producer-data" + ENABLED;

public static final String SPRINGWOLF_SCHEMA_EXAMPLE_GENERATOR = SPRINGWOLF_CONFIG_PREFIX + ".example-generator";

private SpringwolfConfigConstants() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ public String registerSchema(Class<?> type, String contentType) {
String schemaName = getSchemaName(type, schemas);

preProcessSchemas(schemas, schemaName, type);
// TODO Fix Me putIfAbsent
this.schemas.putAll(schemas);
schemas.forEach(this.schemas::putIfAbsent);
schemas.values().forEach(schema -> postProcessSchema(schema, actualContentType));

return schemaName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.schemas.example;

import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import java.util.HashSet;
import java.util.List;
Expand All @@ -15,11 +14,8 @@
import java.util.Optional;
import java.util.Set;

import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCHEMA_EXAMPLE_GENERATOR;

// TODO: Einfach Yaml generieren?
@Slf4j
@ConditionalOnProperty(name = SPRINGWOLF_SCHEMA_EXAMPLE_GENERATOR, havingValue = "buildin-json", matchIfMissing = true)
@RequiredArgsConstructor
public class DefaultSchemaWalker<T, R> implements SchemaWalker<R> {

Expand All @@ -37,7 +33,7 @@ public R fromSchema(Schema schema, Map<String, Schema> definitions) {

T generatedExample = buildSchemaInternal(schema.getName(), schema, definitions, new HashSet<>());

return exampleValueGenerator.serializeIfNeeded(schema.getName(), generatedExample);
return exampleValueGenerator.prepareForSerialization(schema.getName(), generatedExample);
} catch (ExampleGeneratingException ex) {
log.info("Failed to build example for schema {}", schema.getName(), ex);
}
Expand All @@ -57,12 +53,12 @@ private T buildSchemaInternal(String name, Schema schema, Map<String, Schema> de

String type = schema.getType();
return switch (type) {
case "array" -> handleArraySchema(schema, definitions, visited); // Handle array schema
case "array" -> handleArraySchema(schema, definitions, visited);
case "boolean" -> exampleValueGenerator.createBooleanExample();
case "integer" -> exampleValueGenerator.createIntegerExample();
case "number" -> exampleValueGenerator.createDoubleExample();
case "object" -> handleObject(name, schema, definitions, visited); // Handle object schema
case "string" -> handleStringSchema(schema); // Handle string schema
case "object" -> handleObject(name, schema, definitions, visited);
case "string" -> handleStringSchema(schema);
default -> exampleValueGenerator.generateUnknownSchemaStringTypeExample(type);
};
}
Expand Down Expand Up @@ -120,7 +116,6 @@ private T getExampleValueFromSchemaAnnotation(Schema schema) {
}

private T handleArraySchema(Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {

T arrayItem = buildSchemaInternal(schema.getName(), schema.getItems(), definitions, visited);

return exampleValueGenerator.generateArrayExample(arrayItem);
Expand All @@ -133,7 +128,6 @@ private T handleStringSchema(Schema schema) {
}

String format = schema.getFormat();

if (format == null) {
return exampleValueGenerator.generateStringExample();
}
Expand Down Expand Up @@ -165,10 +159,12 @@ private T handleObject(String name, Schema schema, Map<String, Schema> definitio
// TODO Handle missconfiguration when object schema has no name
Map<String, Schema> properties = schema.getProperties();
if (properties != null) {

if (!visited.contains(schema)) {
visited.add(schema);
T example = handleObjectProperties(name, properties, definitions, visited);

List<PropertyExample<T>> propertyList = buildPropertyListFromSchema(properties, definitions, visited);
T example = exampleValueGenerator.createObjectExample(name, propertyList);

visited.remove(schema);
return example;
}
Expand All @@ -177,14 +173,7 @@ private T handleObject(String name, Schema schema, Map<String, Schema> definitio
if (schema.getAllOf() != null && !schema.getAllOf().isEmpty()) {
List<Schema> schemas = schema.getAllOf();

List<PropertySchema> mergedPropertiesOfSchemas = mergePropertiesOfSchemas(schemas, definitions);

List<PropertyExample<T>> mergedProperties = mergedPropertiesOfSchemas.stream()
.map((mergedProperty) -> new PropertyExample<>(
mergedProperty.name(),
buildSchemaInternal(mergedProperty.name(), mergedProperty.schema(), definitions, visited)))
.toList();

List<PropertyExample<T>> mergedProperties = buildPropertyListFromSchemas(schemas, definitions, visited);
return exampleValueGenerator.createObjectExample(name, mergedProperties);
}
if (schema.getAnyOf() != null && !schema.getAnyOf().isEmpty()) {
Expand All @@ -202,25 +191,34 @@ private T handleObject(String name, Schema schema, Map<String, Schema> definitio
return exampleValueGenerator.createEmptyObjectExample();
}

private T handleObjectProperties(
String name, Map<String, Schema> properties, Map<String, Schema> definitions, Set<Schema> visited) {

List<PropertyExample<T>> propertyList = properties.entrySet().stream()
private List<PropertyExample<T>> buildPropertyListFromSchema(
Map<String, Schema> properties, Map<String, Schema> definitions, Set<Schema> visited) {
return properties.entrySet().stream()
// TODO: comparing to sort? Remove or add to buildPropertyListFromSchemas
.sorted(Map.Entry.comparingByKey())
.map(entry -> {
String propertyKey = entry.getKey();
T propertyValue = buildSchemaInternal(propertyKey, entry.getValue(), definitions, visited);
return new PropertyExample<>(propertyKey, propertyValue);
})
.toList();
}

return exampleValueGenerator.createObjectExample(name, propertyList);
private List<PropertyExample<T>> buildPropertyListFromSchemas(
List<Schema> schemas, Map<String, Schema> definitions, Set<Schema> visited) {
return schemas.stream()
.map(schema -> resolveSchemaFromRefIfAny(schema, definitions).orElse(schema))
.map(Schema::getProperties)
.filter(Objects::nonNull)
.flatMap(propertiesFromSchema ->
buildPropertyListFromSchema(propertiesFromSchema, definitions, visited).stream())
.toList();
}

private Optional<Schema<?>> resolveSchemaFromRefIfAny(Schema schema, Map<String, Schema> definitions) {
String ref = schema.get$ref();
if (ref != null) {
String schemaName = StringUtils.substringAfterLast(ref, "/");
String schemaName = MessageReference.extractRefName(ref);
Schema<?> resolvedSchema = definitions.get(schemaName);
if (resolvedSchema == null) {
throw new ExampleGeneratingException("Missing schema during example json generation: " + schemaName);
Expand All @@ -229,14 +227,4 @@ private Optional<Schema<?>> resolveSchemaFromRefIfAny(Schema schema, Map<String,
}
return Optional.empty();
}

private List<PropertySchema> mergePropertiesOfSchemas(List<Schema> schemas, Map<String, Schema> definitions) {
return schemas.stream()
.map(schema -> resolveSchemaFromRefIfAny(schema, definitions).orElse(schema))
.map(Schema::getProperties)
.filter(Objects::nonNull)
.flatMap(propertiesFromSchema -> propertiesFromSchema.entrySet().stream())
.map(entry -> new PropertySchema(entry.getKey(), entry.getValue()))
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,21 @@
import io.swagger.v3.core.util.Json;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Set;

@Slf4j
public class ExampleJsonValueGenerator implements ExampleValueGenerator<JsonNode, JsonNode> {

private static final BooleanNode DEFAULT_BOOLEAN_EXAMPLE = BooleanNode.TRUE;

private static final Set<String> SUPPORTED_CONTENT_TYPES = Set.of("application/json");
private static final BooleanNode DEFAULT_BOOLEAN_EXAMPLE =
BooleanNode.valueOf(ExampleValueGenerator.DEFAULT_BOOLEAN_EXAMPLE);
private static final ObjectMapper objectMapper = Json.mapper();
private static final Double DEFAULT_NUMBER_EXAMPLE = 1.1;
private static final Integer DEFAULT_INTEGER_EXAMPLE = 0;

private static final String DEFAULT_DATE_EXAMPLE = "2015-07-20";
private static final String DEFAULT_DATE_TIME_EXAMPLE = "2015-07-20T15:49:04-07:00";
private static final String DEFAULT_PASSWORD_EXAMPLE = "string-password";
private static final String DEFAULT_BYTE_EXAMPLE = "YmFzZTY0LWV4YW1wbGU=";
private static final String DEFAULT_BINARY_EXAMPLE =
"0111010001100101011100110111010000101101011000100110100101101110011000010110010001111001";
private static final String DEFAULT_STRING_EXAMPLE = "string";
private static final String DEFAULT_EMAIL_EXAMPLE = "example@example.com";
private static final String DEFAULT_UUID_EXAMPLE = "3fa85f64-5717-4562-b3fc-2c963f66afa6";

private static String DEFAULT_UNKNOWN_SCHEMA_EXAMPLE(String type) {
return "unknown schema type: " + type;
}

private static String DEFAULT_UNKNOWN_SCHEMA_STRING_EXAMPLE(String format) {
return "unknown string schema format: " + format;
}

@Override
public boolean canHandle(String contentType) {
return (StringUtils.equals(contentType, "application/json"));
return SUPPORTED_CONTENT_TYPES.contains(contentType);
}

@Override
Expand Down Expand Up @@ -138,12 +119,12 @@ public JsonNode generateUuidExample() {

@Override
public JsonNode generateUnknownSchemaStringTypeExample(String type) {
return JsonNodeFactory.instance.textNode(DEFAULT_UNKNOWN_SCHEMA_EXAMPLE(type));
return JsonNodeFactory.instance.textNode("unknown schema type: " + type);
}

@Override
public JsonNode generateUnknownSchemaFormatExample(String schemaFormat) {
return JsonNodeFactory.instance.textNode(DEFAULT_UNKNOWN_SCHEMA_STRING_EXAMPLE(schemaFormat));
return JsonNodeFactory.instance.textNode("unknown string schema format: " + schemaFormat);
}

@Override
Expand All @@ -154,7 +135,7 @@ public JsonNode generateArrayExample(JsonNode arrayItem) {
}

@Override
public JsonNode serializeIfNeeded(String name, JsonNode exampleObject) {
public JsonNode prepareForSerialization(String name, JsonNode exampleObject) {
return exampleObject;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,42 @@

import java.util.List;

/**
* Provides the building blocks to generate an example
*
* @param <T> The internal representation of a node like object to compose the example
* @param <R> The serializable representation of the example
*/
interface ExampleValueGenerator<T, R> {

Boolean DEFAULT_BOOLEAN_EXAMPLE = true;

String DEFAULT_STRING_EXAMPLE = "string";
Integer DEFAULT_INTEGER_EXAMPLE = 0;
Double DEFAULT_NUMBER_EXAMPLE = 1.1;

String DEFAULT_DATE_EXAMPLE = "2015-07-20";
String DEFAULT_DATE_TIME_EXAMPLE = "2015-07-20T15:49:04-07:00";
String DEFAULT_PASSWORD_EXAMPLE = "string-password";
String DEFAULT_BYTE_EXAMPLE = "YmFzZTY0LWV4YW1wbGU=";
String DEFAULT_BINARY_EXAMPLE =
"0111010001100101011100110111010000101101011000100110100101101110011000010110010001111001";

String DEFAULT_EMAIL_EXAMPLE = "example@example.com";
String DEFAULT_UUID_EXAMPLE = "3fa85f64-5717-4562-b3fc-2c963f66afa6";

boolean canHandle(String contentType);

/**
* Some internal representation need to be initialized per Schema
*/
void initialize();

/**
* @return The serializable representation of the example (object for json & yaml, string for others)
*/
R prepareForSerialization(String name, T exampleObject);

T createIntegerExample(Integer value);

T createDoubleExample(Double value);
Expand Down Expand Up @@ -51,8 +81,6 @@ interface ExampleValueGenerator<T, R> {

T generateArrayExample(T arrayItem);

R serializeIfNeeded(String name, T exampleObject);

T createRaw(Object exampleValueString);

T exampleOrNull(String name, Object example);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package io.github.stavshamir.springwolf.schemas.example;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
Expand All @@ -19,51 +18,27 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Slf4j
public class ExampleXmlValueGenerator implements ExampleValueGenerator<Node, String> {

private final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
private final Set<String> SUPPORTED_CONTENT_TYPES = Set.of("text/xml", "application/xml");

private final ExampleXmlValueSerializer exampleXmlValueSerializer;

private Document document;

private final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
private final Map<String, Node> exampleCache = new HashMap<>();

private static final Boolean DEFAULT_BOOLEAN_EXAMPLE = true;

private static final String DEFAULT_STRING_EXAMPLE = "string";

private static final Integer DEFAULT_INTEGER_EXAMPLE = 0;

private static final Double DEFAULT_NUMBER_EXAMPLE = 1.1;

private static final String DEFAULT_DATE_EXAMPLE = "2015-07-20";
private static final String DEFAULT_DATE_TIME_EXAMPLE = "2015-07-20T15:49:04-07:00";
private static final String DEFAULT_PASSWORD_EXAMPLE = "string-password";
private static final String DEFAULT_BYTE_EXAMPLE = "YmFzZTY0LWV4YW1wbGU=";
private static final String DEFAULT_BINARY_EXAMPLE =
"0111010001100101011100110111010000101101011000100110100101101110011000010110010001111001";

private static final String DEFAULT_EMAIL_EXAMPLE = "example@example.com";
private static final String DEFAULT_UUID_EXAMPLE = "3fa85f64-5717-4562-b3fc-2c963f66afa6";

public ExampleXmlValueGenerator(ExampleXmlValueSerializer exampleXmlValueSerializer) {
this.exampleXmlValueSerializer = exampleXmlValueSerializer;
}

private static String DEFAULT_UNKNOWN_SCHEMA_EXAMPLE(String type) {
return "unknown schema type: " + type;
}

private static String DEFAULT_UNKNOWN_SCHEMA_STRING_EXAMPLE(String format) {
return "unknown string schema format: " + format;
}

@Override
public boolean canHandle(String contentType) {
return (StringUtils.equals(contentType, "text/xml") || StringUtils.equals(contentType, "application/xml"));
return SUPPORTED_CONTENT_TYPES.contains(contentType);
}

@Override
Expand Down Expand Up @@ -103,7 +78,7 @@ public Node createIntegerExample() {
@Override
public Node createObjectExample(String name, List<PropertyExample<Node>> properties) {
if (name == null) {
throw new IllegalArgumentException("Object Name must not be empty");
throw new IllegalArgumentException("Object name must not be empty");
}
try {
Element rootElement = document.createElement(name);
Expand Down Expand Up @@ -193,12 +168,12 @@ public Node generateEnumExample(String anEnumValue) {

@Override
public Node generateUnknownSchemaStringTypeExample(String schemaType) {
return document.createTextNode(DEFAULT_UNKNOWN_SCHEMA_EXAMPLE(schemaType));
return document.createTextNode("unknown schema type: " + schemaType);
}

@Override
public Node generateUnknownSchemaFormatExample(String schemaFormat) {
return document.createTextNode(DEFAULT_UNKNOWN_SCHEMA_STRING_EXAMPLE(schemaFormat));
return document.createTextNode("unknown string schema format: " + schemaFormat);
}

@Override
Expand All @@ -207,7 +182,7 @@ public Node generateArrayExample(Node arrayItem) {
}

@Override
public String serializeIfNeeded(String name, Node exampleObject) {
public String prepareForSerialization(String name, Node exampleObject) {
final Node objectToWrite;
if (exampleObject instanceof Element) {
objectToWrite = exampleObject;
Expand All @@ -217,7 +192,7 @@ public String serializeIfNeeded(String name, Node exampleObject) {
try {
document.appendChild(objectToWrite);
String xml = exampleXmlValueSerializer.writeDocumentAsXmlString(document);
log.info("name {} -> xml: {}", name, xml);
log.debug("name {} -> xml: {}", name, xml);

exampleCache.putIfAbsent(name, exampleObject);
return xml;
Expand Down
Loading

0 comments on commit 8ffbb0b

Please sign in to comment.