Compare commits

..

6 Commits

Author SHA1 Message Date
Stef Heyenrath
1269fb178f 1.5.18 2023-03-09 19:49:29 +01:00
Stef Heyenrath
7426bf76ee Add 'Data' to response which can be used during transforming the response (#893)
* Add 'Data' to response which can be used during transforming the response

* md

* hb

* fix

* Linq

* fix test

* v4

* 14

* .

* x

* remove

* s
2023-03-09 17:20:34 +01:00
dependabot[bot]
36c9d95abb Bump Microsoft.Owin in /examples/WireMock.Net.Service (#896)
Bumps [Microsoft.Owin](https://github.com/aspnet/AspNetKatana) from 2.0.2 to 4.2.2.
- [Release notes](https://github.com/aspnet/AspNetKatana/releases)
- [Commits](https://github.com/aspnet/AspNetKatana/compare/v2.0.2...v4.2.2)

---
updated-dependencies:
- dependency-name: Microsoft.Owin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 17:20:15 +01:00
Stef Heyenrath
674fa89c3e ProxySettings : Add logic to not save some requests depending on HttpMethods (#900)
* Add ExcludedHttpMethods to ProxySettings

* tst

* fix

* SaveMappingSettings

* .
2023-03-09 15:28:52 +01:00
Stef Heyenrath
61cdc13fae Add extra test for WithCallback 2023-03-01 12:10:03 +01:00
Stef Heyenrath
c344b73f45 Cleanup some code from JsonUtils.cs 2023-02-27 21:23:43 +01:00
43 changed files with 743 additions and 317 deletions

View File

@@ -1,3 +1,10 @@
# 1.5.18 (09 March 2023)
- [#893](https://github.com/WireMock-Net/WireMock.Net/pull/893) - Add 'Data' to response which can be used during transforming the response [feature] contributed by [StefH](https://github.com/StefH)
- [#896](https://github.com/WireMock-Net/WireMock.Net/pull/896) - Bump Microsoft.Owin from 2.0.2 to 4.2.2 in /examples/WireMock.Net.Service [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#900](https://github.com/WireMock-Net/WireMock.Net/pull/900) - ProxySettings : Add logic to not save some requests depending on HttpMethods [feature] contributed by [StefH](https://github.com/StefH)
- [#897](https://github.com/WireMock-Net/WireMock.Net/issues/897) - WebHostBuilder.ConfigureServices method not found when using nunit3testadapter 4.4.0 [bug]
- [#899](https://github.com/WireMock-Net/WireMock.Net/issues/899) - Ignore OPTIONS request when using proxyandrecord [feature]
# 1.5.17 (25 February 2023)
- [#881](https://github.com/WireMock-Net/WireMock.Net/pull/881) - Add WithBodyAsJson builder method with accepts a Func [feature] contributed by [StefH](https://github.com/StefH)
- [#882](https://github.com/WireMock-Net/WireMock.Net/pull/882) - Add example code to test HTTP Status 400 and 500 [test] contributed by [StefH](https://github.com/StefH)

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.5.17</VersionPrefix>
<VersionPrefix>1.5.18</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>

View File

@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.5.17
SET version=1.5.18
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%

View File

@@ -1,6 +1,8 @@
# 1.5.17 (25 February 2023)
- #881 Add WithBodyAsJson builder method with accepts a Func [feature]
- #882 Add example code to test HTTP Status 400 and 500 [test]
- #890 AdminApiMappingBuilder [feature]
# 1.5.18 (09 March 2023)
- #893 Add 'Data' to response which can be used during transforming the response [feature]
- #896 Bump Microsoft.Owin from 2.0.2 to 4.2.2 in /examples/WireMock.Net.Service [dependencies]
- #900 ProxySettings : Add logic to not save some requests depending on HttpMethods [feature]
- #897 WebHostBuilder.ConfigureServices method not found when using nunit3testadapter 4.4.0 [bug]
- #899 Ignore OPTIONS request when using proxyandrecord [feature]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -27,7 +27,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />
<!--<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />-->
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />

View File

@@ -643,6 +643,7 @@ namespace WireMock.Net.ConsoleApplication
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { Id = "5bdf076c-5654-4b3e-842c-7caf1fabf8c9" }));
server
.Given(Request.Create().WithPath("/random200or505").UsingGet())
.RespondWith(Response.Create().WithCallback(request =>
@@ -650,7 +651,11 @@ namespace WireMock.Net.ConsoleApplication
int code = new Random().Next(1, 2) == 1 ? 505 : 200;
return new ResponseMessage
{
BodyData = new BodyData { BodyAsString = "random200or505:" + code, DetectedBodyType = Types.BodyType.String },
BodyData = new BodyData
{
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers),
DetectedBodyType = Types.BodyType.String,
},
StatusCode = code
};
}));

View File

@@ -5,7 +5,7 @@
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.OwinSelfHost" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.Owin" version="2.0.2" targetFramework="net452" />
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net452" />
<package id="Microsoft.Owin.Host.HttpListener" version="2.0.2" targetFramework="net452" />
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using WireMock.Models;
namespace WireMock.Admin.Mappings;
@@ -84,4 +85,13 @@ public class MappingModel
/// Fire and forget for webhooks.
/// </summary>
public bool? UseWebhooksFireAndForget { get; set; }
/// <summary>
/// Data Object which can be used when WithTransformer is used.
/// e.g. lookup an path in this object using
/// <example>
/// lookup data "1"
/// </example>
/// </summary>
public object? Data { get; set; }
}

View File

@@ -1,121 +1,120 @@
using System.Collections.Generic;
namespace WireMock.Admin.Mappings
namespace WireMock.Admin.Mappings;
/// <summary>
/// ResponseModel
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class ResponseModel
{
/// <summary>
/// ResponseModel
/// Gets or sets the HTTP status.
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class ResponseModel
{
/// <summary>
/// Gets or sets the HTTP status.
/// </summary>
public object? StatusCode { get; set; }
public object? StatusCode { get; set; }
/// <summary>
/// Gets or sets the body destination (SameAsSource, String or Bytes).
/// </summary>
public string? BodyDestination { get; set; }
/// <summary>
/// Gets or sets the body destination (SameAsSource, String or Bytes).
/// </summary>
public string? BodyDestination { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Gets or sets the body (as JSON object).
/// </summary>
public object? BodyAsJson { get; set; }
/// <summary>
/// Gets or sets the body (as JSON object).
/// </summary>
public object? BodyAsJson { get; set; }
/// <summary>
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
/// </summary>
public bool? BodyAsJsonIndented { get; set; }
/// <summary>
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
/// </summary>
public bool? BodyAsJsonIndented { get; set; }
/// <summary>
/// Gets or sets the body (as bytearray).
/// </summary>
public byte[]? BodyAsBytes { get; set; }
/// <summary>
/// Gets or sets the body (as bytearray).
/// </summary>
public byte[]? BodyAsBytes { get; set; }
/// <summary>
/// Gets or sets the body as a file.
/// </summary>
public string? BodyAsFile { get; set; }
/// <summary>
/// Gets or sets the body as a file.
/// </summary>
public string? BodyAsFile { get; set; }
/// <summary>
/// Is the body as file cached?
/// </summary>
public bool? BodyAsFileIsCached { get; set; }
/// <summary>
/// Is the body as file cached?
/// </summary>
public bool? BodyAsFileIsCached { get; set; }
/// <summary>
/// Gets or sets the body encoding.
/// </summary>
public EncodingModel? BodyEncoding { get; set; }
/// <summary>
/// Gets or sets the body encoding.
/// </summary>
public EncodingModel? BodyEncoding { get; set; }
/// <summary>
/// Use ResponseMessage Transformer.
/// </summary>
public bool? UseTransformer { get; set; }
/// <summary>
/// Use ResponseMessage Transformer.
/// </summary>
public bool? UseTransformer { get; set; }
/// <summary>
/// Gets the type of the transformer.
/// </summary>
public string? TransformerType { get; set; }
/// <summary>
/// Gets the type of the transformer.
/// </summary>
public string? TransformerType { get; set; }
/// <summary>
/// Use the Handlebars transformer for the content from the referenced BodyAsFile.
/// </summary>
public bool? UseTransformerForBodyAsFile { get; set; }
/// <summary>
/// Use the Handlebars transformer for the content from the referenced BodyAsFile.
/// </summary>
public bool? UseTransformerForBodyAsFile { get; set; }
/// <summary>
/// The ReplaceNodeOptions to use when transforming a JSON node.
/// </summary>
public string? TransformerReplaceNodeOptions { get; set; }
/// <summary>
/// The ReplaceNodeOptions to use when transforming a JSON node.
/// </summary>
public string? TransformerReplaceNodeOptions { get; set; }
/// <summary>
/// Gets or sets the headers.
/// </summary>
public IDictionary<string, object>? Headers { get; set; }
/// <summary>
/// Gets or sets the headers.
/// </summary>
public IDictionary<string, object>? Headers { get; set; }
/// <summary>
/// Gets or sets the Headers (Raw).
/// </summary>
public string? HeadersRaw { get; set; }
/// <summary>
/// Gets or sets the Headers (Raw).
/// </summary>
public string? HeadersRaw { get; set; }
/// <summary>
/// Gets or sets the delay in milliseconds.
/// </summary>
public int? Delay { get; set; }
/// <summary>
/// Gets or sets the delay in milliseconds.
/// </summary>
public int? Delay { get; set; }
/// <summary>
/// Gets or sets the minimum random delay in milliseconds.
/// </summary>
public int? MinimumRandomDelay { get; set; }
/// <summary>
/// Gets or sets the minimum random delay in milliseconds.
/// </summary>
public int? MinimumRandomDelay { get; set; }
/// <summary>
/// Gets or sets the maximum random delay in milliseconds.
/// </summary>
public int? MaximumRandomDelay { get; set; }
/// <summary>
/// Gets or sets the maximum random delay in milliseconds.
/// </summary>
public int? MaximumRandomDelay { get; set; }
/// <summary>
/// Gets or sets the Proxy URL.
/// </summary>
public string? ProxyUrl { get; set; }
/// <summary>
/// Gets or sets the Proxy URL.
/// </summary>
public string? ProxyUrl { get; set; }
/// <summary>
/// The client X509Certificate2 Thumbprint or SubjectName to use.
/// </summary>
public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary>
/// The client X509Certificate2 Thumbprint or SubjectName to use.
/// </summary>
public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary>
/// Gets or sets the fault.
/// </summary>
public FaultModel? Fault { get; set; }
/// <summary>
/// Gets or sets the fault.
/// </summary>
public FaultModel? Fault { get; set; }
/// <summary>
/// Gets or sets the WebProxy settings.
/// </summary>
public WebProxyModel? WebProxy { get; set; }
}
/// <summary>
/// Gets or sets the WebProxy settings.
/// </summary>
public WebProxyModel? WebProxy { get; set; }
}

View File

@@ -44,7 +44,7 @@ public interface IResponseMessage
/// Gets or sets the status code.
/// </summary>
object? StatusCode { get; }
/// <summary>
/// Adds the header.
/// </summary>

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using WireMock.Matchers.Request;
using WireMock.Models;
@@ -120,7 +121,16 @@ public interface IMapping
/// <summary>
/// Use Fire and Forget for the defined webhook(s). [Optional]
/// </summary>
public bool? UseWebhooksFireAndForget { get; set; }
bool? UseWebhooksFireAndForget { get; set; }
/// <summary>
/// Data Object which can be used when WithTransformer is used.
/// e.g. lookup an path in this object using
/// <example>
/// lookup data "1"
/// </example>
/// </summary>
object? Data { get; set; }
/// <summary>
/// ProvideResponseAsync

View File

@@ -0,0 +1,10 @@
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
namespace WireMock.Json;
internal class DynamicJsonClassOptions
{
public IntegerBehavior IntegerConvertBehavior { get; set; } = IntegerBehavior.UseLong;
public FloatBehavior FloatConvertBehavior { get; set; } = FloatBehavior.UseDouble;
}

View File

@@ -0,0 +1,15 @@
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
using System.Linq.Dynamic.Core;
namespace WireMock.Json;
public class DynamicPropertyWithValue : DynamicProperty
{
public object? Value { get; }
public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object))
{
Value = value;
}
}

View File

@@ -0,0 +1,24 @@
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
namespace WireMock.Json;
/// <summary>
/// Enum to define how to convert an Float in the Json Object.
/// </summary>
internal enum FloatBehavior
{
/// <summary>
/// Convert all Float types in the Json Object to a double. (default)
/// </summary>
UseDouble = 0,
/// <summary>
/// Convert all Float types in the Json Object to a float (unless overflow).
/// </summary>
UseFloat = 1,
/// <summary>
/// Convert all Float types in the Json Object to a decimal (unless overflow).
/// </summary>
UseDecimal = 2
}

View File

@@ -0,0 +1,20 @@
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
namespace WireMock.Json;
/// <summary>
/// Enum to define how to convert an Integer in the Json Object.
/// </summary>
internal enum IntegerBehavior
{
/// <summary>
/// Convert all Integer types in the Json Object to a int (unless overflow).
/// (default)
/// </summary>
UseInt = 0,
/// <summary>
/// Convert all Integer types in the Json Object to a long.
/// </summary>
UseLong = 1
}

View File

@@ -0,0 +1,202 @@
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Reflection;
using Newtonsoft.Json.Linq;
namespace WireMock.Json;
internal static class JObjectExtensions
{
private class JTokenResolvers : Dictionary<JTokenType, Func<JToken, DynamicJsonClassOptions?, object?>>
{
}
private static readonly JTokenResolvers Resolvers = new()
{
{ JTokenType.Array, ConvertJTokenArray },
{ JTokenType.Boolean, (jToken, _) => jToken.Value<bool>() },
{ JTokenType.Bytes, (jToken, _) => jToken.Value<byte[]>() },
{ JTokenType.Date, (jToken, _) => jToken.Value<DateTime>() },
{ JTokenType.Float, ConvertJTokenFloat },
{ JTokenType.Guid, (jToken, _) => jToken.Value<Guid>() },
{ JTokenType.Integer, ConvertJTokenInteger },
{ JTokenType.None, (_, _) => null },
{ JTokenType.Null, (_, _) => null },
{ JTokenType.Object, ConvertJObject },
{ JTokenType.Property, ConvertJTokenProperty },
{ JTokenType.String, (jToken, _) => jToken.Value<string>() },
{ JTokenType.TimeSpan, (jToken, _) => jToken.Value<TimeSpan>() },
{ JTokenType.Undefined, (_, _) => null },
{ JTokenType.Uri, (o, _) => o.Value<Uri>() },
};
internal static DynamicClass? ToDynamicJsonClass(this JObject? src, DynamicJsonClassOptions? options = null)
{
if (src == null)
{
return null;
}
var dynamicPropertyWithValues = new List<DynamicPropertyWithValue>();
foreach (var prop in src.Properties())
{
var value = Resolvers[prop.Type](prop.Value, options);
if (value != null)
{
dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Name, value));
}
}
return CreateInstance(dynamicPropertyWithValues);
}
internal static IEnumerable ToDynamicClassArray(this JArray? src, DynamicJsonClassOptions? options = null)
{
if (src == null)
{
return new object?[0];
}
return ConvertJTokenArray(src, options);
}
private static object? ConvertJObject(JToken arg, DynamicJsonClassOptions? options = null)
{
if (arg is JObject asJObject)
{
return asJObject.ToDynamicJsonClass(options);
}
return GetResolverFor(arg)(arg, options);
}
private static object PassThrough(JToken arg, DynamicJsonClassOptions? options)
{
return arg;
}
private static Func<JToken, DynamicJsonClassOptions?, object?> GetResolverFor(JToken arg)
{
return Resolvers.TryGetValue(arg.Type, out var result) ? result : PassThrough;
}
private static object ConvertJTokenFloat(JToken arg, DynamicJsonClassOptions? options = null)
{
if (arg.Type != JTokenType.Float)
{
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to double or float.");
}
if (options?.FloatConvertBehavior == FloatBehavior.UseFloat)
{
try
{
return arg.Value<float>();
}
catch
{
return arg.Value<double>();
}
}
if (options?.FloatConvertBehavior == FloatBehavior.UseDecimal)
{
try
{
return arg.Value<decimal>();
}
catch
{
return arg.Value<double>();
}
}
return arg.Value<double>();
}
private static object ConvertJTokenInteger(JToken arg, DynamicJsonClassOptions? options = null)
{
if (arg.Type != JTokenType.Integer)
{
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to long or int.");
}
var longValue = arg.Value<long>();
if (options is null || options.IntegerConvertBehavior == IntegerBehavior.UseInt)
{
if (longValue is >= int.MinValue and <= int.MaxValue)
{
return Convert.ToInt32(longValue);
}
}
return longValue;
}
private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null)
{
var resolver = GetResolverFor(arg);
if (resolver is null)
{
throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}.");
}
return resolver(arg, options);
}
private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOptions? options = null)
{
if (arg is not JArray array)
{
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}.");
}
var result = new List<object?>();
foreach (var item in array)
{
result.Add(ConvertJObject(item));
}
var distinctType = FindSameTypeOf(result);
return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType);
}
private static Type? FindSameTypeOf(IEnumerable<object?> src)
{
var types = src.Select(o => o?.GetType()).Distinct().OfType<Type>().ToArray();
return types.Length == 1 ? types[0] : null;
}
private static IEnumerable ConvertToTypedArray(IEnumerable<object?> src, Type newType)
{
var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType);
return (IEnumerable)method.Invoke(null, new object[] { src })!;
}
private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JObjectExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
private static T[] ConvertToTypedArrayGeneric<T>(IEnumerable<object> src)
{
return src.Cast<T>().ToArray();
}
public static DynamicClass CreateInstance(IList<DynamicPropertyWithValue> dynamicPropertiesWithValue, bool createParameterCtor = true)
{
var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast<DynamicProperty>().ToArray(), createParameterCtor);
var dynamicClass = (DynamicClass)Activator.CreateInstance(type);
foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null))
{
dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!);
}
return dynamicClass;
}
}

View File

@@ -72,6 +72,9 @@ public class Mapping : IMapping
/// <inheritdoc />
public ITimeSettings? TimeSettings { get; }
/// <inheritdoc />
public object? Data { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary>
@@ -91,6 +94,7 @@ public class Mapping : IMapping
/// <param name="webhooks">The Webhooks. [Optional]</param>
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
/// <param name="data">The data object. [Optional]</param>
public Mapping(
Guid guid,
DateTime updatedAt,
@@ -107,7 +111,8 @@ public class Mapping : IMapping
int? stateTimes,
IWebhook[]? webhooks,
bool? useWebhooksFireAndForget,
ITimeSettings? timeSettings)
ITimeSettings? timeSettings,
object? data)
{
Guid = guid;
UpdatedAt = updatedAt;
@@ -125,6 +130,7 @@ public class Mapping : IMapping
Webhooks = webhooks;
UseWebhooksFireAndForget = useWebhooksFireAndForget;
TimeSettings = timeSettings;
Data = data;
}
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />

View File

@@ -5,8 +5,8 @@ using AnyOfTypes;
using Newtonsoft.Json.Linq;
using Stef.Validation;
using WireMock.Extensions;
using WireMock.Json;
using WireMock.Models;
using WireMock.Util;
namespace WireMock.Matchers;
@@ -100,38 +100,55 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
{
double match = MatchScores.Mismatch;
JObject value;
switch (input)
JArray jArray;
try
{
case JObject valueAsJObject:
value = valueAsJObject;
break;
case { } valueAsObject:
value = JObject.FromObject(valueAsObject);
break;
default:
return MatchScores.Mismatch;
jArray = new JArray { input };
}
catch
{
jArray = new JArray { JToken.FromObject(input) };
}
//enumerable = jArray.ToDynamicClassArray();
//JObject value;
//switch (input)
//{
// case JObject valueAsJObject:
// value = valueAsJObject;
// break;
// case { } valueAsObject:
// value = JObject.FromObject(valueAsObject);
// break;
// default:
// return MatchScores.Mismatch;
//}
// Convert a single object to a Queryable JObject-list with 1 entry.
var queryable1 = new[] { value }.AsQueryable();
//var queryable1 = new[] { value }.AsQueryable();
var queryable = jArray.ToDynamicClassArray().AsQueryable();
try
{
// Generate the DynamicLinq select statement.
string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
//string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
// Execute DynamicLinq Select statement.
var queryable2 = queryable1.Select(dynamicSelect);
//var queryable2 = queryable1.Select(dynamicSelect);
// Use the Any(...) method to check if the result matches.
match = MatchScores.ToScore(_patterns.Select(pattern => queryable2.Any(pattern)).ToArray(), MatchOperator);
var patternsAsStringArray = _patterns.Select(p => p.GetPattern()).ToArray();
var scores = patternsAsStringArray.Select(p => queryable.Any(p)).ToArray();
match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator);
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
catch
catch (Exception e)
{
if (ThrowException)
{

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace WireMock.Matchers;

View File

@@ -1,8 +1,10 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Stef.Validation;
using WireMock.Http;
using WireMock.Matchers;
using WireMock.Serialization;
using WireMock.Settings;
using WireMock.Util;
@@ -47,12 +49,33 @@ internal class ProxyHelper
var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate).ConfigureAwait(false);
IMapping? newMapping = null;
if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) &&
(proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
var saveMappingSettings = proxyAndRecordSettings.SaveMappingSettings;
bool save = true;
if (saveMappingSettings != null)
{
save &= Check(saveMappingSettings.StatusCodePattern,
() => HttpStatusRangeParser.IsMatch(saveMappingSettings.StatusCodePattern, responseMessage.StatusCode)
);
save &= Check(saveMappingSettings.HttpMethods,
() => saveMappingSettings.HttpMethods.Value.Contains(requestMessage.Method, StringComparer.OrdinalIgnoreCase)
);
}
if (save && (proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
{
newMapping = _proxyMappingConverter.ToMapping(mapping, proxyAndRecordSettings, requestMessage, responseMessage);
}
return (responseMessage, newMapping);
}
private static bool Check<T>(ProxySaveMappingSetting<T>? saveMappingSetting, Func<bool> action)
{
var isMatch = saveMappingSetting is null || action();
var matchBehaviour = saveMappingSetting?.MatchBehaviour ?? MatchBehaviour.AcceptOnMatch;
return isMatch == (matchBehaviour == MatchBehaviour.AcceptOnMatch);
}
}

View File

@@ -107,14 +107,12 @@ public partial class Response : IResponseBuilder
/// <summary>
/// Initializes a new instance of the <see cref="Response"/> class.
/// </summary>
/// <param name="responseMessage">
/// The response.
/// </param>
/// <param name="responseMessage">The response.</param>
private Response(ResponseMessage responseMessage)
{
ResponseMessage = responseMessage;
}
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(int)"/>
[PublicAPI]
public IResponseBuilder WithStatusCode(int code)

View File

@@ -181,6 +181,7 @@ internal class MappingConverter
Scenario = mapping.Scenario,
WhenStateIs = mapping.ExecutionConditionState,
SetStateTo = mapping.NextState,
Data = mapping.Data,
Request = new RequestModel
{
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel

View File

@@ -26,8 +26,12 @@ internal class ProxyMappingConverter
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
}
public IMapping ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
public IMapping? ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
{
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[0];
var request = (Request?)mapping?.RequestMatcher;
var clientIPMatcher = request?.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
var pathMatcher = request?.GetRequestMessageMatcher<RequestMessagePathMatcher>();
@@ -37,11 +41,6 @@ internal class ProxyMappingConverter
var methodMatcher = request?.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
var bodyMatcher = request?.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
var newRequest = Request.Create();
// ClientIP
@@ -178,7 +177,8 @@ internal class ProxyMappingConverter
stateTimes: null,
webhooks: null,
useWebhooksFireAndForget: null,
timeSettings: null
timeSettings: null,
data: mapping?.Data
);
}
}

View File

@@ -166,4 +166,14 @@ public interface IRespondWithAProvider
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars
);
/// <summary>
/// Data Object which can be used when WithTransformer is used.
/// e.g. lookup an path in this object using
/// <param name="data">The data dictionary object.</param>
/// <example>
/// lookup data "1"
/// </example>
/// </summary>
IRespondWithAProvider WithData(object data);
}

View File

@@ -2,6 +2,7 @@
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Stef.Validation;
using WireMock.Matchers.Request;
using WireMock.Models;
@@ -39,6 +40,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
public ITimeSettings? TimeSettings { get; private set; }
public object? Data { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
/// </summary>
@@ -88,10 +91,20 @@ internal class RespondWithAProvider : IRespondWithAProvider
_timesInSameState,
Webhooks,
_useWebhookFireAndForget,
TimeSettings);
TimeSettings,
Data);
_registrationCallback(mapping, _saveToFile);
}
/// <inheritdoc />
[PublicAPI]
public IRespondWithAProvider WithData(object data)
{
Data = data;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithGuid(string guid)
{

View File

@@ -46,7 +46,7 @@ public partial class WireMockServer
}
var respondProvider = Given(requestBuilder, mappingModel.SaveToFile == true);
if (guid != null)
{
respondProvider = respondProvider.WithGuid(guid.Value);
@@ -56,6 +56,11 @@ public partial class WireMockServer
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
}
if (mappingModel.Data != null)
{
respondProvider = respondProvider.WithData(mappingModel.Data);
}
var timeSettings = TimeSettingsMapper.Map(mappingModel.TimeSettings);
if (timeSettings != null)
{

View File

@@ -28,9 +28,28 @@ public class ProxyAndRecordSettings : HttpClientSettings
/// <summary>
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
///
/// Deprecated : use SaveMappingSettings.
/// </summary>
[PublicAPI]
public string SaveMappingForStatusCodePattern { get; set; } = "*";
public string SaveMappingForStatusCodePattern
{
set
{
if (SaveMappingSettings is null)
{
SaveMappingSettings = new ProxySaveMappingSettings();
}
SaveMappingSettings.StatusCodePattern = value;
}
}
/// <summary>
/// Additional SaveMappingSettings.
/// </summary>
[PublicAPI]
public ProxySaveMappingSettings? SaveMappingSettings { get; set; }
/// <summary>
/// Defines a list from headers which will be excluded from the saved mappings.

View File

@@ -0,0 +1,20 @@
using WireMock.Matchers;
namespace WireMock.Settings;
public class ProxySaveMappingSetting<T>
{
public MatchBehaviour MatchBehaviour { get; } = MatchBehaviour.AcceptOnMatch;
public T Value { get; private set; }
public ProxySaveMappingSetting(T value, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
Value = value;
MatchBehaviour = matchBehaviour;
}
public static implicit operator ProxySaveMappingSetting<T>(T value) => new ProxySaveMappingSetting<T>(value);
public static implicit operator T(ProxySaveMappingSetting<T> @this) => @this.Value;
}

View File

@@ -0,0 +1,22 @@
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// ProxySaveMappingSettings
/// </summary>
public class ProxySaveMappingSettings
{
/// <summary>
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
/// </summary>
[PublicAPI]
public ProxySaveMappingSetting<string>? StatusCodePattern { get; set; } = "*";
/// <summary>
/// Only save these Http Methods. (Note that SaveMapping must also be set to true.)
/// </summary>
[PublicAPI]
public ProxySaveMappingSetting<string[]>? HttpMethods { get; set; }
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using JetBrains.Annotations;
using Stef.Validation;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Types;
using WireMock.Util;
@@ -111,7 +112,12 @@ public static class WireMockServerSettingsParser
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
Url = proxyUrl!
Url = proxyUrl!,
SaveMappingSettings = new ProxySaveMappingSettings
{
StatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
// HttpMethods = new ProxySaveMappingSetting<string[]>(parser.GetValues("DoNotSaveMappingForHttpMethods", new string[0]), MatchBehaviour.RejectOnMatch)
}
};
string? proxyAddress = parser.GetStringValue("WebProxyAddress");

View File

@@ -10,4 +10,6 @@ internal struct TransformModel
public IRequestMessage request { get; set; }
public IResponseMessage? response { get; set; }
public object data { get; set; }
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stef.Validation;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
@@ -115,7 +116,8 @@ internal class Transformer : ITransformer
{
mapping = mapping,
request = request,
response = response
response = response,
data = mapping.Data ?? new { }
});
}

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -11,38 +9,6 @@ namespace WireMock.Util;
internal static class JsonUtils
{
public static Type CreateTypeFromJObject(JObject instance, string? fullName = null)
{
static Type ConvertType(JToken value, string? propertyName = null)
{
var type = value.Type;
return type switch
{
JTokenType.Array => value.HasValues ? ConvertType(value.First!, propertyName).MakeArrayType() : typeof(object).MakeArrayType(),
JTokenType.Boolean => typeof(bool),
JTokenType.Bytes => typeof(byte[]),
JTokenType.Date => typeof(DateTime),
JTokenType.Guid => typeof(Guid),
JTokenType.Float => typeof(float),
JTokenType.Integer => typeof(long),
JTokenType.Null => typeof(object),
JTokenType.Object => CreateTypeFromJObject((JObject)value, propertyName),
JTokenType.String => typeof(string),
JTokenType.TimeSpan => typeof(TimeSpan),
JTokenType.Uri => typeof(string),
_ => typeof(object)
};
}
var properties = new Dictionary<string, Type>();
foreach (var item in instance.Properties())
{
properties.Add(item.Name, ConvertType(item.Value, item.Name));
}
return TypeBuilderUtils.BuildType(properties, fullName) ?? throw new InvalidOperationException();
}
public static bool TryParseAsJObject(string? strInput, [NotNullWhen(true)] out JObject? value)
{
value = null;
@@ -140,100 +106,4 @@ internal static class JsonUtils
_ => throw new NotSupportedException($"Unable to convert value to {typeof(T)}.")
};
}
public static string GenerateDynamicLinqStatement(JToken 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)
{
switch (node.Type)
{
case JTokenType.Object:
ProcessObject(node, propertyName, lines);
break;
case JTokenType.Array:
ProcessArray(node, propertyName, lines);
break;
default:
ProcessItem(node, path ?? "it", propertyName, lines);
break;
}
}
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 (var child in node.Children<JProperty>().ToArray())
{
WalkNode(child.Value, child.Path, child.Name, items);
}
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.
var idx = 0;
foreach (var 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)
{
var castText = node.Type switch
{
JTokenType.Boolean => $"bool({path})",
JTokenType.Date => $"DateTime({path})",
JTokenType.Float => $"double({path})",
JTokenType.Guid => $"Guid({path})",
JTokenType.Integer => $"long({path})",
JTokenType.Null => "null",
JTokenType.String => $"string({path})",
JTokenType.TimeSpan => $"TimeSpan({path})",
JTokenType.Uri => $"Uri({path})",
_ => 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

@@ -53,13 +53,12 @@
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All" />
<PackageReference Include="JsonConverter.Abstractions" Version="0.3.0" />
<PackageReference Include="JsonConverter.Abstractions" Version="0.4.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NJsonSchema.Extensions" Version="0.1.0" />
<PackageReference Include="NSwag.Core" Version="13.16.1" />
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.23" />
<PackageReference Include="JmesPath.Net" Version="1.0.125" />
<PackageReference Include="AnyOf" Version="0.3.0" />
<PackageReference Include="TinyMapper" Version="3.0.3" />
@@ -177,13 +176,13 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Handlebars.Net.Helpers" Version="2.3.12" />
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.3.12" />
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.3.12" />
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.12" />
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.12" />
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.3.12" />
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.12" />
<PackageReference Include="Handlebars.Net.Helpers" Version="2.3.15-preview-01" />
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.3.15-preview-01" />
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.3.15-preview-01" />
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.15-preview-01" />
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.15-preview-01" />
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.3.15-preview-01" />
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.15-preview-01" />
</ItemGroup>
<ItemGroup>

View File

@@ -177,7 +177,7 @@ public class WireMockMiddlewareTests
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null);
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null);
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
var requestBuilder = Request.Create().UsingAnyMethod();
@@ -231,7 +231,7 @@ public class WireMockMiddlewareTests
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null);
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null);
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
var requestBuilder = Request.Create().UsingAnyMethod();

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
@@ -279,4 +280,38 @@ public class ResponseWithCallbackTests
response.Message.BodyData.BodyAsString.Should().Be("/fooBar");
response.Message.StatusCode.Should().Be(302);
}
// https://github.com/WireMock-Net/WireMock.Net/issues/898
[Fact]
public async Task Response_WithCallback_WithRequestHeaders_Should_BeAccessibleInCallback()
{
// Assign
var headerKey = "headerKey";
var headerValues = new[] { "abc" };
var requestHeaders = new Dictionary<string, string[]>
{
{ headerKey, headerValues }
};
var requestMessage = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1", null, requestHeaders);
var responseBuilder = Response.Create()
.WithCallback(request =>
{
var headersFromRequest = request!.Headers![headerKey].ToList();
headersFromRequest.Add("extra");
return new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>>
{
{ headerKey, new WireMockList<string>(headersFromRequest) }
}
};
});
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, requestMessage, _settings).ConfigureAwait(false);
// Assert
response.Message.Headers![headerKey].Should().Contain("extra");
}
}

View File

@@ -163,22 +163,6 @@ public class ResponseWithHandlebarsLinqTests
Check.ThatAsyncCode(() => responseBuilder.ProvideResponseAsync(mappingMock.Object, request, _settings)).Throws<ArgumentException>();
}
[Fact]
public void Response_ProvideResponse_Handlebars_Linq1_Throws_ArgumentNullException()
{
// Assign
var body = new BodyData();
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", "::1", body);
var responseBuilder = Response.Create()
.WithBodyAsJson(new { x = "{{Linq request.body 'Name'}}" })
.WithTransformer();
// Act
Check.ThatAsyncCode(() => responseBuilder.ProvideResponseAsync(mappingMock.Object, request, _settings)).Throws<ArgumentNullException>();
}
[Fact]
public void Response_ProvideResponse_Handlebars_Linq1_Throws_HandlebarsException()
{

View File

@@ -782,4 +782,31 @@ public class ResponseWithTransformerTests
response.Message.BodyData!.BodyAsString.Should().Be(text);
response.Message.BodyData.Encoding.Should().Be(enc);
}
[Theory]
[InlineData("/wiremock-data/1", "one")]
[InlineData("/wiremock-data/2", "two")]
[InlineData("/wiremock-data/3", "N/A")]
public async Task Response_ProvideResponse_Handlebars_DataObject(string path, string expected)
{
// Arrange
var request = new RequestMessage(new UrlDetails("https://localhost" + path), "POST", ClientIp);
var data = new Dictionary<string, object?>
{
{ "1", "one" },
{ "2", "two" }
};
var responseBuilder = Response.Create()
.WithBody("{{lookup data request.PathSegments.[1] 'N/A'}}")
.WithTransformer();
_mappingMock.SetupGet(m => m.Data).Returns(data);
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
response.Message.BodyData!.BodyAsString.Should().Be(expected);
}
}

View File

@@ -119,7 +119,25 @@ public partial class MappingConverterTests
.WithDelay(12345)
.WithTransformer();
return new Mapping(guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
return new Mapping(
guid,
_updatedAt,
string.Empty,
string.Empty,
null,
_settings,
request,
response,
42,
null,
null,
null,
null,
null,
false,
null,
data: null
);
}
}
#endif

View File

@@ -57,7 +57,7 @@ public partial class MappingConverterTests
}
}
};
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null, data: null);
// Act
var model = _sut.ToMappingModel(mapping);
@@ -130,7 +130,7 @@ public partial class MappingConverterTests
}
}
};
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null, data: null);
// Act
var model = _sut.ToMappingModel(mapping);
@@ -168,7 +168,7 @@ public partial class MappingConverterTests
var description = "my-description";
var request = Request.Create();
var response = Response.Create();
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null);
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null, data: null);
// Act
var model = _sut.ToMappingModel(mapping);
@@ -188,7 +188,7 @@ public partial class MappingConverterTests
// Assign
var request = Request.Create();
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
// Act
var model = _sut.ToMappingModel(mapping);
@@ -217,7 +217,7 @@ public partial class MappingConverterTests
End = end,
TTL = ttl
};
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings, data: null);
// Act
var model = _sut.ToMappingModel(mapping);
@@ -248,7 +248,7 @@ public partial class MappingConverterTests
{
var request = Request.Create();
var response = Response.Create().WithDelay(test.Delay);
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null);
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
// Act
var model = _sut.ToMappingModel(mapping);
@@ -266,7 +266,7 @@ public partial class MappingConverterTests
var delay = 1000;
var request = Request.Create();
var response = Response.Create().WithDelay(delay);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
// Act
var model = _sut.ToMappingModel(mapping);
@@ -286,7 +286,7 @@ public partial class MappingConverterTests
int minimumDelay = 1000;
var request = Request.Create();
var response = Response.Create().WithRandomDelay(minimumDelay);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
// Act
var model = _sut.ToMappingModel(mapping);
@@ -309,7 +309,7 @@ public partial class MappingConverterTests
int maximumDelay = 2000;
var request = Request.Create();
var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
// Act
var model = _sut.ToMappingModel(mapping);

View File

@@ -1,6 +1,7 @@
#if !(NET452 || NET461 || NETCOREAPP3_1)
using System;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using VerifyTests;
using VerifyXunit;
@@ -70,7 +71,7 @@ public class ProxyMappingConverterTests
var responseMessage = new ResponseMessage();
// Act
var proxyMapping = _sut.ToMapping(mappingMock.Object, proxyAndRecordSettings, requestMessageMock.Object, responseMessage);
var proxyMapping = _sut.ToMapping(mappingMock.Object, proxyAndRecordSettings, requestMessageMock.Object, responseMessage)!;
// Assert
var model = _mappingConverter.ToMappingModel(proxyMapping);

View File

@@ -12,7 +12,7 @@
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../../src/WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<!--https://developercommunity.visualstudio.com/content/problem/26347/unit-tests-fail-with-fileloadexception-newtonsoftj-1.html-->
@@ -24,6 +24,10 @@
<DefineConstants>NETFRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Util\JsonUtilsTests.cs" />
</ItemGroup>
<ItemGroup>
<!-- https://stackoverflow.com/questions/59406201/filenesting-not-working-for-class-or-shared-library-projects -->
<ProjectCapability Include="ConfigurableFileNesting" />
@@ -59,12 +63,13 @@
</PackageReference>
<PackageReference Include="Moq" Version="4.17.2" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.1-preview-02" />
<PackageReference Include="System.Threading" Version="4.3.0" />
<PackageReference Include="RestEase" Version="1.5.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="NFluent" Version="2.8.0" />
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.23" />
<PackageReference Include="AnyOf" Version="0.3.0" />
</ItemGroup>
@@ -93,7 +98,8 @@
<ItemGroup Condition="'$(TargetFramework)' != 'net452'">
<PackageReference Include="System.Net.Http.Json" Version="3.2.1" />
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.3.0" />
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.4.0" />
<!--<PackageReference Include="JsonConverter.NewtonSoft.Json" Version="0.4.0" />-->
</ItemGroup>
<ItemGroup>
@@ -113,8 +119,8 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="client_cert.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<DependentUpon>WireMockServerTests.ClientCertificate.cs</DependentUpon>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<DependentUpon>WireMockServerTests.ClientCertificate.cs</DependentUpon>
</None>
</ItemGroup>
@@ -123,7 +129,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Pact\files\" />
<Folder Include="Pact\files\" />
</ItemGroup>
</Project>

View File

@@ -248,6 +248,45 @@ public class WireMockServerProxyTests
fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}
[Fact]
public async Task WireMockServer_Proxy_With_DoNotSaveMappingForHttpMethod_Should_Not_SaveMapping()
{
// Assign
var fileSystemHandlerMock = new Mock<IFileSystemHandler>();
fileSystemHandlerMock.Setup(f => f.GetMappingFolder()).Returns("m");
var settings = new WireMockServerSettings
{
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = "http://www.google.com",
SaveMapping = true,
SaveMappingToFile = true,
SaveMappingSettings = new ProxySaveMappingSettings
{
HttpMethods = new ProxySaveMappingSetting<string[]>(new string[] { "GET" }, MatchBehaviour.RejectOnMatch) // To make sure that we don't want this mapping
}
},
FileSystemHandler = fileSystemHandlerMock.Object
};
var server = WireMockServer.Start(settings);
// Act
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(server.Urls[0])
};
var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false };
await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false);
// Assert
server.Mappings.Should().HaveCount(1);
// Verify
fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}
[Fact]
public async Task WireMockServer_Proxy_Should_log_proxied_requests()
{