Skip to content

Commit

Permalink
Use versioned api explorer when available (#1622)
Browse files Browse the repository at this point in the history
* Added missing nuget and removed the version number

* Use versioned api explorer when available

* Fix whitespace
  • Loading branch information
RicoSuter committed Sep 26, 2018
1 parent 82b2a5a commit 26e0c58
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="2.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
8 changes: 8 additions & 0 deletions src/NSwag.SwaggerGeneration.AspNetCore.Tests.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Mvc.Versioning;

namespace NSwag.SwaggerGeneration.AspNetCore.Tests.Web
{
Expand All @@ -21,6 +22,13 @@ public void ConfigureServices(IServiceCollection services)
services.AddApiVersioning(options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
})
.AddMvcCore()
.AddVersionedApiExplorer(o =>
{
o.GroupNameFormat = "VVV";
o.ApiVersionParameterSource = new HeaderApiVersionReader("api-version");
o.AssumeDefaultVersionWhenUnspecified = false;
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ namespace NSwag.SwaggerGeneration.AspNetCore.Tests
{
public class ApiVersionProcessorWithAspNetCoreTests : AspNetCoreTestsBase
{
[Fact]
public async Task When_api_version_parameter_should_be_ignored_then_it_is_ignored()
{
// Arrange
var settings = new AspNetCoreToSwaggerGeneratorSettings();
settings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "1" };

// Act
var document = await GenerateDocumentAsync(settings, typeof(VersionedValuesController), typeof(VersionedV3ValuesController));
var json = document.ToJson();

// Assert
var operations = document.Operations;
Assert.True(operations.All(o => o.Operation.ActualParameters.All(p => p.Name != "api-version")));
}

[Fact]
public async Task When_generating_v1_then_only_v1_operations_are_included()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ public async Task<bool> ProcessAsync(OperationProcessorContext operationProcesso

foreach (var apiParameter in parameters.Where(p => p.Source != null))
{
// TODO: Provide extension point so that this can be implemented in the ApiVersionProcessor class
var versionProcessor = _settings.OperationProcessors.TryGet<ApiVersionProcessor>();
if (versionProcessor != null &&
versionProcessor.IgnoreParameter &&
apiParameter.ModelMetadata?.DataTypeName == "ApiVersion")
{
continue;
}

var parameterDescriptor = apiParameter.TryGetPropertyValue<ParameterDescriptor>("ParameterDescriptor");
var parameterName = parameterDescriptor?.Name ?? apiParameter.Name;

Expand Down
72 changes: 55 additions & 17 deletions src/NSwag.SwaggerGeneration/Processors/ApiVersionProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//-----------------------------------------------------------------------

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
Expand All @@ -21,38 +22,75 @@ public class ApiVersionProcessor : IOperationProcessor
/// <summary>Gets or sets the list of versions which should be included in the generated document (leave empty to include all versions).</summary>
public string[] IncludedVersions { get; set; }

/// <summary>Gets or sets a value indicating whether to ignore the global API version parameter (ASP.NET Core only, default: true).</summary>
public bool IgnoreParameter { get; set; } = true;

/// <summary>Processes the specified method information.</summary>
/// <param name="context">The processor context.</param>
/// <returns>true if the operation should be added to the Swagger specification.</returns>
public Task<bool> ProcessAsync(OperationProcessorContext context)
{
var versions = GetVersions(context, "ApiVersionAttribute");
if (versions.Any())
if (UseVersionedApiExplorer(context))
{
if (versions.Any(v => IncludedVersions == null || IncludedVersions.Length == 0 || IncludedVersions.Contains(v)))
{
var mappedVersions = GetVersions(context, "MapToApiVersionAttribute");
var dynamicContext = (dynamic)context;
var version = (string)dynamicContext.ApiDescription.GroupName;

var version = mappedVersions.FirstOrDefault(v => IncludedVersions == null || IncludedVersions.Length == 0 || IncludedVersions.Contains(v));
if (version == null && mappedVersions.Length == 0)
version = IncludedVersions != null && IncludedVersions.Any() ? IncludedVersions[0] : versions[0];
var isIncluded = IncludedVersions == null || IncludedVersions.Contains(version);
if (isIncluded)
{
RemoveApiVersionPathParameter(context, version);
}

if (version != null)
return Task.FromResult(isIncluded);
}
else
{
var versions = GetVersions(context, "ApiVersionAttribute");
if (versions.Any())
{
if (versions.Any(v => IncludedVersions == null || IncludedVersions.Length == 0 || IncludedVersions.Contains(v)))
{
var operationDescription = context.OperationDescription;
operationDescription.Path = operationDescription.Path.Replace("{version:apiVersion}", version);
operationDescription.Path = operationDescription.Path.Replace("{version}", version);
var mappedVersions = GetVersions(context, "MapToApiVersionAttribute");

return Task.FromResult(true);
var version = mappedVersions.FirstOrDefault(v => IncludedVersions == null || IncludedVersions.Length == 0 || IncludedVersions.Contains(v));
if (version == null && mappedVersions.Length == 0)
version = IncludedVersions != null && IncludedVersions.Any() ? IncludedVersions[0] : versions[0];

if (version != null)
{
RemoveApiVersionPathParameter(context, version);
return Task.FromResult(true);
}
else
return Task.FromResult(false);
}
else
return Task.FromResult(false);

return Task.FromResult(false);
}

return Task.FromResult(false);
return Task.FromResult(true);
}
}

return Task.FromResult(true);
private bool UseVersionedApiExplorer(OperationProcessorContext context)
{
if (context.HasProperty("ApiDescription"))
{
var dynamicContext = (dynamic)context;
var usesVersionedApiExplorer = ((IDictionary<object, object>)dynamicContext.ApiDescription.Properties)
.Any(tuple => ((dynamic)tuple.Key).FullName == "Microsoft.AspNetCore.Mvc.ApiVersion");

return usesVersionedApiExplorer;
}

return false;
}

private void RemoveApiVersionPathParameter(OperationProcessorContext context, string version)
{
var operationDescription = context.OperationDescription;
operationDescription.Path = operationDescription.Path.Replace("{version:apiVersion}", version);
operationDescription.Path = operationDescription.Path.Replace("{version}", version);
}

private string[] GetVersions(OperationProcessorContext context, string attributeType)
Expand Down

0 comments on commit 26e0c58

Please sign in to comment.