Skip to content

Commit 576c72b

Browse files
authored
Updated System.CommandLine in the AspNetCore.CommandLine project (#9083)
1 parent 1518ed3 commit 576c72b

19 files changed

+159
-132
lines changed

src/Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
<PackageVersion Include="Squadron.Redis" Version="0.27.1" />
6161
<PackageVersion Include="StackExchange.Redis" Version="2.6.80" />
6262
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
63-
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
63+
<PackageVersion Include="System.CommandLine" Version="2.0.2" />
6464
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.13.1" />
6565
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
6666
<PackageVersion Include="System.Reactive" Version="6.0.0" />
Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,67 @@
1-
using System.CommandLine.Builder;
1+
using System.CommandLine;
2+
using Microsoft.Extensions.DependencyInjection;
23
using Microsoft.Extensions.Hosting;
34

45
namespace HotChocolate.AspNetCore.CommandLine;
56

67
/// <summary>
78
/// A command line builder for the GraphQL server.
89
/// </summary>
9-
internal sealed class App : CommandLineBuilder
10+
internal sealed class App
1011
{
12+
private readonly IServiceProvider _serviceProvider;
13+
1114
/// <summary>
1215
/// Initializes a new instance of <see cref="App"/>.
1316
/// </summary>
1417
/// <param name="host">
1518
/// The host that is used to resolve services from the GraphQL Server.
1619
/// </param>
17-
public App(IHost host) : base(new GraphQLRootCommand())
20+
public App(IHost host)
1821
{
19-
this.AddMiddleware(x => x.BindingContext.AddService(_ => host))
20-
.UseDefaults();
22+
var serviceCollection = new ServiceCollection();
23+
serviceCollection.AddSingleton(host);
24+
serviceCollection.AddSingleton<ExportCommand>();
25+
serviceCollection.AddSingleton<ListCommand>();
26+
serviceCollection.AddSingleton<PrintCommand>();
27+
serviceCollection.AddSingleton<SchemaCommand>();
28+
serviceCollection.AddSingleton<GraphQLRootCommand>();
29+
_serviceProvider = serviceCollection.BuildServiceProvider();
30+
}
31+
32+
public int Invoke(string args, TextWriter? output = null)
33+
{
34+
var rootCommand = _serviceProvider.GetRequiredService<GraphQLRootCommand>();
35+
var parseResult = rootCommand.Parse(args);
36+
var configuration = output is null ? null : new InvocationConfiguration { Output = output };
37+
38+
return parseResult.Invoke(configuration);
39+
}
40+
41+
public int Invoke(string[] args, TextWriter? output = null)
42+
{
43+
var rootCommand = _serviceProvider.GetRequiredService<GraphQLRootCommand>();
44+
var parseResult = rootCommand.Parse(args);
45+
var configuration = output is null ? null : new InvocationConfiguration { Output = output };
46+
47+
return parseResult.Invoke(configuration);
48+
}
49+
50+
public async Task<int> InvokeAsync(string[] args, TextWriter? output = null)
51+
{
52+
var rootCommand = _serviceProvider.GetRequiredService<GraphQLRootCommand>();
53+
var parseResult = rootCommand.Parse(args);
54+
var configuration = output is null ? null : new InvocationConfiguration { Output = output };
55+
56+
return await parseResult.InvokeAsync(configuration);
57+
}
58+
59+
public async Task<int> InvokeAsync(string args, TextWriter? output = null)
60+
{
61+
var rootCommand = _serviceProvider.GetRequiredService<GraphQLRootCommand>();
62+
var parseResult = rootCommand.Parse(args);
63+
var configuration = output is null ? null : new InvocationConfiguration { Output = output };
64+
65+
return await parseResult.InvokeAsync(configuration);
2166
}
2267
}

src/HotChocolate/AspNetCore/src/AspNetCore.CommandLine/Command/ExportCommand.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,28 @@ internal sealed class ExportCommand : Command
1414
/// <summary>
1515
/// Initializes a new instance of the <see cref="ExportCommand"/> class.
1616
/// </summary>
17-
public ExportCommand() : base("export")
17+
public ExportCommand(IHost host) : base("export")
1818
{
1919
Description = "Export the graphql schema to a schema file";
2020

21-
AddOption(Opt<OutputOption>.Instance);
22-
AddOption(Opt<SchemaNameOption>.Instance);
21+
Options.Add(Opt<OutputOption>.Instance);
22+
Options.Add(Opt<SchemaNameOption>.Instance);
2323

24-
this.SetHandler(
25-
ExecuteAsync,
26-
Bind.FromServiceProvider<IConsole>(),
27-
Bind.FromServiceProvider<IHost>(),
28-
Opt<OutputOption>.Instance,
29-
Opt<SchemaNameOption>.Instance,
30-
Bind.FromServiceProvider<CancellationToken>());
24+
SetAction(
25+
(parseResult, cancellationToken) =>
26+
{
27+
var output = parseResult.InvocationConfiguration.Output;
28+
var outputFile = parseResult.GetValue(Opt<OutputOption>.Instance);
29+
var schemaName = parseResult.GetValue(Opt<SchemaNameOption>.Instance);
30+
31+
return ExecuteAsync(output, host, outputFile, schemaName, cancellationToken);
32+
});
3133
}
3234

3335
private static async Task ExecuteAsync(
34-
IConsole console,
36+
TextWriter output,
3537
IHost host,
36-
FileInfo? output,
38+
FileInfo? outputFile,
3739
string? schemaName,
3840
CancellationToken cancellationToken)
3941
{
@@ -45,7 +47,7 @@ private static async Task ExecuteAsync(
4547

4648
if (schemaNames.IsEmpty)
4749
{
48-
console.WriteLine("No schemas registered.");
50+
await output.WriteLineAsync("No schemas registered.");
4951
return;
5052
}
5153

@@ -55,13 +57,11 @@ private static async Task ExecuteAsync(
5557
}
5658

5759
var executor = await provider.GetExecutorAsync(schemaName, cancellationToken);
58-
output ??= new FileInfo(System.IO.Path.Combine(Environment.CurrentDirectory, "schema.graphqls"));
59-
var result = await SchemaFileExporter.Export(output.FullName, executor, cancellationToken);
60+
outputFile ??= new FileInfo(System.IO.Path.Combine(Environment.CurrentDirectory, "schema.graphqls"));
61+
var result = await SchemaFileExporter.Export(outputFile.FullName, executor, cancellationToken);
6062

61-
// ReSharper disable LocalizableElement
62-
console.WriteLine("Exported Files:");
63-
console.WriteLine($"- {result.SchemaFileName}");
64-
console.WriteLine($"- {result.SettingsFileName}");
65-
// ReSharper restore LocalizableElement
63+
await output.WriteLineAsync("Exported Files:");
64+
await output.WriteLineAsync($"- {result.SchemaFileName}");
65+
await output.WriteLineAsync($"- {result.SettingsFileName}");
6666
}
6767
}

src/HotChocolate/AspNetCore/src/AspNetCore.CommandLine/Command/GraphQLRootCommand.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ namespace HotChocolate.AspNetCore.CommandLine;
55
/// <summary>
66
/// The root command of the GraphQL CLI.
77
/// </summary>
8-
internal sealed class GraphQLRootCommand : Command
8+
internal sealed class GraphQLRootCommand : RootCommand
99
{
1010
/// <summary>
1111
/// Initializes a new instance of <see cref="GraphQLRootCommand"/>.
1212
/// </summary>
13-
public GraphQLRootCommand() : base("graphql")
13+
public GraphQLRootCommand(SchemaCommand schemaCommand)
1414
{
1515
Description = "A command line tool for a GraphQL Server.";
1616

17-
AddCommand(new SchemaCommand());
17+
Subcommands.Add(schemaCommand);
1818
}
1919
}

src/HotChocolate/AspNetCore/src/AspNetCore.CommandLine/Command/ListCommand.cs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,33 @@
66
namespace HotChocolate.AspNetCore.CommandLine;
77

88
/// <summary>
9-
/// The export command can be used to export the schema to a file.
9+
/// The list command can be used to list all registered schemas.
1010
/// </summary>
1111
internal sealed class ListCommand : Command
1212
{
1313
/// <summary>
14-
/// Initializes a new instance of the <see cref="ExportCommand"/> class.
14+
/// Initializes a new instance of the <see cref="ListCommand"/> class.
1515
/// </summary>
16-
public ListCommand() : base("list")
16+
public ListCommand(IHost host) : base("list")
1717
{
1818
Description = "List all registered GraphQL schemas.";
1919

20-
this.SetHandler(
21-
ExecuteAsync,
22-
Bind.FromServiceProvider<IConsole>(),
23-
Bind.FromServiceProvider<IHost>(),
24-
Bind.FromServiceProvider<CancellationToken>());
20+
SetAction(parseResult => ExecuteAsync(parseResult.InvocationConfiguration.Output, host));
2521
}
2622

27-
private static Task ExecuteAsync(
28-
IConsole console,
29-
IHost host,
30-
CancellationToken cancellationToken)
23+
private static async Task ExecuteAsync(TextWriter output, IHost host)
3124
{
3225
var schemaNames = host.Services.GetRequiredService<IRequestExecutorProvider>().SchemaNames;
3326

3427
if (schemaNames.IsEmpty)
3528
{
36-
console.WriteLine("No schemas registered.");
37-
return Task.CompletedTask;
29+
await output.WriteLineAsync("No schemas registered.");
30+
return;
3831
}
3932

4033
foreach (var name in schemaNames)
4134
{
42-
console.WriteLine(name);
35+
await output.WriteLineAsync(name);
4336
}
44-
45-
return Task.CompletedTask;
4637
}
4738
}

src/HotChocolate/AspNetCore/src/AspNetCore.CommandLine/Command/PrintCommand.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,31 @@
66
namespace HotChocolate.AspNetCore.CommandLine;
77

88
/// <summary>
9-
/// The print command can be used to print the schema to the console output
9+
/// The print command can be used to print the schema to the console output.
1010
/// </summary>
1111
internal sealed class PrintCommand : Command
1212
{
1313
/// <summary>
14-
/// Initializes a new instance of the <see cref="ExportCommand"/> class.
14+
/// Initializes a new instance of the <see cref="PrintCommand"/> class.
1515
/// </summary>
16-
public PrintCommand() : base("print")
16+
public PrintCommand(IHost host) : base("print")
1717
{
1818
Description = "Prints the graphql schema to the console output";
1919

20-
AddOption(Opt<SchemaNameOption>.Instance);
20+
Options.Add(Opt<SchemaNameOption>.Instance);
2121

22-
this.SetHandler(
23-
ExecuteAsync,
24-
Bind.FromServiceProvider<IConsole>(),
25-
Bind.FromServiceProvider<IHost>(),
26-
Opt<SchemaNameOption>.Instance,
27-
Bind.FromServiceProvider<CancellationToken>());
22+
SetAction(
23+
(parseResult, cancellationToken) =>
24+
{
25+
var output = parseResult.InvocationConfiguration.Output;
26+
var schemaName = parseResult.GetValue(Opt<SchemaNameOption>.Instance);
27+
28+
return ExecuteAsync(output, host, schemaName, cancellationToken);
29+
});
2830
}
2931

3032
private static async Task ExecuteAsync(
31-
IConsole console,
33+
TextWriter output,
3234
IHost host,
3335
string? schemaName,
3436
CancellationToken cancellationToken)
@@ -41,7 +43,7 @@ private static async Task ExecuteAsync(
4143

4244
if (schemaNames.IsEmpty)
4345
{
44-
console.WriteLine("No schemas registered.");
46+
await output.WriteLineAsync("No schemas registered.");
4547
return;
4648
}
4749

@@ -51,6 +53,6 @@ private static async Task ExecuteAsync(
5153
}
5254

5355
var executor = await provider.GetExecutorAsync(schemaName, cancellationToken);
54-
console.WriteLine(executor.Schema.ToString());
56+
await output.WriteLineAsync(executor.Schema.ToString());
5557
}
5658
}

src/HotChocolate/AspNetCore/src/AspNetCore.CommandLine/Command/SchemaCommand.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@
33
namespace HotChocolate.AspNetCore.CommandLine;
44

55
/// <summary>
6-
/// The schema command
6+
/// The schema command.
77
/// </summary>
88
internal sealed class SchemaCommand : Command
99
{
1010
/// <summary>
1111
/// Initializes a new instance of <see cref="SchemaCommand"/>.
1212
/// </summary>
13-
public SchemaCommand() : base("schema")
13+
public SchemaCommand(
14+
ExportCommand exportCommand,
15+
ListCommand listCommand,
16+
PrintCommand printCommand) : base("schema")
1417
{
1518
Description = "Schema management commands.";
1619

17-
AddCommand(new ExportCommand());
18-
AddCommand(new PrintCommand());
19-
AddCommand(new ListCommand());
20+
Subcommands.Add(exportCommand);
21+
Subcommands.Add(listCommand);
22+
Subcommands.Add(printCommand);
2023
}
2124
}

src/HotChocolate/AspNetCore/src/AspNetCore.CommandLine/Helpers/Bind.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/HotChocolate/AspNetCore/src/AspNetCore.CommandLine/WebApplicationExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public static async Task<int> RunWithGraphQLCommandsAsync(
5757
{
5858
if (args.IsGraphQLCommand())
5959
{
60-
return await new App(host).Build().InvokeAsync(args);
60+
return await new App(host).InvokeAsync(args);
6161
}
6262

6363
await host.RunAsync();
@@ -82,7 +82,7 @@ public static int RunWithGraphQLCommands(
8282
{
8383
if (args.IsGraphQLCommand())
8484
{
85-
return new App(host).Build().Invoke(args);
85+
return new App(host).Invoke(args);
8686
}
8787

8888
host.Run();
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Text.RegularExpressions;
2+
3+
namespace HotChocolate.AspNetCore.CommandLine;
4+
5+
internal static partial class OutputHelper
6+
{
7+
public static string ReplaceExecutableName(string output)
8+
{
9+
return ExecutableNameRegex().Replace(output, "$1 graphql");
10+
}
11+
12+
[GeneratedRegex(@"(Usage:\r?\n) [\w]+")]
13+
private static partial Regex ExecutableNameRegex();
14+
}

0 commit comments

Comments
 (0)