Skip to content

Commit be08867

Browse files
committed
refactor: merge IContextEntityProvider and IFieldsExplorer into IResourceGraphExplorer
1 parent b5c38d4 commit be08867

File tree

52 files changed

+263
-307
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+263
-307
lines changed

src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources
1111
{
1212
public class ArticleResource : ResourceDefinition<Article>
1313
{
14-
public ArticleResource(IContextEntityProvider provider) : base(provider) { }
14+
public ArticleResource(IResourceGraphExplorer graph) : base(graph) { }
1515

1616
public override IEnumerable<Article> OnReturn(HashSet<Article> entities, ResourcePipeline pipeline)
1717
{

src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources
1111
{
1212
public abstract class LockableResource<T> : ResourceDefinition<T> where T : class, IIsLockable, IIdentifiable
1313
{
14-
protected LockableResource(IContextEntityProvider provider) : base(provider) { }
14+
protected LockableResource(IResourceGraphExplorer graph) : base(graph) { }
1515

1616
protected void DisallowLocked(IEnumerable<T> entities)
1717
{

src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources
1111
{
1212
public class PassportResource : ResourceDefinition<Passport>
1313
{
14-
public PassportResource(IContextEntityProvider provider) : base(provider)
14+
public PassportResource(IResourceGraphExplorer graph) : base(graph)
1515
{
1616
}
1717

src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources
99
{
1010
public class PersonResource : LockableResource<Person>, IHasMeta
1111
{
12-
public PersonResource(IContextEntityProvider provider) : base(provider) { }
12+
public PersonResource(IResourceGraphExplorer graph) : base(graph) { }
1313

1414
public override IEnumerable<Person> BeforeUpdate(IDiffableEntityHashSet<Person> entities, ResourcePipeline pipeline)
1515
{

src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExample.Resources
1010
{
1111
public class TagResource : ResourceDefinition<Tag>
1212
{
13-
public TagResource(IContextEntityProvider provider) : base(provider) { }
13+
public TagResource(IResourceGraphExplorer graph) : base(graph) { }
1414

1515
public override IEnumerable<Tag> BeforeCreate(IEntityHashSet<Tag> affected, ResourcePipeline pipeline)
1616
{

src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExample.Resources
1010
{
1111
public class TodoResource : LockableResource<TodoItem>
1212
{
13-
public TodoResource(IContextEntityProvider provider) : base(provider) { }
13+
public TodoResource(IResourceGraphExplorer graph) : base(graph) { }
1414

1515
public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null)
1616
{

src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources
99
{
1010
public class UserResource : ResourceDefinition<User>
1111
{
12-
public UserResource(IContextEntityProvider provider, IFieldsExplorer fieldExplorer) : base(fieldExplorer, provider)
12+
public UserResource(IResourceGraphExplorer graph) : base(graph)
1313
{
1414
HideFields(u => u.Password);
1515
}

src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public interface IResourceGraphBuilder
1313
/// <summary>
1414
/// Construct the <see cref="ResourceGraph"/>
1515
/// </summary>
16-
IContextEntityProvider Build();
16+
IResourceGraphExplorer Build();
1717

1818
/// <summary>
1919
/// Add a json:api resource

src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Reflection;
52
using JsonApiDotNetCore.Configuration;
63
using JsonApiDotNetCore.Data;
74
using JsonApiDotNetCore.Formatters;
@@ -16,16 +13,13 @@
1613
using JsonApiDotNetCore.Hooks;
1714
using JsonApiDotNetCore.Services;
1815
using Microsoft.AspNetCore.Http;
19-
using Microsoft.AspNetCore.Mvc;
2016
using Microsoft.EntityFrameworkCore;
2117
using Microsoft.Extensions.DependencyInjection;
2218
using JsonApiDotNetCore.Internal.Contracts;
2319
using JsonApiDotNetCore.Query;
2420
using JsonApiDotNetCore.Serialization.Server.Builders;
2521
using JsonApiDotNetCore.Serialization.Server;
26-
using JsonApiDotNetCore.Serialization.Client;
2722
using Microsoft.Extensions.DependencyInjection.Extensions;
28-
using JsonApiDotNetCore.Builders;
2923

3024
namespace JsonApiDotNetCore.Builders
3125
{
@@ -139,6 +133,7 @@ public void ConfigureServices()
139133
_services.AddSingleton<ILinksConfiguration>(JsonApiOptions);
140134
_services.AddSingleton(graph);
141135
_services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
136+
_services.AddSingleton<IResourceGraphExplorer>(graph);
142137
_services.AddSingleton<IContextEntityProvider>(graph);
143138
_services.AddScoped<ICurrentRequest, CurrentRequest>();
144139
_services.AddScoped<IScopedServiceProvider, RequestScopedServiceProvider>();
@@ -148,7 +143,6 @@ public void ConfigureServices()
148143
_services.AddScoped(typeof(GenericProcessor<>));
149144
_services.AddScoped<IQueryParameterDiscovery, QueryParameterDiscovery>();
150145
_services.AddScoped<ITargetedFields, TargetedFields>();
151-
_services.AddScoped<IFieldsExplorer, FieldsExplorer>();
152146
_services.AddScoped<IResourceDefinitionProvider, ResourceDefinitionProvider>();
153147
_services.AddScoped<IFieldsToSerialize, FieldsToSerialize>();
154148
_services.AddScoped<IQueryParameterActionFilter, QueryParameterActionFilter>();

src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public ResourceGraphBuilder(IResourceNameFormatter formatter)
3232
}
3333

3434
/// <inheritdoc />
35-
public IContextEntityProvider Build()
35+
public IResourceGraphExplorer Build()
3636
{
3737
_entities.ForEach(SetResourceLinksOptions);
3838
var graph = new ResourceGraph(_entities, _validationResults);

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,15 +419,15 @@ public class DefaultEntityRepository<TEntity> : DefaultEntityRepository<TEntity,
419419
{
420420
public DefaultEntityRepository(ITargetedFields targetedFields,
421421
IDbContextResolver contextResolver,
422-
IContextEntityProvider contextEntityProvider,
422+
IResourceGraphExplorer contextEntityProvider,
423423
IGenericProcessorFactory genericProcessorFactory)
424424
: base(targetedFields, contextResolver, contextEntityProvider, genericProcessorFactory)
425425
{
426426
}
427427

428428
public DefaultEntityRepository(ITargetedFields targetedFields,
429429
IDbContextResolver contextResolver,
430-
IContextEntityProvider contextEntityProvider,
430+
IResourceGraphExplorer contextEntityProvider,
431431
IGenericProcessorFactory genericProcessorFactory,
432432
ILoggerFactory loggerFactory = null)
433433
: base(targetedFields, contextResolver, contextEntityProvider, genericProcessorFactory, loggerFactory)

src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private static void DisableDetailedErrorsIfProduction(IApplicationBuilder app)
4949
private static void LogResourceGraphValidations(IApplicationBuilder app)
5050
{
5151
var logger = app.ApplicationServices.GetService(typeof(ILogger<ResourceGraphBuilder>)) as ILogger;
52-
var resourceGraph = app.ApplicationServices.GetService(typeof(IContextEntityProvider)) as ResourceGraph;
52+
var resourceGraph = app.ApplicationServices.GetService(typeof(IResourceGraphExplorer)) as ResourceGraph;
5353

5454
if (logger != null && resourceGraph != null)
5555
{

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ public static IServiceCollection AddClientSerialization(this IServiceCollection
8080

8181
services.AddScoped<IRequestSerializer>(sp =>
8282
{
83-
var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService<IContextEntityProvider>(), sp.GetService<IResourceObjectBuilderSettingsProvider>().Get());
84-
return new RequestSerializer(sp.GetService<IFieldsExplorer>(), sp.GetService<IContextEntityProvider>(), resourceObjectBuilder);
83+
var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService<IResourceGraphExplorer>(), sp.GetService<IResourceObjectBuilderSettingsProvider>().Get());
84+
return new RequestSerializer(sp.GetService<IResourceGraphExplorer>(), resourceObjectBuilder);
8585
});
8686
return services;
8787
}

src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,59 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Linq.Expressions;
4+
using JsonApiDotNetCore.Internal;
25
using JsonApiDotNetCore.Models;
36

47
namespace JsonApiDotNetCore.Internal.Contracts
58
{
9+
10+
/// <summary>
11+
/// Responsible for retrieving the exposed resource fields (attributes and
12+
/// relationships) of registered resources in the resource graph.
13+
/// </summary>
14+
public interface IResourceGraphExplorer : IContextEntityProvider
15+
{
16+
/// <summary>
17+
/// Gets all fields (attributes and relationships) for <typeparamref name="TResource"/>
18+
/// that are targeted by the selector. If no selector is provided, all
19+
/// exposed fields are returned.
20+
/// </summary>
21+
/// <typeparam name="TResource">The resource for which to retrieve fields</typeparam>
22+
/// <param name="selector">Should be of the form: (TResource e) => new { e.Field1, e.Field2 }</param>
23+
List<IResourceField> GetFields<TResource>(Expression<Func<TResource, dynamic>> selector = null) where TResource : IIdentifiable;
24+
/// <summary>
25+
/// Gets all attributes for <typeparamref name="TResource"/>
26+
/// that are targeted by the selector. If no selector is provided, all
27+
/// exposed fields are returned.
28+
/// </summary>
29+
/// <typeparam name="TResource">The resource for which to retrieve attributes</typeparam>
30+
/// <param name="selector">Should be of the form: (TResource e) => new { e.Attribute1, e.Arttribute2 }</param>
31+
List<AttrAttribute> GetAttributes<TResource>(Expression<Func<TResource, dynamic>> selector = null) where TResource : IIdentifiable;
32+
/// <summary>
33+
/// Gets all relationships for <typeparamref name="TResource"/>
34+
/// that are targeted by the selector. If no selector is provided, all
35+
/// exposed fields are returned.
36+
/// </summary>
37+
/// <typeparam name="TResource">The resource for which to retrieve relationships</typeparam>
38+
/// <param name="selector">Should be of the form: (TResource e) => new { e.Relationship1, e.Relationship2 }</param>
39+
List<RelationshipAttribute> GetRelationships<TResource>(Expression<Func<TResource, dynamic>> selector = null) where TResource : IIdentifiable;
40+
/// <summary>
41+
/// Gets all exposed fields (attributes and relationships) for type <paramref name="type"/>
42+
/// </summary>
43+
/// <param name="type">The resource type. Must extend IIdentifiable.</param>
44+
List<IResourceField> GetFields(Type type);
45+
/// <summary>
46+
/// Gets all exposed attributes for type <paramref name="type"/>
47+
/// </summary>
48+
/// <param name="type">The resource type. Must extend IIdentifiable.</param>
49+
List<AttrAttribute> GetAttributes(Type type);
50+
/// <summary>
51+
/// Gets all exposed relationships for type <paramref name="type"/>
52+
/// </summary>
53+
/// <param name="type">The resource type. Must extend IIdentifiable.</param>
54+
List<RelationshipAttribute> GetRelationships(Type type);
55+
}
56+
657
/// <summary>
758
/// Responsible for getting <see cref="ContextEntity"/>s from the <see cref="ResourceGraph"/>.
859
/// </summary>

src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace JsonApiDotNetCore.Internal.Contracts
55
/// <summary>
66
/// A cache for the models in entity core
77
/// </summary>
8-
public interface IResourceGraph : IContextEntityProvider
8+
public interface IResourceGraph : IResourceGraphExplorer
99
{
1010

1111
}

src/JsonApiDotNetCore/Internal/ResourceGraph.cs

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Linq.Expressions;
45
using JsonApiDotNetCore.Internal.Contracts;
56
using JsonApiDotNetCore.Models;
67

@@ -9,18 +10,12 @@ namespace JsonApiDotNetCore.Internal
910
/// <summary>
1011
/// keeps track of all the models/resources defined in JADNC
1112
/// </summary>
12-
public class ResourceGraph : IContextEntityProvider
13+
public class ResourceGraph : IResourceGraphExplorer
1314
{
1415
internal List<ValidationResult> ValidationResults { get; }
1516
private List<ContextEntity> _entities { get; }
1617

17-
public ResourceGraph(List<ContextEntity> entities)
18-
{
19-
_entities = entities;
20-
ValidationResults = new List<ValidationResult>();
21-
}
22-
23-
public ResourceGraph(List<ContextEntity> entities, List<ValidationResult> validationResults)
18+
public ResourceGraph(List<ContextEntity> entities, List<ValidationResult> validationResults = null)
2419
{
2520
_entities = entities;
2621
ValidationResults = validationResults;
@@ -39,5 +34,108 @@ public ContextEntity GetContextEntity(Type entityType)
3934
/// <inheritdoc />
4035
public ContextEntity GetContextEntity<TResource>() where TResource : class, IIdentifiable
4136
=> GetContextEntity(typeof(TResource));
37+
38+
/// <inheritdoc/>
39+
public List<IResourceField> GetFields<T>(Expression<Func<T, dynamic>> selector = null) where T : IIdentifiable
40+
{
41+
return Getter(selector).ToList();
42+
}
43+
/// <inheritdoc/>
44+
public List<AttrAttribute> GetAttributes<T>(Expression<Func<T, dynamic>> selector = null) where T : IIdentifiable
45+
{
46+
return Getter(selector, FieldFilterType.Attribute).Cast<AttrAttribute>().ToList();
47+
}
48+
/// <inheritdoc/>
49+
public List<RelationshipAttribute> GetRelationships<T>(Expression<Func<T, dynamic>> selector = null) where T : IIdentifiable
50+
{
51+
return Getter(selector, FieldFilterType.Relationship).Cast<RelationshipAttribute>().ToList();
52+
}
53+
/// <inheritdoc/>
54+
public List<IResourceField> GetFields(Type type)
55+
{
56+
return GetContextEntity(type).Fields.ToList();
57+
}
58+
/// <inheritdoc/>
59+
public List<AttrAttribute> GetAttributes(Type type)
60+
{
61+
return GetContextEntity(type).Attributes.ToList();
62+
}
63+
/// <inheritdoc/>
64+
public List<RelationshipAttribute> GetRelationships(Type type)
65+
{
66+
return GetContextEntity(type).Relationships.ToList();
67+
}
68+
69+
private IEnumerable<IResourceField> Getter<T>(Expression<Func<T, dynamic>> selector = null, FieldFilterType type = FieldFilterType.None) where T : IIdentifiable
70+
{
71+
IEnumerable<IResourceField> available;
72+
if (type == FieldFilterType.Attribute)
73+
available = GetContextEntity(typeof(T)).Attributes.Cast<IResourceField>();
74+
else if (type == FieldFilterType.Relationship)
75+
available = GetContextEntity(typeof(T)).Relationships.Cast<IResourceField>();
76+
else
77+
available = GetContextEntity(typeof(T)).Fields;
78+
79+
if (selector == null)
80+
return available;
81+
82+
var targeted = new List<IResourceField>();
83+
84+
if (selector.Body is MemberExpression memberExpression)
85+
{ // model => model.Field1
86+
try
87+
{
88+
targeted.Add(available.Single(f => f.ExposedInternalMemberName == memberExpression.Member.Name));
89+
return targeted;
90+
}
91+
catch (Exception ex)
92+
{
93+
ThrowNotExposedError(memberExpression.Member.Name, type);
94+
}
95+
}
96+
97+
98+
if (selector.Body is NewExpression newExpression)
99+
{ // model => new { model.Field1, model.Field2 }
100+
string memberName = null;
101+
try
102+
{
103+
if (newExpression.Members == null)
104+
return targeted;
105+
106+
foreach (var member in newExpression.Members)
107+
{
108+
memberName = member.Name;
109+
targeted.Add(available.Single(f => f.ExposedInternalMemberName == memberName));
110+
}
111+
return targeted;
112+
}
113+
catch (Exception ex)
114+
{
115+
ThrowNotExposedError(memberName, type);
116+
}
117+
}
118+
119+
throw new ArgumentException($"The expression returned by '{selector}' for '{GetType()}' is of type {selector.Body.GetType()}"
120+
+ " and cannot be used to select resource attributes. The type must be a NewExpression.Example: article => new { article.Author };");
121+
122+
}
123+
124+
private void ThrowNotExposedError(string memberName, FieldFilterType type)
125+
{
126+
throw new ArgumentException($"{memberName} is not an json:api exposed {type.ToString("g")}.");
127+
}
128+
129+
/// <summary>
130+
/// internally used only by <see cref="ResourceGraph"/>.
131+
/// </summary>
132+
private enum FieldFilterType
133+
{
134+
None,
135+
Attribute,
136+
Relationship
137+
}
138+
139+
42140
}
43141
}

src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class CurrentRequestMiddleware
2121
private readonly RequestDelegate _next;
2222
private HttpContext _httpContext;
2323
private ICurrentRequest _currentRequest;
24-
private IContextEntityProvider _contextEntityProvider;
24+
private IResourceGraphExplorer _contextEntityProvider;
2525
private IJsonApiOptions _options;
2626
private IJsonApiRoutingConvention _routingConvention;
2727

@@ -34,8 +34,8 @@ public async Task Invoke(HttpContext httpContext,
3434
IJsonApiRoutingConvention routingConvention,
3535
IJsonApiOptions options,
3636
ICurrentRequest currentRequest,
37-
IContextEntityProvider contextEntityProvider)
38-
{
37+
IResourceGraphExplorer contextEntityProvider)
38+
{
3939
_httpContext = httpContext;
4040
_currentRequest = currentRequest;
4141
_routingConvention = routingConvention;
@@ -47,7 +47,7 @@ public async Task Invoke(HttpContext httpContext,
4747
_currentRequest.SetRequestResource(GetCurrentEntity());
4848
_currentRequest.IsRelationshipPath = PathIsRelationship();
4949
_currentRequest.BasePath = GetBasePath(_currentRequest.GetRequestResource().EntityName);
50-
}
50+
}
5151

5252
if (IsValid())
5353
{

0 commit comments

Comments
 (0)