Skip to content

Commit d329b3e

Browse files
Merge branch 'main' into stdio
2 parents 8412c6d + 4568cf2 commit d329b3e

7 files changed

Lines changed: 82 additions & 28 deletions

File tree

DevProxy.Abstractions/Plugins/BaseLoader.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ private async Task<bool> ValidateFileContentsAsync(string fileContents, Cancella
7979
using var document = JsonDocument.Parse(fileContents, ProxyUtils.JsonDocumentOptions);
8080
var root = document.RootElement;
8181

82-
if (!root.TryGetProperty("$schema", out var schemaUrlElement))
82+
// Schema validation only applies to JSON objects, not arrays
83+
if (root.ValueKind != JsonValueKind.Object ||
84+
!root.TryGetProperty("$schema", out var schemaUrlElement))
8385
{
8486
Logger.LogDebug("Schema reference not found in file {File}. Skipping schema validation", FilePath);
8587
return true;

DevProxy.Abstractions/Proxy/IProxyConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public enum ReleaseType
2020

2121
public interface IProxyConfiguration
2222
{
23-
int ApiPort { get; }
23+
int ApiPort { get; set; }
2424
bool AsSystemProxy { get; set; }
2525
string ConfigFile { get; }
2626
#pragma warning disable CA2227
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using DevProxy.Abstractions.Plugins;
6+
using DevProxy.Abstractions.Proxy;
7+
using DevProxy.Abstractions.Utils;
8+
using Microsoft.Extensions.Logging;
9+
using Newtonsoft.Json.Linq;
10+
11+
namespace DevProxy.Plugins.Mocking;
12+
13+
internal sealed class CrudApiDataLoader(
14+
HttpClient httpClient,
15+
ILogger<CrudApiDataLoader> logger,
16+
CrudApiConfiguration configuration,
17+
IProxyConfiguration proxyConfiguration,
18+
Action<JArray?> onDataLoaded) :
19+
BaseLoader(httpClient, logger, proxyConfiguration)
20+
{
21+
private readonly CrudApiConfiguration _configuration = configuration;
22+
private readonly Action<JArray?> _onDataLoaded = onDataLoaded;
23+
24+
protected override string FilePath =>
25+
Path.GetFullPath(
26+
ProxyUtils.ReplacePathTokens(_configuration.DataFile),
27+
Path.GetDirectoryName(_configuration.ApiFile) ?? string.Empty
28+
);
29+
30+
protected override void LoadData(string fileContents)
31+
{
32+
try
33+
{
34+
var data = JArray.Parse(fileContents);
35+
_onDataLoaded(data);
36+
Logger.LogInformation(
37+
"Data for CRUD API loaded from {DataFile} for API {ApiFile}",
38+
_configuration.DataFile,
39+
_configuration.ApiFile);
40+
}
41+
catch (Exception ex)
42+
{
43+
Logger.LogError(ex, "An error has occurred while reading {DataFile}", _configuration.DataFile);
44+
_onDataLoaded(null);
45+
}
46+
}
47+
}

DevProxy.Plugins/Mocking/CrudApiPlugin.cs

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ public sealed class CrudApiPlugin(
9090
proxyConfiguration,
9191
pluginConfigurationSection)
9292
{
93-
private CrudApiDefinitionLoader? _loader;
93+
private CrudApiDefinitionLoader? _definitionLoader;
94+
private CrudApiDataLoader? _dataLoader;
9495
private JArray? _data;
9596
private OpenIdConnectConfiguration? _openIdConnectConfiguration;
9697

@@ -104,8 +105,8 @@ public override async Task InitializeAsync(InitArgs e, CancellationToken cancell
104105

105106
Configuration.ApiFile = ProxyUtils.GetFullPath(Configuration.ApiFile, ProxyConfiguration.ConfigFile);
106107

107-
_loader = ActivatorUtilities.CreateInstance<CrudApiDefinitionLoader>(e.ServiceProvider, Configuration);
108-
await _loader.InitFileWatcherAsync(cancellationToken);
108+
_definitionLoader = ActivatorUtilities.CreateInstance<CrudApiDefinitionLoader>(e.ServiceProvider, Configuration);
109+
await _definitionLoader.InitFileWatcherAsync(cancellationToken);
109110

110111
if (Configuration.Auth == CrudApiAuthType.Entra &&
111112
Configuration.EntraAuthConfig is null)
@@ -126,7 +127,13 @@ public override async Task InitializeAsync(InitArgs e, CancellationToken cancell
126127
return;
127128
}
128129

129-
LoadData();
130+
_dataLoader = ActivatorUtilities.CreateInstance<CrudApiDataLoader>(
131+
e.ServiceProvider,
132+
Configuration,
133+
(Action<JArray?>)(data => _data = data)
134+
);
135+
await _dataLoader.InitFileWatcherAsync(cancellationToken);
136+
130137
await SetupOpenIdConnectConfigurationAsync();
131138
}
132139

@@ -200,27 +207,6 @@ private async Task SetupOpenIdConnectConfigurationAsync()
200207
}
201208
}
202209

203-
private void LoadData()
204-
{
205-
try
206-
{
207-
var dataFilePath = Path.GetFullPath(ProxyUtils.ReplacePathTokens(Configuration.DataFile), Path.GetDirectoryName(Configuration.ApiFile) ?? string.Empty);
208-
if (!File.Exists(dataFilePath))
209-
{
210-
Logger.LogError("Data file '{DataFilePath}' does not exist. The {APIUrl} API will be disabled.", dataFilePath, Configuration.BaseUrl);
211-
Configuration.Actions = [];
212-
return;
213-
}
214-
215-
var dataString = File.ReadAllText(dataFilePath);
216-
_data = JArray.Parse(dataString);
217-
}
218-
catch (Exception ex)
219-
{
220-
Logger.LogError(ex, "An error has occurred while reading {ConfigFile}", Configuration.DataFile);
221-
}
222-
}
223-
224210
private (Action<SessionEventArgs, CrudApiAction, IDictionary<string, string>> handler, CrudApiAction action, IDictionary<string, string> parameters)? GetMatchingActionHandler(Request request)
225211
{
226212
if (Configuration.Actions is null ||

DevProxy/Commands/DevProxyCommand.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ sealed class DevProxyCommand : RootCommand
1818
private WebApplication? _app;
1919

2020
internal const string PortOptionName = "--port";
21+
internal const string ApiPortOptionName = "--api-port";
2122
internal const string IpAddressOptionName = "--ip-address";
2223
internal const string LogLevelOptionName = "--log-level";
2324
internal const string RecordOptionName = "--record";
@@ -237,6 +238,12 @@ private void ConfigureCommand()
237238
HelpName = "port"
238239
};
239240

241+
var apiPortOption = new Option<int?>(ApiPortOptionName)
242+
{
243+
Description = "The port for the Dev Proxy API to listen on",
244+
HelpName = "api-port"
245+
};
246+
240247
var recordOption = new Option<bool?>(RecordOptionName)
241248
{
242249
Description = "Use this option to record all request logs"
@@ -348,6 +355,7 @@ private void ConfigureCommand()
348355

349356
var options = new List<Option>
350357
{
358+
apiPortOption,
351359
asSystemProxyOption,
352360
configFileOption,
353361
discoverOption,
@@ -392,6 +400,11 @@ private void ConfigureFromOptions(ParseResult parseResult)
392400
{
393401
_proxyConfiguration.Port = port.Value;
394402
}
403+
var apiPort = parseResult.GetValueOrDefault<int?>(ApiPortOptionName);
404+
if (apiPort is not null)
405+
{
406+
_proxyConfiguration.ApiPort = apiPort.Value;
407+
}
395408
var ipAddress = parseResult.GetValueOrDefault<string?>(IpAddressOptionName);
396409
if (ipAddress is not null)
397410
{

DevProxy/Commands/DevProxyConfigOptions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public string? ConfigFile
1717
}
1818
}
1919

20+
public int? ApiPort => _parseResult?.GetValueOrDefault<int?>(DevProxyCommand.ApiPortOptionName);
2021
public bool Discover => _parseResult?.GetValueOrDefault<bool?>(DevProxyCommand.DiscoverOptionName) ?? false;
2122
public string? IPAddress => _parseResult?.GetValueOrDefault<string?>(DevProxyCommand.IpAddressOptionName);
2223
public LogLevel? LogLevel => _parseResult?.GetValueOrDefault<LogLevel?>(DevProxyCommand.LogLevelOptionName);
@@ -113,13 +114,16 @@ public DevProxyConfigOptions()
113114
}
114115
}
115116
};
117+
var apiPortOption = new Option<int?>(DevProxyCommand.ApiPortOptionName);
118+
116119
var discoverOption = new Option<bool>(DevProxyCommand.DiscoverOptionName, "--discover")
117120
{
118121
Arity = ArgumentArity.Zero
119122
};
120123

121124
var options = new List<Option>
122125
{
126+
apiPortOption,
123127
ipAddressOption,
124128
configFileOption,
125129
urlsToWatchOption,

DevProxy/Program.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ static WebApplication BuildApplication(DevProxyConfigOptions options)
2121
var ipAddress = options.IPAddress ??
2222
builder.Configuration.GetValue("ipAddress", defaultIpAddress) ??
2323
defaultIpAddress;
24+
var defaultApiPort = 8897;
25+
var apiPort = options.ApiPort ??
26+
builder.Configuration.GetValue("apiPort", defaultApiPort);
2427
_ = builder.WebHost.ConfigureKestrel(options =>
2528
{
26-
var apiPort = builder.Configuration.GetValue("apiPort", 8897);
2729
options.Listen(IPAddress.Parse(ipAddress), apiPort);
2830
});
2931

0 commit comments

Comments
 (0)