Merge pull request #6301 from elsa-workflows/bug/6299
Fix array type handling
This commit is contained in:
commit
7b45811ef4
|
|
@ -23,10 +23,10 @@ namespace Elsa.Http;
|
|||
public class HttpEndpoint : Trigger<HttpRequest>
|
||||
{
|
||||
internal const string HttpContextInputKey = "HttpContext";
|
||||
internal const string RequestPathInputKey = "RequestPath";
|
||||
internal const string PathInputKey = "Path";
|
||||
|
||||
/// <inheritdoc />
|
||||
public HttpEndpoint([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line)
|
||||
public HttpEndpoint([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ public class HttpEndpoint : Trigger<HttpRequest>
|
|||
UIHint = InputUIHints.SingleLine,
|
||||
UIHandler = typeof(HttpEndpointPathUIHandler)
|
||||
)]
|
||||
public Input<string> Path { get; set; } = default!;
|
||||
public Input<string> Path { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The HTTP methods to accept.
|
||||
|
|
@ -65,37 +65,37 @@ public class HttpEndpoint : Trigger<HttpRequest>
|
|||
/// The maximum time allowed to process the request.
|
||||
/// </summary>
|
||||
[Input(Description = "The maximum time allowed to process the request.", Category = "Upload")]
|
||||
public Input<TimeSpan?> RequestTimeout { get; set; } = default!;
|
||||
public Input<TimeSpan?> RequestTimeout { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum request size allowed in bytes.
|
||||
/// </summary>
|
||||
[Input(Description = "The maximum request size allowed in bytes.", Category = "Upload")]
|
||||
public Input<long?> RequestSizeLimit { get; set; } = default!;
|
||||
public Input<long?> RequestSizeLimit { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum request size allowed in bytes.
|
||||
/// </summary>
|
||||
[Input(Description = "The maximum file size allowed in bytes for an individual file.", Category = "Upload")]
|
||||
public Input<long?> FileSizeLimit { get; set; } = default!;
|
||||
public Input<long?> FileSizeLimit { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The allowed file extensions,
|
||||
/// </summary>
|
||||
[Input(Description = "Only file extensions in this list are allowed. Leave empty to allow all extensions", Category = "Upload", UIHint = InputUIHints.MultiText)]
|
||||
public Input<ICollection<string>> AllowedFileExtensions { get; set; } = default!;
|
||||
public Input<ICollection<string>> AllowedFileExtensions { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The allowed file extensions,
|
||||
/// </summary>
|
||||
[Input(Description = "File extensions in this list are forbidden. Leave empty to not block any extension.", Category = "Upload", UIHint = InputUIHints.MultiText)]
|
||||
public Input<ICollection<string>> BlockedFileExtensions { get; set; } = default!;
|
||||
public Input<ICollection<string>> BlockedFileExtensions { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The allowed file extensions,
|
||||
/// </summary>
|
||||
[Input(Description = "Only MIME types in this list are allowed. Leave empty to allow all types", Category = "Upload", UIHint = InputUIHints.MultiText)]
|
||||
public Input<ICollection<string>> AllowedMimeTypes { get; set; } = default!;
|
||||
public Input<ICollection<string>> AllowedMimeTypes { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether to expose the "Request too large" outcome.
|
||||
|
|
@ -125,31 +125,31 @@ public class HttpEndpoint : Trigger<HttpRequest>
|
|||
/// The parsed request content, if any.
|
||||
/// </summary>
|
||||
[Output(Description = "The parsed request content, if any.")]
|
||||
public Output<object?> ParsedContent { get; set; } = default!;
|
||||
public Output<object?> ParsedContent { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The uploaded files, if any.
|
||||
/// </summary>
|
||||
[Output(Description = "The uploaded files, if any.", IsSerializable = false)]
|
||||
public Output<IFormFile[]> Files { get; set; } = default!;
|
||||
public Output<IFormFile[]> Files { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The parsed route data, if any.
|
||||
/// </summary>
|
||||
[Output(Description = "The parsed route data, if any.")]
|
||||
public Output<IDictionary<string, object>> RouteData { get; set; } = default!;
|
||||
public Output<IDictionary<string, object>> RouteData { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The querystring data, if any.
|
||||
/// </summary>
|
||||
[Output(Description = "The querystring data, if any.")]
|
||||
public Output<IDictionary<string, object>> QueryStringData { get; set; } = default!;
|
||||
public Output<IDictionary<string, object>> QueryStringData { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The headers, if any.
|
||||
/// </summary>
|
||||
[Output(Description = "The headers, if any.")]
|
||||
public Output<IDictionary<string, object>> Headers { get; set; } = default!;
|
||||
public Output<IDictionary<string, object>> Headers { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<object> GetTriggerPayloads(TriggerIndexingContext context) => GetBookmarkPayloads(context.ExpressionExecutionContext);
|
||||
|
|
@ -205,7 +205,7 @@ public class HttpEndpoint : Trigger<HttpRequest>
|
|||
context.Set(Result, request);
|
||||
|
||||
// Read route data, if any.
|
||||
var path = context.GetWorkflowInput<PathString>(RequestPathInputKey);
|
||||
var path = context.GetWorkflowInput<PathString>(PathInputKey);
|
||||
var routeData = GetRouteData(httpContext, path);
|
||||
var routeDictionary = routeData.Values.ToDictionary(route => route.Key, route => route.Value!);
|
||||
var queryStringDictionary = httpContext.Request.Query.ToObjectDictionary();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Elsa.Extensions;
|
|||
public static class RouteExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalizes a route by ensuring a leading slash, removing any trailing slash and converting the path to lowercase.
|
||||
/// Normalizes a route by ensuring a leading slash, removing any trailing slash.
|
||||
/// </summary>
|
||||
public static string NormalizeRoute(this string path) => $"/{path.Trim('/').ToLowerInvariant()}";
|
||||
public static string NormalizeRoute(this string path) => $"/{path.Trim('/')}";
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, ITenantAccessor tenan
|
|||
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)")]
|
||||
public async Task InvokeAsync(HttpContext httpContext, IServiceProvider serviceProvider)
|
||||
{
|
||||
var path = GetPath(httpContext);
|
||||
var path = httpContext.Request.Path.Value!.NormalizeRoute();
|
||||
var matchingPath = GetMatchingRoute(serviceProvider, path).Route;
|
||||
var basePath = options.Value.BasePath?.ToString().NormalizeRoute();
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, ITenantAccessor tenan
|
|||
var input = new Dictionary<string, object>
|
||||
{
|
||||
[HttpEndpoint.HttpContextInputKey] = true,
|
||||
[HttpEndpoint.RequestPathInputKey] = path.NormalizeRoute()
|
||||
[HttpEndpoint.PathInputKey] = path
|
||||
};
|
||||
|
||||
var cancellationToken = httpContext.RequestAborted;
|
||||
|
|
@ -325,8 +325,6 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, ITenantAccessor tenan
|
|||
}
|
||||
}
|
||||
|
||||
private string GetPath(HttpContext httpContext) => httpContext.Request.Path.Value!.NormalizeRoute();
|
||||
|
||||
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)")]
|
||||
private async Task<bool> HandleMultipleWorkflowsFoundAsync(HttpContext httpContext, Func<IEnumerable<object>> workflowMatches, CancellationToken cancellationToken)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,13 +12,11 @@ public class RouteMatcher : IRouteMatcher
|
|||
/// <inheritdoc />
|
||||
public RouteValueDictionary? Match(string routeTemplate, string route)
|
||||
{
|
||||
var normalizedRoute = route.NormalizeRoute();
|
||||
var normalizedRouteTemplate = routeTemplate.NormalizeRoute();
|
||||
var template = TemplateParser.Parse(normalizedRouteTemplate);
|
||||
var template = TemplateParser.Parse(routeTemplate);
|
||||
var matcher = new TemplateMatcher(template, GetDefaults(template));
|
||||
var values = new RouteValueDictionary();
|
||||
|
||||
return matcher.TryMatch(normalizedRoute, values) ? values : null;
|
||||
return matcher.TryMatch(route, values) ? values : null;
|
||||
}
|
||||
|
||||
private static RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ internal class Post(
|
|||
|
||||
var draft = !string.IsNullOrWhiteSpace(definitionId)
|
||||
? await workflowDefinitionPublisher.GetDraftAsync(definitionId, VersionOptions.Latest, cancellationToken)
|
||||
: default;
|
||||
: null;
|
||||
|
||||
var isNew = draft == null;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class ArgumentJsonConverter : JsonConverter<ArgumentDefinition>
|
|||
var type = _wellKnownTypeRegistry.GetTypeOrDefault(typeName);
|
||||
|
||||
if (isArray)
|
||||
type = type.MakeCollectionType();
|
||||
type = type.MakeArrayType();
|
||||
|
||||
var newOptions = new JsonSerializerOptions(options);
|
||||
newOptions.Converters.RemoveWhere(x => x is ArgumentJsonConverterFactory);
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ public class TypeJsonConverter : JsonConverter<Type>
|
|||
}
|
||||
|
||||
// Handle collection types.
|
||||
if (typeAlias.EndsWith("()"))
|
||||
if (typeAlias.StartsWith("List<") && typeAlias.EndsWith(">"))
|
||||
{
|
||||
var elementTypeAlias = typeAlias[..^"()".Length];
|
||||
var elementTypeAlias = typeAlias[5..^1];
|
||||
var elementType = _wellKnownTypeRegistry.TryGetType(elementTypeAlias, out var t) ? t : Type.GetType(elementTypeAlias)!;
|
||||
return typeof(List<>).MakeGenericType(elementType);
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ public class TypeJsonConverter : JsonConverter<Type>
|
|||
|
||||
if (typedEnumerable.IsAssignableFrom(value) && _wellKnownTypeRegistry.TryGetAlias(elementType, out var elementTypeAlias))
|
||||
{
|
||||
writer.WriteStringValue($"{elementTypeAlias}()");
|
||||
writer.WriteStringValue($"List<{elementTypeAlias}>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public class VariableMapper
|
|||
.OnSuccess(value => variable.Value = value)
|
||||
.OnFailure(e => _logger.LogWarning("Failed to convert {SourceValue} to {TargetType}", source.Value, type.Name));
|
||||
|
||||
variable.StorageDriverType = !string.IsNullOrEmpty(source.StorageDriverTypeName) ? Type.GetType(source.StorageDriverTypeName) : default;
|
||||
variable.StorageDriverType = !string.IsNullOrEmpty(source.StorageDriverTypeName) ? Type.GetType(source.StorageDriverTypeName) : null;
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
|
@ -76,6 +76,6 @@ public class VariableMapper
|
|||
var storageDriverTypeName = source.StorageDriverType?.GetSimpleAssemblyQualifiedName();
|
||||
var serializedValue = value.Format();
|
||||
|
||||
return new VariableModel(source.Id, source.Name, valueTypeAlias, serializedValue, storageDriverTypeName);
|
||||
return new(source.Id, source.Name, valueTypeAlias, serializedValue, storageDriverTypeName);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue