Ensure DurableTask classes are hidden in Workflow Runtime package (#1311)

* Ensure DurableTask classes are hidden in Workflow Runtime package

Signed-off-by: Artur Ciocanu <ciocanu@adobe.com>

* Fix the file header

Signed-off-by: Artur Ciocanu <ciocanu@adobe.com>

---------

Signed-off-by: Artur Ciocanu <ciocanu@adobe.com>
Co-authored-by: Artur Ciocanu <ciocanu@adobe.com>
This commit is contained in:
artur-ciocanu 2025-04-14 19:36:21 +03:00 committed by GitHub
parent 8cb80997cf
commit ef1fc2242a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 601 additions and 461 deletions

View File

@ -15,12 +15,14 @@ package io.dapr.workflows.client;
import com.microsoft.durabletask.DurableTaskClient;
import com.microsoft.durabletask.DurableTaskGrpcClientBuilder;
import com.microsoft.durabletask.NewOrchestrationInstanceOptions;
import com.microsoft.durabletask.OrchestrationMetadata;
import com.microsoft.durabletask.PurgeResult;
import io.dapr.config.Properties;
import io.dapr.utils.NetworkUtils;
import io.dapr.workflows.Workflow;
import io.dapr.workflows.internal.ApiTokenClientInterceptor;
import io.dapr.workflows.runtime.DefaultWorkflowInstanceStatus;
import io.grpc.ClientInterceptor;
import io.grpc.ManagedChannel;
@ -76,18 +78,6 @@ public class DaprWorkflowClient implements AutoCloseable {
this.grpcChannel = grpcChannel;
}
/**
* Static method to create the DurableTaskClient.
*
* @param grpcChannel ManagedChannel for GRPC.
* @return a new instance of a DurableTaskClient with a GRPC channel.
*/
private static DurableTaskClient createDurableTaskClient(ManagedChannel grpcChannel) {
return new DurableTaskGrpcClientBuilder()
.grpcChannel(grpcChannel)
.build();
}
/**
* Schedules a new workflow using DurableTask client.
*
@ -133,8 +123,10 @@ public class DaprWorkflowClient implements AutoCloseable {
* @return the <code>instanceId</code> parameter value.
*/
public <T extends Workflow> String scheduleNewWorkflow(Class<T> clazz, NewWorkflowOptions options) {
NewOrchestrationInstanceOptions orchestrationInstanceOptions = fromNewWorkflowOptions(options);
return this.innerClient.scheduleNewOrchestrationInstance(clazz.getCanonicalName(),
options.getNewOrchestrationInstanceOptions());
orchestrationInstanceOptions);
}
/**
@ -158,10 +150,8 @@ public class DaprWorkflowClient implements AutoCloseable {
@Nullable
public WorkflowInstanceStatus getInstanceState(String instanceId, boolean getInputsAndOutputs) {
OrchestrationMetadata metadata = this.innerClient.getInstanceMetadata(instanceId, getInputsAndOutputs);
if (metadata == null) {
return null;
}
return new WorkflowInstanceStatus(metadata);
return metadata == null ? null : new DefaultWorkflowInstanceStatus(metadata);
}
/**
@ -186,7 +176,8 @@ public class DaprWorkflowClient implements AutoCloseable {
throws TimeoutException {
OrchestrationMetadata metadata = this.innerClient.waitForInstanceStart(instanceId, timeout, getInputsAndOutputs);
return metadata == null ? null : new WorkflowInstanceStatus(metadata);
return metadata == null ? null : new DefaultWorkflowInstanceStatus(metadata);
}
/**
@ -210,11 +201,11 @@ public class DaprWorkflowClient implements AutoCloseable {
*/
@Nullable
public WorkflowInstanceStatus waitForInstanceCompletion(String instanceId, Duration timeout,
boolean getInputsAndOutputs) throws TimeoutException {
boolean getInputsAndOutputs) throws TimeoutException {
OrchestrationMetadata metadata =
this.innerClient.waitForInstanceCompletion(instanceId, timeout, getInputsAndOutputs);
return metadata == null ? null : new WorkflowInstanceStatus(metadata);
OrchestrationMetadata metadata = this.innerClient.waitForInstanceCompletion(instanceId, timeout,
getInputsAndOutputs);
return metadata == null ? null : new DefaultWorkflowInstanceStatus(metadata);
}
/**
@ -236,20 +227,14 @@ public class DaprWorkflowClient implements AutoCloseable {
*/
public boolean purgeInstance(String workflowInstanceId) {
PurgeResult result = this.innerClient.purgeInstance(workflowInstanceId);
if (result != null) {
return result.getDeletedInstanceCount() > 0;
}
return false;
}
public void createTaskHub(boolean recreateIfExists) {
this.innerClient.createTaskHub(recreateIfExists);
}
public void deleteTaskHub() {
this.innerClient.deleteTaskHub();
}
/**
* Closes the inner DurableTask client and shutdown the GRPC channel.
*/
@ -267,6 +252,38 @@ public class DaprWorkflowClient implements AutoCloseable {
}
}
/**
* Static method to create the DurableTaskClient.
*
* @param grpcChannel ManagedChannel for GRPC.
* @return a new instance of a DurableTaskClient with a GRPC channel.
*/
private static DurableTaskClient createDurableTaskClient(ManagedChannel grpcChannel) {
return new DurableTaskGrpcClientBuilder()
.grpcChannel(grpcChannel)
.build();
}
private static NewOrchestrationInstanceOptions fromNewWorkflowOptions(NewWorkflowOptions options) {
NewOrchestrationInstanceOptions instanceOptions = new NewOrchestrationInstanceOptions();
if (options.getVersion() != null) {
instanceOptions.setVersion(options.getVersion());
}
if (options.getInstanceId() != null) {
instanceOptions.setInstanceId(options.getInstanceId());
}
if (options.getInput() != null) {
instanceOptions.setInput(options.getInput());
}
if (options.getStartTime() != null) {
instanceOptions.setStartTime(options.getStartTime());
}
return instanceOptions;
}
}

View File

@ -13,15 +13,17 @@ limitations under the License.
package io.dapr.workflows.client;
import com.microsoft.durabletask.NewOrchestrationInstanceOptions;
import java.time.Instant;
/**
* Options for starting a new instance of a workflow.
*/
public class NewWorkflowOptions {
private final NewOrchestrationInstanceOptions newOrchestrationInstanceOptions = new NewOrchestrationInstanceOptions();
private String version;
private String instanceId;
private Object input;
private Instant startTime;
/**
* Sets the version of the workflow to start.
@ -30,7 +32,7 @@ public class NewWorkflowOptions {
* @return this {@link NewWorkflowOptions} object
*/
public NewWorkflowOptions setVersion(String version) {
this.newOrchestrationInstanceOptions.setVersion(version);
this.version = version;
return this;
}
@ -43,7 +45,7 @@ public class NewWorkflowOptions {
* @return this {@link NewWorkflowOptions} object
*/
public NewWorkflowOptions setInstanceId(String instanceId) {
this.newOrchestrationInstanceOptions.setInstanceId(instanceId);
this.instanceId = instanceId;
return this;
}
@ -54,7 +56,7 @@ public class NewWorkflowOptions {
* @return this {@link NewWorkflowOptions} object
*/
public NewWorkflowOptions setInput(Object input) {
this.newOrchestrationInstanceOptions.setInput(input);
this.input = input;
return this;
}
@ -68,7 +70,7 @@ public class NewWorkflowOptions {
* @return this {@link NewWorkflowOptions} object
*/
public NewWorkflowOptions setStartTime(Instant startTime) {
this.newOrchestrationInstanceOptions.setStartTime(startTime);
this.startTime = startTime;
return this;
}
@ -78,7 +80,7 @@ public class NewWorkflowOptions {
* @return the user-specified version of the new workflow.
*/
public String getVersion() {
return this.newOrchestrationInstanceOptions.getVersion();
return this.version;
}
/**
@ -87,7 +89,7 @@ public class NewWorkflowOptions {
* @return the instance ID of the new workflow.
*/
public String getInstanceId() {
return this.newOrchestrationInstanceOptions.getInstanceId();
return this.instanceId;
}
/**
@ -96,7 +98,7 @@ public class NewWorkflowOptions {
* @return the input of the new workflow.
*/
public Object getInput() {
return this.newOrchestrationInstanceOptions.getInput();
return this.input;
}
/**
@ -105,10 +107,7 @@ public class NewWorkflowOptions {
* @return the configured start time of the new workflow instance.
*/
public Instant getStartTime() {
return this.newOrchestrationInstanceOptions.getStartTime();
return this.startTime;
}
public NewOrchestrationInstanceOptions getNewOrchestrationInstanceOptions() {
return newOrchestrationInstanceOptions;
}
}

View File

@ -13,53 +13,30 @@ limitations under the License.
package io.dapr.workflows.client;
import com.microsoft.durabletask.FailureDetails;
/**
* Represents a workflow failure details.
*/
public class WorkflowFailureDetails {
private final FailureDetails workflowFailureDetails;
/**
* Class constructor.
*
* @param failureDetails failure Details
*/
public WorkflowFailureDetails(FailureDetails failureDetails) {
this.workflowFailureDetails = failureDetails;
}
public interface WorkflowFailureDetails {
/**
* Gets the error type, which is the namespace-qualified exception type name.
*
* @return the error type, which is the namespace-qualified exception type name
*/
public String getErrorType() {
return workflowFailureDetails.getErrorType();
}
String getErrorType();
/**
* Gets the error message.
*
* @return the error message
*/
public String getErrorMessage() {
return workflowFailureDetails.getErrorMessage();
}
String getErrorMessage();
/**
* Gets the stack trace.
*
* @return the stack trace
*/
public String getStackTrace() {
return workflowFailureDetails.getStackTrace();
}
String getStackTrace();
@Override
public String toString() {
return workflowFailureDetails.toString();
}
}

View File

@ -13,11 +13,6 @@ limitations under the License.
package io.dapr.workflows.client;
import com.microsoft.durabletask.FailureDetails;
import com.microsoft.durabletask.OrchestrationMetadata;
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
import io.dapr.workflows.runtime.WorkflowRuntimeStatus;
import javax.annotation.Nullable;
import java.time.Instant;
@ -26,48 +21,21 @@ import java.time.Instant;
* Represents a snapshot of a workflow instance's current state, including
* metadata.
*/
public class WorkflowInstanceStatus {
private final OrchestrationMetadata orchestrationMetadata;
@Nullable
private final WorkflowFailureDetails failureDetails;
/**
* Class constructor.
*
* @param orchestrationMetadata Durable task orchestration metadata
*/
public WorkflowInstanceStatus(OrchestrationMetadata orchestrationMetadata) {
if (orchestrationMetadata == null) {
throw new IllegalArgumentException("OrchestrationMetadata cannot be null");
}
this.orchestrationMetadata = orchestrationMetadata;
FailureDetails details = orchestrationMetadata.getFailureDetails();
if (details != null) {
this.failureDetails = new WorkflowFailureDetails(details);
} else {
this.failureDetails = null;
}
}
public interface WorkflowInstanceStatus {
/**
* Gets the name of the workflow.
*
* @return the name of the workflow
*/
public String getName() {
return orchestrationMetadata.getName();
}
String getName();
/**
* Gets the unique ID of the workflow instance.
*
* @return the unique ID of the workflow instance
*/
public String getInstanceId() {
return orchestrationMetadata.getInstanceId();
}
String getInstanceId();
/**
* Gets the current runtime status of the workflow instance at the time this
@ -75,60 +43,48 @@ public class WorkflowInstanceStatus {
*
* @return the current runtime status of the workflow instance at the time this object was fetched
*/
public WorkflowRuntimeStatus getRuntimeStatus() {
return WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(orchestrationMetadata.getRuntimeStatus());
}
WorkflowRuntimeStatus getRuntimeStatus();
/**
* Gets the workflow instance's creation time in UTC.
*
* @return the workflow instance's creation time in UTC
*/
public Instant getCreatedAt() {
return orchestrationMetadata.getCreatedAt();
}
Instant getCreatedAt();
/**
* Gets the workflow instance's last updated time in UTC.
*
* @return the workflow instance's last updated time in UTC
*/
public Instant getLastUpdatedAt() {
return orchestrationMetadata.getLastUpdatedAt();
}
Instant getLastUpdatedAt();
/**
* Gets the workflow instance's serialized input, if any, as a string value.
*
* @return the workflow instance's serialized input or {@code null}
*/
public String getSerializedInput() {
return orchestrationMetadata.getSerializedInput();
}
String getSerializedInput();
/**
* Gets the workflow instance's serialized output, if any, as a string value.
*
* @return the workflow instance's serialized output or {@code null}
*/
public String getSerializedOutput() {
return orchestrationMetadata.getSerializedOutput();
}
String getSerializedOutput();
/**
* Gets the failure details, if any, for the failed workflow instance.
*
* <p>This method returns data only if the workflow is in the
* {@link OrchestrationRuntimeStatus#FAILED} state,
* {@link WorkflowFailureDetails} failureDetails,
* and only if this instance metadata was fetched with the option to include
* output data.
*
* @return the failure details of the failed workflow instance or {@code null}
*/
@Nullable
public WorkflowFailureDetails getFailureDetails() {
return this.failureDetails;
}
WorkflowFailureDetails getFailureDetails();
/**
* Gets a value indicating whether the workflow instance was running at the time
@ -136,9 +92,7 @@ public class WorkflowInstanceStatus {
*
* @return {@code true} if the workflow existed and was in a running state otherwise {@code false}
*/
public boolean isRunning() {
return orchestrationMetadata.isRunning();
}
boolean isRunning();
/**
* Gets a value indicating whether the workflow instance was completed at the
@ -151,9 +105,7 @@ public class WorkflowInstanceStatus {
*
* @return {@code true} if the workflow was in a terminal state; otherwise {@code false}
*/
public boolean isCompleted() {
return orchestrationMetadata.isCompleted();
}
boolean isCompleted();
/**
* Deserializes the workflow's input into an object of the specified type.
@ -169,9 +121,7 @@ public class WorkflowInstanceStatus {
* @throws IllegalStateException if the metadata was fetched without the option
* to read inputs and outputs
*/
public <T> T readInputAs(Class<T> type) {
return orchestrationMetadata.readInputAs(type);
}
<T> T readInputAs(Class<T> type);
/**
* Deserializes the workflow's output into an object of the specified type.
@ -187,17 +137,6 @@ public class WorkflowInstanceStatus {
* @throws IllegalStateException if the metadata was fetched without the option
* to read inputs and outputs
*/
public <T> T readOutputAs(Class<T> type) {
return orchestrationMetadata.readOutputAs(type);
}
<T> T readOutputAs(Class<T> type);
/**
* Generates a user-friendly string representation of the current metadata
* object.
*
* @return a user-friendly string representation of the current metadata object
*/
public String toString() {
return orchestrationMetadata.toString();
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2023 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.workflows.client;
/**
* Enum describing the runtime status of a workflow.
*/
public enum WorkflowRuntimeStatus {
/**
* The workflow started running.
*/
RUNNING,
/**
* The workflow completed normally.
*/
COMPLETED,
/**
* The workflow is continued as new.
*/
CONTINUED_AS_NEW,
/**
* The workflow completed with an unhandled exception.
*/
FAILED,
/**
* The workflow was abruptly cancelled via a management API call.
*/
CANCELED,
/**
* The workflow was abruptly terminated via a management API call.
*/
TERMINATED,
/**
* The workflow was scheduled but hasn't started running.
*/
PENDING,
/**
* The workflow was suspended.
*/
SUSPENDED
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2023 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.workflows.runtime;
import com.microsoft.durabletask.FailureDetails;
import io.dapr.workflows.client.WorkflowFailureDetails;
/**
* Represents a workflow failure details.
*/
public class DefaultWorkflowFailureDetails implements WorkflowFailureDetails {
private final FailureDetails workflowFailureDetails;
/**
* Class constructor.
*
* @param failureDetails failure Details
*/
public DefaultWorkflowFailureDetails(FailureDetails failureDetails) {
this.workflowFailureDetails = failureDetails;
}
/**
* Gets the error type, which is the namespace-qualified exception type name.
*
* @return the error type, which is the namespace-qualified exception type name
*/
@Override
public String getErrorType() {
return workflowFailureDetails.getErrorType();
}
/**
* Gets the error message.
*
* @return the error message
*/
@Override
public String getErrorMessage() {
return workflowFailureDetails.getErrorMessage();
}
/**
* Gets the stack trace.
*
* @return the stack trace
*/
@Override
public String getStackTrace() {
return workflowFailureDetails.getStackTrace();
}
@Override
public String toString() {
return workflowFailureDetails.toString();
}
}

View File

@ -0,0 +1,210 @@
/*
* Copyright 2023 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.workflows.runtime;
import com.microsoft.durabletask.FailureDetails;
import com.microsoft.durabletask.OrchestrationMetadata;
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
import io.dapr.workflows.client.WorkflowFailureDetails;
import io.dapr.workflows.client.WorkflowInstanceStatus;
import io.dapr.workflows.client.WorkflowRuntimeStatus;
import javax.annotation.Nullable;
import java.time.Instant;
/**
* Represents a snapshot of a workflow instance's current state, including
* metadata.
*/
public class DefaultWorkflowInstanceStatus implements WorkflowInstanceStatus {
private final OrchestrationMetadata orchestrationMetadata;
@Nullable
private final WorkflowFailureDetails failureDetails;
/**
* Class constructor.
*
* @param orchestrationMetadata Durable task orchestration metadata
*/
public DefaultWorkflowInstanceStatus(OrchestrationMetadata orchestrationMetadata) {
if (orchestrationMetadata == null) {
throw new IllegalArgumentException("OrchestrationMetadata cannot be null");
}
this.orchestrationMetadata = orchestrationMetadata;
FailureDetails details = orchestrationMetadata.getFailureDetails();
if (details != null) {
this.failureDetails = new DefaultWorkflowFailureDetails(details);
} else {
this.failureDetails = null;
}
}
/**
* Gets the name of the workflow.
*
* @return the name of the workflow
*/
public String getName() {
return orchestrationMetadata.getName();
}
/**
* Gets the unique ID of the workflow instance.
*
* @return the unique ID of the workflow instance
*/
public String getInstanceId() {
return orchestrationMetadata.getInstanceId();
}
/**
* Gets the current runtime status of the workflow instance at the time this
* object was fetched.
*
* @return the current runtime status of the workflow instance at the time this object was fetched
*/
public WorkflowRuntimeStatus getRuntimeStatus() {
OrchestrationRuntimeStatus status = orchestrationMetadata.getRuntimeStatus();
return WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(status);
}
/**
* Gets the workflow instance's creation time in UTC.
*
* @return the workflow instance's creation time in UTC
*/
public Instant getCreatedAt() {
return orchestrationMetadata.getCreatedAt();
}
/**
* Gets the workflow instance's last updated time in UTC.
*
* @return the workflow instance's last updated time in UTC
*/
public Instant getLastUpdatedAt() {
return orchestrationMetadata.getLastUpdatedAt();
}
/**
* Gets the workflow instance's serialized input, if any, as a string value.
*
* @return the workflow instance's serialized input or {@code null}
*/
public String getSerializedInput() {
return orchestrationMetadata.getSerializedInput();
}
/**
* Gets the workflow instance's serialized output, if any, as a string value.
*
* @return the workflow instance's serialized output or {@code null}
*/
public String getSerializedOutput() {
return orchestrationMetadata.getSerializedOutput();
}
/**
* Gets the failure details, if any, for the failed workflow instance.
*
* <p>This method returns data only if the workflow is in the
* {@link OrchestrationRuntimeStatus#FAILED} state,
* and only if this instance metadata was fetched with the option to include
* output data.
*
* @return the failure details of the failed workflow instance or {@code null}
*/
@Nullable
public WorkflowFailureDetails getFailureDetails() {
return this.failureDetails;
}
/**
* Gets a value indicating whether the workflow instance was running at the time
* this object was fetched.
*
* @return {@code true} if the workflow existed and was in a running state otherwise {@code false}
*/
public boolean isRunning() {
return orchestrationMetadata.isRunning();
}
/**
* Gets a value indicating whether the workflow instance was completed at the
* time this object was fetched.
*
* <p>A workflow instance is considered completed when its runtime status value is
* {@link WorkflowRuntimeStatus#COMPLETED},
* {@link WorkflowRuntimeStatus#FAILED}, or
* {@link WorkflowRuntimeStatus#TERMINATED}.
*
* @return {@code true} if the workflow was in a terminal state; otherwise {@code false}
*/
public boolean isCompleted() {
return orchestrationMetadata.isCompleted();
}
/**
* Deserializes the workflow's input into an object of the specified type.
*
* <p>Deserialization is performed using the DataConverter that was
* configured on the DurableTaskClient object that created this workflow
* metadata object.
*
* @param type the class associated with the type to deserialize the input data
* into
* @param <T> the type to deserialize the input data into
* @return the deserialized input value
* @throws IllegalStateException if the metadata was fetched without the option
* to read inputs and outputs
*/
public <T> T readInputAs(Class<T> type) {
return orchestrationMetadata.readInputAs(type);
}
/**
* Deserializes the workflow's output into an object of the specified type.
*
* <p>Deserialization is performed using the DataConverter that was
* configured on the DurableTaskClient
* object that created this workflow metadata object.
*
* @param type the class associated with the type to deserialize the output data
* into
* @param <T> the type to deserialize the output data into
* @return the deserialized input value
* @throws IllegalStateException if the metadata was fetched without the option
* to read inputs and outputs
*/
public <T> T readOutputAs(Class<T> type) {
return orchestrationMetadata.readOutputAs(type);
}
/**
* Generates a user-friendly string representation of the current metadata
* object.
*
* @return a user-friendly string representation of the current metadata object
*/
public String toString() {
return orchestrationMetadata.toString();
}
}

View File

@ -1,139 +0,0 @@
/*
* Copyright 2023 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.workflows.runtime;
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
import java.util.List;
import java.util.stream.Collectors;
/**
* Enum describing the runtime status of a workflow.
*/
public enum WorkflowRuntimeStatus {
/**
* The workflow started running.
*/
RUNNING,
/**
* The workflow completed normally.
*/
COMPLETED,
/**
* The workflow is continued as new.
*/
CONTINUED_AS_NEW,
/**
* The workflow completed with an unhandled exception.
*/
FAILED,
/**
* The workflow was abruptly cancelled via a management API call.
*/
CANCELED,
/**
* The workflow was abruptly terminated via a management API call.
*/
TERMINATED,
/**
* The workflow was scheduled but hasn't started running.
*/
PENDING,
/**
* The workflow was suspended.
*/
SUSPENDED;
/**
* Convert runtime OrchestrationRuntimeStatus to WorkflowRuntimeStatus.
*
* @param status The OrchestrationRuntimeStatus to convert to WorkflowRuntimeStatus.
* @return The runtime status of the workflow.
*/
public static WorkflowRuntimeStatus fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus status) {
if (status == null) {
throw new IllegalArgumentException("status cannot be null");
}
switch (status) {
case RUNNING:
return WorkflowRuntimeStatus.RUNNING;
case COMPLETED:
return WorkflowRuntimeStatus.COMPLETED;
case CONTINUED_AS_NEW:
return WorkflowRuntimeStatus.CONTINUED_AS_NEW;
case FAILED:
return WorkflowRuntimeStatus.FAILED;
case CANCELED:
return WorkflowRuntimeStatus.CANCELED;
case TERMINATED:
return WorkflowRuntimeStatus.TERMINATED;
case PENDING:
return WorkflowRuntimeStatus.PENDING;
case SUSPENDED:
return WorkflowRuntimeStatus.SUSPENDED;
default:
throw new RuntimeException(String.format("Unknown status value: %s", status));
}
}
/**
* Convert runtime WorkflowRuntimeStatus to OrchestrationRuntimeStatus.
*
* @param status The OrchestrationRuntimeStatus to convert to WorkflowRuntimeStatus.
* @return The runtime status of the Orchestration.
*/
public static OrchestrationRuntimeStatus toOrchestrationRuntimeStatus(WorkflowRuntimeStatus status) {
switch (status) {
case RUNNING:
return OrchestrationRuntimeStatus.RUNNING;
case COMPLETED:
return OrchestrationRuntimeStatus.COMPLETED;
case CONTINUED_AS_NEW:
return OrchestrationRuntimeStatus.CONTINUED_AS_NEW;
case FAILED:
return OrchestrationRuntimeStatus.FAILED;
case CANCELED:
return OrchestrationRuntimeStatus.CANCELED;
case TERMINATED:
return OrchestrationRuntimeStatus.TERMINATED;
case PENDING:
return OrchestrationRuntimeStatus.PENDING;
case SUSPENDED:
return OrchestrationRuntimeStatus.SUSPENDED;
default:
throw new RuntimeException(String.format("Unknown status value: %s", status));
}
}
/**
* Convert runtime WorkflowRuntimeStatus to OrchestrationRuntimeStatus.
*
* @param statuses The list of orchestrationRuntimeStatus to convert to a list of WorkflowRuntimeStatuses.
* @return The list runtime status of the Orchestration.
*/
public static List<OrchestrationRuntimeStatus> toOrchestrationRuntimeStatus(List<WorkflowRuntimeStatus> statuses) {
return statuses.stream()
.map(x -> toOrchestrationRuntimeStatus(x))
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.workflows.runtime;
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
import io.dapr.workflows.client.WorkflowRuntimeStatus;
public class WorkflowRuntimeStatusConverter {
private WorkflowRuntimeStatusConverter() {
}
/**
* Converts an OrchestrationRuntimeStatus to a WorkflowRuntimeStatus.
*
* @param status the OrchestrationRuntimeStatus to convert
* @return the corresponding WorkflowRuntimeStatus
* @throws IllegalArgumentException if the status is null or unknown
*/
public static WorkflowRuntimeStatus fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus status) {
if (status == null) {
throw new IllegalArgumentException("status cannot be null");
}
switch (status) {
case RUNNING:
return WorkflowRuntimeStatus.RUNNING;
case COMPLETED:
return WorkflowRuntimeStatus.COMPLETED;
case CONTINUED_AS_NEW:
return WorkflowRuntimeStatus.CONTINUED_AS_NEW;
case FAILED:
return WorkflowRuntimeStatus.FAILED;
case CANCELED:
return WorkflowRuntimeStatus.CANCELED;
case TERMINATED:
return WorkflowRuntimeStatus.TERMINATED;
case PENDING:
return WorkflowRuntimeStatus.PENDING;
case SUSPENDED:
return WorkflowRuntimeStatus.SUSPENDED;
default:
throw new IllegalArgumentException(String.format("Unknown status value: %s", status));
}
}
}

View File

@ -14,6 +14,7 @@ limitations under the License.
package io.dapr.workflows.client;
import com.microsoft.durabletask.DurableTaskClient;
import com.microsoft.durabletask.NewOrchestrationInstanceOptions;
import com.microsoft.durabletask.OrchestrationMetadata;
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
import io.dapr.workflows.Workflow;
@ -23,6 +24,7 @@ import io.grpc.ManagedChannel;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.lang.reflect.Constructor;
import java.time.Duration;
@ -33,12 +35,15 @@ import java.util.concurrent.TimeoutException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.ArgumentMatchers.any;
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 static org.mockito.Mockito.when;
public class DaprWorkflowClientTest {
private static Constructor<DaprWorkflowClient> constructor;
private DaprWorkflowClient client;
private DurableTaskClient mockInnerClient;
@ -65,6 +70,7 @@ public class DaprWorkflowClientTest {
public void setUp() throws Exception {
mockInnerClient = mock(DurableTaskClient.class);
mockGrpcChannel = mock(ManagedChannel.class);
when(mockGrpcChannel.shutdown()).thenReturn(mockGrpcChannel);
client = constructor.newInstance(mockInnerClient, mockGrpcChannel);
@ -110,14 +116,23 @@ public class DaprWorkflowClientTest {
@Test
public void scheduleNewWorkflowWithNewWorkflowOption() {
String expectedName = TestWorkflow.class.getCanonicalName();
Instant expectedStartTime = Instant.now();
Object expectedInput = new Object();
NewWorkflowOptions newWorkflowOptions = new NewWorkflowOptions();
newWorkflowOptions.setInput(expectedInput).setStartTime(Instant.now());
newWorkflowOptions.setInput(expectedInput).setStartTime(expectedStartTime);
mockInnerClient.scheduleNewOrchestrationInstance(any(String.class), any(NewOrchestrationInstanceOptions.class));
client.scheduleNewWorkflow(TestWorkflow.class, newWorkflowOptions);
ArgumentCaptor<NewOrchestrationInstanceOptions> captor = ArgumentCaptor.forClass(
NewOrchestrationInstanceOptions.class
);
verify(mockInnerClient, times(1))
.scheduleNewOrchestrationInstance(expectedName, newWorkflowOptions.getNewOrchestrationInstanceOptions());
.scheduleNewOrchestrationInstance(eq(expectedName), captor.capture());
assertEquals(expectedStartTime, captor.getValue().getStartTime());
assertEquals(expectedInput, captor.getValue().getInput());
}
@Test
@ -209,19 +224,6 @@ public class DaprWorkflowClientTest {
verify(mockInnerClient, times(1)).purgeInstance(expectedArgument);
}
@Test
public void createTaskHub() {
boolean expectedArgument = true;
client.createTaskHub(expectedArgument);
verify(mockInnerClient, times(1)).createTaskHub(expectedArgument);
}
@Test
public void deleteTaskHub() {
client.deleteTaskHub();
verify(mockInnerClient, times(1)).deleteTaskHub();
}
@Test
public void close() throws InterruptedException {
client.close();

View File

@ -16,90 +16,80 @@ package io.dapr.workflows.client;
import com.microsoft.durabletask.FailureDetails;
import com.microsoft.durabletask.OrchestrationMetadata;
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
import io.dapr.workflows.runtime.WorkflowRuntimeStatus;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import io.dapr.workflows.runtime.DefaultWorkflowInstanceStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.time.Instant;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class WorkflowInstanceStatusTest {
private OrchestrationMetadata mockOrchestrationMetadata;
private WorkflowInstanceStatus workflowMetadata;
@Before
public void setUp() throws Exception {
@BeforeEach
public void setUp() {
mockOrchestrationMetadata = mock(OrchestrationMetadata.class);
workflowMetadata = new WorkflowInstanceStatus(mockOrchestrationMetadata);
workflowMetadata = new DefaultWorkflowInstanceStatus(mockOrchestrationMetadata);
}
@Test
public void getInstanceId() {
// Arrange
String expected = "instanceId";
when(mockOrchestrationMetadata.getInstanceId()).thenReturn(expected);
// Act
String result = workflowMetadata.getInstanceId();
// Assert
verify(mockOrchestrationMetadata, times(1)).getInstanceId();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void getName() {
// Arrange
String expected = "WorkflowName";
when(mockOrchestrationMetadata.getName()).thenReturn(expected);
// Act
String result = workflowMetadata.getName();
// Assert
verify(mockOrchestrationMetadata, times(1)).getName();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void getCreatedAt() {
// Arrange
Instant expected = Instant.now();
when(mockOrchestrationMetadata.getCreatedAt()).thenReturn(expected);
// Act
Instant result = workflowMetadata.getCreatedAt();
// Assert
verify(mockOrchestrationMetadata, times(1)).getCreatedAt();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void getLastUpdatedAt() {
// Arrange
Instant expected = Instant.now();
when(mockOrchestrationMetadata.getLastUpdatedAt()).thenReturn(expected);
// Act
Instant result = workflowMetadata.getLastUpdatedAt();
// Assert
verify(mockOrchestrationMetadata, times(1)).getLastUpdatedAt();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void getFailureDetails() {
// Arrange
FailureDetails mockFailureDetails = mock(FailureDetails.class);
when(mockFailureDetails.getErrorType()).thenReturn("errorType");
when(mockFailureDetails.getErrorMessage()).thenReturn("errorMessage");
when(mockFailureDetails.getStackTrace()).thenReturn("stackTrace");
@ -107,126 +97,108 @@ public class WorkflowInstanceStatusTest {
OrchestrationMetadata orchestrationMetadata = mock(OrchestrationMetadata.class);
when(orchestrationMetadata.getFailureDetails()).thenReturn(mockFailureDetails);
// Act
WorkflowInstanceStatus metadata = new WorkflowInstanceStatus(orchestrationMetadata);
WorkflowInstanceStatus metadata = new DefaultWorkflowInstanceStatus(orchestrationMetadata);
WorkflowFailureDetails result = metadata.getFailureDetails();
// Assert
verify(orchestrationMetadata, times(1)).getFailureDetails();
Assert.assertEquals(result.getErrorType(), mockFailureDetails.getErrorType());
Assert.assertEquals(result.getErrorMessage(), mockFailureDetails.getErrorMessage());
Assert.assertEquals(result.getStackTrace(), mockFailureDetails.getStackTrace());
assertEquals(mockFailureDetails.getErrorType(), result.getErrorType());
assertEquals(mockFailureDetails.getErrorMessage(), result.getErrorMessage());
assertEquals(mockFailureDetails.getStackTrace(), result.getStackTrace());
}
@Test
public void getRuntimeStatus() {
// Arrange
WorkflowRuntimeStatus expected = WorkflowRuntimeStatus.RUNNING;
when(mockOrchestrationMetadata.getRuntimeStatus()).thenReturn(OrchestrationRuntimeStatus.RUNNING);
// Act
WorkflowRuntimeStatus result = workflowMetadata.getRuntimeStatus();
// Assert
verify(mockOrchestrationMetadata, times(1)).getRuntimeStatus();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void isRunning() {
// Arrange
boolean expected = true;
when(mockOrchestrationMetadata.isRunning()).thenReturn(expected);
// Act
boolean result = workflowMetadata.isRunning();
// Assert
verify(mockOrchestrationMetadata, times(1)).isRunning();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void isCompleted() {
// Arrange
boolean expected = true;
when(mockOrchestrationMetadata.isCompleted()).thenReturn(expected);
// Act
boolean result = workflowMetadata.isCompleted();
// Assert
verify(mockOrchestrationMetadata, times(1)).isCompleted();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void getSerializedInput() {
// Arrange
String expected = "{input: \"test\"}";
when(mockOrchestrationMetadata.getSerializedInput()).thenReturn(expected);
// Act
String result = workflowMetadata.getSerializedInput();
// Assert
verify(mockOrchestrationMetadata, times(1)).getSerializedInput();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void getSerializedOutput() {
// Arrange
String expected = "{output: \"test\"}";
when(mockOrchestrationMetadata.getSerializedOutput()).thenReturn(expected);
// Act
String result = workflowMetadata.getSerializedOutput();
// Assert
verify(mockOrchestrationMetadata, times(1)).getSerializedOutput();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void readInputAs() {
// Arrange
String expected = "[{property: \"test input\"}}]";
when(mockOrchestrationMetadata.readInputAs(String.class)).thenReturn(expected);
// Act
String result = workflowMetadata.readInputAs(String.class);
// Assert
verify(mockOrchestrationMetadata, times(1)).readInputAs(String.class);
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void readOutputAs() {
// Arrange
String expected = "[{property: \"test output\"}}]";
when(mockOrchestrationMetadata.readOutputAs(String.class)).thenReturn(expected);
// Act
String result = workflowMetadata.readOutputAs(String.class);
// Assert
verify(mockOrchestrationMetadata, times(1)).readOutputAs(String.class);
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
@Test
public void testToString() {
// Arrange
String expected = "string value";
when(mockOrchestrationMetadata.toString()).thenReturn(expected);
// Act
String result = workflowMetadata.toString();
// Assert
//verify(mockOrchestrationMetadata, times(1)).toString();
Assert.assertEquals(result, expected);
assertEquals(expected, result);
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2023 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.workflows.runtime;
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
import io.dapr.workflows.client.WorkflowRuntimeStatus;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class WorkflowRuntimeStatusConverterTest {
@Test
public void fromOrchestrationRuntimeStatus() {
assertEquals(WorkflowRuntimeStatus.RUNNING,
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.RUNNING)
);
assertEquals(WorkflowRuntimeStatus.COMPLETED,
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.COMPLETED)
);
assertEquals(WorkflowRuntimeStatus.CONTINUED_AS_NEW,
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.CONTINUED_AS_NEW)
);
assertEquals(WorkflowRuntimeStatus.FAILED,
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.FAILED)
);
assertEquals(WorkflowRuntimeStatus.CANCELED,
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.CANCELED)
);
assertEquals(WorkflowRuntimeStatus.TERMINATED,
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.TERMINATED)
);
assertEquals(WorkflowRuntimeStatus.PENDING,
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.PENDING)
);
assertEquals(WorkflowRuntimeStatus.SUSPENDED,
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.SUSPENDED)
);
}
@Test
public void fromOrchestrationRuntimeStatusThrowsIllegalArgumentException() {
try {
WorkflowRuntimeStatusConverter.fromOrchestrationRuntimeStatus(null);
fail("Expected exception not thrown");
} catch (IllegalArgumentException e) {
assertEquals("status cannot be null", e.getMessage());
}
}
}

View File

@ -1,96 +0,0 @@
/*
* Copyright 2023 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.workflows.runtime;
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class WorkflowRuntimeStatusTest {
@Before
public void setUp() throws Exception {
}
@Test
public void fromOrchestrationRuntimeStatus() {
Assert.assertEquals(WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.RUNNING),
WorkflowRuntimeStatus.RUNNING);
Assert.assertEquals(WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.COMPLETED),
WorkflowRuntimeStatus.COMPLETED);
Assert.assertEquals(
WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.CONTINUED_AS_NEW),
WorkflowRuntimeStatus.CONTINUED_AS_NEW);
Assert.assertEquals(WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.FAILED),
WorkflowRuntimeStatus.FAILED);
Assert.assertEquals(WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.CANCELED),
WorkflowRuntimeStatus.CANCELED);
Assert.assertEquals(WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.TERMINATED),
WorkflowRuntimeStatus.TERMINATED);
Assert.assertEquals(WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.PENDING),
WorkflowRuntimeStatus.PENDING);
Assert.assertEquals(WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(OrchestrationRuntimeStatus.SUSPENDED),
WorkflowRuntimeStatus.SUSPENDED);
}
@Test
public void toOrchestrationRuntimeStatus() {
Assert.assertEquals(WorkflowRuntimeStatus.toOrchestrationRuntimeStatus(WorkflowRuntimeStatus.RUNNING),
OrchestrationRuntimeStatus.RUNNING);
Assert.assertEquals(WorkflowRuntimeStatus.toOrchestrationRuntimeStatus(WorkflowRuntimeStatus.COMPLETED),
OrchestrationRuntimeStatus.COMPLETED);
Assert.assertEquals(
WorkflowRuntimeStatus.toOrchestrationRuntimeStatus(WorkflowRuntimeStatus.CONTINUED_AS_NEW),
OrchestrationRuntimeStatus.CONTINUED_AS_NEW);
Assert.assertEquals(WorkflowRuntimeStatus.toOrchestrationRuntimeStatus(WorkflowRuntimeStatus.FAILED),
OrchestrationRuntimeStatus.FAILED);
Assert.assertEquals(WorkflowRuntimeStatus.toOrchestrationRuntimeStatus(WorkflowRuntimeStatus.CANCELED),
OrchestrationRuntimeStatus.CANCELED);
Assert.assertEquals(WorkflowRuntimeStatus.toOrchestrationRuntimeStatus(WorkflowRuntimeStatus.TERMINATED),
OrchestrationRuntimeStatus.TERMINATED);
Assert.assertEquals(WorkflowRuntimeStatus.toOrchestrationRuntimeStatus(WorkflowRuntimeStatus.PENDING),
OrchestrationRuntimeStatus.PENDING);
Assert.assertEquals(WorkflowRuntimeStatus.toOrchestrationRuntimeStatus(WorkflowRuntimeStatus.SUSPENDED),
OrchestrationRuntimeStatus.SUSPENDED);
}
@Test
public void fromOrchestrationRuntimeStatusThrowsIllegalArgumentException() {
try {
WorkflowRuntimeStatus.fromOrchestrationRuntimeStatus(null);
Assert.fail("Expected exception not thrown");
} catch (IllegalArgumentException e) {
Assert.assertEquals("status cannot be null", e.getMessage());
}
}
}