diff --git a/CHANGELOG.md b/CHANGELOG.md index 3233790..a722f97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,11 @@ All notable changes to **LightQuery** are documented here. +## v1.9.0: +- Addition of the **LightQuery.Swashbuckle** (thanks to GitHub user @berkayakcay) and **LightQuery.NSwag** packages to support Swagger & OpenAPI generation + ## v1.8.1: -- The Angular library was updateds to be compatible with Angular v9.1 +- The Angular library was updated to be compatible with Angular v9.1 ## v1.8.0: - Add a `thenSort` parameter to specify a second sort option. This translates to something like `queryable.OrderBy(sort).ThenBy(thenSort)` diff --git a/LightQuery.sln b/LightQuery.sln index 4cb9520..49730d6 100644 --- a/LightQuery.sln +++ b/LightQuery.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27004.2005 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LightQuery", "src\LightQuery\LightQuery.csproj", "{33650DEE-5957-45BA-AFE4-2AFEA731DA4D}" EndProject @@ -45,6 +45,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LightQuery.Shared.Tests", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = ".build", "build\.build.csproj", "{506E6C78-4821-4269-A19A-7B60900EFBC6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightQuery.NSwag", "src\LightQuery.NSwag\LightQuery.NSwag.csproj", "{9D88E0DE-D298-4143-B051-E022B20B102E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightQuery.Swashbuckle", "src\LightQuery.Swashbuckle\LightQuery.Swashbuckle.csproj", "{CAF56EA7-A8E2-4AC7-A274-1D26C8E15401}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -101,6 +105,14 @@ Global {38C64E1E-28CD-46D9-A4B2-D2E59683F8C0}.Release|Any CPU.Build.0 = Release|Any CPU {506E6C78-4821-4269-A19A-7B60900EFBC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {506E6C78-4821-4269-A19A-7B60900EFBC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D88E0DE-D298-4143-B051-E022B20B102E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D88E0DE-D298-4143-B051-E022B20B102E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D88E0DE-D298-4143-B051-E022B20B102E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D88E0DE-D298-4143-B051-E022B20B102E}.Release|Any CPU.Build.0 = Release|Any CPU + {CAF56EA7-A8E2-4AC7-A274-1D26C8E15401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAF56EA7-A8E2-4AC7-A274-1D26C8E15401}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAF56EA7-A8E2-4AC7-A274-1D26C8E15401}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAF56EA7-A8E2-4AC7-A274-1D26C8E15401}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -118,6 +130,8 @@ Global {8CCD27FA-22E5-41FA-A796-44290D427ED9} = {7F628AED-CB88-45FF-BF48-8EA02C691700} {B2D54A8A-30DD-489C-847D-9347E4F7D436} = {6526DECE-8511-41B3-B5A6-C8170D362611} {38C64E1E-28CD-46D9-A4B2-D2E59683F8C0} = {7F628AED-CB88-45FF-BF48-8EA02C691700} + {9D88E0DE-D298-4143-B051-E022B20B102E} = {6526DECE-8511-41B3-B5A6-C8170D362611} + {CAF56EA7-A8E2-4AC7-A274-1D26C8E15401} = {6526DECE-8511-41B3-B5A6-C8170D362611} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A225AC12-BAEB-44E1-8A7E-CE6E82AF1756} diff --git a/README.md b/README.md index b9cefaf..a69a0bb 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ e.g. sorting can be done by using `bankAccount.balance`. Take this example: { "name": "Bob", "bankAccount": null - }, + } ] ``` @@ -288,6 +288,54 @@ export class UsersDetailsService extends PaginationBaseService implements } ``` +## Swagger & OpenAPI Support + +The packages **LightQuery.NSwag** and **LightQuery.Swashbuckle** support the automatic generation +of correct Swagger & OpenAPI parameter descriptions for the sort and pagination parameters. + +### Example with NSwag + +Just add the `LightQuery.NSwag.LightQueryOperationsProcessor` to your document generation: + +```csharp +services.AddSwaggerDocument(nSwagConfig => +{ + nSwagConfig.DocumentName = "swagger20"; + nSwagConfig.OperationProcessors.Add(new LightQueryOperationsProcessor()); +}); +services.AddOpenApiDocument(nSwagConfig => +{ + nSwagConfig.DocumentName = "openapi30"; + nSwagConfig.OperationProcessors.Add(new LightQueryOperationsProcessor()); +}); +``` + +### Example with Swashbuckle + +Just add the `LightQuery.Swashbuckle.LightQueryOperationFilter` to your document generation: + +```csharp +services.AddSwaggerGen(options => +{ + options.SwaggerDoc("swagger20", new OpenApiInfo() + { + Description = "swagger20" + }); + options.OperationFilter(); +}); + +services.AddSwaggerGen(options => +{ + options.SwaggerDoc("openapi30", new OpenApiInfo() + { + Description = "openapi30" + }); + options.OperationFilter(); +}); + + +``` + ## Assembly Strong Naming & Usage in Signed Applications This module produces strong named assemblies when compiled. When consumers of this package require strongly named assemblies, for example when they diff --git a/build/Build.cs b/build/Build.cs index bb408b1..4c35671 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -79,6 +79,8 @@ class Build : NukeBuild GlobDirectories(SourceDirectory / "LightQuery.Client", "**/bin", "**/obj").ForEach(DeleteDirectory); GlobDirectories(SourceDirectory / "LightQuery.EntityFrameworkCore", "**/bin", "**/obj").ForEach(DeleteDirectory); GlobDirectories(SourceDirectory / "LightQuery.Shared", "**/bin", "**/obj").ForEach(DeleteDirectory); + GlobDirectories(SourceDirectory / "LightQuery.NSwag", "**/bin", "**/obj").ForEach(DeleteDirectory); + GlobDirectories(SourceDirectory / "LightQuery.Swashbuckle", "**/bin", "**/obj").ForEach(DeleteDirectory); GlobDirectories(RootDirectory / "test", "**/bin", "**/obj").ForEach(DeleteDirectory); EnsureCleanDirectory(OutputDirectory); }); diff --git a/src/LightQuery.NSwag/LightQuery.NSwag.csproj b/src/LightQuery.NSwag/LightQuery.NSwag.csproj new file mode 100644 index 0000000..59a79e2 --- /dev/null +++ b/src/LightQuery.NSwag/LightQuery.NSwag.csproj @@ -0,0 +1,33 @@ + + + + netcoreapp3.0;netstandard2.0;net461 + True + Georg Dangl + + Extensions to use LightQuery with NSwag + (c) $([System.DateTime]::Now.Year) Georg Dangl + MIT + https://github.com/GeorgDangl/LightQuery + https://github.com/GeorgDangl/LightQuery.git + git + Asp-Net-Core Querying Sorting Filtering + gd_icon_256.png + true + LightQuery.NSwag.snk + + + + + + + + + + + + + + + + diff --git a/src/LightQuery.NSwag/LightQuery.NSwag.snk b/src/LightQuery.NSwag/LightQuery.NSwag.snk new file mode 100644 index 0000000..824c8e9 Binary files /dev/null and b/src/LightQuery.NSwag/LightQuery.NSwag.snk differ diff --git a/src/LightQuery.NSwag/LightQueryOperationsProcessor.cs b/src/LightQuery.NSwag/LightQueryOperationsProcessor.cs new file mode 100644 index 0000000..091bdfe --- /dev/null +++ b/src/LightQuery.NSwag/LightQueryOperationsProcessor.cs @@ -0,0 +1,75 @@ +using LightQuery.EntityFrameworkCore; +using NSwag; +using NSwag.Generation.Processors; +using NSwag.Generation.Processors.Contexts; +using System.Linq; +using System.Reflection; + +namespace LightQuery.NSwag +{ + public class LightQueryOperationsProcessor : IOperationProcessor + { + public bool Process(OperationProcessorContext context) + { + if (context.MethodInfo.GetCustomAttributes() + .Any(a => a is LightQueryAttribute + || a is AsyncLightQueryAttribute)) + { + context.OperationDescription + .Operation + .Parameters + .Add(new OpenApiParameter + { + Name = "sort", + Kind = OpenApiParameterKind.Query, + Description = "sort", + Schema = new NJsonSchema.JsonSchema + { + Type = NJsonSchema.JsonObjectType.String + } + }); + context.OperationDescription + .Operation + .Parameters + .Add(new OpenApiParameter + { + Name = "thenSort", + Kind = OpenApiParameterKind.Query, + Description = "then sort", + Schema = new NJsonSchema.JsonSchema + { + Type = NJsonSchema.JsonObjectType.String + } + }); + context.OperationDescription + .Operation + .Parameters + .Add(new OpenApiParameter + { + Name = "pageSize", + Kind = OpenApiParameterKind.Query, + Description = "page size", + Schema = new NJsonSchema.JsonSchema + { + Type = NJsonSchema.JsonObjectType.Integer + } + }); + context.OperationDescription + .Operation + .Parameters + .Add(new OpenApiParameter + { + Name = "page", + Kind = OpenApiParameterKind.Query, + Description = "page", + Schema = new NJsonSchema.JsonSchema + { + Type = NJsonSchema.JsonObjectType.Integer + } + }); + } + + return true; + } + } +} diff --git a/src/LightQuery.Swashbuckle/LightQuery.Swashbuckle.csproj b/src/LightQuery.Swashbuckle/LightQuery.Swashbuckle.csproj new file mode 100644 index 0000000..3f534ff --- /dev/null +++ b/src/LightQuery.Swashbuckle/LightQuery.Swashbuckle.csproj @@ -0,0 +1,36 @@ + + + + netcoreapp3.0;netstandard2.0;net461 + True + Georg Dangl, Berkay AKÇAY + + Extensions to use LightQuery with Swashbuckle + (c) $([System.DateTime]::Now.Year) Georg Dangl + MIT + https://github.com/GeorgDangl/LightQuery + https://github.com/GeorgDangl/LightQuery.git + git + Asp-Net-Core Querying Sorting Filtering + gd_icon_256.png + true + LightQuery.Swashbuckle.snk + + false + + + + + + + + + + + + + + + + diff --git a/src/LightQuery.Swashbuckle/LightQuery.Swashbuckle.snk b/src/LightQuery.Swashbuckle/LightQuery.Swashbuckle.snk new file mode 100644 index 0000000..50d791a Binary files /dev/null and b/src/LightQuery.Swashbuckle/LightQuery.Swashbuckle.snk differ diff --git a/src/LightQuery.Swashbuckle/LightQueryOperationFilter.cs b/src/LightQuery.Swashbuckle/LightQueryOperationFilter.cs new file mode 100644 index 0000000..7d90b69 --- /dev/null +++ b/src/LightQuery.Swashbuckle/LightQueryOperationFilter.cs @@ -0,0 +1,80 @@ +using System.Linq; +using System.Reflection; +using LightQuery.EntityFrameworkCore; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace LightQuery.Swashbuckle +{ + /// + /// Generates operation filter for LightQuery's paramters + /// Swashbuckle + /// + /// OpenAPI Support + public class LightQueryOperationFilter : IOperationFilter + { + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (((ControllerActionDescriptor)context.ApiDescription.ActionDescriptor).MethodInfo.GetCustomAttributes().Any(a => a is AsyncLightQueryAttribute || a is LightQueryAttribute)) + { + // sort + operation.Parameters.Add(new OpenApiParameter() + { + Name = "sort", + In = ParameterLocation.Query, + Description = "sort", + Required = false, + Schema = new OpenApiSchema + { + Type = "string", + Example = new OpenApiString("CreatedAt desc") + } + }); + + // thenSort + operation.Parameters.Add(new OpenApiParameter() + { + Name = "thenSort", + In = ParameterLocation.Query, + Description = "then sort", + Required = false, + Schema = new OpenApiSchema + { + Type = "string", + Example = new OpenApiString("UpdatedAt desc") + } + }); + + // pageSize + operation.Parameters.Add(new OpenApiParameter() + { + Name = "pageSize", + In = ParameterLocation.Query, + Description = "page size", + Required = false, + Schema = new OpenApiSchema + { + Type = "integer", + Example = new OpenApiInteger(10) + } + }); + + // page + operation.Parameters.Add(new OpenApiParameter() + { + Name = "page", + In = ParameterLocation.Query, + Description = "page", + Required = false, + Schema = new OpenApiSchema + { + Type = "integer", + Example = new OpenApiInteger(1) + } + }); + } + } + } +} \ No newline at end of file diff --git a/test/LightQuery.Client.Tests.Integration/NSwagGenerationTests.cs b/test/LightQuery.Client.Tests.Integration/NSwagGenerationTests.cs new file mode 100644 index 0000000..20a8c13 --- /dev/null +++ b/test/LightQuery.Client.Tests.Integration/NSwagGenerationTests.cs @@ -0,0 +1,120 @@ +using LightQuery.IntegrationTestsServer; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace LightQuery.Client.Tests.Integration +{ + public class NSwagGenerationTests + { + private readonly HttpClient _client = IntegrationTestServer.GetTestServer().CreateClient(); + + [Fact] + public async Task CanGetSwaggerDocument() + { + var swaggerDocResponse = await _client.GetAsync("/nswag/swagger20.json"); + var swaggerDoc = await swaggerDocResponse.Content.ReadAsStringAsync(); + Assert.True(swaggerDoc.Length > 0); + } + + [Fact] + public async Task CanGetOpenApiDocument() + { + var swaggerDocResponse = await _client.GetAsync("/nswag/openapi30.json"); + var swaggerDoc = await swaggerDocResponse.Content.ReadAsStringAsync(); + Assert.True(swaggerDoc.Length > 0); + } + + [Fact] + public async Task GeneratesSortParameterInfo_Swagger() + { + var swaggerDoc = await GetSwaggerDocAsync("/nswag/swagger20.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "sort" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesSortParameterInfo_OpenApi() + { + var swaggerDoc = await GetSwaggerDocAsync("/nswag/openapi30.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "sort" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesThenSortParameterInfo_Swagger() + { + var swaggerDoc = await GetSwaggerDocAsync("/nswag/swagger20.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "thenSort" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesThenSortParameterInfo_OpenApi() + { + var swaggerDoc = await GetSwaggerDocAsync("/nswag/openapi30.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "thenSort" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesPageParameterInfo_Swagger() + { + var swaggerDoc = await GetSwaggerDocAsync("/nswag/swagger20.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "page" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesPageParameterInfo_OpenApi() + { + var swaggerDoc = await GetSwaggerDocAsync("/nswag/openapi30.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "page" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesPageSizeParameterInfo_Swagger() + { + var swaggerDoc = await GetSwaggerDocAsync("/nswag/swagger20.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "pageSize" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesPageSizeParameterInfo_OpenApi() + { + var swaggerDoc = await GetSwaggerDocAsync("/nswag/openapi30.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "pageSize" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + private async Task GetSwaggerDocAsync(string specUrl) + { + var swaggerDocResponse = await _client.GetAsync(specUrl); + var swaggerDoc = await swaggerDocResponse.Content.ReadAsStringAsync(); + return JObject.Parse(swaggerDoc); + } + } +} diff --git a/test/LightQuery.Client.Tests.Integration/SwashbuckleGenerationTests.cs b/test/LightQuery.Client.Tests.Integration/SwashbuckleGenerationTests.cs new file mode 100644 index 0000000..868da8e --- /dev/null +++ b/test/LightQuery.Client.Tests.Integration/SwashbuckleGenerationTests.cs @@ -0,0 +1,117 @@ +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using LightQuery.IntegrationTestsServer; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace LightQuery.Client.Tests.Integration +{ + public class SwashbuckleGenerationTests + { + private readonly HttpClient _client = IntegrationTestServer.GetTestServer().CreateClient(); + + [Fact] + public async Task CanGetSwaggerDocument() + { + var swaggerDocResponse = await _client.GetAsync("/swashbuckle/swagger20.json"); + var swaggerDoc = await swaggerDocResponse.Content.ReadAsStringAsync(); + Assert.True(swaggerDoc.Length > 0); + } + + [Fact] + public async Task CanGetOpenApiDocument() + { + var swaggerDocResponse = await _client.GetAsync("/swashbuckle/openapi30.json"); + var swaggerDoc = await swaggerDocResponse.Content.ReadAsStringAsync(); + Assert.True(swaggerDoc.Length > 0); + } + + [Fact] + public async Task GeneratesSortParameterInfo_Swagger() + { + var swaggerDoc = await GetSwaggerDocAsync("/swashbuckle/swagger20.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "sort" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesSortParameterInfo_OpenApi() + { + var swaggerDoc = await GetSwaggerDocAsync("/swashbuckle/openapi30.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "sort" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesThenSortParameterInfo_Swagger() + { + var swaggerDoc = await GetSwaggerDocAsync("/swashbuckle/swagger20.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "thenSort" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesThenSortParameterInfo_OpenApi() + { + var swaggerDoc = await GetSwaggerDocAsync("/swashbuckle/openapi30.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "thenSort" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesPageParameterInfo_Swagger() + { + var swaggerDoc = await GetSwaggerDocAsync("/swashbuckle/swagger20.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "page" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesPageParameterInfo_OpenApi() + { + var swaggerDoc = await GetSwaggerDocAsync("/swashbuckle/openapi30.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "page" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesPageSizeParameterInfo_Swagger() + { + var swaggerDoc = await GetSwaggerDocAsync("/swashbuckle/swagger20.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "pageSize" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + [Fact] + public async Task GeneratesPageSizeParameterInfo_OpenApi() + { + var swaggerDoc = await GetSwaggerDocAsync("/swashbuckle/openapi30.json"); + var sortParameter = swaggerDoc.Descendants() + .OfType() + .FirstOrDefault(d => d["name"]?.Value() == "pageSize" && d.Parent.Path.EndsWith(".parameters")); + Assert.NotNull(sortParameter); + } + + private async Task GetSwaggerDocAsync(string specUrl) + { + var swaggerDocResponse = await _client.GetAsync(specUrl); + var swaggerDoc = await swaggerDocResponse.Content.ReadAsStringAsync(); + return JObject.Parse(swaggerDoc); + } + } +} \ No newline at end of file diff --git a/test/LightQuery.Client.Tests/PaginationBaseServiceTests.cs b/test/LightQuery.Client.Tests/PaginationBaseServiceTests.cs index e5019a9..e6bffb7 100644 --- a/test/LightQuery.Client.Tests/PaginationBaseServiceTests.cs +++ b/test/LightQuery.Client.Tests/PaginationBaseServiceTests.cs @@ -192,7 +192,7 @@ private class User private async Task<(HttpResponseMessage, bool)> GetResponseWithDelay(CancellationToken cancellationToken) { // Adding a short delay to simulate a long running operation, e.g. a web request - await Task.Delay(10); + await Task.Delay(100); var httpResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK); var memStream = new MemoryStream(); diff --git a/test/LightQuery.IntegrationTestsServer/Controllers/AsyncLightQueryController.cs b/test/LightQuery.IntegrationTestsServer/Controllers/AsyncLightQueryController.cs index b79a884..c59ef2d 100644 --- a/test/LightQuery.IntegrationTestsServer/Controllers/AsyncLightQueryController.cs +++ b/test/LightQuery.IntegrationTestsServer/Controllers/AsyncLightQueryController.cs @@ -15,6 +15,7 @@ public AsyncLightQueryController(LightQueryContext context) private readonly LightQueryContext _context; + [HttpGet] [AsyncLightQuery] public IActionResult GetValues() { diff --git a/test/LightQuery.IntegrationTestsServer/Controllers/AsyncPaginatedLightQueryController.cs b/test/LightQuery.IntegrationTestsServer/Controllers/AsyncPaginatedLightQueryController.cs index d6519bd..d3be1dd 100644 --- a/test/LightQuery.IntegrationTestsServer/Controllers/AsyncPaginatedLightQueryController.cs +++ b/test/LightQuery.IntegrationTestsServer/Controllers/AsyncPaginatedLightQueryController.cs @@ -16,6 +16,7 @@ public AsyncPaginatedLightQueryController(LightQueryContext context) private readonly LightQueryContext _context; + [HttpGet] [AsyncLightQuery(forcePagination: true, defaultPageSize: 3)] public IActionResult GetValues(bool returnEmptyList = false) { diff --git a/test/LightQuery.IntegrationTestsServer/Controllers/LightQueryController.cs b/test/LightQuery.IntegrationTestsServer/Controllers/LightQueryController.cs index da89166..0ca5d06 100644 --- a/test/LightQuery.IntegrationTestsServer/Controllers/LightQueryController.cs +++ b/test/LightQuery.IntegrationTestsServer/Controllers/LightQueryController.cs @@ -14,6 +14,7 @@ public LightQueryController(LightQueryContext context) private readonly LightQueryContext _context; + [HttpGet] [LightQuery] public IActionResult GetValues() { diff --git a/test/LightQuery.IntegrationTestsServer/Controllers/PaginatedLightQueryController.cs b/test/LightQuery.IntegrationTestsServer/Controllers/PaginatedLightQueryController.cs index 4ec8201..2f00a59 100644 --- a/test/LightQuery.IntegrationTestsServer/Controllers/PaginatedLightQueryController.cs +++ b/test/LightQuery.IntegrationTestsServer/Controllers/PaginatedLightQueryController.cs @@ -15,6 +15,7 @@ public PaginatedLightQueryController(LightQueryContext context) private readonly LightQueryContext _context; + [HttpGet] [LightQuery(forcePagination: true, defaultPageSize: 3)] public IActionResult GetValues(bool returnEmptyList = false) { diff --git a/test/LightQuery.IntegrationTestsServer/Controllers/ValuesController.cs b/test/LightQuery.IntegrationTestsServer/Controllers/ValuesController.cs index 6613fdc..d5c6cf5 100644 --- a/test/LightQuery.IntegrationTestsServer/Controllers/ValuesController.cs +++ b/test/LightQuery.IntegrationTestsServer/Controllers/ValuesController.cs @@ -14,6 +14,7 @@ public ValuesController(LightQueryContext context) private readonly LightQueryContext _context; + [HttpGet] public IActionResult GetValues() { var users = _context.Users.OrderBy(u => Guid.NewGuid()); diff --git a/test/LightQuery.IntegrationTestsServer/LightQuery.IntegrationTestsServer.csproj b/test/LightQuery.IntegrationTestsServer/LightQuery.IntegrationTestsServer.csproj index ef5e2c7..8319737 100644 --- a/test/LightQuery.IntegrationTestsServer/LightQuery.IntegrationTestsServer.csproj +++ b/test/LightQuery.IntegrationTestsServer/LightQuery.IntegrationTestsServer.csproj @@ -19,6 +19,8 @@ + + diff --git a/test/LightQuery.IntegrationTestsServer/Startup.cs b/test/LightQuery.IntegrationTestsServer/Startup.cs index 872f055..becbb74 100644 --- a/test/LightQuery.IntegrationTestsServer/Startup.cs +++ b/test/LightQuery.IntegrationTestsServer/Startup.cs @@ -1,6 +1,9 @@ -using Microsoft.AspNetCore.Builder; +using LightQuery.NSwag; +using LightQuery.Swashbuckle; +using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; namespace LightQuery.IntegrationTestsServer { @@ -11,8 +14,36 @@ public class Startup public void ConfigureServices(IServiceCollection services) { services.AddDbContext(options => options.UseInMemoryDatabase(_inMemoryDatabaseName)); - services.AddMvc(); + services.AddSwaggerDocument(nSwagConfig => + { + nSwagConfig.DocumentName = "swagger20"; + nSwagConfig.OperationProcessors.Add(new LightQueryOperationsProcessor()); + }); + services.AddOpenApiDocument(nSwagConfig => + { + nSwagConfig.DocumentName = "openapi30"; + nSwagConfig.OperationProcessors.Add(new LightQueryOperationsProcessor()); + }); + + services.AddSwaggerGen(options => + { + options.SwaggerDoc("swagger20", new OpenApiInfo() + { + Description = "swagger20" + }); + options.OperationFilter(); + }); + + services.AddSwaggerGen(options => + { + options.SwaggerDoc("openapi30", new OpenApiInfo() + { + Description = "openapi30" + }); + options.OperationFilter(); + }); + #if NETCORE3 services.AddMvc(mvcOptions => mvcOptions.EnableEndpointRouting = false); ; #else @@ -22,6 +53,32 @@ public void ConfigureServices(IServiceCollection services) public void Configure(IApplicationBuilder app) { + app.UseOpenApi(openApiConfig => + { + openApiConfig.DocumentName = "swagger20"; + openApiConfig.Path = "/nswag/swagger20.json"; + }); + app.UseOpenApi(openApiConfig => + { + openApiConfig.DocumentName = "openapi30"; + openApiConfig.Path = "/nswag/openapi30.json"; + }); + + /* + * Change the path for swageer json endpoints + * https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-the-path-for-swagger-json-endpoints + */ + app.UseSwagger(options => + { + options.RouteTemplate = "swashbuckle/{documentName}.json"; + options.SerializeAsV2 = true; + }); + + app.UseSwagger(options => + { + options.RouteTemplate = "swashbuckle/{documentName}.json"; + }); + app.UseMvc(); } }