Merge remote-tracking branch 'upstream/feature/elsa-2.0' into elsa-2.0/fileactivities
This commit is contained in:
commit
619421e098
7
Elsa.sln
7
Elsa.sln
|
|
@ -275,6 +275,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Activities.File", "src
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.ReadLineToFile", "src\samples\console\Elsa.Samples.ReadLineToFile\Elsa.Samples.ReadLineToFile.csproj", "{3EB488DF-3C28-4DAD-B43E-6062E39629AB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.SendHttp", "src\samples\aspnet\Elsa.Samples.SendHttp\Elsa.Samples.SendHttp.csproj", "{775EFD59-F99C-4D38-B54E-9734C9D9649A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -657,6 +659,10 @@ Global
|
|||
{3EB488DF-3C28-4DAD-B43E-6062E39629AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3EB488DF-3C28-4DAD-B43E-6062E39629AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3EB488DF-3C28-4DAD-B43E-6062E39629AB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{775EFD59-F99C-4D38-B54E-9734C9D9649A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{775EFD59-F99C-4D38-B54E-9734C9D9649A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{775EFD59-F99C-4D38-B54E-9734C9D9649A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{775EFD59-F99C-4D38-B54E-9734C9D9649A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -785,6 +791,7 @@ Global
|
|||
{D55BC08B-CA82-478A-99F7-EDE90AC33160} = {C865B0FD-E505-48F0-BFAF-0D4D7C1B5CA1}
|
||||
{BE284508-9582-4FCB-B0DD-0761AB940AD7} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180}
|
||||
{3EB488DF-3C28-4DAD-B43E-6062E39629AB} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
|
||||
{775EFD59-F99C-4D38-B54E-9734C9D9649A} = {22E75696-6FE9-436A-9097-EE21C603F818}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158}
|
||||
|
|
|
|||
|
|
@ -41,12 +41,22 @@ namespace Elsa.Activities.AzureServiceBus.Services
|
|||
await DisposeExistingWorkersAsync();
|
||||
var queueNames = (await GetQueueNamesAsync(cancellationToken).ToListAsync(cancellationToken)).Distinct();
|
||||
|
||||
foreach (var queueName in queueNames)
|
||||
foreach (var queueName in queueNames)
|
||||
await CreateAndAddWorkerAsync(queueName, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task CreateAndAddWorkerAsync(string queueName, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var receiver = await _messageReceiverClientFactory.GetReceiverAsync(queueName, cancellationToken);
|
||||
var worker = ActivatorUtilities.CreateInstance<QueueWorker>(_serviceProvider, receiver, (Func<IReceiverClient, Task>) DisposeReceiverAsync);
|
||||
_workers.Add(worker);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogWarning(e, "Failed to create a receiver for queue {QueueName}", queueName);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DisposeExistingWorkersAsync()
|
||||
|
|
@ -65,7 +75,7 @@ namespace Elsa.Activities.AzureServiceBus.Services
|
|||
using var scope = _scopeFactory.CreateScope();
|
||||
var workflowRegistry = scope.ServiceProvider.GetRequiredService<IWorkflowRegistry>();
|
||||
var workflowBlueprintReflector = scope.ServiceProvider.GetRequiredService<IWorkflowBlueprintReflector>();
|
||||
var workflows = await workflowRegistry.ListAsync(cancellationToken);
|
||||
var workflows = await workflowRegistry.ListActiveAsync(cancellationToken);
|
||||
|
||||
var query =
|
||||
from workflow in workflows
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Elsa.Services;
|
|||
using Elsa.Services.Models;
|
||||
using Microsoft.Azure.ServiceBus.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Elsa.Activities.AzureServiceBus.Services
|
||||
{
|
||||
|
|
@ -19,16 +20,19 @@ namespace Elsa.Activities.AzureServiceBus.Services
|
|||
private readonly ITopicMessageReceiverFactory _receiverFactory;
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<ServiceBusTopicsStarter> _logger;
|
||||
private readonly ICollection<TopicWorker> _workers;
|
||||
|
||||
public ServiceBusTopicsStarter(
|
||||
ITopicMessageReceiverFactory receiverFactory,
|
||||
IServiceScopeFactory scopeFactory,
|
||||
IServiceProvider serviceProvider)
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<ServiceBusTopicsStarter> logger)
|
||||
{
|
||||
_receiverFactory = receiverFactory;
|
||||
_scopeFactory = scopeFactory;
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
_workers = new List<TopicWorker>();
|
||||
}
|
||||
|
||||
|
|
@ -38,12 +42,22 @@ namespace Elsa.Activities.AzureServiceBus.Services
|
|||
await DisposeExistingWorkersAsync();
|
||||
var entities = (await GetTopicSubscriptionNamesAsync(cancellationToken).ToListAsync(cancellationToken)).Distinct();
|
||||
|
||||
foreach (var entity in entities)
|
||||
foreach (var entity in entities)
|
||||
await CreateAndAddWorkerAsync(entity.topicName, entity.subscriptionName, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task CreateAndAddWorkerAsync(string topicName, string subscriptionName, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var receiver = await _receiverFactory.GetTopicReceiverAsync(entity.topicName, entity.subscriptionName, cancellationToken);
|
||||
var receiver = await _receiverFactory.GetTopicReceiverAsync(topicName, subscriptionName, cancellationToken);
|
||||
var worker = ActivatorUtilities.CreateInstance<TopicWorker>(_serviceProvider, receiver, (Func<IReceiverClient, Task>) DisposeReceiverAsync);
|
||||
_workers.Add(worker);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogWarning(e, "Failed to create a receiver for topic {TopicName} and subscription {SubscriptionName}", topicName, subscriptionName);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DisposeExistingWorkersAsync()
|
||||
|
|
@ -63,7 +77,7 @@ namespace Elsa.Activities.AzureServiceBus.Services
|
|||
var workflowRegistry = scope.ServiceProvider.GetRequiredService<IWorkflowRegistry>();
|
||||
var workflowBlueprintReflector = scope.ServiceProvider.GetRequiredService<IWorkflowBlueprintReflector>();
|
||||
var workflowInstanceStore = scope.ServiceProvider.GetRequiredService<IWorkflowInstanceStore>();
|
||||
var workflows = await workflowRegistry.ListAsync(cancellationToken);
|
||||
var workflows = await workflowRegistry.ListActiveAsync(cancellationToken);
|
||||
|
||||
var query =
|
||||
from workflow in workflows
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
using Elsa.Activities.Http.Models;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Activities.Http
|
||||
{
|
||||
public static class SendHttpRequestBuilderExtensions
|
||||
{
|
||||
public static IActivityBuilder SendHttpRequest(this IActivityBuilder builder, Action<ISetupActivity<SendHttpRequest>> setup, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.Then(setup, null, lineNumber, sourceFile);
|
||||
|
||||
public static IActivityBuilder SendHttpRequest(this IActivityBuilder builder,
|
||||
Func<ActivityExecutionContext, Uri?> url,
|
||||
Func<ActivityExecutionContext, string?> method,
|
||||
Func<ActivityExecutionContext, string?> content,
|
||||
Func<ActivityExecutionContext, string?> contentType,
|
||||
Func<ActivityExecutionContext, string?> authorization,
|
||||
Func<ActivityExecutionContext, HttpRequestHeaders> requestHeaders,
|
||||
Func<ActivityExecutionContext, bool> readContent,
|
||||
Func<ActivityExecutionContext, ICollection<int>> supportedStatusCodes,
|
||||
[CallerLineNumber] int lineNumber = default,
|
||||
[CallerFilePath] string? sourceFile = default) =>
|
||||
builder.SendHttpRequest(
|
||||
setup => setup
|
||||
.Set(x => x.Url, url)
|
||||
.Set(x => x.Method, method)
|
||||
.Set(x => x.Content, content)
|
||||
.Set(x => x.ContentType, contentType)
|
||||
.Set(x => x.Authorization, authorization)
|
||||
.Set(x => x.RequestHeaders, requestHeaders)
|
||||
.Set(x => x.ReadContent, readContent)
|
||||
.Set(x => x.SupportedStatusCodes, supportedStatusCodes),
|
||||
lineNumber,
|
||||
sourceFile);
|
||||
|
||||
public static IActivityBuilder SendHttpRequest(this IActivityBuilder builder,
|
||||
Func<Uri?> url,
|
||||
Func<string?> method,
|
||||
Func<string?> content,
|
||||
Func<string?> contentType,
|
||||
Func<string?> authorization,
|
||||
Func<HttpRequestHeaders> requestHeaders,
|
||||
Func<bool> readContent,
|
||||
Func<ICollection<int>> supportedStatusCodes,
|
||||
[CallerLineNumber] int lineNumber = default,
|
||||
[CallerFilePath] string? sourceFile = default) =>
|
||||
builder.SendHttpRequest(
|
||||
setup => setup
|
||||
.Set(x => x.Url, url)
|
||||
.Set(x => x.Method, method)
|
||||
.Set(x => x.Content, content)
|
||||
.Set(x => x.ContentType, contentType)
|
||||
.Set(x => x.Authorization, authorization)
|
||||
.Set(x => x.RequestHeaders, requestHeaders)
|
||||
.Set(x => x.ReadContent, readContent)
|
||||
.Set(x => x.SupportedStatusCodes, supportedStatusCodes),
|
||||
lineNumber,
|
||||
sourceFile);
|
||||
|
||||
public static IActivityBuilder SendHttpRequest(this IActivityBuilder builder,
|
||||
Uri? url,
|
||||
string? method,
|
||||
string? content,
|
||||
string? contentType,
|
||||
string? authorization,
|
||||
HttpRequestHeaders requestHeaders,
|
||||
bool readContent,
|
||||
ICollection<int> supportedStatusCodes,
|
||||
[CallerLineNumber] int lineNumber = default,
|
||||
[CallerFilePath] string? sourceFile = default) =>
|
||||
builder.SendHttpRequest(
|
||||
setup => setup
|
||||
.Set(x => x.Url, url)
|
||||
.Set(x => x.Method, method)
|
||||
.Set(x => x.Content, content)
|
||||
.Set(x => x.ContentType, contentType)
|
||||
.Set(x => x.Authorization, authorization)
|
||||
.Set(x => x.RequestHeaders, requestHeaders)
|
||||
.Set(x => x.ReadContent, readContent)
|
||||
.Set(x => x.SupportedStatusCodes, supportedStatusCodes),
|
||||
lineNumber,
|
||||
sourceFile);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
using Elsa.Activities.Http.Models;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Activities.Http
|
||||
{
|
||||
public static class SendHttpRequestExtensions
|
||||
{
|
||||
public static ISetupActivity<SendHttpRequest> WithUrl(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ValueTask<Uri?>> url) => activity.Set(x => x.Url, url);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithUrl(this ISetupActivity<SendHttpRequest> activity, Func<ValueTask<Uri?>> url) => activity.Set(x => x.Url, url);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithUrl(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, Uri?> url) => activity.Set(x => x.Url, url);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithUrl(this ISetupActivity<SendHttpRequest> activity, Func<Uri?> url) => activity.Set(x => x.Url, url);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithUrl(this ISetupActivity<SendHttpRequest> activity, Uri? url) => activity.Set(x => x.Url, url);
|
||||
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithMethod(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ValueTask<string?>> method) => activity.Set(x => x.Method, method);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithMethod(this ISetupActivity<SendHttpRequest> activity, Func<ValueTask<string?>> method) => activity.Set(x => x.Method, method);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithMethod(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, string?> method) => activity.Set(x => x.Method, method);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithMethod(this ISetupActivity<SendHttpRequest> activity, Func<string?> method) => activity.Set(x => x.Method, method);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithMethod(this ISetupActivity<SendHttpRequest> activity, string? method) => activity.Set(x => x.Method, method);
|
||||
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContent(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ValueTask<string?>> content
|
||||
) => activity.Set(x => x.Content, content);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContent(this ISetupActivity<SendHttpRequest> activity, Func<ValueTask<string?>> content) => activity.Set(x => x.Content, content);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContent(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, string?> content) => activity.Set(x => x.Content, content);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContent(this ISetupActivity<SendHttpRequest> activity, Func<string?> content) => activity.Set(x => x.Content, content);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContent(this ISetupActivity<SendHttpRequest> activity, string? content) => activity.Set(x => x.Content, content);
|
||||
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContentType(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ValueTask<string?>> contentType
|
||||
) => activity.Set(x => x.ContentType, contentType);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContentType(this ISetupActivity<SendHttpRequest> activity, Func<ValueTask<string?>> contentType) => activity.Set(x => x.ContentType, contentType);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContentType(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, string?> contentType) => activity.Set(x => x.ContentType, contentType);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContentType(this ISetupActivity<SendHttpRequest> activity, Func<string?> contentType) => activity.Set(x => x.ContentType, contentType);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithContentType(this ISetupActivity<SendHttpRequest> activity, string? contentType) => activity.Set(x => x.ContentType, contentType);
|
||||
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithAuthorization(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ValueTask<string?>> authorization
|
||||
) => activity.Set(x => x.Authorization, authorization);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithAuthorization(this ISetupActivity<SendHttpRequest> activity, Func<ValueTask<string?>> authorization) => activity.Set(x => x.Authorization, authorization);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithAuthorization(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, string?> authorization) => activity.Set(x => x.Authorization, authorization);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithAuthorization(this ISetupActivity<SendHttpRequest> activity, Func<string?> authorization) => activity.Set(x => x.Authorization, authorization);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithAuthorization(this ISetupActivity<SendHttpRequest> activity, string? authorization) => activity.Set(x => x.Authorization, authorization);
|
||||
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithRequestHeaders(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ValueTask<HttpRequestHeaders?>> requestHeaders
|
||||
) => activity.Set(x => x.RequestHeaders, requestHeaders);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithRequestHeaders(this ISetupActivity<SendHttpRequest> activity, Func<ValueTask<HttpRequestHeaders?>> requestHeaders) => activity.Set(x => x.RequestHeaders, requestHeaders);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithRequestHeaders(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, HttpRequestHeaders?> requestHeaders) => activity.Set(x => x.RequestHeaders, requestHeaders);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithRequestHeaders(this ISetupActivity<SendHttpRequest> activity, Func<HttpRequestHeaders?> requestHeaders) => activity.Set(x => x.RequestHeaders, requestHeaders);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithRequestHeaders(this ISetupActivity<SendHttpRequest> activity, HttpRequestHeaders? requestHeaders) => activity.Set(x => x.RequestHeaders, requestHeaders);
|
||||
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithReadContent(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ValueTask<bool?>> readContent
|
||||
) => activity.Set(x => x.ReadContent, readContent);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithReadContent(this ISetupActivity<SendHttpRequest> activity, Func<ValueTask<bool?>> readContent) => activity.Set(x => x.ReadContent, readContent);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithReadContent(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, bool?> readContent) => activity.Set(x => x.ReadContent, readContent);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithReadContent(this ISetupActivity<SendHttpRequest> activity, Func<bool?> readContent) => activity.Set(x => x.ReadContent, readContent);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithReadContent(this ISetupActivity<SendHttpRequest> activity, bool? readContent) => activity.Set(x => x.ReadContent, readContent);
|
||||
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithSupportedHttpCodes(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ValueTask<ICollection<int>?>> supportedStatusCodes
|
||||
) => activity.Set(x => x.SupportedStatusCodes, supportedStatusCodes);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithSupportedHttpCodes(this ISetupActivity<SendHttpRequest> activity, Func<ValueTask<ICollection<int>?>> supportedStatusCodes) => activity.Set(x => x.SupportedStatusCodes, supportedStatusCodes);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithSupportedHttpCodes(this ISetupActivity<SendHttpRequest> activity, Func<ActivityExecutionContext, ICollection<int>> supportedStatusCodes) => activity.Set(x => x.SupportedStatusCodes, supportedStatusCodes);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithSupportedHttpCodes(this ISetupActivity<SendHttpRequest> activity, Func<ICollection<int>> supportedStatusCodes) => activity.Set(x => x.SupportedStatusCodes, supportedStatusCodes);
|
||||
|
||||
public static ISetupActivity<SendHttpRequest> WithSupportedHttpCodes(this ISetupActivity<SendHttpRequest> activity, ICollection<int> supportedStatusCodes) => activity.Set(x => x.SupportedStatusCodes, supportedStatusCodes);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Design
|
||||
{
|
||||
public interface IRuntimeSelectListItemsProvider
|
||||
{
|
||||
ValueTask<IEnumerable<SelectListItem>> GetItemsAsync(object? context = default, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Elsa.Serialization.Converters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Elsa.Design
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents settings to be used by the designer to invoke an Elsa's API endpoint to retrieve list options at runtime.
|
||||
/// </summary>
|
||||
public class RuntimeSelectListItemsProviderSettings
|
||||
{
|
||||
public RuntimeSelectListItemsProviderSettings(Type providerType, object? context = default)
|
||||
{
|
||||
RuntimeSelectListItemsProviderType = providerType;
|
||||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of the list items provider.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(FullTypeJsonConverter))]
|
||||
public Type RuntimeSelectListItemsProviderType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optionally provide an object containing useful information for the list select items provider to determine what items to provide.
|
||||
/// </summary>
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
|
||||
public object? Context { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Elsa.Serialization.Converters
|
||||
{
|
||||
public class FullTypeJsonConverter : JsonConverter<Type>
|
||||
{
|
||||
public override bool CanRead => true;
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Type? value, JsonSerializer serializer)
|
||||
{
|
||||
var typeName = value!.AssemblyQualifiedName;
|
||||
serializer.Serialize(writer, typeName);
|
||||
}
|
||||
|
||||
public override Type ReadJson(JsonReader reader, Type objectType, Type? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
var typeName = serializer.Deserialize<string>(reader)!;
|
||||
return typeName is not null and not "" ? Type.GetType(typeName)! : default!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,34 @@ using Elsa.Services.Models;
|
|||
|
||||
namespace Elsa.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a complete store of all versions of all workflow blueprints.
|
||||
/// </summary>
|
||||
public interface IWorkflowRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Lists all versions of all workflow blueprints.
|
||||
/// </summary>
|
||||
Task<IEnumerable<IWorkflowBlueprint>> ListAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists only those workflow blueprints that are published or have at least one non-finished workflow.
|
||||
/// </summary>
|
||||
Task<IEnumerable<IWorkflowBlueprint>> ListActiveAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single workflow blueprint with the specified ID for the specified tenant and version.
|
||||
/// </summary>
|
||||
Task<IWorkflowBlueprint?> GetAsync(string id, string? tenantId, VersionOptions version, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Returns all workflow blueprints that fit the specified predicate.
|
||||
/// </summary>
|
||||
Task<IEnumerable<IWorkflowBlueprint>> FindManyAsync(Func<IWorkflowBlueprint, bool> predicate, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a single workflow blueprint that fits the specified predicate.
|
||||
/// </summary>
|
||||
Task<IWorkflowBlueprint?> FindAsync(Func<IWorkflowBlueprint, bool> predicate, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ namespace Elsa.Bookmarks
|
|||
var workflowInstanceIds = workflowInstanceList.Select(x => x.Id).ToList();
|
||||
await DeleteBookmarksAsync(workflowInstanceIds, cancellationToken);
|
||||
|
||||
var workflowBlueprints = await _workflowRegistry.ListAsync(cancellationToken);
|
||||
var workflowBlueprints = await _workflowRegistry.ListActiveAsync(cancellationToken);
|
||||
var workflowBlueprintsDictionary = workflowBlueprints.ToDictionary(x => (x.Id, x.Version));
|
||||
var entities = new List<Bookmark>();
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ namespace Elsa.Decorators
|
|||
}
|
||||
|
||||
public async Task<IEnumerable<IWorkflowBlueprint>> ListAsync(CancellationToken cancellationToken) => await GetWorkflowBlueprints(cancellationToken);
|
||||
public Task<IEnumerable<IWorkflowBlueprint>> ListActiveAsync(CancellationToken cancellationToken = default) => _workflowRegistry.ListActiveAsync(cancellationToken);
|
||||
|
||||
public async Task<IWorkflowBlueprint?> GetAsync(string id, string? tenantId, VersionOptions version, CancellationToken cancellationToken) =>
|
||||
await FindAsync(x => x.Id == id && x.TenantId == tenantId && x.WithVersion(version), cancellationToken);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Elsa.Builders;
|
|||
using Elsa.Consumers;
|
||||
using Elsa.Converters;
|
||||
using Elsa.Decorators;
|
||||
using Elsa.Design;
|
||||
using Elsa.Dispatch;
|
||||
using Elsa.Dispatch.Consumers;
|
||||
using Elsa.Events;
|
||||
|
|
@ -102,6 +103,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
public static IServiceCollection AddActivityPropertyOptionsProvider<T>(this IServiceCollection services) where T : class, IActivityPropertyOptionsProvider => services.AddSingleton<IActivityPropertyOptionsProvider, T>();
|
||||
public static IServiceCollection AddRuntimeSelectItemsProvider<T>(this IServiceCollection services) where T : class, IRuntimeSelectListItemsProvider => services.AddScoped<IRuntimeSelectListItemsProvider, T>();
|
||||
public static IServiceCollection AddActivityTypeProvider<T>(this IServiceCollection services) where T : class, IActivityTypeProvider => services.AddSingleton<IActivityTypeProvider, T>();
|
||||
|
||||
private static ElsaOptionsBuilder AddWorkflowsCore(this ElsaOptionsBuilder options)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace Elsa.Services
|
|||
}
|
||||
|
||||
public async Task<IEnumerable<IWorkflowBlueprint>> ListAsync(CancellationToken cancellationToken) => await GetWorkflowsInternalAsync(cancellationToken).ToListAsync(cancellationToken);
|
||||
public async Task<IEnumerable<IWorkflowBlueprint>> ListActiveAsync(CancellationToken cancellationToken) => await ListActiveWorkflowsAsync(cancellationToken).ToListAsync(cancellationToken);
|
||||
|
||||
public async Task<IWorkflowBlueprint?> GetAsync(string id, string? tenantId, VersionOptions version, CancellationToken cancellationToken) =>
|
||||
await FindAsync(x => x.Id == id && x.TenantId == tenantId && x.WithVersion(version), cancellationToken);
|
||||
|
|
@ -33,6 +34,26 @@ namespace Elsa.Services
|
|||
|
||||
public async Task<IWorkflowBlueprint?> FindAsync(Func<IWorkflowBlueprint, bool> predicate, CancellationToken cancellationToken) =>
|
||||
(await ListAsync(cancellationToken).Where(predicate).OrderByDescending(x => x.Version)).FirstOrDefault();
|
||||
|
||||
private async IAsyncEnumerable<IWorkflowBlueprint> ListActiveWorkflowsAsync([EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
var workflows = await ListAsync(cancellationToken);
|
||||
|
||||
foreach (var workflow in workflows)
|
||||
{
|
||||
// If a workflow is not published, only consider it for processing if it has at least one non-ended workflow instance.
|
||||
if (!workflow.IsPublished && !await WorkflowHasNonFinishedWorkflowsAsync(workflow, cancellationToken))
|
||||
continue;
|
||||
|
||||
yield return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> WorkflowHasNonFinishedWorkflowsAsync(IWorkflowBlueprint workflowBlueprint, CancellationToken cancellationToken)
|
||||
{
|
||||
var count = await _workflowInstanceStore.CountAsync(new NonFinalizedWorkflowSpecification().WithWorkflowDefinition(workflowBlueprint.Id), cancellationToken);
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<IWorkflowBlueprint> GetWorkflowsInternalAsync([EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace Elsa.Triggers
|
|||
|
||||
public async Task IndexTriggersAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var allWorkflowBlueprints = await _workflowRegistry.ListAsync(cancellationToken);
|
||||
var allWorkflowBlueprints = await _workflowRegistry.ListActiveAsync(cancellationToken);
|
||||
var publishedWorkflowBlueprints = allWorkflowBlueprints.Where(x => x.IsPublished).ToList();
|
||||
await IndexTriggersAsync(publishedWorkflowBlueprints, cancellationToken);
|
||||
await _mediator.Publish(new TriggerIndexingFinished(), cancellationToken);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* It contains typing information for all components that exist in this project.
|
||||
*/
|
||||
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
|
||||
import { ActivityDefinitionProperty, ActivityDescriptor, ActivityModel, ActivityPropertyDescriptor, VersionOptions, WorkflowBlueprint, WorkflowDefinition, WorkflowExecutionLogRecord, WorkflowModel } from "./models";
|
||||
import { ActivityDefinitionProperty, ActivityDescriptor, ActivityModel, ActivityPropertyDescriptor, SelectListItem, VersionOptions, WorkflowBlueprint, WorkflowDefinition, WorkflowExecutionLogRecord, WorkflowModel } from "./models";
|
||||
import { LocationSegments, MatchResults, RouterHistory } from "@stencil/router";
|
||||
import { MenuItem } from "./components/controls/elsa-context-menu/models";
|
||||
import { DropdownButtonItem, DropdownButtonOrigin } from "./components/controls/elsa-dropdown-button/models";
|
||||
|
|
@ -20,6 +20,7 @@ export namespace Components {
|
|||
interface ElsaCheckListProperty {
|
||||
"propertyDescriptor": ActivityPropertyDescriptor;
|
||||
"propertyModel": ActivityDefinitionProperty;
|
||||
"serverUrl": string;
|
||||
}
|
||||
interface ElsaCheckboxProperty {
|
||||
"propertyDescriptor": ActivityPropertyDescriptor;
|
||||
|
|
@ -47,6 +48,7 @@ export namespace Components {
|
|||
interface ElsaDropdownProperty {
|
||||
"propertyDescriptor": ActivityPropertyDescriptor;
|
||||
"propertyModel": ActivityDefinitionProperty;
|
||||
"serverUrl": string;
|
||||
}
|
||||
interface ElsaExpressionEditor {
|
||||
"context"?: string;
|
||||
|
|
@ -65,6 +67,13 @@ export namespace Components {
|
|||
"placeHolder"?: string;
|
||||
"values"?: Array<string>;
|
||||
}
|
||||
interface ElsaInputTagsDropdown {
|
||||
"dropdownValues"?: Array<SelectListItem>;
|
||||
"fieldId"?: string;
|
||||
"fieldName"?: string;
|
||||
"placeHolder"?: string;
|
||||
"values"?: Array<string | SelectListItem>;
|
||||
}
|
||||
interface ElsaModalDialog {
|
||||
"hide": (animate: boolean) => Promise<void>;
|
||||
"show": (animate: boolean) => Promise<void>;
|
||||
|
|
@ -96,6 +105,7 @@ export namespace Components {
|
|||
interface ElsaMultiTextProperty {
|
||||
"propertyDescriptor": ActivityPropertyDescriptor;
|
||||
"propertyModel": ActivityDefinitionProperty;
|
||||
"serverUrl": string;
|
||||
}
|
||||
interface ElsaPager {
|
||||
"history"?: RouterHistory;
|
||||
|
|
@ -130,6 +140,10 @@ export namespace Components {
|
|||
"monacoLibPath": string;
|
||||
"serverUrl": string;
|
||||
}
|
||||
interface ElsaStudioWorkflowBlueprintView {
|
||||
"match": MatchResults;
|
||||
"serverUrl": string;
|
||||
}
|
||||
interface ElsaStudioWorkflowDefinitionsEdit {
|
||||
"match": MatchResults;
|
||||
"monacoLibPath": string;
|
||||
|
|
@ -159,6 +173,11 @@ export namespace Components {
|
|||
"hide": () => Promise<void>;
|
||||
"show": (options: ToastNotificationOptions) => Promise<void>;
|
||||
}
|
||||
interface ElsaWorkflowBlueprintViewerScreen {
|
||||
"getServerUrl": () => Promise<string>;
|
||||
"serverUrl": string;
|
||||
"workflowDefinitionId": string;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorNotifications {
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorScreen {
|
||||
|
|
@ -272,6 +291,12 @@ declare global {
|
|||
prototype: HTMLElsaInputTagsElement;
|
||||
new (): HTMLElsaInputTagsElement;
|
||||
};
|
||||
interface HTMLElsaInputTagsDropdownElement extends Components.ElsaInputTagsDropdown, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaInputTagsDropdownElement: {
|
||||
prototype: HTMLElsaInputTagsDropdownElement;
|
||||
new (): HTMLElsaInputTagsDropdownElement;
|
||||
};
|
||||
interface HTMLElsaModalDialogElement extends Components.ElsaModalDialog, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaModalDialogElement: {
|
||||
|
|
@ -332,6 +357,12 @@ declare global {
|
|||
prototype: HTMLElsaStudioRootElement;
|
||||
new (): HTMLElsaStudioRootElement;
|
||||
};
|
||||
interface HTMLElsaStudioWorkflowBlueprintViewElement extends Components.ElsaStudioWorkflowBlueprintView, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaStudioWorkflowBlueprintViewElement: {
|
||||
prototype: HTMLElsaStudioWorkflowBlueprintViewElement;
|
||||
new (): HTMLElsaStudioWorkflowBlueprintViewElement;
|
||||
};
|
||||
interface HTMLElsaStudioWorkflowDefinitionsEditElement extends Components.ElsaStudioWorkflowDefinitionsEdit, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaStudioWorkflowDefinitionsEditElement: {
|
||||
|
|
@ -374,6 +405,12 @@ declare global {
|
|||
prototype: HTMLElsaToastNotificationElement;
|
||||
new (): HTMLElsaToastNotificationElement;
|
||||
};
|
||||
interface HTMLElsaWorkflowBlueprintViewerScreenElement extends Components.ElsaWorkflowBlueprintViewerScreen, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaWorkflowBlueprintViewerScreenElement: {
|
||||
prototype: HTMLElsaWorkflowBlueprintViewerScreenElement;
|
||||
new (): HTMLElsaWorkflowBlueprintViewerScreenElement;
|
||||
};
|
||||
interface HTMLElsaWorkflowDefinitionEditorNotificationsElement extends Components.ElsaWorkflowDefinitionEditorNotifications, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaWorkflowDefinitionEditorNotificationsElement: {
|
||||
|
|
@ -440,6 +477,7 @@ declare global {
|
|||
"elsa-dropdown-property": HTMLElsaDropdownPropertyElement;
|
||||
"elsa-expression-editor": HTMLElsaExpressionEditorElement;
|
||||
"elsa-input-tags": HTMLElsaInputTagsElement;
|
||||
"elsa-input-tags-dropdown": HTMLElsaInputTagsDropdownElement;
|
||||
"elsa-modal-dialog": HTMLElsaModalDialogElement;
|
||||
"elsa-monaco": HTMLElsaMonacoElement;
|
||||
"elsa-multi-expression-editor": HTMLElsaMultiExpressionEditorElement;
|
||||
|
|
@ -450,6 +488,7 @@ declare global {
|
|||
"elsa-script-property": HTMLElsaScriptPropertyElement;
|
||||
"elsa-single-line-property": HTMLElsaSingleLinePropertyElement;
|
||||
"elsa-studio-root": HTMLElsaStudioRootElement;
|
||||
"elsa-studio-workflow-blueprint-view": HTMLElsaStudioWorkflowBlueprintViewElement;
|
||||
"elsa-studio-workflow-definitions-edit": HTMLElsaStudioWorkflowDefinitionsEditElement;
|
||||
"elsa-studio-workflow-definitions-list": HTMLElsaStudioWorkflowDefinitionsListElement;
|
||||
"elsa-studio-workflow-instances-list": HTMLElsaStudioWorkflowInstancesListElement;
|
||||
|
|
@ -457,6 +496,7 @@ declare global {
|
|||
"elsa-studio-workflow-registry": HTMLElsaStudioWorkflowRegistryElement;
|
||||
"elsa-switch-cases-property": HTMLElsaSwitchCasesPropertyElement;
|
||||
"elsa-toast-notification": HTMLElsaToastNotificationElement;
|
||||
"elsa-workflow-blueprint-viewer-screen": HTMLElsaWorkflowBlueprintViewerScreenElement;
|
||||
"elsa-workflow-definition-editor-notifications": HTMLElsaWorkflowDefinitionEditorNotificationsElement;
|
||||
"elsa-workflow-definition-editor-screen": HTMLElsaWorkflowDefinitionEditorScreenElement;
|
||||
"elsa-workflow-definitions-list-screen": HTMLElsaWorkflowDefinitionsListScreenElement;
|
||||
|
|
@ -476,6 +516,7 @@ declare namespace LocalJSX {
|
|||
interface ElsaCheckListProperty {
|
||||
"propertyDescriptor"?: ActivityPropertyDescriptor;
|
||||
"propertyModel"?: ActivityDefinitionProperty;
|
||||
"serverUrl"?: string;
|
||||
}
|
||||
interface ElsaCheckboxProperty {
|
||||
"propertyDescriptor"?: ActivityPropertyDescriptor;
|
||||
|
|
@ -505,6 +546,7 @@ declare namespace LocalJSX {
|
|||
interface ElsaDropdownProperty {
|
||||
"propertyDescriptor"?: ActivityPropertyDescriptor;
|
||||
"propertyModel"?: ActivityDefinitionProperty;
|
||||
"serverUrl"?: string;
|
||||
}
|
||||
interface ElsaExpressionEditor {
|
||||
"context"?: string;
|
||||
|
|
@ -524,6 +566,14 @@ declare namespace LocalJSX {
|
|||
"placeHolder"?: string;
|
||||
"values"?: Array<string>;
|
||||
}
|
||||
interface ElsaInputTagsDropdown {
|
||||
"dropdownValues"?: Array<SelectListItem>;
|
||||
"fieldId"?: string;
|
||||
"fieldName"?: string;
|
||||
"onValueChanged"?: (event: CustomEvent<Array<string | SelectListItem>>) => void;
|
||||
"placeHolder"?: string;
|
||||
"values"?: Array<string | SelectListItem>;
|
||||
}
|
||||
interface ElsaModalDialog {
|
||||
}
|
||||
interface ElsaMonaco {
|
||||
|
|
@ -554,6 +604,7 @@ declare namespace LocalJSX {
|
|||
interface ElsaMultiTextProperty {
|
||||
"propertyDescriptor"?: ActivityPropertyDescriptor;
|
||||
"propertyModel"?: ActivityDefinitionProperty;
|
||||
"serverUrl"?: string;
|
||||
}
|
||||
interface ElsaPager {
|
||||
"history"?: RouterHistory;
|
||||
|
|
@ -589,6 +640,10 @@ declare namespace LocalJSX {
|
|||
"monacoLibPath"?: string;
|
||||
"serverUrl"?: string;
|
||||
}
|
||||
interface ElsaStudioWorkflowBlueprintView {
|
||||
"match"?: MatchResults;
|
||||
"serverUrl"?: string;
|
||||
}
|
||||
interface ElsaStudioWorkflowDefinitionsEdit {
|
||||
"match"?: MatchResults;
|
||||
"monacoLibPath"?: string;
|
||||
|
|
@ -616,6 +671,10 @@ declare namespace LocalJSX {
|
|||
}
|
||||
interface ElsaToastNotification {
|
||||
}
|
||||
interface ElsaWorkflowBlueprintViewerScreen {
|
||||
"serverUrl"?: string;
|
||||
"workflowDefinitionId"?: string;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorNotifications {
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorScreen {
|
||||
|
|
@ -671,6 +730,7 @@ declare namespace LocalJSX {
|
|||
"elsa-dropdown-property": ElsaDropdownProperty;
|
||||
"elsa-expression-editor": ElsaExpressionEditor;
|
||||
"elsa-input-tags": ElsaInputTags;
|
||||
"elsa-input-tags-dropdown": ElsaInputTagsDropdown;
|
||||
"elsa-modal-dialog": ElsaModalDialog;
|
||||
"elsa-monaco": ElsaMonaco;
|
||||
"elsa-multi-expression-editor": ElsaMultiExpressionEditor;
|
||||
|
|
@ -681,6 +741,7 @@ declare namespace LocalJSX {
|
|||
"elsa-script-property": ElsaScriptProperty;
|
||||
"elsa-single-line-property": ElsaSingleLineProperty;
|
||||
"elsa-studio-root": ElsaStudioRoot;
|
||||
"elsa-studio-workflow-blueprint-view": ElsaStudioWorkflowBlueprintView;
|
||||
"elsa-studio-workflow-definitions-edit": ElsaStudioWorkflowDefinitionsEdit;
|
||||
"elsa-studio-workflow-definitions-list": ElsaStudioWorkflowDefinitionsList;
|
||||
"elsa-studio-workflow-instances-list": ElsaStudioWorkflowInstancesList;
|
||||
|
|
@ -688,6 +749,7 @@ declare namespace LocalJSX {
|
|||
"elsa-studio-workflow-registry": ElsaStudioWorkflowRegistry;
|
||||
"elsa-switch-cases-property": ElsaSwitchCasesProperty;
|
||||
"elsa-toast-notification": ElsaToastNotification;
|
||||
"elsa-workflow-blueprint-viewer-screen": ElsaWorkflowBlueprintViewerScreen;
|
||||
"elsa-workflow-definition-editor-notifications": ElsaWorkflowDefinitionEditorNotifications;
|
||||
"elsa-workflow-definition-editor-screen": ElsaWorkflowDefinitionEditorScreen;
|
||||
"elsa-workflow-definitions-list-screen": ElsaWorkflowDefinitionsListScreen;
|
||||
|
|
@ -714,6 +776,7 @@ declare module "@stencil/core" {
|
|||
"elsa-dropdown-property": LocalJSX.ElsaDropdownProperty & JSXBase.HTMLAttributes<HTMLElsaDropdownPropertyElement>;
|
||||
"elsa-expression-editor": LocalJSX.ElsaExpressionEditor & JSXBase.HTMLAttributes<HTMLElsaExpressionEditorElement>;
|
||||
"elsa-input-tags": LocalJSX.ElsaInputTags & JSXBase.HTMLAttributes<HTMLElsaInputTagsElement>;
|
||||
"elsa-input-tags-dropdown": LocalJSX.ElsaInputTagsDropdown & JSXBase.HTMLAttributes<HTMLElsaInputTagsDropdownElement>;
|
||||
"elsa-modal-dialog": LocalJSX.ElsaModalDialog & JSXBase.HTMLAttributes<HTMLElsaModalDialogElement>;
|
||||
"elsa-monaco": LocalJSX.ElsaMonaco & JSXBase.HTMLAttributes<HTMLElsaMonacoElement>;
|
||||
"elsa-multi-expression-editor": LocalJSX.ElsaMultiExpressionEditor & JSXBase.HTMLAttributes<HTMLElsaMultiExpressionEditorElement>;
|
||||
|
|
@ -724,6 +787,7 @@ declare module "@stencil/core" {
|
|||
"elsa-script-property": LocalJSX.ElsaScriptProperty & JSXBase.HTMLAttributes<HTMLElsaScriptPropertyElement>;
|
||||
"elsa-single-line-property": LocalJSX.ElsaSingleLineProperty & JSXBase.HTMLAttributes<HTMLElsaSingleLinePropertyElement>;
|
||||
"elsa-studio-root": LocalJSX.ElsaStudioRoot & JSXBase.HTMLAttributes<HTMLElsaStudioRootElement>;
|
||||
"elsa-studio-workflow-blueprint-view": LocalJSX.ElsaStudioWorkflowBlueprintView & JSXBase.HTMLAttributes<HTMLElsaStudioWorkflowBlueprintViewElement>;
|
||||
"elsa-studio-workflow-definitions-edit": LocalJSX.ElsaStudioWorkflowDefinitionsEdit & JSXBase.HTMLAttributes<HTMLElsaStudioWorkflowDefinitionsEditElement>;
|
||||
"elsa-studio-workflow-definitions-list": LocalJSX.ElsaStudioWorkflowDefinitionsList & JSXBase.HTMLAttributes<HTMLElsaStudioWorkflowDefinitionsListElement>;
|
||||
"elsa-studio-workflow-instances-list": LocalJSX.ElsaStudioWorkflowInstancesList & JSXBase.HTMLAttributes<HTMLElsaStudioWorkflowInstancesListElement>;
|
||||
|
|
@ -731,6 +795,7 @@ declare module "@stencil/core" {
|
|||
"elsa-studio-workflow-registry": LocalJSX.ElsaStudioWorkflowRegistry & JSXBase.HTMLAttributes<HTMLElsaStudioWorkflowRegistryElement>;
|
||||
"elsa-switch-cases-property": LocalJSX.ElsaSwitchCasesProperty & JSXBase.HTMLAttributes<HTMLElsaSwitchCasesPropertyElement>;
|
||||
"elsa-toast-notification": LocalJSX.ElsaToastNotification & JSXBase.HTMLAttributes<HTMLElsaToastNotificationElement>;
|
||||
"elsa-workflow-blueprint-viewer-screen": LocalJSX.ElsaWorkflowBlueprintViewerScreen & JSXBase.HTMLAttributes<HTMLElsaWorkflowBlueprintViewerScreenElement>;
|
||||
"elsa-workflow-definition-editor-notifications": LocalJSX.ElsaWorkflowDefinitionEditorNotifications & JSXBase.HTMLAttributes<HTMLElsaWorkflowDefinitionEditorNotificationsElement>;
|
||||
"elsa-workflow-definition-editor-screen": LocalJSX.ElsaWorkflowDefinitionEditorScreen & JSXBase.HTMLAttributes<HTMLElsaWorkflowDefinitionEditorScreenElement>;
|
||||
"elsa-workflow-definitions-list-screen": LocalJSX.ElsaWorkflowDefinitionsListScreen & JSXBase.HTMLAttributes<HTMLElsaWorkflowDefinitionsListScreenElement>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
import { Component, h, Prop, Event, EventEmitter, State, Watch } from '@stencil/core';
|
||||
import { SelectListItem } from '../../../models';
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-input-tags-dropdown',
|
||||
styleUrl: 'elsa-input-tags-dropdown.css',
|
||||
shadow: false,
|
||||
})
|
||||
export class ElsaInputTagsDropdown {
|
||||
|
||||
@Prop() fieldName?: string;
|
||||
@Prop() fieldId?: string;
|
||||
@Prop() placeHolder?: string = 'Add tag';
|
||||
@Prop() values?: Array<string | SelectListItem> = [];
|
||||
@Prop() dropdownValues?: Array<SelectListItem> = [];
|
||||
@Event({ bubbles: true }) valueChanged: EventEmitter<Array<string | SelectListItem>>;
|
||||
@State() currentValues?: Array<SelectListItem> = [];
|
||||
@State() dropdownTags?: Array<SelectListItem> = [];
|
||||
|
||||
@Watch('values')
|
||||
valuesChangedHandler(newValue: Array<string | SelectListItem>) {
|
||||
let values: Array<SelectListItem> = [];
|
||||
|
||||
newValue.forEach(value => {
|
||||
this.dropdownValues.forEach(tag => {
|
||||
if (value === tag.value) {
|
||||
values.push(tag);
|
||||
}
|
||||
})
|
||||
})
|
||||
this.currentValues = values || [];
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.dropdownTags = this.dropdownValues;
|
||||
let values: Array<SelectListItem> = [];
|
||||
|
||||
this.values.forEach(value => {
|
||||
this.dropdownValues.forEach(tag => {
|
||||
if (value === tag.value) {
|
||||
values.push(tag);
|
||||
}
|
||||
})
|
||||
})
|
||||
this.currentValues = values;
|
||||
|
||||
const tagsToRemove: Array<string> = this.currentValues.map(tag => tag.value);
|
||||
this.dropdownTags = this.dropdownTags.filter(tag => !tagsToRemove.includes(tag.value));
|
||||
}
|
||||
|
||||
async onTagSelected(e: any) {
|
||||
e.preventDefault();
|
||||
|
||||
const input = e.target as HTMLSelectElement;
|
||||
const currentTag: SelectListItem = {
|
||||
text: input.options[input.selectedIndex].text.trim(),
|
||||
value: input.value
|
||||
}
|
||||
|
||||
if (currentTag.value.length == 0)
|
||||
return;
|
||||
|
||||
const values: Array<SelectListItem> = [...this.currentValues];
|
||||
values.push(currentTag);
|
||||
this.currentValues = values.distinct();
|
||||
input.value = "Add";
|
||||
this.dropdownTags = this.dropdownTags.filter(tag => tag.value !== currentTag.value);
|
||||
await this.valueChanged.emit(values);
|
||||
}
|
||||
|
||||
async onDeleteTagClick(e: any, currentTag: SelectListItem) {
|
||||
e.preventDefault();
|
||||
|
||||
this.currentValues = this.currentValues.filter(tag => tag.value !== currentTag.value);
|
||||
this.dropdownTags = [...this.dropdownTags, currentTag].sort((tag1, tag2) => tag1.value.localeCompare(tag2.value));
|
||||
await this.valueChanged.emit(this.currentValues);
|
||||
}
|
||||
|
||||
render() {
|
||||
let values: Array<SelectListItem> = this.currentValues || [];
|
||||
|
||||
if (!Array.isArray(values))
|
||||
values = [];
|
||||
|
||||
const valuesJson = JSON.stringify(values.map(tag => tag.value));
|
||||
|
||||
return (
|
||||
<div class="py-2 px-3 bg-white shadow-sm border border-gray-300 rounded-md">
|
||||
{values.map(tag =>
|
||||
(
|
||||
<a href="#" onClick={e => this.onDeleteTagClick(e, tag)} class="inline-block text-xs bg-blue-400 text-white py-2 px-3 mr-1 mb-1 rounded">
|
||||
<input type="hidden" value={tag.value} />
|
||||
<span>{tag.text}</span>
|
||||
<span class="text-white hover:text-white ml-1">×</span>
|
||||
</a>
|
||||
))}
|
||||
|
||||
<select id={this.fieldId} class="inline-block text-xs bg-green-400 text-white py-2 px-3 mr-1 mb-1 pr-8 rounded" onChange={(e) => this.onTagSelected(e)}>
|
||||
<option value="Add" disabled selected>{this.placeHolder}</option>
|
||||
{this.dropdownTags.map(tag =>
|
||||
(
|
||||
<option value={tag.value}>
|
||||
<span>{tag.text}</span>
|
||||
</option>
|
||||
)
|
||||
)}
|
||||
</select>
|
||||
<input type="hidden" name={this.fieldName} value={valuesJson} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ export class ElsaStudioRoot {
|
|||
<stencil-route-switch scrollTopOffset={0}>
|
||||
<stencil-route url="/" routeRender={props => <stencil-route-redirect url="/workflow-registry"/>} exact={true}/>
|
||||
<stencil-route url="/workflow-registry" component="elsa-studio-workflow-registry" componentProps={{'serverUrl': serverUrl}} exact={true}/>
|
||||
<stencil-route url="/workflow-registry/:id" component="elsa-studio-workflow-blueprint-view" componentProps={{'serverUrl': serverUrl}} />
|
||||
<stencil-route url="/workflow-definitions" component="elsa-studio-workflow-definitions-list" componentProps={{'serverUrl': serverUrl}} exact={true}/>
|
||||
<stencil-route url="/workflow-definitions/:id" component="elsa-studio-workflow-definitions-edit" componentProps={{'serverUrl': serverUrl, 'monacoLibPath': monacoLibPath}}/>
|
||||
<stencil-route url="/workflow-instances" component="elsa-studio-workflow-instances-list" componentProps={{'serverUrl': serverUrl}} exact={true}/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import {Component, Prop, h} from '@stencil/core';
|
||||
import {MatchResults} from '@stencil/router';
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-studio-workflow-blueprint-view',
|
||||
shadow: false,
|
||||
})
|
||||
export class ElsaStudioWorkflowBlueprintView {
|
||||
@Prop() match: MatchResults;
|
||||
@Prop() serverUrl: string;
|
||||
|
||||
id?: string;
|
||||
|
||||
componentWillLoad() {
|
||||
this.id = this.match.params.id;
|
||||
}
|
||||
|
||||
render() {
|
||||
const id = this.id;
|
||||
|
||||
return <div>
|
||||
<elsa-workflow-blueprint-viewer-screen server-url={this.serverUrl} workflowDefinitionId={id}/>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import {Component, Host, h, Prop, State, Event, EventEmitter, Watch} from '@stencil/core';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import {v4 as uuid} from 'uuid';
|
||||
import {Map, addConnection, findActivity, getChildActivities, getInboundConnections, getOutboundConnections, removeActivity, removeConnection} from '../../../../utils/utils';
|
||||
import {
|
||||
ActivityDescriptor,
|
||||
|
|
@ -94,7 +94,9 @@ export class ElsaWorkflowDesigner {
|
|||
updateWorkflowModel(model: WorkflowModel) {
|
||||
this.workflowModel = model;
|
||||
this.workflowChanged.emit(model);
|
||||
this.rerenderTree();
|
||||
setTimeout(() => {
|
||||
this.rerenderTree();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
|
|
@ -339,13 +341,12 @@ export class ElsaWorkflowDesigner {
|
|||
|
||||
this.workflowModel.connections.forEach(({sourceId, targetId, outcome}) => {
|
||||
const sourceName = `${sourceId}/${outcome}`;
|
||||
|
||||
if(!this.graph.hasNode(sourceName))
|
||||
{
|
||||
|
||||
if (!this.graph.hasNode(sourceName)) {
|
||||
console.warn(`No source node with ID '${sourceName}' exists.`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.graph.setEdge(sourceName, targetId, {arrowhead: 'undirected'});
|
||||
});
|
||||
}
|
||||
|
|
@ -373,7 +374,9 @@ export class ElsaWorkflowDesigner {
|
|||
if (this.editMode) {
|
||||
d3.selectAll('.node.add').each((n: any) => {
|
||||
const node = this.graph.node(n) as any;
|
||||
d3.select(node.elem).on('click', e => {
|
||||
|
||||
d3.select(node.elem)
|
||||
.on('click', e => {
|
||||
e.preventDefault();
|
||||
d3.selectAll('.node.add svg').classed('text-green-400', false).classed('text-gray-400', true).classed('hover:text-blue-500', true);
|
||||
this.parentActivityId = node.activity.activityId;
|
||||
|
|
@ -385,6 +388,14 @@ export class ElsaWorkflowDesigner {
|
|||
}
|
||||
|
||||
this.showActivityPicker();
|
||||
})
|
||||
.on("mouseover", e => {
|
||||
if (this.editMode && e.shiftKey)
|
||||
d3.select(node.elem).select('svg').classed('text-green-400', true).classed('hover:text-blue-500', false);
|
||||
})
|
||||
.on("mouseout", e => {
|
||||
if (this.editMode)
|
||||
d3.select(node.elem).select('svg').classed('text-green-400', false).classed('hover:text-blue-500', true);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -515,27 +526,19 @@ export class ElsaWorkflowDesigner {
|
|||
${contextButton}
|
||||
</div>
|
||||
</div>
|
||||
${this.renderActivityBody(activity.description)}
|
||||
${this.renderActivityBody(displayContext)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderActivityBody(description: string | null) {
|
||||
if (!description) return '';
|
||||
renderActivityBody(displayContext: ActivityDesignDisplayContext) {
|
||||
return (
|
||||
`<div class="p-6 text-gray-400 text-sm border-t border-t-solid">
|
||||
${description}
|
||||
<div>${displayContext.activityModel.activityId}</div>
|
||||
<div>${displayContext.bodyDisplay}</div>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
// private onActivitySelected(e: CustomEvent<ActivityModel>) {
|
||||
// this.activitySelected.emit(e.detail);
|
||||
// }
|
||||
|
||||
// private onActivityDeselected(e: CustomEvent<ActivityModel>) {
|
||||
// this.activityDeselected.emit(e.detail);
|
||||
// }
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Host class="workflow-canvas flex-1 flex" ref={el => (this.el = el)}>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import {Component, h, Prop, State} from '@stencil/core';
|
||||
import {ActivityDefinitionProperty, ActivityPropertyDescriptor, SyntaxNames} from "../../../../models";
|
||||
import {parseJson} from "../../../../utils/utils";
|
||||
import {getSelectListItems} from "../../../../utils/select-list-items";
|
||||
import Tunnel from "../../../../data/workflow-editor";
|
||||
import {ElsaDropdownProperty} from "../elsa-dropdown-property/elsa-dropdown-property";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-check-list-property',
|
||||
|
|
@ -11,8 +14,11 @@ export class ElsaCheckListProperty {
|
|||
|
||||
@Prop() propertyDescriptor: ActivityPropertyDescriptor;
|
||||
@Prop() propertyModel: ActivityDefinitionProperty;
|
||||
@Prop({mutable: true}) serverUrl: string;
|
||||
@State() currentValue?: string;
|
||||
|
||||
monacoEditor: HTMLElsaMonacoElement;
|
||||
items: any[];
|
||||
|
||||
async componentWillLoad() {
|
||||
this.currentValue = this.propertyModel.expressions[SyntaxNames.Json] || '[]';
|
||||
|
|
@ -37,11 +43,15 @@ export class ElsaCheckListProperty {
|
|||
this.currentValue = e.detail;
|
||||
}
|
||||
|
||||
async componentWillRender(){
|
||||
this.items = await getSelectListItems(this.serverUrl, this.propertyDescriptor);
|
||||
}
|
||||
|
||||
render() {
|
||||
const propertyDescriptor = this.propertyDescriptor;
|
||||
const propertyModel = this.propertyModel;
|
||||
const fieldId = propertyDescriptor.name;
|
||||
const options = propertyDescriptor.options as Array<any>;
|
||||
const items = this.items;
|
||||
const values = parseJson(this.currentValue) || [];
|
||||
|
||||
return (
|
||||
|
|
@ -51,11 +61,11 @@ export class ElsaCheckListProperty {
|
|||
editor-height="2.75em"
|
||||
single-line={true}>
|
||||
<div class="max-w-lg space-y-3 my-4">
|
||||
{options.map((option, index) => {
|
||||
{items.map((item, index) => {
|
||||
const inputId = `${fieldId}_${index}`;
|
||||
const optionIsString = typeof(option) == 'string';
|
||||
const value = optionIsString ? option : option.value;
|
||||
const text = optionIsString ? option : option.text;
|
||||
const optionIsString = typeof(item) == 'string';
|
||||
const value = optionIsString ? item : item.value;
|
||||
const text = optionIsString ? item : item.text;
|
||||
const isSelected = values.findIndex(x => x == value) >= 0;
|
||||
|
||||
return (
|
||||
|
|
@ -74,3 +84,5 @@ export class ElsaCheckListProperty {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
Tunnel.injectProps(ElsaCheckListProperty, ['serverUrl']);
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
import {Component, h, Prop, State} from '@stencil/core';
|
||||
import {ActivityDefinitionProperty, ActivityPropertyDescriptor, SyntaxNames} from "../../../../models";
|
||||
import {ActivityDefinitionProperty, ActivityPropertyDescriptor, RuntimeSelectListItemsProviderSettings, SelectListItem, SyntaxNames} from "../../../../models";
|
||||
import Tunnel from "../../../../data/workflow-editor";
|
||||
import {createElsaClient} from "../../../../services/elsa-client";
|
||||
import {getSelectListItems} from "../../../../utils/select-list-items";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-dropdown-property',
|
||||
|
|
@ -10,11 +13,15 @@ export class ElsaDropdownProperty {
|
|||
|
||||
@Prop() propertyDescriptor: ActivityPropertyDescriptor;
|
||||
@Prop() propertyModel: ActivityDefinitionProperty;
|
||||
@Prop({mutable: true}) serverUrl: string;
|
||||
@State() currentValue?: string;
|
||||
|
||||
items: any[];
|
||||
|
||||
async componentWillLoad() {
|
||||
const defaultSyntax = this.propertyDescriptor.defaultSyntax || SyntaxNames.Literal;
|
||||
this.currentValue = this.propertyModel.expressions[defaultSyntax] || '';
|
||||
this.items = await getSelectListItems(this.serverUrl, this.propertyDescriptor);
|
||||
}
|
||||
|
||||
onChange(e: Event) {
|
||||
|
|
@ -26,15 +33,15 @@ export class ElsaDropdownProperty {
|
|||
onDefaultSyntaxValueChanged(e: CustomEvent) {
|
||||
this.currentValue = e.detail;
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const propertyDescriptor = this.propertyDescriptor;
|
||||
const propertyModel = this.propertyModel;
|
||||
const propertyName = propertyDescriptor.name;
|
||||
const fieldId = propertyName;
|
||||
const fieldName = propertyName;
|
||||
const options = propertyDescriptor.options as Array<any> || [];
|
||||
const currentValue = this.currentValue;
|
||||
const items = this.items;
|
||||
|
||||
return (
|
||||
<elsa-property-editor propertyDescriptor={propertyDescriptor}
|
||||
|
|
@ -43,10 +50,10 @@ export class ElsaDropdownProperty {
|
|||
editor-height="2.75em"
|
||||
single-line={true}>
|
||||
<select id={fieldId} name={fieldName} onChange={e => this.onChange(e)} class="mt-1 block focus:ring-blue-500 focus:border-blue-500 w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-md">
|
||||
{options.map(option => {
|
||||
const optionIsObject = typeof (option) == 'object';
|
||||
const value = optionIsObject ? option.value : option.toString();
|
||||
const text = optionIsObject ? option.text : option.toString();
|
||||
{items.map(item => {
|
||||
const optionIsObject = typeof (item) == 'object';
|
||||
const value = optionIsObject ? item.value : item.toString();
|
||||
const text = optionIsObject ? item.text : item.toString();
|
||||
return <option value={value} selected={value === currentValue}>{text}</option>;
|
||||
})}
|
||||
</select>
|
||||
|
|
@ -54,3 +61,5 @@ export class ElsaDropdownProperty {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
Tunnel.injectProps(ElsaDropdownProperty, ['serverUrl']);
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import {Component, h, Prop, State} from '@stencil/core';
|
||||
import {ActivityDefinitionProperty, ActivityPropertyDescriptor, SyntaxNames} from "../../../../models";
|
||||
import {ActivityDefinitionProperty, ActivityPropertyDescriptor, SyntaxNames, SelectListItem} from "../../../../models";
|
||||
import {parseJson} from "../../../../utils/utils";
|
||||
import Tunnel from "../../../../data/workflow-editor";
|
||||
import {getSelectListItems} from "../../../../utils/select-list-items";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-multi-text-property',
|
||||
|
|
@ -11,14 +13,25 @@ export class ElsaMultiTextProperty {
|
|||
|
||||
@Prop() propertyDescriptor: ActivityPropertyDescriptor;
|
||||
@Prop() propertyModel: ActivityDefinitionProperty;
|
||||
@State() currentValue?: string
|
||||
@Prop({mutable: true}) serverUrl: string;
|
||||
@State() currentValue?: string;
|
||||
|
||||
items: any[];
|
||||
|
||||
async componentWillLoad() {
|
||||
this.currentValue = this.propertyModel.expressions[SyntaxNames.Json] || '[]';
|
||||
}
|
||||
|
||||
onValueChanged(newValue: Array<string>){
|
||||
this.currentValue = JSON.stringify(newValue);
|
||||
onValueChanged(newValue: Array<string | number | boolean | SelectListItem>) {
|
||||
const newValues = newValue.map(dropdown => {
|
||||
if (typeof dropdown === 'string') return dropdown;
|
||||
if (typeof dropdown === 'number') return dropdown.toString();
|
||||
if (typeof dropdown === 'boolean') return dropdown.toString();
|
||||
|
||||
return dropdown.value;
|
||||
})
|
||||
|
||||
this.currentValue = JSON.stringify(newValues);
|
||||
this.propertyModel.expressions[SyntaxNames.Json] = this.currentValue;
|
||||
}
|
||||
|
||||
|
|
@ -26,6 +39,17 @@ export class ElsaMultiTextProperty {
|
|||
this.currentValue = e.detail;
|
||||
}
|
||||
|
||||
createKeyValueOptions(options: Array<SelectListItem>) {
|
||||
if (options === null)
|
||||
return options;
|
||||
|
||||
return options.map(option => typeof option === 'string' ? { text: option, value: option } : option);
|
||||
}
|
||||
|
||||
async componentWillRender(){
|
||||
this.items = await getSelectListItems(this.serverUrl, this.propertyDescriptor);
|
||||
}
|
||||
|
||||
render() {
|
||||
const propertyDescriptor = this.propertyDescriptor;
|
||||
const propertyModel = this.propertyModel;
|
||||
|
|
@ -33,15 +57,24 @@ export class ElsaMultiTextProperty {
|
|||
const fieldId = propertyName;
|
||||
const fieldName = propertyName;
|
||||
const values = parseJson(this.currentValue);
|
||||
|
||||
const items = this.items;
|
||||
const valueType = propertyDescriptor.options !== null ? 'dropdown' : 'multi-text';
|
||||
const propertyOptions = this.createKeyValueOptions(items);
|
||||
|
||||
const elsaInputTags = valueType === 'multi-text' ?
|
||||
<elsa-input-tags values={values} fieldId={fieldId} fieldName={fieldName} onValueChanged={e => this.onValueChanged(e.detail)} /> :
|
||||
<elsa-input-tags-dropdown dropdownValues={propertyOptions} values={values} fieldId={fieldId} fieldName={fieldName} onValueChanged={e => this.onValueChanged(e.detail)} />;
|
||||
|
||||
return (
|
||||
<elsa-property-editor propertyDescriptor={propertyDescriptor}
|
||||
propertyModel={propertyModel}
|
||||
onDefaultSyntaxValueChanged={e => this.onDefaultSyntaxValueChanged(e)}
|
||||
editor-height="2em"
|
||||
single-line={true}>
|
||||
<elsa-input-tags values={values} fieldId={fieldId} fieldName={fieldName} onValueChanged={e => this.onValueChanged(e.detail)}/>
|
||||
{elsaInputTags}
|
||||
</elsa-property-editor>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Tunnel.injectProps(ElsaMultiTextProperty, ['serverUrl']);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
import {Component, h, Host, Method, Prop, State, Watch} from '@stencil/core';
|
||||
import * as collection from 'lodash/collection';
|
||||
import {
|
||||
ActivityBlueprint, ActivityDefinitionProperty,
|
||||
ActivityDescriptor,
|
||||
ActivityModel, Connection,
|
||||
ConnectionModel,
|
||||
SyntaxNames,
|
||||
WorkflowBlueprint, WorkflowModel,
|
||||
WorkflowPersistenceBehavior
|
||||
} from "../../../../models";
|
||||
import {createElsaClient} from "../../../../services/elsa-client";
|
||||
import {pluginManager} from '../../../../services/plugin-manager';
|
||||
import state from '../../../../utils/store';
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-workflow-blueprint-viewer-screen',
|
||||
shadow: false,
|
||||
})
|
||||
export class ElsaWorkflowBlueprintViewerScreen {
|
||||
|
||||
constructor() {
|
||||
pluginManager.initialize();
|
||||
}
|
||||
|
||||
@Prop() workflowDefinitionId: string;
|
||||
@Prop({attribute: 'server-url', reflect: true}) serverUrl: string;
|
||||
@State() workflowBlueprint: WorkflowBlueprint;
|
||||
@State() workflowModel: WorkflowModel;
|
||||
el: HTMLElement;
|
||||
designer: HTMLElsaDesignerTreeElement;
|
||||
|
||||
@Method()
|
||||
async getServerUrl(): Promise<string> {
|
||||
return this.serverUrl;
|
||||
}
|
||||
|
||||
@Watch('workflowDefinitionId')
|
||||
async workflowDefinitionIdChangedHandler(newValue: string) {
|
||||
const workflowDefinitionId = newValue;
|
||||
let workflowBlueprint: WorkflowBlueprint = {
|
||||
id: null,
|
||||
version: 1,
|
||||
activities: [],
|
||||
connections: [],
|
||||
persistenceBehavior: WorkflowPersistenceBehavior.WorkflowBurst,
|
||||
customAttributes: {data: {}},
|
||||
persistWorkflow: false,
|
||||
isLatest: false,
|
||||
isPublished: false,
|
||||
loadWorkflowContext: false,
|
||||
isSingleton: false,
|
||||
persistOutput: false,
|
||||
saveWorkflowContext: false,
|
||||
variables: {data: {}},
|
||||
type: null,
|
||||
properties: {data: {}}
|
||||
};
|
||||
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
|
||||
if (workflowDefinitionId && workflowDefinitionId.length > 0) {
|
||||
try {
|
||||
workflowBlueprint = await client.workflowRegistryApi.get(workflowDefinitionId, {isLatest: true});
|
||||
} catch {
|
||||
console.warn(`The specified workflow blueprint does not exist. Creating a new one.`);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateModels(workflowBlueprint);
|
||||
}
|
||||
|
||||
@Watch("serverUrl")
|
||||
async serverUrlChangedHandler(newValue: string) {
|
||||
if (newValue && newValue.length > 0)
|
||||
await this.loadActivityDescriptors();
|
||||
}
|
||||
|
||||
async componentWillLoad() {
|
||||
await this.serverUrlChangedHandler(this.serverUrl);
|
||||
await this.workflowDefinitionIdChangedHandler(this.workflowDefinitionId);
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
if (!this.designer) {
|
||||
this.designer = this.el.querySelector("elsa-designer-tree") as HTMLElsaDesignerTreeElement;
|
||||
this.designer.model = this.workflowModel;
|
||||
}
|
||||
}
|
||||
|
||||
async loadActivityDescriptors() {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
state.activityDescriptors = await client.activitiesApi.list();
|
||||
}
|
||||
|
||||
updateModels(workflowBlueprint: WorkflowBlueprint) {
|
||||
this.workflowBlueprint = workflowBlueprint;
|
||||
this.workflowModel = this.mapWorkflowModel(workflowBlueprint);
|
||||
debugger;
|
||||
}
|
||||
|
||||
mapWorkflowModel(workflowBlueprint: WorkflowBlueprint): WorkflowModel {
|
||||
return {
|
||||
activities: workflowBlueprint.activities.filter(x => x.parentId == null).map(this.mapActivityModel),
|
||||
connections: workflowBlueprint.connections.map(this.mapConnectionModel),
|
||||
persistenceBehavior: workflowBlueprint.persistenceBehavior,
|
||||
};
|
||||
}
|
||||
|
||||
mapActivityModel(source: ActivityBlueprint): ActivityModel {
|
||||
const descriptors: Array<ActivityDescriptor> = state.activityDescriptors;
|
||||
const descriptor = descriptors.find(x => x.type == source.type);
|
||||
const properties: Array<ActivityDefinitionProperty> = collection.map(source.properties.data, (v, k) => ({name: k, expressions: {'Literal': v}, syntax: SyntaxNames.Literal}));
|
||||
|
||||
return {
|
||||
activityId: source.id,
|
||||
description: source.description,
|
||||
displayName: source.displayName || source.name || source.type,
|
||||
name: source.name,
|
||||
type: source.type,
|
||||
properties: properties,
|
||||
outcomes: [...descriptor.outcomes],
|
||||
persistOutput: source.persistOutput,
|
||||
persistWorkflow: source.persistWorkflow,
|
||||
saveWorkflowContext: source.saveWorkflowContext,
|
||||
loadWorkflowContext: source.loadWorkflowContext
|
||||
}
|
||||
}
|
||||
|
||||
mapConnectionModel(connection: Connection): ConnectionModel {
|
||||
return {
|
||||
sourceId: connection.sourceActivityId,
|
||||
targetId: connection.targetActivityId,
|
||||
outcome: connection.outcome
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Host class="flex flex-col w-full relative" ref={el => this.el = el}>
|
||||
{this.renderCanvas()}
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
|
||||
renderCanvas() {
|
||||
return (
|
||||
<div class="flex-1 flex">
|
||||
<elsa-designer-tree model={this.workflowModel} class="flex-1" ref={el => this.designer = el} editMode={false}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# elsa-workflow-instance-viewer-screen
|
||||
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| -------------------- | ---------------------- | ----------- | -------- | ----------- |
|
||||
| `serverUrl` | `server-url` | | `string` | `undefined` |
|
||||
| `workflowInstanceId` | `workflow-instance-id` | | `string` | `undefined` |
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
### `getServerUrl() => Promise<string>`
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
Type: `Promise<string>`
|
||||
|
||||
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Used by
|
||||
|
||||
- [elsa-studio-workflow-instances-view](../../../dashboard/pages/elsa-studio-workflow-instances-view)
|
||||
|
||||
### Depends on
|
||||
|
||||
- [elsa-workflow-instance-journal](.)
|
||||
- [elsa-designer-tree](../../../designers/tree/elsa-designer-tree)
|
||||
|
||||
### Graph
|
||||
```mermaid
|
||||
graph TD;
|
||||
elsa-workflow-instance-viewer-screen --> elsa-workflow-instance-journal
|
||||
elsa-workflow-instance-viewer-screen --> elsa-designer-tree
|
||||
elsa-studio-workflow-instances-view --> elsa-workflow-instance-viewer-screen
|
||||
style elsa-workflow-instance-viewer-screen fill:#f9f,stroke:#333,stroke-width:4px
|
||||
```
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
*Built with [StencilJS](https://stenciljs.com/)*
|
||||
|
|
@ -19,13 +19,18 @@ export class ElsaActivityEditorModal {
|
|||
form: HTMLFormElement;
|
||||
formContext: FormContext;
|
||||
renderProps: any;
|
||||
|
||||
// Force a new key every time we show the editor to make sure Stencil creates new components.
|
||||
// This prevents the issue where the designer has e.g. one activity where the user edits the properties, cancels out, then opens the editor again, seeing the entered value still there.
|
||||
timestamp: Date = new Date();
|
||||
|
||||
componentDidLoad() {
|
||||
eventBus.on(EventTypes.ShowActivityEditor, async (activity: ActivityModel, animate: boolean) => {
|
||||
this.activityModel = JSON.parse(JSON.stringify(activity));
|
||||
this.activityDescriptor = state.activityDescriptors.find(x => x.type == activity.type);
|
||||
this.formContext = new FormContext(this.activityModel, newValue => this.activityModel = newValue);
|
||||
this.selectedTab = 'Properties';
|
||||
this.selectedTab = 'Properties';
|
||||
this.timestamp = new Date();
|
||||
await this.dialog.show(animate);
|
||||
});
|
||||
}
|
||||
|
|
@ -99,9 +104,9 @@ export class ElsaActivityEditorModal {
|
|||
|
||||
return (
|
||||
<Host>
|
||||
<elsa-modal-dialog ref={el => this.dialog = el}>
|
||||
<elsa-modal-dialog ref={el => this.dialog = el} >
|
||||
<div slot="content" class="py-8 pb-0">
|
||||
<form onSubmit={e => this.onSubmit(e)} ref={el => this.form = el}>
|
||||
<form onSubmit={e => this.onSubmit(e)} ref={el => this.form = el} key={this.timestamp.getTime().toString()}>
|
||||
<div class="flex px-8">
|
||||
<div class="space-y-8 divide-y divide-gray-200 w-full">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -62,9 +62,6 @@ export class ElsaWorkflowDefinitionEditorScreen {
|
|||
this.imported = false;
|
||||
this.networkError = null;
|
||||
|
||||
// A small hack to make sure JS Plumb is cleaned up before HTML elements get removed.
|
||||
// await this.designer.destroyJsPlumb();
|
||||
|
||||
try {
|
||||
const workflowDefinition = await client.workflowDefinitionsApi.import(this.workflowDefinition.definitionId, file);
|
||||
this.workflowDefinition = workflowDefinition;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import {Component, EventEmitter, h, Host, Method, Prop, State, Watch, Event} fro
|
|||
import * as collection from 'lodash/collection';
|
||||
import moment from 'moment';
|
||||
import {enter, leave, toggle} from 'el-transition'
|
||||
import {registerClickOutside} from "stencil-click-outside";
|
||||
import {
|
||||
ActivityBlueprint,
|
||||
ActivityDescriptor, PagedList, WorkflowBlueprint, WorkflowExecutionLogRecord, WorkflowModel,
|
||||
|
|
@ -71,8 +70,7 @@ export class ElsaWorkflowInstanceJournal {
|
|||
@Method()
|
||||
async selectActivityRecord(activityId?: string) {
|
||||
const record = !!activityId ? this.records.items.find(x => x.activityId == activityId) : null;
|
||||
this.selectedRecordId = !!record ? record.id : null;
|
||||
this.selectedActivityId = activityId;
|
||||
this.selectActivityRecordInternal(record);
|
||||
await this.show();
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +92,12 @@ export class ElsaWorkflowInstanceJournal {
|
|||
await this.workflowInstanceIdChangedHandler(this.workflowInstanceId);
|
||||
}
|
||||
|
||||
selectActivityRecordInternal(record?: WorkflowExecutionLogRecord) {
|
||||
const activity = !!record ? this.workflowBlueprint.activities.find(x => x.id === record.activityId) : null;
|
||||
this.selectedRecordId = !!record ? record.id : null;
|
||||
this.selectedActivityId = activity != null ? activity.parentId != null ? activity.parentId : activity.id : null;
|
||||
}
|
||||
|
||||
getEventColor(eventName: string) {
|
||||
const map = {
|
||||
'Executing': 'blue',
|
||||
|
|
@ -115,8 +119,7 @@ export class ElsaWorkflowInstanceJournal {
|
|||
}
|
||||
|
||||
onRecordClick(record: WorkflowExecutionLogRecord) {
|
||||
this.selectedRecordId = record.id;
|
||||
this.selectedActivityId = record.activityId;
|
||||
this.selectActivityRecordInternal(record);
|
||||
this.recordSelected.emit(record);
|
||||
}
|
||||
|
||||
|
|
@ -246,7 +249,7 @@ export class ElsaWorkflowInstanceJournal {
|
|||
|
||||
if (!value)
|
||||
continue;
|
||||
|
||||
|
||||
let valueText = null;
|
||||
|
||||
if (typeof value == 'string')
|
||||
|
|
@ -121,7 +121,7 @@ export class ElsaWorkflowInstanceViewerScreen {
|
|||
|
||||
mapWorkflowModel(workflowBlueprint: WorkflowBlueprint): WorkflowModel {
|
||||
return {
|
||||
activities: workflowBlueprint.activities.map(this.mapActivityModel),
|
||||
activities: workflowBlueprint.activities.filter(x => x.parentId == null).map(this.mapActivityModel),
|
||||
connections: workflowBlueprint.connections.map(this.mapConnectionModel),
|
||||
persistenceBehavior: workflowBlueprint.persistenceBehavior,
|
||||
};
|
||||
|
|
@ -160,7 +160,9 @@ export class ElsaWorkflowInstanceViewerScreen {
|
|||
}
|
||||
|
||||
onRecordSelected(e: CustomEvent<WorkflowExecutionLogRecord>) {
|
||||
this.selectedActivityId = e.detail.activityId;
|
||||
const record = e.detail;
|
||||
const activity = !!record ? this.workflowBlueprint.activities.find(x => x.id === record.activityId) : null;
|
||||
this.selectedActivityId = activity != null ? activity.parentId != null ? activity.parentId : activity.id : null;
|
||||
}
|
||||
|
||||
onActivitySelected(e: CustomEvent<ActivityModel>) {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export class ElsaWorkflowRegistryListScreen {
|
|||
if (!workflowDisplayName || workflowDisplayName.trim().length == 0)
|
||||
workflowDisplayName = 'Untitled';
|
||||
|
||||
const editUrl = `/workflow-definitions/${workflowBlueprint.id}`;
|
||||
const editUrl = `/workflow-registry/${workflowBlueprint.id}`;
|
||||
const instancesUrl = `/workflow-instances?workflow=${workflowBlueprint.id}`;
|
||||
|
||||
const editIcon = (
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
<body class="h-screen" style="background-size: 30px 30px; background-image: url(/build/assets/images/tile.png); background-color: #FBFBFB;">
|
||||
|
||||
<elsa-studio-root server-url="https://localhost:11000/" monaco-lib-path="build/assets/js"></elsa-studio-root>
|
||||
<!--<elsa-studio-root server-url="https://localhost:6080/" monaco-lib-path="build/assets/js"></elsa-studio-root>-->
|
||||
<!--<elsa-studio-root server-url="https://skynet-workflow.azurewebsites.net" monaco-lib-path="build/assets/js"></elsa-studio-root>-->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ export interface ActivityBlueprint {
|
|||
displayName?: string;
|
||||
description?: string;
|
||||
type: string;
|
||||
parentId?: string;
|
||||
persistWorkflow: boolean;
|
||||
loadWorkflowContext: boolean;
|
||||
saveWorkflowContext: boolean;
|
||||
|
|
@ -286,6 +287,16 @@ export enum ActivityTraits {
|
|||
Job = 4
|
||||
}
|
||||
|
||||
export interface SelectListItem {
|
||||
text: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface RuntimeSelectListItemsProviderSettings {
|
||||
runtimeSelectListItemsProviderType: string;
|
||||
context?: any;
|
||||
}
|
||||
|
||||
export class SyntaxNames {
|
||||
static readonly Literal = 'Literal';
|
||||
static readonly JavaScript = 'JavaScript';
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export interface ActivityPickedEventArgs {
|
|||
export interface ActivityDesignDisplayContext {
|
||||
activityModel: ActivityModel;
|
||||
activityIcon: any;
|
||||
bodyDisplay: any;
|
||||
bodyDisplay: string;
|
||||
outcomes: Array<string>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ export class CronPlugin implements ElsaPlugin {
|
|||
const props = activityModel.properties || [];
|
||||
const condition = props.find(x => x.name == 'CronExpression') || { name: 'CronExpression', expressions: {'Literal': ''}, syntax: 'Literal'};
|
||||
const expression = condition.expressions[condition.syntax] || '';
|
||||
context.bodyDisplay = <p>{expression}</p>;
|
||||
context.bodyDisplay = `<p>${expression}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {ElsaPlugin} from "../services/elsa-plugin";
|
||||
import {eventBus} from '../services/event-bus';
|
||||
import {ActivityDesignDisplayContext, EventTypes} from "../models";
|
||||
import {ActivityDesignDisplayContext, EventTypes, SyntaxNames} from "../models";
|
||||
import {h} from "@stencil/core";
|
||||
|
||||
export class HttpEndpointPlugin implements ElsaPlugin {
|
||||
|
|
@ -15,7 +15,8 @@ export class HttpEndpointPlugin implements ElsaPlugin {
|
|||
return;
|
||||
|
||||
const props = activityModel.properties || [];
|
||||
const path = props.find(x => x.name == 'Path') || { name: 'Path', expressions: { 'Literal': '', syntax: 'Literal' } };
|
||||
context.bodyDisplay = <p>{path.expressions[path.syntax]}</p>;
|
||||
const path = props.find(x => x.name == 'Path') || { name: 'Path', expressions: { 'Literal': '', syntax: SyntaxNames.Literal } };
|
||||
const syntax = path.syntax || SyntaxNames.Literal;
|
||||
context.bodyDisplay = `<p>${path.expressions[syntax]}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ export class IfPlugin implements ElsaPlugin {
|
|||
const expression = condition.expressions[condition.syntax] || '';
|
||||
const description = activityModel.description;
|
||||
const bodyText = description && description.length > 0 ? description : expression;
|
||||
context.bodyDisplay = <p>{bodyText}</p>;
|
||||
context.bodyDisplay = `<p>${bodyText}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ export class SendEmailPlugin implements ElsaPlugin {
|
|||
const expression = to.expressions[to.syntax || SyntaxNames.Json] || '';
|
||||
const description = activityModel.description;
|
||||
const bodyText = description && description.length > 0 ? description : expression;
|
||||
context.bodyDisplay = <p>To: {bodyText}</p>;
|
||||
context.bodyDisplay = `<p>To: ${bodyText}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ export class StartAtPlugin implements ElsaPlugin {
|
|||
const props = activityModel.properties || [];
|
||||
const condition = props.find(x => x.name == 'Instant') || { name: 'Instant', expressions: {'Literal': ''}, syntax: 'Literal'};
|
||||
const expression = condition.expressions[condition.syntax] || '';
|
||||
context.bodyDisplay = <p>{expression}</p>;
|
||||
context.bodyDisplay = `<p>${expression}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ export class TimerPlugin implements ElsaPlugin {
|
|||
const props = activityModel.properties || [];
|
||||
const condition = props.find(x => x.name == 'Timeout') || { name: 'Timeout', expressions: {'Literal': ''}, syntax: 'Literal'};
|
||||
const expression = condition.expressions[condition.syntax] || '';
|
||||
context.bodyDisplay = <p>{expression}</p>;
|
||||
context.bodyDisplay = `<p>${expression}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ export class WhilePlugin implements ElsaPlugin {
|
|||
const expression = condition.expressions[condition.syntax] || '';
|
||||
const description = activityModel.description;
|
||||
const bodyText = description && description.length > 0 ? description : expression;
|
||||
context.bodyDisplay = <p>{bodyText}</p>;
|
||||
context.bodyDisplay = `<p>${bodyText}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ export class WriteLinePlugin implements ElsaPlugin {
|
|||
const props = activityModel.properties || [];
|
||||
const condition = props.find(x => x.name == 'Text') || { name: 'Text', expressions: {'Literal': ''}, syntax: SyntaxNames.Literal };
|
||||
const expression = condition.expressions[condition.syntax || 'Literal'] || '';
|
||||
context.bodyDisplay = <p>{expression}</p>;
|
||||
context.bodyDisplay = `<p>${expression}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
ActivityDescriptor,
|
||||
ConnectionDefinition,
|
||||
getVersionOptionsString, OrderBy,
|
||||
PagedList,
|
||||
PagedList, SelectListItem,
|
||||
Variables,
|
||||
VersionOptions, WorkflowBlueprint, WorkflowBlueprintSummary,
|
||||
WorkflowContextOptions,
|
||||
|
|
@ -152,6 +152,14 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
|
|||
const response = await httpClient.get<string>(`v1/scripting/javascript/type-definitions/${workflowDefinitionId}?t=${new Date().getTime()}&context=${context}`);
|
||||
return response.data;
|
||||
}
|
||||
},
|
||||
designerApi:{
|
||||
runtimeSelectItemsApi:{
|
||||
get: async (providerTypeName: string, context?: any): Promise<Array<SelectListItem>> => {
|
||||
const response = await httpClient.post('v1/designer/runtime-select-list-items', { providerTypeName: providerTypeName, context: context });
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,6 +171,7 @@ export interface ElsaClient {
|
|||
workflowInstancesApi: WorkflowInstancesApi;
|
||||
workflowExecutionLogApi: WorkflowExecutionLogApi;
|
||||
scriptingApi: ScriptingApi;
|
||||
designerApi: DesignerApi;
|
||||
}
|
||||
|
||||
export interface ActivitiesApi {
|
||||
|
|
@ -220,6 +229,14 @@ export interface ScriptingApi {
|
|||
getJavaScriptTypeDefinitions(workflowDefinitionId: string, context?: string): Promise<string>
|
||||
}
|
||||
|
||||
export interface DesignerApi {
|
||||
runtimeSelectItemsApi: RuntimeSelectItemsApi;
|
||||
}
|
||||
|
||||
export interface RuntimeSelectItemsApi {
|
||||
get(providerTypeName: string, context?: any): Promise<Array<SelectListItem>>
|
||||
}
|
||||
|
||||
export interface SaveWorkflowDefinitionRequest {
|
||||
workflowDefinitionId?: string;
|
||||
name?: string;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
import {ActivityPropertyDescriptor, RuntimeSelectListItemsProviderSettings, SelectListItem} from "../models";
|
||||
import {createElsaClient, ElsaClient} from "../services/elsa-client";
|
||||
|
||||
async function fetchRuntimeItems(serverUrl: string, options: RuntimeSelectListItemsProviderSettings): Promise<Array<SelectListItem>> {
|
||||
const elsaClient = createElsaClient(serverUrl);
|
||||
return await elsaClient.designerApi.runtimeSelectItemsApi.get(options.runtimeSelectListItemsProviderType, options.context || {});
|
||||
}
|
||||
|
||||
export async function getSelectListItems(serverUrl: string, propertyDescriptor: ActivityPropertyDescriptor): Promise<Array<SelectListItem>> {
|
||||
const options = propertyDescriptor.options;
|
||||
let items = [];
|
||||
|
||||
if (!!options.runtimeSelectListItemsProviderType) {
|
||||
items = await fetchRuntimeItems(serverUrl, options);
|
||||
} else {
|
||||
items = options as Array<any> || [];
|
||||
}
|
||||
|
||||
return items || [];
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Elsa.Persistence.EntityFramework.SqlServer
|
||||
{
|
||||
public static class DbContextOptionsBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the context to use SqlServer.
|
||||
/// </summary>
|
||||
public static DbContextOptionsBuilder UseSqlServer(this DbContextOptionsBuilder builder, string connectionString) =>
|
||||
builder.UseSqlServer(connectionString, db => db.MigrationsAssembly(typeof(SqlServerElsaContextFactory).Assembly.GetName().Name));
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ namespace Elsa.Persistence.YesSql.Documents
|
|||
|
||||
public WorkflowFault? Fault { get; set; }
|
||||
public SimpleStack<ScheduledActivity> ScheduledActivities { get; set; } = new();
|
||||
public SimpleStack<string> Scopes { get; set; } = new();
|
||||
public SimpleStack<ActivityScope> Scopes { get; set; } = new();
|
||||
public ScheduledActivity? CurrentActivity { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Http\Elsa.Activities.Http.csproj" />
|
||||
<ProjectReference Include="..\..\..\core\Elsa\Elsa.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
26
src/samples/aspnet/Elsa.Samples.SendHttp/Program.cs
Normal file
26
src/samples/aspnet/Elsa.Samples.SendHttp/Program.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Samples.SendHttp
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:8929",
|
||||
"sslPort": 44312
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Elsa.Samples.SendHttp": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": "true",
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/samples/aspnet/Elsa.Samples.SendHttp/SendHttpWorkflow.cs
Normal file
29
src/samples/aspnet/Elsa.Samples.SendHttp/SendHttpWorkflow.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Elsa.Activities.Http;
|
||||
using Elsa.Activities.Http.Models;
|
||||
using Elsa.Builders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Samples.SendHttp
|
||||
{
|
||||
public class SendHttpWorkflow : IWorkflow
|
||||
{
|
||||
public void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithWorkflowDefinitionId("SendHttp")
|
||||
.WithVersion(1)
|
||||
.WithDescription("Send test http request and return response")
|
||||
.HttpEndpoint(setup => setup.WithPath("/helloworld")
|
||||
.WithMethod("GET"))
|
||||
.SendHttpRequest(setup => setup.WithUrl(new Uri("https://jsonplaceholder.typicode.com/todos/3"))
|
||||
.WithMethod("GET")
|
||||
.WithReadContent(true))
|
||||
.WithName("TestHttpRequest")
|
||||
.WriteHttpResponse(setup => setup.WithStatusCode(System.Net.HttpStatusCode.OK)
|
||||
.WithContent(x => JsonSerializer.Serialize(x.GetOutputFrom<HttpResponseModel>("TestHttpRequest").Content)));
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/samples/aspnet/Elsa.Samples.SendHttp/Startup.cs
Normal file
29
src/samples/aspnet/Elsa.Samples.SendHttp/Startup.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Samples.SendHttp
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddElsa(options => options.AddHttpActivities()
|
||||
.AddWorkflowsFrom<Startup>());
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseHttpActivities();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/samples/aspnet/Elsa.Samples.SendHttp/appsettings.json
Normal file
10
src/samples/aspnet/Elsa.Samples.SendHttp/appsettings.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using Elsa.Persistence.Specifications.WorkflowInstances;
|
|||
using Elsa.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Elsa.Persistence.EntityFramework.SqlServer;
|
||||
|
||||
namespace Elsa.Samples.Persistence.EntityFramework
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
using Elsa.Activities.Console;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services;
|
||||
|
||||
namespace Elsa.Samples.Server.Host.Activities
|
||||
{
|
||||
public class SampleCompositeActivity : CompositeActivity
|
||||
{
|
||||
public override void Build(ICompositeActivityBuilder builder)
|
||||
{
|
||||
builder.WriteLine("Line 1");
|
||||
builder.WriteLine("Line 2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Design;
|
||||
using Elsa.Expressions;
|
||||
using Elsa.Metadata;
|
||||
using Elsa.Services;
|
||||
|
||||
namespace Elsa.Samples.Server.Host.Activities
|
||||
{
|
||||
[Action]
|
||||
public class VehicleActivity : Activity, IActivityPropertyOptionsProvider, IRuntimeSelectListItemsProvider
|
||||
{
|
||||
private Random _random;
|
||||
|
||||
public VehicleActivity()
|
||||
{
|
||||
_random = new Random();
|
||||
}
|
||||
|
||||
[ActivityProperty(
|
||||
UIHint = ActivityPropertyUIHints.Dropdown,
|
||||
OptionsProvider = typeof(VehicleActivity),
|
||||
DefaultSyntax = SyntaxNames.Literal,
|
||||
SupportedSyntaxes = new[] { SyntaxNames.Literal, SyntaxNames.Json, SyntaxNames.JavaScript, SyntaxNames.Liquid }
|
||||
)]
|
||||
public string? Brand { get; set; }
|
||||
|
||||
public object GetOptions(PropertyInfo property) => new RuntimeSelectListItemsProviderSettings(GetType(), new VehicleContext(_random.Next(100)));
|
||||
|
||||
public ValueTask<IEnumerable<SelectListItem>> GetItemsAsync(object? context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var vehicleContext = (VehicleContext) context!;
|
||||
var brands = new[] { "BMW", "Peugot", "Tesla", vehicleContext.RandomNumber.ToString() };
|
||||
var items = brands.Select(x => new SelectListItem(x)).ToList();
|
||||
return new ValueTask<IEnumerable<SelectListItem>>(items);
|
||||
}
|
||||
|
||||
protected override IActivityExecutionResult OnExecute() => Done(Brand);
|
||||
}
|
||||
|
||||
public record VehicleContext(int RandomNumber);
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using Elsa.Activities.UserTask.Extensions;
|
|||
using Elsa.Persistence.EntityFramework.Core.Extensions;
|
||||
using Elsa.Persistence.EntityFramework.Sqlite;
|
||||
using Elsa.Persistence.YesSql;
|
||||
using Elsa.Samples.Server.Host.Activities;
|
||||
using Elsa.Server.Hangfire.Extensions;
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
|
@ -48,6 +49,8 @@ namespace Elsa.Samples.Server.Host
|
|||
});
|
||||
|
||||
services
|
||||
.AddActivityPropertyOptionsProvider<VehicleActivity>()
|
||||
.AddRuntimeSelectItemsProvider<VehicleActivity>()
|
||||
.AddElsa(elsa => elsa
|
||||
//.UseEntityFrameworkPersistence(ef => ef.UseSqlite())
|
||||
.UseYesSqlPersistence()
|
||||
|
|
@ -62,9 +65,11 @@ namespace Elsa.Samples.Server.Host
|
|||
.AddJavaScriptActivities()
|
||||
.AddUserTaskActivities()
|
||||
.AddTelnyx()
|
||||
.AddActivitiesFrom<VehicleActivity>()
|
||||
.AddWorkflowsFrom<Startup>()
|
||||
);
|
||||
|
||||
// Elsa API endpoints.
|
||||
services
|
||||
.AddElsaApiEndpoints()
|
||||
.AddElsaSwagger();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Design;
|
||||
using Elsa.Serialization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
|
||||
namespace Elsa.Server.Api.Endpoints.Designer.RuntimeSelectListItems
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1")]
|
||||
[Route("v{apiVersion:apiVersion}/designer/runtime-select-list-items")]
|
||||
[Produces("application/json")]
|
||||
public class Get : Controller
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IContentSerializer _contentSerializer;
|
||||
|
||||
public Get(IServiceProvider serviceProvider, IContentSerializer contentSerializer)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_contentSerializer = contentSerializer;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OkObjectResult))]
|
||||
[SwaggerOperation(
|
||||
Summary = "Returns a list of items to be used by the list control requesting the items.",
|
||||
Description = "Returns a list of items to be used by the list control requesting the items.",
|
||||
OperationId = "Designer.RuntimeSelectListItems.Get",
|
||||
Tags = new[] { "Designer.RuntimeSelectListItems" })
|
||||
]
|
||||
public async Task<IActionResult> Handle([FromBody]RuntimeSelectListItemsContextHolder model, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var type = Type.GetType(model.ProviderTypeName)!;
|
||||
var provider = (IRuntimeSelectListItemsProvider) ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, type);
|
||||
var context = model?.Context;
|
||||
var items = (await provider.GetItemsAsync(context, cancellationToken)).ToList();
|
||||
var serializerSettings = _contentSerializer.GetSettings();
|
||||
|
||||
return Json(items, serializerSettings);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonObject(ItemTypeNameHandling = TypeNameHandling.All)]
|
||||
public record RuntimeSelectListItemsContextHolder
|
||||
{
|
||||
public string ProviderTypeName { get; set; } = default!;
|
||||
|
||||
public object? Context { get; set; }
|
||||
};
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ namespace Elsa.Server.Api.Endpoints.WorkflowRegistry
|
|||
public string? DisplayName { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string Type { get; set; } = default!;
|
||||
public string? ParentId { get; set; }
|
||||
public bool PersistWorkflow { get; set; }
|
||||
public bool LoadWorkflowContext { get; set; }
|
||||
public bool SaveWorkflowContext { get; set; }
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ namespace Elsa.Server.Api.Mapping
|
|||
DisplayName = source.DisplayName,
|
||||
Description = source.Description,
|
||||
Type = source.Type,
|
||||
ParentId = source.Parent?.Id,
|
||||
PersistWorkflow = source.PersistWorkflow,
|
||||
LoadWorkflowContext = source.LoadWorkflowContext,
|
||||
SaveWorkflowContext = source.SaveWorkflowContext,
|
||||
|
|
|
|||
Loading…
Reference in a new issue