Skip to content

Commit 5fc9ffd

Browse files
committed
feat(core): allow to specify multiple header values within one AsyncOperation.Headers.Header annotation
1 parent f1dcb17 commit 5fc9ffd

File tree

9 files changed

+103
-49
lines changed

9 files changed

+103
-49
lines changed

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/annotations/AsyncOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656

5757
String description() default "";
5858

59-
String value() default "";
59+
String[] value() default "";
6060

6161
/**
6262
* The schema type of the header value according to AsyncAPI specification.

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/annotation/AsyncAnnotationUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ private static Set<String> getHeadersType(List<AsyncOperation.Headers.Header> va
9797
private static List<String> getHeaderValues(
9898
List<AsyncOperation.Headers.Header> value, StringValueResolver stringValueResolver) {
9999
return value.stream()
100-
.map(AsyncOperation.Headers.Header::value)
100+
.flatMap(it -> Arrays.stream(it.value()))
101101
.filter(StringUtils::hasText)
102102
.flatMap(text -> Optional.ofNullable(stringValueResolver.resolveStringValue(text)).stream())
103103
.sorted()

springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/annotation/AsyncAnnotationUtilTest.java

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void getAsyncHeaders(Class<?> classWithOperationBindingProcessor) throws Excepti
5858
.as(headers.getProperties() + " does not contain key 'headerResolved'")
5959
.isTrue();
6060
SchemaObject headerResolved = (SchemaObject) headers.getProperties().get("headerResolved");
61-
assertThat(headerResolved.getType()).containsExactly("string");
61+
assertThat(headerResolved.getType()).containsExactly("integer");
6262
assertThat(headerResolved.getExamples().get(0)).isEqualTo("valueResolved");
6363
assertThat(headerResolved.getDescription()).isEqualTo("descriptionResolved");
6464
assertThat(headerResolved.getFormat()).isEqualTo("int32Resolved");
@@ -101,6 +101,23 @@ void getAsyncHeadersWithoutSchemaName() throws Exception {
101101
assertThat(headers.getTitle()).isNotNull();
102102
}
103103

104+
@Test
105+
void getAsyncHeadersWithMergedValue() throws Exception {
106+
// given
107+
Method m = ClassWithHeaders.class.getDeclaredMethod("withMergedValue", String.class);
108+
AsyncOperation operation = m.getAnnotation(AsyncListener.class).operation();
109+
110+
// when
111+
SchemaObject headers = AsyncAnnotationUtil.getAsyncHeaders(operation, stringValueResolver);
112+
113+
// then
114+
SchemaObject headerProperty = (SchemaObject) headers.getProperties().get("headerResolved");
115+
assertThat(headerProperty.getEnumValues())
116+
.containsExactlyInAnyOrder("valueResolved", "value2Resolved", "value3Resolved");
117+
assertThat(headerProperty.getExamples())
118+
.containsExactlyInAnyOrder("valueResolved", "value2Resolved", "value3Resolved");
119+
}
120+
104121
@Test
105122
void getAsyncHeadersWithoutValue() throws Exception {
106123
// given
@@ -131,7 +148,7 @@ void getAsyncHeadersWithFormat() throws Exception {
131148
}
132149

133150
@Test
134-
void getAsyncHeadersWithEmptyFormat() throws Exception {
151+
void processAsyncHeadersWithEmptyFormat() throws Exception {
135152
// given
136153
Method m = ClassWithHeaders.class.getDeclaredMethod("withoutFormat", String.class);
137154
AsyncOperation operation = m.getAnnotation(AsyncListener.class).operation();
@@ -145,7 +162,7 @@ void getAsyncHeadersWithEmptyFormat() throws Exception {
145162
}
146163

147164
@Test
148-
void getAsyncHeadersWithType() throws Exception {
165+
void processAsyncHeadersWithType() throws Exception {
149166
// given
150167
Method m = ClassWithHeaders.class.getDeclaredMethod("withType", String.class);
151168
AsyncOperation operation = m.getAnnotation(AsyncListener.class).operation();
@@ -159,7 +176,7 @@ void getAsyncHeadersWithType() throws Exception {
159176
}
160177

161178
@Test
162-
void getAsyncHeadersWithoutType() throws Exception {
179+
void processAsyncHeadersWithoutType() throws Exception {
163180
// given
164181
Method m = ClassWithHeaders.class.getDeclaredMethod("withoutType", String.class);
165182
AsyncOperation operation = m.getAnnotation(AsyncListener.class).operation();
@@ -303,7 +320,7 @@ void processMessageFromAnnotationWithAsyncMessage(Class<?> classWithOperationBin
303320
}
304321

305322
@Test
306-
void getServers() throws Exception {
323+
void processServers() throws Exception {
307324
Method m = ClassWithOperationBindingProcessor.class.getDeclaredMethod("methodWithAnnotation", String.class);
308325
AsyncOperation operation = m.getAnnotation(AsyncListener.class).operation();
309326

@@ -371,6 +388,7 @@ private static class ClassWithOperationBindingProcessor {
371388
name = "header",
372389
value = "value",
373390
description = "description",
391+
type = SchemaType.INTEGER,
374392
format = "int32"),
375393
@AsyncOperation.Headers.Header(
376394
name = "headerWithoutValue",
@@ -384,15 +402,6 @@ private void methodWithAnnotation(String payload) {}
384402
@AsyncOperation(
385403
channelName = "${test.property.test-channel}",
386404
description = "${test.property.description}",
387-
headers =
388-
@AsyncOperation.Headers(
389-
schemaName = "TestSchema",
390-
values = {
391-
@AsyncOperation.Headers.Header(
392-
name = "header",
393-
value = "value",
394-
description = "description")
395-
}),
396405
message =
397406
@AsyncMessage(
398407
description = "Message description",
@@ -419,6 +428,7 @@ private static class ClassWithAbstractOperationBindingProcessor {
419428
name = "header",
420429
value = "value",
421430
description = "description",
431+
type = SchemaType.INTEGER,
422432
format = "int32"),
423433
@AsyncOperation.Headers.Header(
424434
name = "headerWithoutValue",
@@ -432,15 +442,6 @@ private void methodWithAnnotation(String payload) {}
432442
@AsyncOperation(
433443
channelName = "${test.property.test-channel}",
434444
description = "${test.property.description}",
435-
headers =
436-
@AsyncOperation.Headers(
437-
schemaName = "TestSchema",
438-
values = {
439-
@AsyncOperation.Headers.Header(
440-
name = "header",
441-
value = "value",
442-
description = "description")
443-
}),
444445
message =
445446
@AsyncMessage(
446447
description = "Message description",
@@ -454,7 +455,6 @@ private void methodWithAsyncMessageAnnotation(String payload) {}
454455

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

460460
@AsyncListener(
@@ -466,17 +466,32 @@ private void emptyHeaders(String payload) {}
466466
values = {
467467
@AsyncOperation.Headers.Header(name = "header", value = "value")
468468
})))
469-
@TestOperationBindingProcessor.TestOperationBinding()
470469
private void withoutSchemaName(String payload) {}
471470

471+
@AsyncListener(
472+
operation =
473+
@AsyncOperation(
474+
channelName = "${test.property.test-channel}",
475+
headers =
476+
@AsyncOperation.Headers(
477+
values = {
478+
@AsyncOperation.Headers.Header(name = "header", value = "value"),
479+
@AsyncOperation.Headers.Header(
480+
name = "header",
481+
value = {"value2", "value3"}),
482+
@AsyncOperation.Headers.Header(
483+
name = "unrelated-header",
484+
value = "otherValue")
485+
})))
486+
private void withMergedValue(String payload) {}
487+
472488
@AsyncListener(
473489
operation =
474490
@AsyncOperation(
475491
channelName = "${test.property.test-channel}",
476492
headers =
477493
@AsyncOperation.Headers(
478494
values = {@AsyncOperation.Headers.Header(name = "header")})))
479-
@TestOperationBindingProcessor.TestOperationBinding()
480495
private void withoutValue(String payload) {}
481496

482497
@AsyncListener(
@@ -488,7 +503,6 @@ private void withoutValue(String payload) {}
488503
values = {
489504
@AsyncOperation.Headers.Header(name = "header", format = "int32")
490505
})))
491-
@TestOperationBindingProcessor.TestOperationBinding()
492506
private void withFormat(String payload) {}
493507

494508
@AsyncListener(
@@ -498,7 +512,6 @@ private void withFormat(String payload) {}
498512
headers =
499513
@AsyncOperation.Headers(
500514
values = {@AsyncOperation.Headers.Header(name = "header")})))
501-
@TestOperationBindingProcessor.TestOperationBinding()
502515
private void withoutFormat(String payload) {}
503516

504517
@AsyncListener(
@@ -512,7 +525,6 @@ private void withoutFormat(String payload) {}
512525
name = "header",
513526
type = SchemaType.INTEGER)
514527
})))
515-
@TestOperationBindingProcessor.TestOperationBinding()
516528
private void withType(String payload) {}
517529

518530
@AsyncListener(
@@ -522,7 +534,6 @@ private void withType(String payload) {}
522534
headers =
523535
@AsyncOperation.Headers(
524536
values = {@AsyncOperation.Headers.Header(name = "header")})))
525-
@TestOperationBindingProcessor.TestOperationBinding()
526537
private void withoutType(String payload) {}
527538

528539
@AsyncListener(
@@ -537,7 +548,6 @@ private void withoutType(String payload) {}
537548
value = "value",
538549
description = "non unique header")
539550
})))
540-
@TestOperationBindingProcessor.TestOperationBinding()
541551
private void differentHeadersWithoutSchemaName(String payload) {}
542552
}
543553

springwolf-examples/springwolf-kafka-example/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ plugins {
1414
}
1515

1616
dependencies {
17+
implementation project(":springwolf-asyncapi")
1718
implementation project(":springwolf-core")
1819
implementation project(":springwolf-plugins:springwolf-kafka")
1920
permitUnusedDeclared project(":springwolf-plugins:springwolf-kafka")

springwolf-examples/springwolf-kafka-example/src/main/java/io/github/springwolf/examples/kafka/producers/AnotherProducer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package io.github.springwolf.examples.kafka.producers;
33

4+
import io.github.springwolf.asyncapi.v3.model.schema.SchemaType;
45
import io.github.springwolf.bindings.kafka.annotations.KafkaAsyncOperationBinding;
56
import io.github.springwolf.core.asyncapi.annotations.AsyncOperation;
67
import io.github.springwolf.core.asyncapi.annotations.AsyncPublisher;
@@ -32,6 +33,11 @@ public class AnotherProducer {
3233
@AsyncOperation.Headers.Header(
3334
name = "my_uuid_field",
3435
description = "Event identifier",
36+
value = {
37+
"00000000-0000-0000-0000-000000000000",
38+
"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"
39+
},
40+
type = SchemaType.STRING,
3541
format = "uuid")
3642
})))
3743
@KafkaAsyncOperationBinding

springwolf-examples/springwolf-kafka-example/src/main/java/io/github/springwolf/examples/kafka/producers/NestedProducer.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package io.github.springwolf.examples.kafka.producers;
33

4+
import io.github.springwolf.asyncapi.v3.model.schema.SchemaType;
45
import io.github.springwolf.bindings.kafka.annotations.KafkaAsyncOperationBinding;
56
import io.github.springwolf.core.asyncapi.annotations.AsyncOperation;
67
import io.github.springwolf.core.asyncapi.annotations.AsyncPublisher;
@@ -57,7 +58,9 @@ public class NestedProducer {
5758
@AsyncOperation.Headers.Header(
5859
name = AsyncHeadersCloudEventConstants.TIME,
5960
description = AsyncHeadersCloudEventConstants.TIME_DESC,
60-
value = "2023-10-28 20:01:23+00:00"),
61+
value = "2023-10-28T20:01:23+00:00",
62+
type = SchemaType.STRING,
63+
format = "date-time"),
6164
@AsyncOperation.Headers.Header(
6265
name = AsyncHeadersCloudEventConstants.TYPE,
6366
description = AsyncHeadersCloudEventConstants.TYPE_DESC,

springwolf-examples/springwolf-kafka-example/src/test/resources/asyncapi.json

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,12 @@
326326
"title": "ce_time",
327327
"type": "string",
328328
"description": "CloudEvent Time Header",
329+
"format": "date-time",
329330
"enum": [
330-
"2023-10-28 20:01:23+00:00"
331+
"2023-10-28T20:01:23+00:00"
331332
],
332333
"examples": [
333-
"2023-10-28 20:01:23+00:00"
334+
"2023-10-28T20:01:23+00:00"
334335
]
335336
},
336337
"ce_type": {
@@ -364,7 +365,7 @@
364365
"ce_source": "http://localhost",
365366
"ce_specversion": "1.0",
366367
"ce_subject": "Springwolf example project - Kafka",
367-
"ce_time": "2023-10-28 20:01:23+00:00",
368+
"ce_time": "2023-10-28T20:01:23+00:00",
368369
"ce_type": "NestedPayloadDto.v1",
369370
"content-type": "application/json"
370371
}
@@ -416,8 +417,9 @@
416417
"ce_time": {
417418
"description": "CloudEvent Time Header",
418419
"enum": [
419-
"2023-10-28 20:01:23+00:00"
420+
"2023-10-28T20:01:23+00:00"
420421
],
422+
"format": "date-time",
421423
"title": "ce_time",
422424
"type": "string"
423425
},
@@ -529,13 +531,21 @@
529531
"title": "my_uuid_field",
530532
"type": "string",
531533
"description": "Event identifier",
532-
"format": "uuid"
534+
"format": "uuid",
535+
"enum": [
536+
"00000000-0000-0000-0000-000000000000",
537+
"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"
538+
],
539+
"examples": [
540+
"00000000-0000-0000-0000-000000000000",
541+
"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"
542+
]
533543
}
534544
},
535545
"examples": [
536546
{
537547
"__TypeId__": "string",
538-
"my_uuid_field": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
548+
"my_uuid_field": "00000000-0000-0000-0000-000000000000"
539549
}
540550
],
541551
"x-json-schema": {
@@ -548,6 +558,10 @@
548558
},
549559
"my_uuid_field": {
550560
"description": "Event identifier",
561+
"enum": [
562+
"00000000-0000-0000-0000-000000000000",
563+
"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"
564+
],
551565
"format": "uuid",
552566
"title": "my_uuid_field",
553567
"type": "string"

springwolf-examples/springwolf-kafka-example/src/test/resources/asyncapi.openapiv31.json

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@
259259
"ce_source": "http://localhost",
260260
"ce_specversion": "1.0",
261261
"ce_subject": "Springwolf example project - Kafka",
262-
"ce_time": "2023-10-28 20:01:23+00:00",
262+
"ce_time": "2023-10-28T20:01:23+00:00",
263263
"ce_type": "NestedPayloadDto.v1",
264264
"content-type": "application/json"
265265
},
@@ -321,12 +321,13 @@
321321
},
322322
"ce_time": {
323323
"type": "string",
324+
"format": "date-time",
324325
"description": "CloudEvent Time Header",
325326
"enum": [
326-
"2023-10-28 20:01:23+00:00"
327+
"2023-10-28T20:01:23+00:00"
327328
],
328329
"examples": [
329-
"2023-10-28 20:01:23+00:00"
330+
"2023-10-28T20:01:23+00:00"
330331
],
331332
"title": "ce_time"
332333
},
@@ -408,7 +409,7 @@
408409
"type": "object",
409410
"example": {
410411
"__TypeId__": "string",
411-
"my_uuid_field": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
412+
"my_uuid_field": "00000000-0000-0000-0000-000000000000"
412413
},
413414
"properties": {
414415
"__TypeId__": {
@@ -420,6 +421,14 @@
420421
"type": "string",
421422
"format": "uuid",
422423
"description": "Event identifier",
424+
"enum": [
425+
"00000000-0000-0000-0000-000000000000",
426+
"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"
427+
],
428+
"examples": [
429+
"00000000-0000-0000-0000-000000000000",
430+
"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"
431+
],
423432
"title": "my_uuid_field"
424433
}
425434
},

0 commit comments

Comments
 (0)