mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-20 07:51:41 +02:00
Regex.Match (#201)
This commit is contained in:
@@ -1,8 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Dynamic.Core;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using HandlebarsDotNet;
|
using HandlebarsDotNet;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using WireMock.Utils;
|
||||||
using WireMock.Validation;
|
using WireMock.Validation;
|
||||||
|
|
||||||
namespace WireMock.Transformers
|
namespace WireMock.Transformers
|
||||||
@@ -11,9 +15,41 @@ namespace WireMock.Transformers
|
|||||||
{
|
{
|
||||||
public static void Register()
|
public static void Register()
|
||||||
{
|
{
|
||||||
|
Handlebars.RegisterHelper("Regex.Match", (writer, context, arguments) =>
|
||||||
|
{
|
||||||
|
(string stringToProcess, string regexPattern, object defaultValue) = ParseRegexArguments(arguments);
|
||||||
|
|
||||||
|
Match match = Regex.Match(stringToProcess, regexPattern);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
writer.WriteSafeString(match.Value);
|
||||||
|
}
|
||||||
|
else if (defaultValue != null)
|
||||||
|
{
|
||||||
|
writer.WriteSafeString(defaultValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.RegisterHelper("Regex.Match", (writer, options, context, arguments) =>
|
||||||
|
{
|
||||||
|
(string stringToProcess, string regexPattern, object defaultValue) = ParseRegexArguments(arguments);
|
||||||
|
|
||||||
|
var regex = new Regex(regexPattern);
|
||||||
|
var namedGroups = RegexUtils.GetNamedGroups(regex, stringToProcess);
|
||||||
|
if (namedGroups.Any())
|
||||||
|
{
|
||||||
|
options.Template(writer, namedGroups);
|
||||||
|
}
|
||||||
|
else if (defaultValue != null)
|
||||||
|
{
|
||||||
|
writer.WriteSafeString(defaultValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Handlebars.RegisterHelper("JsonPath.SelectToken", (writer, context, arguments) =>
|
Handlebars.RegisterHelper("JsonPath.SelectToken", (writer, context, arguments) =>
|
||||||
{
|
{
|
||||||
(JObject valueToProcess, string jsonpath) = Parse(arguments);
|
(JObject valueToProcess, string jsonpath) = ParseJsonPathArguments(arguments);
|
||||||
|
|
||||||
JToken result = null;
|
JToken result = null;
|
||||||
try
|
try
|
||||||
@@ -34,7 +70,7 @@ namespace WireMock.Transformers
|
|||||||
|
|
||||||
Handlebars.RegisterHelper("JsonPath.SelectTokens", (writer, options, context, arguments) =>
|
Handlebars.RegisterHelper("JsonPath.SelectTokens", (writer, options, context, arguments) =>
|
||||||
{
|
{
|
||||||
(JObject valueToProcess, string jsonpath) = Parse(arguments);
|
(JObject valueToProcess, string jsonpath) = ParseJsonPathArguments(arguments);
|
||||||
|
|
||||||
IEnumerable<JToken> values = null;
|
IEnumerable<JToken> values = null;
|
||||||
try
|
try
|
||||||
@@ -61,7 +97,7 @@ namespace WireMock.Transformers
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (JObject valueToProcess, string jsonpath) Parse(object[] arguments)
|
private static (JObject valueToProcess, string jsonpath) ParseJsonPathArguments(object[] arguments)
|
||||||
{
|
{
|
||||||
Check.Condition(arguments, args => args.Length == 2, nameof(arguments));
|
Check.Condition(arguments, args => args.Length == 2, nameof(arguments));
|
||||||
Check.NotNull(arguments[0], "arguments[0]");
|
Check.NotNull(arguments[0], "arguments[0]");
|
||||||
@@ -80,10 +116,29 @@ namespace WireMock.Transformers
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException($"The value '{arguments[0]}' with type {arguments[0].GetType()} cannot be used in Handlebars JsonPath.");
|
throw new NotSupportedException($"The value '{arguments[0]}' with type '{arguments[0]?.GetType()}' cannot be used in Handlebars JsonPath.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (valueToProcess, arguments[1] as string);
|
return (valueToProcess, arguments[1] as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static (string stringToProcess, string regexPattern, object defaultValue) ParseRegexArguments(object[] arguments)
|
||||||
|
{
|
||||||
|
Check.Condition(arguments, args => args.Length >= 2, nameof(arguments));
|
||||||
|
|
||||||
|
string ParseAsString(object arg)
|
||||||
|
{
|
||||||
|
if (arg is string)
|
||||||
|
{
|
||||||
|
return arg as string;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"The value '{arg}' with type '{arg?.GetType()}' cannot be used in Handlebars Regex.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ParseAsString(arguments[0]), ParseAsString(arguments[1]), arguments.Length == 3 ? arguments[2] : null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
26
src/WireMock.Net/Util/IndexableDictionary.cs
Normal file
26
src/WireMock.Net/Util/IndexableDictionary.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace WireMock.Util
|
||||||
|
{
|
||||||
|
public class IndexableDictionary<TKey, TValue> : Dictionary<TKey, TValue>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value associated with the specified index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"> The index of the value to get.</param>
|
||||||
|
/// <returns>The value associated with the specified index.</returns>
|
||||||
|
public TValue this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// get the item for that index.
|
||||||
|
if (index < 0 || index > Count)
|
||||||
|
{
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
return Values.Cast<TValue>().ToArray()[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/WireMock.Net/Util/RegexUtils.cs
Normal file
24
src/WireMock.Net/Util/RegexUtils.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace WireMock.Utils
|
||||||
|
{
|
||||||
|
internal static class RegexUtils
|
||||||
|
{
|
||||||
|
public static Dictionary<string, string> GetNamedGroups(Regex regex, string input)
|
||||||
|
{
|
||||||
|
var namedGroupsDictionary = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
GroupCollection groups = regex.Match(input).Groups;
|
||||||
|
foreach (string groupName in regex.GetGroupNames())
|
||||||
|
{
|
||||||
|
if (groups[groupName].Captures.Count > 0)
|
||||||
|
{
|
||||||
|
namedGroupsDictionary.Add(groupName, groups[groupName].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return namedGroupsDictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Remove="Util\IndexableDictionary.cs" />
|
||||||
<Compile Remove="Util\NamedReaderWriterLocker.cs" />
|
<Compile Remove="Util\NamedReaderWriterLocker.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -494,6 +494,136 @@ namespace WireMock.Net.Tests.ResponseBuilderTests
|
|||||||
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<ArgumentNullException>();
|
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<ArgumentNullException>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Response_ProvideResponse_Handlebars_RegexMatch1()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData { BodyAsString = "abc" };
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithBody("{{Regex.Match request.body \"^(?<x>\\w+)$\"}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Check.That(responseMessage.Body).Equals("abc");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Response_ProvideResponse_Handlebars_RegexMatch1_NoMatch()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData { BodyAsString = "abc" };
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithBody("{{Regex.Match request.body \"^?0$\"}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Check.That(responseMessage.Body).Equals("");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Response_ProvideResponse_Handlebars_RegexMatch1_NoMatch_WithDefaultValue()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData { BodyAsString = "abc" };
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithBody("{{Regex.Match request.body \"^?0$\" \"d\"}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Check.That(responseMessage.Body).Equals("d");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Response_ProvideResponse_Handlebars_RegexMatch2()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData { BodyAsString = "https://localhost:5000/" };
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithBody("{{#Regex.Match request.body \"^(?<proto>\\w+)://[^/]+?(?<port>\\d+)/?\"}}{{this.port}}-{{this.proto}}{{/Regex.Match}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Check.That(responseMessage.Body).Equals("5000-https");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Response_ProvideResponse_Handlebars_RegexMatch2_NoMatch()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData { BodyAsString = "{{\\test" };
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithBody("{{#Regex.Match request.body \"^(?<proto>\\w+)://[^/]+?(?<port>\\d+)/?\"}}{{this}}{{/Regex.Match}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Check.That(responseMessage.Body).Equals("");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Response_ProvideResponse_Handlebars_RegexMatch2_NoMatch_WithDefaultValue()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData { BodyAsString = "{{\\test" };
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithBody("{{#Regex.Match request.body \"^(?<proto>\\w+)://[^/]+?(?<port>\\d+)/?\" \"x\"}}{{this}}{{/Regex.Match}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Check.That(responseMessage.Body).Equals("x");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Response_ProvideResponse_Handlebars_RegexMatch_Throws()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData { BodyAsString = "{{\\test" };
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithBody("{{#Regex.Match request.bodyAsJson \"^(?<proto>\\w+)://[^/]+?(?<port>\\d+)/?\"}}{{/Regex.Match}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act and Assert
|
||||||
|
Check.ThatAsyncCode(() => response.ProvideResponseAsync(request)).Throws<NotSupportedException>();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsArray()
|
public async Task Response_ProvideResponse_Handlebars_WithBodyAsJson_ResultAsArray()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user