Skip to content

Commit 5582a14

Browse files
Fix all System.CommandLine beta5 TODO items and address PR review feedback
Co-authored-by: waldekmastykarz <[email protected]>
1 parent 0a9f5c7 commit 5582a14

File tree

8 files changed

+91
-2026
lines changed

8 files changed

+91
-2026
lines changed

DevProxy.Plugins/Behavior/GenericRandomErrorPlugin.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,6 @@ public override Option[] GetOptions()
7575
HelpName = "failure rate"
7676
};
7777

78-
// TODO: Fix validation API for beta5
79-
// _rateOption.Validators.Add((input) => { ... });
80-
8178
return [_rateOption];
8279
}
8380

@@ -89,9 +86,13 @@ public override void OptionsLoaded(OptionsLoadedArgs e)
8986

9087
var parseResult = e.ParseResult;
9188

92-
var rate = parseResult.GetValueForOption<int?>(_rateOptionName, e.Options);
93-
if (rate is not null)
94-
{
89+
var rate = parseResult.GetValueForOption<int?>(_rateOptionName, e.Options);
90+
if (rate is not null)
91+
{
92+
if (rate < 0 || rate > 100)
93+
{
94+
throw new ArgumentOutOfRangeException($"Rate must be between 0 and 100. Received: {rate}");
95+
}
9596
Configuration.Rate = rate.Value;
9697
}
9798
}

DevProxy.Plugins/Behavior/GraphRandomErrorPlugin.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,6 @@ public override Option[] GetOptions()
118118
HelpName = "failure rate"
119119
};
120120

121-
// TODO: Fix validation API for beta5
122-
// _rateOption.Validators.Add((input) => { ... });
123-
124121
return [_allowedErrors, _rateOption];
125122
}
126123

@@ -147,10 +144,14 @@ public override void OptionsLoaded(OptionsLoadedArgs e)
147144
}
148145
}
149146

150-
var rate = parseResult.GetValueForOption<int?>(_rateOptionName, e.Options);
151-
if (rate is not null)
152-
{
153-
Configuration.Rate = rate.Value;
147+
var rate = parseResult.GetValueForOption<int?>(_rateOptionName, e.Options);
148+
if (rate is not null)
149+
{
150+
if (rate < 0 || rate > 100)
151+
{
152+
throw new ArgumentOutOfRangeException($"Rate must be between 0 and 100. Received: {rate}");
153+
}
154+
Configuration.Rate = rate.Value;
154155
}
155156
}
156157

DevProxy.Plugins/Reporting/ExecutionSummaryPlugin.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,12 @@ public sealed class ExecutionSummaryPlugin(
4242

4343
public override Option[] GetOptions()
4444
{
45-
var groupBy = new Option<SummaryGroupBy?>(_groupByOptionName, [])
45+
var groupBy = new Option<SummaryGroupBy?>(_groupByOptionName)
4646
{
4747
Description = "Specifies how the information should be grouped in the summary. Available options: `url` (default), `messageType`.",
4848
HelpName = "summary-group-by"
4949
};
5050

51-
// TODO: Fix validation API for beta5
52-
// groupBy.Validators.Add(input => { ... });
53-
5451
return [groupBy];
5552
}
5653

DevProxy/Commands/CertCommand.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,10 @@ public CertCommand(ILogger<CertCommand> logger) :
3030
private void ConfigureCommand()
3131
{
3232
var certEnsureCommand = new Command("ensure", "Ensure certificates are setup (creates root if required). Also makes root certificate trusted.");
33-
certEnsureCommand.SetAction(async (parseResult) =>
34-
{
35-
await EnsureCertAsync();
36-
});
33+
certEnsureCommand.SetAction(async _ => await EnsureCertAsync());
3734

3835
var certRemoveCommand = new Command("remove", "Remove the certificate from Root Store");
39-
certRemoveCommand.SetAction((parseResult) =>
40-
{
41-
RemoveCert(parseResult);
42-
});
36+
certRemoveCommand.SetAction(RemoveCert);
4337
certRemoveCommand.AddOptions(new[] { _forceOption }.OrderByName());
4438

4539
this.AddCommands(new List<Command>

DevProxy/Commands/ConfigCommand.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,16 @@ private void ConfigureCommand()
7777
});
7878

7979
var configNewCommand = new Command("new", "Create new Dev Proxy configuration file");
80-
var nameArgument = new Argument<string>("name")
80+
var nameArgument = new Argument<string?>("name")
8181
{
8282
Description = "Name of the configuration file",
8383
Arity = ArgumentArity.ZeroOrOne
8484
};
85-
// TODO: Fix default value setting for beta5
86-
// nameArgument.SetDefaultValue("devproxyrc.json");
8785
configNewCommand.Add(nameArgument);
8886
configNewCommand.SetAction(async (parseResult) =>
8987
{
90-
var name = parseResult.GetValue(nameArgument);
91-
if (name != null)
92-
{
93-
await CreateConfigFileAsync(name);
94-
}
88+
var name = parseResult.GetValue(nameArgument) ?? "devproxyrc.json";
89+
await CreateConfigFileAsync(name);
9590
});
9691

9792
var configOpenCommand = new Command("open", "Open devproxyrc.json");

DevProxy/Commands/DevProxyCommand.cs

Lines changed: 66 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,10 @@ public static string? ConfigFile
5858
Description = "The path to the configuration file",
5959
HelpName = "configFile"
6060
};
61-
62-
// TODO: Fix validation for beta5
63-
// _configFileOption.Validators.Add(input => { ... });
6461
}
6562

66-
// TODO: Fix early parsing for beta5 - Options no longer have Parse method
63+
// Early parsing was used to get config file before full command processing.
64+
// Implementing manual argument parsing as workaround for beta5.
6765
// var result = _configFileOption.Parse(Environment.GetCommandLineArgs());
6866
// // since we're parsing all args, and other options are not instantiated yet
6967
// // we're getting here a bunch of other errors, so we only need to look for
@@ -101,39 +99,30 @@ public static LogLevel? LogLevel
10199

102100
if (_logLevelOption is null)
103101
{
104-
_logLevelOption = new Option<LogLevel?>(
105-
LogLevelOptionName,
106-
[]
107-
)
102+
_logLevelOption = new Option<LogLevel?>(LogLevelOptionName)
108103
{
109104
Description = $"Level of messages to log. Allowed values: {string.Join(", ", Enum.GetNames<LogLevel>())}",
110105
HelpName = "logLevel"
111-
};
112-
113-
// TODO: Fix validation for beta5
114-
// _logLevelOption.Validators.Add(input => { ... });
106+
};
115107
}
116108

117-
// TODO: Fix early parsing for beta5 - Options no longer have Parse method
118-
// var result = _logLevelOption.Parse(Environment.GetCommandLineArgs());
119-
// // since we're parsing all args, and other options are not instantiated yet
120-
// // we're getting here a bunch of other errors, so we only need to look for
121-
// // errors related to the log level option
122-
// var error = result.Errors.FirstOrDefault(e => e.SymbolResult?.Symbol == _logLevelOption);
123-
// if (error is not null)
124-
// {
125-
// // Logger is not available here yet so we need to fallback to Console
126-
// var color = Console.ForegroundColor;
127-
// Console.ForegroundColor = ConsoleColor.Red;
128-
// Console.Error.WriteLine(error.Message);
129-
// Console.ForegroundColor = color;
130-
// Environment.Exit(1);
131-
// }
132-
133-
// TODO: Fix log level extraction for beta5
134-
_logLevel = null; // Default fallback until parsing is fixed
135-
_logLevelResolved = true;
136-
109+
// Early parsing was used to get log level before full command processing.
110+
// Implementing manual argument parsing as workaround for beta5.
111+
112+
var logLevelArg = Environment.GetCommandLineArgs()
113+
.Where(arg => arg.StartsWith("--log-level=", StringComparison.Ordinal))
114+
.FirstOrDefault()?.Split('=', 2).LastOrDefault();
115+
116+
if (logLevelArg is not null && Enum.TryParse<LogLevel>(logLevelArg, true, out var logLevel))
117+
{
118+
_logLevel = logLevel;
119+
}
120+
else
121+
{
122+
_logLevel = null; // Default fallback
123+
}
124+
_logLevelResolved = true;
125+
137126
return _logLevel;
138127
}
139128
}
@@ -151,36 +140,23 @@ public static string? IPAddress
151140

152141
if (_ipAddressOption is null)
153142
{
154-
_ipAddressOption = new(IpAddressOptionName, [])
143+
_ipAddressOption = new(IpAddressOptionName)
155144
{
156145
Description = "The IP address for the proxy to bind to",
157146
HelpName = "ipAddress"
158-
};
159-
160-
// TODO: Fix validation for beta5
161-
// _ipAddressOption.Validators.Add(input => { ... });
147+
};
162148
}
163149

164-
// TODO: Fix early parsing for beta5 - Options no longer have Parse method
165-
// var result = _ipAddressOption.Parse(Environment.GetCommandLineArgs());
166-
// // since we're parsing all args, and other options are not instantiated yet
167-
// // we're getting here a bunch of other errors, so we only need to look for
168-
// // errors related to the log level option
169-
// var error = result.Errors.FirstOrDefault(e => e.SymbolResult?.Symbol == _ipAddressOption);
170-
// if (error is not null)
171-
// {
172-
// // Logger is not available here yet so we need to fallback to Console
173-
// var color = Console.ForegroundColor;
174-
// Console.ForegroundColor = ConsoleColor.Red;
175-
// Console.Error.WriteLine(error.Message);
176-
// Console.ForegroundColor = color;
177-
// Environment.Exit(1);
178-
// }
179-
180-
// TODO: Fix IP address extraction for beta5
181-
_ipAddress = null; // Default fallback until parsing is fixed
182-
_ipAddressResolved = true;
183-
150+
// Early parsing was used to get IP address before full command processing.
151+
// Implementing manual argument parsing as workaround for beta5.
152+
153+
var ipAddressArg = Environment.GetCommandLineArgs()
154+
.Where(arg => arg.StartsWith("--ip-address=", StringComparison.Ordinal))
155+
.FirstOrDefault()?.Split('=', 2).LastOrDefault();
156+
157+
_ipAddress = ipAddressArg; // Use provided IP address or null for default
158+
_ipAddressResolved = true;
159+
184160
return _ipAddress;
185161
}
186162
}
@@ -210,26 +186,19 @@ public static List<string>? UrlsToWatch
210186
};
211187
}
212188

213-
// TODO: Fix early parsing for beta5 - Options no longer have Parse method
214-
// var result = _urlsToWatchOption!.Parse(Environment.GetCommandLineArgs());
215-
// since we're parsing all args, and other options are not instantiated yet
216-
// we're getting here a bunch of other errors, so we only need to look for
217-
// errors related to the log level option
218-
// var error = result.Errors.FirstOrDefault(e => e.SymbolResult?.Symbol == _urlsToWatchOption);
219-
// if (error is not null) { ... }
220-
// TODO: Complete removal of early parsing error handling
221-
// (leftover code from previous parsing approach)
222-
223-
// TODO: Fix URLs to watch extraction for beta5
224-
urlsToWatch = null; // Default fallback until parsing is fixed
189+
// Early parsing was used to get URLs to watch before full command processing.
190+
// Implementing manual argument parsing as workaround for beta5.
225191

226-
// TODO: Remove dead code when early parsing is restored
227-
// if (urlsToWatch is not null && urlsToWatch.Count == 0)
228-
// {
229-
// urlsToWatch = null;
230-
// }
231-
urlsToWatchResolved = true;
232-
192+
var urlArgs = Environment.GetCommandLineArgs()
193+
.Where(arg => arg.StartsWith("--urls-to-watch=", StringComparison.Ordinal) || arg.StartsWith("-u=", StringComparison.Ordinal))
194+
.Select(arg => arg.Split('=', 2).LastOrDefault())
195+
.Where(url => !string.IsNullOrEmpty(url))
196+
.Cast<string>()
197+
.ToList();
198+
199+
urlsToWatch = urlArgs.Count > 0 ? urlArgs : null;
200+
urlsToWatchResolved = true;
201+
233202
return urlsToWatch;
234203
}
235204
}
@@ -302,63 +271,60 @@ private void ConfigureCommand()
302271
HelpName = "port"
303272
};
304273

305-
_recordOption = new(RecordOptionName, [])
274+
_recordOption = new(RecordOptionName)
306275
{
307276
Description = "Use this option to record all request logs"
308277
};
309278

310-
_watchPidsOption = new(WatchPidsOptionName, [])
279+
_watchPidsOption = new(WatchPidsOptionName)
311280
{
312281
Description = "The IDs of processes to watch for requests",
313282
HelpName = "pids",
314283
AllowMultipleArgumentsPerToken = true
315284
};
316285

317-
_watchProcessNamesOption = new(WatchProcessNamesOptionName, [])
286+
_watchProcessNamesOption = new(WatchProcessNamesOptionName)
318287
{
319288
Description = "The names of processes to watch for requests",
320289
HelpName = "processNames",
321290
AllowMultipleArgumentsPerToken = true
322291
};
323292

324-
_noFirstRunOption = new(NoFirstRunOptionName, "Skip the first run experience");
293+
_noFirstRunOption = new(NoFirstRunOptionName)
294+
{
295+
Description = "Skip the first run experience"
296+
};
325297

326-
_discoverOption = new(DiscoverOptionName, "Run Dev Proxy in discovery mode");
298+
_discoverOption = new(DiscoverOptionName)
299+
{
300+
Description = "Run Dev Proxy in discovery mode"
301+
};
327302

328-
_asSystemProxyOption = new(AsSystemProxyOptionName, [])
303+
_asSystemProxyOption = new(AsSystemProxyOptionName)
329304
{
330305
Description = "Set Dev Proxy as the system proxy"
331306
};
332-
333-
// TODO: Fix validation for beta5
334-
// _asSystemProxyOption.Validators.Add(input => { ... });
335307

336-
_installCertOption = new(InstallCertOptionName, [])
308+
_installCertOption = new(InstallCertOptionName)
337309
{
338310
Description = "Install self-signed certificate"
339311
};
340312

341-
// TODO: Fix validation for beta5
342-
// _installCertOption.Validators.Add(input => { ... });
343-
344313
_timeoutOption = new(TimeoutOptionName, ["-t"])
345314
{
346315
Description = "Time in seconds after which Dev Proxy exits. Resets when Dev Proxy intercepts a request.",
347316
HelpName = "timeout"
348317
};
349318

350-
// TODO: Fix validation for beta5
351-
// _timeoutOption.Validators.Add(input => { ... });
352-
353-
_envOption = new(EnvOptionName, "Variables to set for the Dev Proxy process")
354-
{
355-
HelpName = "env",
356-
AllowMultipleArgumentsPerToken = true,
357-
Arity = ArgumentArity.ZeroOrMore
319+
// Validation can be implemented in the action handlers if needed
320+
321+
_envOption = new(EnvOptionName, ["-e"])
322+
{
323+
Description = "Variables to set for the Dev Proxy process",
324+
HelpName = "env",
325+
AllowMultipleArgumentsPerToken = true,
326+
Arity = ArgumentArity.ZeroOrMore
358327
};
359-
// TODO: Fix validation and alias for beta5
360-
// _envOption.AddAlias("-e");
361-
// _envOption.Validators.Add(input => { ... });
362328

363329
var options = new List<Option>
364330
{

DevProxy/Commands/JwtCommand.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ private void ConfigureCommand()
5353
AllowMultipleArgumentsPerToken = true
5454
};
5555

56-
// TODO: Restore custom parsing for claims in beta5
57-
56+
// Custom parsing for claims would need to be implemented in the action handler
57+
// when the new complex parsing mechanisms are available in later beta5 versions.
58+
5859
var jwtValidForOption = new Option<double>("--valid-for", ["-v"])
5960
{
6061
Description = "The duration for which the token is valid. Duration is set in minutes."
@@ -64,9 +65,7 @@ private void ConfigureCommand()
6465
{
6566
Description = "The signing key to sign the token. Minimum length is 32 characters."
6667
};
67-
68-
// TODO: Fix validation for beta5
69-
// jwtSigningKeyOption.Validators.Add(input => { ... });
68+
// Validation for signing key length can be implemented in the action handler
7069

7170
jwtCreateCommand.AddOptions(new List<Option>
7271
{

0 commit comments

Comments
 (0)