Skip to content

Commit 6ee1f59

Browse files
javier-aliagacicoyledapr-bot
authored
feat: Support registering activities with custom name (#1431)
* feat: Support register of activities with custom name Signed-off-by: Javier Aliaga <[email protected]> * chore: Apply suggestions Signed-off-by: Javier Aliaga <[email protected]> --------- Signed-off-by: Javier Aliaga <[email protected]> Co-authored-by: Cassie Coyle <[email protected]> Co-authored-by: Dapr Bot <[email protected]>
1 parent a782438 commit 6ee1f59

File tree

8 files changed

+165
-29
lines changed

8 files changed

+165
-29
lines changed

sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,29 @@ public void testSuspendAndResumeWorkflows() throws Exception {
147147

148148
}
149149

150+
@Test
151+
public void testNamedActivitiesWorkflows() throws Exception {
152+
TestWorkflowPayload payload = new TestWorkflowPayload(new ArrayList<>());
153+
String instanceId = workflowClient.scheduleNewWorkflow(TestNamedActivitiesWorkflow.class, payload);
154+
155+
workflowClient.waitForInstanceStart(instanceId, Duration.ofSeconds(10), false);
156+
157+
Duration timeout = Duration.ofSeconds(10);
158+
WorkflowInstanceStatus workflowStatus = workflowClient.waitForInstanceCompletion(instanceId, timeout, true);
159+
160+
assertNotNull(workflowStatus);
161+
162+
TestWorkflowPayload workflowOutput = deserialize(workflowStatus.getSerializedOutput());
163+
164+
assertEquals(5, workflowOutput.getPayloads().size());
165+
assertEquals("First Activity", workflowOutput.getPayloads().get(0));
166+
assertEquals("First Activity", workflowOutput.getPayloads().get(1));
167+
assertEquals("Second Activity", workflowOutput.getPayloads().get(2));
168+
assertEquals("Anonymous Activity", workflowOutput.getPayloads().get(3));
169+
assertEquals("Anonymous Activity 2", workflowOutput.getPayloads().get(4));
170+
171+
assertEquals(instanceId, workflowOutput.getWorkflowId());
172+
}
150173

151174
private TestWorkflowPayload deserialize(String value) throws JsonProcessingException {
152175
return OBJECT_MAPPER.readValue(value, TestWorkflowPayload.class);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.it.testcontainers.workflows;
15+
16+
import io.dapr.workflows.Workflow;
17+
import io.dapr.workflows.WorkflowStub;
18+
import org.slf4j.Logger;
19+
20+
public class TestNamedActivitiesWorkflow implements Workflow {
21+
22+
@Override
23+
public WorkflowStub create() {
24+
return ctx -> {
25+
Logger logger = ctx.getLogger();
26+
String instanceId = ctx.getInstanceId();
27+
logger.info("Starting Workflow: " + ctx.getName());
28+
logger.info("Instance ID: " + instanceId);
29+
logger.info("Current Orchestration Time: " + ctx.getCurrentInstant());
30+
31+
TestWorkflowPayload workflowPayload = ctx.getInput(TestWorkflowPayload.class);
32+
workflowPayload.setWorkflowId(instanceId);
33+
34+
var payloadAfterA = ctx.callActivity("a", workflowPayload, TestWorkflowPayload.class)
35+
.await();
36+
37+
var payloadAfterB = ctx.callActivity("b", payloadAfterA, TestWorkflowPayload.class)
38+
.await();
39+
40+
var payloadAfterC = ctx.callActivity("c", payloadAfterB, TestWorkflowPayload.class)
41+
.await();
42+
43+
var payloadAfterD = ctx.callActivity("d", payloadAfterC, TestWorkflowPayload.class)
44+
.await();
45+
46+
var payloadAfterE = ctx.callActivity("e", payloadAfterD, TestWorkflowPayload.class)
47+
.await();
48+
49+
ctx.complete(payloadAfterE);
50+
};
51+
}
52+
53+
}

sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/TestWorkflowsConfiguration.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515

1616
import com.fasterxml.jackson.databind.ObjectMapper;
1717
import io.dapr.config.Properties;
18+
import io.dapr.workflows.WorkflowActivityContext;
1819
import io.dapr.workflows.client.DaprWorkflowClient;
1920
import io.dapr.workflows.runtime.WorkflowRuntimeBuilder;
21+
import io.dapr.workflows.WorkflowActivity;
2022
import org.springframework.beans.factory.annotation.Value;
2123
import org.springframework.context.annotation.Bean;
2224
import org.springframework.context.annotation.Configuration;
@@ -56,8 +58,31 @@ public WorkflowRuntimeBuilder workflowRuntimeBuilder(
5658
WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder(new Properties(overrides));
5759

5860
builder.registerWorkflow(TestWorkflow.class);
61+
builder.registerWorkflow(TestNamedActivitiesWorkflow.class);
62+
5963
builder.registerActivity(FirstActivity.class);
6064
builder.registerActivity(SecondActivity.class);
65+
builder.registerActivity("a",FirstActivity.class);
66+
builder.registerActivity("b",FirstActivity.class);
67+
builder.registerActivity("c", new SecondActivity());
68+
builder.registerActivity("d", new WorkflowActivity() {
69+
@Override
70+
public Object run(WorkflowActivityContext ctx) {
71+
TestWorkflowPayload workflowPayload = ctx.getInput(TestWorkflowPayload.class);
72+
workflowPayload.getPayloads().add("Anonymous Activity");
73+
return workflowPayload;
74+
}
75+
});
76+
builder.registerActivity("e", new WorkflowActivity() {
77+
@Override
78+
public Object run(WorkflowActivityContext ctx) {
79+
TestWorkflowPayload workflowPayload = ctx.getInput(TestWorkflowPayload.class);
80+
workflowPayload.getPayloads().add("Anonymous Activity 2");
81+
return workflowPayload;
82+
}
83+
});
84+
85+
6186

6287
return builder;
6388
}

sdk-workflows/src/main/java/io/dapr/workflows/runtime/WorkflowActivityClassWrapper.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,28 @@ public class WorkflowActivityClassWrapper<T extends WorkflowActivity> implements
3030
/**
3131
* Constructor for WorkflowActivityWrapper.
3232
*
33+
* @param name Name of the activity to wrap.
3334
* @param clazz Class of the activity to wrap.
3435
*/
35-
public WorkflowActivityClassWrapper(Class<T> clazz) {
36-
this.name = clazz.getCanonicalName();
36+
public WorkflowActivityClassWrapper(String name, Class<T> clazz) {
37+
this.name = name;
3738
try {
3839
this.activityConstructor = clazz.getDeclaredConstructor();
3940
} catch (NoSuchMethodException e) {
4041
throw new RuntimeException(
41-
String.format("No constructor found for activity class '%s'.", this.name), e
42-
);
42+
String.format("No constructor found for activity class '%s'.", this.name), e);
4343
}
4444
}
4545

46+
/**
47+
* Constructor for WorkflowActivityWrapper.
48+
*
49+
* @param clazz Class of the activity to wrap.
50+
*/
51+
public WorkflowActivityClassWrapper(Class<T> clazz) {
52+
this(clazz.getCanonicalName(), clazz);
53+
}
54+
4655
@Override
4756
public String getName() {
4857
return name;
@@ -53,13 +62,12 @@ public TaskActivity create() {
5362
return ctx -> {
5463
Object result;
5564
T activity;
56-
65+
5766
try {
5867
activity = this.activityConstructor.newInstance();
5968
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
6069
throw new RuntimeException(
61-
String.format("Unable to instantiate instance of activity class '%s'", this.name), e
62-
);
70+
String.format("Unable to instantiate instance of activity class '%s'", this.name), e);
6371
}
6472

6573
result = activity.run(new DefaultWorkflowActivityContext(ctx));

sdk-workflows/src/main/java/io/dapr/workflows/runtime/WorkflowActivityInstanceWrapper.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,23 @@ public class WorkflowActivityInstanceWrapper<T extends WorkflowActivity> impleme
2727
/**
2828
* Constructor for WorkflowActivityWrapper.
2929
*
30+
* @param name Name of the activity to wrap.
3031
* @param instance Instance of the activity to wrap.
3132
*/
32-
public WorkflowActivityInstanceWrapper(T instance) {
33-
this.name = instance.getClass().getCanonicalName();
33+
public WorkflowActivityInstanceWrapper(String name, T instance) {
34+
this.name = name;
3435
this.activity = instance;
3536
}
3637

38+
/**
39+
* Constructor for WorkflowActivityWrapper.
40+
*
41+
* @param instance Instance of the activity to wrap.
42+
*/
43+
public WorkflowActivityInstanceWrapper(T instance) {
44+
this(instance.getClass().getCanonicalName(), instance);
45+
}
46+
3747
@Override
3848
public String getName() {
3949
return name;

sdk-workflows/src/main/java/io/dapr/workflows/runtime/WorkflowRuntimeBuilder.java

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,23 @@ public <T extends Workflow> WorkflowRuntimeBuilder registerWorkflow(T instance)
149149
* @return the WorkflowRuntimeBuilder
150150
*/
151151
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(Class<T> clazz) {
152-
this.builder.addActivity(new WorkflowActivityClassWrapper<>(clazz));
153-
this.activitySet.add(clazz.getCanonicalName());
154-
this.activities.add(clazz.getSimpleName());
152+
return registerActivity(clazz.getCanonicalName(), clazz);
153+
}
155154

156-
this.logger.info("Registered Activity: {}", clazz.getSimpleName());
155+
/**
156+
* Registers an Activity object.
157+
*
158+
* @param <T> any WorkflowActivity type
159+
* @param name Name of the activity to register.
160+
* @param clazz Class of the activity to register.
161+
* @return the WorkflowRuntimeBuilder
162+
*/
163+
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(String name, Class<T> clazz) {
164+
this.builder.addActivity(new WorkflowActivityClassWrapper<>(name, clazz));
165+
this.activitySet.add(name);
166+
this.activities.add(name);
167+
168+
this.logger.info("Registered Activity: {}", name);
157169

158170
return this;
159171
}
@@ -166,13 +178,23 @@ public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(Clas
166178
* @return the WorkflowRuntimeBuilder
167179
*/
168180
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(T instance) {
169-
Class<T> clazz = (Class<T>) instance.getClass();
181+
return this.registerActivity(instance.getClass().getCanonicalName(), instance);
182+
}
170183

171-
this.builder.addActivity(new WorkflowActivityInstanceWrapper<>(instance));
172-
this.activitySet.add(clazz.getCanonicalName());
173-
this.activities.add(clazz.getSimpleName());
184+
/**
185+
* Registers an Activity object.
186+
*
187+
* @param <T> any WorkflowActivity type
188+
* @param name Name of the activity to register.
189+
* @param instance the class instance being registered
190+
* @return the WorkflowRuntimeBuilder
191+
*/
192+
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(String name, T instance) {
193+
this.builder.addActivity(new WorkflowActivityInstanceWrapper<>(name, instance));
194+
this.activitySet.add(name);
195+
this.activities.add(name);
174196

175-
this.logger.info("Registered Activity: {}", clazz.getSimpleName());
197+
this.logger.info("Registered Activity: {}", name);
176198

177199
return this;
178200
}

sdk-workflows/src/test/java/io/dapr/workflows/runtime/WorkflowActivityClassWrapperTest.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
import org.junit.Test;
77

88
import static org.junit.jupiter.api.Assertions.assertEquals;
9-
import static org.mockito.Mockito.mock;
10-
import static org.mockito.Mockito.times;
11-
import static org.mockito.Mockito.verify;
12-
import static org.mockito.Mockito.when;
9+
import static org.mockito.Mockito.*;
1310

1411
public class WorkflowActivityClassWrapperTest {
1512
public static class TestActivity implements WorkflowActivity {

sdk-workflows/src/test/java/io/dapr/workflows/runtime/WorkflowRuntimeBuilderTest.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@
1919
import org.junit.jupiter.api.Test;
2020
import org.slf4j.Logger;
2121

22-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
23-
import static org.mockito.ArgumentMatchers.eq;
24-
import static org.mockito.Mockito.mock;
25-
import static org.mockito.Mockito.times;
26-
import static org.mockito.Mockito.verify;
27-
2822
import java.io.ByteArrayOutputStream;
2923
import java.io.PrintStream;
3024

25+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
26+
import static org.mockito.ArgumentMatchers.eq;
27+
import static org.mockito.Mockito.*;
28+
3129
public class WorkflowRuntimeBuilderTest {
3230
public static class TestWorkflow implements Workflow {
3331
@Override
@@ -94,6 +92,6 @@ public void loggingOutputTest() {
9492
.info(eq("Registered Workflow: {}"), eq("TestWorkflow"));
9593

9694
verify(testLogger, times(1))
97-
.info(eq("Registered Activity: {}"), eq("TestActivity"));
95+
.info(eq("Registered Activity: {}"), eq("io.dapr.workflows.runtime.WorkflowRuntimeBuilderTest.TestActivity"));
9896
}
9997
}

0 commit comments

Comments
 (0)