Skip to content

Commit 085e424

Browse files
Add support for passing API spec variables values. Closes #1019 (#1074)
1 parent bd52987 commit 085e424

File tree

7 files changed

+79
-6
lines changed

7 files changed

+79
-6
lines changed

dev-proxy-abstractions/IProxyConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public interface IProxyConfiguration
1111
int ApiPort { get; }
1212
bool AsSystemProxy { get; }
1313
string ConfigFile { get; }
14+
Dictionary<string, string> Env { get; set; }
1415
MockRequestHeader[]? FilterByHeaders { get; }
1516
bool InstallCert { get; }
1617
string? IPAddress { get; }

dev-proxy-abstractions/ProxyUtils.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,4 +496,18 @@ private static string GetCommonPrefix(List<string> paths)
496496
return (false, [ex.Message]);
497497
}
498498
}
499+
500+
public static string ReplaceVariables(string s, Dictionary<string, string> variables, Func<string, string>? formatVariableRef = null)
501+
{
502+
formatVariableRef ??= v => $"@{v}";
503+
504+
var s1 = s;
505+
506+
foreach (var variable in variables)
507+
{
508+
s1 = s1.Replace(formatVariableRef(variable.Key), variable.Value, StringComparison.OrdinalIgnoreCase);
509+
}
510+
511+
return s1;
512+
}
499513
}

dev-proxy-plugins/RequestLogs/MinimalPermissionsGuidancePlugin.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,10 @@ private Dictionary<string, OpenApiDocument> LoadApiSpecs(string apiSpecsFolderPa
187187
Logger.LogDebug("Processing file '{file}'...", file);
188188
try
189189
{
190-
var apiDefinition = new OpenApiStringReader().Read(File.ReadAllText(file), out _);
190+
var fileContents = File.ReadAllText(file);
191+
fileContents = ProxyUtils.ReplaceVariables(fileContents, Context.Configuration.Env, v => $"{{{v}}}");
192+
193+
var apiDefinition = new OpenApiStringReader().Read(fileContents, out _);
191194
if (apiDefinition is null)
192195
{
193196
continue;

dev-proxy-plugins/RequestLogs/MinimalPermissionsPlugin.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ private async Task AfterRecordingStopAsync(object sender, RecordingArgs e)
6868
.Where(l =>
6969
l.MessageType == MessageType.InterceptedRequest &&
7070
!l.Message.StartsWith("OPTIONS") &&
71-
l.Context?.Session is not null &&
72-
l.Context.Session.HttpClient.Request.Headers.Any(h => h.Name.Equals("authorization", StringComparison.OrdinalIgnoreCase))
71+
l.Context?.Session is not null
7372
);
7473
if (!interceptedRequests.Any())
7574
{
@@ -165,7 +164,10 @@ private Dictionary<string, OpenApiDocument> LoadApiSpecs(string apiSpecsFolderPa
165164
Logger.LogDebug("Processing file '{file}'...", file);
166165
try
167166
{
168-
var apiDefinition = new OpenApiStringReader().Read(File.ReadAllText(file), out _);
167+
var fileContents = File.ReadAllText(file);
168+
fileContents = ProxyUtils.ReplaceVariables(fileContents, Context.Configuration.Env, v => $"{{{v}}}");
169+
170+
var apiDefinition = new OpenApiStringReader().Read(fileContents, out _);
169171
if (apiDefinition is null)
170172
{
171173
continue;

dev-proxy/CommandHandlers/ProxyCommandHandler.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,20 @@ private void ParseOptions(InvocationContext context)
161161
{
162162
Configuration.Record = true;
163163
}
164+
var env = context.ParseResult.GetValueForOption<string[]?>(ProxyHost.EnvOptionName, _options);
165+
if (env is not null)
166+
{
167+
Configuration.Env = env.Select(e =>
168+
{
169+
// Split on first '=' only
170+
var parts = e.Split('=', 2);
171+
if (parts.Length != 2)
172+
{
173+
throw new ArgumentException($"Invalid env format: {e}. Expected format is 'key=value'.");
174+
}
175+
return new KeyValuePair<string, string>(parts[0], parts[1]);
176+
}).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
177+
}
164178
}
165179

166180
private async Task CheckForNewVersionAsync()

dev-proxy/ProxyConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class ProxyConfiguration : IProxyConfiguration
2424
public int ApiPort { get; set; } = 8897;
2525
public bool AsSystemProxy { get; set; } = true;
2626
public string ConfigFile { get; set; } = "devproxyrc.json";
27+
public Dictionary<string, string> Env { get; set; } = [];
2728
public MockRequestHeader[]? FilterByHeaders { get; set; }
2829
public string? IPAddress { get; set; } = "127.0.0.1";
2930
public bool InstallCert { get; set; } = true;

dev-proxy/ProxyHost.cs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ internal class ProxyHost
3939
private readonly Option<long?> _timeoutOption;
4040
internal static readonly string DiscoverOptionName = "--discover";
4141
private readonly Option<bool?> _discoverOption;
42+
internal static readonly string EnvOptionName = "--env";
43+
private readonly Option<string[]?> _envOption;
4244

4345
private static bool _configFileResolved = false;
4446
private static string _configFile = "devproxyrc.json";
@@ -273,7 +275,7 @@ public ProxyHost()
273275
Arity = ArgumentArity.ZeroOrMore
274276
};
275277
_urlsToWatchOption.AddAlias("-u");
276-
278+
277279
_timeoutOption = new Option<long?>(TimeoutOptionName, "Time in seconds after which Dev Proxy exits. Resets when Dev Proxy intercepts a request.")
278280
{
279281
ArgumentHelpName = "timeout",
@@ -294,6 +296,40 @@ public ProxyHost()
294296
});
295297
_timeoutOption.AddAlias("-t");
296298

299+
_envOption = new Option<string[]?>(EnvOptionName, "Variables to set for the Dev Proxy process")
300+
{
301+
ArgumentHelpName = "env",
302+
AllowMultipleArgumentsPerToken = true,
303+
Arity = ArgumentArity.ZeroOrMore
304+
};
305+
_envOption.AddAlias("-e");
306+
_envOption.AddValidator(input =>
307+
{
308+
try
309+
{
310+
var envVars = input.GetValueForOption(_envOption);
311+
if (envVars is null || envVars.Length == 0)
312+
{
313+
return;
314+
}
315+
316+
foreach (var envVar in envVars)
317+
{
318+
// Split on first '=' only
319+
var parts = envVar.Split('=', 2);
320+
if (parts.Length != 2)
321+
{
322+
input.ErrorMessage = $"Invalid environment variable format: '{envVar}'. Expected format is 'name=value'.";
323+
return;
324+
}
325+
}
326+
}
327+
catch (InvalidOperationException ex)
328+
{
329+
input.ErrorMessage = ex.Message;
330+
}
331+
});
332+
297333
ProxyCommandHandler.Configuration.ConfigFile = ConfigFile;
298334
}
299335

@@ -318,7 +354,8 @@ public RootCommand GetRootCommand(ILogger logger)
318354
// As such, it's always set here
319355
_urlsToWatchOption!,
320356
_timeoutOption,
321-
_discoverOption
357+
_discoverOption,
358+
_envOption
322359
};
323360
command.Description = "Dev Proxy is a command line tool for testing Microsoft Graph, SharePoint Online and any other HTTP APIs.";
324361

@@ -492,6 +529,7 @@ public RootCommand GetRootCommand(ILogger logger)
492529
_installCertOption,
493530
_timeoutOption,
494531
_discoverOption,
532+
_envOption,
495533
.. optionsFromPlugins,
496534
],
497535
urlsToWatch,

0 commit comments

Comments
 (0)