Skip to content

Commit

Permalink
Implement pluggable storage for activity input and output properties
Browse files Browse the repository at this point in the history
  • Loading branch information
sfmskywalker committed Jun 14, 2021
1 parent 6a49f7a commit 332741d
Show file tree
Hide file tree
Showing 65 changed files with 1,922 additions and 331 deletions.
7 changes: 0 additions & 7 deletions Elsa.sln
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.FileBasedWorkf
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.ForLoopConsole", "src\samples\console\Elsa.Samples.ForLoopConsole\Elsa.Samples.ForLoopConsole.csproj", "{F5124847-D046-4458-A89D-CD796FEDEE1A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.GoBackConsole", "src\samples\console\Elsa.Samples.GoBackConsole\Elsa.Samples.GoBackConsole.csproj", "{0B147307-64FF-4496-9DC9-8F83013AC045}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.HelloWorldConsole", "src\samples\console\Elsa.Samples.HelloWorldConsole\Elsa.Samples.HelloWorldConsole.csproj", "{935888CF-E871-42BB-A47D-5E1168269A03}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.HappinessConsole", "src\samples\console\Elsa.Samples.HappinessConsole\Elsa.Samples.HappinessConsole.csproj", "{EA1D7E39-D77D-4B13-BAD0-76BA30547019}"
Expand Down Expand Up @@ -419,10 +417,6 @@ Global
{F5124847-D046-4458-A89D-CD796FEDEE1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5124847-D046-4458-A89D-CD796FEDEE1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5124847-D046-4458-A89D-CD796FEDEE1A}.Release|Any CPU.Build.0 = Release|Any CPU
{0B147307-64FF-4496-9DC9-8F83013AC045}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B147307-64FF-4496-9DC9-8F83013AC045}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B147307-64FF-4496-9DC9-8F83013AC045}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B147307-64FF-4496-9DC9-8F83013AC045}.Release|Any CPU.Build.0 = Release|Any CPU
{935888CF-E871-42BB-A47D-5E1168269A03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{935888CF-E871-42BB-A47D-5E1168269A03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{935888CF-E871-42BB-A47D-5E1168269A03}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -727,7 +721,6 @@ Global
{7EC9B4E1-820E-4D53-B4E2-9815E31C7AE8} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
{C6A54F89-271D-402E-A38D-6C3A9DB7DF9C} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
{F5124847-D046-4458-A89D-CD796FEDEE1A} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
{0B147307-64FF-4496-9DC9-8F83013AC045} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
{935888CF-E871-42BB-A47D-5E1168269A03} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
{EA1D7E39-D77D-4B13-BAD0-76BA30547019} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
{0FC36CEC-35B3-4EB7-9DD7-0F2A1C4E9313} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public Task Handle(EvaluatingLiquidExpression notification, CancellationToken ca
options.Scope.SetValue("Request", new ObjectValue(new LiquidRequestAccessor()));

options.MemberAccessStrategy.Register<HttpResponseModel>();
options.MemberAccessStrategy.Register<HttpRequestModel>();
options.MemberAccessStrategy.Register<LiquidRequestAccessor, FluidValue>((_, name, _) =>
{
var request = _httpContextAccessor.HttpContext?.Request;
Expand Down
16 changes: 9 additions & 7 deletions src/core/Elsa.Abstractions/ActivityResults/OutputResult.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
using System;
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Providers.WorkflowStorage;
using Elsa.Services.Models;
using Elsa.Services.WorkflowStorage;

namespace Elsa.ActivityResults
{
public class OutputResult : ActivityExecutionResult
{
public OutputResult(object? output, string? storageName = default)
public OutputResult(object? output, string? storageProviderName = default)
{
Output = output;
StorageName = storageName;
StorageProviderName = storageProviderName;
}

public object? Output { get; }
public string? StorageName { get; }
public string? StorageProviderName { get; }

public override async ValueTask ExecuteAsync(ActivityExecutionContext activityExecutionContext, CancellationToken cancellationToken)
{
var workflowStorageService = activityExecutionContext.GetService<IWorkflowStorageService>();
var provider = workflowStorageService.GetProviderByNameOrDefault(StorageName);
await provider.SaveAsync(activityExecutionContext, ActivityOutput.PropertyName, Output, cancellationToken);
var workflowStorageContext = new WorkflowStorageContext(activityExecutionContext.WorkflowInstance, activityExecutionContext.ActivityId);
await workflowStorageService.SaveAsync(StorageProviderName, workflowStorageContext, ActivityOutput.PropertyName, Output, cancellationToken);
activityExecutionContext.WorkflowInstance.Output = new WorkflowOutputReference(StorageProviderName, workflowStorageContext.ActivityId);
}
}
}
2 changes: 2 additions & 0 deletions src/core/Elsa.Abstractions/Builders/IActivityBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public interface IActivityBuilder : IBuilder
public string? DisplayName { get; set; }
public string? Description { get; set; }
IDictionary<string, IActivityPropertyValueProvider>? PropertyValueProviders { get; }
IDictionary<string,string> PropertyStorageProviders { get; set; }
string? OutputStorageProviderName { get; set; }
bool PersistWorkflowEnabled { get; set; }
bool LoadWorkflowContextEnabled { get; set; }
bool SaveWorkflowContextEnabled { get; set; }
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Services.Models;

namespace Elsa
Expand All @@ -14,7 +16,7 @@ public static class WorkflowExecutionContextExtensions

public static IEnumerable<string> GetInboundActivityPath(this WorkflowExecutionContext workflowExecutionContext, string activityId) => workflowExecutionContext.GetInboundActivityPathInternal(activityId, activityId).Distinct();
public static IEnumerable<string> GetOutboundActivityPath(this WorkflowExecutionContext workflowExecutionContext, string activityId) => workflowExecutionContext.GetOutboundActivityPathInternal(activityId, activityId).Distinct();
public static T GetOutputFrom<T>(this WorkflowExecutionContext workflowExecutionContext, string activityName) => (T)workflowExecutionContext.GetOutputFrom(activityName)!;
public static async ValueTask<T?> GetOutputFromAsync<T>(this WorkflowExecutionContext workflowExecutionContext, string activityName, CancellationToken cancellationToken = default) => await workflowExecutionContext.GetOutputFromAsync<T>(activityName, cancellationToken);

private static IEnumerable<string> GetInboundActivityPathInternal(this WorkflowExecutionContext workflowExecutionContext, string activityId, string startingPointActivityId)
{
Expand Down
2 changes: 2 additions & 0 deletions src/core/Elsa.Abstractions/Models/WorkflowDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public WorkflowDefinition()
/// Allows for applications to store an application-specific, queryable value to associate with the workflow.
/// </summary>
public string? Tag { get; set; }

public string? OutputStorageProviderName { get; set; }

public ICollection<ActivityDefinition> Activities { get; set; }
public ICollection<ConnectionDefinition> Connections { get; set; }
Expand Down
4 changes: 3 additions & 1 deletion src/core/Elsa.Abstractions/Models/WorkflowInstance.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using Elsa.Comparers;
using Elsa.Services.Models;
using Newtonsoft.Json.Linq;
using NodaTime;

Expand Down Expand Up @@ -30,7 +31,7 @@ public WorkflowInstance()
public Instant? CancelledAt { get; set; }
public Instant? FaultedAt { get; set; }
public Variables Variables { get; set; }
public object? Output { get; set; }
public WorkflowOutputReference? Output { get; set; }
public IDictionary<string, IDictionary<string, object>> ActivityData { get; set; } = new Dictionary<string, IDictionary<string, object>>();
public IDictionary<string, object> ActivityOutput { get; set; } = new Dictionary<string, object>();

Expand All @@ -44,5 +45,6 @@ public HashSet<BlockingActivity> BlockingActivities
public SimpleStack<ScheduledActivity> ScheduledActivities { get; set; }
public SimpleStack<ActivityScope> Scopes { get; set; }
public ScheduledActivity? CurrentActivity { get; set; }
public string? LastExecutedActivityId { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Services.Models;

namespace Elsa.Providers.WorkflowStorage
{
Expand All @@ -11,9 +10,9 @@ public interface IWorkflowStorageProvider
{
string Name { get; }
string DisplayName { get; }
ValueTask SaveAsync(ActivityExecutionContext context, string propertyName, object? value, CancellationToken cancellationToken = default);
ValueTask<object?> LoadAsync(ActivityExecutionContext context, string propertyName, CancellationToken cancellationToken = default);
ValueTask DeleteAsync(ActivityExecutionContext context, string propertyName, CancellationToken cancellationToken = default);
ValueTask DeleteAsync(ActivityExecutionContext context, CancellationToken cancellationToken = default);
ValueTask SaveAsync(WorkflowStorageContext context, string key, object? value, CancellationToken cancellationToken = default);
ValueTask<object?> LoadAsync(WorkflowStorageContext context, string key, CancellationToken cancellationToken = default);
ValueTask DeleteAsync(WorkflowStorageContext context, string key, CancellationToken cancellationToken = default);
ValueTask DeleteAsync(WorkflowStorageContext context, CancellationToken cancellationToken = default);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Elsa.Models;

namespace Elsa.Providers.WorkflowStorage
{
public record WorkflowStorageContext(WorkflowInstance WorkflowInstance, string ActivityId);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Services.Models;

namespace Elsa.Providers.WorkflowStorage
{
public abstract class WorkflowStorageProvider : IWorkflowStorageProvider
{
public string Name => GetType().Name.Replace("WorkflowStorageProvider", "");
public string DisplayName => Name;
public abstract ValueTask SaveAsync(ActivityExecutionContext context, string propertyName, object? value, CancellationToken cancellationToken = default);
public abstract ValueTask<object?> LoadAsync(ActivityExecutionContext context, string propertyName, CancellationToken cancellationToken = default);
public abstract ValueTask DeleteAsync(ActivityExecutionContext context, string propertyName, CancellationToken cancellationToken = default);
public abstract ValueTask DeleteAsync(ActivityExecutionContext context, CancellationToken cancellationToken = default);
public abstract ValueTask SaveAsync(WorkflowStorageContext context, string key, object? value, CancellationToken cancellationToken = default);
public abstract ValueTask<object?> LoadAsync(WorkflowStorageContext context, string key, CancellationToken cancellationToken = default);
public abstract ValueTask DeleteAsync(WorkflowStorageContext context, string key, CancellationToken cancellationToken = default);
public abstract ValueTask DeleteAsync(WorkflowStorageContext context, CancellationToken cancellationToken = default);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public ActivityBlueprint()
bool loadWorkflowContext,
bool saveWorkflowContext,
IDictionary<string, string> propertyStorageProviders,
string? outputStorageProviderName,
string? source)
{
Id = id;
Expand All @@ -31,6 +32,7 @@ public ActivityBlueprint()
LoadWorkflowContext = loadWorkflowContext;
SaveWorkflowContext = saveWorkflowContext;
PropertyStorageProviders = propertyStorageProviders;
OutputStorageProviderName = outputStorageProviderName;
Source = source;
}

Expand All @@ -44,6 +46,7 @@ public ActivityBlueprint()
public bool LoadWorkflowContext { get; set; }
public bool SaveWorkflowContext { get; set; }
public IDictionary<string, string> PropertyStorageProviders { get; } = new Dictionary<string, string>();
public string? OutputStorageProviderName { get; }
public string? Source { get; set; }


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Threading.Tasks;
using Elsa.Models;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;

namespace Elsa.Services.Models
{
Expand Down Expand Up @@ -54,7 +53,7 @@ public class ActivityExecutionContext

public CancellationToken CancellationToken { get; }

public IDictionary<string, object> GetData() => WorkflowInstance.ActivityData.GetItem(ActivityBlueprint.Id, () => new Dictionary<string, object>());
public IDictionary<string, object?> GetData() => WorkflowInstance.ActivityData.GetItem(ActivityBlueprint.Id, () => new Dictionary<string, object?>());

public void SetState(string propertyName, object? value)
{
Expand All @@ -68,10 +67,10 @@ public void SetState(string propertyName, object? value)
return data.GetState<T>(propertyName);
}

public T GetState<T>(string propertyName, Func<T> defaultValue)
public T? GetState<T>(string propertyName, Func<T> defaultValue)
{
var data = GetData();
return data.GetState<T>(propertyName, defaultValue);
return data.GetState(propertyName, defaultValue);
}

public object? GetState(string propertyName, Type targetType)
Expand Down Expand Up @@ -166,12 +165,12 @@ public async ValueTask<RuntimeActivityInstance> ActivateActivityAsync(Cancellati
public T? GetInput<T>(Func<T?> defaultValue) => Input != null ? Input.ConvertTo<T>() : defaultValue();
public T? GetInput<T>(T? defaultValue) => Input != null ? Input.ConvertTo<T>() : defaultValue;

public object? GetOutputFrom(string activityName) => WorkflowExecutionContext.GetOutputFrom(activityName);
public object? GetOutputFrom(string activityName, object? defaultValue) => WorkflowExecutionContext.GetOutputFrom(activityName) ?? defaultValue;
public object? GetOutputFrom(string activityName, Func<object?> defaultValue) => WorkflowExecutionContext.GetOutputFrom(activityName) ?? defaultValue();
public T? GetOutputFrom<T>(string activityName) => (T?) GetOutputFrom(activityName)!;
public T? GetOutputFrom<T>(string activityName, Func<T?> defaultValue) => (T?) GetOutputFrom(activityName, defaultValue())!;
public T? GetOutputFrom<T>(string activityName, T? defaultValue) => (T?) GetOutputFrom(activityName, () => defaultValue)!;
public ValueTask<object?> GetOutputFromAsync(string activityName, CancellationToken cancellationToken = default) => WorkflowExecutionContext.GetOutputFromAsync(activityName, cancellationToken);
public async ValueTask<object?> GetOutputFromAsync(string activityName, object? defaultValue, CancellationToken cancellationToken = default) => await WorkflowExecutionContext.GetOutputFromAsync(activityName, cancellationToken) ?? defaultValue;
public async ValueTask<object?> GetOutputFromAsync(string activityName, Func<object?> defaultValue, CancellationToken cancellationToken = default) => await WorkflowExecutionContext.GetOutputFromAsync(activityName, cancellationToken) ?? defaultValue();
public async ValueTask<T?> GetOutputFromAsync<T>(string activityName, CancellationToken cancellationToken = default) => await WorkflowExecutionContext.GetOutputFromAsync<T>(activityName, cancellationToken);
public async ValueTask<T?> GetOutputFromAsync<T>(string activityName, Func<T?> defaultValue, CancellationToken cancellationToken = default) => await GetOutputFromAsync<T>(activityName, cancellationToken) ?? defaultValue();
public async ValueTask<T?> GetOutputFromAsync<T>(string activityName, T? defaultValue) => await GetOutputFromAsync(activityName, () => defaultValue, CancellationToken);
public void SetWorkflowContext(object? value) => WorkflowExecutionContext.SetWorkflowContext(value);
public object? GetWorkflowContext() => WorkflowExecutionContext.GetWorkflowContext();
public T GetWorkflowContext<T>() => WorkflowExecutionContext.GetWorkflowContext<T>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public CompositeActivityBlueprint()
bool loadWorkflowContext,
bool saveWorkflowContext,
IDictionary<string, string> propertyStorageProviders,
string? source) : base(id, parent, name, displayName, description, type, persistWorkflow, loadWorkflowContext, saveWorkflowContext, propertyStorageProviders, source)
string? outputStorageProviderName,
string? source) : base(id, parent, name, displayName, description, type, persistWorkflow, loadWorkflowContext, saveWorkflowContext, propertyStorageProviders, outputStorageProviderName, source)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ public interface IActivityBlueprint
bool SaveWorkflowContext { get; set; }
string? Source { get; set; }
IDictionary<string, string> PropertyStorageProviders { get; }
string? OutputStorageProviderName { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using NodaTime;

namespace Elsa.Services.Models
{
Expand Down
15 changes: 14 additions & 1 deletion src/core/Elsa.Abstractions/Services/Models/WorkflowBlueprint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,20 @@ public WorkflowBlueprint()
bool deleteCompletedInstances,
IEnumerable<IActivityBlueprint> activities,
IEnumerable<IConnection> connections,
IActivityPropertyProviders activityPropertyValueProviders) : base(id, default, name, displayName, description, id, true, false, false, new Dictionary<string, string>(), default)
string? outputStorageProviderName,
IActivityPropertyProviders activityPropertyValueProviders) : base(
id,
default,
name,
displayName,
description,
id,
true,
false,
false,
new Dictionary<string, string>(),
outputStorageProviderName,
default)
{
Id = id;
Parent = this;
Expand Down

0 comments on commit 332741d

Please sign in to comment.