diff --git a/Elsa.sln.DotSettings b/Elsa.sln.DotSettings
index 176b45769..0c8033163 100644
--- a/Elsa.sln.DotSettings
+++ b/Elsa.sln.DotSettings
@@ -3,6 +3,7 @@
True
True
True
+ True
True
True
True
diff --git a/src/bundles/Elsa.WorkflowServer.Web/Program.cs b/src/bundles/Elsa.WorkflowServer.Web/Program.cs
index 5d20b3b54..d7f8181d3 100644
--- a/src/bundles/Elsa.WorkflowServer.Web/Program.cs
+++ b/src/bundles/Elsa.WorkflowServer.Web/Program.cs
@@ -19,7 +19,7 @@ using Proto.Persistence.SqlServer;
const bool useMongoDb = false;
const bool useSqlServer = false;
const bool useDapper = false;
-const bool useProtoActor = false;
+const bool useProtoActor = true;
const bool useHangfire = false;
var builder = WebApplication.CreateBuilder(args);
diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/Bookmark.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/Bookmark.cs
index 085193157..f4a48d1bf 100644
--- a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/Bookmark.cs
+++ b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/Bookmark.cs
@@ -23,7 +23,8 @@ public record Bookmark(
string ActivityNodeId,
string ActivityInstanceId,
bool AutoBurn = true,
- string? CallbackMethodName = default
+ string? CallbackMethodName = default,
+ IDictionary? Metadata = default
)
{
///
diff --git a/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs b/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
index d57e3ed8a..768f1bc0c 100644
--- a/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
@@ -31,6 +31,7 @@ public class Initial : Migration
.WithColumn("ActivityInstanceId").AsString().NotNullable()
.WithColumn("Hash").AsString().Nullable()
.WithColumn("SerializedPayload").AsString().Nullable()
+ .WithColumn("SerializedMetadata").AsString().Nullable()
.WithColumn("CreatedAt").AsDateTimeOffset().NotNullable();
IfDatabase("Sqlite")
@@ -43,6 +44,7 @@ public class Initial : Migration
.WithColumn("ActivityInstanceId").AsString().NotNullable()
.WithColumn("Hash").AsString().Nullable()
.WithColumn("SerializedPayload").AsString().Nullable()
+ .WithColumn("SerializedMetadata").AsString().Nullable()
.WithColumn("CreatedAt").AsDateTime2().NotNullable();
IfDatabase("SqlServer", "Oracle", "MySql", "Postgres")
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs
index f366840f9..caa1a9c9b 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs
@@ -9,5 +9,6 @@ internal class StoredBookmarkRecord
public string? CorrelationId { get; set; }
public string? ActivityInstanceId { get; set; }
public string? SerializedPayload { get; set; }
+ public string? SerializedMetadata { get; set; }
public DateTimeOffset CreatedAt { get; set; }
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperBookmarkStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperBookmarkStore.cs
index e19c325cd..164bfd5b9 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperBookmarkStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperBookmarkStore.cs
@@ -48,7 +48,7 @@ public class DapperBookmarkStore : IBookmarkStore
{
return await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
-
+
private void ApplyFilter(ParameterizedQuery query, BookmarkFilter filter)
{
query
@@ -62,7 +62,7 @@ public class DapperBookmarkStore : IBookmarkStore
.Is(nameof(StoredBookmarkRecord.ActivityInstanceId), filter.ActivityInstanceId)
;
}
-
+
private IEnumerable Map(IEnumerable source) => source.Select(Map);
private StoredBookmarkRecord Map(StoredBookmark source)
@@ -76,10 +76,11 @@ public class DapperBookmarkStore : IBookmarkStore
ActivityTypeName = source.ActivityTypeName,
Hash = source.Hash,
SerializedPayload = source.Payload != null ? _payloadSerializer.Serialize(source.Payload) : default,
+ SerializedMetadata = source.Metadata != null ? _payloadSerializer.Serialize(source.Metadata) : default,
CreatedAt = source.CreatedAt
};
}
-
+
private StoredBookmark Map(StoredBookmarkRecord source)
{
return new StoredBookmark
@@ -91,6 +92,7 @@ public class DapperBookmarkStore : IBookmarkStore
ActivityTypeName = source.ActivityTypeName,
Hash = source.Hash,
Payload = source.SerializedPayload != null ? _payloadSerializer.Deserialize(source.SerializedPayload) : default,
+ Metadata = source.SerializedMetadata != null ? _payloadSerializer.Deserialize>(source.SerializedMetadata) : default,
CreatedAt = source.CreatedAt
};
}
diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230911202213_Initial.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230915185743_Initial.Designer.cs
similarity index 98%
rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230911202213_Initial.Designer.cs
rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230915185743_Initial.Designer.cs
index 5e7dc03e4..e0688e9b0 100644
--- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230911202213_Initial.Designer.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230915185743_Initial.Designer.cs
@@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime
{
[DbContext(typeof(RuntimeElsaDbContext))]
- [Migration("20230911202213_Initial")]
+ [Migration("20230915185743_Initial")]
partial class Initial
{
///
@@ -127,6 +127,9 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime
.IsRequired()
.HasColumnType("varchar(255)");
+ b.Property("SerializedMetadata")
+ .HasColumnType("longtext");
+
b.Property("SerializedPayload")
.HasColumnType("longtext");
diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230911202213_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230915185743_Initial.cs
similarity index 99%
rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230911202213_Initial.cs
rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230915185743_Initial.cs
index 4ea5076a7..68020f524 100644
--- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230911202213_Initial.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230915185743_Initial.cs
@@ -70,6 +70,8 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime
CorrelationId = table.Column(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedAt = table.Column(type: "datetime(6)", nullable: false),
+ SerializedMetadata = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
SerializedPayload = table.Column(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
index 0eb4a2e62..1a2441f74 100644
--- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
@@ -124,6 +124,9 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime
.IsRequired()
.HasColumnType("varchar(255)");
+ b.Property("SerializedMetadata")
+ .HasColumnType("longtext");
+
b.Property("SerializedPayload")
.HasColumnType("longtext");
diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230911202223_Initial.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230915185758_Initial.Designer.cs
similarity index 98%
rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230911202223_Initial.Designer.cs
rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230915185758_Initial.Designer.cs
index d74a13b62..1b5b43127 100644
--- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230911202223_Initial.Designer.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230915185758_Initial.Designer.cs
@@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime
{
[DbContext(typeof(RuntimeElsaDbContext))]
- [Migration("20230911202223_Initial")]
+ [Migration("20230915185758_Initial")]
partial class Initial
{
///
@@ -130,6 +130,9 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime
.IsRequired()
.HasColumnType("text");
+ b.Property("SerializedMetadata")
+ .HasColumnType("text");
+
b.Property("SerializedPayload")
.HasColumnType("text");
diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230911202223_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230915185758_Initial.cs
similarity index 99%
rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230911202223_Initial.cs
rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230915185758_Initial.cs
index fd85141ec..e962de2be 100644
--- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230911202223_Initial.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230915185758_Initial.cs
@@ -51,6 +51,7 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime
ActivityInstanceId = table.Column(type: "text", nullable: true),
CorrelationId = table.Column(type: "text", nullable: true),
CreatedAt = table.Column(type: "timestamp with time zone", nullable: false),
+ SerializedMetadata = table.Column(type: "text", nullable: true),
SerializedPayload = table.Column(type: "text", nullable: true)
},
constraints: table =>
diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
index 300f2b9d0..967e84dcc 100644
--- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
@@ -127,6 +127,9 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime
.IsRequired()
.HasColumnType("text");
+ b.Property("SerializedMetadata")
+ .HasColumnType("text");
+
b.Property("SerializedPayload")
.HasColumnType("text");
diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230911202217_Initial.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230915185751_Initial.Designer.cs
similarity index 98%
rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230911202217_Initial.Designer.cs
rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230915185751_Initial.Designer.cs
index df161b077..1028f836b 100644
--- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230911202217_Initial.Designer.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230915185751_Initial.Designer.cs
@@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime
{
[DbContext(typeof(RuntimeElsaDbContext))]
- [Migration("20230911202217_Initial")]
+ [Migration("20230915185751_Initial")]
partial class Initial
{
///
@@ -130,6 +130,9 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime
.IsRequired()
.HasColumnType("nvarchar(450)");
+ b.Property("SerializedMetadata")
+ .HasColumnType("nvarchar(max)");
+
b.Property("SerializedPayload")
.HasColumnType("nvarchar(max)");
diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230911202217_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230915185751_Initial.cs
similarity index 99%
rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230911202217_Initial.cs
rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230915185751_Initial.cs
index 255977b24..0cba84f36 100644
--- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230911202217_Initial.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230915185751_Initial.cs
@@ -51,6 +51,7 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime
ActivityInstanceId = table.Column(type: "nvarchar(450)", nullable: true),
CorrelationId = table.Column(type: "nvarchar(max)", nullable: true),
CreatedAt = table.Column(type: "datetimeoffset", nullable: false),
+ SerializedMetadata = table.Column(type: "nvarchar(max)", nullable: true),
SerializedPayload = table.Column(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
index 5506bcd1b..2904fb96c 100644
--- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
@@ -127,6 +127,9 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime
.IsRequired()
.HasColumnType("nvarchar(450)");
+ b.Property("SerializedMetadata")
+ .HasColumnType("nvarchar(max)");
+
b.Property("SerializedPayload")
.HasColumnType("nvarchar(max)");
diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230911202220_Initial.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230915185754_Initial.Designer.cs
similarity index 98%
rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230911202220_Initial.Designer.cs
rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230915185754_Initial.Designer.cs
index 24c3efb09..378bcb984 100644
--- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230911202220_Initial.Designer.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230915185754_Initial.Designer.cs
@@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime
{
[DbContext(typeof(RuntimeElsaDbContext))]
- [Migration("20230911202220_Initial")]
+ [Migration("20230915185754_Initial")]
partial class Initial
{
///
@@ -125,6 +125,9 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime
.IsRequired()
.HasColumnType("TEXT");
+ b.Property("SerializedMetadata")
+ .HasColumnType("TEXT");
+
b.Property("SerializedPayload")
.HasColumnType("TEXT");
diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230911202220_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230915185754_Initial.cs
similarity index 99%
rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230911202220_Initial.cs
rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230915185754_Initial.cs
index 3388f4c8a..520f3ba13 100644
--- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230911202220_Initial.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20230915185754_Initial.cs
@@ -45,6 +45,7 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime
ActivityInstanceId = table.Column(type: "TEXT", nullable: true),
CorrelationId = table.Column(type: "TEXT", nullable: true),
CreatedAt = table.Column(type: "TEXT", nullable: false),
+ SerializedMetadata = table.Column(type: "TEXT", nullable: true),
SerializedPayload = table.Column(type: "TEXT", nullable: true)
},
constraints: table =>
diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
index fae87ddf7..90684a2a6 100644
--- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs
@@ -122,6 +122,9 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime
.IsRequired()
.HasColumnType("TEXT");
+ b.Property("SerializedMetadata")
+ .HasColumnType("TEXT");
+
b.Property("SerializedPayload")
.HasColumnType("TEXT");
diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/BookmarkStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/BookmarkStore.cs
index 9fd05ada0..abe18b015 100644
--- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/BookmarkStore.cs
+++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/BookmarkStore.cs
@@ -31,10 +31,11 @@ public class EFCoreBookmarkStore : IBookmarkStore
///
public async ValueTask DeleteAsync(BookmarkFilter filter, CancellationToken cancellationToken = default) => await _store.DeleteWhereAsync(filter.Apply, cancellationToken);
-
+
private ValueTask OnSaveAsync(RuntimeElsaDbContext dbContext, StoredBookmark entity, CancellationToken cancellationToken)
{
dbContext.Entry(entity).Property("SerializedPayload").CurrentValue = entity.Payload != null ? _serializer.Serialize(entity.Payload) : default;
+ dbContext.Entry(entity).Property("SerializedMetadata").CurrentValue = entity.Metadata != null ? _serializer.Serialize(entity.Metadata) : default;
return default;
}
@@ -43,9 +44,11 @@ public class EFCoreBookmarkStore : IBookmarkStore
if (entity is null)
return default;
- var json = dbContext.Entry(entity).Property("SerializedPayload").CurrentValue;
- entity.Payload = !string.IsNullOrEmpty(json) ? _serializer.Deserialize(json) : null;
-
+ var payloadJson = dbContext.Entry(entity).Property("SerializedPayload").CurrentValue;
+ var metadataJson = dbContext.Entry(entity).Property("SerializedMetadata").CurrentValue;
+ entity.Payload = !string.IsNullOrEmpty(payloadJson) ? _serializer.Deserialize(payloadJson) : null;
+ entity.Metadata = !string.IsNullOrEmpty(metadataJson) ? _serializer.Deserialize>(metadataJson) : null;
+
return default;
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs
index 2b35ce80d..7360307e1 100644
--- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs
+++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs
@@ -78,7 +78,9 @@ public class Configurations :
public void Configure(EntityTypeBuilder builder)
{
builder.Ignore(x => x.Payload);
+ builder.Ignore(x => x.Metadata);
builder.Property("SerializedPayload");
+ builder.Property("SerializedMetadata");
builder.HasKey(x => x.BookmarkId);
builder.HasIndex(x => x.ActivityTypeName, $"IX_{nameof(StoredBookmark)}_{nameof(StoredBookmark.ActivityTypeName)}");
builder.HasIndex(x => x.Hash, $"IX_{nameof(StoredBookmark)}_{nameof(StoredBookmark.Hash)}");
diff --git a/src/modules/Elsa.Http/Abstractions/DownloadableProviderBase.cs b/src/modules/Elsa.Http/Abstractions/DownloadableProviderBase.cs
new file mode 100644
index 000000000..7f1f9a696
--- /dev/null
+++ b/src/modules/Elsa.Http/Abstractions/DownloadableProviderBase.cs
@@ -0,0 +1,49 @@
+using Elsa.Http.Contexts;
+using Elsa.Http.Contracts;
+using Elsa.Http.Models;
+
+namespace Elsa.Http.Abstractions;
+
+///
+/// Provides a base class for implementations.
+///
+public abstract class DownloadableProviderBase : IDownloadableProvider
+{
+ ///
+ public virtual float Priority => 0;
+
+ ///
+ public abstract bool GetSupportsContent(object content);
+
+ ///
+ /// Returns a list of downloadables from the specified content.
+ ///
+ protected virtual async ValueTask> GetDownloadablesAsync(DownloadableContext context)
+ {
+ var downloadable = await GetDownloadableAsync(context);
+ return new[]{ downloadable };
+ }
+
+ ///
+ /// Returns a downloadable from the specified content.
+ ///
+ protected virtual ValueTask GetDownloadableAsync(DownloadableContext context)
+ {
+ var downloadable = GetDownloadable(context);
+ return new (downloadable);
+ }
+
+ ///
+ /// Returns a downloadable from the specified content.
+ ///
+ /// This method is not implemented. The derived class must implement at least one of the GetDownloadable methods.
+ protected virtual Downloadable GetDownloadable(DownloadableContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ async ValueTask> IDownloadableProvider.GetDownloadablesAsync(DownloadableContext context)
+ {
+ return await GetDownloadablesAsync(context);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Http/Activities/HttpEndpoint.cs b/src/modules/Elsa.Http/Activities/HttpEndpoint.cs
index 1d3681407..7e1372260 100644
--- a/src/modules/Elsa.Http/Activities/HttpEndpoint.cs
+++ b/src/modules/Elsa.Http/Activities/HttpEndpoint.cs
@@ -1,6 +1,7 @@
using System.Runtime.CompilerServices;
using Elsa.Expressions.Models;
using Elsa.Extensions;
+using Elsa.Http.Bookmarks;
using Elsa.Http.Contracts;
using Elsa.Http.Models;
using Elsa.Workflows.Core;
@@ -103,7 +104,7 @@ public class HttpEndpoint : Trigger
{
// 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);
+ context.CreateBookmark(OnResumeAsync, BookmarkMetadata.HttpCrossBoundary);
return;
}
diff --git a/src/modules/Elsa.Http/Activities/WriteFileHttpResponse.cs b/src/modules/Elsa.Http/Activities/WriteFileHttpResponse.cs
new file mode 100644
index 000000000..3c2837b94
--- /dev/null
+++ b/src/modules/Elsa.Http/Activities/WriteFileHttpResponse.cs
@@ -0,0 +1,165 @@
+using System.IO.Compression;
+using System.Net;
+using Elsa.Extensions;
+using Elsa.Http.Contracts;
+using Elsa.Http.Models;
+using Elsa.Workflows.Core;
+using Elsa.Workflows.Core.Attributes;
+using Elsa.Workflows.Core.Exceptions;
+using Elsa.Workflows.Core.Models;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.StaticFiles;
+
+namespace Elsa.Http;
+
+///
+/// Sends a file to the HTTP response.
+///
+[Activity("Elsa", "HTTP", "Send one ore more files (zipped) to the HTTP response.", DisplayName = "HTTP File Response")]
+public class WriteFileHttpResponse : Activity
+{
+ ///
+ /// The MIME type of the file to serve.
+ ///
+ [Input(Description = "The content type of the file to serve. Leave empty to let the system determine the content type.")]
+ public Input ContentType { get; set; } = default!;
+
+ ///
+ /// The name of the file to serve.
+ ///
+ [Input(Description = "The name of the file to serve. Leave empty to let the system determine the file name.")]
+ public Input FileName { get; set; } = default!;
+
+ ///
+ /// The file content to serve. Supports byte array, streams, string, Uri and an array of the aforementioned types.
+ ///
+ [Input(Description = "The file content to serve. Supports various types, such as byte array, stream, string, Uri, Downloadable and a (mixed) array of the aforementioned types.")]
+ public Input