Merge branch 'v3.0.4'
This commit is contained in:
commit
d0a72deadf
|
|
@ -10,7 +10,6 @@ using Elsa.EntityFrameworkCore.Modules.Identity;
|
|||
using Elsa.EntityFrameworkCore.Modules.Management;
|
||||
using Elsa.EntityFrameworkCore.Modules.Runtime;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Http.Handlers;
|
||||
using Elsa.Http.Options;
|
||||
using Elsa.MongoDb.Extensions;
|
||||
using Elsa.MongoDb.Modules.Identity;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
"Sqlite": "Data Source=App_Data/elsa.sqlite.db;Cache=Shared;",
|
||||
"MongoDb": "mongodb://localhost:27017/elsa-workflows",
|
||||
"AzureServiceBus": "",
|
||||
"RabbitMq": "amqp://guest:guest@localhost:5672/elsa"
|
||||
"RabbitMq": "amqp://guest:guest@localhost:5672"
|
||||
},
|
||||
"Smtp": {
|
||||
"Host": "localhost",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<ProjectReference Include="..\..\modules\Elsa.Identity\Elsa.Identity.csproj"/>
|
||||
<ProjectReference Include="..\..\modules\Elsa.Liquid\Elsa.Liquid.csproj"/>
|
||||
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore\Elsa.EntityFrameworkCore.csproj"/>
|
||||
<ProjectReference Include="..\..\modules\Elsa.MassTransit\Elsa.MassTransit.csproj" />
|
||||
<ProjectReference Include="..\..\modules\Elsa.Python\Elsa.Python.csproj" />
|
||||
<ProjectReference Include="..\..\modules\Elsa.Quartz\Elsa.Quartz.csproj"/>
|
||||
<ProjectReference Include="..\..\modules\Elsa.Webhooks\Elsa.Webhooks.csproj"/>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ using Elsa.Extensions;
|
|||
using Elsa.Webhooks.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
const bool useMassTransit = true;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.WebHost.UseStaticWebAssets();
|
||||
var services = builder.Services;
|
||||
|
|
@ -16,32 +18,39 @@ var identityTokenSection = identitySection.GetSection("Tokens");
|
|||
|
||||
// Add Elsa services.
|
||||
services
|
||||
.AddElsa(elsa => elsa
|
||||
.UseSasTokens()
|
||||
.UseIdentity(identity =>
|
||||
.AddElsa(elsa =>
|
||||
{
|
||||
elsa
|
||||
.UseSasTokens()
|
||||
.UseIdentity(identity =>
|
||||
{
|
||||
identity.IdentityOptions = options => identitySection.Bind(options);
|
||||
identity.TokenOptions = options => identityTokenSection.Bind(options);
|
||||
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
|
||||
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
|
||||
identity.UseConfigurationBasedRoleProvider(options => identitySection.Bind(options));
|
||||
})
|
||||
.UseDefaultAuthentication()
|
||||
.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
.UseScheduling()
|
||||
.UseJavaScript(options => options.AllowClrAccess = true)
|
||||
.UseLiquid()
|
||||
.UseCSharp()
|
||||
.UsePython()
|
||||
.UseHttp(http => http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options))
|
||||
.UseEmail(email => email.ConfigureOptions = options => configuration.GetSection("Smtp").Bind(options))
|
||||
.UseWebhooks(webhooks => webhooks.WebhookOptions = options => builder.Configuration.GetSection("Webhooks").Bind(options))
|
||||
.UseWorkflowsApi()
|
||||
.UseRealTimeWorkflows()
|
||||
.AddActivitiesFrom<Program>()
|
||||
.AddWorkflowsFrom<Program>();
|
||||
|
||||
if (useMassTransit)
|
||||
{
|
||||
identity.IdentityOptions = options => identitySection.Bind(options);
|
||||
identity.TokenOptions = options => identityTokenSection.Bind(options);
|
||||
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
|
||||
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
|
||||
identity.UseConfigurationBasedRoleProvider(options => identitySection.Bind(options));
|
||||
})
|
||||
.UseDefaultAuthentication()
|
||||
.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
.UseScheduling()
|
||||
.UseJavaScript(options => options.AllowClrAccess = true)
|
||||
.UseLiquid()
|
||||
.UseCSharp()
|
||||
.UsePython()
|
||||
.UseHttp(http => http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options))
|
||||
.UseEmail(email => email.ConfigureOptions = options => configuration.GetSection("Smtp").Bind(options))
|
||||
.UseWebhooks(webhooks => webhooks.WebhookOptions = options => builder.Configuration.GetSection("Webhooks").Bind(options))
|
||||
.UseWorkflowsApi()
|
||||
.UseRealTimeWorkflows()
|
||||
.AddActivitiesFrom<Program>()
|
||||
.AddWorkflowsFrom<Program>()
|
||||
);
|
||||
elsa.UseMassTransit();
|
||||
}
|
||||
});
|
||||
|
||||
services.AddHealthChecks();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"Sqlite": "Data Source=elsa.sqlite.db;Cache=Shared;"
|
||||
"Sqlite": "Data Source=elsa.sqlite.db;Cache=Shared;",
|
||||
"RabbitMq": "rabbitmq://guest:guest@localhost:5672"
|
||||
},
|
||||
"Hosting": {
|
||||
"BasePath": ""
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Elsa.MassTransit.Implementations;
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Attributes;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.UIHints;
|
||||
using MassTransit;
|
||||
|
||||
namespace Elsa.MassTransit.Activities;
|
||||
|
|
@ -29,7 +30,10 @@ public class PublishMessage : CodeActivity
|
|||
/// <summary>
|
||||
/// The message to send. Must be a concrete implementation of the configured <see cref="MessageType"/>.
|
||||
/// </summary>
|
||||
[Input(Description = "The message to send. Must be a concrete implementation of the configured message type.")]
|
||||
[Input(
|
||||
Description = "The message to send. Must be a concrete implementation of the configured message type.",
|
||||
UIHint = InputUIHints.MultiLine
|
||||
)]
|
||||
public Input<object> Message { get; set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ using Elsa.MassTransit.Options;
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Attributes;
|
||||
using Elsa.Workflows.Contracts;
|
||||
using Elsa.Workflows.UIHints;
|
||||
using Elsa.Workflows.Helpers;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Models;
|
||||
using Humanizer;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -17,40 +17,31 @@ namespace Elsa.MassTransit.Implementations;
|
|||
/// <summary>
|
||||
/// Provides activities to the system from the configured MassTransit message types.
|
||||
/// </summary>
|
||||
public class MassTransitActivityTypeProvider : IActivityProvider
|
||||
public class MassTransitActivityTypeProvider(IActivityFactory activityFactory, IOptions<MassTransitActivityOptions> options, IActivityDescriber activityDescriber) : IActivityProvider
|
||||
{
|
||||
private readonly IActivityFactory _activityFactory;
|
||||
private readonly MassTransitActivityOptions _options;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public MassTransitActivityTypeProvider(IActivityFactory activityFactory, IOptions<MassTransitActivityOptions> options)
|
||||
{
|
||||
_activityFactory = activityFactory;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask<IEnumerable<ActivityDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
|
||||
public async ValueTask<IEnumerable<ActivityDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var messageTypes = _options.MessageTypes;
|
||||
var descriptors = CreateDescriptors(messageTypes).ToList();
|
||||
return new(descriptors);
|
||||
var messageTypes = options.Value.MessageTypes;
|
||||
var descriptors = await CreateDescriptorsAsync(messageTypes, cancellationToken);
|
||||
return descriptors.ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<ActivityDescriptor> CreateDescriptors(IEnumerable<Type> messageTypes)
|
||||
private async Task<IEnumerable<ActivityDescriptor>> CreateDescriptorsAsync(IEnumerable<Type> messageTypes, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var descriptors = new List<ActivityDescriptor>();
|
||||
foreach (var messageType in messageTypes)
|
||||
{
|
||||
yield return CreateMessageReceivedDescriptor(messageType);
|
||||
descriptors.Add(await CreateMessageReceivedDescriptor(messageType, cancellationToken));
|
||||
|
||||
if(messageType.IsClass)
|
||||
yield return CreatePublishMessageDescriptor(messageType);
|
||||
descriptors.Add(await CreatePublishMessageDescriptor(messageType, cancellationToken));
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
private ActivityDescriptor CreateMessageReceivedDescriptor(Type messageType)
|
||||
private async Task<ActivityDescriptor> CreateMessageReceivedDescriptor(Type messageType, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var activityAttr = messageType.GetCustomAttribute<ActivityAttribute>();
|
||||
var typeName = activityAttr?.Type ?? messageType.Name;
|
||||
|
|
@ -61,6 +52,11 @@ public class MassTransitActivityTypeProvider : IActivityProvider
|
|||
var category = categoryAttr?.Category ?? activityAttr?.Category ?? "MassTransit";
|
||||
var descriptionAttr = messageType.GetCustomAttribute<DescriptionAttribute>();
|
||||
var description = descriptionAttr?.Description ?? activityAttr?.Description;
|
||||
|
||||
var outputDescriptor = await activityDescriber.DescribeOutputProperty<MessageReceived, object>(x => x.Result!, cancellationToken);
|
||||
var openOutputType = typeof(Output<>);
|
||||
var outputType = openOutputType.MakeGenericType(messageType);
|
||||
outputDescriptor.Type = outputType;
|
||||
|
||||
return new()
|
||||
{
|
||||
|
|
@ -73,17 +69,11 @@ public class MassTransitActivityTypeProvider : IActivityProvider
|
|||
IsBrowsable = true,
|
||||
Outputs =
|
||||
{
|
||||
new OutputDescriptor
|
||||
{
|
||||
Description = "The received message",
|
||||
DisplayName = "Received Message",
|
||||
Name = nameof(MessageReceived.Result),
|
||||
Type = typeof(object)
|
||||
}
|
||||
outputDescriptor
|
||||
},
|
||||
Constructor = context =>
|
||||
{
|
||||
var activity = _activityFactory.Create<MessageReceived>(context);
|
||||
var activity = activityFactory.Create<MessageReceived>(context);
|
||||
activity.Type = fullTypeName;
|
||||
activity.MessageType = messageType;
|
||||
return activity;
|
||||
|
|
@ -91,7 +81,7 @@ public class MassTransitActivityTypeProvider : IActivityProvider
|
|||
};
|
||||
}
|
||||
|
||||
private ActivityDescriptor CreatePublishMessageDescriptor(Type messageType)
|
||||
private async Task<ActivityDescriptor> CreatePublishMessageDescriptor(Type messageType, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var activityAttr = messageType.GetCustomAttribute<ActivityAttribute>();
|
||||
var typeName = activityAttr?.Type ?? messageType.Name;
|
||||
|
|
@ -104,6 +94,11 @@ public class MassTransitActivityTypeProvider : IActivityProvider
|
|||
var descriptionAttr = messageType.GetCustomAttribute<DescriptionAttribute>();
|
||||
var description = descriptionAttr?.Description ?? activityAttr?.Description;
|
||||
|
||||
var messageInputDescriptor = await activityDescriber.DescribeInputPropertyAsync<PublishMessage, object>(x => x.Message, cancellationToken: cancellationToken);
|
||||
var openInputType = typeof(Input<>);
|
||||
var inputType = openInputType.MakeGenericType(messageType);
|
||||
messageInputDescriptor.Type = inputType;
|
||||
|
||||
return new()
|
||||
{
|
||||
TypeName = fullTypeName,
|
||||
|
|
@ -115,18 +110,11 @@ public class MassTransitActivityTypeProvider : IActivityProvider
|
|||
IsBrowsable = true,
|
||||
Inputs =
|
||||
{
|
||||
new InputDescriptor
|
||||
{
|
||||
Description = "The message to publish.",
|
||||
UIHint = InputUIHints.MultiLine,
|
||||
DisplayName = "Message",
|
||||
Type = typeof(Input<object>),
|
||||
Name = nameof(PublishMessage.Message)
|
||||
}
|
||||
messageInputDescriptor
|
||||
},
|
||||
Constructor = context =>
|
||||
{
|
||||
var activity = _activityFactory.Create<PublishMessage>(context);
|
||||
var activity = activityFactory.Create<PublishMessage>(context);
|
||||
activity.Type = fullTypeName;
|
||||
activity.MessageType = messageType;
|
||||
return activity;
|
||||
|
|
|
|||
|
|
@ -292,7 +292,12 @@ public static class ExpressionExecutionContextExtensions
|
|||
select v;
|
||||
|
||||
var variable = q.FirstOrDefault();
|
||||
variable?.Set(context, value);
|
||||
|
||||
if(variable != null)
|
||||
variable.Set(context, value);
|
||||
|
||||
if (variable == null)
|
||||
CreateVariable(context, variableName, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,31 @@ public class WorkflowStateExtractor : IWorkflowStateExtractor
|
|||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public WorkflowExecutionContext Apply(WorkflowExecutionContext workflowExecutionContext, WorkflowState state)
|
||||
{
|
||||
workflowExecutionContext.Id = state.Id;
|
||||
workflowExecutionContext.CorrelationId = state.CorrelationId;
|
||||
workflowExecutionContext.SubStatus = state.SubStatus;
|
||||
workflowExecutionContext.Bookmarks = state.Bookmarks;
|
||||
workflowExecutionContext.Output = state.Output;
|
||||
workflowExecutionContext.ExecutionLogSequence = state.ExecutionLogSequence;
|
||||
workflowExecutionContext.CreatedAt = state.CreatedAt;
|
||||
ApplyInput(state, workflowExecutionContext);
|
||||
ApplyProperties(state, workflowExecutionContext);
|
||||
ApplyActivityExecutionContexts(state, workflowExecutionContext);
|
||||
ApplyCompletionCallbacks(state, workflowExecutionContext);
|
||||
ApplyScheduledActivities(state, workflowExecutionContext);
|
||||
return workflowExecutionContext;
|
||||
}
|
||||
|
||||
private void ApplyInput(WorkflowState state, WorkflowExecutionContext workflowExecutionContext)
|
||||
{
|
||||
// Only add input from state if the input doesn't already exist on the workflow execution context.
|
||||
foreach (var inputItem in state.Input)
|
||||
if (!workflowExecutionContext.Input.ContainsKey(inputItem.Key)) workflowExecutionContext.Input.Add(inputItem.Key, inputItem.Value);
|
||||
}
|
||||
|
||||
private IDictionary<string, object> GetPersistableInput(WorkflowExecutionContext workflowExecutionContext)
|
||||
{
|
||||
// TODO: This is a temporary solution. We need to find a better way to handle this.
|
||||
|
|
@ -52,24 +77,6 @@ public class WorkflowStateExtractor : IWorkflowStateExtractor
|
|||
return filteredInput;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public WorkflowExecutionContext Apply(WorkflowExecutionContext workflowExecutionContext, WorkflowState state)
|
||||
{
|
||||
// Do not map input. We don't want to overwrite the input that was passed to the workflow.
|
||||
workflowExecutionContext.Id = state.Id;
|
||||
workflowExecutionContext.CorrelationId = state.CorrelationId;
|
||||
workflowExecutionContext.SubStatus = state.SubStatus;
|
||||
workflowExecutionContext.Bookmarks = state.Bookmarks;
|
||||
workflowExecutionContext.Output = state.Output;
|
||||
workflowExecutionContext.ExecutionLogSequence = state.ExecutionLogSequence;
|
||||
workflowExecutionContext.CreatedAt = state.CreatedAt;
|
||||
ApplyProperties(state, workflowExecutionContext);
|
||||
ApplyActivityExecutionContexts(state, workflowExecutionContext);
|
||||
ApplyCompletionCallbacks(state, workflowExecutionContext);
|
||||
ApplyScheduledActivities(state, workflowExecutionContext);
|
||||
return workflowExecutionContext;
|
||||
}
|
||||
|
||||
private void ExtractProperties(WorkflowState state, WorkflowExecutionContext workflowExecutionContext)
|
||||
{
|
||||
state.Properties = workflowExecutionContext.Properties;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\bundles\Elsa\Elsa.csproj" />
|
||||
<ProjectReference Include="..\..\..\modules\Elsa.CSharp\Elsa.CSharp.csproj" />
|
||||
<ProjectReference Include="..\..\..\modules\Elsa.EntityFrameworkCore.Sqlite\Elsa.EntityFrameworkCore.Sqlite.csproj" />
|
||||
<ProjectReference Include="..\..\..\modules\Elsa.EntityFrameworkCore\Elsa.EntityFrameworkCore.csproj" />
|
||||
<ProjectReference Include="..\..\..\modules\Elsa.Http\Elsa.Http.csproj" />
|
||||
<ProjectReference Include="..\..\..\modules\Elsa.Identity\Elsa.Identity.csproj" />
|
||||
|
|
|
|||
|
|
@ -28,16 +28,20 @@ builder.Services.AddElsa(elsa =>
|
|||
|
||||
// Add services for HTTP activities and workflow middleware.
|
||||
elsa.UseHttp();
|
||||
|
||||
// Use JavaScript.
|
||||
elsa.UseJavaScript(javaScript =>
|
||||
|
||||
// Use C#.
|
||||
elsa.UseCSharp(options =>
|
||||
{
|
||||
javaScript.AllowClrAccess = true;
|
||||
javaScript.ConfigureEngine(options =>
|
||||
{
|
||||
options.RegisterType<OrderCompleted>();
|
||||
options.RegisterType<OrderCreated>();
|
||||
});
|
||||
options.Assemblies.Add(typeof(OrderCreated).Assembly);
|
||||
options.Namespaces.Add(typeof(OrderCreated).Namespace!);
|
||||
});
|
||||
|
||||
// Use JavaScript.
|
||||
elsa.UseJavaScript(options =>
|
||||
{
|
||||
options.AllowClrAccess = true;
|
||||
options.RegisterType<OrderCreated>();
|
||||
options.RegisterType<OrderCompleted>();
|
||||
});
|
||||
|
||||
// Use C#.
|
||||
|
|
@ -56,7 +60,7 @@ builder.Services.AddElsa(elsa =>
|
|||
identity.UseAdminUserProvider();
|
||||
identity.TokenOptions = options =>
|
||||
{
|
||||
options.SigningKey = "secret-token-signing-key";
|
||||
options.SigningKey = "super-secret-and-securely-stored-token-signing-key";
|
||||
options.AccessTokenLifetime = TimeSpan.FromDays(1);
|
||||
};
|
||||
});
|
||||
|
|
@ -67,7 +71,7 @@ builder.Services.AddElsa(elsa =>
|
|||
// Configure MassTransit.
|
||||
elsa.UseMassTransit(massTransit =>
|
||||
{
|
||||
massTransit.UseRabbitMq(rabbitMqConnectionString);
|
||||
//massTransit.UseRabbitMq(rabbitMqConnectionString);
|
||||
massTransit.AddMessageType<OrderCompleted>();
|
||||
massTransit.AddMessageType<OrderCreated>();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue