< Summary - Code Coverage

Information
Class: Plainquire.Sort.Swashbuckle.OpenApiOperationExtensions
Assembly: Plainquire.Sort.Swashbuckle
File(s): /home/runner/work/plainquire/plainquire/Plainquire.Sort/Plainquire.Sort.Swashbuckle/Extensions/OpenApiOperationExtensions.cs
Tag: 70_19770578580
Line coverage
98%
Covered lines: 86
Uncovered lines: 1
Coverable lines: 87
Total lines: 153
Line coverage: 98.8%
Branch coverage
78%
Covered branches: 11
Total branches: 14
Branch coverage: 78.5%
Method coverage
100%
Covered methods: 8
Total methods: 8
Method coverage: 100%

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
ReplaceSortParameters(...)75%44100%
GroupByHttpQueryParameterName(...)100%11100%
MarkExistingParametersForDeletion(...)83.33%6683.33%
RemoveParametersMarkedForDeletion(...)75%44100%
GetSortPrefixes(...)100%11100%
CreatePropertyNamePattern(...)100%11100%
GetSortablePropertyNames(...)100%11100%
GetSortPropertyNames(...)100%11100%

File(s)

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

#LineLine coverage
 1using Microsoft.OpenApi;
 2using Plainquire.Filter.Abstractions;
 3using Plainquire.Sort.Swashbuckle.Models;
 4using System;
 5using System.Collections.Generic;
 6using System.Linq;
 7using System.Reflection;
 8using System.Text.Json.Nodes;
 9using System.Text.RegularExpressions;
 10
 11namespace Plainquire.Sort.Swashbuckle;
 12
 13/// <summary>
 14/// Extension methods for <see cref="OpenApiOperation"/>.
 15/// </summary>
 16public static class OpenApiOperationExtensions
 17{
 18    private const string ENTITY_SORT_EXTENSION = "x-entity-sort";
 19    private const string ENTITY_DELETE_EXTENSION = "x-entity-sort-delete";
 20
 21    /// <summary>
 22    /// Replaces <see cref="EntitySort{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 ReplaceSortParameters(this OpenApiOperation operation, IList<SortParameterReplacement> parameters
 27    {
 5828        MarkExistingParametersForDeletion(parametersToReplace);
 29
 5830        var httpQueryParameterGroup = GroupByHttpQueryParameterName(parametersToReplace);
 19831        foreach (var (queryParameter, parameters) in httpQueryParameterGroup)
 32        {
 4133            var (prefixes, postfixes, primaryAscendingPostfix, primaryDescendingPostfix) = GetSortPrefixes(parameters);
 4134            var allowedPropertyNamePattern = CreatePropertyNamePattern(parameters, prefixes, postfixes);
 35
 4136            var openApiParameter = new OpenApiParameter
 4137            {
 4138                Name = queryParameter,
 4139                Description = $"Sorts the result by the given property in ascending ({primaryAscendingPostfix}) or desce
 4140                Schema = new OpenApiSchema
 4141                {
 4142                    Type = JsonSchemaType.Array,
 4143                    Items = new OpenApiSchema
 4144                    {
 4145                        Type = JsonSchemaType.String,
 4146                        Example = string.Empty,
 4147                        Pattern = allowedPropertyNamePattern
 4148                    },
 4149                },
 4150                In = ParameterLocation.Query,
 4151                Extensions = new Dictionary<string, IOpenApiExtension>(StringComparer.OrdinalIgnoreCase)
 4152                {
 4153                    [ENTITY_SORT_EXTENSION] = new JsonNodeExtension(JsonValue.Create(true))
 4154                }
 4155            };
 56
 4157            operation.Parameters ??= new List<IOpenApiParameter>();
 4158            var insertionIndex = operation.Parameters.IndexOf(parameters[0].OpenApiParameter);
 4159            operation.Parameters.Insert(insertionIndex, openApiParameter);
 60        }
 61
 5862        RemoveParametersMarkedForDeletion(operation);
 5863    }
 64
 65    private static Dictionary<string, List<SortParameterReplacement>> GroupByHttpQueryParameterName(IList<SortParameterR
 5866        => parametersToReplace
 5867            .GroupBy(parameter =>
 5868            {
 5869                var bindingParameterName = parameter.OpenApiDescription.ParameterDescriptor.BindingInfo?.BinderModelName
 5870                var actionParameterName = parameter.OpenApiDescription.ParameterDescriptor.Name;
 5871                return bindingParameterName ?? actionParameterName;
 5872            }, StringComparer.OrdinalIgnoreCase)
 5873            .ToDictionary(
 5874                group => group.Key,
 5875                group => group.ToList(),
 5876                StringComparer.OrdinalIgnoreCase
 5877            );
 78
 79    private static void MarkExistingParametersForDeletion(IList<SortParameterReplacement> parameters)
 80    {
 26681        foreach (var parameter in parameters)
 82        {
 7583            if (parameter.OpenApiParameter is not IOpenApiExtensible extensibleParameter)
 084                throw new InvalidOperationException("The OpenApiParameter must implement IOpenApiExtensible to be replac
 85
 7586            extensibleParameter.Extensions ??= new Dictionary<string, IOpenApiExtension>(StringComparer.OrdinalIgnoreCas
 7587            extensibleParameter.Extensions.TryAdd(ENTITY_DELETE_EXTENSION, new JsonNodeExtension(JsonValue.Create(true))
 88        }
 5889    }
 90
 91    private static void RemoveParametersMarkedForDeletion(OpenApiOperation operation)
 92    {
 5893        operation.Parameters ??= new List<IOpenApiParameter>();
 5894        var parametersToRemove = operation.Parameters
 5895            .Where(parameter =>
 5896            {
 5897                if (parameter is not IOpenApiExtensible extensibleParameter)
 5898                    throw new InvalidOperationException("The OpenApiParameter must implement IOpenApiExtensible to be re
 5899
 58100                extensibleParameter.Extensions ??= new Dictionary<string, IOpenApiExtension>(StringComparer.OrdinalIgnor
 58101
 58102                return extensibleParameter.Extensions.ContainsKey(ENTITY_DELETE_EXTENSION);
 58103            })
 58104            .ToList();
 105
 246106        foreach (var parameter in parametersToRemove)
 65107            operation.Parameters.Remove(parameter);
 58108    }
 109
 110    private static (List<string>, List<string>, string, string) GetSortPrefixes(IReadOnlyCollection<SortParameterReplace
 111    {
 41112        var ascendingPrefixes = parameters.Select(parameter => parameter.Configuration.AscendingPrefixes).SelectMany(x =
 41113        var descendingPrefixes = parameters.Select(parameter => parameter.Configuration.DescendingPrefixes).SelectMany(x
 41114        var ascendingPostfixes = parameters.Select(parameter => parameter.Configuration.AscendingPostfixes).SelectMany(x
 41115        var descendingPostfixes = parameters.Select(parameter => parameter.Configuration.DescendingPostfixes).SelectMany
 116
 41117        var prefixes = ascendingPrefixes.Concat(descendingPrefixes).Distinct(StringComparer.OrdinalIgnoreCase).Select(Re
 41118        var postfixes = ascendingPostfixes.Concat(descendingPostfixes).Distinct(StringComparer.OrdinalIgnoreCase).Select
 119
 41120        var primaryAscendingPostfix = ascendingPostfixes[0];
 41121        var primaryDescendingPostfix = descendingPostfixes[0];
 122
 41123        return (prefixes, postfixes, primaryAscendingPostfix, primaryDescendingPostfix);
 124    }
 125
 126    private static string CreatePropertyNamePattern(IEnumerable<SortParameterReplacement> parameters, IEnumerable<string
 127    {
 41128        var sortablePropertyNames = GetSortablePropertyNames(parameters);
 129
 41130        var allowedPropertyNamePattern = $"^({string.Join("|", prefixes)})?({string.Join("|", sortablePropertyNames)})(\
 41131        return allowedPropertyNamePattern;
 132    }
 133
 134    private static List<string> GetSortablePropertyNames(IEnumerable<SortParameterReplacement> parameters)
 41135        => parameters
 41136            .Select(parameter => parameter.SortedType)
 41137            .SelectMany(sortedType => sortedType.GetSortPropertyNames())
 41138            .Distinct(StringComparer.OrdinalIgnoreCase)
 41139            .Select(Regex.Escape)
 41140            .ToList();
 141
 142    private static List<string> GetSortPropertyNames(this Type sortedType)
 143    {
 75144        var entityFilterAttribute = sortedType.GetCustomAttribute<EntityFilterAttribute>();
 145
 75146        var sortableProperties = sortedType
 75147            .GetSortableProperties()
 75148            .Select(property => property.GetSortParameterName(entityFilterAttribute?.Prefix))
 75149            .ToList();
 150
 75151        return sortableProperties;
 152    }
 153}