C# code generator improvements (#933)

* Escape quotes in generated C# strings

* Handle response with JSON body in C# code generator
This commit is contained in:
Cezary Piątek
2023-05-07 14:37:48 +02:00
committed by GitHub
parent 7019a5a78c
commit 7d9e450814
3 changed files with 95 additions and 4 deletions

View File

@@ -5,6 +5,7 @@ using System.Net;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stef.Validation;
using WireMock.Admin.Mappings;
using WireMock.Constants;
@@ -143,13 +144,26 @@ internal class MappingConverter
}
}
if (response.ResponseMessage.BodyData is { })
if (response.ResponseMessage.BodyData is { } bodyData)
{
switch (response.ResponseMessage.BodyData.DetectedBodyType)
{
case BodyType.String:
case BodyType.FormUrlEncoded:
sb.AppendLine($" .WithBody(\"{response.ResponseMessage.BodyData.BodyAsString}\")");
sb.AppendLine($" .WithBody(\"{EscapeCSharpString(bodyData.BodyAsString)}\")");
break;
case BodyType.Json:
if (bodyData.BodyAsJson is string bodyStringValue)
{
sb.AppendLine($" .WithBody(\"{EscapeCSharpString(bodyStringValue)}\")");
}
else
{
var serializedBody = JsonConvert.SerializeObject(bodyData.BodyAsJson);
var deserializedBody = JToken.Parse(serializedBody);
sb.AppendLine($" .WithBodyAsJson({ConvertJsonToAnonymousObjectDefinition(deserializedBody, 2)})");
}
break;
}
}
@@ -173,6 +187,50 @@ internal class MappingConverter
return sb.ToString();
}
private string ConvertJsonToAnonymousObjectDefinition(JToken token, int ind = 0)
{
string FormatObject(JObject jObject)
{
var indStr = new string(' ', 4 * ind);
var indStrSub = new string(' ', 4 * (ind + 1));
var items = jObject.Properties().Select(x => ConvertJsonToAnonymousObjectDefinition(x, ind + 1));
return $"new\r\n{indStr}{{\r\n{indStrSub}{string.Join($",\r\n{indStrSub}", items)}\r\n{indStr}}}";
}
string FormatArray(JArray jArray, int ind)
{
var hasComplexItems = jArray.FirstOrDefault() is JObject or JArray;
var items = jArray.Select(x => ConvertJsonToAnonymousObjectDefinition(x, hasComplexItems ? ind + 1 : ind));
if (hasComplexItems)
{
var indStr = new string(' ', 4 * ind);
var indStrSub = new string(' ', 4 * (ind + 1));
return $"new []\r\n{indStr}{{\r\n{indStrSub}{string.Join($",\r\n{indStrSub}", items)}\r\n{indStr}}}";
}
return $"new [] {{ {string.Join(", ", items)} }}";
}
return token switch
{
JArray jArray => FormatArray(jArray, ind),
JObject jObject => FormatObject(jObject),
JProperty jProperty => $"{jProperty.Name} = {ConvertJsonToAnonymousObjectDefinition(jProperty.Value, ind)}",
JValue jValue => jValue.Type switch
{
JTokenType.None => "null",
JTokenType.Integer => jValue.Value?.ToString() ?? "null",
JTokenType.Float => jValue.Value?.ToString() ?? "null",
JTokenType.String => $"\"{EscapeCSharpString(jValue.Value?.ToString())}\"",
JTokenType.Boolean => jValue.Value?.ToString().ToLower() ?? "null",
JTokenType.Null => "null",
JTokenType.Undefined => "null",
_ => $"UNHANDLED_CASE: {jValue.Type}"
},
_ => $"UNHANDLED_CASE: {token}"
};
}
public MappingModel ToMappingModel(IMapping mapping)
{
var request = (Request)mapping.RequestMatcher;
@@ -445,9 +503,11 @@ internal class MappingConverter
private static string ToValueArguments(string[]? values, string defaultValue = "")
{
return values is { } ? string.Join(", ", values.Select(v => $"\"{v}\"")) : $"\"{defaultValue}\"";
return values is { } ? string.Join(", ", values.Select(v => $"\"{EscapeCSharpString(v)}\"")) : $"\"{EscapeCSharpString(defaultValue)}\"";
}
private static string? EscapeCSharpString(string? value) => value?.Replace("\"", "\\\"");
private static WebProxyModel? MapWebProxy(WebProxySettings? settings)
{
return settings != null ? new WebProxyModel

View File

@@ -16,11 +16,13 @@ server
.UsingMethod("POST")
.WithPath("/foo2")
.WithParam("p2", "abc")
.WithHeader("h1", "W/\"234f2q3r\"", true)
)
.WithGuid("1b731398-4a5b-457f-a6e3-d65e541c428f")
.RespondWith(Response.Create()
.WithStatusCode("201")
.WithHeader("hk", "hv")
.WithHeader("ETag", "W/\"168d8e\"")
.WithBody("2")
);
@@ -32,5 +34,32 @@ server
.WithGuid("f74fd144-df53-404f-8e35-da22a640bd5f")
.RespondWith(Response.Create()
.WithStatusCode(208)
.WithBodyAsJson(new
{
a = 1,
b = 1.2,
d = true,
e = false,
f = new [] { 1, 2, 3, 4 },
g = new
{
z1 = 1,
z2 = 2,
z3 = new [] { "a", "b", "c" },
z4 = new []
{
new
{
a = 1,
b = 2
},
new
{
a = 2,
b = 3
}
}
}
})
);

View File

@@ -740,6 +740,7 @@ public class WireMockAdminApiTests
Request.Create()
.WithPath("/foo2")
.WithParam("p2", "abc")
.WithHeader("h1", "W/\"234f2q3r\"")
.UsingPost()
)
.WithGuid(guid2)
@@ -747,6 +748,7 @@ public class WireMockAdminApiTests
Response.Create()
.WithStatusCode("201")
.WithHeader("hk", "hv")
.WithHeader("ETag", "W/\"168d8e\"")
.WithBody("2")
);
@@ -760,7 +762,7 @@ public class WireMockAdminApiTests
.RespondWith(
Response.Create()
.WithStatusCode(HttpStatusCode.AlreadyReported)
.WithBodyAsJson(new { x = 1 })
.WithBodyAsJson(new { a = 1, b=1.2, d=true, e=false, f=new[]{1,2,3,4}, g= new{z1=1, z2=2, z3=new []{"a","b","c"}, z4=new[]{new {a=1, b=2},new {a=2, b=3}}} })
);
// Act