Skip to content

Commit 9259ae9

Browse files
committed
Merge pull request #8 from csantero/serialize-errors
Merged
2 parents 460591f + d4bc665 commit 9259ae9

14 files changed

+322
-18
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"errors": [
3+
{
4+
"id": "TEST-ERROR-ID",
5+
"status": "500",
6+
"title": "System.Exception",
7+
"detail": "This is the exception message!",
8+
"inner": null,
9+
"stackTrace": "Stack trace would go here"
10+
}
11+
]
12+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"test":"foo"}

JSONAPI.Tests/JSONAPI.Tests.csproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@
3838
<Prefer32Bit>false</Prefer32Bit>
3939
</PropertyGroup>
4040
<ItemGroup>
41+
<Reference Include="FluentAssertions">
42+
<HintPath>..\packages\FluentAssertions.3.2.2\lib\net45\FluentAssertions.dll</HintPath>
43+
</Reference>
44+
<Reference Include="FluentAssertions.Core">
45+
<HintPath>..\packages\FluentAssertions.3.2.2\lib\net45\FluentAssertions.Core.dll</HintPath>
46+
</Reference>
4147
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
4248
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
4349
<SpecificVersion>False</SpecificVersion>
@@ -60,6 +66,8 @@
6066
<SpecificVersion>False</SpecificVersion>
6167
<HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.2\lib\net45\System.Web.Http.WebHost.dll</HintPath>
6268
</Reference>
69+
<Reference Include="System.XML" />
70+
<Reference Include="System.Xml.Linq" />
6371
</ItemGroup>
6472
<ItemGroup>
6573
<CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
@@ -68,7 +76,9 @@
6876
</ItemGroup>
6977
<ItemGroup>
7078
<Compile Include="Core\MetadataManagerTests.cs" />
79+
<Compile Include="Json\ErrorSerializerTests.cs" />
7180
<Compile Include="Json\JsonApiMediaFormaterTests.cs" />
81+
<Compile Include="Json\JsonHelpers.cs" />
7282
<Compile Include="Models\Author.cs" />
7383
<Compile Include="Models\Comment.cs" />
7484
<Compile Include="Models\Post.cs" />
@@ -82,6 +92,12 @@
8292
</ItemGroup>
8393
<ItemGroup>
8494
<None Include="app.config" />
95+
<None Include="Data\FormatterErrorSerializationTest.json">
96+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
97+
</None>
98+
<None Include="Data\ErrorSerializerTest.json">
99+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
100+
</None>
85101
<None Include="Data\SerializerIntegrationTest.json">
86102
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
87103
</None>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.IO;
3+
using System.Web.Http;
4+
using FluentAssertions;
5+
using JSONAPI.Json;
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
using Newtonsoft.Json;
8+
9+
namespace JSONAPI.Tests.Json
10+
{
11+
[TestClass]
12+
public class ErrorSerializerTests
13+
{
14+
private class TestErrorIdProvider : IErrorIdProvider
15+
{
16+
public string GenerateId(HttpError error)
17+
{
18+
return "TEST-ERROR-ID";
19+
}
20+
}
21+
22+
[TestMethod]
23+
public void CanSerialize_returns_true_for_HttpError()
24+
{
25+
var serializer = new ErrorSerializer();
26+
var result = serializer.CanSerialize(typeof (HttpError));
27+
result.Should().BeTrue();
28+
}
29+
30+
[TestMethod]
31+
public void CanSerialize_returns_false_for_Exception()
32+
{
33+
var serializer = new ErrorSerializer();
34+
var result = serializer.CanSerialize(typeof(Exception));
35+
result.Should().BeFalse();
36+
}
37+
38+
[TestMethod]
39+
[DeploymentItem(@"Data\ErrorSerializerTest.json")]
40+
public void SerializeError_serializes_httperror()
41+
{
42+
using (var stream = new MemoryStream())
43+
{
44+
var textWriter = new StreamWriter(stream);
45+
var writer = new JsonTextWriter(textWriter);
46+
var error = new HttpError(new Exception("This is the exception message!"), true)
47+
{
48+
StackTrace = "Stack trace would go here"
49+
};
50+
var jsonSerializer = new JsonSerializer();
51+
52+
var serializer = new ErrorSerializer(new TestErrorIdProvider());
53+
serializer.SerializeError(error, stream, writer, jsonSerializer);
54+
55+
writer.Flush();
56+
57+
var expectedJson = File.ReadAllText("ErrorSerializerTest.json");
58+
var minifiedExpectedJson = JsonHelpers.MinifyJson(expectedJson);
59+
var output = System.Text.Encoding.ASCII.GetString(stream.ToArray());
60+
output.Should().Be(minifiedExpectedJson);
61+
}
62+
}
63+
}
64+
}

JSONAPI.Tests/Json/JsonApiMediaFormaterTests.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using System;
22
using System.Linq;
3+
using System.Text.RegularExpressions;
4+
using System.Web.Http;
5+
using FluentAssertions;
36
using Microsoft.VisualStudio.TestTools.UnitTesting;
47
using JSONAPI.Tests.Models;
58
using Newtonsoft.Json;
@@ -17,6 +20,22 @@ public class JsonApiMediaFormaterTests
1720
Author a;
1821
Post p, p2, p3, p4;
1922

23+
private class MockErrorSerializer : IErrorSerializer
24+
{
25+
public bool CanSerialize(Type type)
26+
{
27+
return true;
28+
}
29+
30+
public void SerializeError(object error, Stream writeStream, JsonWriter writer, JsonSerializer serializer)
31+
{
32+
writer.WriteStartObject();
33+
writer.WritePropertyName("test");
34+
serializer.Serialize(writer, "foo");
35+
writer.WriteEndObject();
36+
}
37+
}
38+
2039
[TestInitialize]
2140
public void SetupModels()
2241
{
@@ -155,6 +174,52 @@ public void SerializeArrayIntegrationTest()
155174
//Assert.AreEqual("[2,3,4]", sw.ToString());
156175
}
157176

177+
[TestMethod]
178+
[DeploymentItem(@"Data\FormatterErrorSerializationTest.json")]
179+
public void Should_serialize_error()
180+
{
181+
// Arrange
182+
var formatter = new JSONAPI.Json.JsonApiFormatter(new MockErrorSerializer());
183+
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
184+
var stream = new MemoryStream();
185+
186+
// Act
187+
var payload = new HttpError(new Exception(), true);
188+
formatter.WriteToStreamAsync(typeof(HttpError), payload, stream, (System.Net.Http.HttpContent)null, (System.Net.TransportContext)null);
189+
190+
// Assert
191+
var expectedJson = File.ReadAllText("FormatterErrorSerializationTest.json");
192+
var minifiedExpectedJson = JsonHelpers.MinifyJson(expectedJson);
193+
var output = System.Text.Encoding.ASCII.GetString(stream.ToArray());
194+
output.Should().Be(minifiedExpectedJson);
195+
}
196+
197+
[TestMethod]
198+
[DeploymentItem(@"Data\ErrorSerializerTest.json")]
199+
public void SerializeErrorIntegrationTest()
200+
{
201+
// Arrange
202+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
203+
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
204+
MemoryStream stream = new MemoryStream();
205+
206+
// Act
207+
var payload = new HttpError(new Exception("This is the exception message!"), true)
208+
{
209+
StackTrace = "Stack trace would go here"
210+
};
211+
formatter.WriteToStreamAsync(typeof(HttpError), payload, stream, (System.Net.Http.HttpContent)null, (System.Net.TransportContext)null);
212+
213+
// Assert
214+
var expectedJson = File.ReadAllText("ErrorSerializerTest.json");
215+
var minifiedExpectedJson = JsonHelpers.MinifyJson(expectedJson);
216+
var output = System.Text.Encoding.ASCII.GetString(stream.ToArray());
217+
output = Regex.Replace(output,
218+
@"[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}",
219+
"TEST-ERROR-ID"); // We don't know what the GUID will be, so replace it
220+
output.Should().Be(minifiedExpectedJson);
221+
}
222+
158223
[TestMethod]
159224
public void DeserializeCollectionIntegrationTest()
160225
{

JSONAPI.Tests/Json/JsonHelpers.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Text.RegularExpressions;
2+
3+
namespace JSONAPI.Tests.Json
4+
{
5+
static class JsonHelpers
6+
{
7+
// http://stackoverflow.com/questions/8913138/minify-indented-json-string-in-net
8+
public static string MinifyJson(string input)
9+
{
10+
return Regex.Replace(input, "(\"(?:[^\"\\\\]|\\\\.)*\")|\\s+", "$1");
11+
}
12+
}
13+
}

JSONAPI.Tests/packages.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3+
<package id="FluentAssertions" version="3.2.2" targetFramework="net45" />
34
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.2" targetFramework="net45" />
45
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.2" targetFramework="net45" />
56
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.2" targetFramework="net45" />

JSONAPI/JSONAPI.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@
7171
<Compile Include="Core\IMaterializer.cs" />
7272
<Compile Include="Core\MetadataManager.cs" />
7373
<Compile Include="Http\ApiController.cs" />
74+
<Compile Include="Json\ErrorSerializer.cs" />
75+
<Compile Include="Json\GuidErrorIdProvider.cs" />
76+
<Compile Include="Json\IErrorIdProvider.cs" />
77+
<Compile Include="Json\IErrorSerializer.cs" />
7478
<Compile Include="Json\JsonApiFormatter.cs" />
7579
<Compile Include="Json\RelationAggregator.cs" />
7680
<Compile Include="Properties\AssemblyInfo.cs" />

JSONAPI/Json/ErrorSerializer.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.IO;
3+
using System.Web.Http;
4+
using Newtonsoft.Json;
5+
6+
namespace JSONAPI.Json
7+
{
8+
internal class ErrorSerializer : IErrorSerializer
9+
{
10+
private class JsonApiError
11+
{
12+
[JsonProperty(PropertyName = "id")]
13+
public string Id { get; set; }
14+
15+
[JsonProperty(PropertyName = "status")]
16+
public string Status { get; set; }
17+
18+
[JsonProperty(PropertyName = "title")]
19+
public string Title { get; set; }
20+
21+
[JsonProperty(PropertyName = "detail")]
22+
public string Detail { get; set; }
23+
24+
[JsonProperty(PropertyName = "inner")]
25+
public JsonApiError Inner { get; set; }
26+
27+
[JsonProperty(PropertyName = "stackTrace")]
28+
public string StackTrace { get; set; }
29+
30+
public JsonApiError(HttpError error)
31+
{
32+
Title = error.ExceptionType ?? error.Message;
33+
Status = "500";
34+
Detail = error.ExceptionMessage ?? error.MessageDetail;
35+
StackTrace = error.StackTrace;
36+
37+
if (error.InnerException != null)
38+
Inner = new JsonApiError(error.InnerException);
39+
}
40+
}
41+
42+
private readonly IErrorIdProvider _errorIdProvider;
43+
44+
public ErrorSerializer()
45+
: this(new GuidErrorIdProvider())
46+
{
47+
48+
}
49+
50+
public ErrorSerializer(IErrorIdProvider errorIdProvider)
51+
{
52+
_errorIdProvider = errorIdProvider;
53+
}
54+
55+
public bool CanSerialize(Type type)
56+
{
57+
return type == typeof (HttpError);
58+
}
59+
60+
public void SerializeError(object error, Stream writeStream, JsonWriter writer, JsonSerializer serializer)
61+
{
62+
var httpError = error as HttpError;
63+
if (httpError == null) throw new Exception("Unsupported error type.");
64+
65+
writer.WriteStartObject();
66+
writer.WritePropertyName("errors");
67+
68+
var jsonApiError = new JsonApiError(httpError)
69+
{
70+
Id = _errorIdProvider.GenerateId(httpError)
71+
};
72+
serializer.Serialize(writer, new[] { jsonApiError });
73+
74+
writer.WriteEndObject();
75+
}
76+
}
77+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Web.Http;
3+
4+
namespace JSONAPI.Json
5+
{
6+
internal class GuidErrorIdProvider : IErrorIdProvider
7+
{
8+
public string GenerateId(HttpError error)
9+
{
10+
return Guid.NewGuid().ToString();
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)