Skip to content

Fix/patch relationships #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
attr.SetValue(oldEntity, attr.GetValue(entity));
});

foreach(var relationship in _jsonApiContext.RelationshipsToUpdate)
relationship.Key.SetValue(oldEntity, relationship.Value);

await _context.SaveChangesAsync();

return oldEntity;
return oldEntity;
}

public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds)
Expand Down
11 changes: 10 additions & 1 deletion src/JsonApiDotNetCore/Models/HasManyAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System.Reflection;

namespace JsonApiDotNetCore.Models
{
Expand All @@ -9,5 +9,14 @@ public HasManyAttribute(string publicName)
{
PublicRelationshipName = publicName;
}

public override void SetValue(object entity, object newValue)
{
var propertyInfo = entity
.GetType()
.GetProperty(InternalRelationshipName);

propertyInfo.SetValue(entity, newValue);
}
}
}
15 changes: 15 additions & 0 deletions src/JsonApiDotNetCore/Models/HasOneAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Reflection;

namespace JsonApiDotNetCore.Models
{
public class HasOneAttribute : RelationshipAttribute
Expand All @@ -7,5 +9,18 @@ public HasOneAttribute(string publicName)
{
PublicRelationshipName = publicName;
}

public override void SetValue(object entity, object newValue)
{
var propertyName = (newValue.GetType() == Type)
? InternalRelationshipName
: $"{InternalRelationshipName}Id";

var propertyInfo = entity
.GetType()
.GetProperty(propertyName);

propertyInfo.SetValue(entity, newValue);
}
}
}
12 changes: 2 additions & 10 deletions src/JsonApiDotNetCore/Models/RelationshipAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System;
using System.Reflection;

namespace JsonApiDotNetCore.Models
{
public class RelationshipAttribute : Attribute
public abstract class RelationshipAttribute : Attribute
{
protected RelationshipAttribute(string publicName)
{
Expand All @@ -16,13 +15,6 @@ protected RelationshipAttribute(string publicName)
public bool IsHasMany { get { return this.GetType() == typeof(HasManyAttribute); } }
public bool IsHasOne { get { return this.GetType() == typeof(HasOneAttribute); } }

public void SetValue(object entity, object newValue)
{
var propertyInfo = entity
.GetType()
.GetProperty(InternalRelationshipName);

propertyInfo.SetValue(entity, newValue);
}
public abstract void SetValue(object entity, object newValue);
}
}
6 changes: 6 additions & 0 deletions src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,18 @@ private object _setHasOneRelationship(object entity,

if (relationships.TryGetValue(relationshipName, out RelationshipData relationshipData))
{
var relationshipAttr = _jsonApiContext.RequestEntity.Relationships
.SingleOrDefault(r => r.PublicRelationshipName == relationshipName);

var data = (Dictionary<string, string>)relationshipData.ExposedData;

if (data == null) return entity;

var newValue = data["id"];
var convertedValue = TypeHelper.ConvertType(newValue, entityProperty.PropertyType);

_jsonApiContext.RelationshipsToUpdate[relationshipAttr] = convertedValue;

entityProperty.SetValue(entity, convertedValue);
}

Expand Down
2 changes: 2 additions & 0 deletions src/JsonApiDotNetCore/Services/IJsonApiContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Internal;
using JsonApiDotNetCore.Internal.Query;
using JsonApiDotNetCore.Models;

namespace JsonApiDotNetCore.Services
{
Expand All @@ -20,5 +21,6 @@ public interface IJsonApiContext
PageManager PageManager { get; set; }
IMetaBuilder MetaBuilder { get; set; }
IGenericProcessorFactory GenericProcessorFactory { get; set; }
Dictionary<RelationshipAttribute, object> RelationshipsToUpdate { get; set; }
}
}
3 changes: 3 additions & 0 deletions src/JsonApiDotNetCore/Services/JsonApiContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Internal;
using JsonApiDotNetCore.Internal.Query;
using JsonApiDotNetCore.Models;
using Microsoft.AspNetCore.Http;

namespace JsonApiDotNetCore.Services
Expand All @@ -23,6 +24,7 @@ public JsonApiContext(
Options = options;
MetaBuilder = metaBuilder;
GenericProcessorFactory = genericProcessorFactory;
RelationshipsToUpdate = new Dictionary<RelationshipAttribute, object>();
}

public JsonApiOptions Options { get; set; }
Expand All @@ -36,6 +38,7 @@ public JsonApiContext(
public PageManager PageManager { get; set; }
public IMetaBuilder MetaBuilder { get; set; }
public IGenericProcessorFactory GenericProcessorFactory { get; set; }
public Dictionary<RelationshipAttribute, object> RelationshipsToUpdate { get; set; }

public IJsonApiContext ApplyContext<T>()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
Expand All @@ -7,15 +6,15 @@
using Bogus;
using DotNetCoreDocs;
using DotNetCoreDocs.Writers;
using JsonApiDotNetCore.Serialization;
using JsonApiDotNetCore.Services;
using JsonApiDotNetCoreExample;
using JsonApiDotNetCoreExample.Data;
using JsonApiDotNetCoreExample.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Xunit;
using Person = JsonApiDotNetCoreExample.Models.Person;

namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec
{
Expand All @@ -25,14 +24,18 @@ public class UpdatingDataTests
private DocsFixture<Startup, JsonDocWriter> _fixture;
private AppDbContext _context;
private Faker<TodoItem> _todoItemFaker;
private Faker<Person> _personFaker;

public UpdatingDataTests(DocsFixture<Startup, JsonDocWriter> fixture)
{
_fixture = fixture;
_context = fixture.GetService<AppDbContext>();
_todoItemFaker = new Faker<TodoItem>()
_todoItemFaker = new Faker<TodoItem>()
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
.RuleFor(t => t.Ordinal, f => f.Random.Number());
_personFaker = new Faker<Person>()
.RuleFor(p => p.FirstName, f => f.Name.FirstName())
.RuleFor(p => p.LastName, f => f.Name.LastName());
}

[Fact]
Expand Down Expand Up @@ -73,5 +76,62 @@ public async Task Respond_404_If_EntityDoesNotExist()
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}

[Fact]
public async Task Can_Patch_Entity_And_HasOne_Relationships()
{
// arrange
var todoItem = _todoItemFaker.Generate();
var person = _personFaker.Generate();
_context.TodoItems.Add(todoItem);
_context.People.Add(person);
_context.SaveChanges();

var builder = new WebHostBuilder()
.UseStartup<Startup>();
var server = new TestServer(builder);
var client = server.CreateClient();

var content = new
{
data = new
{
type = "todo-items",
attributes = new
{
description = todoItem.Description,
ordinal = todoItem.Ordinal
},
relationships = new
{
owner = new
{
data = new
{
type = "people",
id = person.Id.ToString()
}
}
}
}
};

var httpMethod = new HttpMethod("PATCH");
var route = $"/api/v1/todo-items/{todoItem.Id}";
var request = new HttpRequestMessage(httpMethod, route);

request.Content = new StringContent(JsonConvert.SerializeObject(content));
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");

// Act
var response = await client.SendAsync(request);
var updatedTodoItem = _context.TodoItems.AsNoTracking()
.Include(t => t.Owner)
.SingleOrDefault(t => t.Id == todoItem.Id);

// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(person.Id, updatedTodoItem.OwnerId);
}
}
}