Skip to content

Commit 447e2bf

Browse files
salaboyjavier-aliagacicoyleartur-ciocanusiri-varma
authored
Adding remote endpoint request from inside activity with retry (#1388)
* adding remote endpoint request from inside activity with retry Signed-off-by: salaboy <[email protected]> * adding retry with Microcks payloads Signed-off-by: salaboy <[email protected]> * fixing review comments Signed-off-by: salaboy <[email protected]> * chore: New task execution task id test (#1352) * chore: New task execution task id test test how taskExecutionTaskId can be used for idempotency Signed-off-by: Javier Aliaga <[email protected]> * chore: Clean up not used files Signed-off-by: Javier Aliaga <[email protected]> * docs: Task execution keys Signed-off-by: Javier Aliaga <[email protected]> * test: Modify unit tests Signed-off-by: Javier Aliaga <[email protected]> * Remove new lines Signed-off-by: artur-ciocanu <[email protected]> --------- Signed-off-by: Javier Aliaga <[email protected]> Signed-off-by: artur-ciocanu <[email protected]> Co-authored-by: Cassie Coyle <[email protected]> Co-authored-by: artur-ciocanu <[email protected]> Signed-off-by: salaboy <[email protected]> * Revert "chore: New task execution task id test (#1352)" (#1389) This reverts commit 949584f. Signed-off-by: Javier Aliaga <[email protected]> Signed-off-by: salaboy <[email protected]> * 1.5.5 (#1390) Signed-off-by: Cassandra Coyle <[email protected]> Signed-off-by: salaboy <[email protected]> * Add Documentation for Conversation AI SDK (#1387) Signed-off-by: salaboy <[email protected]> * Cleanup Spring Dependencies (#1334) * Update CONTRIBUTING.md Signed-off-by: Siri Varma Vegiraju <[email protected]> * Fix spring Signed-off-by: siri-varma <[email protected]> * Add context Signed-off-by: siri-varma <[email protected]> * Phase 1 Signed-off-by: siri-varma <[email protected]> * Fix things Signed-off-by: siri-varma <[email protected]> * Fix things Signed-off-by: siri-varma <[email protected]> * Fix spring Signed-off-by: siri-varma <[email protected]> * Add context Signed-off-by: siri-varma <[email protected]> * Phase 1 Signed-off-by: siri-varma <[email protected]> * Fix things Signed-off-by: siri-varma <[email protected]> * Fix things Signed-off-by: siri-varma <[email protected]> * move version Signed-off-by: sirivarma <[email protected]> * Fix pom Signed-off-by: sirivarma <[email protected]> * change version Signed-off-by: sirivarma <[email protected]> * remove unused Signed-off-by: siri-varma <[email protected]> * Address comments Signed-off-by: siri-varma <[email protected]> * Fix test Signed-off-by: siri-varma <[email protected]> * Fix test Signed-off-by: siri-varma <[email protected]> * Fix things Signed-off-by: sirivarma <[email protected]> --------- Signed-off-by: Siri Varma Vegiraju <[email protected]> Signed-off-by: siri-varma <[email protected]> Signed-off-by: sirivarma <[email protected]> Co-authored-by: artur-ciocanu <[email protected]> Signed-off-by: salaboy <[email protected]> * network is needed Signed-off-by: salaboy <[email protected]> --------- Signed-off-by: salaboy <[email protected]> Signed-off-by: Javier Aliaga <[email protected]> Signed-off-by: artur-ciocanu <[email protected]> Signed-off-by: Cassandra Coyle <[email protected]> Signed-off-by: Siri Varma Vegiraju <[email protected]> Signed-off-by: siri-varma <[email protected]> Signed-off-by: sirivarma <[email protected]> Co-authored-by: Javier Aliaga <[email protected]> Co-authored-by: Cassie Coyle <[email protected]> Co-authored-by: artur-ciocanu <[email protected]> Co-authored-by: Siri Varma Vegiraju <[email protected]>
1 parent 114e354 commit 447e2bf

File tree

10 files changed

+358
-1
lines changed

10 files changed

+358
-1
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<commons-cli.version>1.9.0</commons-cli.version>
6363
<commons-io.version>2.14.0</commons-io.version>
6464
<zipkin.version>3.4.0</zipkin.version>
65+
<microcks.version>0.3.1</microcks.version>
6566
</properties>
6667

6768
<distributionManagement>

spring-boot-examples/workflows/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
<artifactId>rest-assured</artifactId>
4141
<scope>test</scope>
4242
</dependency>
43+
<dependency>
44+
<groupId>io.github.microcks</groupId>
45+
<artifactId>microcks-testcontainers</artifactId>
46+
<version>${microcks.version}</version>
47+
<scope>test</scope>
48+
</dependency>
4349
</dependencies>
4450

4551
<build>

spring-boot-examples/workflows/src/main/java/io/dapr/springboot/examples/wfp/WorkflowPatternsConfiguration.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,27 @@
1313

1414
package io.dapr.springboot.examples.wfp;
1515

16+
import com.fasterxml.jackson.databind.ObjectMapper;
1617
import io.dapr.springboot.examples.wfp.continueasnew.CleanUpLog;
18+
import org.springframework.boot.web.client.RestTemplateBuilder;
1719
import org.springframework.context.annotation.Bean;
1820
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.web.client.RestTemplate;
1922

2023
@Configuration
2124
public class WorkflowPatternsConfiguration {
2225
@Bean
2326
public CleanUpLog cleanUpLog(){
2427
return new CleanUpLog();
2528
}
29+
30+
@Bean
31+
public RestTemplate restTemplate() {
32+
return new RestTemplateBuilder().build();
33+
}
34+
35+
@Bean
36+
public ObjectMapper mapper() {
37+
return new ObjectMapper();
38+
}
2639
}

spring-boot-examples/workflows/src/main/java/io/dapr/springboot/examples/wfp/WorkflowPatternsRestController.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import io.dapr.springboot.examples.wfp.externalevent.ExternalEventWorkflow;
2323
import io.dapr.springboot.examples.wfp.fanoutin.FanOutInWorkflow;
2424
import io.dapr.springboot.examples.wfp.fanoutin.Result;
25+
import io.dapr.springboot.examples.wfp.remoteendpoint.Payload;
26+
import io.dapr.springboot.examples.wfp.remoteendpoint.RemoteEndpointWorkflow;
2527
import io.dapr.workflows.client.DaprWorkflowClient;
2628
import io.dapr.workflows.client.WorkflowInstanceStatus;
2729
import org.slf4j.Logger;
@@ -53,6 +55,7 @@ public class WorkflowPatternsRestController {
5355
private Map<String, String> ordersToApprove = new HashMap<>();
5456

5557

58+
5659
/**
5760
* Run Chain Demo Workflow
5861
* @return the output of the ChainWorkflow execution
@@ -137,4 +140,17 @@ public CleanUpLog continueAsNew()
137140
return workflowInstanceStatus.readOutputAs(CleanUpLog.class);
138141
}
139142

143+
@PostMapping("wfp/remote-endpoint")
144+
public Payload remoteEndpoint(@RequestBody Payload payload)
145+
throws TimeoutException {
146+
147+
String instanceId = daprWorkflowClient.scheduleNewWorkflow(RemoteEndpointWorkflow.class, payload);
148+
logger.info("Workflow instance " + instanceId + " started");
149+
150+
WorkflowInstanceStatus workflowInstanceStatus = daprWorkflowClient
151+
.waitForInstanceCompletion(instanceId, null, true);
152+
System.out.printf("workflow instance with ID: %s completed.", instanceId);
153+
return workflowInstanceStatus.readOutputAs(Payload.class);
154+
}
155+
140156
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2023 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp.remoteendpoint;
15+
16+
import io.dapr.workflows.WorkflowActivity;
17+
import io.dapr.workflows.WorkflowActivityContext;
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
import org.springframework.beans.factory.annotation.Autowired;
21+
import org.springframework.beans.factory.annotation.Value;
22+
import org.springframework.http.HttpEntity;
23+
import org.springframework.stereotype.Component;
24+
import org.springframework.web.client.RestTemplate;
25+
26+
@Component
27+
public class CallRemoteEndpointActivity implements WorkflowActivity {
28+
29+
private Logger logger = LoggerFactory.getLogger(CallRemoteEndpointActivity.class);
30+
31+
@Value("${application.process-base-url:}")
32+
private String processBaseURL;
33+
34+
@Autowired
35+
private RestTemplate restTemplate;
36+
37+
38+
@Override
39+
public Object run(WorkflowActivityContext ctx) {
40+
logger.info("Starting Activity: " + ctx.getName());
41+
var payload = ctx.getInput(Payload.class);
42+
43+
HttpEntity<Payload> request =
44+
new HttpEntity<>(payload);
45+
payload = restTemplate.postForObject(processBaseURL + "/process", request, Payload.class);
46+
47+
logger.info("Payload from the remote service: " + payload);
48+
49+
return payload;
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2025 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp.remoteendpoint;
15+
16+
public class Payload {
17+
private String id;
18+
private String content;
19+
private Boolean processed = false;
20+
21+
public Payload(String id, String content) {
22+
this.id = id;
23+
this.content = content;
24+
}
25+
26+
public Payload() {
27+
}
28+
29+
public String getId() {
30+
return id;
31+
}
32+
33+
public void setId(String id) {
34+
this.id = id;
35+
}
36+
37+
38+
public String getContent() {
39+
return content;
40+
}
41+
42+
public void setContent(String content) {
43+
this.content = content;
44+
}
45+
46+
public Boolean getProcessed() {
47+
return processed;
48+
}
49+
50+
public void setProcessed(Boolean processed) {
51+
this.processed = processed;
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return "Payload{" +
57+
"id='" + id + '\'' +
58+
", content='" + content + '\'' +
59+
", processed=" + processed +
60+
'}';
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2023 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp.remoteendpoint;
15+
16+
import io.dapr.workflows.Workflow;
17+
import io.dapr.workflows.WorkflowStub;
18+
import io.dapr.workflows.WorkflowTaskOptions;
19+
import io.dapr.workflows.WorkflowTaskRetryPolicy;
20+
import org.springframework.stereotype.Component;
21+
22+
import java.time.Duration;
23+
24+
@Component
25+
public class RemoteEndpointWorkflow implements Workflow {
26+
27+
@Override
28+
public WorkflowStub create() {
29+
return ctx -> {
30+
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
31+
32+
Payload payload = ctx.getInput(Payload.class);
33+
payload = ctx.callActivity(CallRemoteEndpointActivity.class.getName(), payload ,
34+
new WorkflowTaskOptions(new WorkflowTaskRetryPolicy(5,
35+
Duration.ofSeconds(2), 1.0, Duration.ofSeconds(10), Duration.ofSeconds(20))),
36+
Payload.class).await();
37+
38+
ctx.getLogger().info("Workflow finished with result: " + payload);
39+
ctx.complete(payload);
40+
};
41+
}
42+
}

spring-boot-examples/workflows/src/test/java/io/dapr/springboot/examples/wfp/DaprTestContainersConfig.java

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@
1515

1616
import io.dapr.testcontainers.Component;
1717
import io.dapr.testcontainers.DaprContainer;
18+
import io.github.microcks.testcontainers.MicrocksContainersEnsemble;
19+
import org.junit.runner.Description;
20+
import org.junit.runners.model.Statement;
1821
import org.springframework.boot.test.context.TestConfiguration;
1922
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
2023
import org.springframework.context.annotation.Bean;
24+
import org.springframework.core.env.Environment;
25+
import org.springframework.test.context.DynamicPropertyRegistrar;
26+
import org.testcontainers.DockerClientFactory;
27+
import org.testcontainers.containers.Network;
2128

2229
import java.util.Collections;
30+
import java.util.List;
2331

2432
import static io.dapr.testcontainers.DaprContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
2533

@@ -28,16 +36,66 @@ public class DaprTestContainersConfig {
2836

2937
@Bean
3038
@ServiceConnection
31-
public DaprContainer daprContainer() {
39+
public DaprContainer daprContainer(Network network) {
3240

3341
return new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
3442
.withAppName("workflow-patterns-app")
3543
.withComponent(new Component("kvstore", "state.in-memory", "v1", Collections.singletonMap("actorStateStore", String.valueOf(true))))
3644
.withAppPort(8080)
45+
.withNetwork(network)
3746
.withAppHealthCheckPath("/actuator/health")
3847
.withAppChannelAddress("host.testcontainers.internal");
3948
}
4049

4150

51+
@Bean
52+
MicrocksContainersEnsemble microcksEnsemble(Network network) {
53+
return new MicrocksContainersEnsemble(network, "quay.io/microcks/microcks-uber:1.11.2")
54+
.withAccessToHost(true) // We need this to access our webapp while it runs
55+
.withMainArtifacts("third-parties/remote-http-service.yaml");
56+
}
57+
58+
@Bean
59+
public DynamicPropertyRegistrar endpointsProperties(MicrocksContainersEnsemble ensemble) {
60+
// We need to replace the default endpoints with those provided by Microcks.
61+
return (properties) -> {
62+
properties.add("application.process-base-url", () -> ensemble.getMicrocksContainer()
63+
.getRestMockEndpoint("API Payload Processor", "1.0.0"));
64+
};
65+
}
66+
67+
@Bean
68+
public Network getDaprNetwork(Environment env) {
69+
boolean reuse = env.getProperty("reuse", Boolean.class, false);
70+
if (reuse) {
71+
Network defaultDaprNetwork = new Network() {
72+
@Override
73+
public String getId() {
74+
return "dapr-network";
75+
}
76+
77+
@Override
78+
public void close() {
79+
80+
}
81+
82+
@Override
83+
public Statement apply(Statement base, Description description) {
84+
return null;
85+
}
86+
};
87+
88+
List<com.github.dockerjava.api.model.Network> networks = DockerClientFactory.instance().client().listNetworksCmd()
89+
.withNameFilter("dapr-network").exec();
90+
if (networks.isEmpty()) {
91+
Network.builder().createNetworkCmdModifier(cmd -> cmd.withName("dapr-network")).build().getId();
92+
return defaultDaprNetwork;
93+
} else {
94+
return defaultDaprNetwork;
95+
}
96+
} else {
97+
return Network.newNetwork();
98+
}
99+
}
42100

43101
}

spring-boot-examples/workflows/src/test/java/io/dapr/springboot/examples/wfp/WorkflowPatternsAppTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import io.dapr.client.DaprClient;
1717
import io.dapr.springboot.DaprAutoConfiguration;
1818
import io.dapr.springboot.examples.wfp.continueasnew.CleanUpLog;
19+
import io.dapr.springboot.examples.wfp.remoteendpoint.Payload;
20+
import io.github.microcks.testcontainers.MicrocksContainersEnsemble;
1921
import io.restassured.RestAssured;
2022
import io.restassured.http.ContentType;
2123
import org.junit.jupiter.api.BeforeEach;
@@ -39,6 +41,9 @@ class WorkflowPatternsAppTests {
3941
@Autowired
4042
private DaprClient daprClient;
4143

44+
@Autowired
45+
private MicrocksContainersEnsemble ensemble;
46+
4247
@BeforeEach
4348
void setUp() {
4449
RestAssured.baseURI = "http://localhost:" + 8080;
@@ -139,4 +144,20 @@ void testContinueAsNew() {
139144
assertEquals(5, cleanUpLog.getCleanUpTimes());
140145
}
141146

147+
@Test
148+
void testRemoteEndpoint() {
149+
150+
Payload payload = given().contentType(ContentType.JSON)
151+
.body(new Payload("123", "content goes here"))
152+
.when()
153+
.post("/wfp/remote-endpoint")
154+
.then()
155+
.statusCode(200).extract().as(Payload.class);
156+
157+
assertEquals(true, payload.getProcessed());
158+
159+
assertEquals(2, ensemble.getMicrocksContainer()
160+
.getServiceInvocationsCount("API Payload Processor", "1.0.0"));
161+
}
162+
142163
}

0 commit comments

Comments
 (0)