Support json path in the response (#170)

* jsonpath in response

* Fix tests

* Support also BodyAsJson

* Fix Sonar Issue

* Add example (zubinix2)

* Fix batch file

* Solve CodeFactor issues

* netcoreapp2.0;netcoreapp2.1

* 1.0.4.8
This commit is contained in:
Stef Heyenrath
2018-07-23 17:28:32 +02:00
committed by GitHub
parent 215f051218
commit 1f226f7361
19 changed files with 591 additions and 74 deletions

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
<Version>1.0.4.7</Version>
<Version>1.0.4.8-preview-01</Version>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>

View File

@@ -73,10 +73,21 @@ namespace WireMock.Matchers
JToken jtokenInput = input is JToken tokenInput ? tokenInput : JObject.FromObject(input);
// Check if JToken or string or object
JToken jtokenValue =
Value is JToken tokenValue ? tokenValue :
Value is string stringValue ? JToken.Parse(stringValue) :
JObject.FromObject(input);
JToken jtokenValue;
switch (Value)
{
case JToken tokenValue:
jtokenValue = tokenValue;
break;
case string stringValue:
jtokenValue = JToken.Parse(stringValue);
break;
default:
jtokenValue = JObject.FromObject(Value);
break;
}
match = JToken.DeepEquals(jtokenValue, jtokenInput);
}

View File

@@ -1,5 +1,4 @@
namespace WireMock.Matchers
namespace WireMock.Matchers
{
internal static class MatchBehaviourHelper
{

View File

@@ -76,6 +76,11 @@ namespace WireMock.Owin
#endif
)
{
if (responseMessage == null)
{
return;
}
response.StatusCode = responseMessage.StatusCode;
byte[] bytes = null;

View File

@@ -701,7 +701,6 @@ namespace WireMock.Server
{
responseBuilder = responseBuilder.WithBodyFromBase64(responseModel.BodyFromBase64, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.BodyAsFile != null)
{
responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile);

View File

@@ -14,6 +14,7 @@ using WireMock.Owin;
using WireMock.RequestBuilders;
using WireMock.ResponseProviders;
using WireMock.Settings;
using WireMock.Transformers;
using WireMock.Validation;
namespace WireMock.Server
@@ -387,7 +388,6 @@ namespace WireMock.Server
public void SetMaxRequestLogCount([CanBeNull] int? maxRequestLogCount)
{
_options.MaxRequestLogCount = maxRequestLogCount;
}
/// <summary>

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using HandlebarsDotNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WireMock.Validation;
namespace WireMock.Transformers
{
internal static class HandlebarsHelpers
{
public static void Register()
{
Handlebars.RegisterHelper("JsonPath.SelectToken", (writer, context, arguments) =>
{
(JObject valueToProcess, string jsonpath) = Parse(arguments);
JToken result = null;
try
{
result = valueToProcess.SelectToken(jsonpath);
}
catch (JsonException)
{
// Ignore JsonException and return
return;
}
if (result != null)
{
writer.WriteSafeString(result);
}
});
Handlebars.RegisterHelper("JsonPath.SelectTokens", (writer, options, context, arguments) =>
{
(JObject valueToProcess, string jsonpath) = Parse(arguments);
IEnumerable<JToken> values = null;
try
{
values = valueToProcess.SelectTokens(jsonpath);
}
catch (JsonException)
{
// Ignore JsonException and return
return;
}
if (values == null)
{
return;
}
int id = 0;
foreach (JToken value in values)
{
options.Template(writer, new { id, value });
id++;
}
});
}
private static (JObject valueToProcess, string jsonpath) Parse(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]");
JObject valueToProcess;
switch (arguments[0])
{
case string jsonAsString:
valueToProcess = JObject.Parse(jsonAsString);
break;
case JObject jsonAsJObject:
valueToProcess = jsonAsJObject;
break;
default:
throw new NotSupportedException($"The value '{arguments[0]}' with type {arguments[0].GetType()} cannot be used in Handlebars JsonPath.");
}
return (valueToProcess, arguments[1] as string);
}
}
}

View File

@@ -2,12 +2,18 @@
using System.Linq;
using HandlebarsDotNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WireMock.Util;
namespace WireMock.Transformers
{
internal static class ResponseMessageTransformer
{
static ResponseMessageTransformer()
{
HandlebarsHelpers.Register();
}
public static ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original)
{
bool bodyIsJson = original.BodyAsJson != null;
@@ -20,21 +26,13 @@ namespace WireMock.Transformers
var template = new { request = requestMessage };
// Body
Formatting formatting = original.BodyAsJsonIndented == true ? Formatting.Indented : Formatting.None;
string body = bodyIsJson ? JsonConvert.SerializeObject(original.BodyAsJson, formatting) : original.Body;
if (body != null)
if (!bodyIsJson)
{
var templateBody = Handlebars.Compile(body);
if (!bodyIsJson)
{
responseMessage.Body = templateBody(template);
}
else
{
responseMessage.BodyAsJson = JsonConvert.DeserializeObject(templateBody(template));
}
TransformBodyAsString(template, original, responseMessage);
}
else
{
TransformBodyAsJson(template, original, responseMessage);
}
// Headers
@@ -54,5 +52,79 @@ namespace WireMock.Transformers
return responseMessage;
}
private static void TransformBodyAsJson(object template, ResponseMessage original, ResponseMessage responseMessage)
{
JObject jobject;
switch (original.BodyAsJson)
{
case JObject bodyAsJObject:
jobject = bodyAsJObject;
break;
default:
jobject = JObject.FromObject(original.BodyAsJson);
break;
}
WalkNode(jobject, template);
responseMessage.BodyAsJson = jobject;
}
private static void WalkNode(JToken node, object template)
{
if (node.Type == JTokenType.Object)
{
// In case of Object, loop all children.
foreach (JProperty child in node.Children<JProperty>())
{
WalkNode(child.Value, template);
}
}
else if (node.Type == JTokenType.Array)
{
// In case of Array, loop all items.
foreach (JToken child in node.Children())
{
WalkNode(child, template);
}
}
else if (node.Type == JTokenType.String)
{
// In case of string, try to transform the value.
string stringValue = node.Value<string>();
if (string.IsNullOrEmpty(stringValue))
{
return;
}
var templateForStringValue = Handlebars.Compile(stringValue);
string transformedString = templateForStringValue(template);
if (!string.Equals(stringValue, transformedString))
{
JToken value;
try
{
// Try to convert this string into a real JsonObject
value = JToken.Parse(transformedString);
}
catch (JsonException)
{
// Ignore JsonException and just convert to JToken
value = transformedString;
}
node.Replace(value);
}
}
}
private static void TransformBodyAsString(object template, ResponseMessage original, ResponseMessage responseMessage)
{
var templateBody = Handlebars.Compile(original.Body);
responseMessage.Body = templateBody(template);
}
}
}

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle>
<Version>1.0.4.7</Version>
<Version>1.0.4.8-preview-01</Version>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -41,7 +41,7 @@
<PackageReference Include="JetBrains.Annotations" Version="11.1.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Handlebars.Net" Version="1.9.0" />
<PackageReference Include="Handlebars.Net" Version="1.9.5" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="SimMetrics.Net" Version="1.0.4" />
<PackageReference Include="System.Net.Http" Version="4.3.3" />
@@ -53,6 +53,7 @@
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.6" />
<PackageReference Include="XPath2" Version="1.0.5.1" />
<Reference Include="System.Net.Http.WebRequest" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
@@ -62,6 +63,7 @@
<PackageReference Include="Microsoft.Owin.Hosting" Version="4.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.3" />
<PackageReference Include="XPath2" Version="1.0.5.1" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
@@ -69,6 +71,7 @@
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="1.1.3" />
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">