From 3410c8575dba60e479fc407983e17a825b2eb705 Mon Sep 17 00:00:00 2001 From: Alexander Konietzko Date: Sat, 9 Sep 2023 21:21:23 +0200 Subject: [PATCH] feat: Add swagger attribute for sorting --- .../CleanArchitecture.Api.csproj | 32 +++++++++---------- .../Controllers/TenantController.cs | 5 ++- .../Controllers/UserController.cs | 5 ++- .../Extensions/ServiceCollectionExtension.cs | 5 +++ .../Swagger/SortableFieldsAttribute.cs | 16 ++++++++++ .../Swagger/SortableFieldsAttributeFilter.cs | 32 +++++++++++++++++++ .../Swagger/SwaggerSortableFieldsAttribute.cs | 9 ++++++ .../ViewModels/Sorting/SortQuery.cs | 1 + 8 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 CleanArchitecture.Api/Swagger/SortableFieldsAttribute.cs create mode 100644 CleanArchitecture.Api/Swagger/SortableFieldsAttributeFilter.cs create mode 100644 CleanArchitecture.Api/Swagger/SwaggerSortableFieldsAttribute.cs diff --git a/CleanArchitecture.Api/CleanArchitecture.Api.csproj b/CleanArchitecture.Api/CleanArchitecture.Api.csproj index 2a4b57b..345bdf5 100644 --- a/CleanArchitecture.Api/CleanArchitecture.Api.csproj +++ b/CleanArchitecture.Api/CleanArchitecture.Api.csproj @@ -7,29 +7,29 @@ - - - - - - - + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + - - - - + + + + diff --git a/CleanArchitecture.Api/Controllers/TenantController.cs b/CleanArchitecture.Api/Controllers/TenantController.cs index a5bf588..4a312eb 100644 --- a/CleanArchitecture.Api/Controllers/TenantController.cs +++ b/CleanArchitecture.Api/Controllers/TenantController.cs @@ -1,10 +1,13 @@ using System; using System.Threading.Tasks; using CleanArchitecture.Api.Models; +using CleanArchitecture.Api.Swagger; using CleanArchitecture.Application.Interfaces; +using CleanArchitecture.Application.SortProviders; using CleanArchitecture.Application.ViewModels; using CleanArchitecture.Application.ViewModels.Sorting; using CleanArchitecture.Application.ViewModels.Tenants; +using CleanArchitecture.Domain.Entities; using CleanArchitecture.Domain.Notifications; using MediatR; using Microsoft.AspNetCore.Authorization; @@ -34,7 +37,7 @@ public sealed class TenantController : ApiController [FromQuery] PageQuery query, [FromQuery] string searchTerm = "", [FromQuery] bool includeDeleted = false, - [FromQuery] SortQuery? sortQuery = null) + [FromQuery, SortableFieldsAttribute] SortQuery? sortQuery = null) { var tenants = await _tenantService.GetAllTenantsAsync( query, diff --git a/CleanArchitecture.Api/Controllers/UserController.cs b/CleanArchitecture.Api/Controllers/UserController.cs index 807a9b2..6e12fea 100644 --- a/CleanArchitecture.Api/Controllers/UserController.cs +++ b/CleanArchitecture.Api/Controllers/UserController.cs @@ -1,10 +1,13 @@ using System; using System.Threading.Tasks; using CleanArchitecture.Api.Models; +using CleanArchitecture.Api.Swagger; using CleanArchitecture.Application.Interfaces; +using CleanArchitecture.Application.SortProviders; using CleanArchitecture.Application.ViewModels; using CleanArchitecture.Application.ViewModels.Sorting; using CleanArchitecture.Application.ViewModels.Users; +using CleanArchitecture.Domain.Entities; using CleanArchitecture.Domain.Notifications; using MediatR; using Microsoft.AspNetCore.Authorization; @@ -34,7 +37,7 @@ public sealed class UserController : ApiController [FromQuery] PageQuery query, [FromQuery] string searchTerm = "", [FromQuery] bool includeDeleted = false, - [FromQuery] SortQuery? sortQuery = null) + [FromQuery, SortableFieldsAttribute] SortQuery? sortQuery = null) { var users = await _userService.GetAllUsersAsync( query, diff --git a/CleanArchitecture.Api/Extensions/ServiceCollectionExtension.cs b/CleanArchitecture.Api/Extensions/ServiceCollectionExtension.cs index 7224bcc..793d22f 100644 --- a/CleanArchitecture.Api/Extensions/ServiceCollectionExtension.cs +++ b/CleanArchitecture.Api/Extensions/ServiceCollectionExtension.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Text; +using CleanArchitecture.Api.Swagger; using CleanArchitecture.Domain.Settings; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Configuration; @@ -34,6 +35,10 @@ public static class ServiceCollectionExtension Scheme = "bearer" }); + c.ParameterFilter(); + + c.SupportNonNullableReferenceTypes(); + c.AddSecurityRequirement(new OpenApiSecurityRequirement { { diff --git a/CleanArchitecture.Api/Swagger/SortableFieldsAttribute.cs b/CleanArchitecture.Api/Swagger/SortableFieldsAttribute.cs new file mode 100644 index 0000000..83cca2d --- /dev/null +++ b/CleanArchitecture.Api/Swagger/SortableFieldsAttribute.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using CleanArchitecture.Application.ViewModels.Sorting; + +namespace CleanArchitecture.Api.Swagger; + +[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] +public sealed class SortableFieldsAttribute + : SwaggerSortableFieldsAttribute + where TSortingProvider : ISortingExpressionProvider, new() +{ + public override IEnumerable GetFields() + { + return new TSortingProvider().GetSortingExpressions().Keys; + } +} diff --git a/CleanArchitecture.Api/Swagger/SortableFieldsAttributeFilter.cs b/CleanArchitecture.Api/Swagger/SortableFieldsAttributeFilter.cs new file mode 100644 index 0000000..d628229 --- /dev/null +++ b/CleanArchitecture.Api/Swagger/SortableFieldsAttributeFilter.cs @@ -0,0 +1,32 @@ +using System.Linq; +using System.Reflection; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace CleanArchitecture.Api.Swagger; + +public sealed class SortableFieldsAttributeFilter : IParameterFilter +{ + public void Apply(OpenApiParameter parameter, ParameterFilterContext context) + { + if (context.ParameterInfo is null) + { + return; + } + + var attribute = context.ParameterInfo + .GetCustomAttributes() + .SingleOrDefault(); + + if (attribute is null) + { + return; + } + + var description = string.Join("
", attribute.GetFields().Order()); + + parameter.Description = $"{parameter.Description}

" + + $"**Allowed values:**
{description}"; + } +} + diff --git a/CleanArchitecture.Api/Swagger/SwaggerSortableFieldsAttribute.cs b/CleanArchitecture.Api/Swagger/SwaggerSortableFieldsAttribute.cs new file mode 100644 index 0000000..1dff29d --- /dev/null +++ b/CleanArchitecture.Api/Swagger/SwaggerSortableFieldsAttribute.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; + +namespace CleanArchitecture.Api.Swagger; + +public abstract class SwaggerSortableFieldsAttribute : Attribute +{ + public abstract IEnumerable GetFields(); +} diff --git a/CleanArchitecture.Application/ViewModels/Sorting/SortQuery.cs b/CleanArchitecture.Application/ViewModels/Sorting/SortQuery.cs index 3be687c..bbdc55b 100644 --- a/CleanArchitecture.Application/ViewModels/Sorting/SortQuery.cs +++ b/CleanArchitecture.Application/ViewModels/Sorting/SortQuery.cs @@ -10,6 +10,7 @@ public sealed class SortQuery public ReadOnlyCollection Parameters { get; private set; } = new(Array.Empty()); + [FromQuery(Name = "order_by")] public string? Query {