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/create new header schema when modified #941

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 @@ -13,11 +13,11 @@
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersBuilder;
import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderClassExtractor;
import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderSchemaObjectMerger;
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadMethodService;
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject;
import io.github.springwolf.core.asyncapi.scanners.common.utils.AnnotationScannerUtil;
import io.github.springwolf.core.asyncapi.scanners.operations.annotations.SpringAnnotationClassLevelOperationsScanner;
import io.github.springwolf.core.asyncapi.schemas.SchemaObjectMerger;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

Expand Down Expand Up @@ -97,7 +97,7 @@ protected Map<String, MessageReference> buildMessages(
protected MessageObject buildMessage(
ClassAnnotation classAnnotation, PayloadSchemaObject payloadSchema, SchemaObject headers) {
SchemaObject headerSchema = asyncHeadersBuilder.buildHeaders(payloadSchema);
SchemaObject mergedHeaderSchema = SchemaObjectMerger.merge(headerSchema, headers);
SchemaObject mergedHeaderSchema = HeaderSchemaObjectMerger.merge(headerSchema, headers);
String headerSchemaName = componentsService.registerSchema(mergedHeaderSchema);

Map<String, MessageBinding> messageBinding = bindingFactory.buildMessageBinding(classAnnotation, headerSchema);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
import io.github.springwolf.core.asyncapi.components.ComponentsService;
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersBuilder;
import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderSchemaObjectMerger;
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject;
import io.github.springwolf.core.asyncapi.schemas.SchemaObjectMerger;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -31,7 +31,7 @@ public abstract class MethodLevelAnnotationScanner<MethodAnnotation extends Anno
protected MessageObject buildMessage(
MethodAnnotation annotation, PayloadSchemaObject payloadSchema, SchemaObject headers) {
SchemaObject headerSchema = asyncHeadersBuilder.buildHeaders(payloadSchema);
SchemaObject mergedHeaderSchema = SchemaObjectMerger.merge(headerSchema, headers);
SchemaObject mergedHeaderSchema = HeaderSchemaObjectMerger.merge(headerSchema, headers);
String headerModelName = componentsService.registerSchema(mergedHeaderSchema);

Map<String, MessageBinding> messageBinding = bindingFactory.buildMessageBinding(annotation, mergedHeaderSchema);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.core.asyncapi.scanners.common.headers;

import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
import io.github.springwolf.asyncapi.v3.model.schema.SchemaType;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class HeaderSchemaObjectMerger {

public static SchemaObject merge(SchemaObject initial, SchemaObject... schemas) {

int additionalProperties = Arrays.stream(schemas)
.filter(schemaObject -> schemaObject != null && schemaObject.getProperties() != null)
.mapToInt(schema -> schema.getProperties().size())
.sum();
if (additionalProperties == 0) {
return initial;
}

SchemaObject.SchemaObjectBuilder headerSchemaBuilder =
SchemaObject.builder().type(SchemaType.OBJECT);

String title = initial.getTitle();
String description = initial.getDescription();
Map<String, Object> headerProperties = new HashMap<>(initial.getProperties());

for (SchemaObject schema : schemas) {
if (schema == null) {
continue;
}

if (StringUtils.isBlank(description)) {
description = schema.getDescription();
}

schema.getProperties().forEach(headerProperties::putIfAbsent);
}

return headerSchemaBuilder
.title(generateTitle(initial, headerProperties))
.description(description)
.properties(headerProperties)
.build();
}

public static String generateHeaderSchemaName(Object object) {
return generateHeaderSchemaName("Headers", object);
}

private static String generateHeaderSchemaName(String prefix, Object object) {
return prefix + "-" + Math.abs(object.hashCode());
}

private static String generateTitle(SchemaObject initial, Map<String, Object> headerProperties) {
if (StringUtils.isBlank(initial.getTitle())) {
return generateHeaderSchemaName(headerProperties);
} else {
return generateHeaderSchemaName(initial.getTitle(), headerProperties.hashCode());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Optional;
import java.util.stream.Collectors;

import static io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderSchemaObjectMerger.generateHeaderSchemaName;
import static java.util.stream.Collectors.groupingBy;

public class AsyncAnnotationUtil {
Expand All @@ -44,8 +45,9 @@ public static SchemaObject getAsyncHeaders(AsyncOperation op, StringValueResolve
return AsyncHeadersNotDocumented.NOT_DOCUMENTED;
}

String headerSchemaTitle =
StringUtils.hasText(headers.schemaName()) ? headers.schemaName() : generateHeadersSchemaName(headers);
String headerSchemaTitle;
headerSchemaTitle =
StringUtils.hasText(headers.schemaName()) ? headers.schemaName() : generateHeaderSchemaName(headers);
String headerDescription =
StringUtils.hasText(headers.description()) ? resolver.resolveStringValue(headers.description()) : null;

Expand Down Expand Up @@ -93,10 +95,6 @@ private static String getDescription(List<AsyncOperation.Headers.Header> value,
.orElse(null);
}

private static String generateHeadersSchemaName(AsyncOperation.Headers headers) {
return "Headers-" + Math.abs(headers.hashCode());
}

public static Map<String, OperationBinding> processOperationBindingFromAnnotation(
Method method, List<OperationBindingProcessor> operationBindingProcessors) {
return operationBindingProcessors.stream()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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.asyncapi.v3.model.schema.SchemaType;
import io.github.springwolf.core.asyncapi.components.ComponentsService;
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersNotDocumented;
Expand All @@ -26,7 +27,10 @@
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand All @@ -36,9 +40,11 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class SpringAnnotationMethodLevelChannelsScannerTest {
Expand Down Expand Up @@ -209,6 +215,48 @@ private void methodWithAnnotation(String payload) {}
private void anotherMethodWithAnnotation(SimpleFoo payload) {}
}

@Nested
class HeaderAnnotation {

@Test
void scan_componentHasHeaderAnnotation() {
// given
when(headerClassExtractor.extractHeader(any(), any()))
.thenReturn(SchemaObject.builder()
.type(SchemaType.OBJECT)
.properties(Map.of(
"header_name",
SchemaObject.builder()
.type(SchemaType.STRING)
.examples(List.of("foobar"))
.build()))
.build());

// when
scanner.scan(ClassWithMethodWithHeaderAnnotation.class).toList();

// then
verify(componentsService)
.registerSchema(eq(SchemaObject.builder()
.title("HeadersNotDocumented-934983093")
.type(SchemaType.OBJECT)
.description("There can be headers, but they are not explicitly documented.")
.properties(Map.of(
"header_name",
SchemaObject.builder()
.type(SchemaType.STRING)
.examples(List.of("foobar"))
.build()))
.build()));
}

private static class ClassWithMethodWithHeaderAnnotation {
@TestListener
private void methodWithAnnotationAndHeader(
@Payload String payload, @Header("header_name") String headerValue) {}
}
}

@Data
@NoArgsConstructor
private static class SimpleFoo {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.core.asyncapi.scanners.common.headers;

import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;

class HeaderSchemaObjectMergerTest {

@Test
void merge() {
SchemaObject initial =
SchemaObject.builder().properties(new HashMap<>()).build();
initial.getProperties().put("key1", "value1");

// when
SchemaObject merged = HeaderSchemaObjectMerger.merge(initial);

// then
assertEquals(
merged,
SchemaObject.builder().properties(Map.of("key1", "value1")).build());
}

@Test
void mergeAndIgnoreNullValues() {
SchemaObject initial =
SchemaObject.builder().properties(new HashMap<>()).build();
initial.getProperties().put("key1", "value1");

// when
SchemaObject merged = HeaderSchemaObjectMerger.merge(
initial,
null,
SchemaObject.builder().build(),
SchemaObject.builder().properties(Collections.emptyMap()).build());

// then
assertEquals(
merged,
SchemaObject.builder().properties(Map.of("key1", "value1")).build());
}

@Test
void mergeWhileNotOverwritingInitialValues() {
SchemaObject initial = SchemaObject.builder()
.title("initial-title")
.description("this-is-initial")
.properties(new HashMap<>())
.build();
initial.getProperties().put("key1", "value1");

SchemaObject schema1 = SchemaObject.builder()
.description("this-is-schema1")
.properties(new HashMap<>())
.build();
schema1.getProperties().put("key1", "value2");
schema1.getProperties().put("key2", "value2");

SchemaObject schema2 = SchemaObject.builder()
.description("this-is-schema2")
.properties(new HashMap<>())
.build();
schema2.getProperties().put("key2", "value3");
schema2.getProperties().put("key3", "value3");

// when
SchemaObject merged = HeaderSchemaObjectMerger.merge(initial, schema1, schema2);

// then
assertEquals(merged.getTitle(), "initial-title-1820791802");
assertEquals(merged.getDescription(), "this-is-initial");
assertEquals(merged.getProperties().size(), 3);
assertEquals(merged.getProperties().get("key1"), "value1");
assertEquals(merged.getProperties().get("key2"), "value2");
assertEquals(merged.getProperties().get("key3"), "value3");
}

@Test
void mergeAndTakeFirstNonNull() {
SchemaObject initial =
SchemaObject.builder().properties(new HashMap<>()).build();

SchemaObject schema1 = SchemaObject.builder()
.description("this-is-schema1")
.properties(new HashMap<>())
.build();
schema1.getProperties().put("key2", "value2");

SchemaObject schema2 = SchemaObject.builder()
.description("this-is-schema2")
.properties(new HashMap<>())
.build();
schema2.getProperties().put("key2", "value3");

// when
SchemaObject merged = HeaderSchemaObjectMerger.merge(initial, schema1, schema2);

// then
assertEquals(merged.getTitle(), "Headers-824725166");
assertEquals(merged.getDescription(), "this-is-schema1");
assertEquals(merged.getProperties().size(), 1);
assertEquals(merged.getProperties().get("key2"), "value2");
}
}
Loading