< Summary

Information
Class: Elsa.Http.WriteHttpResponse
Assembly: Elsa.Http
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Http/Activities/WriteHttpResponse.cs
Line coverage
84%
Covered lines: 43
Uncovered lines: 8
Coverable lines: 51
Total lines: 152
Line coverage: 84.3%
Branch coverage
80%
Covered branches: 21
Total branches: 26
Branch coverage: 80.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%210%
.ctor(...)100%210%
get_StatusCode()100%11100%
get_Content()100%11100%
get_ContentType()100%11100%
get_ResponseHeaders()100%11100%
ExecuteAsync()100%22100%
WriteResponseAsync()81.25%161695.23%
DetermineContentType(...)62.5%88100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Http/Activities/WriteHttpResponse.cs

#LineLine coverage
 1using System.Net;
 2using System.Runtime.CompilerServices;
 3using Elsa.Extensions;
 4using Elsa.Http.ContentWriters;
 5using Elsa.Http.UIHints;
 6using Elsa.Workflows;
 7using Elsa.Workflows.Attributes;
 8using Elsa.Workflows.UIHints;
 9using Elsa.Workflows.Exceptions;
 10using Elsa.Workflows.Models;
 11using Microsoft.AspNetCore.Http;
 12using Microsoft.Extensions.Options;
 13using Elsa.Http.Options;
 14
 15namespace Elsa.Http;
 16
 17/// <summary>
 18/// Write a response to the current HTTP response object.
 19/// </summary>
 20[Activity("Elsa", "HTTP", "Write a response to the current HTTP response object.", DisplayName = "HTTP Response")]
 21public class WriteHttpResponse : Activity
 22{
 23    /// <inheritdoc />
 86624    public WriteHttpResponse([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source,
 25    {
 86626    }
 27
 28    /// <inheritdoc />
 029    public WriteHttpResponse(Input<object?> content, [CallerFilePath] string? source = null, [CallerLineNumber] int? lin
 30    {
 031        Content = content;
 032    }
 33
 34    /// <inheritdoc />
 035    public WriteHttpResponse(Input<object?> content, Input<string?> contentType, [CallerFilePath] string? source = null,
 36    {
 037        Content = content;
 038        ContentType = contentType;
 039    }
 40
 41    /// <summary>
 42    /// The status code to return.
 43    /// </summary>
 44    [Input(
 45        DefaultValue = HttpStatusCode.OK,
 46        Description = "The status code to return.",
 47        UIHint = InputUIHints.DropDown
 48    )]
 317749    public Input<HttpStatusCode> StatusCode { get; set; } = new(HttpStatusCode.OK);
 50
 51    /// <summary>
 52    /// The content to write back.
 53    /// </summary>
 54    [Input(Description = "The content to write back. String values will be sent as-is, while objects will be serialized 
 238955    public Input<object?> Content { get; set; } = null!;
 56
 57    /// <summary>
 58    /// The content type to use when returning the response.
 59    /// </summary>
 60    [Input(
 61        Description = "The content type to write when sending the response.",
 62        UIHandler = typeof(HttpContentTypeOptionsProvider),
 63        UIHint = InputUIHints.DropDown
 64    )]
 237365    public Input<string?> ContentType { get; set; } = null!;
 66
 67    /// <summary>
 68    /// The headers to return along with the response.
 69    /// </summary>
 70    [Input(
 71        Description = "The headers to send along with the response.",
 72        UIHint = InputUIHints.JsonEditor,
 73        Category = "Advanced"
 74    )]
 248275    public Input<HttpHeaders?> ResponseHeaders { get; set; } = new(new HttpHeaders());
 76
 77    /// <inheritdoc />
 78    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
 79    {
 7180        var httpContextAccessor = context.GetRequiredService<IHttpContextAccessor>();
 7181        var httpContext = httpContextAccessor.HttpContext;
 82
 7183        if (httpContext == null)
 84        {
 285            throw new FaultException(
 286                HttpFaultCodes.NoHttpContext,
 287                HttpFaultCategories.Http,
 288                DefaultFaultTypes.System,
 289                "The HTTP context was lost during workflow execution. This typically occurs when a workflow initiated fr
 90        }
 91
 6992        await WriteResponseAsync(context, httpContext.Response);
 6993    }
 94
 95    private async Task WriteResponseAsync(ActivityExecutionContext context, HttpResponse response)
 96    {
 97        // Set status code.
 6998        var statusCode = StatusCode.GetOrDefault(context, () => HttpStatusCode.OK);
 6999        response.StatusCode = (int)statusCode;
 100
 101        // Add headers.
 69102        var headers = context.GetHeaders(ResponseHeaders);
 150103        foreach (var header in headers)
 6104            response.Headers[header.Key] = header.Value;
 105
 106        // Get content and content type.
 69107        var content = context.Get(Content);
 108
 69109        if (content != null)
 110        {
 57111            var contentType = ContentType.GetOrDefault(context);
 112
 57113            if (string.IsNullOrWhiteSpace(contentType))
 3114                contentType = DetermineContentType(content);
 115
 57116            var factories = context.GetServices<IHttpContentFactory>();
 192117            var factory = factories.FirstOrDefault(httpContentFactory => httpContentFactory.SupportedContentTypes.Any(c 
 57118            var httpContent = factory.CreateHttpContent(content, contentType);
 119
 120            // Set content type.
 57121            response.ContentType = httpContent.Headers.ContentType?.ToString() ?? contentType;
 122
 123            // Write content.
 57124            if (statusCode != HttpStatusCode.NoContent)
 125            {
 126                try
 127                {
 56128                    await httpContent.CopyToAsync(response.Body);
 56129                }
 130                catch (NotSupportedException)
 131                {
 132                    // This can happen the Content property is a type that cannot be serialized or contains a type that 
 0133                    await response.WriteAsync("The response includes a type that cannot be serialized.");
 134                }
 135            }
 136        }
 137
 138        // Check if the configuration is set to flush immediatly the response to the caller.
 69139        var options = context.GetRequiredService<IOptions<HttpActivityOptions>>();
 69140        if (options.Value.WriteHttpResponseSynchronously)
 1141            await response.CompleteAsync();
 142
 143        // Complete activity.
 69144        await context.CompleteActivityAsync();
 69145    }
 146
 3147    private string DetermineContentType(object? content) => content is byte[] or Stream
 3148        ? "application/octet-stream"
 3149        : content is string
 3150            ? "text/plain"
 3151            : "application/json";
 152}