Merge remote-tracking branch 'origin/develop/3.5.0' into develop/3.6.0
This commit is contained in:
commit
931d242f40
|
|
@ -180,6 +180,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ElsaStudioVersion>3.5.0-preview.1092</ElsaStudioVersion>
|
<ElsaStudioVersion>3.5.0-preview.1092</ElsaStudioVersion>
|
||||||
|
<MicrosoftVersion>9.0.7</MicrosoftVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1"/>
|
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1"/>
|
||||||
|
|
@ -209,7 +210,7 @@
|
||||||
<PackageVersion Include="DistributedLock.FileSystem" Version="1.0.3"/>
|
<PackageVersion Include="DistributedLock.FileSystem" Version="1.0.3"/>
|
||||||
<PackageVersion Include="DistributedLock.Postgres" Version="1.3.0"/>
|
<PackageVersion Include="DistributedLock.Postgres" Version="1.3.0"/>
|
||||||
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3"/>
|
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3"/>
|
||||||
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="9.0.6"/>
|
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Elsa.Studio" Version="$(ElsaStudioVersion)"/>
|
<PackageVersion Include="Elsa.Studio" Version="$(ElsaStudioVersion)"/>
|
||||||
<PackageVersion Include="Elsa.Studio.Agents" Version="$(ElsaStudioVersion)"/>
|
<PackageVersion Include="Elsa.Studio.Agents" Version="$(ElsaStudioVersion)"/>
|
||||||
<PackageVersion Include="Elsa.Studio.Core.BlazorWasm" Version="$(ElsaStudioVersion)"/>
|
<PackageVersion Include="Elsa.Studio.Core.BlazorWasm" Version="$(ElsaStudioVersion)"/>
|
||||||
|
|
@ -234,7 +235,7 @@
|
||||||
<PackageVersion Include="Humanizer.Core" Version="2.14.1"/>
|
<PackageVersion Include="Humanizer.Core" Version="2.14.1"/>
|
||||||
<PackageVersion Include="IronCompress" Version="1.6.3"/>
|
<PackageVersion Include="IronCompress" Version="1.6.3"/>
|
||||||
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0"/>
|
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0"/>
|
||||||
<PackageVersion Include="Jint" Version="4.2.2"/>
|
<PackageVersion Include="Jint" Version="4.3.0"/>
|
||||||
<PackageVersion Include="LinqKit.Core" Version="1.2.8"/>
|
<PackageVersion Include="LinqKit.Core" Version="1.2.8"/>
|
||||||
<PackageVersion Include="MailKit" Version="4.12.1"/>
|
<PackageVersion Include="MailKit" Version="4.12.1"/>
|
||||||
<PackageVersion Include="MassTransit" Version="8.4.1"/>
|
<PackageVersion Include="MassTransit" Version="8.4.1"/>
|
||||||
|
|
@ -293,8 +294,8 @@
|
||||||
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.5"/>
|
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.5"/>
|
||||||
<PackageVersion Include="System.Net.Http" Version="4.3.4"/>
|
<PackageVersion Include="System.Net.Http" Version="4.3.4"/>
|
||||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1"/>
|
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1"/>
|
||||||
<PackageVersion Include="System.Formats.Asn1" Version="9.0.6"/>
|
<PackageVersion Include="System.Formats.Asn1" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="System.Text.Json" Version="9.0.6"/>
|
<PackageVersion Include="System.Text.Json" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Testcontainers" Version="4.5.0"/>
|
<PackageVersion Include="Testcontainers" Version="4.5.0"/>
|
||||||
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.5.0"/>
|
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.5.0"/>
|
||||||
<PackageVersion Include="Testcontainers.RabbitMq" Version="4.5.0"/>
|
<PackageVersion Include="Testcontainers.RabbitMq" Version="4.5.0"/>
|
||||||
|
|
@ -302,36 +303,36 @@
|
||||||
<PackageVersion Include="ThrottleDebounce" Version="2.0.1"/>
|
<PackageVersion Include="ThrottleDebounce" Version="2.0.1"/>
|
||||||
<PackageVersion Include="WebhooksCore" Version="0.0.1"/>
|
<PackageVersion Include="WebhooksCore" Version="0.0.1"/>
|
||||||
<PackageVersion Include="Yarp.ReverseProxy" Version="2.3.0"/>
|
<PackageVersion Include="Yarp.ReverseProxy" Version="2.3.0"/>
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Data.Sqlite" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.6"/>
|
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftVersion)"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.6.0"/>
|
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.6.0"/>
|
||||||
<PackageVersion Include="Microsoft.Extensions.Resilience" Version="9.6.0"/>
|
<PackageVersion Include="Microsoft.Extensions.Resilience" Version="9.6.0"/>
|
||||||
<PackageVersion Include="MySql.Data" Version="9.3.0"/>
|
<PackageVersion Include="MySql.Data" Version="9.3.0"/>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ public abstract class Entity
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the ID of this entity.
|
/// Gets or sets the ID of this entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; set; } = default!;
|
public string Id { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the ID of the tenant that own this entity.
|
/// Gets or sets the ID of the tenant that own this entity.
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ public class ConfigureEngineWithCommonFunctions(IOptions<JintOptions> options) :
|
||||||
engine.SetValue("bytesFromBase64", (Func<string, byte[]>)(value => Convert.FromBase64String(value)));
|
engine.SetValue("bytesFromBase64", (Func<string, byte[]>)(value => Convert.FromBase64String(value)));
|
||||||
engine.SetValue("stringToBase64", (Func<string, string>)(value => Convert.ToBase64String(Encoding.UTF8.GetBytes(value))));
|
engine.SetValue("stringToBase64", (Func<string, string>)(value => Convert.ToBase64String(Encoding.UTF8.GetBytes(value))));
|
||||||
engine.SetValue("stringFromBase64", (Func<string, string>)(value => Encoding.UTF8.GetString(Convert.FromBase64String(value))));
|
engine.SetValue("stringFromBase64", (Func<string, string>)(value => Encoding.UTF8.GetString(Convert.FromBase64String(value))));
|
||||||
|
engine.SetValue("streamToBytes", (Func<Stream, byte[]>)(value => StreamToBytes(value)));
|
||||||
|
engine.SetValue("streamToBase64", (Func<Stream, string>)(value => Convert.ToBase64String(StreamToBytes(value))));
|
||||||
|
|
||||||
// Deprecated, use newGuidString instead.
|
// Deprecated, use newGuidString instead.
|
||||||
engine.SetValue("getGuidString", (Func<string>)(() => Guid.NewGuid().ToString()));
|
engine.SetValue("getGuidString", (Func<string>)(() => Guid.NewGuid().ToString()));
|
||||||
|
|
@ -67,7 +69,7 @@ public class ConfigureEngineWithCommonFunctions(IOptions<JintOptions> options) :
|
||||||
engine.SetValue("getShortGuid", (Func<string>)(() => Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "")));
|
engine.SetValue("getShortGuid", (Func<string>)(() => Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "")));
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Serialize(object value)
|
private string Serialize(object value)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Serialize(value, _jsonSerializerOptions);
|
return JsonSerializer.Serialize(value, _jsonSerializerOptions);
|
||||||
|
|
@ -82,4 +84,11 @@ public class ConfigureEngineWithCommonFunctions(IOptions<JintOptions> options) :
|
||||||
options.Converters.Add(new JsonStringEnumConverter());
|
options.Converters.Add(new JsonStringEnumConverter());
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] StreamToBytes(Stream stream)
|
||||||
|
{
|
||||||
|
using var memoryStream = new MemoryStream();
|
||||||
|
stream.CopyTo(memoryStream);
|
||||||
|
return memoryStream.ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +136,16 @@ internal class CommonFunctionsDefinitionProvider(ITypeAliasRegistry typeAliasReg
|
||||||
.Name("stringToBase64")
|
.Name("stringToBase64")
|
||||||
.Parameter("value", "string")
|
.Parameter("value", "string")
|
||||||
.ReturnType("string"));
|
.ReturnType("string"));
|
||||||
|
|
||||||
|
yield return CreateFunctionDefinition(builder => builder
|
||||||
|
.Name("streamToBytes")
|
||||||
|
.Parameter("value", "Stream")
|
||||||
|
.ReturnType("Byte[]"));
|
||||||
|
|
||||||
|
yield return CreateFunctionDefinition(builder => builder
|
||||||
|
.Name("streamToBase64")
|
||||||
|
.Parameter("value", "Stream")
|
||||||
|
.ReturnType("string"));
|
||||||
|
|
||||||
if (!options.Value.DisableWrappers)
|
if (!options.Value.DisableWrappers)
|
||||||
{
|
{
|
||||||
|
|
@ -151,7 +161,7 @@ internal class CommonFunctionsDefinitionProvider(ITypeAliasRegistry typeAliasReg
|
||||||
|
|
||||||
// set{Variable}.
|
// set{Variable}.
|
||||||
yield return CreateFunctionDefinition(builder => builder.Name($"set{pascalName}").Parameter("value", typeAlias));
|
yield return CreateFunctionDefinition(builder => builder.Name($"set{pascalName}").Parameter("value", typeAlias));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +80,7 @@ public class CreateZipArchive : CodeActivity<Stream>
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, leaveOpen: true);
|
using var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Update, leaveOpen: true);
|
||||||
var entryIndex = 0;
|
var entryIndex = 0;
|
||||||
|
|
||||||
var compressionLevel = CompressionLevel.Get(context);
|
var compressionLevel = CompressionLevel.Get(context);
|
||||||
|
|
@ -122,19 +122,87 @@ public class CreateZipArchive : CodeActivity<Stream>
|
||||||
CompressionLevel compressionLevel)
|
CompressionLevel compressionLevel)
|
||||||
{
|
{
|
||||||
var binaryContent = await resolver.ResolveAsync(entryContent, context.CancellationToken);
|
var binaryContent = await resolver.ResolveAsync(entryContent, context.CancellationToken);
|
||||||
|
|
||||||
var entryName = binaryContent.Name?.GetNameAndExtension()
|
var entryName = binaryContent.Name?.GetNameAndExtension()
|
||||||
?? string.Format(DefaultEntryNameFormat, entryIndex + 1);
|
?? string.Format(DefaultEntryNameFormat, entryIndex + 1);
|
||||||
|
|
||||||
|
// Get a unique name following Windows convention
|
||||||
|
entryName = GetUniqueEntryName(zipArchive, entryName);
|
||||||
|
|
||||||
var archiveEntry = zipArchive.CreateEntry(entryName, compressionLevel);
|
var archiveEntry = zipArchive.CreateEntry(entryName, compressionLevel);
|
||||||
|
|
||||||
await using var entryStream = archiveEntry.Open();
|
await using var entryStream = archiveEntry.Open();
|
||||||
await binaryContent.Stream.CopyToAsync(entryStream, context.CancellationToken);
|
await binaryContent.Stream.CopyToAsync(entryStream, context.CancellationToken);
|
||||||
await entryStream.FlushAsync(context.CancellationToken);
|
await entryStream.FlushAsync(context.CancellationToken);
|
||||||
|
|
||||||
if (entryContent is not Stream)
|
if (entryContent is not Stream)
|
||||||
{
|
{
|
||||||
await binaryContent.Stream.DisposeAsync();
|
await binaryContent.Stream.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetUniqueEntryName(ZipArchive zipArchive, string originalName)
|
||||||
|
{
|
||||||
|
var filenameWithoutExtension = Path.GetFileNameWithoutExtension(originalName);
|
||||||
|
var extension = Path.GetExtension(originalName);
|
||||||
|
|
||||||
|
var originalExists = false;
|
||||||
|
var highestIndex = 0;
|
||||||
|
|
||||||
|
foreach (var entry in zipArchive.Entries)
|
||||||
|
{
|
||||||
|
if (!entry.Name.Equals(originalName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
originalExists = true;
|
||||||
|
|
||||||
|
var entryNameWithoutExtension = Path.GetFileNameWithoutExtension(entry.Name);
|
||||||
|
var entryExtension = Path.GetExtension(entry.Name);
|
||||||
|
|
||||||
|
// Only process entries with the same extension
|
||||||
|
if (!entryExtension.Equals(extension, StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if this entry follows our naming pattern
|
||||||
|
highestIndex = HighestEntryNameIndex(entryNameWithoutExtension, filenameWithoutExtension, highestIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!originalExists)
|
||||||
|
{
|
||||||
|
return originalName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{filenameWithoutExtension}({highestIndex + 1}){extension}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int HighestEntryNameIndex(string entryNameWithoutExtension, string filenameWithoutExtension,
|
||||||
|
int highestIndex)
|
||||||
|
{
|
||||||
|
if (!entryNameWithoutExtension.StartsWith(filenameWithoutExtension, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
entryNameWithoutExtension.Length <= filenameWithoutExtension.Length ||
|
||||||
|
entryNameWithoutExtension[filenameWithoutExtension.Length] != '(')
|
||||||
|
{
|
||||||
|
return highestIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the number between parentheses
|
||||||
|
var closingParenIndex = entryNameWithoutExtension.LastIndexOf(')');
|
||||||
|
if (closingParenIndex <= filenameWithoutExtension.Length + 1)
|
||||||
|
{
|
||||||
|
return highestIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexStr = entryNameWithoutExtension.Substring(
|
||||||
|
filenameWithoutExtension.Length + 1,
|
||||||
|
closingParenIndex - filenameWithoutExtension.Length - 1);
|
||||||
|
|
||||||
|
if (int.TryParse(indexStr, out var index))
|
||||||
|
{
|
||||||
|
highestIndex = Math.Max(highestIndex, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return highestIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ public class ZipEntryContentStrategy(IServiceProvider serviceProvider) : IConten
|
||||||
var innerContentName = innerContent.Name?.GetNameAndExtension();
|
var innerContentName = innerContent.Name?.GetNameAndExtension();
|
||||||
var innerContentExtension = Path.GetExtension(innerContentName);
|
var innerContentExtension = Path.GetExtension(innerContentName);
|
||||||
innerContent.Name = !string.IsNullOrWhiteSpace(innerContentExtension)
|
innerContent.Name = !string.IsNullOrWhiteSpace(innerContentExtension)
|
||||||
? zipEntry.EntryName + innerContentExtension
|
? Path.HasExtension(zipEntry.EntryName) ? zipEntry.EntryName : zipEntry.EntryName + innerContentExtension
|
||||||
: innerContent.Name;
|
: innerContent.Name;
|
||||||
|
|
||||||
return innerContent;
|
return innerContent;
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,62 @@ public static class ContentTypeExtensions
|
||||||
return Path.GetExtension(filePath).ToLowerInvariant();
|
return Path.GetExtension(filePath).ToLowerInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsBase64String(this string s)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(s))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s = s.Trim();
|
||||||
|
|
||||||
|
// Length must be divisible by 4
|
||||||
|
if (s.Length % 4 != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check padding position and count
|
||||||
|
var paddingIndex = s.IndexOf('=');
|
||||||
|
|
||||||
|
switch (paddingIndex)
|
||||||
|
{
|
||||||
|
// Padding cannot be at index 0
|
||||||
|
case 0:
|
||||||
|
// Padding must be at the end
|
||||||
|
case > 0 when paddingIndex < s.Length - 2:
|
||||||
|
// All characters after first '=' must also be '='
|
||||||
|
case > 0 when s[paddingIndex..].Any(c => c != '='):
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for valid Base64 characters
|
||||||
|
for (var i = 0; i < paddingIndex; i++)
|
||||||
|
{
|
||||||
|
var c = s[i];
|
||||||
|
var isValid =
|
||||||
|
c is >= 'A' and <= 'Z' ||
|
||||||
|
c is >= 'a' and <= 'z' ||
|
||||||
|
c is >= '0' and <= '9' ||
|
||||||
|
c == '+' || c == '/';
|
||||||
|
|
||||||
|
if (!isValid)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional check for short strings that are just lowercase+numbers
|
||||||
|
// This catches "whatever" and similar false positives
|
||||||
|
if (s.Length <= 10 && s.All(c => char.IsLower(c) || char.IsDigit(c)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Try actual decoding
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ = Convert.FromBase64String(s);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string DetermineExtensionFromMimeType(string mimeType)
|
private static string DetermineExtensionFromMimeType(string mimeType)
|
||||||
{
|
{
|
||||||
if (mimeType.Contains("/pdf"))
|
if (mimeType.Contains("/pdf"))
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,8 @@ public class Base64ContentStrategy : IContentResolverStrategy
|
||||||
|
|
||||||
private static bool IsBase64String(string base64)
|
private static bool IsBase64String(string base64)
|
||||||
{
|
{
|
||||||
if (IsUriDataBase64String(base64))
|
return IsUriDataBase64String(base64)
|
||||||
{
|
|| base64.IsBase64String();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = new Span<byte>(new byte[base64.Length]);
|
|
||||||
return Convert.TryFromBase64String(base64, buffer , out _);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsUriDataBase64String(string base64)
|
private static bool IsUriDataBase64String(string base64)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using System.Text.Json.Nodes;
|
||||||
using Elsa.Expressions.Helpers;
|
using Elsa.Expressions.Helpers;
|
||||||
using Elsa.Extensions;
|
using Elsa.Extensions;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Elsa.Workflows;
|
namespace Elsa.Workflows;
|
||||||
|
|
||||||
|
|
@ -12,7 +13,7 @@ namespace Elsa.Workflows;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Display(Name = "Workflow Instance")]
|
[Display(Name = "Workflow Instance")]
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class WorkflowInstanceStorageDriver(IPayloadSerializer payloadSerializer) : IStorageDriver
|
public class WorkflowInstanceStorageDriver(IPayloadSerializer payloadSerializer, ILogger<WorkflowInstanceStorageDriver> logger) : IStorageDriver
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key used to store the variables in the workflow state.
|
/// The key used to store the variables in the workflow state.
|
||||||
|
|
@ -29,8 +30,18 @@ public class WorkflowInstanceStorageDriver(IPayloadSerializer payloadSerializer)
|
||||||
{
|
{
|
||||||
UpdateVariablesDictionary(context, dictionary =>
|
UpdateVariablesDictionary(context, dictionary =>
|
||||||
{
|
{
|
||||||
var node = JsonSerializer.SerializeToNode(value);
|
try
|
||||||
dictionary[id] = node;
|
{
|
||||||
|
var node = JsonSerializer.SerializeToNode(value);
|
||||||
|
dictionary[id] = node;
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is JsonException or NotSupportedException or ObjectDisposedException)
|
||||||
|
{
|
||||||
|
logger.LogWarning(ex, "Failed to serialize variable '{VariableId}' of type '{VariableType}' for workflow instance storage. The variable will be skipped.",
|
||||||
|
id, value?.GetType().FullName ?? "null");
|
||||||
|
|
||||||
|
dictionary.Remove(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return ValueTask.CompletedTask;
|
return ValueTask.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,5 @@ public interface IActivityExecutionMapper
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps an activity execution context to an activity execution record.
|
/// Maps an activity execution context to an activity execution record.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ActivityExecutionRecord Map(ActivityExecutionContext source);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps an activity execution context to an activity execution record.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete( "Use Map instead.", error: false)]
|
|
||||||
Task<ActivityExecutionRecord> MapAsync(ActivityExecutionContext source);
|
Task<ActivityExecutionRecord> MapAsync(ActivityExecutionContext source);
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Elsa.Common;
|
using Elsa.Common;
|
||||||
using Elsa.Common.Entities;
|
using Elsa.Common.Entities;
|
||||||
using Elsa.Workflows.State;
|
using Elsa.Workflows.State;
|
||||||
|
|
@ -7,18 +9,18 @@ namespace Elsa.Workflows.Runtime.Entities;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a single activity execution of an activity instance.
|
/// Represents a single activity execution of an activity instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ActivityExecutionRecord : Entity, ILogRecord
|
public partial class ActivityExecutionRecord : Entity, ILogRecord
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the workflow instance ID.
|
/// Gets or sets the workflow instance ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string WorkflowInstanceId { get; set; } = null!;
|
public string WorkflowInstanceId { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the activity ID.
|
/// Gets or sets the activity ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ActivityId { get; set; } = null!;
|
public string ActivityId { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the activity node ID.
|
/// Gets or sets the activity node ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -38,17 +40,17 @@ public class ActivityExecutionRecord : Entity, ILogRecord
|
||||||
/// The name of the activity.
|
/// The name of the activity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? ActivityName { get; set; }
|
public string? ActivityName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The state of the activity at the time this record is created or last updated.
|
/// The state of the activity at the time this record is created or last updated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IDictionary<string, object?>? ActivityState { get; set; }
|
public IDictionary<string, object?>? ActivityState { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Any additional payload associated with the log record.
|
/// Any additional payload associated with the log record.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IDictionary<string, object>? Payload { get; set; }
|
public IDictionary<string, object>? Payload { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Any outputs provided by the activity.
|
/// Any outputs provided by the activity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -58,7 +60,7 @@ public class ActivityExecutionRecord : Entity, ILogRecord
|
||||||
/// Any properties provided by the activity.
|
/// Any properties provided by the activity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IDictionary<string, object>? Properties { get; set; }
|
public IDictionary<string, object>? Properties { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lightweight metadata associated with the activity execution.
|
/// Lightweight metadata associated with the activity execution.
|
||||||
/// This information will be retained as part of the activity execution summary record.
|
/// This information will be retained as part of the activity execution summary record.
|
||||||
|
|
@ -79,7 +81,7 @@ public class ActivityExecutionRecord : Entity, ILogRecord
|
||||||
/// Gets or sets whether the activity has any bookmarks.
|
/// Gets or sets whether the activity has any bookmarks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasBookmarks { get; set; }
|
public bool HasBookmarks { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the status of the activity.
|
/// Gets or sets the status of the activity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -89,9 +91,14 @@ public class ActivityExecutionRecord : Entity, ILogRecord
|
||||||
/// Gets or sets the aggregated count of faults encountered during the execution of the activity instance and its descendants.
|
/// Gets or sets the aggregated count of faults encountered during the execution of the activity instance and its descendants.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int AggregateFaultCount { get; set; }
|
public int AggregateFaultCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the time at which the activity execution completed.
|
/// Gets or sets the time at which the activity execution completed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTimeOffset? CompletedAt { get; set; }
|
public DateTimeOffset? CompletedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class ActivityExecutionRecord
|
||||||
|
{
|
||||||
|
[NotMapped] [JsonIgnore] public ActivityExecutionRecordSnapshot? SerializedSnapshot { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ using Elsa.Workflows.Runtime.Stimuli;
|
||||||
// ReSharper disable once CheckNamespace
|
// ReSharper disable once CheckNamespace
|
||||||
namespace Elsa.Extensions;
|
namespace Elsa.Extensions;
|
||||||
|
|
||||||
public static class ActivityExecutionContextExtensions
|
public static class ActivityExecutionContextEventExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Suspends the current activity's execution and waits for a specified event to occur before continuing.
|
/// Suspends the current activity's execution and waits for a specified event to occur before continuing.
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
using Elsa.Workflows;
|
||||||
|
using Elsa.Workflows.Runtime;
|
||||||
|
using Elsa.Workflows.Runtime.Entities;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Elsa.Extensions;
|
||||||
|
|
||||||
|
public static class ActivityExecutionContextRecordExtensions
|
||||||
|
{
|
||||||
|
private const string ActivityExecutionRecordKey = "CapturedActivityExecutionRecord";
|
||||||
|
|
||||||
|
public static async Task CaptureActivityExecutionRecordAsync(this ActivityExecutionContext context)
|
||||||
|
{
|
||||||
|
var mapper = context.GetRequiredService<IActivityExecutionMapper>();
|
||||||
|
var record = await mapper.MapAsync(context);
|
||||||
|
context.TransientProperties[ActivityExecutionRecordKey] = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ActivityExecutionRecord? GetCapturedActivityExecutionRecord(this ActivityExecutionContext context)
|
||||||
|
{
|
||||||
|
return context.TransientProperties.TryGetValue(ActivityExecutionRecordKey, out var record) ? (ActivityExecutionRecord?)record : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<ActivityExecutionRecord> GetOrMapCapturedActivityExecutionRecordAsync(this ActivityExecutionContext context)
|
||||||
|
{
|
||||||
|
if(context.TransientProperties.TryGetValue(ActivityExecutionRecordKey, out var record))
|
||||||
|
return (ActivityExecutionRecord)record;
|
||||||
|
|
||||||
|
var mapper = context.GetRequiredService<IActivityExecutionMapper>();
|
||||||
|
return await mapper.MapAsync(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,7 @@ public static class PipelineWorkflowsFeatureExtensions
|
||||||
.UseExecutionLogging()
|
.UseExecutionLogging()
|
||||||
.UseNotifications()
|
.UseNotifications()
|
||||||
.UseLogPersistenceModeEvaluation()
|
.UseLogPersistenceModeEvaluation()
|
||||||
|
.UseActivityExecutionLogCapturing()
|
||||||
.UseBackgroundActivityInvoker();
|
.UseBackgroundActivityInvoker();
|
||||||
|
|
||||||
configurePipeline?.Invoke(pipeline);
|
configurePipeline?.Invoke(pipeline);
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,12 @@ public static class ActivityExecutionPipelineBuilderExtensions
|
||||||
/// Installs the <see cref="EvaluateLogPersistenceModesMiddleware"/> which evaluates log persistence modes during activity execution.
|
/// Installs the <see cref="EvaluateLogPersistenceModesMiddleware"/> which evaluates log persistence modes during activity execution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IActivityExecutionPipelineBuilder UseLogPersistenceModeEvaluation(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<EvaluateLogPersistenceModesMiddleware>();
|
public static IActivityExecutionPipelineBuilder UseLogPersistenceModeEvaluation(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<EvaluateLogPersistenceModesMiddleware>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Installs the <see cref="CaptureActivityExecutionRecordMiddleware"/> into the activity execution pipeline to capture and map activity execution details.
|
||||||
|
/// </summary>
|
||||||
|
public static IActivityExecutionPipelineBuilder UseActivityExecutionLogCapturing(this IActivityExecutionPipelineBuilder pipelineBuilder)
|
||||||
|
{
|
||||||
|
return pipelineBuilder.UseMiddleware<CaptureActivityExecutionRecordMiddleware>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
using Elsa.Extensions;
|
||||||
|
using Elsa.Workflows.Pipelines.ActivityExecution;
|
||||||
|
|
||||||
|
namespace Elsa.Workflows.Runtime.Middleware.Activities;
|
||||||
|
|
||||||
|
public class CaptureActivityExecutionRecordMiddleware(ActivityMiddlewareDelegate next) : IActivityExecutionMiddleware
|
||||||
|
{
|
||||||
|
public async ValueTask InvokeAsync(ActivityExecutionContext context)
|
||||||
|
{
|
||||||
|
await next(context);
|
||||||
|
await context.CaptureActivityExecutionRecordAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
namespace Elsa.Workflows.Runtime;
|
||||||
|
|
||||||
|
public class ActivityExecutionRecordSnapshot
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
public string? TenantId { get; set; }
|
||||||
|
public string WorkflowInstanceId { get; set; } = null!;
|
||||||
|
public string ActivityId { get; set; } = null!;
|
||||||
|
public string ActivityNodeId { get; set; } = null!;
|
||||||
|
public string ActivityType { get; set; } = null!;
|
||||||
|
public int ActivityTypeVersion { get; set; }
|
||||||
|
public string? ActivityName { get; set; }
|
||||||
|
public DateTimeOffset StartedAt { get; set; }
|
||||||
|
public bool HasBookmarks { get; set; }
|
||||||
|
public ActivityStatus Status { get; set; }
|
||||||
|
public int AggregateFaultCount { get; set; }
|
||||||
|
public DateTimeOffset? CompletedAt { get; set; }
|
||||||
|
public string? SerializedActivityState { get; set; }
|
||||||
|
public string? SerializedOutputs { get; set; }
|
||||||
|
public string? SerializedProperties { get; set; }
|
||||||
|
public string? SerializedPayload { get; set; }
|
||||||
|
public string? SerializedMetadata { get; set; }
|
||||||
|
public string? SerializedException { get; set; }
|
||||||
|
public string? SerializedActivityStateCompressionAlgorithm { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,23 @@
|
||||||
|
using Elsa.Common;
|
||||||
|
using Elsa.Common.Codecs;
|
||||||
using Elsa.Workflows.LogPersistence;
|
using Elsa.Workflows.LogPersistence;
|
||||||
|
using Elsa.Workflows.Management.Options;
|
||||||
using Elsa.Workflows.Runtime.Entities;
|
using Elsa.Workflows.Runtime.Entities;
|
||||||
|
using Elsa.Workflows.Runtime.Extensions;
|
||||||
using Elsa.Workflows.State;
|
using Elsa.Workflows.State;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Elsa.Workflows.Runtime;
|
namespace Elsa.Workflows.Runtime;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class DefaultActivityExecutionMapper() : IActivityExecutionMapper
|
public class DefaultActivityExecutionMapper(
|
||||||
|
ISafeSerializer safeSerializer,
|
||||||
|
IPayloadSerializer payloadSerializer,
|
||||||
|
ICompressionCodecResolver compressionCodecResolver,
|
||||||
|
IOptions<ManagementOptions> options) : IActivityExecutionMapper
|
||||||
{
|
{
|
||||||
public ActivityExecutionRecord Map(ActivityExecutionContext source)
|
/// <inheritdoc />
|
||||||
|
public async Task<ActivityExecutionRecord> MapAsync(ActivityExecutionContext source)
|
||||||
{
|
{
|
||||||
var outputs = source.GetOutputs();
|
var outputs = source.GetOutputs();
|
||||||
var inputs = source.GetInputs();
|
var inputs = source.GetInputs();
|
||||||
|
|
@ -16,8 +26,9 @@ public class DefaultActivityExecutionMapper() : IActivityExecutionMapper
|
||||||
var persistableOutputs = GetPersistableInputOutput(outputs, persistenceMap.Outputs);
|
var persistableOutputs = GetPersistableInputOutput(outputs, persistenceMap.Outputs);
|
||||||
var persistableProperties = GetPersistableDictionary(source.Properties!, persistenceMap.InternalState);
|
var persistableProperties = GetPersistableDictionary(source.Properties!, persistenceMap.InternalState);
|
||||||
var persistableJournalData = GetPersistableDictionary(source.JournalData!, persistenceMap.InternalState);
|
var persistableJournalData = GetPersistableDictionary(source.JournalData!, persistenceMap.InternalState);
|
||||||
|
var cancellationToken = source.CancellationToken;
|
||||||
|
|
||||||
return new()
|
var record = new ActivityExecutionRecord
|
||||||
{
|
{
|
||||||
Id = source.Id,
|
Id = source.Id,
|
||||||
ActivityId = source.Activity.Id,
|
ActivityId = source.Activity.Id,
|
||||||
|
|
@ -38,15 +49,41 @@ public class DefaultActivityExecutionMapper() : IActivityExecutionMapper
|
||||||
AggregateFaultCount = source.AggregateFaultCount,
|
AggregateFaultCount = source.AggregateFaultCount,
|
||||||
CompletedAt = source.CompletedAt
|
CompletedAt = source.CompletedAt
|
||||||
};
|
};
|
||||||
|
|
||||||
|
record = record.SanitizeLogMessage();
|
||||||
|
var compressionAlgorithm = options.Value.CompressionAlgorithm ?? nameof(None);
|
||||||
|
var serializedActivityState = record.ActivityState?.Count > 0 ? safeSerializer.Serialize(record.ActivityState) : null;
|
||||||
|
var compressedSerializedActivityState = serializedActivityState != null ? await compressionCodecResolver.Resolve(compressionAlgorithm).CompressAsync(serializedActivityState, cancellationToken) : null;
|
||||||
|
var serializedProperties = record.Properties != null ? payloadSerializer.Serialize(record.Properties) : null;
|
||||||
|
var serializedMetadata = record.Metadata != null ? payloadSerializer.Serialize(record.Metadata) : null;
|
||||||
|
record.SerializedSnapshot = new()
|
||||||
|
{
|
||||||
|
Id = record.Id,
|
||||||
|
TenantId = record.TenantId,
|
||||||
|
WorkflowInstanceId = record.WorkflowInstanceId,
|
||||||
|
ActivityId = record.ActivityId,
|
||||||
|
ActivityNodeId = record.ActivityNodeId,
|
||||||
|
ActivityType = record.ActivityType,
|
||||||
|
ActivityTypeVersion = record.ActivityTypeVersion,
|
||||||
|
ActivityName = record.ActivityName,
|
||||||
|
StartedAt = record.StartedAt,
|
||||||
|
HasBookmarks = record.HasBookmarks,
|
||||||
|
Status = record.Status,
|
||||||
|
AggregateFaultCount = record.AggregateFaultCount,
|
||||||
|
CompletedAt = record.CompletedAt,
|
||||||
|
SerializedActivityState = compressedSerializedActivityState,
|
||||||
|
SerializedActivityStateCompressionAlgorithm = compressionAlgorithm,
|
||||||
|
SerializedOutputs = record.Outputs?.Any() == true ? safeSerializer.Serialize(record.Outputs) : null,
|
||||||
|
SerializedProperties = serializedProperties,
|
||||||
|
SerializedMetadata = serializedMetadata,
|
||||||
|
SerializedException = record.Exception != null ? payloadSerializer.Serialize(record.Exception) : null,
|
||||||
|
SerializedPayload = record.Payload?.Any() == true ? payloadSerializer.Serialize(record.Payload) : null
|
||||||
|
};
|
||||||
|
|
||||||
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
private IDictionary<string, object?> GetPersistableInputOutput(IDictionary<string, object> state, IDictionary<string, LogPersistenceMode> map, bool deepCopy = false)
|
||||||
public Task<ActivityExecutionRecord> MapAsync(ActivityExecutionContext source)
|
|
||||||
{
|
|
||||||
return Task.FromResult(Map(source));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IDictionary<string, object?> GetPersistableInputOutput(IDictionary<string, object> state, IDictionary<string, LogPersistenceMode> map)
|
|
||||||
{
|
{
|
||||||
var result = new Dictionary<string, object?>();
|
var result = new Dictionary<string, object?>();
|
||||||
foreach (var stateEntry in state)
|
foreach (var stateEntry in state)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Elsa.Extensions;
|
||||||
using Elsa.Mediator.Contracts;
|
using Elsa.Mediator.Contracts;
|
||||||
using Elsa.Workflows.Runtime.Entities;
|
using Elsa.Workflows.Runtime.Entities;
|
||||||
using Elsa.Workflows.Runtime.Notifications;
|
using Elsa.Workflows.Runtime.Notifications;
|
||||||
|
|
@ -22,7 +23,7 @@ public class StoreActivityExecutionLogSink(
|
||||||
if (activityExecutionContexts.Count == 0)
|
if (activityExecutionContexts.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var records = activityExecutionContexts.Select(mapper.Map).ToList();
|
var records = await Task.WhenAll(activityExecutionContexts.Select(x => x.GetOrMapCapturedActivityExecutionRecordAsync()));
|
||||||
await activityExecutionStore.SaveManyAsync(records, cancellationToken);
|
await activityExecutionStore.SaveManyAsync(records, cancellationToken);
|
||||||
|
|
||||||
// Untaint activity execution contexts.
|
// Untaint activity execution contexts.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue