diff --git a/spring-boot-examples/consumer-app/pom.xml b/spring-boot-examples/consumer-app/pom.xml new file mode 100644 index 000000000..63df3df40 --- /dev/null +++ b/spring-boot-examples/consumer-app/pom.xml @@ -0,0 +1,99 @@ + + + 4.0.0 + + io.dapr + spring-boot-examples + 0.14.0-SNAPSHOT + + + consumer-app + consumer-app + Spring Boot, Testcontainers and Dapr Integration Examples :: Consumer App + + + 3.2.6 + + + + + + org.springframework.boot + spring-boot-dependencies + ${springboot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + io.dapr.spring + dapr-spring-boot-starter + ${dapr-java-sdk.alpha-version} + + + + io.dapr.spring + dapr-spring-boot-starter + ${dapr.sdk.alpha.version} + + + + io.dapr.spring + dapr-spring-boot-starter-test + ${dapr.sdk.alpha.version} + test + + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + postgresql + 1.20.0 + test + + + org.testcontainers + rabbitmq + 1.20.0 + test + + + org.testcontainers + kafka + 1.20.0 + test + + + + io.rest-assured + rest-assured + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/ConsumerApplication.java b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/ConsumerApplication.java new file mode 100644 index 000000000..615340861 --- /dev/null +++ b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/ConsumerApplication.java @@ -0,0 +1,13 @@ +package io.dapr.springboot.examples.consumer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConsumerApplication.class, args); + } + +} diff --git a/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/Order.java b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/Order.java new file mode 100644 index 000000000..f556d5b08 --- /dev/null +++ b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/Order.java @@ -0,0 +1,46 @@ +package io.dapr.springboot.examples.consumer; + +public class Order { + private String id; + private String item; + private Integer amount; + + public Order() { + } + + /** + * Creates a new Order. + * @param id order id + * @param item item reference + * @param amount of items in the order + */ + public Order(String id, String item, Integer amount) { + this.id = id; + this.item = item; + this.amount = amount; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } +} diff --git a/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/SubscriberRestController.java b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/SubscriberRestController.java new file mode 100644 index 000000000..d3416f147 --- /dev/null +++ b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/SubscriberRestController.java @@ -0,0 +1,36 @@ +package io.dapr.springboot.examples.consumer; + +import io.dapr.Topic; +import io.dapr.client.domain.CloudEvent; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +@RestController +public class SubscriberRestController { + + private List events = new ArrayList<>(); + + /** + * Subscribe to cloud events. + * @param cloudEvent payload + */ + @PostMapping("subscribe") + @Topic(pubsubName = "pubsub", name = "topic") + public void subscribe(@RequestBody CloudEvent cloudEvent) { + System.out.println("CONSUME +++++ " + cloudEvent); + System.out.println("ORDER +++++ " + cloudEvent.getData()); + events.add(cloudEvent); + } + + @GetMapping("events") + public List getAllEvents() { + return events; + } + +} + diff --git a/spring-boot-examples/consumer-app/src/main/resources/application.properties b/spring-boot-examples/consumer-app/src/main/resources/application.properties new file mode 100644 index 000000000..fa29657d1 --- /dev/null +++ b/spring-boot-examples/consumer-app/src/main/resources/application.properties @@ -0,0 +1,2 @@ +spring.application.name=consumer-app +server.port=8081 diff --git a/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppTestConfiguration.java b/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppTestConfiguration.java new file mode 100644 index 000000000..57c8b8b7f --- /dev/null +++ b/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppTestConfiguration.java @@ -0,0 +1,18 @@ +package io.dapr.springboot.examples.consumer; + +import io.dapr.client.DaprClient; +import io.dapr.spring.boot.autoconfigure.pubsub.DaprPubSubProperties; +import io.dapr.spring.messaging.DaprMessagingTemplate; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties({DaprPubSubProperties.class}) +public class ConsumerAppTestConfiguration { + @Bean + public DaprMessagingTemplate messagingTemplate(DaprClient daprClient, + DaprPubSubProperties daprPubSubProperties) { + return new DaprMessagingTemplate<>(daprClient, daprPubSubProperties.getName(), false); + } +} diff --git a/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppTests.java b/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppTests.java new file mode 100644 index 000000000..fc4a1538b --- /dev/null +++ b/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppTests.java @@ -0,0 +1,64 @@ +package io.dapr.springboot.examples.consumer; + +import io.dapr.spring.messaging.DaprMessagingTemplate; +import io.dapr.springboot.DaprAutoConfiguration; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.io.IOException; +import java.time.Duration; + +import static io.restassured.RestAssured.given; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.CoreMatchers.equalTo; + + +@SpringBootTest(classes = {TestConsumerApplication.class, DaprTestContainersConfig.class, + ConsumerAppTestConfiguration.class, DaprAutoConfiguration.class}, + webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +class ConsumerAppTests { + + @Autowired + private DaprMessagingTemplate messagingTemplate; + + @Autowired + private SubscriberRestController subscriberRestController; + + @BeforeAll + public static void setup() { + org.testcontainers.Testcontainers.exposeHostPorts(8081); + } + + @BeforeEach + void setUp() { + RestAssured.baseURI = "http://localhost:" + 8081; + } + + + @Test + void testMessageConsumer() throws InterruptedException, IOException { + + messagingTemplate.send("topic", new Order("abc-123", "the mars volta LP", 1)); + + + given() + .contentType(ContentType.JSON) + .when() + .get("/events") + .then() + .statusCode(200); + + + await() + .atMost(Duration.ofSeconds(5)) + .until(subscriberRestController.getAllEvents()::size, equalTo(1)); + + + } + +} diff --git a/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/DaprTestContainersConfig.java b/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/DaprTestContainersConfig.java new file mode 100644 index 000000000..7eaa13a7b --- /dev/null +++ b/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/DaprTestContainersConfig.java @@ -0,0 +1,78 @@ +package io.dapr.springboot.examples.consumer; + +import io.dapr.testcontainers.Component; +import io.dapr.testcontainers.DaprContainer; +import io.dapr.testcontainers.DaprLogLevel; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.context.annotation.Bean; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.RabbitMQContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@TestConfiguration(proxyBeanMethods = false) +public class DaprTestContainersConfig { + + @Bean + public Network getDaprNetwork() { + Network defaultDaprNetwork = new Network() { + @Override + public String getId() { + return "dapr-network"; + } + + @Override + public void close() { + + } + + @Override + public Statement apply(Statement base, Description description) { + return null; + } + }; + + List networks = DockerClientFactory.instance().client().listNetworksCmd() + .withNameFilter("dapr-network").exec(); + if (networks.isEmpty()) { + Network.builder().createNetworkCmdModifier(cmd -> cmd.withName("dapr-network")).build().getId(); + return defaultDaprNetwork; + } else { + return defaultDaprNetwork; + } + } + + @Bean + public RabbitMQContainer rabbitMQContainer(Network daprNetwork) { + return new RabbitMQContainer(DockerImageName.parse("rabbitmq:3.7.25-management-alpine")) + .withExposedPorts(5672).withNetworkAliases("rabbitmq").withReuse(true).withNetwork(daprNetwork); + + } + + @Bean + @ServiceConnection + public DaprContainer daprContainer(Network daprNetwork, RabbitMQContainer rabbitMQContainer) { + + Map rabbitMqProperties = new HashMap<>(); + rabbitMqProperties.put("connectionString", "amqp://guest:guest@rabbitmq:5672"); + rabbitMqProperties.put("user", "guest"); + rabbitMqProperties.put("password", "guest"); + + return new DaprContainer("daprio/daprd:1.14.4").withAppName("consumer-app") + .withNetwork(daprNetwork).withComponent(new Component("pubsub", + "pubsub.rabbitmq", "v1", rabbitMqProperties)) + .withDaprLogLevel(DaprLogLevel.DEBUG) + .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) + .withAppPort(8081).withAppChannelAddress("host.testcontainers.internal") + .withReusablePlacement(true).dependsOn(rabbitMQContainer); + } + + +} diff --git a/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/TestConsumerApplication.java b/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/TestConsumerApplication.java new file mode 100644 index 000000000..d616b3b71 --- /dev/null +++ b/spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/TestConsumerApplication.java @@ -0,0 +1,19 @@ +package io.dapr.springboot.examples.consumer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + + +@SpringBootApplication +public class TestConsumerApplication { + + public static void main(String[] args) { + org.testcontainers.Testcontainers.exposeHostPorts(8081); + SpringApplication + .from(ConsumerApplication::main) + .with(DaprTestContainersConfig.class) + .run(args); + } + + +} diff --git a/spring-boot-examples/consumer-app/src/test/resources/application.properties b/spring-boot-examples/consumer-app/src/test/resources/application.properties new file mode 100644 index 000000000..212a88a83 --- /dev/null +++ b/spring-boot-examples/consumer-app/src/test/resources/application.properties @@ -0,0 +1,2 @@ +dapr.pubsub.name=pubsub +server.port=8081 \ No newline at end of file diff --git a/spring-boot-examples/pom.xml b/spring-boot-examples/pom.xml index ef0c2d4d3..a7c685935 100644 --- a/spring-boot-examples/pom.xml +++ b/spring-boot-examples/pom.xml @@ -15,6 +15,7 @@ producer-app + consumer-app \ No newline at end of file