Implement multitenant HTTP routing (#6031)

* Add tenant awareness to bookmark handling and route resolution

Added tenant ID support across various components, including bookmark updates, route resolution, and middleware processing. This ensures that bookmark and route operations can now appropriately handle tenant-specific data, improving the system's multitenancy capabilities.

* Add Multitenant HTTP Routing feature to Tenants module

Introduced a new MultitenantHttpRoutingFeature class to the Elsa.Tenants.AspNetCore module, enhancing the tenant resolution capabilities. Moved RoutePrefixTenantResolver from Elsa.Http to Elsa.Tenants.AspNetCore and updated relevant project references and namespaces accordingly. This refactor improves modularity and separation of concerns between HTTP and tenancy features.

* Refactor route handling and tenant configuration

Removed redundant `RouteTableExtensions` and replaced with new route providers and updaters, enhancing flexibility and modularity. Introduced tenant-specific HTTP endpoint configurations for better customization and configuration management.

* Rename HttpEndpointBookmarkStimulus to HttpEndpointBookmarkPayload

Refactor various classes and methods to reflect the renaming from `HttpEndpointBookmarkStimulus` to `HttpEndpointBookmarkPayload`. Add and configure new extension methods for tenant route handling, update the route provider to support multi-tenancy, and adjust the tenants provider to bind configuration properly.

* Add HeaderTenantResolver and refactor Http namespace.

Introduce HeaderTenantResolver to resolve tenants via HTTP headers. Refactor multiple classes and interfaces to move from the Elsa.Http.Models namespace directly into Elsa.Http for clarity and consistency.

* Add Host-based tenant resolution

Implemented a HostTenantResolver to resolve tenants based on the request's host and updated tenant configurations with host information. Modified the tenant resolver pipeline and added the new host resolver to the service registrations.

* Add tenant-aware caching and accessor support

Enhanced caching by incorporating tenant identifiers into cache keys for more granular cache management. Introduced ITenantAccessor dependencies in various services to retrieve the current tenant information. This ensures that cache entries are correctly isolated per tenant.

* Reorder tenant resolvers for pipeline setup.

Reordered the tenant resolvers in the pipeline to prioritize HostTenantResolver before RoutePrefixTenantResolver. This ensures that tenant resolution is correctly aligned with host-based resolving before checking the route prefix.

* Remove unused imports

This commit eliminates redundant `using` directives across multiple files to streamline the codebase. This cleanup helps improve code readability and maintainability by removing unnecessary dependencies.
This commit is contained in:
Sipke Schoorstra 2024-10-14 21:27:11 +02:00 committed by GitHub
parent 9808020d57
commit 7e7a899bbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
193 changed files with 677 additions and 433 deletions

View file

@ -95,7 +95,7 @@
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.4.3" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1"/>
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="Testcontainers" Version="3.9.0" />
<PackageVersion Include="Testcontainers.RabbitMq" Version="3.9.0" />
<PackageVersion Include="Testcontainers.Redis" Version="3.9.0" />
@ -130,6 +130,7 @@
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
@ -164,6 +165,7 @@
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />

View file

@ -301,8 +301,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integrations", "integration
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.OrchardCore", "src\modules\Elsa.OrchardCore\Elsa.OrchardCore.csproj", "{B0460856-7E6D-4B1E-B243-8E20662FC32E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.AspNet.OrchardCoreIntegration", "samples\aspnet\Elsa.Samples.AspNet.OrchardCoreIntegration\Elsa.Samples.AspNet.OrchardCoreIntegration.csproj", "{1451B84D-F224-4D5D-9A0D-985A8BD23D7B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "agents", "agents", "{50470834-4CD8-479A-8B58-0A1869BA5D37}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Agents.Core", "src\modules\Elsa.Agents.Core\Elsa.Agents.Core.csproj", "{5BD671F0-9603-4C2F-8364-54B02986EAA2}"

View file

@ -13,7 +13,6 @@ using Elsa.EntityFrameworkCore.Modules.Management;
using Elsa.EntityFrameworkCore.Modules.Runtime;
using Elsa.Extensions;
using Elsa.Features.Services;
using Elsa.Http.Multitenancy;
using Elsa.Identity.Multitenancy;
using Elsa.MassTransit.Extensions;
using Elsa.MongoDb.Extensions;
@ -26,6 +25,7 @@ using Elsa.Secrets.Extensions;
using Elsa.Secrets.Persistence;
using Elsa.Server.Web;
using Elsa.Server.Web.Filters;
using Elsa.Tenants.AspNetCore;
using Elsa.Tenants.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Api;
@ -442,16 +442,16 @@ services
.UseSecrets()
.UseSecretsManagement(management =>
{
management.ConfigureOptions(options => configuration.GetSection("Secrets:Management").Bind(options));
if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
management.UseEntityFrameworkCore(ef =>
ef.UseSqlServer(sqlServerConnectionString)
);
else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
management.UseEntityFrameworkCore(ef =>
ef.UsePostgreSql(postgresConnectionString)
);
else
management.ConfigureOptions(options => configuration.GetSection("Secrets:Management").Bind(options));
if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
management.UseEntityFrameworkCore(ef =>
ef.UseSqlServer(sqlServerConnectionString)
);
else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
management.UseEntityFrameworkCore(ef =>
ef.UsePostgreSql(postgresConnectionString)
);
else
management.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString));
})
.UseSecretsApi()
@ -467,10 +467,15 @@ services
{
configuration.GetSection("Multitenancy").Bind(options);
options.TenantResolverPipelineBuilder
.Append<HostTenantResolver>()
.Append<RoutePrefixTenantResolver>()
.Append<HeaderTenantResolver>()
.Append<ClaimsTenantResolver>();
};
tenants.UseConfigurationBasedTenantsProvider();
});
elsa.UseTenantHttpRouting();
}
elsa.InstallDropIns(options => options.DropInRootDirectory = Path.Combine(Directory.GetCurrentDirectory(), "App_Data", "DropIns"));

View file

@ -25,21 +25,57 @@
"Multitenancy": {
"Tenants": [
{
"Id": "default",
"Name": "Default"
"Id": null,
"Name": "Default",
"Configuration": {
"Http": {
"Prefix": "",
"Host": "localhost:5001"
},
"ConnectionStrings": {
"Sqlite": "Data Source=App_Data/elsa.sqlite.db;Cache=Shared;"
}
}
},
{
"Id": "tenant-1",
"Name": "Tenant 1"
"Name": "Tenant 1",
"Configuration": {
"Http": {
"Prefix": "tenant-1",
"Host": "localhost:5002"
},
"ConnectionStrings": {
"Sqlite": "Data Source=App_Data/elsa.tenant-1.sqlite.db;Cache=Shared;"
}
}
},
{
"Id": "tenant-2",
"Name": "Tenant 2"
"Name": "Tenant 2",
"Configuration": {
"Http": {
"Prefix": "tenant-2",
"Host": "localhost:5003"
},
"ConnectionStrings": {
"Sqlite": "Data Source=App_Data/elsa.tenant-2.sqlite.db;Cache=Shared;"
}
}
},
{
"Id": "tenant-2a",
"TenantId": "tenant-2",
"Name": "Tenant 2a"
"Name": "Tenant 2a",
"Configuration": {
"Http": {
"Prefix": "tenant-2a",
"Host": "localhost:5004"
},
"ConnectionStrings": {
"Sqlite": "Data Source=App_Data/elsa.tenant-2a.sqlite.db;Cache=Shared;"
}
}
}
]
},
@ -84,7 +120,7 @@
"Roles": [
"admin"
],
"TenantId": "default"
"TenantId": null
},
{
"id": "b0cd0e506e713a9d",

View file

@ -8,8 +8,6 @@ using Elsa.JavaScript.Libraries.Extensions;
using Elsa.ServerAndStudio.Web.Extensions;
using Elsa.MassTransit.Extensions;
using Elsa.ServerAndStudio.Web.Enums;
using Jint;
using Jint.Runtime.Modules;
using Medallion.Threading.FileSystem;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;

View file

@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
using Elsa.Studio.Models;
// Build the host.
var builder = WebAssemblyHostBuilder.CreateDefault(args);

View file

@ -1,5 +1,3 @@
using System.Linq.Expressions;
using Elsa.Api.Client.Resources.WorkflowInstances.Models;
using Elsa.Api.Client.Shared.Models;
namespace Elsa.Api.Client.Resources.ActivityExecutions.Models;

View file

@ -1,5 +1,4 @@
using FastEndpoints;
using JetBrains.Annotations;
namespace Elsa.Abstractions;

View file

@ -5,7 +5,6 @@ using Elsa.Workflows;
using Elsa.Workflows.Models;
using Humanizer;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace Elsa.Agents.Activities.ActivityProviders;

View file

@ -1,6 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
using Elsa.Extensions;
using JetBrains.Annotations;

View file

@ -1,6 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Filters;
using JetBrains.Annotations;
namespace Elsa.Agents.Api.Endpoints.Agents.IsUniqueName;

View file

@ -1,6 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
using Elsa.Extensions;
using Elsa.Models;
using JetBrains.Annotations;

View file

@ -1,7 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
using Elsa.Agents.Persistence.Filters;
using Elsa.Extensions;
using JetBrains.Annotations;

View file

@ -1,7 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
using Elsa.Models;
using JetBrains.Annotations;
namespace Elsa.Agents.Api.Endpoints.ApiKeys.Get;

View file

@ -1,6 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
using Elsa.Models;
using JetBrains.Annotations;

View file

@ -1,6 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
using JetBrains.Annotations;
namespace Elsa.Agents.Api.Endpoints.Services.Get;

View file

@ -1,6 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
using Elsa.Models;
using JetBrains.Annotations;

View file

@ -1,6 +1,5 @@
using Elsa.Abstractions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
using Elsa.Agents.Persistence.Filters;
using JetBrains.Annotations;

View file

@ -1,5 +1,4 @@
using Elsa.Agents.Persistence.Entities;
using Elsa.EntityFrameworkCore;
using Elsa.EntityFrameworkCore.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

View file

@ -4,7 +4,6 @@ using Elsa.Workflows;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Filters;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Filters;
namespace Elsa.Alterations.Core.Services;

View file

@ -4,7 +4,6 @@ using Elsa.AzureServiceBus.Models;
using Elsa.Extensions;
using Elsa.Workflows.Helpers;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Filters;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;

View file

@ -1,4 +1,3 @@
using System.Text.Json.Serialization;
using JetBrains.Annotations;
namespace Elsa.AzureServiceBus.Models;

View file

@ -1,9 +1,7 @@
using Azure.Messaging.ServiceBus;
using Elsa.AzureServiceBus.Activities;
using Elsa.AzureServiceBus.Models;
using Elsa.Workflows.Helpers;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

View file

@ -16,6 +16,7 @@
<ItemGroup>
<PackageReference Include="DistributedLock.Core" />
<PackageReference Include="LinqKit.Core" />
<PackageReference Include="Microsoft.Extensions.Configuration" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">

View file

@ -1,5 +1,6 @@
using Elsa.Common.Entities;
using JetBrains.Annotations;
using Microsoft.Extensions.Configuration;
namespace Elsa.Common.Multitenancy;
@ -13,9 +14,9 @@ public class Tenant : Entity
/// Gets or sets the name.
/// </summary>
public string Name { get; set; } = default!;
/// <summary>
/// Gets or sets the configuration.
/// </summary>
IDictionary<string, object> Configuration { get; set; } = new Dictionary<string, object>();
public IConfiguration Configuration { get; set; } = new ConfigurationBuilder().Build();
}

View file

@ -6,7 +6,6 @@ using Elsa.Elasticsearch.Common;
using Elsa.Elasticsearch.Shared.Models;
using Elsa.Extensions;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
using Elsa.Workflows.Runtime.OrderDefinitions;

View file

@ -1,5 +1,4 @@
using Elsa.Common.Entities;
using Elsa.EntityFrameworkCore.Common.Contracts;
using Microsoft.EntityFrameworkCore;
namespace Elsa.EntityFrameworkCore.Common;

View file

@ -7,7 +7,6 @@ using Elsa.EntityFrameworkCore.Modules.Labels;
using Elsa.EntityFrameworkCore.Modules.Management;
using Elsa.EntityFrameworkCore.Modules.Runtime;
using Elsa.EntityFrameworkCore.PostgreSql.Handlers;
using Elsa.Extensions;
using Microsoft.Extensions.DependencyInjection.Extensions;
// ReSharper disable once CheckNamespace

View file

@ -7,7 +7,6 @@ using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Compression;
using Elsa.Workflows.Management.Options;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Extensions;
using Elsa.Workflows.Runtime.Filters;

View file

@ -1,7 +1,6 @@
using Elsa.EntityFrameworkCore.Common;
using Elsa.Workflows;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
using JetBrains.Annotations;

View file

@ -1,11 +1,9 @@
using Elsa.EntityFrameworkCore.Common;
using Elsa.Workflows;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
using JetBrains.Annotations;
using Open.Linq.AsyncExtensions;
namespace Elsa.EntityFrameworkCore.Modules.Runtime;

View file

@ -3,7 +3,6 @@ using Elsa.Features.Attributes;
using Elsa.Features.Services;
using Elsa.Hangfire.Services;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Features;
using Microsoft.Extensions.DependencyInjection;

View file

@ -1,5 +1,4 @@
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
namespace Elsa.Hangfire.Jobs;

View file

@ -1,5 +1,4 @@
using Elsa.Scheduling;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Messages;

View file

@ -1,7 +1,6 @@
using Elsa.Hangfire.Extensions;
using Elsa.Hangfire.Jobs;
using Elsa.Scheduling;
using Elsa.Workflows.Runtime.Requests;
using Hangfire;
using Hangfire.Storage;

View file

@ -1,6 +1,4 @@
using Elsa.Http.Contexts;
using Elsa.Http.Contracts;
using Elsa.Http.Models;
namespace Elsa.Http.Abstractions;

View file

@ -1,4 +1,3 @@
using Elsa.Http.Contracts;
using Microsoft.AspNetCore.Http;
namespace Elsa.Http.Abstractions;

View file

@ -1,4 +1,3 @@
using Elsa.Http.Contracts;
using Microsoft.AspNetCore.Http;
namespace Elsa.Http.Abstractions;

View file

@ -3,14 +3,12 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using Elsa.Extensions;
using Elsa.Http.ContentWriters;
using Elsa.Http.Models;
using Elsa.Http.UIHints;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.UIHints;
using Elsa.Workflows.Models;
using Microsoft.Extensions.Logging;
using HttpHeaders = Elsa.Http.Models.HttpHeaders;
namespace Elsa.Http;

View file

@ -3,7 +3,6 @@ using System.Text.Json;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.Http.Bookmarks;
using Elsa.Http.Contracts;
using Elsa.Http.UIHints;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
@ -208,7 +207,6 @@ public class HttpEndpoint : Trigger<HttpRequest>
// Read route data, if any.
var path = context.GetWorkflowInput<PathString>(RequestPathInputKey);
var routeData = GetRouteData(httpContext, path);
var routeDictionary = routeData.Values.ToDictionary(route => route.Key, route => route.Value!);
var queryStringDictionary = httpContext.Request.Query.ToObjectDictionary();
var headersDictionary = httpContext.Request.Headers.ToObjectDictionary();
@ -499,7 +497,7 @@ public class HttpEndpoint : Trigger<HttpRequest>
var matchingRouteQuery =
from route in routeTable
let routeValues = routeMatcher.Match(route, path)
let routeValues = routeMatcher.Match(route.Route, path)
where routeValues != null
select new { route, routeValues };

View file

@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
using Elsa.Http.Models;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;

View file

@ -7,7 +7,6 @@ using Elsa.Workflows.Attributes;
using Elsa.Workflows.UIHints;
using Elsa.Workflows.Models;
using Microsoft.Extensions.Logging;
using HttpHeaders = Elsa.Http.Models.HttpHeaders;
namespace Elsa.Http;

View file

@ -1,8 +1,6 @@
using System.Security.Cryptography;
using Elsa.Extensions;
using Elsa.Http.Contracts;
using Elsa.Http.Exceptions;
using Elsa.Http.Models;
using Elsa.Http.Options;
using Elsa.Http.Services;
using Elsa.Workflows;

View file

@ -2,7 +2,6 @@ using System.Net;
using System.Runtime.CompilerServices;
using Elsa.Extensions;
using Elsa.Http.ContentWriters;
using Elsa.Http.Models;
using Elsa.Http.UIHints;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
@ -72,7 +71,6 @@ public class WriteHttpResponse : Activity
{
// We're executing in a non-HTTP context (e.g. in a virtual actor).
// Create a bookmark to allow the invoker to export the state and resume execution from there.
context.CreateBookmark(OnResumeAsync, BookmarkMetadata.HttpCrossBoundary);
return;
}
@ -135,11 +133,11 @@ public class WriteHttpResponse : Activity
await response.WriteAsync("The response includes a type that cannot be serialized.");
}
}
}
//Check if the configuration is set to flush immediatly the response to the caller.
var options = context.GetRequiredService<IOptions<HttpActivityOptions>>();
if (options.Value.WriteHttpResponseSynchronously)
}
// Check if the configuration is set to flush immediatly the response to the caller.
var options = context.GetRequiredService<IOptions<HttpActivityOptions>>();
if (options.Value.WriteHttpResponseSynchronously)
await response.CompleteAsync();
// Complete activity.

View file

@ -1,4 +1,3 @@
using Elsa.Http.Contracts;
using Elsa.Http.Options;
namespace Elsa.Http.Contexts;

View file

@ -0,0 +1,7 @@
using Elsa.Http.Bookmarks;
using JetBrains.Annotations;
namespace Elsa.Http.Contexts;
[UsedImplicitly]
public record HttpEndpointRouteProviderContext(HttpEndpointBookmarkPayload Payload, string? TenantId, CancellationToken CancellationToken);

View file

@ -1,4 +1,4 @@
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Provides a way to convert a relative URL to an absolute URL.

View file

@ -1,7 +1,6 @@
using Elsa.Http.Contexts;
using Elsa.Http.Models;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Provides downloadables from the specified content, if supported.

View file

@ -1,7 +1,6 @@
using Elsa.Http.Models;
using Elsa.Http.Options;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Provides downloadables from the specified content, if supported.

View file

@ -1,6 +1,6 @@
using FluentStorage.Blobs;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Represents a provider of a file cache storage.

View file

@ -1,6 +1,6 @@
using Elsa.Http.Options;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// A general-purpose downloader of files from a given URL.

View file

@ -1,6 +1,6 @@
using Elsa.Http.Contexts;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// A strategy that reads a <see cref="HttpResponseMessage"/> of a given content type.

View file

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Http;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Provides a way to select the correlation ID for a request.

View file

@ -1,6 +1,4 @@
using Elsa.Http.Models;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// A handler that is invoked when authorizing an inbound HTTP request.

View file

@ -1,6 +1,4 @@
using Elsa.Http.Models;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Implement this to control what to return to the client in case an unhandled exception occurs while executing the workflow.

View file

@ -0,0 +1,8 @@
using Elsa.Http.Contexts;
namespace Elsa.Http;
public interface IHttpEndpointRoutesProvider
{
Task<IEnumerable<HttpRouteData>> GetRoutesAsync(HttpEndpointRouteProviderContext context);
}

View file

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Http;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Provides a way to select the workflow instance ID for a request.

View file

@ -1,6 +1,4 @@
using Elsa.Http.Models;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Represents a service that looks up HTTP workflows and triggers.

View file

@ -1,6 +1,6 @@
using Elsa.Caching;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Represents a service that caches HTTP workflows and triggers.

View file

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Routing;
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Matches a given request path against the specified route template.

View file

@ -1,9 +1,9 @@
namespace Elsa.Http.Contracts;
namespace Elsa.Http;
/// <summary>
/// Stores a list of all routes provided by <see cref="HttpEndpoint"/> activities.
/// </summary>
public interface IRouteTable : IEnumerable<string>
public interface IRouteTable : IEnumerable<HttpRouteData>
{
/// <summary>
/// Adds a route to the table.
@ -11,6 +11,12 @@ public interface IRouteTable : IEnumerable<string>
/// <param name="route">The route to add.</param>
void Add(string route);
/// <summary>
/// Adds a route to the table.
/// </summary>
/// <param name="httpRouteData">The route to add.</param>
void Add(HttpRouteData httpRouteData);
/// <summary>
/// Removes a route from the table.
/// </summary>

View file

@ -1,4 +1,8 @@
namespace Elsa.Http.Contracts;
using Elsa.Workflows;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime.Entities;
namespace Elsa.Http;
/// <summary>
/// Updates the route table based on current workflow triggers and bookmarks.
@ -6,8 +10,42 @@ namespace Elsa.Http.Contracts;
public interface IRouteTableUpdater
{
/// <summary>
/// Updates the route table.
/// Updates the route table based on current workflow triggers and bookmarks.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
Task UpdateAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Adds routes to the route table based on the specified triggers.
/// </summary>
/// <param name="triggers">The triggers to create routes for.</param>
/// <param name="cancellationToken">The cancellation token.</param>
Task AddRoutesAsync(IEnumerable<StoredTrigger> triggers, CancellationToken cancellationToken = default);
/// <summary>
/// Adds routes to the route table based on the specified bookmarks.
/// </summary>
/// <param name="bookmarks">The bookmarks to create routes for.</param>
/// <param name="cancellationToken">The cancellation token.</param>
Task AddRoutesAsync(IEnumerable<StoredBookmark> bookmarks, CancellationToken cancellationToken = default);
/// <summary>
/// Adds routes to the route table based on the specified bookmarks.
/// </summary>
/// <param name="bookmarks">The bookmarks to create routes for.</param>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <param name="cancellationToken">The cancellation token.</param>
Task AddRoutesAsync(IEnumerable<Bookmark> bookmarks, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken = default);
/// <summary>
/// Removes routes from the route table based on the specified triggers.
/// </summary>
/// <param name="triggers">The triggers to remove routes for.</param>
void RemoveRoutes(IEnumerable<StoredTrigger> triggers);
/// <summary>
/// Removes routes from the route table based on the specified bookmarks.
/// </summary>
/// <param name="bookmarks">The bookmarks to remove routes for.</param>
void RemoveRoutes(IEnumerable<Bookmark> bookmarks);
}

View file

@ -1,6 +1,5 @@
using Elsa.Http.Abstractions;
using Elsa.Http.Contexts;
using Elsa.Http.Models;
namespace Elsa.Http.DownloadableContentHandlers;

View file

@ -1,6 +1,5 @@
using Elsa.Http.Abstractions;
using Elsa.Http.Contexts;
using Elsa.Http.Models;
namespace Elsa.Http.DownloadableContentHandlers;

View file

@ -1,6 +1,5 @@
using Elsa.Http.Abstractions;
using Elsa.Http.Contexts;
using Elsa.Http.Models;
using Microsoft.AspNetCore.Http;
namespace Elsa.Http.DownloadableContentHandlers;

View file

@ -1,6 +1,5 @@
using Elsa.Http.Abstractions;
using Elsa.Http.Contexts;
using Elsa.Http.Models;
namespace Elsa.Http.DownloadableContentHandlers;

View file

@ -1,7 +1,6 @@
using System.Collections;
using Elsa.Http.Abstractions;
using Elsa.Http.Contexts;
using Elsa.Http.Models;
namespace Elsa.Http.DownloadableContentHandlers;

View file

@ -1,6 +1,5 @@
using Elsa.Http.Abstractions;
using Elsa.Http.Contexts;
using Elsa.Http.Models;
namespace Elsa.Http.DownloadableContentHandlers;

View file

@ -1,7 +1,6 @@
using System.Text;
using Elsa.Http.Abstractions;
using Elsa.Http.Contexts;
using Elsa.Http.Models;
namespace Elsa.Http.DownloadableContentHandlers;

View file

@ -1,7 +1,5 @@
using Elsa.Http.Abstractions;
using Elsa.Http.Contexts;
using Elsa.Http.Contracts;
using Elsa.Http.Models;
using Elsa.Http.Options;
using Microsoft.AspNetCore.StaticFiles;

View file

@ -20,10 +20,10 @@
<ProjectReference Include="..\Elsa.Liquid\Elsa.Liquid.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Runtime.ProtoActor\Elsa.Workflows.Runtime.ProtoActor.csproj" />
<ProjectReference Include="..\Elsa.SasTokens\Elsa.SasTokens.csproj" />
<ProjectReference Include="..\Elsa.Tenants\Elsa.Tenants.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Management\Elsa.Workflows.Management.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
<ProjectReference Include="..\Elsa.JavaScript\Elsa.JavaScript.csproj" />
</ItemGroup>
</Project>

View file

@ -2,4 +2,6 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Chttpendpoint/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Chttptrigger/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=constants/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=constants/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=contracts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View file

@ -1,6 +1,5 @@
using Elsa.Expressions.Models;
using Elsa.Http.Contracts;
using Elsa.Http.Models;
using Elsa.Http;
using Elsa.Http.Options;
using Elsa.SasTokens.Contracts;
using Microsoft.Extensions.Options;

View file

@ -1,5 +1,5 @@
using Elsa.Http;
using Elsa.Http.Contexts;
using Elsa.Http.Contracts;
using Elsa.Workflows;
using Elsa.Workflows.Models;

View file

@ -1,78 +0,0 @@
using Elsa.Http;
using Elsa.Http.Bookmarks;
using Elsa.Http.Contracts;
using Elsa.Workflows.Helpers;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime.Entities;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
/// Adds extensions to <see cref="IRouteTable"/>.
/// </summary>
public static class RouteTableExtensions
{
/// <summary>
/// Adds routes from the specified set of triggers.
/// </summary>
public static void AddRoutes(this IRouteTable routeTable, IEnumerable<StoredTrigger> triggers)
{
var paths = Filter(triggers).Select(x => x.GetPayload<HttpEndpointBookmarkPayload>().Path).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
routeTable.AddRange(paths);
}
/// <summary>
/// Adds routes from the specified set of bookmarks.
/// </summary>
public static void AddRoutes(this IRouteTable routeTable, IEnumerable<StoredBookmark> bookmarks)
{
var paths = Filter(bookmarks).Select(x => x.GetPayload<HttpEndpointBookmarkPayload>().Path).ToList();
routeTable.AddRange(paths);
}
/// <summary>
/// Adds routes from the specified set of bookmarks.
/// </summary>
public static void AddRoutes(this IRouteTable routeTable, IEnumerable<Bookmark> bookmarks)
{
var paths = Filter(bookmarks).Select(x => x.GetPayload<HttpEndpointBookmarkPayload>().Path).ToList();
routeTable.AddRange(paths);
}
/// <summary>
/// Removes routes from the specified set of triggers.
/// </summary>
public static void RemoveRoutes(this IRouteTable routeTable, IEnumerable<StoredTrigger> triggers)
{
var paths = Filter(triggers).Select(x => x.GetPayload<HttpEndpointBookmarkPayload>().Path).ToList();
routeTable.RemoveRange(paths);
}
/// <summary>
/// Removes routes from the specified set of bookmarks.
/// </summary>
public static void RemoveRoutes(this IRouteTable routeTable, IEnumerable<Bookmark> bookmarks)
{
var paths = Filter(bookmarks).Select(x => x.GetPayload<HttpEndpointBookmarkPayload>().Path).ToList();
routeTable.RemoveRange(paths);
}
private static IEnumerable<StoredTrigger> Filter(IEnumerable<StoredTrigger> triggers)
{
var triggerName = ActivityTypeNameHelper.GenerateTypeName<HttpEndpoint>();
return triggers.Where(x => x.Name == triggerName && x.Payload != null);
}
private static IEnumerable<StoredBookmark> Filter(IEnumerable<StoredBookmark> bookmarks)
{
var activityTypeName = ActivityTypeNameHelper.GenerateTypeName<HttpEndpoint>();
return bookmarks.Where(x => x.ActivityTypeName == activityTypeName && x.Payload != null);
}
private static IEnumerable<Bookmark> Filter(IEnumerable<Bookmark> bookmarks)
{
var bookmarkName = ActivityTypeNameHelper.GenerateTypeName<HttpEndpoint>();
return bookmarks.Where(x => x.Name == bookmarkName && x.Payload != null);
}
}

View file

@ -1,4 +1,4 @@
using Elsa.Http.Contracts;
using Elsa.Http;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;

View file

@ -0,0 +1,11 @@
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
public static class StringArrayExtensions
{
public static string JoinSegments(this IEnumerable<string?> segments, string separator = "/")
{
return string.Join(separator, segments.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x!.Trim('/')));
}
}

View file

@ -1,5 +1,6 @@
using Microsoft.Extensions.Primitives;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
@ -20,4 +21,4 @@ public static class StringValueExtensions
item.Value[0]!
: item.Value.ToArray());
}
}
}

View file

@ -1,7 +1,6 @@
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
using Elsa.Features.Services;
using Elsa.Http.Contracts;
using Elsa.Http.Handlers;
using Elsa.Http.Services;
using Elsa.Workflows.Management.Features;

View file

@ -1,17 +1,13 @@
using Elsa.Common.Multitenancy;
using Elsa.Expressions.Options;
using Elsa.Extensions;
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
using Elsa.Features.Services;
using Elsa.Http.ContentWriters;
using Elsa.Http.Contracts;
using Elsa.Http.DownloadableContentHandlers;
using Elsa.Http.FileCaches;
using Elsa.Http.Handlers;
using Elsa.Http.HostedServices;
using Elsa.Http.Models;
using Elsa.Http.Multitenancy;
using Elsa.Http.Options;
using Elsa.Http.Parsers;
using Elsa.Http.PortResolvers;
@ -23,7 +19,6 @@ using Elsa.Workflows.Management.Requests;
using Elsa.Workflows.Management.Responses;
using FluentStorage;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@ -34,12 +29,9 @@ namespace Elsa.Http.Features;
/// Installs services related to HTTP services and activities.
/// </summary>
[DependsOn(typeof(HttpJavaScriptFeature))]
public class HttpFeature : FeatureBase
public class HttpFeature(IModule module) : FeatureBase(module)
{
/// <inheritdoc />
public HttpFeature(IModule module) : base(module)
{
}
private Func<IServiceProvider, IHttpEndpointRoutesProvider> _httpEndpointRouteProvider = sp => sp.GetRequiredService<DefaultHttpEndpointRoutesProvider>();
/// <summary>
/// A delegate to configure <see cref="HttpActivityOptions"/>.
@ -104,14 +96,24 @@ public class HttpFeature : FeatureBase
typeof(QueryStringHttpWorkflowInstanceIdSelector)
};
public HttpFeature WithHttpEndpointRoutesProvider<T>() where T : IHttpEndpointRoutesProvider
{
return WithHttpEndpointRoutesProvider(sp => sp.GetRequiredService<T>());
}
public HttpFeature WithHttpEndpointRoutesProvider(Func<IServiceProvider, IHttpEndpointRoutesProvider> httpEndpointRouteProvider)
{
_httpEndpointRouteProvider = httpEndpointRouteProvider;
return this;
}
/// <inheritdoc />
public override void Configure()
{
Module.UseWorkflowManagement(management =>
{
management.AddVariableTypes(new[]
{
typeof(RouteData),
management.AddVariableTypes([
typeof(HttpRouteData),
typeof(HttpRequest),
typeof(HttpResponse),
typeof(HttpResponseMessage),
@ -119,7 +121,7 @@ public class HttpFeature : FeatureBase
typeof(IFormFile),
typeof(HttpFile),
typeof(Downloadable)
}, "HTTP");
], "HTTP");
management.AddActivitiesFrom<HttpFeature>();
});
@ -184,8 +186,10 @@ public class HttpFeature : FeatureBase
.AddScoped<AuthenticationBasedHttpEndpointAuthorizationHandler>()
.AddScoped<AllowAnonymousHttpEndpointAuthorizationHandler>()
.AddScoped<DefaultHttpEndpointFaultHandler>()
.AddScoped<DefaultHttpEndpointRoutesProvider>()
.AddScoped(HttpEndpointWorkflowFaultHandler)
.AddScoped(HttpEndpointAuthorizationHandler)
.AddScoped(_httpEndpointRouteProvider)
// Downloadable content handlers.
.AddScoped<IDownloadableManager, DefaultDownloadableManager>()
@ -208,9 +212,6 @@ public class HttpFeature : FeatureBase
// HTTP clients.
Services.AddHttpClient<IFileDownloader, HttpClientFileDownloader>();
// Tenant resolvers.
Services.AddScoped<ITenantResolver, RoutePrefixTenantResolver>();
// Add selectors.
foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes)
@ -225,7 +226,7 @@ public class HttpFeature : FeatureBase
options.AddTypeAlias<HttpResponse>("HttpResponse");
options.AddTypeAlias<HttpResponseMessage>("HttpResponseMessage");
options.AddTypeAlias<HttpHeaders>("HttpHeaders");
options.AddTypeAlias<RouteData>("RouteData");
options.AddTypeAlias<HttpRouteData>("RouteData");
options.AddTypeAlias<IFormFile>("FormFile");
options.AddTypeAlias<IFormFile[]>("FormFile[]");
options.AddTypeAlias<HttpFile>("HttpFile");

View file

@ -1,4 +1,3 @@
using Elsa.Http.Contracts;
using FluentStorage.Blobs;
namespace Elsa.Http.FileCaches;

View file

@ -1,6 +1,3 @@
using Elsa.Http.Contracts;
using Elsa.Http.Models;
namespace Elsa.Http.Handlers;
/// <summary>

View file

@ -1,5 +1,3 @@
using Elsa.Http.Contracts;
using Elsa.Http.Models;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authorization;

View file

@ -1,6 +1,4 @@
using Elsa.Http.Models;
using Microsoft.AspNetCore.Http;
using Elsa.Http.Contracts;
using Elsa.Http.Exceptions;
namespace Elsa.Http.Handlers;

View file

@ -1,6 +1,4 @@
using System.Net.Mime;
using Elsa.Http.Contracts;
using Elsa.Http.Models;
using Elsa.Workflows;
using Microsoft.AspNetCore.Http;

View file

@ -1,5 +1,4 @@
using Elsa.Http.Bookmarks;
using Elsa.Http.Contracts;
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.Management.Notifications;

View file

@ -1,38 +1,31 @@
using Elsa.Extensions;
using Elsa.Http.Contracts;
using Elsa.Http.Options;
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Runtime.Notifications;
using JetBrains.Annotations;
using Microsoft.Extensions.Options;
namespace Elsa.Http.Handlers;
/// <summary>
/// A handler that updates the route table when workflow triggers and bookmarks are indexed.
/// </summary>
public class UpdateRouteTable :
[UsedImplicitly]
public class UpdateRouteTable(IRouteTableUpdater routeTableUpdater, IOptions<HttpActivityOptions> options) :
INotificationHandler<WorkflowTriggersIndexed>,
INotificationHandler<WorkflowBookmarksIndexed>
{
private readonly IRouteTable _routeTable;
/// <summary>
/// Initializes a new instance of the <see cref="UpdateRouteTable"/> class.
/// </summary>
public UpdateRouteTable(IRouteTable routeTable) => _routeTable = routeTable;
/// <inheritdoc />
public Task HandleAsync(WorkflowTriggersIndexed notification, CancellationToken cancellationToken)
public async Task HandleAsync(WorkflowTriggersIndexed notification, CancellationToken cancellationToken)
{
_routeTable.RemoveRoutes(notification.IndexedWorkflowTriggers.RemovedTriggers);
_routeTable.AddRoutes(notification.IndexedWorkflowTriggers.AddedTriggers);
_routeTable.AddRoutes(notification.IndexedWorkflowTriggers.UnchangedTriggers);
return Task.CompletedTask;
routeTableUpdater.RemoveRoutes(notification.IndexedWorkflowTriggers.RemovedTriggers);
await routeTableUpdater.AddRoutesAsync(notification.IndexedWorkflowTriggers.AddedTriggers, cancellationToken);
await routeTableUpdater.AddRoutesAsync(notification.IndexedWorkflowTriggers.UnchangedTriggers, cancellationToken);
}
/// <inheritdoc />
public Task HandleAsync(WorkflowBookmarksIndexed notification, CancellationToken cancellationToken)
public async Task HandleAsync(WorkflowBookmarksIndexed notification, CancellationToken cancellationToken)
{
_routeTable.RemoveRoutes(notification.IndexedWorkflowBookmarks.RemovedBookmarks);
_routeTable.AddRoutes(notification.IndexedWorkflowBookmarks.AddedBookmarks);
return Task.CompletedTask;
routeTableUpdater.RemoveRoutes(notification.IndexedWorkflowBookmarks.RemovedBookmarks);
await routeTableUpdater.AddRoutesAsync(notification.IndexedWorkflowBookmarks.AddedBookmarks, notification.IndexedWorkflowBookmarks.WorkflowExecutionContext, cancellationToken);
}
}

View file

@ -1,5 +1,4 @@
using Elsa.Http.Contracts;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Elsa.Http.HostedServices;

View file

@ -1,7 +1,5 @@
using Elsa.Extensions;
using Elsa.Http.Bookmarks;
using Elsa.Http.Contracts;
using Elsa.Http.Models;
using Elsa.Http.Options;
using Elsa.Workflows.Runtime.Filters;
using JetBrains.Annotations;
@ -17,8 +15,6 @@ using Elsa.Workflows.Runtime.Entities;
using FastEndpoints;
using System.Diagnostics.CodeAnalysis;
using Elsa.Common.Multitenancy;
using Elsa.Tenants;
using Elsa.Tenants.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Entities;
@ -33,7 +29,7 @@ namespace Elsa.Http.Middleware;
/// An ASP.NET middleware component that tries to match the inbound request path to an associated workflow and then run that workflow.
/// </summary>
[PublicAPI]
public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions<HttpActivityOptions> options)
public class HttpWorkflowsMiddleware(RequestDelegate next, ITenantAccessor tenantAccessor, IOptions<HttpActivityOptions> options)
{
private readonly string _activityTypeName = ActivityTypeNameHelper.GenerateTypeName<HttpEndpoint>();
@ -59,7 +55,7 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions<HttpActivity
path = path[basePath.Length..];
}
var matchingPath = GetMatchingRoute(serviceProvider, path);
var matchingPath = GetMatchingRoute(serviceProvider, path).Route;
var input = new Dictionary<string, object>
{
[HttpEndpoint.HttpContextInputKey] = true,
@ -252,23 +248,23 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions<HttpActivity
return result;
}
private string GetMatchingRoute(IServiceProvider serviceProvider, string path)
private HttpRouteData GetMatchingRoute(IServiceProvider serviceProvider, string path)
{
var routeMatcher = serviceProvider.GetRequiredService<IRouteMatcher>();
var routeTable = serviceProvider.GetRequiredService<IRouteTable>();
var matchingRouteQuery =
from route in routeTable
let routeValues = routeMatcher.Match(route, path)
from routeData in routeTable
let routeValues = routeMatcher.Match(routeData.Route, path)
where routeValues != null
select new
{
route,
route = routeData,
routeValues
};
var matchingRoute = matchingRouteQuery.FirstOrDefault();
var routeTemplate = matchingRoute?.route ?? path;
var routeTemplate = matchingRoute?.route ?? new HttpRouteData(path);
return routeTemplate;
}
@ -366,7 +362,7 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions<HttpActivity
{
var httpEndpointAuthorizationHandler = serviceProvider.GetRequiredService<IHttpEndpointAuthorizationHandler>();
if (bookmarkPayload.Authorize ?? false)
if (bookmarkPayload.Authorize == false)
return true;
return await httpEndpointAuthorizationHandler.AuthorizeAsync(new AuthorizeHttpEndpointContext(httpContext, workflow, bookmarkPayload.Policy));

View file

@ -1,7 +1,7 @@
using Elsa.Workflows.Activities;
using Microsoft.AspNetCore.Http;
namespace Elsa.Http.Models;
namespace Elsa.Http;
/// <summary>
/// Represents the context for authorizing an HTTP endpoint.

View file

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace Elsa.Http.Models;
namespace Elsa.Http;
/// <summary>
/// Represents a downloadable object.

View file

@ -1,4 +1,4 @@
namespace Elsa.Http.Models;
namespace Elsa.Http;
/// <summary>
/// Represents the payload of an event, serialized as a secured token.

View file

@ -1,7 +1,7 @@
using Elsa.Workflows.State;
using Microsoft.AspNetCore.Http;
namespace Elsa.Http.Models;
namespace Elsa.Http;
/// <summary>
/// Provides context about the faulted workflow.

View file

@ -1,7 +1,7 @@
using System.Net.Http.Headers;
using System.Text.Json.Serialization;
namespace Elsa.Http.Models;
namespace Elsa.Http;
/// <summary>
/// Represents a downloadable object.

View file

@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
using Elsa.Extensions;
using Elsa.Http.Serialization;
namespace Elsa.Http.Models;
namespace Elsa.Http;
/// <summary>
/// Represents the headers of an HTTP message.

View file

@ -0,0 +1,31 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Routing;
namespace Elsa.Http;
[UsedImplicitly]
public class HttpRouteData
{
public HttpRouteData()
{
}
public HttpRouteData(string route) : this()
{
Route = route;
}
public HttpRouteData(string route, RouteValueDictionary dataTokens) : this(route)
{
DataTokens = dataTokens;
}
public HttpRouteData(string route, RouteValueDictionary dataTokens, RouteValueDictionary routeValues) : this(route, dataTokens)
{
RouteValues = routeValues;
}
public string Route { get; set; } = default!;
public RouteValueDictionary DataTokens { get; set; } = new();
public RouteValueDictionary RouteValues { get; set; } = new();
}

View file

@ -1,7 +1,7 @@
using System.Text.Json.Serialization;
using Elsa.Workflows;
namespace Elsa.Http.Models;
namespace Elsa.Http;
/// <summary>
/// A binding between an HTTP status code and an activity.

View file

@ -1,7 +1,7 @@
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime.Entities;
namespace Elsa.Http.Models;
namespace Elsa.Http;
/// <summary>
/// Represents the result of a workflow lookup.

View file

@ -1,7 +1,5 @@
using Elsa.Extensions;
using Elsa.Http.Contexts;
using Elsa.Http.Contracts;
using Elsa.Http.Models;
namespace Elsa.Http.Parsers;

View file

@ -3,7 +3,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Elsa.Expressions.Helpers;
using Elsa.Http.Contexts;
using Elsa.Http.Contracts;
using Elsa.Workflows.Serialization.Converters;
namespace Elsa.Http.Parsers;

Some files were not shown because too many files have changed in this diff Show more