mirror of https://github.com/dapr/java-sdk.git
feat: Support registering activities with custom name (#1431)
* feat: Support register of activities with custom name Signed-off-by: Javier Aliaga <javier@diagrid.io> * chore: Apply suggestions Signed-off-by: Javier Aliaga <javier@diagrid.io> --------- Signed-off-by: Javier Aliaga <javier@diagrid.io> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Co-authored-by: Dapr Bot <56698301+dapr-bot@users.noreply.github.com>
This commit is contained in:
parent
a782438c36
commit
6ee1f59d61
|
@ -147,6 +147,29 @@ public class DaprWorkflowsIT {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNamedActivitiesWorkflows() throws Exception {
|
||||||
|
TestWorkflowPayload payload = new TestWorkflowPayload(new ArrayList<>());
|
||||||
|
String instanceId = workflowClient.scheduleNewWorkflow(TestNamedActivitiesWorkflow.class, payload);
|
||||||
|
|
||||||
|
workflowClient.waitForInstanceStart(instanceId, Duration.ofSeconds(10), false);
|
||||||
|
|
||||||
|
Duration timeout = Duration.ofSeconds(10);
|
||||||
|
WorkflowInstanceStatus workflowStatus = workflowClient.waitForInstanceCompletion(instanceId, timeout, true);
|
||||||
|
|
||||||
|
assertNotNull(workflowStatus);
|
||||||
|
|
||||||
|
TestWorkflowPayload workflowOutput = deserialize(workflowStatus.getSerializedOutput());
|
||||||
|
|
||||||
|
assertEquals(5, workflowOutput.getPayloads().size());
|
||||||
|
assertEquals("First Activity", workflowOutput.getPayloads().get(0));
|
||||||
|
assertEquals("First Activity", workflowOutput.getPayloads().get(1));
|
||||||
|
assertEquals("Second Activity", workflowOutput.getPayloads().get(2));
|
||||||
|
assertEquals("Anonymous Activity", workflowOutput.getPayloads().get(3));
|
||||||
|
assertEquals("Anonymous Activity 2", workflowOutput.getPayloads().get(4));
|
||||||
|
|
||||||
|
assertEquals(instanceId, workflowOutput.getWorkflowId());
|
||||||
|
}
|
||||||
|
|
||||||
private TestWorkflowPayload deserialize(String value) throws JsonProcessingException {
|
private TestWorkflowPayload deserialize(String value) throws JsonProcessingException {
|
||||||
return OBJECT_MAPPER.readValue(value, TestWorkflowPayload.class);
|
return OBJECT_MAPPER.readValue(value, TestWorkflowPayload.class);
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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.it.testcontainers.workflows;
|
||||||
|
|
||||||
|
import io.dapr.workflows.Workflow;
|
||||||
|
import io.dapr.workflows.WorkflowStub;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
public class TestNamedActivitiesWorkflow implements Workflow {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorkflowStub create() {
|
||||||
|
return ctx -> {
|
||||||
|
Logger logger = ctx.getLogger();
|
||||||
|
String instanceId = ctx.getInstanceId();
|
||||||
|
logger.info("Starting Workflow: " + ctx.getName());
|
||||||
|
logger.info("Instance ID: " + instanceId);
|
||||||
|
logger.info("Current Orchestration Time: " + ctx.getCurrentInstant());
|
||||||
|
|
||||||
|
TestWorkflowPayload workflowPayload = ctx.getInput(TestWorkflowPayload.class);
|
||||||
|
workflowPayload.setWorkflowId(instanceId);
|
||||||
|
|
||||||
|
var payloadAfterA = ctx.callActivity("a", workflowPayload, TestWorkflowPayload.class)
|
||||||
|
.await();
|
||||||
|
|
||||||
|
var payloadAfterB = ctx.callActivity("b", payloadAfterA, TestWorkflowPayload.class)
|
||||||
|
.await();
|
||||||
|
|
||||||
|
var payloadAfterC = ctx.callActivity("c", payloadAfterB, TestWorkflowPayload.class)
|
||||||
|
.await();
|
||||||
|
|
||||||
|
var payloadAfterD = ctx.callActivity("d", payloadAfterC, TestWorkflowPayload.class)
|
||||||
|
.await();
|
||||||
|
|
||||||
|
var payloadAfterE = ctx.callActivity("e", payloadAfterD, TestWorkflowPayload.class)
|
||||||
|
.await();
|
||||||
|
|
||||||
|
ctx.complete(payloadAfterE);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,8 +15,10 @@ package io.dapr.it.testcontainers.workflows;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import io.dapr.config.Properties;
|
import io.dapr.config.Properties;
|
||||||
|
import io.dapr.workflows.WorkflowActivityContext;
|
||||||
import io.dapr.workflows.client.DaprWorkflowClient;
|
import io.dapr.workflows.client.DaprWorkflowClient;
|
||||||
import io.dapr.workflows.runtime.WorkflowRuntimeBuilder;
|
import io.dapr.workflows.runtime.WorkflowRuntimeBuilder;
|
||||||
|
import io.dapr.workflows.WorkflowActivity;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -56,8 +58,31 @@ public class TestWorkflowsConfiguration {
|
||||||
WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder(new Properties(overrides));
|
WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder(new Properties(overrides));
|
||||||
|
|
||||||
builder.registerWorkflow(TestWorkflow.class);
|
builder.registerWorkflow(TestWorkflow.class);
|
||||||
|
builder.registerWorkflow(TestNamedActivitiesWorkflow.class);
|
||||||
|
|
||||||
builder.registerActivity(FirstActivity.class);
|
builder.registerActivity(FirstActivity.class);
|
||||||
builder.registerActivity(SecondActivity.class);
|
builder.registerActivity(SecondActivity.class);
|
||||||
|
builder.registerActivity("a",FirstActivity.class);
|
||||||
|
builder.registerActivity("b",FirstActivity.class);
|
||||||
|
builder.registerActivity("c", new SecondActivity());
|
||||||
|
builder.registerActivity("d", new WorkflowActivity() {
|
||||||
|
@Override
|
||||||
|
public Object run(WorkflowActivityContext ctx) {
|
||||||
|
TestWorkflowPayload workflowPayload = ctx.getInput(TestWorkflowPayload.class);
|
||||||
|
workflowPayload.getPayloads().add("Anonymous Activity");
|
||||||
|
return workflowPayload;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.registerActivity("e", new WorkflowActivity() {
|
||||||
|
@Override
|
||||||
|
public Object run(WorkflowActivityContext ctx) {
|
||||||
|
TestWorkflowPayload workflowPayload = ctx.getInput(TestWorkflowPayload.class);
|
||||||
|
workflowPayload.getPayloads().add("Anonymous Activity 2");
|
||||||
|
return workflowPayload;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,19 +30,28 @@ public class WorkflowActivityClassWrapper<T extends WorkflowActivity> implements
|
||||||
/**
|
/**
|
||||||
* Constructor for WorkflowActivityWrapper.
|
* Constructor for WorkflowActivityWrapper.
|
||||||
*
|
*
|
||||||
|
* @param name Name of the activity to wrap.
|
||||||
* @param clazz Class of the activity to wrap.
|
* @param clazz Class of the activity to wrap.
|
||||||
*/
|
*/
|
||||||
public WorkflowActivityClassWrapper(Class<T> clazz) {
|
public WorkflowActivityClassWrapper(String name, Class<T> clazz) {
|
||||||
this.name = clazz.getCanonicalName();
|
this.name = name;
|
||||||
try {
|
try {
|
||||||
this.activityConstructor = clazz.getDeclaredConstructor();
|
this.activityConstructor = clazz.getDeclaredConstructor();
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
String.format("No constructor found for activity class '%s'.", this.name), e
|
String.format("No constructor found for activity class '%s'.", this.name), e);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for WorkflowActivityWrapper.
|
||||||
|
*
|
||||||
|
* @param clazz Class of the activity to wrap.
|
||||||
|
*/
|
||||||
|
public WorkflowActivityClassWrapper(Class<T> clazz) {
|
||||||
|
this(clazz.getCanonicalName(), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
|
@ -58,8 +67,7 @@ public class WorkflowActivityClassWrapper<T extends WorkflowActivity> implements
|
||||||
activity = this.activityConstructor.newInstance();
|
activity = this.activityConstructor.newInstance();
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
String.format("Unable to instantiate instance of activity class '%s'", this.name), e
|
String.format("Unable to instantiate instance of activity class '%s'", this.name), e);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = activity.run(new DefaultWorkflowActivityContext(ctx));
|
result = activity.run(new DefaultWorkflowActivityContext(ctx));
|
||||||
|
|
|
@ -24,14 +24,24 @@ public class WorkflowActivityInstanceWrapper<T extends WorkflowActivity> impleme
|
||||||
private final T activity;
|
private final T activity;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for WorkflowActivityWrapper.
|
||||||
|
*
|
||||||
|
* @param name Name of the activity to wrap.
|
||||||
|
* @param instance Instance of the activity to wrap.
|
||||||
|
*/
|
||||||
|
public WorkflowActivityInstanceWrapper(String name, T instance) {
|
||||||
|
this.name = name;
|
||||||
|
this.activity = instance;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for WorkflowActivityWrapper.
|
* Constructor for WorkflowActivityWrapper.
|
||||||
*
|
*
|
||||||
* @param instance Instance of the activity to wrap.
|
* @param instance Instance of the activity to wrap.
|
||||||
*/
|
*/
|
||||||
public WorkflowActivityInstanceWrapper(T instance) {
|
public WorkflowActivityInstanceWrapper(T instance) {
|
||||||
this.name = instance.getClass().getCanonicalName();
|
this(instance.getClass().getCanonicalName(), instance);
|
||||||
this.activity = instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -149,11 +149,23 @@ public class WorkflowRuntimeBuilder {
|
||||||
* @return the WorkflowRuntimeBuilder
|
* @return the WorkflowRuntimeBuilder
|
||||||
*/
|
*/
|
||||||
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(Class<T> clazz) {
|
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(Class<T> clazz) {
|
||||||
this.builder.addActivity(new WorkflowActivityClassWrapper<>(clazz));
|
return registerActivity(clazz.getCanonicalName(), clazz);
|
||||||
this.activitySet.add(clazz.getCanonicalName());
|
}
|
||||||
this.activities.add(clazz.getSimpleName());
|
|
||||||
|
|
||||||
this.logger.info("Registered Activity: {}", clazz.getSimpleName());
|
/**
|
||||||
|
* Registers an Activity object.
|
||||||
|
*
|
||||||
|
* @param <T> any WorkflowActivity type
|
||||||
|
* @param name Name of the activity to register.
|
||||||
|
* @param clazz Class of the activity to register.
|
||||||
|
* @return the WorkflowRuntimeBuilder
|
||||||
|
*/
|
||||||
|
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(String name, Class<T> clazz) {
|
||||||
|
this.builder.addActivity(new WorkflowActivityClassWrapper<>(name, clazz));
|
||||||
|
this.activitySet.add(name);
|
||||||
|
this.activities.add(name);
|
||||||
|
|
||||||
|
this.logger.info("Registered Activity: {}", name);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -166,13 +178,23 @@ public class WorkflowRuntimeBuilder {
|
||||||
* @return the WorkflowRuntimeBuilder
|
* @return the WorkflowRuntimeBuilder
|
||||||
*/
|
*/
|
||||||
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(T instance) {
|
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(T instance) {
|
||||||
Class<T> clazz = (Class<T>) instance.getClass();
|
return this.registerActivity(instance.getClass().getCanonicalName(), instance);
|
||||||
|
}
|
||||||
|
|
||||||
this.builder.addActivity(new WorkflowActivityInstanceWrapper<>(instance));
|
/**
|
||||||
this.activitySet.add(clazz.getCanonicalName());
|
* Registers an Activity object.
|
||||||
this.activities.add(clazz.getSimpleName());
|
*
|
||||||
|
* @param <T> any WorkflowActivity type
|
||||||
|
* @param name Name of the activity to register.
|
||||||
|
* @param instance the class instance being registered
|
||||||
|
* @return the WorkflowRuntimeBuilder
|
||||||
|
*/
|
||||||
|
public <T extends WorkflowActivity> WorkflowRuntimeBuilder registerActivity(String name, T instance) {
|
||||||
|
this.builder.addActivity(new WorkflowActivityInstanceWrapper<>(name, instance));
|
||||||
|
this.activitySet.add(name);
|
||||||
|
this.activities.add(name);
|
||||||
|
|
||||||
this.logger.info("Registered Activity: {}", clazz.getSimpleName());
|
this.logger.info("Registered Activity: {}", name);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,7 @@ import io.dapr.workflows.WorkflowActivityContext;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class WorkflowActivityClassWrapperTest {
|
public class WorkflowActivityClassWrapperTest {
|
||||||
public static class TestActivity implements WorkflowActivity {
|
public static class TestActivity implements WorkflowActivity {
|
||||||
|
|
|
@ -19,15 +19,13 @@ import io.dapr.workflows.WorkflowStub;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
public class WorkflowRuntimeBuilderTest {
|
public class WorkflowRuntimeBuilderTest {
|
||||||
public static class TestWorkflow implements Workflow {
|
public static class TestWorkflow implements Workflow {
|
||||||
@Override
|
@Override
|
||||||
|
@ -94,6 +92,6 @@ public class WorkflowRuntimeBuilderTest {
|
||||||
.info(eq("Registered Workflow: {}"), eq("TestWorkflow"));
|
.info(eq("Registered Workflow: {}"), eq("TestWorkflow"));
|
||||||
|
|
||||||
verify(testLogger, times(1))
|
verify(testLogger, times(1))
|
||||||
.info(eq("Registered Activity: {}"), eq("TestActivity"));
|
.info(eq("Registered Activity: {}"), eq("io.dapr.workflows.runtime.WorkflowRuntimeBuilderTest.TestActivity"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue