Merge remote-tracking branch 'origin/develop/3.5.0' into develop/3.6.0
This commit is contained in:
commit
ed49f21895
|
|
@ -9,101 +9,169 @@
|
|||
<ResilienceVersion>9.6.0</ResilienceVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Elsa.Studio" Version="$(ElsaStudioVersion)" />
|
||||
<PackageVersion Include="Elsa.Studio.Core.BlazorWasm" Version="$(ElsaStudioVersion)" />
|
||||
<PackageVersion Include="Elsa.Studio.Login.BlazorWasm" Version="$(ElsaStudioVersion)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1" />
|
||||
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1" />
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.15.0" />
|
||||
<PackageVersion Include="Bogus" Version="35.6.3" />
|
||||
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" PrivateAssets="All" />
|
||||
<PackageVersion Include="Confluent.Kafka" Version="2.10.0" />
|
||||
<PackageVersion Include="Confluent.SchemaRegistry.Serdes.Avro" Version="2.10.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" PrivateAssets="All" />
|
||||
<PackageVersion Include="Cronos" Version="0.11.0" />
|
||||
<PackageVersion Include="DistributedLock" Version="2.6.0" />
|
||||
<PackageVersion Include="DistributedLock.Core" Version="1.0.8" />
|
||||
<PackageVersion Include="DistributedLock.FileSystem" Version="1.0.3" />
|
||||
<PackageVersion Include="FastEndpoints" Version="6.1.0" />
|
||||
<PackageVersion Include="FastEndpoints.Security" Version="6.1.0" />
|
||||
<PackageVersion Include="FastEndpoints.Swagger" Version="6.1.0" />
|
||||
<PackageVersion Include="FluentStorage" Version="5.6.0" />
|
||||
<PackageVersion Include="Fluid.Core" Version="2.24.0" />
|
||||
<PackageVersion Include="Fody" Version="6.9.2" />
|
||||
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
|
||||
<PackageVersion Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageVersion Include="IronCompress" Version="1.6.3" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
|
||||
<PackageVersion Include="Jint" Version="4.2.2" />
|
||||
<PackageVersion Include="LinqKit.Core" Version="1.2.8" />
|
||||
<PackageVersion Include="MassTransit.RabbitMQ" Version="8.4.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="$(ResilienceVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Resilience" Version="$(ResilienceVersion)" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client" Version="4.71.1" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="Npgsql" Version="9.0.3" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageVersion Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageVersion Include="NuGet.Packaging" Version="6.13.2" />
|
||||
<PackageVersion Include="NuGet.Protocol" Version="6.13.2" />
|
||||
<PackageVersion Include="Nuke.Components" Version="9.0.4" />
|
||||
<PackageVersion Include="Open.Linq.AsyncExtensions" Version="1.2.0" />
|
||||
<PackageVersion Include="Polly" Version="8.5.2" />
|
||||
<PackageVersion Include="Polly.Extensions" Version="8.5.2" />
|
||||
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
|
||||
<PackageVersion Include="PolySharp" Version="1.15.0" />
|
||||
<PackageVersion Include="pythonnet" Version="3.1.0-preview2024-09-06" />
|
||||
<PackageVersion Include="Refit" Version="8.0.0" />
|
||||
<PackageVersion Include="Refit.HttpClientFactory" Version="8.0.0" />
|
||||
<PackageVersion Include="Scrutor" Version="6.1.0" />
|
||||
<PackageVersion Include="ShortGuid" Version="2.0.1" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0" />
|
||||
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.5" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Formats.Asn1" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="System.Text.Json" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Testcontainers" Version="4.4.0" />
|
||||
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.5.0" />
|
||||
<PackageVersion Include="Testcontainers.RabbitMq" Version="4.5.0" />
|
||||
<PackageVersion Include="Testcontainers.Redis" Version="4.4.0" />
|
||||
<PackageVersion Include="ThrottleDebounce" Version="2.0.1" />
|
||||
<PackageVersion Include="WebhooksCore" Version="0.0.1" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageVersion Include="xunit.extensibility.core" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.1" />
|
||||
<PackageVersion Include="Yarp.ReverseProxy" Version="2.3.0" />
|
||||
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1"/>
|
||||
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations" Version="0.5.1"/>
|
||||
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.MySql" Version="0.5.1"/>
|
||||
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL" Version="0.5.1"/>
|
||||
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer" Version="0.5.1"/>
|
||||
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite" Version="0.5.1"/>
|
||||
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="9.0.0"/>
|
||||
<PackageVersion Include="AutoMapper" Version="14.0.0" PrivateAssets="All"/>
|
||||
<PackageVersion Include="Azure.Identity" Version="1.14.0"/>
|
||||
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.19.0"/>
|
||||
<PackageVersion Include="Azure.ResourceManager" Version="1.13.1"/>
|
||||
<PackageVersion Include="Azure.ResourceManager.AppContainers" Version="1.3.0"/>
|
||||
<PackageVersion Include="Azure.ResourceManager.Resources" Version="1.9.1"/>
|
||||
<PackageVersion Include="Azure.ResourceManager.ServiceBus" Version="1.1.0"/>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.15.1"/>
|
||||
<PackageVersion Include="Bogus" Version="35.6.3"/>
|
||||
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" PrivateAssets="All"/>
|
||||
<PackageVersion Include="Confluent.Kafka" Version="2.10.0"/>
|
||||
<PackageVersion Include="Confluent.SchemaRegistry.Serdes.Avro" Version="2.10.0"/>
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" PrivateAssets="All"/>
|
||||
<PackageVersion Include="Cronos" Version="0.11.0"/>
|
||||
<PackageVersion Include="Dapper" Version="2.1.66"/>
|
||||
<PackageVersion Include="Datadog.Trace.Bundle" Version="3.16.0"/>
|
||||
<PackageVersion Include="DistributedLock" Version="2.6.0"/>
|
||||
<PackageVersion Include="DistributedLock.Core" Version="1.0.8"/>
|
||||
<PackageVersion Include="DistributedLock.FileSystem" Version="1.0.3"/>
|
||||
<PackageVersion Include="DistributedLock.Postgres" Version="1.3.0"/>
|
||||
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3"/>
|
||||
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="9.0.6"/>
|
||||
<PackageVersion Include="Elsa.Studio" Version="$(ElsaStudioVersion)"/>
|
||||
<PackageVersion Include="Elsa.Studio.Agents" Version="$(ElsaStudioVersion)"/>
|
||||
<PackageVersion Include="Elsa.Studio.Core.BlazorWasm" Version="$(ElsaStudioVersion)"/>
|
||||
<PackageVersion Include="Elsa.Studio.Login.BlazorWasm" Version="$(ElsaStudioVersion)"/>
|
||||
<PackageVersion Include="FastEndpoints" Version="6.1.0"/>
|
||||
<PackageVersion Include="FastEndpoints.Security" Version="6.1.0"/>
|
||||
<PackageVersion Include="FastEndpoints.Swagger" Version="6.1.0"/>
|
||||
<PackageVersion Include="FluentMigrator" Version="7.1.0"/>
|
||||
<PackageVersion Include="FluentMigrator.Runner" Version="7.1.0"/>
|
||||
<PackageVersion Include="FluentStorage" Version="5.6.0"/>
|
||||
<PackageVersion Include="FluentStorage.Azure.Blobs" Version="5.3.0"/>
|
||||
<PackageVersion Include="Fluid.Core" Version="2.24.0"/>
|
||||
<PackageVersion Include="Fody" Version="6.9.2"/>
|
||||
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1"/>
|
||||
<PackageVersion Include="Google.Protobuf" Version="3.31.1"/>
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.71.0"/>
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.72.0"/>
|
||||
<PackageVersion Include="Hangfire" Version="1.8.20"/>
|
||||
<PackageVersion Include="Hangfire.MemoryStorage" Version="1.8.1.1"/>
|
||||
<PackageVersion Include="Hangfire.PostgreSql" Version="1.20.12"/>
|
||||
<PackageVersion Include="Hangfire.Storage.SQLite" Version="0.4.2"/>
|
||||
<PackageVersion Include="Humanizer.Core" Version="2.14.1"/>
|
||||
<PackageVersion Include="IronCompress" Version="1.6.3"/>
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0"/>
|
||||
<PackageVersion Include="Jint" Version="4.2.2"/>
|
||||
<PackageVersion Include="LinqKit.Core" Version="1.2.8"/>
|
||||
<PackageVersion Include="MailKit" Version="4.12.1"/>
|
||||
<PackageVersion Include="MassTransit" Version="8.4.1"/>
|
||||
<PackageVersion Include="MassTransit.Azure.ServiceBus.Core" Version="8.4.1"/>
|
||||
<PackageVersion Include="MassTransit.Extensions.DependencyInjection" Version="7.3.1"/>
|
||||
<PackageVersion Include="MassTransit.RabbitMQ" Version="8.4.1"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0"/>
|
||||
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.2"/>
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="$(ResilienceVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Resilience" Version="$(ResilienceVersion)"/>
|
||||
<PackageVersion Include="Microsoft.Identity.Client" Version="4.72.1"/>
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0"/>
|
||||
<PackageVersion Include="Moq" Version="4.20.72"/>
|
||||
<PackageVersion Include="MongoDB.Driver" Version="3.4.0"/>
|
||||
<PackageVersion Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="2.1.0"/>
|
||||
<PackageVersion Include="MongoDB.Driver.Extensions" Version="2.0.2"/>
|
||||
<PackageVersion Include="MySql.Data" Version="9.3.0"/>
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||
<PackageVersion Include="Npgsql" Version="9.0.3"/>
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4"/>
|
||||
<PackageVersion Include="NSubstitute" Version="5.3.0"/>
|
||||
<PackageVersion Include="NuGet.Packaging" Version="6.14.0"/>
|
||||
<PackageVersion Include="NuGet.Protocol" Version="6.14.0"/>
|
||||
<PackageVersion Include="Nuke.Components" Version="9.0.4"/>
|
||||
<PackageVersion Include="Open.Linq.AsyncExtensions" Version="1.2.0"/>
|
||||
<PackageVersion Include="OpenTelemetry" Version="1.12.0"/>
|
||||
<PackageVersion Include="OpenTelemetry.AutoInstrumentation" Version="1.11.0"/>
|
||||
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.12.0"/>
|
||||
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0"/>
|
||||
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0"/>
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0"/>
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0"/>
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.SqlClient" Version="1.12.0-beta.1"/>
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.StackExchangeRedis" Version="1.12.0-beta.1"/>
|
||||
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.80"/>
|
||||
<PackageVersion Include="Polly" Version="8.6.0"/>
|
||||
<PackageVersion Include="Polly.Extensions" Version="8.6.0"/>
|
||||
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0"/>
|
||||
<PackageVersion Include="PolySharp" Version="1.15.0"/>
|
||||
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.3.efcore.9.0.0"/>
|
||||
<PackageVersion Include="Proto.Actor" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Cluster" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Cluster.CodeGen" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Cluster.AzureContainerApps" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Cluster.Kubernetes" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Cluster.TestProvider" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.OpenTelemetry" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Persistence" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Persistence.Sqlite" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Persistence.SqlServer" Version="1.7.0"/>
|
||||
<PackageVersion Include="Proto.Remote" Version="1.7.0"/>
|
||||
<PackageVersion Include="pythonnet" Version="3.1.0-preview2024-09-06"/>
|
||||
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.14.0"/>
|
||||
<PackageVersion Include="Quartz.Serialization.Json" Version="3.14.0"/>
|
||||
<PackageVersion Include="Refit" Version="8.0.0"/>
|
||||
<PackageVersion Include="Refit.HttpClientFactory" Version="8.0.0"/>
|
||||
<PackageVersion Include="Scrutor" Version="6.1.0"/>
|
||||
<PackageVersion Include="ShortGuid" Version="2.0.1"/>
|
||||
<PackageVersion Include="StackExchange.Redis" Version="2.8.37"/>
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1"/>
|
||||
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0"/>
|
||||
<PackageVersion Include="System.Linq.Async" Version="6.0.1"/>
|
||||
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.5"/>
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4"/>
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1"/>
|
||||
<PackageVersion Include="System.Formats.Asn1" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="System.Text.Json" Version="$(MicrosoftVersion)"/>
|
||||
<PackageVersion Include="Testcontainers" Version="4.5.0"/>
|
||||
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.5.0"/>
|
||||
<PackageVersion Include="Testcontainers.RabbitMq" Version="4.5.0"/>
|
||||
<PackageVersion Include="Testcontainers.Redis" Version="4.5.0"/>
|
||||
<PackageVersion Include="ThrottleDebounce" Version="2.0.1"/>
|
||||
<PackageVersion Include="WebhooksCore" Version="0.0.1"/>
|
||||
<PackageVersion Include="xunit" Version="2.9.3"/>
|
||||
<PackageVersion Include="xunit.abstractions" Version="2.0.3"/>
|
||||
<PackageVersion Include="xunit.extensibility.core" Version="2.9.3"/>
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.1"/>
|
||||
<PackageVersion Include="Yarp.ReverseProxy" Version="2.3.0"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
92
Elsa.sln
92
Elsa.sln
|
|
@ -1,4 +1,5 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.7.34003.232
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
|
|
@ -132,9 +133,9 @@ EndProject
|
|||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Resilience.IntegrationTests", "test\integration\Elsa.Resilience.IntegrationTests\Elsa.Resilience.IntegrationTests.csproj", "{832675FA-C597-4554-AE6B-18F189198A1F}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "apps", "apps", "{D92BEAB2-60D6-4BB4-885A-6BA681C6CCF1}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\apps\Directory.Build.props = src\apps\Directory.Build.props
|
||||
EndProjectSection
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\apps\Directory.Build.props = src\apps\Directory.Build.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Server.Web", "src\apps\Elsa.Server.Web\Elsa.Server.Web.csproj", "{5ADDDFB1-E59B-4097-97B7-8C24E2D60463}"
|
||||
EndProject
|
||||
|
|
@ -236,6 +237,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Caching", "src\modules
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "http", "http", "{C55015F0-E9DF-4BF9-8131-D7539E938220}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "io", "io", "{7FD1FD1E-5778-4065-AAA5-1F878129EF77}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.IO", "src\modules\Elsa.IO\Elsa.IO.csproj", "{EB24F9FE-D7BD-4FCC-907E-AE400288C2A5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.IO.Compression", "src\modules\Elsa.IO.Compression\Elsa.IO.Compression.csproj", "{9CA02818-F7EB-4A0B-B27B-BC74ACD499C9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.IO.Http", "src\modules\Elsa.IO.Http\Elsa.IO.Http.csproj", "{C583AF05-D517-4B7F-8955-6B61500ED3D8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Http", "src\modules\Elsa.Http\Elsa.Http.csproj", "{E0B22AB7-7CB5-6D17-562C-4A989DC61F8A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Expressions.CSharp", "src\modules\Elsa.Expressions.CSharp\Elsa.Expressions.CSharp.csproj", "{16C3FBDE-A832-E3CD-5FBF-F51744D1F79B}"
|
||||
|
|
@ -402,6 +411,74 @@ Global
|
|||
{D5720DBC-8C2B-42D5-9D9F-2FF6EAD4001C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D5720DBC-8C2B-42D5-9D9F-2FF6EAD4001C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D5720DBC-8C2B-42D5-9D9F-2FF6EAD4001C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BF934627-F531-44FB-BEC2-ECA801FF31E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BF934627-F531-44FB-BEC2-ECA801FF31E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BF934627-F531-44FB-BEC2-ECA801FF31E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BF934627-F531-44FB-BEC2-ECA801FF31E7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FD3CD5A8-E9B3-467F-90EB-2B7D5B83F348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FD3CD5A8-E9B3-467F-90EB-2B7D5B83F348}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FD3CD5A8-E9B3-467F-90EB-2B7D5B83F348}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FD3CD5A8-E9B3-467F-90EB-2B7D5B83F348}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3BED411B-79B5-4CCC-BD46-9549A427B908}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3BED411B-79B5-4CCC-BD46-9549A427B908}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3BED411B-79B5-4CCC-BD46-9549A427B908}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3BED411B-79B5-4CCC-BD46-9549A427B908}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6CC5FBC7-D3D7-4FE3-AD08-C67939BDB24D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6CC5FBC7-D3D7-4FE3-AD08-C67939BDB24D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6CC5FBC7-D3D7-4FE3-AD08-C67939BDB24D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6CC5FBC7-D3D7-4FE3-AD08-C67939BDB24D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA5E857F-B173-4B5D-8049-B817A210DEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA5E857F-B173-4B5D-8049-B817A210DEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA5E857F-B173-4B5D-8049-B817A210DEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA5E857F-B173-4B5D-8049-B817A210DEF5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A51F9683-DA9F-45E7-82DE-1E261ACD6D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A51F9683-DA9F-45E7-82DE-1E261ACD6D68}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A51F9683-DA9F-45E7-82DE-1E261ACD6D68}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A51F9683-DA9F-45E7-82DE-1E261ACD6D68}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{75E08B0E-A41A-425C-9DCA-155505CE41CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{75E08B0E-A41A-425C-9DCA-155505CE41CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{75E08B0E-A41A-425C-9DCA-155505CE41CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{75E08B0E-A41A-425C-9DCA-155505CE41CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BD120D97-4862-469E-B2CF-4109F7F21C20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BD120D97-4862-469E-B2CF-4109F7F21C20}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BD120D97-4862-469E-B2CF-4109F7F21C20}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BD120D97-4862-469E-B2CF-4109F7F21C20}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8A3443F3-D40E-44B9-9805-B4CB7A3F022C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A3443F3-D40E-44B9-9805-B4CB7A3F022C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A3443F3-D40E-44B9-9805-B4CB7A3F022C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A3443F3-D40E-44B9-9805-B4CB7A3F022C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA2B2B44-7E3E-444B-A157-7526B376E9CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA2B2B44-7E3E-444B-A157-7526B376E9CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA2B2B44-7E3E-444B-A157-7526B376E9CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA2B2B44-7E3E-444B-A157-7526B376E9CA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DF0CA7E2-F5DF-40ED-91B6-748767084FC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DF0CA7E2-F5DF-40ED-91B6-748767084FC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DF0CA7E2-F5DF-40ED-91B6-748767084FC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DF0CA7E2-F5DF-40ED-91B6-748767084FC7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DB20B168-CD90-41F2-BE08-3D8C6F142459}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DB20B168-CD90-41F2-BE08-3D8C6F142459}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DB20B168-CD90-41F2-BE08-3D8C6F142459}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DB20B168-CD90-41F2-BE08-3D8C6F142459}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{70593549-8B26-4D63-9857-6BA8BB3E31DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70593549-8B26-4D63-9857-6BA8BB3E31DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70593549-8B26-4D63-9857-6BA8BB3E31DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{70593549-8B26-4D63-9857-6BA8BB3E31DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E7137FB0-1988-4562-AD8D-D0D9D2EE85F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E7137FB0-1988-4562-AD8D-D0D9D2EE85F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E7137FB0-1988-4562-AD8D-D0D9D2EE85F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E7137FB0-1988-4562-AD8D-D0D9D2EE85F6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EB24F9FE-D7BD-4FCC-907E-AE400288C2A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EB24F9FE-D7BD-4FCC-907E-AE400288C2A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EB24F9FE-D7BD-4FCC-907E-AE400288C2A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EB24F9FE-D7BD-4FCC-907E-AE400288C2A5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9CA02818-F7EB-4A0B-B27B-BC74ACD499C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9CA02818-F7EB-4A0B-B27B-BC74ACD499C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9CA02818-F7EB-4A0B-B27B-BC74ACD499C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9CA02818-F7EB-4A0B-B27B-BC74ACD499C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C583AF05-D517-4B7F-8955-6B61500ED3D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C583AF05-D517-4B7F-8955-6B61500ED3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C583AF05-D517-4B7F-8955-6B61500ED3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C583AF05-D517-4B7F-8955-6B61500ED3D8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6F14B066-DF7B-2409-59D8-CCCA90A4974C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6F14B066-DF7B-2409-59D8-CCCA90A4974C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6F14B066-DF7B-2409-59D8-CCCA90A4974C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
@ -519,6 +596,13 @@ Global
|
|||
{66E2E2CF-967F-4564-89E8-F46FA973C99B} = {986E5482-0482-448C-B9E4-EC67A9474B85}
|
||||
{0A04B1FD-06C0-4271-A910-A08C263DBC44} = {0354F050-3992-4DD4-B0EE-5FBA04AC72B6}
|
||||
{9B80A705-2E31-4012-964A-83963DCDB384} = {0354F050-3992-4DD4-B0EE-5FBA04AC72B6}
|
||||
{CD7DC0D1-FFDC-417A-89BE-7F32408F583E} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
|
||||
{70593549-8B26-4D63-9857-6BA8BB3E31DB} = {CD7DC0D1-FFDC-417A-89BE-7F32408F583E}
|
||||
{E7137FB0-1988-4562-AD8D-D0D9D2EE85F6} = {CD7DC0D1-FFDC-417A-89BE-7F32408F583E}
|
||||
{7FD1FD1E-5778-4065-AAA5-1F878129EF77} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
|
||||
{EB24F9FE-D7BD-4FCC-907E-AE400288C2A5} = {7FD1FD1E-5778-4065-AAA5-1F878129EF77}
|
||||
{9CA02818-F7EB-4A0B-B27B-BC74ACD499C9} = {7FD1FD1E-5778-4065-AAA5-1F878129EF77}
|
||||
{C583AF05-D517-4B7F-8955-6B61500ED3D8} = {7FD1FD1E-5778-4065-AAA5-1F878129EF77}
|
||||
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
|
||||
{6F14B066-DF7B-2409-59D8-CCCA90A4974C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{C55015F0-E9DF-4BF9-8131-D7539E938220} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@
|
|||
<NoWarn>$(NoWarn);CS0162;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ElsaStudioVersion>3.5.0-preview.1040</ElsaStudioVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ConfigureAwait.Fody" PrivateAssets="All"/>
|
||||
<PackageReference Include="Fody" PrivateAssets="All"/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ services
|
|||
};
|
||||
})
|
||||
.UseWorkflowsApi()
|
||||
.UseCompression()
|
||||
.Use<IOHttpFeature>()
|
||||
.AddActivitiesFrom<Program>()
|
||||
.AddWorkflowsFrom<Program>();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,11 +47,13 @@ public class Module : IModule
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T Configure<T>(Action<T>? configure = default) where T : class, IFeature
|
||||
=> Configure(module => (T)Activator.CreateInstance(typeof(T), module)!, configure);
|
||||
public T Configure<T>(Action<T>? configure = null) where T : class, IFeature
|
||||
{
|
||||
return Configure(module => (T)Activator.CreateInstance(typeof(T), module)!, configure);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T Configure<T>(Func<IModule, T> factory, Action<T>? configure = default) where T : class, IFeature
|
||||
public T Configure<T>(Func<IModule, T> factory, Action<T>? configure = null) where T : class, IFeature
|
||||
{
|
||||
if (!_features.TryGetValue(typeof(T), out var feature))
|
||||
{
|
||||
|
|
@ -81,7 +83,7 @@ public class Module : IModule
|
|||
/// <inheritdoc />
|
||||
public IModule ConfigureHostedService(Type hostedServiceType, int priority = 0)
|
||||
{
|
||||
_hostedServiceDescriptors.Add(new HostedServiceDescriptor(priority, hostedServiceType));
|
||||
_hostedServiceDescriptors.Add(new(priority, hostedServiceType));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +123,7 @@ public class Module : IModule
|
|||
var ns = "Elsa";
|
||||
var displayName = type.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? name;
|
||||
var description = type.GetCustomAttribute<DescriptionAttribute>()?.Description;
|
||||
registry.Add(new FeatureDescriptor(name, ns, displayName, description));
|
||||
registry.Add(new(name, ns, displayName, description));
|
||||
}
|
||||
|
||||
Services.AddSingleton<IInstalledFeatureRegistry>(registry);
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ public interface IModule
|
|||
/// <summary>
|
||||
/// Creates and configures a feature of the specified type.
|
||||
/// </summary>
|
||||
T Configure<T>(Action<T>? configure = default) where T : class, IFeature;
|
||||
T Configure<T>(Action<T>? configure = null) where T : class, IFeature;
|
||||
|
||||
/// <summary>
|
||||
/// Creates and configures a feature of the specified type.
|
||||
/// </summary>
|
||||
T Configure<T>(Func<IModule, T> factory, Action<T>? configure = default) where T : class, IFeature;
|
||||
T Configure<T>(Func<IModule, T> factory, Action<T>? configure = null) where T : class, IFeature;
|
||||
|
||||
/// <summary>
|
||||
/// Configures a <see cref="IHostedService"/> using an optional priority to control in which order it will be registered with the service container.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
namespace Elsa.Common.Features;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class CompressionFeature(IModule module) : FeatureBase(module)
|
||||
public class StringCompressionFeature(IModule module) : FeatureBase(module)
|
||||
{
|
||||
public override void Apply()
|
||||
{
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
// ReSharper disable once CheckNamespace
|
||||
using Elsa.Http;
|
||||
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
140
src/modules/Elsa.IO.Compression/Activities/CreateZipArchive.cs
Normal file
140
src/modules/Elsa.IO.Compression/Activities/CreateZipArchive.cs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
using System.IO.Compression;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.IO.Contracts;
|
||||
using Elsa.IO.Extensions;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Attributes;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.UIHints;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Elsa.IO.Compression.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ZIP archive from a collection of entries.
|
||||
/// </summary>
|
||||
[Activity("Elsa", "Compression", "Creates a ZIP archive from a collection of entries.", DisplayName = "Create Zip Archive")]
|
||||
public class CreateZipArchive : CodeActivity<Stream>
|
||||
{
|
||||
private const string DefaultArchiveName = "archive.zip";
|
||||
private const string ZipExtension = ".zip";
|
||||
private const string DefaultEntryNameFormat = "entry_{0}";
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonConstructor]
|
||||
public CreateZipArchive(string? source = null, int? line = null) : base(source, line)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entries to include in the ZIP archive. Can be byte[], Stream, file path, file URL, base64 string, ZipEntry objects, or arrays of these types.
|
||||
/// </summary>
|
||||
[Input(
|
||||
Description = "The entries to include in the ZIP archive. Can be byte[], Stream, file path, file URL, base64 string, ZipEntry objects, or arrays of these types",
|
||||
UIHint = InputUIHints.MultiLine
|
||||
)]
|
||||
public Input<object?> Entries { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The compression level for the Zip Entries. Default is Optimal
|
||||
/// </summary>
|
||||
[Input(
|
||||
Description = "The compression level for the Zip Entries. Default is Optimal",
|
||||
UIHint = InputUIHints.DropDown
|
||||
)]
|
||||
public Input<CompressionLevel> CompressionLevel { get; set; } = new(System.IO.Compression.CompressionLevel.Optimal);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var entriesInput = Entries.Get(context);
|
||||
var resolver = context.GetRequiredService<IContentResolver>();
|
||||
var logger = context.GetRequiredService<ILogger<CreateZipArchive>>();
|
||||
|
||||
var entries = ParseEntries(entriesInput);
|
||||
|
||||
var zipStream = await CreateZipStreamFromEntries(entries, resolver, context, logger);
|
||||
|
||||
Result.Set(context, zipStream);
|
||||
}
|
||||
|
||||
private static IEnumerable<object> ParseEntries(object? entriesInput)
|
||||
{
|
||||
return entriesInput switch
|
||||
{
|
||||
null => [],
|
||||
IEnumerable<object> enumerable => enumerable,
|
||||
Array array => array.Cast<object>(),
|
||||
_ => [entriesInput]
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<Stream> CreateZipStreamFromEntries(
|
||||
IEnumerable<object> entries,
|
||||
IContentResolver resolver,
|
||||
ActivityExecutionContext context,
|
||||
ILogger logger)
|
||||
{
|
||||
var zipStream = new MemoryStream();
|
||||
|
||||
try
|
||||
{
|
||||
using var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, leaveOpen: true);
|
||||
var entryIndex = 0;
|
||||
|
||||
var compressionLevel = CompressionLevel.Get(context);
|
||||
foreach (var entryContent in entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessZipEntry(entryContent, zipArchive, resolver, context, entryIndex, compressionLevel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Failed to add entry {EntryIndex} to ZIP archive. Reason: {ExceptionMessage}",
|
||||
entryIndex, ex.Message);
|
||||
}
|
||||
entryIndex++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to create ZIP archive");
|
||||
await zipStream.DisposeAsync();
|
||||
throw;
|
||||
}
|
||||
|
||||
// Reset stream position for reading
|
||||
zipStream.Position = 0;
|
||||
return zipStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a single zip entry and adds it to the archive.
|
||||
/// </summary>
|
||||
private static async Task ProcessZipEntry(
|
||||
object entryContent,
|
||||
ZipArchive zipArchive,
|
||||
IContentResolver resolver,
|
||||
ActivityExecutionContext context,
|
||||
int entryIndex,
|
||||
CompressionLevel compressionLevel)
|
||||
{
|
||||
var binaryContent = await resolver.ResolveAsync(entryContent, context.CancellationToken);
|
||||
|
||||
var entryName = binaryContent.Name?.GetNameAndExtension()
|
||||
?? string.Format(DefaultEntryNameFormat, entryIndex + 1);
|
||||
|
||||
var archiveEntry = zipArchive.CreateEntry(entryName, compressionLevel);
|
||||
|
||||
await using var entryStream = archiveEntry.Open();
|
||||
await binaryContent.Stream.CopyToAsync(entryStream, context.CancellationToken);
|
||||
await entryStream.FlushAsync(context.CancellationToken);
|
||||
|
||||
if (entryContent is not Stream)
|
||||
{
|
||||
await binaryContent.Stream.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/modules/Elsa.IO.Compression/Common/Constants.cs
Normal file
6
src/modules/Elsa.IO.Compression/Common/Constants.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.IO.Compression.Common;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public const float ZipEntryStrategyPriority = 0.5f;
|
||||
}
|
||||
23
src/modules/Elsa.IO.Compression/Elsa.IO.Compression.csproj
Normal file
23
src/modules/Elsa.IO.Compression/Elsa.IO.Compression.csproj
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>
|
||||
Provides compression and archiving activities for Elsa Workflows.
|
||||
</Description>
|
||||
<PackageTags>elsa module compression zip archive workflows</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Http" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
|
||||
<ProjectReference Include="..\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
|
||||
<ProjectReference Include="..\Elsa.IO\Elsa.IO.csproj" />
|
||||
<ProjectReference Include="..\..\common\Elsa.Features\Elsa.Features.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
using Elsa.IO.Compression.Features;
|
||||
using Elsa.Features.Services;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extensions to install the <see cref="CompressionFeature"/> feature.
|
||||
/// </summary>
|
||||
public static class ModuleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Install the <see cref="CompressionFeature"/> feature.
|
||||
/// </summary>
|
||||
public static IModule UseCompression(this IModule module, Action<CompressionFeature>? configure = default)
|
||||
{
|
||||
module.Configure(configure);
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.Features.Abstractions;
|
||||
using Elsa.Features.Attributes;
|
||||
using Elsa.Features.Services;
|
||||
using Elsa.IO.Compression.Models;
|
||||
using Elsa.IO.Compression.Services.Strategies;
|
||||
using Elsa.IO.Features;
|
||||
using Elsa.IO.Services.Strategies;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.IO.Compression.Features;
|
||||
|
||||
/// <summary>
|
||||
/// Configures compression activities and services.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[DependsOn(typeof(IOFeature))]
|
||||
public class CompressionFeature(IModule module) : FeatureBase(module)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Configure()
|
||||
{
|
||||
Module.AddActivitiesFrom<CompressionFeature>();
|
||||
Module.AddVariableTypeAndAlias<ZipEntry>("ZipEntry", "Compression");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services.AddScoped<IContentResolverStrategy, ZipEntryContentStrategy>();
|
||||
}
|
||||
}
|
||||
3
src/modules/Elsa.IO.Compression/FodyWeavers.xml
Normal file
3
src/modules/Elsa.IO.Compression/FodyWeavers.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ConfigureAwait />
|
||||
</Weavers>
|
||||
11
src/modules/Elsa.IO.Compression/Models/ZipEntry.cs
Normal file
11
src/modules/Elsa.IO.Compression/Models/ZipEntry.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.IO.Compression.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a zip entry with content and metadata.
|
||||
/// </summary>
|
||||
/// <param name="Content">The content of the zip entry. Can be byte[], Stream, file path, file URL, or base64 string.</param>
|
||||
/// <param name="EntryName">The name of the entry in the zip archive.</param>
|
||||
[UsedImplicitly]
|
||||
public record ZipEntry(object Content, string? EntryName = null);
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
using Elsa.IO.Compression.Common;
|
||||
using Elsa.IO.Compression.Models;
|
||||
using Elsa.IO.Contracts;
|
||||
using Elsa.IO.Extensions;
|
||||
using Elsa.IO.Models;
|
||||
using Elsa.IO.Services.Strategies;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.IO.Compression.Services.Strategies;
|
||||
|
||||
/// <summary>
|
||||
/// Strategy for resolving ZipEntry content with proper entry names.
|
||||
/// </summary>
|
||||
public class ZipEntryContentStrategy(IServiceProvider serviceProvider) : IContentResolverStrategy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public float Priority => Constants.ZipEntryStrategyPriority;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanResolve(object content) => content is ZipEntry;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var zipEntry = (ZipEntry)content;
|
||||
|
||||
var resolver = serviceProvider.GetRequiredService<IContentResolver>();
|
||||
|
||||
var innerContent = await resolver.ResolveAsync(zipEntry.Content, cancellationToken);
|
||||
|
||||
if (string.IsNullOrEmpty(zipEntry.EntryName))
|
||||
{
|
||||
return innerContent;
|
||||
}
|
||||
|
||||
var innerContentName = innerContent.Name?.GetNameAndExtension();
|
||||
var innerContentExtension = Path.GetExtension(innerContentName);
|
||||
innerContent.Name = !string.IsNullOrWhiteSpace(innerContentExtension)
|
||||
? zipEntry.EntryName + innerContentExtension
|
||||
: innerContent.Name;
|
||||
|
||||
return innerContent;
|
||||
}
|
||||
}
|
||||
14
src/modules/Elsa.IO.Http/Common/Constants.cs
Normal file
14
src/modules/Elsa.IO.Http/Common/Constants.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
namespace Elsa.IO.Http.Common;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the HTTP client used for IO operations.
|
||||
/// </summary>
|
||||
public const string IOFeatureHttpClient = "IOFeatureHttpClient";
|
||||
|
||||
public static class StrategyPriorities
|
||||
{
|
||||
public const float Uri = 2.5f;
|
||||
}
|
||||
}
|
||||
21
src/modules/Elsa.IO.Http/Elsa.IO.Http.csproj
Normal file
21
src/modules/Elsa.IO.Http/Elsa.IO.Http.csproj
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>
|
||||
Provides http capabilities to IO modules in Elsa Workflows.
|
||||
</Description>
|
||||
<PackageTags>elsa module io http</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Elsa.Http\Elsa.Http.csproj" />
|
||||
<ProjectReference Include="..\Elsa.IO.Compression\Elsa.IO.Compression.csproj" />
|
||||
<ProjectReference Include="..\Elsa.IO\Elsa.IO.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Http" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
29
src/modules/Elsa.IO.Http/Features/IOHttpFeature.cs
Normal file
29
src/modules/Elsa.IO.Http/Features/IOHttpFeature.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Elsa.Features.Abstractions;
|
||||
using Elsa.Features.Attributes;
|
||||
using Elsa.Features.Services;
|
||||
using Elsa.IO.Compression.Features;
|
||||
using Elsa.IO.Features;
|
||||
using Elsa.IO.Http.Common;
|
||||
using Elsa.IO.Http.Services.Strategies;
|
||||
using Elsa.IO.Services.Strategies;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.IO.Http.Features;
|
||||
|
||||
/// <summary>
|
||||
/// Configures HTTP-based IO services.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[DependsOn(typeof(IOFeature))]
|
||||
[DependencyOf(typeof(CompressionFeature))]
|
||||
public class IOHttpFeature(IModule module) : FeatureBase(module)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services.AddHttpClient(Constants.IOFeatureHttpClient);
|
||||
|
||||
Services.AddScoped<IContentResolverStrategy, UrlContentStrategy>();
|
||||
}
|
||||
}
|
||||
3
src/modules/Elsa.IO.Http/FodyWeavers.xml
Normal file
3
src/modules/Elsa.IO.Http/FodyWeavers.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ConfigureAwait />
|
||||
</Weavers>
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.IO.Extensions;
|
||||
using Elsa.IO.Http.Common;
|
||||
using Elsa.IO.Models;
|
||||
using Elsa.IO.Services.Strategies;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Elsa.IO.Http.Services.Strategies;
|
||||
|
||||
/// <summary>
|
||||
/// Strategy for handling URL content by downloading from HTTP/HTTPS URLs.
|
||||
/// </summary>
|
||||
public class UrlContentStrategy(ILogger<UrlContentStrategy> logger, IHttpClientFactory httpClientFactory) : IContentResolverStrategy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public float Priority => Constants.StrategyPriorities.Uri;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanResolve(object content) => content is string str && (str.StartsWith("http://") || str.StartsWith("https://"));
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var url = (string)content;
|
||||
|
||||
try
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
var response = await httpClient.GetAsync(url, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
var filename = ExtractFilenameFromResponse(response, url);
|
||||
var contentType = response.Content.Headers.ContentType?.MediaType;
|
||||
|
||||
return new BinaryContent
|
||||
{
|
||||
Name = filename.GetNameAndExtension(contentType.GetExtensionFromContentType()),
|
||||
Stream = stream
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to download file from URL: {Url}", url);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts a filename from the HTTP response, either from Content-Disposition header or URL.
|
||||
/// </summary>
|
||||
private string ExtractFilenameFromResponse(HttpResponseMessage response, string url)
|
||||
{
|
||||
var filename = response.GetFilename();
|
||||
if (!string.IsNullOrWhiteSpace(filename))
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
var path = uri.AbsolutePath;
|
||||
filename = Path.GetFileName(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(filename) && Path.HasExtension(filename))
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogDebug(ex, "Failed to extract filename from URL: {Url}", url);
|
||||
}
|
||||
|
||||
return "download";
|
||||
}
|
||||
}
|
||||
38
src/modules/Elsa.IO/Common/Constants.cs
Normal file
38
src/modules/Elsa.IO/Common/Constants.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
namespace Elsa.IO.Common;
|
||||
|
||||
/// <summary>
|
||||
/// IO module constants.
|
||||
/// </summary>
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Priorities for content resolver strategies.
|
||||
/// </summary>
|
||||
public static class StrategyPriorities
|
||||
{
|
||||
/// <summary>
|
||||
/// Stream content priority.
|
||||
/// </summary>
|
||||
public const float Stream = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Byte array content priority.
|
||||
/// </summary>
|
||||
public const float ByteArray = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Base64 content priority.
|
||||
/// </summary>
|
||||
public const float Base64 = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// File path content priority.
|
||||
/// </summary>
|
||||
public const float FilePath = 3.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Text content priority.
|
||||
/// </summary>
|
||||
public const float Text = 100.0f;
|
||||
}
|
||||
}
|
||||
17
src/modules/Elsa.IO/Contracts/IContentResolver.cs
Normal file
17
src/modules/Elsa.IO/Contracts/IContentResolver.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace Elsa.IO.Contracts;
|
||||
|
||||
using Elsa.IO.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods to resolve various content types to BinaryContent.
|
||||
/// </summary>
|
||||
public interface IContentResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves arbitrary content to a BinaryContent object that includes the content stream and metadata.
|
||||
/// </summary>
|
||||
/// <param name="content">The content to resolve. Can be byte[], Stream, file path, file URL, base64 string, or plain text.</param>
|
||||
/// <param name="cancellationToken">A cancellation token.</param>
|
||||
/// <returns>A BinaryContent object containing the content stream and associated metadata.</returns>
|
||||
Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default);
|
||||
}
|
||||
21
src/modules/Elsa.IO/Elsa.IO.csproj
Normal file
21
src/modules/Elsa.IO/Elsa.IO.csproj
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>
|
||||
Provides IO services for resolving various content types to streams.
|
||||
</Description>
|
||||
<PackageTags>elsa module io content streams</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\Elsa.Features\Elsa.Features.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
110
src/modules/Elsa.IO/Extensions/ContentTypeExtensions.cs
Normal file
110
src/modules/Elsa.IO/Extensions/ContentTypeExtensions.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
namespace Elsa.IO.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
public static class ContentTypeExtensions
|
||||
{
|
||||
private static readonly Dictionary<string, string> MimeMapping = new(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly Dictionary<string, HashSet<string>> ContentTypeToExtensionsMap = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
static ContentTypeExtensions()
|
||||
{
|
||||
// Define all mappings in a single place
|
||||
AddMapping(".txt", "text/plain");
|
||||
AddMapping(".html", "text/html");
|
||||
AddMapping(".htm", "text/html");
|
||||
AddMapping(".css", "text/css");
|
||||
AddMapping(".js", "application/javascript");
|
||||
AddMapping(".json", "application/json");
|
||||
AddMapping(".xml", "application/xml");
|
||||
AddMapping(".jpg", "image/jpeg");
|
||||
AddMapping(".jpeg", "image/jpeg");
|
||||
AddMapping(".png", "image/png");
|
||||
AddMapping(".gif", "image/gif");
|
||||
AddMapping(".svg", "image/svg+xml");
|
||||
AddMapping(".pdf", "application/pdf");
|
||||
AddMapping(".doc", "application/msword");
|
||||
AddMapping(".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
||||
AddMapping(".xls", "application/vnd.ms-excel");
|
||||
AddMapping(".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
AddMapping(".ppt", "application/vnd.ms-powerpoint");
|
||||
AddMapping(".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
|
||||
AddMapping(".zip", "application/zip");
|
||||
AddMapping(".csv", "text/csv");
|
||||
}
|
||||
private static void AddMapping(string extension, string contentType)
|
||||
{
|
||||
// Map extension to content type
|
||||
MimeMapping[extension] = contentType;
|
||||
// Map content type to extension(s)
|
||||
if (!ContentTypeToExtensionsMap.TryGetValue(contentType, out var extensions))
|
||||
{
|
||||
extensions = new(StringComparer.OrdinalIgnoreCase);
|
||||
ContentTypeToExtensionsMap[contentType] = extensions;
|
||||
}
|
||||
|
||||
extensions.Add(extension);
|
||||
}
|
||||
public static string GetExtensionFromContentType(this string? contentType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
return ".bin";
|
||||
|
||||
if (contentType.EndsWith("/pdf") || contentType == "application/pdf")
|
||||
return ".pdf";
|
||||
|
||||
if (ContentTypeToExtensionsMap.TryGetValue(contentType, out var extensions) && extensions.Any())
|
||||
{
|
||||
// Return the first extension for this content type
|
||||
return extensions.First();
|
||||
}
|
||||
|
||||
return DetermineExtensionFromMimeType(contentType);
|
||||
}
|
||||
public static string GetContentTypeFromExtension(this string filePath)
|
||||
{
|
||||
var extension = filePath.GetFileExtension();
|
||||
|
||||
return MimeMapping.GetValueOrDefault(extension, "application/octet-stream");
|
||||
}
|
||||
|
||||
public static string GetNameAndExtension(this string fileName, string? extension = ".bin")
|
||||
{
|
||||
var currentExtension = fileName.GetFileExtension();
|
||||
if (!string.IsNullOrWhiteSpace(currentExtension))
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
return fileName + extension;
|
||||
}
|
||||
|
||||
public static string GetFileExtension(this string filePath)
|
||||
{
|
||||
return Path.GetExtension(filePath).ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static string DetermineExtensionFromMimeType(string mimeType)
|
||||
{
|
||||
if (mimeType.Contains("/pdf"))
|
||||
return ".pdf";
|
||||
if (mimeType.Contains("image/"))
|
||||
return ".img";
|
||||
if (mimeType.Contains("text/"))
|
||||
return ".txt";
|
||||
if (mimeType.Contains("audio/"))
|
||||
return ".audio";
|
||||
if (mimeType.Contains("video/"))
|
||||
return ".video";
|
||||
|
||||
if (mimeType.StartsWith("file/") || mimeType.StartsWith("@file/"))
|
||||
{
|
||||
var extension = mimeType[(mimeType.IndexOf('/') + 1)..];
|
||||
if (!string.IsNullOrWhiteSpace(extension))
|
||||
return "." + extension;
|
||||
}
|
||||
|
||||
return ".bin";
|
||||
}
|
||||
}
|
||||
18
src/modules/Elsa.IO/Extensions/FilePathExtensions.cs
Normal file
18
src/modules/Elsa.IO/Extensions/FilePathExtensions.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
namespace Elsa.IO.Extensions;
|
||||
|
||||
public static class FilePathExtensions
|
||||
{
|
||||
public static string CleanFilePath(this string filePath)
|
||||
{
|
||||
// Clean up the path - trim quotes and whitespace that might come from copy-paste
|
||||
filePath = filePath.Trim().Trim('"', '\'');
|
||||
|
||||
// Replace backslashes with forward slashes on Unix/Mac systems
|
||||
if (Path.DirectorySeparatorChar == '/')
|
||||
{
|
||||
filePath = filePath.Replace('\\', '/');
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
19
src/modules/Elsa.IO/Extensions/ModuleExtensions.cs
Normal file
19
src/modules/Elsa.IO/Extensions/ModuleExtensions.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.Features.Services;
|
||||
using Elsa.IO.Features;
|
||||
|
||||
namespace Elsa.IO.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for configuring IO services.
|
||||
/// </summary>
|
||||
public static class ModuleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs the IO module.
|
||||
/// </summary>
|
||||
public static IModule UseIO(this IModule module, Action<IOFeature>? configure = null)
|
||||
{
|
||||
return module.Use(configure);
|
||||
}
|
||||
}
|
||||
26
src/modules/Elsa.IO/Features/IOFeature.cs
Normal file
26
src/modules/Elsa.IO/Features/IOFeature.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using Elsa.Features.Abstractions;
|
||||
using Elsa.Features.Services;
|
||||
using Elsa.IO.Contracts;
|
||||
using Elsa.IO.Services;
|
||||
using Elsa.IO.Services.Strategies;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.IO.Features;
|
||||
|
||||
/// <summary>
|
||||
/// A feature that installs IO services for resolving various content types to streams.
|
||||
/// </summary>
|
||||
public class IOFeature(IModule module) : FeatureBase(module)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services.AddScoped<IContentResolverStrategy, StreamContentStrategy>();
|
||||
Services.AddScoped<IContentResolverStrategy, ByteArrayContentStrategy>();
|
||||
Services.AddScoped<IContentResolverStrategy, Base64ContentStrategy>();
|
||||
Services.AddScoped<IContentResolverStrategy, FilePathContentStrategy>();
|
||||
Services.AddScoped<IContentResolverStrategy, TextContentStrategy>();
|
||||
|
||||
Services.AddScoped<IContentResolver, ContentResolver>();
|
||||
}
|
||||
}
|
||||
1
src/modules/Elsa.IO/FodyWeavers.xml
Normal file
1
src/modules/Elsa.IO/FodyWeavers.xml
Normal file
|
|
@ -0,0 +1 @@
|
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd" />
|
||||
33
src/modules/Elsa.IO/Models/BinaryContent.cs
Normal file
33
src/modules/Elsa.IO/Models/BinaryContent.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Elsa.IO.Extensions;
|
||||
|
||||
namespace Elsa.IO.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents normalized binary content with metadata.
|
||||
/// </summary>
|
||||
public class BinaryContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the content.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type (MIME type) based on file extension.
|
||||
/// </summary>
|
||||
public string? ContentType => !string.IsNullOrWhiteSpace(Name)
|
||||
? Path.GetExtension(Name).GetContentTypeFromExtension()
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets optional metadata headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> Headers { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content stream.
|
||||
/// </summary>
|
||||
public Stream Stream { get; init; } = null!;
|
||||
}
|
||||
34
src/modules/Elsa.IO/Services/ContentResolver.cs
Normal file
34
src/modules/Elsa.IO/Services/ContentResolver.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using Elsa.IO.Contracts;
|
||||
using Elsa.IO.Models;
|
||||
using Elsa.IO.Services.Strategies;
|
||||
|
||||
namespace Elsa.IO.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves various content types to BinaryContent using a strategy pattern.
|
||||
/// </summary>
|
||||
public class ContentResolver : IContentResolver
|
||||
{
|
||||
private readonly IEnumerable<IContentResolverStrategy> _strategies;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentResolver"/> class.
|
||||
/// </summary>
|
||||
public ContentResolver(IEnumerable<IContentResolverStrategy> strategies)
|
||||
{
|
||||
_strategies = strategies.OrderBy(s => s.Priority).ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var strategy = _strategies.FirstOrDefault(s => s.CanResolve(content));
|
||||
|
||||
if (strategy == null)
|
||||
{
|
||||
throw new ArgumentException($"Unsupported content type: {content.GetType().Name}");
|
||||
}
|
||||
|
||||
return await strategy.ResolveAsync(content, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
using Elsa.IO.Common;
|
||||
using Elsa.IO.Extensions;
|
||||
using Elsa.IO.Models;
|
||||
|
||||
namespace Elsa.IO.Services.Strategies;
|
||||
|
||||
/// <summary>
|
||||
/// Strategy for handling base64 encoded content.
|
||||
/// </summary>
|
||||
public class Base64ContentStrategy : IContentResolverStrategy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public float Priority => Constants.StrategyPriorities.Base64;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanResolve(object content)
|
||||
{
|
||||
return content is string str && IsBase64String(str);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var str = content.ToString()!;
|
||||
var extension = ".bin";
|
||||
string? name = null;
|
||||
|
||||
if (IsUriDataBase64String(str))
|
||||
{
|
||||
var dataUrlParts = str.Split(';');
|
||||
if (dataUrlParts.Length > 0 && dataUrlParts[0].StartsWith("data:"))
|
||||
{
|
||||
var contentType = dataUrlParts[0][5..];
|
||||
extension = contentType.GetExtensionFromContentType();
|
||||
|
||||
name = "data" + extension;
|
||||
}
|
||||
|
||||
str = str[(str.IndexOf("base64,", StringComparison.Ordinal) + 7)..];
|
||||
}
|
||||
|
||||
var base64Bytes = Convert.FromBase64String(str);
|
||||
var stream = new MemoryStream(base64Bytes);
|
||||
|
||||
return Task.FromResult(new BinaryContent
|
||||
{
|
||||
Name = name?.GetNameAndExtension(extension) ?? "data.bin",
|
||||
Stream = stream,
|
||||
});
|
||||
}
|
||||
|
||||
private static bool IsBase64String(string base64)
|
||||
{
|
||||
if (IsUriDataBase64String(base64))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var buffer = new Span<byte>(new byte[base64.Length]);
|
||||
return Convert.TryFromBase64String(base64, buffer , out _);
|
||||
}
|
||||
|
||||
private static bool IsUriDataBase64String(string base64)
|
||||
{
|
||||
return base64.StartsWith("data:") && base64.Contains("base64");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using Elsa.IO.Common;
|
||||
using Elsa.IO.Models;
|
||||
|
||||
namespace Elsa.IO.Services.Strategies;
|
||||
|
||||
/// <summary>
|
||||
/// Strategy for handling byte array content.
|
||||
/// </summary>
|
||||
public class ByteArrayContentStrategy : IContentResolverStrategy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public float Priority => Constants.StrategyPriorities.ByteArray;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanResolve(object content) => content is byte[];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var bytes = (byte[])content;
|
||||
var stream = new MemoryStream(bytes);
|
||||
|
||||
var result = new BinaryContent
|
||||
{
|
||||
Stream = stream,
|
||||
Name = "data.bin",
|
||||
};
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
using Elsa.IO.Common;
|
||||
using Elsa.IO.Extensions;
|
||||
using Elsa.IO.Models;
|
||||
|
||||
namespace Elsa.IO.Services.Strategies;
|
||||
|
||||
/// <summary>
|
||||
/// Strategy for handling file path content by reading from the filesystem.
|
||||
/// </summary>
|
||||
public class FilePathContentStrategy : IContentResolverStrategy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public float Priority => Constants.StrategyPriorities.FilePath;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanResolve(object content)
|
||||
{
|
||||
if (content is not string filePath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
filePath = filePath.CleanFilePath();
|
||||
|
||||
try
|
||||
{
|
||||
if (Path.IsPathRooted(filePath) && File.Exists(filePath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var normalized = Path.GetFullPath(filePath);
|
||||
if (File.Exists(normalized))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var combined = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), filePath));
|
||||
return File.Exists(combined);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filePath = (string)content;
|
||||
filePath = ResolveActualPath(filePath);
|
||||
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
var fileStream = File.OpenRead(filePath);
|
||||
|
||||
var result = new BinaryContent
|
||||
{
|
||||
Name = fileName.GetNameAndExtension(),
|
||||
Stream = fileStream
|
||||
};
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
catch (Exception ex) when (ex is not FileNotFoundException)
|
||||
{
|
||||
throw new FileNotFoundException($"Error opening file: {content}", content.ToString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string ResolveActualPath(string filePath)
|
||||
{
|
||||
filePath = filePath.CleanFilePath();
|
||||
|
||||
if (Path.IsPathRooted(filePath) && File.Exists(filePath))
|
||||
return filePath;
|
||||
|
||||
var normalized = Path.GetFullPath(filePath);
|
||||
if (File.Exists(normalized))
|
||||
return normalized;
|
||||
|
||||
var combined = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), filePath));
|
||||
if (File.Exists(combined))
|
||||
return combined;
|
||||
|
||||
throw new FileNotFoundException($"Could not find file at path: {filePath}", filePath);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
namespace Elsa.IO.Services.Strategies;
|
||||
|
||||
using Elsa.IO.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a strategy for resolving specific content types to BinaryContent.
|
||||
/// </summary>
|
||||
public interface IContentResolverStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// The priority of the strategy.
|
||||
/// </summary>
|
||||
float Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this strategy can handle the specified content.
|
||||
/// </summary>
|
||||
/// <param name="content">The content to check.</param>
|
||||
/// <returns>True if this strategy can handle the content, false otherwise.</returns>
|
||||
bool CanResolve(object content);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the content to a BinaryContent object that includes the content stream and metadata.
|
||||
/// </summary>
|
||||
/// <param name="content">The content to resolve.</param>
|
||||
/// <param name="cancellationToken">A cancellation token.</param>
|
||||
/// <returns>A BinaryContent object containing the content stream and associated metadata.</returns>
|
||||
Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using Elsa.IO.Common;
|
||||
using Elsa.IO.Extensions;
|
||||
using Elsa.IO.Models;
|
||||
|
||||
namespace Elsa.IO.Services.Strategies;
|
||||
|
||||
/// <summary>
|
||||
/// Strategy for handling Stream content.
|
||||
/// </summary>
|
||||
public class StreamContentStrategy : IContentResolverStrategy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public float Priority => Constants.StrategyPriorities.Stream;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanResolve(object content) => content is Stream;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var stream = (Stream)content;
|
||||
|
||||
string? name = null;
|
||||
if (stream is FileStream fileStream)
|
||||
{
|
||||
name = Path.GetFileName(fileStream.Name);
|
||||
}
|
||||
|
||||
var result = new BinaryContent
|
||||
{
|
||||
Stream = stream,
|
||||
Name = name?.GetNameAndExtension() ?? "data.bin",
|
||||
};
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using System.Text;
|
||||
using Elsa.IO.Common;
|
||||
using Elsa.IO.Models;
|
||||
|
||||
namespace Elsa.IO.Services.Strategies;
|
||||
|
||||
/// <summary>
|
||||
/// Strategy for handling plain text content by encoding as UTF-8.
|
||||
/// </summary>
|
||||
public class TextContentStrategy : IContentResolverStrategy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public float Priority => Constants.StrategyPriorities.Text;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanResolve(object content) => content is string;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<BinaryContent> ResolveAsync(object content, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var textContent = (string)content;
|
||||
var textBytes = Encoding.UTF8.GetBytes(textContent);
|
||||
var stream = new MemoryStream(textBytes);
|
||||
|
||||
return Task.FromResult(new BinaryContent
|
||||
{
|
||||
Name = "text.txt",
|
||||
Stream = stream
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Elsa.MongoDb.Serializers;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Activities.Flowchart.Models;
|
||||
using Elsa.Workflows.Memory;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MongoDB.Bson.Serialization.Serializers;
|
||||
using static MongoDB.Bson.Serialization.BsonSerializer;
|
||||
|
||||
namespace Elsa.MongoDb.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// A hosted service that configures and registers custom MongoDB serializers for various types.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class implements <see cref="IHostedService"/> and is responsible for registering serializers to handle
|
||||
/// specific types such as <see cref="object"/>, <see cref="Type"/>, <see cref="Variable"/>, <see cref="Version"/>,
|
||||
/// <see cref="JsonElement"/>, <see cref="JsonNode"/>, and <see cref="FlowScope"/>.
|
||||
/// It uses helper methods to register these serializers during the application's startup process.
|
||||
/// </remarks>
|
||||
[UsedImplicitly]
|
||||
public class ConfigureMongoDbSerializers(IPayloadSerializer payloadSerializer) : IHostedService
|
||||
{
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
TryRegisterSerializer(typeof(object), new PolymorphicSerializer());
|
||||
TryRegisterSerializer(typeof(Type), new TypeSerializer());
|
||||
TryRegisterSerializer(typeof(Variable), new VariableSerializer());
|
||||
TryRegisterSerializer(typeof(Version), new VersionSerializer());
|
||||
TryRegisterSerializer(typeof(JsonElement), new JsonElementSerializer());
|
||||
TryRegisterSerializer(typeof(JsonNode), new JsonNodeBsonConverter());
|
||||
TryRegisterSerializer(typeof(FlowScope), new FlowScopeSerializer(payloadSerializer));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
52
src/modules/Elsa.MongoDb/Serializers/FlowScopeSerializer.cs
Normal file
52
src/modules/Elsa.MongoDb/Serializers/FlowScopeSerializer.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Activities.Flowchart.Models;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
|
||||
namespace Elsa.MongoDb.Serializers;
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a <see cref="FlowScope"/>.
|
||||
/// </summary>
|
||||
public class FlowScopeSerializer(IPayloadSerializer payloadSerializer) : IBsonSerializer<FlowScope?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Type ValueType => typeof(FlowScope);
|
||||
|
||||
void IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) => Serialize(context, args, (FlowScope)value);
|
||||
object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context, args);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, FlowScope? value)
|
||||
{
|
||||
if (value is null)
|
||||
context.Writer.WriteNull();
|
||||
else
|
||||
{
|
||||
var json = payloadSerializer.Serialize(value);
|
||||
context.Writer.WriteString(json);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public FlowScope Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
|
||||
{
|
||||
var reader = context.Reader;
|
||||
var bsonType = reader.GetCurrentBsonType();
|
||||
|
||||
if (bsonType == BsonType.Null)
|
||||
{
|
||||
reader.ReadNull();
|
||||
return new();
|
||||
}
|
||||
|
||||
if(bsonType == BsonType.String)
|
||||
{
|
||||
var json = context.Reader.ReadString();
|
||||
|
||||
return string.IsNullOrEmpty(json) ? new() : payloadSerializer.Deserialize<FlowScope>(json);
|
||||
}
|
||||
|
||||
return new();
|
||||
}
|
||||
}
|
||||
|
|
@ -50,8 +50,8 @@ public static class WorkflowExecutionContextExtensions
|
|||
public static ActivityWorkItem ScheduleActivityExecutionContext(this WorkflowExecutionContext workflowExecutionContext, ActivityExecutionContext activityExecutionContext, IDictionary<string, object>? input = null, IEnumerable<Variable>? variables = null)
|
||||
{
|
||||
var workItem = new ActivityWorkItem(
|
||||
activityExecutionContext.Activity,
|
||||
input: input,
|
||||
activityExecutionContext.Activity,
|
||||
input: input,
|
||||
variables: variables,
|
||||
existingActivityExecutionContext: activityExecutionContext);
|
||||
workflowExecutionContext.Scheduler.Schedule(workItem);
|
||||
|
|
@ -112,7 +112,7 @@ public static class WorkflowExecutionContextExtensions
|
|||
// Validate that the specified activity is part of the workflow.
|
||||
if (!workflowExecutionContext.NodeActivityLookup.ContainsKey(activityNode.Activity))
|
||||
throw new InvalidOperationException("The specified activity is not part of the workflow.");
|
||||
|
||||
|
||||
var scheduler = workflowExecutionContext.Scheduler;
|
||||
|
||||
if (options?.PreventDuplicateScheduling == true)
|
||||
|
|
@ -145,4 +145,21 @@ public static class WorkflowExecutionContextExtensions
|
|||
var outputRegister = workflowExecutionContext.GetActivityOutputRegister();
|
||||
return outputRegister.FindOutputByActivityId(activityId, outputName);
|
||||
}
|
||||
|
||||
public static IEnumerable<ActivityExecutionContext> FindActivityExecutionContexts(this WorkflowExecutionContext workflowExecutionContext, ActivityHandle activityHandle)
|
||||
{
|
||||
if (activityHandle.ActivityInstanceId != null)
|
||||
return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.Id == activityHandle.ActivityInstanceId);
|
||||
if (activityHandle.ActivityNodeId != null)
|
||||
return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.NodeId == activityHandle.ActivityNodeId);
|
||||
if (activityHandle.ActivityId != null)
|
||||
return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.Activity.Id == activityHandle.ActivityId);
|
||||
if (activityHandle.ActivityHash != null)
|
||||
{
|
||||
var activity = workflowExecutionContext.FindActivityByHash(activityHandle.ActivityHash);
|
||||
return activity != null ? workflowExecutionContext.ActivityExecutionContexts.Where(x => x.Activity.NodeId == activity.NodeId) : [];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
using Elsa.Common;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Pipelines.ActivityExecution;
|
||||
using Elsa.Workflows.State;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Elsa.Workflows.Middleware.Activities;
|
||||
|
|
@ -34,34 +32,14 @@ public class ExceptionHandlingMiddleware(ActivityMiddlewareDelegate next, IIncid
|
|||
catch (Exception e)
|
||||
{
|
||||
logger.LogWarning(e, "An exception was caught from a downstream middleware component");
|
||||
LogExceptionAndTransition(context, e);
|
||||
FaultAncestors(context);
|
||||
context.Fault(e);
|
||||
await HandleIncidentAsync(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogExceptionAndTransition(ActivityExecutionContext context, Exception e)
|
||||
{
|
||||
context.Exception = e;
|
||||
context.TransitionTo(ActivityStatus.Faulted);
|
||||
var activity = context.Activity;
|
||||
var exceptionState = ExceptionState.FromException(e);
|
||||
var now = systemClock.UtcNow;
|
||||
var incident = new ActivityIncident(activity.Id, activity.NodeId ,activity.Type, e.Message, exceptionState, now);
|
||||
context.WorkflowExecutionContext.Incidents.Add(incident);
|
||||
}
|
||||
|
||||
private async Task HandleIncidentAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var strategy = await incidentStrategyResolver.ResolveStrategyAsync(context);
|
||||
strategy.HandleIncident(context);
|
||||
}
|
||||
|
||||
private static void FaultAncestors(ActivityExecutionContext context)
|
||||
{
|
||||
var ancestors = context.GetAncestors();
|
||||
|
||||
foreach (var ancestor in ancestors)
|
||||
ancestor.TransitionTo(ActivityStatus.Faulted);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,12 +5,33 @@ namespace Elsa.Workflows.Models;
|
|||
/// </summary>
|
||||
public class ActivityHandle
|
||||
{
|
||||
public static ActivityHandle FromActivityId(string activityId) => new() { ActivityId = activityId };
|
||||
public static ActivityHandle FromActivityNodeId(string activityNodeId) => new() { ActivityNodeId = activityNodeId };
|
||||
public static ActivityHandle FromActivityInstanceId(string activityInstanceId) => new() { ActivityInstanceId = activityInstanceId };
|
||||
public static ActivityHandle FromActivityHash(string activityHash) => new() { ActivityHash = activityHash };
|
||||
public static ActivityHandle FromActivityId(string activityId) => new()
|
||||
{
|
||||
ActivityId = activityId
|
||||
};
|
||||
|
||||
public static ActivityHandle FromActivityNodeId(string activityNodeId) => new()
|
||||
{
|
||||
ActivityNodeId = activityNodeId
|
||||
};
|
||||
|
||||
public static ActivityHandle FromActivityInstanceId(string activityInstanceId) => new()
|
||||
{
|
||||
ActivityInstanceId = activityInstanceId
|
||||
};
|
||||
|
||||
public static ActivityHandle FromActivityHash(string activityHash) => new()
|
||||
{
|
||||
ActivityHash = activityHash
|
||||
};
|
||||
|
||||
public string? ActivityId { get; init; }
|
||||
public string? ActivityNodeId { get; init;}
|
||||
public string? ActivityInstanceId { get; init;}
|
||||
public string? ActivityHash { get; init;}
|
||||
public string? ActivityNodeId { get; init; }
|
||||
public string? ActivityInstanceId { get; init; }
|
||||
public string? ActivityHash { get; init; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ActivityId ?? (ActivityNodeId ?? (ActivityInstanceId ?? (ActivityHash ?? "")));
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ namespace Elsa.Workflows.Management.Features;
|
|||
/// <summary>
|
||||
/// Installs and configures the workflow management feature.
|
||||
/// </summary>
|
||||
[DependsOn(typeof(CompressionFeature))]
|
||||
[DependsOn(typeof(StringCompressionFeature))]
|
||||
[DependsOn(typeof(MediatorFeature))]
|
||||
[DependsOn(typeof(MemoryCacheFeature))]
|
||||
[DependsOn(typeof(SystemClockFeature))]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.State;
|
||||
|
||||
|
|
@ -8,6 +9,12 @@ namespace Elsa.Workflows.Management.Mappers;
|
|||
/// </summary>
|
||||
public class WorkflowStateMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// [Obsolete] The property key name used to store the workflow instance name.
|
||||
/// </summary>
|
||||
[Obsolete("This constant is obsolete and retained only for backward compatibility. Avoid using it in new code.")]
|
||||
private const string WorkflowInstanceNameKey = "WorkflowInstanceName";
|
||||
|
||||
/// <summary>
|
||||
/// Maps a workflow state to a workflow instance.
|
||||
/// </summary>
|
||||
|
|
@ -43,6 +50,10 @@ public class WorkflowStateMapper
|
|||
target.UpdatedAt = source.UpdatedAt;
|
||||
target.FinishedAt = source.FinishedAt;
|
||||
target.WorkflowState = source;
|
||||
|
||||
// Keep for backward compatibility with workflow instances created before the introduction of the Name property.
|
||||
if (source.Properties.TryGetValue<string>(WorkflowInstanceNameKey, out var name))
|
||||
target.Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Reference in a new issue