mirror of https://github.com/dapr/java-sdk.git
Adding producer and consumer app examples for Spring Boot integration (#1208)
* adding spring boot producer Signed-off-by: salaboy <Salaboy@gmail.com> * adding consumer app Signed-off-by: salaboy <Salaboy@gmail.com> * increasing wait for events to popup Signed-off-by: salaboy <Salaboy@gmail.com> * adding readme and examples Signed-off-by: salaboy <Salaboy@gmail.com> * aligning tests for examples Signed-off-by: salaboy <Salaboy@gmail.com> * increasing time out Signed-off-by: salaboy <Salaboy@gmail.com> * adding health check from the sidecar Signed-off-by: salaboy <Salaboy@gmail.com> * feat: Adding basic HTTPEndpoint configuration support in testcontainers module (#1210) * feat: Adding basic HTTPEndpoint configuration support in testcontainers module Signed-off-by: Laurent Broudoux <laurent.broudoux@gmail.com> * feat: #1209 Adding test for HTTPEndpoint in testcontainers module Signed-off-by: Laurent Broudoux <laurent.broudoux@gmail.com> --------- Signed-off-by: Laurent Broudoux <laurent.broudoux@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * updating example Signed-off-by: salaboy <Salaboy@gmail.com> * fixing example Signed-off-by: salaboy <Salaboy@gmail.com> * Add app health check support to Dapr Testcontainer (#1213) * Add app health check support to Dapr Testcontainer Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Some minor cleanup Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Move waiting to beforeEach, it looks more natural Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> --------- Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com> Signed-off-by: salaboy <Salaboy@gmail.com> * commenting reuse Signed-off-by: salaboy <Salaboy@gmail.com> * adding how to run on Kubernetes, unfortunately we need to create containers Signed-off-by: salaboy <Salaboy@gmail.com> * removing subscription and fixing scopes Signed-off-by: salaboy <Salaboy@gmail.com> * adding license headers and logger Signed-off-by: salaboy <Salaboy@gmail.com> * updating logs Signed-off-by: salaboy <Salaboy@gmail.com> * updating READMEs and update_sdk_version for new module Signed-off-by: salaboy <Salaboy@gmail.com> * removing old line Signed-off-by: salaboy <Salaboy@gmail.com> * removing sleeps, using Wait Signed-off-by: salaboy <Salaboy@gmail.com> * updating Kubernetes tutorial to use local registry with KIND, and provide steps to create containers with spring boot Signed-off-by: salaboy <Salaboy@gmail.com> * adding new lines and formatting Signed-off-by: salaboy <Salaboy@gmail.com> * updating reuse and removing comments Signed-off-by: salaboy <Salaboy@gmail.com> * fixing reuse Signed-off-by: salaboy <Salaboy@gmail.com> * removing line break Signed-off-by: salaboy <Salaboy@gmail.com> * fixing custom line breaks Signed-off-by: salaboy <Salaboy@gmail.com> * fixing xml indent to 2 spaces Signed-off-by: salaboy <Salaboy@gmail.com> * Update spring-boot-examples/README.md Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Update spring-boot-examples/README.md Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Update spring-boot-examples/README.md Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Update spring-boot-examples/README.md Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Update spring-boot-examples/README.md Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Update spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppTests.java Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Update spring-boot-examples/producer-app/src/test/java/io/dapr/springboot/examples/producer/TestSubscriberRestController.java Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * adding license header to missing files Signed-off-by: salaboy <Salaboy@gmail.com> * adding automated testing for spring boot example Signed-off-by: salaboy <Salaboy@gmail.com> * adding sb examples to the validation pipeline Signed-off-by: salaboy <Salaboy@gmail.com> * updating timeouts Signed-off-by: salaboy <Salaboy@gmail.com> * updating return codes Signed-off-by: salaboy <Salaboy@gmail.com> --------- Signed-off-by: salaboy <Salaboy@gmail.com> Signed-off-by: Laurent Broudoux <laurent.broudoux@gmail.com> Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Signed-off-by: Artur Souza <artursouza.ms@outlook.com> Co-authored-by: Laurent Broudoux <laurent.broudoux@gmail.com> Co-authored-by: artur-ciocanu <artur.ciocanu@gmail.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Co-authored-by: Cassie Coyle <cassie@diagrid.io> Co-authored-by: Artur Souza <artursouza.ms@outlook.com>
This commit is contained in:
parent
59abd5dbcc
commit
5dbeafc24a
|
|
@ -27,4 +27,7 @@ mvn versions:set -DnewVersion=$DAPR_JAVA_SDK_ALPHA_VERSION -f testcontainers-dap
|
|||
# dapr-spring
|
||||
mvn versions:set -DnewVersion=$DAPR_JAVA_SDK_ALPHA_VERSION -f dapr-spring/pom.xml
|
||||
|
||||
# spring-boot-examples
|
||||
mvn versions:set -DnewVersion=$DAPR_JAVA_SDK_ALPHA_VERSION -f spring-boot-examples/pom.xml
|
||||
|
||||
git clean -f
|
||||
|
|
|
|||
|
|
@ -164,3 +164,7 @@ jobs:
|
|||
working-directory: ./examples
|
||||
run: |
|
||||
mm.py ./src/main/java/io/dapr/examples/pubsub/stream/README.md
|
||||
- name: Validate Spring Boot examples
|
||||
working-directory: ./spring-boot-examples
|
||||
run: |
|
||||
mm.py README.md
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ This is the Dapr SDK for Java, including the following features:
|
|||
* Binding
|
||||
* State Store
|
||||
* Actors
|
||||
* Workflows
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
|
@ -112,6 +113,13 @@ Try the following examples to learn more about Dapr's Java SDK:
|
|||
* [Exception handling](./examples/src/main/java/io/dapr/examples/exception)
|
||||
* [Unit testing](./examples/src/main/java/io/dapr/examples/unittesting)
|
||||
|
||||
### Running Spring Boot examples
|
||||
|
||||
The Spring Boot integration for Dapr use [Testcontainers](https://testcontainers.com) to set up a local environment development flow that doesn't
|
||||
require the use of the `dapr` CLI and it integrates with the Spring Boot programming model.
|
||||
|
||||
You can find a [step-by-step tutorial showing this integration here](./spring-boot-examples/README.md).
|
||||
|
||||
### API Documentation
|
||||
|
||||
Please, refer to our [Javadoc](https://dapr.github.io/java-sdk/) website.
|
||||
|
|
|
|||
1
pom.xml
1
pom.xml
|
|
@ -337,6 +337,7 @@
|
|||
<module>sdk-springboot</module>
|
||||
<module>dapr-spring</module>
|
||||
<module>examples</module>
|
||||
<module>spring-boot-examples</module>
|
||||
<!-- We are following test containers artifact convention on purpose, don't rename -->
|
||||
<module>testcontainers-dapr</module>
|
||||
</modules>
|
||||
|
|
|
|||
|
|
@ -63,9 +63,10 @@ public class DaprContainerIT {
|
|||
|
||||
@Container
|
||||
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(IMAGE_TAG)
|
||||
.withAppName("dapr-app")
|
||||
.withAppPort(8081)
|
||||
.withAppChannelAddress("host.testcontainers.internal");
|
||||
.withAppName("dapr-app")
|
||||
.withAppPort(8081)
|
||||
.withAppHealthCheckPath("/actuator/health")
|
||||
.withAppChannelAddress("host.testcontainers.internal");
|
||||
|
||||
/**
|
||||
* Sets the Dapr properties for the test.
|
||||
|
|
@ -77,18 +78,21 @@ public class DaprContainerIT {
|
|||
}
|
||||
|
||||
private void configStub() {
|
||||
stubFor(any(urlMatching("/actuator/health"))
|
||||
.willReturn(aResponse().withBody("[]").withStatus(200)));
|
||||
|
||||
stubFor(any(urlMatching("/dapr/subscribe"))
|
||||
.willReturn(aResponse().withBody("[]").withStatus(200)));
|
||||
.willReturn(aResponse().withBody("[]").withStatus(200)));
|
||||
|
||||
stubFor(get(urlMatching("/dapr/config"))
|
||||
.willReturn(aResponse().withBody("[]").withStatus(200)));
|
||||
.willReturn(aResponse().withBody("[]").withStatus(200)));
|
||||
|
||||
stubFor(any(urlMatching("/([a-z1-9]*)"))
|
||||
.willReturn(aResponse().withBody("[]").withStatus(200)));
|
||||
.willReturn(aResponse().withBody("[]").withStatus(200)));
|
||||
|
||||
// create a stub
|
||||
stubFor(post(urlEqualTo("/events"))
|
||||
.willReturn(aResponse().withBody("event received!").withStatus(200)));
|
||||
.willReturn(aResponse().withBody("event received!").withStatus(200)));
|
||||
|
||||
configureFor("localhost", 8081);
|
||||
}
|
||||
|
|
@ -96,13 +100,13 @@ public class DaprContainerIT {
|
|||
@Test
|
||||
public void testDaprContainerDefaults() {
|
||||
assertEquals(2,
|
||||
DAPR_CONTAINER.getComponents().size(),
|
||||
"The pubsub and kvstore component should be configured by default"
|
||||
DAPR_CONTAINER.getComponents().size(),
|
||||
"The pubsub and kvstore component should be configured by default"
|
||||
);
|
||||
assertEquals(
|
||||
1,
|
||||
DAPR_CONTAINER.getSubscriptions().size(),
|
||||
"A subscription should be configured by default if none is provided"
|
||||
1,
|
||||
DAPR_CONTAINER.getSubscriptions().size(),
|
||||
"A subscription should be configured by default if none is provided"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -129,10 +133,10 @@ public class DaprContainerIT {
|
|||
Thread.sleep(1000);
|
||||
|
||||
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||
.build();
|
||||
.build();
|
||||
Request request = new Request.Builder()
|
||||
.url(DAPR_CONTAINER.getHttpEndpoint() + "/v1.0/metadata")
|
||||
.build();
|
||||
.url(DAPR_CONTAINER.getHttpEndpoint() + "/v1.0/metadata")
|
||||
.build();
|
||||
|
||||
try (Response response = okHttpClient.newCall(request).execute()) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
|
|
@ -158,7 +162,7 @@ public class DaprContainerIT {
|
|||
|
||||
private DaprClientBuilder createDaprClientBuilder() {
|
||||
return new DaprClientBuilder()
|
||||
.withPropertyOverride(Properties.HTTP_ENDPOINT, DAPR_CONTAINER.getHttpEndpoint())
|
||||
.withPropertyOverride(Properties.GRPC_ENDPOINT, DAPR_CONTAINER.getGrpcEndpoint());
|
||||
.withPropertyOverride(Properties.HTTP_ENDPOINT, DAPR_CONTAINER.getHttpEndpoint())
|
||||
.withPropertyOverride(Properties.GRPC_ENDPOINT, DAPR_CONTAINER.getGrpcEndpoint());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
# Dapr Spring Boot and Testcontainers integration Example
|
||||
|
||||
This example consists of two applications:
|
||||
- Producer App:
|
||||
- Publish messages using a Spring Messaging approach
|
||||
- Store and retrieve information using Spring Data CrudRepository
|
||||
- Implements a Workflow with Dapr Workflows
|
||||
- Consumer App:
|
||||
- Subscribe to messages
|
||||
|
||||
## Running these examples from source code
|
||||
|
||||
To run these examples you will need:
|
||||
- Java SDK
|
||||
- Maven
|
||||
- Docker or a container runtime such as Podman
|
||||
|
||||
From the `spring-boot-examples/` directory you can start each service using the test configuration that uses
|
||||
[Testcontainers](https://testcontainers.com) to boostrap [Dapr](https://dapr.io) by running the following command:
|
||||
|
||||
<!-- STEP
|
||||
name: Run Demo Producer Service
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- 'Started ProducerApplication'
|
||||
background: true
|
||||
expected_return_code: 143
|
||||
sleep: 30
|
||||
timeout_seconds: 45
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
cd producer-app/
|
||||
../../mvnw -Dspring-boot.run.arguments="--reuse=true" spring-boot:test-run
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
This will start the `producer-app` with Dapr services and the infrastructure needed by the application to run,
|
||||
in this case RabbitMQ and PostgreSQL. The `producer-app` starts on port `8080` by default.
|
||||
|
||||
The `-Dspring-boot.run.arguments="--reuse=true"` flag helps the application to connect to an existing shared
|
||||
infrastructure if it already exists. For development purposes, and to connect both applications we will set the flag
|
||||
in both. For more details check the `DaprTestContainersConfig.java` classes in both, the `producer-app` and the `consumer-app`.
|
||||
|
||||
Then run in a different terminal:
|
||||
|
||||
<!-- STEP
|
||||
name: Run Demo Consumer Service
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- 'Started ConsumerApplication'
|
||||
background: true
|
||||
expected_return_code: 143
|
||||
sleep: 30
|
||||
timeout_seconds: 45
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
cd consumer-app/
|
||||
../../mvnw -Dspring-boot.run.arguments="--reuse=true" spring-boot:test-run
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
The `consumer-app` starts in port `8081` by default.
|
||||
|
||||
## Interacting with the applications
|
||||
|
||||
Now that both applications are up you can place an order by sending a POST request to `:8080/orders/`
|
||||
You can use `curl` to send a POST request to the `producer-app`:
|
||||
|
||||
|
||||
<!-- STEP
|
||||
name: Send POST request to Producer App
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- 'Order Stored and Event Published'
|
||||
background: true
|
||||
sleep: 1
|
||||
timeout_seconds: 2
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
curl -X POST localhost:8080/orders -H 'Content-Type: application/json' -d '{ "item": "the mars volta EP", "amount": 1 }'
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
|
||||
If you check the `producer-app` logs you should see the following lines:
|
||||
|
||||
```bash
|
||||
...
|
||||
Storing Order: Order{id='null', item='the mars volta EP', amount=1}
|
||||
Publishing Order Event: Order{id='d4f8ea15-b774-441e-bcd2-7a4208a80bec', item='the mars volta EP', amount=1}
|
||||
|
||||
```
|
||||
|
||||
If you check the `consumer-app` logs you should see the following lines, showing that the message
|
||||
published by the `producer-app` was correctly consumed by the `consumer-app`:
|
||||
|
||||
```bash
|
||||
Order Event Received: Order{id='d4f8ea15-b774-441e-bcd2-7a4208a80bec', item='the mars volta EP', amount=1}
|
||||
```
|
||||
|
||||
Next, you can create a new customer to trigger the customer's tracking workflow:
|
||||
|
||||
<!-- STEP
|
||||
name: Start Customer Workflow
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- 'New Workflow Instance created for Customer'
|
||||
background: true
|
||||
sleep: 1
|
||||
timeout_seconds: 2
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
curl -X POST localhost:8080/customers -H 'Content-Type: application/json' -d '{ "customerName": "salaboy" }'
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
|
||||
A new Workflow Instance was created to track the customers interactions. Now, the workflow instance
|
||||
is waiting for the customer to request a follow-up.
|
||||
|
||||
You should see in the `producer-app` logs:
|
||||
|
||||
```bash
|
||||
Workflow instance <Workflow Instance Id> started
|
||||
Let's register the customer: salaboy
|
||||
Customer: salaboy registered.
|
||||
Let's wait for the customer: salaboy to request a follow up.
|
||||
```
|
||||
|
||||
Send an event simulating the customer request for a follow-up:
|
||||
|
||||
<!-- STEP
|
||||
name: Emit Customer Follow-up event
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- 'Customer Follow-up requested'
|
||||
background: true
|
||||
sleep: 1
|
||||
timeout_seconds: 5
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
curl -X POST localhost:8080/customers/followup -H 'Content-Type: application/json' -d '{ "customerName": "salaboy" }'
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
In the `producer-app` logs you should see that the workflow instance id moved forward to the Customer Follow Up activity:
|
||||
|
||||
```bash
|
||||
Customer follow-up requested: salaboy
|
||||
Let's book a follow up for the customer: salaboy
|
||||
Customer: salaboy follow-up done.
|
||||
Congratulations the customer: salaboy is happy!
|
||||
```
|
||||
|
||||
## Running on Kubernetes
|
||||
|
||||
You can run the same example on a Kubernetes cluster. [Check the Kubernetes tutorial here](kubernetes/README.md).
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>spring-boot-examples</artifactId>
|
||||
<version>0.14.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>consumer-app</artifactId>
|
||||
<name>consumer-app</name>
|
||||
<description>Spring Boot, Testcontainers and Dapr Integration Examples :: Consumer App</description>
|
||||
|
||||
<properties>
|
||||
<springboot.version>3.2.6</springboot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter</artifactId>
|
||||
<version>${dapr-java-sdk.alpha-version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter-test</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>1.20.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>rabbitmq</artifactId>
|
||||
<version>1.20.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>kafka</artifactId>
|
||||
<version>1.20.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Order{" + "id='" + id + '\'' + ", item='" + item + '\'' + ", amount=" + amount + '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.consumer;
|
||||
|
||||
import io.dapr.Topic;
|
||||
import io.dapr.client.domain.CloudEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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 final Logger logger = LoggerFactory.getLogger(SubscriberRestController.class);
|
||||
|
||||
private List<CloudEvent> events = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Subscribe to cloud events.
|
||||
* @param cloudEvent payload
|
||||
*/
|
||||
@PostMapping("subscribe")
|
||||
@Topic(pubsubName = "pubsub", name = "topic")
|
||||
public void subscribe(@RequestBody CloudEvent<Order> cloudEvent) {
|
||||
logger.info("Order Event Received: " + cloudEvent.getData());
|
||||
events.add(cloudEvent);
|
||||
}
|
||||
|
||||
@GetMapping("events")
|
||||
public List<CloudEvent> getAllEvents() {
|
||||
return events;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
dapr.pubsub.name=pubsub
|
||||
spring.application.name=consumer-app
|
||||
server.port=8081
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.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<Order> messagingTemplate(DaprClient daprClient,
|
||||
DaprPubSubProperties daprPubSubProperties) {
|
||||
return new DaprMessagingTemplate<>(daprClient, daprPubSubProperties.getName(), false);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.consumer;
|
||||
|
||||
import io.dapr.client.DaprClient;
|
||||
import io.dapr.spring.messaging.DaprMessagingTemplate;
|
||||
import io.dapr.springboot.DaprAutoConfiguration;
|
||||
import io.dapr.testcontainers.DaprContainer;
|
||||
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 org.testcontainers.containers.wait.strategy.Wait;
|
||||
|
||||
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 {
|
||||
|
||||
private static final String SUBSCRIPTION_MESSAGE_PATTERN = ".*app is subscribed to the following topics.*";
|
||||
|
||||
@Autowired
|
||||
private DaprMessagingTemplate<Order> messagingTemplate;
|
||||
|
||||
@Autowired
|
||||
private SubscriberRestController subscriberRestController;
|
||||
|
||||
@Autowired
|
||||
private DaprClient daprClient;
|
||||
|
||||
@Autowired
|
||||
private DaprContainer daprContainer;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
org.testcontainers.Testcontainers.exposeHostPorts(8081);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
RestAssured.baseURI = "http://localhost:" + 8081;
|
||||
Wait.forLogMessage(SUBSCRIPTION_MESSAGE_PATTERN, 1).waitUntilReady(daprContainer);
|
||||
}
|
||||
|
||||
|
||||
@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(10))
|
||||
.until(subscriberRestController.getAllEvents()::size, equalTo(1));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.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.springframework.core.env.Environment;
|
||||
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<com.github.dockerjava.api.model.Network> 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, Environment env) {
|
||||
boolean reuse = env.getProperty("reuse", Boolean.class, false);
|
||||
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, Environment env) {
|
||||
boolean reuse = env.getProperty("reuse", Boolean.class, false);
|
||||
Map<String, String> 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.INFO)
|
||||
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
|
||||
.withAppPort(8081).withAppChannelAddress("host.testcontainers.internal")
|
||||
.withReusablePlacement(reuse)
|
||||
.withAppHealthCheckPath("/actuator/health")
|
||||
.dependsOn(rabbitMQContainer);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.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) {
|
||||
SpringApplication.from(ConsumerApplication::main)
|
||||
.with(DaprTestContainersConfig.class)
|
||||
.run(args);
|
||||
org.testcontainers.Testcontainers.exposeHostPorts(8081);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
dapr.pubsub.name=pubsub
|
||||
server.port=8081
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
# Running this example on Kubernetes
|
||||
|
||||
To run this example on Kubernetes, you can use any Kubernetes distribution.
|
||||
We install Dapr on a Kubernetes cluster and then we will deploy both the `producer-app` and `consumer-app`.
|
||||
|
||||
## Creating a cluster and installing Dapr
|
||||
|
||||
If you don't have any Kubernetes cluster you can use Kubernetes KIND to create a local cluster. We will create a cluster
|
||||
with a local container registry, so we can push our container images to it. This is covered in the
|
||||
[KIND documentation here](https://kind.sigs.k8s.io/docs/user/local-registry/).
|
||||
|
||||
```bash
|
||||
./kind-with-registry.sh
|
||||
```
|
||||
|
||||
Once you have the cluster up and running you can install Dapr:
|
||||
|
||||
```bash
|
||||
helm repo add dapr https://dapr.github.io/helm-charts/
|
||||
helm repo update
|
||||
helm upgrade --install dapr dapr/dapr \
|
||||
--version=1.14.4 \
|
||||
--namespace dapr-system \
|
||||
--create-namespace \
|
||||
--wait
|
||||
```
|
||||
|
||||
## Creating containers using Spring Boot and pushing to local registry
|
||||
|
||||
Now that we have our cluster set up with a local container registry, we need to build our `producer-app` and `consumer-app` containers.
|
||||
For this we will use Spring Boot build it functions to create container images using [Buildpacks](https://buildpacks.io):
|
||||
|
||||
From inside the `spring-boot-examples/producer-app` directory you can run the following command to create a container:
|
||||
```bash
|
||||
mvn spring-boot:build-image
|
||||
```
|
||||
|
||||
Once we have the container image created, we need to tag and push to the local registry, so the image can be used from our local cluster.
|
||||
Alternatively, you can push the images to a public registry and update the Kubernetes manifests accordingly.
|
||||
|
||||
```bash
|
||||
docker tag producer-app:0.14.0-SNAPSHOT localhost:5001/sb-producer-app
|
||||
docker push localhost:5001/sb-producer-app
|
||||
```
|
||||
|
||||
From inside the `spring-boot-examples/consumer-app` directory you can run the following command to create a container:
|
||||
```bash
|
||||
mvn spring-boot:build-image
|
||||
```
|
||||
|
||||
Once we have the container image created, we need to tag and push to the local registry, so the image can be used from our local cluster.
|
||||
Alternatively, you can push the images to a public registry and update the Kubernetes manifests accordingly.
|
||||
|
||||
```bash
|
||||
docker tag consumer-app:0.14.0-SNAPSHOT localhost:5001/sb-consumer-app
|
||||
docker push localhost:5001/sb-consumer-app
|
||||
```
|
||||
|
||||
Now we are ready to install our application into the cluster.
|
||||
|
||||
## Installing and interacting with the application
|
||||
|
||||
Now that we have a running Kubernetes cluster, we need to first install the components needed by the application.
|
||||
In this case RabbitMQ and PostgreSQL. We will use Helm to do so:
|
||||
|
||||
Let's start with RabbitMQ:
|
||||
```bash
|
||||
helm install rabbitmq oci://registry-1.docker.io/bitnamicharts/rabbitmq --set auth.username=guest --set auth.password=guest --set auth.erlangCookie=ABC
|
||||
```
|
||||
|
||||
Then PostgreSQL:
|
||||
```bash
|
||||
helm install postgresql oci://registry-1.docker.io/bitnamicharts/postgresql --set global.postgresql.auth.database=dapr --set global.postgresql.auth.postgresPassword=password
|
||||
```
|
||||
|
||||
Once we have these components up and running we can install the application by running from inside
|
||||
the `spring-boot-examples/kubernetes/` directory:
|
||||
|
||||
```bash
|
||||
kubectl apply -f .
|
||||
```
|
||||
|
||||
Next you need to use `kubectl port-forward` to be able to send requests to the applications.
|
||||
|
||||
```bash
|
||||
kubectl port-forward svc/producer-app 8080:8080
|
||||
```
|
||||
|
||||
In a different terminals you can check the logs of the `producer-app` and `consumer-app`:
|
||||
|
||||
```bash
|
||||
kubectl logs -f producer-app-<POD_ID>
|
||||
```
|
||||
and
|
||||
|
||||
```bash
|
||||
kubectl logs -f consumer-app-<POD_ID>
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: consumer-app
|
||||
name: consumer-app
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- name: "consumer-app"
|
||||
port: 8081
|
||||
targetPort: 8081
|
||||
nodePort: 31001
|
||||
selector:
|
||||
app: consumer-app
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: consumer-app
|
||||
name: consumer-app
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: consumer-app
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
dapr.io/app-id: consumer-app
|
||||
dapr.io/app-port: "8081"
|
||||
dapr.io/enabled: "true"
|
||||
labels:
|
||||
app: consumer-app
|
||||
spec:
|
||||
containers:
|
||||
- image: localhost:5001/sb-consumer-app
|
||||
name: consumer-app
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
name: consumer-app
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/sh
|
||||
set -o errexit
|
||||
|
||||
# 1. Create registry container unless it already exists
|
||||
reg_name='kind-registry'
|
||||
reg_port='5001'
|
||||
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
|
||||
docker run \
|
||||
-d --restart=always -p "127.0.0.1:${reg_port}:5000" --network bridge --name "${reg_name}" \
|
||||
registry:2
|
||||
fi
|
||||
|
||||
# 2. Create kind cluster with containerd registry config dir enabled
|
||||
# TODO: kind will eventually enable this by default and this patch will
|
||||
# be unnecessary.
|
||||
#
|
||||
# See:
|
||||
# https://github.com/kubernetes-sigs/kind/issues/2875
|
||||
# https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration
|
||||
# See: https://github.com/containerd/containerd/blob/main/docs/hosts.md
|
||||
cat <<EOF | kind create cluster --config=-
|
||||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
containerdConfigPatches:
|
||||
- |-
|
||||
[plugins."io.containerd.grpc.v1.cri".registry]
|
||||
config_path = "/etc/containerd/certs.d"
|
||||
EOF
|
||||
|
||||
# 3. Add the registry config to the nodes
|
||||
#
|
||||
# This is necessary because localhost resolves to loopback addresses that are
|
||||
# network-namespace local.
|
||||
# In other words: localhost in the container is not localhost on the host.
|
||||
#
|
||||
# We want a consistent name that works from both ends, so we tell containerd to
|
||||
# alias localhost:${reg_port} to the registry container when pulling images
|
||||
REGISTRY_DIR="/etc/containerd/certs.d/localhost:${reg_port}"
|
||||
for node in $(kind get nodes); do
|
||||
docker exec "${node}" mkdir -p "${REGISTRY_DIR}"
|
||||
cat <<EOF | docker exec -i "${node}" cp /dev/stdin "${REGISTRY_DIR}/hosts.toml"
|
||||
[host."http://${reg_name}:5000"]
|
||||
EOF
|
||||
done
|
||||
|
||||
# 4. Connect the registry to the cluster network if not already connected
|
||||
# This allows kind to bootstrap the network but ensures they're on the same network
|
||||
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
|
||||
docker network connect "kind" "${reg_name}"
|
||||
fi
|
||||
|
||||
# 5. Document the local registry
|
||||
# https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: local-registry-hosting
|
||||
namespace: kube-public
|
||||
data:
|
||||
localRegistryHosting.v1: |
|
||||
host: "localhost:${reg_port}"
|
||||
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
|
||||
EOF
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: kvbinding
|
||||
spec:
|
||||
type: bindings.postgresql
|
||||
version: v1
|
||||
metadata:
|
||||
- name: connectionString
|
||||
value: host=postgresql.default.svc.cluster.local user=postgres password=password port=5432 connect_timeout=10
|
||||
database=dapr
|
||||
scopes:
|
||||
- producer-app
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: kvstore
|
||||
spec:
|
||||
type: state.postgresql
|
||||
version: v1
|
||||
metadata:
|
||||
- name: keyPrefix
|
||||
value: name
|
||||
- name: actorStateStore
|
||||
value: 'true'
|
||||
- name: connectionString
|
||||
value: host=postgresql.default.svc.cluster.local user=postgres password=password port=5432 connect_timeout=10
|
||||
database=dapr
|
||||
scopes:
|
||||
- producer-app
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: producer-app
|
||||
name: producer-app
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- name: "producer-app"
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
nodePort: 31000
|
||||
selector:
|
||||
app: producer-app
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: producer-app
|
||||
name: producer-app
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: producer-app
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
dapr.io/app-id: producer-app
|
||||
dapr.io/app-port: "8080"
|
||||
dapr.io/enabled: "true"
|
||||
labels:
|
||||
app: producer-app
|
||||
spec:
|
||||
containers:
|
||||
- image: localhost:5001/sb-producer-app
|
||||
name: producer-app
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: producer-app
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: pubsub
|
||||
spec:
|
||||
type: pubsub.rabbitmq
|
||||
version: v1
|
||||
metadata:
|
||||
- name: connectionString
|
||||
value: amqp://guest:guest@rabbitmq.default.svc.cluster.local:5672
|
||||
- name: user
|
||||
value: guest
|
||||
- name: password
|
||||
value: guest
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.14.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-boot-examples</artifactId>
|
||||
<version>0.14.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>producer-app</module>
|
||||
<module>consumer-app</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>spring-boot-examples</artifactId>
|
||||
<version>0.14.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>producer-app</artifactId>
|
||||
<name>producer-app</name>
|
||||
<description>Spring Boot, Testcontainers and Dapr Integration Examples :: Producer App</description>
|
||||
|
||||
<properties>
|
||||
<springboot.version>3.2.6</springboot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter-test</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>1.20.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>rabbitmq</artifactId>
|
||||
<version>1.20.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
public class Customer {
|
||||
private String customerName;
|
||||
private String workflowId;
|
||||
private boolean inCustomerDB = false;
|
||||
private boolean followUp = false;
|
||||
|
||||
public boolean isFollowUp() {
|
||||
return followUp;
|
||||
}
|
||||
|
||||
public void setFollowUp(boolean followUp) {
|
||||
this.followUp = followUp;
|
||||
}
|
||||
|
||||
public boolean isInCustomerDB() {
|
||||
return inCustomerDB;
|
||||
}
|
||||
|
||||
public void setInCustomerDB(boolean inCustomerDB) {
|
||||
this.inCustomerDB = inCustomerDB;
|
||||
}
|
||||
|
||||
public String getWorkflowId() {
|
||||
return workflowId;
|
||||
}
|
||||
|
||||
public void setWorkflowId(String workflowId) {
|
||||
this.workflowId = workflowId;
|
||||
}
|
||||
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
}
|
||||
|
||||
public void setCustomerName(String customerName) {
|
||||
this.customerName = customerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Customer [customerName=" + customerName + ", workflowId=" + workflowId + ", inCustomerDB="
|
||||
+ inCustomerDB + ", followUp=" + followUp + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class CustomerStore {
|
||||
private Map<String, Customer> customers = new HashMap<>();
|
||||
|
||||
public void addCustomer(Customer customer) {
|
||||
customers.put(customer.getCustomerName(), customer);
|
||||
}
|
||||
|
||||
public Customer getCustomer(String customerName) {
|
||||
return customers.get(customerName);
|
||||
}
|
||||
|
||||
public Collection<Customer> getCustomers() {
|
||||
return customers.values();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import io.dapr.spring.workflows.config.EnableDaprWorkflows;
|
||||
import io.dapr.springboot.examples.producer.workflow.CustomerWorkflow;
|
||||
import io.dapr.workflows.client.DaprWorkflowClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@EnableDaprWorkflows
|
||||
public class CustomersRestController {
|
||||
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(CustomersRestController.class);
|
||||
|
||||
@Autowired
|
||||
private DaprWorkflowClient daprWorkflowClient;
|
||||
|
||||
@Autowired
|
||||
private CustomerStore customerStore;
|
||||
|
||||
@GetMapping("/")
|
||||
public String root() {
|
||||
return "OK";
|
||||
}
|
||||
|
||||
private Map<String, String> customersWorkflows = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Track customer endpoint.
|
||||
*
|
||||
* @param customer provided customer to track
|
||||
* @return confirmation that the workflow instance was created for a given customer
|
||||
*/
|
||||
@PostMapping("/customers")
|
||||
public String trackCustomer(@RequestBody Customer customer) {
|
||||
String instanceId = daprWorkflowClient.scheduleNewWorkflow(CustomerWorkflow.class, customer);
|
||||
logger.info("Workflow instance " + instanceId + " started");
|
||||
customersWorkflows.put(customer.getCustomerName(), instanceId);
|
||||
return "New Workflow Instance created for Customer: " + customer.getCustomerName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request customer follow-up.
|
||||
* @param customer associated with a workflow instance
|
||||
* @return confirmation that the follow-up was requested
|
||||
*/
|
||||
@PostMapping("/customers/followup")
|
||||
public String customerNotification(@RequestBody Customer customer) {
|
||||
logger.info("Customer follow-up requested: " + customer.getCustomerName());
|
||||
String workflowIdForCustomer = customersWorkflows.get(customer.getCustomerName());
|
||||
if (workflowIdForCustomer == null || workflowIdForCustomer.isEmpty()) {
|
||||
return "There is no workflow associated with customer: " + customer.getCustomerName();
|
||||
} else {
|
||||
daprWorkflowClient.raiseEvent(workflowIdForCustomer, "CustomerReachOut", customer);
|
||||
return "Customer Follow-up requested";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Collection<Customer> getCustomers() {
|
||||
return customerStore.getCustomers();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
public class Order {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
private String item;
|
||||
private Integer amount;
|
||||
|
||||
public Order() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Order{" + "id='" + id + '\'' + ", item='" + item + '\'' + ", amount=" + amount + '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OrderRepository extends CrudRepository<Order, String> {
|
||||
|
||||
List<Order> findByItem(String item);
|
||||
|
||||
List<Order> findByAmount(Integer amount);
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import io.dapr.spring.data.repository.config.EnableDaprRepositories;
|
||||
import io.dapr.spring.messaging.DaprMessagingTemplate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@EnableDaprRepositories
|
||||
public class OrdersRestController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OrdersRestController.class);
|
||||
|
||||
@Autowired
|
||||
private OrderRepository repository;
|
||||
|
||||
@Autowired
|
||||
private DaprMessagingTemplate<Order> messagingTemplate;
|
||||
|
||||
/**
|
||||
* Store orders from customers.
|
||||
* @param order from the customer
|
||||
*
|
||||
* @return confirmation that the order was stored and the event published
|
||||
*/
|
||||
@PostMapping("/orders")
|
||||
public String storeOrder(@RequestBody Order order) {
|
||||
logger.info("Storing Order: " + order);
|
||||
repository.save(order);
|
||||
logger.info("Publishing Order Event: " + order);
|
||||
messagingTemplate.send("topic", order);
|
||||
return "Order Stored and Event Published";
|
||||
}
|
||||
|
||||
@GetMapping("/orders")
|
||||
public Iterable<Order> getAll() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
@GetMapping("/orders/byItem/")
|
||||
public Iterable<Order> getAllByItem(@RequestParam("item") String item) {
|
||||
return repository.findByItem(item);
|
||||
}
|
||||
|
||||
@GetMapping("/orders/byAmount/")
|
||||
public Iterable<Order> getAllByItem(@RequestParam("amount") Integer amount) {
|
||||
return repository.findByAmount(amount);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.dapr.client.DaprClient;
|
||||
import io.dapr.spring.boot.autoconfigure.pubsub.DaprPubSubProperties;
|
||||
import io.dapr.spring.boot.autoconfigure.statestore.DaprStateStoreProperties;
|
||||
import io.dapr.spring.data.DaprKeyValueAdapterResolver;
|
||||
import io.dapr.spring.data.DaprKeyValueTemplate;
|
||||
import io.dapr.spring.data.KeyValueAdapterResolver;
|
||||
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, DaprStateStoreProperties.class})
|
||||
public class ProducerAppConfiguration {
|
||||
@Bean
|
||||
public ObjectMapper mapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Produce a KeyValueAdapterResolver for Dapr.
|
||||
* @param daprClient dapr client
|
||||
* @param mapper object mapper
|
||||
* @param daprStatestoreProperties properties to configure state store
|
||||
* @return KeyValueAdapterResolver
|
||||
*/
|
||||
@Bean
|
||||
public KeyValueAdapterResolver keyValueAdapterResolver(DaprClient daprClient, ObjectMapper mapper,
|
||||
DaprStateStoreProperties daprStatestoreProperties) {
|
||||
String storeName = daprStatestoreProperties.getName();
|
||||
String bindingName = daprStatestoreProperties.getBinding();
|
||||
|
||||
return new DaprKeyValueAdapterResolver(daprClient, mapper, storeName, bindingName);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DaprKeyValueTemplate daprKeyValueTemplate(KeyValueAdapterResolver keyValueAdapterResolver) {
|
||||
return new DaprKeyValueTemplate(keyValueAdapterResolver);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DaprMessagingTemplate<Order> messagingTemplate(DaprClient daprClient,
|
||||
DaprPubSubProperties daprPubSubProperties) {
|
||||
return new DaprMessagingTemplate<>(daprClient, daprPubSubProperties.getName(), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
public class ProducerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ProducerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer.workflow;
|
||||
|
||||
import io.dapr.springboot.examples.producer.Customer;
|
||||
import io.dapr.springboot.examples.producer.CustomerStore;
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CustomerFollowupActivity implements WorkflowActivity {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(CustomerFollowupActivity.class);
|
||||
|
||||
private final CustomerStore customerStore;
|
||||
|
||||
public CustomerFollowupActivity(CustomerStore customerStore) {
|
||||
this.customerStore = customerStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run(WorkflowActivityContext ctx) {
|
||||
Customer customer = ctx.getInput(Customer.class);
|
||||
//Let's get the hydrate the real customer from the CustomerStore
|
||||
customer = customerStore.getCustomer(customer.getCustomerName());
|
||||
customer.setFollowUp(true);
|
||||
customerStore.addCustomer(customer);
|
||||
logger.info("Customer: " + customer.getCustomerName() + " follow-up done.");
|
||||
return customer;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer.workflow;
|
||||
|
||||
import io.dapr.springboot.examples.producer.Customer;
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Component
|
||||
public class CustomerWorkflow implements Workflow {
|
||||
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
String instanceId = ctx.getInstanceId();
|
||||
Customer customer = ctx.getInput(Customer.class);
|
||||
customer.setWorkflowId(instanceId);
|
||||
ctx.getLogger().info("Let's register the customer: " + customer.getCustomerName());
|
||||
ctx.callActivity(RegisterCustomerActivity.class.getName(), customer, Customer.class).await();
|
||||
ctx.getLogger().info("Let's wait for the customer: " + customer.getCustomerName() + " to request a follow up.");
|
||||
customer = ctx.waitForExternalEvent("CustomerReachOut", Duration.ofMinutes(5), Customer.class).await();
|
||||
ctx.getLogger().info("Let's book a follow up for the customer: " + customer.getCustomerName());
|
||||
customer = ctx.callActivity(CustomerFollowupActivity.class.getName(), customer, Customer.class).await();
|
||||
ctx.getLogger().info("Congratulations the customer: " + customer.getCustomerName() + " is happy!");
|
||||
ctx.complete(customer);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer.workflow;
|
||||
|
||||
|
||||
import io.dapr.springboot.examples.producer.Customer;
|
||||
import io.dapr.springboot.examples.producer.CustomerStore;
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class RegisterCustomerActivity implements WorkflowActivity {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RegisterCustomerActivity.class);
|
||||
private final CustomerStore customerStore;
|
||||
|
||||
public RegisterCustomerActivity(CustomerStore customerStore) {
|
||||
this.customerStore = customerStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run(WorkflowActivityContext ctx) {
|
||||
Customer customer = ctx.getInput(Customer.class);
|
||||
customer.setInCustomerDB(true);
|
||||
logger.info("Customer: " + customer.getCustomerName() + " registered.");
|
||||
customerStore.addCustomer(customer);
|
||||
return customer;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
spring.application.name=producer-app
|
||||
dapr.pubsub.name=pubsub
|
||||
dapr.statestore.name=kvstore
|
||||
dapr.statestore.binding=kvbinding
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import io.dapr.testcontainers.Component;
|
||||
import io.dapr.testcontainers.DaprContainer;
|
||||
import io.dapr.testcontainers.Subscription;
|
||||
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.springframework.core.env.Environment;
|
||||
import org.testcontainers.DockerClientFactory;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.containers.RabbitMQContainer;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@TestConfiguration(proxyBeanMethods = false)
|
||||
public class DaprTestContainersConfig {
|
||||
|
||||
static final String CONNECTION_STRING =
|
||||
"host=postgres user=postgres password=password port=5432 connect_timeout=10 database=dapr_db_repository";
|
||||
static final Map<String, String> STATE_STORE_PROPERTIES = createStateStoreProperties();
|
||||
|
||||
static final Map<String, String> BINDING_PROPERTIES = Collections.singletonMap("connectionString", CONNECTION_STRING);
|
||||
|
||||
|
||||
@Bean
|
||||
public Network getNetwork() {
|
||||
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<com.github.dockerjava.api.model.Network> 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, Environment env) {
|
||||
boolean reuse = env.getProperty("reuse", Boolean.class, false);
|
||||
return new RabbitMQContainer(DockerImageName.parse("rabbitmq:3.7.25-management-alpine"))
|
||||
.withExposedPorts(5672)
|
||||
.withNetworkAliases("rabbitmq")
|
||||
.withReuse(true)
|
||||
.withNetwork(daprNetwork);
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PostgreSQLContainer<?> postgreSQLContainer(Network daprNetwork) {
|
||||
return new PostgreSQLContainer<>("postgres:16-alpine")
|
||||
.withNetworkAliases("postgres")
|
||||
.withDatabaseName("dapr_db_repository")
|
||||
.withUsername("postgres")
|
||||
.withPassword("password")
|
||||
.withExposedPorts(5432)
|
||||
.withNetwork(daprNetwork);
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceConnection
|
||||
public DaprContainer daprContainer(Network daprNetwork, PostgreSQLContainer<?> postgreSQLContainer, RabbitMQContainer rabbitMQContainer) {
|
||||
|
||||
Map<String, String> 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("producer-app")
|
||||
.withNetwork(daprNetwork)
|
||||
.withComponent(new Component("kvstore", "state.postgresql", "v1", STATE_STORE_PROPERTIES))
|
||||
.withComponent(new Component("kvbinding", "bindings.postgresql", "v1", BINDING_PROPERTIES))
|
||||
.withComponent(new Component("pubsub", "pubsub.rabbitmq", "v1", rabbitMqProperties))
|
||||
.withSubscription(new Subscription("app", "pubsub", "topic", "/subscribe"))
|
||||
// .withDaprLogLevel(DaprLogLevel.DEBUG)
|
||||
// .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
|
||||
.withAppPort(8080)
|
||||
.withAppHealthCheckPath("/actuator/health")
|
||||
.withAppChannelAddress("host.testcontainers.internal")
|
||||
.dependsOn(rabbitMQContainer)
|
||||
.dependsOn(postgreSQLContainer);
|
||||
}
|
||||
|
||||
|
||||
private static Map<String, String> createStateStoreProperties() {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
||||
result.put("keyPrefix", "name");
|
||||
result.put("actorStateStore", String.valueOf(true));
|
||||
result.put("connectionString", CONNECTION_STRING);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import io.dapr.client.DaprClient;
|
||||
import io.dapr.springboot.DaprAutoConfiguration;
|
||||
import io.dapr.springboot.examples.producer.workflow.CustomerFollowupActivity;
|
||||
import io.dapr.springboot.examples.producer.workflow.CustomerWorkflow;
|
||||
import io.dapr.springboot.examples.producer.workflow.RegisterCustomerActivity;
|
||||
import io.dapr.testcontainers.DaprContainer;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
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 org.testcontainers.containers.wait.strategy.Wait;
|
||||
|
||||
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;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@SpringBootTest(classes = {TestProducerApplication.class, DaprTestContainersConfig.class,
|
||||
DaprAutoConfiguration.class, CustomerWorkflow.class, CustomerFollowupActivity.class,
|
||||
RegisterCustomerActivity.class, CustomerStore.class},
|
||||
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
class ProducerAppTests {
|
||||
|
||||
private static final String SUBSCRIPTION_MESSAGE_PATTERN = ".*app is subscribed to the following topics.*";
|
||||
|
||||
@Autowired
|
||||
private TestSubscriberRestController controller;
|
||||
|
||||
@Autowired
|
||||
private CustomerStore customerStore;
|
||||
|
||||
@Autowired
|
||||
private DaprClient daprClient;
|
||||
|
||||
|
||||
@Autowired
|
||||
private DaprContainer daprContainer;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
RestAssured.baseURI = "http://localhost:" + 8080;
|
||||
org.testcontainers.Testcontainers.exposeHostPorts(8080);
|
||||
// Ensure the subscriptions are registered
|
||||
Wait.forLogMessage(SUBSCRIPTION_MESSAGE_PATTERN, 1).waitUntilReady(daprContainer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testOrdersEndpointAndMessaging() throws InterruptedException, IOException {
|
||||
|
||||
given().contentType(ContentType.JSON)
|
||||
.body("{ \"id\": \"abc-123\",\"item\": \"the mars volta LP\",\"amount\": 1}")
|
||||
.when()
|
||||
.post("/orders")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
await().atMost(Duration.ofSeconds(15))
|
||||
.until(controller.getAllEvents()::size, equalTo(1));
|
||||
|
||||
given().contentType(ContentType.JSON)
|
||||
.when()
|
||||
.get("/orders")
|
||||
.then()
|
||||
.statusCode(200).body("size()", is(1));
|
||||
|
||||
given().contentType(ContentType.JSON)
|
||||
.when()
|
||||
.queryParam("item", "the mars volta LP")
|
||||
.get("/orders/byItem/")
|
||||
.then()
|
||||
.statusCode(200).body("size()", is(1));
|
||||
|
||||
given().contentType(ContentType.JSON)
|
||||
.when()
|
||||
.queryParam("item", "other")
|
||||
.get("/orders/byItem/")
|
||||
.then()
|
||||
.statusCode(200).body("size()", is(0));
|
||||
|
||||
given().contentType(ContentType.JSON)
|
||||
.when()
|
||||
.queryParam("amount", 1)
|
||||
.get("/orders/byAmount/")
|
||||
.then()
|
||||
.statusCode(200).body("size()", is(1));
|
||||
|
||||
given().contentType(ContentType.JSON)
|
||||
.when()
|
||||
.queryParam("amount", 2)
|
||||
.get("/orders/byAmount/")
|
||||
.then()
|
||||
.statusCode(200).body("size()", is(0));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomersWorkflows() throws InterruptedException, IOException {
|
||||
|
||||
given().contentType(ContentType.JSON)
|
||||
.body("{\"customerName\": \"salaboy\"}")
|
||||
.when()
|
||||
.post("/customers")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
|
||||
await().atMost(Duration.ofSeconds(15))
|
||||
.until(customerStore.getCustomers()::size, equalTo(1));
|
||||
Customer customer = customerStore.getCustomer("salaboy");
|
||||
assertEquals(true, customer.isInCustomerDB());
|
||||
String workflowId = customer.getWorkflowId();
|
||||
given().contentType(ContentType.JSON)
|
||||
.body("{ \"workflowId\": \"" + workflowId + "\",\"customerName\": \"salaboy\" }")
|
||||
.when()
|
||||
.post("/customers/followup")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
assertEquals(1, customerStore.getCustomers().size());
|
||||
|
||||
await().atMost(Duration.ofSeconds(10))
|
||||
.until(customerStore.getCustomer("salaboy")::isFollowUp, equalTo(true));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
public class TestProducerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
SpringApplication.from(ProducerApplication::main)
|
||||
.with(DaprTestContainersConfig.class)
|
||||
.run(args);
|
||||
org.testcontainers.Testcontainers.exposeHostPorts(8080);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr 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
|
||||
* http://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.dapr.springboot.examples.producer;
|
||||
|
||||
import io.dapr.Topic;
|
||||
import io.dapr.client.domain.CloudEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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 TestSubscriberRestController {
|
||||
|
||||
private List<CloudEvent> events = new ArrayList<>();
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(TestSubscriberRestController.class);
|
||||
|
||||
@PostMapping("subscribe")
|
||||
@Topic(pubsubName = "pubsub", name = "topic")
|
||||
public void subscribe(@RequestBody CloudEvent<Order> cloudEvent){
|
||||
logger.info("Order Event Received: " + cloudEvent.getData());
|
||||
events.add(cloudEvent);
|
||||
}
|
||||
|
||||
public List<CloudEvent> getAllEvents() {
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
dapr.statestore.name=kvstore
|
||||
dapr.statestore.binding=kvbinding
|
||||
dapr.pubsub.name=pubsub
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<FindBugsFilter>
|
||||
<!--Ignoring checking for examples-->
|
||||
<Match>
|
||||
<Package name="~io\.dapr\.springboot.examples.*"/>
|
||||
</Match>
|
||||
</FindBugsFilter>
|
||||
Loading…
Reference in New Issue