Update REST Admin interface to support "Get Mapping(s) as C# Code" (#878)

* Add /__admin/mappings/code endpoint

* api

* fix

* .

* fix

* .

* .

* .
This commit is contained in:
Stef Heyenrath
2023-01-29 10:24:58 +01:00
committed by GitHub
parent 0fc664b404
commit 7fe2c8af78
45 changed files with 900 additions and 448 deletions

View File

@@ -78,6 +78,11 @@ public interface IRequestMessage
/// </summary>
IDictionary<string, WireMockList<string>>? Query { get; }
/// <summary>
/// Gets the query.
/// </summary>
IDictionary<string, WireMockList<string>>? QueryIgnoreCase { get; }
/// <summary>
/// Gets the raw query.
/// </summary>

View File

@@ -3,212 +3,227 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using WireMock.Admin.Mappings;
using WireMock.Logging;
using WireMock.Types;
namespace WireMock.Server
namespace WireMock.Server;
/// <summary>
/// The fluent mock server interface.
/// </summary>
public interface IWireMockServer : IDisposable
{
/// <summary>
/// The fluent mock server interface.
/// Gets a value indicating whether this server is started.
/// </summary>
public interface IWireMockServer : IDisposable
{
/// <summary>
/// Gets a value indicating whether this server is started.
/// </summary>
bool IsStarted { get; }
bool IsStarted { get; }
/// <summary>
/// Gets the request logs.
/// </summary>
IEnumerable<ILogEntry> LogEntries { get; }
/// <summary>
/// Gets the request logs.
/// </summary>
IEnumerable<ILogEntry> LogEntries { get; }
/// <summary>
/// Gets the mappings as MappingModels.
/// </summary>
IEnumerable<MappingModel> MappingModels { get; }
/// <summary>
/// Gets the mappings as MappingModels.
/// </summary>
IEnumerable<MappingModel> MappingModels { get; }
// <summary>
// Gets the mappings.
// </summary>
//[PublicAPI]
//IEnumerable<IMapping> Mappings { get; }
// <summary>
// Gets the mappings.
// </summary>
//[PublicAPI]
//IEnumerable<IMapping> Mappings { get; }
/// <summary>
/// Gets the ports.
/// </summary>
List<int> Ports { get; }
/// <summary>
/// Gets the ports.
/// </summary>
List<int> Ports { get; }
/// <summary>
/// Gets the first port.
/// </summary>
int Port { get; }
/// <summary>
/// Gets the first port.
/// </summary>
int Port { get; }
/// <summary>
/// Gets the urls.
/// </summary>
string[] Urls { get; }
/// <summary>
/// Gets the urls.
/// </summary>
string[] Urls { get; }
/// <summary>
/// Gets the first url.
/// </summary>
string? Url { get; }
/// <summary>
/// Gets the first url.
/// </summary>
string? Url { get; }
/// <summary>
/// Gets the consumer.
/// </summary>
string? Consumer { get; }
/// <summary>
/// Gets the consumer.
/// </summary>
string? Consumer { get; }
/// <summary>
/// Gets the provider.
/// </summary>
string? Provider { get; }
/// <summary>
/// Gets the provider.
/// </summary>
string? Provider { get; }
//ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
//ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
/// <summary>
/// Occurs when [log entries changed].
/// </summary>
event NotifyCollectionChangedEventHandler LogEntriesChanged;
/// <summary>
/// Occurs when [log entries changed].
/// </summary>
event NotifyCollectionChangedEventHandler LogEntriesChanged;
/// <summary>
/// Adds a 'catch all mapping'
///
/// - matches all Paths and any Methods
/// - priority is set to 1000
/// - responds with a 404 "No matching mapping found"
/// </summary>
void AddCatchAllMapping();
/// <summary>
/// Adds a 'catch all mapping'
///
/// - matches all Paths and any Methods
/// - priority is set to 1000
/// - responds with a 404 "No matching mapping found"
/// </summary>
void AddCatchAllMapping();
/// <summary>
/// The add request processing delay.
/// </summary>
/// <param name="delay">The delay.</param>
void AddGlobalProcessingDelay(TimeSpan delay);
/// <summary>
/// The add request processing delay.
/// </summary>
/// <param name="delay">The delay.</param>
void AddGlobalProcessingDelay(TimeSpan delay);
/// <summary>
/// Allows the partial mapping.
/// </summary>
void AllowPartialMapping(bool allow = true);
/// <summary>
/// Allows the partial mapping.
/// </summary>
void AllowPartialMapping(bool allow = true);
/// <summary>
/// Deletes a LogEntry.
/// </summary>
/// <param name="guid">The unique identifier.</param>
bool DeleteLogEntry(Guid guid);
/// <summary>
/// Deletes a LogEntry.
/// </summary>
/// <param name="guid">The unique identifier.</param>
bool DeleteLogEntry(Guid guid);
/// <summary>
/// Deletes the mapping.
/// </summary>
/// <param name="guid">The unique identifier.</param>
bool DeleteMapping(Guid guid);
/// <summary>
/// Deletes the mapping.
/// </summary>
/// <param name="guid">The unique identifier.</param>
bool DeleteMapping(Guid guid);
//IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers);
//IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers);
// IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false);
// IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false);
/// <summary>
/// Reads a static mapping file and adds or updates a single mapping.
///
/// Calling this method manually forces WireMock.Net to read and apply the specified static mapping file.
/// </summary>
/// <param name="path">The path to the static mapping file.</param>
bool ReadStaticMappingAndAddOrUpdate(string path);
/// <summary>
/// Reads a static mapping file and adds or updates a single mapping.
///
/// Calling this method manually forces WireMock.Net to read and apply the specified static mapping file.
/// </summary>
/// <param name="path">The path to the static mapping file.</param>
bool ReadStaticMappingAndAddOrUpdate(string path);
/// <summary>
/// Reads the static mappings from a folder.
/// (This method is also used when WireMockServerSettings.ReadStaticMappings is set to true.
///
/// Calling this method manually forces WireMock.Net to read and apply all static mapping files in the specified folder.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
void ReadStaticMappings(string? folder = null);
/// <summary>
/// Reads the static mappings from a folder.
/// (This method is also used when WireMockServerSettings.ReadStaticMappings is set to true.
///
/// Calling this method manually forces WireMock.Net to read and apply all static mapping files in the specified folder.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
void ReadStaticMappings(string? folder = null);
/// <summary>
/// Removes the authentication.
/// </summary>
void RemoveAuthentication();
/// <summary>
/// Removes the authentication.
/// </summary>
void RemoveAuthentication();
/// <summary>
/// Resets LogEntries and Mappings.
/// </summary>
void Reset();
/// <summary>
/// Resets LogEntries and Mappings.
/// </summary>
void Reset();
/// <summary>
/// Resets the Mappings.
/// </summary>
void ResetMappings();
/// <summary>
/// Resets the Mappings.
/// </summary>
void ResetMappings();
/// <summary>
/// Resets all Scenarios.
/// </summary>
void ResetScenarios();
/// <summary>
/// Resets all Scenarios.
/// </summary>
void ResetScenarios();
/// <summary>
/// Resets a specific Scenario by the name.
/// </summary>
bool ResetScenario(string name);
/// <summary>
/// Resets a specific Scenario by the name.
/// </summary>
bool ResetScenario(string name);
/// <summary>
/// Resets the LogEntries.
/// </summary>
void ResetLogEntries();
/// <summary>
/// Resets the LogEntries.
/// </summary>
void ResetLogEntries();
/// <summary>
/// Saves the static mappings.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
void SaveStaticMappings(string? folder = null);
/// <summary>
/// Saves the static mappings.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
void SaveStaticMappings(string? folder = null);
/// <summary>
/// Sets the basic authentication.
/// </summary>
/// <param name="tenant">The Tenant.</param>
/// <param name="audience">The Audience or Resource.</param>
void SetAzureADAuthentication(string tenant, string audience);
/// <summary>
/// Sets the basic authentication.
/// </summary>
/// <param name="tenant">The Tenant.</param>
/// <param name="audience">The Audience or Resource.</param>
void SetAzureADAuthentication(string tenant, string audience);
/// <summary>
/// Sets the basic authentication.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
void SetBasicAuthentication(string username, string password);
/// <summary>
/// Sets the basic authentication.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
void SetBasicAuthentication(string username, string password);
/// <summary>
/// Sets the maximum RequestLog count.
/// </summary>
/// <param name="maxRequestLogCount">The maximum RequestLog count.</param>
void SetMaxRequestLogCount(int? maxRequestLogCount);
/// <summary>
/// Sets the maximum RequestLog count.
/// </summary>
/// <param name="maxRequestLogCount">The maximum RequestLog count.</param>
void SetMaxRequestLogCount(int? maxRequestLogCount);
/// <summary>
/// Sets RequestLog expiration in hours.
/// </summary>
/// <param name="requestLogExpirationDuration">The RequestLog expiration in hours.</param>
void SetRequestLogExpirationDuration(int? requestLogExpirationDuration);
/// <summary>
/// Sets RequestLog expiration in hours.
/// </summary>
/// <param name="requestLogExpirationDuration">The RequestLog expiration in hours.</param>
void SetRequestLogExpirationDuration(int? requestLogExpirationDuration);
/// <summary>
/// Stop this server.
/// </summary>
void Stop();
/// <summary>
/// Stop this server.
/// </summary>
void Stop();
/// <summary>
/// Watches the static mappings for changes.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
void WatchStaticMappings(string? folder = null);
/// <summary>
/// Watches the static mappings for changes.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
void WatchStaticMappings(string? folder = null);
/// <summary>
/// Register the mappings (via <see cref="MappingModel"/>).
///
/// This can be used if you have 1 or more <see cref="MappingModel"/> defined and want to register these in WireMock.Net directly instead of using the fluent syntax.
/// </summary>
/// <param name="mappings">The MappingModels</param>
IWireMockServer WithMapping(params MappingModel[] mappings);
/// <summary>
/// Register the mappings (via <see cref="MappingModel"/>).
///
/// This can be used if you have 1 or more <see cref="MappingModel"/> defined and want to register these in WireMock.Net directly instead of using the fluent syntax.
/// </summary>
/// <param name="mappings">The MappingModels</param>
IWireMockServer WithMapping(params MappingModel[] mappings);
/// <summary>
/// Register the mappings (via json string).
///
/// This can be used if you the mappings as json string defined and want to register these in WireMock.Net directly instead of using the fluent syntax.
/// </summary>
/// <param name="mappings">The mapping(s) as json string.</param>
IWireMockServer WithMapping(string mappings);
}
/// <summary>
/// Register the mappings (via json string).
///
/// This can be used if you the mappings as json string defined and want to register these in WireMock.Net directly instead of using the fluent syntax.
/// </summary>
/// <param name="mappings">The mapping(s) as json string.</param>
IWireMockServer WithMapping(string mappings);
/// <summary>
/// Get the C# code for a mapping.
/// </summary>
/// <param name="guid">The Mapping Guid.</param>
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
/// <returns>C# code (null in case the mapping is not found)</returns>
string? MappingToCSharpCode(Guid guid, MappingConverterType converterType);
/// <summary>
/// Get the C# code for all mappings.
/// </summary>
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
/// <returns>C# code</returns>
public string MappingsToCSharpCode(MappingConverterType converterType);
}

View File

@@ -7,6 +7,7 @@ using WireMock.Admin.Mappings;
using WireMock.Admin.Requests;
using WireMock.Admin.Scenarios;
using WireMock.Admin.Settings;
using WireMock.Types;
namespace WireMock.Client
{
@@ -52,6 +53,13 @@ namespace WireMock.Client
[Get("mappings")]
Task<IList<MappingModel>> GetMappingsAsync();
/// <summary>
/// Get the C# code from all mappings
/// </summary>
/// <returns>C# code</returns>
[Get("mappings/code")]
Task<string> GetMappingsCodeAsync([Query] MappingConverterType mappingConverterType = MappingConverterType.Server);
/// <summary>
/// Add a new mapping.
/// </summary>
@@ -97,6 +105,15 @@ namespace WireMock.Client
[Get("mappings/{guid}")]
Task<MappingModel> GetMappingAsync([Path] Guid guid);
/// <summary>
/// Get the C# code from a mapping based on the guid
/// </summary>
/// <param name="guid">The Guid</param>
/// <param name="mappingConverterType">The optional mappingConverterType (can be Server or Builder)</param>
/// <returns>C# code</returns>
[Get("mappings/code/{guid}")]
Task<string> GetMappingCodeAsync([Path] Guid guid, [Query] MappingConverterType mappingConverterType = MappingConverterType.Server);
/// <summary>
/// Update a mapping based on the guid
/// </summary>

View File

@@ -7,4 +7,5 @@ internal static class WireMockConstants
public const int ProxyPriority = -2_000_000;
public const string ContentTypeJson = "application/json";
public const string ContentTypeTextPlain = "text/plain";
}

View File

@@ -1,6 +1,8 @@
using System;
using WireMock.Admin.Mappings;
using WireMock.Matchers.Request;
using WireMock.Server;
using WireMock.Types;
namespace WireMock;
@@ -40,4 +42,19 @@ public interface IMappingBuilder
/// </summary>
/// <param name="folder">The folder to write the files to.</param>
void SaveMappingsToFolder(string folder);
/// <summary>
/// Get the C# code for a mapping.
/// </summary>
/// <param name="guid">The Mapping Guid.</param>
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
/// <returns>C# code (null in case the mapping is not found)</returns>
string? ToCSharpCode(Guid guid, MappingConverterType converterType);
/// <summary>
/// Get the C# code for all mappings.
/// </summary>
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
/// <returns>C# code</returns>
public string ToCSharpCode(MappingConverterType converterType);
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Stef.Validation;
using WireMock.Admin.Mappings;
@@ -7,6 +9,7 @@ using WireMock.Owin;
using WireMock.Serialization;
using WireMock.Server;
using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
namespace WireMock;
@@ -66,10 +69,12 @@ public class MappingBuilder : IMappingBuilder
/// <inheritdoc />
public MappingModel[] GetMappings()
{
return _options.Mappings.Values.ToArray()
.Where(m => !m.IsAdminInterface)
.Select(_mappingConverter.ToMappingModel)
.ToArray();
return GetMappingsInternal().Select(_mappingConverter.ToMappingModel).ToArray();
}
internal IMapping[] GetMappingsInternal()
{
return _options.Mappings.Values.ToArray().Where(m => !m.IsAdminInterface).ToArray();
}
/// <inheritdoc />
@@ -78,6 +83,37 @@ public class MappingBuilder : IMappingBuilder
return ToJson(GetMappings());
}
/// <inheritdoc />
public string? ToCSharpCode(Guid guid, MappingConverterType converterType)
{
var mapping = GetMappingsInternal().FirstOrDefault(m => m.Guid == guid);
if (mapping is null)
{
return null;
}
var settings = new MappingConverterSettings { AddStart = true, ConverterType = converterType };
return _mappingConverter.ToCSharpCode(mapping, settings);
}
/// <inheritdoc />
public string ToCSharpCode(MappingConverterType converterType)
{
var sb = new StringBuilder();
bool addStart = true;
foreach (var mapping in GetMappingsInternal())
{
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
if (addStart)
{
addStart = false;
}
}
return sb.ToString();
}
/// <inheritdoc />
public void SaveMappingsToFile(string path)
{

View File

@@ -56,9 +56,12 @@ public class RequestMessage : IRequestMessage
/// <inheritdoc cref="IRequestMessage.Cookies" />
public IDictionary<string, string>? Cookies { get; }
/// <inheritdoc cref="IRequestMessage.Query" />
/// <inheritdoc />
public IDictionary<string, WireMockList<string>>? Query { get; }
/// <inheritdoc />
public IDictionary<string, WireMockList<string>>? QueryIgnoreCase { get; }
/// <inheritdoc cref="IRequestMessage.RawQuery" />
public string RawQuery { get; }
@@ -171,6 +174,7 @@ public class RequestMessage : IRequestMessage
Cookies = cookies;
RawQuery = urlDetails.Url.Query;
Query = QueryStringParser.Parse(RawQuery, options?.QueryParameterMultipleValueSupport);
QueryIgnoreCase = new Dictionary<string, WireMockList<string>>(Query, StringComparer.OrdinalIgnoreCase);
#if USE_ASPNETCORE
ClientCertificate = clientCertificate;
#endif

View File

@@ -33,7 +33,7 @@ internal class MappingConverter
settings ??= new MappingConverterSettings();
var request = (Request)mapping.RequestMatcher;
var response = (Response)mapping.Provider;
var response = (Response) mapping.Provider;
var clientIPMatcher = request.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
var pathMatcher = request.GetRequestMessageMatcher<RequestMessagePathMatcher>();

View File

@@ -31,7 +31,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
private readonly IDateTimeUtils _dateTimeUtils;
private readonly bool _saveToFile;
private bool _useWebhookFireAndForget;
private bool? _useWebhookFireAndForget;
public Guid Guid { get; private set; }

View File

@@ -32,6 +32,7 @@ public partial class WireMockServer
private const int EnhancedFileSystemWatcherTimeoutMs = 1000;
private const string AdminFiles = "/__admin/files";
private const string AdminMappings = "/__admin/mappings";
private const string AdminMappingsCode = "/__admin/mappings/code";
private const string AdminMappingsWireMockOrg = "/__admin/mappings/wiremock.org";
private const string AdminRequests = "/__admin/requests";
private const string AdminSettings = "/__admin/settings";
@@ -41,6 +42,7 @@ public partial class WireMockServer
private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true);
private static readonly RegexMatcher AdminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
private static readonly RegexMatcher AdminMappingsCodeGuidPathMatcher = new(@"^\/__admin\/mappings\/code\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
private static readonly RegexMatcher AdminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
private static readonly RegexMatcher AdminScenariosNameMatcher = new(@"^\/__admin\/scenarios\/.+$");
private static readonly RegexMatcher AdminScenariosNameWithResetMatcher = new(@"^\/__admin\/scenarios\/.+\/reset$");
@@ -57,9 +59,14 @@ public partial class WireMockServer
// __admin/mappings
Given(Request.Create().WithPath(AdminMappings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsGet));
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost));
Given(Request.Create().WithPath(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg));
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete));
// __admin/mappings/code
Given(Request.Create().WithPath(AdminMappingsCode).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsCodeGet));
// __admin/mappings/wiremock.org
Given(Request.Create().WithPath(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg));
// __admin/mappings/reset
Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsReset));
@@ -68,6 +75,9 @@ public partial class WireMockServer
Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
// __admin/mappings/code/{guid}
Given(Request.Create().WithPath(AdminMappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet));
// __admin/mappings/save
Given(Request.Create().WithPath($"{AdminMappings}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave));
@@ -286,13 +296,11 @@ public partial class WireMockServer
#region Mapping/{guid}
private IResponseMessage MappingGet(IRequestMessage requestMessage)
{
Guid guid = ParseGuidFromRequestMessage(requestMessage);
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
var mapping = FindMappingByGuid(requestMessage);
if (mapping == null)
{
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", 404);
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
}
var model = _mappingConverter.ToMappingModel(mapping);
@@ -300,31 +308,71 @@ public partial class WireMockServer
return ToJson(model);
}
private IResponseMessage MappingCodeGet(IRequestMessage requestMessage)
{
if (TryParseGuidFromRequestMessage(requestMessage, out var guid))
{
var code = _mappingBuilder.ToCSharpCode(guid, GetMappingConverterType(requestMessage));
if (code is null)
{
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
}
return ToResponseMessage(code);
}
_settings.Logger.Warn("HttpStatusCode set to 400");
return ResponseMessageBuilder.Create("GUID is missing", HttpStatusCode.BadRequest);
}
private static MappingConverterType GetMappingConverterType(IRequestMessage requestMessage)
{
var mappingConverterType = MappingConverterType.Server;
if (requestMessage.QueryIgnoreCase?.TryGetValue(nameof(MappingConverterType), out var values) == true &&
Enum.TryParse(values.FirstOrDefault(), true, out MappingConverterType parsed))
{
mappingConverterType = parsed;
}
return mappingConverterType;
}
private IMapping? FindMappingByGuid(IRequestMessage requestMessage)
{
return TryParseGuidFromRequestMessage(requestMessage, out var guid) ? Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid) : null;
}
private IResponseMessage MappingPut(IRequestMessage requestMessage)
{
Guid guid = ParseGuidFromRequestMessage(requestMessage);
if (TryParseGuidFromRequestMessage(requestMessage, out var guid))
{
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
var guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
Guid? guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
return ResponseMessageBuilder.Create("Mapping added or updated", HttpStatusCode.OK, guidFromPut);
}
return ResponseMessageBuilder.Create("Mapping added or updated", HttpStatusCode.OK, guidFromPut);
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
}
private IResponseMessage MappingDelete(IRequestMessage requestMessage)
{
Guid guid = ParseGuidFromRequestMessage(requestMessage);
if (DeleteMapping(guid))
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteMapping(guid))
{
return ResponseMessageBuilder.Create("Mapping removed", HttpStatusCode.OK, guid);
}
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
}
private static Guid ParseGuidFromRequestMessage(IRequestMessage requestMessage)
private static bool TryParseGuidFromRequestMessage(IRequestMessage requestMessage, out Guid guid)
{
return Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1));
var lastPart = requestMessage.Path.Split('/').LastOrDefault();
return Guid.TryParse(lastPart, out guid);
}
#endregion Mapping/{guid}
@@ -360,6 +408,15 @@ public partial class WireMockServer
return ToJson(ToMappingModels());
}
private IResponseMessage MappingsCodeGet(IRequestMessage requestMessage)
{
var converterType = GetMappingConverterType(requestMessage);
var code = _mappingBuilder.ToCSharpCode(converterType);
return ToResponseMessage(code);
}
private IResponseMessage MappingsPost(IRequestMessage requestMessage)
{
try
@@ -464,30 +521,29 @@ public partial class WireMockServer
#region Request/{guid}
private IResponseMessage RequestGet(IRequestMessage requestMessage)
{
Guid guid = ParseGuidFromRequestMessage(requestMessage);
var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid);
if (entry == null)
if (TryParseGuidFromRequestMessage(requestMessage, out var guid))
{
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
return ResponseMessageBuilder.Create("Request not found", 404);
var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid);
if (entry is { })
{
var model = new LogEntryMapper(_options).Map(entry);
return ToJson(model);
}
}
var model = new LogEntryMapper(_options).Map(entry);
return ToJson(model);
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
return ResponseMessageBuilder.Create("Request not found", HttpStatusCode.NotFound);
}
private IResponseMessage RequestDelete(IRequestMessage requestMessage)
{
Guid guid = ParseGuidFromRequestMessage(requestMessage);
if (DeleteLogEntry(guid))
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteLogEntry(guid))
{
return ResponseMessageBuilder.Create("Request removed");
}
return ResponseMessageBuilder.Create("Request not found", 404);
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
return ResponseMessageBuilder.Create("Request not found", HttpStatusCode.NotFound);
}
#endregion Request/{guid}
@@ -564,7 +620,7 @@ public partial class WireMockServer
return ResetScenario(name) ?
ResponseMessageBuilder.Create("Scenario reset") :
ResponseMessageBuilder.Create($"No scenario found by name '{name}'.", 404);
ResponseMessageBuilder.Create($"No scenario found by name '{name}'.", HttpStatusCode.NotFound);
}
#endregion
@@ -687,6 +743,20 @@ public partial class WireMockServer
};
}
private static ResponseMessage ToResponseMessage(string text)
{
return new ResponseMessage
{
BodyData = new BodyData
{
DetectedBodyType = BodyType.String,
BodyAsString = text
},
StatusCode = (int)HttpStatusCode.OK,
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(WireMockConstants.ContentTypeTextPlain) } }
};
}
private static T DeserializeObject<T>(IRequestMessage requestMessage) where T : new()
{
return requestMessage.BodyData?.DetectedBodyType switch

View File

@@ -540,6 +540,20 @@ public partial class WireMockServer : IWireMockServer
return _mappingBuilder.Given(requestMatcher, saveToFile);
}
/// <inheritdoc />
[PublicAPI]
public string? MappingToCSharpCode(Guid guid, MappingConverterType converterType)
{
return _mappingBuilder.ToCSharpCode(guid, converterType);
}
/// <inheritdoc />
[PublicAPI]
public string MappingsToCSharpCode(MappingConverterType converterType)
{
return _mappingBuilder.ToCSharpCode(converterType);
}
private void InitSettings(WireMockServerSettings settings)
{
if (settings.AllowBodyForAllHttpMethods == true)