Compare commits

..

3 Commits

Author SHA1 Message Date
Stef Heyenrath 5483fecd4f When Response.Create() is called without a pre-built ResponseMessage, use the DateTime.UtcNow 2026-07-07 21:01:56 +02:00
Stef Heyenrath 4666757df9 Upgrade RamlToOpenApiConverter.SourceOnly to 0.21.0 (#1480) 2026-07-03 21:04:58 +02:00
Stef Heyenrath 49dcbda647 Upgrade Microsoft.OpenApi to 3.7.0 and YamlDotNet to 18.1.0 (#1479)
* Upgrade Microsoft.OpenApi to 3.7.0 and YamlDotNet to 18.1.0

* fix
2026-07-01 22:44:34 +02:00
11 changed files with 39 additions and 268 deletions
@@ -1,6 +1,8 @@
// Copyright © WireMock.Net
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using log4net;
using log4net.Config;
using log4net.Repository;
@@ -10,7 +12,7 @@ namespace WireMock.Net.Console.NET8;
static class Program
{
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()!);
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
static async Task Main(params string[] args)
@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" />
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="3.7.0" />
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj" />
@@ -14,10 +14,7 @@
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="Microsoft.OpenApi" Version="2.3.0" PrivateAssets="All" />
<!--<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" PrivateAssets="All" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="SharpYaml" Version="2.1.3" />-->
<PackageReference Include="Microsoft.OpenApi" Version="3.7.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
@@ -70,7 +70,7 @@ public class WireMockService : IWireMockService
_server = WireMockServer.Start(_settings);
_logger.LogInformation("WireMock.Net server settings {Settings}", JsonConvert.SerializeObject(_settings));
_logger.LogInformation($"WireMock.Net server settings {JsonConvert.SerializeObject(_settings)}");
}
public void Stop()
@@ -9,16 +9,17 @@ using WireMock.Util;
namespace WireMock.Owin;
internal class WireMockMiddlewareLogger(
IWireMockMiddlewareOptions _options,
LogEntryMapper _logEntryMapper,
IGuidUtils _guidUtils
IWireMockMiddlewareOptions options,
LogEntryMapper logEntryMapper,
IGuidUtils guidUtils,
IDateTimeUtils dateTimeUtils
) : IWireMockMiddlewareLogger
{
public void LogRequestAndResponse(bool logRequest, RequestMessage request, IResponseMessage? response, MappingMatcherResult? match, MappingMatcherResult? partialMatch, Activity? activity)
{
var logEntry = new LogEntry
{
Guid = _guidUtils.NewGuid(),
Guid = guidUtils.NewGuid(),
RequestMessage = request,
ResponseMessage = response,
@@ -31,17 +32,17 @@ internal class WireMockMiddlewareLogger(
PartialMatchResult = partialMatch?.RequestMatchResult
};
WireMockActivitySource.EnrichWithLogEntry(activity, logEntry, _options.ActivityTracingOptions);
WireMockActivitySource.EnrichWithLogEntry(activity, logEntry, options.ActivityTracingOptions);
activity?.Dispose();
LogLogEntry(logEntry, logRequest);
try
{
if (_options.SaveUnmatchedRequests == true && match?.RequestMatchResult is not { IsPerfectMatch: true })
if (options.SaveUnmatchedRequests == true && match?.RequestMatchResult is not { IsPerfectMatch: true })
{
var filename = $"{logEntry.Guid}.LogEntry.json";
_options.FileSystemHandler?.WriteUnmatchedRequest(filename, _options.DefaultJsonSerializer.Serialize(logEntry));
options.FileSystemHandler?.WriteUnmatchedRequest(filename, options.DefaultJsonSerializer.Serialize(logEntry));
}
}
catch
@@ -52,34 +53,34 @@ internal class WireMockMiddlewareLogger(
public void LogLogEntry(LogEntry entry, bool addRequest)
{
_options.Logger.DebugRequestResponse(_logEntryMapper.Map(entry), entry.RequestMessage?.Path.StartsWith("/__admin/") == true);
options.Logger.DebugRequestResponse(logEntryMapper.Map(entry), entry.RequestMessage?.Path.StartsWith("/__admin/") == true);
// If addRequest is set to true and MaxRequestLogCount is null or does have a value greater than 0, try to add a new request log.
if (addRequest && _options.MaxRequestLogCount is null or > 0)
if (addRequest && options.MaxRequestLogCount is null or > 0)
{
TryAddLogEntry(entry);
}
// In case MaxRequestLogCount has a value greater than 0, try to delete existing request logs based on the count.
if (_options.MaxRequestLogCount is > 0)
if (options.MaxRequestLogCount is > 0)
{
var logEntries = _options.LogEntries.ToList();
var logEntries = options.LogEntries.ToList();
foreach (var logEntry in logEntries
.OrderBy(le => le.RequestMessage?.DateTime ?? le.ResponseMessage?.DateTime)
.Take(logEntries.Count - _options.MaxRequestLogCount.Value))
.Take(logEntries.Count - options.MaxRequestLogCount.Value))
{
TryRemoveLogEntry(logEntry);
}
}
// In case RequestLogExpirationDuration has a value greater than 0, try to delete existing request logs based on the date.
if (_options.RequestLogExpirationDuration is > 0)
if (options.RequestLogExpirationDuration is > 0)
{
var logEntries = _options.LogEntries.ToList();
var logEntries = options.LogEntries.ToList();
var checkTime = DateTime.UtcNow.AddHours(-_options.RequestLogExpirationDuration.Value);
var checkTime = dateTimeUtils.UtcNow.AddHours(-options.RequestLogExpirationDuration.Value);
foreach (var logEntry in logEntries.Where(le => le.RequestMessage?.DateTime < checkTime || le.ResponseMessage?.DateTime < checkTime))
{
TryRemoveLogEntry(logEntry);
@@ -91,7 +92,7 @@ internal class WireMockMiddlewareLogger(
{
try
{
_options.LogEntries.Add(logEntry);
options.LogEntries.Add(logEntry);
}
catch
{
@@ -103,7 +104,7 @@ internal class WireMockMiddlewareLogger(
{
try
{
_options.LogEntries.Remove(logEntry);
options.LogEntries.Remove(logEntry);
}
catch
{
@@ -11,7 +11,6 @@ using WireMock.RequestBuilders;
using WireMock.Settings;
using WireMock.Transformers;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.ResponseBuilders;
@@ -86,7 +85,11 @@ public partial class Response : IResponseBuilder
[PublicAPI]
public static IResponseBuilder Create(ResponseMessage? responseMessage = null)
{
var message = responseMessage ?? new ResponseMessage();
var message = responseMessage ?? new ResponseMessage
{
DateTime = DateTime.UtcNow // We set it here to the current time.
};
return new Response(message);
}
@@ -159,7 +159,7 @@ internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
};
}
private static bool TryGetContent(IDictionary<string, OpenApiMediaType>? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
private static bool TryGetContent(IDictionary<string, IOpenApiMediaType>? contents, [NotNullWhen(true)] out IOpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
{
openApiMediaType = null;
contentType = null;
@@ -40,21 +40,21 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="RamlToOpenApiConverter.SourceOnly" Version="0.11.0" />
<PackageReference Include="YamlDotNet" Version="16.3.0" />
<PackageReference Include="RamlToOpenApiConverter.SourceOnly" Version="0.21.0" />
<PackageReference Include="YamlDotNet" Version="18.1.0" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.19.1" />
<PackageReference Include="Stef.Validation" Version="0.3.0" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" />
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="3.7.0" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="ILRepack.Lib.MSBuild.Task" Version="2.0.43" PrivateAssets="All" />
<PackageReference Include="Microsoft.OpenApi" Version="2.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" PrivateAssets="All" />
<PackageReference Include="SharpYaml" Version="2.1.3" />
<PackageReference Include="Microsoft.OpenApi" Version="3.7.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="3.7.0" PrivateAssets="All" />
<PackageReference Include="SharpYaml" Version="2.1.4" />
</ItemGroup>
<ItemGroup>
@@ -357,7 +357,6 @@ public class WireMockServerSettings
/// Default is <see cref="NewtonsoftJsonConverter"/>.
/// </remarks>
[PublicAPI]
[JsonConverter(typeof(WireMockSettingsJsonConverter))]
public IJsonConverter DefaultJsonSerializer { get; set; }
/// <summary>
@@ -368,7 +367,7 @@ public class WireMockServerSettings
/// Default is <see cref="NewtonsoftJsonBodyTransformer"/>.
/// </remarks>
[PublicAPI]
[JsonConverter(typeof(WireMockSettingsJsonConverter))]
[JsonIgnore]
public IJsonBodyTransformer DefaultJsonBodyTransformer { get; set; }
/// <summary>
@@ -1,151 +0,0 @@
// Copyright © WireMock.Net
using System.Reflection;
using JsonConverter.Abstractions;
using JsonConverter.Newtonsoft.Json;
using JsonConverter.System.Text.Json;
using Newtonsoft.Json;
using WireMock.Transformers;
using NewtonsoftJsonConverterBase = Newtonsoft.Json.JsonConverter;
namespace WireMock.Settings;
/// <summary>
/// WireMockSettingsJsonConverter serializes and deserializes the special interface-based settings properties as type names.
/// </summary>
public class WireMockSettingsJsonConverter : NewtonsoftJsonConverterBase
{
/// <inheritdoc />
public override bool CanConvert(Type objectType)
{
return typeof(IJsonConverter).IsAssignableFrom(objectType) || typeof(IJsonBodyTransformer).IsAssignableFrom(objectType);
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
writer.WriteValue(value?.GetType().FullName);
}
/// <inheritdoc />
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue ?? CreateDefaultValue(objectType);
}
if (reader.TokenType != JsonToken.String)
{
reader.Skip();
return existingValue ?? CreateDefaultValue(objectType);
}
var typeName = reader.Value as string;
if (typeof(IJsonConverter).IsAssignableFrom(objectType))
{
return CreateJsonConverter(typeName) ?? existingValue ?? new NewtonsoftJsonConverter();
}
if (typeof(IJsonBodyTransformer).IsAssignableFrom(objectType))
{
var settings = ExtractSettings(existingValue as IJsonBodyTransformer);
return settings != null
? CreateJsonBodyTransformer(typeName, settings) ?? existingValue ?? new NewtonsoftJsonBodyTransformer(settings)
: existingValue;
}
return existingValue;
}
private static object? CreateDefaultValue(Type objectType)
{
if (typeof(IJsonConverter).IsAssignableFrom(objectType))
{
return new NewtonsoftJsonConverter();
}
return null;
}
private static IJsonConverter? CreateJsonConverter(string? typeName)
{
if (string.IsNullOrWhiteSpace(typeName))
{
return null;
}
if (typeName == nameof(NewtonsoftJsonConverter) || typeName == typeof(NewtonsoftJsonConverter).FullName)
{
return new NewtonsoftJsonConverter();
}
if (typeName == nameof(SystemTextJsonConverter) || typeName == typeof(SystemTextJsonConverter).FullName)
{
return new SystemTextJsonConverter();
}
var type = GetType(typeName);
if (type == null || !typeof(IJsonConverter).IsAssignableFrom(type))
{
return null;
}
return Activator.CreateInstance(type) as IJsonConverter;
}
private static IJsonBodyTransformer? CreateJsonBodyTransformer(string? typeName, WireMockServerSettings settings)
{
if (string.IsNullOrWhiteSpace(typeName))
{
return null;
}
if (typeName == nameof(NewtonsoftJsonBodyTransformer) || typeName == typeof(NewtonsoftJsonBodyTransformer).FullName)
{
return new NewtonsoftJsonBodyTransformer(settings);
}
if (typeName == nameof(SystemTextJsonBodyTransformer) || typeName == typeof(SystemTextJsonBodyTransformer).FullName)
{
return new SystemTextJsonBodyTransformer(settings);
}
var type = GetType(typeName);
if (type == null || !typeof(IJsonBodyTransformer).IsAssignableFrom(type))
{
return null;
}
var constructorWithSettings = type.GetConstructor([typeof(WireMockServerSettings)]);
if (constructorWithSettings != null)
{
return constructorWithSettings.Invoke([settings]) as IJsonBodyTransformer;
}
return Activator.CreateInstance(type) as IJsonBodyTransformer;
}
private static WireMockServerSettings? ExtractSettings(IJsonBodyTransformer? existingValue)
{
if (existingValue == null)
{
return null;
}
return existingValue.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.Where(field => typeof(WireMockServerSettings).IsAssignableFrom(field.FieldType))
.Select(field => field.GetValue(existingValue) as WireMockServerSettings)
.FirstOrDefault(settings => settings != null);
}
private static Type? GetType(string typeName)
{
return Type.GetType(typeName, throwOnError: false) ??
AppDomain.CurrentDomain.GetAssemblies()
.Select(assembly => assembly.GetType(typeName, throwOnError: false))
.FirstOrDefault(foundType => foundType != null);
}
}
+2
View File
@@ -1,5 +1,7 @@
// Copyright © WireMock.Net
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using WireMock.Logging;
using WireMock.Net.StandAlone;
@@ -1,82 +0,0 @@
// Copyright © WireMock.Net
using JsonConverter.Newtonsoft.Json;
using JsonConverter.System.Text.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WireMock.Settings;
using WireMock.Transformers;
namespace WireMock.Net.Tests.Settings;
public class WireMockServerSettingsJsonTests
{
[Fact]
public void NewtonsoftJson_Serialize_WireMockServerSettings_HandlesSpecialProperties_AndKeepsOtherPropertiesAsIs()
{
// Arrange
var settings = new WireMockServerSettings
{
Port = 1234,
AdminPath = "/custom-admin",
StartAdminInterface = true,
DefaultJsonSerializer = new SystemTextJsonConverter()
};
settings.DefaultJsonBodyTransformer = new SystemTextJsonBodyTransformer(settings);
// Act
var json = JsonConvert.SerializeObject(settings);
var jsonObject = JObject.Parse(json);
// Assert
jsonObject[nameof(WireMockServerSettings.Port)]!.Value<int>().Should().Be(1234);
jsonObject[nameof(WireMockServerSettings.AdminPath)]!.Value<string>().Should().Be("/custom-admin");
jsonObject[nameof(WireMockServerSettings.StartAdminInterface)]!.Value<bool>().Should().BeTrue();
jsonObject[nameof(WireMockServerSettings.DefaultJsonSerializer)]!.Type.Should().Be(JTokenType.String);
jsonObject[nameof(WireMockServerSettings.DefaultJsonSerializer)]!.Value<string>().Should().Be(typeof(SystemTextJsonConverter).FullName);
jsonObject[nameof(WireMockServerSettings.DefaultJsonBodyTransformer)]!.Type.Should().Be(JTokenType.String);
jsonObject[nameof(WireMockServerSettings.DefaultJsonBodyTransformer)]!.Value<string>().Should().Be(typeof(SystemTextJsonBodyTransformer).FullName);
}
[Fact]
public void NewtonsoftJson_Deserialize_WireMockServerSettings_HandlesSpecialProperties_AndKeepsOtherPropertiesAsIs()
{
// Arrange
var json =
$"{{\"{nameof(WireMockServerSettings.Port)}\":4321," +
$"\"{nameof(WireMockServerSettings.AdminPath)}\":\"/custom-admin\"," +
$"\"{nameof(WireMockServerSettings.StartAdminInterface)}\":true," +
$"\"{nameof(WireMockServerSettings.DefaultJsonSerializer)}\":\"{typeof(SystemTextJsonConverter).FullName}\"," +
$"\"{nameof(WireMockServerSettings.DefaultJsonBodyTransformer)}\":\"{typeof(SystemTextJsonBodyTransformer).FullName}\"}}";
// Act
var settings = JsonConvert.DeserializeObject<WireMockServerSettings>(json);
// Assert
settings.Should().NotBeNull();
settings!.Port.Should().Be(4321);
settings.AdminPath.Should().Be("/custom-admin");
settings.StartAdminInterface.Should().BeTrue();
settings.DefaultJsonSerializer.Should().BeOfType<SystemTextJsonConverter>();
settings.DefaultJsonBodyTransformer.Should().BeOfType<SystemTextJsonBodyTransformer>();
}
[Fact]
public void NewtonsoftJson_Deserialize_WireMockServerSettings_LegacyObjects_AreIgnored_AndOtherPropertiesStayUntouched()
{
// Arrange
var json =
$"{{\"{nameof(WireMockServerSettings.Port)}\":9876," +
$"\"{nameof(WireMockServerSettings.DefaultJsonSerializer)}\":{{}}," +
$"\"{nameof(WireMockServerSettings.DefaultJsonBodyTransformer)}\":{{}}}}";
// Act
var settings = JsonConvert.DeserializeObject<WireMockServerSettings>(json);
// Assert
settings.Should().NotBeNull();
settings!.Port.Should().Be(9876);
settings.DefaultJsonSerializer.Should().BeOfType<NewtonsoftJsonConverter>();
settings.DefaultJsonBodyTransformer.Should().BeOfType<NewtonsoftJsonBodyTransformer>();
}
}