Skip to content

Minimal Swashbuckle setup #1073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ function CreateNuGetPackage {
CheckLastExitCode
}

# In a PR the base branch needs to be fetched in order for regitlint to work.
function FetchBaseBranchIfNotMaster(){
if ($env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -ne "master"){
git fetch -q origin ${env:APPVEYOR_REPO_BRANCH}:${env:APPVEYOR_REPO_BRANCH}
}
}

FetchBaseBranchIfNotMaster

dotnet tool restore
CheckLastExitCode

Expand Down
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<AspNetCoreVersion>5.0.*</AspNetCoreVersion>
<EFCoreVersion>5.0.*</EFCoreVersion>
<NpgsqlPostgreSQLVersion>5.0.*</NpgsqlPostgreSQLVersion>
<SwashbuckleVersion>6.1.*</SwashbuckleVersion>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodingGuidelines.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>

Expand Down
30 changes: 30 additions & 0 deletions JsonApiDotNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextTests", "test
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBuildingBlocks", "test\TestBuildingBlocks\TestBuildingBlocks.csproj", "{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore.OpenApi", "src\JsonApiDotNetCore.OpenApi\JsonApiDotNetCore.OpenApi.csproj", "{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenApiTests", "test\OpenApiTests\OpenApiTests.csproj", "{B693DE14-BB28-496F-AB39-B4E674ABCA80}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -210,6 +214,30 @@ Global
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x64.Build.0 = Release|Any CPU
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x86.ActiveCfg = Release|Any CPU
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x86.Build.0 = Release|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x64.ActiveCfg = Debug|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x64.Build.0 = Debug|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x86.ActiveCfg = Debug|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x86.Build.0 = Debug|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|Any CPU.Build.0 = Release|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x64.ActiveCfg = Release|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x64.Build.0 = Release|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x86.ActiveCfg = Release|Any CPU
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x86.Build.0 = Release|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x64.ActiveCfg = Debug|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x64.Build.0 = Debug|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x86.ActiveCfg = Debug|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x86.Build.0 = Debug|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|Any CPU.Build.0 = Release|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x64.ActiveCfg = Release|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x64.Build.0 = Release|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x86.ActiveCfg = Release|Any CPU
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -228,6 +256,8 @@ Global
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
{B693DE14-BB28-496F-AB39-B4E674ABCA80} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4}
Expand Down
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ environment:
branches:
only:
- master
- openapi
- develop
- unstable
- /release\/.+/
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ Install-Package JsonApiDotnetCore
```xml
<ItemGroup>
<!-- Be sure to check NuGet for the latest version # -->
<PackageReference Include="JsonApiDotNetCore" Version="4.0.0" />
<PackageReference Include="JsonApiDotnetCore" Version="4.0.0" />
</ItemGroup>
```
72 changes: 72 additions & 0 deletions docs/usage/openapi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# OpenAPI

You can describe your API with an OpenAPI specification using the [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) integration for JsonApiDotNetCore.

## Installation

Install the `JsonApiDotnetCore.OpenApi` NuGet package.

### CLI

```
dotnet add package JsonApiDotnetCore.OpenApi
```

### Visual Studio

```powershell
Install-Package JsonApiDotnetCore.OpenApi
```

### *.csproj

```xml
<ItemGroup>
<!-- Be sure to check NuGet for the latest version # -->
<PackageReference Include="JsonApiDotnetCore.OpenApi" Version="4.0.0" />
</ItemGroup>
```

## Usage

Add the integration in your `Startup` class.

```c#
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
IMvcCoreBuilder builder = services.AddMvcCore();
services.AddJsonApi<AppDbContext>(mvcBuilder: builder);

// Adds the Swashbuckle integration.
services.AddOpenApi(builder);
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseJsonApi();

// Adds the Swashbuckle middleware.
app.UseSwagger();

app.UseEndpoints(endpoints => endpoints.MapControllers());
}
}
```

By default, the OpenAPI specification will be available at `http://localhost:<port>/swagger/v1/swagger.json`.

Swashbuckle also ships with [SwaggerUI](https://swagger.io/tools/swagger-ui/), tooling for a generated documentation page. This can be enabled by adding the following to your `Startup` class.

```c#
// Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwaggerUI();
}
```

By default, SwaggerUI will be available at `http://localhost:<port>/swagger`.

2 changes: 2 additions & 0 deletions docs/usage/toc.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@
## [Resource Repositories](extensibility/repositories.md)
## [Middleware](extensibility/middleware.md)
## [Query Strings](extensibility/query-strings.md)

# [OpenAPI](openapi.md)
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\JsonApiDotNetCore.OpenApi\JsonApiDotNetCore.OpenApi.csproj" />
<ProjectReference Include="..\..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="$(EFCoreVersion)" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(NpgsqlPostgreSQLVersion)" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="$(SwashbuckleVersion)" />
</ItemGroup>
</Project>
1 change: 0 additions & 1 deletion src/Examples/JsonApiDotNetCoreExample/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using JsonApiDotNetCoreExample.Startups;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Diagnostics;
using JsonApiDotNetCore.OpenApi;
using JsonApiDotNetCoreExample.Data;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
Expand All @@ -12,7 +13,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace JsonApiDotNetCoreExample.Startups
namespace JsonApiDotNetCoreExample
{
public sealed class Startup
{
Expand Down Expand Up @@ -44,6 +45,10 @@ public void ConfigureServices(IServiceCollection services)
#endif
});

IMvcCoreBuilder mvcCoreBuilder = services.AddMvcCore();

services.AddOpenApi(mvcCoreBuilder);

using (CodeTimingSessionManager.Current.Measure("Configure JSON:API (startup)"))
{
services.AddJsonApi<AppDbContext>(options =>
Expand All @@ -57,7 +62,7 @@ public void ConfigureServices(IServiceCollection services)
#if DEBUG
options.IncludeExceptionStackTraceInErrors = true;
#endif
}, discovery => discovery.AddCurrentAssembly());
}, discovery => discovery.AddCurrentAssembly(), mvcBuilder: mvcCoreBuilder);
}
}
}
Expand All @@ -77,6 +82,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment environment,

app.UseRouting();

app.UseSwagger();
app.UseSwaggerUI();

using (CodeTimingSessionManager.Current.Measure("Initialize JSON:API (startup)"))
{
app.UseJsonApi();
Expand Down
15 changes: 15 additions & 0 deletions src/JsonApiDotNetCore.OpenApi/JsonApiDotNetCore.OpenApi.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="$(SwashbuckleVersion)" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="$(SwashbuckleVersion)" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="$(SwashbuckleVersion)" />
</ItemGroup>
</Project>
19 changes: 19 additions & 0 deletions src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Routing;

namespace JsonApiDotNetCore.OpenApi
{
internal sealed class OpenApiEndpointConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
ArgumentGuard.NotNull(action, nameof(action));

if (!action.ActionMethod.GetCustomAttributes(true).OfType<HttpMethodAttribute>().Any())
{
action.ApiExplorer.IsVisible = false;
}
}
}
}
21 changes: 21 additions & 0 deletions src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace JsonApiDotNetCore.OpenApi
{
public static class ServiceCollectionExtensions
{
public static void AddOpenApi(this IServiceCollection services, IMvcCoreBuilder builder, Action<SwaggerGenOptions> setupSwaggerGenAction = null)
{
ArgumentGuard.NotNull(services, nameof(services));
ArgumentGuard.NotNull(builder, nameof(builder));

builder.AddApiExplorer();

builder.AddMvcOptions(options => options.Conventions.Add(new OpenApiEndpointConvention()));

services.AddSwaggerGen(setupSwaggerGenAction);
}
}
}
1 change: 1 addition & 0 deletions src/JsonApiDotNetCore/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("JsonApiDotNetCore.OpenApi")]
[assembly: InternalsVisibleTo("Benchmarks")]
[assembly: InternalsVisibleTo("JsonApiDotNetCoreTests")]
[assembly: InternalsVisibleTo("UnitTests")]
Expand Down
21 changes: 21 additions & 0 deletions test/OpenApiTests/Airplane.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;

namespace OpenApiTests
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public sealed class Airplane : Identifiable
{
[Attr]
public int SeatingCapacity { get; set; }

[Attr]
public DateTimeOffset ManufacturedAt { get; set; }

[HasMany]
public ISet<Flight> Flights { get; set; }
}
}
15 changes: 15 additions & 0 deletions test/OpenApiTests/AirplanesController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
using Microsoft.Extensions.Logging;

namespace OpenApiTests
{
public sealed class AirplanesController : JsonApiController<Airplane>
{
public AirplanesController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<Airplane> resourceService)
: base(options, loggerFactory, resourceService)
{
}
}
}
17 changes: 17 additions & 0 deletions test/OpenApiTests/Flight.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using JetBrains.Annotations;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;

namespace OpenApiTests
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public sealed class Flight : Identifiable
{
[Attr]
public string Destination { get; set; }

[Attr]
public DateTimeOffset DepartsAt { get; set; }
}
}
15 changes: 15 additions & 0 deletions test/OpenApiTests/FlightsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
using Microsoft.Extensions.Logging;

namespace OpenApiTests
{
public sealed class FlightsController : JsonApiController<Flight>
{
public FlightsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<Flight> resourceService)
: base(options, loggerFactory, resourceService)
{
}
}
}
17 changes: 17 additions & 0 deletions test/OpenApiTests/OpenApiDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;

namespace OpenApiTests
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public sealed class OpenApiDbContext : DbContext
{
public DbSet<Airplane> Airplanes { get; set; }
public DbSet<Flight> Flights { get; set; }

public OpenApiDbContext(DbContextOptions<OpenApiDbContext> options)
: base(options)
{
}
}
}
Loading