Skip to content

Commit

Permalink
refactor/use asyncapi headers schema (springwolf#658)
Browse files Browse the repository at this point in the history
* refactor(core): switch to asyncapi headers

* refactor(core): remove unused jackson-annotations dependency
  • Loading branch information
timonback authored Apr 12, 2024
1 parent 074f5c0 commit d1aa036
Show file tree
Hide file tree
Showing 24 changed files with 200 additions and 177 deletions.
1 change: 0 additions & 1 deletion springwolf-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ dependencies {
implementation "io.swagger.core.v3:swagger-core-jakarta:${swaggerVersion}"

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

implementation "org.slf4j:slf4j-api:${slf4jApiVersion}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject;
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference;
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders;

import java.util.Map;

public interface ComponentsService {

Map<String, SchemaObject> getSchemas();

String registerSchema(AsyncHeaders headers);
String registerSchema(SchemaObject headers);

String registerSchema(Class<?> type);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference;
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
import io.github.springwolf.core.asyncapi.annotations.AsyncApiPayload;
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders;
import io.github.springwolf.core.asyncapi.components.postprocessors.SchemasPostProcessor;
import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties;
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.jackson.TypeNameResolver;
import io.swagger.v3.oas.models.media.MapSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -62,18 +61,22 @@ public Map<String, SchemaObject> getSchemas() {
}

@Override
public String registerSchema(AsyncHeaders headers) {
log.debug("Registering schema for {}", headers.getSchemaName());
public String registerSchema(SchemaObject headers) {
log.debug("Registering schema for {}", headers.getTitle());

MapSchema headerSchema = new MapSchema();
headerSchema.setName(headers.getSchemaName());
ObjectSchema headerSchema = new ObjectSchema();
headerSchema.setName(headers.getTitle());
headerSchema.setDescription(headers.getDescription());
headerSchema.properties(headers);
Map<String, Schema> properties = headers.getProperties().entrySet().stream()
.map((property) -> Map.entry(property.getKey(), (Schema<?>)
swaggerSchemaUtil.mapToSwagger((SchemaObject) property.getValue())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
headerSchema.setProperties(properties);

this.schemas.put(headers.getSchemaName(), headerSchema);
this.schemas.put(headers.getTitle(), headerSchema);
postProcessSchema(headerSchema, DEFAULT_CONTENT_TYPE);

return headers.getSchemaName();
return headers.getTitle();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,14 @@ public SchemaObject mapSchema(Schema value) {

return builder.build();
}

public Schema mapToSwagger(SchemaObject asyncApiSchema) {
Schema swaggerSchema = new Schema();
swaggerSchema.setType("string");
swaggerSchema.setDescription(asyncApiSchema.getDescription());
swaggerSchema.setExamples(asyncApiSchema.getExamples());
swaggerSchema.setEnum(asyncApiSchema.getEnumValues());

return swaggerSchema;
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.core.asyncapi.components.headers;

import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;

public interface AsyncHeadersBuilder {
AsyncHeaders buildHeaders(Class<?> payloadType);
SchemaObject buildHeaders(Class<?> payloadType);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.core.asyncapi.components.headers;

import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;

import java.util.List;
import java.util.Map;

public class AsyncHeadersNotDocumented implements AsyncHeadersBuilder {
/**
* Per default, if no headers are explicitly defined, {@link AsyncHeadersNotUsed#NOT_USED} is used.
* There can be headers, but don't have to be.
*/
public static final AsyncHeaders NOT_DOCUMENTED =
new AsyncHeaders("HeadersNotDocumented", "There can be headers, but they are not explicitly documented.");
public static final SchemaObject NOT_DOCUMENTED = new SchemaObject();

static {
NOT_DOCUMENTED.setType("object");
NOT_DOCUMENTED.setTitle("HeadersNotDocumented");
NOT_DOCUMENTED.setDescription("There can be headers, but they are not explicitly documented.");
NOT_DOCUMENTED.setProperties(Map.of());
NOT_DOCUMENTED.setExamples(List.of(new Object()));
}

@Override
public AsyncHeaders buildHeaders(Class<?> payloadType) {
public SchemaObject buildHeaders(Class<?> payloadType) {
return NOT_DOCUMENTED;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.core.asyncapi.components.headers;

import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;

import java.util.List;
import java.util.Map;

public class AsyncHeadersNotUsed implements AsyncHeadersBuilder {
/**
* Explicitly document that no headers are used.
*/
public static final AsyncHeaders NOT_USED = new AsyncHeaders("HeadersNotUsed", "No headers are present.");
public static final SchemaObject NOT_USED = new SchemaObject();

static {
NOT_USED.setType("object");
NOT_USED.setTitle("HeadersNotUsed");
NOT_USED.setDescription("No headers are present.");
NOT_USED.setProperties(Map.of());
NOT_USED.setExamples(List.of(new Object()));
}

@Override
public AsyncHeaders buildHeaders(Class<?> payloadType) {
public SchemaObject buildHeaders(Class<?> payloadType) {
return NOT_USED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
import io.github.springwolf.asyncapi.v3.model.operation.Operation;
import io.github.springwolf.asyncapi.v3.model.operation.OperationAction;
import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference;
import io.github.springwolf.core.asyncapi.annotations.AsyncOperation;
import io.github.springwolf.core.asyncapi.components.ComponentsService;
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders;
import io.github.springwolf.core.asyncapi.scanners.bindings.messages.MessageBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.bindings.operations.OperationBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
Expand Down Expand Up @@ -92,7 +92,7 @@ protected MessageObject buildMessage(AsyncOperation operationData, Method method

String modelName = this.componentsService.registerSchema(
payloadType, operationData.message().contentType());
AsyncHeaders asyncHeaders = AsyncAnnotationUtil.getAsyncHeaders(operationData, resolver);
SchemaObject asyncHeaders = AsyncAnnotationUtil.getAsyncHeaders(operationData, resolver);
String headerModelName = this.componentsService.registerSchema(asyncHeaders);
var headers = MessageHeaders.of(MessageReference.toSchema(headerModelName));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
import io.github.springwolf.asyncapi.v3.bindings.MessageBinding;
import io.github.springwolf.asyncapi.v3.bindings.OperationBinding;
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject;
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
import io.github.springwolf.core.asyncapi.annotations.AsyncMessage;
import io.github.springwolf.core.asyncapi.annotations.AsyncOperation;
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaderSchema;
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders;
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersNotDocumented;
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersNotUsed;
import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor;
Expand All @@ -22,7 +21,9 @@

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -33,7 +34,7 @@
public class AsyncAnnotationUtil {
private AsyncAnnotationUtil() {}

public static AsyncHeaders getAsyncHeaders(AsyncOperation op, StringValueResolver resolver) {
public static SchemaObject getAsyncHeaders(AsyncOperation op, StringValueResolver resolver) {
if (op.headers().values().length == 0) {
if (op.headers().notUsed()) {
return AsyncHeadersNotUsed.NOT_USED;
Expand All @@ -44,29 +45,29 @@ public static AsyncHeaders getAsyncHeaders(AsyncOperation op, StringValueResolve
String headerDescription = StringUtils.hasText(op.headers().description())
? resolver.resolveStringValue(op.headers().description())
: null;
AsyncHeaders asyncHeaders = new AsyncHeaders(op.headers().schemaName(), headerDescription);

SchemaObject headerSchema = new SchemaObject();
headerSchema.setType("object");
headerSchema.setTitle(op.headers().schemaName());
headerSchema.setDescription(headerDescription);
headerSchema.setProperties(new HashMap<>());

Arrays.stream(op.headers().values())
.collect(groupingBy(AsyncOperation.Headers.Header::name))
.forEach((headerName, headers) -> {
String propertyName = resolver.resolveStringValue(headerName);

SchemaObject property = new SchemaObject();
property.setType("string");
property.setTitle(propertyName);
property.setDescription(getDescription(headers, resolver));
List<String> values = getHeaderValues(headers, resolver);
String exampleValue = values.stream().findFirst().orElse(null);
asyncHeaders.addHeader(AsyncHeaderSchema.headerBuilder()
.headerName(resolver.resolveStringValue(headerName))
.description(getDescription(headers, resolver))
.enumValue(values)
.example(exampleValue)
.build());

// FIXME: Replace AsyncHeaders by proper AsyncAPI v3 Headers
// MessageHeaders.of(
// SchemaObject.builder()
// .description(getDescription(headers, resolver))
// .enumValues(values)
// .examples(value != null ? List.of(value) : null)
// .build());
property.setExamples(new ArrayList<>(values));
property.setEnumValues(values);
headerSchema.getProperties().put(propertyName, property);
});

return asyncHeaders;
return headerSchema;
}

private static List<String> getHeaderValues(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.media.Discriminator;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -326,9 +327,7 @@ void mapAdditionalProperties() {
SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema);

// then
assertThat(((ComponentSchema) componentSchema.getAdditionalProperties())
.getSchema()
.getType())
assertThat(componentSchema.getAdditionalProperties().getSchema().getType())
.isEqualTo(additionalProperties.getType());
}

Expand Down Expand Up @@ -491,4 +490,46 @@ void mapMaxItems() {
assertThat(componentSchema.getMaxItems()).isEqualTo(schema.getMaxItems());
}
}

@Nested
class MapToSwagger {
@Test
void mapDescription() {
// given
SchemaObject schema = new SchemaObject();
schema.setDescription("description");

// when
Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema);

// then
assertThat(componentSchema.getDescription()).isEqualTo(schema.getDescription());
}

@Test
void mapExamples() {
// given
SchemaObject schema = new SchemaObject();
schema.setExamples(List.of("example1", "example2"));

// when
Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema);

// then
assertThat(componentSchema.getExamples()).isEqualTo(schema.getExamples());
}

@Test
void mapEnum() {
// given
SchemaObject schema = new SchemaObject();
schema.setEnumValues(List.of("enum1", "enum2"));

// when
Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema);

// then
assertThat(componentSchema.getEnum()).isEqualTo(schema.getEnumValues());
}
}
}
Loading

0 comments on commit d1aa036

Please sign in to comment.