< Summary

Information
Class: Elsa.Bpmn.Interchange.Endpoints.Bpmn.BpmnImportErrorResponses
Assembly: Elsa.Bpmn.Interchange
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Bpmn.Interchange/Endpoints/Bpmn/BpmnImportErrorResponses.cs
Line coverage
68%
Covered lines: 28
Uncovered lines: 13
Coverable lines: 41
Total lines: 126
Line coverage: 68.2%
Branch coverage
45%
Covered branches: 5
Total branches: 11
Branch coverage: 45.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
RunAsync()45.45%231153.57%
NotFoundResponseFor(...)100%11100%
PreconditionFailedResponseFor(...)100%11100%
BindingInvalidResponseFor(...)100%11100%
DuplicateElementIdResponseFor(...)100%11100%
CapabilityResponseFor(...)100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Bpmn.Interchange/Endpoints/Bpmn/BpmnImportErrorResponses.cs

#LineLine coverage
 1using Bpmn.Interchange;
 2using Bpmn.Semantics;
 3using Elsa.Bpmn.Interchange.Exceptions;
 4using Elsa.Bpmn.Interchange.Services;
 5using Microsoft.AspNetCore.Http;
 6
 7namespace Elsa.Bpmn.Interchange.Endpoints.Bpmn;
 8
 9/// <summary>
 10/// The exception-to-response mapping shared by every endpoint that imports a BPMN document through
 11/// <c>BpmnInterchangeDocumentService</c> — <c>Import</c> and the document <c>Put</c> endpoint — plus the identical
 12/// handling both give a result whose <c>ImportResult</c> did not succeed.
 13/// </summary>
 14internal static class BpmnImportErrorResponses
 15{
 16    /// <summary>
 17    /// Runs <paramref name="import"/>, reporting the shared error response for whichever exception it throws, or for
 18    /// an unsuccessful <see cref="BpmnDocumentImportResult.ImportResult"/>. A refusal this type gives a
 19    /// <see cref="BpmnErrorCodes"/> code goes out through <paramref name="httpResponse"/> as a
 20    /// <see cref="BpmnErrorResponse"/>; the remaining, uncoded refusals still go through <paramref name="addError"/>
 21    /// and <paramref name="sendErrorsAsync"/>, exactly as before. Returns <c>null</c> in every case that already sent
 22    /// a response; the caller sends its own success response otherwise.
 23    /// </summary>
 24    public static async Task<BpmnDocumentImportResult?> RunAsync(
 25        Func<Task<BpmnDocumentImportResult>> import,
 26        HttpResponse httpResponse,
 27        Action<string> addError,
 28        Func<int, CancellationToken, Task> sendErrorsAsync,
 29        CancellationToken cancellationToken)
 30    {
 31        BpmnDocumentImportResult result;
 32
 33        try
 34        {
 5735            result = await import();
 4936        }
 037        catch (BpmnDefinitionNotFoundException exception)
 38        {
 039            await BpmnErrorResponse.SendAsync(httpResponse, NotFoundResponseFor(exception), cancellationToken);
 040            return null;
 41        }
 042        catch (BpmnDocumentPreconditionFailedException exception)
 43        {
 044            await BpmnErrorResponse.SendAsync(httpResponse, PreconditionFailedResponseFor(exception), cancellationToken)
 045            return null;
 46        }
 147        catch (BpmnInterchangeException exception)
 48        {
 149            addError(exception.Message);
 150            await sendErrorsAsync(StatusCodes.Status400BadRequest, cancellationToken);
 151            return null;
 52        }
 253        catch (BpmnBindingException exception)
 54        {
 255            await BpmnErrorResponse.SendAsync(httpResponse, BindingInvalidResponseFor(exception), cancellationToken);
 256            return null;
 57        }
 558        catch (BpmnDuplicateElementIdException exception)
 59        {
 560            await BpmnErrorResponse.SendAsync(httpResponse, DuplicateElementIdResponseFor(exception), cancellationToken)
 561            return null;
 62        }
 063        catch (BpmnCapabilityException exception)
 64        {
 065            await BpmnErrorResponse.SendAsync(httpResponse, CapabilityResponseFor(exception), cancellationToken);
 066            return null;
 67        }
 68
 4969        if (!result.ImportResult.Succeeded)
 70        {
 071            foreach (var validationError in result.ImportResult.ValidationErrors)
 072                addError(validationError.Message);
 73
 074            await sendErrorsAsync(StatusCodes.Status400BadRequest, cancellationToken);
 075            return null;
 76        }
 77
 4978        return result;
 5779    }
 80
 81    /// <summary>
 82    /// The <see cref="BpmnErrorCodes.DocumentNotFound"/> response for <paramref name="exception"/>, as its own pure,
 83    /// synchronous step so it can be asserted on directly: the endpoints that can throw
 84    /// <see cref="BpmnDefinitionNotFoundException"/> already refuse the ordinary "no such definition" case earlier,
 85    /// through their own existence check, so reaching this from a real request needs the definition to be deleted in
 86    /// the narrow window between that check and the import call this wraps — not something an HTTP-level test can
 87    /// reliably force without a race.
 88    /// </summary>
 89    internal static BpmnErrorResponse NotFoundResponseFor(BpmnDefinitionNotFoundException exception) =>
 190        BpmnErrorResponse.Create(exception.Message, BpmnErrorCodes.DocumentNotFound, StatusCodes.Status404NotFound);
 91
 92    /// <summary>
 93    /// The <see cref="BpmnErrorCodes.DocumentPreconditionFailed"/> response for a lost compare-and-swap —
 94    /// the same code and status the PUT already sends when <c>If-Match</c> is stale on arrival.
 95    /// </summary>
 96    internal static BpmnErrorResponse PreconditionFailedResponseFor(BpmnDocumentPreconditionFailedException exception) =
 197        BpmnErrorResponse.Create(exception.Message, BpmnErrorCodes.DocumentPreconditionFailed, StatusCodes.Status412Prec
 98
 99    /// <summary>The <see cref="BpmnErrorCodes.ImportBindingInvalid"/> response for <paramref name="exception"/>.</summa
 100    internal static BpmnErrorResponse BindingInvalidResponseFor(BpmnBindingException exception) =>
 3101        BpmnErrorResponse.Create(exception.Message, BpmnErrorCodes.ImportBindingInvalid, StatusCodes.Status422Unprocessa
 102
 103    /// <summary>
 104    /// The <see cref="BpmnErrorCodes.ImportDuplicateElementId"/> response for <paramref name="exception"/>, carrying
 105    /// the duplicated ids as <c>data.elementIds</c>.
 106    /// </summary>
 107    internal static BpmnErrorResponse DuplicateElementIdResponseFor(BpmnDuplicateElementIdException exception) =>
 6108        BpmnErrorResponse.Create(
 6109            exception.Message,
 6110            BpmnErrorCodes.ImportDuplicateElementId,
 6111            StatusCodes.Status422UnprocessableEntity,
 6112            new { ElementIds = exception.DuplicateElementIds });
 113
 114    /// <summary>
 115    /// The <see cref="BpmnErrorCodes.ImportCapabilityUnsupported"/> response for <paramref name="exception"/>, as its
 116    /// own pure, synchronous step so it can be asserted on directly: <c>Bpmn.*</c> 0.2.0 declares every capability
 117    /// this deployment's runtime needs, so nothing in <c>Elsa.Bpmn.Interchange.IntegrationTests</c> can currently
 118    /// make a real import throw <see cref="BpmnCapabilityException"/> to exercise this through the endpoint itself.
 119    /// </summary>
 120    internal static BpmnErrorResponse CapabilityResponseFor(BpmnCapabilityException exception) =>
 1121        BpmnErrorResponse.Create(
 1122            BpmnCapabilityErrorFormatter.Format(exception),
 1123            BpmnErrorCodes.ImportCapabilityUnsupported,
 1124            StatusCodes.Status422UnprocessableEntity,
 1125            BpmnCapabilityErrorFormatter.DataFor(exception));
 126}