< Summary

Information
Class: Elsa.Workflows.Api.Endpoints.WorkflowInstances.Import.Import
Assembly: Elsa.Workflows.Api
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Import/Endpoint.cs
Line coverage
26%
Covered lines: 24
Uncovered lines: 66
Coverable lines: 90
Total lines: 169
Line coverage: 26.6%
Branch coverage
0%
Covered branches: 0
Total branches: 18
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Configure()100%11100%
HandleAsync()100%210%
ImportFilesAsync()0%7280%
ImportJsonStreamAsync()100%210%
ImportSingleWorkflowInstanceAsync()0%110100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Import/Endpoint.cs

#LineLine coverage
 1using Elsa.Authorization;
 2using System.IO.Compression;
 3using System.Text.Json;
 4using Elsa.Abstractions;
 5using Elsa.Common.Multitenancy;
 6using Elsa.Workflows.Api.Models;
 7using Elsa.Workflows.Management;
 8using Elsa.Workflows.Runtime;
 9using Elsa.Workflows.Runtime.Entities;
 10using JetBrains.Annotations;
 11using Microsoft.AspNetCore.Http;
 12
 13namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Import;
 14
 15/// <summary>
 16/// Imports JSON and/or ZIP files containing a workflow instances.
 17/// </summary>
 18[PublicAPI]
 19internal class Import : ElsaEndpointWithoutRequest<Response>
 20{
 21    private readonly IWorkflowInstanceManager _workflowInstanceManager;
 22    private readonly IWorkflowInstanceStore _workflowInstanceStore;
 23    private readonly IActivityExecutionStore _activityExecutionStore;
 24    private readonly IWorkflowExecutionLogStore _workflowExecutionLogStore;
 25    private readonly IBookmarkStore _bookmarkStore;
 26    private readonly IWorkflowStateSerializer _workflowStateSerializer;
 27    private readonly IPayloadSerializer _payloadSerializer;
 28    private readonly ISafeSerializer _safeSerializer;
 29    private readonly ITenantAccessor _tenantAccessor;
 30
 31    /// <inheritdoc />
 332    public Import(
 333        IWorkflowInstanceManager workflowInstanceManager,
 334        IWorkflowInstanceStore workflowInstanceStore,
 335        IActivityExecutionStore activityExecutionStore,
 336        IWorkflowExecutionLogStore workflowExecutionLogStore,
 337        IBookmarkStore bookmarkStore,
 338        IWorkflowStateSerializer workflowStateSerializer,
 339        IPayloadSerializer payloadSerializer,
 340        ISafeSerializer safeSerializer,
 341        ITenantAccessor tenantAccessor)
 42    {
 343        _workflowInstanceManager = workflowInstanceManager;
 344        _workflowInstanceStore = workflowInstanceStore;
 345        _activityExecutionStore = activityExecutionStore;
 346        _workflowExecutionLogStore = workflowExecutionLogStore;
 347        _bookmarkStore = bookmarkStore;
 348        _workflowStateSerializer = workflowStateSerializer;
 349        _payloadSerializer = payloadSerializer;
 350        _safeSerializer = safeSerializer;
 351        _tenantAccessor = tenantAccessor;
 352    }
 53
 54    /// <inheritdoc />
 55    public override void Configure()
 56    {
 357        Post("/bulk-actions/import/workflow-instances");
 358        RequirePermission(Elsa.Workflows.Api.Permissions.WorkflowPermissions.Instances, CoreVerbs.Write);
 359        AllowFileUploads();
 360    }
 61
 62    /// <inheritdoc />
 63    public override async Task HandleAsync(CancellationToken cancellationToken)
 64    {
 065        var count = await ImportFilesAsync(Files, cancellationToken);
 066        var response = new Response { Imported = count };
 67
 068        await Send.OkAsync(response, cancellationToken);
 069    }
 70
 71    private async Task<int> ImportFilesAsync(IFormFileCollection files, CancellationToken cancellationToken)
 72    {
 073        var count = 0;
 74
 075        foreach (var file in files)
 76        {
 077            var fileStream = file.OpenReadStream();
 78
 79            // Check if the file is a JSON file or a ZIP file.
 080            var isJsonFile = file.ContentType == "application/json";
 81
 82            // If the file is a JSON file, read it.
 083            if (isJsonFile)
 84            {
 085                await ImportJsonStreamAsync(fileStream, cancellationToken);
 086                count++;
 87            }
 88            else
 89            {
 90                // If the file is a ZIP file, extract the JSON files and read them.
 091                var zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read);
 92
 093                foreach (var entry in zipArchive.Entries)
 94                {
 095                    if (!entry.FullName.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
 96                        continue;
 97
 098                    var jsonStream = entry.Open();
 099                    await ImportJsonStreamAsync(jsonStream, cancellationToken);
 0100                    count++;
 101                }
 102            }
 103        }
 104
 0105        return count;
 0106    }
 107
 108    private async Task ImportJsonStreamAsync(Stream jsonStream, CancellationToken cancellationToken)
 109    {
 0110        var json = await new StreamReader(jsonStream).ReadToEndAsync();
 0111        var model = JsonSerializer.Deserialize<ExportedWorkflowState>(json)!;
 0112        await ImportSingleWorkflowInstanceAsync(model, cancellationToken);
 0113    }
 114
 115    private async Task ImportSingleWorkflowInstanceAsync(ExportedWorkflowState model, CancellationToken cancellationToke
 116    {
 0117        var workflowState = _workflowStateSerializer.Deserialize(model.WorkflowState);
 0118        await _workflowInstanceManager.SaveAsync(workflowState, cancellationToken);
 119
 0120        if (model.Bookmarks != null)
 121        {
 0122            var bookmarksElement = model.Bookmarks.Value.EnumerateArray().ToList();
 0123            var bookmarks = bookmarksElement.Select(x =>
 0124            {
 0125                var bookmarkId = x.GetProperty("id").GetString()!;
 0126                var activityTypeName = x.GetProperty("activityTypeName").GetString()!;
 0127                var workflowInstanceId = x.GetProperty("workflowInstanceId").GetString()!;
 0128                var activityInstanceId = x.GetProperty("activityInstanceId").GetString();
 0129                var hash = x.GetProperty("hash").GetString()!;
 0130                var correlationId = x.GetProperty("correlationId").GetString();
 0131                var createdAt = x.GetProperty("createdAt").GetDateTimeOffset();
 0132                var payloadElement = x.GetProperty("payload");
 0133                var metadataElement = x.GetProperty("metadata");
 0134                var payload = _payloadSerializer.Deserialize<object>(payloadElement);
 0135                var metadata = _payloadSerializer.Deserialize<IDictionary<string, string>>(metadataElement);
 0136
 0137                return new StoredBookmark
 0138                {
 0139                    Id = bookmarkId,
 0140                    Name = activityTypeName,
 0141                    Hash = hash,
 0142                    WorkflowInstanceId = workflowInstanceId,
 0143                    CreatedAt = createdAt,
 0144                    ActivityInstanceId = activityInstanceId,
 0145                    CorrelationId = correlationId,
 0146                    Payload = payload,
 0147                    Metadata = metadata
 0148                };
 0149            }).ToList();
 0150            await _bookmarkStore.SaveManyAsync(bookmarks, cancellationToken);
 151        }
 152
 0153        if (model.ActivityExecutionRecords != null)
 154        {
 0155            var activityExecutionRecords = _safeSerializer.Deserialize<ICollection<ActivityExecutionRecord>>(model.Activ
 0156            foreach (var record in activityExecutionRecords)
 0157                record.TenantId = _tenantAccessor.TenantId;
 0158            await _activityExecutionStore.SaveManyAsync(activityExecutionRecords, cancellationToken);
 159        }
 160
 0161        if (model.WorkflowExecutionLogRecords != null)
 162        {
 0163            var workflowExecutionLogRecords = _safeSerializer.Deserialize<ICollection<WorkflowExecutionLogRecord>>(model
 0164            foreach (var record in workflowExecutionLogRecords)
 0165                record.TenantId = _tenantAccessor.TenantId;
 0166            await _workflowExecutionLogStore.SaveManyAsync(workflowExecutionLogRecords, cancellationToken);
 167        }
 0168    }
 169}