From 6fc6a1c7fbb380a96e8243603b576ea489e93101 Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 16:57:04 -0600 Subject: [PATCH 01/10] chore(project.json): bump package version --- src/JsonApiDotNetCore/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/project.json b/src/JsonApiDotNetCore/project.json index 8785c130a7..86cf04693f 100644 --- a/src/JsonApiDotNetCore/project.json +++ b/src/JsonApiDotNetCore/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-beta1-*", + "version": "1.0.0-beta2-*", "dependencies": { "Microsoft.NETCore.App": { From 85723350c0d26ef7dc42c741f8a674592986fd72 Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 16:59:44 -0600 Subject: [PATCH 02/10] docs(readme): update usage documentation --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cbd344b80f..6f8c350398 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ public class Person : Identifiable #### Relationships In order for navigation properties to be identified in the model, -they should be labeled as virtual. +they should be labeled with the appropriate attribute (either `HasOne` or `HasMany`). ```csharp public class Person : Identifiable @@ -121,6 +121,7 @@ public class Person : Identifiable [Attr("first-name")] public string FirstName { get; set; } + [HasMany("todo-items")] public virtual List TodoItems { get; set; } } ``` @@ -135,6 +136,8 @@ public class TodoItem : Identifiable public string Description { get; set; } public int OwnerId { get; set; } + + [HasOne("owner")] public virtual Person Owner { get; set; } } ``` From e05180eb34d41f1a7af60a039a16a3dead56f8ec Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 20:10:47 -0600 Subject: [PATCH 03/10] refactor(attr-attribute): move into models namespace non-breaking change since all models already require the models namespace for Identifiable --- src/JsonApiDotNetCore/Internal/ContextEntity.cs | 1 + src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs | 1 + src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs | 2 ++ src/JsonApiDotNetCore/Internal/Query/QuerySet.cs | 1 + src/JsonApiDotNetCore/Internal/Query/SortQuery.cs | 2 ++ src/JsonApiDotNetCore/{Internal => Models}/AttrAttribute.cs | 2 +- 6 files changed, 8 insertions(+), 1 deletion(-) rename src/JsonApiDotNetCore/{Internal => Models}/AttrAttribute.cs (96%) diff --git a/src/JsonApiDotNetCore/Internal/ContextEntity.cs b/src/JsonApiDotNetCore/Internal/ContextEntity.cs index a1afeeefed..d63496dd14 100644 --- a/src/JsonApiDotNetCore/Internal/ContextEntity.cs +++ b/src/JsonApiDotNetCore/Internal/ContextEntity.cs @@ -1,4 +1,5 @@ using System; +using JsonApiDotNetCore.Models; using System.Collections.Generic; namespace JsonApiDotNetCore.Internal diff --git a/src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs b/src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs index db01fabc6c..b48a8b1f61 100644 --- a/src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using Microsoft.EntityFrameworkCore; +using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Internal { diff --git a/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs b/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs index 45811e88bd..364d0342c6 100644 --- a/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs +++ b/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs @@ -1,3 +1,5 @@ +using JsonApiDotNetCore.Models; + namespace JsonApiDotNetCore.Internal.Query { public class FilterQuery diff --git a/src/JsonApiDotNetCore/Internal/Query/QuerySet.cs b/src/JsonApiDotNetCore/Internal/Query/QuerySet.cs index 2dd9e56b97..ed31fd7969 100644 --- a/src/JsonApiDotNetCore/Internal/Query/QuerySet.cs +++ b/src/JsonApiDotNetCore/Internal/Query/QuerySet.cs @@ -4,6 +4,7 @@ using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Http; +using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Internal.Query { diff --git a/src/JsonApiDotNetCore/Internal/Query/SortQuery.cs b/src/JsonApiDotNetCore/Internal/Query/SortQuery.cs index 827ac3523b..7ef6682cc6 100644 --- a/src/JsonApiDotNetCore/Internal/Query/SortQuery.cs +++ b/src/JsonApiDotNetCore/Internal/Query/SortQuery.cs @@ -1,3 +1,5 @@ +using JsonApiDotNetCore.Models; + namespace JsonApiDotNetCore.Internal.Query { public class SortQuery diff --git a/src/JsonApiDotNetCore/Internal/AttrAttribute.cs b/src/JsonApiDotNetCore/Models/AttrAttribute.cs similarity index 96% rename from src/JsonApiDotNetCore/Internal/AttrAttribute.cs rename to src/JsonApiDotNetCore/Models/AttrAttribute.cs index 743412e3f2..3ce9a2196a 100644 --- a/src/JsonApiDotNetCore/Internal/AttrAttribute.cs +++ b/src/JsonApiDotNetCore/Models/AttrAttribute.cs @@ -1,7 +1,7 @@ using System; using System.Reflection; -namespace JsonApiDotNetCore.Internal +namespace JsonApiDotNetCore.Models { public class AttrAttribute : Attribute { From 07ff7174715fef6bdc5849c49f72f1c369cade5d Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 20:14:03 -0600 Subject: [PATCH 04/10] feat(relationship-attr): add hasMany and hasOne attribute --- src/JsonApiDotNetCore/Models/HasManyAttribute.cs | 11 +++++++++++ src/JsonApiDotNetCore/Models/HasOneAttribute.cs | 11 +++++++++++ .../Models/RelationshipAttribute.cs | 15 +++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 src/JsonApiDotNetCore/Models/HasManyAttribute.cs create mode 100644 src/JsonApiDotNetCore/Models/HasOneAttribute.cs create mode 100644 src/JsonApiDotNetCore/Models/RelationshipAttribute.cs diff --git a/src/JsonApiDotNetCore/Models/HasManyAttribute.cs b/src/JsonApiDotNetCore/Models/HasManyAttribute.cs new file mode 100644 index 0000000000..990cb9d983 --- /dev/null +++ b/src/JsonApiDotNetCore/Models/HasManyAttribute.cs @@ -0,0 +1,11 @@ +namespace JsonApiDotNetCore.Models +{ + public class HasManyAttribute : RelationshipAttribute + { + public HasManyAttribute(string publicName) + : base(publicName) + { + PublicRelationshipName = publicName; + } + } +} diff --git a/src/JsonApiDotNetCore/Models/HasOneAttribute.cs b/src/JsonApiDotNetCore/Models/HasOneAttribute.cs new file mode 100644 index 0000000000..e5670eae29 --- /dev/null +++ b/src/JsonApiDotNetCore/Models/HasOneAttribute.cs @@ -0,0 +1,11 @@ +namespace JsonApiDotNetCore.Models +{ + public class HasOneAttribute : RelationshipAttribute + { + public HasOneAttribute(string publicName) + : base(publicName) + { + PublicRelationshipName = publicName; + } + } +} diff --git a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs new file mode 100644 index 0000000000..47ed779c3e --- /dev/null +++ b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace JsonApiDotNetCore.Models +{ + public class RelationshipAttribute : Attribute + { + protected RelationshipAttribute(string publicName) + { + PublicRelationshipName = publicName; + } + + public string PublicRelationshipName { get; set; } + public string InternalRelationshipName { get; set; } + } +} From 0b0e8d1577d000b0d925b175c511a73237f1a444 Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 20:46:47 -0600 Subject: [PATCH 05/10] feat(relationships): implement relational attrs --- .../Builders/DocumentBuilder.cs | 18 +++---- .../Controllers/JsonApiController.cs | 2 +- .../Data/DefaultEntityRepository.cs | 6 +-- .../Data/IEntityRepository.cs | 2 +- .../Internal/ContextEntity.cs | 4 +- .../Internal/ContextGraph.cs | 4 +- .../Internal/ContextGraphBuilder.cs | 51 ++++++++----------- .../Internal/Generics/GenericProcessor.cs | 4 +- .../Internal/Generics/IGenericProcessor.cs | 3 +- .../Internal/Relationship.cs | 30 ----------- .../Models/HasManyAttribute.cs | 2 + .../Models/RelationshipAttribute.cs | 13 +++++ .../Serialization/JsonApiDeSerializer.cs | 6 +-- 13 files changed, 61 insertions(+), 84 deletions(-) delete mode 100644 src/JsonApiDotNetCore/Internal/Relationship.cs diff --git a/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs b/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs index 9dc64e06b4..284b2184c5 100644 --- a/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs @@ -124,25 +124,25 @@ private void _addRelationships(DocumentData data, ContextEntity contextEntity, I { Links = new Links { - Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.RelationshipName), - Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.RelationshipName) + Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.InternalRelationshipName), + Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.InternalRelationshipName) } }; - if (_relationshipIsIncluded(r.RelationshipName)) + if (_relationshipIsIncluded(r.InternalRelationshipName)) { var navigationEntity = _jsonApiContext.ContextGraph - .GetRelationship(entity, r.RelationshipName); + .GetRelationship(entity, r.InternalRelationshipName); if(navigationEntity == null) relationshipData.SingleData = null; else if (navigationEntity is IEnumerable) - relationshipData.ManyData = _getRelationships((IEnumerable)navigationEntity, r.RelationshipName); + relationshipData.ManyData = _getRelationships((IEnumerable)navigationEntity, r.InternalRelationshipName); else - relationshipData.SingleData = _getRelationship(navigationEntity, r.RelationshipName); + relationshipData.SingleData = _getRelationship(navigationEntity, r.InternalRelationshipName); } - data.Relationships.Add(r.RelationshipName.Dasherize(), relationshipData); + data.Relationships.Add(r.InternalRelationshipName.Dasherize(), relationshipData); }); } @@ -152,9 +152,9 @@ private List _getIncludedEntities(ContextEntity contextEntity, IId contextEntity.Relationships.ForEach(r => { - if (!_relationshipIsIncluded(r.RelationshipName)) return; + if (!_relationshipIsIncluded(r.InternalRelationshipName)) return; - var navigationEntity = _jsonApiContext.ContextGraph.GetRelationship(entity, r.RelationshipName); + var navigationEntity = _jsonApiContext.ContextGraph.GetRelationship(entity, r.InternalRelationshipName); if (navigationEntity is IEnumerable) foreach (var includedEntity in (IEnumerable)navigationEntity) diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs index 6a4723e77a..ab6eedbf9d 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs @@ -183,7 +183,7 @@ public virtual async Task PatchRelationshipsAsync(TId id, string var relationship = _jsonApiContext.ContextGraph .GetContextEntity(typeof(T)) .Relationships - .FirstOrDefault(r => r.RelationshipName == relationshipName); + .FirstOrDefault(r => r.InternalRelationshipName == relationshipName); var relationshipIds = relationships.Select(r=>r.Id); diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 95d8c1a0b2..5a44bb076c 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -108,9 +108,9 @@ public virtual async Task UpdateAsync(TId id, TEntity entity) return oldEntity; } - public async Task UpdateRelationshipsAsync(object parent, Relationship relationship, IEnumerable relationshipIds) + public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds) { - var genericProcessor = GenericProcessorFactory.GetProcessor(relationship.BaseType, _context); + var genericProcessor = GenericProcessorFactory.GetProcessor(relationship.Type, _context); await genericProcessor.UpdateRelationshipsAsync(parent, relationship, relationshipIds); } @@ -131,7 +131,7 @@ public virtual async Task DeleteAsync(TId id) public virtual IQueryable Include(IQueryable entities, string relationshipName) { var entity = _jsonApiContext.RequestEntity; - if(entity.Relationships.Any(r => r.RelationshipName == relationshipName)) + if(entity.Relationships.Any(r => r.InternalRelationshipName == relationshipName)) return entities.Include(relationshipName); throw new JsonApiException("400", "Invalid relationship", diff --git a/src/JsonApiDotNetCore/Data/IEntityRepository.cs b/src/JsonApiDotNetCore/Data/IEntityRepository.cs index 3eb449c8cf..8df7019f13 100644 --- a/src/JsonApiDotNetCore/Data/IEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/IEntityRepository.cs @@ -34,7 +34,7 @@ public interface IEntityRepository Task UpdateAsync(TId id, TEntity entity); - Task UpdateRelationshipsAsync(object parent, Relationship relationship, IEnumerable relationshipIds); + Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds); Task DeleteAsync(TId id); } diff --git a/src/JsonApiDotNetCore/Internal/ContextEntity.cs b/src/JsonApiDotNetCore/Internal/ContextEntity.cs index d63496dd14..e43d85ab0f 100644 --- a/src/JsonApiDotNetCore/Internal/ContextEntity.cs +++ b/src/JsonApiDotNetCore/Internal/ContextEntity.cs @@ -1,6 +1,6 @@ using System; -using JsonApiDotNetCore.Models; using System.Collections.Generic; +using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Internal { @@ -9,6 +9,6 @@ public class ContextEntity public string EntityName { get; set; } public Type EntityType { get; set; } public List Attributes { get; set; } - public List Relationships { get; set; } + public List Relationships { get; set; } } } diff --git a/src/JsonApiDotNetCore/Internal/ContextGraph.cs b/src/JsonApiDotNetCore/Internal/ContextGraph.cs index 7d1493f2c1..f59ce6f497 100644 --- a/src/JsonApiDotNetCore/Internal/ContextGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ContextGraph.cs @@ -46,8 +46,8 @@ public string GetRelationshipName(string relationshipName) e.EntityType == entityType) .Relationships .FirstOrDefault(r => - r.RelationshipName.ToLower() == relationshipName.ToLower()) - ?.RelationshipName; + r.InternalRelationshipName.ToLower() == relationshipName.ToLower()) + ?.InternalRelationshipName; } } } diff --git a/src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs b/src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs index b48a8b1f61..e856714b40 100644 --- a/src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs @@ -15,7 +15,6 @@ public class ContextGraphBuilder where T : DbContext public ContextGraph Build() { _getFirstLevelEntities(); - _loadRelationships(); var graph = new ContextGraph { @@ -42,7 +41,8 @@ private void _getFirstLevelEntities() entities.Add(new ContextEntity { EntityName = property.Name, EntityType = entityType, - Attributes = _getAttributes(entityType) + Attributes = _getAttributes(entityType), + Relationships = _getRelationships(entityType) }); } } @@ -66,38 +66,29 @@ private List _getAttributes(Type entityType) return attributes; } - private void _loadRelationships() - { - _entities.ForEach(entity => { - - var relationships = new List(); - var properties = entity.EntityType.GetProperties(); - - foreach(var entityProperty in properties) - { - var propertyType = entityProperty.PropertyType; - - if(_isValidEntity(propertyType) - || (propertyType.GetTypeInfo().IsGenericType && _isValidEntity(propertyType.GetGenericArguments()[0]))) - relationships.Add(_getRelationshipFromPropertyInfo(entityProperty)); - } - - entity.Relationships = relationships; - }); - } - - private bool _isValidEntity(Type type) + private List _getRelationships(Type entityType) { - var validEntityRelationshipTypes = _entities.Select(e => e.EntityType); - return validEntityRelationshipTypes.Contains(type); + var attributes = new List(); + + var properties = entityType.GetProperties(); + + foreach(var prop in properties) + { + var attribute = (RelationshipAttribute)prop.GetCustomAttribute(typeof(RelationshipAttribute)); + if(attribute == null) continue; + attribute.InternalRelationshipName = prop.Name; + attribute.Type = _getRelationshipType(attribute, prop); + attributes.Add(attribute); + } + return attributes; } - private Relationship _getRelationshipFromPropertyInfo(PropertyInfo propertyInfo) + private Type _getRelationshipType(RelationshipAttribute relation, PropertyInfo prop) { - return new Relationship { - Type = propertyInfo.PropertyType, - RelationshipName = propertyInfo.Name - }; + if(relation.IsHasMany) + return prop.PropertyType.GetGenericArguments()[0]; + else + return prop.PropertyType; } } } diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs index 669e931006..dae932c199 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs @@ -17,9 +17,9 @@ public GenericProcessor(DbContext context) _context = context; } - public async Task UpdateRelationshipsAsync(object parent, Relationship relationship, IEnumerable relationshipIds) + public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds) { - var relationshipType = relationship.BaseType; + var relationshipType = relationship.Type; // TODO: replace with relationship.IsMany if(relationship.Type.GetInterfaces().Contains(typeof(IEnumerable))) diff --git a/src/JsonApiDotNetCore/Internal/Generics/IGenericProcessor.cs b/src/JsonApiDotNetCore/Internal/Generics/IGenericProcessor.cs index 5b60dc0738..313db15dc1 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/IGenericProcessor.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/IGenericProcessor.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; using System.Threading.Tasks; +using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Internal { public interface IGenericProcessor { - Task UpdateRelationshipsAsync(object parent, Relationship relationship, IEnumerable relationshipIds); + Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds); } } diff --git a/src/JsonApiDotNetCore/Internal/Relationship.cs b/src/JsonApiDotNetCore/Internal/Relationship.cs deleted file mode 100644 index c477c2a248..0000000000 --- a/src/JsonApiDotNetCore/Internal/Relationship.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using JsonApiDotNetCore.Extensions; - -namespace JsonApiDotNetCore.Internal -{ - public class Relationship - { - public Type Type { get; set; } - public Type BaseType { get { - return (Type.GetInterfaces().Contains(typeof(IEnumerable))) ? - Type.GenericTypeArguments[0] : - Type; - } } - - public string RelationshipName { get; set; } - - public void SetValue(object entity, object newValue) - { - var propertyInfo = entity - .GetType() - .GetProperty(RelationshipName); - - propertyInfo.SetValue(entity, newValue); - } - } -} diff --git a/src/JsonApiDotNetCore/Models/HasManyAttribute.cs b/src/JsonApiDotNetCore/Models/HasManyAttribute.cs index 990cb9d983..13e4a9efad 100644 --- a/src/JsonApiDotNetCore/Models/HasManyAttribute.cs +++ b/src/JsonApiDotNetCore/Models/HasManyAttribute.cs @@ -1,3 +1,5 @@ +using System; + namespace JsonApiDotNetCore.Models { public class HasManyAttribute : RelationshipAttribute diff --git a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs index 47ed779c3e..45b3565592 100644 --- a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; namespace JsonApiDotNetCore.Models { @@ -11,5 +12,17 @@ protected RelationshipAttribute(string publicName) public string PublicRelationshipName { get; set; } public string InternalRelationshipName { get; set; } + public Type Type { get; set; } + 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); + } } } diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs index edb895a5db..ad7e7cba18 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs @@ -98,12 +98,12 @@ private static object _setRelationships( foreach (var attr in contextEntity.Relationships) { - var entityProperty = entityProperties.FirstOrDefault(p => p.Name == $"{attr.RelationshipName}Id"); + var entityProperty = entityProperties.FirstOrDefault(p => p.Name == $"{attr.InternalRelationshipName}Id"); if (entityProperty == null) - throw new JsonApiException("400", $"{contextEntity.EntityType.Name} does not contain an relationsip named {attr.RelationshipName}"); + throw new JsonApiException("400", $"{contextEntity.EntityType.Name} does not contain an relationsip named {attr.InternalRelationshipName}"); - var relationshipName = attr.RelationshipName.Dasherize(); + var relationshipName = attr.InternalRelationshipName.Dasherize(); RelationshipData relationshipData; if (relationships.TryGetValue(relationshipName, out relationshipData)) { From 1fe487054bc05749230cabd523bb4ac4346b3b82 Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 20:56:00 -0600 Subject: [PATCH 06/10] fix(example): use relationship attrs --- src/JsonApiDotNetCoreExample/Models/Person.cs | 3 +++ src/JsonApiDotNetCoreExample/Models/TodoItem.cs | 5 ++++- src/JsonApiDotNetCoreExample/Models/TodoItemCollection.cs | 7 ++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/JsonApiDotNetCoreExample/Models/Person.cs b/src/JsonApiDotNetCoreExample/Models/Person.cs index 900971951d..3689f39537 100644 --- a/src/JsonApiDotNetCoreExample/Models/Person.cs +++ b/src/JsonApiDotNetCoreExample/Models/Person.cs @@ -13,7 +13,10 @@ public class Person : Identifiable, IHasMeta [Attr("last-name")] public string LastName { get; set; } + [HasMany("todo-items")] public virtual List TodoItems { get; set; } + + [HasMany("todo-item-collections")] public virtual List TodoItemCollections { get; set; } public Dictionary GetMeta(IJsonApiContext context) diff --git a/src/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/JsonApiDotNetCoreExample/Models/TodoItem.cs index fbf4fe8ba6..5d706da5db 100644 --- a/src/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -12,9 +12,12 @@ public class TodoItem : Identifiable public long Ordinal { get; set; } public int? OwnerId { get; set; } + public int? CollectionId { get; set; } + + [HasOne("owner")] public virtual Person Owner { get; set; } - public int? CollectionId { get; set; } + [HasOne("collection")] public virtual TodoItemCollection Collection { get; set; } } } diff --git a/src/JsonApiDotNetCoreExample/Models/TodoItemCollection.cs b/src/JsonApiDotNetCoreExample/Models/TodoItemCollection.cs index 2c4e42c3f0..c5b1dda453 100644 --- a/src/JsonApiDotNetCoreExample/Models/TodoItemCollection.cs +++ b/src/JsonApiDotNetCoreExample/Models/TodoItemCollection.cs @@ -5,9 +5,14 @@ namespace JsonApiDotNetCoreExample.Models { public class TodoItemCollection : Identifiable { + [Attr("name")] public string Name { get; set; } - public virtual List TodoItems { get; set; } public int OwnerId { get; set; } + + [HasMany("todo-items")] + public virtual List TodoItems { get; set; } + + [HasOne("owner")] public virtual Person Owner { get; set; } } } \ No newline at end of file From 7de22a14d1e2522ea813ee057ac6533c40851149 Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 20:56:25 -0600 Subject: [PATCH 07/10] refactor(generic-processor): use new attr on relationship --- src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs index dae932c199..5fa1857b94 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs @@ -21,8 +21,7 @@ public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute { var relationshipType = relationship.Type; - // TODO: replace with relationship.IsMany - if(relationship.Type.GetInterfaces().Contains(typeof(IEnumerable))) + if(relationship.IsHasMany) { var entities = _context.GetDbSet().Where(x => relationshipIds.Contains(x.Id.ToString())).ToList(); relationship.SetValue(parent, entities); From cf00db40ecb9e50208b115093dd6934b6fd5e35f Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 21:03:43 -0600 Subject: [PATCH 08/10] debug(travis-ci): add logging to debug travis ci builds are failing in travis -- working fine locally --- .../Acceptance/Spec/DocumentTests/Meta.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs index 0be777a4f7..ea2a494bf5 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs @@ -14,6 +14,7 @@ using JsonApiDotNetCoreExampleTests.Startups; using JsonApiDotNetCoreExample.Models; using System.Collections; +using System; namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec.DocumentTests { @@ -32,6 +33,7 @@ public Meta(DocsFixture fixture) public async Task Total_Record_Count_Included() { // arrange + _context.TodoItems.RemoveRange(_context.TodoItems); var expectedCount = _context.TodoItems.Count(); var builder = new WebHostBuilder() .UseStartup(); @@ -45,7 +47,9 @@ public async Task Total_Record_Count_Included() // act var response = await client.SendAsync(request); - var documents = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + var responseBody = await response.Content.ReadAsStringAsync(); + Console.WriteLine(responseBody); + var documents = JsonConvert.DeserializeObject(responseBody); // assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); From d59794e91f8dc19ddbf542779fd4681b54718046 Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Wed, 1 Mar 2017 21:09:44 -0600 Subject: [PATCH 09/10] test(meta): ensure there is at least one item in the context --- .../Acceptance/Spec/DocumentTests/Meta.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs index ea2a494bf5..71d1ffd12a 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs @@ -34,7 +34,9 @@ public async Task Total_Record_Count_Included() { // arrange _context.TodoItems.RemoveRange(_context.TodoItems); - var expectedCount = _context.TodoItems.Count(); + _context.TodoItems.Add(new TodoItem()); + _context.SaveChanges(); + var expectedCount = 1; var builder = new WebHostBuilder() .UseStartup(); From 9d6c4a1448206b6f72dc3b90e7ab0415e2bf79b5 Mon Sep 17 00:00:00 2001 From: Jared Nance Date: Thu, 2 Mar 2017 06:23:35 -0600 Subject: [PATCH 10/10] clean(test/meta): remove console log statement --- .../Acceptance/Spec/DocumentTests/Meta.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs index 71d1ffd12a..e024786252 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs @@ -50,7 +50,6 @@ public async Task Total_Record_Count_Included() // act var response = await client.SendAsync(request); var responseBody = await response.Content.ReadAsStringAsync(); - Console.WriteLine(responseBody); var documents = JsonConvert.DeserializeObject(responseBody); // assert