Merge remote-tracking branch 'origin/patch/3.2.x' into blueberry
This commit is contained in:
commit
6ed6c8a30e
2
.github/workflows/packages.yml
vendored
2
.github/workflows/packages.yml
vendored
|
|
@ -51,7 +51,7 @@ jobs:
|
|||
TAG_NAME=${TAG_NAME#refs/tags/} # remove the refs/tags/ prefix
|
||||
echo "VERSION=${TAG_NAME}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "VERSION=3.2.1-blueberry.${{github.run_number}}" >> $GITHUB_ENV
|
||||
echo "VERSION=3.2.2-blueberry.${{github.run_number}}" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ElsaStudioVersion>3.2.0-rc4.473</ElsaStudioVersion>
|
||||
<ElsaStudioVersion>3.2.1-preview.558</ElsaStudioVersion>
|
||||
<SystemTextJsonVersion>8.0.5</SystemTextJsonVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -165,8 +165,8 @@
|
|||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||
<PackageVersion Include="Npgsql" Version="8.0.4" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.8" />
|
||||
<PackageVersion Include="Npgsql" Version="8.0.5" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
|
||||
<PackageVersion Include="Polly" Version="8.4.2" />
|
||||
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
|
||||
|
|
|
|||
|
|
@ -17,13 +17,12 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nuke.Components" Version="8.0.0" />
|
||||
<PackageReference Include="Nuke.Components" Version="8.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--Overridden for vulnaribility reasons with dependencies referencing older versions.-->
|
||||
<ItemGroup>
|
||||
<!-- <PackageReference Include="Azure.Identity" VersionOverride="1.11.4" />-->
|
||||
<PackageReference Include="Microsoft.Identity.Client" VersionOverride="4.61.3" />
|
||||
<PackageReference Include="Microsoft.Identity.Client" VersionOverride="4.65.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const bool runEFCoreMigrations = true;
|
|||
const bool useMemoryStores = false;
|
||||
const bool useCaching = true;
|
||||
const bool useReadOnlyMode = false;
|
||||
const bool useSignalR = true;
|
||||
const bool useSignalR = false; // Disable until Elsa Studio is updated to send authenticated requests to the SignalR hub.
|
||||
const bool useAzureServiceBus = false;
|
||||
const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit;
|
||||
const MassTransitBroker useMassTransitBroker = MassTransitBroker.Memory;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Elsa.Features.Abstractions;
|
||||
using Elsa.Features.Services;
|
||||
using Elsa.MongoDb.Contracts;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MongoDB.Driver;
|
||||
|
||||
|
|
@ -24,8 +25,7 @@ public abstract class PersistenceFeatureBase : FeatureBase
|
|||
{
|
||||
Services
|
||||
.AddScoped<MongoDbStore<TDocument>>()
|
||||
.AddScoped<TStore>()
|
||||
;
|
||||
.AddScoped<TStore>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -35,8 +35,13 @@ public abstract class PersistenceFeatureBase : FeatureBase
|
|||
/// <typeparam name="TDocument">The document type of the collection.</typeparam>
|
||||
protected void AddCollection<TDocument>(string collectionName) where TDocument : class
|
||||
{
|
||||
Services.AddScoped(
|
||||
sp => sp.GetRequiredService<IMongoDatabase>()
|
||||
.GetCollection<TDocument>(collectionName));
|
||||
Services.AddScoped(sp =>
|
||||
{
|
||||
var collectionNamingStrategy = sp.GetRequiredService<ICollectionNamingStrategy>();
|
||||
var formattedCollectionName = collectionNamingStrategy.GetCollectionName(collectionName);
|
||||
|
||||
return sp.GetRequiredService<IMongoDatabase>()
|
||||
.GetCollection<TDocument>(formattedCollectionName);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
namespace Elsa.MongoDb.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a naming strategy to use when creating the name of a MongoDB collection.
|
||||
/// </summary>
|
||||
public interface ICollectionNamingStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a collection name from the specified base collection name.
|
||||
/// </summary>
|
||||
string GetCollectionName(string collectionName);
|
||||
}
|
||||
|
|
@ -1,12 +1,17 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Elsa.Features.Abstractions;
|
||||
using Elsa.Features.Services;
|
||||
using Elsa.KeyValues.Entities;
|
||||
using Elsa.MongoDb.Contracts;
|
||||
using Elsa.MongoDb.NamingStrategies;
|
||||
using Elsa.MongoDb.Options;
|
||||
using Elsa.MongoDb.Serializers;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Elsa.Workflows.Runtime.Entities;
|
||||
using Elsa.Workflows.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
|
|
@ -36,12 +41,22 @@ public class MongoDbFeature : FeatureBase
|
|||
/// </summary>
|
||||
public Action<MongoDbOptions> Options { get; set; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that creates an instance of an implementation of <see cref="ICollectionNamingStrategy"/>.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, ICollectionNamingStrategy> CollectionNamingStrategy { get; set; } = sp => sp.GetRequiredService<DefaultNamingStrategy>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services.Configure(Options);
|
||||
|
||||
Services.AddScoped(sp => CreateDatabase(sp, ConnectionString));
|
||||
var mongoUrl = new MongoUrl(ConnectionString);
|
||||
Services.AddSingleton(sp => CreateMongoClient(sp, mongoUrl));
|
||||
Services.AddScoped(sp => CreateDatabase(sp, mongoUrl));
|
||||
|
||||
Services.TryAddScoped<DefaultNamingStrategy>();
|
||||
Services.AddScoped(CollectionNamingStrategy);
|
||||
|
||||
RegisterSerializers();
|
||||
RegisterClassMaps();
|
||||
|
|
@ -54,6 +69,7 @@ public class MongoDbFeature : FeatureBase
|
|||
TryRegisterSerializerOrSkipWhenExist(typeof(Variable), new VariableSerializer());
|
||||
TryRegisterSerializerOrSkipWhenExist(typeof(Version), new VersionSerializer());
|
||||
TryRegisterSerializerOrSkipWhenExist(typeof(JsonElement), new JsonElementSerializer());
|
||||
TryRegisterSerializerOrSkipWhenExist(typeof(JsonNode), new JsonNodeBsonConverter());
|
||||
}
|
||||
|
||||
private static void RegisterClassMaps()
|
||||
|
|
@ -84,11 +100,10 @@ public class MongoDbFeature : FeatureBase
|
|||
}
|
||||
}
|
||||
|
||||
private static IMongoDatabase CreateDatabase(IServiceProvider sp, string connectionString)
|
||||
private static IMongoClient CreateMongoClient(IServiceProvider sp, MongoUrl mongoUrl)
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<MongoDbOptions>>().Value;
|
||||
|
||||
var mongoUrl = new MongoUrl(connectionString);
|
||||
var settings = MongoClientSettings.FromUrl(mongoUrl);
|
||||
|
||||
settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber());
|
||||
|
|
@ -100,8 +115,13 @@ public class MongoDbFeature : FeatureBase
|
|||
settings.RetryWrites = options.RetryWrites;
|
||||
settings.SslSettings = options.SslSettings;
|
||||
|
||||
var mongoClient = new MongoClient(settings);
|
||||
return mongoClient.GetDatabase(mongoUrl.DatabaseName);
|
||||
return new MongoClient(settings);
|
||||
}
|
||||
|
||||
private static IMongoDatabase CreateDatabase(IServiceProvider sp, MongoUrl mongoUrl)
|
||||
{
|
||||
var client = sp.GetRequiredService<IMongoClient>();
|
||||
return client.GetDatabase(mongoUrl.DatabaseName);
|
||||
}
|
||||
|
||||
private static string GetApplicationName(MongoClientSettings settings) =>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
using Elsa.MongoDb.Contracts;
|
||||
|
||||
namespace Elsa.MongoDb.NamingStrategies;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the same collection name, without modifying it.
|
||||
/// </summary>
|
||||
public class DefaultNamingStrategy : ICollectionNamingStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the same collection name, without modifying it.
|
||||
/// </summary>
|
||||
public string GetCollectionName(string collectionName)
|
||||
{
|
||||
return collectionName;
|
||||
}
|
||||
}
|
||||
135
src/modules/Elsa.MongoDb/Serializers/JsonNodeBsonSerializer.cs
Normal file
135
src/modules/Elsa.MongoDb/Serializers/JsonNodeBsonSerializer.cs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.IO;
|
||||
using MongoDB.Bson.Serialization;
|
||||
|
||||
namespace Elsa.MongoDb.Serializers;
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a <see cref="JsonNode"/>.
|
||||
/// </summary>
|
||||
public class JsonNodeBsonConverter : IBsonSerializer<JsonNode>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Type ValueType => typeof(JsonNode);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JsonNode value)
|
||||
{
|
||||
if (value == null!)
|
||||
{
|
||||
context.Writer.WriteNull();
|
||||
return;
|
||||
}
|
||||
|
||||
context.Writer.WriteStartDocument();
|
||||
context.Writer.WriteName("type");
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case JsonObject jsonObject:
|
||||
context.Writer.WriteString("JsonObject");
|
||||
context.Writer.WriteName("value");
|
||||
context.Writer.WriteString(jsonObject.ToJsonString());
|
||||
break;
|
||||
|
||||
case JsonArray jsonArray:
|
||||
context.Writer.WriteString("JsonArray");
|
||||
context.Writer.WriteName("value");
|
||||
context.Writer.WriteString(jsonArray.ToJsonString());
|
||||
break;
|
||||
|
||||
case JsonValue jsonValue:
|
||||
context.Writer.WriteString("JsonValue");
|
||||
context.Writer.WriteName("value");
|
||||
if (jsonValue.TryGetValue(out string? stringValue))
|
||||
context.Writer.WriteString(stringValue);
|
||||
else if (jsonValue.TryGetValue(out int intValue))
|
||||
context.Writer.WriteInt32(intValue);
|
||||
else if (jsonValue.TryGetValue(out int longValue))
|
||||
context.Writer.WriteInt64(longValue);
|
||||
else if (jsonValue.TryGetValue(out double doubleValue))
|
||||
context.Writer.WriteDouble(doubleValue);
|
||||
else if (jsonValue.TryGetValue(out bool boolValue))
|
||||
context.Writer.WriteBoolean(boolValue);
|
||||
else if (jsonValue.TryGetValue(out DateTimeOffset dateTimeOffsetValue))
|
||||
context.Writer.WriteDateTime(dateTimeOffsetValue.ToUnixTimeMilliseconds());
|
||||
else if (jsonValue.TryGetValue(out DateTime dateTimeValue))
|
||||
context.Writer.WriteDateTime(new DateTimeOffset(dateTimeValue).ToUnixTimeMilliseconds());
|
||||
else
|
||||
throw new BsonSerializationException("Unsupported JsonValue type");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new BsonSerializationException($"Unexpected JsonNode type: {value.GetType()}");
|
||||
}
|
||||
|
||||
context.Writer.WriteEndDocument();
|
||||
}
|
||||
|
||||
public JsonNode Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
|
||||
{
|
||||
context.Reader.ReadStartDocument();
|
||||
var type = context.Reader.ReadString();
|
||||
context.Reader.ReadName(Utf8NameDecoder.Instance);
|
||||
|
||||
JsonNode result;
|
||||
switch (type)
|
||||
{
|
||||
case "JsonObject":
|
||||
var jsonObjectString = context.Reader.ReadString();
|
||||
result = JsonNode.Parse(jsonObjectString);
|
||||
break;
|
||||
|
||||
case "JsonArray":
|
||||
var jsonArrayString = context.Reader.ReadString();
|
||||
result = JsonNode.Parse(jsonArrayString);
|
||||
break;
|
||||
|
||||
case "JsonValue":
|
||||
var bsonType = context.Reader.GetCurrentBsonType();
|
||||
switch (bsonType)
|
||||
{
|
||||
case BsonType.String:
|
||||
result = JsonValue.Create(context.Reader.ReadString());
|
||||
break;
|
||||
case BsonType.Int32:
|
||||
result = JsonValue.Create(context.Reader.ReadInt32());
|
||||
break;
|
||||
case BsonType.Int64:
|
||||
result = JsonValue.Create(context.Reader.ReadInt64());
|
||||
break;
|
||||
case BsonType.Double:
|
||||
result = JsonValue.Create(context.Reader.ReadDouble());
|
||||
break;
|
||||
case BsonType.Boolean:
|
||||
result = JsonValue.Create(context.Reader.ReadBoolean());
|
||||
break;
|
||||
case BsonType.DateTime:
|
||||
result = JsonValue.Create(context.Reader.ReadDateTime());
|
||||
break;
|
||||
default:
|
||||
throw new BsonSerializationException($"Unsupported BSON type: {bsonType}");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new BsonSerializationException($"Unsupported JsonNode type: {type}");
|
||||
}
|
||||
|
||||
context.Reader.ReadEndDocument();
|
||||
return result!;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
|
||||
{
|
||||
Serialize(context, args, (JsonNode)value);
|
||||
}
|
||||
|
||||
object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
|
||||
{
|
||||
return Deserialize(context, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ internal class Export : ElsaEndpoint<Request>
|
|||
foreach (var definition in definitions)
|
||||
{
|
||||
var model = await CreateWorkflowModelAsync(definition, cancellationToken);
|
||||
var binaryJson = SerializeWorkflowDefinition(model);
|
||||
var binaryJson = await SerializeWorkflowDefinitionAsync(model, cancellationToken);
|
||||
var fileName = GetFileName(model);
|
||||
var entry = zipArchive.CreateEntry(fileName, CompressionLevel.Optimal);
|
||||
await using var entryStream = entry.Open();
|
||||
|
|
@ -103,7 +103,7 @@ internal class Export : ElsaEndpoint<Request>
|
|||
}
|
||||
|
||||
var model = await CreateWorkflowModelAsync(definition, cancellationToken);
|
||||
var binaryJson = SerializeWorkflowDefinition(model);
|
||||
var binaryJson = await SerializeWorkflowDefinitionAsync(model, cancellationToken);
|
||||
var fileName = GetFileName(model);
|
||||
|
||||
await SendBytesAsync(binaryJson, fileName, cancellation: cancellationToken);
|
||||
|
|
@ -117,10 +117,25 @@ internal class Export : ElsaEndpoint<Request>
|
|||
return fileName;
|
||||
}
|
||||
|
||||
private byte[] SerializeWorkflowDefinition(WorkflowDefinitionModel model)
|
||||
private async Task<byte[]> SerializeWorkflowDefinitionAsync(WorkflowDefinitionModel model, CancellationToken cancellationToken)
|
||||
{
|
||||
JsonSerializerOptions serializerOptions = _serializer.GetOptions();
|
||||
var binaryJson = JsonSerializer.SerializeToUtf8Bytes(model, serializerOptions);
|
||||
var serializerOptions = _serializer.GetOptions();
|
||||
var document = JsonSerializer.SerializeToDocument(model, serializerOptions);
|
||||
var rootElement = document.RootElement;
|
||||
|
||||
using var output = new MemoryStream();
|
||||
await using var writer = new Utf8JsonWriter(output);
|
||||
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("$schema", "https://elsaworkflows.io/schemas/workflow-definition/v3.0.0/schema.json");
|
||||
|
||||
foreach (var property in rootElement.EnumerateObject())
|
||||
property.WriteTo(writer);
|
||||
|
||||
writer.WriteEndObject();
|
||||
|
||||
await writer.FlushAsync(cancellationToken);
|
||||
var binaryJson = output.ToArray();
|
||||
return binaryJson;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Elsa.Workflows.Api.RealTime.Contracts;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace Elsa.Workflows.Api.RealTime.Hubs;
|
||||
|
|
@ -9,6 +10,7 @@ namespace Elsa.Workflows.Api.RealTime.Hubs;
|
|||
/// Represents a SignalR hub for receiving workflow events on the client.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[Authorize]
|
||||
public class WorkflowInstanceHub : Hub<IWorkflowInstanceClient>
|
||||
{
|
||||
private readonly IWorkflowRuntime _workflowRuntime;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Workflows.Serialization.Converters;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.Serialization.Configurators;
|
||||
|
|
@ -8,23 +9,14 @@ namespace Elsa.Workflows.Serialization.Configurators;
|
|||
/// <summary>
|
||||
/// Add additional <see cref="JsonConverter"/> objects.
|
||||
/// </summary>
|
||||
public class AdditionalConvertersConfigurator : SerializationOptionsConfiguratorBase
|
||||
[UsedImplicitly]
|
||||
public class AdditionalConvertersConfigurator(IServiceProvider serviceProvider) : SerializationOptionsConfiguratorBase
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AdditionalConvertersConfigurator"/> class.
|
||||
/// </summary>
|
||||
public AdditionalConvertersConfigurator(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Configure(JsonSerializerOptions options)
|
||||
{
|
||||
options.Converters.Add(Create<VariableConverterFactory>());
|
||||
}
|
||||
|
||||
private T Create<T>() => ActivatorUtilities.CreateInstance<T>(_serviceProvider);
|
||||
private T Create<T>() => ActivatorUtilities.CreateInstance<T>(serviceProvider);
|
||||
}
|
||||
Loading…
Reference in a new issue