Skip to content

Commit e665173

Browse files
committed
Merge branch 'master' into feat/simplification-hooks
2 parents 8a9056b + 9aea5de commit e665173

File tree

5 files changed

+127
-6
lines changed

5 files changed

+127
-6
lines changed

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,14 @@ private IList GetTrackedManyRelationshipValue(IEnumerable<IIdentifiable> relatio
326326
{
327327
if (relationshipValueList == null) return null;
328328
bool _wasAlreadyAttached = false;
329-
Type entityType = null;
329+
/// if we're not using entity resource separation, we can just read off the related type
330+
/// from the RelationshipAttribute. If we DO use separation, RelationshipAttribute.DependentType
331+
/// will point to the Resource, not the Entity, which is not the one we need here.
332+
bool entityResourceSeparation = relationshipAttr.EntityPropertyName != null;
333+
Type entityType = entityResourceSeparation ? null : relationshipAttr.DependentType;
330334
var trackedPointerCollection = relationshipValueList.Select(pointer =>
331335
{
332-
/// todo: we can't just use relationshipAttr.Type because
336+
/// todo: we can't just use relationshipAttr.DependentType because
333337
/// this will point to the Resource type in the case of entity resource
334338
/// separation. We should consider to store entity type on
335339
/// the relationship attribute too.

src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,9 @@ private void SetHasOneForeignKeyValue(object entity, HasOneAttribute hasOneAttr,
242242

243243
// e.g. PATCH /articles
244244
// {... { "relationships":{ "Owner": { "data": null } } } }
245-
if (rio == null && Nullable.GetUnderlyingType(foreignKeyProperty.PropertyType) == null)
245+
bool foreignKeyPropertyIsNullableType = Nullable.GetUnderlyingType(foreignKeyProperty.PropertyType) != null
246+
|| foreignKeyProperty.PropertyType == typeof(string);
247+
if (rio == null && !foreignKeyPropertyIsNullableType)
246248
throw new JsonApiException(400, $"Cannot set required relationship identifier '{hasOneAttr.IdentifiablePropertyName}' to null because it is a non-nullable type.");
247249

248250
var convertedValue = TypeHelper.ConvertType(foreignKeyPropertyValue, foreignKeyProperty.PropertyType);

src/JsonApiDotNetCore/Services/EntityResourceService.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ public virtual async Task<TResource> UpdateAsync(TId id, TResource resource)
220220
_hookExecutor.AfterUpdate(AsList(entity), ResourcePipeline.Patch);
221221
entity = _hookExecutor.OnReturn(AsList(entity), ResourcePipeline.Patch).SingleOrDefault();
222222
}
223-
224223
return MapOut(entity);
225224
}
226225

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ public async Task Can_Update_ToOne_Relationship_ThroughLink()
528528
}
529529

530530
[Fact]
531-
public async Task Can_Delete_Relationship_By_Patching_Resource()
531+
public async Task Can_Delete_ToOne_Relationship_By_Patching_Resource()
532532
{
533533
// arrange
534534
var person = _personFaker.Generate();
@@ -580,6 +580,64 @@ public async Task Can_Delete_Relationship_By_Patching_Resource()
580580
Assert.Null(todoItemResult.Owner);
581581
}
582582

583+
584+
[Fact]
585+
public async Task Can_Delete_ToMany_Relationship_By_Patching_Resource()
586+
{
587+
// arrange
588+
var person = _personFaker.Generate();
589+
var todoItem = _todoItemFaker.Generate();
590+
person.TodoItems = new List<TodoItem>() { todoItem };
591+
_context.People.Add(person);
592+
_context.SaveChanges();
593+
594+
var builder = new WebHostBuilder()
595+
.UseStartup<Startup>();
596+
597+
var server = new TestServer(builder);
598+
var client = server.CreateClient();
599+
600+
var content = new
601+
{
602+
data = new
603+
{
604+
id = person.Id,
605+
type = "people",
606+
relationships = new Dictionary<string, object>
607+
{
608+
{ "todo-items", new
609+
{
610+
data = new List<object>
611+
{
612+
613+
}
614+
}
615+
}
616+
}
617+
}
618+
};
619+
620+
var httpMethod = new HttpMethod("PATCH");
621+
var route = $"/api/v1/people/{person.Id}";
622+
var request = new HttpRequestMessage(httpMethod, route);
623+
624+
string serializedContent = JsonConvert.SerializeObject(content);
625+
request.Content = new StringContent(serializedContent);
626+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
627+
628+
// Act
629+
var response = await _fixture.Client.SendAsync(request);
630+
631+
// Assert
632+
var personResult = _context.People
633+
.AsNoTracking()
634+
.Include(p => p.TodoItems)
635+
.Single(p => p.Id == person.Id);
636+
637+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
638+
Assert.Empty(personResult.TodoItems);
639+
}
640+
583641
[Fact]
584642
public async Task Can_Delete_Relationship_By_Patching_Relationship()
585643
{

test/UnitTests/Serialization/JsonApiDeSerializerTests.cs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,51 @@ public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship()
222222
Assert.Equal(property, result.Property);
223223
}
224224

225+
[Fact]
226+
public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship_With_String_Keys()
227+
{
228+
// arrange
229+
var resourceGraphBuilder = new ResourceGraphBuilder();
230+
resourceGraphBuilder.AddResource<IndependentWithStringKey, string>("independents");
231+
resourceGraphBuilder.AddResource<DependentWithStringKey, string>("dependents");
232+
var resourceGraph = resourceGraphBuilder.Build();
233+
234+
var jsonApiContextMock = new Mock<IJsonApiContext>();
235+
jsonApiContextMock.SetupAllProperties();
236+
jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph);
237+
jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary<AttrAttribute, object>());
238+
jsonApiContextMock.Setup(m => m.HasOneRelationshipPointers).Returns(new HasOneRelationshipPointers());
239+
240+
var jsonApiOptions = new JsonApiOptions();
241+
jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions);
242+
243+
var deserializer = new JsonApiDeSerializer(jsonApiContextMock.Object);
244+
245+
var property = Guid.NewGuid().ToString();
246+
var content = new Document
247+
{
248+
Data = new ResourceObject
249+
{
250+
Type = "independents",
251+
Id = "natural-key",
252+
Attributes = new Dictionary<string, object> { { "property", property } },
253+
Relationships = new Dictionary<string, RelationshipData>
254+
{
255+
{ "dependent" , new RelationshipData { } }
256+
}
257+
}
258+
};
259+
260+
var contentString = JsonConvert.SerializeObject(content);
261+
262+
// act
263+
var result = deserializer.Deserialize<IndependentWithStringKey>(contentString);
264+
265+
// assert
266+
Assert.NotNull(result);
267+
Assert.Equal(property, result.Property);
268+
}
269+
225270
[Fact]
226271
public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship_With_Relationship_Body()
227272
{
@@ -348,6 +393,19 @@ private class Dependent : Identifiable
348393
public int IndependentId { get; set; }
349394
}
350395

396+
private class IndependentWithStringKey : Identifiable<string>
397+
{
398+
[Attr("property")] public string Property { get; set; }
399+
[HasOne("dependent")] public Dependent Dependent { get; set; }
400+
public string DependentId { get; set; }
401+
}
402+
403+
private class DependentWithStringKey : Identifiable<string>
404+
{
405+
[HasOne("independent")] public Independent Independent { get; set; }
406+
public string IndependentId { get; set; }
407+
}
408+
351409
[Fact]
352410
public void Can_Deserialize_Object_With_HasManyRelationship()
353411
{
@@ -538,7 +596,7 @@ public void Can_Deserialize_Nested_Included_HasMany_Relationships()
538596
resourceGraphBuilder.AddResource<ManyToManyNested>("many-to-manys");
539597

540598
var deserializer = GetDeserializer(resourceGraphBuilder);
541-
599+
542600
var contentString =
543601
@"{
544602
""data"": {

0 commit comments

Comments
 (0)