Compare commits

...

5 Commits

Author SHA1 Message Date
Stef Heyenrath
beaf93d035 Merge branch 'master' into webapp 2019-05-04 10:42:25 +02:00
Stef Heyenrath
a7fbc051c9 1.0.15.0 2019-05-04 10:41:42 +02:00
Stef Heyenrath
6e45255c9e Dynamic response handlebars templating (2) (#273)
* Dynamic response files using Handlebars templating
(#270)

* * Response templating / transformation using Handlebars and extensions

* Add unit test for JsonPath and BodyAsFile mapping (#272)

* fix merge issue
2019-05-04 10:20:36 +02:00
Stef Heyenrath
96e68ae2a0 Support Dynamic response files using Handlebars templating (#271)
* Dynamic response files using Handlebars templating
(#270)

* * Response templating / transformation using Handlebars and extensions
2019-05-04 10:02:09 +02:00
Stef Heyenrath
10a21dbc57 webapp (work in progress) 2019-04-08 07:41:32 +02:00
18 changed files with 353 additions and 27 deletions

View File

@@ -1,3 +1,9 @@
# 1.0.15.0 (04 May 2019)
- [#271](https://github.com/WireMock-Net/WireMock.Net/pull/271) - Support Dynamic response files using Handlebars templating [bug, feature] contributed by [StefH](https://github.com/StefH)
- [#272](https://github.com/WireMock-Net/WireMock.Net/pull/272) - Add unit test for JsonPath and BodyAsFile mapping contributed by [denstorti](https://github.com/denstorti)
- [#273](https://github.com/WireMock-Net/WireMock.Net/pull/273) - Dynamic response handlebars templating (2) [bug, feature] contributed by [StefH](https://github.com/StefH)
- [#270](https://github.com/WireMock-Net/WireMock.Net/issues/270) - Dynamic response files using Handlebars templating [bug, question]
# 1.0.14.0 (20 April 2019)
- [#269](https://github.com/WireMock-Net/WireMock.Net/pull/269) - Add JmesPath matcher [feature] contributed by [StefH](https://github.com/StefH)
- [#268](https://github.com/WireMock-Net/WireMock.Net/issues/268) - Swagger UI for admin api [question]

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.0.14</VersionPrefix>
<VersionPrefix>1.0.15</VersionPrefix>
</PropertyGroup>
<Choose>

View File

@@ -1,3 +1,3 @@
https://github.com/StefH/GitHubReleaseNotes
GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.14.0
GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.15.0

View File

@@ -8,7 +8,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
* Record/playback of stubs (proxying)
* Per-request conditional proxying
* Stateful behaviour simulation
* Response transformation
* Response templating / transformation using Handlebars and extensions
## Info
| | |

View File

@@ -62,6 +62,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.Net452
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NETCoreApp2", "examples\WireMock.Net.Console.NETCoreApp2\WireMock.Net.Console.NETCoreApp2.csproj", "{83645809-9E01-4E81-8733-BA9497554ABF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.WebApp", "src\WireMock.Net.WebApp\WireMock.Net.WebApp.csproj", "{D4782470-6CC1-4454-9D17-409F6CC286CE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -136,6 +138,10 @@ Global
{83645809-9E01-4E81-8733-BA9497554ABF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83645809-9E01-4E81-8733-BA9497554ABF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83645809-9E01-4E81-8733-BA9497554ABF}.Release|Any CPU.Build.0 = Release|Any CPU
{D4782470-6CC1-4454-9D17-409F6CC286CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4782470-6CC1-4454-9D17-409F6CC286CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4782470-6CC1-4454-9D17-409F6CC286CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4782470-6CC1-4454-9D17-409F6CC286CE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -158,6 +164,7 @@ Global
{1261BB9B-A7D4-456C-8985-3CE560361B8E} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{668F689E-57B4-422E-8846-C0FF643CA268} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{83645809-9E01-4E81-8733-BA9497554ABF} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{D4782470-6CC1-4454-9D17-409F6CC286CE} = {EF242EDF-7133-4277-9A0C-18744DE08707}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BF428BCC-C837-433B-87D2-15C7014B73E9}

View File

@@ -0,0 +1,22 @@
using Microsoft.Extensions.Logging;
namespace WireMock.Net.WebApp
{
public class App
{
private readonly IWireMockService _service;
private readonly ILogger _logger;
public App(IWireMockService service, ILogger logger)
{
_service = service;
_logger = logger;
}
public void Run()
{
_logger.LogInformation("WireMock.Net WebApp running");
_service.Run();
}
}
}

View File

@@ -0,0 +1,7 @@
namespace WireMock.Net.WebApp
{
public interface IWireMockService
{
void Run();
}
}

View File

@@ -0,0 +1,52 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using WireMock.Logging;
using WireMock.Settings;
namespace WireMock.Net.WebApp
{
public class Program
{
public static void Main(string[] args)
{
// Create service collection
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
// Create service provider
var serviceProvider = serviceCollection.BuildServiceProvider();
// Run app
serviceProvider.GetService<App>().Run();
}
private static void ConfigureServices(IServiceCollection services)
{
// Build configuration
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables() // <-- this is needed to to override settings via the Azure Portal App Settings
.Build();
services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddConfiguration(configuration.GetSection("Logging"));
loggingBuilder.AddConsole();
});
// Add access to IFluentMockServerSettings
var settings = configuration.GetSection("FluentMockServerSettings").Get<FluentMockServerSettings>();
services.AddSingleton<IFluentMockServerSettings>(settings);
// Add services
services.AddTransient<IWireMockLogger, WireMockLogger>();
services.AddTransient<IWireMockService, WireMockService>();
// Add app
services.AddTransient<App>();
}
}
}

View File

@@ -0,0 +1,38 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iis": {
"applicationUrl": "http://localhost//wiremock",
"sslPort": 0
},
"iisExpress": {
"applicationUrl": "http://localhost:56513/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchUrl": "__admin/settings",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WireMock.Net.WebApp": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "__admin/settings",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:56514/"
},
"IIS": {
"commandName": "IIS",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "development"
}
}
}
}

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,43 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using WireMock.Admin.Requests;
using WireMock.Logging;
namespace WireMock.Net.WebApp
{
public class WireMockLogger : IWireMockLogger
{
private readonly ILogger<WireMockService> _logger;
public WireMockLogger(ILogger<WireMockService> logger)
{
_logger = logger;
}
public void Debug(string formatString, params object[] args)
{
_logger.LogDebug(formatString, args);
}
public void Info(string formatString, params object[] args)
{
_logger.LogInformation(formatString, args);
}
public void Warn(string formatString, params object[] args)
{
_logger.LogWarning(formatString, args);
}
public void Error(string formatString, params object[] args)
{
_logger.LogError(formatString, args);
}
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
{
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
_logger.LogDebug("Admin[{0}] {1}", isAdminRequest, message);
}
}
}

View File

@@ -0,0 +1,39 @@
using Newtonsoft.Json;
using System.Threading;
using WireMock.Logging;
using WireMock.Net.StandAlone;
using WireMock.Settings;
namespace WireMock.Net.WebApp
{
public class WireMockService : IWireMockService
{
private static int sleepTime = 30000;
private readonly IWireMockLogger _logger;
private readonly IFluentMockServerSettings _settings;
public WireMockService(IWireMockLogger logger, IFluentMockServerSettings settings)
{
_logger = logger;
_settings = settings;
_settings.Logger = logger;
}
public void Run()
{
_logger.Info("WireMock.Net server starting");
StandAloneApp.Start(_settings);
_logger.Info($"WireMock.Net server settings {JsonConvert.SerializeObject(_settings)}");
while (true)
{
_logger.Info("WireMock.Net server running");
Thread.Sleep(sleepTime);
}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

View File

@@ -0,0 +1,21 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Debug"
}
},
"Console": {
"LogLevel": {
"Default": "Debug"
}
}
},
"AllowedHosts": "*",
"FluentMockServerSettings": {
"AdminUsername": "a",
"AdminPassword": "b",
"StartAdminInterface": true
}
}

View File

@@ -224,7 +224,7 @@ namespace WireMock.ResponseBuilders
BodyAsFileIsCached = cache
};
if (cache)
if (cache && !UseTransformer)
{
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
ResponseMessage.BodyData.BodyAsBytes = _fileSystemHandler.ReadResponseBodyAsFile(filename);

View File

@@ -722,6 +722,11 @@ namespace WireMock.Server
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
}
if (responseModel.UseTransformer)
{
responseBuilder = responseBuilder.WithTransformer();
}
if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
{
if (string.IsNullOrEmpty(responseModel.X509Certificate2ThumbprintOrSubjectName))
@@ -778,11 +783,6 @@ namespace WireMock.Server
responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile);
}
if (responseModel.UseTransformer)
{
responseBuilder = responseBuilder.WithTransformer();
}
return responseBuilder;
}

View File

@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using HandlebarsDotNet;
using HandlebarsDotNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using WireMock.Util;
namespace WireMock.Transformers
@@ -24,23 +24,24 @@ namespace WireMock.Transformers
public static ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original)
{
bool bodyIsJson = original.BodyData.DetectedBodyType == BodyType.Json;
var responseMessage = new ResponseMessage { StatusCode = original.StatusCode };
if (!bodyIsJson)
{
responseMessage.BodyOriginal = original.BodyData.BodyAsString;
}
var template = new { request = requestMessage };
if (!bodyIsJson)
switch (original.BodyData.DetectedBodyType)
{
TransformBodyAsString(template, original, responseMessage);
}
else
{
TransformBodyAsJson(template, original, responseMessage);
case BodyType.Json:
TransformBodyAsJson(template, original, responseMessage);
break;
case BodyType.File:
TransformBodyAsFile(template, original, responseMessage);
break;
case BodyType.String:
responseMessage.BodyOriginal = original.BodyData.BodyAsString;
TransformBodyAsString(template, original, responseMessage);
break;
}
// Headers
@@ -150,13 +151,25 @@ namespace WireMock.Transformers
private static void TransformBodyAsString(object template, ResponseMessage original, ResponseMessage responseMessage)
{
var templateBody = HandlebarsContext.Compile(original.BodyData.BodyAsString);
var templateBodyAsString = HandlebarsContext.Compile(original.BodyData.BodyAsString);
responseMessage.BodyData = new BodyData
{
DetectedBodyType = original.BodyData.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
BodyAsString = templateBody(template)
BodyAsString = templateBodyAsString(template)
};
}
private static void TransformBodyAsFile(object template, ResponseMessage original, ResponseMessage responseMessage)
{
var templateBodyAsFile = HandlebarsContext.Compile(original.BodyData.BodyAsFile);
responseMessage.BodyData = new BodyData
{
DetectedBodyType = original.BodyData.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
BodyAsFile = templateBodyAsFile(template)
};
}
}

View File

@@ -214,5 +214,49 @@ namespace WireMock.Net.Tests.ResponseBuilders
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson)).Equals("[\"first\",\"/foo_array\",\"test 1\",\"test 2\",\"last\"]");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsFile()
{
// Assign
var request = new RequestMessage(new UrlDetails("http://localhost/foo?MyUniqueNumber=1"), "GET", ClientIp);
var response = Response.Create()
.WithTransformer()
.WithBodyFromFile(@"c:\\{{request.query.MyUniqueNumber}}\test.xml"); // why use a \\ here ?
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// Assert
Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.xml");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_WithBodyAsFile_JsonPath()
{
// Assign
string jsonString = "{ \"MyUniqueNumber\": \"1\" }";
var bodyData = new BodyData
{
BodyAsString = jsonString,
BodyAsJson = JsonConvert.DeserializeObject(jsonString),
DetectedBodyType = BodyType.Json,
DetectedBodyTypeFromContentType = BodyType.Json,
Encoding = Encoding.UTF8
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, bodyData);
string jsonPath = "\"$.MyUniqueNumber\"";
var response = Response.Create()
.WithTransformer()
.WithBodyFromFile(@"c:\\{{JsonPath.SelectToken request.body " + jsonPath + "}}\\test.json"); // why use a \\ here ?
// Act
var responseMessage = await response.ProvideResponseAsync(request);
// Assert
Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.json");
}
}
}