GET "/__admin/mappings"

This commit is contained in:
Stef Heyenrath
2017-01-24 22:06:25 +01:00
parent 45aa83ee91
commit 3f84ba8b20
44 changed files with 805 additions and 302 deletions

View File

@@ -5,7 +5,6 @@ using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
namespace WireMock.Net.ConsoleApplication
{
static class Program
@@ -14,9 +13,9 @@ namespace WireMock.Net.ConsoleApplication
{
int port;
if (args.Length == 0 || !int.TryParse(args[0], out port))
port = 8080;
port = 9090;
var server = FluentMockServer.Start(port);
var server = FluentMockServer.StartWithAdminInterface(port);
Console.WriteLine("FluentMockServer running at {0}", server.Port);
server
@@ -24,17 +23,17 @@ namespace WireMock.Net.ConsoleApplication
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""/x with FUNC 200""}"));
.WithBody(@"{ ""result"": ""Contains x with FUNC 200""}"));
// http://localhost:8080/gffgfgf/sddsds?start=1000&stop=1&stop=2
server
.Given(Request.Create().WithUrl("/*").UsingGet())
.Given(Request.Create().WithUrl("/*").UsingGet().WithParam("start"))
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithHeader("Transformed-Postman-Token", "token is {{request.headers.Postman-Token}}")
.WithBody(@"{""msg"": ""Hello world! : {{request.url}} : {{request.path}} :
bykey={{request.query.start}} : bykey={{request.query.stop}} byidx0={{request.query.stop.[0]}} byidx1={{request.query.stop.[1]}}""")
.WithBody(@"{""msg"": ""Hello world, {{request.url}}, {{request.path}} :
bykey={{request.query.start}}, bykey={{request.query.stop}}, byidx0={{request.query.stop.[0]}}, byidx1={{request.query.stop.[1]}}""")
.WithTransformer()
.WithDelay(TimeSpan.FromMilliseconds(100))
);
@@ -47,7 +46,7 @@ namespace WireMock.Net.ConsoleApplication
.WithBody(@"{ ""result"": ""data posted with FUNC 201""}"));
server
.Given(Request.Create().WithUrl("/data", "/ax").UsingPost())
.Given(Request.Create().WithUrl("/data", "/ax").UsingPost().WithHeader("Content-Type", "application/json*"))
.RespondWith(Response.Create()
.WithStatusCode(201)
.WithHeader("Content-Type", "application/json")
@@ -60,6 +59,13 @@ namespace WireMock.Net.ConsoleApplication
.WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""json posted with 201""}"));
server
.Given(Request.Create().WithUrl("/json2").UsingPost().WithBody("x"))
.RespondWith(Response.Create()
.WithStatusCode(201)
.WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""json posted with x - 201""}"));
server
.Given(Request.Create().WithUrl("/data").UsingDelete())
.RespondWith(Response.Create()
@@ -67,6 +73,11 @@ namespace WireMock.Net.ConsoleApplication
.WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""data deleted with 200""}"));
server
.Given(Request.Create().WithUrl("/nobody").UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200));
Console.WriteLine("Press any key to stop the server");
Console.ReadKey();

View File

@@ -0,0 +1,16 @@
namespace WireMock.Admin
{
/// <summary>
/// Body Model
/// </summary>
public class BodyModel
{
/// <summary>
/// Gets or sets the matcher.
/// </summary>
/// <value>
/// The matcher.
/// </value>
public MatcherModel Matcher { get; set; }
}
}

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace WireMock.Admin
{
/// <summary>
/// Cookie Model
/// </summary>
public class CookieModel
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the matchers.
/// </summary>
/// <value>
/// The matchers.
/// </value>
public IList<MatcherModel> Matchers { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
//namespace WireMock.Admin
//{
// public class FuncModel
// {
// public string Name { get; set; }
// }
//}

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace WireMock.Admin
{
/// <summary>
/// Header Model
/// </summary>
public class HeaderModel
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the matchers.
/// </summary>
/// <value>
/// The matchers.
/// </value>
public IList<MatcherModel> Matchers { get; set; }
}
}

View File

@@ -0,0 +1,34 @@
using System;
namespace WireMock.Admin
{
/// <summary>
/// MappingModel
/// </summary>
public class MappingModel
{
/// <summary>
/// Gets or sets the unique identifier.
/// </summary>
/// <value>
/// The unique identifier.
/// </value>
public Guid Guid { get; set; }
/// <summary>
/// Gets or sets the request.
/// </summary>
/// <value>
/// The request.
/// </value>
public RequestModel Request { get; set; }
/// <summary>
/// Gets or sets the response.
/// </summary>
/// <value>
/// The response.
/// </value>
public ResponseModel Response { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
namespace WireMock.Admin
{
/// <summary>
/// MatcherModel
/// </summary>
public class MatcherModel
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the pattern.
/// </summary>
/// <value>
/// The pattern.
/// </value>
public string Pattern { get; set; }
}
}

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace WireMock.Admin
{
/// <summary>
/// Param Model
/// </summary>
public class ParamModel
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the values.
/// </summary>
/// <value>
/// The values.
/// </value>
public IList<string> Values { get; set; }
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
namespace WireMock.Admin
{
/// <summary>
/// RequestModel
/// </summary>
public class RequestModel
{
/// <summary>
/// Gets or sets the URL.
/// </summary>
/// <value>
/// The URL.
/// </value>
public UrlModel Url { get; set; }
/// <summary>
/// The verbs
/// </summary>
public string[] Verbs { get; set; }
/// <summary>
/// Gets or sets the Headers.
/// </summary>
/// <value>
/// The Headers.
/// </value>
public IList<HeaderModel> Headers { get; set; }
/// <summary>
/// Gets or sets the Cookies.
/// </summary>
/// <value>
/// The Cookies.
/// </value>
public IList<CookieModel> Cookies { get; set; }
/// <summary>
/// Gets or sets the Params.
/// </summary>
/// <value>
/// The Headers.
/// </value>
public IList<ParamModel> Params { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
/// <value>
/// The body.
/// </value>
public BodyModel Body { get; set; }
}
}

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
namespace WireMock.Admin
{
/// <summary>
/// ResponseModel
/// </summary>
public class ResponseModel
{
/// <summary>
/// Gets or sets the HTTP status.
/// </summary>
/// <value>
/// The HTTP status.
/// </value>
public int StatusCode { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
/// <value>
/// The body.
/// </value>
public string Body { get; set; }
/// <summary>
/// Gets or sets the headers.
/// </summary>
/// <value>
/// The headers.
/// </value>
public IDictionary<string, string> Headers { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace WireMock.Admin
{
/// <summary>
/// UrlModel
/// </summary>
public class UrlModel
{
/// <summary>
/// Gets or sets the matchers.
/// </summary>
/// <value>
/// The matchers.
/// </value>
public IList<MatcherModel> Matchers { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Validation;
namespace WireMock
{
internal class DynamicResponseProvider : IResponseProvider
{
private readonly Func<ResponseMessage> _responseMessageFunc;
public DynamicResponseProvider([NotNull] Func<ResponseMessage> responseMessageFunc)
{
Check.NotNull(responseMessageFunc, nameof(responseMessageFunc));
_responseMessageFunc = responseMessageFunc;
}
public Task<ResponseMessage> ProvideResponse(RequestMessage requestMessage)
{
return Task.FromResult(_responseMessageFunc());
}
}
}

View File

@@ -1,17 +1,11 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
[module:
SuppressMessage("StyleCop.CSharp.DocumentationRules",
"SA1633:FileMustHaveHeader",
Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
using System.Threading.Tasks;
namespace WireMock
{
/// <summary>
/// The ProvideResponses interface.
/// The Response Provider interface.
/// </summary>
public interface IProvideResponses
public interface IResponseProvider
{
/// <summary>
/// The provide response.

View File

@@ -6,24 +6,24 @@ namespace WireMock
/// <summary>
/// The route.
/// </summary>
public class Route
public class Mapping
{
/// <summary>
/// The _request matcher.
/// The Request matcher.
/// </summary>
public IRequestMatcher RequestMatcher { get; }
/// <summary>
/// The _provider.
/// The Provider.
/// </summary>
public IProvideResponses Provider { get; }
public IResponseProvider Provider { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Route"/> class.
/// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary>
/// <param name="requestMatcher">The request matcher.</param>
/// <param name="provider">The provider.</param>
public Route(IRequestMatcher requestMatcher, IProvideResponses provider)
public Mapping(IRequestMatcher requestMatcher, IResponseProvider provider)
{
RequestMatcher = requestMatcher;
Provider = provider;

View File

@@ -13,5 +13,11 @@
/// <c>true</c> if the specified input is match; otherwise, <c>false</c>.
/// </returns>
bool IsMatch(string input);
/// <summary>
/// Gets the pattern.
/// </summary>
/// <returns>Pattern</returns>
string GetPattern();
}
}

View File

@@ -48,5 +48,14 @@ namespace WireMock.Matchers
return false;
}
}
/// <summary>
/// Gets the pattern.
/// </summary>
/// <returns>Pattern</returns>
public string GetPattern()
{
return _pattern;
}
}
}

View File

@@ -11,6 +11,7 @@ namespace WireMock.Matchers
/// <seealso cref="WireMock.Matchers.IMatcher" />
public class RegexMatcher : IMatcher
{
private readonly string _pattern;
private readonly Regex _expression;
/// <summary>
@@ -21,7 +22,8 @@ namespace WireMock.Matchers
{
Check.NotNull(pattern, nameof(pattern));
_expression = new Regex(pattern, RegexOptions.Compiled);
_pattern = pattern;
_expression = new Regex(_pattern, RegexOptions.Compiled);
}
/// <summary>
@@ -45,5 +47,14 @@ namespace WireMock.Matchers
return false;
}
}
/// <summary>
/// Gets the pattern.
/// </summary>
/// <returns>Pattern</returns>
public string GetPattern()
{
return _pattern;
}
}
}

View File

@@ -19,11 +19,6 @@ namespace WireMock.Matchers.Request
/// </summary>
private readonly byte[] _bodyData;
/// <summary>
/// The matcher.
/// </summary>
private readonly IMatcher _matcher;
/// <summary>
/// The body function
/// </summary>
@@ -34,6 +29,11 @@ namespace WireMock.Matchers.Request
/// </summary>
private readonly Func<byte[], bool> _bodyDataFunc;
/// <summary>
/// The matcher.
/// </summary>
public readonly IMatcher Matcher;
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
@@ -91,7 +91,7 @@ namespace WireMock.Matchers.Request
public RequestMessageBodyMatcher([NotNull] IMatcher matcher)
{
Check.NotNull(matcher, nameof(matcher));
_matcher = matcher;
Matcher = matcher;
}
/// <summary>
@@ -103,8 +103,8 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
if (_matcher != null)
return _matcher.IsMatch(requestMessage.Body);
if (Matcher != null)
return Matcher.IsMatch(requestMessage.Body);
if (_body != null)
return requestMessage.Body == _body;

View File

@@ -1,13 +1,14 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Validation;
namespace WireMock.Matchers.Request
{
/// <summary>
/// The composite request matcher.
/// </summary>
public class RequestMessageCompositeMatcher : IRequestMatcher
public abstract class RequestMessageCompositeMatcher : IRequestMatcher
{
private readonly CompositeMatcherType _type;
@@ -17,15 +18,17 @@ namespace WireMock.Matchers.Request
/// <value>
/// The request matchers.
/// </value>
public IEnumerable<IRequestMatcher> RequestMatchers { get; }
private IEnumerable<IRequestMatcher> RequestMatchers { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
/// </summary>
/// <param name="requestMatchers">The request matchers.</param>
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
public RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
{
Check.NotNull(requestMatchers, nameof(requestMatchers));
_type = type;
RequestMatchers = requestMatchers;
}
@@ -37,11 +40,11 @@ namespace WireMock.Matchers.Request
/// <returns>
/// <c>true</c> if the specified RequestMessage is match; otherwise, <c>false</c>.
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
public virtual bool IsMatch(RequestMessage requestMessage)
{
return _type == CompositeMatcherType.And ?
RequestMatchers.All(spec => spec.IsMatch(requestMessage)) :
RequestMatchers.Any(spec => spec.IsMatch(requestMessage));
RequestMatchers.All(matcher => matcher.IsMatch(requestMessage)) :
RequestMatchers.Any(matcher => matcher.IsMatch(requestMessage));
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Validation;
@@ -10,12 +11,21 @@ namespace WireMock.Matchers.Request
/// </summary>
public class RequestMessageCookieMatcher : IRequestMatcher
{
private readonly string _name;
private readonly Func<IDictionary<string, string>, bool>[] _cookieFuncs;
private readonly IMatcher _matcher;
private readonly Func<IDictionary<string, string>, bool> _cookieFunc;
/// <summary>
/// The name
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the matchers.
/// </summary>
/// <value>
/// The matchers.
/// </value>
public IMatcher[] Matchers { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
@@ -27,20 +37,18 @@ namespace WireMock.Matchers.Request
Check.NotNull(name, nameof(name));
Check.NotNull(pattern, nameof(pattern));
_name = name;
_matcher = new WildcardMatcher(pattern, ignoreCase);
Name = name;
Matchers = new IMatcher[] { new WildcardMatcher(pattern, ignoreCase) };
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="func">
/// The func.
/// </param>
public RequestMessageCookieMatcher([NotNull] Func<IDictionary<string, string>, bool> func)
/// <param name="funcs">The funcs.</param>
public RequestMessageCookieMatcher([NotNull] params Func<IDictionary<string, string>, bool>[] funcs)
{
Check.NotNull(func, nameof(func));
_cookieFunc = func;
Check.NotNull(funcs, nameof(funcs));
_cookieFuncs = funcs;
}
/// <summary>
@@ -52,14 +60,14 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
if (_cookieFunc != null)
return _cookieFunc(requestMessage.Cookies);
if (_cookieFuncs != null)
return _cookieFuncs.Any(cf => cf(requestMessage.Cookies));
if (requestMessage.Cookies == null)
return false;
string headerValue = requestMessage.Cookies[_name];
return _matcher.IsMatch(headerValue);
string headerValue = requestMessage.Cookies[Name];
return Matchers.Any(m => m.IsMatch(headerValue));
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Validation;
@@ -10,20 +11,20 @@ namespace WireMock.Matchers.Request
/// </summary>
public class RequestMessageHeaderMatcher : IRequestMatcher
{
/// <summary>
/// The name.
/// </summary>
private readonly string _name;
private readonly Func<IDictionary<string, string>, bool>[] _headerFuncs;
/// <summary>
/// The matcher.
/// The name
/// </summary>
private readonly IMatcher _matcher;
public string Name { get; }
/// <summary>
/// The header function
/// Gets the matchers.
/// </summary>
private readonly Func<IDictionary<string, string>, bool> _headerFunc;
/// <value>
/// The matchers.
/// </value>
public IMatcher[] Matchers { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
@@ -36,20 +37,18 @@ namespace WireMock.Matchers.Request
Check.NotNull(name, nameof(name));
Check.NotNull(pattern, nameof(pattern));
_name = name;
_matcher = new WildcardMatcher(pattern, ignoreCase);
Name = name;
Matchers = new IMatcher[] { new WildcardMatcher(pattern, ignoreCase) };
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
/// </summary>
/// <param name="func">
/// The func.
/// </param>
public RequestMessageHeaderMatcher([NotNull] Func<IDictionary<string, string>, bool> func)
/// <param name="funcs">The funcs.</param>
public RequestMessageHeaderMatcher([NotNull] params Func<IDictionary<string, string>, bool>[] funcs)
{
Check.NotNull(func, nameof(func));
_headerFunc = func;
Check.NotNull(funcs, nameof(funcs));
_headerFuncs = funcs;
}
/// <summary>
@@ -61,14 +60,14 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
if (_headerFunc != null)
return _headerFunc(requestMessage.Headers);
if (_headerFuncs != null)
return _headerFuncs.Any(hf => hf(requestMessage.Headers));
if (requestMessage.Headers == null)
return false;
string headerValue = requestMessage.Headers[_name];
return _matcher.IsMatch(headerValue);
string headerValue = requestMessage.Headers[Name];
return Matchers.Any(m => m.IsMatch(headerValue));
}
}
}

View File

@@ -12,17 +12,17 @@ namespace WireMock.Matchers.Request
/// </summary>
public class RequestMessageParamMatcher : IRequestMatcher
{
/// <summary>
/// The _key.
/// </summary>
private readonly string _key;
private readonly Func<IDictionary<string, WireMockList<string>>, bool>[] _funcs;
/// <summary>
/// The _values.
/// The key
/// </summary>
private readonly IEnumerable<string> _values;
public string Key { get; }
private readonly Func<IDictionary<string, WireMockList<string>>, bool> _func;
/// <summary>
/// The values
/// </summary>
public IEnumerable<string> Values { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
@@ -38,20 +38,18 @@ namespace WireMock.Matchers.Request
Check.NotNull(key, nameof(key));
Check.NotNull(values, nameof(values));
_key = key;
_values = values;
Key = key;
Values = values;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
/// </summary>
/// <param name="func">
/// The func.
/// </param>
public RequestMessageParamMatcher([NotNull] Func<IDictionary<string, WireMockList<string>>, bool> func)
/// <param name="funcs">The funcs.</param>
public RequestMessageParamMatcher([NotNull] params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
{
Check.NotNull(func, nameof(func));
_func = func;
Check.NotNull(funcs, nameof(funcs));
_funcs = funcs;
}
/// <summary>
@@ -63,12 +61,11 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
if (_func != null)
{
return _func(requestMessage.Query);
}
if (_funcs != null)
return _funcs.Any(f => f(requestMessage.Query));
return requestMessage.GetParameter(_key).Intersect(_values).Count() == _values.Count();
var values = requestMessage.GetParameter(Key);
return values?.Intersect(Values).Count() == Values.Count();
}
}
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Validation;
@@ -12,48 +14,39 @@ namespace WireMock.Matchers.Request
/// <summary>
/// The matcher.
/// </summary>
private readonly string _path;
public IReadOnlyList<IMatcher> Matchers { get; }
/// <summary>
/// The matcher.
/// The path functions
/// </summary>
private readonly IMatcher _matcher;
/// <summary>
/// The path function
/// </summary>
private readonly Func<string, bool> _pathFunc;
private readonly Func<string, bool>[] _pathFuncs;
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary>
/// <param name="path">The path.</param>
public RequestMessagePathMatcher([NotNull] string path)
/// <param name="paths">The paths.</param>
public RequestMessagePathMatcher([NotNull] params string[] paths) : this(paths.Select(path => new WildcardMatcher(path)).ToArray())
{
Check.NotNull(path, nameof(path));
_path = path;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary>
/// <param name="matcher">The matcher.</param>
public RequestMessagePathMatcher([NotNull] IMatcher matcher)
/// <param name="matchers">The matchers.</param>
public RequestMessagePathMatcher([NotNull] params IMatcher[] matchers)
{
Check.NotNull(matcher, nameof(matcher));
_matcher = matcher;
Check.NotNull(matchers, nameof(matchers));
Matchers = matchers;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary>
/// <param name="func">
/// The path func.
/// </param>
public RequestMessagePathMatcher([NotNull] Func<string, bool> func)
/// <param name="funcs">The path functions.</param>
public RequestMessagePathMatcher([NotNull] params Func<string, bool>[] funcs)
{
Check.NotNull(func, nameof(func));
_pathFunc = func;
Check.NotNull(funcs, nameof(funcs));
_pathFuncs = funcs;
}
/// <summary>
@@ -65,14 +58,11 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
if (_path != null)
return string.CompareOrdinal(_path, requestMessage.Path) == 0;
if (Matchers != null)
return Matchers.Any(matcher => matcher.IsMatch(requestMessage.Path));
if (_matcher != null)
return _matcher.IsMatch(requestMessage.Path);
if (_pathFunc != null)
return _pathFunc(requestMessage.Path);
if (_pathFuncs != null)
return _pathFuncs.Any(func => func(requestMessage.Path));
return false;
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Validation;
@@ -12,42 +14,39 @@ namespace WireMock.Matchers.Request
/// <summary>
/// The matcher.
/// </summary>
private readonly IMatcher _matcher;
public readonly IReadOnlyList<IMatcher> Matchers;
/// <summary>
/// The url function
/// The url functions
/// </summary>
private readonly Func<string, bool> _urlFunc;
private readonly Func<string, bool>[] _urlFuncs;
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
/// </summary>
/// <param name="url">The url.</param>
public RequestMessageUrlMatcher([NotNull] string url) : this(new WildcardMatcher(url))
/// <param name="urls">The urls.</param>
public RequestMessageUrlMatcher([NotNull] params string[] urls) : this(urls.Select(url => new WildcardMatcher(url)).ToArray())
{
_matcher = new WildcardMatcher(url);
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
/// </summary>
/// <param name="matcher">The matcher.</param>
public RequestMessageUrlMatcher([NotNull] IMatcher matcher)
/// <param name="matchers">The matchers.</param>
public RequestMessageUrlMatcher([NotNull] params IMatcher[] matchers)
{
Check.NotNull(matcher, nameof(matcher));
_matcher = matcher;
Check.NotNull(matchers, nameof(matchers));
Matchers = matchers;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
/// </summary>
/// <param name="func">
/// The url func.
/// </param>
public RequestMessageUrlMatcher([NotNull] Func<string, bool> func)
/// <param name="funcs">The url functions.</param>
public RequestMessageUrlMatcher([NotNull] params Func<string, bool>[] funcs)
{
Check.NotNull(func, nameof(func));
_urlFunc = func;
Check.NotNull(funcs, nameof(funcs));
_urlFuncs = funcs;
}
/// <summary>
@@ -59,11 +58,11 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
if (_matcher != null)
return _matcher.IsMatch(requestMessage.Path);
if (Matchers != null)
return Matchers.Any(matcher => matcher.IsMatch(requestMessage.Path));
if (_urlFunc != null)
return _urlFunc(requestMessage.Url);
if (_urlFuncs != null)
return _urlFuncs.Any(func => func(requestMessage.Url));
return false;
}

View File

@@ -1,4 +1,5 @@
using JetBrains.Annotations;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Validation;
namespace WireMock.Matchers.Request
@@ -9,20 +10,20 @@ namespace WireMock.Matchers.Request
internal class RequestMessageVerbMatcher : IRequestMatcher
{
/// <summary>
/// The _verb.
/// The verbs
/// </summary>
private readonly string _verb;
public string[] Verbs { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageVerbMatcher"/> class.
/// </summary>
/// <param name="verb">
/// <param name="verbs">
/// The verb.
/// </param>
public RequestMessageVerbMatcher([NotNull] string verb)
public RequestMessageVerbMatcher([NotNull] params string[] verbs)
{
Check.NotNull(verb, nameof(verb));
_verb = verb.ToLower();
Check.NotNull(verbs, nameof(verbs));
Verbs = verbs.Select(v => v.ToLower()).ToArray();
}
/// <summary>
@@ -34,7 +35,7 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
return requestMessage.Verb == _verb;
return Verbs.Contains(requestMessage.Verb);
}
}
}

View File

@@ -37,6 +37,15 @@ namespace WireMock.Matchers
return MatchWildcardString(_pattern, input);
}
/// <summary>
/// Gets the pattern.
/// </summary>
/// <returns>Pattern</returns>
public string GetPattern()
{
return _pattern;
}
/// <summary>
/// Copy/paste from http://www.codeproject.com/Tips/57304/Use-wildcard-characters-and-to-compare-strings
/// </summary>

View File

@@ -49,5 +49,14 @@ namespace WireMock.Matchers
return false;
}
}
/// <summary>
/// Gets the pattern.
/// </summary>
/// <returns>Pattern</returns>
public string GetPattern()
{
return _pattern;
}
}
}

View File

@@ -22,9 +22,9 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The with header.
/// </summary>
/// <param name="func">The headers func.</param>
/// <param name="funcs">The headers funcs.</param>
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
IHeadersAndCookiesRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string>, bool>[] func);
IHeadersAndCookiesRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string>, bool>[] funcs);
/// <summary>
/// The with header.
@@ -38,8 +38,8 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The with header.
/// </summary>
/// <param name="cookieFunc">The func.</param>
/// <param name="cookieFuncs">The funcs.</param>
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
IHeadersAndCookiesRequestBuilder WithCookie([NotNull] params Func<IDictionary<string, string>, bool>[] cookieFunc);
IHeadersAndCookiesRequestBuilder WithCookie([NotNull] params Func<IDictionary<string, string>, bool>[] cookieFuncs);
}
}

View File

@@ -28,12 +28,8 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The with parameters.
/// </summary>
/// <param name="func">
/// The func.
/// </param>
/// <returns>
/// The <see cref="IRequestMatcher"/>.
/// </returns>
IRequestMatcher WithParam([NotNull] Func<IDictionary<string, WireMockList<string>>, bool> func);
/// <param name="funcs">The funcs.</param>
/// <returns>The <see cref="IRequestMatcher"/>.</returns>
IRequestMatcher WithParam([NotNull] params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs);
}
}

View File

@@ -12,37 +12,37 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The with url.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithUrl([NotNull] IMatcher matcher);
IUrlAndPathRequestBuilder WithUrl([NotNull] params IMatcher[] matchers);
/// <summary>
/// The with url.
/// </summary>
/// <param name="url">The url.</param>
/// <param name="urls">The urls.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithUrl([NotNull] params string[] url);
IUrlAndPathRequestBuilder WithUrl([NotNull] params string[] urls);
/// <summary>
/// The with url.
/// </summary>
/// <param name="func">The url func.</param>
/// <param name="funcs">The url funcs.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithUrl([NotNull] params Func<string, bool>[] func);
IUrlAndPathRequestBuilder WithUrl([NotNull] params Func<string, bool>[] funcs);
/// <summary>
/// The with path.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithPath([NotNull] IMatcher matcher);
IUrlAndPathRequestBuilder WithPath([NotNull] params IMatcher[] matchers);
/// <summary>
/// The with path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithPath([NotNull] params string[] path);
IUrlAndPathRequestBuilder WithPath([NotNull] params string[] paths);
/// <summary>
/// The with path.

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using WireMock.Matchers;
using WireMock.Matchers.Request;
@@ -32,15 +33,34 @@ namespace WireMock.RequestBuilders
_requestMatchers = requestMatchers;
}
/// <summary>
/// Gets the request message matchers.
/// </summary>
/// <typeparam name="T">Type of IRequestMatcher</typeparam>
/// <returns>A List{T}</returns>
public IList<T> GetRequestMessageMatchers<T>() where T : IRequestMatcher
{
return new ReadOnlyCollection<T>(_requestMatchers.Where(rm => rm is T).Cast<T>().ToList());
}
/// <summary>
/// Gets the request message matcher.
/// </summary>
/// <typeparam name="T">Type of IRequestMatcher</typeparam>
/// <returns>A RequestMatcher</returns>
public T GetRequestMessageMatcher<T>() where T : IRequestMatcher
{
return _requestMatchers.Where(rm => rm is T).Cast<T>().FirstOrDefault();
}
/// <summary>
/// The with url.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithUrl(IMatcher matcher)
public IUrlAndPathRequestBuilder WithUrl(params IMatcher[] matchers)
{
_requestMatchers.Add(new RequestMessageUrlMatcher(matcher));
_requestMatchers.Add(new RequestMessageUrlMatcher(matchers));
return this;
}
@@ -51,9 +71,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithUrl(params string[] urls)
{
var or = new RequestMessageCompositeMatcher(urls.Select(url => new RequestMessageUrlMatcher(url)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
_requestMatchers.Add(new RequestMessageUrlMatcher(urls));
return this;
}
@@ -64,9 +82,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithUrl(params Func<string, bool>[] funcs)
{
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageUrlMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
return this;
}
@@ -75,10 +91,9 @@ namespace WireMock.RequestBuilders
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithPath(IMatcher matcher)
public IUrlAndPathRequestBuilder WithPath(params IMatcher[] matcher)
{
_requestMatchers.Add(new RequestMessagePathMatcher(matcher));
return this;
}
@@ -89,9 +104,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithPath(params string[] paths)
{
var or = new RequestMessageCompositeMatcher(paths.Select(path => new RequestMessageUrlMatcher(path)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
_requestMatchers.Add(new RequestMessagePathMatcher(paths));
return this;
}
@@ -102,9 +115,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithPath(params Func<string, bool>[] funcs)
{
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageUrlMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
_requestMatchers.Add(new RequestMessagePathMatcher(funcs));
return this;
}
@@ -192,9 +203,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
public IHeadersAndCookiesRequestBuilder UsingVerb(params string[] verbs)
{
var or = new RequestMessageCompositeMatcher(verbs.Select(verb => new RequestMessageVerbMatcher(verb)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
_requestMatchers.Add(new RequestMessageVerbMatcher(verbs));
return this;
}
@@ -292,15 +301,11 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The with parameters.
/// </summary>
/// <param name="func">
/// The func.
/// </param>
/// <returns>
/// The <see cref="IRequestMatcher"/>.
/// </returns>
public IRequestMatcher WithParam(Func<IDictionary<string, WireMockList<string>>, bool> func)
/// <param name="funcs">The funcs.</param>
/// <returns>The <see cref="IRequestMatcher"/>.</returns>
public IRequestMatcher WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
{
_requestMatchers.Add(new RequestMessageParamMatcher(func));
_requestMatchers.Add(new RequestMessageParamMatcher(funcs));
return this;
}
@@ -324,9 +329,7 @@ namespace WireMock.RequestBuilders
/// <returns></returns>
public IHeadersAndCookiesRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
{
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageHeaderMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
_requestMatchers.Add(new RequestMessageHeaderMatcher(funcs));
return this;
}
@@ -350,9 +353,7 @@ namespace WireMock.RequestBuilders
/// <returns></returns>
public IHeadersAndCookiesRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
{
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageCookieMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
_requestMatchers.Add(new RequestMessageCookieMatcher(funcs));
return this;
}
}

View File

@@ -61,7 +61,7 @@ namespace WireMock
/// <param name="body">The body string.</param>
/// <param name="headers">The headers.</param>
/// <param name="cookies">The cookies.</param>
public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte[] bodyAsBytes, [CanBeNull] string body, [CanBeNull] IDictionary<string, string> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte[] bodyAsBytes = null, [CanBeNull] string body = null, [CanBeNull] IDictionary<string, string> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
{
Check.NotNull(url, nameof(url));
Check.NotNull(verb, nameof(verb));
@@ -85,16 +85,19 @@ namespace WireMock
Query = query.Split('&').Aggregate(
new Dictionary<string, WireMockList<string>>(),
(dict, term) =>
{
var parts = term.Split('=');
var key = parts[0];
if (!dict.ContainsKey(key))
{
var key = term.Split('=')[0];
if (!dict.ContainsKey(key))
{
dict.Add(key, new WireMockList<string>());
}
dict.Add(key, new WireMockList<string>());
}
dict[key].Add(term.Split('=')[1]);
return dict;
});
if (parts.Length == 2)
dict[key].Add(parts[1]);
return dict;
});
}
}
@@ -105,7 +108,7 @@ namespace WireMock
/// <returns>The query parameter.</returns>
public List<string> GetParameter(string key)
{
return Query.ContainsKey(key) ? Query[key] : new WireMockList<string>();
return Query.ContainsKey(key) ? Query[key] : null;
}
}
}

View File

@@ -5,7 +5,7 @@ namespace WireMock.ResponseBuilders
/// <summary>
/// The DelayResponseBuilder interface.
/// </summary>
public interface IDelayResponseBuilder : IProvideResponses
public interface IDelayResponseBuilder : IResponseProvider
{
/// <summary>
/// The after delay.

View File

@@ -14,19 +14,39 @@ namespace WireMock.ResponseBuilders
/// </summary>
public class Response : IResponseBuilder
{
private readonly ResponseMessage _responseMessage;
private TimeSpan _delay = TimeSpan.Zero;
private bool _useTransformer;
/// <summary>
/// Gets the response message.
/// </summary>
/// <value>
/// The response message.
/// </value>
public ResponseMessage ResponseMessage { get; }
/// <summary>
/// Creates this instance.
/// </summary>
/// <param name="responseMessage">ResponseMessage</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
[PublicAPI]
public static IResponseBuilder Create([CanBeNull] ResponseMessage responseMessage = null)
{
var message = responseMessage ?? new ResponseMessage { StatusCode = (int)HttpStatusCode.OK };
return new Response(message);
}
/// <summary>
/// Creates this instance.
/// </summary>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
[PublicAPI]
public static IResponseBuilder Create([CanBeNull] ResponseMessage responseMessage = null)
public static IResponseBuilder Create([NotNull] Func<ResponseMessage> func)
{
var message = responseMessage ?? new ResponseMessage { StatusCode = (int) HttpStatusCode.OK };
return new Response(message);
Check.NotNull(func, nameof(func));
return new Response(func());
}
/// <summary>
@@ -37,7 +57,7 @@ namespace WireMock.ResponseBuilders
/// </param>
private Response(ResponseMessage responseMessage)
{
_responseMessage = responseMessage;
ResponseMessage = responseMessage;
}
/// <summary>
@@ -48,7 +68,7 @@ namespace WireMock.ResponseBuilders
[PublicAPI]
public IHeadersResponseBuilder WithStatusCode(int code)
{
_responseMessage.StatusCode = code;
ResponseMessage.StatusCode = code;
return this;
}
@@ -60,7 +80,7 @@ namespace WireMock.ResponseBuilders
[PublicAPI]
public IHeadersResponseBuilder WithStatusCode(HttpStatusCode code)
{
return WithStatusCode((int) code);
return WithStatusCode((int)code);
}
/// <summary>
@@ -70,7 +90,7 @@ namespace WireMock.ResponseBuilders
[PublicAPI]
public IHeadersResponseBuilder WithSuccess()
{
return WithStatusCode((int) HttpStatusCode.OK);
return WithStatusCode((int)HttpStatusCode.OK);
}
/// <summary>
@@ -93,7 +113,7 @@ namespace WireMock.ResponseBuilders
{
Check.NotNull(name, nameof(name));
_responseMessage.AddHeader(name, value);
ResponseMessage.AddHeader(name, value);
return this;
}
@@ -106,7 +126,7 @@ namespace WireMock.ResponseBuilders
{
Check.NotNull(body, nameof(body));
_responseMessage.Body = body;
ResponseMessage.Body = body;
return this;
}
@@ -120,7 +140,7 @@ namespace WireMock.ResponseBuilders
{
Check.NotNull(bodyAsbase64, nameof(bodyAsbase64));
_responseMessage.Body = (encoding ?? Encoding.UTF8).GetString(Convert.FromBase64String(bodyAsbase64));
ResponseMessage.Body = (encoding ?? Encoding.UTF8).GetString(Convert.FromBase64String(bodyAsbase64));
return this;
}
@@ -143,7 +163,7 @@ namespace WireMock.ResponseBuilders
/// The delay.
/// </param>
/// <returns>
/// The <see cref="IProvideResponses"/>.
/// The <see cref="IResponseProvider"/>.
/// </returns>
public IResponseBuilder WithDelay(TimeSpan delay)
{
@@ -162,29 +182,36 @@ namespace WireMock.ResponseBuilders
/// </returns>
public async Task<ResponseMessage> ProvideResponse(RequestMessage requestMessage)
{
ResponseMessage responseMessage;
if (_useTransformer)
{
responseMessage = new ResponseMessage { StatusCode = ResponseMessage.StatusCode };
var template = new { request = requestMessage };
// Body
var templateBody = Handlebars.Compile(_responseMessage.Body);
_responseMessage.Body = templateBody(template);
var templateBody = Handlebars.Compile(ResponseMessage.Body);
responseMessage.Body = templateBody(template);
// Headers
var newHeaders = new Dictionary<string, string>();
foreach (var header in _responseMessage.Headers)
foreach (var header in ResponseMessage.Headers)
{
var templateHeaderKey = Handlebars.Compile(header.Key);
var templateHeaderValue = Handlebars.Compile(header.Value);
newHeaders.Add(templateHeaderKey(template), templateHeaderValue(template));
}
_responseMessage.Headers = newHeaders;
responseMessage.Headers = newHeaders;
}
else
{
responseMessage = ResponseMessage;
}
await Task.Delay(_delay);
return _responseMessage;
return responseMessage;
}
}
}

View File

@@ -3,8 +3,8 @@
/// <summary>
/// The registration callback.
/// </summary>
/// <param name="route">
/// <param name="mapping">
/// The route.
/// </param>
public delegate void RegistrationCallback(Route route);
public delegate void RegistrationCallback(Mapping mapping);
}

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Admin;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
namespace WireMock.Server
{
/// <summary>
/// The fluent mock server.
/// </summary>
public partial class FluentMockServer
{
private void InitAdmin()
{
Given(Request.Create().WithUrl("/__admin/mappings").UsingGet()).RespondWith(new DynamicResponseProvider(MappingsGet));
}
private ResponseMessage MappingsGet()
{
var result = new List<MappingModel>();
foreach (var mapping in Mappings.Where(m => !(m.Provider is DynamicResponseProvider)))
{
var request = (Request) mapping.RequestMatcher;
var urlMatchers = request.GetRequestMessageMatchers<RequestMessageUrlMatcher>();
var headerMatchers = request.GetRequestMessageMatchers<RequestMessageHeaderMatcher>();
var cookieMatchers = request.GetRequestMessageMatchers<RequestMessageCookieMatcher>();
var paramsMatchers = request.GetRequestMessageMatchers<RequestMessageParamMatcher>();
var bodyMatcher = request.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
var verbMatcher = request.GetRequestMessageMatcher<RequestMessageVerbMatcher>();
var response = (Response) mapping.Provider;
var model = new MappingModel
{
Guid = Guid.NewGuid(),
Request = new RequestModel
{
Url = new UrlModel
{
Matchers = urlMatchers != null ? Map(urlMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)) : null
},
Verbs = verbMatcher != null ? verbMatcher.Verbs : new [] { "any" },
Headers = headerMatchers?.Select(hm => new HeaderModel
{
Name = hm.Name,
Matchers = Map(hm.Matchers)
}).ToList(),
Cookies = cookieMatchers?.Select(hm => new CookieModel
{
Name = hm.Name,
Matchers = Map(hm.Matchers)
}).ToList(),
Params = paramsMatchers?.Select(hm => new ParamModel
{
Name = hm.Key,
Values = hm.Values?.ToList()
}).ToList(),
Body = new BodyModel
{
Matcher = bodyMatcher != null ? Map(bodyMatcher.Matcher) : null
}
},
Response = new ResponseModel
{
StatusCode = response.ResponseMessage.StatusCode,
Headers = response.ResponseMessage.Headers,
Body = response.ResponseMessage.Body
}
};
result.Add(model);
}
return ToJson(result);
}
private IList<MatcherModel> Map([CanBeNull] IEnumerable<IMatcher> matchers)
{
return matchers?.Select(Map).Where(x => x != null).ToList();
}
private MatcherModel Map([CanBeNull] IMatcher matcher)
{
if (matcher == null)
return null;
return new MatcherModel
{
Name = matcher.GetType().Name,
Pattern = matcher.GetPattern()
};
}
private ResponseMessage ToJson<T>(T result)
{
return new ResponseMessage
{
Body = JsonConvert.SerializeObject(result, Formatting.Indented),
StatusCode = 200,
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
};
}
}
}

View File

@@ -16,7 +16,7 @@ namespace WireMock.Server
/// <summary>
/// The fluent mock server.
/// </summary>
public class FluentMockServer
public partial class FluentMockServer
{
/// <summary>
/// The _http server.
@@ -24,9 +24,9 @@ namespace WireMock.Server
private readonly TinyHttpServer _httpServer;
/// <summary>
/// The _routes.
/// The _mappings.
/// </summary>
private readonly IList<Route> _routes = new List<Route>();
private readonly IList<Mapping> _mappings = new List<Mapping>();
/// <summary>
/// The _request logs.
@@ -75,13 +75,13 @@ namespace WireMock.Server
/// <summary>
/// Gets the routes.
/// </summary>
public IEnumerable<Route> Routes
public IEnumerable<Mapping> Mappings
{
get
{
lock (((ICollection)_routes).SyncRoot)
lock (((ICollection)_mappings).SyncRoot)
{
return new ReadOnlyCollection<Route>(_routes);
return new ReadOnlyCollection<Mapping>(_mappings);
}
}
}
@@ -89,15 +89,9 @@ namespace WireMock.Server
/// <summary>
/// Start this FluentMockServer.
/// </summary>
/// <param name="port">
/// The port.
/// </param>
/// <param name="ssl">
/// The SSL support.
/// </param>
/// <returns>
/// The <see cref="FluentMockServer"/>.
/// </returns>
/// <param name="port">The port.</param>
/// <param name="ssl">The SSL support.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(int port = 0, bool ssl = false)
{
@@ -106,24 +100,37 @@ namespace WireMock.Server
if (port == 0)
port = Ports.FindFreeTcpPort();
return new FluentMockServer(port, ssl);
return new FluentMockServer(false, port, ssl);
}
/// <summary>
/// Initializes a new instance of the <see cref="FluentMockServer"/> class, and starts the server.
/// Start this FluentMockServer with the admin interface.
/// </summary>
/// <param name="port">
/// The port.
/// </param>
/// <param name="ssl">
/// The SSL support.
/// </param>
private FluentMockServer(int port, bool ssl)
/// <param name="port">The port.</param>
/// <param name="ssl">The SSL support.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(int port = 0, bool ssl = false)
{
Check.Condition(port, p => p >= 0, nameof(port));
if (port == 0)
port = Ports.FindFreeTcpPort();
return new FluentMockServer(true, port, ssl);
}
private FluentMockServer(bool startAdmin, int port, bool ssl)
{
string protocol = ssl ? "https" : "http";
_httpServer = new TinyHttpServer(protocol + "://localhost:" + port + "/", HandleRequest);
_httpServer = new TinyHttpServer(protocol + "://localhost:" + port + "/", HandleRequestAsync);
Port = port;
_httpServer.Start();
if (startAdmin)
{
InitAdmin();
}
}
/// <summary>
@@ -144,9 +151,9 @@ namespace WireMock.Server
_requestLogs.Clear();
}
lock (((ICollection)_routes).SyncRoot)
lock (((ICollection)_mappings).SyncRoot)
{
_routes.Clear();
_mappings.Clear();
}
}
@@ -184,28 +191,24 @@ namespace WireMock.Server
/// <summary>
/// The given.
/// </summary>
/// <param name="requestMatcher">
/// The request matcher.
/// </param>
/// <returns>
/// The <see cref="IRespondWithAProvider"/>.
/// </returns>
/// <param name="requestMatcher">The request matcher.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
public IRespondWithAProvider Given(IRequestMatcher requestMatcher)
{
return new RespondWithAProvider(RegisterRoute, requestMatcher);
return new RespondWithAProvider(RegisterMapping, requestMatcher);
}
/// <summary>
/// The register route.
/// The register mapping.
/// </summary>
/// <param name="route">
/// The route.
/// <param name="mapping">
/// The mapping.
/// </param>
private void RegisterRoute(Route route)
private void RegisterMapping(Mapping mapping)
{
lock (((ICollection)_routes).SyncRoot)
lock (((ICollection)_mappings).SyncRoot)
{
_routes.Add(route);
_mappings.Add(mapping);
}
}
@@ -226,10 +229,8 @@ namespace WireMock.Server
/// <summary>
/// The handle request.
/// </summary>
/// <param name="ctx">
/// The context.
/// </param>
private async void HandleRequest(HttpListenerContext ctx)
/// <param name="ctx">The HttpListenerContext.</param>
private async void HandleRequestAsync(HttpListenerContext ctx)
{
lock (_syncRoot)
{
@@ -241,7 +242,7 @@ namespace WireMock.Server
try
{
var targetRoute = _routes.FirstOrDefault(route => route.IsRequestHandled(request));
var targetRoute = _mappings.FirstOrDefault(route => route.IsRequestHandled(request));
if (targetRoute == null)
{
ctx.Response.StatusCode = 404;

View File

@@ -11,6 +11,6 @@
/// <param name="provider">
/// The provider.
/// </param>
void RespondWith(IProvideResponses provider);
void RespondWith(IResponseProvider provider);
}
}

View File

@@ -15,21 +15,17 @@ namespace WireMock.Server
/// <summary>
/// The _request matcher.
/// </summary>
private readonly IRequestMatcher _requestSpec;
private readonly IRequestMatcher _requestMatcher;
/// <summary>
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
/// </summary>
/// <param name="registrationCallback">
/// The registration callback.
/// </param>
/// <param name="requestSpec">
/// The request matcher.
/// </param>
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestSpec)
/// <param name="registrationCallback">The registration callback.</param>
/// <param name="requestMatcher">The request matcher.</param>
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher)
{
_registrationCallback = registrationCallback;
_requestSpec = requestSpec;
_requestMatcher = requestMatcher;
}
/// <summary>
@@ -38,9 +34,9 @@ namespace WireMock.Server
/// <param name="provider">
/// The provider.
/// </param>
public void RespondWith(IProvideResponses provider)
public void RespondWith(IResponseProvider provider)
{
_registrationCallback(new Route(_requestSpec, provider));
_registrationCallback(new Mapping(_requestMatcher, provider));
}
}
}

View File

@@ -17,6 +17,14 @@ namespace WireMock.Util
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new list.</param>
public WireMockList(params T[] collection) : base(collection)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
/// </summary>

View File

@@ -6,13 +6,14 @@
"packOptions": {
"summary": "Lightweigth Http Mocking Server for .Net, inspired by WireMock from the Java landscape.",
"tags": [ "system", "linq", "dynamic", "core" ],
"tags": [ "tdd", "mock", "http", "wiremock", "test" ],
"owners": [ "Stef Heyenrath" ],
"repository": {
"type": "git",
"url": "https://github.com/StefH/WireMock.Net"
},
"projectUrl": "https://github.com/StefH/WireMock.Net",
"iconUrl": "https://raw.githubusercontent.com/StefH/WireMock.Net/master/WireMock.Net-Logo.png",
"licenseUrl": "https://raw.githubusercontent.com/StefH/WireMock.Net/master/LICENSE",
"releaseNotes": "First version"
},

View File

@@ -30,7 +30,7 @@ namespace WireMock.Net.Tests
_server.Given(Request.Create().WithUrl("/foo2").UsingGet())
.RespondWith(Response.Create().WithStatusCode(202).WithBody("2"));
var routes = _server.Routes;
var routes = _server.Mappings;
Check.That(routes).HasSize(2);
Check.That(routes.First().RequestMatcher).IsNotNull();

View File

@@ -12,12 +12,10 @@ namespace WireMock.Net.Tests
public void Should_handle_empty_query()
{
// given
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", body, bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST");
// then
Check.That(request.GetParameter("foo")).IsEmpty();
Check.That(request.GetParameter("not_there")).IsNull();
}
[Test]

View File

@@ -40,6 +40,19 @@ namespace WireMock.Net.Tests
Check.That(requestBuilder.IsMatch(request2)).IsTrue();
}
[Test]
public void Should_specify_requests_matching_given_urlFuncs()
{
// given
var spec = Request.Create().WithUrl(url => url.EndsWith("/foo"));
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla");
// then
Check.That(spec.IsMatch(request)).IsTrue();
}
[Test]
public void Should_specify_requests_matching_given_url_prefix()
{
@@ -394,7 +407,7 @@ namespace WireMock.Net.Tests
}
[Test]
public void Should_specify_requests_matching_given_params()
public void Should_specify_requests_matching_given_param()
{
// given
var spec = Request.Create().WithPath("/foo").WithParam("bar", "1", "2");
@@ -409,7 +422,20 @@ namespace WireMock.Net.Tests
}
[Test]
public void Should_specify_requests_matching_given_params_func()
public void Should_specify_requests_matching_given_paramNoValue()
{
// given
var spec = Request.Create().WithPath("/foo").WithParam("bar");
// when
var request = new RequestMessage(new Uri("http://localhost/foo?bar"), "PUT");
// then
Check.That(spec.IsMatch(request)).IsTrue();
}
[Test]
public void Should_specify_requests_matching_given_param_func()
{
// given
var spec = Request.Create().WithPath("/foo").UsingAnyVerb().WithParam(p => p.ContainsKey("bar"));