Skip to content

Commit a6cc2e0

Browse files
committed
feat(include): include relationships
1 parent 84d105e commit a6cc2e0

File tree

9 files changed

+105
-9
lines changed

9 files changed

+105
-9
lines changed

src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using System.Collections;
13
using System.Collections.Generic;
24
using JsonApiDotNetCore.Extensions;
35
using JsonApiDotNetCore.Internal;
@@ -9,7 +11,7 @@ namespace JsonApiDotNetCore.Builders
911
public class DocumentBuilder
1012
{
1113
private IJsonApiContext _jsonApiContext;
12-
private IContextGraph _contextGraph;
14+
private IContextGraph _contextGraph;
1315

1416
public DocumentBuilder(IJsonApiContext jsonApiContext)
1517
{
@@ -24,7 +26,7 @@ public Document Build(IIdentifiable entity)
2426
var document = new Document
2527
{
2628
Data = _getData(contextEntity, entity)
27-
};
29+
};
2830

2931
return document;
3032
}
@@ -44,7 +46,7 @@ public Documents Build(IEnumerable<IIdentifiable> entities)
4446

4547
foreach (var entity in entities)
4648
documents.Data.Add(_getData(contextEntity, entity));
47-
49+
4850
return documents;
4951
}
5052

@@ -56,7 +58,7 @@ private DocumentData _getData(ContextEntity contextEntity, IIdentifiable entity)
5658
Id = entity.Id.ToString()
5759
};
5860

59-
if(_jsonApiContext.IsRelationshipData)
61+
if (_jsonApiContext.IsRelationshipData)
6062
return data;
6163

6264
data.Attributes = new Dictionary<string, object>();
@@ -67,7 +69,7 @@ private DocumentData _getData(ContextEntity contextEntity, IIdentifiable entity)
6769
data.Attributes.Add(attr.PublicAttributeName, attr.GetValue(entity));
6870
});
6971

70-
_addRelationships(data, contextEntity, entity);
72+
_addRelationships(data, contextEntity, entity);
7173

7274
return data;
7375
}
@@ -76,15 +78,59 @@ private void _addRelationships(DocumentData data, ContextEntity contextEntity, I
7678
{
7779
var linkBuilder = new LinkBuilder(_jsonApiContext);
7880

79-
contextEntity.Relationships.ForEach(r => {
80-
var relationshipData = new RelationshipData {
81-
Links = new Links {
81+
contextEntity.Relationships.ForEach(r =>
82+
{
83+
var relationshipData = new RelationshipData
84+
{
85+
Links = new Links
86+
{
8287
Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.RelationshipName),
8388
Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.RelationshipName)
8489
}
8590
};
91+
92+
if (_jsonApiContext.IncludedRelationships.Contains(r.RelationshipName.ToProperCase()))
93+
{
94+
var navigationEntity = _jsonApiContext.ContextGraph
95+
.GetRelationship(entity, r.RelationshipName);
96+
97+
if(navigationEntity is IEnumerable)
98+
relationshipData.ManyData = GetRelationships((IEnumerable<object>)navigationEntity, r.RelationshipName);
99+
else
100+
relationshipData.SingleData = GetRelationship(navigationEntity, r.RelationshipName);
101+
}
102+
103+
104+
86105
data.Relationships.Add(r.RelationshipName.Dasherize(), relationshipData);
87106
});
88107
}
108+
private List<Dictionary<string, string>> GetRelationships(IEnumerable<object> entities, string relationshipName)
109+
{
110+
var objType = entities.GetType().GenericTypeArguments[0];
111+
112+
var typeName = _jsonApiContext.ContextGraph.GetContextEntity(objType);
113+
114+
var relationships = new List<Dictionary<string, string>>();
115+
foreach(var entity in entities)
116+
{
117+
relationships.Add(new Dictionary<string, string> {
118+
{"type", typeName.EntityName },
119+
{"id", ((IIdentifiable)entity).Id.ToString() }
120+
});
121+
}
122+
return relationships;
123+
}
124+
private Dictionary<string, string> GetRelationship(object entity, string relationshipName)
125+
{
126+
var objType = entity.GetType();
127+
128+
var typeName = _jsonApiContext.ContextGraph.GetContextEntity(objType);
129+
130+
return new Dictionary<string, string> {
131+
{"type", typeName.EntityName },
132+
{"id", ((IIdentifiable)entity).Id.ToString() }
133+
};
134+
}
89135
}
90136
}

src/JsonApiDotNetCore/Controllers/JsonApiController.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
using System.Collections.Generic;
12
using System.Linq;
23
using System.Threading.Tasks;
34
using JsonApiDotNetCore.Data;
5+
using JsonApiDotNetCore.Extensions;
46
using JsonApiDotNetCore.Internal.Query;
57
using JsonApiDotNetCore.Models;
68
using JsonApiDotNetCore.Services;
@@ -157,6 +159,19 @@ private IQueryable<T> ApplyQuery(IQueryable<T> entities)
157159

158160
entities = _entities.Sort(entities, querySet.SortParameters);
159161

162+
if(querySet != null)
163+
entities = IncludeRelationships(entities, querySet.IncludedRelationships);
164+
165+
return entities;
166+
}
167+
168+
private IQueryable<T> IncludeRelationships(IQueryable<T> entities, List<string> relationships)
169+
{
170+
_jsonApiContext.IncludedRelationships = relationships;
171+
172+
foreach(var r in relationships)
173+
entities = _entities.Include(entities, r.ToProperCase());
174+
160175
return entities;
161176
}
162177
}

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq;
34
using System.Threading.Tasks;
@@ -120,5 +121,10 @@ public virtual async Task<bool> DeleteAsync(TId id)
120121

121122
return true;
122123
}
124+
125+
public IQueryable<TEntity> Include(IQueryable<TEntity> entities, string relationshipName)
126+
{
127+
return entities.Include(relationshipName);
128+
}
123129
}
124130
}

src/JsonApiDotNetCore/Data/IEntityRepository.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public interface IEntityRepository<TEntity, in TId>
1717
{
1818
IQueryable<TEntity> Get();
1919

20+
IQueryable<TEntity> Include(IQueryable<TEntity> entities, string relationshipName);
21+
2022
IQueryable<TEntity> Filter(IQueryable<TEntity> entities, FilterQuery filterQuery);
2123

2224
IQueryable<TEntity> Sort(IQueryable<TEntity> entities, List<SortQuery> sortQueries);

src/JsonApiDotNetCore/Internal/ContextGraph.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public ContextEntity GetContextEntity(Type entityType)
2626

2727
public object GetRelationship<TParent>(TParent entity, string relationshipName)
2828
{
29-
var parentEntityType = typeof(TParent);
29+
var parentEntityType = entity.GetType();
3030

3131
var navigationProperty = parentEntityType
3232
.GetProperties()

src/JsonApiDotNetCore/Internal/Query/QuerySet.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq;
3+
using JsonApiDotNetCore.Models;
34
using JsonApiDotNetCore.Services;
45

56
namespace JsonApiDotNetCore.Internal.Query
@@ -16,6 +17,7 @@ public QuerySet(IJsonApiContext jsonApiContext)
1617

1718
public FilterQuery Filter { get; set; }
1819
public List<SortQuery> SortParameters { get; set; }
20+
public List<string> IncludedRelationships { get; set; }
1921

2022
private void BuildQuerySet()
2123
{
@@ -30,6 +32,12 @@ private void BuildQuerySet()
3032
if (pair.Key.StartsWith("sort"))
3133
{
3234
SortParameters = ParseSortParameters(pair.Value);
35+
continue;
36+
}
37+
38+
if (pair.Key.StartsWith("include"))
39+
{
40+
IncludedRelationships = ParseIncludedRelationships(pair.Value);
3341
}
3442
}
3543
}
@@ -68,6 +76,11 @@ private List<SortQuery> ParseSortParameters(string value)
6876
return sortParameters;
6977
}
7078

79+
private List<string> ParseIncludedRelationships(string value)
80+
{
81+
return value.Split(',').ToList();
82+
}
83+
7184
private AttrAttribute GetAttribute(string propertyName)
7285
{
7386
return _jsonApiContext.RequestEntity.Attributes
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace JsonApiDotNetCore.Models
2+
{
3+
public class Inclusion
4+
{
5+
public string Id { get; set; }
6+
public string Type { get; set; }
7+
}
8+
}

src/JsonApiDotNetCore/Services/IJsonApiContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using System.Collections.Generic;
12
using JsonApiDotNetCore.Internal;
3+
using JsonApiDotNetCore.Models;
24
using Microsoft.AspNetCore.Http;
35

46
namespace JsonApiDotNetCore.Services
@@ -11,5 +13,6 @@ public interface IJsonApiContext
1113
string BasePath { get; set; }
1214
IQueryCollection Query { get; set; }
1315
bool IsRelationshipData { get; set; }
16+
List<string> IncludedRelationships { get; set; }
1417
}
1518
}

src/JsonApiDotNetCore/Services/JsonApiContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using System.Collections.Generic;
12
using JsonApiDotNetCore.Builders;
23
using JsonApiDotNetCore.Internal;
4+
using JsonApiDotNetCore.Models;
35
using Microsoft.AspNetCore.Http;
46

57
namespace JsonApiDotNetCore.Services
@@ -20,6 +22,7 @@ public JsonApiContext(
2022
public string BasePath { get; set; }
2123
public IQueryCollection Query { get; set; }
2224
public bool IsRelationshipData { get; set; }
25+
public List<string> IncludedRelationships { get; set; }
2326

2427
public IJsonApiContext ApplyContext<T>()
2528
{

0 commit comments

Comments
 (0)