From 5c64ab662b4b989b840cb500337b51f70cc4224b Mon Sep 17 00:00:00 2001 From: Nicholas Tsim Date: Fri, 15 Nov 2024 14:55:15 +0000 Subject: [PATCH] EES-5663 Remove `version` path parameters from OpenAPI documents --- .../VersionedPathsDocumentFilterTests.cs | 130 ++++++++++++++++++ .../Swagger/SwaggerConfig.cs | 3 + .../Swagger/VersionedPathsDocumentFilter.cs | 32 +++++ 3 files changed, 165 insertions(+) create mode 100644 src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api.Tests/Swagger/VersionedPathsDocumentFilterTests.cs create mode 100644 src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api/Swagger/VersionedPathsDocumentFilter.cs diff --git a/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api.Tests/Swagger/VersionedPathsDocumentFilterTests.cs b/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api.Tests/Swagger/VersionedPathsDocumentFilterTests.cs new file mode 100644 index 00000000000..6f8ae622251 --- /dev/null +++ b/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api.Tests/Swagger/VersionedPathsDocumentFilterTests.cs @@ -0,0 +1,130 @@ +using System.Text.Json; +using GovUk.Education.ExploreEducationStatistics.Public.Data.Api.Swagger; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace GovUk.Education.ExploreEducationStatistics.Public.Data.Api.Tests.Swagger; + +public class VersionedPathsDocumentFilterTests +{ + [Fact] + public void VersionsInlinedIntoPaths() + { + var document = new OpenApiDocument + { + Info = new OpenApiInfo { Version = "1" }, + Paths = new OpenApiPaths + { + {"/v{version}/endpoint-1", new OpenApiPathItem()}, + {"/v{version}/endpoint-2/{id}", new OpenApiPathItem()} + } + }; + + var filter = new VersionedPathsDocumentFilter(); + var context = CreateDocumentFilterContext(); + + filter.Apply(document, context); + + Assert.Equal(2, document.Paths.Count); + Assert.Contains("/v1/endpoint-1", document.Paths.Keys); + Assert.Contains("/v1/endpoint-2/{id}", document.Paths.Keys); + } + + [Fact] + public void VersionParametersRemoved() + { + var document = new OpenApiDocument + { + Info = new OpenApiInfo { Version = "1" }, + Paths = new OpenApiPaths + { + { + "/v{version}/endpoint-1", + new OpenApiPathItem + { + Parameters = + [ + new OpenApiParameter { Name = "version" } + ], + Operations = + { + { + OperationType.Get, + new OpenApiOperation + { + Parameters = + [ + new OpenApiParameter { Name = "version" } + ] + } + } + + } + } + }, + { + "/v{version}/endpoint-2/{id}", + new OpenApiPathItem + { + Parameters = + [ + new OpenApiParameter { Name = "version" }, + new OpenApiParameter { Name = "id" } + ], + Operations = + { + { + OperationType.Get, + new OpenApiOperation + { + Parameters = + [ + new OpenApiParameter { Name = "version" }, + new OpenApiParameter { Name = "id" } + ] + } + } + + } + } + } + } + }; + + var filter = new VersionedPathsDocumentFilter(); + var context = CreateDocumentFilterContext(); + + filter.Apply(document, context); + + Assert.Equal(2, document.Paths.Count); + + var endpoint1Paths = document.Paths["/v1/endpoint-1"]; + + Assert.Empty(endpoint1Paths.Parameters); + Assert.Empty(endpoint1Paths.Operations[OperationType.Get].Parameters); + + var endpoint2Paths = document.Paths["/v1/endpoint-2/{id}"]; + + Assert.Single(endpoint2Paths.Parameters); + Assert.Equal("id", endpoint2Paths.Parameters[0].Name); + + Assert.Single(endpoint2Paths.Operations[OperationType.Get].Parameters); + Assert.Equal("id", endpoint2Paths.Parameters[0].Name); + } + + private static DocumentFilterContext CreateDocumentFilterContext() + { + var schemaGenerator = new SchemaGenerator( + new SchemaGeneratorOptions(), + new JsonSerializerDataContractResolver( + new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + } + ) + ); + var schemaRepository = new SchemaRepository(); + + return new DocumentFilterContext([], schemaGenerator, schemaRepository); + } +} diff --git a/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api/Swagger/SwaggerConfig.cs b/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api/Swagger/SwaggerConfig.cs index 8d00439bc1e..babd1bdec18 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api/Swagger/SwaggerConfig.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api/Swagger/SwaggerConfig.cs @@ -16,8 +16,11 @@ public class SwaggerConfig( { public void Configure(SwaggerGenOptions options) { + options.DocumentFilter(); + options.OperationFilter(); options.OperationFilter(); + options.SchemaFilter(); options.SchemaFilter(); options.SchemaFilter(); diff --git a/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api/Swagger/VersionedPathsDocumentFilter.cs b/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api/Swagger/VersionedPathsDocumentFilter.cs new file mode 100644 index 00000000000..3e0b85e334a --- /dev/null +++ b/src/GovUk.Education.ExploreEducationStatistics.Public.Data.Api/Swagger/VersionedPathsDocumentFilter.cs @@ -0,0 +1,32 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace GovUk.Education.ExploreEducationStatistics.Public.Data.Api.Swagger; + +public class VersionedPathsDocumentFilter : IDocumentFilter +{ + public void Apply(OpenApiDocument document, DocumentFilterContext context) + { + var newPaths = new OpenApiPaths(); + + foreach (var path in document.Paths) + { + var versionedPath = path.Key.Replace("{version}", document.Info.Version); + + newPaths[versionedPath] = path.Value; + + path.Value.Parameters = path.Value.Parameters + .Where(p => p.Name != "version") + .ToList(); + + foreach (var operation in path.Value.Operations.Values) + { + operation.Parameters = operation.Parameters + .Where(p => p.Name != "version") + .ToList(); + } + } + + document.Paths = newPaths; + } +}