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(); } }