Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ By default, Swashbuckle will generate and expose Swagger JSON in version 3.0 of
```csharp
app.UseSwagger(c =>
{
c.SerializeAsV2 = true;
c.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0;
});
```

Expand Down
91 changes: 67 additions & 24 deletions src/Swashbuckle.AspNetCore.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Writers;
using Swashbuckle.AspNetCore.Swagger;

namespace Swashbuckle.AspNetCore.Cli
{
internal class Program
{
private const string OpenApiVersionOption = "--openapiversion";
private const string SerializeAsV2Flag = "--serializeasv2";

public static int Main(string[] args)
{
// Helper to simplify command line parsing etc.
Expand All @@ -26,25 +30,28 @@ public static int Main(string[] args)
// startupassembly and it's transitive dependencies. See https://github.com/dotnet/coreclr/issues/13277 for more.

// > dotnet swagger tofile ...
runner.SubCommand("tofile", "retrieves Swagger from a startup assembly, and writes to file ", c =>
runner.SubCommand("tofile", "retrieves Swagger from a startup assembly, and writes to file", c =>
{
c.Argument("startupassembly", "relative path to the application's startup assembly");
c.Argument("swaggerdoc", "name of the swagger doc you want to retrieve, as configured in your startup class");

c.Option("--output", "relative path where the Swagger will be output, defaults to stdout");
c.Option("--host", "a specific host to include in the Swagger output");
c.Option("--basepath", "a specific basePath to include in the Swagger output");
c.Option("--serializeasv2", "output Swagger in the V2 format rather than V3", true);
c.Option(OpenApiVersionOption, "output Swagger in the specified version, defaults to 3.0");
c.Option("--yaml", "exports swagger in a yaml format", true);

// TODO Remove this option in the major version that adds support for OpenAPI 3.1
c.Option(SerializeAsV2Flag, "output Swagger in the V2 format rather than V3 [deprecated]", true);

c.OnRun((namedArgs) =>
{
string subProcessCommandLine = PrepareCommandLine(args, namedArgs);

var subProcess = Process.Start("dotnet", subProcessCommandLine);
using var child = Process.Start("dotnet", subProcessCommandLine);

subProcess.WaitForExit();
return subProcess.ExitCode;
child.WaitForExit();
return child.ExitCode;
});
});

Expand All @@ -56,8 +63,12 @@ public static int Main(string[] args)
c.Option("--output", "");
c.Option("--host", "");
c.Option("--basepath", "");
c.Option("--serializeasv2", "", true);
c.Option(OpenApiVersionOption, "");
c.Option("--yaml", "", true);

// TODO Remove this option in the major version that adds support for OpenAPI 3.1
c.Option(SerializeAsV2Flag, "", true);

c.OnRun((namedArgs) =>
{
SetupAndRetrieveSwaggerProviderAndOptions(namedArgs, out var swaggerProvider, out var swaggerOptions);
Expand Down Expand Up @@ -94,24 +105,40 @@ public static int Main(string[] args)
writer = new OpenApiJsonWriter(streamWriter);
}

if (namedArgs.ContainsKey("--serializeasv2"))
OpenApiSpecVersion specVersion = OpenApiSpecVersion.OpenApi3_0;

if (namedArgs.TryGetValue(OpenApiVersionOption, out var versionArg))
{
if (swaggerDocumentSerializer != null)
specVersion = versionArg switch
{
swaggerDocumentSerializer.SerializeDocument(swagger, writer, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0);
}
else
{
swagger.SerializeAsV2(writer);
}
"2.0" => OpenApiSpecVersion.OpenApi2_0,
"3.0" => OpenApiSpecVersion.OpenApi3_0,
_ => throw new NotSupportedException($"The specified OpenAPI version \"{versionArg}\" is not supported."),
};
}
else if (swaggerDocumentSerializer != null)
else if (namedArgs.ContainsKey(SerializeAsV2Flag))
{
specVersion = OpenApiSpecVersion.OpenApi2_0;
WriteSerializeAsV2DeprecationWarning();
}

if (swaggerDocumentSerializer != null)
{
swaggerDocumentSerializer.SerializeDocument(swagger, writer, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0);
swaggerDocumentSerializer.SerializeDocument(swagger, writer, specVersion);
}
else
{
swagger.SerializeAsV3(writer);
switch (specVersion)
{
case OpenApiSpecVersion.OpenApi2_0:
swagger.SerializeAsV2(writer);
break;

case OpenApiSpecVersion.OpenApi3_0:
default:
swagger.SerializeAsV3(writer);
break;
}
}

if (outputPath != null)
Expand All @@ -132,10 +159,10 @@ public static int Main(string[] args)
{
string subProcessCommandLine = PrepareCommandLine(args, namedArgs);

var subProcess = Process.Start("dotnet", subProcessCommandLine);
using var child = Process.Start("dotnet", subProcessCommandLine);

subProcess.WaitForExit();
return subProcess.ExitCode;
child.WaitForExit();
return child.ExitCode;
});
});

Expand All @@ -147,7 +174,7 @@ public static int Main(string[] args)
c.OnRun((namedArgs) =>
{
SetupAndRetrieveSwaggerProviderAndOptions(namedArgs, out var swaggerProvider, out var swaggerOptions);
IList<string> docNames = new List<string>();
IList<string> docNames = [];

string outputPath = namedArgs.TryGetValue("--output", out var arg1)
? Path.Combine(Directory.GetCurrentDirectory(), arg1)
Expand Down Expand Up @@ -185,7 +212,7 @@ public static int Main(string[] args)
return runner.Run(args);
}

private static void SetupAndRetrieveSwaggerProviderAndOptions(System.Collections.Generic.IDictionary<string, string> namedArgs, out ISwaggerProvider swaggerProvider, out IOptions<SwaggerOptions> swaggerOptions)
private static void SetupAndRetrieveSwaggerProviderAndOptions(IDictionary<string, string> namedArgs, out ISwaggerProvider swaggerProvider, out IOptions<SwaggerOptions> swaggerOptions)
{
// 1) Configure host with provided startupassembly
var startupAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(
Expand All @@ -199,7 +226,7 @@ private static void SetupAndRetrieveSwaggerProviderAndOptions(System.Collections
swaggerOptions = serviceProvider.GetService<IOptions<SwaggerOptions>>();
}

private static string PrepareCommandLine(string[] args, System.Collections.Generic.IDictionary<string, string> namedArgs)
private static string PrepareCommandLine(string[] args, IDictionary<string, string> namedArgs)
{
if (!File.Exists(namedArgs["startupassembly"]))
{
Expand All @@ -222,7 +249,7 @@ private static string PrepareCommandLine(string[] args, System.Collections.Gener
EscapePath(runtimeConfig),
EscapePath(typeof(Program).GetTypeInfo().Assembly.Location),
commandName,
string.Join(" ", subProcessArguments.Select(x => EscapePath(x)))
string.Join(" ", subProcessArguments.Select(EscapePath))
);
return subProcessCommandLine;
}
Expand Down Expand Up @@ -301,5 +328,21 @@ private static bool TryGetCustomHost<THost>(
host = (THost)factoryMethod.Invoke(null, null);
return true;
}

private static void WriteSerializeAsV2DeprecationWarning()
{
const string AppName = "Swashbuckle.AspNetCore.Cli";

string message = $"The {SerializeAsV2Flag} flag will be removed in a future version of {AppName}. Use the {OpenApiVersionOption} option instead.";

Console.WriteLine(message);

// See https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#setting-a-warning-message
// and https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS") is "true")
{
Console.WriteLine($"::warning title={AppName}::{message}");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,22 @@ public static IEndpointConventionBuilder MapSwagger(
throw new ArgumentException("Pattern must contain '{documentName}' parameter", nameof(pattern));
}

Action<SwaggerOptions> endpointSetupAction = options =>
var pipeline = endpoints.CreateApplicationBuilder()
.UseSwagger(Configure)
.Build();

return endpoints.MapGet(pattern, pipeline);

void Configure(SwaggerOptions options)
{
var endpointOptions = new SwaggerEndpointOptions();

setupAction?.Invoke(endpointOptions);

options.RouteTemplate = pattern;
options.SerializeAsV2 = endpointOptions.SerializeAsV2;
options.OpenApiVersion = endpointOptions.OpenApiVersion;
options.PreSerializeFilters.AddRange(endpointOptions.PreSerializeFilters);
};

var pipeline = endpoints.CreateApplicationBuilder()
.UseSwagger(endpointSetupAction)
.Build();

return endpoints.MapGet(pattern, pipeline);
}
}
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,13 @@ Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentMetadataProvider
Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentMetadataProvider.GetDocumentNames() -> System.Collections.Generic.IList<string>
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.PreSerializeFilters.get -> System.Collections.Generic.List<System.Action<Microsoft.OpenApi.Models.OpenApiDocument, Microsoft.AspNetCore.Http.HttpRequest>>
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SwaggerEndpointOptions() -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.CustomDocumentSerializer.get -> Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.CustomDocumentSerializer.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.PreSerializeFilters.get -> System.Collections.Generic.List<System.Action<Microsoft.OpenApi.Models.OpenApiDocument, Microsoft.AspNetCore.Http.HttpRequest>>
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.RouteTemplate.get -> string
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.RouteTemplate.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SwaggerOptions() -> void
Swashbuckle.AspNetCore.Swagger.UnknownSwaggerDocument
Swashbuckle.AspNetCore.Swagger.UnknownSwaggerDocument.UnknownSwaggerDocument(string documentName, System.Collections.Generic.IEnumerable<string> knownDocuments) -> void
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.OpenApiVersion.get -> Microsoft.OpenApi.OpenApiSpecVersion
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.OpenApiVersion.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.OpenApiVersion.get -> Microsoft.OpenApi.OpenApiSpecVersion
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.OpenApiVersion.set -> void
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.set -> void
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.set -> void
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.set -> void
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SerializeAsV2.set -> void
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.get -> bool
Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SerializeAsV2.set -> void
23 changes: 18 additions & 5 deletions src/Swashbuckle.AspNetCore.Swagger/SwaggerEndpointOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Models;

namespace Swashbuckle.AspNetCore.Swagger
Expand All @@ -7,15 +8,27 @@ public class SwaggerEndpointOptions
{
public SwaggerEndpointOptions()
{
PreSerializeFilters = new List<Action<OpenApiDocument, HttpRequest>>();
SerializeAsV2 = false;
PreSerializeFilters = [];
OpenApiVersion = OpenApiSpecVersion.OpenApi3_0;
}

/// <summary>
/// Return Swagger JSON/YAML in the V2.0 format rather than V3.0.
/// </summary>
[Obsolete($"This property will be removed in a future version of Swashbuckle.AspNetCore. Use the {nameof(OpenApiVersion)} property instead.")]
public bool SerializeAsV2
{
get => OpenApiVersion == OpenApiSpecVersion.OpenApi2_0;
set => OpenApiVersion = value ? OpenApiSpecVersion.OpenApi2_0 : OpenApiSpecVersion.OpenApi3_0;
}

/// <summary>
/// Return Swagger JSON in the V2 format rather than V3
/// Gets or sets the OpenAPI (Swagger) document version to use.
/// </summary>
public bool SerializeAsV2 { get; set; }
/// <remarks>
/// The default value is <see cref="OpenApiSpecVersion.OpenApi3_0"/>.
/// </remarks>
public OpenApiSpecVersion OpenApiVersion { get; set; }

/// <summary>
/// Actions that can be applied SwaggerDocument's before they're serialized to JSON.
Expand Down
Loading