< 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: 22
Uncovered lines: 62
Coverable lines: 84
Total lines: 161
Line coverage: 26.1%
Branch coverage
0%
Covered branches: 0
Total branches: 14
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%4260%

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.Workflows.Api.Models;
 6using Elsa.Workflows.Management;
 7using Elsa.Workflows.Runtime;
 8using Elsa.Workflows.Runtime.Entities;
 9using JetBrains.Annotations;
 10using Microsoft.AspNetCore.Http;
 11
 12namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Import;
 13
 14/// <summary>
 15/// Imports JSON and/or ZIP files containing a workflow instances.
 16/// </summary>
 17[PublicAPI]
 18internal class Import : ElsaEndpointWithoutRequest<Response>
 19{
 20    private readonly IWorkflowInstanceManager _workflowInstanceManager;
 21    private readonly IWorkflowInstanceStore _workflowInstanceStore;
 22    private readonly IActivityExecutionStore _activityExecutionStore;
 23    private readonly IWorkflowExecutionLogStore _workflowExecutionLogStore;
 24    private readonly IBookmarkStore _bookmarkStore;
 25    private readonly IWorkflowStateSerializer _workflowStateSerializer;
 26    private readonly IPayloadSerializer _payloadSerializer;
 27    private readonly ISafeSerializer _safeSerializer;
 28
 29    /// <inheritdoc />
 330    public Import(
 331        IWorkflowInstanceManager workflowInstanceManager,
 332        IWorkflowInstanceStore workflowInstanceStore,
 333        IActivityExecutionStore activityExecutionStore,
 334        IWorkflowExecutionLogStore workflowExecutionLogStore,
 335        IBookmarkStore bookmarkStore,
 336        IWorkflowStateSerializer workflowStateSerializer,
 337        IPayloadSerializer payloadSerializer,
 338        ISafeSerializer safeSerializer)
 39    {
 340        _workflowInstanceManager = workflowInstanceManager;
 341        _workflowInstanceStore = workflowInstanceStore;
 342        _activityExecutionStore = activityExecutionStore;
 343        _workflowExecutionLogStore = workflowExecutionLogStore;
 344        _bookmarkStore = bookmarkStore;
 345        _workflowStateSerializer = workflowStateSerializer;
 346        _payloadSerializer = payloadSerializer;
 347        _safeSerializer = safeSerializer;
 348    }
 49
 50    /// <inheritdoc />
 51    public override void Configure()
 52    {
 353        Post("/bulk-actions/import/workflow-instances");
 354        RequirePermission(Elsa.Workflows.Api.Permissions.WorkflowPermissions.Instances, CoreVerbs.Write);
 355        AllowFileUploads();
 356    }
 57
 58    /// <inheritdoc />
 59    public override async Task HandleAsync(CancellationToken cancellationToken)
 60    {
 061        var count = await ImportFilesAsync(Files, cancellationToken);
 062        var response = new Response { Imported = count };
 63
 064        await Send.OkAsync(response, cancellationToken);
 065    }
 66
 67    private async Task<int> ImportFilesAsync(IFormFileCollection files, CancellationToken cancellationToken)
 68    {
 069        var count = 0;
 70
 071        foreach (var file in files)
 72        {
 073            var fileStream = file.OpenReadStream();
 74
 75            // Check if the file is a JSON file or a ZIP file.
 076            var isJsonFile = file.ContentType == "application/json";
 77
 78            // If the file is a JSON file, read it.
 079            if (isJsonFile)
 80            {
 081                await ImportJsonStreamAsync(fileStream, cancellationToken);
 082                count++;
 83            }
 84            else
 85            {
 86                // If the file is a ZIP file, extract the JSON files and read them.
 087                var zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read);
 88
 089                foreach (var entry in zipArchive.Entries)
 90                {
 091                    if (!entry.FullName.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
 92                        continue;
 93
 094                    var jsonStream = entry.Open();
 095                    await ImportJsonStreamAsync(jsonStream, cancellationToken);
 096                    count++;
 97                }
 98            }
 99        }
 100
 0101        return count;
 0102    }
 103
 104    private async Task ImportJsonStreamAsync(Stream jsonStream, CancellationToken cancellationToken)
 105    {
 0106        var json = await new StreamReader(jsonStream).ReadToEndAsync();
 0107        var model = JsonSerializer.Deserialize<ExportedWorkflowState>(json)!;
 0108        await ImportSingleWorkflowInstanceAsync(model, cancellationToken);
 0109    }
 110
 111    private async Task ImportSingleWorkflowInstanceAsync(ExportedWorkflowState model, CancellationToken cancellationToke
 112    {
 0113        var workflowState = _workflowStateSerializer.Deserialize(model.WorkflowState);
 0114        await _workflowInstanceManager.SaveAsync(workflowState, cancellationToken);
 115
 0116        if (model.Bookmarks != null)
 117        {
 0118            var bookmarksElement = model.Bookmarks.Value.EnumerateArray().ToList();
 0119            var bookmarks = bookmarksElement.Select(x =>
 0120            {
 0121                var bookmarkId = x.GetProperty("id").GetString()!;
 0122                var activityTypeName = x.GetProperty("activityTypeName").GetString()!;
 0123                var workflowInstanceId = x.GetProperty("workflowInstanceId").GetString()!;
 0124                var activityInstanceId = x.GetProperty("activityInstanceId").GetString();
 0125                var hash = x.GetProperty("hash").GetString()!;
 0126                var correlationId = x.GetProperty("correlationId").GetString();
 0127                var createdAt = x.GetProperty("createdAt").GetDateTimeOffset();
 0128                var payloadElement = x.GetProperty("payload");
 0129                var metadataElement = x.GetProperty("metadata");
 0130                var payload = _payloadSerializer.Deserialize<object>(payloadElement);
 0131                var metadata = _payloadSerializer.Deserialize<IDictionary<string, string>>(metadataElement);
 0132
 0133                return new StoredBookmark
 0134                {
 0135                    Id = bookmarkId,
 0136                    Name = activityTypeName,
 0137                    Hash = hash,
 0138                    WorkflowInstanceId = workflowInstanceId,
 0139                    CreatedAt = createdAt,
 0140                    ActivityInstanceId = activityInstanceId,
 0141                    CorrelationId = correlationId,
 0142                    Payload = payload,
 0143                    Metadata = metadata
 0144                };
 0145            }).ToList();
 0146            await _bookmarkStore.SaveManyAsync(bookmarks, cancellationToken);
 147        }
 148
 0149        if (model.ActivityExecutionRecords != null)
 150        {
 0151            var activityExecutionRecords = _safeSerializer.Deserialize<ICollection<ActivityExecutionRecord>>(model.Activ
 0152            await _activityExecutionStore.SaveManyAsync(activityExecutionRecords, cancellationToken);
 153        }
 154
 0155        if (model.WorkflowExecutionLogRecords != null)
 156        {
 0157            var workflowExecutionLogRecords = _safeSerializer.Deserialize<ICollection<WorkflowExecutionLogRecord>>(model
 0158            await _workflowExecutionLogStore.SaveManyAsync(workflowExecutionLogRecords, cancellationToken);
 159        }
 0160    }
 161}