Skip to content

Commit da09a89

Browse files
Fixes hot reload of CRUD API data file. Closes #1390 (#1484)
* Fixes hot reload of CRUD API data file. Closes #1390 * Update DevProxy.Plugins/Mocking/CrudApiDataLoader.cs Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 456beab commit da09a89

File tree

3 files changed

+61
-26
lines changed

3 files changed

+61
-26
lines changed

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;
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 ||

0 commit comments

Comments
 (0)