Skip to content

Commit 514c304

Browse files
bt-Knodelclaude
andcommitted
Use IAsyncSwaggerProvider in CLI to support async filters
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d22870e commit 514c304

16 files changed

+1561
-62
lines changed

Swashbuckle.AspNetCore.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
<Project Path="test/Swashbuckle.AspNetCore.TestSupport/Swashbuckle.AspNetCore.TestSupport.csproj" />
5959
</Folder>
6060
<Folder Name="/test/WebSites/">
61+
<Project Path="test/WebSites/Authorization/Authorization.csproj" />
6162
<Project Path="test/WebSites/Basic/Basic.csproj" />
6263
<Project Path="test/WebSites/CliExample/CliExample.csproj" />
6364
<Project Path="test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj" />

src/Swashbuckle.AspNetCore.Cli/CommandRunner.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ internal class CommandRunner(string commandName, string commandDescription, Text
44
{
55
private readonly Dictionary<string, string> _argumentDescriptors = [];
66
private readonly Dictionary<string, OptionDescriptor> _optionDescriptors = [];
7-
private Func<IDictionary<string, string>, int> _runFunc = (_) => 1;
7+
private Func<IDictionary<string, string>, Task<int>> _runFunc = (_) => Task.FromResult(1);
88
private readonly List<CommandRunner> _subRunners = [];
99
private readonly TextWriter _output = output;
1010

@@ -27,7 +27,7 @@ public void Option(string name, string description, bool isFlag = false)
2727
_optionDescriptors.Add(name, new OptionDescriptor { Description = description, IsFlag = isFlag });
2828
}
2929

30-
public void OnRun(Func<IDictionary<string, string>, int> runFunc)
30+
public void OnRun(Func<IDictionary<string, string>, Task<int>> runFunc)
3131
{
3232
_runFunc = runFunc;
3333
}
@@ -39,12 +39,12 @@ public void SubCommand(string name, string description, Action<CommandRunner> co
3939
_subRunners.Add(runner);
4040
}
4141

42-
public int Run(IEnumerable<string> args)
42+
public async Task<int> RunAsync(IEnumerable<string> args)
4343
{
4444
if (args.Any())
4545
{
4646
var subRunner = _subRunners.FirstOrDefault(r => r.CommandName.Split(' ').Last() == args.First());
47-
if (subRunner != null) return subRunner.Run(args.Skip(1));
47+
if (subRunner != null) return await subRunner.RunAsync(args.Skip(1));
4848
}
4949

5050
if (_subRunners.Count != 0 || !TryParseArgs(args, out IDictionary<string, string> namedArgs))
@@ -53,7 +53,7 @@ public int Run(IEnumerable<string> args)
5353
return 1;
5454
}
5555

56-
return _runFunc(namedArgs);
56+
return await _runFunc(namedArgs);
5757
}
5858

5959
private bool TryParseArgs(IEnumerable<string> args, out IDictionary<string, string> namedArgs)

src/Swashbuckle.AspNetCore.Cli/Program.cs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ internal class Program
1616
{
1717
private const string OpenApiVersionOption = "--openapiversion";
1818

19-
public static int Main(string[] args)
19+
public static async Task<int> Main(string[] args)
2020
{
2121
// Helper to simplify command line parsing etc.
2222
var runner = new CommandRunner("dotnet swagger", "Swashbuckle (Swagger) Command Line Tools", Console.Out);
@@ -45,7 +45,7 @@ public static int Main(string[] args)
4545
using var child = Process.Start("dotnet", subProcessCommandLine);
4646

4747
child.WaitForExit();
48-
return child.ExitCode;
48+
return Task.FromResult(child.ExitCode);
4949
});
5050
});
5151

@@ -60,14 +60,16 @@ public static int Main(string[] args)
6060
c.Option(OpenApiVersionOption, "");
6161
c.Option("--yaml", "", true);
6262

63-
c.OnRun((namedArgs) =>
63+
c.OnRun(async (namedArgs) =>
6464
{
65-
SetupAndRetrieveSwaggerProviderAndOptions(namedArgs, out var swaggerProvider, out var swaggerOptions);
65+
SetupAndRetrieveSwaggerProviderAndOptions(namedArgs, out var asyncSwaggerProvider, out var swaggerProvider, out var swaggerOptions);
6666
var swaggerDocumentSerializer = swaggerOptions?.Value?.CustomDocumentSerializer;
67-
var swagger = swaggerProvider.GetSwagger(
68-
namedArgs["swaggerdoc"],
69-
namedArgs.TryGetValue("--host", out var arg) ? arg : null,
70-
namedArgs.TryGetValue("--basepath", out var namedArg) ? namedArg : null);
67+
68+
var host = namedArgs.TryGetValue("--host", out var arg) ? arg : null;
69+
var basePath = namedArgs.TryGetValue("--basepath", out var namedArg) ? namedArg : null;
70+
var swagger = asyncSwaggerProvider != null
71+
? await asyncSwaggerProvider.GetSwaggerAsync(namedArgs["swaggerdoc"], host, basePath)
72+
: swaggerProvider.GetSwagger(namedArgs["swaggerdoc"], host, basePath);
7173

7274
// 4) Serialize to specified output location or stdout
7375
var outputPath = namedArgs.TryGetValue("--output", out var arg1)
@@ -139,7 +141,7 @@ public static int Main(string[] args)
139141
using var child = Process.Start("dotnet", subProcessCommandLine);
140142

141143
child.WaitForExit();
142-
return child.ExitCode;
144+
return Task.FromResult(child.ExitCode);
143145
});
144146
});
145147

@@ -150,7 +152,7 @@ public static int Main(string[] args)
150152
c.Option("--output", "");
151153
c.OnRun((namedArgs) =>
152154
{
153-
SetupAndRetrieveSwaggerProviderAndOptions(namedArgs, out var swaggerProvider, out var swaggerOptions);
155+
SetupAndRetrieveSwaggerProviderAndOptions(namedArgs, out _, out var swaggerProvider, out var swaggerOptions);
154156
IList<string> docNames = [];
155157

156158
string outputPath = namedArgs.TryGetValue("--output", out var arg1)
@@ -172,7 +174,7 @@ public static int Main(string[] args)
172174
if (swaggerProvider is not ISwaggerDocumentMetadataProvider docMetaProvider)
173175
{
174176
writer.WriteLine($"The registered {nameof(ISwaggerProvider)} instance does not implement {nameof(ISwaggerDocumentMetadataProvider)}; unable to list the Swagger document names.");
175-
return -1;
177+
return Task.FromResult(-1);
176178
}
177179

178180
docNames = docMetaProvider.GetDocumentNames();
@@ -182,14 +184,14 @@ public static int Main(string[] args)
182184
writer.WriteLine($"\"{name}\"");
183185
}
184186

185-
return 0;
187+
return Task.FromResult(0);
186188
});
187189
});
188190

189-
return runner.Run(args);
191+
return await runner.RunAsync(args);
190192
}
191193

192-
private static void SetupAndRetrieveSwaggerProviderAndOptions(IDictionary<string, string> namedArgs, out ISwaggerProvider swaggerProvider, out IOptions<SwaggerOptions> swaggerOptions)
194+
private static void SetupAndRetrieveSwaggerProviderAndOptions(IDictionary<string, string> namedArgs, out IAsyncSwaggerProvider asyncSwaggerProvider, out ISwaggerProvider swaggerProvider, out IOptions<SwaggerOptions> swaggerOptions)
193195
{
194196
// 1) Configure host with provided startupassembly
195197
var startupAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(
@@ -199,6 +201,7 @@ private static void SetupAndRetrieveSwaggerProviderAndOptions(IDictionary<string
199201
var serviceProvider = GetServiceProvider(startupAssembly);
200202

201203
// 3) Retrieve Swagger via configured provider
204+
asyncSwaggerProvider = serviceProvider.GetService<IAsyncSwaggerProvider>();
202205
swaggerProvider = serviceProvider.GetRequiredService<ISwaggerProvider>();
203206
swaggerOptions = serviceProvider.GetService<IOptions<SwaggerOptions>>();
204207
}

test/Swashbuckle.AspNetCore.Cli.Test/CommandRunnerTests.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Swashbuckle.AspNetCore.Cli.Test;
55
public static class CommandRunnerTests
66
{
77
[Fact]
8-
public static void Run_ParsesArgumentsAndExecutesCommands_AccordingToConfiguredMetadata()
8+
public static async Task Run_ParsesArgumentsAndExecutesCommands_AccordingToConfiguredMetadata()
99
{
1010
var receivedValues = new List<string>();
1111
var subject = new CommandRunner("test", "a test", new StringWriter());
@@ -18,7 +18,7 @@ public static void Run_ParsesArgumentsAndExecutesCommands_AccordingToConfiguredM
1818
receivedValues.Add(namedArgs["--opt1"]);
1919
receivedValues.Add(namedArgs["--opt2"]);
2020
receivedValues.Add(namedArgs["arg1"]);
21-
return 2;
21+
return Task.FromResult(2);
2222
});
2323
});
2424
subject.SubCommand("cmd2", "", c => {
@@ -30,42 +30,42 @@ public static void Run_ParsesArgumentsAndExecutesCommands_AccordingToConfiguredM
3030
receivedValues.Add(namedArgs["--opt1"]);
3131
receivedValues.Add(namedArgs["--opt2"]);
3232
receivedValues.Add(namedArgs["arg1"]);
33-
return 3;
33+
return Task.FromResult(3);
3434
});
3535
});
3636

37-
var cmd1ExitCode = subject.Run(["cmd1", "--opt1", "foo", "--opt2", "bar"]);
38-
var cmd2ExitCode = subject.Run(["cmd2", "--opt1", "blah", "--opt2", "dblah"]);
37+
var cmd1ExitCode = await subject.RunAsync(["cmd1", "--opt1", "foo", "--opt2", "bar"]);
38+
var cmd2ExitCode = await subject.RunAsync(["cmd2", "--opt1", "blah", "--opt2", "dblah"]);
3939

4040
Assert.Equal(2, cmd1ExitCode);
4141
Assert.Equal(3, cmd2ExitCode);
4242
Assert.Equal(["foo", null, "bar", "blah", null, "dblah"], [.. receivedValues]);
4343
}
4444

4545
[Fact]
46-
public static void Run_PrintsAvailableCommands_WhenUnexpectedCommandIsProvided()
46+
public static async Task Run_PrintsAvailableCommands_WhenUnexpectedCommandIsProvided()
4747
{
4848
var output = new StringWriter();
4949
var subject = new CommandRunner("test", "a test", output);
5050
subject.SubCommand("cmd", "does something", c => {
5151
});
5252

53-
var exitCode = subject.Run(["foo"]);
53+
var exitCode = await subject.RunAsync(["foo"]);
5454

5555
Assert.StartsWith("a test", output.ToString());
5656
Assert.Contains("Commands:", output.ToString());
5757
Assert.Contains("cmd: does something", output.ToString());
5858
}
5959

6060
[Fact]
61-
public static void Run_PrintsAvailableCommands_WhenHelpOptionIsProvided()
61+
public static async Task Run_PrintsAvailableCommands_WhenHelpOptionIsProvided()
6262
{
6363
var output = new StringWriter();
6464
var subject = new CommandRunner("test", "a test", output);
6565
subject.SubCommand("cmd", "does something", c => {
6666
});
6767

68-
var exitCode = subject.Run(["--help"]);
68+
var exitCode = await subject.RunAsync(["--help"]);
6969

7070
Assert.StartsWith("a test", output.ToString());
7171
Assert.Contains("Commands:", output.ToString());
@@ -81,7 +81,7 @@ public static void Run_PrintsAvailableCommands_WhenHelpOptionIsProvided()
8181
[InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "--opt1" }, true)]
8282
[InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "foo", "bar" }, true)]
8383
[InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "foo" }, false)]
84-
public static void Run_PrintsCommandUsage_WhenUnexpectedArgumentsAreProvided(
84+
public static async Task Run_PrintsCommandUsage_WhenUnexpectedArgumentsAreProvided(
8585
string[] optionNames,
8686
string[] argNames,
8787
string[] providedArgs,
@@ -97,7 +97,7 @@ public static void Run_PrintsCommandUsage_WhenUnexpectedArgumentsAreProvided(
9797
c.Argument(name, "");
9898
});
9999

100-
subject.Run(providedArgs);
100+
await subject.RunAsync(providedArgs);
101101

102102
if (shouldPrintUsage)
103103
Assert.StartsWith("Usage: test cmd", output.ToString());

test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
<ItemGroup>
99
<ProjectReference Include="..\Swashbuckle.AspNetCore.TestSupport\Swashbuckle.AspNetCore.TestSupport.csproj" />
10+
<ProjectReference Include="..\WebSites\Authorization\Authorization.csproj" />
1011
<ProjectReference Include="..\WebSites\Basic\Basic.csproj" />
1112
<ProjectReference Include="..\WebSites\CustomDocumentSerializer\CustomDocumentSerializer.csproj" />
1213
<ProjectReference Include="..\WebSites\MinimalAppWithHostedServices\MinimalAppWithHostedServices.csproj" />

0 commit comments

Comments
 (0)