diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7957cca..cd836b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,7 @@ jobs: - name: Create the package run: dotnet pack --configuration Release SimpleEndPoints working-directory: ./src - - name: Publish the package to NUGET.ORG + - name: Publish the package to NUGET.ORG + if: github.ref == 'refs/heads/master' run: dotnet nuget push SimpleEndPoints/bin/Release/*.nupkg -k ${{secrets.NUGET_APIKEY}} -s https://api.nuget.org/v3/index.json --skip-duplicate working-directory: ./src diff --git a/README.md b/README.md index c16a20a..40082d8 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,10 @@ In the NuGet Package Manager Console, type: services.AddControllers((options) => { - options.AddEndpointRoutingConvention(); // This is required to translate endpoint names + options.AddSimpleEndpointsRouting(); // This is required to translate endpoint names + + // Optional: If you need to support Swashbuckle, you will need the "SimpleEndpoints.Swashbuckle" package too + options.AddSwashbuckleCompatibilityForSimpleEndpoints(); // this is required to support Swashbuckle }); } ``` diff --git a/src/SimpleEndPoints/Conventions/EndpointRoutingConvention.cs b/src/SimpleEndPoints/Conventions/EndpointRoutingConvention.cs index 33a7c41..860c1ca 100644 --- a/src/SimpleEndPoints/Conventions/EndpointRoutingConvention.cs +++ b/src/SimpleEndPoints/Conventions/EndpointRoutingConvention.cs @@ -6,7 +6,7 @@ public class EndpointRoutingConvention: IApplicationModelConvention { private const string EndpointString = "endpoint"; - public void Apply(ApplicationModel application) + public virtual void Apply(ApplicationModel application) { foreach (var controller in application.Controllers) { diff --git a/src/SimpleEndPoints/Extensions/MvcOptionsExtensions.cs b/src/SimpleEndPoints/Extensions/MvcOptionsExtensions.cs index 51107b1..8be5437 100644 --- a/src/SimpleEndPoints/Extensions/MvcOptionsExtensions.cs +++ b/src/SimpleEndPoints/Extensions/MvcOptionsExtensions.cs @@ -8,7 +8,7 @@ namespace SimpleEndpoints.Extensions { public static class MvcOptionsExtensions { - public static void AddEndpointRoutingConvention(this MvcOptions options) + public static void AddSimpleEndpointsRouting(this MvcOptions options) { options.Conventions.Add(new EndpointRoutingConvention()); } diff --git a/src/SimpleEndPoints/SimpleEndpoints.csproj b/src/SimpleEndPoints/SimpleEndpoints.csproj index 70ba725..2e1e60c 100644 --- a/src/SimpleEndPoints/SimpleEndpoints.csproj +++ b/src/SimpleEndPoints/SimpleEndpoints.csproj @@ -10,7 +10,7 @@ https://github.com/dasiths/SimpleEndpoints Git aspnetcore api controller endpoints - 1.1.0 + 1.2.0 diff --git a/src/SimpleEndpoints.Example/Endpoints/WeatherForecast/WeatherForecastEndpoint.cs b/src/SimpleEndpoints.Example/Endpoints/WeatherForecast/WeatherForecastEndpoint.cs index e482ce7..cbd7da6 100644 --- a/src/SimpleEndpoints.Example/Endpoints/WeatherForecast/WeatherForecastEndpoint.cs +++ b/src/SimpleEndpoints.Example/Endpoints/WeatherForecast/WeatherForecastEndpoint.cs @@ -8,28 +8,22 @@ namespace SimpleEndpoints.Example.Endpoints.WeatherForecast { - public class WeatherForecastEndpoint : AsyncGetEndpoint> + public class WeatherForecastEndpoint : AsyncGetEndpoint> { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; - [NonAction] - public override async Task>> HandleAsync(int someParams, CancellationToken cancellationToken = default) + public override async Task>> HandleAsync(CancellationToken cancellationToken = default) { var rng = new Random(); - return await Task.FromResult(Enumerable.Range(1, someParams).Select(index => new WeatherForecast + return await Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }).ToList()); } - - public async Task>> Get(int count, CancellationToken cancellationToken) - { - return await this.HandleAsync(count + 5, cancellationToken); - } } } diff --git a/src/SimpleEndpoints.Example/SimpleEndpoints.Example.csproj b/src/SimpleEndpoints.Example/SimpleEndpoints.Example.csproj index b1c2b3e..755d8bc 100644 --- a/src/SimpleEndpoints.Example/SimpleEndpoints.Example.csproj +++ b/src/SimpleEndpoints.Example/SimpleEndpoints.Example.csproj @@ -5,6 +5,11 @@ + + + + + diff --git a/src/SimpleEndpoints.Example/Startup.cs b/src/SimpleEndpoints.Example/Startup.cs index c5029d0..4d82788 100644 --- a/src/SimpleEndpoints.Example/Startup.cs +++ b/src/SimpleEndpoints.Example/Startup.cs @@ -4,9 +4,11 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; using SimpleEndpoints.Conventions; using SimpleEndpoints.Core; using SimpleEndpoints.Extensions; +using SimpleEndpoints.Swashbuckle; namespace SimpleEndpoints.Example { @@ -24,7 +26,14 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers((options) => { - options.AddEndpointRoutingConvention(); // This is required to translate endpoint names + options.AddSimpleEndpointsRouting(); // This is required to translate endpoint names + options.AddSwashbuckleCompatibilityForSimpleEndpoints(); // this is required to support Swashbuckle + }); + + // Register the Swagger generator, defining 1 or more Swagger documents + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); }); } @@ -38,6 +47,16 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseHttpsRedirection(); + // Enable middleware to serve generated Swagger as a JSON endpoint. + app.UseSwagger(); + + // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), + // specifying the Swagger JSON endpoint. + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + }); + app.UseRouting(); app.UseAuthorization(); diff --git a/src/SimpleEndpoints.Swashbuckle/MvcOptionsExtensions.cs b/src/SimpleEndpoints.Swashbuckle/MvcOptionsExtensions.cs new file mode 100644 index 0000000..b9de5cf --- /dev/null +++ b/src/SimpleEndpoints.Swashbuckle/MvcOptionsExtensions.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; + +namespace SimpleEndpoints.Swashbuckle +{ + public static class MvcOptionsExtensions + { + public static void AddSwashbuckleCompatibilityForSimpleEndpoints(this MvcOptions options) + { + options.Conventions.Add(new SwashbuckleCompatibilityConvention()); + } + } +} diff --git a/src/SimpleEndpoints.Swashbuckle/SimpleEndpoints.Swashbuckle.csproj b/src/SimpleEndpoints.Swashbuckle/SimpleEndpoints.Swashbuckle.csproj new file mode 100644 index 0000000..1e2d3bb --- /dev/null +++ b/src/SimpleEndpoints.Swashbuckle/SimpleEndpoints.Swashbuckle.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + true + Swashbuckle support for SimpleEndpoints. +A simple, convention-based, endpoint per action pattern implementation for AspNetCore 3.0+ + 2020 Dasith Wijesiriwardena + https://github.com/dasiths/SimpleEndpoints + https://github.com/dasiths/SimpleEndpoints + Git + aspnetcore api controller endpoints swashbuckle + 1.2.0 + + + + + + + diff --git a/src/SimpleEndpoints.Swashbuckle/SwashbuckleCompatibilityConvention.cs b/src/SimpleEndpoints.Swashbuckle/SwashbuckleCompatibilityConvention.cs new file mode 100644 index 0000000..2abd57c --- /dev/null +++ b/src/SimpleEndpoints.Swashbuckle/SwashbuckleCompatibilityConvention.cs @@ -0,0 +1,29 @@ +using System.Linq; +using Microsoft.AspNetCore.Mvc.ActionConstraints; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.Routing; + +namespace SimpleEndpoints.Swashbuckle +{ + public class SwashbuckleCompatibilityConvention : IApplicationModelConvention + { + public void Apply(ApplicationModel application) + { + foreach (var action in application.Controllers.SelectMany(c => c.Actions)) + { + if (action.Selectors.Any() && + action.Selectors[0].ActionConstraints.Count == 0 && + action.ApiExplorer.IsVisible.GetValueOrDefault(false)) + { + var attribute = action.Attributes.FirstOrDefault(a => a is HttpMethodAttribute); + + if (attribute is HttpMethodAttribute item) + { + var o = new HttpMethodActionConstraint(item.HttpMethods); + action.Selectors[0].ActionConstraints.Add(o); + } + } + } + } + } +} diff --git a/src/SimpleEndpoints.sln b/src/SimpleEndpoints.sln index f87d481..a720f39 100644 --- a/src/SimpleEndpoints.sln +++ b/src/SimpleEndpoints.sln @@ -7,7 +7,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleEndpoints.Example", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleEndpoints", "SimpleEndPoints\SimpleEndpoints.csproj", "{06D8DE86-AC08-452C-882D-6F57CFF4835E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleEndpoints.Tests", "SimpleEndpoints.Tests\SimpleEndpoints.Tests.csproj", "{60D80A29-1FFD-495A-9CF0-9285920F0356}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleEndpoints.Tests", "SimpleEndpoints.Tests\SimpleEndpoints.Tests.csproj", "{60D80A29-1FFD-495A-9CF0-9285920F0356}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleEndpoints.Swashbuckle", "SimpleEndpoints.Swashbuckle\SimpleEndpoints.Swashbuckle.csproj", "{2AE58DD8-87F7-4136-8F61-9ECFB9A3B728}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +29,10 @@ Global {60D80A29-1FFD-495A-9CF0-9285920F0356}.Debug|Any CPU.Build.0 = Debug|Any CPU {60D80A29-1FFD-495A-9CF0-9285920F0356}.Release|Any CPU.ActiveCfg = Release|Any CPU {60D80A29-1FFD-495A-9CF0-9285920F0356}.Release|Any CPU.Build.0 = Release|Any CPU + {2AE58DD8-87F7-4136-8F61-9ECFB9A3B728}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2AE58DD8-87F7-4136-8F61-9ECFB9A3B728}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AE58DD8-87F7-4136-8F61-9ECFB9A3B728}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2AE58DD8-87F7-4136-8F61-9ECFB9A3B728}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE