Skip to content

Commit 6411797

Browse files
authored
Adding examples to Spring Boot (duration, zoneddatetime and suspend/resume) (dapr#1413)
* implementing createtime with zoneddatetime Signed-off-by: salaboy <[email protected]> * adding duration and zoneddatetime examples Signed-off-by: salaboy <[email protected]> * using external event wf to test suspend resume Signed-off-by: salaboy <[email protected]> --------- Signed-off-by: salaboy <[email protected]>
1 parent a159db2 commit 6411797

File tree

7 files changed

+248
-6
lines changed

7 files changed

+248
-6
lines changed

sdk-workflows/src/test/java/io/dapr/workflows/DefaultWorkflowContextTest.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,19 @@
1414
package io.dapr.workflows;
1515

1616
import io.dapr.durabletask.CompositeTaskFailedException;
17-
import io.dapr.durabletask.FailureDetails;
1817
import io.dapr.durabletask.RetryContext;
1918
import io.dapr.durabletask.RetryHandler;
2019
import io.dapr.durabletask.Task;
2120
import io.dapr.durabletask.TaskCanceledException;
2221
import io.dapr.durabletask.TaskOptions;
2322
import io.dapr.durabletask.TaskOrchestrationContext;
24-
2523
import io.dapr.workflows.runtime.DefaultWorkflowContext;
26-
2724
import org.junit.jupiter.api.BeforeEach;
2825
import org.junit.jupiter.api.Test;
2926
import org.mockito.ArgumentCaptor;
3027
import org.slf4j.Logger;
3128

3229
import javax.annotation.Nullable;
33-
3430
import java.time.Duration;
3531
import java.time.Instant;
3632
import java.time.ZonedDateTime;

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
import io.dapr.springboot.examples.wfp.fanoutin.Result;
2525
import io.dapr.springboot.examples.wfp.remoteendpoint.Payload;
2626
import io.dapr.springboot.examples.wfp.remoteendpoint.RemoteEndpointWorkflow;
27-
import io.dapr.springboot.examples.wfp.suspendresume.SuspendResumeWorkflow;
27+
import io.dapr.springboot.examples.wfp.timer.DurationTimerWorkflow;
28+
import io.dapr.springboot.examples.wfp.timer.ZonedDateTimeTimerWorkflow;
2829
import io.dapr.workflows.client.DaprWorkflowClient;
2930
import io.dapr.workflows.client.WorkflowInstanceStatus;
3031
import org.slf4j.Logger;
@@ -156,7 +157,7 @@ public Payload remoteEndpoint(@RequestBody Payload payload)
156157

157158
@PostMapping("wfp/suspendresume")
158159
public String suspendResume(@RequestParam("orderId") String orderId) {
159-
String instanceId = daprWorkflowClient.scheduleNewWorkflow(SuspendResumeWorkflow.class);
160+
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ExternalEventWorkflow.class);
160161
logger.info("Workflow instance " + instanceId + " started");
161162
ordersToApprove.put(orderId, instanceId);
162163
return instanceId;
@@ -189,4 +190,16 @@ public Decision suspendResumeContinue(@RequestParam("orderId") String orderId, @
189190
.waitForInstanceCompletion(instanceId, null, true);
190191
return workflowInstanceStatus.readOutputAs(Decision.class);
191192
}
193+
194+
@PostMapping("wfp/durationtimer")
195+
public String durationTimerWorkflow() {
196+
return daprWorkflowClient.scheduleNewWorkflow(DurationTimerWorkflow.class);
197+
}
198+
199+
@PostMapping("wfp/zoneddatetimetimer")
200+
public String zonedDateTimeTimerWorkflow() {
201+
return daprWorkflowClient.scheduleNewWorkflow(ZonedDateTimeTimerWorkflow.class);
202+
}
203+
192204
}
205+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.timer;
15+
16+
import io.dapr.workflows.Workflow;
17+
import io.dapr.workflows.WorkflowStub;
18+
import org.springframework.stereotype.Component;
19+
20+
import java.time.Duration;
21+
import java.util.Date;
22+
23+
@Component
24+
public class DurationTimerWorkflow implements Workflow {
25+
@Override
26+
public WorkflowStub create() {
27+
return ctx -> {
28+
ctx.getLogger().info("Starting Workflow: {}, instanceId: {}", ctx.getName(), ctx.getInstanceId());
29+
30+
ctx.getLogger().info("Let's call the first LogActivity at {}", new Date());
31+
ctx.callActivity(LogActivity.class.getName()).await();
32+
33+
ctx.getLogger().info("Let's schedule a 10 seconds timer at {}", new Date());
34+
ctx.createTimer(Duration.ofSeconds(10)).await();
35+
36+
ctx.getLogger().info("Let's call the second LogActivity at {}", new Date());
37+
ctx.callActivity(LogActivity.class.getName()).await();
38+
39+
ctx.complete(true);
40+
ctx.getLogger().info("Workflow completed at {}", new Date());
41+
};
42+
}
43+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.timer;
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.stereotype.Component;
22+
23+
import java.util.Date;
24+
25+
@Component
26+
public class LogActivity implements WorkflowActivity {
27+
28+
@Autowired
29+
private TimerLogService logService;
30+
31+
@Override
32+
public Object run(WorkflowActivityContext ctx) {
33+
Logger logger = LoggerFactory.getLogger(LogActivity.class);
34+
Date now = new Date();
35+
logger.info("Running Activity: {} at {}", ctx.getName(), now);
36+
logService.logDate(now);
37+
return true;
38+
}
39+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
package io.dapr.springboot.examples.wfp.timer;
14+
15+
import org.springframework.stereotype.Component;
16+
17+
import java.util.ArrayList;
18+
import java.util.Date;
19+
import java.util.List;
20+
21+
@Component
22+
public class TimerLogService {
23+
private final List<Date> logDates = new ArrayList<>();
24+
25+
public void logDate(Date date){
26+
logDates.add(date);
27+
}
28+
29+
public void clearLog(){
30+
logDates.clear();
31+
}
32+
33+
public List<Date> getLogDates(){
34+
return logDates;
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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.timer;
15+
16+
import io.dapr.workflows.Workflow;
17+
import io.dapr.workflows.WorkflowStub;
18+
import org.springframework.stereotype.Component;
19+
20+
import java.time.ZonedDateTime;
21+
import java.util.Date;
22+
23+
@Component
24+
public class ZonedDateTimeTimerWorkflow implements Workflow {
25+
@Override
26+
public WorkflowStub create() {
27+
return ctx -> {
28+
ctx.getLogger().info("Starting Workflow: {}, instanceId: {}", ctx.getName(), ctx.getInstanceId());
29+
30+
ctx.getLogger().info("Let's call the first LogActivity at {}", new Date());
31+
ctx.callActivity(LogActivity.class.getName()).await();
32+
33+
ZonedDateTime now = ZonedDateTime.now();
34+
//Let's create a ZonedDateTime 10 seconds in the future
35+
ZonedDateTime inTheFuture = now.plusSeconds(10);
36+
ctx.getLogger().info("Creating a timer that due {} at: {}", inTheFuture, new Date());
37+
ctx.createTimer(inTheFuture).await();
38+
ctx.getLogger().info("The timer fired at: {}", new Date());
39+
40+
ctx.getLogger().info("Let's call the second LogActivity at {}", new Date());
41+
ctx.callActivity(LogActivity.class.getName()).await();
42+
43+
ctx.complete(true);
44+
ctx.getLogger().info("Workflow completed at {}", new Date());
45+
};
46+
}
47+
}

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.dapr.springboot.DaprAutoConfiguration;
1717
import io.dapr.springboot.examples.wfp.continueasnew.CleanUpLog;
1818
import io.dapr.springboot.examples.wfp.remoteendpoint.Payload;
19+
import io.dapr.springboot.examples.wfp.timer.TimerLogService;
1920
import io.dapr.workflows.client.WorkflowRuntimeStatus;
2021
import io.github.microcks.testcontainers.MicrocksContainersEnsemble;
2122
import io.restassured.RestAssured;
@@ -25,10 +26,13 @@
2526
import org.springframework.beans.factory.annotation.Autowired;
2627
import org.springframework.boot.test.context.SpringBootTest;
2728

29+
import java.time.Duration;
2830
import java.util.Arrays;
2931
import java.util.List;
32+
import java.util.concurrent.TimeUnit;
3033

3134
import static io.restassured.RestAssured.given;
35+
import static org.awaitility.Awaitility.await;
3236
import static org.hamcrest.CoreMatchers.containsString;
3337
import static org.hamcrest.Matchers.equalTo;
3438
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -42,10 +46,14 @@ class WorkflowPatternsAppTests {
4246
@Autowired
4347
private MicrocksContainersEnsemble ensemble;
4448

49+
@Autowired
50+
private TimerLogService logService;
51+
4552
@BeforeEach
4653
void setUp() {
4754
RestAssured.baseURI = "http://localhost:" + 8080;
4855
org.testcontainers.Testcontainers.exposeHostPorts(8080);
56+
logService.clearLog();
4957
}
5058

5159

@@ -201,4 +209,64 @@ void testSuspendResume() {
201209

202210
}
203211

212+
@Test
213+
void testDurationTimer() throws InterruptedException {
214+
215+
String instanceId = given()
216+
.when()
217+
.post("/wfp/durationtimer")
218+
.then()
219+
.statusCode(200).extract().asString();
220+
221+
assertNotNull(instanceId);
222+
223+
// Check that the workflow completed successfully
224+
await().atMost(Duration.ofSeconds(30))
225+
.pollDelay(500, TimeUnit.MILLISECONDS)
226+
.pollInterval(500, TimeUnit.MILLISECONDS)
227+
.until(() -> {
228+
System.out.println("Log Size: " + logService.getLogDates().size());
229+
if( logService.getLogDates().size() == 2 ) {
230+
long diffInMillis = Math.abs(logService.getLogDates().get(1).getTime() - logService.getLogDates().get(0).getTime());
231+
long diff = TimeUnit.SECONDS.convert(diffInMillis, TimeUnit.MILLISECONDS);
232+
System.out.println("First Log at: " + logService.getLogDates().get(0));
233+
System.out.println("Second Log at: " + logService.getLogDates().get(1));
234+
System.out.println("Diff in seconds: " + diff);
235+
// The updated time differences should be between 9 and 11 seconds
236+
return diff >= 9 && diff <= 11;
237+
}
238+
return false;
239+
});
240+
}
241+
242+
@Test
243+
void testZonedDateTimeTimer() throws InterruptedException {
244+
245+
String instanceId = given()
246+
.when()
247+
.post("/wfp/zoneddatetimetimer")
248+
.then()
249+
.statusCode(200).extract().asString();
250+
251+
assertNotNull(instanceId);
252+
253+
// Check that the workflow completed successfully
254+
await().atMost(Duration.ofSeconds(30))
255+
.pollDelay(500, TimeUnit.MILLISECONDS)
256+
.pollInterval(500, TimeUnit.MILLISECONDS)
257+
.until(() -> {
258+
System.out.println("Log Size: " + logService.getLogDates().size());
259+
if( logService.getLogDates().size() == 2 ) {
260+
long diffInMillis = Math.abs(logService.getLogDates().get(1).getTime() - logService.getLogDates().get(0).getTime());
261+
long diff = TimeUnit.SECONDS.convert(diffInMillis, TimeUnit.MILLISECONDS);
262+
System.out.println("First Log at: " + logService.getLogDates().get(0));
263+
System.out.println("Second Log at: " + logService.getLogDates().get(1));
264+
System.out.println("Diff in seconds: " + diff);
265+
// The updated time differences should be between 9 and 11 seconds
266+
return diff >= 9 && diff <= 11;
267+
}
268+
return false;
269+
});
270+
}
271+
204272
}

0 commit comments

Comments
 (0)