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 7 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
8 changes: 8 additions & 0 deletions Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ function CreateNuGetPackage {
CheckLastExitCode
}

function LoadBaseBranchIfNotMaster(){
if ($env:APPVEYOR_REPO_BRANCH -ne "master") {
git fetch origin ${env:APPVEYOR_REPO_BRANCH}:${env:APPVEYOR_REPO_BRANCH}
}
}

LoadBaseBranchIfNotMaster

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
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>
<PackageReference Include="Swashbuckle.AspNetCore" Version="$(SwashbuckleVersion)" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="$(SwashbuckleVersion)" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="$(SwashbuckleVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" />
</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)
{
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, int> 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)
{
}
}
}
26 changes: 26 additions & 0 deletions test/OpenApiTests/OpenApiDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;

// @formatter:wrap_chained_method_calls chop_always
// @formatter:keep_existing_linebreaks true

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)
{
}

protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Airplane>()
.HasMany(airplane => airplane.Flights);
}
}
}
59 changes: 59 additions & 0 deletions test/OpenApiTests/OpenApiDocumentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using FluentAssertions;
using TestBuildingBlocks;
using Xunit;

namespace OpenApiTests
{
public sealed class OpenApiDocumentTests : IntegrationTestContext<OpenApiStartup<OpenApiDbContext>, OpenApiDbContext>
{
public OpenApiDocumentTests()
{
UseController<AirplanesController>();
UseController<FlightsController>();
}

[Fact]
public async Task Retrieved_document_should_match_expected_document()
{
// Arrange
string embeddedResourceName = $"{nameof(OpenApiTests)}.openapi.json";
string expectedDocument = await LoadEmbeddedResourceAsync(embeddedResourceName);
string requestUrl = $"swagger/{nameof(OpenApiTests)}/swagger.json";

// Act
string actualDocument = await GetAsync(requestUrl);

// Assert
actualDocument.Should().BeJson(expectedDocument);
}

private async Task<string> GetAsync(string requestUrl)
{
var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);

using HttpClient client = Factory.CreateClient();
HttpResponseMessage responseMessage = await client.SendAsync(request);

return await responseMessage.Content.ReadAsStringAsync();
}

private static async Task<string> LoadEmbeddedResourceAsync(string name)
{
var assembly = Assembly.GetExecutingAssembly();
await using Stream stream = assembly.GetManifestResourceStream(name);

if (stream == null)
{
throw new Exception($"Failed to load embedded resource '{name}'. Set Build Action to Embedded Resource in properties.");
}

using var reader = new StreamReader(stream);
return await reader.ReadToEndAsync();
}
}
}
40 changes: 40 additions & 0 deletions test/OpenApiTests/OpenApiStartup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.OpenApi;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using TestBuildingBlocks;

namespace OpenApiTests
{
public sealed class OpenApiStartup<TDbContext> : TestableStartup<TDbContext>
where TDbContext : DbContext
{
public override void ConfigureServices(IServiceCollection services)
{
IMvcCoreBuilder mvcCoreBuilder = services.AddMvcCore();

services.AddJsonApi<TDbContext>(SetJsonApiOptions, mvcBuilder: mvcCoreBuilder);

services.AddOpenApi(mvcCoreBuilder, options => options.SwaggerDoc(nameof(OpenApiTests), new OpenApiInfo
{
Title = nameof(OpenApiTests),
Version = "1"
}));
}

public override void Configure(IApplicationBuilder app, IWebHostEnvironment environment, ILoggerFactory loggerFactory)
{
app.UseRouting();

app.UseJsonApi();

app.UseSwagger();

app.UseEndpoints(endpoints => endpoints.MapControllers());
}
}
}
26 changes: 26 additions & 0 deletions test/OpenApiTests/OpenApiTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

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

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="$(CoverletVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="openapi.json" />
</ItemGroup>
</Project>
Loading