diff --git a/performance/resultsComparer/Program.cs b/performance/resultsComparer/Program.cs index 1c6fb7bc2..9d3c0f871 100644 --- a/performance/resultsComparer/Program.cs +++ b/performance/resultsComparer/Program.cs @@ -11,7 +11,8 @@ public class Program public static async Task Main(string[] args) { var rootCommand = CreateRootCommand(); - return await rootCommand.InvokeAsync(args); + var parseResult = rootCommand.Parse(args); + return await parseResult.InvokeAsync(); } internal static RootCommand CreateRootCommand() { @@ -21,25 +22,40 @@ internal static RootCommand CreateRootCommand() { Description = "Compare the benchmark results." }; - var oldResultsPathArgument = new Argument("existingReportPath", () => ExistingReportPath, "The path to the existing benchmark report."); - compareCommand.AddArgument(oldResultsPathArgument); - var newResultsPathArgument = new Argument("newReportPath", () => ExistingReportPath, "The path to the new benchmark report."); - compareCommand.AddArgument(newResultsPathArgument); - var logLevelOption = new Option(["--log-level", "-l"], () => LogLevel.Warning, "The log level to use."); - compareCommand.AddOption(logLevelOption); + var oldResultsPathArgument = new Argument("existingReportPath") + { + DefaultValueFactory = (_) => ExistingReportPath, + Description = "The path to the existing benchmark report.", + }; + compareCommand.Arguments.Add(oldResultsPathArgument); + var newResultsPathArgument = new Argument("newReportPath") + { + DefaultValueFactory = (_) => ExistingReportPath, + Description = "The path to the new benchmark report.", + }; + compareCommand.Arguments.Add(newResultsPathArgument); + var logLevelOption = new Option("--log-level", "-l") + { + DefaultValueFactory = (_) => LogLevel.Warning, + Description = "The log level to use.", + }; + compareCommand.Options.Add(logLevelOption); var allPolicyNames = IBenchmarkComparisonPolicy.GetAllPolicies().Select(static p => p.Name).Order(StringComparer.OrdinalIgnoreCase).ToArray(); - var policiesOption = new Option(["--policies", "-p"], () => ["all"], $"The policies to use for comparison: {string.Join(',', allPolicyNames)}.") + var policiesOption = new Option("--policies", "-p") { - Arity = ArgumentArity.ZeroOrMore + Arity = ArgumentArity.ZeroOrMore, + DefaultValueFactory = (_) => ["all"], + Description = $"The policies to use for comparison: {string.Join(',', allPolicyNames)}.", }; - compareCommand.AddOption(policiesOption); - compareCommand.Handler = new CompareCommandHandler + compareCommand.Options.Add(policiesOption); + var compareCommandHandler = new CompareCommandHandler { OldResultsPath = oldResultsPathArgument, NewResultsPath = newResultsPathArgument, LogLevel = logLevelOption, Policies = policiesOption, }; + compareCommand.SetAction(compareCommandHandler.InvokeAsync); rootCommand.Add(compareCommand); return rootCommand; } diff --git a/performance/resultsComparer/handlers/AsyncCommandHandler.cs b/performance/resultsComparer/handlers/AsyncCommandHandler.cs deleted file mode 100644 index f4c65b566..000000000 --- a/performance/resultsComparer/handlers/AsyncCommandHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.CommandLine.Invocation; -using System.Threading.Tasks; - -namespace resultsComparer.Handlers; - -internal abstract class AsyncCommandHandler : ICommandHandler -{ - public int Invoke(InvocationContext context) - { - throw new InvalidOperationException("This method should not be called"); - } - public abstract Task InvokeAsync(InvocationContext context); -} diff --git a/performance/resultsComparer/handlers/CompareCommandHandler.cs b/performance/resultsComparer/handlers/CompareCommandHandler.cs index ed079953e..1565fdf77 100644 --- a/performance/resultsComparer/handlers/CompareCommandHandler.cs +++ b/performance/resultsComparer/handlers/CompareCommandHandler.cs @@ -3,30 +3,38 @@ using System.CommandLine.Invocation; using System.Text.Json; using System.Text.Json.Serialization; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using resultsComparer.Models; using resultsComparer.Policies; namespace resultsComparer.Handlers; -internal class CompareCommandHandler : AsyncCommandHandler +internal class CompareCommandHandler : AsynchronousCommandLineAction { public required Argument OldResultsPath { get; set; } public required Argument NewResultsPath { get; set; } public required Option LogLevel { get; set; } public required Option Policies { get; set; } - public override Task InvokeAsync(InvocationContext context) + public override Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken = default) { - var cancellationToken = context.BindingContext.GetRequiredService(); - var oldResultsPath = context.ParseResult.GetValueForArgument(OldResultsPath); - var newResultsPath = context.ParseResult.GetValueForArgument(NewResultsPath); - var policyNames = context.ParseResult.GetValueForOption(Policies) ?? []; + var oldResultsPath = parseResult.GetValue(OldResultsPath); + var newResultsPath = parseResult.GetValue(NewResultsPath); + var policyNames = parseResult.GetValue(Policies) ?? []; var policies = IBenchmarkComparisonPolicy.GetSelectedPolicies(policyNames).ToArray(); - var logLevel = context.ParseResult.GetValueForOption(LogLevel); + var logLevel = parseResult.GetValue(LogLevel); using var loggerFactory = Logger.ConfigureLogger(logLevel); var logger = loggerFactory.CreateLogger(); + if (string.IsNullOrWhiteSpace(oldResultsPath)) + { + logger.LogError("Old results path is required."); + return Task.FromResult(1); + } + if (string.IsNullOrWhiteSpace(newResultsPath)) + { + logger.LogError("New results path is required."); + return Task.FromResult(1); + } return CompareResultsAsync(oldResultsPath, newResultsPath, logger, policies, cancellationToken); } private static async Task CompareResultsAsync(string existingReportPath, string newReportPath, ILogger logger, IBenchmarkComparisonPolicy[] comparisonPolicies, CancellationToken cancellationToken = default) diff --git a/performance/resultsComparer/resultsComparer.csproj b/performance/resultsComparer/resultsComparer.csproj index d42ecb451..294fcc217 100644 --- a/performance/resultsComparer/resultsComparer.csproj +++ b/performance/resultsComparer/resultsComparer.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/Microsoft.OpenApi.Hidi/Extensions/CommandExtensions.cs b/src/Microsoft.OpenApi.Hidi/Extensions/CommandExtensions.cs index 5b83212d5..1a2dc477d 100644 --- a/src/Microsoft.OpenApi.Hidi/Extensions/CommandExtensions.cs +++ b/src/Microsoft.OpenApi.Hidi/Extensions/CommandExtensions.cs @@ -12,7 +12,7 @@ public static void AddOptions(this Command command, IReadOnlyList - + - + diff --git a/src/Microsoft.OpenApi.Hidi/Options/CommandOptions.cs b/src/Microsoft.OpenApi.Hidi/Options/CommandOptions.cs index 908435c33..cd817011b 100644 --- a/src/Microsoft.OpenApi.Hidi/Options/CommandOptions.cs +++ b/src/Microsoft.OpenApi.Hidi/Options/CommandOptions.cs @@ -8,51 +8,86 @@ namespace Microsoft.OpenApi.Hidi.Options internal class CommandOptions { // command option parameters and aliases - public readonly Option OpenApiDescriptionOption = new("--openapi", "Input OpenAPI description file path or URL"); - public readonly Option CsdlOption = new("--csdl", "Input CSDL file path or URL"); - public readonly Option CsdlFilterOption = new("--csdl-filter", "Comma delimited list of EntitySets or Singletons to filter CSDL on. e.g. tasks,accounts"); - public readonly Option OutputOption = new("--output", "The output file path for the generated file.") { Arity = ArgumentArity.ZeroOrOne }; - public readonly Option OutputFolderOption = new("--output-folder", "The output directory path for the generated files.") { Arity = ArgumentArity.ZeroOrOne }; - public readonly Option CleanOutputOption = new("--clean-output", "Overwrite an existing file"); - public readonly Option VersionOption = new("--version", "OpenAPI specification version"); - public readonly Option MetadataVersionOption = new("--metadata-version", "Graph metadata version to use."); - public readonly Option FormatOption = new("--format", "File format"); - public readonly Option TerseOutputOption = new("--terse-output", "Produce terse json output"); - public readonly Option SettingsFileOption = new("--settings-path", "The configuration file with CSDL conversion settings."); - public readonly Option LogLevelOption = new("--log-level", () => LogLevel.Information, "The log level to use when logging messages to the main output."); - public readonly Option FilterByOperationIdsOption = new("--filter-by-operationids", "Filters OpenApiDocument by comma delimited list of OperationId(s) provided"); - public readonly Option FilterByTagsOption = new("--filter-by-tags", "Filters OpenApiDocument by comma delimited list of Tag(s) provided. Also accepts a single regex."); - public readonly Option FilterByCollectionOption = new("--filter-by-collection", "Filters OpenApiDocument by Postman collection provided. Provide path to collection file."); - public readonly Option ManifestOption = new("--manifest", "Path to API manifest file to locate and filter an OpenApiDocument"); - public readonly Option InlineLocalOption = new("--inline-local", "Inline local $ref instances"); - public readonly Option InlineExternalOption = new("--inline-external", "Inline external $ref instances"); - - public CommandOptions() - { - OpenApiDescriptionOption.AddAlias("-d"); - CsdlOption.AddAlias("--cs"); - CsdlFilterOption.AddAlias("--csf"); - OutputOption.AddAlias("-o"); - OutputFolderOption.AddAlias("--of"); - CleanOutputOption.AddAlias("--co"); - VersionOption.AddAlias("-v"); - MetadataVersionOption.AddAlias("--mv"); - FormatOption.AddAlias("-f"); - TerseOutputOption.AddAlias("--to"); - SettingsFileOption.AddAlias("--sp"); - LogLevelOption.AddAlias("--ll"); - FilterByOperationIdsOption.AddAlias("--op"); - FilterByTagsOption.AddAlias("--t"); - FilterByCollectionOption.AddAlias("-c"); - ManifestOption.AddAlias("-m"); - InlineLocalOption.AddAlias("--il"); - InlineExternalOption.AddAlias("--ie"); - } + public readonly Option OpenApiDescriptionOption = new("--openapi", "-d") + { + Description = "Input OpenAPI description file path or URL", + }; + public readonly Option CsdlOption = new("--csdl", "--cs") + { + Description = "Input CSDL file path or URL", + }; + public readonly Option CsdlFilterOption = new("--csdl-filter", "--csf") + { + Description = "Comma delimited list of EntitySets or Singletons to filter CSDL on. e.g. tasks,accounts", + }; + public readonly Option OutputOption = new("--output", "-o") + { + Description = "The output file path for the generated file.", + Arity = ArgumentArity.ZeroOrOne + }; + public readonly Option OutputFolderOption = new("--output-folder", "--of") + { + Description = "The output directory path for the generated files.", + Arity = ArgumentArity.ZeroOrOne + }; + public readonly Option CleanOutputOption = new("--clean-output", "--co") + { + Description = "Overwrite an existing file", + }; + public readonly Option VersionOption = new("--version", "-v") + { + Description = "OpenAPI specification version", + }; + public readonly Option MetadataVersionOption = new("--metadata-version", "--mv") + { + Description = "Graph metadata version to use.", + }; + public readonly Option FormatOption = new("--format", "-f") + { + Description = "File format", + }; + public readonly Option TerseOutputOption = new("--terse-output", "--to") + { + Description = "Produce terse json output", + }; + public readonly Option SettingsFileOption = new("--settings-path", "--sp") + { + Description = "The configuration file with CSDL conversion settings.", + }; + public readonly Option LogLevelOption = new("--log-level", "--ll") + { + Description = "The log level to use when logging messages to the main output.", + DefaultValueFactory = (_) => LogLevel.Information, + }; + public readonly Option FilterByOperationIdsOption = new("--filter-by-operationids", "--op") + { + Description = "Filters OpenApiDocument by comma delimited list of OperationId(s) provided", + }; + public readonly Option FilterByTagsOption = new("--filter-by-tags", "-t") + { + Description = "Filters OpenApiDocument by comma delimited list of Tag(s) provided. Also accepts a single regex.", + }; + public readonly Option FilterByCollectionOption = new("--filter-by-collection", "-c") + { + Description = "Filters OpenApiDocument by Postman collection provided. Provide path to collection file.", + }; + public readonly Option ManifestOption = new("--manifest", "-m") + { + Description = "Path to API manifest file to locate and filter an OpenApiDocument", + }; + public readonly Option InlineLocalOption = new("--inline-local", "--il") + { + Description = "Inline local $ref instances", + }; + public readonly Option InlineExternalOption = new("--inline-external", "--ie") + { + Description = "Inline external $ref instances", + }; public IReadOnlyList