Skip to content

Commit 7355df9

Browse files
feat/1421 added feature to enable and disable mappings
1 parent 85d61a1 commit 7355df9

12 files changed

Lines changed: 276 additions & 3 deletions

File tree

src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,17 @@ public class MappingModel
5555
/// In case the value is null state will not be changed.
5656
/// </summary>
5757
public string? SetStateTo { get; set; }
58-
58+
5959
/// <summary>
6060
/// The number of times this match should be matched before the state will be changed to the specified one.
6161
/// </summary>
6262
public int? TimesInSameState { get; set; }
6363

64+
/// <summary>
65+
/// Value to determing if the mapping is active
66+
/// </summary>
67+
public bool? IsEnabled { get; set; }
68+
6469
/// <summary>
6570
/// The request model.
6671
/// </summary>
@@ -100,7 +105,7 @@ public class MappingModel
100105
/// </summary>
101106
public object? Data { get; set; }
102107

103-
/// <summary>
108+
/// <summary>
104109
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
105110
/// </summary>
106111
public double? Probability { get; set; }

src/WireMock.Net.Minimal/Mapping.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public class Mapping : IMapping
6262
/// <inheritdoc />
6363
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
6464

65+
/// <inheritdoc />
66+
public bool IsEnabled { get; set; } = true;
67+
6568
/// <inheritdoc />
6669
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
6770

src/WireMock.Net.Minimal/Owin/MappingMatcher.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDou
1919
var possibleMappings = new List<MappingMatcherResult>();
2020

2121
var mappings = _options.Mappings.Values
22+
.Where(m=>m.IsEnabled)
2223
.Where(m => m.TimeSettings.IsValid())
2324
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
2425
.ToArray();

src/WireMock.Net.Minimal/Serialization/MappingConverter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ public MappingModel ToMappingModel(IMapping mapping)
275275
TimesInSameState = !string.IsNullOrWhiteSpace(mapping.NextState) ? mapping.TimesInSameState : null,
276276
Data = mapping.Data,
277277
Probability = mapping.Probability,
278+
IsEnabled = mapping.IsEnabled ? null : false,
278279
Request = new RequestModel
279280
{
280281
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel

src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,13 @@ IRespondWithAProvider WithWebhook(
234234
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
235235
IRespondWithAProvider WithProbability(double probability);
236236

237+
/// <summary>
238+
/// Define whether this mapping is enabled. Defaults to <c>true</c>.
239+
/// </summary>
240+
/// <param name="isEnabled">Whether this mapping is enabled.</param>
241+
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
242+
IRespondWithAProvider WithIsEnabled(bool isEnabled);
243+
237244
/// <summary>
238245
/// Define a Grpc ProtoDefinition which is used for the request and the response.
239246
/// This can be a ProtoDefinition as a string, or an id when the ProtoDefinitions are defined at the WireMockServer.

src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
3737
private int _timesInSameState = 1;
3838
private bool? _useWebhookFireAndForget;
3939
private double? _probability;
40+
private bool _isEnabled = true;
4041
private GraphQLSchemaDetails? _graphQLSchemaDetails; // Future Use.
4142

4243
public Guid Guid { get; private set; }
@@ -108,6 +109,11 @@ public void RespondWith(IResponseProvider provider)
108109
mapping.WithProbability(_probability.Value);
109110
}
110111

112+
if (!_isEnabled)
113+
{
114+
mapping.IsEnabled = false;
115+
}
116+
111117
if (ProtoDefinition != null)
112118
{
113119
mapping.WithProtoDefinition(ProtoDefinition.Value);
@@ -354,6 +360,13 @@ public IRespondWithAProvider WithProbability(double probability)
354360
return this;
355361
}
356362

363+
/// <inheritdoc />
364+
public IRespondWithAProvider WithIsEnabled(bool isEnabled)
365+
{
366+
_isEnabled = isEnabled;
367+
return this;
368+
}
369+
357370
/// <inheritdoc />
358371
public IRespondWithAProvider WithProtoDefinition(params string[] protoDefinitionOrId)
359372
{

src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public AdminPaths(WireMockServerSettings settings)
5757
public string OpenApi => $"{_prefix}/openapi";
5858

5959
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}})$");
60+
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$");
61+
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$");
6062
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}})$");
6163
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}})$");
6264
public RegexMatcher ScenariosNameMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+$");
@@ -100,6 +102,12 @@ private void InitAdmin()
100102
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
101103
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
102104

105+
// __admin/mappings/{guid}/enable
106+
Given(Request.Create().WithPath(_adminPaths.MappingsGuidEnablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingEnable));
107+
108+
// __admin/mappings/{guid}/disable
109+
Given(Request.Create().WithPath(_adminPaths.MappingsGuidDisablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDisable));
110+
103111
// __admin/mappings/code/{guid}
104112
Given(Request.Create().WithPath(_adminPaths.MappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet));
105113

@@ -426,6 +434,47 @@ private static bool TryParseGuidFromRequestMessage(IRequestMessage requestMessag
426434
var lastPart = requestMessage.Path.Split('/').LastOrDefault();
427435
return Guid.TryParse(lastPart, out guid);
428436
}
437+
438+
private static bool TryParseGuidFromSecondToLastSegment(IRequestMessage requestMessage, out Guid guid)
439+
{
440+
var parts = requestMessage.Path.Split('/');
441+
if (parts.Length >= 2 && Guid.TryParse(parts[parts.Length - 2], out guid))
442+
return true;
443+
guid = Guid.Empty;
444+
return false;
445+
}
446+
447+
private IResponseMessage MappingEnable(HttpContext _, IRequestMessage requestMessage)
448+
{
449+
if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
450+
{
451+
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
452+
if (mapping != null)
453+
{
454+
mapping.IsEnabled = true;
455+
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping enabled", guid);
456+
}
457+
}
458+
459+
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
460+
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
461+
}
462+
463+
private IResponseMessage MappingDisable(HttpContext _, IRequestMessage requestMessage)
464+
{
465+
if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
466+
{
467+
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
468+
if (mapping != null)
469+
{
470+
mapping.IsEnabled = false;
471+
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping disabled", guid);
472+
}
473+
}
474+
475+
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
476+
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
477+
}
429478
#endregion Mapping/{guid}
430479

431480
#region Mappings

src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ private Guid ConvertMappingAndRegisterAsRespondProvider(MappingModel mappingMode
120120
respondProvider.WithProbability(mappingModel.Probability.Value);
121121
}
122122

123+
if (mappingModel.IsEnabled == false)
124+
{
125+
respondProvider.WithIsEnabled(false);
126+
}
127+
123128
// ProtoDefinition is defined at Mapping level
124129
if (mappingModel.ProtoDefinition != null)
125130
{

src/WireMock.Net.RestClient/IWireMockAdminApi.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,22 @@ public interface IWireMockAdminApi
163163
[Header("Content-Type", "application/json")]
164164
Task<StatusModel> PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping, CancellationToken cancellationToken = default);
165165

166+
/// <summary>
167+
/// Enable a mapping based on the guid.
168+
/// </summary>
169+
/// <param name="guid">The Guid.</param>
170+
/// <param name="cancellationToken">The optional cancellationToken.</param>
171+
[Put("mappings/{guid}/enable")]
172+
Task<StatusModel> EnableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
173+
174+
/// <summary>
175+
/// Disable a mapping based on the guid.
176+
/// </summary>
177+
/// <param name="guid">The Guid.</param>
178+
/// <param name="cancellationToken">The optional cancellationToken.</param>
179+
[Put("mappings/{guid}/disable")]
180+
Task<StatusModel> DisableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
181+
166182
/// <summary>
167183
/// Delete a mapping based on the guid
168184
/// </summary>

src/WireMock.Net.Shared/IMapping.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ public interface IMapping
108108
/// </value>
109109
bool IsProxy { get; }
110110

111+
/// <summary>
112+
/// Gets a value indicating whether this mapping is a Proxy Mapping.
113+
/// </summary>
114+
/// <value>
115+
/// <c>true</c> if this mapping is a Proxy Mapping; otherwise, <c>false</c>.
116+
/// </value>
117+
bool IsEnabled { get; set; }
118+
111119
/// <summary>
112120
/// Gets a value indicating whether this mapping to be logged.
113121
/// </summary>
@@ -135,7 +143,7 @@ public interface IMapping
135143
/// </summary>
136144
object? Data { get; }
137145

138-
/// <summary>
146+
/// <summary>
139147
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
140148
/// </summary>
141149
double? Probability { get; }

0 commit comments

Comments
 (0)