Reverting wrong commits
This commit is contained in:
parent
e22ecf83a0
commit
9ab15d3d15
15
src/apps/Elsa.Studio.Web/appsettings.json
Normal file
15
src/apps/Elsa.Studio.Web/appsettings.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
},
|
||||
"ElsaServer": {
|
||||
"Url": "https://localhost:5001/elsa/api"
|
||||
},
|
||||
"Hosting": {
|
||||
"BasePath": ""
|
||||
}
|
||||
}
|
||||
|
|
@ -6,34 +6,32 @@ using Elsa.Expressions.JavaScript.TypeDefinitions.Models;
|
|||
using Elsa.Workflows.Management.Options;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Elsa.Expressions.JavaScript.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Produces <see cref="TypeDefinition"/>s for variable types.
|
||||
/// </summary>
|
||||
internal class VariableTypeDefinitionProvider(ITypeDescriber typeDescriber) : TypeDefinitionProvider
|
||||
[UsedImplicitly]
|
||||
internal class VariableTypeDefinitionProvider(ITypeDescriber typeDescriber, IOptions<ManagementOptions> options) : TypeDefinitionProvider
|
||||
{
|
||||
protected override IEnumerable<TypeDefinition> GetTypeDefinitions(TypeDefinitionContext context)
|
||||
{
|
||||
var excludedTypes = new Func<Type, bool>[]
|
||||
{
|
||||
type => type == typeof(ExpandoObject),
|
||||
type => typeof(IDictionary<string, object>).IsAssignableFrom(type),
|
||||
type => type == typeof(object)
|
||||
type => type.IsPrimitive,
|
||||
type => type.ContainsGenericParameters,
|
||||
type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>),
|
||||
type => type == typeof(object),
|
||||
type => type == typeof(string)
|
||||
};
|
||||
|
||||
var variables = context.WorkflowGraph.Workflow.Variables;
|
||||
|
||||
var variableTypeQuery =
|
||||
from variable in variables
|
||||
let variableType = variable.GetVariableType()
|
||||
where (variableType.IsClass || variableType.IsInterface || variableType.IsEnum) && !variableType.IsPrimitive && !excludedTypes.Any(x => x(variableType))
|
||||
var variableTypes =
|
||||
from variableDescriptor in options.Value.VariableDescriptors
|
||||
let variableType = variableDescriptor.Type
|
||||
where (variableType.IsClass || variableType.IsInterface || variableType.IsEnum) && !excludedTypes.Any(x => x(variableType))
|
||||
select variableType;
|
||||
|
||||
var variableTypes = variableTypeQuery.Distinct();
|
||||
|
||||
foreach (var variableType in variableTypes)
|
||||
foreach (var variableType in variableTypes.Distinct())
|
||||
{
|
||||
yield return typeDescriber.DescribeType(variableType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using System.Text;
|
||||
using Elsa.Expressions.JavaScript.TypeDefinitions.Contracts;
|
||||
using Elsa.Expressions.JavaScript.TypeDefinitions.Models;
|
||||
|
||||
namespace Elsa.Expressions.JavaScript.TypeDefinitions.Services;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
|
||||
{
|
||||
|
|
@ -11,19 +9,14 @@ public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
|
|||
public string Render(TypeDefinitionsDocument document)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
foreach (var functionDefinition in document.Functions)
|
||||
Render(functionDefinition, stringBuilder);
|
||||
|
||||
foreach (var typeDefinition in document.Types)
|
||||
Render(typeDefinition, stringBuilder);
|
||||
|
||||
foreach (var variableDefinition in document.Variables)
|
||||
Render(variableDefinition, stringBuilder);
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
private void Render(FunctionDefinition functionDefinition, StringBuilder output)
|
||||
{
|
||||
var returnType = functionDefinition.ReturnType != null ? $": {functionDefinition.ReturnType}" : "";
|
||||
|
|
@ -35,11 +28,9 @@ public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
|
|||
var returnType = functionDefinition.ReturnType != null ? $" => {functionDefinition.ReturnType}" : "";
|
||||
output.AppendLine($"{functionDefinition.Name}: ({RenderParameters(functionDefinition.Parameters)}){returnType};");
|
||||
}
|
||||
|
||||
private void Render(TypeDefinition typeDefinition, StringBuilder output)
|
||||
{
|
||||
output.AppendLine($"declare {typeDefinition.DeclarationKeyword} {typeDefinition.Name} {{");
|
||||
|
||||
if (typeDefinition.DeclarationKeyword == "enum")
|
||||
{
|
||||
foreach (var property in typeDefinition.Properties)
|
||||
|
|
@ -50,15 +41,13 @@ public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
|
|||
foreach (var property in typeDefinition.Properties)
|
||||
Render(property, output);
|
||||
}
|
||||
|
||||
foreach (var method in typeDefinition.Methods)
|
||||
RenderMethod(method, output);
|
||||
|
||||
output.AppendLine("}");
|
||||
}
|
||||
|
||||
private void Render(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name}{(property.IsOptional ? "?" : "")}: {property.Type};");
|
||||
private void RenderEnumMember(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name} = \"{property.Name}\";");
|
||||
private void RenderEnumMember(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name} = \"{property.Name}\",");
|
||||
private void Render(VariableDefinition variable, StringBuilder output) => output.AppendLine($"declare var {variable.Name}: {variable.Type};");
|
||||
string RenderParameter(ParameterDefinition parameter) => $"{parameter.Name}{(parameter.IsOptional ? "?" : "")}: {parameter.Type}";
|
||||
string RenderParameters(IEnumerable<ParameterDefinition> parameters) => string.Join(", ", parameters.Select(RenderParameter));
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Expressions.JavaScript.Contracts;
|
||||
using Elsa.Expressions.JavaScript.TypeDefinitions.Contracts;
|
||||
using Elsa.Expressions.JavaScript.TypeDefinitions.Models;
|
||||
|
||||
namespace Elsa.Expressions.JavaScript.TypeDefinitions.Services;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TypeDescriber : ITypeDescriber
|
||||
{
|
||||
private readonly ITypeAliasRegistry _typeAliasRegistry;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
|
|
@ -30,17 +28,18 @@ public class TypeDescriber : ITypeDescriber
|
|||
Properties = GetPropertyDefinitions(type).DistinctBy(x => x.Name).ToList(),
|
||||
Methods = GetMethodDefinitions(type).DistinctBy(x => x.Name).ToList()
|
||||
};
|
||||
|
||||
return typeDefinition;
|
||||
}
|
||||
|
||||
private IEnumerable<FunctionDefinition> GetMethodDefinitions(Type type)
|
||||
{
|
||||
if(type.IsEnum)
|
||||
yield break;
|
||||
|
||||
|
||||
#pragma warning disable IL2070
|
||||
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static).Where(x => !x.IsSpecialName).ToList();
|
||||
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
|
||||
.Where(x => !x.IsSpecialName)
|
||||
.Where(x => x.GetCustomAttribute<CompilerGeneratedAttribute>() == null)
|
||||
.ToList();
|
||||
#pragma warning restore IL2070
|
||||
|
||||
foreach (var method in methods)
|
||||
|
|
@ -53,11 +52,9 @@ public class TypeDescriber : ITypeDescriber
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<ParameterDefinition> GetMethodParameters(MethodInfo method)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
yield return new ParameterDefinition
|
||||
|
|
@ -68,7 +65,6 @@ public class TypeDescriber : ITypeDescriber
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<PropertyDefinition> GetPropertyDefinitions([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type)
|
||||
{
|
||||
// If the type is an enum, enumerate its members.
|
||||
|
|
@ -83,12 +79,10 @@ public class TypeDescriber : ITypeDescriber
|
|||
IsOptional = false,
|
||||
};
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
var properties = type.GetProperties();
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
yield return new PropertyDefinition
|
||||
|
|
@ -99,7 +93,6 @@ public class TypeDescriber : ITypeDescriber
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetDeclarationKeyword(Type type) =>
|
||||
type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Attributes;
|
||||
|
||||
namespace Elsa.Http;
|
||||
|
||||
/// <summary>
|
||||
/// Send an HTTP request.
|
||||
/// </summary>
|
||||
|
|
@ -14,7 +12,6 @@ public class SendHttpRequest : SendHttpRequestBase
|
|||
public SendHttpRequest([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of expected status codes to handle and the corresponding activity to execute when the status code matches.
|
||||
/// </summary>
|
||||
|
|
@ -23,21 +20,21 @@ public class SendHttpRequest : SendHttpRequestBase
|
|||
UIHint = "http-status-codes"
|
||||
)]
|
||||
public ICollection<HttpStatusCodeCase> ExpectedStatusCodes { get; set; } = new List<HttpStatusCodeCase>();
|
||||
|
||||
/// <summary>
|
||||
/// The activity to execute when the HTTP status code does not match any of the expected status codes.
|
||||
/// </summary>
|
||||
[Port]
|
||||
public IActivity? UnmatchedStatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The activity to execute when the HTTP request fails to connect.
|
||||
/// </summary>
|
||||
[Port]
|
||||
public IActivity? FailedToConnect { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The activity to execute when the HTTP request times out.
|
||||
/// </summary>
|
||||
[Port]
|
||||
public IActivity? Timeout { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -47,22 +44,18 @@ public class SendHttpRequest : SendHttpRequestBase
|
|||
var statusCode = (int)response.StatusCode;
|
||||
var matchingCase = expectedStatusCodes.FirstOrDefault(x => x.StatusCode == statusCode);
|
||||
var activity = matchingCase != null ? matchingCase.Activity : UnmatchedStatusCode;
|
||||
|
||||
await context.ScheduleActivityAsync(activity, OnChildActivityCompletedAsync);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask HandleRequestExceptionAsync(ActivityExecutionContext context, HttpRequestException exception)
|
||||
{
|
||||
await context.ScheduleActivityAsync(FailedToConnect, OnChildActivityCompletedAsync);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask HandleTaskCanceledExceptionAsync(ActivityExecutionContext context, TaskCanceledException exception)
|
||||
{
|
||||
await context.ScheduleActivityAsync(Timeout, OnChildActivityCompletedAsync);
|
||||
}
|
||||
|
||||
private async ValueTask OnChildActivityCompletedAsync(ActivityCompletedContext context)
|
||||
{
|
||||
await context.TargetContext.CompleteActivityAsync();
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ using Microsoft.AspNetCore.StaticFiles;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Elsa.Http.Features;
|
||||
|
||||
/// <summary>
|
||||
/// Installs services related to HTTP services and activities.
|
||||
/// </summary>
|
||||
|
|
@ -38,32 +36,26 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
{
|
||||
private Func<IServiceProvider, IHttpEndpointRoutesProvider> _httpEndpointRouteProvider = sp => sp.GetRequiredService<DefaultHttpEndpointRoutesProvider>();
|
||||
private Func<IServiceProvider, IHttpEndpointBasePathProvider> _httpEndpointBasePathProvider = sp => sp.GetRequiredService<DefaultHttpEndpointBasePathProvider>();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure <see cref="HttpActivityOptions"/>.
|
||||
/// </summary>
|
||||
public Action<HttpActivityOptions>? ConfigureHttpOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure <see cref="HttpFileCacheOptions"/>.
|
||||
/// </summary>
|
||||
public Action<HttpFileCacheOptions>? ConfigureHttpFileCacheOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that is invoked when authorizing an inbound HTTP request.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, IHttpEndpointAuthorizationHandler> HttpEndpointAuthorizationHandler { get; set; } = sp => sp.GetRequiredService<AuthenticationBasedHttpEndpointAuthorizationHandler>();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that is invoked when an HTTP workflow faults.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, IHttpEndpointFaultHandler> HttpEndpointWorkflowFaultHandler { get; set; } = sp => sp.GetRequiredService<DefaultHttpEndpointFaultHandler>();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure the <see cref="IContentTypeProvider"/>.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, IContentTypeProvider> ContentTypeProvider { get; set; } = _ => new FileExtensionContentTypeProvider();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure the <see cref="IFileCacheStorageProvider"/>.
|
||||
/// </summary>
|
||||
|
|
@ -75,7 +67,7 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
};
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure the <see cref="HttpClient"/> used when by the <see cref="FlowSendHttpRequest"/> activity.
|
||||
/// A delegate to configure the <see cref="HttpClient"/> used when by the <see cref="FlowSendHttpRequest"/> and <see cref="SendHttpRequest"/> activities.
|
||||
/// </summary>
|
||||
public Action<IServiceProvider, HttpClient> HttpClient { get; set; } = (_, _) => { };
|
||||
|
||||
|
|
@ -83,7 +75,6 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
/// A delegate to configure the <see cref="HttpClientBuilder"/> for <see cref="HttpClient"/>.
|
||||
/// </summary>
|
||||
public Action<IHttpClientBuilder> HttpClientBuilder { get; set; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// A list of <see cref="IHttpCorrelationIdSelector"/> types to register with the service collection.
|
||||
/// </summary>
|
||||
|
|
@ -92,7 +83,6 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
typeof(HeaderHttpCorrelationIdSelector),
|
||||
typeof(QueryStringHttpCorrelationIdSelector)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A list of <see cref="IHttpWorkflowInstanceIdSelector"/> types to register with the service collection.
|
||||
/// </summary>
|
||||
|
|
@ -101,12 +91,10 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
typeof(HeaderHttpWorkflowInstanceIdSelector),
|
||||
typeof(QueryStringHttpWorkflowInstanceIdSelector)
|
||||
};
|
||||
|
||||
public HttpFeature WithHttpEndpointRoutesProvider<T>() where T : IHttpEndpointRoutesProvider
|
||||
{
|
||||
return WithHttpEndpointRoutesProvider(sp => sp.GetRequiredService<T>());
|
||||
}
|
||||
|
||||
public HttpFeature WithHttpEndpointRoutesProvider(Func<IServiceProvider, IHttpEndpointRoutesProvider> httpEndpointRouteProvider)
|
||||
{
|
||||
_httpEndpointRouteProvider = httpEndpointRouteProvider;
|
||||
|
|
@ -124,7 +112,6 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
_httpEndpointBasePathProvider = httpEndpointBasePathProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Configure()
|
||||
{
|
||||
|
|
@ -140,13 +127,10 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
typeof(HttpFile),
|
||||
typeof(Downloadable)
|
||||
], "HTTP");
|
||||
|
||||
management.AddActivitiesFrom<HttpFeature>();
|
||||
});
|
||||
|
||||
Module.UseResilience(resilience => resilience.AddResilienceStrategyType<HttpResilienceStrategy>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
|
|
@ -155,15 +139,11 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
options.BasePath = "/workflows";
|
||||
options.BaseUrl = new Uri("http://localhost");
|
||||
});
|
||||
|
||||
var configureFileCacheOptions = ConfigureHttpFileCacheOptions ?? (options => { options.TimeToLive = TimeSpan.FromDays(7); });
|
||||
|
||||
Services.Configure(configureOptions);
|
||||
Services.Configure(configureFileCacheOptions);
|
||||
|
||||
var httpClientBuilder = Services.AddHttpClient<SendHttpRequestBase>(HttpClient);
|
||||
HttpClientBuilder(httpClientBuilder);
|
||||
|
||||
Services
|
||||
.AddScoped<IRouteMatcher, RouteMatcher>()
|
||||
.AddScoped<IRouteTable, RouteTable>()
|
||||
|
|
@ -172,31 +152,25 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
.AddScoped<IHttpWorkflowLookupService, HttpWorkflowLookupService>()
|
||||
.AddScoped(ContentTypeProvider)
|
||||
.AddHttpContextAccessor()
|
||||
|
||||
// Handlers.
|
||||
.AddNotificationHandler<UpdateRouteTable>()
|
||||
|
||||
// Content parsers.
|
||||
.AddSingleton<IHttpContentParser, JsonHttpContentParser>()
|
||||
.AddSingleton<IHttpContentParser, XmlHttpContentParser>()
|
||||
.AddSingleton<IHttpContentParser, PlainTextHttpContentParser>()
|
||||
.AddSingleton<IHttpContentParser, TextHtmlHttpContentParser>()
|
||||
.AddSingleton<IHttpContentParser, FileHttpContentParser>()
|
||||
|
||||
// HTTP content factories.
|
||||
.AddScoped<IHttpContentFactory, TextContentFactory>()
|
||||
.AddScoped<IHttpContentFactory, JsonContentFactory>()
|
||||
.AddScoped<IHttpContentFactory, XmlContentFactory>()
|
||||
.AddScoped<IHttpContentFactory, FormUrlEncodedHttpContentFactory>()
|
||||
|
||||
// Activity property options providers.
|
||||
.AddScoped<IPropertyUIHandler, HttpContentTypeOptionsProvider>()
|
||||
.AddScoped<IPropertyUIHandler, HttpEndpointPathUIHandler>()
|
||||
.AddScoped(_httpEndpointBasePathProvider)
|
||||
|
||||
// Port resolvers.
|
||||
.AddScoped<IActivityResolver, SendHttpRequestActivityResolver>()
|
||||
|
||||
// HTTP endpoint handlers.
|
||||
.AddScoped<AuthenticationBasedHttpEndpointAuthorizationHandler>()
|
||||
.AddScoped<AllowAnonymousHttpEndpointAuthorizationHandler>()
|
||||
|
|
@ -209,7 +183,6 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
|
||||
// Startup tasks.
|
||||
.AddStartupTask<UpdateRouteTableStartupTask>()
|
||||
|
||||
// Downloadable content handlers.
|
||||
.AddScoped<IDownloadableManager, DefaultDownloadableManager>()
|
||||
.AddScoped<IDownloadableContentHandler, MultiDownloadableContentHandler>()
|
||||
|
|
@ -220,28 +193,21 @@ public class HttpFeature(IModule module) : FeatureBase(module)
|
|||
.AddScoped<IDownloadableContentHandler, UrlDownloadableContentHandler>()
|
||||
.AddScoped<IDownloadableContentHandler, StringDownloadableContentHandler>()
|
||||
.AddScoped<IDownloadableContentHandler, HttpFileDownloadableContentHandler>()
|
||||
|
||||
//Trigger payload validators.
|
||||
.AddTriggerPayloadValidator<HttpEndpointTriggerPayloadValidator, HttpEndpointBookmarkPayload>()
|
||||
|
||||
// File caches.
|
||||
.AddScoped(FileCache)
|
||||
.AddScoped<ZipManager>()
|
||||
|
||||
// AuthenticationBasedHttpEndpointAuthorizationHandler requires Authorization services.
|
||||
// We could consider creating a separate module for installing authorization services.
|
||||
.AddAuthorization();
|
||||
|
||||
// HTTP clients.
|
||||
Services.AddHttpClient<IFileDownloader, HttpClientFileDownloader>();
|
||||
|
||||
// Add selectors.
|
||||
foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes)
|
||||
Services.AddScoped(typeof(IHttpCorrelationIdSelector), httpCorrelationIdSelectorType);
|
||||
|
||||
foreach (var httpWorkflowInstanceIdSelectorType in HttpWorkflowInstanceIdSelectorTypes)
|
||||
Services.AddScoped(typeof(IHttpWorkflowInstanceIdSelector), httpWorkflowInstanceIdSelectorType);
|
||||
|
||||
Services.Configure<ExpressionOptions>(options =>
|
||||
{
|
||||
options.AddTypeAlias<HttpRequest>("HttpRequest");
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Models;
|
||||
|
||||
namespace Elsa.Http.PortResolvers;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of outbound activities for a given <see cref="SendHttpRequest"/> activity's expected status codes.
|
||||
/// </summary>
|
||||
|
|
@ -10,32 +8,25 @@ public class SendHttpRequestActivityResolver : IActivityResolver
|
|||
{
|
||||
/// <inheritdoc />
|
||||
public int Priority => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool GetSupportsActivity(IActivity activity) => activity is SendHttpRequest;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask<IEnumerable<ActivityPort>> GetActivityPortsAsync(IActivity activity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
IEnumerable<ActivityPort> ports = GetPortsInternal(activity);
|
||||
return new ValueTask<IEnumerable<ActivityPort>>(ports);
|
||||
var ports = GetPortsInternal(activity);
|
||||
return new(ports);
|
||||
}
|
||||
|
||||
private IEnumerable<ActivityPort> GetPortsInternal(IActivity activity)
|
||||
{
|
||||
var sendHttpRequest = (SendHttpRequest)activity;
|
||||
var cases = sendHttpRequest.ExpectedStatusCodes.Where(x => x.Activity != null);
|
||||
|
||||
foreach (var @case in cases)
|
||||
yield return ActivityPort.FromActivity(@case.Activity!, @case.StatusCode.ToString());
|
||||
|
||||
if (sendHttpRequest.Timeout != null)
|
||||
yield return ActivityPort.FromActivity(sendHttpRequest.Timeout, nameof(SendHttpRequest.Timeout));
|
||||
|
||||
if (sendHttpRequest.FailedToConnect != null)
|
||||
yield return ActivityPort.FromActivity(sendHttpRequest.FailedToConnect, nameof(SendHttpRequest.FailedToConnect));
|
||||
|
||||
if (sendHttpRequest.UnmatchedStatusCode != null)
|
||||
yield return ActivityPort.FromActivity(sendHttpRequest.UnmatchedStatusCode, nameof(SendHttpRequest.UnmatchedStatusCode));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,12 @@ using Elsa.Workflows.Runtime;
|
|||
using FastEndpoints;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Workflows.Api.Endpoints.Bookmarks.Resume;
|
||||
|
||||
/// <summary>
|
||||
/// Resumes a bookmarked workflow instance with the bookmark ID specified in the provided SAS token.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
internal class Resume(ITokenService tokenService, IBookmarkQueue bookmarkQueue, IPayloadSerializer serializer) : ElsaEndpoint<Request>
|
||||
internal class Resume(ITokenService tokenService, IWorkflowResumer workflowResumer, IBookmarkQueue bookmarkQueue, IPayloadSerializer serializer) : ElsaEndpoint<Request>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Configure()
|
||||
|
|
@ -20,25 +18,27 @@ internal class Resume(ITokenService tokenService, IBookmarkQueue bookmarkQueue,
|
|||
Verbs(Http.GET, Http.POST);
|
||||
AllowAnonymous();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
|
||||
{
|
||||
var token = Query<string>("t")!;
|
||||
var asynchronous = Query<bool>("async", false);
|
||||
|
||||
if (!tokenService.TryDecryptToken<BookmarkTokenPayload>(token, out var payload))
|
||||
AddError("Invalid token.");
|
||||
|
||||
var input = HttpContext.Request.Method == HttpMethods.Post ? request.Input : GetInputFromQueryString();
|
||||
|
||||
if (ValidationFailed)
|
||||
{
|
||||
await Send.ErrorsAsync(cancellation: cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
await ResumeBookmarkedWorkflowAsync(payload, input, cancellationToken);
|
||||
|
||||
|
||||
// Some clients, like Blazor, may prematurely cancel their request upon navigation away from the page.
|
||||
// In this case, we don't want to cancel the workflow execution.
|
||||
// We need to better understand the conditions that cause this.
|
||||
var workflowCancellationToken = CancellationToken.None;
|
||||
await ResumeBookmarkedWorkflowAsync(payload, input, asynchronous, workflowCancellationToken);
|
||||
|
||||
if (!HttpContext.Response.HasStarted)
|
||||
await Send.OkAsync(cancellationToken);
|
||||
}
|
||||
|
|
@ -48,7 +48,6 @@ internal class Resume(ITokenService tokenService, IBookmarkQueue bookmarkQueue,
|
|||
var inputJson = Query<string?>("in", false);
|
||||
if (string.IsNullOrWhiteSpace(inputJson))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return serializer.Deserialize<IDictionary<string, object>>(inputJson);
|
||||
|
|
@ -59,21 +58,36 @@ internal class Resume(ITokenService tokenService, IBookmarkQueue bookmarkQueue,
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ResumeBookmarkedWorkflowAsync(BookmarkTokenPayload tokenPayload, IDictionary<string, object>? input, CancellationToken cancellationToken)
|
||||
|
||||
private async Task ResumeBookmarkedWorkflowAsync(BookmarkTokenPayload tokenPayload, IDictionary<string, object>? input, bool asynchronous, CancellationToken cancellationToken)
|
||||
{
|
||||
var bookmarkId = tokenPayload.BookmarkId;
|
||||
var workflowInstanceId = tokenPayload.WorkflowInstanceId;
|
||||
var item = new NewBookmarkQueueItem
|
||||
|
||||
if (asynchronous)
|
||||
{
|
||||
var item = new NewBookmarkQueueItem
|
||||
{
|
||||
BookmarkId = bookmarkId,
|
||||
WorkflowInstanceId = workflowInstanceId,
|
||||
Options = new()
|
||||
{
|
||||
Input = input
|
||||
}
|
||||
};
|
||||
|
||||
await bookmarkQueue.EnqueueAsync(item, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
var resumeRequest = new ResumeBookmarkRequest
|
||||
{
|
||||
BookmarkId = bookmarkId,
|
||||
WorkflowInstanceId = workflowInstanceId,
|
||||
Options = new()
|
||||
{
|
||||
Input = input
|
||||
}
|
||||
Input = input
|
||||
};
|
||||
await bookmarkQueue.EnqueueAsync(item, cancellationToken);
|
||||
|
||||
await workflowResumer.ResumeAsync(resumeRequest, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.Workflows.Exceptions;
|
||||
|
||||
public class InputEvaluationException(string inputName, string message, Exception exception) : Exception(message, exception)
|
||||
{
|
||||
public string InputName { get; } = inputName;
|
||||
}
|
||||
|
|
@ -3,11 +3,11 @@ using Elsa.Expressions.Contracts;
|
|||
using Elsa.Expressions.Helpers;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Exceptions;
|
||||
using Elsa.Workflows.Models;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
public static partial class ActivityExecutionContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -17,14 +17,11 @@ public static partial class ActivityExecutionContextExtensions
|
|||
{
|
||||
var activityDescriptor = context.ActivityDescriptor;
|
||||
var inputDescriptors = activityDescriptor.Inputs.Where(x => x.AutoEvaluate).ToList();
|
||||
|
||||
// Evaluate inputs.
|
||||
foreach (var inputDescriptor in inputDescriptors)
|
||||
await EvaluateInputPropertyAsync(context, activityDescriptor, inputDescriptor);
|
||||
|
||||
context.SetHasEvaluatedProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the specified input property of the activity.
|
||||
/// </summary>
|
||||
|
|
@ -34,7 +31,6 @@ public static partial class ActivityExecutionContextExtensions
|
|||
var input = await EvaluateInputPropertyAsync(context, inputName);
|
||||
return input.ConvertTo<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates a specific input property of the activity.
|
||||
/// </summary>
|
||||
|
|
@ -44,13 +40,10 @@ public static partial class ActivityExecutionContextExtensions
|
|||
var activityRegistryLookup = context.GetRequiredService<IActivityRegistryLookupService>();
|
||||
var activityDescriptor = await activityRegistryLookup.FindAsync(activity.Type) ?? throw new Exception("Activity descriptor not found");
|
||||
var inputDescriptor = activityDescriptor.GetWrappedInputPropertyDescriptor(activity, inputName);
|
||||
|
||||
if (inputDescriptor == null)
|
||||
throw new Exception($"No input with name {inputName} could be found");
|
||||
|
||||
return await EvaluateInputPropertyAsync(context, activityDescriptor, inputDescriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the specified input and sets the result in the activity execution context's memory space.
|
||||
/// </summary>
|
||||
|
|
@ -66,19 +59,29 @@ public static partial class ActivityExecutionContextExtensions
|
|||
memoryBlockReference.Set(context, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
private static async Task<object?> EvaluateInputPropertyAsync(this ActivityExecutionContext context, ActivityDescriptor activityDescriptor, InputDescriptor inputDescriptor)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await EvaluateInputPropertyCoreAsync(context, activityDescriptor, inputDescriptor);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InputEvaluationException(inputDescriptor.Name, $"Failed to evaluate activity input '{inputDescriptor.Name}'", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<object?> EvaluateInputPropertyCoreAsync(this ActivityExecutionContext context, ActivityDescriptor activityDescriptor, InputDescriptor inputDescriptor)
|
||||
{
|
||||
var activity = context.Activity;
|
||||
var defaultValue = inputDescriptor.DefaultValue;
|
||||
var value = defaultValue;
|
||||
var input = inputDescriptor.ValueGetter(activity);
|
||||
var identityGenerator = context.GetRequiredService<IIdentityGenerator>();
|
||||
|
||||
if (inputDescriptor.IsWrapped)
|
||||
{
|
||||
var wrappedInput = (Input?)input;
|
||||
|
||||
if (defaultValue != null && wrappedInput == null)
|
||||
{
|
||||
var typedInput = typeof(Input<>).MakeGenericType(inputDescriptor.Type);
|
||||
|
|
@ -94,7 +97,6 @@ public static partial class ActivityExecutionContextExtensions
|
|||
var expressionEvaluator = context.GetRequiredService<IExpressionEvaluator>();
|
||||
var expressionExecutionContext = context.ExpressionExecutionContext;
|
||||
var inputEvaluatorType = inputDescriptor.EvaluatorType ?? typeof(DefaultActivityInputEvaluator);
|
||||
|
||||
if (wrappedInput?.Expression != null)
|
||||
{
|
||||
var inputEvaluator = (IActivityInputEvaluator)context.GetRequiredService(inputEvaluatorType);
|
||||
|
|
@ -102,9 +104,7 @@ public static partial class ActivityExecutionContextExtensions
|
|||
value = await inputEvaluator.EvaluateAsync(inputEvaluatorContext);
|
||||
}
|
||||
}
|
||||
|
||||
var memoryReference = wrappedInput?.MemoryBlockReference();
|
||||
|
||||
if (memoryReference != null)
|
||||
{
|
||||
// When input is created from an activity provider, there may be no memory block reference ID.
|
||||
|
|
@ -119,9 +119,7 @@ public static partial class ActivityExecutionContextExtensions
|
|||
{
|
||||
value = input;
|
||||
}
|
||||
|
||||
await StoreInputValueAsync(context, inputDescriptor, value!);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +136,6 @@ public static partial class ActivityExecutionContextExtensions
|
|||
// var filterResult = await manager.RunFiltersAsync(filterContext);
|
||||
context.ActivityState[inputDescriptor.Name] = value;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue