< 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
92%
Covered lines: 63
Uncovered lines: 5
Coverable lines: 68
Total lines: 146
Line coverage: 92.6%
Branch coverage
78%
Covered branches: 30
Total branches: 38
Branch coverage: 78.9%
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%8886.95%
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;
 10
 11namespace Elsa.Workflows.Serialization.Converters;
 12
 13/// <summary>
 14/// (De)serializes objects of type <see cref="IActivity"/>.
 15/// </summary>
 405016public class ActivityJsonConverter(
 405017    IActivityRegistry activityRegistry,
 405018    IExpressionDescriptorRegistry expressionDescriptorRegistry,
 405019    ActivityWriter activityWriter,
 405020    IServiceProvider serviceProvider)
 21    : JsonConverter<IActivity>
 22{
 23    /// <inheritdoc />
 24    public override IActivity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 25    {
 359326        if (!JsonDocument.TryParseValue(ref reader, out var doc))
 027            throw new JsonException("Failed to parse JsonDocument");
 28
 359329        var activityRoot = doc.RootElement;
 359330        var activityTypeName = GetActivityDetails(activityRoot, out var activityTypeVersion, out var activityDescriptor)
 359331        var notFoundActivityTypeName = ActivityTypeNameHelper.GenerateTypeName<NotFoundActivity>();
 32
 33        // If the activity type is a NotFoundActivity, try to extract the original activity type name and version.
 359334        if (activityTypeName.Equals(notFoundActivityTypeName) && activityRoot.TryGetProperty("originalActivityJson", out
 35        {
 036            activityRoot = JsonDocument.Parse(originalActivityJson.GetString()!).RootElement;
 037            activityTypeName = GetActivityDetails(activityRoot, out activityTypeVersion, out activityDescriptor);
 38        }
 39
 359340        var clonedOptions = GetClonedOptions(options);
 41
 42        // If the activity type is not found, create a NotFoundActivity instead.
 359343        if (activityDescriptor == null)
 44        {
 845            var notFoundActivityDescriptor = activityRegistry.Find<NotFoundActivity>()!;
 846            var notFoundActivity = JsonActivityConstructorContextHelper.CreateActivity<NotFoundActivity>(notFoundActivit
 47
 848            notFoundActivity.Type = notFoundActivityTypeName;
 849            notFoundActivity.Version = 1;
 850            notFoundActivity.MissingTypeName = activityTypeName;
 851            notFoundActivity.MissingTypeVersion = activityTypeVersion;
 852            notFoundActivity.OriginalActivityJson = activityRoot.ToString();
 853            notFoundActivity.SetDisplayText($"Not Found: {activityTypeName}");
 854            notFoundActivity.SetDescription($"Could not find activity type {activityTypeName} with version {activityType
 855            return notFoundActivity;
 56        }
 57
 358558        var context = JsonActivityConstructorContextHelper.Create(activityDescriptor, activityRoot, clonedOptions);
 358559        var activity = activityDescriptor.Constructor(context);
 358560        return activity;
 61    }
 62
 63    /// <inheritdoc />
 64    public override void Write(Utf8JsonWriter writer, IActivity value, JsonSerializerOptions options)
 65    {
 345466        var clonedOptions = GetClonedWriterOptions(options);
 345467        var activityDescriptor = activityRegistry.Find(value.Type, value.Version);
 68
 69        // Give the activity descriptor a chance to customize the serializer options.
 345470        clonedOptions = activityDescriptor?.ConfigureSerializerOptions?.Invoke(clonedOptions) ?? clonedOptions;
 71
 345472        activityWriter.WriteActivity(writer, value, clonedOptions);
 345473    }
 74
 75    private string GetActivityDetails(JsonElement activityRoot, out int activityTypeVersion, out ActivityDescriptor? act
 76    {
 359377        if (!activityRoot.TryGetProperty("type", out var activityTypeNameElement))
 078            throw new JsonException("Failed to extract activity type property");
 79
 359380        var activityTypeName = activityTypeNameElement.GetString()!;
 359381        activityDescriptor = null;
 359382        activityTypeVersion = 0;
 83
 84        // First, we check whether the activity type name is a 'well-known' activity; not a workflow-as-activity
 85
 86        // If the activity type version is specified, use that to find the activity descriptor.
 359387        if (activityRoot.TryGetProperty("version", out var activityVersionElement))
 88        {
 358889            activityTypeVersion = activityVersionElement.GetInt32();
 358890            activityDescriptor = activityRegistry.Find(activityTypeName, activityTypeVersion);
 91        }
 92
 93        // If a version is not specified, or activity with specified version is not found: use the latest version of the
 359394        if (activityDescriptor == null)
 95        {
 4796            activityDescriptor = activityRegistry.Find(activityTypeName);
 4797            activityTypeVersion = activityDescriptor?.Version ?? 0;
 98        }
 99
 100        // This is a special case when working with the WorkflowDefinitionActivity: workflowDefinitionVersionId should b
 3593101        if (activityRoot.TryGetProperty("workflowDefinitionVersionId", out var workflowDefinitionVersionIdElement))
 102        {
 558103            var activityDescriptorOverride = FindActivityDescriptorByCustomProperty("WorkflowDefinitionVersionId", workf
 558104            if (activityDescriptorOverride is null)
 40105                return activityTypeName;
 106
 518107            activityDescriptor = activityDescriptorOverride;
 518108            activityTypeVersion = activityDescriptor.Version;
 109        }
 110        // This is also a special case when working with the WorkflowDefinitionActivity: if no 'well-known' activity cou
 3035111        else if (activityDescriptor is null
 3035112                 && activityRoot.TryGetProperty("workflowDefinitionId", out var workflowDefinitionIdElement)
 3035113                 && workflowDefinitionIdElement.ValueKind == JsonValueKind.String)
 114        {
 1115            activityDescriptor = FindActivityDescriptorByCustomProperty("WorkflowDefinitionId", workflowDefinitionIdElem
 1116            activityTypeVersion = activityDescriptor?.Version ?? 0;
 117        }
 118
 3553119        return activityTypeName;
 120    }
 121
 122    private ActivityDescriptor? FindActivityDescriptorByCustomProperty(string customPropertyName, JsonElement valueEleme
 123    {
 559124        if (valueElement.ValueKind != JsonValueKind.String)
 0125            return null;
 126
 559127        var searchValue = valueElement.GetString();
 15609128        return activityRegistry.Find(x => x.CustomProperties.TryGetValue(customPropertyName, out var value) && (string?)
 129    }
 130
 131    private JsonSerializerOptions GetClonedOptions(JsonSerializerOptions options)
 132    {
 7047133        var clonedOptions = new JsonSerializerOptions(options);
 7047134        clonedOptions.Converters.Add(new InputJsonConverterFactory(serviceProvider));
 7047135        clonedOptions.Converters.Add(new OutputJsonConverterFactory(serviceProvider));
 7047136        clonedOptions.Converters.Add(new ExpressionJsonConverterFactory(expressionDescriptorRegistry));
 7047137        return clonedOptions;
 138    }
 139
 140    private JsonSerializerOptions GetClonedWriterOptions(JsonSerializerOptions options)
 141    {
 3454142        var clonedOptions = GetClonedOptions(options);
 3454143        clonedOptions.Converters.Add(new JsonIgnoreCompositeRootConverterFactory(serviceProvider.GetRequiredService<Acti
 3454144        return clonedOptions;
 145    }
 146}