-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds support for showing in-product announcements (#895)
- Loading branch information
1 parent
7910f51
commit c07b90a
Showing
2 changed files
with
149 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
namespace Microsoft.DevProxy; | ||
|
||
static class Announcement | ||
{ | ||
private static readonly string announcementUrl = "https://aka.ms/devproxy/announcement"; | ||
|
||
public static async Task ShowAsync() | ||
{ | ||
var announcement = await GetAsync(); | ||
if (!string.IsNullOrEmpty(announcement)) | ||
{ | ||
await Console.Error.WriteLineAsync(announcement); | ||
} | ||
} | ||
|
||
public static async Task<string?> GetAsync() | ||
{ | ||
try | ||
{ | ||
using var client = new HttpClient(); | ||
return await client.GetStringAsync(announcementUrl); | ||
} | ||
catch | ||
{ | ||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,116 +1,118 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.DevProxy; | ||
using Microsoft.DevProxy.Abstractions; | ||
using Microsoft.DevProxy.Abstractions.LanguageModel; | ||
using Microsoft.DevProxy.CommandHandlers; | ||
using Microsoft.DevProxy.Logging; | ||
using Microsoft.Extensions.Logging.Console; | ||
using System.CommandLine; | ||
|
||
PluginEvents pluginEvents = new(); | ||
|
||
ILogger BuildLogger() | ||
{ | ||
var loggerFactory = LoggerFactory.Create(builder => | ||
{ | ||
builder | ||
.AddConsole(options => | ||
{ | ||
options.FormatterName = "devproxy"; | ||
options.LogToStandardErrorThreshold = LogLevel.Warning; | ||
}) | ||
.AddConsoleFormatter<ProxyConsoleFormatter, ConsoleFormatterOptions>(options => { | ||
options.IncludeScopes = true; | ||
}) | ||
.AddRequestLogger(pluginEvents) | ||
.SetMinimumLevel(ProxyHost.LogLevel ?? ProxyCommandHandler.Configuration.LogLevel); | ||
}); | ||
return loggerFactory.CreateLogger("devproxy"); | ||
} | ||
|
||
var logger = BuildLogger(); | ||
|
||
var lmClient = new OllamaLanguageModelClient(ProxyCommandHandler.Configuration.LanguageModel, logger); | ||
IProxyContext context = new ProxyContext(ProxyCommandHandler.Configuration, ProxyEngine.Certificate, lmClient); | ||
ProxyHost proxyHost = new(); | ||
|
||
// this is where the root command is created which contains all commands and subcommands | ||
RootCommand rootCommand = proxyHost.GetRootCommand(logger); | ||
|
||
// store the global options that are created automatically for us | ||
// rootCommand doesn't return the global options, so we have to store them manually | ||
string[] globalOptions = ["--version", "--help", "-h", "/h", "-?", "/?"]; | ||
|
||
// check if any of the global options are present | ||
var hasGlobalOption = args.Any(arg => globalOptions.Contains(arg)); | ||
|
||
// get the list of available subcommands | ||
var subCommands = rootCommand.Children.OfType<Command>().Select(c => c.Name).ToArray(); | ||
|
||
// check if any of the subcommands are present | ||
var hasSubCommand = args.Any(arg => subCommands.Contains(arg)); | ||
|
||
if (hasGlobalOption || hasSubCommand) | ||
{ | ||
// we don't need to load plugins if the user is using a global option or using a subcommand, so we can exit early | ||
await rootCommand.InvokeAsync(args); | ||
return; | ||
} | ||
|
||
var pluginLoader = new PluginLoader(logger); | ||
PluginLoaderResult loaderResults = await pluginLoader.LoadPluginsAsync(pluginEvents, context); | ||
// have all the plugins init | ||
pluginEvents.RaiseInit(new InitArgs()); | ||
|
||
var options = loaderResults.ProxyPlugins | ||
.SelectMany(p => p.GetOptions()) | ||
// remove duplicates by comparing the option names | ||
.GroupBy(o => o.Name) | ||
.Select(g => g.First()) | ||
.ToList(); | ||
options.ForEach(rootCommand.AddOption); | ||
// register all plugin commands | ||
loaderResults.ProxyPlugins | ||
.SelectMany(p => p.GetCommands()) | ||
.ToList() | ||
.ForEach(rootCommand.AddCommand); | ||
|
||
rootCommand.Handler = proxyHost.GetCommandHandler(pluginEvents, [.. options], loaderResults.UrlsToWatch, logger); | ||
|
||
// filter args to retrieve options | ||
var incomingOptions = args.Where(arg => arg.StartsWith('-')).ToArray(); | ||
|
||
// remove the global options from the incoming options | ||
incomingOptions = incomingOptions.Except(globalOptions).ToArray(); | ||
|
||
// compare the incoming options against the root command options | ||
foreach (var option in rootCommand.Options) | ||
{ | ||
// get the option aliases | ||
var aliases = option.Aliases.ToArray(); | ||
|
||
// iterate over aliases | ||
foreach (string alias in aliases) | ||
{ | ||
// if the alias is present | ||
if (incomingOptions.Contains(alias)) | ||
{ | ||
// remove the option from the incoming options | ||
incomingOptions = incomingOptions.Where(val => val != alias).ToArray(); | ||
} | ||
} | ||
} | ||
|
||
// list the remaining incoming options as unknown in the output | ||
if (incomingOptions.Length > 0) | ||
{ | ||
logger.LogError("Unknown option(s): {unknownOptions}", string.Join(" ", incomingOptions)); | ||
logger.LogInformation("TIP: Use --help view available options"); | ||
logger.LogInformation("TIP: Are you missing a plugin? See: https://aka.ms/devproxy/plugins"); | ||
} | ||
else | ||
{ | ||
await rootCommand.InvokeAsync(args); | ||
} | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.DevProxy; | ||
using Microsoft.DevProxy.Abstractions; | ||
using Microsoft.DevProxy.Abstractions.LanguageModel; | ||
using Microsoft.DevProxy.CommandHandlers; | ||
using Microsoft.DevProxy.Logging; | ||
using Microsoft.Extensions.Logging.Console; | ||
using System.CommandLine; | ||
|
||
_ = Announcement.ShowAsync(); | ||
|
||
PluginEvents pluginEvents = new(); | ||
|
||
ILogger BuildLogger() | ||
{ | ||
var loggerFactory = LoggerFactory.Create(builder => | ||
{ | ||
builder | ||
.AddConsole(options => | ||
{ | ||
options.FormatterName = "devproxy"; | ||
options.LogToStandardErrorThreshold = LogLevel.Warning; | ||
}) | ||
.AddConsoleFormatter<ProxyConsoleFormatter, ConsoleFormatterOptions>(options => { | ||
options.IncludeScopes = true; | ||
}) | ||
.AddRequestLogger(pluginEvents) | ||
.SetMinimumLevel(ProxyHost.LogLevel ?? ProxyCommandHandler.Configuration.LogLevel); | ||
}); | ||
return loggerFactory.CreateLogger("devproxy"); | ||
} | ||
|
||
var logger = BuildLogger(); | ||
|
||
var lmClient = new OllamaLanguageModelClient(ProxyCommandHandler.Configuration.LanguageModel, logger); | ||
IProxyContext context = new ProxyContext(ProxyCommandHandler.Configuration, ProxyEngine.Certificate, lmClient); | ||
ProxyHost proxyHost = new(); | ||
|
||
// this is where the root command is created which contains all commands and subcommands | ||
RootCommand rootCommand = proxyHost.GetRootCommand(logger); | ||
|
||
// store the global options that are created automatically for us | ||
// rootCommand doesn't return the global options, so we have to store them manually | ||
string[] globalOptions = ["--version", "--help", "-h", "/h", "-?", "/?"]; | ||
|
||
// check if any of the global options are present | ||
var hasGlobalOption = args.Any(arg => globalOptions.Contains(arg)); | ||
|
||
// get the list of available subcommands | ||
var subCommands = rootCommand.Children.OfType<Command>().Select(c => c.Name).ToArray(); | ||
|
||
// check if any of the subcommands are present | ||
var hasSubCommand = args.Any(arg => subCommands.Contains(arg)); | ||
|
||
if (hasGlobalOption || hasSubCommand) | ||
{ | ||
// we don't need to load plugins if the user is using a global option or using a subcommand, so we can exit early | ||
await rootCommand.InvokeAsync(args); | ||
return; | ||
} | ||
|
||
var pluginLoader = new PluginLoader(logger); | ||
PluginLoaderResult loaderResults = await pluginLoader.LoadPluginsAsync(pluginEvents, context); | ||
// have all the plugins init | ||
pluginEvents.RaiseInit(new InitArgs()); | ||
|
||
var options = loaderResults.ProxyPlugins | ||
.SelectMany(p => p.GetOptions()) | ||
// remove duplicates by comparing the option names | ||
.GroupBy(o => o.Name) | ||
.Select(g => g.First()) | ||
.ToList(); | ||
options.ForEach(rootCommand.AddOption); | ||
// register all plugin commands | ||
loaderResults.ProxyPlugins | ||
.SelectMany(p => p.GetCommands()) | ||
.ToList() | ||
.ForEach(rootCommand.AddCommand); | ||
|
||
rootCommand.Handler = proxyHost.GetCommandHandler(pluginEvents, [.. options], loaderResults.UrlsToWatch, logger); | ||
|
||
// filter args to retrieve options | ||
var incomingOptions = args.Where(arg => arg.StartsWith('-')).ToArray(); | ||
|
||
// remove the global options from the incoming options | ||
incomingOptions = incomingOptions.Except(globalOptions).ToArray(); | ||
|
||
// compare the incoming options against the root command options | ||
foreach (var option in rootCommand.Options) | ||
{ | ||
// get the option aliases | ||
var aliases = option.Aliases.ToArray(); | ||
|
||
// iterate over aliases | ||
foreach (string alias in aliases) | ||
{ | ||
// if the alias is present | ||
if (incomingOptions.Contains(alias)) | ||
{ | ||
// remove the option from the incoming options | ||
incomingOptions = incomingOptions.Where(val => val != alias).ToArray(); | ||
} | ||
} | ||
} | ||
|
||
// list the remaining incoming options as unknown in the output | ||
if (incomingOptions.Length > 0) | ||
{ | ||
logger.LogError("Unknown option(s): {unknownOptions}", string.Join(" ", incomingOptions)); | ||
logger.LogInformation("TIP: Use --help view available options"); | ||
logger.LogInformation("TIP: Are you missing a plugin? See: https://aka.ms/devproxy/plugins"); | ||
} | ||
else | ||
{ | ||
await rootCommand.InvokeAsync(args); | ||
} |