diff --git a/pom.xml b/pom.xml index fcba4ac4..ecf7625d 100644 --- a/pom.xml +++ b/pom.xml @@ -158,6 +158,9 @@ 3.2.0 + + https://docs.spring.io/spring-framework/docs/current/javadoc-api/ + diff --git a/spring/src/main/java/io/cloudevents/spring/http/CloudEventHttpUtils.java b/spring/src/main/java/io/cloudevents/spring/http/CloudEventHttpUtils.java index 5d722002..5ff14887 100644 --- a/spring/src/main/java/io/cloudevents/spring/http/CloudEventHttpUtils.java +++ b/spring/src/main/java/io/cloudevents/spring/http/CloudEventHttpUtils.java @@ -15,20 +15,21 @@ */ package io.cloudevents.spring.http; -import java.util.function.Consumer; -import java.util.function.Supplier; - import io.cloudevents.CloudEvent; import io.cloudevents.CloudEventContext; +import io.cloudevents.SpecVersion; import io.cloudevents.core.CloudEventUtils; import io.cloudevents.core.builder.CloudEventBuilder; import io.cloudevents.core.message.MessageReader; import io.cloudevents.http.HttpMessageFactory; import io.cloudevents.http.impl.HttpMessageWriter; - +import io.cloudevents.rw.CloudEventRWException; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; +import java.util.function.Consumer; +import java.util.function.Supplier; + /** * Miscellaneous utility methods to assist with Cloud Events in the context of Spring Web * frameworks. Primarily intended for the internal use within Spring-based frameworks or @@ -42,54 +43,58 @@ public class CloudEventHttpUtils { private CloudEventHttpUtils() { } - /** - * Create a {@link MessageReader} to assist in conversion of an HTTP request to a - * {@link CloudEvent}. - * @param headers the HTTP request headers - * @param body the HTTP request body as a byte array - * @return a {@link MessageReader} representing the {@link CloudEvent} - */ - public static MessageReader toReader(HttpHeaders headers, Supplier body) { - return HttpMessageFactory.createReaderFromMultimap(headers, body.get()); - } + /** + * Create a {@link MessageReader} to convert an HTTP request to a {@link CloudEvent}. + * + * @param headers the HTTP request headers + * @param body the HTTP request body as a byte array + * @return a {@link MessageReader} representing the {@link CloudEvent} + * @throws CloudEventRWException if something goes wrong while resolving the {@link SpecVersion} or if the message has unknown encoding + */ + public static MessageReader toReader(HttpHeaders headers, Supplier body) throws CloudEventRWException { + return HttpMessageFactory.createReaderFromMultimap(headers, body.get()); + } /** * Create an {@link HttpMessageWriter} that can hand off a {@link CloudEvent} to an * HTTP response. Mainly useful in a blocking (not async) setting because the response * body has to be consumed directly. + * * @param headers the response headers (will be mutated) * @param sendBody a consumer for the response body that puts the bytes on the wire */ public static HttpMessageWriter toWriter(HttpHeaders headers, Consumer sendBody) { return HttpMessageFactory.createWriter(headers::set, sendBody); - } + } - /** - * Helper method for extracting {@link HttpHeaders} from a {@link CloudEvent}. Can, - * for instance, be used in a @RequestMapping to return a - * {@link ResponseEntity} that has headers copied from a {@link CloudEvent}. - * @param event the input {@link CloudEvent} - * @return the response headers represented by the event - */ - public static HttpHeaders toHttp(CloudEventContext event) { - HttpHeaders headers = new HttpHeaders(); - CloudEventUtils.toReader(CloudEventBuilder.fromContext(event).build()).read(toWriter(headers, bytes -> { - })); - return headers; - } + /** + * Helper method for extracting {@link HttpHeaders} from a {@link CloudEvent}. Can, + * for instance, be used in a {@link org.springframework.web.bind.annotation.RequestMapping} to return a + * {@link ResponseEntity} that has headers copied from a {@link CloudEvent}. + * + * @param event the input {@link CloudEvent} + * @return the response headers represented by the event + * @throws CloudEventRWException if something goes wrong while writing the context to the http headers + */ + public static HttpHeaders toHttp(CloudEventContext event) throws CloudEventRWException { + HttpHeaders headers = new HttpHeaders(); + CloudEventUtils.toReader(CloudEventBuilder.fromContext(event).build()).read(toWriter(headers, bytes -> { + })); + return headers; + } - /** - * Helper method for converting {@link HttpHeaders} to a {@link CloudEvent}. The input - * headers must represent a valid event in "binary" form, i.e. it must have headers - * "ce-id", "ce-specversion" etc. - * @param headers the input request headers - * @return a {@link CloudEventBuilder} that can be used to create a new - * {@link CloudEvent} - * - */ - public static CloudEventBuilder fromHttp(HttpHeaders headers) { - return CloudEventBuilder - .fromContext(CloudEventUtils.toEvent(CloudEventHttpUtils.toReader(headers, () -> null))); + /** + * Helper method for converting {@link HttpHeaders} to a {@link CloudEvent}. The input + * headers must represent a valid event in "binary" form, i.e. it must have headers + * {@code ce-id}, {@code ce-specversion} etc. + * + * @param headers the input request headers + * @return a {@link CloudEventBuilder} that can be used to create a new {@link CloudEvent} + * @throws CloudEventRWException if something goes wrong while reading the context from the http headers + */ + public static CloudEventBuilder fromHttp(HttpHeaders headers) throws CloudEventRWException { + return CloudEventBuilder + .fromContext(CloudEventUtils.toEvent(CloudEventHttpUtils.toReader(headers, () -> null))); } } diff --git a/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventContextUtils.java b/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventContextUtils.java new file mode 100644 index 00000000..73ce0665 --- /dev/null +++ b/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventContextUtils.java @@ -0,0 +1,70 @@ +/* + * Copyright 2019-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudevents.spring.messaging; + +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventContext; +import io.cloudevents.SpecVersion; +import io.cloudevents.core.CloudEventUtils; +import io.cloudevents.rw.CloudEventRWException; +import org.springframework.messaging.MessageHeaders; + +import java.util.HashMap; +import java.util.Map; + +import static io.cloudevents.spring.messaging.CloudEventsHeaders.CE_PREFIX; + +/** + * Utility class for copying message headers to and from {@link CloudEventContext}. + * + * @author Dave Syer + */ +public class CloudEventContextUtils { + + /** + * Helper method for converting {@link MessageHeaders} to a {@link CloudEventContext}. + * The input headers must represent a valid event in "binary" form, i.e. it must have headers + * {@code ce-id}, {@code ce-specversion} etc. + * + * @param headers the input message headers + * @return a {@link CloudEventContext} that can be used to create a new {@link CloudEvent} + * @throws CloudEventRWException if something goes wrong while converting the headers to {@link CloudEventContext} + */ + public static CloudEventContext fromMap(Map headers) throws CloudEventRWException { + Object value = headers.get(CloudEventsHeaders.SPEC_VERSION); + SpecVersion version = value == null ? SpecVersion.V1 : SpecVersion.parse(value.toString()); + return CloudEventUtils.toEvent(new MessageBinaryMessageReader(version, headers)); + } + + /** + * Helper method for extracting {@link MessageHeaders} from a {@link CloudEventContext}. The + * result will contain headers canonicalized with a {@code ce-} prefix, analogous to the + * "binary" message format in Cloud Events. + * + * @param context the input {@link CloudEventContext} + * @return the response headers represented by the event + * @throws CloudEventRWException if something goes wrong while converting the {@link CloudEventContext} to headers + */ + public static Map toMap(CloudEventContext context) throws CloudEventRWException { + Map headers = new HashMap<>(); + // Probably this should be done in CloudEventContextReaderAdapter + headers.put(CE_PREFIX + "specversion", context.getSpecVersion().toString()); + MessageBuilderMessageWriter writer = new MessageBuilderMessageWriter(headers); + CloudEventUtils.toContextReader(context).readContext(writer); + return writer.end().getHeaders(); + } + +} diff --git a/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventHeaderUtils.java b/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventHeaderUtils.java deleted file mode 100644 index 5ecc6da6..00000000 --- a/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventHeaderUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.cloudevents.spring.messaging; - -import java.util.HashMap; -import java.util.Map; - -import io.cloudevents.CloudEvent; -import io.cloudevents.CloudEventContext; -import io.cloudevents.SpecVersion; -import io.cloudevents.core.CloudEventUtils; - -import org.springframework.messaging.MessageHeaders; - -import static io.cloudevents.spring.messaging.CloudEventsHeaders.CE_PREFIX; - -/** - * Utility class for copying message headers to and from {@link CloudEventContext}. - * - * @author Dave Syer - * - */ -public class CloudEventHeaderUtils { - - /** - * Helper method for converting {@link MessageHeaders} to a {@link CloudEventContext}. - * The input headers must represent a valid event in "binary" form, i.e. it must have - * headers "ce-id", "ce-specversion" etc. - * @param headers the input request headers - * @return a {@link CloudEventContext} that can be used to create a new - * {@link CloudEvent} - * - */ - public static CloudEventContext fromMap(Map headers) { - Object value = headers.get(CloudEventsHeaders.SPEC_VERSION); - SpecVersion version = value == null ? SpecVersion.V1 : SpecVersion.parse(value.toString()); - return CloudEventUtils.toEvent(new MessageBinaryMessageReader(version, headers)); - } - - /** - * Helper method for extracting {@link MessageHeaders} from a - * {@link CloudEventContext}. The result will contain headers canonicalized with a - * "ce-" prefix, analogous to the "binary" message format in Cloud Events. - * @param event the input {@link CloudEventContext} - * @return the response headers represented by the event - */ - public static Map toMap(CloudEventContext event) { - Map headers = new HashMap<>(); - // Probably this should be done in CloudEventContextReaderAdapter - headers.put(CE_PREFIX + "specversion", event.getSpecVersion().toString()); - MessageBuilderMessageWriter writer = new MessageBuilderMessageWriter(headers); - CloudEventUtils.toContextReader(event).readContext(writer); - return writer.end().getHeaders(); - } - -} diff --git a/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventMessageConverter.java b/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventMessageConverter.java index 03cd79ce..ab31a954 100644 --- a/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventMessageConverter.java +++ b/spring/src/main/java/io/cloudevents/spring/messaging/CloudEventMessageConverter.java @@ -15,8 +15,6 @@ */ package io.cloudevents.spring.messaging; -import java.nio.charset.Charset; - import io.cloudevents.CloudEvent; import io.cloudevents.CloudEventContext; import io.cloudevents.SpecVersion; @@ -25,19 +23,19 @@ import io.cloudevents.core.format.EventFormat; import io.cloudevents.core.message.MessageReader; import io.cloudevents.core.message.impl.GenericStructuredMessageReader; import io.cloudevents.core.message.impl.MessageUtils; - import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.MessageConverter; +import java.nio.charset.Charset; + /** * A {@link MessageConverter} that can translate to and from a {@link Message * Message<byte[]>} or {@link Message Message<String>} and a {@link CloudEvent}. The - * {@link CloudEventContext} is canonicalized, with key names given a "ce-" prefix in the + * {@link CloudEventContext} is canonicalized, with key names given a {@code ce-} prefix in the * {@link MessageHeaders}. - * - * @author Dave Syer * + * @author Dave Syer */ public class CloudEventMessageConverter implements MessageConverter { diff --git a/spring/src/main/java/io/cloudevents/spring/mvc/CloudEventHttpMessageConverter.java b/spring/src/main/java/io/cloudevents/spring/mvc/CloudEventHttpMessageConverter.java index fcfe5794..fe558aa4 100644 --- a/spring/src/main/java/io/cloudevents/spring/mvc/CloudEventHttpMessageConverter.java +++ b/spring/src/main/java/io/cloudevents/spring/mvc/CloudEventHttpMessageConverter.java @@ -15,12 +15,9 @@ */ package io.cloudevents.spring.mvc; -import java.io.IOException; - import io.cloudevents.CloudEvent; import io.cloudevents.core.CloudEventUtils; import io.cloudevents.spring.http.CloudEventHttpUtils; - import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; @@ -30,13 +27,14 @@ import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.util.StreamUtils; +import java.io.IOException; + /** * An {@link HttpMessageConverter} for {@link CloudEvent CloudEvents}. Supports the use of - * {@link CloudEvent} in a @RequestMapping as either a method parameter + * {@link CloudEvent} in a {@link org.springframework.web.bind.annotation.RequestMapping} as either a method parameter * or a return value. - * - * @author Dave Syer * + * @author Dave Syer */ public class CloudEventHttpMessageConverter extends AbstractHttpMessageConverter { diff --git a/spring/src/main/java/io/cloudevents/spring/webflux/CloudEventHttpMessageReader.java b/spring/src/main/java/io/cloudevents/spring/webflux/CloudEventHttpMessageReader.java index b2a6efd9..17a31162 100644 --- a/spring/src/main/java/io/cloudevents/spring/webflux/CloudEventHttpMessageReader.java +++ b/spring/src/main/java/io/cloudevents/spring/webflux/CloudEventHttpMessageReader.java @@ -15,16 +15,9 @@ */ package io.cloudevents.spring.webflux; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - import io.cloudevents.CloudEvent; import io.cloudevents.core.message.MessageReader; import io.cloudevents.spring.http.CloudEventHttpUtils; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.HttpHeaders; @@ -32,14 +25,19 @@ import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpInputMessage; import org.springframework.http.codec.HttpMessageReader; import org.springframework.util.StreamUtils; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; /** * A reactive {@link HttpMessageReader} for {@link CloudEvent CloudEvents}, converting - * from an HTTP request to a cloud event. Supports the use of {@link CloudEvent} as an + * from an HTTP request to a CloudEvent. Supports the use of {@link CloudEvent} as an * input to a reactive endpoint. - * - * @author Dave Syer * + * @author Dave Syer */ public class CloudEventHttpMessageReader implements HttpMessageReader { @@ -74,4 +72,4 @@ public class CloudEventHttpMessageReader implements HttpMessageReader CloudEventHttpUtils.toReader(headers, () -> bytes)).map(MessageReader::toEvent); } -} \ No newline at end of file +} diff --git a/spring/src/main/java/io/cloudevents/spring/webflux/CloudEventHttpMessageWriter.java b/spring/src/main/java/io/cloudevents/spring/webflux/CloudEventHttpMessageWriter.java index ae6933cb..39eaeb0a 100644 --- a/spring/src/main/java/io/cloudevents/spring/webflux/CloudEventHttpMessageWriter.java +++ b/spring/src/main/java/io/cloudevents/spring/webflux/CloudEventHttpMessageWriter.java @@ -40,11 +40,10 @@ import java.util.Map; /** * A reactive {@link HttpMessageWriter} for {@link CloudEvent CloudEvents}, converting - * from a cloud event to an HTTP response. Supports the use of {@link CloudEvent} as an + * from a CloudEvent to an HTTP response. Supports the use of {@link CloudEvent} as an * output from a reactive endpoint. * * @author Dave Syer - * */ public class CloudEventHttpMessageWriter implements HttpMessageWriter { diff --git a/spring/src/test/java/io/cloudevents/spring/messaging/CloudEventContextUtilsTests.java b/spring/src/test/java/io/cloudevents/spring/messaging/CloudEventContextUtilsTests.java new file mode 100644 index 00000000..a8fc1928 --- /dev/null +++ b/spring/src/test/java/io/cloudevents/spring/messaging/CloudEventContextUtilsTests.java @@ -0,0 +1,75 @@ +package io.cloudevents.spring.messaging; + +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventContext; +import io.cloudevents.SpecVersion; +import io.cloudevents.core.builder.CloudEventBuilder; +import io.cloudevents.rw.CloudEventRWException; +import org.junit.jupiter.api.Test; +import org.springframework.messaging.MessageHeaders; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class CloudEventContextUtilsTests { + + @Test + public void testWithEmpty() { + Map headers = new HashMap<>(); + assertThatExceptionOfType(CloudEventRWException.class).isThrownBy(() -> { + CloudEventContext attributes = CloudEventContextUtils.fromMap(new MessageHeaders(headers)); + assertThat(attributes.getSpecVersion()).isEqualTo(SpecVersion.V1); + assertThat(attributes.getId()).isNull(); + assertThat(attributes.getSource()).isNull(); + assertThat(attributes.getType()).isNull(); + }); + } + + @Test + public void testWithPrefix() { + Map headers = new HashMap<>(); + headers.put("ce-scpecversion", "1.0"); + headers.put("ce-id", "A234-1234-1234"); + headers.put("ce-source", "https://spring.io/"); + headers.put("ce-type", "org.springframework"); + headers.put("ce-datacontenttype", "application/json"); + CloudEventContext attributes = CloudEventContextUtils.fromMap(new MessageHeaders(headers)); + assertThat(attributes.getSpecVersion()).isEqualTo(SpecVersion.V1); + assertThat(attributes.getId()).isEqualTo("A234-1234-1234"); + assertThat(attributes.getSource()).isEqualTo(URI.create("https://spring.io/")); + assertThat(attributes.getType()).isEqualTo("org.springframework"); + assertThat(attributes.getDataContentType()).isEqualTo("application/json"); + } + + @Test + public void testExtensionsWithPrefix() { + Map headers = new HashMap<>(); + headers.put("ce-scpecversion", "1.0"); + headers.put("ce-id", "A234-1234-1234"); + headers.put("ce-source", "https://spring.io/"); + headers.put("ce-type", "org.springframework"); + headers.put("ce-foo", "bar"); + CloudEventContext attributes = CloudEventContextUtils.fromMap(new MessageHeaders(headers)); + assertThat(attributes.getSpecVersion()).isEqualTo(SpecVersion.V1); + assertThat(attributes.getId()).isEqualTo("A234-1234-1234"); + assertThat(attributes.getSource()).isEqualTo(URI.create("https://spring.io/")); + assertThat(attributes.getType()).isEqualTo("org.springframework"); + assertThat(attributes.getExtension("foo")).isEqualTo("bar"); + } + + @Test + public void testToHeaders() { + CloudEvent attributes = CloudEventBuilder.v1().withId("A234-1234-1234") + .withSource(URI.create("https://spring.io/")).withType("org.springframework").build(); + Map headers = CloudEventContextUtils.toMap(attributes); + assertThat(headers.get("ce-id")).isEqualTo("A234-1234-1234"); + assertThat(headers.get("ce-specversion")).isEqualTo("1.0"); + assertThat(headers.get("ce-source")).isEqualTo("https://spring.io/"); + assertThat(headers.get("ce-type")).isEqualTo("org.springframework"); + } + +} diff --git a/spring/src/test/java/io/cloudevents/spring/messaging/CloudEventHeaderUtilsTests.java b/spring/src/test/java/io/cloudevents/spring/messaging/CloudEventHeaderUtilsTests.java deleted file mode 100644 index 073704e6..00000000 --- a/spring/src/test/java/io/cloudevents/spring/messaging/CloudEventHeaderUtilsTests.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.cloudevents.spring.messaging; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import io.cloudevents.CloudEvent; -import io.cloudevents.CloudEventContext; -import io.cloudevents.SpecVersion; -import io.cloudevents.core.builder.CloudEventBuilder; -import io.cloudevents.rw.CloudEventRWException; -import org.junit.jupiter.api.Test; - -import org.springframework.messaging.MessageHeaders; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class CloudEventHeaderUtilsTests { - - @Test - public void testWithEmpty() { - Map headers = new HashMap<>(); - assertThatExceptionOfType(CloudEventRWException.class).isThrownBy(() -> { - CloudEventContext attributes = CloudEventHeaderUtils.fromMap(new MessageHeaders(headers)); - assertThat(attributes.getSpecVersion()).isEqualTo(SpecVersion.V1); - assertThat(attributes.getId()).isNull(); - assertThat(attributes.getSource()).isNull(); - assertThat(attributes.getType()).isNull(); - }); - } - - @Test - public void testWithPrefix() { - Map headers = new HashMap<>(); - headers.put("ce-scpecversion", "1.0"); - headers.put("ce-id", "A234-1234-1234"); - headers.put("ce-source", "https://spring.io/"); - headers.put("ce-type", "org.springframework"); - headers.put("ce-datacontenttype", "application/json"); - CloudEventContext attributes = CloudEventHeaderUtils.fromMap(new MessageHeaders(headers)); - assertThat(attributes.getSpecVersion()).isEqualTo(SpecVersion.V1); - assertThat(attributes.getId()).isEqualTo("A234-1234-1234"); - assertThat(attributes.getSource()).isEqualTo(URI.create("https://spring.io/")); - assertThat(attributes.getType()).isEqualTo("org.springframework"); - assertThat(attributes.getDataContentType()).isEqualTo("application/json"); - } - - @Test - public void testExtensionsWithPrefix() { - Map headers = new HashMap<>(); - headers.put("ce-scpecversion", "1.0"); - headers.put("ce-id", "A234-1234-1234"); - headers.put("ce-source", "https://spring.io/"); - headers.put("ce-type", "org.springframework"); - headers.put("ce-foo", "bar"); - CloudEventContext attributes = CloudEventHeaderUtils.fromMap(new MessageHeaders(headers)); - assertThat(attributes.getSpecVersion()).isEqualTo(SpecVersion.V1); - assertThat(attributes.getId()).isEqualTo("A234-1234-1234"); - assertThat(attributes.getSource()).isEqualTo(URI.create("https://spring.io/")); - assertThat(attributes.getType()).isEqualTo("org.springframework"); - assertThat(attributes.getExtension("foo")).isEqualTo("bar"); - } - - @Test - public void testToHeaders() { - CloudEvent attributes = CloudEventBuilder.v1().withId("A234-1234-1234") - .withSource(URI.create("https://spring.io/")).withType("org.springframework").build(); - Map headers = CloudEventHeaderUtils.toMap(attributes); - assertThat(headers.get("ce-id")).isEqualTo("A234-1234-1234"); - assertThat(headers.get("ce-specversion")).isEqualTo("1.0"); - assertThat(headers.get("ce-source")).isEqualTo("https://spring.io/"); - assertThat(headers.get("ce-type")).isEqualTo("org.springframework"); - } - -}