Skip to content

Commit

Permalink
Chore/async api 3 clean up (#567)
Browse files Browse the repository at this point in the history
* refactor: rename SchemasService to ComponentsService

* refactor: use AsyncApiSerializerService (wip)

� Conflicts:
�	springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiSerializerService.java

* feat(asyncapi): use BigDecimal as minimum, maximum

* refactor(core)!: remove AsyncAPI and Components (wip)

replaced with AsyncAPI 3 classes

* feat(json-generator): update JsonSchemaGenerator

* test(json-generator): re-activate JsonSchemaGeneratorTest test cases

* feat(json-generator): add missing schema types

* feat(core): add schema type exclusiveMinimum and maximum

* test(core): resolve TODO

* test(core): add SwaggerSchemaUtilTest

* test(core): not resolvable easily

* test: update asyncapi.json files

* chore(core): update dependencies

* feat(ui): fix exclusiveMaximum handling

* chore: move schemaMapping inside DefaultComponentsService

* chore(core): create SwaggerSchemaUtil as component

* feat(core): remove SwaggerSchemaPostProcessor (replaced by SwaggerSchemaUtil)

* chore(ui): fix after rebase
  • Loading branch information
timonback authored Feb 2, 2024
1 parent 078d507 commit 2f6d73b
Show file tree
Hide file tree
Showing 89 changed files with 1,556 additions and 1,064 deletions.
3 changes: 2 additions & 1 deletion springwolf-add-ons/springwolf-json-schema/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ plugins {

dependencies {
api project(":springwolf-core")
api project(":springwolf-asyncapi")

implementation "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}"
implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"

testImplementation "io.swagger.core.v3:swagger-core-jakarta:${swaggerVersion}"
implementation "io.swagger.core.v3:swagger-models-jakarta:${swaggerVersion}"
testImplementation "io.swagger.core.v3:swagger-models-jakarta:${swaggerVersion}"

implementation "org.apache.commons:commons-lang3:${commonsLang3Version}"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
package io.github.stavshamir.springwolf.addons.json_schema;

import io.github.stavshamir.springwolf.asyncapi.AsyncApiCustomizer;
import io.github.stavshamir.springwolf.asyncapi.types.AsyncAPI;
import io.swagger.v3.oas.models.media.Schema;
import io.github.stavshamir.springwolf.asyncapi.v3.model.AsyncAPI;
import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -19,20 +19,23 @@ public class JsonSchemaCustomizer implements AsyncApiCustomizer {

@Override
public void customize(AsyncAPI asyncAPI) {
Map<String, Schema> schemas = asyncAPI.getComponents().getSchemas();
for (Map.Entry<String, Schema> entry : schemas.entrySet()) {
Schema schema = entry.getValue();
if (schema.getExtensions() == null) {
schema.setExtensions(new HashMap<>());
}
Map<String, SchemaObject> schemas = asyncAPI.getComponents().getSchemas();
for (Map.Entry<String, SchemaObject> entry : schemas.entrySet()) {
SchemaObject schema = entry.getValue();

if (schema != null) {
if (schema.getExtensionFields() == null) {
schema.setExtensionFields(new HashMap<>());
}

try {
log.debug("Generate json-schema for %s".formatted(entry.getKey()));
try {
log.debug("Generate json-schema for %s".formatted(entry.getKey()));

Object jsonSchema = jsonSchemaGenerator.fromSchema(schema, schemas);
schema.getExtensions().putIfAbsent(EXTENSION_JSON_SCHEMA, jsonSchema);
} catch (Exception ex) {
log.warn("Unable to create json-schema for %s".formatted(schema.getName()), ex);
Object jsonSchema = jsonSchemaGenerator.fromSchema(schema, schemas);
schema.getExtensionFields().putIfAbsent(EXTENSION_JSON_SCHEMA, jsonSchema);
} catch (Exception ex) {
log.warn("Unable to create json-schema for %s".formatted(entry.getKey()), ex);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.models.media.Schema;
import io.github.stavshamir.springwolf.asyncapi.v3.model.components.ComponentSchema;
import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaObject;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;

Expand All @@ -18,14 +19,16 @@
public class JsonSchemaGenerator {
private final ObjectMapper objectMapper;

public Object fromSchema(Schema<?> schema, Map<String, Schema> definitions) throws JsonProcessingException {
public Object fromSchema(SchemaObject schema, Map<String, SchemaObject> definitions)
throws JsonProcessingException {
ObjectNode node = fromSchemaInternal(schema, definitions, new HashSet<>());
node.put("$schema", "https://json-schema.org/draft-04/schema#");

return objectMapper.readValue(node.toString(), Object.class);
}

private ObjectNode fromSchemaInternal(Schema<?> schema, Map<String, Schema> definitions, Set<Schema> visited) {
private ObjectNode fromSchemaInternal(
SchemaObject schema, Map<String, SchemaObject> definitions, Set<SchemaObject> visited) {
if (schema != null && !visited.contains(schema)) {
visited.add(schema);

Expand All @@ -34,44 +37,51 @@ private ObjectNode fromSchemaInternal(Schema<?> schema, Map<String, Schema> defi
return objectMapper.createObjectNode();
}

private ObjectNode mapToJsonSchema(Schema<?> schema, Map<String, Schema> definitions, Set<Schema> visited) {
private ObjectNode mapToJsonSchema(
SchemaObject schema, Map<String, SchemaObject> definitions, Set<SchemaObject> visited) {
ObjectNode node = objectMapper.createObjectNode();

if (schema.getAnyOf() != null) {
ArrayNode arrayNode = objectMapper.createArrayNode();
for (Schema ofSchema : schema.getAnyOf()) {
arrayNode.add(fromSchemaInternal(ofSchema, definitions, visited));
for (ComponentSchema ofSchema : schema.getAnyOf()) {
SchemaObject schemaObject = getSchemaObject(ofSchema, definitions);
arrayNode.add(fromSchemaInternal(schemaObject, definitions, visited));
}
node.put("anyOf", arrayNode);
node.set("anyOf", arrayNode);
}
if (schema.getAllOf() != null) {
ArrayNode arrayNode = objectMapper.createArrayNode();
for (Schema ofSchema : schema.getAllOf()) {
arrayNode.add(fromSchemaInternal(ofSchema, definitions, visited));
for (ComponentSchema allSchema : schema.getAllOf()) {
SchemaObject schemaObject = getSchemaObject(allSchema, definitions);
arrayNode.add(fromSchemaInternal(schemaObject, definitions, visited));
}
node.put("allOf", arrayNode);
node.set("allOf", arrayNode);
}
if (schema.getConst() != null) {
node.put("const", schema.getConst().toString());
if (schema.getConstValue() != null) {
node.put("const", schema.getConstValue().toString());
}
if (schema.getDescription() != null) {
node.put("description", schema.getDescription());
}
if (schema.getEnum() != null) {
if (schema.getEnumValues() != null) {
ArrayNode arrayNode = objectMapper.createArrayNode();
for (Object property : schema.getEnum()) {
for (Object property : schema.getEnumValues()) {
arrayNode.add(property.toString());
}
if (schema.getNullable() != null && schema.getNullable()) {
arrayNode.add("null");
}
node.set("enum", arrayNode);
}
if (schema.getExclusiveMinimum() != null) {
node.put("exclusiveMinimum", schema.getExclusiveMinimum());
}
if (schema.getExclusiveMaximum() != null) {
node.put("exclusiveMaximum", schema.getExclusiveMaximum());
}
if (schema.getFormat() != null) {
node.put("format", schema.getFormat());
}
if (schema.getItems() != null) {
node.set("items", fromSchemaInternal(schema.getItems(), definitions, visited));
SchemaObject schemaObject = getSchemaObject(schema.getItems(), definitions);
node.set("items", fromSchemaInternal(schemaObject, definitions, visited));
}
if (schema.getMaximum() != null) {
node.put("maximum", schema.getMaximum());
Expand All @@ -94,18 +104,17 @@ private ObjectNode mapToJsonSchema(Schema<?> schema, Map<String, Schema> definit
if (schema.getMultipleOf() != null) {
node.put("multipleOf", schema.getMultipleOf());
}
if (schema.getName() != null) {
node.put("name", schema.getName());
}
if (schema.getNot() != null) {
node.put("not", fromSchemaInternal(schema.getNot(), definitions, visited));
SchemaObject schemaObject = getSchemaObject(schema.getNot(), definitions);
node.set("not", fromSchemaInternal(schemaObject, definitions, visited));
}
if (schema.getOneOf() != null) {
ArrayNode arrayNode = objectMapper.createArrayNode();
for (Schema ofSchema : schema.getOneOf()) {
arrayNode.add(fromSchemaInternal(ofSchema, definitions, visited));
for (ComponentSchema ofSchema : schema.getOneOf()) {
SchemaObject schemaObject = getSchemaObject(ofSchema, definitions);
arrayNode.add(fromSchemaInternal(schemaObject, definitions, visited));
}
node.put("oneOf", arrayNode);
node.set("oneOf", arrayNode);
}
if (schema.getPattern() != null) {
node.put("pattern", schema.getPattern());
Expand All @@ -124,14 +133,7 @@ private ObjectNode mapToJsonSchema(Schema<?> schema, Map<String, Schema> definit
node.put("title", schema.getTitle());
}
if (schema.getType() != null) {
if (schema.getNullable() != null && schema.getNullable()) {
ArrayNode arrayNode = objectMapper.createArrayNode();
arrayNode.add(schema.getType());
arrayNode.add("null");
node.set("type", arrayNode);
} else {
node.put("type", schema.getType());
}
node.put("type", schema.getType());
}
if (schema.getUniqueItems() != null) {
node.put("uniqueItems", schema.getUniqueItems());
Expand All @@ -140,21 +142,33 @@ private ObjectNode mapToJsonSchema(Schema<?> schema, Map<String, Schema> definit
return node;
}

private JsonNode buildProperties(Schema<?> schema, Map<String, Schema> definitions, Set<Schema> visited) {
private JsonNode buildProperties(
SchemaObject schema, Map<String, SchemaObject> definitions, Set<SchemaObject> visited) {
ObjectNode node = objectMapper.createObjectNode();

for (Map.Entry<String, Schema> propertySchemaSet :
for (Map.Entry<String, Object> propertySchemaSet :
schema.getProperties().entrySet()) {
Schema propertySchema = propertySchemaSet.getValue();

if (propertySchema != null && propertySchema.get$ref() != null) {
String schemaName = StringUtils.substringAfterLast(propertySchema.get$ref(), "/");
propertySchema = definitions.get(schemaName);
}

node.set(propertySchemaSet.getKey(), fromSchemaInternal(propertySchema, definitions, visited));
SchemaObject propertySchema = getSchemaObject(propertySchemaSet.getValue(), definitions);
ObjectNode propertySchemaMapped = fromSchemaInternal(propertySchema, definitions, visited);
node.set(propertySchemaSet.getKey(), propertySchemaMapped);
}

return node;
}

private static SchemaObject getSchemaObject(Object schema, Map<String, SchemaObject> definitions) {
if (schema instanceof SchemaObject) {
return (SchemaObject) schema;
} else if (schema instanceof ComponentSchema) {
ComponentSchema componentSchema = (ComponentSchema) schema;
if (componentSchema.getReference() != null
&& componentSchema.getReference().getRef() != null) {
String schemaName = StringUtils.substringAfterLast(
componentSchema.getReference().getRef(), "/");
return definitions.get(schemaName);
}
return componentSchema.getSchema();
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
package io.github.stavshamir.springwolf.addons.json_schema;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.github.stavshamir.springwolf.asyncapi.types.AsyncAPI;
import io.github.stavshamir.springwolf.asyncapi.types.Components;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.github.stavshamir.springwolf.asyncapi.v3.model.AsyncAPI;
import io.github.stavshamir.springwolf.asyncapi.v3.model.components.Components;
import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaObject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -42,15 +42,17 @@ public void handleEmptySchemaTest() {
public void shouldAddJsonSchemaExtensionTest() throws JsonProcessingException {
// given
AsyncAPI asyncAPI = createAsyncApi();
asyncAPI.getComponents().setSchemas(Map.of("schema", new ObjectSchema()));
SchemaObject schemaObject = new SchemaObject();
schemaObject.setType("object");
asyncAPI.getComponents().setSchemas(Map.of("schema", schemaObject));

when(jsonSchemaGenerator.fromSchema(any(), any())).thenReturn("mock-string");

// when
jsonSchemaCustomizer.customize(asyncAPI);

// then
assertThat(asyncAPI.getComponents().getSchemas().get("schema").getExtensions())
assertThat(asyncAPI.getComponents().getSchemas().get("schema").getExtensionFields())
.isEqualTo(Map.of("x-json-schema", "mock-string"));
}

Expand Down
Loading

0 comments on commit 2f6d73b

Please sign in to comment.