diff --git a/docs/src/main/asciidoc/sqs.adoc b/docs/src/main/asciidoc/sqs.adoc index 8acffc7ea..659fe5c39 100644 --- a/docs/src/main/asciidoc/sqs.adoc +++ b/docs/src/main/asciidoc/sqs.adoc @@ -639,6 +639,29 @@ public void listen(@SnsNotificationMessage List pojos) { } ---- +==== EventBridge Messages + +Since 3.3.0, similar to the @SnsNotificationMessage, there is also a @EventBridgeMessage annotation that can be used to receive EventBridge messages. To only receive the `detail` part of the payload, you can utilize the `@EventBridgeMessage` annotation. +As with @SnsNotificationMessage, this supports both individual messages, or lists. + +[source, java] +---- +@SqsListener("my-queue") +public void listen(@EventBridgeMessage Pojo pojo) { + System.out.println(pojo.field); +} +---- + +For batch message processing, use the @EventBridgeMessage annotation with a List parameter. + +[source, java] +---- +@SqsListener("my-queue") +public void listen(@EventBridgeMessage List pojos) { + System.out.println(pojos.size()); +} +---- + ===== Specifying a MessageListenerContainerFactory A `MessageListenerContainerFactory` can be specified through the `factory` property. Such factory will then be used to create the container for the annotated method. diff --git a/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/annotation/EventBridgeMessage.java b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/annotation/EventBridgeMessage.java new file mode 100644 index 000000000..436b26412 --- /dev/null +++ b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/annotation/EventBridgeMessage.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2024 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.awspring.cloud.sqs.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that is used to map EventBridge messages value on an SQS Queue to a variable that is annotated. Used in + * controllers method for handling/receiving SQS notifications. + * + * @author Fredrik Jonsén + * @since 3.3.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface EventBridgeMessage { +} diff --git a/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/annotation/SqsListenerAnnotationBeanPostProcessor.java b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/annotation/SqsListenerAnnotationBeanPostProcessor.java index 3755e7590..ec2fabff0 100644 --- a/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/annotation/SqsListenerAnnotationBeanPostProcessor.java +++ b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/annotation/SqsListenerAnnotationBeanPostProcessor.java @@ -21,6 +21,7 @@ import io.awspring.cloud.sqs.config.SqsEndpoint; import io.awspring.cloud.sqs.listener.SqsHeaders; import io.awspring.cloud.sqs.support.resolver.BatchVisibilityHandlerMethodArgumentResolver; +import io.awspring.cloud.sqs.support.resolver.EventBridgeMessageArgumentResolver; import io.awspring.cloud.sqs.support.resolver.NotificationMessageArgumentResolver; import io.awspring.cloud.sqs.support.resolver.QueueAttributesMethodArgumentResolver; import io.awspring.cloud.sqs.support.resolver.SqsMessageMethodArgumentResolver; @@ -84,6 +85,7 @@ protected Collection createAdditionalArgumentReso List argumentResolvers = new ArrayList<>(createAdditionalArgumentResolvers()); if (objectMapper != null) { argumentResolvers.add(new NotificationMessageArgumentResolver(messageConverter, objectMapper)); + argumentResolvers.add(new EventBridgeMessageArgumentResolver(messageConverter, objectMapper)); } return argumentResolvers; } diff --git a/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/EventBridgeMessageConverter.java b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/EventBridgeMessageConverter.java new file mode 100644 index 000000000..7c717c0ae --- /dev/null +++ b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/EventBridgeMessageConverter.java @@ -0,0 +1,48 @@ +package io.awspring.cloud.sqs.support.converter; + + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.lang.Nullable; +import org.springframework.messaging.converter.MessageConversionException; +import org.springframework.messaging.converter.MessageConverter; +import org.springframework.messaging.converter.SmartMessageConverter; +import org.springframework.messaging.support.GenericMessage; + +public class EventBridgeMessageConverter extends WrappedMessageConverter { + private static final String WRITING_CONVERSION_ERROR = "This converter only supports reading an EventBridge message and not writing them"; + + public EventBridgeMessageConverter(MessageConverter payloadConverter, ObjectMapper jsonMapper) { + super(payloadConverter, jsonMapper); + } + + @Override + protected Object fromGenericMessage(GenericMessage message, Class targetClass, @Nullable Object conversionHint) { + JsonNode jsonNode; + try { + jsonNode = this.jsonMapper.readTree(message.getPayload().toString()); + } + catch (Exception e) { + throw new MessageConversionException("Could not read JSON", e); + } + + if (!jsonNode.has("detail")) { + throw new MessageConversionException( + "Payload: '" + message.getPayload() + "' does not contain a detail attribute", null); + } + + // Unlike SNS, where the message is a nested JSON string, the message payload in EventBridge is a JSON object + JsonNode messagePayload = jsonNode.get("detail"); + GenericMessage genericMessage = new GenericMessage<>(messagePayload); + if (payloadConverter instanceof SmartMessageConverter payloadConverter) { + return payloadConverter.fromMessage(genericMessage, targetClass, conversionHint); + } else { + return payloadConverter.fromMessage(genericMessage, targetClass); + } + } + + @Override + protected String getWritingConversionErrorMessage() { + return WRITING_CONVERSION_ERROR; + } +} diff --git a/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/SnsMessageConverter.java b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/SnsMessageConverter.java index 5cfe5da66..7e562ff86 100644 --- a/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/SnsMessageConverter.java +++ b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/SnsMessageConverter.java @@ -38,48 +38,20 @@ * @author Wei Jiang * @since 3.1.1 */ -public class SnsMessageConverter implements SmartMessageConverter { - - private final ObjectMapper jsonMapper; - - private final MessageConverter payloadConverter; +public class SnsMessageConverter extends WrappedMessageConverter { + private static final String WRITING_CONVERSION_ERROR = "This converter only supports reading a SNS notification and not writing them"; public SnsMessageConverter(MessageConverter payloadConverter, ObjectMapper jsonMapper) { - Assert.notNull(payloadConverter, "payloadConverter must not be null"); - Assert.notNull(jsonMapper, "jsonMapper must not be null"); - this.payloadConverter = payloadConverter; - this.jsonMapper = jsonMapper; + super(payloadConverter, jsonMapper); } @Override - @SuppressWarnings("unchecked") - public Object fromMessage(Message message, Class targetClass, @Nullable Object conversionHint) { - Assert.notNull(message, "message must not be null"); - Assert.notNull(targetClass, "target class must not be null"); - - Object payload = message.getPayload(); - - if (payload instanceof List messages) { - return fromGenericMessages(messages, targetClass, conversionHint); - } - else { - return fromGenericMessage((GenericMessage) message, targetClass, conversionHint); - } + protected String getWritingConversionErrorMessage() { + return WRITING_CONVERSION_ERROR; } - private Object fromGenericMessages(List> messages, Class targetClass, - @Nullable Object conversionHint) { - Type resolvedType = getResolvedType(targetClass, conversionHint); - Class resolvedClazz = ResolvableType.forType(resolvedType).resolve(); - - Object hint = targetClass.isAssignableFrom(List.class) && conversionHint instanceof MethodParameter mp - ? mp.nested() - : conversionHint; - - return messages.stream().map(message -> fromGenericMessage(message, resolvedClazz, hint)).toList(); - } - - private Object fromGenericMessage(GenericMessage message, Class targetClass, + @Override + protected Object fromGenericMessage(GenericMessage message, Class targetClass, @Nullable Object conversionHint) { JsonNode jsonNode; try { @@ -111,41 +83,4 @@ private Object fromGenericMessage(GenericMessage message, Class targetClas : this.payloadConverter.fromMessage(genericMessage, targetClass); return convertedMessage; } - - @Override - public Object fromMessage(Message message, Class targetClass) { - return fromMessage(message, targetClass, null); - } - - @Override - public Message toMessage(Object payload, MessageHeaders headers) { - throw new UnsupportedOperationException( - "This converter only supports reading a SNS notification and not writing them"); - } - - @Override - public Message toMessage(Object payload, MessageHeaders headers, Object conversionHint) { - throw new UnsupportedOperationException( - "This converter only supports reading a SNS notification and not writing them"); - } - - private static Type getResolvedType(Class targetClass, @Nullable Object conversionHint) { - if (conversionHint instanceof MethodParameter param) { - param = param.nestedIfOptional(); - if (Message.class.isAssignableFrom(param.getParameterType())) { - param = param.nested(); - } - Type genericParameterType = param.getNestedGenericParameterType(); - Class contextClass = param.getContainingClass(); - Type resolveType = GenericTypeResolver.resolveType(genericParameterType, contextClass); - if (resolveType instanceof ParameterizedType parameterizedType) { - return parameterizedType.getActualTypeArguments()[0]; - } - else { - return resolveType; - } - } - return targetClass; - } - } diff --git a/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/WrappedMessageConverter.java b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/WrappedMessageConverter.java new file mode 100644 index 000000000..eea01c1b5 --- /dev/null +++ b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/converter/WrappedMessageConverter.java @@ -0,0 +1,97 @@ +package io.awspring.cloud.sqs.support.converter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; +import org.springframework.core.GenericTypeResolver; +import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; +import org.springframework.lang.Nullable; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.converter.MessageConverter; +import org.springframework.messaging.converter.SmartMessageConverter; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.util.Assert; + +/** + * @author Fredrik Jonsen + * @since 3.3.0 + */ +abstract class WrappedMessageConverter implements SmartMessageConverter { + protected final MessageConverter payloadConverter; + protected final ObjectMapper jsonMapper; + + protected WrappedMessageConverter(MessageConverter payloadConverter, ObjectMapper jsonMapper) { + Assert.notNull(payloadConverter, "payloadConverter must not be null"); + Assert.notNull(jsonMapper, "jsonMapper must not be null"); + this.jsonMapper = jsonMapper; + this.payloadConverter = payloadConverter; + } + + protected abstract String getWritingConversionErrorMessage(); + protected abstract Object fromGenericMessage(GenericMessage message, Class targetClass, @Nullable Object conversionHint); + + @Override + @SuppressWarnings("unchecked") + public Object fromMessage(Message message, Class targetClass, @Nullable Object conversionHint) { + Assert.notNull(message, "message must not be null"); + Assert.notNull(targetClass, "target class must not be null"); + + Object payload = message.getPayload(); + + if (payload instanceof List messages) { + return fromGenericMessages(messages, targetClass, conversionHint); + } + else { + return fromGenericMessage((GenericMessage) message, targetClass, conversionHint); + } + } + + @Override + public Object fromMessage(Message message, Class targetClass) { + return fromMessage(message, targetClass, null); + } + + protected Object fromGenericMessages(List> messages, Class targetClass, + @Nullable Object conversionHint) { + Type resolvedType = getResolvedType(targetClass, conversionHint); + Class resolvedClazz = ResolvableType.forType(resolvedType).resolve(); + + Object hint = targetClass.isAssignableFrom(List.class) && conversionHint instanceof MethodParameter mp + ? mp.nested() + : conversionHint; + + return messages.stream().map(message -> fromGenericMessage(message, resolvedClazz, hint)).toList(); + } + + protected static Type getResolvedType(Class targetClass, @Nullable Object conversionHint) { + if (conversionHint instanceof MethodParameter param) { + param = param.nestedIfOptional(); + if (Message.class.isAssignableFrom(param.getParameterType())) { + param = param.nested(); + } + Type genericParameterType = param.getNestedGenericParameterType(); + Class contextClass = param.getContainingClass(); + Type resolveType = GenericTypeResolver.resolveType(genericParameterType, contextClass); + if (resolveType instanceof ParameterizedType parameterizedType) { + return parameterizedType.getActualTypeArguments()[0]; + } + else { + return resolveType; + } + } + return targetClass; + } + + @Override + public Message toMessage(Object payload, MessageHeaders headers) { + throw new UnsupportedOperationException(getWritingConversionErrorMessage()); + } + + @Override + public Message toMessage(Object payload, MessageHeaders headers, Object conversionHint) { + throw new UnsupportedOperationException(getWritingConversionErrorMessage()); + } +} diff --git a/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/resolver/EventBridgeMessageArgumentResolver.java b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/resolver/EventBridgeMessageArgumentResolver.java new file mode 100644 index 000000000..fb03c3fcb --- /dev/null +++ b/spring-cloud-aws-sqs/src/main/java/io/awspring/cloud/sqs/support/resolver/EventBridgeMessageArgumentResolver.java @@ -0,0 +1,29 @@ +package io.awspring.cloud.sqs.support.resolver; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.awspring.cloud.sqs.annotation.EventBridgeMessage; +import io.awspring.cloud.sqs.support.converter.EventBridgeMessageConverter; +import org.springframework.core.MethodParameter; +import org.springframework.messaging.Message; +import org.springframework.messaging.converter.MessageConverter; +import org.springframework.messaging.converter.SmartMessageConverter; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; + +public class EventBridgeMessageArgumentResolver implements HandlerMethodArgumentResolver { + private final SmartMessageConverter converter; + + public EventBridgeMessageArgumentResolver(MessageConverter converter, ObjectMapper jsonMapper) { + this.converter = new EventBridgeMessageConverter(converter, jsonMapper); + } + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(EventBridgeMessage.class); + } + + @Override + public Object resolveArgument(MethodParameter par, Message msg) { + Class parameterType = par.getParameterType(); + return this.converter.fromMessage(msg, parameterType, par); + } +} diff --git a/spring-cloud-aws-sqs/src/test/java/io/awspring/cloud/sqs/integration/SqsMessageConversionIntegrationTests.java b/spring-cloud-aws-sqs/src/test/java/io/awspring/cloud/sqs/integration/SqsMessageConversionIntegrationTests.java index fb9ad300b..0142e9604 100644 --- a/spring-cloud-aws-sqs/src/test/java/io/awspring/cloud/sqs/integration/SqsMessageConversionIntegrationTests.java +++ b/spring-cloud-aws-sqs/src/test/java/io/awspring/cloud/sqs/integration/SqsMessageConversionIntegrationTests.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.databind.ObjectMapper; +import io.awspring.cloud.sqs.annotation.EventBridgeMessage; import io.awspring.cloud.sqs.annotation.SnsNotificationMessage; import io.awspring.cloud.sqs.annotation.SqsListener; import io.awspring.cloud.sqs.config.SqsBootstrapConfiguration; @@ -74,6 +75,8 @@ class SqsMessageConversionIntegrationTests extends BaseSqsIntegrationTest { static final String RESOLVES_MY_OTHER_POJO_FROM_HEADER_QUEUE_NAME = "resolves_my_other_pojo_from_mapping_test_queue"; static final String RESOLVES_POJO_FROM_NOTIFICATION_MESSAGE_QUEUE_NAME = "resolves_pojo_from_notification_message_queue"; static final String RESOLVES_POJO_FROM_NOTIFICATION_MESSAGE_LIST_QUEUE_NAME = "resolves_pojo_from_notification_message_list_test_queue"; + static final String RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_QUEUE_NAME = "resolves_pojo_from_eventbridge_message_queue"; + static final String RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_LIST_QUEUE_NAME = "resolves_pojo_from_eventbridge_message_list_test_queue"; @Autowired LatchContainer latchContainer; @@ -91,7 +94,10 @@ static void beforeTests() { createQueue(client, RESOLVES_POJO_FROM_HEADER_QUEUE_NAME), createQueue(client, RESOLVES_MY_OTHER_POJO_FROM_HEADER_QUEUE_NAME), createQueue(client, RESOLVES_POJO_FROM_NOTIFICATION_MESSAGE_QUEUE_NAME), - createQueue(client, RESOLVES_POJO_FROM_NOTIFICATION_MESSAGE_LIST_QUEUE_NAME)).join(); + createQueue(client, RESOLVES_POJO_FROM_NOTIFICATION_MESSAGE_LIST_QUEUE_NAME), + createQueue(client, RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_QUEUE_NAME), + createQueue(client, RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_QUEUE_NAME), + createQueue(client, RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_LIST_QUEUE_NAME)).join(); } @Test @@ -170,6 +176,29 @@ void resolvesMyPojoFromNotificationMessageList() throws Exception { assertThat(latchContainer.resolvesPojoNotificationMessageListLatch.await(10, TimeUnit.SECONDS)).isTrue(); } + @Test + void resolvesMyPojoFromEventBridgeMessage() throws Exception { + byte[] eventBridgeJsonContent = FileCopyUtils + .copyToByteArray(getClass().getClassLoader().getResourceAsStream("eventBridgeMessage.json")); + String payload = new String(eventBridgeJsonContent); + sqsTemplate.send(RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_QUEUE_NAME, payload); + logger.debug("Sent message to queue {} with messageBody {}", RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_QUEUE_NAME, + payload); + assertThat(latchContainer.resolvesPojoEventBridgeMessageLatch.await(10, TimeUnit.SECONDS)).isTrue(); + } + @Test + void resolvesMyPojoFromEventBridgeMessageList() throws Exception { + byte[] eventBridgeJsonContent = FileCopyUtils + .copyToByteArray(getClass().getClassLoader().getResourceAsStream("eventBridgeMessage.json")); + String payload = new String(eventBridgeJsonContent); + List> messages = IntStream.range(0, 10) + .mapToObj(index -> MessageBuilder.withPayload(payload).build()).toList(); + sqsTemplate.sendMany(RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_LIST_QUEUE_NAME, messages); + logger.debug("Sent message to queue {} with messageBody {}", + RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_LIST_QUEUE_NAME, payload); + assertThat(latchContainer.resolvesPojoEventBridgeMessageListLatch.await(10, TimeUnit.SECONDS)).isTrue(); + } + private Map getHeaderMapping(Class clazz) { return Collections.singletonMap(SqsHeaders.SQS_DEFAULT_TYPE_HEADER, clazz.getName()); } @@ -294,6 +323,41 @@ void listen(@SnsNotificationMessage List> myPojos) { } } + static class ResolvesPojoWithEventBridgeAnnotationListener { + @Autowired + LatchContainer latchContainer; + @SqsListener( + queueNames = RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_QUEUE_NAME, + id = "resolves-pojo-with-eventbridge-message", + factory = "defaultSqsListenerContainerFactory" + ) + void listen(@EventBridgeMessage MyEnvelope myPojo) { + Assert.notNull((myPojo).data.firstField, "Received null message"); + logger.debug("Received message {} from queue {}", myPojo, + RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_QUEUE_NAME); + assertThat(myPojo.data.getFirstField()).isEqualTo("pojoEventMessage"); + assertThat(myPojo.data.getSecondField()).isEqualTo("secondValue"); + assertThat(myPojo.specversion).isEqualTo("1.0.2"); + latchContainer.resolvesPojoEventBridgeMessageLatch.countDown(); + } + @SqsListener( + queueNames = RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_LIST_QUEUE_NAME, + id = "resolves-pojo-with-eventbridge-message-list", + factory = "defaultSqsListenerContainerFactory" + ) + void listen(@EventBridgeMessage List> myPojos) { + Assert.notEmpty(myPojos, "Received empty messages"); + logger.debug("Received messages {} from queue {}", myPojos, + RESOLVES_POJO_FROM_EVENTBRIDGE_MESSAGE_LIST_QUEUE_NAME); + for (MyEnvelope myPojo : myPojos) { + assertThat(myPojo.data.getFirstField()).isEqualTo("pojoEventMessage"); + assertThat(myPojo.data.getSecondField()).isEqualTo("secondValue"); + assertThat(myPojo.specversion).isEqualTo("1.0.2"); + } + latchContainer.resolvesPojoEventBridgeMessageListLatch.countDown(); + } + } + static class LatchContainer { CountDownLatch resolvesPojoLatch = new CountDownLatch(1); @@ -304,6 +368,8 @@ static class LatchContainer { CountDownLatch resolvesMyOtherPojoFromMappingLatch = new CountDownLatch(1); CountDownLatch resolvesPojoNotificationMessageLatch = new CountDownLatch(1); CountDownLatch resolvesPojoNotificationMessageListLatch = new CountDownLatch(1); + CountDownLatch resolvesPojoEventBridgeMessageLatch = new CountDownLatch(1); + CountDownLatch resolvesPojoEventBridgeMessageListLatch = new CountDownLatch(1); } @@ -388,6 +454,11 @@ ResolvesPojoWithNotificationAnnotationListener resolvesPojoWithNotificationAnnot return new ResolvesPojoWithNotificationAnnotationListener(); } + @Bean + ResolvesPojoWithEventBridgeAnnotationListener resolvesPojoWithEventBridgeAnnotationListener() { + return new ResolvesPojoWithEventBridgeAnnotationListener(); + } + @Bean LatchContainer latchContainer() { return this.latchContainer; diff --git a/spring-cloud-aws-sqs/src/test/resources/eventBridgeMessage.json b/spring-cloud-aws-sqs/src/test/resources/eventBridgeMessage.json new file mode 100644 index 000000000..e36593cec --- /dev/null +++ b/spring-cloud-aws-sqs/src/test/resources/eventBridgeMessage.json @@ -0,0 +1,17 @@ +{ + "version": "0", + "id": "17793124-05d4-b198-2fde-7ededc63b103", + "detail-type": "TestPojo", + "source": "TestService", + "account": "123456789012", + "time": "2017-01-12T22:37:39Z", + "region": "eu-north-1", + "resources": [], + "detail": { + "specversion": "1.0.2", + "data": { + "firstField": "pojoEventMessage", + "secondField": "secondValue" + } + } +}