Respond to much of the feedback from review
Default to requerying swagger for each exec Auto-set the content-type if not yet set for requests based on swagger Fix swagger UI command Make error and warning colors configurable Allow aliases to be specified in structured input based commands Have delete not require a body Show allowed methods for . and .. Show verbs on the announced path after CD Issue a request on set base and warn if there's a socket error
This commit is contained in:
parent
8a9b407761
commit
43c74cfa53
|
|
@ -32,7 +32,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
private const string BodyFileOption = nameof(BodyFileOption);
|
||||
private const string NoBodyOption = nameof(NoBodyOption);
|
||||
private const string NoFormattingOption = nameof(NoFormattingOption);
|
||||
private const string NoStreamingOption = nameof(NoStreamingOption);
|
||||
private const string StreamingOption = nameof(StreamingOption);
|
||||
private const string BodyContentOption = nameof(BodyContentOption);
|
||||
private static readonly char[] HeaderSeparatorChars = new[] { '=', ':' };
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
.WithOption(new CommandOptionSpecification(ResponseHeadersFileOption, requiresValue: true, maximumOccurrences: 1, forms: new[] { "--response:headers", }))
|
||||
.WithOption(new CommandOptionSpecification(ResponseBodyFileOption, requiresValue: true, maximumOccurrences: 1, forms: new[] { "--response:body", }))
|
||||
.WithOption(new CommandOptionSpecification(NoFormattingOption, maximumOccurrences: 1, forms: new[] { "--no-formatting", "-F" }))
|
||||
.WithOption(new CommandOptionSpecification(NoStreamingOption, maximumOccurrences: 1, forms: new[] { "--no-streaming", "-S" }));
|
||||
.WithOption(new CommandOptionSpecification(StreamingOption, maximumOccurrences: 1, forms: new[] { "--streaming", "-s" }));
|
||||
|
||||
if (RequiresBody)
|
||||
{
|
||||
|
|
@ -76,10 +76,20 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
if (programState.BaseAddress == null && (commandInput.Arguments.Count == 0 || !Uri.TryCreate(commandInput.Arguments[0].Text, UriKind.Absolute, out Uri _)))
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("'set base {url}' must be called before issuing requests to a relative path".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("'set base {url}' must be called before issuing requests to a relative path".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
if (programState.SwaggerEndpoint != null)
|
||||
{
|
||||
string swaggerRequeryBehaviorSetting = programState.GetStringPreference(WellKnownPreference.SwaggerRequeryBehavior, "auto");
|
||||
|
||||
if (swaggerRequeryBehaviorSetting.StartsWith("auto", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await SetSwaggerCommand.CreateDirectoryStructureForSwaggerEndpointAsync(shellState, programState, programState.SwaggerEndpoint, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, string> thisRequestHeaders = new Dictionary<string, string>();
|
||||
|
||||
foreach (InputElement header in commandInput.Options[HeaderOption])
|
||||
|
|
@ -88,7 +98,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (equalsIndex < 0)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Headers must be formatted as {header}={value} or {header}:{value}".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Headers must be formatted as {header}={value} or {header}:{value}".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +124,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine($"Content file {filePath} does not exist".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine($"Content file {filePath} does not exist".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +137,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
string defaultEditorCommand = programState.GetStringPreference(WellKnownPreference.DefaultEditorCommand);
|
||||
if (defaultEditorCommand == null)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine($"The default editor must be configured using the command `pref set {WellKnownPreference.DefaultEditorCommand} \"{{commandline}}\"`".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine($"The default editor must be configured using the command `pref set {WellKnownPreference.DefaultEditorCommand} \"{{commandline}}\"`".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +155,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
}
|
||||
|
||||
string exampleBody = programState.GetExampleBody(commandInput.Arguments.Count > 0 ? commandInput.Arguments[0].Text : string.Empty, contentType, Verb);
|
||||
request.Headers.TryAddWithoutValidation("Content-Type", contentType);
|
||||
|
||||
if (!string.IsNullOrEmpty(exampleBody))
|
||||
{
|
||||
|
|
@ -224,7 +235,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
string method = response.RequestMessage.Method.ToString().ToUpperInvariant().SetColor(requestConfig.MethodColor);
|
||||
string pathAndQuery = response.RequestMessage.RequestUri.PathAndQuery.SetColor(requestConfig.AddressColor);
|
||||
protocolInfo = $"{"HTTP".SetColor(requestConfig.ProtocolNameColor)}{"/".SetColor(requestConfig.ProtocolSeparatorColor)}{response.RequestMessage.Version.ToString().SetColor(requestConfig.ProtocolVersionColor)}";
|
||||
protocolInfo = $"{"HTTP".SetColor(requestConfig.ProtocolNameColor)}{"/".SetColor(requestConfig.ProtocolSeparatorColor)}{response.Version.ToString().SetColor(requestConfig.ProtocolVersionColor)}";
|
||||
|
||||
consoleManager.WriteLine($"{method} {pathAndQuery} {protocolInfo}");
|
||||
IEnumerable<KeyValuePair<string, IEnumerable<string>>> requestHeaders = response.RequestMessage.Headers;
|
||||
|
|
@ -327,6 +338,42 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
private static async Task FormatBodyAsync(DefaultCommandInput<ICoreParseResult> commandInput, HttpState programState, IConsoleManager consoleManager, HttpContent content, StreamWriter bodyFileWriter, CancellationToken cancellationToken)
|
||||
{
|
||||
if (commandInput.Options[StreamingOption].Count > 0)
|
||||
{
|
||||
Memory<char> buffer = new Memory<char>(new char[2048]);
|
||||
Stream s = await content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
StreamReader reader = new StreamReader(s);
|
||||
consoleManager.WriteLine("Streaming the response, press any key to stop...".SetColor(programState.WarningColor));
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
ValueTask<int> readTask = reader.ReadAsync(buffer, cancellationToken);
|
||||
if (await WaitForCompletionAsync(readTask, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
if (readTask.Result == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
string str = new string(buffer.Span.Slice(0, readTask.Result));
|
||||
consoleManager.Write(str);
|
||||
bodyFileWriter.Write(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
string contentType = null;
|
||||
if (content.Headers.TryGetValues("Content-Type", out IEnumerable<string> contentTypeValues))
|
||||
{
|
||||
|
|
@ -363,43 +410,6 @@ namespace Microsoft.HttpRepl.Commands
|
|||
}
|
||||
}
|
||||
|
||||
//Unless the user has explicitly specified to not stream the response, if we don't have content length, assume streaming
|
||||
if (commandInput.Options[NoStreamingOption].Count == 0 && !content.Headers.TryGetValues("Content-Length", out IEnumerable<string> _))
|
||||
{
|
||||
Memory<char> buffer = new Memory<char>(new char[2048]);
|
||||
Stream s = await content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
StreamReader reader = new StreamReader(s);
|
||||
consoleManager.WriteLine("Streaming the response, press any key to stop...".Bold().Yellow());
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
ValueTask<int> readTask = reader.ReadAsync(buffer, cancellationToken);
|
||||
if (await WaitForCompletionAsync(readTask, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
if (readTask.Result == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
string str = new string(buffer.Span.Slice(0, readTask.Result));
|
||||
consoleManager.Write(str);
|
||||
bodyFileWriter.Write(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
string responseContent = await content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
bodyFileWriter.WriteLine(responseContent);
|
||||
consoleManager.WriteLine(responseContent);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,13 @@ namespace Microsoft.HttpRepl.Commands
|
|||
}
|
||||
}
|
||||
|
||||
shellState.ConsoleManager.WriteLine($"/{string.Join("/", programState.PathSections.Reverse())}");
|
||||
IDirectoryStructure s = programState.Structure.TraverseTo(programState.PathSections.Reverse());
|
||||
|
||||
string thisDirMethod = s.RequestInfo != null && s.RequestInfo.Methods.Count > 0
|
||||
? "[" + string.Join("|", s.RequestInfo.Methods) + "]"
|
||||
: "[]";
|
||||
|
||||
shellState.ConsoleManager.WriteLine($"/{string.Join("/", programState.PathSections.Reverse())} {thisDirMethod}");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
if (programState.BaseAddress == null)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Must be connected to a server to query configuration".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Must be connected to a server to query configuration".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(programState.DiagnosticsState.DiagnosticsEndpoint))
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Diagnostics endpoint must be set to query configuration (see set diag)".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Diagnostics endpoint must be set to query configuration (see set diag)".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (configUrl == null)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Diagnostics endpoint does not expose configuration information".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Diagnostics endpoint does not expose configuration information".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Unable to get configuration information from diagnostics endpoint".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Unable to get configuration information from diagnostics endpoint".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
protected override string Verb => "delete";
|
||||
|
||||
protected override bool RequiresBody => true;
|
||||
protected override bool RequiresBody => false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
if (commandInput.Arguments.Count == 0 || !_allowedModes.Contains(commandInput.Arguments[0]?.Text))
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Allowed echo modes are 'on' and 'off'".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Allowed echo modes are 'on' and 'off'".SetColor(programState.ErrorColor));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.HttpRepl.Preferences;
|
||||
using Microsoft.HttpRepl.Suggestions;
|
||||
using Microsoft.Repl;
|
||||
using Microsoft.Repl.Commanding;
|
||||
|
|
@ -24,7 +25,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
: null;
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(IShellState shellState, HttpState programState, ICoreParseResult parseResult, CancellationToken cancellationToken)
|
||||
public async Task ExecuteAsync(IShellState shellState, HttpState programState, ICoreParseResult parseResult, CancellationToken cancellationToken)
|
||||
{
|
||||
if (shellState.CommandDispatcher is ICommandDispatcher<HttpState, ICoreParseResult> dispatcher)
|
||||
{
|
||||
|
|
@ -55,6 +56,17 @@ namespace Microsoft.HttpRepl.Commands
|
|||
//Maybe the input is an URL
|
||||
if (parseResult.Sections.Count == 2)
|
||||
{
|
||||
|
||||
if (programState.SwaggerEndpoint != null)
|
||||
{
|
||||
string swaggerRequeryBehaviorSetting = programState.GetStringPreference(WellKnownPreference.SwaggerRequeryBehavior, "auto");
|
||||
|
||||
if (swaggerRequeryBehaviorSetting.StartsWith("auto", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await SetSwaggerCommand.CreateDirectoryStructureForSwaggerEndpointAsync(shellState, programState, programState.SwaggerEndpoint, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
IDirectoryStructure structure = programState.Structure.TraverseTo(parseResult.Sections[1]);
|
||||
if (structure.DirectoryNames.Any())
|
||||
{
|
||||
|
|
@ -101,8 +113,6 @@ namespace Microsoft.HttpRepl.Commands
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string GetHelpDetails(IShellState shellState, HttpState programState, ICoreParseResult parseResult)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Globalization;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.HttpRepl.Preferences;
|
||||
using Microsoft.Repl;
|
||||
using Microsoft.Repl.Commanding;
|
||||
using Microsoft.Repl.Parsing;
|
||||
|
|
@ -17,11 +18,21 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
private const string RecursiveOption = nameof(RecursiveOption);
|
||||
|
||||
protected override Task ExecuteAsync(IShellState shellState, HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult, CancellationToken cancellationToken)
|
||||
protected override async Task ExecuteAsync(IShellState shellState, HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult, CancellationToken cancellationToken)
|
||||
{
|
||||
if (programState.SwaggerEndpoint != null)
|
||||
{
|
||||
string swaggerRequeryBehaviorSetting = programState.GetStringPreference(WellKnownPreference.SwaggerRequeryBehavior, "auto");
|
||||
|
||||
if (swaggerRequeryBehaviorSetting.StartsWith("auto", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await SetSwaggerCommand.CreateDirectoryStructureForSwaggerEndpointAsync(shellState, programState, programState.SwaggerEndpoint, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (programState.Structure == null || programState.BaseAddress == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
return;
|
||||
}
|
||||
|
||||
string path = commandInput.Arguments.Count > 0 ? commandInput.Arguments[0].Text : string.Empty;
|
||||
|
|
@ -29,19 +40,27 @@ namespace Microsoft.HttpRepl.Commands
|
|||
//If it's an absolute URI, nothing to suggest
|
||||
if (Uri.TryCreate(path, UriKind.Absolute, out Uri _))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
return;
|
||||
}
|
||||
|
||||
IDirectoryStructure s = programState.Structure.TraverseTo(programState.PathSections.Reverse()).TraverseTo(path);
|
||||
|
||||
string thisDirMethod = s.RequestInfo != null && s.RequestInfo.Methods.Count > 0
|
||||
? "[" + string.Join("|", s.RequestInfo.Methods) + "]"
|
||||
: "[]";
|
||||
|
||||
List<TreeNode> roots = new List<TreeNode>();
|
||||
Formatter formatter = new Formatter();
|
||||
|
||||
roots.Add(new TreeNode(formatter, ".", string.Empty));
|
||||
roots.Add(new TreeNode(formatter, ".", thisDirMethod));
|
||||
|
||||
if (s.Parent != null)
|
||||
{
|
||||
roots.Add(new TreeNode(formatter, "..", string.Empty));
|
||||
string parentDirMethod = s.Parent.RequestInfo != null && s.Parent.RequestInfo.Methods.Count > 0
|
||||
? "[" + string.Join("|", s.Parent.RequestInfo.Methods) + "]"
|
||||
: "[]";
|
||||
|
||||
roots.Add(new TreeNode(formatter, "..", parentDirMethod));
|
||||
}
|
||||
|
||||
int recursionDepth = 1;
|
||||
|
|
@ -75,8 +94,6 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
shellState.ConsoleManager.WriteLine(node.ToString());
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static void Recurse(TreeNode parentNode, IDirectoryStructure parent, int remainingDepth)
|
||||
|
|
@ -101,7 +118,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
|
||||
|
||||
protected override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("ls")
|
||||
protected override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("ls").AlternateName("dir")
|
||||
.MaximumArgCount(1)
|
||||
.WithOption(new CommandOptionSpecification(RecursiveOption, maximumOccurrences: 1, acceptsValue: true, forms: new[] {"-r", "--recursive"}))
|
||||
.Finish();
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (!programState.SavePreferences())
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Error saving preferences".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Error saving preferences".SetColor(programState.ErrorColor));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
@ -109,7 +109,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
}
|
||||
else
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine((commandInput.Arguments[1].Text + " does not have a configured value").Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine((commandInput.Arguments[1].Text + " does not have a configured value").SetColor(programState.ErrorColor));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Repl;
|
||||
|
|
@ -34,11 +36,20 @@ namespace Microsoft.HttpRepl.Commands
|
|||
}
|
||||
else if (parseResult.Sections.Count != 3 || string.IsNullOrEmpty(parseResult.Sections[2]) || !Uri.TryCreate(EnsureTrailingSlash(parseResult.Sections[2]), UriKind.Absolute, out Uri serverUri))
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Must specify a server".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Must specify a server".SetColor(state.ErrorColor));
|
||||
}
|
||||
else
|
||||
{
|
||||
state.BaseAddress = serverUri;
|
||||
try
|
||||
{
|
||||
await state.Client.SendAsync(new HttpRequestMessage(HttpMethod.Head, serverUri)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) when (ex.InnerException is SocketException se)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine($"Warning: HEAD request to the specified address was unsuccessful ({se.Message})".SetColor(state.WarningColor));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (state.BaseAddress == null || !Uri.TryCreate(state.BaseAddress, "/swagger/v1/swagger.json", out Uri result))
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (parseResult.Sections.Count != 3 || string.IsNullOrEmpty(parseResult.Sections[2]) || !Uri.TryCreate(parseResult.Sections[2], UriKind.Relative, out Uri _))
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Must specify a relative path".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Must specify a relative path".SetColor(programState.ErrorColor));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -50,7 +50,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Unable to access diagnostics endpoint".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Unable to access diagnostics endpoint".SetColor(programState.ErrorColor));
|
||||
programState.DiagnosticsState.DiagnosticsEndpoint = null;
|
||||
programState.DiagnosticsState.DiagnosticItems = null;
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (!endpointsResponse.IsSuccessStatusCode)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Unable to get endpoints information from diagnostics endpoint".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Unable to get endpoints information from diagnostics endpoint".SetColor(programState.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (parseResult.Sections.Count != 3 || string.IsNullOrEmpty(parseResult.Sections[2]) || !Uri.TryCreate(parseResult.Sections[2], UriKind.Absolute, out Uri serverUri))
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Must specify a swagger document".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Must specify a swagger document".SetColor(programState.ErrorColor));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -253,6 +253,8 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
internal static async Task CreateDirectoryStructureForSwaggerEndpointAsync(IShellState shellState, HttpState programState, Uri serverUri, CancellationToken cancellationToken)
|
||||
{
|
||||
programState.SwaggerEndpoint = serverUri;
|
||||
|
||||
try
|
||||
{
|
||||
IEnumerable<EndpointMetadata> doc = await GetSwaggerDocAsync(programState.Client, serverUri).ConfigureAwait(false);
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
if (programState.BaseAddress == null)
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("Must be connected to a server to launch Swagger UI".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("Must be connected to a server to launch Swagger UI".SetColor(programState.ErrorColor));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Uri uri = new Uri(programState.BaseAddress, "swagger");
|
||||
string agent = "cmd";
|
||||
string agentParam = $"/c {uri.AbsoluteUri}";
|
||||
string agentParam = $"/c start {uri.AbsoluteUri}";
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Net.Http;
|
|||
using System.Runtime.InteropServices;
|
||||
using Microsoft.HttpRepl.Diagnostics;
|
||||
using Microsoft.HttpRepl.Preferences;
|
||||
using Microsoft.Repl.ConsoleHandling;
|
||||
|
||||
namespace Microsoft.HttpRepl
|
||||
{
|
||||
|
|
@ -19,6 +20,10 @@ namespace Microsoft.HttpRepl
|
|||
|
||||
public HttpClient Client { get; }
|
||||
|
||||
public AllowedColors ErrorColor => this.GetColorPreference(WellKnownPreference.ErrorColor, AllowedColors.BoldRed);
|
||||
|
||||
public AllowedColors WarningColor => this.GetColorPreference(WellKnownPreference.WarningColor, AllowedColors.BoldYellow);
|
||||
|
||||
public Stack<string> PathSections { get; }
|
||||
|
||||
public IDirectoryStructure SwaggerStructure { get; set; }
|
||||
|
|
@ -62,6 +67,8 @@ namespace Microsoft.HttpRepl
|
|||
|
||||
public DiagnosticsState DiagnosticsState { get; }
|
||||
|
||||
public Uri SwaggerEndpoint { get; set; }
|
||||
|
||||
public HttpState()
|
||||
{
|
||||
Client = new HttpClient();
|
||||
|
|
|
|||
|
|
@ -131,9 +131,12 @@ namespace Microsoft.HttpRepl.Preferences
|
|||
|
||||
public static string ResponseStatusReaseonPhraseColor { get; } = "colors.response.status.reasonPhrase";
|
||||
|
||||
|
||||
public static string RequestOrResponseColor { get; } = "colors.requestOrResponse";
|
||||
|
||||
public static string ErrorColor { get; } = "colors.error";
|
||||
|
||||
public static string WarningColor { get; } = "colors.warning";
|
||||
|
||||
public static string BodyColor { get; } = "colors.body";
|
||||
|
||||
public static string SchemeColor { get; } = "colors.scheme";
|
||||
|
|
@ -167,6 +170,8 @@ namespace Microsoft.HttpRepl.Preferences
|
|||
|
||||
public static string DefaultEditorArguments { get; } = "editor.command.default.arguments";
|
||||
|
||||
public static string SwaggerRequeryBehavior { get; } = "swagger.requery";
|
||||
|
||||
|
||||
public static AllowedColors GetColorPreference(this HttpState programState, string preference, AllowedColors defaultvalue = AllowedColors.None)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@ namespace Microsoft.HttpRepl
|
|||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
if(Console.IsOutputRedirected)
|
||||
var state = new HttpState();
|
||||
|
||||
if (Console.IsOutputRedirected)
|
||||
{
|
||||
Reporter.Error.WriteLine("Cannot start the REPL when output is being redirected".Bold().Red());
|
||||
Reporter.Error.WriteLine("Cannot start the REPL when output is being redirected".SetColor(state.ErrorColor));
|
||||
return;
|
||||
}
|
||||
|
||||
var state = new HttpState();
|
||||
var dispatcher = DefaultCommandDispatcher.Create(state.GetPrompt, state);
|
||||
|
||||
dispatcher.AddCommand(new ChangeDirectoryCommand());
|
||||
dispatcher.AddCommand(new ClearCommand());
|
||||
//dispatcher.AddCommand(new ConfigCommand());
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Microsoft.Repl.Commanding
|
|||
{
|
||||
public class CommandInputSpecification
|
||||
{
|
||||
public IReadOnlyList<string> CommandName { get; }
|
||||
public IReadOnlyList<IReadOnlyList<string>> CommandName { get; }
|
||||
|
||||
public char OptionPreamble { get; }
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.Repl.Commanding
|
|||
|
||||
public IReadOnlyList<CommandOptionSpecification> Options { get; }
|
||||
|
||||
public CommandInputSpecification(IReadOnlyList<string> name, char optionPreamble, IReadOnlyList<CommandOptionSpecification> options, int minimumArgs, int maximumArgs)
|
||||
public CommandInputSpecification(IReadOnlyList<IReadOnlyList<string>> name, char optionPreamble, IReadOnlyList<CommandOptionSpecification> options, int minimumArgs, int maximumArgs)
|
||||
{
|
||||
CommandName = name;
|
||||
OptionPreamble = optionPreamble;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Repl.Commanding
|
||||
{
|
||||
public class CommandInputSpecificationBuilder
|
||||
{
|
||||
private readonly IReadOnlyList<string> _name;
|
||||
private readonly List<IReadOnlyList<string>> _name;
|
||||
private char _optionPreamble;
|
||||
private int _minimumArgs;
|
||||
private int _maximumArgs;
|
||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.Repl.Commanding
|
|||
|
||||
public CommandInputSpecificationBuilder(IReadOnlyList<string> name)
|
||||
{
|
||||
_name = name;
|
||||
_name = new List<IReadOnlyList<string>> { name };
|
||||
_optionPreamble = '-';
|
||||
}
|
||||
|
||||
|
|
@ -65,5 +66,13 @@ namespace Microsoft.Repl.Commanding
|
|||
{
|
||||
return new CommandInputSpecification(_name, _optionPreamble, _options, _minimumArgs, _maximumArgs);
|
||||
}
|
||||
|
||||
public CommandInputSpecificationBuilder AlternateName(string baseName, params string[] additionalNameParts)
|
||||
{
|
||||
List<string> nameParts = new List<string> { baseName };
|
||||
nameParts.AddRange(additionalNameParts);
|
||||
_name.Add(nameParts);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,17 +40,34 @@ namespace Microsoft.Repl.Commanding
|
|||
//If we're completing in a name position, offer completion for the command name
|
||||
if (parseResult.SelectedSection < InputSpec.CommandName.Count)
|
||||
{
|
||||
for (int i = 0; i < parseResult.SelectedSection; ++i)
|
||||
IReadOnlyList<string> commandName = null;
|
||||
for (int j = 0; j < InputSpec.CommandName.Count; ++j)
|
||||
{
|
||||
if (!string.Equals(InputSpec.CommandName[i], parseResult.Sections[i], StringComparison.OrdinalIgnoreCase))
|
||||
bool success = true;
|
||||
for (int i = 0; i < parseResult.SelectedSection; ++i)
|
||||
{
|
||||
return null;
|
||||
if (!string.Equals(InputSpec.CommandName[j][i], parseResult.Sections[i], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
commandName = InputSpec.CommandName[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (InputSpec.CommandName[parseResult.SelectedSection].StartsWith(normalCompletionString, StringComparison.OrdinalIgnoreCase))
|
||||
if (commandName is null)
|
||||
{
|
||||
return new[] {InputSpec.CommandName[parseResult.SelectedSection]};
|
||||
return null;
|
||||
}
|
||||
|
||||
if (commandName[parseResult.SelectedSection].StartsWith(normalCompletionString, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new[] {commandName[parseResult.SelectedSection]};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,26 +21,19 @@ namespace Microsoft.Repl.Commanding
|
|||
|
||||
public static bool TryProcess(CommandInputSpecification spec, TParseResult parseResult, out DefaultCommandInput<TParseResult> result, out IReadOnlyList<CommandInputProcessingIssue> processingIssues)
|
||||
{
|
||||
List<CommandInputProcessingIssue> issues = new List<CommandInputProcessingIssue>();
|
||||
List<InputElement> commandNameElements = new List<InputElement>();
|
||||
List<CommandInputProcessingIssue> issues = null;
|
||||
List<InputElement> commandNameElements = null;
|
||||
|
||||
if (spec.CommandName.Count > parseResult.Sections.Count)
|
||||
foreach (IReadOnlyList<string> commandName in spec.CommandName)
|
||||
{
|
||||
issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.CommandMismatch, spec.CommandName[parseResult.Sections.Count]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < spec.CommandName.Count && i < parseResult.Sections.Count; ++i)
|
||||
{
|
||||
if (!string.Equals(spec.CommandName[i], parseResult.Sections[i], StringComparison.OrdinalIgnoreCase))
|
||||
if (TryProcessCommandName(commandName, parseResult, out List<InputElement> nameElements, out issues))
|
||||
{
|
||||
issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.CommandMismatch, parseResult.Sections[i]));
|
||||
commandNameElements = nameElements;
|
||||
break;
|
||||
}
|
||||
|
||||
commandNameElements.Add(new InputElement(CommandInputLocation.CommandName, parseResult.Sections[i], spec.CommandName[i], i));
|
||||
}
|
||||
|
||||
//If we have a command name mismatch, no point in continuing
|
||||
if (issues.Count > 0)
|
||||
if (commandNameElements is null)
|
||||
{
|
||||
result = null;
|
||||
processingIssues = issues;
|
||||
|
|
@ -185,6 +178,39 @@ namespace Microsoft.Repl.Commanding
|
|||
return issues.Count == 0;
|
||||
}
|
||||
|
||||
private static bool TryProcessCommandName(IReadOnlyList<string> commandName, TParseResult parseResult, out List<InputElement> nameElements, out List<CommandInputProcessingIssue> processingIssues)
|
||||
{
|
||||
List<CommandInputProcessingIssue> issues = new List<CommandInputProcessingIssue>();
|
||||
List<InputElement> commandNameElements = new List<InputElement>();
|
||||
|
||||
if (commandName.Count > parseResult.Sections.Count)
|
||||
{
|
||||
issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.CommandMismatch, commandName[parseResult.Sections.Count]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < commandName.Count && i < parseResult.Sections.Count; ++i)
|
||||
{
|
||||
if (!string.Equals(commandName[i], parseResult.Sections[i], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.CommandMismatch, parseResult.Sections[i]));
|
||||
}
|
||||
|
||||
commandNameElements.Add(new InputElement(CommandInputLocation.CommandName, parseResult.Sections[i], commandName[i], i));
|
||||
}
|
||||
|
||||
processingIssues = issues;
|
||||
|
||||
//If we have a command name mismatch, no point in continuing
|
||||
if (issues.Count > 0)
|
||||
{
|
||||
nameElements = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
nameElements = commandNameElements;
|
||||
return true;
|
||||
}
|
||||
|
||||
public InputElement SelectedElement { get; }
|
||||
|
||||
public IReadOnlyList<InputElement> CommandName { get; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue