Merge branch 'remove-workflow-methods' into remote-workflow-methods-4

# Conflicts:
#	test/Dapr.E2E.Test/Workflows/WorkflowTest.cs
This commit is contained in:
Whit Waldo 2024-10-14 13:11:21 -05:00
commit 8948152a87
28 changed files with 51 additions and 820 deletions

View File

@ -11,6 +11,9 @@
// limitations under the License.
// ------------------------------------------------------------------------
using Dapr.Actors.Communication;
using IDemoActor;
namespace ActorClient
{
using System;
@ -18,7 +21,6 @@ namespace ActorClient
using System.Threading.Tasks;
using Dapr.Actors;
using Dapr.Actors.Client;
using IDemoActorInterface;
/// <summary>
/// Actor Client class.
@ -43,7 +45,7 @@ namespace ActorClient
// Make strongly typed Actor calls with Remoting.
// DemoActor is the type registered with Dapr runtime in the service.
var proxy = ActorProxy.Create<IDemoActor>(actorId, "DemoActor");
var proxy = ActorProxy.Create<IDemoActor.IDemoActor>(actorId, "DemoActor");
Console.WriteLine("Making call using actor proxy to save data.");
await proxy.SaveData(data, TimeSpan.FromMinutes(10));

View File

@ -11,9 +11,9 @@
// limitations under the License.
// ------------------------------------------------------------------------
using IDemoActorInterface;
using IDemoActor;
namespace DaprDemoActor
namespace DemoActor
{
public class BankService
{

View File

@ -11,14 +11,14 @@
// limitations under the License.
// ------------------------------------------------------------------------
namespace DaprDemoActor
{
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Dapr.Actors.Runtime;
using IDemoActorInterface;
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Dapr.Actors.Runtime;
using IDemoActor;
namespace DemoActor
{
// The following example showcases a few features of Actors
//
// Every actor should inherit from the Actor type, and must implement one or more actor interfaces.
@ -27,7 +27,7 @@ namespace DaprDemoActor
// For Actors to use Reminders, it must derive from IRemindable.
// If you don't intend to use Reminder feature, you can skip implementing IRemindable and reminder
// specific methods which are shown in the code below.
public class DemoActor : Actor, IDemoActor, IBankActor, IRemindable
public class DemoActor : Actor, IDemoActor.IDemoActor, IBankActor, IRemindable
{
private const string StateName = "my_data";

View File

@ -11,11 +11,11 @@
// limitations under the License.
// ------------------------------------------------------------------------
namespace DaprDemoActor
{
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace DemoActor
{
public class Program
{
public static void Main(string[] args)

View File

@ -11,14 +11,14 @@
// limitations under the License.
// ------------------------------------------------------------------------
namespace DaprDemoActor
{
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace DemoActor
{
public class Startup
{
public Startup(IConfiguration configuration)

View File

@ -11,12 +11,12 @@
// limitations under the License.
// ------------------------------------------------------------------------
namespace IDemoActorInterface
{
using System;
using System.Threading.Tasks;
using Dapr.Actors;
using System;
using System.Threading.Tasks;
using Dapr.Actors;
namespace IDemoActor
{
public interface IBankActor : IActor
{
Task<AccountBalance> GetAccountBalance();

View File

@ -11,13 +11,12 @@
// limitations under the License.
// ------------------------------------------------------------------------
namespace IDemoActorInterface
{
using System;
using System.Threading.Tasks;
using Dapr.Actors;
using Dapr.Actors.Runtime;
using System;
using System.Threading.Tasks;
using Dapr.Actors;
namespace IDemoActor
{
/// <summary>
/// Interface for Actor method.
/// </summary>

View File

@ -11,6 +11,8 @@
// limitations under the License.
// ------------------------------------------------------------------------
using Dapr.AspNetCore;
namespace ControllerSample
{
using System;

View File

@ -11,6 +11,8 @@
// limitations under the License.
// ------------------------------------------------------------------------
using Dapr.AspNetCore;
namespace ControllerSample
{
using Microsoft.AspNetCore.Builder;

View File

@ -22,7 +22,7 @@ using Grpc.Core;
using GrpcServiceSample.Generated;
using Microsoft.Extensions.Logging;
namespace GrpcServiceSample
namespace GrpcServiceSample.Services
{
/// <summary>
/// BankAccount gRPC service

View File

@ -11,6 +11,8 @@
// limitations under the License.
// ------------------------------------------------------------------------
using Dapr.AspNetCore;
using GrpcServiceSample.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

View File

@ -1,4 +1,5 @@
using System;
using Dapr.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;

View File

@ -11,10 +11,9 @@
// limitations under the License.
// ------------------------------------------------------------------------
using Cryptography;
using Cryptography.Examples;
namespace Samples.Client
namespace Cryptography
{
class Program
{

View File

@ -1,4 +1,5 @@
using DistributedLock.Services;
using Dapr.AspNetCore;
using DistributedLock.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;

View File

@ -1,7 +1,6 @@
using Dapr.Client;
using Dapr.Workflow;
using Microsoft.Extensions.Logging;
using WorkflowConsoleApp.Models;
namespace WorkflowConsoleApp.Activities
{

View File

@ -1,6 +1,5 @@
using Dapr.Workflow;
using Microsoft.Extensions.Logging;
using WorkflowConsoleApp.Models;
namespace WorkflowConsoleApp.Activities
{

View File

@ -1,7 +1,6 @@
using Dapr.Client;
using Dapr.Workflow;
using Microsoft.Extensions.Logging;
using WorkflowConsoleApp.Models;
namespace WorkflowConsoleApp.Activities
{

View File

@ -1,6 +1,5 @@
using Dapr.Client;
using Dapr.Workflow;
using WorkflowConsoleApp.Models;
using Microsoft.Extensions.Logging;
namespace WorkflowConsoleApp.Activities

View File

@ -1,4 +1,4 @@
namespace WorkflowConsoleApp.Models
namespace WorkflowConsoleApp
{
public record OrderPayload(string Name, double TotalCost, int Quantity = 1);
public record InventoryRequest(string RequestId, string ItemName, int Quantity);

View File

@ -1,10 +1,10 @@
using Dapr.Client;
using Dapr.Workflow;
using WorkflowConsoleApp.Activities;
using WorkflowConsoleApp.Models;
using WorkflowConsoleApp.Workflows;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using WorkflowConsoleApp;
const string StoreName = "statestore";

View File

@ -1,6 +1,5 @@
using Dapr.Workflow;
using WorkflowConsoleApp.Activities;
using WorkflowConsoleApp.Models;
namespace WorkflowConsoleApp.Workflows
{

View File

@ -1,8 +1,8 @@
using System.Threading.Tasks;
using Dapr.Workflow;
using Moq;
using WorkflowConsoleApp;
using WorkflowConsoleApp.Activities;
using WorkflowConsoleApp.Models;
using WorkflowConsoleApp.Workflows;
using Xunit;

View File

@ -1296,192 +1296,7 @@ namespace Dapr.Client
string resourceId,
string lockOwner,
CancellationToken cancellationToken = default);
/// <summary>
/// Attempt to start the given workflow with response indicating success.
/// </summary>
/// <param name="workflowComponent">The component to interface with.</param>
/// <param name="workflowName">Name of the workflow to run.</param>
/// <param name="instanceId">Identifier of the specific run.</param>
/// <param name="input">The JSON-serializeable input for the given workflow.</param>
/// <param name="workflowOptions">The list of options that are potentially needed to start a workflow.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
/// <returns>A <see cref="Task"/> containing a <see cref="StartWorkflowResponse"/></returns>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public abstract Task<StartWorkflowResponse> StartWorkflowAsync(
string workflowComponent,
string workflowName,
string instanceId = null,
object input = null,
IReadOnlyDictionary<string, string> workflowOptions = default,
CancellationToken cancellationToken = default);
/// <summary>
/// Waits for a workflow to start running and returns a <see cref="GetWorkflowResponse"/> object that contains metadata
/// about the started workflow.
/// </summary>
/// <remarks>
/// <para>
/// A "started" workflow instance is any instance not in the <see cref="WorkflowRuntimeStatus.Pending"/> state.
/// </para><para>
/// This method will return a completed task if the workflow has already started running or has already completed.
/// </para>
/// </remarks>
/// <param name="instanceId">The unique ID of the workflow instance to wait for.</param>
/// <param name="workflowComponent">The component to interface with.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the wait operation.</param>
/// <returns>
/// Returns a <see cref="GetWorkflowResponse"/> record that describes the workflow instance and its execution status.
/// </returns>
/// <exception cref="OperationCanceledException">
/// Thrown if <paramref name="cancellationToken"/> is canceled before the workflow starts running.
/// </exception>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public virtual async Task<GetWorkflowResponse> WaitForWorkflowStartAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default)
{
while (true)
{
var response = await this.GetWorkflowAsync(instanceId, workflowComponent, cancellationToken);
if (response.RuntimeStatus != WorkflowRuntimeStatus.Pending)
{
return response;
}
await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken);
}
}
/// <summary>
/// Waits for a workflow to complete and returns a <see cref="GetWorkflowResponse"/>
/// object that contains metadata about the started instance.
/// </summary>
/// <remarks>
/// <para>
/// A "completed" workflow instance is any instance in one of the terminal states. For example, the
/// <see cref="WorkflowRuntimeStatus.Completed"/>, <see cref="WorkflowRuntimeStatus.Failed"/>, or
/// <see cref="WorkflowRuntimeStatus.Terminated"/> states.
/// </para><para>
/// Workflows are long-running and could take hours, days, or months before completing.
/// Workflows can also be eternal, in which case they'll never complete unless terminated.
/// In such cases, this call may block indefinitely, so care must be taken to ensure appropriate timeouts are
/// enforced using the <paramref name="cancellationToken"/> parameter.
/// </para><para>
/// If a workflow instance is already complete when this method is called, the method will return immediately.
/// </para>
/// </remarks>
/// <returns>
/// Returns a <see cref="GetWorkflowResponse"/> record that describes the workflow instance and its execution status.
/// </returns>
/// <exception cref="OperationCanceledException">
/// Thrown if <paramref name="cancellationToken"/> is canceled before the workflow completes.
/// </exception>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public virtual async Task<GetWorkflowResponse> WaitForWorkflowCompletionAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default)
{
while (true)
{
var response = await this.GetWorkflowAsync(instanceId, workflowComponent, cancellationToken);
if (response.RuntimeStatus == WorkflowRuntimeStatus.Completed ||
response.RuntimeStatus == WorkflowRuntimeStatus.Failed ||
response.RuntimeStatus == WorkflowRuntimeStatus.Terminated)
{
return response;
}
await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken);
}
}
/// <summary>
/// Attempt to get information about the given workflow.
/// </summary>
/// <param name="instanceId">The unique ID of the target workflow instance.</param>
/// <param name="workflowComponent">The component to interface with.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
/// <returns>A <see cref="Task"/> containing a <see cref="GetWorkflowResponse"/></returns>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public abstract Task<GetWorkflowResponse> GetWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default);
/// <summary>
/// Attempt to get terminate the given workflow.
/// </summary>
/// <param name="instanceId">The unique ID of the target workflow instance.</param>
/// <param name="workflowComponent">The component to interface with.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
/// <returns>A <see cref="Task" /> that will complete when the terminate operation has been scheduled. If the wrapped value is true the operation suceeded.</returns>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public abstract Task TerminateWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default);
/// <summary>
/// Attempt to raise an event the given workflow with response indicating success.
/// </summary>
/// <param name="instanceId">Identifier of the specific run.</param>
/// <param name="workflowComponent">The component to interface with.</param>
/// <param name="eventName">Name of the event to raise.</param>
/// <param name="eventData">The JSON-serializable event payload to include in the raised event.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
/// <returns>A <see cref="Task" /> that will complete when the raise event operation has been scheduled. If the wrapped value is true the operation suceeded.</returns>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public abstract Task RaiseWorkflowEventAsync(
string instanceId,
string workflowComponent,
string eventName,
object eventData = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Pauses the specified workflow instance.
/// </summary>
/// <param name="instanceId">The unique ID of the target workflow instance.</param>
/// <param name="workflowComponent">The component to interface with.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
/// <returns>A <see cref="Task" /> that will complete when the pause operation has been scheduled. If the wrapped value is true the operation suceeded.</returns>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public abstract Task PauseWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default);
/// <summary>
/// Resumes a paused workflow instance.
/// </summary>
/// <param name="instanceId">The unique ID of the target workflow instance.</param>
/// <param name="workflowComponent">The component to interface with.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
/// <returns>A <see cref="Task" /> that will complete when the resume operation has been scheduled. If the wrapped value is true the operation suceeded.</returns>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public abstract Task ResumeWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default);
/// <summary>
/// Delete all state associated with the specified workflow instance. The workflow must be in a non-running state to be purged.
/// </summary>
/// <param name="instanceId">The unique ID of the target workflow instance.</param>
/// <param name="workflowComponent">The component to interface with.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
/// <returns>A <see cref="Task" /> that will complete when the purge operation has been scheduled. If the wrapped value is true the operation suceeded.</returns>
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public abstract Task PurgeWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default);
/// <inheritdoc />
public void Dispose()
{

View File

@ -2036,287 +2036,6 @@ namespace Dapr.Client
#endregion
#region Workflow API
/// <inheritdoc/>
[Obsolete]
public async override Task<StartWorkflowResponse> StartWorkflowAsync(
string workflowComponent,
string workflowName,
string instanceId = null,
object input = null,
IReadOnlyDictionary<string, string> workflowOptions = default,
CancellationToken cancellationToken = default)
{
ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId));
ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent));
ArgumentVerifier.ThrowIfNullOrEmpty(workflowName, nameof(workflowName));
ArgumentVerifier.ThrowIfNull(input, nameof(input));
// Serialize json data. Converts input object to bytes and then bytestring inside the request.
byte[] jsonUtf8Bytes = null;
if (input is not null)
{
jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(input);
}
var request = new Autogenerated.StartWorkflowRequest()
{
InstanceId = instanceId,
WorkflowComponent = workflowComponent,
WorkflowName = workflowName,
Input = jsonUtf8Bytes is not null ? ByteString.CopyFrom(jsonUtf8Bytes) : null,
};
if (workflowOptions?.Count > 0)
{
foreach (var item in workflowOptions)
{
request.Options[item.Key] = item.Value;
}
}
try
{
var options = CreateCallOptions(headers: null, cancellationToken);
var response = await client.StartWorkflowAlpha1Async(request, options);
return new StartWorkflowResponse(response.InstanceId);
}
catch (RpcException ex)
{
throw new DaprException("Start Workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex);
}
}
/// <inheritdoc/>
[Obsolete]
public async override Task<GetWorkflowResponse> GetWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default)
{
ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId));
ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent));
var request = new Autogenerated.GetWorkflowRequest()
{
InstanceId = instanceId,
WorkflowComponent = workflowComponent
};
try
{
var options = CreateCallOptions(headers: null, cancellationToken);
var response = await client.GetWorkflowAlpha1Async(request, options);
if (response == null)
{
throw new DaprException("Get workflow operation failed: the Dapr endpoint returned an empty result.");
}
response.CreatedAt ??= new Timestamp();
response.LastUpdatedAt ??= response.CreatedAt;
return new GetWorkflowResponse
{
InstanceId = response.InstanceId,
WorkflowName = response.WorkflowName,
WorkflowComponentName = workflowComponent,
CreatedAt = response.CreatedAt.ToDateTime(),
LastUpdatedAt = response.LastUpdatedAt.ToDateTime(),
RuntimeStatus = GetWorkflowRuntimeStatus(response.RuntimeStatus),
Properties = response.Properties,
FailureDetails = GetWorkflowFailureDetails(response, workflowComponent),
};
}
catch (RpcException ex)
{
throw new DaprException("Get workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex);
}
}
private static WorkflowRuntimeStatus GetWorkflowRuntimeStatus(string runtimeStatus)
{
if (!System.Enum.TryParse(runtimeStatus, true /* ignoreCase */, out WorkflowRuntimeStatus status))
{
status = WorkflowRuntimeStatus.Unknown;
}
return status;
}
private static WorkflowFailureDetails GetWorkflowFailureDetails(Autogenerated.GetWorkflowResponse response, string componentName)
{
// FUTURE: Make this part of the protobuf contract instead of getting it from properties
// NOTE: The use of | instead of || is intentional. We want to get all the values.
if (response.Properties.TryGetValue($"{componentName}.workflow.failure.error_type", out string errorType) |
response.Properties.TryGetValue($"{componentName}.workflow.failure.error_message", out string errorMessage) |
response.Properties.TryGetValue($"{componentName}.workflow.failure.stack_trace", out string stackTrace))
{
return new WorkflowFailureDetails(errorMessage, errorType, stackTrace);
}
return null;
}
/// <inheritdoc/>
[Obsolete]
public async override Task TerminateWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default)
{
ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId));
ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent));
var request = new Autogenerated.TerminateWorkflowRequest()
{
InstanceId = instanceId,
WorkflowComponent = workflowComponent
};
var options = CreateCallOptions(headers: null, cancellationToken);
try
{
await client.TerminateWorkflowAlpha1Async(request, options);
}
catch (RpcException ex)
{
throw new DaprException("Terminate workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex);
}
}
/// <inheritdoc/>
[Obsolete]
public async override Task RaiseWorkflowEventAsync(
string instanceId,
string workflowComponent,
string eventName,
Object eventData,
CancellationToken cancellationToken = default)
{
ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId));
ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent));
ArgumentVerifier.ThrowIfNullOrEmpty(eventName, nameof(eventName));
byte[] jsonUtf8Bytes = new byte[0];
// Serialize json data. Converts eventData object to bytes and then bytestring inside the request.
if (eventData != null)
{
jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(eventData);
}
var request = new Autogenerated.RaiseEventWorkflowRequest()
{
InstanceId = instanceId,
WorkflowComponent = workflowComponent,
EventName = eventName,
EventData = ByteString.CopyFrom(jsonUtf8Bytes),
};
var options = CreateCallOptions(headers: null, cancellationToken);
try
{
await client.RaiseEventWorkflowAlpha1Async(request, options);
}
catch (RpcException ex)
{
throw new DaprException("Start Workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex);
}
}
/// <inheritdoc/>
[Obsolete]
public async override Task PauseWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default)
{
ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId));
ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent));
var request = new Autogenerated.PauseWorkflowRequest()
{
InstanceId = instanceId,
WorkflowComponent = workflowComponent
};
var options = CreateCallOptions(headers: null, cancellationToken);
try
{
await client.PauseWorkflowAlpha1Async(request, options);
}
catch (RpcException ex)
{
throw new DaprException("Pause workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex);
}
}
/// <inheritdoc/>
[Obsolete]
public async override Task ResumeWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default)
{
ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId));
ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent));
var request = new Autogenerated.ResumeWorkflowRequest()
{
InstanceId = instanceId,
WorkflowComponent = workflowComponent
};
var options = CreateCallOptions(headers: null, cancellationToken);
try
{
await client.ResumeWorkflowAlpha1Async(request, options);
}
catch (RpcException ex)
{
throw new DaprException("Resume workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex);
}
}
/// <inheritdoc/>
[Obsolete]
public async override Task PurgeWorkflowAsync(
string instanceId,
string workflowComponent,
CancellationToken cancellationToken = default)
{
ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId));
ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent));
var request = new Autogenerated.PurgeWorkflowRequest()
{
InstanceId = instanceId,
WorkflowComponent = workflowComponent
};
var options = CreateCallOptions(headers: null, cancellationToken);
try
{
await client.PurgeWorkflowAlpha1Async(request, options);
}
catch (RpcException ex)
{
throw new DaprException("Purge workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex);
}
}
#endregion
#region Dapr Sidecar Methods
/// <inheritdoc/>

View File

@ -1,100 +0,0 @@
// ------------------------------------------------------------------------
// Copyright 2021 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.
// ------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace Dapr.Client
{
/// <summary>
/// The response type for the <see cref="DaprClient.GetWorkflowAsync"/> API.
/// </summary>
public class GetWorkflowResponse
{
/// <summary>
/// Gets the instance ID of the workflow.
/// </summary>
public string InstanceId { get; init; }
/// <summary>
/// Gets the name of the workflow.
/// </summary>
public string WorkflowName { get; init; }
/// <summary>
/// Gets the name of the workflow component.
/// </summary>
public string WorkflowComponentName { get; init; }
/// <summary>
/// Gets the time at which the workflow was created.
/// </summary>
public DateTime CreatedAt { get; init; }
/// <summary>
/// Gets the time at which the workflow was last updated.
/// </summary>
public DateTime LastUpdatedAt { get; init; }
/// <summary>
/// Gets the runtime status of the workflow.
/// </summary>
public WorkflowRuntimeStatus RuntimeStatus { get; init; }
/// <summary>
/// Gets the component-specific workflow properties.
/// </summary>
public IReadOnlyDictionary<string, string> Properties { get; init; }
/// <summary>
/// Gets the details associated with the workflow failure, if any.
/// </summary>
public WorkflowFailureDetails FailureDetails { get; init; }
/// <summary>
/// Deserializes the workflow input into <typeparamref name="T"/> using <see cref="JsonSerializer"/>.
/// </summary>
/// <typeparam name="T">The type to deserialize the workflow input into.</typeparam>
/// <param name="options">Options to control the behavior during parsing.</param>
/// <returns>Returns the input as <typeparamref name="T"/>, or returns a default value if the workflow doesn't have an input.</returns>
public T ReadInputAs<T>(JsonSerializerOptions options = null)
{
// FUTURE: Make this part of the protobuf contract instead of properties
string defaultInputKey = $"{this.WorkflowComponentName}.workflow.input";
if (!this.Properties.TryGetValue(defaultInputKey, out string serializedInput))
{
return default;
}
return JsonSerializer.Deserialize<T>(serializedInput, options);
}
/// <summary>
/// Deserializes the workflow output into <typeparamref name="T"/> using <see cref="JsonSerializer"/>.
/// </summary>
/// <typeparam name="T">The type to deserialize the workflow output into.</typeparam>
/// <param name="options">Options to control the behavior during parsing.</param>
/// <returns>Returns the output as <typeparamref name="T"/>, or returns a default value if the workflow doesn't have an output.</returns>
public T ReadOutputAs<T>(JsonSerializerOptions options = null)
{
// FUTURE: Make this part of the protobuf contract instead of properties
string defaultOutputKey = $"{this.WorkflowComponentName}.workflow.output";
if (!this.Properties.TryGetValue(defaultOutputKey, out string serializedOutput))
{
return default;
}
return JsonSerializer.Deserialize<T>(serializedOutput, options);
}
}
}

View File

@ -1,35 +0,0 @@
// ------------------------------------------------------------------------
// Copyright 2021 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.
// ------------------------------------------------------------------------
namespace Dapr.Client
{
/// <summary>
/// Represents workflow failure details.
/// </summary>
/// <param name="ErrorMessage">A summary description of the failure, which is typically an exception message.</param>
/// <param name="ErrorType">The error type, which is defined by the workflow component implementation.</param>
/// <param name="StackTrace">The stack trace of the failure.</param>
public record WorkflowFailureDetails(
string ErrorMessage,
string ErrorType,
string StackTrace = null)
{
/// <summary>
/// Creates a user-friendly string representation of the failure information.
/// </summary>
public override string ToString()
{
return $"{this.ErrorType}: {this.ErrorMessage}";
}
}
}

View File

@ -11,7 +11,7 @@
// limitations under the License.
// ------------------------------------------------------------------------
namespace Dapr.Client
namespace Dapr.Workflow
{
/// <summary>
/// Enum describing the runtime status of a workflow.

View File

@ -1,171 +0,0 @@
// ------------------------------------------------------------------------
// Copyright 2022 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.
// ------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dapr.Client;
using FluentAssertions;
using Xunit;
namespace Dapr.E2E.Test
{
[Obsolete]
public partial class E2ETests
{
[Fact]
public async Task TestWorkflowLogging()
{
// This test starts the daprclient and searches through the logfile to ensure the
// workflow logger is correctly logging the registered workflow(s) and activity(s)
Dictionary<string, bool> logStrings = new Dictionary<string, bool>();
logStrings["PlaceOrder"] = false;
logStrings["ShipProduct"] = false;
var logFilePath = "../../../../../test/Dapr.E2E.Test.App/log.txt";
var allLogsFound = false;
var timeout = 30; // 30s
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout));
using var daprClient = new DaprClientBuilder().UseGrpcEndpoint(this.GrpcEndpoint).UseHttpEndpoint(this.HttpEndpoint).Build();
var health = await daprClient.CheckHealthAsync();
health.Should().Be(true, "DaprClient is not healthy");
var searchTask = Task.Run(async () =>
{
using (StreamReader reader = new StreamReader(logFilePath))
{
string line;
while ((line = await reader.ReadLineAsync().WaitAsync(cts.Token)) != null)
{
foreach (var entry in logStrings)
{
if (line.Contains(entry.Key))
{
logStrings[entry.Key] = true;
}
}
allLogsFound = logStrings.All(k => k.Value);
if (allLogsFound)
{
break;
}
}
}
}, cts.Token);
try
{
await searchTask;
}
finally
{
File.Delete(logFilePath);
}
if (!allLogsFound)
{
Assert.Fail("The logs were not able to found within the timeout");
}
}
[Fact]
public async Task TestWorkflows()
{
var instanceId = "testInstanceId";
var instanceId2 = "EventRaiseId";
var workflowComponent = "dapr";
var workflowName = "PlaceOrder";
object input = "paperclips";
Dictionary<string, string> workflowOptions = new Dictionary<string, string>();
workflowOptions.Add("task_queue", "testQueue");
using var daprClient = new DaprClientBuilder().UseGrpcEndpoint(this.GrpcEndpoint).UseHttpEndpoint(this.HttpEndpoint).Build();
var health = await daprClient.CheckHealthAsync();
health.Should().Be(true, "DaprClient is not healthy");
// START WORKFLOW TEST
var startResponse = await daprClient.StartWorkflowAsync(
instanceId: instanceId,
workflowComponent: workflowComponent,
workflowName: workflowName,
input: input,
workflowOptions: workflowOptions);
startResponse.InstanceId.Should().Be("testInstanceId", $"Instance ID {startResponse.InstanceId} was not correct");
// GET INFO TEST
var getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent);
getResponse.InstanceId.Should().Be("testInstanceId");
getResponse.RuntimeStatus.Should().Be(WorkflowRuntimeStatus.Running, $"Instance ID {getResponse.RuntimeStatus} was not correct");
// PAUSE TEST:
await daprClient.PauseWorkflowAsync(instanceId, workflowComponent);
getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent);
getResponse.RuntimeStatus.Should().Be(WorkflowRuntimeStatus.Suspended, $"Instance ID {getResponse.RuntimeStatus} was not correct");
// RESUME TEST:
await daprClient.ResumeWorkflowAsync(instanceId, workflowComponent);
getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent);
getResponse.RuntimeStatus.Should().Be(WorkflowRuntimeStatus.Running, $"Instance ID {getResponse.RuntimeStatus} was not correct");
// RAISE EVENT TEST
await daprClient.RaiseWorkflowEventAsync(instanceId, workflowComponent, "ChangePurchaseItem", "computers");
getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent);
// TERMINATE TEST:
await daprClient.TerminateWorkflowAsync(instanceId, workflowComponent);
getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent);
getResponse.RuntimeStatus.Should().Be(WorkflowRuntimeStatus.Terminated, $"Instance ID {getResponse.RuntimeStatus} was not correct");
// PURGE TEST
await daprClient.PurgeWorkflowAsync(instanceId, workflowComponent);
try
{
getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent);
Assert.Fail("The GetWorkflowAsync call should have failed since the instance was purged");
}
catch (DaprException ex)
{
ex.InnerException.Message.Should().Contain("no such instance exists", $"Instance {instanceId} was not correctly purged");
}
// Start another workflow for event raising purposes
startResponse = await daprClient.StartWorkflowAsync(
instanceId: instanceId2,
workflowComponent: workflowComponent,
workflowName: workflowName,
input: input,
workflowOptions: workflowOptions);
// PARALLEL RAISE EVENT TEST
var event1 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers");
var event2 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers");
var event3 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers");
var event4 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers");
var event5 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers");
var externalEvents = Task.WhenAll(event1, event2, event3, event4, event5);
var winner = await Task.WhenAny(externalEvents, Task.Delay(TimeSpan.FromSeconds(30)));
externalEvents.IsCompletedSuccessfully.Should().BeTrue($"Unsuccessful at raising events. Status of events: {externalEvents.IsCompletedSuccessfully}");
// Wait up to 30 seconds for the workflow to complete and check the output
using var cts = new CancellationTokenSource(delay: TimeSpan.FromSeconds(30));
getResponse = await daprClient.WaitForWorkflowCompletionAsync(instanceId2, workflowComponent, cts.Token);
var outputString = getResponse.Properties["dapr.workflow.output"];
outputString.Should().Be("\"computers\"", $"Purchased item {outputString} was not correct");
var deserializedOutput = getResponse.ReadOutputAs<string>();
deserializedOutput.Should().Be("computers", $"Deserialized output '{deserializedOutput}' was not expected");
}
}
}