From 22a32500ac53283bb0db681ec1f22a95091eb717 Mon Sep 17 00:00:00 2001 From: Mike Lorbetske Date: Wed, 31 Oct 2018 12:40:16 -0700 Subject: [PATCH] Fix several issues Fix #502 Fix #492 Fix #491 Fix #486 Improves #489 --- .../Commands/BaseHttpCommand.cs | 30 +++++++------ .../Commands/HelpCommand.cs | 2 +- .../Commands/SetBaseCommand.cs | 17 +++++++- .../Commands/SetSwaggerCommand.cs | 2 +- src/Microsoft.HttpRepl/DirectoryStructure.cs | 11 ++++- src/Microsoft.HttpRepl/HttpState.cs | 4 +- src/Microsoft.HttpRepl/IRequestInfo.cs | 2 +- .../Commanding/DefaultCommandDispatcher.cs | 2 +- .../Commanding/ICommandDispatcher.cs | 2 +- .../ConsoleHandling/ConsoleManager.cs | 42 +++++++++++-------- src/Microsoft.Repl/Input/InputManager.cs | 26 ++++++++---- .../Suggestions/SuggestionManager.cs | 4 +- 12 files changed, 93 insertions(+), 51 deletions(-) diff --git a/src/Microsoft.HttpRepl/Commands/BaseHttpCommand.cs b/src/Microsoft.HttpRepl/Commands/BaseHttpCommand.cs index a8ad52fc24..30191306e4 100644 --- a/src/Microsoft.HttpRepl/Commands/BaseHttpCommand.cs +++ b/src/Microsoft.HttpRepl/Commands/BaseHttpCommand.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -116,8 +117,18 @@ namespace Microsoft.HttpRepl.Commands bool deleteFile = false; noBody = commandInput.Options[NoBodyOption].Count > 0; + if (!thisRequestHeaders.TryGetValue("content-type", out string contentType) && programState.Headers.TryGetValue("content-type", out IEnumerable contentTypes)) + { + contentType = contentTypes.FirstOrDefault(); + } + if (!noBody) { + if (string.IsNullOrEmpty(contentType)) + { + contentType = "application/json"; + } + if (commandInput.Options[BodyFileOption].Count > 0) { filePath = commandInput.Options[BodyFileOption][0].Text; @@ -144,18 +155,7 @@ namespace Microsoft.HttpRepl.Commands deleteFile = true; filePath = Path.GetTempFileName(); - if (!thisRequestHeaders.TryGetValue("content-type", out string contentType) && programState.Headers.TryGetValue("content-type", out IEnumerable contentTypes)) - { - contentType = contentTypes.FirstOrDefault(); - } - - if (contentType == null) - { - contentType = "application/json"; - } - - string exampleBody = programState.GetExampleBody(commandInput.Arguments.Count > 0 ? commandInput.Arguments[0].Text : string.Empty, contentType, Verb); - request.Headers.TryAddWithoutValidation("Content-Type", contentType); + string exampleBody = programState.GetExampleBody(commandInput.Arguments.Count > 0 ? commandInput.Arguments[0].Text : string.Empty, ref contentType, Verb); if (!string.IsNullOrEmpty(exampleBody)) { @@ -179,6 +179,11 @@ namespace Microsoft.HttpRepl.Commands } } + if (string.IsNullOrEmpty(contentType)) + { + contentType = "application/json"; + } + byte[] data = noBody ? new byte[0] : string.IsNullOrEmpty(bodyContent) @@ -186,6 +191,7 @@ namespace Microsoft.HttpRepl.Commands : Encoding.UTF8.GetBytes(bodyContent); HttpContent content = new ByteArrayContent(data); + content.Headers.ContentType = new MediaTypeHeaderValue(contentType); request.Content = content; if (deleteFile) diff --git a/src/Microsoft.HttpRepl/Commands/HelpCommand.cs b/src/Microsoft.HttpRepl/Commands/HelpCommand.cs index 26e4468e8a..17dbef4d1f 100644 --- a/src/Microsoft.HttpRepl/Commands/HelpCommand.cs +++ b/src/Microsoft.HttpRepl/Commands/HelpCommand.cs @@ -218,7 +218,7 @@ namespace Microsoft.HttpRepl.Commands shellState.ConsoleManager.WriteLine($"{"HEAD",navCommandColumn}{"Issues a HEAD request."}"); shellState.ConsoleManager.WriteLine($"{"OPTIONS",navCommandColumn}{"Issues an OPTIONS request."}"); shellState.ConsoleManager.WriteLine(); - shellState.ConsoleManager.WriteLine($"{"set header",navCommandColumn}{"Sets or clears a header for all requests. e.g. `set header content-type:application/json`"}"); + shellState.ConsoleManager.WriteLine($"{"set header",navCommandColumn}{"Sets or clears a header for all requests. e.g. `set header content-type application/json`"}"); shellState.ConsoleManager.WriteLine(); shellState.ConsoleManager.WriteLine(); diff --git a/src/Microsoft.HttpRepl/Commands/SetBaseCommand.cs b/src/Microsoft.HttpRepl/Commands/SetBaseCommand.cs index aa12ec819e..70e829a891 100644 --- a/src/Microsoft.HttpRepl/Commands/SetBaseCommand.cs +++ b/src/Microsoft.HttpRepl/Commands/SetBaseCommand.cs @@ -53,7 +53,7 @@ namespace Microsoft.HttpRepl.Commands catch { } } - if (state.BaseAddress == null || !Uri.TryCreate(state.BaseAddress, "/swagger/v1/swagger.json", out Uri result)) + if (state.BaseAddress == null || !Uri.TryCreate(state.BaseAddress, "swagger.json", out Uri result)) { state.SwaggerStructure = null; } @@ -64,6 +64,21 @@ namespace Microsoft.HttpRepl.Commands { shellState.ConsoleManager.WriteLine("Using swagger metadata from " + result); } + else + { + if (state.BaseAddress == null || !Uri.TryCreate(state.BaseAddress, "swagger/v1/swagger.json", out result)) + { + state.SwaggerStructure = null; + } + else + { + await SetSwaggerCommand.CreateDirectoryStructureForSwaggerEndpointAsync(shellState, state, result, cancellationToken).ConfigureAwait(false); + if (state.SwaggerStructure != null) + { + shellState.ConsoleManager.WriteLine("Using swagger metadata from " + result); + } + } + } } } diff --git a/src/Microsoft.HttpRepl/Commands/SetSwaggerCommand.cs b/src/Microsoft.HttpRepl/Commands/SetSwaggerCommand.cs index 41c1564a99..53d3743d6d 100644 --- a/src/Microsoft.HttpRepl/Commands/SetSwaggerCommand.cs +++ b/src/Microsoft.HttpRepl/Commands/SetSwaggerCommand.cs @@ -47,7 +47,7 @@ namespace Microsoft.HttpRepl.Commands { if (string.IsNullOrEmpty(parameterSetsByContentType.Key)) { - dirRequestInfo.SetFallbackRequestBody(method, GetBodyString(null, parameterSetsByContentType.Value)); + dirRequestInfo.SetFallbackRequestBody(method, parameterSetsByContentType.Key, GetBodyString(null, parameterSetsByContentType.Value)); } dirRequestInfo.SetRequestBody(method, parameterSetsByContentType.Key, GetBodyString(parameterSetsByContentType.Key, parameterSetsByContentType.Value)); diff --git a/src/Microsoft.HttpRepl/DirectoryStructure.cs b/src/Microsoft.HttpRepl/DirectoryStructure.cs index 8787f962fc..7587327de0 100644 --- a/src/Microsoft.HttpRepl/DirectoryStructure.cs +++ b/src/Microsoft.HttpRepl/DirectoryStructure.cs @@ -55,13 +55,14 @@ namespace Microsoft.HttpRepl private readonly HashSet _methods = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _requestBodiesByMethodByContentType = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _fallbackBodyStringsByMethod = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _fallbackContentTypeStringsByMethod = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _contentTypesByMethod = new Dictionary>(StringComparer.OrdinalIgnoreCase); public IReadOnlyList Methods => _methods.ToList(); public IReadOnlyDictionary> ContentTypesByMethod => _contentTypesByMethod; - public string GetRequestBodyForContentType(string contentType, string method) + public string GetRequestBodyForContentType(ref string contentType, string method) { if (_requestBodiesByMethodByContentType.TryGetValue(method, out Dictionary bodiesByContentType) && bodiesByContentType.TryGetValue(contentType, out string body)) @@ -71,6 +72,11 @@ namespace Microsoft.HttpRepl if (_fallbackBodyStringsByMethod.TryGetValue(method, out body)) { + if (_fallbackContentTypeStringsByMethod.TryGetValue(method, out string newContentType)) + { + contentType = newContentType; + } + return body; } @@ -100,9 +106,10 @@ namespace Microsoft.HttpRepl _methods.Add(method); } - public void SetFallbackRequestBody(string method, string fallbackBodyString) + public void SetFallbackRequestBody(string method, string contentType, string fallbackBodyString) { _fallbackBodyStringsByMethod[method] = fallbackBodyString; + _fallbackContentTypeStringsByMethod[method] = contentType; } } } diff --git a/src/Microsoft.HttpRepl/HttpState.cs b/src/Microsoft.HttpRepl/HttpState.cs index 9104e4e02c..4ac8e09ed4 100644 --- a/src/Microsoft.HttpRepl/HttpState.cs +++ b/src/Microsoft.HttpRepl/HttpState.cs @@ -148,12 +148,12 @@ namespace Microsoft.HttpRepl } } - public string GetExampleBody(string path, string contentType, string method) + public string GetExampleBody(string path, ref string contentType, string method) { Uri effectivePath = GetEffectivePath(path); string rootRelativePath = effectivePath.LocalPath.Substring(BaseAddress.LocalPath.Length).TrimStart('/'); IDirectoryStructure structure = SwaggerStructure?.TraverseTo(rootRelativePath); - return structure?.RequestInfo?.GetRequestBodyForContentType(contentType, method); + return structure?.RequestInfo?.GetRequestBodyForContentType(ref contentType, method); } public IEnumerable GetApplicableContentTypes(string method, string path) diff --git a/src/Microsoft.HttpRepl/IRequestInfo.cs b/src/Microsoft.HttpRepl/IRequestInfo.cs index 24054f6142..d420d36d2a 100644 --- a/src/Microsoft.HttpRepl/IRequestInfo.cs +++ b/src/Microsoft.HttpRepl/IRequestInfo.cs @@ -11,6 +11,6 @@ namespace Microsoft.HttpRepl IReadOnlyList Methods { get; } - string GetRequestBodyForContentType(string contentType, string method); + string GetRequestBodyForContentType(ref string contentType, string method); } } diff --git a/src/Microsoft.Repl/Commanding/DefaultCommandDispatcher.cs b/src/Microsoft.Repl/Commanding/DefaultCommandDispatcher.cs index 22be314827..bdd9426868 100644 --- a/src/Microsoft.Repl/Commanding/DefaultCommandDispatcher.cs +++ b/src/Microsoft.Repl/Commanding/DefaultCommandDispatcher.cs @@ -79,7 +79,7 @@ namespace Microsoft.Repl.Commanding public IParser Parser => _parser; - public IReadOnlyList CollectSuggesetions(IShellState shellState) + public IReadOnlyList CollectSuggestions(IShellState shellState) { string line = shellState.InputManager.GetCurrentBuffer(); TParseResult parseResult = _parser.Parse(line, shellState.ConsoleManager.CaretPosition); diff --git a/src/Microsoft.Repl/Commanding/ICommandDispatcher.cs b/src/Microsoft.Repl/Commanding/ICommandDispatcher.cs index d2d9000123..b02dd830a7 100644 --- a/src/Microsoft.Repl/Commanding/ICommandDispatcher.cs +++ b/src/Microsoft.Repl/Commanding/ICommandDispatcher.cs @@ -12,7 +12,7 @@ namespace Microsoft.Repl.Commanding { IParser Parser { get; } - IReadOnlyList CollectSuggesetions(IShellState shellState); + IReadOnlyList CollectSuggestions(IShellState shellState); void OnReady(IShellState shellState); diff --git a/src/Microsoft.Repl/ConsoleHandling/ConsoleManager.cs b/src/Microsoft.Repl/ConsoleHandling/ConsoleManager.cs index ad4231ad5e..b6d471ad52 100644 --- a/src/Microsoft.Repl/ConsoleHandling/ConsoleManager.cs +++ b/src/Microsoft.Repl/ConsoleHandling/ConsoleManager.cs @@ -50,37 +50,41 @@ namespace Microsoft.Repl.ConsoleHandling return; } + int bufferWidth = Console.BufferWidth; + int cursorTop = Console.CursorTop; + int cursorLeft = Console.CursorLeft; + while (positions < 0 && CaretPosition > 0) { - if (-positions > Console.BufferWidth) + if (-positions > bufferWidth) { - if (Console.CursorTop == 0) + if (cursorTop == 0) { - Console.CursorLeft = 0; + cursorLeft = 0; positions = 0; } else { - positions += Console.BufferWidth; - --Console.CursorTop; + positions += bufferWidth; + --cursorTop; } } else { - int remaining = Console.CursorLeft + positions; + int remaining = cursorLeft + positions; if (remaining >= 0) { - Console.CursorLeft = remaining; + cursorLeft = remaining; } - else if (Console.CursorTop == 0) + else if (cursorTop == 0) { - Console.CursorLeft = 0; + cursorLeft = 0; } else { - --Console.CursorTop; - Console.CursorLeft = Console.BufferWidth + remaining; + --cursorTop; + cursorLeft = bufferWidth + remaining; } positions = 0; @@ -89,27 +93,29 @@ namespace Microsoft.Repl.ConsoleHandling while (positions > 0) { - if (positions > Console.BufferWidth) + if (positions > bufferWidth) { - positions -= Console.BufferWidth; - ++Console.CursorTop; + positions -= bufferWidth; + ++cursorTop; } else { - int spaceLeftOnLine = Console.BufferWidth - Console.CursorLeft - 1; + int spaceLeftOnLine = bufferWidth - cursorLeft - 1; if (positions > spaceLeftOnLine) { - ++Console.CursorTop; - Console.CursorLeft = positions - spaceLeftOnLine - 1; + ++cursorTop; + cursorLeft = positions - spaceLeftOnLine - 1; } else { - Console.CursorLeft += positions; + cursorLeft += positions; } positions = 0; } } + + Console.SetCursorPosition(cursorLeft, cursorTop); } } diff --git a/src/Microsoft.Repl/Input/InputManager.cs b/src/Microsoft.Repl/Input/InputManager.cs index cd1157c6ff..b35633ad4e 100644 --- a/src/Microsoft.Repl/Input/InputManager.cs +++ b/src/Microsoft.Repl/Input/InputManager.cs @@ -112,14 +112,22 @@ namespace Microsoft.Repl.Input private void StashEchoState() { - _ttyState = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX) - ? GetTtyState() - : null; - - if (!string.IsNullOrEmpty(_ttyState)) + string sttyFlags = null; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) { - //"gfmt1:cflag=4300:iflag=6b02:lflag=200005c7:oflag=3:discard=f:dsusp=19:eof=4:eol=ff:eol2=ff:erase=7f:intr=3:kill=15:lnext=16:min=1:quit=1c:reprint=12:start=11:status=14:stop=13:susp=1a:time=0:werase=17:ispeed=38400:ospeed=38400\n" - ProcessStartInfo psi = new ProcessStartInfo("stty", "gfmt1:erase=08:werase=08 -echo"); + _ttyState = GetTtyState(); + sttyFlags = "gfmt1:erase=08:werase=08 -echo"; + } + //If it's any of the ubuntu variants on 18.x, stty tweaks are required + else if (System.Runtime.InteropServices.RuntimeInformation.OSDescription.IndexOf("buntu", StringComparison.OrdinalIgnoreCase) > -1) + { + _ttyState = GetTtyState(); + sttyFlags = "erase 0x08 werase 0x08 -echo"; + } + + if (!string.IsNullOrEmpty(sttyFlags)) + { + ProcessStartInfo psi = new ProcessStartInfo("stty", sttyFlags); Process p = Process.Start(psi); p?.WaitForExit(); } @@ -133,7 +141,7 @@ namespace Microsoft.Repl.Input }; Process p = Process.Start(psi); p?.WaitForExit(); - string result = p?.StandardOutput.ReadToEnd(); + string result = p?.StandardOutput.ReadToEnd().Trim(); return result; } @@ -331,7 +339,7 @@ namespace Microsoft.Repl.Input } private void FlushInput(IShellState state, ref List presses) - { + { string str = new string(presses.Select(x => x.KeyChar).ToArray()); if (state.ConsoleManager.CaretPosition == _inputBuffer.Count) diff --git a/src/Microsoft.Repl/Suggestions/SuggestionManager.cs b/src/Microsoft.Repl/Suggestions/SuggestionManager.cs index ba68ee9d46..5080e6ae6f 100644 --- a/src/Microsoft.Repl/Suggestions/SuggestionManager.cs +++ b/src/Microsoft.Repl/Suggestions/SuggestionManager.cs @@ -36,7 +36,7 @@ namespace Microsoft.Repl.Suggestions else { _currentSuggestion = 0; - _suggestions = shellState.CommandDispatcher.CollectSuggesetions(shellState); + _suggestions = shellState.CommandDispatcher.CollectSuggestions(shellState); if (_suggestions == null || _suggestions.Count == 0) { @@ -76,7 +76,7 @@ namespace Microsoft.Repl.Suggestions } else { - _suggestions = shellState.CommandDispatcher.CollectSuggesetions(shellState); + _suggestions = shellState.CommandDispatcher.CollectSuggestions(shellState); _currentSuggestion = _suggestions.Count - 1; if (_suggestions == null || _suggestions.Count == 0)