Skip to content

Commit 7608d0f

Browse files
author
Bart Koelman
committed
Merged ErrorDocument and AtomicOperationsDocument into Document
Bugfix: jsonapi/version was missing in error responses
1 parent 68b4805 commit 7608d0f

File tree

98 files changed

+655
-615
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+655
-615
lines changed

src/JsonApiDotNetCore/Controllers/CoreJsonApiController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ protected IActionResult Error(IEnumerable<ErrorObject> errors)
2121
{
2222
ArgumentGuard.NotNull(errors, nameof(errors));
2323

24-
var document = new ErrorDocument
24+
var document = new Document
2525
{
2626
Errors = errors.ToList()
2727
};

src/JsonApiDotNetCore/Middleware/AsyncJsonApiExceptionFilter.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ public Task OnExceptionAsync(ExceptionContext context)
2626

2727
if (context.HttpContext.IsJsonApiRequest())
2828
{
29-
ErrorDocument errorDocument = _exceptionHandler.HandleException(context.Exception);
29+
Document document = _exceptionHandler.HandleException(context.Exception);
3030

31-
context.Result = new ObjectResult(errorDocument)
31+
context.Result = new ObjectResult(document)
3232
{
33-
StatusCode = (int)errorDocument.GetErrorStatusCode()
33+
StatusCode = (int)document.GetErrorStatusCode()
3434
};
3535
}
3636

src/JsonApiDotNetCore/Middleware/ExceptionHandler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public ExceptionHandler(ILoggerFactory loggerFactory, IJsonApiOptions options)
2828
_logger = loggerFactory.CreateLogger<ExceptionHandler>();
2929
}
3030

31-
public ErrorDocument HandleException(Exception exception)
31+
public Document HandleException(Exception exception)
3232
{
3333
ArgumentGuard.NotNull(exception, nameof(exception));
3434

@@ -71,7 +71,7 @@ protected virtual string GetLogMessage(Exception exception)
7171
return exception.Message;
7272
}
7373

74-
protected virtual ErrorDocument CreateErrorDocument(Exception exception)
74+
protected virtual Document CreateErrorDocument(Exception exception)
7575
{
7676
ArgumentGuard.NotNull(exception, nameof(exception));
7777

@@ -90,7 +90,7 @@ protected virtual ErrorDocument CreateErrorDocument(Exception exception)
9090
ApplyOptions(error, exception);
9191
}
9292

93-
return new ErrorDocument
93+
return new Document
9494
{
9595
Errors = errors.ToList()
9696
};

src/JsonApiDotNetCore/Middleware/IExceptionHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ namespace JsonApiDotNetCore.Middleware
88
/// </summary>
99
public interface IExceptionHandler
1010
{
11-
ErrorDocument HandleException(Exception exception);
11+
Document HandleException(Exception exception);
1212
}
1313
}

src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ private static async Task FlushResponseAsync(HttpResponse httpResponse, JsonSeri
209209
httpResponse.ContentType = HeaderConstants.MediaType;
210210
httpResponse.StatusCode = (int)error.StatusCode;
211211

212-
var errorDocument = new ErrorDocument
212+
var errorDocument = new Document
213213
{
214214
Errors = error.AsList()
215215
};

src/JsonApiDotNetCore/Serialization/AtomicOperationsResponseSerializer.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public string Serialize(object content)
5959
return SerializeOperationsDocument(operations);
6060
}
6161

62-
if (content is ErrorDocument errorDocument)
62+
if (content is Document errorDocument)
6363
{
6464
return SerializeErrorDocument(errorDocument);
6565
}
@@ -69,12 +69,19 @@ public string Serialize(object content)
6969

7070
private string SerializeOperationsDocument(IEnumerable<OperationContainer> operations)
7171
{
72-
var document = new AtomicOperationsDocument
72+
var document = new Document
7373
{
7474
Results = operations.Select(SerializeOperation).ToList(),
7575
Meta = _metaBuilder.Build()
7676
};
7777

78+
SetApiVersion(document);
79+
80+
return SerializeObject(document, _options.SerializerSettings);
81+
}
82+
83+
private void SetApiVersion(Document document)
84+
{
7885
if (_options.IncludeJsonApiVersion)
7986
{
8087
document.JsonApi = new JsonApiObject
@@ -86,8 +93,6 @@ private string SerializeOperationsDocument(IEnumerable<OperationContainer> opera
8693
}
8794
};
8895
}
89-
90-
return SerializeObject(document, _options.SerializerSettings);
9196
}
9297

9398
private AtomicResultObject SerializeOperation(OperationContainer operation)
@@ -120,9 +125,11 @@ private AtomicResultObject SerializeOperation(OperationContainer operation)
120125
};
121126
}
122127

123-
private string SerializeErrorDocument(ErrorDocument errorDocument)
128+
private string SerializeErrorDocument(Document document)
124129
{
125-
return SerializeObject(errorDocument, _options.SerializerSettings, serializer =>
130+
SetApiVersion(document);
131+
132+
return SerializeObject(document, _options.SerializerSettings, serializer =>
126133
{
127134
serializer.ApplyErrorSettings();
128135
});

src/JsonApiDotNetCore/Serialization/BaseSerializer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ protected Document Build(IIdentifiable resource, IReadOnlyCollection<AttrAttribu
4848

4949
if (resource == null)
5050
{
51-
return new Document();
51+
return new Document
52+
{
53+
IsPopulated = true
54+
};
5255
}
5356

5457
return new Document

src/JsonApiDotNetCore/Serialization/IJsonApiDeserializer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ namespace JsonApiDotNetCore.Serialization
88
public interface IJsonApiDeserializer
99
{
1010
/// <summary>
11-
/// Deserializes JSON into a <see cref="Document" /> or <see cref="AtomicOperationsDocument" /> and constructs resources from
12-
/// <see cref="ExposableData{T}.Data" />.
11+
/// Deserializes JSON into a <see cref="Document" /> and constructs resources from <see cref="ExposableData{TResource}.Data" />.
1312
/// </summary>
1413
/// <param name="body">
1514
/// The JSON to be deserialized.

src/JsonApiDotNetCore/Serialization/JsonApiWriter.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ public async Task WriteAsync(OutputFormatterWriteContext context)
6565
catch (Exception exception)
6666
#pragma warning restore AV1210 // Catch a specific exception instead of Exception, SystemException or ApplicationException
6767
{
68-
ErrorDocument errorDocument = _exceptionHandler.HandleException(exception);
69-
responseContent = _serializer.Serialize(errorDocument);
68+
Document document = _exceptionHandler.HandleException(exception);
69+
responseContent = _serializer.Serialize(document);
7070

71-
response.StatusCode = (int)errorDocument.GetErrorStatusCode();
71+
response.StatusCode = (int)document.GetErrorStatusCode();
7272
}
7373

7474
bool hasMatchingETag = SetETagResponseHeader(request, response, responseContent);
@@ -132,15 +132,15 @@ private static object WrapErrors(object contextObject)
132132
{
133133
if (contextObject is IEnumerable<ErrorObject> errors)
134134
{
135-
return new ErrorDocument
135+
return new Document
136136
{
137137
Errors = errors.ToList()
138138
};
139139
}
140140

141141
if (contextObject is ErrorObject error)
142142
{
143-
return new ErrorDocument
143+
return new Document
144144
{
145145
Errors = error.AsList()
146146
};

src/JsonApiDotNetCore/Serialization/Objects/AtomicOperationsDocument.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,53 @@
1+
using System;
12
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
25
using Newtonsoft.Json;
36

47
namespace JsonApiDotNetCore.Serialization.Objects
58
{
69
/// <summary>
7-
/// See https://jsonapi.org/format/1.1/#document-top-level.
10+
/// See https://jsonapi.org/format/1.1/#document-top-level and https://jsonapi.org/ext/atomic/#document-structure.
811
/// </summary>
912
public sealed class Document : ExposableData<ResourceObject>
1013
{
11-
[JsonProperty("meta", NullValueHandling = NullValueHandling.Ignore)]
12-
public IDictionary<string, object> Meta { get; set; }
13-
1414
[JsonProperty("jsonapi", NullValueHandling = NullValueHandling.Ignore)]
1515
public JsonApiObject JsonApi { get; set; }
1616

17+
[JsonProperty("atomic:operations", NullValueHandling = NullValueHandling.Ignore)]
18+
public IList<AtomicOperationObject> Operations { get; set; }
19+
20+
[JsonProperty("atomic:results", NullValueHandling = NullValueHandling.Ignore)]
21+
public IList<AtomicResultObject> Results { get; set; }
22+
23+
[JsonProperty("errors", NullValueHandling = NullValueHandling.Ignore)]
24+
public IList<ErrorObject> Errors { get; set; }
25+
26+
[JsonProperty("meta", NullValueHandling = NullValueHandling.Ignore)]
27+
public IDictionary<string, object> Meta { get; set; }
28+
1729
[JsonProperty("links", NullValueHandling = NullValueHandling.Ignore)]
1830
public TopLevelLinks Links { get; set; }
1931

2032
[JsonProperty("included", NullValueHandling = NullValueHandling.Ignore, Order = 1)]
2133
public IList<ResourceObject> Included { get; set; }
34+
35+
internal HttpStatusCode GetErrorStatusCode()
36+
{
37+
if (Errors.IsNullOrEmpty())
38+
{
39+
throw new InvalidOperationException("No errors found.");
40+
}
41+
42+
int[] statusCodes = Errors.Select(error => (int)error.StatusCode).Distinct().ToArray();
43+
44+
if (statusCodes.Length == 1)
45+
{
46+
return (HttpStatusCode)statusCodes[0];
47+
}
48+
49+
int statusCode = int.Parse($"{statusCodes.Max().ToString()[0]}00");
50+
return (HttpStatusCode)statusCode;
51+
}
2252
}
2353
}

src/JsonApiDotNetCore/Serialization/Objects/ErrorDocument.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/JsonApiDotNetCore/Serialization/Objects/ExposableData.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public abstract class ExposableData<TResource>
1818
/// Internally used to indicate if the document's primary data should still be serialized when it's value is null. This is used when a single resource is
1919
/// requested but not present (eg /articles/1/author).
2020
/// </summary>
21-
internal bool IsPopulated { get; private set; }
21+
internal bool IsPopulated { get; set; }
2222

2323
internal bool HasResource => IsPopulated && !IsEmpty;
2424

@@ -59,12 +59,7 @@ public object Data
5959
/// </remarks>
6060
public bool ShouldSerializeData()
6161
{
62-
if (GetType() == typeof(RelationshipObject))
63-
{
64-
return IsPopulated;
65-
}
66-
67-
return true;
62+
return IsPopulated;
6863
}
6964

7065
/// <summary>

src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ public object Deserialize(string body)
7575

7676
private object DeserializeOperationsDocument(string body)
7777
{
78-
AtomicOperationsDocument document;
78+
Document document;
7979

8080
using (CodeTimingSessionManager.Current.Measure("Newtonsoft.Deserialize", MeasurementSettings.ExcludeJsonSerializationInPercentages))
8181
{
8282
JToken bodyToken = LoadJToken(body);
83-
document = bodyToken.ToObject<AtomicOperationsDocument>();
83+
document = bodyToken.ToObject<Document>();
8484
}
8585

8686
if ((document?.Operations).IsNullOrEmpty())

src/JsonApiDotNetCore/Serialization/ResponseSerializer.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,19 @@ public string Serialize(object content)
7272
return SerializeMany(collectionOfIdentifiable.ToArray());
7373
}
7474

75-
if (content is ErrorDocument errorDocument)
75+
if (content is Document errorDocument)
7676
{
7777
return SerializeErrorDocument(errorDocument);
7878
}
7979

8080
throw new InvalidOperationException("Data being returned must be errors or resources.");
8181
}
8282

83-
private string SerializeErrorDocument(ErrorDocument errorDocument)
83+
private string SerializeErrorDocument(Document document)
8484
{
85-
return SerializeObject(errorDocument, _options.SerializerSettings, serializer =>
85+
SetApiVersion(document);
86+
87+
return SerializeObject(document, _options.SerializerSettings, serializer =>
8688
{
8789
serializer.ApplyErrorSettings();
8890
});
@@ -165,6 +167,15 @@ internal string SerializeMany(IReadOnlyCollection<IIdentifiable> resources)
165167
/// Adds top-level objects that are only added to a document in the case of server-side serialization.
166168
/// </summary>
167169
private void AddTopLevelObjects(Document document)
170+
{
171+
SetApiVersion(document);
172+
173+
document.Links = _linkBuilder.GetTopLevelLinks();
174+
document.Meta = _metaBuilder.Build();
175+
document.Included = _includedBuilder.Build();
176+
}
177+
178+
private void SetApiVersion(Document document)
168179
{
169180
if (_options.IncludeJsonApiVersion)
170181
{
@@ -173,10 +184,6 @@ private void AddTopLevelObjects(Document document)
173184
Version = "1.1"
174185
};
175186
}
176-
177-
document.Links = _linkBuilder.GetTopLevelLinks();
178-
document.Meta = _metaBuilder.Build();
179-
document.Included = _includedBuilder.Build();
180187
}
181188
}
182189
}

0 commit comments

Comments
 (0)