mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-28 19:58:09 +02:00
Added feature to enable and disable mappings (#1437)
* feat/1421 added feature to enable and disable mappings * feat/1421 updated test constants to reflect 2 new admin endpoints /enable and /disable * feat/1421 updated tests to fix flakyness - removed delay before assertion that is causing upstream connection from proxy to teardown prematurely before test ends * feat/1421 addressing PR comments - Updated logic to represent IsDisable insted of IsEnabled
This commit is contained in:
committed by
GitHub
parent
85d61a1877
commit
1962437dcd
@@ -55,12 +55,17 @@ public class MappingModel
|
||||
/// In case the value is null state will not be changed.
|
||||
/// </summary>
|
||||
public string? SetStateTo { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The number of times this match should be matched before the state will be changed to the specified one.
|
||||
/// </summary>
|
||||
public int? TimesInSameState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value to determine if the mapping is disabled. Defaults to <c>null</c> (not disabled).
|
||||
/// </summary>
|
||||
public bool? IsDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The request model.
|
||||
/// </summary>
|
||||
@@ -100,7 +105,7 @@ public class MappingModel
|
||||
/// </summary>
|
||||
public object? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||
/// </summary>
|
||||
public double? Probability { get; set; }
|
||||
|
||||
@@ -62,6 +62,9 @@ public class Mapping : IMapping
|
||||
/// <inheritdoc />
|
||||
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsDisabled { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDou
|
||||
var possibleMappings = new List<MappingMatcherResult>();
|
||||
|
||||
var mappings = _options.Mappings.Values
|
||||
.Where(m => !m.IsDisabled)
|
||||
.Where(m => m.TimeSettings.IsValid())
|
||||
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
|
||||
.ToArray();
|
||||
|
||||
@@ -275,6 +275,7 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
TimesInSameState = !string.IsNullOrWhiteSpace(mapping.NextState) ? mapping.TimesInSameState : null,
|
||||
Data = mapping.Data,
|
||||
Probability = mapping.Probability,
|
||||
IsDisabled = mapping.IsDisabled ? true : null,
|
||||
Request = new RequestModel
|
||||
{
|
||||
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||
|
||||
@@ -234,6 +234,13 @@ public interface IRespondWithAProvider
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithProbability(double probability);
|
||||
|
||||
/// <summary>
|
||||
/// Define whether this mapping is disabled. Defaults to <c>false</c>.
|
||||
/// </summary>
|
||||
/// <param name="isDisabled">Whether this mapping is disabled.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithIsDisabled(bool isDisabled);
|
||||
|
||||
/// <summary>
|
||||
/// Define a Grpc ProtoDefinition which is used for the request and the response.
|
||||
/// This can be a ProtoDefinition as a string, or an id when the ProtoDefinitions are defined at the WireMockServer.
|
||||
|
||||
@@ -37,6 +37,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
private int _timesInSameState = 1;
|
||||
private bool? _useWebhookFireAndForget;
|
||||
private double? _probability;
|
||||
private bool _isDisabled = false;
|
||||
private GraphQLSchemaDetails? _graphQLSchemaDetails; // Future Use.
|
||||
|
||||
public Guid Guid { get; private set; }
|
||||
@@ -108,6 +109,11 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
mapping.WithProbability(_probability.Value);
|
||||
}
|
||||
|
||||
if (_isDisabled)
|
||||
{
|
||||
mapping.IsDisabled = true;
|
||||
}
|
||||
|
||||
if (ProtoDefinition != null)
|
||||
{
|
||||
mapping.WithProtoDefinition(ProtoDefinition.Value);
|
||||
@@ -354,6 +360,13 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider WithIsDisabled(bool isDisabled)
|
||||
{
|
||||
_isDisabled = isDisabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider WithProtoDefinition(params string[] protoDefinitionOrId)
|
||||
{
|
||||
|
||||
@@ -57,6 +57,8 @@ public partial class WireMockServer
|
||||
public string OpenApi => $"{_prefix}/openapi";
|
||||
|
||||
public RegexMatcher MappingsGuidPathMatcher => new($"^{_prefixEscaped}\\/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}})$");
|
||||
public RegexMatcher MappingsGuidEnablePathMatcher => new($"^{_prefixEscaped}\\/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}})\\/enable$");
|
||||
public RegexMatcher MappingsGuidDisablePathMatcher => new($"^{_prefixEscaped}\\/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}})\\/disable$");
|
||||
public RegexMatcher MappingsCodeGuidPathMatcher => new($"^{_prefixEscaped}\\/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}})$");
|
||||
public RegexMatcher RequestsGuidPathMatcher => new($"^{_prefixEscaped}\\/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}})$");
|
||||
public RegexMatcher ScenariosNameMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+$");
|
||||
@@ -100,6 +102,12 @@ public partial class WireMockServer
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
||||
|
||||
// __admin/mappings/{guid}/enable
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidEnablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingEnable));
|
||||
|
||||
// __admin/mappings/{guid}/disable
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidDisablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDisable));
|
||||
|
||||
// __admin/mappings/code/{guid}
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet));
|
||||
|
||||
@@ -426,6 +434,47 @@ public partial class WireMockServer
|
||||
var lastPart = requestMessage.Path.Split('/').LastOrDefault();
|
||||
return Guid.TryParse(lastPart, out guid);
|
||||
}
|
||||
|
||||
private static bool TryParseGuidFromSecondToLastSegment(IRequestMessage requestMessage, out Guid guid)
|
||||
{
|
||||
var parts = requestMessage.Path.Split('/');
|
||||
if (parts.Length >= 2 && Guid.TryParse(parts[parts.Length - 2], out guid))
|
||||
return true;
|
||||
guid = Guid.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
private IResponseMessage MappingEnable(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
|
||||
{
|
||||
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
|
||||
if (mapping != null)
|
||||
{
|
||||
mapping.IsDisabled = false;
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping enabled", guid);
|
||||
}
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
private IResponseMessage MappingDisable(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
|
||||
{
|
||||
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
|
||||
if (mapping != null)
|
||||
{
|
||||
mapping.IsDisabled = true;
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping disabled", guid);
|
||||
}
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
#endregion Mapping/{guid}
|
||||
|
||||
#region Mappings
|
||||
|
||||
@@ -120,6 +120,11 @@ public partial class WireMockServer
|
||||
respondProvider.WithProbability(mappingModel.Probability.Value);
|
||||
}
|
||||
|
||||
if (mappingModel.IsDisabled == true)
|
||||
{
|
||||
respondProvider.WithIsDisabled(true);
|
||||
}
|
||||
|
||||
// ProtoDefinition is defined at Mapping level
|
||||
if (mappingModel.ProtoDefinition != null)
|
||||
{
|
||||
|
||||
@@ -163,6 +163,22 @@ public interface IWireMockAdminApi
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Enable a mapping based on the guid.
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Put("mappings/{guid}/enable")]
|
||||
Task<StatusModel> EnableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Disable a mapping based on the guid.
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Put("mappings/{guid}/disable")]
|
||||
Task<StatusModel> DisableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a mapping based on the guid
|
||||
/// </summary>
|
||||
|
||||
@@ -108,6 +108,14 @@ public interface IMapping
|
||||
/// </value>
|
||||
bool IsProxy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping is disabled.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this mapping is disabled; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping to be logged.
|
||||
/// </summary>
|
||||
@@ -135,7 +143,7 @@ public interface IMapping
|
||||
/// </summary>
|
||||
object? Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||
/// </summary>
|
||||
double? Probability { get; }
|
||||
|
||||
Reference in New Issue
Block a user