mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-18 23:20:11 +02:00
Feature/xpath transformer (#398)
* XPath transformer, added handlerbars helper to select nodes using xpath and setting the outerxml value in response * Added test to select attribute value and node text * Removed extra empty lines
This commit is contained in:
committed by
Stef Heyenrath
parent
3f802c3948
commit
368fdd4c7d
@@ -17,6 +17,8 @@ namespace WireMock.Transformers
|
|||||||
|
|
||||||
HandleBarsXeger.Register(handlebarsContext);
|
HandleBarsXeger.Register(handlebarsContext);
|
||||||
|
|
||||||
|
HandleBarsXPath.Register(handlebarsContext);
|
||||||
|
|
||||||
HandleBarsFile.Register(handlebarsContext, fileSystemHandler);
|
HandleBarsFile.Register(handlebarsContext, fileSystemHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
102
src/WireMock.Net/Transformers/HandleBarsXPath.cs
Normal file
102
src/WireMock.Net/Transformers/HandleBarsXPath.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using HandlebarsDotNet;
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.XPath;
|
||||||
|
using WireMock.Validation;
|
||||||
|
#if !NETSTANDARD1_3
|
||||||
|
using Wmhelp.XPath2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace WireMock.Transformers
|
||||||
|
{
|
||||||
|
internal static class HandleBarsXPath
|
||||||
|
{
|
||||||
|
public static void Register(IHandlebars handlebarsContext)
|
||||||
|
{
|
||||||
|
handlebarsContext.RegisterHelper("XPath.SelectSingleNode", (writer, context, arguments) =>
|
||||||
|
{
|
||||||
|
(XPathNavigator nav, string xpath) = ParseArguments(arguments);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#if NETSTANDARD1_3
|
||||||
|
var result = nav.SelectSingleNode(xpath);
|
||||||
|
#else
|
||||||
|
var result = nav.XPath2SelectSingleNode(xpath);
|
||||||
|
#endif
|
||||||
|
writer.WriteSafeString(result.OuterXml);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Ignore Exception
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handlebarsContext.RegisterHelper("XPath.SelectNodes", (writer, context, arguments) =>
|
||||||
|
{
|
||||||
|
(XPathNavigator nav, string xpath) = ParseArguments(arguments);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#if NETSTANDARD1_3
|
||||||
|
var result = nav.Select(xpath);
|
||||||
|
#else
|
||||||
|
var result = nav.XPath2SelectNodes(xpath);
|
||||||
|
#endif
|
||||||
|
var resultXml = new StringBuilder();
|
||||||
|
foreach (XPathNavigator node in result)
|
||||||
|
{
|
||||||
|
resultXml.Append(node.OuterXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteSafeString(resultXml);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Ignore Exception
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handlebarsContext.RegisterHelper("XPath.Evaluate", (writer, context, arguments) =>
|
||||||
|
{
|
||||||
|
(XPathNavigator nav, string xpath) = ParseArguments(arguments);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#if NETSTANDARD1_3
|
||||||
|
var result = nav.Evaluate(xpath);
|
||||||
|
#else
|
||||||
|
var result = nav.XPath2Evaluate(xpath);
|
||||||
|
#endif
|
||||||
|
writer.WriteSafeString(result);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Ignore Exception
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (XPathNavigator nav, string xpath) ParseArguments(object[] arguments)
|
||||||
|
{
|
||||||
|
Check.Condition(arguments, args => args.Length == 2, nameof(arguments));
|
||||||
|
Check.NotNull(arguments[0], "arguments[0]");
|
||||||
|
Check.NotNullOrEmpty(arguments[1] as string, "arguments[1]");
|
||||||
|
|
||||||
|
XPathNavigator nav;
|
||||||
|
|
||||||
|
switch (arguments[0])
|
||||||
|
{
|
||||||
|
case string stringValue:
|
||||||
|
nav = new XmlDocument { InnerXml = stringValue }.CreateNavigator();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"The value '{arguments[0]}' with type '{arguments[0]?.GetType()}' cannot be used in Handlebars XPath.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (nav, (string)arguments[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
|
using NFluent;
|
||||||
|
using WireMock.Models;
|
||||||
|
using WireMock.ResponseBuilders;
|
||||||
|
using WireMock.Settings;
|
||||||
|
using WireMock.Types;
|
||||||
|
using WireMock.Util;
|
||||||
|
using Xunit;
|
||||||
|
#if !NETSTANDARD1_3
|
||||||
|
using Wmhelp.XPath2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.ResponseBuilders
|
||||||
|
{
|
||||||
|
public class ResponseWithHandlebarsXPathTests
|
||||||
|
{
|
||||||
|
private readonly WireMockServerSettings _settings = new WireMockServerSettings();
|
||||||
|
private const string ClientIp = "::1";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Response_ProvideResponse_Handlebars_XPath_SelectSingleNode_Request_BodyAsString()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = @"<todo-list>
|
||||||
|
<todo-item id='a1'>abc</todo-item>
|
||||||
|
<todo-item id='a2'>def</todo-item>
|
||||||
|
<todo-item id='a3'>xyz</todo-item>
|
||||||
|
</todo-list>",
|
||||||
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithHeader("Content-Type", "application/xml")
|
||||||
|
.WithBody("<response>{{XPath.SelectSingleNode request.body \"/todo-list/todo-item[1]\"}}</response>")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request, _settings);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var nav = new XmlDocument { InnerXml = responseMessage.BodyData.BodyAsString }.CreateNavigator();
|
||||||
|
var node = nav.XPath2SelectSingleNode("/response/todo-item");
|
||||||
|
Check.That(node.Value).Equals("abc");
|
||||||
|
Check.That(node.GetAttribute("id", "")).Equals("a1");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Response_ProvideResponse_Handlebars_XPath_SelectSingleNode_Text_Request_BodyAsString()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = @"<todo-list>
|
||||||
|
<todo-item id='a1'>abc</todo-item>
|
||||||
|
<todo-item id='a2'>def</todo-item>
|
||||||
|
<todo-item id='a3'>xyz</todo-item>
|
||||||
|
</todo-list>",
|
||||||
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithHeader("Content-Type", "application/xml")
|
||||||
|
.WithBody("{{XPath.SelectSingleNode request.body \"/todo-list/todo-item[1]/text()\"}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request, _settings);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(responseMessage.BodyData.BodyAsString).IsEqualTo("abc");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Response_ProvideResponse_Handlebars_XPath_SelectNodes_Request_BodyAsString()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = @"<todo-list>
|
||||||
|
<todo-item id='a1'>abc</todo-item>
|
||||||
|
<todo-item id='a2'>def</todo-item>
|
||||||
|
<todo-item id='a3'>xyz</todo-item>
|
||||||
|
</todo-list>",
|
||||||
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithHeader("Content-Type", "application/xml")
|
||||||
|
.WithBody("<response>{{XPath.SelectNodes request.body \"/todo-list/todo-item\"}}</response>")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request, _settings);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var nav = new XmlDocument { InnerXml = responseMessage.BodyData.BodyAsString }.CreateNavigator();
|
||||||
|
var nodes = nav.XPath2SelectNodes("/response/todo-item");
|
||||||
|
Check.That(nodes.Count + 1).IsEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Response_ProvideResponse_Handlebars_XPath_Evaluate_Request_BodyAsString()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = @"<todo-list>
|
||||||
|
<todo-item id='a1'>abc</todo-item>
|
||||||
|
<todo-item id='a2'>def</todo-item>
|
||||||
|
<todo-item id='a3'>xyz</todo-item>
|
||||||
|
</todo-list>",
|
||||||
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithHeader("Content-Type", "application/xml")
|
||||||
|
.WithBody("{{XPath.Evaluate request.body \"boolean(/todo-list[count(todo-item) = 3])\"}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request, _settings);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(responseMessage.BodyData.BodyAsString).IsEqualIgnoringCase("True");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Response_ProvideResponse_Handlebars_XPath_Evaluate_Attribute_Request_BodyAsString()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = @"<todo-list>
|
||||||
|
<todo-item id='a1'>abc</todo-item>
|
||||||
|
<todo-item id='a2'>def</todo-item>
|
||||||
|
<todo-item id='a3'>xyz</todo-item>
|
||||||
|
</todo-list>",
|
||||||
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", ClientIp, body);
|
||||||
|
|
||||||
|
var response = Response.Create()
|
||||||
|
.WithHeader("Content-Type", "application/xml")
|
||||||
|
.WithBody("{{XPath.Evaluate request.body \"string(/todo-list/todo-item[1]/@id)\"}}")
|
||||||
|
.WithTransformer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var responseMessage = await response.ProvideResponseAsync(request, _settings);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(responseMessage.BodyData.BodyAsString).IsEqualTo("a1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user