using CShells.AspNetCore.Features;
using CShells.Features;
using Elsa.Expressions.Options;
using Elsa.Extensions;
using Elsa.Http.Bookmarks;
using Elsa.Http.ContentWriters;
using Elsa.Http.DownloadableContentHandlers;
using Elsa.Http.FileCaches;
using Elsa.Http.Handlers;
using Elsa.Http.Middleware;
using Elsa.Http.Options;
using Elsa.Http.Parsers;
using Elsa.Http.PortResolvers;
using Elsa.Http.Resilience;
using Elsa.Http.Selectors;
using Elsa.Http.Services;
using Elsa.Http.Tasks;
using Elsa.Http.TriggerPayloadValidators;
using Elsa.Http.UIHints;
using Elsa.Resilience.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Management.Extensions;
using FluentStorage;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
namespace Elsa.Http.ShellFeatures;
///
/// Installs services related to HTTP services and activities.
///
[ShellFeature(
DisplayName = "HTTP",
Description = "Provides HTTP-related activities and services for workflow execution",
DependsOn = ["HttpJavaScript", "Resilience"])]
[UsedImplicitly]
public class HttpFeature : IMiddlewareShellFeature
{
///
/// The to configure.
///
public HttpActivityOptions HttpActivityOptions { get; set; } = new();
///
/// A delegate to configure .
///
public Action? ConfigureHttpFileCacheOptions { get; set; }
///
/// A delegate that is invoked when authorizing an inbound HTTP request.
///
public Func HttpEndpointAuthorizationHandler { get; set; } = sp => sp.GetRequiredService();
///
/// A delegate that is invoked when an HTTP workflow faults.
///
public Func HttpEndpointWorkflowFaultHandler { get; set; } = sp => sp.GetRequiredService();
///
/// A delegate to configure the .
///
public Func ContentTypeProvider { get; set; } = _ => new FileExtensionContentTypeProvider();
///
/// A delegate to configure the .
///
public Func FileCache { get; set; } = sp =>
{
var options = sp.GetRequiredService>().Value;
var blobStorage = StorageFactory.Blobs.DirectoryFiles(options.LocalCacheDirectory);
return new BlobFileCacheStorageProvider(blobStorage);
};
///
/// A delegate to configure the used when by the and activities.
///
public Action HttpClient { get; set; } = (_, _) => { };
///
/// A delegate to configure the for .
///
public Action HttpClientBuilder { get; set; } = _ => { };
///
/// A list of types to register with the service collection.
///
public ICollection HttpCorrelationIdSelectorTypes { get; } = new List
{
typeof(HeaderHttpCorrelationIdSelector),
typeof(QueryStringHttpCorrelationIdSelector)
};
///
/// A list of types to register with the service collection.
///
public ICollection HttpWorkflowInstanceIdSelectorTypes { get; } = new List
{
typeof(HeaderHttpWorkflowInstanceIdSelector),
typeof(QueryStringHttpWorkflowInstanceIdSelector)
};
public void ConfigureServices(IServiceCollection services)
{
// Register HTTP activities.
services.AddActivitiesFrom();
// Register HTTP variable types.
services.AddVariableDescriptors([
new(typeof(HttpRouteData), "HTTP", null),
new(typeof(HttpRequest), "HTTP", null),
new(typeof(HttpResponse), "HTTP", null),
new(typeof(HttpResponseMessage), "HTTP", null),
new(typeof(HttpHeaders), "HTTP", null),
new(typeof(IFormFile), "HTTP", null),
new(typeof(HttpFile), "HTTP", null),
new(typeof(Downloadable), "HTTP", null),
]);
// Register the HTTP resilience strategy.
services.AddResilienceStrategy();
var configureFileCacheOptions = ConfigureHttpFileCacheOptions ?? (options => { options.TimeToLive = TimeSpan.FromDays(7); });
services.Configure(options =>
{
options.BasePath = HttpActivityOptions.BasePath;
options.BaseUrl = HttpActivityOptions.BaseUrl;
options.AvailableContentTypes = HttpActivityOptions.AvailableContentTypes;
options.WriteHttpResponseSynchronously = HttpActivityOptions.WriteHttpResponseSynchronously;
});
services.Configure(configureFileCacheOptions);
var httpClientBuilder = services.AddHttpClient(HttpClient);
HttpClientBuilder(httpClientBuilder);
services
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped(ContentTypeProvider)
.AddHttpContextAccessor()
// Handlers.
.AddNotificationHandler()
// Content parsers.
.AddSingleton()
.AddSingleton()
.AddSingleton()
.AddSingleton()
.AddSingleton()
// HTTP content factories.
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
// Activity property options providers.
.AddScoped()
.AddScoped()
// Default providers.
.AddScoped()
.AddScoped(sp => sp.GetRequiredService())
// Port resolvers.
.AddScoped()
// HTTP endpoint handlers.
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped(HttpEndpointWorkflowFaultHandler)
.AddScoped(HttpEndpointAuthorizationHandler)
.AddScoped(sp => sp.GetRequiredService())
// Startup tasks.
.AddStartupTask()
// Downloadable content handlers.
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
//Trigger payload validators.
.AddTriggerPayloadValidator()
// File caches.
.AddScoped(FileCache)
.AddScoped()
// AuthenticationBasedHttpEndpointAuthorizationHandler requires Authorization services.
.AddAuthorization();
// HTTP clients.
services.AddHttpClient();
// Add selectors.
foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes)
services.AddScoped(typeof(IHttpCorrelationIdSelector), httpCorrelationIdSelectorType);
foreach (var httpWorkflowInstanceIdSelectorType in HttpWorkflowInstanceIdSelectorTypes)
services.AddScoped(typeof(IHttpWorkflowInstanceIdSelector), httpWorkflowInstanceIdSelectorType);
services.Configure(options =>
{
options.AddTypeAlias("HttpRequest");
options.AddTypeAlias("HttpResponse");
options.AddTypeAlias("HttpResponseMessage");
options.AddTypeAlias("HttpHeaders");
options.AddTypeAlias("RouteData");
options.AddTypeAlias("FormFile");
options.AddTypeAlias("FormFile[]");
options.AddTypeAlias("HttpFile");
options.AddTypeAlias("HttpFile[]");
options.AddTypeAlias("Downloadable");
options.AddTypeAlias("Downloadable[]");
});
}
///
public void UseMiddleware(IApplicationBuilder app, IHostEnvironment? environment)
{
app.UseWorkflows();
}
}