Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/patch/3.2.x' into blueberry
Browse files Browse the repository at this point in the history
  • Loading branch information
sfmskywalker committed Nov 5, 2024
2 parents 52516aa + b3c73e9 commit 6ed6c8a
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -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>
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down
5 changes: 2 additions & 3 deletions build/_build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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>
2 changes: 1 addition & 1 deletion src/bundles/Elsa.Server.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
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;
Expand Down
15 changes: 10 additions & 5 deletions src/modules/Elsa.MongoDb/Common/PersistenceFeatureBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Elsa.Features.Abstractions;
using Elsa.Features.Services;
using Elsa.MongoDb.Contracts;
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Driver;

Expand All @@ -24,8 +25,7 @@ protected void AddStore<TDocument, TStore>() where TDocument : class where TStor
{
Services
.AddScoped<MongoDbStore<TDocument>>()
.AddScoped<TStore>()
;
.AddScoped<TStore>();
}

/// <summary>
Expand All @@ -35,8 +35,13 @@ protected void AddStore<TDocument, TStore>() where TDocument : class where TStor
/// <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);
});
}
}
12 changes: 12 additions & 0 deletions src/modules/Elsa.MongoDb/Contracts/ICollectionNamingStrategy.cs
Original file line number Diff line number Diff line change
@@ -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);
}
30 changes: 25 additions & 5 deletions src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -36,12 +41,22 @@ public MongoDbFeature(IModule module) : base(module)
/// </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();
Expand All @@ -54,6 +69,7 @@ private static void RegisterSerializers()
TryRegisterSerializerOrSkipWhenExist(typeof(Variable), new VariableSerializer());
TryRegisterSerializerOrSkipWhenExist(typeof(Version), new VersionSerializer());
TryRegisterSerializerOrSkipWhenExist(typeof(JsonElement), new JsonElementSerializer());
TryRegisterSerializerOrSkipWhenExist(typeof(JsonNode), new JsonNodeBsonConverter());
}

private static void RegisterClassMaps()
Expand Down Expand Up @@ -84,11 +100,10 @@ private static void TryRegisterSerializerOrSkipWhenExist(Type type, IBsonSeriali
}
}

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());
Expand All @@ -100,8 +115,13 @@ private static IMongoDatabase CreateDatabase(IServiceProvider sp, string connect
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) =>
Expand Down
17 changes: 17 additions & 0 deletions src/modules/Elsa.MongoDb/NamingStrategies/DefaultNamingStrategy.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 135 additions & 0 deletions src/modules/Elsa.MongoDb/Serializers/JsonNodeBsonSerializer.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private async Task DownloadMultipleWorkflowsAsync(ICollection<string> ids, Cance
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();
Expand Down Expand Up @@ -103,7 +103,7 @@ private async Task DownloadSingleWorkflowAsync(string definitionId, string? vers
}

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);
Expand All @@ -117,10 +117,25 @@ private string GetFileName(WorkflowDefinitionModel definition)
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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 6ed6c8a

Please sign in to comment.