< Summary - Code Coverage

Information
Class: Plainquire.Page.Swashbuckle.OpenApiOperationExtensions
Assembly: Plainquire.Page.Swashbuckle
File(s): /home/runner/work/plainquire/plainquire/Plainquire.Page/Plainquire.Page.Swashbuckle/Extensions/OpenApiOperationExtensions.cs
Tag: 74_23635074410
Line coverage
94%
Covered lines: 104
Uncovered lines: 6
Coverable lines: 110
Total lines: 188
Line coverage: 94.5%
Branch coverage
70%
Covered branches: 28
Total branches: 40
Branch coverage: 70%
Method coverage
100%
Covered methods: 11
Total methods: 11
Method coverage: 100%

Metrics

File(s)

/home/runner/work/plainquire/plainquire/Plainquire.Page/Plainquire.Page.Swashbuckle/Extensions/OpenApiOperationExtensions.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Mvc.Abstractions;
 2using Microsoft.OpenApi;
 3using Plainquire.Page.Swashbuckle.Models;
 4using Swashbuckle.AspNetCore.SwaggerGen;
 5using System;
 6using System.Collections.Generic;
 7using System.Linq;
 8using System.Text.Json.Nodes;
 9
 10namespace Plainquire.Page.Swashbuckle;
 11
 12/// <summary>
 13/// Extension methods for <see cref="OpenApiOperation"/>.
 14/// </summary>
 15internal static class OpenApiOperationExtensions
 16{
 17    private const string PARAMETER_INDEX_EXTENSION = "x-original-parameter-index";
 18    private const string ENTITY_PAGE_EXTENSION = "x-entity-page";
 19    private const string ENTITY_DELETE_EXTENSION = "x-entity-page-delete";
 20
 21    /// <summary>
 22    /// Replaces <see cref="EntityPage{TEntity}"/> and with the sort parameters.
 23    /// </summary>
 24    /// <param name="operation">The <see cref="OpenApiOperation"/> to operate on.</param>
 25    /// <param name="parametersToReplace">The parameters to replace.</param>
 26    public static void ReplacePageParameters(this OpenApiOperation operation, IList<PageParameterReplacement> parameters
 27    {
 2528        MarkExistingParametersForDeletion(parametersToReplace);
 2529        ReplacePageNumberParameters(operation, parametersToReplace);
 2530        ReplacePageSizeParameters(operation, parametersToReplace);
 2531        RemoveParametersMarkedForDeletion(operation);
 2532    }
 33
 34    public static void AddOriginalIndexExtensionIfMissing(this OpenApiOperation operation, OperationFilterContext contex
 35    {
 2536        if (operation.Parameters == null)
 037            return;
 38
 2539        var indexExtensionsAlreadyAdded = operation.Parameters.Any(p => p.Extensions?.ContainsKey(PARAMETER_INDEX_EXTENS
 2540        if (indexExtensionsAlreadyAdded)
 441            return;
 42
 15043        for (var index = 0; index < operation.Parameters.Count; index++)
 44        {
 5445            var parameter = operation.Parameters[index];
 5446            if (parameter is not IOpenApiExtensible openApiParameter)
 047                throw new InvalidOperationException("The OpenApiParameter must implement IOpenApiExtensible to be replac
 48
 5449            openApiParameter.Extensions ??= new Dictionary<string, IOpenApiExtension>(StringComparer.OrdinalIgnoreCase);
 5450            openApiParameter.Extensions.Add(PARAMETER_INDEX_EXTENSION, new JsonNodeExtension(JsonValue.Create(index)));
 51        }
 2152    }
 53
 54    public static int GetOriginalIndex(this IOpenApiParameter parameter)
 55    {
 9056        if (parameter.Extensions == null)
 057            return -1;
 58
 9059        if (!parameter.Extensions.TryGetValue(PARAMETER_INDEX_EXTENSION, out var extension))
 1660            return -1;
 61
 7462        if (extension is not JsonNodeExtension nodeExtension)
 063            throw new InvalidOperationException($"Extension '{PARAMETER_INDEX_EXTENSION}' must be of type {nameof(JsonNo
 64
 7465        if (nodeExtension.Node is not JsonValue jsonValue)
 066            throw new InvalidOperationException($"Value of extension '{PARAMETER_INDEX_EXTENSION}' must be of type {name
 67
 7468        return jsonValue.GetValue<int>();
 69    }
 70
 71    private static void ReplacePageNumberParameters(OpenApiOperation operation, IList<PageParameterReplacement> paramete
 72    {
 2573        var httpQueryParameterGroup = GroupByPageNumberHttpQueryParameterName(parametersToReplace);
 11474        foreach (var (queryParameter, parameters) in httpQueryParameterGroup)
 75        {
 3276            var openApiParameter = new OpenApiParameter
 3277            {
 3278                Name = queryParameter,
 3279                Description = "Pages the result by the given page number.",
 3280                Schema = new OpenApiSchema
 3281                {
 3282                    Type = JsonSchemaType.Integer,
 3283                    Format = "int32"
 3284                },
 3285                In = ParameterLocation.Query,
 3286                Extensions = new Dictionary<string, IOpenApiExtension>(StringComparer.OrdinalIgnoreCase)
 3287                {
 3288                    [ENTITY_PAGE_EXTENSION] = new JsonNodeExtension(JsonValue.Create(true))
 3289                }
 3290            };
 91
 3292            operation.Parameters ??= new List<IOpenApiParameter>();
 3293            var insertionIndex = operation.Parameters.IndexOf(parameters[0].OpenApiParameter);
 3294            operation.Parameters.Insert(insertionIndex, openApiParameter);
 95        }
 2596    }
 97
 98    private static void ReplacePageSizeParameters(OpenApiOperation operation, IList<PageParameterReplacement> parameters
 99    {
 25100        var httpQueryParameterGroup = GroupByPageSizeHttpQueryParameterName(parametersToReplace);
 112101        foreach (var (queryParameter, parameters) in httpQueryParameterGroup)
 102        {
 31103            var openApiParameter = new OpenApiParameter
 31104            {
 31105                Name = queryParameter,
 31106                Description = "Pages the result by the given page size.",
 31107                Schema = new OpenApiSchema
 31108                {
 31109                    Type = JsonSchemaType.Integer,
 31110                    Format = "int32"
 31111                },
 31112                In = ParameterLocation.Query,
 31113                Extensions = new Dictionary<string, IOpenApiExtension>(StringComparer.OrdinalIgnoreCase)
 31114                {
 31115                    [ENTITY_PAGE_EXTENSION] = new JsonNodeExtension(JsonValue.Create(true))
 31116                }
 31117            };
 118
 31119            operation.Parameters ??= new List<IOpenApiParameter>();
 31120            var insertionIndex = operation.Parameters.IndexOf(parameters[0].OpenApiParameter);
 31121            operation.Parameters.Insert(insertionIndex, openApiParameter);
 122        }
 25123    }
 124
 125    private static Dictionary<string, List<PageParameterReplacement>> GroupByPageNumberHttpQueryParameterName(IList<Page
 25126        => parametersToReplace
 25127            .GroupBy(parameter => GetPageNumberParameterName(parameter.OpenApiDescription.ParameterDescriptor), StringCo
 25128            .ToDictionary(
 25129                group => group.Key,
 25130                group => group.ToList(),
 25131                StringComparer.OrdinalIgnoreCase
 25132            );
 133
 134    private static Dictionary<string, List<PageParameterReplacement>> GroupByPageSizeHttpQueryParameterName(IList<PagePa
 25135        => parametersToReplace
 25136            .GroupBy(parameter => GetPageSizeParameterName(parameter.OpenApiDescription.ParameterDescriptor), StringComp
 25137            .ToDictionary(
 25138                group => group.Key,
 25139                group => group.ToList(),
 25140                StringComparer.OrdinalIgnoreCase
 25141            );
 142
 143    private static void MarkExistingParametersForDeletion(IList<PageParameterReplacement> parameters)
 144    {
 158145        foreach (var parameter in parameters)
 146        {
 54147            if (parameter.OpenApiParameter is not IOpenApiExtensible extensibleParameter)
 0148                throw new InvalidOperationException("The OpenApiParameter must implement IOpenApiExtensible to be replac
 149
 54150            extensibleParameter.Extensions ??= new Dictionary<string, IOpenApiExtension>(StringComparer.OrdinalIgnoreCas
 54151            extensibleParameter.Extensions.TryAdd(ENTITY_DELETE_EXTENSION, new JsonNodeExtension(JsonValue.Create(true))
 152        }
 25153    }
 154
 155    private static void RemoveParametersMarkedForDeletion(OpenApiOperation operation)
 156    {
 25157        operation.Parameters ??= new List<IOpenApiParameter>();
 25158        var parametersToRemove = operation.Parameters
 25159            .Where(parameter =>
 25160            {
 25161                if (parameter is not IOpenApiExtensible extensibleParameter)
 25162                    throw new InvalidOperationException("The OpenApiParameter must implement IOpenApiExtensible to be re
 25163
 25164                extensibleParameter.Extensions ??= new Dictionary<string, IOpenApiExtension>(StringComparer.OrdinalIgnor
 25165                return extensibleParameter.Extensions.ContainsKey(ENTITY_DELETE_EXTENSION);
 25166            })
 25167            .ToList();
 168
 158169        foreach (var parameter in parametersToRemove)
 54170            operation.Parameters.Remove(parameter);
 25171    }
 172
 173    private static string GetPageNumberParameterName(ParameterDescriptor parameterDescriptor)
 174    {
 54175        var actionParameterName = parameterDescriptor.Name;
 54176        var bindingParameterName = parameterDescriptor.BindingInfo?.BinderModelName;
 54177        var (pageNumberName, _) = ParameterExtensions.GetPageParameterNames(actionParameterName, bindingParameterName);
 54178        return pageNumberName;
 179    }
 180
 181    private static string GetPageSizeParameterName(ParameterDescriptor parameterDescriptor)
 182    {
 54183        var actionParameterName = parameterDescriptor.Name;
 54184        var bindingParameterName = parameterDescriptor.BindingInfo?.BinderModelName;
 54185        var (_, pageSizeName) = ParameterExtensions.GetPageParameterNames(actionParameterName, bindingParameterName);
 54186        return pageSizeName;
 187    }
 188}