From 74db9654796f39072111b415ce214e03c8301461 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 14 Dec 2022 12:06:14 +0300 Subject: [PATCH 1/7] Enable termination of transform process --- src/Microsoft.OpenApi.Hidi/Program.cs | 35 +++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/Program.cs b/src/Microsoft.OpenApi.Hidi/Program.cs index 71e9e0d00..f8934a0f4 100644 --- a/src/Microsoft.OpenApi.Hidi/Program.cs +++ b/src/Microsoft.OpenApi.Hidi/Program.cs @@ -1,15 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.CommandLine; -using System.CommandLine.Builder; -using System.CommandLine.Hosting; using System.CommandLine.Parsing; - +using System.Diagnostics; using System.IO; +using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Hidi.Handlers; @@ -19,6 +17,9 @@ static class Program { static async Task Main(string[] args) { + // subscribe to CancelKeyPress event to listen for termination requests from users through Ctrl+C or Ctrl+Break keys + Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPressEvent); + var rootCommand = new RootCommand() { }; @@ -127,5 +128,29 @@ static async Task Main(string[] args) //// Wait for logger to write messages to the console before exiting await Task.Delay(10); } + + /// + /// This event is raised when the user presses either of the two breaking key combinations: Ctrl+C or Ctrl+Break keys. + /// + /// + /// + private static void Console_CancelKeyPressEvent(object sender, ConsoleCancelEventArgs eventArgs) + { + if ((eventArgs.SpecialKey == ConsoleSpecialKey.ControlC) || (eventArgs.SpecialKey == ConsoleSpecialKey.ControlBreak)) + { + Console.WriteLine("CTRL+C pressed, aborting current process..."); + Thread.Sleep(5000); + + if (Process.GetCurrentProcess().HasExited) + { + Console.WriteLine("Process has already exited."); + } + else + { + Console.WriteLine("Process has not exited, attempting to kill process..."); + Process.GetCurrentProcess().Kill(); + } + } + } } } From 7867fdd569c13969fe34345c9a81f09133a7adde Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 19 Dec 2022 13:46:34 +0300 Subject: [PATCH 2/7] Use an IConsole instance to register and handle cancellation when CTRL+C is pressed --- src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs b/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs index 696837d3f..c0af09728 100644 --- a/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs +++ b/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs @@ -50,6 +50,8 @@ public async Task InvokeAsync(InvocationContext context) string filterbyoperationids = context.ParseResult.GetValueForOption(FilterByOperationIdsOption); string filterbytags = context.ParseResult.GetValueForOption(FilterByTagsOption); string filterbycollection = context.ParseResult.GetValueForOption(FilterByCollectionOption); + + var console = context.Console; CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); using var loggerFactory = Logger.ConfigureLogger(logLevel); From 6c1e502616c4338e17f7d818d802b66a6220599a Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 19 Dec 2022 13:47:32 +0300 Subject: [PATCH 3/7] Pass cancellation token to the conversion method and degrade gracefully when an operation is terminated --- src/Microsoft.OpenApi.Hidi/OpenApiService.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index 56dda4d9a..dbcdf5457 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -102,7 +102,7 @@ CancellationToken cancellationToken stream.Position = 0; } - document = await ConvertCsdlToOpenApi(stream, settingsFile); + document = await ConvertCsdlToOpenApi(stream, cancellationToken, settingsFile); stopwatch.Stop(); logger.LogTrace("{timestamp}ms: Generated OpenAPI with {paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count); } @@ -216,6 +216,10 @@ CancellationToken cancellationToken textWriter.Flush(); } } + catch(TaskCanceledException) + { + Console.Error.WriteLine("CTRL+C pressed, aborting the operation."); + } catch (Exception ex) { throw new InvalidOperationException($"Could not transform the document, reason: {ex.Message}", ex); @@ -324,12 +328,12 @@ internal static IConfiguration GetConfiguration(string settingsFile) /// /// The CSDL stream. /// An OpenAPI document. - public static async Task ConvertCsdlToOpenApi(Stream csdl, string settingsFile = null) + public static async Task ConvertCsdlToOpenApi(Stream csdl, CancellationToken token, string settingsFile = null) { using var reader = new StreamReader(csdl); - var csdlText = await reader.ReadToEndAsync(); + var csdlText = await reader.ReadToEndAsync(token); var edmModel = CsdlReader.Parse(XElement.Parse(csdlText).CreateReader()); - + var config = GetConfiguration(settingsFile); var settings = new OpenApiConvertSettings() { @@ -353,9 +357,8 @@ public static async Task ConvertCsdlToOpenApi(Stream csdl, stri EnableTypeDisambiguationForDefaultValueOfOdataTypeProperty = true }; config.GetSection("OpenApiConvertSettings").Bind(settings); - - OpenApiDocument document = edmModel.ConvertToOpenApi(settings); + OpenApiDocument document = edmModel.ConvertToOpenApi(settings); document = FixReferences(document); return document; From 5d8ef7f208fd379693b57b570915bce05aea69b1 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 19 Dec 2022 13:47:44 +0300 Subject: [PATCH 4/7] Clean up code --- src/Microsoft.OpenApi.Hidi/Program.cs | 34 +++------------------------ 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/Program.cs b/src/Microsoft.OpenApi.Hidi/Program.cs index f8934a0f4..e9246eb6c 100644 --- a/src/Microsoft.OpenApi.Hidi/Program.cs +++ b/src/Microsoft.OpenApi.Hidi/Program.cs @@ -16,12 +16,8 @@ namespace Microsoft.OpenApi.Hidi static class Program { static async Task Main(string[] args) - { - // subscribe to CancelKeyPress event to listen for termination requests from users through Ctrl+C or Ctrl+Break keys - Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPressEvent); - - var rootCommand = new RootCommand() { - }; + { + var rootCommand = new RootCommand() {}; // command option parameters and aliases var descriptionOption = new Option("--openapi", "Input OpenAPI description file path or URL"); @@ -121,36 +117,12 @@ static async Task Main(string[] args) rootCommand.Add(transformCommand); rootCommand.Add(validateCommand); - + // Parse the incoming args and invoke the handler await rootCommand.InvokeAsync(args); //// Wait for logger to write messages to the console before exiting await Task.Delay(10); - } - - /// - /// This event is raised when the user presses either of the two breaking key combinations: Ctrl+C or Ctrl+Break keys. - /// - /// - /// - private static void Console_CancelKeyPressEvent(object sender, ConsoleCancelEventArgs eventArgs) - { - if ((eventArgs.SpecialKey == ConsoleSpecialKey.ControlC) || (eventArgs.SpecialKey == ConsoleSpecialKey.ControlBreak)) - { - Console.WriteLine("CTRL+C pressed, aborting current process..."); - Thread.Sleep(5000); - - if (Process.GetCurrentProcess().HasExited) - { - Console.WriteLine("Process has already exited."); - } - else - { - Console.WriteLine("Process has not exited, attempting to kill process..."); - Process.GetCurrentProcess().Kill(); - } - } } } } From 05256ddbc71359739b845bc71dd315f44bb50a57 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 19 Dec 2022 14:01:37 +0300 Subject: [PATCH 5/7] Fix failing tests --- .../Services/OpenApiServiceTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs index c2fb3798f..70b222750 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs @@ -20,7 +20,7 @@ public async Task ReturnConvertedCSDLFile() var csdlStream = fileInput.OpenRead(); // Act - var openApiDoc = await OpenApiService.ConvertCsdlToOpenApi(csdlStream); + var openApiDoc = await OpenApiService.ConvertCsdlToOpenApi(csdlStream, CancellationToken.None); var expectedPathCount = 5; // Assert @@ -39,9 +39,9 @@ public async Task ReturnFilteredOpenApiDocBasedOnOperationIdsAndInputCsdlDocumen var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles\\Todo.xml"); var fileInput = new FileInfo(filePath); var csdlStream = fileInput.OpenRead(); - + // Act - var openApiDoc = await OpenApiService.ConvertCsdlToOpenApi(csdlStream); + var openApiDoc = await OpenApiService.ConvertCsdlToOpenApi(csdlStream, CancellationToken.None); var predicate = OpenApiFilterService.CreatePredicate(operationIds, tags); var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(openApiDoc, predicate); From 214774b07889286f9019830465a9fbd8a8696450 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 19 Dec 2022 08:18:17 -0500 Subject: [PATCH 6/7] Update src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs --- src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs b/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs index c0af09728..e46b34340 100644 --- a/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs +++ b/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs @@ -51,7 +51,6 @@ public async Task InvokeAsync(InvocationContext context) string filterbytags = context.ParseResult.GetValueForOption(FilterByTagsOption); string filterbycollection = context.ParseResult.GetValueForOption(FilterByCollectionOption); - var console = context.Console; CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); using var loggerFactory = Logger.ConfigureLogger(logLevel); From 4a163a6ea3701734989ad270d7e5071e95c5317e Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 19 Dec 2022 19:12:18 +0300 Subject: [PATCH 7/7] Code clean up --- src/Microsoft.OpenApi.Hidi/OpenApiService.cs | 4 ++-- .../Services/OpenApiServiceTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index dbcdf5457..60bba4aef 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -102,7 +102,7 @@ CancellationToken cancellationToken stream.Position = 0; } - document = await ConvertCsdlToOpenApi(stream, cancellationToken, settingsFile); + document = await ConvertCsdlToOpenApi(stream, settingsFile, cancellationToken); stopwatch.Stop(); logger.LogTrace("{timestamp}ms: Generated OpenAPI with {paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count); } @@ -328,7 +328,7 @@ internal static IConfiguration GetConfiguration(string settingsFile) /// /// The CSDL stream. /// An OpenAPI document. - public static async Task ConvertCsdlToOpenApi(Stream csdl, CancellationToken token, string settingsFile = null) + public static async Task ConvertCsdlToOpenApi(Stream csdl, string settingsFile = null, CancellationToken token = default) { using var reader = new StreamReader(csdl); var csdlText = await reader.ReadToEndAsync(token); diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs index 70b222750..3d764b4fb 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs @@ -20,7 +20,7 @@ public async Task ReturnConvertedCSDLFile() var csdlStream = fileInput.OpenRead(); // Act - var openApiDoc = await OpenApiService.ConvertCsdlToOpenApi(csdlStream, CancellationToken.None); + var openApiDoc = await OpenApiService.ConvertCsdlToOpenApi(csdlStream); var expectedPathCount = 5; // Assert @@ -41,7 +41,7 @@ public async Task ReturnFilteredOpenApiDocBasedOnOperationIdsAndInputCsdlDocumen var csdlStream = fileInput.OpenRead(); // Act - var openApiDoc = await OpenApiService.ConvertCsdlToOpenApi(csdlStream, CancellationToken.None); + var openApiDoc = await OpenApiService.ConvertCsdlToOpenApi(csdlStream); var predicate = OpenApiFilterService.CreatePredicate(operationIds, tags); var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(openApiDoc, predicate);