From 47dea1d853f1d278f154b22c4f933ccc754f2c43 Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Mon, 17 Oct 2022 11:21:59 -0700 Subject: [PATCH 01/20] Initial workflow sdk implementation Signed-off-by: Amulya Varote --- examples/Workflow/Program.cs | 16 ++++++ .../Workflow/Properties/launchSettings.json | 12 ++++ examples/Workflow/Workflow.csproj | 14 +++++ src/Dapr.Workflow/ActivityContext.cs | 17 ++++++ src/Dapr.Workflow/Dapr.Workflow.csproj | 21 +++++++ src/Dapr.Workflow/WorkflowContext.cs | 36 ++++++++++++ src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 55 +++++++++++++++++++ .../WorkflowServiceCollectionExtensions.cs | 49 +++++++++++++++++ 8 files changed, 220 insertions(+) create mode 100644 examples/Workflow/Program.cs create mode 100644 examples/Workflow/Properties/launchSettings.json create mode 100644 examples/Workflow/Workflow.csproj create mode 100644 src/Dapr.Workflow/ActivityContext.cs create mode 100644 src/Dapr.Workflow/Dapr.Workflow.csproj create mode 100644 src/Dapr.Workflow/WorkflowContext.cs create mode 100644 src/Dapr.Workflow/WorkflowRuntimeOptions.cs create mode 100644 src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs diff --git a/examples/Workflow/Program.cs b/examples/Workflow/Program.cs new file mode 100644 index 000000000..1eb50d465 --- /dev/null +++ b/examples/Workflow/Program.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using Dapr.Workflow; +using Microsoft.AspNetCore.Mvc; +using JsonOptions = Microsoft.AspNetCore.Http.Json.JsonOptions; + +// The workflow host is a background service that connects to the sidecar over gRPC +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + +builder.Services.AddWorkflow(options => +{ + options.RegisterWorkflow("helloWorld", implementation: async (context, input) => + { + await context.CallActivityAsync("SayHello", "Hello World"); + }); + options.RegisterActivity("SayHello", (context, message) => $"{message}!"); +}); \ No newline at end of file diff --git a/examples/Workflow/Properties/launchSettings.json b/examples/Workflow/Properties/launchSettings.json new file mode 100644 index 000000000..15fed009c --- /dev/null +++ b/examples/Workflow/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "OrderingWebApi": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:57899;http://localhost:57900" + } + } +} \ No newline at end of file diff --git a/examples/Workflow/Workflow.csproj b/examples/Workflow/Workflow.csproj new file mode 100644 index 000000000..4907e15dd --- /dev/null +++ b/examples/Workflow/Workflow.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + \ No newline at end of file diff --git a/src/Dapr.Workflow/ActivityContext.cs b/src/Dapr.Workflow/ActivityContext.cs new file mode 100644 index 000000000..41b84f230 --- /dev/null +++ b/src/Dapr.Workflow/ActivityContext.cs @@ -0,0 +1,17 @@ +namespace Microsoft.DurableTask; + +/// +/// Defines properties and methods for task activity context objects. +/// +public abstract class ActivityContext +{ + /// + /// Gets the name of the activity. + /// + public abstract string Name { get; } + + /// + /// Gets the unique ID of the current workflow instance. + /// + public abstract string InstanceId { get; } +} \ No newline at end of file diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj new file mode 100644 index 000000000..b77b2a2a2 --- /dev/null +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + enable + + + + + Dapr.Workflows + Dapr Workflows Client SDK + Dapr Workflows client SDK for building workflows as code with Dapr + + + + + + + + \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowContext.cs b/src/Dapr.Workflow/WorkflowContext.cs new file mode 100644 index 000000000..0da9e7e4f --- /dev/null +++ b/src/Dapr.Workflow/WorkflowContext.cs @@ -0,0 +1,36 @@ +using System.Text.Json; +using Microsoft.DurableTask; + +namespace Dapr.Workflow; + +public class WorkflowsContext +{ + readonly TaskOrchestrationContext innerContext; + + internal WorkflowsContext(TaskOrchestrationContext innerContext) + { + this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); + } + + public TaskName Name => this.innerContext.Name; + public string InstanceId => this.innerContext.InstanceId; + + public DateTime CurrentUtcDateTime => this.innerContext.CurrentUtcDateTime; + + public void SetCustomStatus(object? customStatus) => this.innerContext.SetCustomStatus(customStatus); + + public Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) + { + return this.innerContext.CreateTimer(delay, cancellationToken); + } + + public Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) + { + return this.innerContext.WaitForExternalEvent(eventName, timeout); + } + + public Task CallActivityAsync(TaskName name, object? input = null, TaskOptions? options = null) + { + return this.innerContext.CallActivityAsync(name, input, options); + } +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs new file mode 100644 index 000000000..5e063d3cc --- /dev/null +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -0,0 +1,55 @@ +using Microsoft.DurableTask; + +namespace Dapr.Workflows; + +public sealed class WorkflowsRuntimeOptions +{ + readonly Dictionary> factories = new(); + + public void RegisterWorkflow(string name, Func> implementation) + { + // Dapr workflows are implemented as specialized Durable Task orchestrations + this.factories.Add(name, (IDurableTaskRegistry registry) => + { + registry.AddOrchestrator(name, (innerContext, input) => + { + WorkflowContext workflowContext = new(innerContext); + return implementation(workflowContext, input); + }); + }); + } + + public void RegisterActivity(string name, Func> implementation) + { + // Dapr activities are implemented as specialized Durable Task activities + this.factories.Add(name, (IDurableTaskRegistry registry) => + { + registry.AddActivity(name, (innerContext, input) => + { + ActivityContext activityContext = new(innerContext); + return implementation(activityContext, input); + }); + }); + } + + public void RegisterActivity(string name, Func> implementation) + { + // Dapr activities are implemented as specialized Durable Task activities + this.factories.Add(name, (IDurableTaskRegistry registry) => + { + registry.AddActivity(name, (innerContext, input) => + { + ActivityContext activityContext = new(innerContext); + return implementation(activityContext, input); + }); + }); + } + + internal void AddWorkflowsToRegistry(IDurableTaskRegistry registry) + { + foreach (Action factory in this.factories.Values) + { + factory.Invoke(registry); + } + } +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs new file mode 100644 index 000000000..1e2baf1a1 --- /dev/null +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -0,0 +1,49 @@ +using Microsoft.DurableTask.Grpc; +using Microsoft.Extensions.DependencyInjection; + +namespace Dapr.Workflows; + +/// +/// Contains extension methods for using Dapr Workflow with dependency injection. +/// +public static class WorkflowsServiceCollectionExtensions +{ + /// + /// Adds Dapr Workflow support to the service collection. + /// + /// The . + /// A delegate used to configure actor options and register workflow functions. + public static IServiceCollection AddWorkflow( + this IServiceCollection serviceCollection, + Action configure) + { + if (serviceCollection == null) + { + throw new ArgumentNullException(nameof(serviceCollection)); + } + + serviceCollection.AddSingleton(); + serviceCollection.AddDaprClient(); + serviceCollection.AddSingleton(services => new WorkflowClient(services)); + + serviceCollection.AddHostedService(services => + { + DurableTaskGrpcWorker.Builder workerBuilder = DurableTaskGrpcWorker.CreateBuilder().UseServices(services); + + WorkflowRuntimeOptions options = services.GetRequiredService(); + configure?.Invoke(options); + + workerBuilder.UseServices(services); + + workerBuilder.AddTasks(registry => + { + options.AddWorkflowsToRegistry(registry); + }); + + DurableTaskGrpcWorker worker = workerBuilder.Build(); + return worker; + }); + + return serviceCollection; + } +} \ No newline at end of file From 1e649cc0008fcad930338c917db2ab1226142da9 Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Fri, 21 Oct 2022 11:35:42 -0700 Subject: [PATCH 02/20] Dapr Workflow initial methods for dotnet-sdk Signed-off-by: Amulya Varote --- examples/Workflow/Program.cs | 53 +++++++++-- examples/Workflow/Workflow.csproj | 9 +- src/Dapr.Workflow/ActivityContext.cs | 35 +++++--- src/Dapr.Workflow/Dapr.Workflow.csproj | 14 ++- src/Dapr.Workflow/WorkflowClient.cs | 59 +++++++++++++ src/Dapr.Workflow/WorkflowContext.cs | 87 ++++++++++++------- src/Dapr.Workflow/WorkflowMetadata.cs | 27 ++++++ src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 85 +++++++++--------- .../WorkflowServiceCollectionExtensions.cs | 72 ++++++++------- ...WorkflowServiceCollectionExtensionsTest.cs | 23 +++++ 10 files changed, 332 insertions(+), 132 deletions(-) create mode 100644 src/Dapr.Workflow/WorkflowClient.cs create mode 100644 src/Dapr.Workflow/WorkflowMetadata.cs create mode 100644 test/Dapr.Workflow.Test/WorkflowServiceCollectionExtensionsTest.cs diff --git a/examples/Workflow/Program.cs b/examples/Workflow/Program.cs index 1eb50d465..46fdf73bc 100644 --- a/examples/Workflow/Program.cs +++ b/examples/Workflow/Program.cs @@ -1,7 +1,12 @@ -using System.Text.Json.Serialization; +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using System.Web; +using Microsoft.AspNetCore.Http; using Dapr.Workflow; -using Microsoft.AspNetCore.Mvc; -using JsonOptions = Microsoft.AspNetCore.Http.Json.JsonOptions; // The workflow host is a background service that connects to the sidecar over gRPC WebApplicationBuilder builder = WebApplication.CreateBuilder(args); @@ -10,7 +15,43 @@ { options.RegisterWorkflow("helloWorld", implementation: async (context, input) => { - await context.CallActivityAsync("SayHello", "Hello World"); + string result = ""; + result = await context.CallActivityAsync("SayHello", "World"); + return result; }); - options.RegisterActivity("SayHello", (context, message) => $"{message}!"); -}); \ No newline at end of file + options.RegisterActivity("SayHello", implementation: async (context, input) => await Task.FromResult($"Hello, {input}!")); +}); + +WebApplication app = builder.Build(); + +app.UseEndpoints(endpoints => +{ + endpoints.MapPost("/workflow", Schedule); + + endpoints.MapGet("/workflow/{id}", GetWorkflow); +}); + +async Task Schedule(HttpContext context) +{ + var client = context.RequestServices.GetRequiredService(); + string id = Guid.NewGuid().ToString()[..8]; + await client.ScheduleNewWorkflowAsync("HelloSequence", id); +} + +async Task GetWorkflow(HttpContext context) +{ + var id = (string)context.Request.RouteValues["id"]; + var client = context.RequestServices.GetRequiredService(); + WorkflowMetadata metadata = await client.GetWorkflowMetadata(id, getInputsAndOutputs: true); + if (metadata.Exists) + { + Console.WriteLine($"Created workflow id: '{id}'"); + return Results.Ok(metadata); + } + else + { + return Results.NotFound($"No workflow with ID = '{id}' was found."); + } +} + +app.Run("http://0.0.0.0:8080"); \ No newline at end of file diff --git a/examples/Workflow/Workflow.csproj b/examples/Workflow/Workflow.csproj index 4907e15dd..cee461375 100644 --- a/examples/Workflow/Workflow.csproj +++ b/examples/Workflow/Workflow.csproj @@ -1,14 +1,13 @@ + + + + Exe net6.0 - enable - enable - - - \ No newline at end of file diff --git a/src/Dapr.Workflow/ActivityContext.cs b/src/Dapr.Workflow/ActivityContext.cs index 41b84f230..8a41d3f0e 100644 --- a/src/Dapr.Workflow/ActivityContext.cs +++ b/src/Dapr.Workflow/ActivityContext.cs @@ -1,17 +1,30 @@ -namespace Microsoft.DurableTask; +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.DurableTask; -/// -/// Defines properties and methods for task activity context objects. -/// -public abstract class ActivityContext +namespace Microsoft.Extensions.DependencyInjection { /// - /// Gets the name of the activity. + /// Defines properties and methods for task activity context objects. /// - public abstract string Name { get; } + public class ActivityContext + { + readonly TaskActivityContext innerContext; - /// - /// Gets the unique ID of the current workflow instance. - /// - public abstract string InstanceId { get; } + internal ActivityContext(TaskActivityContext innerContext) + { + this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); + } + + /// + /// Gets the name of the activity. + /// + public TaskName Name => this.innerContext.Name; + + /// + /// Gets the unique ID of the current workflow instance. + /// + public string InstanceId => this.innerContext.InstanceId; + } } \ No newline at end of file diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj index b77b2a2a2..770414eb2 100644 --- a/src/Dapr.Workflow/Dapr.Workflow.csproj +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -1,16 +1,14 @@  + net6.0 - enable enable - - - - - Dapr.Workflows - Dapr Workflows Client SDK - Dapr Workflows client SDK for building workflows as code with Dapr + Dapr.Workflow + Dapr Workflow Client SDK + Dapr Workflow client SDK for building workflows as code with Dapr + 0.1.0 + alpha diff --git a/src/Dapr.Workflow/WorkflowClient.cs b/src/Dapr.Workflow/WorkflowClient.cs new file mode 100644 index 000000000..ba2e76270 --- /dev/null +++ b/src/Dapr.Workflow/WorkflowClient.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.DurableTask; +using Microsoft.DurableTask.Grpc; + +namespace Dapr.Workflow +{ + /// + /// Defines properties and methods for workflow client. + /// + public sealed class WorkflowClient : IAsyncDisposable + { + readonly DurableTaskClient innerClient; + + internal WorkflowClient(IServiceProvider? services = null) + { + DurableTaskGrpcClient.Builder builder = new(); + if (services != null) + { + builder.UseServices(services); + } + + this.innerClient = builder.Build(); + } + + /// + /// Method to schedule a new workflow. + /// + public Task ScheduleNewWorkflowAsync( + string name, + string? instanceId = null, + object? input = null, + DateTime? startTime = null) + { + return this.innerClient.ScheduleNewOrchestrationInstanceAsync(name, instanceId, input, startTime); + } + + /// + /// Method to get the workflow metadata. + /// + public async Task GetWorkflowMetadata(string instanceId, bool getInputsAndOutputs = false) + { + OrchestrationMetadata? metadata = await this.innerClient.GetInstanceMetadataAsync( + instanceId, + getInputsAndOutputs); + return new WorkflowMetadata(metadata); + } + /// + /// Method to implement interface member. + /// + public ValueTask DisposeAsync() + { + return ((IAsyncDisposable)this.innerClient).DisposeAsync(); + } + } +} + + diff --git a/src/Dapr.Workflow/WorkflowContext.cs b/src/Dapr.Workflow/WorkflowContext.cs index 0da9e7e4f..8f8bb93f6 100644 --- a/src/Dapr.Workflow/WorkflowContext.cs +++ b/src/Dapr.Workflow/WorkflowContext.cs @@ -1,36 +1,63 @@ -using System.Text.Json; +using System; +using System.Threading; +using System.Threading.Tasks; using Microsoft.DurableTask; -namespace Dapr.Workflow; - -public class WorkflowsContext +namespace Dapr.Workflow { - readonly TaskOrchestrationContext innerContext; - - internal WorkflowsContext(TaskOrchestrationContext innerContext) - { - this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); - } - - public TaskName Name => this.innerContext.Name; - public string InstanceId => this.innerContext.InstanceId; - - public DateTime CurrentUtcDateTime => this.innerContext.CurrentUtcDateTime; - - public void SetCustomStatus(object? customStatus) => this.innerContext.SetCustomStatus(customStatus); - - public Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) - { - return this.innerContext.CreateTimer(delay, cancellationToken); - } - - public Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) - { - return this.innerContext.WaitForExternalEvent(eventName, timeout); - } - - public Task CallActivityAsync(TaskName name, object? input = null, TaskOptions? options = null) + /// + /// Defines context methods to be called in the workflow definition. + /// + public class WorkflowContext { - return this.innerContext.CallActivityAsync(name, input, options); + readonly TaskOrchestrationContext innerContext; + + internal WorkflowContext(TaskOrchestrationContext innerContext) + { + this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); + } + + /// + /// Method to get the workflow name. + /// + public TaskName Name => this.innerContext.Name; + /// + /// Method to get the workflow id. + /// + public string InstanceId => this.innerContext.InstanceId; + + /// + /// Method to get the current UTC Date time. + /// + public DateTime CurrentUtcDateTime => this.innerContext.CurrentUtcDateTime; + + /// + /// Method to set the custom status to the workflow. + /// + public void SetCustomStatus(object? customStatus) => this.innerContext.SetCustomStatus(customStatus); + + /// + /// Method to create a timer for the workflow. + /// + public Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) + { + return this.innerContext.CreateTimer(delay, cancellationToken); + } + + /// + /// Method to wait for the external event. + /// + public Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) + { + return this.innerContext.WaitForExternalEvent(eventName, timeout); + } + + /// + /// Method to call the activity. + /// + public Task CallActivityAsync(TaskName name, object? input = null, TaskOptions? options = null) + { + return this.innerContext.CallActivityAsync(name, input, options); + } } } \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowMetadata.cs b/src/Dapr.Workflow/WorkflowMetadata.cs new file mode 100644 index 000000000..8ee7c690f --- /dev/null +++ b/src/Dapr.Workflow/WorkflowMetadata.cs @@ -0,0 +1,27 @@ +using Microsoft.DurableTask; + +namespace Dapr.Workflow +{ + /// + /// Defines properties and methods for workflow metadata. + /// + public class WorkflowMetadata + { + internal WorkflowMetadata(OrchestrationMetadata? metadata) + { + this.Details = metadata; + } + /// + /// Method to check if workflow exists. + /// + public bool Exists => this.Details != null; + /// + /// Method to check if workflow is running. + /// + public bool IsWorkflowRunning => this.Details?.RuntimeStatus == OrchestrationRuntimeStatus.Running; + /// + /// Method to get the workflow details. + /// + public OrchestrationMetadata? Details { get; } + } +} \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 5e063d3cc..6a5b75198 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -1,55 +1,62 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; using Microsoft.DurableTask; +using Dapr.Workflow; -namespace Dapr.Workflows; - -public sealed class WorkflowsRuntimeOptions +namespace Microsoft.Extensions.DependencyInjection { - readonly Dictionary> factories = new(); - - public void RegisterWorkflow(string name, Func> implementation) + /// + /// Defines runtime options for workflows. + /// + public sealed class WorkflowRuntimeOptions { - // Dapr workflows are implemented as specialized Durable Task orchestrations - this.factories.Add(name, (IDurableTaskRegistry registry) => - { - registry.AddOrchestrator(name, (innerContext, input) => - { - WorkflowContext workflowContext = new(innerContext); - return implementation(workflowContext, input); - }); - }); - } + readonly Dictionary> factories = new(); - public void RegisterActivity(string name, Func> implementation) - { - // Dapr activities are implemented as specialized Durable Task activities - this.factories.Add(name, (IDurableTaskRegistry registry) => + /// + /// Method regitering the workflow. + /// + public void RegisterWorkflow(string name, Func> implementation) { - registry.AddActivity(name, (innerContext, input) => + // Dapr workflows are implemented as specialized Durable Task orchestrations + this.factories.Add(name, (IDurableTaskRegistry registry) => { - ActivityContext activityContext = new(innerContext); - return implementation(activityContext, input); + registry.AddOrchestrator(name, (innerContext, input) => + { + WorkflowContext workflowContext = new(innerContext); + return implementation(workflowContext, input); + }); }); - }); - } + } - public void RegisterActivity(string name, Func> implementation) - { - // Dapr activities are implemented as specialized Durable Task activities - this.factories.Add(name, (IDurableTaskRegistry registry) => + /// + /// Method regitering the activity.. + /// + public void RegisterActivity(string name, Func> implementation) { - registry.AddActivity(name, (innerContext, input) => + // Dapr activities are implemented as specialized Durable Task activities + this.factories.Add(name, (IDurableTaskRegistry registry) => { - ActivityContext activityContext = new(innerContext); - return implementation(activityContext, input); + registry.AddActivity(name, (innerContext, input) => + { + ActivityContext activityContext = new(innerContext); + return implementation(activityContext, input); + }); }); - }); - } + } - internal void AddWorkflowsToRegistry(IDurableTaskRegistry registry) - { - foreach (Action factory in this.factories.Values) + + /// + /// Method to add workflow to the registry. + /// + internal void AddWorkflowsToRegistry(IDurableTaskRegistry registry) { - factory.Invoke(registry); + foreach (Action factory in this.factories.Values) + { + factory.Invoke(registry); + } } } -} \ No newline at end of file +} + diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index 1e2baf1a1..3df47692e 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -1,49 +1,55 @@ +using System; +using System.Threading; +using System.Threading.Tasks; using Microsoft.DurableTask.Grpc; using Microsoft.Extensions.DependencyInjection; +using Dapr.Workflow; -namespace Dapr.Workflows; - -/// -/// Contains extension methods for using Dapr Workflow with dependency injection. -/// -public static class WorkflowsServiceCollectionExtensions +namespace Microsoft.Extensions.DependencyInjection { /// - /// Adds Dapr Workflow support to the service collection. + /// Contains extension methods for using Dapr Workflow with dependency injection. /// - /// The . - /// A delegate used to configure actor options and register workflow functions. - public static IServiceCollection AddWorkflow( - this IServiceCollection serviceCollection, - Action configure) + public static class WorkflowServiceCollectionExtensions { - if (serviceCollection == null) + /// + /// Adds Dapr Workflow support to the service collection. + /// + /// The . + /// A delegate used to configure actor options and register workflow functions. + public static IServiceCollection AddWorkflow( + this IServiceCollection serviceCollection, + Action configure) { - throw new ArgumentNullException(nameof(serviceCollection)); - } + if (serviceCollection == null) + { + throw new ArgumentNullException(nameof(serviceCollection)); + } - serviceCollection.AddSingleton(); - serviceCollection.AddDaprClient(); - serviceCollection.AddSingleton(services => new WorkflowClient(services)); + serviceCollection.AddSingleton(); + serviceCollection.AddDaprClient(); + serviceCollection.AddSingleton(services => new WorkflowClient(services)); - serviceCollection.AddHostedService(services => - { - DurableTaskGrpcWorker.Builder workerBuilder = DurableTaskGrpcWorker.CreateBuilder().UseServices(services); + serviceCollection.AddHostedService(services => + { + DurableTaskGrpcWorker.Builder workerBuilder = DurableTaskGrpcWorker.CreateBuilder().UseServices(services); - WorkflowRuntimeOptions options = services.GetRequiredService(); - configure?.Invoke(options); + WorkflowRuntimeOptions options = services.GetRequiredService(); + configure?.Invoke(options); - workerBuilder.UseServices(services); + workerBuilder.UseServices(services); - workerBuilder.AddTasks(registry => - { - options.AddWorkflowsToRegistry(registry); - }); + workerBuilder.AddTasks(registry => + { + options.AddWorkflowsToRegistry(registry); + }); - DurableTaskGrpcWorker worker = workerBuilder.Build(); - return worker; - }); + DurableTaskGrpcWorker worker = workerBuilder.Build(); + return worker; + }); - return serviceCollection; + return serviceCollection; + } } -} \ No newline at end of file +} + diff --git a/test/Dapr.Workflow.Test/WorkflowServiceCollectionExtensionsTest.cs b/test/Dapr.Workflow.Test/WorkflowServiceCollectionExtensionsTest.cs new file mode 100644 index 000000000..885dd6e82 --- /dev/null +++ b/test/Dapr.Workflow.Test/WorkflowServiceCollectionExtensionsTest.cs @@ -0,0 +1,23 @@ +namespace Dapr.Client +{ + public partial class DaprWorkflowTest + { + public void CanRegisterWorkflow() + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddWorkflow(options => + { + options.RegisterWorkflow("testName", implementation: async (context, input) => + { + string result = "Testing"; + return result; + }); + }); + + var runtime = services.BuildServiceProvider().GetRequiredService(); + Assert.Equal("testName", runtime.factories.Keys[0]); + } + } +} \ No newline at end of file From 6dbbe26e26ac2eb6cc4ea79040e67562ea50aacd Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Tue, 1 Nov 2022 15:31:09 -0700 Subject: [PATCH 03/20] Handled mistake in the workflow name Signed-off-by: Amulya Varote --- examples/Workflow/Program.cs | 10 +++++--- examples/Workflow/demo.http | 6 +++++ src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 7 +++++- ...WorkflowServiceCollectionExtensionsTest.cs | 23 ------------------- 4 files changed, 19 insertions(+), 27 deletions(-) create mode 100644 examples/Workflow/demo.http delete mode 100644 test/Dapr.Workflow.Test/WorkflowServiceCollectionExtensionsTest.cs diff --git a/examples/Workflow/Program.cs b/examples/Workflow/Program.cs index 46fdf73bc..681e5c8ac 100644 --- a/examples/Workflow/Program.cs +++ b/examples/Workflow/Program.cs @@ -13,7 +13,7 @@ builder.Services.AddWorkflow(options => { - options.RegisterWorkflow("helloWorld", implementation: async (context, input) => + options.RegisterWorkflow("HelloWorld", implementation: async (context, input) => { string result = ""; result = await context.CallActivityAsync("SayHello", "World"); @@ -24,6 +24,8 @@ WebApplication app = builder.Build(); +app.UseRouting(); + app.UseEndpoints(endpoints => { endpoints.MapPost("/workflow", Schedule); @@ -31,11 +33,12 @@ endpoints.MapGet("/workflow/{id}", GetWorkflow); }); -async Task Schedule(HttpContext context) +async Task Schedule(HttpContext context) { var client = context.RequestServices.GetRequiredService(); string id = Guid.NewGuid().ToString()[..8]; - await client.ScheduleNewWorkflowAsync("HelloSequence", id); + await client.ScheduleNewWorkflowAsync("HelloWorld", id); + return Results.Ok(id); } async Task GetWorkflow(HttpContext context) @@ -46,6 +49,7 @@ async Task GetWorkflow(HttpContext context) if (metadata.Exists) { Console.WriteLine($"Created workflow id: '{id}'"); + Console.WriteLine($"Metadata: {metadata.Details}"); return Results.Ok(metadata); } else diff --git a/examples/Workflow/demo.http b/examples/Workflow/demo.http new file mode 100644 index 000000000..e16f4518b --- /dev/null +++ b/examples/Workflow/demo.http @@ -0,0 +1,6 @@ +### Create new order +POST http://localhost:8080/workflow +Content-Type: application/json + +### Query placeholder +GET http://localhost:8080/workflow/40b1c35c \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 6a5b75198..37e0273f3 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -12,7 +12,12 @@ namespace Microsoft.Extensions.DependencyInjection /// public sealed class WorkflowRuntimeOptions { - readonly Dictionary> factories = new(); + // Dictionary> factories = new(); + + /// + /// Dictionary to and name and registery of a workflow. + /// + public Dictionary> factories => new(); /// /// Method regitering the workflow. diff --git a/test/Dapr.Workflow.Test/WorkflowServiceCollectionExtensionsTest.cs b/test/Dapr.Workflow.Test/WorkflowServiceCollectionExtensionsTest.cs deleted file mode 100644 index 885dd6e82..000000000 --- a/test/Dapr.Workflow.Test/WorkflowServiceCollectionExtensionsTest.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Dapr.Client -{ - public partial class DaprWorkflowTest - { - public void CanRegisterWorkflow() - { - var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); - services.AddWorkflow(options => - { - options.RegisterWorkflow("testName", implementation: async (context, input) => - { - string result = "Testing"; - return result; - }); - }); - - var runtime = services.BuildServiceProvider().GetRequiredService(); - Assert.Equal("testName", runtime.factories.Keys[0]); - } - } -} \ No newline at end of file From f69a314c704d920d81bd48fceb18d0ec1e3dee63 Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Thu, 3 Nov 2022 09:44:45 -0700 Subject: [PATCH 04/20] Added common license header to .cs files Signed-off-by: Amulya Varote --- src/Dapr.Workflow/ActivityContext.cs | 13 +++++++++++++ src/Dapr.Workflow/WorkflowClient.cs | 13 +++++++++++++ src/Dapr.Workflow/WorkflowContext.cs | 13 +++++++++++++ src/Dapr.Workflow/WorkflowMetadata.cs | 13 +++++++++++++ src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 15 +++++++++++++-- .../WorkflowServiceCollectionExtensions.cs | 13 +++++++++++++ 6 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/Dapr.Workflow/ActivityContext.cs b/src/Dapr.Workflow/ActivityContext.cs index 8a41d3f0e..b69a68317 100644 --- a/src/Dapr.Workflow/ActivityContext.cs +++ b/src/Dapr.Workflow/ActivityContext.cs @@ -1,3 +1,16 @@ +// ------------------------------------------------------------------------ +// 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.Threading; using System.Threading.Tasks; diff --git a/src/Dapr.Workflow/WorkflowClient.cs b/src/Dapr.Workflow/WorkflowClient.cs index ba2e76270..803760afd 100644 --- a/src/Dapr.Workflow/WorkflowClient.cs +++ b/src/Dapr.Workflow/WorkflowClient.cs @@ -1,3 +1,16 @@ +// ------------------------------------------------------------------------ +// 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.Threading; using System.Threading.Tasks; diff --git a/src/Dapr.Workflow/WorkflowContext.cs b/src/Dapr.Workflow/WorkflowContext.cs index 8f8bb93f6..f67c7f6a0 100644 --- a/src/Dapr.Workflow/WorkflowContext.cs +++ b/src/Dapr.Workflow/WorkflowContext.cs @@ -1,3 +1,16 @@ +// ------------------------------------------------------------------------ +// 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.Threading; using System.Threading.Tasks; diff --git a/src/Dapr.Workflow/WorkflowMetadata.cs b/src/Dapr.Workflow/WorkflowMetadata.cs index 8ee7c690f..7c54b9aab 100644 --- a/src/Dapr.Workflow/WorkflowMetadata.cs +++ b/src/Dapr.Workflow/WorkflowMetadata.cs @@ -1,3 +1,16 @@ +// ------------------------------------------------------------------------ +// 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 Microsoft.DurableTask; namespace Dapr.Workflow diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 37e0273f3..817818f83 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -1,3 +1,16 @@ +// ------------------------------------------------------------------------ +// 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.Threading; using System.Threading.Tasks; @@ -12,8 +25,6 @@ namespace Microsoft.Extensions.DependencyInjection /// public sealed class WorkflowRuntimeOptions { - // Dictionary> factories = new(); - /// /// Dictionary to and name and registery of a workflow. /// diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index 3df47692e..9534e092c 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -1,3 +1,16 @@ +// ------------------------------------------------------------------------ +// 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.Threading; using System.Threading.Tasks; From 266cefd2502c739f3796b2e4f1e35039ec10bbda Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Thu, 3 Nov 2022 13:59:21 -0700 Subject: [PATCH 05/20] Changed namespace to Dapr.Workflow Signed-off-by: Amulya Varote --- src/Dapr.Workflow/ActivityContext.cs | 12 ++++++------ src/Dapr.Workflow/WorkflowContext.cs | 10 +++++----- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 16 ++++++++-------- .../WorkflowServiceCollectionExtensions.cs | 16 ++++++++-------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Dapr.Workflow/ActivityContext.cs b/src/Dapr.Workflow/ActivityContext.cs index b69a68317..f348cda2e 100644 --- a/src/Dapr.Workflow/ActivityContext.cs +++ b/src/Dapr.Workflow/ActivityContext.cs @@ -11,13 +11,13 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.DurableTask; - -namespace Microsoft.Extensions.DependencyInjection +namespace Dapr.Workflow { + using System; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.DurableTask; + /// /// Defines properties and methods for task activity context objects. /// diff --git a/src/Dapr.Workflow/WorkflowContext.cs b/src/Dapr.Workflow/WorkflowContext.cs index f67c7f6a0..a664fde8f 100644 --- a/src/Dapr.Workflow/WorkflowContext.cs +++ b/src/Dapr.Workflow/WorkflowContext.cs @@ -11,13 +11,13 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.DurableTask; - namespace Dapr.Workflow { + using System; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.DurableTask; + /// /// Defines context methods to be called in the workflow definition. /// diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 817818f83..11cfef83a 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -11,15 +11,15 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.DurableTask; -using Dapr.Workflow; - -namespace Microsoft.Extensions.DependencyInjection +namespace Dapr.Workflow { + using System; + using System.Threading; + using System.Threading.Tasks; + using System.Collections.Generic; + using Microsoft.DurableTask; + using Dapr.Workflow; + /// /// Defines runtime options for workflows. /// diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index 9534e092c..227ba3f59 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -11,15 +11,15 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.DurableTask.Grpc; -using Microsoft.Extensions.DependencyInjection; -using Dapr.Workflow; - -namespace Microsoft.Extensions.DependencyInjection +namespace Dapr.Workflow { + using System; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.DurableTask.Grpc; + using Microsoft.Extensions.DependencyInjection; + using Dapr.Workflow; + /// /// Contains extension methods for using Dapr Workflow with dependency injection. /// From 01c01e5cd3b7f41770fb1f32a18dcc630dfc2dbf Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Wed, 9 Nov 2022 16:29:52 -0800 Subject: [PATCH 06/20] Addressed docs related review comments Signed-off-by: Amulya Varote --- src/Dapr.Workflow/Dapr.Workflow.csproj | 4 ++-- src/Dapr.Workflow/WorkflowClient.cs | 11 ++++++----- src/Dapr.Workflow/WorkflowContext.cs | 18 ++++++++++-------- src/Dapr.Workflow/WorkflowMetadata.cs | 14 +++++++++----- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 6 +++--- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj index 770414eb2..16bf781b9 100644 --- a/src/Dapr.Workflow/Dapr.Workflow.csproj +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -5,8 +5,8 @@ net6.0 enable Dapr.Workflow - Dapr Workflow Client SDK - Dapr Workflow client SDK for building workflows as code with Dapr + Dapr Workflow Authoring SDK + Dapr Workflow SDK for building workflows as code with Dapr 0.1.0 alpha diff --git a/src/Dapr.Workflow/WorkflowClient.cs b/src/Dapr.Workflow/WorkflowClient.cs index 803760afd..f2ccf1ec6 100644 --- a/src/Dapr.Workflow/WorkflowClient.cs +++ b/src/Dapr.Workflow/WorkflowClient.cs @@ -20,7 +20,7 @@ namespace Dapr.Workflow { /// - /// Defines properties and methods for workflow client. + /// Defines client operations for managing Dapr Workflow instances. /// public sealed class WorkflowClient : IAsyncDisposable { @@ -38,7 +38,7 @@ internal WorkflowClient(IServiceProvider? services = null) } /// - /// Method to schedule a new workflow. + /// Schedules a new workflow instance for execution. /// public Task ScheduleNewWorkflowAsync( string name, @@ -50,17 +50,18 @@ public Task ScheduleNewWorkflowAsync( } /// - /// Method to get the workflow metadata. + /// Fetches runtime metadata for the specified workflow instance. /// - public async Task GetWorkflowMetadata(string instanceId, bool getInputsAndOutputs = false) + public async Task GetWorkflowMetadataAsync(string instanceId, bool getInputsAndOutputs = false) { OrchestrationMetadata? metadata = await this.innerClient.GetInstanceMetadataAsync( instanceId, getInputsAndOutputs); return new WorkflowMetadata(metadata); } + /// - /// Method to implement interface member. + /// /// Disposes any unmanaged resources associated with this client. /// public ValueTask DisposeAsync() { diff --git a/src/Dapr.Workflow/WorkflowContext.cs b/src/Dapr.Workflow/WorkflowContext.cs index a664fde8f..4cf1265a9 100644 --- a/src/Dapr.Workflow/WorkflowContext.cs +++ b/src/Dapr.Workflow/WorkflowContext.cs @@ -19,7 +19,8 @@ namespace Dapr.Workflow using Microsoft.DurableTask; /// - /// Defines context methods to be called in the workflow definition. + /// Context object used by workflow implementations to perform actions such as scheduling activities, durable timers, waiting for + /// external events, and for getting basic information about the current workflow instance. /// public class WorkflowContext { @@ -31,26 +32,27 @@ internal WorkflowContext(TaskOrchestrationContext innerContext) } /// - /// Method to get the workflow name. + /// Gets the name of the current workflow. /// public TaskName Name => this.innerContext.Name; + /// - /// Method to get the workflow id. + /// Gets the instance ID of the current workflow. /// public string InstanceId => this.innerContext.InstanceId; /// - /// Method to get the current UTC Date time. + /// Gets the current workflow time in UTC. /// public DateTime CurrentUtcDateTime => this.innerContext.CurrentUtcDateTime; /// - /// Method to set the custom status to the workflow. + /// Assigns a custom status value to the current workflow. /// public void SetCustomStatus(object? customStatus) => this.innerContext.SetCustomStatus(customStatus); /// - /// Method to create a timer for the workflow. + /// Creates a durable timer that expires after the specified delay. /// public Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) { @@ -58,7 +60,7 @@ public Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = de } /// - /// Method to wait for the external event. + /// Waits for an event to be raised with name and returns the event data. /// public Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) { @@ -66,7 +68,7 @@ public Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) } /// - /// Method to call the activity. + /// Asynchronously invokes an activity by name and with the specified input value. /// public Task CallActivityAsync(TaskName name, object? input = null, TaskOptions? options = null) { diff --git a/src/Dapr.Workflow/WorkflowMetadata.cs b/src/Dapr.Workflow/WorkflowMetadata.cs index 7c54b9aab..e50bf8dea 100644 --- a/src/Dapr.Workflow/WorkflowMetadata.cs +++ b/src/Dapr.Workflow/WorkflowMetadata.cs @@ -16,7 +16,7 @@ namespace Dapr.Workflow { /// - /// Defines properties and methods for workflow metadata. + /// Represents a snapshot of a workflow instance's current state, including metadata. /// public class WorkflowMetadata { @@ -24,16 +24,20 @@ internal WorkflowMetadata(OrchestrationMetadata? metadata) { this.Details = metadata; } + /// - /// Method to check if workflow exists. + /// Gets a value indicating whether the requested workflow instance exists. /// public bool Exists => this.Details != null; + /// - /// Method to check if workflow is running. + /// Gets a value indicating whether the requested workflow is in a running state. /// - public bool IsWorkflowRunning => this.Details?.RuntimeStatus == OrchestrationRuntimeStatus.Running; + public bool IsWorkflowRunning => this.Details?.RuntimeStatus == OrchestrationRuntimeStatus.Running; + /// - /// Method to get the workflow details. + /// Gets the detailed metadata for the requested workflow instance. + /// This value will be null when is false. /// public OrchestrationMetadata? Details { get; } } diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 11cfef83a..e03383306 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -28,10 +28,10 @@ public sealed class WorkflowRuntimeOptions /// /// Dictionary to and name and registery of a workflow. /// - public Dictionary> factories => new(); + readonly Dictionary> factories => new(); /// - /// Method regitering the workflow. + /// Registers a workflow as a function that takes a specified input type and returns a specified output type. /// public void RegisterWorkflow(string name, Func> implementation) { @@ -47,7 +47,7 @@ public void RegisterWorkflow(string name, Func - /// Method regitering the activity.. + /// Registers a workflow activity as a function that takes a specified input type and returns a specified output type. /// public void RegisterActivity(string name, Func> implementation) { From 73b08a50d23aea83e2bf4d3892d346e696686298 Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Wed, 9 Nov 2022 16:46:43 -0800 Subject: [PATCH 07/20] Reverted few changes to avoid compile time error Signed-off-by: Amulya Varote --- examples/Workflow/Program.cs | 2 +- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Workflow/Program.cs b/examples/Workflow/Program.cs index 681e5c8ac..53f6fadb4 100644 --- a/examples/Workflow/Program.cs +++ b/examples/Workflow/Program.cs @@ -45,7 +45,7 @@ async Task GetWorkflow(HttpContext context) { var id = (string)context.Request.RouteValues["id"]; var client = context.RequestServices.GetRequiredService(); - WorkflowMetadata metadata = await client.GetWorkflowMetadata(id, getInputsAndOutputs: true); + WorkflowMetadata metadata = await client.GetWorkflowMetadataAsync(id, getInputsAndOutputs: true); if (metadata.Exists) { Console.WriteLine($"Created workflow id: '{id}'"); diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index e03383306..3a2856a4d 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -28,7 +28,7 @@ public sealed class WorkflowRuntimeOptions /// /// Dictionary to and name and registery of a workflow. /// - readonly Dictionary> factories => new(); + public Dictionary> factories = new(); /// /// Registers a workflow as a function that takes a specified input type and returns a specified output type. From b832ab8f504d57542a357da89ea1b53aa3a34f53 Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Wed, 9 Nov 2022 16:48:53 -0800 Subject: [PATCH 08/20] Examples changes Signed-off-by: Amulya Varote --- examples/Workflow/demo.http | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Workflow/demo.http b/examples/Workflow/demo.http index e16f4518b..00210e840 100644 --- a/examples/Workflow/demo.http +++ b/examples/Workflow/demo.http @@ -3,4 +3,4 @@ POST http://localhost:8080/workflow Content-Type: application/json ### Query placeholder -GET http://localhost:8080/workflow/40b1c35c \ No newline at end of file +GET http://localhost:8080/workflow/XXX \ No newline at end of file From 2515d01c46acc05a15c8f4849bfe25d7f72f0239 Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Wed, 9 Nov 2022 22:25:17 -0800 Subject: [PATCH 09/20] Added readonly dictionary Signed-off-by: Amulya Varote --- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 3a2856a4d..611bdf2c0 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -28,7 +28,7 @@ public sealed class WorkflowRuntimeOptions /// /// Dictionary to and name and registery of a workflow. /// - public Dictionary> factories = new(); + readonly Dictionary> factories = new(); /// /// Registers a workflow as a function that takes a specified input type and returns a specified output type. From fc1ee56584b52ddebf4be9732bdee24a22476317 Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Thu, 10 Nov 2022 13:04:47 -0800 Subject: [PATCH 10/20] Added .csproj to sln file with few minor comments Signed-off-by: Amulya Varote --- all.sln | 7 +++++++ src/Dapr.Workflow/WorkflowClient.cs | 2 +- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/all.sln b/all.sln index d5db7a9e9..247141aad 100644 --- a/all.sln +++ b/all.sln @@ -87,6 +87,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.E2E.Test.App.Reentrant EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistributedLock", "examples\Client\DistributedLock\DistributedLock.csproj", "{35031EDB-C0DE-453A-8335-D2EBEA2FC640}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Workflow", "src\Dapr.Workflow\Dapr.Workflow.csproj", "{07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -217,6 +219,10 @@ Global {35031EDB-C0DE-453A-8335-D2EBEA2FC640}.Debug|Any CPU.Build.0 = Debug|Any CPU {35031EDB-C0DE-453A-8335-D2EBEA2FC640}.Release|Any CPU.ActiveCfg = Release|Any CPU {35031EDB-C0DE-453A-8335-D2EBEA2FC640}.Release|Any CPU.Build.0 = Release|Any CPU + {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -256,6 +262,7 @@ Global {F80F837E-D2FC-4FFC-B68F-3CF0EC015F66} = {A7F41094-8648-446B-AECD-DCC2CC871F73} {5BE7F505-7D77-4C3A-ABFD-54088774DAA7} = {DD020B34-460F-455F-8D17-CF4A949F100B} {35031EDB-C0DE-453A-8335-D2EBEA2FC640} = {A7F41094-8648-446B-AECD-DCC2CC871F73} + {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99} = {27C5D71D-0721-4221-9286-B94AB07B58CF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} diff --git a/src/Dapr.Workflow/WorkflowClient.cs b/src/Dapr.Workflow/WorkflowClient.cs index f2ccf1ec6..7a47cfcb0 100644 --- a/src/Dapr.Workflow/WorkflowClient.cs +++ b/src/Dapr.Workflow/WorkflowClient.cs @@ -61,7 +61,7 @@ public async Task GetWorkflowMetadataAsync(string instanceId, } /// - /// /// Disposes any unmanaged resources associated with this client. + /// Disposes any unmanaged resources associated with this client. /// public ValueTask DisposeAsync() { diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 611bdf2c0..7ede9131d 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -26,7 +26,7 @@ namespace Dapr.Workflow public sealed class WorkflowRuntimeOptions { /// - /// Dictionary to and name and registery of a workflow. + /// Dictionary to name and register a workflow. /// readonly Dictionary> factories = new(); From dd5a294ac8433841d829317a05ff285be00ec254 Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Thu, 17 Nov 2022 16:48:02 -0800 Subject: [PATCH 11/20] Change nuget dependency and update folder structure This commit re-adds commit https://github.com/dapr/dotnet-sdk/pull/973/commits/d5b9189da5aa0d2d085f17246a95a7de95d9f726 but with DCO. Signed-off-by: Tiago Alves Macambira --- all.sln | 60 +++++++++++-------- .../Workflow/{ => WorkflowWebApp}/Program.cs | 0 .../Properties/launchSettings.json | 0 .../WorkflowWebApp.csproj} | 2 +- .../Workflow/{ => WorkflowWebApp}/demo.http | 0 src/Dapr.Workflow/Dapr.Workflow.csproj | 2 +- 6 files changed, 37 insertions(+), 27 deletions(-) rename examples/Workflow/{ => WorkflowWebApp}/Program.cs (100%) rename examples/Workflow/{ => WorkflowWebApp}/Properties/launchSettings.json (100%) rename examples/Workflow/{Workflow.csproj => WorkflowWebApp/WorkflowWebApp.csproj} (70%) rename examples/Workflow/{ => WorkflowWebApp}/demo.http (100%) diff --git a/all.sln b/all.sln index 247141aad..6339c47b0 100644 --- a/all.sln +++ b/all.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29318.209 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors", "src\Dapr.Actors\Dapr.Actors.csproj", "{C2DB4B64-B7C3-4FED-8753-C040F677C69A}" EndProject @@ -35,59 +35,63 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Actors.AspNetCore.Test", "test\Dapr.Actors.AspNetCore.Test\Dapr.Actors.AspNetCore.Test.csproj", "{9C1D6ABA-5EDE-4FA0-A8A9-0AB98CB74737}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.AspNetCore.Test", "test\Dapr.Actors.AspNetCore.Test\Dapr.Actors.AspNetCore.Test.csproj", "{9C1D6ABA-5EDE-4FA0-A8A9-0AB98CB74737}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Actors.AspNetCore.IntegrationTest", "test\Dapr.Actors.AspNetCore.IntegrationTest\Dapr.Actors.AspNetCore.IntegrationTest.csproj", "{95BAF30B-8089-42CE-8530-6DFBCE1F6A07}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.AspNetCore.IntegrationTest", "test\Dapr.Actors.AspNetCore.IntegrationTest\Dapr.Actors.AspNetCore.IntegrationTest.csproj", "{95BAF30B-8089-42CE-8530-6DFBCE1F6A07}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Actors.AspNetCore.IntegrationTest.App", "test\Dapr.Actors.AspNetCore.IntegrationTest.App\Dapr.Actors.AspNetCore.IntegrationTest.App.csproj", "{1BA7E772-8AA7-4D5A-800D-66B17F62421C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.AspNetCore.IntegrationTest.App", "test\Dapr.Actors.AspNetCore.IntegrationTest.App\Dapr.Actors.AspNetCore.IntegrationTest.App.csproj", "{1BA7E772-8AA7-4D5A-800D-66B17F62421C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Extensions.Configuration.Test", "test\Dapr.Extensions.Configuration.Test\Dapr.Extensions.Configuration.Test.csproj", "{78FC19B2-396C-4ED2-BFD9-6C5667C61666}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Extensions.Configuration.Test", "test\Dapr.Extensions.Configuration.Test\Dapr.Extensions.Configuration.Test.csproj", "{78FC19B2-396C-4ED2-BFD9-6C5667C61666}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Extensions.Configuration", "src\Dapr.Extensions.Configuration\Dapr.Extensions.Configuration.csproj", "{B615B353-476C-43B9-A776-B193B0DBD256}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Extensions.Configuration", "src\Dapr.Extensions.Configuration\Dapr.Extensions.Configuration.csproj", "{B615B353-476C-43B9-A776-B193B0DBD256}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{D687DDC4-66C5-4667-9E3A-FD8B78ECAA78}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetCore", "AspNetCore", "{A11DC259-D1DB-4686-AD28-A427D0BABA83}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcServiceSample", "examples\AspNetCore\GrpcServiceSample\GrpcServiceSample.csproj", "{2EC50C79-782D-4985-ABB1-AD07F35D1621}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GrpcServiceSample", "examples\AspNetCore\GrpcServiceSample\GrpcServiceSample.csproj", "{2EC50C79-782D-4985-ABB1-AD07F35D1621}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoutingSample", "examples\AspNetCore\RoutingSample\RoutingSample.csproj", "{15A16323-2CCA-472E-BE79-07259DAD5F6F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RoutingSample", "examples\AspNetCore\RoutingSample\RoutingSample.csproj", "{15A16323-2CCA-472E-BE79-07259DAD5F6F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SecretStoreConfigurationProviderSample", "examples\AspNetCore\SecretStoreConfigurationProviderSample\SecretStoreConfigurationProviderSample.csproj", "{5BACBA51-03FE-4CE1-B0F5-9E9C2A132FAB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SecretStoreConfigurationProviderSample", "examples\AspNetCore\SecretStoreConfigurationProviderSample\SecretStoreConfigurationProviderSample.csproj", "{5BACBA51-03FE-4CE1-B0F5-9E9C2A132FAB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControllerSample", "examples\AspNetCore\ControllerSample\ControllerSample.csproj", "{3160CC92-1D6E-42CB-AE89-9401C8CEC5CB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControllerSample", "examples\AspNetCore\ControllerSample\ControllerSample.csproj", "{3160CC92-1D6E-42CB-AE89-9401C8CEC5CB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Actor", "Actor", "{02374BD0-BF0B-40F8-A04A-C4C4D61D4992}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IDemoActor", "examples\Actor\IDemoActor\IDemoActor.csproj", "{7957E852-1291-4FAA-9034-FB66CE817FF1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IDemoActor", "examples\Actor\IDemoActor\IDemoActor.csproj", "{7957E852-1291-4FAA-9034-FB66CE817FF1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoActor", "examples\Actor\DemoActor\DemoActor.csproj", "{626D74DD-4F37-4F74-87A3-5A6888684F5E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoActor", "examples\Actor\DemoActor\DemoActor.csproj", "{626D74DD-4F37-4F74-87A3-5A6888684F5E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorClient", "examples\Actor\ActorClient\ActorClient.csproj", "{CC0A5C98-ACDE-4139-BA2F-2995A9B8E18C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActorClient", "examples\Actor\ActorClient\ActorClient.csproj", "{CC0A5C98-ACDE-4139-BA2F-2995A9B8E18C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{A7F41094-8648-446B-AECD-DCC2CC871F73}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StateManagement", "examples\Client\StateManagement\StateManagement.csproj", "{F70AC78E-8925-4770-832A-2FC67A620EB2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StateManagement", "examples\Client\StateManagement\StateManagement.csproj", "{F70AC78E-8925-4770-832A-2FC67A620EB2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceInvocation", "examples\Client\ServiceInvocation\ServiceInvocation.csproj", "{8B570E70-0E73-4042-A4B6-1CC3CC782A65}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceInvocation", "examples\Client\ServiceInvocation\ServiceInvocation.csproj", "{8B570E70-0E73-4042-A4B6-1CC3CC782A65}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PublishSubscribe", "examples\Client\PublishSubscribe\PublishSubscribe.csproj", "{DE6913E3-E5D9-4D1D-95F9-9FED87BD09BC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublishSubscribe", "examples\Client\PublishSubscribe\PublishSubscribe.csproj", "{DE6913E3-E5D9-4D1D-95F9-9FED87BD09BC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.E2E.Test", "test\Dapr.E2E.Test\Dapr.E2E.Test.csproj", "{4AA9E7B7-36BF-4AAE-BFA3-C9CE8740F4A0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test", "test\Dapr.E2E.Test\Dapr.E2E.Test.csproj", "{4AA9E7B7-36BF-4AAE-BFA3-C9CE8740F4A0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.E2E.Test.App", "test\Dapr.E2E.Test.App\Dapr.E2E.Test.App.csproj", "{345FC3FB-D1E9-4AE8-9052-17D20AB01FA2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.App", "test\Dapr.E2E.Test.App\Dapr.E2E.Test.App.csproj", "{345FC3FB-D1E9-4AE8-9052-17D20AB01FA2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.E2E.Test.Actors", "test\Dapr.E2E.Test.Actors\Dapr.E2E.Test.Actors.csproj", "{2AED1542-A8ED-488D-B6D0-E16AB5D6EF6C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.Actors", "test\Dapr.E2E.Test.Actors\Dapr.E2E.Test.Actors.csproj", "{2AED1542-A8ED-488D-B6D0-E16AB5D6EF6C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.E2E.Test.App.Grpc", "test\Dapr.E2E.Test.App.Grpc\Dapr.E2E.Test.App.Grpc.csproj", "{E8212911-344B-4638-ADC3-B215BCDCAFD1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.App.Grpc", "test\Dapr.E2E.Test.App.Grpc\Dapr.E2E.Test.App.Grpc.csproj", "{E8212911-344B-4638-ADC3-B215BCDCAFD1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigurationApi", "examples\Client\ConfigurationApi\ConfigurationApi.csproj", "{F80F837E-D2FC-4FFC-B68F-3CF0EC015F66}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConfigurationApi", "examples\Client\ConfigurationApi\ConfigurationApi.csproj", "{F80F837E-D2FC-4FFC-B68F-3CF0EC015F66}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.E2E.Test.App.ReentrantActors", "test\Dapr.E2E.Test.App.ReentrantActor\Dapr.E2E.Test.App.ReentrantActors.csproj", "{5BE7F505-7D77-4C3A-ABFD-54088774DAA7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.App.ReentrantActors", "test\Dapr.E2E.Test.App.ReentrantActor\Dapr.E2E.Test.App.ReentrantActors.csproj", "{5BE7F505-7D77-4C3A-ABFD-54088774DAA7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistributedLock", "examples\Client\DistributedLock\DistributedLock.csproj", "{35031EDB-C0DE-453A-8335-D2EBEA2FC640}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DistributedLock", "examples\Client\DistributedLock\DistributedLock.csproj", "{35031EDB-C0DE-453A-8335-D2EBEA2FC640}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Workflow", "src\Dapr.Workflow\Dapr.Workflow.csproj", "{07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Workflow", "src\Dapr.Workflow\Dapr.Workflow.csproj", "{07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflow", "Workflow", "{BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowWebApp", "examples\Workflow\WorkflowWebApp\WorkflowWebApp.csproj", "{5C61ABED-7623-4C28-A5C9-C5972A0F669C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -223,6 +227,10 @@ Global {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}.Debug|Any CPU.Build.0 = Debug|Any CPU {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}.Release|Any CPU.ActiveCfg = Release|Any CPU {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99}.Release|Any CPU.Build.0 = Release|Any CPU + {5C61ABED-7623-4C28-A5C9-C5972A0F669C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C61ABED-7623-4C28-A5C9-C5972A0F669C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C61ABED-7623-4C28-A5C9-C5972A0F669C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C61ABED-7623-4C28-A5C9-C5972A0F669C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -263,6 +271,8 @@ Global {5BE7F505-7D77-4C3A-ABFD-54088774DAA7} = {DD020B34-460F-455F-8D17-CF4A949F100B} {35031EDB-C0DE-453A-8335-D2EBEA2FC640} = {A7F41094-8648-446B-AECD-DCC2CC871F73} {07578B6C-9B96-4B3D-BA2E-7800EFCA7F99} = {27C5D71D-0721-4221-9286-B94AB07B58CF} + {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9} = {D687DDC4-66C5-4667-9E3A-FD8B78ECAA78} + {5C61ABED-7623-4C28-A5C9-C5972A0F669C} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} diff --git a/examples/Workflow/Program.cs b/examples/Workflow/WorkflowWebApp/Program.cs similarity index 100% rename from examples/Workflow/Program.cs rename to examples/Workflow/WorkflowWebApp/Program.cs diff --git a/examples/Workflow/Properties/launchSettings.json b/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json similarity index 100% rename from examples/Workflow/Properties/launchSettings.json rename to examples/Workflow/WorkflowWebApp/Properties/launchSettings.json diff --git a/examples/Workflow/Workflow.csproj b/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj similarity index 70% rename from examples/Workflow/Workflow.csproj rename to examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj index cee461375..f74130672 100644 --- a/examples/Workflow/Workflow.csproj +++ b/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj @@ -1,7 +1,7 @@ - + diff --git a/examples/Workflow/demo.http b/examples/Workflow/WorkflowWebApp/demo.http similarity index 100% rename from examples/Workflow/demo.http rename to examples/Workflow/WorkflowWebApp/demo.http diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj index 16bf781b9..d68c89556 100644 --- a/src/Dapr.Workflow/Dapr.Workflow.csproj +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -12,7 +12,7 @@ - + From 99285a92c601aff2b6f4051944657d562beafca2 Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Thu, 8 Dec 2022 18:01:17 -0800 Subject: [PATCH 12/20] Update Durable Task SDK version & simplify example Signed-off-by: Chris Gillum --- examples/Workflow/WorkflowWebApp/Program.cs | 50 ++++++++----------- .../WorkflowWebApp/WorkflowWebApp.csproj | 2 + src/Dapr.Workflow/Dapr.Workflow.csproj | 3 +- src/Dapr.Workflow/WorkflowClient.cs | 26 +++++----- src/Dapr.Workflow/WorkflowMetadata.cs | 6 +-- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 27 +++++----- .../WorkflowServiceCollectionExtensions.cs | 33 ++++++------ 7 files changed, 65 insertions(+), 82 deletions(-) diff --git a/examples/Workflow/WorkflowWebApp/Program.cs b/examples/Workflow/WorkflowWebApp/Program.cs index 53f6fadb4..535ea4eac 100644 --- a/examples/Workflow/WorkflowWebApp/Program.cs +++ b/examples/Workflow/WorkflowWebApp/Program.cs @@ -1,61 +1,51 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using System.Web; -using Microsoft.AspNetCore.Http; -using Dapr.Workflow; +using Dapr.Workflow; // The workflow host is a background service that connects to the sidecar over gRPC WebApplicationBuilder builder = WebApplication.CreateBuilder(args); +// Dapr workflows are registered as part of the service configuration builder.Services.AddWorkflow(options => { + // Example of registering a "Hello World" workflow function options.RegisterWorkflow("HelloWorld", implementation: async (context, input) => { string result = ""; result = await context.CallActivityAsync("SayHello", "World"); return result; }); - options.RegisterActivity("SayHello", implementation: async (context, input) => await Task.FromResult($"Hello, {input}!")); + + // Example of registering a "Say Hello" workflow activity function + options.RegisterActivity("SayHello", implementation: (context, input) => + { + return Task.FromResult($"Hello, {input}!"); + }); }); WebApplication app = builder.Build(); -app.UseRouting(); - -app.UseEndpoints(endpoints => +// POST starts new workflow instances +app.MapPost("/workflow", async (HttpContext context, WorkflowClient client) => { - endpoints.MapPost("/workflow", Schedule); - - endpoints.MapGet("/workflow/{id}", GetWorkflow); -}); - -async Task Schedule(HttpContext context) -{ - var client = context.RequestServices.GetRequiredService(); string id = Guid.NewGuid().ToString()[..8]; await client.ScheduleNewWorkflowAsync("HelloWorld", id); - return Results.Ok(id); -} -async Task GetWorkflow(HttpContext context) + // return an HTTP 202 and a Location header to be used for status query + return Results.AcceptedAtRoute("GetWorkflowEndpoint", new { id }); +}); + +// GET fetches metadata for specific workflow instances +app.MapGet("/workflow/{id}", async (string id, WorkflowClient client) => { - var id = (string)context.Request.RouteValues["id"]; - var client = context.RequestServices.GetRequiredService(); WorkflowMetadata metadata = await client.GetWorkflowMetadataAsync(id, getInputsAndOutputs: true); if (metadata.Exists) { - Console.WriteLine($"Created workflow id: '{id}'"); - Console.WriteLine($"Metadata: {metadata.Details}"); return Results.Ok(metadata); } else { return Results.NotFound($"No workflow with ID = '{id}' was found."); } -} +}).WithName("GetWorkflowEndpoint"); + +app.Run("http://0.0.0.0:8080"); -app.Run("http://0.0.0.0:8080"); \ No newline at end of file diff --git a/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj b/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj index f74130672..aeedaf1e8 100644 --- a/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj +++ b/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj @@ -7,6 +7,8 @@ Exe net6.0 + enable + latest diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj index d68c89556..e64987166 100644 --- a/src/Dapr.Workflow/Dapr.Workflow.csproj +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -12,7 +12,8 @@ - + + diff --git a/src/Dapr.Workflow/WorkflowClient.cs b/src/Dapr.Workflow/WorkflowClient.cs index 7a47cfcb0..d3f998391 100644 --- a/src/Dapr.Workflow/WorkflowClient.cs +++ b/src/Dapr.Workflow/WorkflowClient.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -12,13 +12,13 @@ // ------------------------------------------------------------------------ using System; -using System.Threading; using System.Threading.Tasks; using Microsoft.DurableTask; -using Microsoft.DurableTask.Grpc; +using Microsoft.DurableTask.Client; namespace Dapr.Workflow { + // TODO: This will be replaced by the official Dapr Workflow management client. /// /// Defines client operations for managing Dapr Workflow instances. /// @@ -26,15 +26,14 @@ public sealed class WorkflowClient : IAsyncDisposable { readonly DurableTaskClient innerClient; - internal WorkflowClient(IServiceProvider? services = null) + /// + /// Initializes a new instance of the class. + /// + /// The Durable Task client used to communicate with the Dapr sidecar. + /// Thrown if is null. + public WorkflowClient(DurableTaskClient innerClient) { - DurableTaskGrpcClient.Builder builder = new(); - if (services != null) - { - builder.UseServices(services); - } - - this.innerClient = builder.Build(); + this.innerClient = innerClient ?? throw new ArgumentNullException(nameof(innerClient)); } /// @@ -46,7 +45,8 @@ public Task ScheduleNewWorkflowAsync( object? input = null, DateTime? startTime = null) { - return this.innerClient.ScheduleNewOrchestrationInstanceAsync(name, instanceId, input, startTime); + StartOrchestrationOptions options = new(instanceId, startTime); + return this.innerClient.ScheduleNewOrchestrationInstanceAsync(name, input, options); } /// @@ -69,5 +69,3 @@ public ValueTask DisposeAsync() } } } - - diff --git a/src/Dapr.Workflow/WorkflowMetadata.cs b/src/Dapr.Workflow/WorkflowMetadata.cs index e50bf8dea..603b058c6 100644 --- a/src/Dapr.Workflow/WorkflowMetadata.cs +++ b/src/Dapr.Workflow/WorkflowMetadata.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -11,7 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ -using Microsoft.DurableTask; +using Microsoft.DurableTask.Client; namespace Dapr.Workflow { @@ -41,4 +41,4 @@ internal WorkflowMetadata(OrchestrationMetadata? metadata) /// public OrchestrationMetadata? Details { get; } } -} \ No newline at end of file +} diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 7ede9131d..ff58fa253 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -14,12 +14,10 @@ namespace Dapr.Workflow { using System; - using System.Threading; - using System.Threading.Tasks; using System.Collections.Generic; + using System.Threading.Tasks; using Microsoft.DurableTask; - using Dapr.Workflow; - + /// /// Defines runtime options for workflows. /// @@ -28,17 +26,17 @@ public sealed class WorkflowRuntimeOptions /// /// Dictionary to name and register a workflow. /// - readonly Dictionary> factories = new(); + readonly Dictionary> factories = new(); /// /// Registers a workflow as a function that takes a specified input type and returns a specified output type. /// - public void RegisterWorkflow(string name, Func> implementation) + public void RegisterWorkflow(string name, Func> implementation) { // Dapr workflows are implemented as specialized Durable Task orchestrations - this.factories.Add(name, (IDurableTaskRegistry registry) => + this.factories.Add(name, (DurableTaskRegistry registry) => { - registry.AddOrchestrator(name, (innerContext, input) => + registry.AddOrchestratorFunc(name, (innerContext, input) => { WorkflowContext workflowContext = new(innerContext); return implementation(workflowContext, input); @@ -49,12 +47,12 @@ public void RegisterWorkflow(string name, Func /// Registers a workflow activity as a function that takes a specified input type and returns a specified output type. /// - public void RegisterActivity(string name, Func> implementation) + public void RegisterActivity(string name, Func> implementation) { // Dapr activities are implemented as specialized Durable Task activities - this.factories.Add(name, (IDurableTaskRegistry registry) => + this.factories.Add(name, (DurableTaskRegistry registry) => { - registry.AddActivity(name, (innerContext, input) => + registry.AddActivityFunc(name, (innerContext, input) => { ActivityContext activityContext = new(innerContext); return implementation(activityContext, input); @@ -62,13 +60,12 @@ public void RegisterActivity(string name, Func /// Method to add workflow to the registry. /// - internal void AddWorkflowsToRegistry(IDurableTaskRegistry registry) + internal void AddWorkflowsToRegistry(DurableTaskRegistry registry) { - foreach (Action factory in this.factories.Values) + foreach (Action factory in this.factories.Values) { factory.Invoke(registry); } diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index 227ba3f59..b753fbeca 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -14,12 +14,10 @@ namespace Dapr.Workflow { using System; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.DurableTask.Grpc; using Microsoft.Extensions.DependencyInjection; - using Dapr.Workflow; - + using Microsoft.DurableTask.Client; + using Microsoft.DurableTask.Worker; + /// /// Contains extension methods for using Dapr Workflow with dependency injection. /// @@ -41,24 +39,21 @@ public static IServiceCollection AddWorkflow( serviceCollection.AddSingleton(); serviceCollection.AddDaprClient(); - serviceCollection.AddSingleton(services => new WorkflowClient(services)); + serviceCollection.AddSingleton(); - serviceCollection.AddHostedService(services => + serviceCollection.AddDurableTaskClient(builder => { - DurableTaskGrpcWorker.Builder workerBuilder = DurableTaskGrpcWorker.CreateBuilder().UseServices(services); + builder.UseGrpc(); + builder.RegisterDirectly(); + }); - WorkflowRuntimeOptions options = services.GetRequiredService(); + serviceCollection.AddDurableTaskWorker(builder => + { + WorkflowRuntimeOptions options = new(); configure?.Invoke(options); - workerBuilder.UseServices(services); - - workerBuilder.AddTasks(registry => - { - options.AddWorkflowsToRegistry(registry); - }); - - DurableTaskGrpcWorker worker = workerBuilder.Build(); - return worker; + builder.UseGrpc(); + builder.AddTasks(registry => options.AddWorkflowsToRegistry(registry)); }); return serviceCollection; From 6d7f5ffde804e8b607b05de38080a5af63f6e811 Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Mon, 9 Jan 2023 18:44:12 -0800 Subject: [PATCH 13/20] Addresses some PR comments. * Rename ActivityContext to WorkflowActivityContext * Change example webapp port away from 8080x Signed-off-by: Tiago Alves Macambira --- examples/Workflow/WorkflowWebApp/Program.cs | 6 ++---- examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj | 2 +- src/Dapr.Workflow/Dapr.Workflow.csproj | 2 +- .../{ActivityContext.cs => WorkflowActivityContext.cs} | 8 ++++---- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 4 ++-- 5 files changed, 10 insertions(+), 12 deletions(-) rename src/Dapr.Workflow/{ActivityContext.cs => WorkflowActivityContext.cs} (87%) diff --git a/examples/Workflow/WorkflowWebApp/Program.cs b/examples/Workflow/WorkflowWebApp/Program.cs index 535ea4eac..0992bc575 100644 --- a/examples/Workflow/WorkflowWebApp/Program.cs +++ b/examples/Workflow/WorkflowWebApp/Program.cs @@ -9,9 +9,7 @@ // Example of registering a "Hello World" workflow function options.RegisterWorkflow("HelloWorld", implementation: async (context, input) => { - string result = ""; - result = await context.CallActivityAsync("SayHello", "World"); - return result; + return await context.CallActivityAsync("SayHello", "World"); }); // Example of registering a "Say Hello" workflow activity function @@ -47,5 +45,5 @@ } }).WithName("GetWorkflowEndpoint"); -app.Run("http://0.0.0.0:8080"); +app.Run("http://0.0.0.0:10080"); diff --git a/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj b/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj index aeedaf1e8..bf5823d1f 100644 --- a/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj +++ b/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj @@ -8,7 +8,7 @@ Exe net6.0 enable - latest + 10.0 diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj index e64987166..18d5f9bce 100644 --- a/src/Dapr.Workflow/Dapr.Workflow.csproj +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -14,7 +14,7 @@ - + \ No newline at end of file diff --git a/src/Dapr.Workflow/ActivityContext.cs b/src/Dapr.Workflow/WorkflowActivityContext.cs similarity index 87% rename from src/Dapr.Workflow/ActivityContext.cs rename to src/Dapr.Workflow/WorkflowActivityContext.cs index f348cda2e..267ba4cd9 100644 --- a/src/Dapr.Workflow/ActivityContext.cs +++ b/src/Dapr.Workflow/WorkflowActivityContext.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -21,11 +21,11 @@ namespace Dapr.Workflow /// /// Defines properties and methods for task activity context objects. /// - public class ActivityContext + public class WorkflowActivityContext { readonly TaskActivityContext innerContext; - internal ActivityContext(TaskActivityContext innerContext) + internal WorkflowActivityContext(TaskActivityContext innerContext) { this.innerContext = innerContext ?? throw new ArgumentNullException(nameof(innerContext)); } @@ -40,4 +40,4 @@ internal ActivityContext(TaskActivityContext innerContext) /// public string InstanceId => this.innerContext.InstanceId; } -} \ No newline at end of file +} diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index ff58fa253..eee55e204 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -47,14 +47,14 @@ public void RegisterWorkflow(string name, Func /// Registers a workflow activity as a function that takes a specified input type and returns a specified output type. /// - public void RegisterActivity(string name, Func> implementation) + public void RegisterActivity(string name, Func> implementation) { // Dapr activities are implemented as specialized Durable Task activities this.factories.Add(name, (DurableTaskRegistry registry) => { registry.AddActivityFunc(name, (innerContext, input) => { - ActivityContext activityContext = new(innerContext); + WorkflowActivityContext activityContext = new(innerContext); return implementation(activityContext, input); }); }); From 1bbaa0a860e886b0a70fd4aa93245f4b9afd85be Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Wed, 11 Jan 2023 18:33:34 -0800 Subject: [PATCH 14/20] Addressing review comments. * Rename workflow and activity in example to be more meangniful * Add parameter documentation to some methods * Use local project references when appropriate Signed-off-by: Tiago Alves Macambira --- examples/Workflow/WorkflowWebApp/Program.cs | 30 ++++++++++--------- .../Properties/launchSettings.json | 2 +- .../WorkflowWebApp/WorkflowWebApp.csproj | 4 +-- src/Dapr.Workflow/Dapr.Workflow.csproj | 10 +++++-- src/Dapr.Workflow/WorkflowClient.cs | 16 ++++++++++ src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 4 +++ .../WorkflowServiceCollectionExtensions.cs | 21 +++++++++++-- 7 files changed, 64 insertions(+), 23 deletions(-) diff --git a/examples/Workflow/WorkflowWebApp/Program.cs b/examples/Workflow/WorkflowWebApp/Program.cs index 0992bc575..6bab25874 100644 --- a/examples/Workflow/WorkflowWebApp/Program.cs +++ b/examples/Workflow/WorkflowWebApp/Program.cs @@ -4,35 +4,37 @@ WebApplicationBuilder builder = WebApplication.CreateBuilder(args); // Dapr workflows are registered as part of the service configuration -builder.Services.AddWorkflow(options => +builder.Services.AddDaprWorkflow(options => { - // Example of registering a "Hello World" workflow function - options.RegisterWorkflow("HelloWorld", implementation: async (context, input) => + // Example of registering a "PlaceOrder" workflow function + options.RegisterWorkflow("PlaceOrder", implementation: async (context, input) => { - return await context.CallActivityAsync("SayHello", "World"); + // In real life there are other steps related to placing an order, like reserving + // inventory and charging the customer credit card etc. But let's keep it simple ;) + return await context.CallActivityAsync("ShipProduct", "Coffee Beans"); }); - // Example of registering a "Say Hello" workflow activity function - options.RegisterActivity("SayHello", implementation: (context, input) => + // Example of registering a "ShipProduct" workflow activity function + options.RegisterActivity("ShipProduct", implementation: (context, input) => { - return Task.FromResult($"Hello, {input}!"); + return Task.FromResult($"We are shipping {input} to the customer using our hoard of drones!"); }); }); WebApplication app = builder.Build(); // POST starts new workflow instances -app.MapPost("/workflow", async (HttpContext context, WorkflowClient client) => +app.MapPost("/order", async (HttpContext context, WorkflowClient client) => { string id = Guid.NewGuid().ToString()[..8]; - await client.ScheduleNewWorkflowAsync("HelloWorld", id); + await client.ScheduleNewWorkflowAsync("PlaceOrder", id); // return an HTTP 202 and a Location header to be used for status query - return Results.AcceptedAtRoute("GetWorkflowEndpoint", new { id }); + return Results.AcceptedAtRoute("GetOrderEndpoint", new { id }); }); -// GET fetches metadata for specific workflow instances -app.MapGet("/workflow/{id}", async (string id, WorkflowClient client) => +// GET fetches metadata for specific order workflow instances +app.MapGet("/order/{id}", async (string id, WorkflowClient client) => { WorkflowMetadata metadata = await client.GetWorkflowMetadataAsync(id, getInputsAndOutputs: true); if (metadata.Exists) @@ -41,9 +43,9 @@ } else { - return Results.NotFound($"No workflow with ID = '{id}' was found."); + return Results.NotFound($"No workflow created for order with ID = '{id}' was found."); } -}).WithName("GetWorkflowEndpoint"); +}).WithName("GetOrderEndpoint"); app.Run("http://0.0.0.0:10080"); diff --git a/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json b/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json index 15fed009c..f889772eb 100644 --- a/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json +++ b/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json @@ -6,7 +6,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "https://localhost:57899;http://localhost:57900" + "applicationUrl": "https://localhost:10080;http://localhost:10080" } } } \ No newline at end of file diff --git a/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj b/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj index bf5823d1f..28e9dadb9 100644 --- a/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj +++ b/examples/Workflow/WorkflowWebApp/WorkflowWebApp.csproj @@ -6,9 +6,9 @@ Exe - net6.0 + net6 enable - 10.0 + latest diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj index 18d5f9bce..28e4e2513 100644 --- a/src/Dapr.Workflow/Dapr.Workflow.csproj +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -2,7 +2,7 @@ - net6.0 + netcoreapp3.1;net5;net6 enable Dapr.Workflow Dapr Workflow Authoring SDK @@ -10,11 +10,15 @@ 0.1.0 alpha - + - + + + + + \ No newline at end of file diff --git a/src/Dapr.Workflow/WorkflowClient.cs b/src/Dapr.Workflow/WorkflowClient.cs index d3f998391..597768fa4 100644 --- a/src/Dapr.Workflow/WorkflowClient.cs +++ b/src/Dapr.Workflow/WorkflowClient.cs @@ -39,6 +39,17 @@ public WorkflowClient(DurableTaskClient innerClient) /// /// Schedules a new workflow instance for execution. /// + /// The name of the orchestrator to schedule. + /// + /// The unique ID of the orchestration instance to schedule. If not specified, a new GUID value is used. + /// + /// + /// The time when the orchestration instance should start executing. If not specified or if a date-time in the past + /// is specified, the orchestration instance will be scheduled immediately. + /// + /// + /// The optional input to pass to the scheduled orchestration instance. This must be a serializable value. + /// public Task ScheduleNewWorkflowAsync( string name, string? instanceId = null, @@ -52,6 +63,11 @@ public Task ScheduleNewWorkflowAsync( /// /// Fetches runtime metadata for the specified workflow instance. /// + /// The unique ID of the orchestration instance to fetch. + /// + /// Specify true to fetch the orchestration instance's inputs, outputs, and custom status, or false to + /// omit them. Defaults to false. + /// public async Task GetWorkflowMetadataAsync(string instanceId, bool getInputsAndOutputs = false) { OrchestrationMetadata? metadata = await this.innerClient.GetInstanceMetadataAsync( diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index eee55e204..d37b2a2c9 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -31,6 +31,8 @@ public sealed class WorkflowRuntimeOptions /// /// Registers a workflow as a function that takes a specified input type and returns a specified output type. /// + /// Workflow name + /// Function implementing the workflow definition public void RegisterWorkflow(string name, Func> implementation) { // Dapr workflows are implemented as specialized Durable Task orchestrations @@ -47,6 +49,8 @@ public void RegisterWorkflow(string name, Func /// Registers a workflow activity as a function that takes a specified input type and returns a specified output type. /// + /// Activity name + /// Activity implemetation public void RegisterActivity(string name, Func> implementation) { // Dapr activities are implemented as specialized Durable Task activities diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index b753fbeca..adbae0a2e 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -14,21 +14,36 @@ namespace Dapr.Workflow { using System; + using System.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.DurableTask.Client; using Microsoft.DurableTask.Worker; + using System.Collections.Generic; /// /// Contains extension methods for using Dapr Workflow with dependency injection. /// public static class WorkflowServiceCollectionExtensions { + static bool None(this IEnumerable source, Func predicate) => !source.Any(predicate); + + static IServiceCollection AddSingletonIfNotPresent(this IServiceCollection services) + where TService : class + { + if (services.None(s => s.ImplementationType == typeof(TService))) + { + return services.AddSingleton(typeof(TService)); + } + + return services; + } + /// /// Adds Dapr Workflow support to the service collection. /// /// The . /// A delegate used to configure actor options and register workflow functions. - public static IServiceCollection AddWorkflow( + public static IServiceCollection AddDaprWorkflow( this IServiceCollection serviceCollection, Action configure) { @@ -37,9 +52,9 @@ public static IServiceCollection AddWorkflow( throw new ArgumentNullException(nameof(serviceCollection)); } - serviceCollection.AddSingleton(); + serviceCollection.AddSingletonIfNotPresent(); + serviceCollection.AddSingletonIfNotPresent(); serviceCollection.AddDaprClient(); - serviceCollection.AddSingleton(); serviceCollection.AddDurableTaskClient(builder => { From 601f09e14e31e06bb310bdfced704cf49c692497 Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Thu, 12 Jan 2023 08:29:27 -0800 Subject: [PATCH 15/20] Adding more documentation Signed-off-by: Tiago Alves Macambira --- src/Dapr.Workflow/WorkflowContext.cs | 7 ++++-- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 1 + .../WorkflowServiceCollectionExtensions.cs | 24 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Dapr.Workflow/WorkflowContext.cs b/src/Dapr.Workflow/WorkflowContext.cs index 4cf1265a9..ede013c0b 100644 --- a/src/Dapr.Workflow/WorkflowContext.cs +++ b/src/Dapr.Workflow/WorkflowContext.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // 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. @@ -54,6 +54,7 @@ internal WorkflowContext(TaskOrchestrationContext innerContext) /// /// Creates a durable timer that expires after the specified delay. /// + /// public Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = default) { return this.innerContext.CreateTimer(delay, cancellationToken); @@ -62,6 +63,7 @@ public Task CreateTimer(TimeSpan delay, CancellationToken cancellationToken = de /// /// Waits for an event to be raised with name and returns the event data. /// + /// public Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) { return this.innerContext.WaitForExternalEvent(eventName, timeout); @@ -70,9 +72,10 @@ public Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) /// /// Asynchronously invokes an activity by name and with the specified input value. /// + /// public Task CallActivityAsync(TaskName name, object? input = null, TaskOptions? options = null) { return this.innerContext.CallActivityAsync(name, input, options); } } -} \ No newline at end of file +} diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index d37b2a2c9..d377408fc 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -67,6 +67,7 @@ public void RegisterActivity(string name, Func /// Method to add workflow to the registry. /// + /// The registry we will add workfows to internal void AddWorkflowsToRegistry(DurableTaskRegistry registry) { foreach (Action factory in this.factories.Values) diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index adbae0a2e..85e5110b0 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -25,11 +25,35 @@ namespace Dapr.Workflow /// public static class WorkflowServiceCollectionExtensions { + /// + /// Determines whether all element of a sequence fail to satisfy a condition -- or if none satisfies a condition. + /// + /// The type of the elements of . + /// + /// An System.Collections.Generic.IEnumerable`1 whose elements to apply the predicate to. + /// + /// A function to test each element for a condition. + /// + /// true if all elements in the source sequence fail the test in the specified predicate; otherwise, true + /// static bool None(this IEnumerable source, Func predicate) => !source.Any(predicate); + /// + /// Adds a singleton service of the type specified in to the + /// specified if and only if it isn't already present. + /// + /// The type of the service to register and the implementation to use. + /// The to add the service to. + /// A reference to this instance after the operation has completed. + /// static IServiceCollection AddSingletonIfNotPresent(this IServiceCollection services) where TService : class { + if (services is null) + { + throw new ArgumentNullException(nameof(services)); + } + if (services.None(s => s.ImplementationType == typeof(TService))) { return services.AddSingleton(typeof(TService)); From b9c99cfa4a618577ab7ec510f5b536b31a70308c Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Thu, 12 Jan 2023 10:46:11 -0800 Subject: [PATCH 16/20] Replaces custom AddSingletonIfNotPresent with std. TryAddSingleton Signed-off-by: Tiago Alves Macambira --- .../WorkflowServiceCollectionExtensions.cs | 42 ++----------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index 85e5110b0..caf6f3cd0 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -16,6 +16,7 @@ namespace Dapr.Workflow using System; using System.Linq; using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.DurableTask.Client; using Microsoft.DurableTask.Worker; using System.Collections.Generic; @@ -25,43 +26,6 @@ namespace Dapr.Workflow /// public static class WorkflowServiceCollectionExtensions { - /// - /// Determines whether all element of a sequence fail to satisfy a condition -- or if none satisfies a condition. - /// - /// The type of the elements of . - /// - /// An System.Collections.Generic.IEnumerable`1 whose elements to apply the predicate to. - /// - /// A function to test each element for a condition. - /// - /// true if all elements in the source sequence fail the test in the specified predicate; otherwise, true - /// - static bool None(this IEnumerable source, Func predicate) => !source.Any(predicate); - - /// - /// Adds a singleton service of the type specified in to the - /// specified if and only if it isn't already present. - /// - /// The type of the service to register and the implementation to use. - /// The to add the service to. - /// A reference to this instance after the operation has completed. - /// - static IServiceCollection AddSingletonIfNotPresent(this IServiceCollection services) - where TService : class - { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } - - if (services.None(s => s.ImplementationType == typeof(TService))) - { - return services.AddSingleton(typeof(TService)); - } - - return services; - } - /// /// Adds Dapr Workflow support to the service collection. /// @@ -76,8 +40,8 @@ public static IServiceCollection AddDaprWorkflow( throw new ArgumentNullException(nameof(serviceCollection)); } - serviceCollection.AddSingletonIfNotPresent(); - serviceCollection.AddSingletonIfNotPresent(); + serviceCollection.TryAddSingleton(); + serviceCollection.TryAddSingleton(); serviceCollection.AddDaprClient(); serviceCollection.AddDurableTaskClient(builder => From f49b6485bac888b30a1ce3626ab18579734033c5 Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Thu, 12 Jan 2023 10:53:27 -0800 Subject: [PATCH 17/20] Renames AddWorkflowsToRegistry to AddActivitiesToRegistry to better match what it does Signed-off-by: Tiago Alves Macambira --- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 2 +- src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index d377408fc..1b6c24112 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -68,7 +68,7 @@ public void RegisterActivity(string name, Func /// The registry we will add workfows to - internal void AddWorkflowsToRegistry(DurableTaskRegistry registry) + internal void AddActivitiesToRegistry(DurableTaskRegistry registry) { foreach (Action factory in this.factories.Values) { diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index caf6f3cd0..277229e78 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -56,7 +56,7 @@ public static IServiceCollection AddDaprWorkflow( configure?.Invoke(options); builder.UseGrpc(); - builder.AddTasks(registry => options.AddWorkflowsToRegistry(registry)); + builder.AddTasks(registry => options.AddActivitiesToRegistry(registry)); }); return serviceCollection; From ad7ade292b2a495500da2f151b0f448e3c31fa53 Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Thu, 12 Jan 2023 11:04:04 -0800 Subject: [PATCH 18/20] Add cancelation token overload for WorkflowContext.WaitForExternalEventAsync Signed-off-by: Tiago Alves Macambira --- src/Dapr.Workflow/WorkflowContext.cs | 9 +++++++++ src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Dapr.Workflow/WorkflowContext.cs b/src/Dapr.Workflow/WorkflowContext.cs index ede013c0b..9a9bd8a7e 100644 --- a/src/Dapr.Workflow/WorkflowContext.cs +++ b/src/Dapr.Workflow/WorkflowContext.cs @@ -69,6 +69,15 @@ public Task WaitForExternalEventAsync(string eventName, TimeSpan timeout) return this.innerContext.WaitForExternalEvent(eventName, timeout); } + /// + /// Waits for an event to be raised with name and returns the event data. + /// + /// + public Task WaitForExternalEventAsync(string eventName, CancellationToken cancellationToken = default) + { + return this.innerContext.WaitForExternalEvent(eventName, cancellationToken); + } + /// /// Asynchronously invokes an activity by name and with the specified input value. /// diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 1b6c24112..f27ccdfcc 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -65,9 +65,9 @@ public void RegisterActivity(string name, Func - /// Method to add workflow to the registry. + /// Method to add activities to the registry. /// - /// The registry we will add workfows to + /// The registry we will add activities to internal void AddActivitiesToRegistry(DurableTaskRegistry registry) { foreach (Action factory in this.factories.Values) From 509656b35a9282d4740a781c1800005b96b36262 Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Thu, 12 Jan 2023 15:33:44 -0800 Subject: [PATCH 19/20] Renaming AddWorkflowsAndActivitiesToRegistry Signed-off-by: Tiago Alves Macambira --- src/Dapr.Workflow/WorkflowRuntimeOptions.cs | 8 ++++---- src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index f27ccdfcc..0bc602311 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -65,14 +65,14 @@ public void RegisterActivity(string name, Func - /// Method to add activities to the registry. + /// Method to add workflows and activities to the registry. /// - /// The registry we will add activities to - internal void AddActivitiesToRegistry(DurableTaskRegistry registry) + /// The registry we will add workflows and activities to + internal void AddWorkflowsAndActivitiesToRegistry(DurableTaskRegistry registry) { foreach (Action factory in this.factories.Values) { - factory.Invoke(registry); + factory.Invoke(registry); // This adds workflows to the registry indirectly. } } } diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index 277229e78..eaee2c670 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -56,7 +56,7 @@ public static IServiceCollection AddDaprWorkflow( configure?.Invoke(options); builder.UseGrpc(); - builder.AddTasks(registry => options.AddActivitiesToRegistry(registry)); + builder.AddTasks(registry => options.AddWorkflowsAndActivitiesToRegistry(registry)); }); return serviceCollection; From fb8589cf6100f7481532685e77de911e3ca71954 Mon Sep 17 00:00:00 2001 From: Tiago Alves Macambira Date: Thu, 12 Jan 2023 15:55:09 -0800 Subject: [PATCH 20/20] Defer launch URL and port to launchSettings.json Signed-off-by: Tiago Alves Macambira --- examples/Workflow/WorkflowWebApp/Program.cs | 2 +- examples/Workflow/WorkflowWebApp/Properties/launchSettings.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Workflow/WorkflowWebApp/Program.cs b/examples/Workflow/WorkflowWebApp/Program.cs index 6bab25874..838141baf 100644 --- a/examples/Workflow/WorkflowWebApp/Program.cs +++ b/examples/Workflow/WorkflowWebApp/Program.cs @@ -47,5 +47,5 @@ } }).WithName("GetOrderEndpoint"); -app.Run("http://0.0.0.0:10080"); +app.Run(); diff --git a/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json b/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json index f889772eb..8254ce0fe 100644 --- a/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json +++ b/examples/Workflow/WorkflowWebApp/Properties/launchSettings.json @@ -6,7 +6,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "https://localhost:10080;http://localhost:10080" + "applicationUrl": "https://localhost:10080" } } } \ No newline at end of file