Skip to content

Commit

Permalink
Additional unification of error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Bart Koelman committed Sep 29, 2021
1 parent 5f618d2 commit 2c60a42
Show file tree
Hide file tree
Showing 16 changed files with 48 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private static void AssertNoHref(AtomicOperationObject atomicOperationObject, Re
{
using (state.Position.PushElement("href"))
{
throw new DeserializationException(state.Position, "Usage of the 'href' element is not supported.", null);
throw new ModelConversionException(state.Position, "The 'href' element is not supported.", null);
}
}
}
Expand All @@ -78,7 +78,7 @@ private WriteOperationKind ConvertOperationCode(AtomicOperationObject atomicOper
{
using (state.Position.PushElement("ref"))
{
throw new DeserializationException(state.Position, "The 'ref.relationship' element is required.", null);
throw new ModelConversionException(state.Position, "The 'relationship' element is required.", null);
}
}

Expand All @@ -92,7 +92,7 @@ private WriteOperationKind ConvertOperationCode(AtomicOperationObject atomicOper
{
if (atomicOperationObject.Ref == null)
{
throw new DeserializationException(state.Position, "The 'ref' element is required.", null);
throw new ModelConversionException(state.Position, "The 'ref' element is required.", null);
}

return atomicOperationObject.Ref.Relationship != null ? WriteOperationKind.RemoveFromRelationship : WriteOperationKind.DeleteResource;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;
using JsonApiDotNetCore.Serialization.Objects;
Expand Down Expand Up @@ -44,17 +43,5 @@ private RelationshipAttribute ConvertRelationship(string relationshipName, Resou

return relationship;
}

private static void AssertToManyInAddOrRemoveRelationship(RelationshipAttribute relationship, RequestAdapterState state)
{
bool requireToManyRelationship = state.Request.WriteOperation == WriteOperationKind.AddToRelationship ||
state.Request.WriteOperation == WriteOperationKind.RemoveFromRelationship;

if (requireToManyRelationship && relationship is not HasManyAttribute)
{
throw new DeserializationException(state.Position, "Only to-many relationships can be targeted through this operation.",
$"Relationship '{relationship.PublicName}' must be a to-many relationship.");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@ private static void AssertHasOperations(IEnumerable<AtomicOperationObject> atomi
{
if (atomicOperationObjects.IsNullOrEmpty())
{
throw new DeserializationException(state.Position, "No operations found.", null);
throw new ModelConversionException(state.Position, "No operations found.", null);
}
}

private void AssertMaxOperationsNotExceeded(ICollection<AtomicOperationObject> atomicOperationObjects, RequestAdapterState state)
{
if (atomicOperationObjects.Count > _options.MaximumOperationsPerRequest)
{
throw new DeserializationException(state.Position, "Request exceeds the maximum number of operations.",
$"The number of operations in this request ({atomicOperationObjects.Count}) is higher than {_options.MaximumOperationsPerRequest}.");
throw new ModelConversionException(state.Position, "Too many operations in request.",
$"The number of operations in this request ({atomicOperationObjects.Count}) is higher " +
$"than the maximum of {_options.MaximumOperationsPerRequest}.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ public object Convert(SingleOrManyData<ResourceIdentifierObject> data, Relations
private IIdentifiable ConvertToOneRelationshipData(SingleOrManyData<ResourceIdentifierObject> data, RelationshipAttribute relationship,
ResourceIdentityRequirements requirements, RequestAdapterState state)
{
AssertHasNoManyValue(data, relationship, state);
AssertHasSingleValue(data, relationship, state);

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

private static void AssertHasNoManyValue(SingleOrManyData<ResourceIdentifierObject> data, RelationshipAttribute relationship, RequestAdapterState state)
private static void AssertHasSingleValue(SingleOrManyData<ResourceIdentifierObject> data, RelationshipAttribute relationship, RequestAdapterState state)
{
if (data.ManyValue != null)
if (!data.IsAssigned || data.ManyValue != null)
{
throw new DeserializationException(state.Position, "Expected single data element for to-one relationship.",
$"Expected single data element for '{relationship.PublicName}' relationship.");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System.Collections.Generic;
using System.Net;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Errors;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;
using JsonApiDotNetCore.Serialization.Objects;

namespace JsonApiDotNetCore.Serialization.RequestAdapters
Expand Down Expand Up @@ -50,7 +47,7 @@ public object Convert(Document document, RequestAdapterState state)
return new HashSet<IIdentifiable>(IdentifiableComparer.Instance);
}

AssertToManyInAddOrRemoveRelationship(state);
ResourceIdentityAdapter.AssertToManyInAddOrRemoveRelationship(state.Request.Relationship, state);

state.WritableTargetedFields.Relationships.Add(state.Request.Relationship);
return _relationshipDataAdapter.Convert(document.Data, state.Request.Relationship, false, state);
Expand All @@ -75,20 +72,5 @@ private ResourceIdentityRequirements CreateIdentityRequirements(RequestAdapterSt

return requirements;
}

private static void AssertToManyInAddOrRemoveRelationship(RequestAdapterState state)
{
bool requireToManyRelationship = state.Request.WriteOperation == WriteOperationKind.AddToRelationship ||
state.Request.WriteOperation == WriteOperationKind.RemoveFromRelationship;

if (requireToManyRelationship && state.Request.Relationship is not HasManyAttribute)
{
throw new JsonApiException(new ErrorObject(HttpStatusCode.Forbidden)
{
Title = "Only to-many relationships can be targeted through this endpoint.",
Detail = $"Relationship '{state.Request.Relationship.PublicName}' must be a to-many relationship."
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,5 +203,21 @@ protected static void AssertIsKnownRelationship(RelationshipAttribute relationsh
$"Relationship '{relationshipName}' does not exist on resource type '{resourceContext.PublicName}'.");
}
}

protected internal static void AssertToManyInAddOrRemoveRelationship(RelationshipAttribute relationship, RequestAdapterState state)
{
bool requireToManyRelationship = state.Request.WriteOperation == WriteOperationKind.AddToRelationship ||
state.Request.WriteOperation == WriteOperationKind.RemoveFromRelationship;

if (requireToManyRelationship && relationship is not HasManyAttribute)
{
string message = state.Request.Kind == EndpointKind.AtomicOperations
? "Only to-many relationships can be targeted through this operation."
: "Only to-many relationships can be targeted through this endpoint.";

throw new ModelConversionException(state.Position, message, $"Relationship '{relationship.PublicName}' is not a to-many relationship.",
HttpStatusCode.Forbidden);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ public async Task Cannot_create_resource_for_href_element()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: Usage of the 'href' element is not supported.");
error.Title.Should().Be("Failed to deserialize request body: The 'href' element is not supported.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/href");
}
Expand Down Expand Up @@ -530,7 +530,7 @@ public async Task Cannot_create_resource_for_ref_element()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: The 'ref.relationship' element is required.");
error.Title.Should().Be("Failed to deserialize request body: The 'relationship' element is required.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/ref");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public async Task Cannot_delete_resource_for_href_element()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: Usage of the 'href' element is not supported.");
error.Title.Should().Be("Failed to deserialize request body: The 'href' element is not supported.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/href");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public async Task Cannot_process_more_operations_than_maximum()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: Request exceeds the maximum number of operations.");
error.Detail.Should().Be("The number of operations in this request (3) is higher than 2.");
error.Title.Should().Be("Failed to deserialize request body: Too many operations in request.");
error.Detail.Should().Be("The number of operations in this request (3) is higher than the maximum of 2.");
error.Source.Pointer.Should().Be("/atomic:operations");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
(HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync<Document>(route, requestBody);

// Assert
httpResponse.Should().HaveStatusCode(HttpStatusCode.UnprocessableEntity);
httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden);

responseDocument.Errors.Should().HaveCount(1);

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.StatusCode.Should().Be(HttpStatusCode.Forbidden);
error.Title.Should().Be("Failed to deserialize request body: Only to-many relationships can be targeted through this operation.");
error.Detail.Should().Be("Relationship 'ownedBy' must be a to-many relationship.");
error.Detail.Should().Be("Relationship 'ownedBy' is not a to-many relationship.");
error.Source.Pointer.Should().Be("/atomic:operations[0]/ref/relationship");
}

Expand Down Expand Up @@ -263,7 +263,7 @@ public async Task Cannot_add_for_href_element()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: Usage of the 'href' element is not supported.");
error.Title.Should().Be("Failed to deserialize request body: The 'href' element is not supported.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/href");
}
Expand Down Expand Up @@ -507,7 +507,7 @@ public async Task Cannot_add_for_missing_relationship_in_ref()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: The 'ref.relationship' element is required.");
error.Title.Should().Be("Failed to deserialize request body: The 'relationship' element is required.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/ref");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
(HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync<Document>(route, requestBody);

// Assert
httpResponse.Should().HaveStatusCode(HttpStatusCode.UnprocessableEntity);
httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden);

responseDocument.Errors.Should().HaveCount(1);

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.StatusCode.Should().Be(HttpStatusCode.Forbidden);
error.Title.Should().Be("Failed to deserialize request body: Only to-many relationships can be targeted through this operation.");
error.Detail.Should().Be("Relationship 'ownedBy' must be a to-many relationship.");
error.Detail.Should().Be("Relationship 'ownedBy' is not a to-many relationship.");
error.Source.Pointer.Should().Be("/atomic:operations[0]/ref/relationship");
}

Expand Down Expand Up @@ -263,7 +263,7 @@ public async Task Cannot_remove_for_href_element()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: Usage of the 'href' element is not supported.");
error.Title.Should().Be("Failed to deserialize request body: The 'href' element is not supported.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/href");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ public async Task Cannot_replace_for_href_element()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: Usage of the 'href' element is not supported.");
error.Title.Should().Be("Failed to deserialize request body: The 'href' element is not supported.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/href");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ public async Task Cannot_create_for_href_element()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: Usage of the 'href' element is not supported.");
error.Title.Should().Be("Failed to deserialize request body: The 'href' element is not supported.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/href");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ public async Task Cannot_update_resource_for_href_element()

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
error.Title.Should().Be("Failed to deserialize request body: Usage of the 'href' element is not supported.");
error.Title.Should().Be("Failed to deserialize request body: The 'href' element is not supported.");
error.Detail.Should().BeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]/href");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.Forbidden);
error.Title.Should().Be("Only to-many relationships can be targeted through this endpoint.");
error.Detail.Should().Be("Relationship 'assignee' must be a to-many relationship.");
error.Title.Should().Be("Failed to deserialize request body: Only to-many relationships can be targeted through this endpoint.");
error.Detail.Should().Be("Relationship 'assignee' is not a to-many relationship.");
error.Source.Should().BeNull();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>

ErrorObject error = responseDocument.Errors[0];
error.StatusCode.Should().Be(HttpStatusCode.Forbidden);
error.Title.Should().Be("Only to-many relationships can be targeted through this endpoint.");
error.Detail.Should().Be("Relationship 'assignee' must be a to-many relationship.");
error.Title.Should().Be("Failed to deserialize request body: Only to-many relationships can be targeted through this endpoint.");
error.Detail.Should().Be("Relationship 'assignee' is not a to-many relationship.");
error.Source.Should().BeNull();
}

Expand Down

0 comments on commit 2c60a42

Please sign in to comment.