-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* GH-134: fix: Handle NPE in ExampleJsonGenerator i.e. A MapSchema is an object, but doesn't have properties Also catch recursions * fix: Handle unknown schema * feat: Provide example value for uuid and email
- Loading branch information
Showing
5 changed files
with
281 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
bin/ | ||
build/ | ||
springwolf-add-ons/build/ | ||
springwolf-core/build/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,8 +10,11 @@ | |
import org.springframework.stereotype.Component; | ||
|
||
import java.math.BigDecimal; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCHEMA_EXAMPLE_GENERATOR; | ||
|
@@ -32,6 +35,16 @@ public class ExampleJsonGenerator implements ExampleGenerator { | |
private static final String DEFAULT_BINARY_EXAMPLE = | ||
"\"0111010001100101011100110111010000101101011000100110100101101110011000010110010001111001\""; | ||
private static final String DEFAULT_STRING_EXAMPLE = "\"string\""; | ||
public static final String DEFAULT_EMAIL_EXAMPLE = "\"[email protected]\""; | ||
public 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 Object fromSchema(Schema schema, Map<String, Schema> definitions) { | ||
|
@@ -45,30 +58,34 @@ public Object fromSchema(Schema schema, Map<String, Schema> definitions) { | |
} | ||
|
||
static String buildSchema(Schema schema, Map<String, Schema> definitions) { | ||
return buildSchemaInternal(schema, definitions, new HashSet<>()); | ||
} | ||
|
||
private static String buildSchemaInternal(Schema schema, Map<String, Schema> definitions, Set<Schema> visited) { | ||
String exampleValue = ExampleJsonGenerator.getExampleValue(schema); | ||
if (exampleValue != null) { | ||
return exampleValue; | ||
} | ||
|
||
String type = schema.getType(); | ||
if (type == null) { | ||
String schemaName = StringUtils.substringAfterLast(schema.get$ref(), "/"); | ||
String ref = schema.get$ref(); | ||
if (ref != null) { | ||
String schemaName = StringUtils.substringAfterLast(ref, "/"); | ||
Schema resolvedSchema = definitions.get(schemaName); | ||
if (resolvedSchema == null) { | ||
throw new ExampleGeneratingException("Missing schema during example json generation: " + schemaName); | ||
} | ||
return buildSchema(resolvedSchema, definitions); | ||
return buildSchemaInternal(resolvedSchema, definitions, visited); | ||
} | ||
|
||
String type = schema.getType(); | ||
return switch (type) { | ||
case "array" -> ExampleJsonGenerator.handleArraySchema(schema, definitions); | ||
case "array" -> ExampleJsonGenerator.handleArraySchema(schema, definitions, visited); | ||
case "boolean" -> DEFAULT_BOOLEAN_EXAMPLE; | ||
case "integer" -> DEFAULT_INTEGER_EXAMPLE; | ||
case "number" -> DEFAULT_NUMBER_EXAMPLE; | ||
case "object" -> ExampleJsonGenerator.handleObject(schema, definitions); | ||
case "object" -> ExampleJsonGenerator.handleObject(schema, definitions, visited); | ||
case "string" -> ExampleJsonGenerator.handleStringSchema(schema); | ||
default -> "unknown schema type: " + type; | ||
default -> DEFAULT_UNKNOWN_SCHEMA_EXAMPLE(type); | ||
}; | ||
} | ||
|
||
|
@@ -86,10 +103,10 @@ private static String getExampleValue(Schema schema) { | |
return null; | ||
} | ||
|
||
private static String handleArraySchema(Schema schema, Map<String, Schema> definitions) { | ||
private static String handleArraySchema(Schema schema, Map<String, Schema> definitions, Set<Schema> visited) { | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append("["); | ||
sb.append(buildSchema(schema.getItems(), definitions)); | ||
sb.append(buildSchemaInternal(schema.getItems(), definitions, visited)); | ||
sb.append("]"); | ||
return sb.toString(); | ||
} | ||
|
@@ -109,35 +126,54 @@ private static String handleStringSchema(Schema schema) { | |
return switch (format) { | ||
case "date" -> DEFUALT_DATE_EXAMPLE; | ||
case "date-time" -> DEFAULT_DATE_TIME_EXAMPLE; | ||
case "email" -> DEFAULT_EMAIL_EXAMPLE; | ||
case "password" -> DEFAULT_PASSWORD_EXAMPLE; | ||
case "byte" -> DEFAULT_BYTE_EXAMPLE; | ||
case "binary" -> DEFAULT_BINARY_EXAMPLE; | ||
default -> "unknown type format: " + format; | ||
case "uuid" -> DEFAULT_UUID_EXAMPLE; | ||
default -> DEFAULT_UNKNOWN_SCHEMA_STRING_EXAMPLE(format); | ||
}; | ||
} | ||
|
||
private static String getFirstEnumValue(Schema schema) { | ||
if (schema.getEnum() != null) { | ||
Optional<String> firstEnumEntry = schema.getEnum().stream().findFirst(); | ||
List<String> enums = schema.getEnum(); | ||
if (enums != null) { | ||
Optional<String> firstEnumEntry = enums.stream().findFirst(); | ||
if (firstEnumEntry.isPresent()) { | ||
return firstEnumEntry.get(); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private static String handleObject(Schema schema, Map<String, Schema> definitions) { | ||
private static String handleObject(Schema schema, Map<String, Schema> definitions, Set<Schema> visited) { | ||
Map<String, Schema> properties = schema.getProperties(); | ||
if (properties != null) { | ||
|
||
if (!visited.contains(schema)) { | ||
visited.add(schema); | ||
String example = handleObjectProperties(properties, definitions, visited); | ||
visited.remove(schema); | ||
|
||
return example; | ||
} | ||
} | ||
// i.e. A MapSchema is type=object, but has properties=null | ||
return "{}"; | ||
} | ||
|
||
private static String handleObjectProperties( | ||
Map<String, Schema> properties, Map<String, Schema> definitions, Set<Schema> visited) { | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append("{"); | ||
|
||
Map<String, Schema> properties = schema.getProperties(); | ||
String data = properties.entrySet().stream() | ||
.map(entry -> { | ||
StringBuilder propertyStringBuilder = new StringBuilder(); | ||
propertyStringBuilder.append("\""); | ||
propertyStringBuilder.append(entry.getKey()); | ||
propertyStringBuilder.append("\": "); | ||
propertyStringBuilder.append(buildSchema(entry.getValue(), definitions)); | ||
propertyStringBuilder.append(buildSchemaInternal(entry.getValue(), definitions, visited)); | ||
return propertyStringBuilder.toString(); | ||
}) | ||
.sorted() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,12 +5,14 @@ | |
import io.swagger.v3.oas.models.media.BooleanSchema; | ||
import io.swagger.v3.oas.models.media.DateSchema; | ||
import io.swagger.v3.oas.models.media.DateTimeSchema; | ||
import io.swagger.v3.oas.models.media.EmailSchema; | ||
import io.swagger.v3.oas.models.media.IntegerSchema; | ||
import io.swagger.v3.oas.models.media.NumberSchema; | ||
import io.swagger.v3.oas.models.media.ObjectSchema; | ||
import io.swagger.v3.oas.models.media.PasswordSchema; | ||
import io.swagger.v3.oas.models.media.Schema; | ||
import io.swagger.v3.oas.models.media.StringSchema; | ||
import io.swagger.v3.oas.models.media.UUIDSchema; | ||
import org.junit.jupiter.api.Nested; | ||
import org.junit.jupiter.api.Test; | ||
|
||
|
@@ -197,6 +199,15 @@ void type_string_format_datetime() { | |
assertThat(actual).isEqualTo("\"2015-07-20T15:49:04-07:00\""); | ||
} | ||
|
||
@Test | ||
void type_string_format_email() { | ||
EmailSchema schema = new EmailSchema(); | ||
|
||
String actual = ExampleJsonGenerator.buildSchema(schema, emptyMap()); | ||
|
||
assertThat(actual).isEqualTo("\"[email protected]\""); | ||
} | ||
|
||
@Test | ||
void type_string_format_password() { | ||
PasswordSchema schema = new PasswordSchema(); | ||
|
@@ -206,6 +217,40 @@ void type_string_format_password() { | |
assertThat(actual).isEqualTo("\"string-password\""); | ||
} | ||
|
||
@Test | ||
void type_string_format_uuid() { | ||
UUIDSchema schema = new UUIDSchema(); | ||
|
||
String actual = ExampleJsonGenerator.buildSchema(schema, emptyMap()); | ||
|
||
assertThat(actual).isEqualTo("\"3fa85f64-5717-4562-b3fc-2c963f66afa6\""); | ||
} | ||
|
||
@Test | ||
void type_string_format_unknown() { | ||
StringSchema schema = new StringSchema(); | ||
schema.setFormat("unknown"); | ||
|
||
String actual = ExampleJsonGenerator.buildSchema(schema, emptyMap()); | ||
|
||
assertThat(actual).isEqualTo("\"unknown string schema format: unknown\""); | ||
} | ||
|
||
@Test | ||
void type_unknown_schema() { | ||
class TestSchema extends Schema<StringSchema> { | ||
TestSchema() { | ||
super("test-schema", (String) null); | ||
} | ||
} | ||
|
||
TestSchema schema = new TestSchema(); | ||
|
||
String actual = ExampleJsonGenerator.buildSchema(schema, emptyMap()); | ||
|
||
assertThat(actual).isEqualTo("\"unknown schema type: test-schema\""); | ||
} | ||
|
||
@Test | ||
void type_primitive_array() { | ||
ArraySchema schema = new ArraySchema(); | ||
|
Oops, something went wrong.