Merge branch 'LinqMatcher_JObject'

This commit is contained in:
Stef Heyenrath
2018-09-07 11:41:10 +02:00
10 changed files with 265 additions and 65 deletions

View File

@@ -1,6 +1,8 @@
using System.Linq;
using System.Linq.Dynamic.Core;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using WireMock.Util;
namespace WireMock.Matchers
{
@@ -63,29 +65,33 @@ namespace WireMock.Matchers
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
///// <inheritdoc cref="IObjectMatcher.IsMatch"/>
//public double IsMatch(object input)
//{
// object value;
// switch (input)
// {
// case JObject valueAsJObject:
// value = valueAsJObject.ToObject<object>();
// break;
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object input)
{
JObject value;
switch (input)
{
case JObject valueAsJObject:
value = valueAsJObject;
break;
// default:
// value = input;
// break;
// }
default:
value = JObject.FromObject(input);
break;
}
// // Convert a single object to a Queryable object-list with 1 entry.
// IQueryable queryable = new[] { value }.AsQueryable().Select("new (it as x)");
// Convert a single object to a Queryable JObject-list with 1 entry.
var queryable1 = new[] { value }.AsQueryable();
// // Use the Any(...) method to check if the result matches
// double match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern)));
// Generate the dynamic linq select statement and generate a dynamic Queryable.
string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
var queryable2 = queryable1.Select(dynamicSelect);
// return MatchBehaviourHelper.Convert(MatchBehaviour, match);
//}
// Use the Any(...) method to check if the result matches
double match = MatchScores.ToScore(_patterns.Select(pattern => queryable2.Any(pattern)));
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns()

View File

@@ -7,9 +7,9 @@ using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using WireMock.Http;
using WireMock.HttpsCertificate;
using WireMock.Logging;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Owin
@@ -43,7 +43,7 @@ namespace WireMock.Owin
{
Urls.Add(uriPrefix);
PortUtil.TryExtractProtocolAndPort(uriPrefix, out string host, out int port);
PortUtils.TryExtractProtocolAndPort(uriPrefix, out string host, out int port);
Ports.Add(port);
}
@@ -75,13 +75,13 @@ namespace WireMock.Owin
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?tabs=aspnetcore2x
foreach (string url in _urls.Where(u => u.StartsWith("http://", StringComparison.OrdinalIgnoreCase)))
{
PortUtil.TryExtractProtocolAndPort(url, out string host, out int port);
PortUtils.TryExtractProtocolAndPort(url, out string host, out int port);
options.Listen(System.Net.IPAddress.Any, port);
}
foreach (string url in _urls.Where(u => u.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))
{
PortUtil.TryExtractProtocolAndPort(url, out string host, out int port);
PortUtils.TryExtractProtocolAndPort(url, out string host, out int port);
options.Listen(System.Net.IPAddress.Any, port, listenOptions =>
{
listenOptions.UseHttps(PublicCertificateHelper.GetX509Certificate2());

View File

@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using WireMock.Http;
using WireMock.Logging;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Owin
@@ -30,7 +31,7 @@ namespace WireMock.Owin
{
Urls.Add(uriPrefix);
PortUtil.TryExtractProtocolAndPort(uriPrefix, out string host, out int port);
PortUtils.TryExtractProtocolAndPort(uriPrefix, out string host, out int port);
Ports.Add(port);
}

View File

@@ -16,6 +16,7 @@ using WireMock.Owin;
using WireMock.RequestBuilders;
using WireMock.ResponseProviders;
using WireMock.Settings;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Server
@@ -199,7 +200,7 @@ namespace WireMock.Server
}
else
{
int port = settings.Port > 0 ? settings.Port.Value : PortUtil.FindFreeTcpPort();
int port = settings.Port > 0 ? settings.Port.Value : PortUtils.FindFreeTcpPort();
Urls = new[] { $"{(settings.UseSSL == true ? "https" : "http")}://localhost:{port}" };
}

View File

@@ -1,4 +1,8 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WireMock.Util
{
@@ -6,12 +10,138 @@ namespace WireMock.Util
{
public static T ParseJTokenToObject<T>(object value)
{
if (value == null)
switch (value)
{
return default(T);
case JToken tokenValue:
return tokenValue.ToObject<T>();
default:
return default(T);
}
}
public static string GenerateDynamicLinqStatement(JObject jsonObject)
{
var lines = new List<string>();
WalkNode(jsonObject, null, null, lines);
return lines.First();
}
private static void WalkNode(JToken node, string path, string propertyName, List<string> lines)
{
if (node.Type == JTokenType.Object)
{
ProcessObject(node, propertyName, lines);
}
else if (node.Type == JTokenType.Array)
{
ProcessArray(node, propertyName, lines);
}
else
{
ProcessItem(node, path, propertyName, lines);
}
}
private static void ProcessObject(JToken node, string propertyName, List<string> lines)
{
var items = new List<string>();
var text = new StringBuilder("new (");
// In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions.
foreach (JProperty child in node.Children<JProperty>().ToArray())
{
WalkNode(child.Value, child.Path, child.Name, items);
}
return !(value is JToken token) ? default(T) : token.ToObject<T>();
text.Append(string.Join(", ", items));
text.Append(")");
if (!string.IsNullOrEmpty(propertyName))
{
text.AppendFormat(" as {0}", propertyName);
}
lines.Add(text.ToString());
}
private static void ProcessArray(JToken node, string propertyName, List<string> lines)
{
var items = new List<string>();
var text = new StringBuilder("(new [] { ");
// In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions.
int idx = 0;
foreach (JToken child in node.Children().ToArray())
{
WalkNode(child, $"{node.Path}[{idx}]", null, items);
idx++;
}
text.Append(string.Join(", ", items));
text.Append("})");
if (!string.IsNullOrEmpty(propertyName))
{
text.AppendFormat(" as {0}", propertyName);
}
lines.Add(text.ToString());
}
private static void ProcessItem(JToken node, string path, string propertyName, List<string> lines)
{
string castText = string.Empty;
switch (node.Type)
{
case JTokenType.Boolean:
castText = $"bool({path})";
break;
case JTokenType.Date:
castText = $"DateTime({path})";
break;
case JTokenType.Float:
castText = $"double({path})";
break;
case JTokenType.Guid:
castText = $"Guid({path})";
break;
case JTokenType.Integer:
castText = $"int({path})";
break;
case JTokenType.Null:
castText = "null";
break;
case JTokenType.String:
castText = $"string({path})";
break;
case JTokenType.TimeSpan:
castText = $"TimeSpan({path})";
break;
case JTokenType.Uri:
castText = $"Uri({path})";
break;
default:
throw new NotSupportedException(
$"JTokenType '{node.Type}' cannot be converted to a Dynamic Linq cast operator.");
}
if (!string.IsNullOrEmpty(propertyName))
{
castText += $" as {propertyName}";
}
lines.Add(castText);
}
}
}

View File

@@ -2,12 +2,12 @@
using System.Net.Sockets;
using System.Text.RegularExpressions;
namespace WireMock.Http
namespace WireMock.Util
{
/// <summary>
/// Port Utility class
/// </summary>
public static class PortUtil
public static class PortUtils
{
private static readonly Regex UrlDetailsRegex = new Regex(@"^(?<proto>\w+)://[^/]+?(?<port>\d+)/?", RegexOptions.Compiled);

View File

@@ -49,7 +49,7 @@
<PackageReference Include="System.Net.Http" Version="4.3.3" />
<PackageReference Include="RestEase" Version="1.4.4" />
<PackageReference Include="MimeKitLite" Version="2.0.1" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.8.17" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.8.18" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">

View File

@@ -1,4 +1,5 @@
using NFluent;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -45,43 +46,41 @@ namespace WireMock.Net.Tests.Matchers
Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d);
}
//[Fact]
//public void LinqMatcher_For_Object_IsMatch()
//{
// // Assign
// var input = new
// {
// Id = 9,
// Name = "Test"
// };
[Fact]
public void LinqMatcher_For_Object_IsMatch()
{
// Assign
var input = new
{
Id = 9,
Name = "Test"
};
// // Act
// var matcher = new LinqMatcher("Id > 1 AND Name == \"Test\"");
// Act
var matcher = new LinqMatcher("Id > 1 AND Name == \"Test\"");
double match = matcher.IsMatch(input);
// double match = matcher.IsMatch(input);
// Assert
Assert.Equal(1.0, match);
}
// // Assert
// Assert.Equal(1.0, match);
//}
[Fact]
public void LinqMatcher_For_JObject_IsMatch()
{
// Assign
var input = new JObject
{
{ "Id", new JValue(9) },
{ "Name", new JValue("Test") }
};
//[Fact]
//public void LinqMatcher_For_JObject_IsMatch()
//{
// // Assign
// var input = new JObject
// {
// { "Id", new JValue(9) },
// { "Name", new JValue("Test") }
// };
// Act
var matcher = new LinqMatcher("Id > 1 AND Name == \"Test\"");
double match = matcher.IsMatch(input);
// // Act
// var matcher = new LinqMatcher("it.Id > 1 AND it.Name == \"Test\"");
// double match = matcher.IsMatch(input);
// // Assert
// Assert.Equal(1.0, match);
//}
// Assert
Assert.Equal(1.0, match);
}
[Fact]
public void LinqMatcher_GetName()

View File

@@ -0,0 +1,62 @@
using System;
using System.Linq;
using Newtonsoft.Json.Linq;
using NFluent;
using System.Linq.Dynamic.Core;
using WireMock.Util;
using Xunit;
namespace WireMock.Net.Tests.Util
{
public class JsonUtilsTests
{
[Fact]
public void JsonUtils_GenerateDynamicLinqStatement()
{
// Assign
var j = new JObject
{
{"U", new JValue(new Uri("http://localhost:80/abc?a=5"))},
{"N", new JValue((object) null)},
{"G", new JValue(Guid.NewGuid())},
{"Flt", new JValue(10.0f)},
{"Dbl", new JValue(Math.PI)},
{"Check", new JValue(true)},
{"Items", new JArray(new[] {new JValue(4), new JValue(8)})},
{
"Child", new JObject
{
{"ChildId", new JValue(4)},
{"ChildDateTime", new JValue(new DateTime(2018, 2, 17))},
{"TS", new JValue(TimeSpan.FromMilliseconds(999))}
}
},
{"Id", new JValue(9)},
{"Name", new JValue("Test")}
};
// Act
string line = JsonUtils.GenerateDynamicLinqStatement(j);
// Assert
var queryable = new[] {j}.AsQueryable().Select(line);
bool result = queryable.Any("Id > 4");
Check.That(result).IsTrue();
Check.That(line).IsEqualTo("new (Uri(U) as U, null as N, Guid(G) as G, double(Flt) as Flt, double(Dbl) as Dbl, bool(Check) as Check, (new [] { int(Items[0]), int(Items[1])}) as Items, new (int(Child.ChildId) as ChildId, DateTime(Child.ChildDateTime) as ChildDateTime, TimeSpan(Child.TS) as TS) as Child, int(Id) as Id, string(Name) as Name)");
}
[Fact]
public void JsonUtils_GenerateDynamicLinqStatement_Throws()
{
// Assign
var j = new JObject
{
{"B", new JValue(new byte[] {48, 49})}
};
// Act and Assert
Check.ThatCode(() => JsonUtils.GenerateDynamicLinqStatement(j)).Throws<NotSupportedException>();
}
}
}

View File

@@ -24,7 +24,8 @@
<PackageReference Include="SimMetrics.Net" Version="1.0.4" />
<PackageReference Include="System.Threading" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.8.18" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net452'">