< Summary

Information
Class: Elsa.Workflows.Serialization.Converters.ActivityJsonConverter
Assembly: Elsa.Workflows.Core
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverter.cs
Line coverage
88%
Covered lines: 68
Uncovered lines: 9
Coverable lines: 77
Total lines: 160
Line coverage: 88.3%
Branch coverage
73%
Covered branches: 31
Total branches: 42
Branch coverage: 73.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Read(...)62.5%8888.46%
LogExceptionsIfAny(...)25%9433.33%
Write(...)66.66%66100%
GetActivityDetails(...)85%202091.3%
FindActivityDescriptorByCustomProperty(...)75%4475%
GetClonedOptions(...)100%11100%
GetClonedWriterOptions(...)100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverter.cs

#LineLine coverage
 1using System.Text.Json;
 2using System.Text.Json.Serialization;
 3using Elsa.Expressions.Contracts;
 4using Elsa.Extensions;
 5using Elsa.Workflows.Activities;
 6using Elsa.Workflows.Helpers;
 7using Elsa.Workflows.Models;
 8using Elsa.Workflows.Serialization.Helpers;
 9using Microsoft.Extensions.DependencyInjection;
 10using Microsoft.Extensions.Logging;
 11
 12namespace Elsa.Workflows.Serialization.Converters;
 13
 14/// <summary>
 15/// (De)serializes objects of type <see cref="IActivity"/>.
 16/// </summary>
 466517public class ActivityJsonConverter(
 466518    IActivityRegistry activityRegistry,
 466519    IExpressionDescriptorRegistry expressionDescriptorRegistry,
 466520    ActivityWriter activityWriter,
 466521    IServiceProvider serviceProvider)
 22    : JsonConverter<IActivity>
 23{
 24    /// <inheritdoc />
 25    public override IActivity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 26    {
 557427        if (!JsonDocument.TryParseValue(ref reader, out var doc))
 028            throw new JsonException("Failed to parse JsonDocument");
 29
 557430        var activityRoot = doc.RootElement;
 557431        var activityTypeName = GetActivityDetails(activityRoot, out var activityTypeVersion, out var activityDescriptor)
 557432        var notFoundActivityTypeName = ActivityTypeNameHelper.GenerateTypeName<NotFoundActivity>();
 33
 34        // If the activity type is a NotFoundActivity, try to extract the original activity type name and version.
 557435        if (activityTypeName.Equals(notFoundActivityTypeName) && activityRoot.TryGetProperty("originalActivityJson", out
 36        {
 037            activityRoot = JsonDocument.Parse(originalActivityJson.GetString()!).RootElement;
 038            activityTypeName = GetActivityDetails(activityRoot, out activityTypeVersion, out activityDescriptor);
 39        }
 40
 557441        var clonedOptions = GetClonedOptions(options);
 42        // If the activity type is not found, create a NotFoundActivity instead.
 557443        if (activityDescriptor == null)
 44        {
 30245            var notFoundActivityDescriptor = activityRegistry.Find<NotFoundActivity>()!;
 30246            var notFoundActivityResult = JsonActivityConstructorContextHelper.CreateActivity<NotFoundActivity>(notFoundA
 30247            LogExceptionsIfAny(notFoundActivityResult);
 48
 30249            var notFoundActivity = notFoundActivityResult.Activity;
 30250            notFoundActivity.Type = notFoundActivityTypeName;
 30251            notFoundActivity.Version = 1;
 30252            notFoundActivity.MissingTypeName = activityTypeName;
 30253            notFoundActivity.MissingTypeVersion = activityTypeVersion;
 30254            notFoundActivity.OriginalActivityJson = activityRoot.ToString();
 30255            notFoundActivity.SetDisplayText($"Not Found: {activityTypeName}");
 30256            notFoundActivity.SetDescription($"Could not find activity type {activityTypeName} with version {activityType
 30257            return notFoundActivity;
 58        }
 59
 527260        var context = JsonActivityConstructorContextHelper.Create(activityDescriptor, activityRoot, clonedOptions);
 527261        var activityResult = activityDescriptor.Constructor(context);
 527262        LogExceptionsIfAny(activityResult);
 63
 527264        return activityResult.Activity;
 65    }
 66
 67    void LogExceptionsIfAny(ActivityConstructionResult result)
 68    {
 557469        if (!result.HasExceptions)
 557470            return;
 71
 072        var logger = serviceProvider.GetRequiredService<ILogger<ActivityJsonConverter>>();
 073        foreach (var exception in result.Exceptions)
 074            logger.LogWarning("An exception was thrown while constructing activity with id '{activityId}': {Message}", r
 075    }
 76
 77    /// <inheritdoc />
 78    public override void Write(Utf8JsonWriter writer, IActivity value, JsonSerializerOptions options)
 79    {
 345480        var clonedOptions = GetClonedWriterOptions(options);
 345481        var activityDescriptor = activityRegistry.Find(value.Type, value.Version);
 82
 83        // Give the activity descriptor a chance to customize the serializer options.
 345484        clonedOptions = activityDescriptor?.ConfigureSerializerOptions?.Invoke(clonedOptions) ?? clonedOptions;
 85
 345486        activityWriter.WriteActivity(writer, value, clonedOptions);
 345487    }
 88
 89    private string GetActivityDetails(JsonElement activityRoot, out int activityTypeVersion, out ActivityDescriptor? act
 90    {
 557491        if (!activityRoot.TryGetProperty("type", out var activityTypeNameElement))
 092            throw new JsonException("Failed to extract activity type property");
 93
 557494        var activityTypeName = activityTypeNameElement.GetString()!;
 557495        activityDescriptor = null;
 557496        activityTypeVersion = 0;
 97
 98        // First, we check whether the activity type name is a 'well-known' activity; not a workflow-as-activity
 99
 100        // If the activity type version is specified, use that to find the activity descriptor.
 5574101        if (activityRoot.TryGetProperty("version", out var activityVersionElement))
 102        {
 5569103            activityTypeVersion = activityVersionElement.GetInt32();
 5569104            activityDescriptor = activityRegistry.Find(activityTypeName, activityTypeVersion);
 105        }
 106
 107        // If a version is not specified, or activity with specified version is not found: use the latest version of the
 5574108        if (activityDescriptor == null)
 109        {
 341110            activityDescriptor = activityRegistry.Find(activityTypeName);
 341111            activityTypeVersion = activityDescriptor?.Version ?? 0;
 112        }
 113
 114        // This is a special case when working with the WorkflowDefinitionActivity: workflowDefinitionVersionId should b
 5574115        if (activityRoot.TryGetProperty("workflowDefinitionVersionId", out var workflowDefinitionVersionIdElement))
 116        {
 734117            var activityDescriptorOverride = FindActivityDescriptorByCustomProperty("WorkflowDefinitionVersionId", workf
 734118            if (activityDescriptorOverride is null)
 334119                return activityTypeName;
 120
 400121            activityDescriptor = activityDescriptorOverride;
 400122            activityTypeVersion = activityDescriptor.Version;
 123        }
 124        // This is also a special case when working with the WorkflowDefinitionActivity: if no 'well-known' activity cou
 4840125        else if (activityDescriptor is null
 4840126                 && activityRoot.TryGetProperty("workflowDefinitionId", out var workflowDefinitionIdElement)
 4840127                 && workflowDefinitionIdElement.ValueKind == JsonValueKind.String)
 128        {
 1129            activityDescriptor = FindActivityDescriptorByCustomProperty("WorkflowDefinitionId", workflowDefinitionIdElem
 1130            activityTypeVersion = activityDescriptor?.Version ?? 0;
 131        }
 132
 5240133        return activityTypeName;
 134    }
 135
 136    private ActivityDescriptor? FindActivityDescriptorByCustomProperty(string customPropertyName, JsonElement valueEleme
 137    {
 735138        if (valueElement.ValueKind != JsonValueKind.String)
 0139            return null;
 140
 735141        var searchValue = valueElement.GetString();
 30211142        return activityRegistry.Find(x => x.CustomProperties.TryGetValue(customPropertyName, out var value) && (string?)
 143    }
 144
 145    private JsonSerializerOptions GetClonedOptions(JsonSerializerOptions options)
 146    {
 9028147        var clonedOptions = new JsonSerializerOptions(options);
 9028148        clonedOptions.Converters.Add(new InputJsonConverterFactory(serviceProvider));
 9028149        clonedOptions.Converters.Add(new OutputJsonConverterFactory(serviceProvider));
 9028150        clonedOptions.Converters.Add(new ExpressionJsonConverterFactory(expressionDescriptorRegistry));
 9028151        return clonedOptions;
 152    }
 153
 154    private JsonSerializerOptions GetClonedWriterOptions(JsonSerializerOptions options)
 155    {
 3454156        var clonedOptions = GetClonedOptions(options);
 3454157        clonedOptions.Converters.Add(new JsonIgnoreCompositeRootConverterFactory(serviceProvider.GetRequiredService<Acti
 3454158        return clonedOptions;
 159    }
 160}