diff --git a/build/dependencies.private.props b/build/dependencies.private.props
index 78c413be1..e62f55d68 100644
--- a/build/dependencies.private.props
+++ b/build/dependencies.private.props
@@ -8,8 +8,8 @@
8.0.0
17.11.0
4.20.70
- 2.4.2
- 2.4.2
+ 2.8.1
+ 2.8.2
4.2.2
8.0.8
4.5.4
diff --git a/build/dependencies.props b/build/dependencies.props
index 451dfddd2..6a30ff89c 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -54,7 +54,7 @@
1.1.1
- 1.1.1
+ 2.0.0-beta4.22272.1
8.0.11
diff --git a/src/Microsoft.Azure.SignalR.Emulator/Microsoft.Azure.SignalR.Emulator.csproj b/src/Microsoft.Azure.SignalR.Emulator/Microsoft.Azure.SignalR.Emulator.csproj
index 6d18f412a..9f52ad2ef 100644
--- a/src/Microsoft.Azure.SignalR.Emulator/Microsoft.Azure.SignalR.Emulator.csproj
+++ b/src/Microsoft.Azure.SignalR.Emulator/Microsoft.Azure.SignalR.Emulator.csproj
@@ -14,9 +14,7 @@
-
+
-
-
diff --git a/src/Microsoft.Azure.SignalR.Emulator/Program.cs b/src/Microsoft.Azure.SignalR.Emulator/Program.cs
index 97a0d9b13..d83400a87 100644
--- a/src/Microsoft.Azure.SignalR.Emulator/Program.cs
+++ b/src/Microsoft.Azure.SignalR.Emulator/Program.cs
@@ -2,15 +2,18 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
+using System.CommandLine;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
+#nullable enable
namespace Microsoft.Azure.SignalR.Emulator
{
public class Program
@@ -19,189 +22,199 @@ public class Program
private static readonly string SettingsFileName = "settings.json";
private static readonly string SettingsFile = Path.GetFullPath(SettingsFileName);
private static readonly string AppSettingsFile = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
-
internal static readonly string ProgramDefaultSettingsFile = Path.Combine(AppContext.BaseDirectory, SettingsFileName);
- public static void Main(string[] args)
+ public static async Task Main(string[] args)
{
- var app = new CommandLineApplication();
- app.Name = "asrs-emulator";
- app.Description = "The local emulator for Azure SignalR Serverless features.";
- app.HelpOption("-h|--help");
-
- app.Command("upstream", command =>
+ var rootCommand = new RootCommand("The local emulator for Azure SignalR Serverless features.")
{
- command.Description = "To init/list the upstream options";
- command.HelpOption("-h|--help");
- command.Command("init", c =>
- {
- c.Description = "Init the default upstream options into a settings.json config. Use -o to specify the folder to export the default settings.";
- var configOptions = c.Option("-o|--output", "Specify the folder to init the upstream settings file.", CommandOptionType.SingleValue);
- c.HelpOption("-h|--help");
- c.OnExecute(() =>
- {
- string outputFile = configOptions.HasValue() ? Path.GetFullPath(Path.Combine(configOptions.Value(), SettingsFileName)) : SettingsFile;
- if (File.Exists(outputFile))
- {
- Console.WriteLine($"Already contains '{outputFile}', still want to override it with the default one? (N/y)");
- if (Console.ReadKey().Key != ConsoleKey.Y)
- {
- return 0;
- }
+ Name = "asrs-emulator"
+ };
- Console.WriteLine();
- }
+ rootCommand.AddCommand(CreateUpstreamCommand());
+ rootCommand.AddCommand(CreateStartCommand());
- Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
- File.Copy(ProgramDefaultSettingsFile, outputFile, true);
+ return await rootCommand.InvokeAsync(args);
+ }
- Console.WriteLine($"Exported default settings to '{outputFile}'.");
- return 0;
- });
- });
- command.Command("list", c =>
- {
- c.Description = "List current upstream options. Use -c to specify the folder or file to read the settings.";
- var configOptions = c.Option("-c|--config", "Specify the upstream settings file to load from.", CommandOptionType.SingleValue);
- c.HelpOption("-h|--help");
- c.OnExecute(() =>
- {
- if (!TryGetConfigFilePath(configOptions, out var config))
- {
- return 1;
- }
+ private static Command CreateUpstreamCommand()
+ {
+ var upstreamCommand = new Command("upstream", "To init/list the upstream options")
+ {
+ CreateInitCommand(),
+ CreateListCommand()
+ };
- var host = CreateHostBuilder(args, null, DefaultPort, config).Build();
-
- Console.WriteLine($"Loaded upstream settings from '{config}'");
-
- var option = host.Services.GetRequiredService>();
- option.Value.Print();
- return 0;
- });
- });
- });
+ return upstreamCommand;
+ }
- app.Command("start", command =>
+ private static Command CreateInitCommand()
+ {
+ var outputOption = new Option(
+ new[] { "-o", "--output" },
+ "Specify the folder to init the upstream settings file."
+ );
+
+ var initCommand = new Command("init", "Init the default upstream options into a settings.json config")
{
- command.Description = "To start the emulator.";
- var portOptions = command.Option("-p|--port", "Specify the port to use.", CommandOptionType.SingleValue);
- var ipOptions = command.Option("-i|--ip", "Specify the IP address to use.", CommandOptionType.SingleValue);
- var configOptions = command.Option("-c|--config", "Specify the upstream settings file to load from.", CommandOptionType.SingleValue);
- command.HelpOption("-h|--help");
- command.OnExecute(() =>
+ outputOption
+ };
+
+ initCommand.SetHandler((string? output) =>
+ {
+ string outputFile = !string.IsNullOrEmpty(output)
+ ? Path.GetFullPath(Path.Combine(output, SettingsFileName))
+ : SettingsFile;
+
+ if (File.Exists(outputFile))
{
- if (!TryGetPort(portOptions, out var port) || !TryGetConfigFilePath(configOptions, out var config) || !TryGetIpAddress(ipOptions, out var ip))
+ Console.WriteLine($"Already contains '{outputFile}', still want to override it with the default one? (N/y)");
+ if (Console.ReadKey().Key != ConsoleKey.Y)
{
- return 1;
+ return;
}
- Console.WriteLine($"Loaded settings from '{config}'. Changes to the settings file will be hot-loaded into the emulator.");
+ Console.WriteLine();
+ }
- CreateHostBuilder(args, ip, port, config).Build().Run();
- return 0;
- });
- });
+ Directory.CreateDirectory(Path.GetDirectoryName(outputFile)!);
+ File.Copy(ProgramDefaultSettingsFile, outputFile, true);
- app.OnExecute(() =>
+ Console.WriteLine($"Exported default settings to '{outputFile}'.");
+ }, outputOption);
+
+ return initCommand;
+ }
+
+ private static Command CreateListCommand()
+ {
+ var configOption = new Option(
+ new[] { "-c", "--config" },
+ "Specify the upstream settings file to load from."
+ );
+
+ var listCommand = new Command("list", "List current upstream options")
{
- app.ShowHelp();
- return 0;
- });
+ configOption
+ };
- try
+ listCommand.SetHandler((string? config) =>
{
- app.Execute(args);
- }
- catch (Exception e)
+ if (!TryGetConfigFilePath(config, out var configFile))
+ {
+ return;
+ }
+
+ var host = CreateHostBuilder(null, null, DefaultPort, configFile).Build();
+
+ Console.WriteLine($"Loaded upstream settings from '{configFile}'");
+
+ var options = host.Services.GetRequiredService>();
+ options.Value.Print();
+ }, configOption);
+
+ return listCommand;
+ }
+
+ private static Command CreateStartCommand()
+ {
+ var portOption = new Option(
+ new[] { "-p", "--port" },
+ () => DefaultPort,
+ "Specify the port to use."
+ );
+ var ipOption = new Option(
+ new[] { "-i", "--ip" },
+ "Specify the IP address to use."
+ );
+ var configOption = new Option(
+ new[] { "-c", "--config" },
+ "Specify the upstream settings file to load from."
+ );
+
+ var startCommand = new Command("start", "To start the emulator.")
{
- Console.WriteLine($"Error starting emulator: {e.Message}.");
- }
+ portOption,
+ ipOption,
+ configOption
+ };
+
+ startCommand.SetHandler((int? port, string? ip, string? config) =>
+ {
+ if (!TryGetPort(port, out var actualPort) ||
+ !TryGetConfigFilePath(config, out var configFile) ||
+ !TryGetIpAddress(ip, out var actualIp))
+ {
+ return;
+ }
+
+ Console.WriteLine($"Loaded settings from '{configFile}'. Changes to the settings file will be hot-loaded into the emulator.");
+
+ CreateHostBuilder(null, actualIp, actualPort, configFile).Build().Run();
+ }, portOption, ipOption, configOption);
+
+ return startCommand;
}
- public static IHostBuilder CreateHostBuilder(string[] args, IPAddress ip, int port, string configFile)
+ private static IHostBuilder CreateHostBuilder(string[]? args, IPAddress? ip, int port, string configFile)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
- webBuilder.UseKestrel(o =>
+ webBuilder.UseKestrel(options =>
{
if (ip == null)
{
- o.ListenLocalhost(port);
+ options.ListenLocalhost(port);
}
else
{
- o.Listen(ip, port);
+ options.Listen(ip, port);
}
});
})
- .ConfigureAppConfiguration(s =>
+ .ConfigureAppConfiguration(config =>
{
- s.AddJsonFile(AppSettingsFile, optional: true, reloadOnChange: true);
- s.AddJsonFile(configFile, optional: true, reloadOnChange: true);
+ config.AddJsonFile(AppSettingsFile, optional: true, reloadOnChange: true);
+ config.AddJsonFile(configFile, optional: true, reloadOnChange: true);
});
}
- private static bool TryGetIpAddress(CommandOption ipOptions, out IPAddress ip)
+ private static bool TryGetIpAddress(string? ipOption, out IPAddress? ip)
{
- if (ipOptions.HasValue())
+ if (!string.IsNullOrEmpty(ipOption))
{
- var val = ipOptions.Value();
-
- if (IPAddress.TryParse(val, out ip))
+ if (IPAddress.TryParse(ipOption, out ip))
{
return true;
}
- else
- {
- Console.WriteLine($"Invalid IP address value: {val}");
- return false;
- }
- }
- else
- {
- ip = null;
- return true;
+
+ Console.WriteLine($"Invalid IP address value: {ipOption}");
+ return false;
}
+
+ ip = null;
+ return true;
}
- private static bool TryGetPort(CommandOption portOption, out int port)
+ private static bool TryGetPort(int? portOption, out int port)
{
- if (portOption.HasValue())
- {
- var val = portOption.Value();
-
- if( int.TryParse(val, out port))
- {
- return true;
- }
- else
- {
- Console.WriteLine($"Invalid port value: {val}");
- return false;
- }
- }
- else
- {
- port = DefaultPort;
- return true;
- }
+ port = portOption ?? DefaultPort;
+ return true;
}
- private static bool TryGetConfigFilePath(CommandOption configOption, out string path)
+ private static bool TryGetConfigFilePath(string? configOption, [NotNullWhen(true)]out string? path)
{
- if (configOption.HasValue())
+ if (!string.IsNullOrEmpty(configOption))
{
- var fileAttempt = Path.GetFullPath(configOption.Value());
+ var fileAttempt = Path.GetFullPath(configOption);
if (File.Exists(fileAttempt))
{
path = fileAttempt;
return true;
}
- // Try this as a folder
var folderAttempt = Path.GetFullPath(Path.Combine(fileAttempt, SettingsFileName));
if (File.Exists(folderAttempt))
{
@@ -213,11 +226,9 @@ private static bool TryGetConfigFilePath(CommandOption configOption, out string
path = null;
return false;
}
- else
- {
- path = SettingsFile;
- return true;
- }
+
+ path = SettingsFile;
+ return true;
}
}
}
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index b0aaa81b2..189d3799b 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -6,7 +6,5 @@
-
-
diff --git a/test/Microsoft.Azure.SignalR.Emulator.Tests/CommandInputFacts.cs b/test/Microsoft.Azure.SignalR.Emulator.Tests/CommandInputFacts.cs
index 86c8b9dfc..5ee639058 100644
--- a/test/Microsoft.Azure.SignalR.Emulator.Tests/CommandInputFacts.cs
+++ b/test/Microsoft.Azure.SignalR.Emulator.Tests/CommandInputFacts.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
@@ -23,61 +24,69 @@ public class CommandInputFacts : IDisposable
private readonly ITestOutputHelper _output;
private const string HelpInfo = @"
+Description:
+ The local emulator for Azure SignalR Serverless features.
-Usage: asrs-emulator [options] [command]
+Usage:
+ asrs-emulator [command] [options]
Options:
- -h|--help Show help information
+ --version Show version information
+ -?, -h, --help Show help and usage information
Commands:
- start To start the emulator.
upstream To init/list the upstream options
-
-Use ""asrs-emulator [command] --help"" for more information about a command.
-
+ start To start the emulator.
";
private const string StartHelpInfo = @"
+Description:
+ To start the emulator.
-Usage: asrs-emulator start [options]
+Usage:
+ asrs-emulator start [options]
Options:
- -p|--port Specify the port to use.
- -i|--ip Specify the IP address to use.
- -c|--config Specify the upstream settings file to load from.
- -h|--help Show help information
-
+ -p, --port Specify the port to use. [default: 8888]
+ -i, --ip Specify the IP address to use.
+ -c, --config Specify the upstream settings file to load from.
+ -?, -h, --help Show help and usage information
";
private const string UpstreamHelpInfo = @"
+Description:
+ To init/list the upstream options
-Usage: asrs-emulator upstream [options] [command]
+Usage:
+ asrs-emulator upstream [command] [options]
Options:
- -h|--help Show help information
+ -?, -h, --help Show help and usage information
Commands:
- init Init the default upstream options into a settings.json config. Use -o to specify the folder to export the default settings.
- list List current upstream options. Use -c to specify the folder or file to read the settings.
-
-Use ""upstream [command] --help"" for more information about a command.
-
+ init Init the default upstream options into a settings.json config
+ list List current upstream options
";
private const string UpstreamInitHelpInfo = @"
+Description:
+ Init the default upstream options into a settings.json config
-Usage: asrs-emulator upstream init [options]
+Usage:
+ asrs-emulator upstream init [options]
Options:
- -o|--output Specify the folder to init the upstream settings file.
- -h|--help Show help information
+ -o, --output