Skip to content

Commit fe10147

Browse files
author
Bart Koelman
committed
Unified error messages about data presense and its value: null/object/array
1 parent 72f02b0 commit fe10147

28 files changed

+1430
-412
lines changed

src/JsonApiDotNetCore/Errors/ToManyRelationshipRequiredException.cs

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

src/JsonApiDotNetCore/Serialization/RequestAdapters/AtomicOperationObjectAdapter.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,8 @@ private static void AssertNoHref(AtomicOperationObject atomicOperationObject, Re
6161
{
6262
if (atomicOperationObject.Href != null)
6363
{
64-
using (state.Position.PushElement("href"))
65-
{
66-
throw new ModelConversionException(state.Position, "The 'href' element is not supported.", null);
67-
}
64+
using IDisposable _ = state.Position.PushElement("href");
65+
throw new ModelConversionException(state.Position, "The 'href' element is not supported.", null);
6866
}
6967
}
7068

@@ -76,10 +74,8 @@ private WriteOperationKind ConvertOperationCode(AtomicOperationObject atomicOper
7674
{
7775
if (atomicOperationObject.Ref is { Relationship: null })
7876
{
79-
using (state.Position.PushElement("ref"))
80-
{
81-
throw new ModelConversionException(state.Position, "The 'relationship' element is required.", null);
82-
}
77+
using IDisposable _ = state.Position.PushElement("ref");
78+
throw new ModelConversionException(state.Position, "The 'relationship' element is required.", null);
8379
}
8480

8581
return atomicOperationObject.Ref == null ? WriteOperationKind.CreateResource : WriteOperationKind.AddToRelationship;

src/JsonApiDotNetCore/Serialization/RequestAdapters/AtomicReferenceAdapter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public AtomicReferenceResult Convert(AtomicReference atomicReference, ResourceId
2222
ArgumentGuard.NotNull(state, nameof(state));
2323

2424
using IDisposable _ = state.Position.PushElement("ref");
25-
2625
(IIdentifiable resource, ResourceContext resourceContext) = ConvertResourceIdentity(atomicReference, requirements, state);
2726

2827
RelationshipAttribute relationship = atomicReference.Relationship != null
@@ -35,7 +34,6 @@ public AtomicReferenceResult Convert(AtomicReference atomicReference, ResourceId
3534
private RelationshipAttribute ConvertRelationship(string relationshipName, ResourceContext resourceContext, RequestAdapterState state)
3635
{
3736
using IDisposable _ = state.Position.PushElement("relationship");
38-
3937
RelationshipAttribute relationship = resourceContext.TryGetRelationshipByPublicName(relationshipName);
4038

4139
AssertIsKnownRelationship(relationship, relationshipName, resourceContext, state);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using JetBrains.Annotations;
2+
using JsonApiDotNetCore.Serialization.Objects;
3+
4+
namespace JsonApiDotNetCore.Serialization.RequestAdapters
5+
{
6+
/// <summary>
7+
/// Contains shared assertions for derived types.
8+
/// </summary>
9+
public abstract class BaseDataAdapter
10+
{
11+
[AssertionMethod]
12+
protected static void AssertHasData<T>(SingleOrManyData<T> data, RequestAdapterState state)
13+
where T : class, IResourceIdentity
14+
{
15+
if (!data.IsAssigned)
16+
{
17+
throw new ModelConversionException(state.Position, "The 'data' element is required.", null);
18+
}
19+
}
20+
21+
[AssertionMethod]
22+
protected static void AssertHasSingleValue<T>(SingleOrManyData<T> data, bool allowNull, RequestAdapterState state)
23+
where T : class, IResourceIdentity
24+
{
25+
if (data.SingleValue == null)
26+
{
27+
if (!allowNull)
28+
{
29+
throw new ModelConversionException(state.Position,
30+
data.ManyValue == null
31+
? "Expected an object in 'data' element, instead of 'null'."
32+
: "Expected an object in 'data' element, instead of an array.", null);
33+
}
34+
35+
if (data.ManyValue != null)
36+
{
37+
throw new ModelConversionException(state.Position, "Expected an object or 'null' in 'data' element, instead of an array.", null);
38+
}
39+
}
40+
}
41+
42+
[AssertionMethod]
43+
protected static void AssertHasManyValue<T>(SingleOrManyData<T> data, RequestAdapterState state)
44+
where T : class, IResourceIdentity
45+
{
46+
if (data.ManyValue == null)
47+
{
48+
throw new ModelConversionException(state.Position,
49+
data.SingleValue == null
50+
? "Expected an array in 'data' element, instead of 'null'."
51+
: "Expected an array in 'data' element, instead of an object.", null);
52+
}
53+
}
54+
}
55+
}

src/JsonApiDotNetCore/Serialization/RequestAdapters/OperationsDocumentAdapter.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using JsonApiDotNetCore.Configuration;
34
using JsonApiDotNetCore.Resources;
@@ -26,12 +27,10 @@ public IList<OperationContainer> Convert(Document document, RequestAdapterState
2627
ArgumentGuard.NotNull(state, nameof(state));
2728
AssertHasOperations(document.Operations, state);
2829

29-
using (state.Position.PushElement("atomic:operations"))
30-
{
31-
AssertMaxOperationsNotExceeded(document.Operations, state);
30+
using IDisposable _ = state.Position.PushElement("atomic:operations");
31+
AssertMaxOperationsNotExceeded(document.Operations, state);
3232

33-
return ConvertOperations(document.Operations, state);
34-
}
33+
return ConvertOperations(document.Operations, state);
3534
}
3635

3736
private static void AssertHasOperations(IEnumerable<AtomicOperationObject> atomicOperationObjects, RequestAdapterState state)
@@ -59,13 +58,12 @@ private IList<OperationContainer> ConvertOperations(IEnumerable<AtomicOperationO
5958

6059
foreach (AtomicOperationObject atomicOperationObject in atomicOperationObjects)
6160
{
62-
using (state.Position.PushArrayIndex(operationIndex))
63-
{
64-
OperationContainer operation = _atomicOperationObjectAdapter.Convert(atomicOperationObject, state);
65-
operations.Add(operation);
61+
using IDisposable _ = state.Position.PushArrayIndex(operationIndex);
62+
63+
OperationContainer operation = _atomicOperationObjectAdapter.Convert(atomicOperationObject, state);
64+
operations.Add(operation);
6665

67-
operationIndex++;
68-
}
66+
operationIndex++;
6967
}
7068

7169
return operations;

src/JsonApiDotNetCore/Serialization/RequestAdapters/RelationshipDataAdapter.cs

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010
namespace JsonApiDotNetCore.Serialization.RequestAdapters
1111
{
12-
/// <inheritdoc />
13-
public sealed class RelationshipDataAdapter : IRelationshipDataAdapter
12+
/// <inheritdoc cref="IRelationshipDataAdapter" />
13+
public sealed class RelationshipDataAdapter : BaseDataAdapter, IRelationshipDataAdapter
1414
{
1515
private static readonly CollectionConverter CollectionConverter = new();
1616

@@ -70,9 +70,9 @@ public object Convert(SingleOrManyData<ResourceIdentifierObject> data, Relations
7070
{
7171
ArgumentGuard.NotNull(relationship, nameof(relationship));
7272
ArgumentGuard.NotNull(state, nameof(state));
73+
AssertHasData(data, state);
7374

7475
using IDisposable _ = state.Position.PushElement("data");
75-
7676
ResourceContext rightResourceContext = _resourceGraph.GetResourceContext(relationship.RightType);
7777

7878
var requirements = new ResourceIdentityRequirements
@@ -83,31 +83,22 @@ public object Convert(SingleOrManyData<ResourceIdentifierObject> data, Relations
8383
};
8484

8585
return relationship is HasOneAttribute
86-
? ConvertToOneRelationshipData(data, relationship, requirements, state)
86+
? ConvertToOneRelationshipData(data, requirements, state)
8787
: ConvertToManyRelationshipData(data, relationship, requirements, useToManyElementType, state);
8888
}
8989

90-
private IIdentifiable ConvertToOneRelationshipData(SingleOrManyData<ResourceIdentifierObject> data, RelationshipAttribute relationship,
91-
ResourceIdentityRequirements requirements, RequestAdapterState state)
90+
private IIdentifiable ConvertToOneRelationshipData(SingleOrManyData<ResourceIdentifierObject> data, ResourceIdentityRequirements requirements,
91+
RequestAdapterState state)
9292
{
93-
AssertHasSingleValue(data, relationship, state);
93+
AssertHasSingleValue(data, true, state);
9494

9595
return data.SingleValue != null ? _resourceIdentifierObjectAdapter.Convert(data.SingleValue, requirements, state) : null;
9696
}
9797

98-
private static void AssertHasSingleValue(SingleOrManyData<ResourceIdentifierObject> data, RelationshipAttribute relationship, RequestAdapterState state)
99-
{
100-
if (!data.IsAssigned || data.ManyValue != null)
101-
{
102-
throw new DeserializationException(state.Position, "Expected single data element for to-one relationship.",
103-
$"Expected single data element for '{relationship.PublicName}' relationship.");
104-
}
105-
}
106-
10798
private IEnumerable ConvertToManyRelationshipData(SingleOrManyData<ResourceIdentifierObject> data, RelationshipAttribute relationship,
10899
ResourceIdentityRequirements requirements, bool useToManyElementType, RequestAdapterState state)
109100
{
110-
AssertHasManyValue(data, relationship, state);
101+
AssertHasManyValue(data, state);
111102

112103
int arrayIndex = 0;
113104
var rightResources = new List<IIdentifiable>();
@@ -131,14 +122,5 @@ private IEnumerable ConvertToManyRelationshipData(SingleOrManyData<ResourceIdent
131122
resourceSet.AddRange(rightResources);
132123
return resourceSet;
133124
}
134-
135-
private static void AssertHasManyValue(SingleOrManyData<ResourceIdentifierObject> data, RelationshipAttribute relationship, RequestAdapterState state)
136-
{
137-
if (data.ManyValue == null)
138-
{
139-
throw new DeserializationException(state.Position, "Expected data[] element for to-many relationship.",
140-
$"Expected data[] element for '{relationship.PublicName}' relationship.");
141-
}
142-
}
143125
}
144126
}

src/JsonApiDotNetCore/Serialization/RequestAdapters/ResourceDataAdapter.cs

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
using System;
12
using JsonApiDotNetCore.Configuration;
23
using JsonApiDotNetCore.Resources;
34
using JsonApiDotNetCore.Serialization.Objects;
45

56
namespace JsonApiDotNetCore.Serialization.RequestAdapters
67
{
7-
/// <inheritdoc />
8-
public class ResourceDataAdapter : IResourceDataAdapter
8+
/// <inheritdoc cref="IResourceDataAdapter" />
9+
public class ResourceDataAdapter : BaseDataAdapter, IResourceDataAdapter
910
{
1011
private readonly IResourceDefinitionAccessor _resourceDefinitionAccessor;
1112
private readonly IResourceObjectAdapter _resourceObjectAdapter;
@@ -24,36 +25,19 @@ public IIdentifiable Convert(SingleOrManyData<ResourceObject> data, ResourceIden
2425
{
2526
ArgumentGuard.NotNull(requirements, nameof(requirements));
2627
ArgumentGuard.NotNull(state, nameof(state));
27-
AssertHasData(data, state);
2828

29-
using (state.Position.PushElement("data"))
30-
{
31-
AssertNoManyValue(data, state);
29+
AssertHasData(data, state);
3230

33-
(IIdentifiable resource, ResourceContext _) = ConvertResourceObject(data, requirements, state);
31+
using IDisposable _ = state.Position.PushElement("data");
32+
AssertHasSingleValue(data, false, state);
3433

35-
// Ensure that IResourceDefinition extensibility point sees the current operation, it case it injects IJsonApiRequest.
36-
state.RefreshInjectables();
34+
(IIdentifiable resource, ResourceContext _) = ConvertResourceObject(data, requirements, state);
3735

38-
_resourceDefinitionAccessor.OnDeserialize(resource);
39-
return resource;
40-
}
41-
}
36+
// Ensure that IResourceDefinition extensibility point sees the current operation, it case it injects IJsonApiRequest.
37+
state.RefreshInjectables();
4238

43-
private static void AssertHasData(SingleOrManyData<ResourceObject> data, RequestAdapterState state)
44-
{
45-
if (data.Value == null)
46-
{
47-
throw new DeserializationException(state.Position, "The 'data' element is required.", null);
48-
}
49-
}
50-
51-
private static void AssertNoManyValue(SingleOrManyData<ResourceObject> data, RequestAdapterState state)
52-
{
53-
if (data.ManyValue != null)
54-
{
55-
throw new DeserializationException(state.Position, "Expected 'data' object instead of array.", null);
56-
}
39+
_resourceDefinitionAccessor.OnDeserialize(resource);
40+
return resource;
5741
}
5842

5943
protected virtual (IIdentifiable resource, ResourceContext resourceContext) ConvertResourceObject(SingleOrManyData<ResourceObject> data,

src/JsonApiDotNetCore/Serialization/RequestAdapters/ResourceObjectAdapter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ private void ConvertAttribute(IIdentifiable resource, string attributeName, obje
5757
RequestAdapterState state)
5858
{
5959
using IDisposable _ = state.Position.PushElement(attributeName);
60-
6160
AttrAttribute attr = resourceContext.TryGetAttributeByPublicName(attributeName);
6261

6362
if (attr == null && _options.AllowUnknownFieldsInRequestBody)
@@ -142,7 +141,6 @@ private void ConvertRelationship(string relationshipName, SingleOrManyData<Resou
142141
ResourceContext resourceContext, RequestAdapterState state)
143142
{
144143
using IDisposable _ = state.Position.PushElement(relationshipName);
145-
146144
RelationshipAttribute relationship = resourceContext.TryGetRelationshipByPublicName(relationshipName);
147145

148146
if (relationship == null && _options.AllowUnknownFieldsInRequestBody)

0 commit comments

Comments
 (0)