Skip to content

Commit d226a0e

Browse files
maureiBart Koelman
and
Bart Koelman
authored
Minimal Swashbuckle setup (#1073)
* Add minimal Swashbuckle setup * add tests, run ci build for openapi, use build props for openapi projects * review feedback * CI clone_script fix * review feedback * fix CI build * Reformat project files (using VS, doesn't appear to work in Rider) * Fixed schema error in VS * reorder csproj * review feedback * add SwaggerUI to example project * rm redundant bit from dbcontext * add documentation * typo * remove redundant formatter instructions * review feedback * review feedback * bump version * updated swagger.json to pass test * add nuget specification in project file, add sourcelink * adjust buildscript * Add comment to AddOpenApi, introduced authors tag and build property in project files * Reformatted project file Co-authored-by: Bart Koelman <bart@degreed.com>
1 parent a3890fe commit d226a0e

23 files changed

+1375
-3
lines changed

Build.ps1

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,25 @@ function CreateNuGetPackage {
8585

8686
if ([string]::IsNullOrWhitespace($versionSuffix)) {
8787
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts
88+
dotnet pack .\src\JsonApiDotNetCore.OpenApi -c Release -o .\artifacts
8889
}
8990
else {
9091
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=$versionSuffix
92+
dotnet pack .\src\JsonApiDotNetCore.OpenApi -c Release -o .\artifacts --version-suffix=$versionSuffix
9193
}
9294

9395
CheckLastExitCode
9496
}
9597

98+
# In a PR the base branch needs to be fetched in order for regitlint to work.
99+
function FetchBaseBranchIfNotMaster(){
100+
if ($env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -ne "master"){
101+
git fetch -q origin ${env:APPVEYOR_REPO_BRANCH}:${env:APPVEYOR_REPO_BRANCH}
102+
}
103+
}
104+
105+
FetchBaseBranchIfNotMaster
106+
96107
dotnet tool restore
97108
CheckLastExitCode
98109

Directory.Build.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
<AspNetCoreVersion>5.0.*</AspNetCoreVersion>
55
<EFCoreVersion>5.0.*</EFCoreVersion>
66
<NpgsqlPostgreSQLVersion>5.0.*</NpgsqlPostgreSQLVersion>
7+
<SwashbuckleVersion>6.2.*</SwashbuckleVersion>
8+
<JsonApiDotNetCoreVersionPrefix>4.2.0</JsonApiDotNetCoreVersionPrefix>
79
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodingGuidelines.ruleset</CodeAnalysisRuleSet>
810
</PropertyGroup>
911

JsonApiDotNetCore.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextTests", "test
4444
EndProject
4545
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBuildingBlocks", "test\TestBuildingBlocks\TestBuildingBlocks.csproj", "{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}"
4646
EndProject
47+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore.OpenApi", "src\JsonApiDotNetCore.OpenApi\JsonApiDotNetCore.OpenApi.csproj", "{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}"
48+
EndProject
49+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenApiTests", "test\OpenApiTests\OpenApiTests.csproj", "{B693DE14-BB28-496F-AB39-B4E674ABCA80}"
50+
EndProject
4751
Global
4852
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4953
Debug|Any CPU = Debug|Any CPU
@@ -210,6 +214,30 @@ Global
210214
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x64.Build.0 = Release|Any CPU
211215
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x86.ActiveCfg = Release|Any CPU
212216
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x86.Build.0 = Release|Any CPU
217+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
218+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|Any CPU.Build.0 = Debug|Any CPU
219+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x64.ActiveCfg = Debug|Any CPU
220+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x64.Build.0 = Debug|Any CPU
221+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x86.ActiveCfg = Debug|Any CPU
222+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x86.Build.0 = Debug|Any CPU
223+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|Any CPU.ActiveCfg = Release|Any CPU
224+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|Any CPU.Build.0 = Release|Any CPU
225+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x64.ActiveCfg = Release|Any CPU
226+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x64.Build.0 = Release|Any CPU
227+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x86.ActiveCfg = Release|Any CPU
228+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x86.Build.0 = Release|Any CPU
229+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
230+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|Any CPU.Build.0 = Debug|Any CPU
231+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x64.ActiveCfg = Debug|Any CPU
232+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x64.Build.0 = Debug|Any CPU
233+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x86.ActiveCfg = Debug|Any CPU
234+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x86.Build.0 = Debug|Any CPU
235+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|Any CPU.ActiveCfg = Release|Any CPU
236+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|Any CPU.Build.0 = Release|Any CPU
237+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x64.ActiveCfg = Release|Any CPU
238+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x64.Build.0 = Release|Any CPU
239+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x86.ActiveCfg = Release|Any CPU
240+
{B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x86.Build.0 = Release|Any CPU
213241
EndGlobalSection
214242
GlobalSection(SolutionProperties) = preSolution
215243
HideSolutionNode = FALSE
@@ -228,6 +256,8 @@ Global
228256
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
229257
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
230258
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
259+
{71287D6F-6C3B-44B4-9FCA-E78FE3F02289} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
260+
{B693DE14-BB28-496F-AB39-B4E674ABCA80} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
231261
EndGlobalSection
232262
GlobalSection(ExtensibilityGlobals) = postSolution
233263
SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4}

appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ environment:
1515
branches:
1616
only:
1717
- master
18+
- openapi
1819
- develop
1920
- unstable
2021
- /release\/.+/

docs/usage/extensibility/middleware.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class Startup
2727
{
2828
services.AddSingleton<CustomAsyncQueryStringActionFilter>();
2929

30-
IMvcCoreBuilder builder = services.AddMvcCore();
30+
IMvcCoreBuilder mvcBuilder = services.AddMvcCore();
3131
services.AddJsonApi<AppDbContext>(mvcBuilder: builder);
3232

3333
// Ensure this call is placed after the AddJsonApi call.

docs/usage/openapi.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# OpenAPI
2+
3+
You can describe your API with an OpenAPI specification using the [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) integration for JsonApiDotNetCore.
4+
5+
## Installation
6+
7+
Install the `JsonApiDotNetCore.OpenApi` NuGet package.
8+
9+
### CLI
10+
11+
```
12+
dotnet add package JsonApiDotNetCore.OpenApi
13+
```
14+
15+
### Visual Studio
16+
17+
```powershell
18+
Install-Package JsonApiDotNetCore.OpenApi
19+
```
20+
21+
### *.csproj
22+
23+
```xml
24+
<ItemGroup>
25+
<!-- Be sure to check NuGet for the latest version # -->
26+
<PackageReference Include="JsonApiDotNetCore.OpenApi" Version="4.0.0" />
27+
</ItemGroup>
28+
```
29+
30+
## Usage
31+
32+
Add the integration in your `Startup` class.
33+
34+
```c#
35+
public class Startup
36+
{
37+
public void ConfigureServices(IServiceCollection services)
38+
{
39+
IMvcCoreBuilder mvcBuilder = services.AddMvcCore();
40+
services.AddJsonApi<AppDbContext>(mvcBuilder: mvcBuilder);
41+
42+
// Adds the Swashbuckle integration.
43+
services.AddOpenApi(mvcBuilder);
44+
}
45+
46+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
47+
{
48+
app.UseRouting();
49+
app.UseJsonApi();
50+
51+
// Adds the Swashbuckle middleware.
52+
app.UseSwagger();
53+
54+
app.UseEndpoints(endpoints => endpoints.MapControllers());
55+
}
56+
}
57+
```
58+
59+
By default, the OpenAPI specification will be available at `http://localhost:<port>/swagger/v1/swagger.json`.
60+
61+
Swashbuckle also ships with [SwaggerUI](https://swagger.io/tools/swagger-ui/), tooling for a generated documentation page. This can be enabled by installing the `Swashbuckle.AspNetCore.SwaggerUI` NuGet package and adding the following to your `Startup` class.
62+
63+
```c#
64+
// Startup.cs
65+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
66+
{
67+
app.UseSwaggerUI();
68+
}
69+
```
70+
71+
By default, SwaggerUI will be available at `http://localhost:<port>/swagger`.
72+

docs/usage/toc.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
# [Errors](errors.md)
2222
# [Metadata](meta.md)
2323
# [Caching](caching.md)
24+
# [OpenAPI](openapi.md)
2425

2526
# Extensibility
2627
## [Layer Overview](extensibility/layer-overview.md)
@@ -30,3 +31,4 @@
3031
## [Resource Repositories](extensibility/repositories.md)
3132
## [Middleware](extensibility/middleware.md)
3233
## [Query Strings](extensibility/query-strings.md)
34+

src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55

66
<ItemGroup>
77
<ProjectReference Include="..\..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" />
8+
<ProjectReference Include="..\..\JsonApiDotNetCore.OpenApi\JsonApiDotNetCore.OpenApi.csproj" />
89
</ItemGroup>
910

1011
<ItemGroup>
1112
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="$(EFCoreVersion)" />
1213
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(NpgsqlPostgreSQLVersion)" />
14+
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="$(SwashbuckleVersion)" />
1315
</ItemGroup>
1416
</Project>

src/Examples/JsonApiDotNetCoreExample/Startup.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using JsonApiDotNetCore.Configuration;
33
using JsonApiDotNetCore.Diagnostics;
4+
using JsonApiDotNetCore.OpenApi;
45
using JsonApiDotNetCoreExample.Data;
56
using Microsoft.AspNetCore.Authentication;
67
using Microsoft.AspNetCore.Builder;
@@ -44,6 +45,8 @@ public void ConfigureServices(IServiceCollection services)
4445
#endif
4546
});
4647

48+
IMvcCoreBuilder mvcBuilder = services.AddMvcCore();
49+
4750
using (CodeTimingSessionManager.Current.Measure("Configure JSON:API (startup)"))
4851
{
4952
services.AddJsonApi<AppDbContext>(options =>
@@ -57,8 +60,10 @@ public void ConfigureServices(IServiceCollection services)
5760
#if DEBUG
5861
options.IncludeExceptionStackTraceInErrors = true;
5962
#endif
60-
}, discovery => discovery.AddCurrentAssembly());
63+
}, discovery => discovery.AddCurrentAssembly(), mvcBuilder: mvcBuilder);
6164
}
65+
66+
services.AddOpenApi(mvcBuilder);
6267
}
6368
}
6469

@@ -82,6 +87,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment environment,
8287
app.UseJsonApi();
8388
}
8489

90+
app.UseSwagger();
91+
app.UseSwaggerUI();
92+
8593
app.UseEndpoints(endpoints => endpoints.MapControllers());
8694
}
8795

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<VersionPrefix>$(JsonApiDotNetCoreVersionPrefix)</VersionPrefix>
4+
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
5+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
6+
</PropertyGroup>
7+
8+
<PropertyGroup>
9+
<PackageTags>jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net;openapi;swagger;swaggerui;swashbuckle</PackageTags>
10+
<Description>A Swashbuckle integration that enables you to describe a JsonApiDotNetCore API with an OpenAPI specification.</Description>
11+
<Authors>json-api-dotnet</Authors>
12+
<PackageProjectUrl>https://www.jsonapi.net/</PackageProjectUrl>
13+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
14+
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
15+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
16+
<EmbedUntrackedSources>true</EmbedUntrackedSources>
17+
<DebugType>embedded</DebugType>
18+
</PropertyGroup>
19+
20+
<ItemGroup>
21+
<ProjectReference Include="..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" />
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
26+
<PrivateAssets>all</PrivateAssets>
27+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
28+
</PackageReference>
29+
<PackageReference Include="Swashbuckle.AspNetCore" Version="$(SwashbuckleVersion)" />
30+
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="$(SwashbuckleVersion)" />
31+
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="$(SwashbuckleVersion)" />
32+
</ItemGroup>
33+
</Project>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Linq;
2+
using Microsoft.AspNetCore.Mvc.ApplicationModels;
3+
using Microsoft.AspNetCore.Mvc.Routing;
4+
5+
namespace JsonApiDotNetCore.OpenApi
6+
{
7+
internal sealed class OpenApiEndpointConvention : IActionModelConvention
8+
{
9+
public void Apply(ActionModel action)
10+
{
11+
ArgumentGuard.NotNull(action, nameof(action));
12+
13+
if (!action.ActionMethod.GetCustomAttributes(true).OfType<HttpMethodAttribute>().Any())
14+
{
15+
action.ApiExplorer.IsVisible = false;
16+
}
17+
}
18+
}
19+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Swashbuckle.AspNetCore.SwaggerGen;
4+
5+
namespace JsonApiDotNetCore.OpenApi
6+
{
7+
public static class ServiceCollectionExtensions
8+
{
9+
/// <summary>
10+
/// Adds the OpenAPI integration to JsonApiDotNetCore by configuring Swashbuckle.
11+
/// </summary>
12+
public static void AddOpenApi(this IServiceCollection services, IMvcCoreBuilder mvcBuilder, Action<SwaggerGenOptions> setupSwaggerGenAction = null)
13+
{
14+
ArgumentGuard.NotNull(services, nameof(services));
15+
ArgumentGuard.NotNull(mvcBuilder, nameof(mvcBuilder));
16+
17+
mvcBuilder.AddApiExplorer();
18+
19+
mvcBuilder.AddMvcOptions(options => options.Conventions.Add(new OpenApiEndpointConvention()));
20+
21+
services.AddSwaggerGen(setupSwaggerGenAction);
22+
}
23+
}
24+
}

src/JsonApiDotNetCore/JsonApiDotNetCore.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<VersionPrefix>4.2.0</VersionPrefix>
3+
<VersionPrefix>$(JsonApiDotNetCoreVersionPrefix)</VersionPrefix>
44
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
55
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
66
</PropertyGroup>
77

88
<PropertyGroup>
99
<PackageTags>jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net</PackageTags>
1010
<Description>A framework for building JSON:API compliant REST APIs using .NET Core and Entity Framework Core. Includes support for Atomic Operations. The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection making extensibility incredibly easy.</Description>
11+
<Authors>json-api-dotnet</Authors>
1112
<PackageProjectUrl>https://www.jsonapi.net/</PackageProjectUrl>
1213
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1314
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>

src/JsonApiDotNetCore/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Runtime.CompilerServices;
22

3+
[assembly: InternalsVisibleTo("JsonApiDotNetCore.OpenApi")]
34
[assembly: InternalsVisibleTo("Benchmarks")]
45
[assembly: InternalsVisibleTo("JsonApiDotNetCoreTests")]
56
[assembly: InternalsVisibleTo("UnitTests")]

test/OpenApiTests/Airplane.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using JetBrains.Annotations;
4+
using JsonApiDotNetCore.Resources;
5+
using JsonApiDotNetCore.Resources.Annotations;
6+
7+
namespace OpenApiTests
8+
{
9+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
10+
public sealed class Airplane : Identifiable
11+
{
12+
[Attr]
13+
public int SeatingCapacity { get; set; }
14+
15+
[Attr]
16+
public DateTimeOffset ManufacturedAt { get; set; }
17+
18+
[HasMany]
19+
public ISet<Flight> Flights { get; set; }
20+
}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.Services;
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace OpenApiTests
7+
{
8+
public sealed class AirplanesController : JsonApiController<Airplane>
9+
{
10+
public AirplanesController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<Airplane> resourceService)
11+
: base(options, loggerFactory, resourceService)
12+
{
13+
}
14+
}
15+
}

test/OpenApiTests/Flight.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Resources;
4+
using JsonApiDotNetCore.Resources.Annotations;
5+
6+
namespace OpenApiTests
7+
{
8+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9+
public sealed class Flight : Identifiable
10+
{
11+
[Attr]
12+
public string Destination { get; set; }
13+
14+
[Attr]
15+
public DateTimeOffset DepartsAt { get; set; }
16+
}
17+
}

0 commit comments

Comments
 (0)