diff --git a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
index 0e91132c8..fa09a5b05 100644
--- a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
@@ -69,6 +69,11 @@ public class ProxyAndRecordSettingsModel
///
public bool AppendGuidToSavedMappingFile { get; set; }
+ ///
+ /// Set prefix for saved mapping file.
+ ///
+ public string PrefixForSavedMappingFile { get; set; }
+
///
/// Defines the Replace Settings.
///
diff --git a/src/WireMock.Net/Serialization/MappingFileNameSanitizer.cs b/src/WireMock.Net/Serialization/MappingFileNameSanitizer.cs
new file mode 100644
index 000000000..8e5e73f09
--- /dev/null
+++ b/src/WireMock.Net/Serialization/MappingFileNameSanitizer.cs
@@ -0,0 +1,48 @@
+using System.IO;
+using System.Linq;
+using Stef.Validation;
+using WireMock.Settings;
+
+namespace WireMock.Serialization;
+
+///
+/// Creates sanitized file names for mappings
+///
+public class MappingFileNameSanitizer
+{
+ private const char ReplaceChar = '_';
+
+ private readonly WireMockServerSettings _settings;
+
+ public MappingFileNameSanitizer(WireMockServerSettings settings)
+ {
+ _settings = Guard.NotNull(settings);
+ }
+
+ ///
+ /// Creates sanitized file names for mappings
+ ///
+ public string BuildSanitizedFileName(IMapping mapping)
+ {
+ string name;
+ if (!string.IsNullOrEmpty(mapping.Title))
+ {
+ // remove 'Proxy Mapping for ' and an extra space character after the HTTP request method
+ name = mapping.Title.Replace(ProxyAndRecordSettings.DefaultPrefixForSavedMappingFile, "").Replace(' '.ToString(), string.Empty);
+ if (_settings.ProxyAndRecordSettings?.AppendGuidToSavedMappingFile == true)
+ {
+ name += $"{ReplaceChar}{mapping.Guid}";
+ }
+ }
+ else
+ {
+ name = mapping.Guid.ToString();
+ }
+
+ if (!string.IsNullOrEmpty(_settings.ProxyAndRecordSettings?.PrefixForSavedMappingFile))
+ {
+ name = $"{_settings.ProxyAndRecordSettings.PrefixForSavedMappingFile}{ReplaceChar}{name}";
+ }
+ return $"{Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, ReplaceChar))}.json";
+ }
+}
diff --git a/src/WireMock.Net/Serialization/MappingToFileSaver.cs b/src/WireMock.Net/Serialization/MappingToFileSaver.cs
index dcaa29127..7b548299b 100644
--- a/src/WireMock.Net/Serialization/MappingToFileSaver.cs
+++ b/src/WireMock.Net/Serialization/MappingToFileSaver.cs
@@ -10,11 +10,13 @@ internal class MappingToFileSaver
{
private readonly WireMockServerSettings _settings;
private readonly MappingConverter _mappingConverter;
+ private readonly MappingFileNameSanitizer _fileNameSanitizer;
public MappingToFileSaver(WireMockServerSettings settings, MappingConverter mappingConverter)
{
_settings = Guard.NotNull(settings);
_mappingConverter = Guard.NotNull(mappingConverter);
+ _fileNameSanitizer = new MappingFileNameSanitizer(settings);
}
public void SaveMappingsToFile(IMapping[] mappings, string? folder = null)
@@ -42,7 +44,7 @@ public void SaveMappingToFile(IMapping mapping, string? folder = null)
var model = _mappingConverter.ToMappingModel(mapping);
- var filename = BuildSanitizedFileName(mapping);
+ var filename = _fileNameSanitizer.BuildSanitizedFileName(mapping);
var path = Path.Combine(folder, filename);
Save(model, path);
@@ -54,23 +56,4 @@ private void Save(object value, string path)
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsDefault));
}
-
- private string BuildSanitizedFileName(IMapping mapping, char replaceChar = '_')
- {
- string name;
- if (!string.IsNullOrEmpty(mapping.Title))
- {
- name = mapping.Title!;
- if (_settings.ProxyAndRecordSettings?.AppendGuidToSavedMappingFile == true)
- {
- name += $"{replaceChar}{mapping.Guid}";
- }
- }
- else
- {
- name = mapping.Guid.ToString();
- }
-
- return $"{Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar))}.json";
- }
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
index eb8631636..ed1b4febe 100644
--- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
+++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
@@ -7,6 +7,11 @@ namespace WireMock.Settings;
///
public class ProxyAndRecordSettings : HttpClientSettings
{
+ ///
+ /// Default prefix value for saved mapping file
+ ///
+ public const string DefaultPrefixForSavedMappingFile = "Proxy Mapping for ";
+
///
/// The URL to proxy.
///
@@ -95,9 +100,14 @@ public string SaveMappingForStatusCodePattern
///
public bool AppendGuidToSavedMappingFile { get; set; }
+ ///
+ /// Set prefix for saved mapping file.
+ ///
+ public string PrefixForSavedMappingFile { get; set; } = DefaultPrefixForSavedMappingFile;
+
///
/// Proxy all Api calls, irrespective of any condition
///
[PublicAPI]
public bool ProxyAll { get; set; } = false;
-}
\ No newline at end of file
+}
diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
index 962b94812..65a581200 100644
--- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
+++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
@@ -120,6 +120,7 @@ private static void ParseProxyAndRecordSettings(WireMockServerSettings settings,
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
+ PrefixForSavedMappingFile = parser.GetStringValue(nameof(ProxyAndRecordSettings.PrefixForSavedMappingFile), null),
Url = proxyUrl!,
SaveMappingSettings = new ProxySaveMappingSettings
{
diff --git a/test/WireMock.Net.Tests/Serialization/MappingFileNameSanitizerTests.cs b/test/WireMock.Net.Tests/Serialization/MappingFileNameSanitizerTests.cs
new file mode 100644
index 000000000..2c0db1a42
--- /dev/null
+++ b/test/WireMock.Net.Tests/Serialization/MappingFileNameSanitizerTests.cs
@@ -0,0 +1,160 @@
+using System;
+using Moq;
+using WireMock.Serialization;
+using WireMock.Settings;
+using Xunit;
+
+namespace WireMock.Net.Tests.Serialization;
+
+public class MappingFileNameSanitizerTests
+{
+ private const string MappingGuid = "ce216a13-e7d6-42d7-91ac-8ae709e2add1";
+ private const string MappingTitle = "Proxy Mapping for POST _ordermanagement_v1_orders_cancel";
+
+ [Fact]
+ public void BuildSanitizedFileName_WithTitleAndGuid_AppendsGuid()
+ {
+ // Arrange
+ var mappingMock = new Mock();
+ mappingMock.Setup(m => m.Title).Returns(MappingTitle);
+ mappingMock.Setup(m => m.Guid).Returns(new Guid(MappingGuid));
+
+ var settings = new WireMockServerSettings
+ {
+ ProxyAndRecordSettings = new ProxyAndRecordSettings
+ {
+ AppendGuidToSavedMappingFile = true
+ }
+ };
+
+ var sanitizer = new MappingFileNameSanitizer(settings);
+
+ // Act
+ var result = sanitizer.BuildSanitizedFileName(mappingMock.Object);
+
+ // Assert
+ Assert.Equal($"Proxy Mapping for _POST_ordermanagement_v1_orders_cancel_{MappingGuid}.json", result);
+ }
+
+ [Fact]
+ public void BuildSanitizedFileName_WithoutTitle_UsesGuid()
+ {
+ // Arrange
+ var mappingMock = new Mock();
+ mappingMock.Setup(m => m.Title).Returns((string?)null);
+ mappingMock.Setup(m => m.Guid).Returns(new Guid(MappingGuid));
+
+ var settings = new WireMockServerSettings
+ {
+ ProxyAndRecordSettings = new ()
+ };
+ var sanitizer = new MappingFileNameSanitizer(settings);
+
+ // Act
+ var result = sanitizer.BuildSanitizedFileName(mappingMock.Object);
+
+ // Assert
+ Assert.Equal($"Proxy Mapping for _{MappingGuid}.json", result);
+ }
+
+ [Fact]
+ public void BuildSanitizedFileName_WithTitleAndGuid_NoAppendGuidSetting()
+ {
+ // Arrange
+ var mappingMock = new Mock();
+ mappingMock.Setup(m => m.Title).Returns(MappingTitle);
+ mappingMock.Setup(m => m.Guid).Returns(new Guid(MappingGuid));
+
+ var settings = new WireMockServerSettings
+ {
+ ProxyAndRecordSettings = new ProxyAndRecordSettings
+ {
+ AppendGuidToSavedMappingFile = false
+ }
+ };
+
+ var sanitizer = new MappingFileNameSanitizer(settings);
+
+ // Act
+ var result = sanitizer.BuildSanitizedFileName(mappingMock.Object);
+
+ // Assert
+ Assert.Equal("Proxy Mapping for _POST_ordermanagement_v1_orders_cancel.json", result);
+ }
+
+ [Fact]
+ public void BuildSanitizedFileName_WithPrefix_AddsPrefix()
+ {
+ // Arrange
+ var mappingMock = new Mock();
+ mappingMock.Setup(m => m.Title).Returns(MappingTitle);
+ mappingMock.Setup(m => m.Guid).Returns(new Guid(MappingGuid));
+
+ var settings = new WireMockServerSettings
+ {
+ ProxyAndRecordSettings = new ProxyAndRecordSettings
+ {
+ PrefixForSavedMappingFile = "Prefix"
+ }
+ };
+
+ var sanitizer = new MappingFileNameSanitizer(settings);
+
+ // Act
+ var result = sanitizer.BuildSanitizedFileName(mappingMock.Object);
+
+ // Assert
+ Assert.Equal($"Prefix_POST_ordermanagement_v1_orders_cancel.json", result);
+ }
+
+ [Fact]
+ public void BuildSanitizedFileName_WithPrefix_AddsPrefixEmptyString()
+ {
+ // Arrange
+ var mappingMock = new Mock();
+ mappingMock.Setup(m => m.Title).Returns(MappingTitle);
+ mappingMock.Setup(m => m.Guid).Returns(new Guid(MappingGuid));
+
+ var settings = new WireMockServerSettings
+ {
+ ProxyAndRecordSettings = new ProxyAndRecordSettings
+ {
+ PrefixForSavedMappingFile = string.Empty
+ }
+ };
+
+ var sanitizer = new MappingFileNameSanitizer(settings);
+
+ // Act
+ var result = sanitizer.BuildSanitizedFileName(mappingMock.Object);
+
+ // Assert
+ Assert.Equal($"POST_ordermanagement_v1_orders_cancel.json", result);
+ }
+
+ [Fact]
+ public void BuildSanitizedFileName_WithTitleAndGuid_WithPrefixAndAppendGuidSetting()
+ {
+ // Arrange
+ var mappingMock = new Mock();
+ mappingMock.Setup(m => m.Title).Returns(MappingTitle);
+ mappingMock.Setup(m => m.Guid).Returns(new Guid(MappingGuid));
+
+ var settings = new WireMockServerSettings
+ {
+ ProxyAndRecordSettings = new ProxyAndRecordSettings
+ {
+ PrefixForSavedMappingFile = "Prefix",
+ AppendGuidToSavedMappingFile = true
+ }
+ };
+
+ var sanitizer = new MappingFileNameSanitizer(settings);
+
+ // Act
+ var result = sanitizer.BuildSanitizedFileName(mappingMock.Object);
+
+ // Assert
+ Assert.Equal($"Prefix_POST_ordermanagement_v1_orders_cancel_{MappingGuid}.json", result);
+ }
+}
diff --git a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
index 95f85e0cd..1d7240eca 100644
--- a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
+++ b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
@@ -174,7 +174,7 @@ public async Task WireMockServer_Proxy_With_SaveMappingToFile_Is_True_ShouldSave
server.Mappings.Should().HaveCount(2);
// Verify
- fileSystemHandlerMock.Verify(f => f.WriteMappingFile($"m{System.IO.Path.DirectorySeparatorChar}{title}.json", It.IsRegex(stringBody)), Times.Once);
+ fileSystemHandlerMock.Verify(f => f.WriteMappingFile($"m{System.IO.Path.DirectorySeparatorChar}Proxy Mapping for _{title}.json", It.IsRegex(stringBody)), Times.Once);
}
[Fact]