Skip to content

Commit

Permalink
feat(core): add validation for schema name in AsyncOperation.Headers (#…
Browse files Browse the repository at this point in the history
…896)

motivated by GH-893
  • Loading branch information
timonback authored Aug 3, 2024
1 parent 289a2cd commit 5684310
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,33 +35,37 @@ public class AsyncAnnotationUtil {
private AsyncAnnotationUtil() {}

public static SchemaObject getAsyncHeaders(AsyncOperation op, StringValueResolver resolver) {
if (op.headers().values().length == 0) {
if (op.headers().notUsed()) {
AsyncOperation.Headers headers = op.headers();
if (headers.values().length == 0) {
if (headers.notUsed()) {
return AsyncHeadersNotUsed.NOT_USED;
}
return AsyncHeadersNotDocumented.NOT_DOCUMENTED;
}
if (!StringUtils.hasText(headers.schemaName())) {
throw new IllegalArgumentException("The schemaName in @AsyncOperation.Headers must be set for values: "
+ Arrays.toString(headers.values()));
}

String headerDescription = StringUtils.hasText(op.headers().description())
? resolver.resolveStringValue(op.headers().description())
: null;
String headerDescription =
StringUtils.hasText(headers.description()) ? resolver.resolveStringValue(headers.description()) : null;

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

Arrays.stream(op.headers().values())
Arrays.stream(headers.values())
.collect(groupingBy(AsyncOperation.Headers.Header::name))
.forEach((headerName, headers) -> {
.forEach((headerName, headersValues) -> {
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);
property.setDescription(getDescription(headersValues, resolver));
List<String> values = getHeaderValues(headersValues, resolver);
property.setExamples(new ArrayList<>(values));
property.setEnumValues(values);
headerSchema.getProperties().put(propertyName, property);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.github.springwolf.core.asyncapi.scanners.bindings.processor.TestChannelBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.bindings.processor.TestMessageBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.bindings.processor.TestOperationBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersNotDocumented;
import org.assertj.core.util.Maps;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand All @@ -24,6 +25,8 @@
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand All @@ -41,13 +44,13 @@ void getAsyncHeaders(Class<?> classWithOperationBindingProcessor) throws NoSuchM
AsyncOperation operation = m.getAnnotation(AsyncListener.class).operation();

StringValueResolver resolver = mock(StringValueResolver.class);

// when
when(resolver.resolveStringValue(any()))
.thenAnswer(invocation -> invocation.getArgument(0).toString() + "Resolved");

// then
// when
SchemaObject headers = AsyncAnnotationUtil.getAsyncHeaders(operation, resolver);

// then
assertEquals("TestSchema", headers.getTitle());
assertEquals("header-descriptionResolved", headers.getDescription());
assertTrue(
Expand All @@ -69,6 +72,39 @@ void getAsyncHeaders(Class<?> classWithOperationBindingProcessor) throws NoSuchM
assertEquals("descriptionResolved", headerWithoutValueResolved.getDescription());
}

@Test
void getAsyncHeadersWithEmptyHeaders() throws NoSuchMethodException {
// given
Method m = ClassWithHeaders.class.getDeclaredMethod("emptyHeaders", String.class);
AsyncOperation operation = m.getAnnotation(AsyncListener.class).operation();

StringValueResolver resolver = mock(StringValueResolver.class);
when(resolver.resolveStringValue(any()))
.thenAnswer(invocation -> invocation.getArgument(0).toString() + "Resolved");

// when
SchemaObject headers = AsyncAnnotationUtil.getAsyncHeaders(operation, resolver);

// then
assertThat(headers).isEqualTo(AsyncHeadersNotDocumented.NOT_DOCUMENTED);
}

@Test
void getAsyncHeadersWithoutSchemaName() throws NoSuchMethodException {
// given
Method m = ClassWithHeaders.class.getDeclaredMethod("withoutSchemaName", String.class);
AsyncOperation operation = m.getAnnotation(AsyncListener.class).operation();

StringValueResolver resolver = mock(StringValueResolver.class);
when(resolver.resolveStringValue(any()))
.thenAnswer(invocation -> invocation.getArgument(0).toString() + "Resolved");

// when + then
assertThatIllegalArgumentException()
.isThrownBy(() -> AsyncAnnotationUtil.getAsyncHeaders(operation, resolver))
.withMessageContaining("The schemaName in @AsyncOperation.Headers must be set for values");
}

@Test
void processOperationBindingFromAnnotation() throws NoSuchMethodException {
// given
Expand Down Expand Up @@ -307,6 +343,27 @@ private void methodWithAnnotation(String payload) {}
private void methodWithAsyncMessageAnnotation(String payload) {}
}

private static class ClassWithHeaders {
@AsyncListener(operation = @AsyncOperation(channelName = "${test.property.test-channel}"))
@TestOperationBindingProcessor.TestOperationBinding()
private void emptyHeaders(String payload) {}

@AsyncListener(
operation =
@AsyncOperation(
channelName = "${test.property.test-channel}",
headers =
@AsyncOperation.Headers(
values = {
@AsyncOperation.Headers.Header(
name = "header",
value = "value",
description = "description")
})))
@TestOperationBindingProcessor.TestOperationBinding()
private void withoutSchemaName(String payload) {}
}

private static class ClassWithMultipleOperationBindingProcessors {
@AsyncListener(
operation =
Expand Down

0 comments on commit 5684310

Please sign in to comment.