From 0ced9da08afa4ec18f1fe7987a90842d6b758faa Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 13:05:27 +0200 Subject: [PATCH 01/29] refactor: move HasManyThrough GetValue and SetValue logic to HasManyThroughAttribute --- .../Data/DefaultEntityRepository.cs | 46 ++++--------- .../IServiceCollectionExtensions.cs | 2 +- .../Internal/Contracts/IResourceGraph.cs | 32 --------- .../Internal/ResourceGraph.cs | 47 +------------- src/JsonApiDotNetCore/Internal/TypeHelper.cs | 2 +- .../Models/Annotation/HasManyAttribute.cs | 21 ++++-- .../Annotation/HasManyThroughAttribute.cs | 63 ++++++++++++++++++ .../Models/Annotation/HasOneAttribute.cs | 16 +++-- .../Annotation/RelationshipAttribute.cs | 29 +-------- .../Common/ResourceObjectBuilder.cs | 10 +-- .../Builders/IncludedResourceObjectBuilder.cs | 4 +- .../Services/EntityResourceService.cs | 2 +- .../Acceptance/ManyToManyTests.cs | 65 +++++++++++++++++++ 13 files changed, 184 insertions(+), 155 deletions(-) diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 866c398be7..f06e933038 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -24,26 +24,26 @@ public class DefaultEntityRepository : IEntityRepository _dbSet; - private readonly IResourceGraph _resourceGraph; + private readonly IContextEntityProvider _provider; private readonly IGenericProcessorFactory _genericProcessorFactory; public DefaultEntityRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, - IResourceGraph resourceGraph, + IContextEntityProvider provider, IGenericProcessorFactory genericProcessorFactory) - : this(targetedFields, contextResolver, resourceGraph, genericProcessorFactory, null) + : this(targetedFields, contextResolver, provider, genericProcessorFactory, null) { } public DefaultEntityRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, - IResourceGraph resourceGraph, + IContextEntityProvider provider, IGenericProcessorFactory genericProcessorFactory, ILoggerFactory loggerFactory = null) { _targetedFields = targetedFields; - _resourceGraph = resourceGraph; + _provider = provider; _genericProcessorFactory = genericProcessorFactory; _context = contextResolver.GetContext(); _dbSet = _context.Set(); @@ -85,17 +85,15 @@ public virtual async Task CreateAsync(TEntity entity) { foreach (var relationshipAttr in _targetedFields.Relationships) { - object trackedRelationshipValue = GetTrackedRelationshipValue(relationshipAttr, entity, out bool wasAlreadyTracked); + object trackedRelationshipValue = GetTrackedRelationshipValue(relationshipAttr, entity, out bool relationshipWasAlreadyTracked); LoadInverseRelationships(trackedRelationshipValue, relationshipAttr); - if (wasAlreadyTracked) + if (relationshipWasAlreadyTracked || relationshipAttr is HasManyThroughAttribute) /// We only need to reassign the relationship value to the to-be-added - /// entity when we're using a different instance (because this different one + /// entity when we're using a different instance of the relationship (because this different one /// was already tracked) than the one assigned to the to-be-created entity. - AssignRelationshipValue(entity, trackedRelationshipValue, relationshipAttr); - else if (relationshipAttr is HasManyThroughAttribute throughAttr) - /// even if we don't have to reassign anything because of already tracked + /// Alternatively, even if we don't have to reassign anything because of already tracked /// entities, we still need to assign the "through" entities in the case of many-to-many. - AssignHasManyThrough(entity, throughAttr, (IList)trackedRelationshipValue); + relationshipAttr.SetValue(entity, trackedRelationshipValue); } _dbSet.Add(entity); await _context.SaveChangesAsync(); @@ -139,7 +137,7 @@ private void LoadInverseRelationships(object trackedRelationshipValue, Relations private bool IsHasOneRelationship(string internalRelationshipName, Type type) { - var relationshipAttr = _resourceGraph.GetContextEntity(type).Relationships.FirstOrDefault(r => r.InternalRelationshipName == internalRelationshipName); + var relationshipAttr = _provider.GetContextEntity(type).Relationships.FirstOrDefault(r => r.InternalRelationshipName == internalRelationshipName); if (relationshipAttr != null) { if (relationshipAttr is HasOneAttribute) @@ -149,7 +147,7 @@ private bool IsHasOneRelationship(string internalRelationshipName, Type type) } // relationshipAttr is null when we don't put a [RelationshipAttribute] on the inverse navigation property. // In this case we use relfection to figure out what kind of relationship is pointing back. - return !(type.GetProperty(internalRelationshipName).PropertyType.Inherits(typeof(IEnumerable))); + return !type.GetProperty(internalRelationshipName).PropertyType.Inherits(typeof(IEnumerable)); } private void DetachRelationships(TEntity entity) @@ -199,7 +197,8 @@ public virtual async Task UpdateAsync(TEntity updatedEntity) /// to the todoItems in trackedRelationshipValue LoadInverseRelationships(trackedRelationshipValue, relationshipAttr); /// assigns the updated relationship to the database entity - AssignRelationshipValue(databaseEntity, trackedRelationshipValue, relationshipAttr); + //AssignRelationshipValue(databaseEntity, trackedRelationshipValue, relationshipAttr); + relationshipAttr.SetValue(databaseEntity, trackedRelationshipValue); } await _context.SaveChangesAsync(); @@ -357,7 +356,6 @@ protected void LoadCurrentRelationships(TEntity oldEntity, RelationshipAttribute if (relationshipAttribute is HasManyThroughAttribute throughAttribute) { _context.Entry(oldEntity).Collection(throughAttribute.InternalThroughName).Load(); - } else if (relationshipAttribute is HasManyAttribute hasManyAttribute) { @@ -365,22 +363,6 @@ protected void LoadCurrentRelationships(TEntity oldEntity, RelationshipAttribute } } - /// - /// Assigns the to - /// - private void AssignRelationshipValue(TEntity targetEntity, object relationshipValue, RelationshipAttribute relationshipAttribute) - { - if (relationshipAttribute is HasManyThroughAttribute throughAttribute) - { - // todo: this logic should be put in the HasManyThrough attribute - AssignHasManyThrough(targetEntity, throughAttribute, (IList)relationshipValue); - } - else - { - relationshipAttribute.SetValue(targetEntity, relationshipValue); - } - } - /// /// The relationshipValue parameter contains the dependent side of the relationship (Tags). /// We can't directly add them to the principal entity (Article): we need to diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index 85d82abb32..c85176c7e4 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -44,7 +44,7 @@ public static IServiceCollection AddJsonApi(this IServiceCollection se /// /// /// - /// + /// /// public static IServiceCollection AddJsonApi(this IServiceCollection services, Action configureOptions, diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs index e702b1981e..1dfe948a0e 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs @@ -11,38 +11,6 @@ namespace JsonApiDotNetCore.Internal.Contracts public interface IResourceGraph : IContextEntityProvider { RelationshipAttribute GetInverseRelationship(RelationshipAttribute relationship); - /// - /// Gets the value of the navigation property, defined by the relationshipName, - /// on the provided instance. - /// - /// The resource instance - /// The navigation property name. - /// - /// - /// _graph.GetRelationship(todoItem, nameof(TodoItem.Owner)); - /// - /// - /// - /// In the case of a `HasManyThrough` relationship, it will not traverse the relationship - /// and will instead return the value of the shadow property (e.g. Articles.Tags). - /// If you want to traverse the relationship, you should use . - /// - object GetRelationship(TParent resource, string propertyName); - - /// - /// Gets the value of the navigation property (defined by the ) - /// on the provided instance. - /// In the case of `HasManyThrough` relationships, it will traverse the through entity and return the - /// value of the relationship on the other side of a join entity (e.g. Articles.ArticleTags.Tag). - /// - /// The resource instance - /// The attribute used to define the relationship. - /// - /// - /// _graph.GetRelationshipValue(todoItem, nameof(TodoItem.Owner)); - /// - /// - object GetRelationshipValue(TParent resource, RelationshipAttribute relationship) where TParent : IIdentifiable; /// /// Get the internal navigation property name for the specified public diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index b1b408b3bd..f268caaddb 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -52,51 +52,6 @@ internal ResourceGraph(List entities, bool usesDbContext, List public bool UsesDbContext { get; } - /// - public object GetRelationship(TParent entity, string relationshipName) - { - var parentEntityType = entity.GetType(); - - var navigationProperty = parentEntityType - .GetProperties() - .SingleOrDefault(p => string.Equals(p.Name, relationshipName, StringComparison.OrdinalIgnoreCase)); - - if (navigationProperty == null) - throw new JsonApiException(400, $"{parentEntityType} does not contain a relationship named {relationshipName}"); - - return navigationProperty.GetValue(entity); - } - - public object GetRelationshipValue(TParent resource, RelationshipAttribute relationship) where TParent : IIdentifiable - { - if (relationship is HasManyThroughAttribute hasManyThroughRelationship) - { - return GetHasManyThrough(resource, hasManyThroughRelationship); - } - - return GetRelationship(resource, relationship.InternalRelationshipName); - } - - private IEnumerable GetHasManyThrough(IIdentifiable parent, HasManyThroughAttribute hasManyThrough) - { - var throughProperty = GetRelationship(parent, hasManyThrough.InternalThroughName); - if (throughProperty is IEnumerable hasManyNavigationEntity) - { - // wrap "yield return" in a sub-function so we can correctly return null if the property is null. - return GetHasManyThroughIter(hasManyThrough, hasManyNavigationEntity); - } - return null; - } - - private IEnumerable GetHasManyThroughIter(HasManyThroughAttribute hasManyThrough, IEnumerable hasManyNavigationEntity) - { - foreach (var includedEntity in hasManyNavigationEntity) - { - var targetValue = hasManyThrough.RightProperty.GetValue(includedEntity) as IIdentifiable; - yield return targetValue; - } - } - /// public string GetRelationshipName(string relationshipName) { @@ -104,7 +59,7 @@ public string GetRelationshipName(string relationshipName) return Entities .SingleOrDefault(e => e.EntityType == entityType) ?.Relationships - .SingleOrDefault(r => r.Is(relationshipName)) +  .SingleOrDefault(r => r.Is(relationshipName)) ?.InternalRelationshipName; } diff --git a/src/JsonApiDotNetCore/Internal/TypeHelper.cs b/src/JsonApiDotNetCore/Internal/TypeHelper.cs index 4acee2d910..e5938472c1 100644 --- a/src/JsonApiDotNetCore/Internal/TypeHelper.cs +++ b/src/JsonApiDotNetCore/Internal/TypeHelper.cs @@ -80,7 +80,7 @@ public static T ConvertType(object value) public static Type GetTypeOfList(Type type) { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) + if (type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { return type.GetGenericArguments()[0]; } diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasManyAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasManyAttribute.cs index a69cb82c56..cb1218987e 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasManyAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasManyAttribute.cs @@ -31,18 +31,31 @@ public HasManyAttribute(string publicName = null, Link relationshipLinks = Link. InverseNavigation = inverseNavigationProperty; } + /// + /// Gets the value of the navigation property, defined by the relationshipName, + /// on the provided instance. + /// + + public override object GetValue(object entity) + { + return entity?.GetType()? + .GetProperty(InternalRelationshipName)? + .GetValue(entity); + } + + /// /// Sets the value of the property identified by this attribute /// - /// The target object + /// The target object /// The new property value - public override void SetValue(object resource, object newValue) + public override void SetValue(object entity, object newValue) { - var propertyInfo = resource + var propertyInfo = entity .GetType() .GetProperty(InternalRelationshipName); - propertyInfo.SetValue(resource, newValue); + propertyInfo.SetValue(entity, newValue); } } } diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs index 235607c6d3..a019e97ae5 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs @@ -1,5 +1,10 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; using System.Reflection; +using JsonApiDotNetCore.Extensions; +using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models.Links; namespace JsonApiDotNetCore.Models @@ -65,6 +70,64 @@ public HasManyThroughAttribute(string publicName, string internalThroughName, Li InternalThroughName = internalThroughName; } + /// + /// Traverses the through the provided entity and returns the + /// value of the relationship on the other side of a join entity + /// (e.g. Articles.ArticleTags.Tag). + /// + public override object GetValue(object entity) + { + var throughNavigationProperty = entity.GetType() + .GetProperties() + .SingleOrDefault(p => string.Equals(p.Name, InternalThroughName, StringComparison.OrdinalIgnoreCase)); + + var throughEntities = throughNavigationProperty.GetValue(entity); + + if (throughEntities == null) + // return an empty list for the right-type of the property. + return TypeHelper.CreateListFor(DependentType); + + // the right entities are included on the navigation/through entities. Extract and return them. + var rightEntities = new List(); + foreach (var rightEntity in (IList)throughEntities) + rightEntities.Add((IIdentifiable)RightProperty.GetValue(rightEntity)); + + return rightEntities.Cast(DependentType); + } + + + /// + /// Sets the value of the property identified by this attribute + /// + /// The target object + /// The new property value + public override void SetValue(object entity, object newValue) + { + + var propertyInfo = entity + .GetType() + .GetProperty(InternalRelationshipName); + propertyInfo.SetValue(entity, newValue); + + if (newValue == null) + { + ThroughProperty.SetValue(entity, null); + } + else + { + var throughRelationshipCollection = (IList)Activator.CreateInstance(ThroughProperty.PropertyType); + ThroughProperty.SetValue(entity, throughRelationshipCollection); + + foreach (IIdentifiable pointer in (IList)newValue) + { + var throughInstance = Activator.CreateInstance(ThroughType); + LeftProperty.SetValue(throughInstance, entity); + RightProperty.SetValue(throughInstance, pointer); + throughRelationshipCollection.Add(throughInstance); + } + } + } + /// /// The name of the join property on the parent resource. /// diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs index 1d5ac7c2de..1037aa9882 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs @@ -37,6 +37,14 @@ public HasOneAttribute(string publicName = null, Link links = Link.NotConfigured InverseNavigation = inverseNavigationProperty; } + + public override object GetValue(object entity) + { + return entity?.GetType()? + .GetProperty(InternalRelationshipName)? + .GetValue(entity); + } + private readonly string _explicitIdentifiablePropertyName; /// @@ -49,23 +57,23 @@ public HasOneAttribute(string publicName = null, Link links = Link.NotConfigured /// /// Sets the value of the property identified by this attribute /// - /// The target object + /// The target object /// The new property value - public override void SetValue(object resource, object newValue) + public override void SetValue(object entity, object newValue) { string propertyName = InternalRelationshipName; // if we're deleting the relationship (setting it to null), // we set the foreignKey to null. We could also set the actual property to null, // but then we would first need to load the current relationship, which requires an extra query. if (newValue == null) propertyName = IdentifiablePropertyName; - var resourceType = resource.GetType(); + var resourceType = entity.GetType(); var propertyInfo = resourceType.GetProperty(propertyName); if (propertyInfo == null) { // we can't set the FK to null because there isn't any. propertyInfo = resourceType.GetProperty(RelationshipPath); } - propertyInfo.SetValue(resource, newValue); + propertyInfo.SetValue(entity, newValue); } // HACK: this will likely require boxing diff --git a/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs index 86625f0092..1a5bd5dea2 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs @@ -66,34 +66,9 @@ protected RelationshipAttribute(string publicName, Link relationshipLinks, bool public bool CanInclude { get; } public string EntityPropertyName { get; } - public bool TryGetHasOne(out HasOneAttribute result) - { - if (IsHasOne) - { - result = (HasOneAttribute)this; - return true; - } - result = null; - return false; - } - - public bool TryGetHasMany(out HasManyAttribute result) - { - if (IsHasMany) - { - result = (HasManyAttribute)this; - return true; - } - result = null; - return false; - } - public abstract void SetValue(object entity, object newValue); - - public object GetValue(object entity) => entity - ?.GetType()? - .GetProperty(InternalRelationshipName)? - .GetValue(entity); + + public abstract object GetValue(object entity); public override string ToString() { diff --git a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs index f57d954f46..d144020972 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs @@ -69,10 +69,10 @@ protected object GetRelatedResourceLinkage(RelationshipAttribute relationship, I /// /// Builds a for a HasOne relationship /// - private ResourceIdentifierObject GetRelatedResourceLinkage(HasOneAttribute attr, IIdentifiable entity) + private ResourceIdentifierObject GetRelatedResourceLinkage(HasOneAttribute relationship, IIdentifiable entity) { - var relatedEntity = (IIdentifiable)_resourceGraph.GetRelationshipValue(entity, attr); - if (relatedEntity == null && IsRequiredToOneRelationship(attr, entity)) + var relatedEntity = (IIdentifiable)relationship.GetValue(entity); + if (relatedEntity == null && IsRequiredToOneRelationship(relationship, entity)) throw new NotSupportedException("Cannot serialize a required to one relationship that is not populated but was included in the set of relationships to be serialized."); if (relatedEntity != null) @@ -84,9 +84,9 @@ private ResourceIdentifierObject GetRelatedResourceLinkage(HasOneAttribute attr, /// /// Builds the s for a HasMany relationship /// - private List GetRelatedResourceLinkage(HasManyAttribute attr, IIdentifiable entity) + private List GetRelatedResourceLinkage(HasManyAttribute relationship, IIdentifiable entity) { - var relatedEntities = (IEnumerable)_resourceGraph.GetRelationshipValue(entity, attr); + var relatedEntities = (IEnumerable)relationship.GetValue(entity); var manyData = new List(); if (relatedEntities != null) foreach (IIdentifiable relatedEntity in relatedEntities) diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs index 5d66cedfa9..295f8db1a8 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs @@ -55,7 +55,7 @@ public void IncludeRelationshipChain(List inclusionChain, /// starting from the first related entity. var relationship = inclusionChain.First(); var chainRemainder = ShiftChain(inclusionChain); - var related = _resourceGraph.GetRelationshipValue(rootEntity, relationship); + var related = relationship.GetValue(rootEntity); ProcessChain(relationship, related, chainRemainder); } @@ -88,7 +88,7 @@ private void ProcessRelationship(RelationshipAttribute originRelationship, IIden if (relationshipEntry.HasResource) { // if the relationship is set, continue parsing the chain. - var related = _resourceGraph.GetRelationshipValue(parent, nextRelationship); + var related = nextRelationship.GetValue(parent); ProcessChain(nextRelationship, related, chainRemainder); } } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 232ac7308b..63eef18d5a 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -157,7 +157,7 @@ public virtual async Task GetRelationshipAsync(TId id, string relationsh { var relationship = GetRelationship(relationshipName); var resource = await GetRelationshipsAsync(id, relationshipName); - return _resourceGraph.GetRelationship(resource, relationship.InternalRelationshipName); + return relationship.GetValue(resource); } public virtual async Task UpdateAsync(TId id, TResource entity) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs index 0c5161b00e..4fe635952d 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs @@ -108,6 +108,71 @@ public async Task Can_Fetch_Many_To_Many_Through_GetById() Assert.Equal(tag.Name, tagResponse.Name); } + [Fact] + public async Task Can_Fetch_Many_To_Many_Through_GetById_Relationship_Link() + { + // arrange + var context = _fixture.GetService(); + var article = _articleFaker.Generate(); + var tag = _tagFaker.Generate(); + var articleTag = new ArticleTag + { + Article = article, + Tag = tag + }; + context.ArticleTags.Add(articleTag); + await context.SaveChangesAsync(); + + var route = $"/api/v1/articles/{article.Id}/tags"; + + // act + var response = await _fixture.Client.GetAsync(route); + + // assert + var body = await response.Content.ReadAsStringAsync(); + Assert.True(HttpStatusCode.OK == response.StatusCode, $"{route} returned {response.StatusCode} status code with payload: {body}"); + + var document = JsonConvert.DeserializeObject(body); + Assert.Null(document.Included); + + var tagResponse = _fixture.GetDeserializer().DeserializeList(body).Data.First(); + Assert.NotNull(tagResponse); + Assert.Equal(tag.Id, tagResponse.Id); + } + + + [Fact] + public async Task Can_Fetch_Many_To_Many_Through_Relationship_Link() + { + // arrange + var context = _fixture.GetService(); + var article = _articleFaker.Generate(); + var tag = _tagFaker.Generate(); + var articleTag = new ArticleTag + { + Article = article, + Tag = tag + }; + context.ArticleTags.Add(articleTag); + await context.SaveChangesAsync(); + + var route = $"/api/v1/articles/{article.Id}/relationships/tags"; + + // act + var response = await _fixture.Client.GetAsync(route); + + // assert + var body = await response.Content.ReadAsStringAsync(); + Assert.True(HttpStatusCode.OK == response.StatusCode, $"{route} returned {response.StatusCode} status code with payload: {body}"); + + var document = JsonConvert.DeserializeObject(body); + Assert.Null(document.Included); + + var tagResponse = _fixture.GetDeserializer().DeserializeList(body).Data.First(); + Assert.NotNull(tagResponse); + Assert.Equal(tag.Id, tagResponse.Id); + } + [Fact] public async Task Can_Fetch_Many_To_Many_Without_Include() { From 2e94d8e2102443e16804fb9798ed95a45993b023 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 13:54:25 +0200 Subject: [PATCH 02/29] feat: remove GetPublicAttributeName from ResourceGraph and remove dependency on ResourceGraph of controller layer --- .../Controllers/ArticlesController.cs | 4 +- .../Controllers/CamelCasedModelsController.cs | 4 +- .../Controllers/PassportsController.cs | 6 +- .../Controllers/PeopleController.cs | 4 +- .../Controllers/PersonRolesController.cs | 4 +- .../Controllers/TodoCollectionsController.cs | 3 +- .../Controllers/TodoItemsController.cs | 6 +- .../Controllers/TodoItemsTestController.cs | 7 +- .../Controllers/UsersController.cs | 4 +- .../Controllers/CustomTodoItemsController.cs | 4 +- .../Controllers/BaseJsonApiController.cs | 25 +- .../Controllers/JsonApiController.cs | 12 +- .../Extensions/ModelStateExtensions.cs | 8 +- .../Internal/Contracts/IResourceGraph.cs | 19 - .../Internal/ResourceGraph.cs | 19 - .../Models/Annotation/HasManyAttribute.cs | 1 - .../Acceptance/Spec/UpdatingDataTests.cs | 2 +- .../BaseJsonApiController_Tests.cs | 64 ++-- .../using JsonApiDotNetCore.Controllers; | 346 ++++++++++++++++++ 19 files changed, 412 insertions(+), 130 deletions(-) create mode 100644 wiki/v4/content/using JsonApiDotNetCore.Controllers; diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/ArticlesController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/ArticlesController.cs index abf6ce50ea..faa533093c 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/ArticlesController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/ArticlesController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; @@ -10,9 +9,8 @@ public class ArticlesController : JsonApiController
{ public ArticlesController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService
resourceService) - : base(jsonApiOptions, resourceGraph, resourceService) + : base(jsonApiOptions, resourceService) { } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/CamelCasedModelsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/CamelCasedModelsController.cs index c4ce48fc75..ee98b7f23d 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/CamelCasedModelsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/CamelCasedModelsController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; @@ -11,10 +10,9 @@ public class CamelCasedModelsController : JsonApiController { public CamelCasedModelsController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService, ILoggerFactory loggerFactory) - : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) { } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs index f6733da236..a040ff21e4 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; @@ -9,7 +8,10 @@ namespace JsonApiDotNetCoreExample.Controllers { public class PassportsController : JsonApiController { - public PassportsController(IJsonApiOptions jsonApiOptions, IResourceGraph resourceGraph, IResourceService resourceService, ILoggerFactory loggerFactory = null) : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory) + public PassportsController(IJsonApiOptions jsonApiOptions, + IResourceService resourceService, + ILoggerFactory loggerFactory = null) + : base(jsonApiOptions, resourceService, loggerFactory) { } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs index d29cafe508..851b2cfc80 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; @@ -11,10 +10,9 @@ public class PeopleController : JsonApiController { public PeopleController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService, ILoggerFactory loggerFactory) - : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) { } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/PersonRolesController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/PersonRolesController.cs index 0234eb6899..bee457a1cb 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/PersonRolesController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/PersonRolesController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; @@ -11,10 +10,9 @@ public class PersonRolesController : JsonApiController { public PersonRolesController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService, ILoggerFactory loggerFactory) - : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) { } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoCollectionsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoCollectionsController.cs index 56bc6fda48..d300e24f46 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoCollectionsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoCollectionsController.cs @@ -19,11 +19,10 @@ public class TodoCollectionsController : JsonApiController resourceService, ILoggerFactory loggerFactory) - : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) { _dbResolver = contextResolver; } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs index c7f3a6244e..818e082db9 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; @@ -10,11 +9,10 @@ namespace JsonApiDotNetCoreExample.Controllers public class TodoItemsController : JsonApiController { public TodoItemsController( - IJsonApiOptions jsonApiOPtions, - IResourceGraph resourceGraph, + IJsonApiOptions jsonApiOptions, IResourceService resourceService, ILoggerFactory loggerFactory) - : base(jsonApiOPtions, resourceGraph, resourceService, loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) { } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs index 18e566dc07..971f579b69 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; @@ -14,10 +13,9 @@ public abstract class AbstractTodoItemsController { protected AbstractTodoItemsController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService service, ILoggerFactory loggerFactory) - : base(jsonApiOptions, resourceGraph, service, loggerFactory) + : base(jsonApiOptions, service, loggerFactory) { } } @@ -26,10 +24,9 @@ public class TodoItemsTestController : AbstractTodoItemsController { public TodoItemsTestController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService service, ILoggerFactory loggerFactory) - : base(jsonApiOptions, resourceGraph, service, loggerFactory) + : base(jsonApiOptions, service, loggerFactory) { } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/UsersController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/UsersController.cs index 475b93b300..cc47e88d84 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/UsersController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/UsersController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; @@ -11,10 +10,9 @@ public class UsersController : JsonApiController { public UsersController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService, ILoggerFactory loggerFactory) - : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) { } } } diff --git a/src/Examples/NoEntityFrameworkExample/Controllers/CustomTodoItemsController.cs b/src/Examples/NoEntityFrameworkExample/Controllers/CustomTodoItemsController.cs index 4e93d26bea..9a593cfa9f 100644 --- a/src/Examples/NoEntityFrameworkExample/Controllers/CustomTodoItemsController.cs +++ b/src/Examples/NoEntityFrameworkExample/Controllers/CustomTodoItemsController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; @@ -11,10 +10,9 @@ public class CustomTodoItemsController : JsonApiController { public CustomTodoItemsController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService, ILoggerFactory loggerFactory) - : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) { } } } diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index 67e5a45107..931a73eaa8 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -1,3 +1,5 @@ +using System; +using System.Reflection; using System.Threading.Tasks; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Extensions; @@ -24,11 +26,9 @@ public class BaseJsonApiController private readonly IDeleteService _delete; private readonly ILogger> _logger; private readonly IJsonApiOptions _jsonApiOptions; - private readonly IResourceGraph _resourceGraph; public BaseJsonApiController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraphManager, IResourceService resourceService, ILoggerFactory loggerFactory) { @@ -41,7 +41,6 @@ public BaseJsonApiController( _logger = new Logger>(new LoggerFactory()); } _jsonApiOptions = jsonApiOptions; - _resourceGraph = resourceGraphManager; _getAll = resourceService; _getById = resourceService; _getRelationship = resourceService; @@ -84,7 +83,6 @@ public BaseJsonApiController( /// public BaseJsonApiController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IGetAllService getAll = null, IGetByIdService getById = null, IGetRelationshipService getRelationship = null, @@ -94,7 +92,6 @@ public BaseJsonApiController( IUpdateRelationshipService updateRelationships = null, IDeleteService delete = null) { - _resourceGraph = resourceGraph; _jsonApiOptions = jsonApiOptions; _getAll = getAll; _getById = getById; @@ -156,9 +153,7 @@ public virtual async Task PostAsync([FromBody] T entity) return Forbidden(); if (_jsonApiOptions.ValidateModelState && !ModelState.IsValid) - { - return UnprocessableEntity(ModelStateExtensions.ConvertToErrorCollection(ModelState, _resourceGraph)); - } + return UnprocessableEntity(ModelStateExtensions.ConvertToErrorCollection(ModelState, GetAssociatedResource())); entity = await _create.CreateAsync(entity); @@ -172,9 +167,7 @@ public virtual async Task PatchAsync(TId id, [FromBody] T entity) return UnprocessableEntity(); if (_jsonApiOptions.ValidateModelState && !ModelState.IsValid) - { - return UnprocessableEntity(ModelStateExtensions.ConvertToErrorCollection(ModelState, _resourceGraph)); - } + return UnprocessableEntity(ModelStateExtensions.ConvertToErrorCollection(ModelState, GetAssociatedResource())); var updatedEntity = await _update.UpdateAsync(id, entity); @@ -199,6 +192,13 @@ public virtual async Task DeleteAsync(TId id) return NotFound(); return NoContent(); } + + internal Type GetAssociatedResource() + { + return GetType().GetMethod(nameof(GetAssociatedResource), BindingFlags.Instance | BindingFlags.NonPublic) + .DeclaringType + .GetGenericArguments()[0]; + } } public class BaseJsonApiController : BaseJsonApiController @@ -218,7 +218,6 @@ public BaseJsonApiController( public BaseJsonApiController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IGetAllService getAll = null, IGetByIdService getById = null, IGetRelationshipService getRelationship = null, @@ -227,6 +226,6 @@ public BaseJsonApiController( IUpdateService update = null, IUpdateRelationshipService updateRelationships = null, IDeleteService delete = null - ) : base(jsonApiOptions, resourceGraph, getAll, getById, getRelationship, getRelationships, create, update, updateRelationships, delete) { } + ) : base(jsonApiOptions, getAll, getById, getRelationship, getRelationships, create, update, updateRelationships, delete) { } } } diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs index b960e94a39..058ef5c313 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs @@ -20,15 +20,13 @@ public class JsonApiController : BaseJsonApiController where T : /// public JsonApiController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService, ILoggerFactory loggerFactory = null) - : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory = null) + : base(jsonApiOptions, resourceService, loggerFactory = null) { } public JsonApiController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IGetAllService getAll = null, IGetByIdService getById = null, IGetRelationshipService getRelationship = null, @@ -37,7 +35,7 @@ public JsonApiController( IUpdateService update = null, IUpdateRelationshipService updateRelationships = null, IDeleteService delete = null - ) : base(jsonApiOptions, resourceGraph, getAll, getById, getRelationship, getRelationships, create, update, updateRelationships, delete) { } + ) : base(jsonApiOptions, getAll, getById, getRelationship, getRelationships, create, update, updateRelationships, delete) { } [HttpGet] public override async Task GetAsync() => await base.GetAsync(); @@ -87,16 +85,14 @@ public class JsonApiController : JsonApiController where T : class, I /// public JsonApiController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService, ILoggerFactory loggerFactory = null ) - : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) { } public JsonApiController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IGetAllService getAll = null, IGetByIdService getById = null, IGetRelationshipService getRelationship = null, @@ -105,7 +101,7 @@ public JsonApiController( IUpdateService update = null, IUpdateRelationshipService updateRelationships = null, IDeleteService delete = null - ) : base(jsonApiOptions, resourceGraph, getAll, getById, getRelationship, getRelationships, create, update, updateRelationships, delete) { } + ) : base(jsonApiOptions, getAll, getById, getRelationship, getRelationships, create, update, updateRelationships, delete) { } } diff --git a/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs b/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs index 587f3749f3..3bbfbbb09d 100644 --- a/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs @@ -1,6 +1,7 @@ using System; +using System.Reflection; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Models; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.EntityFrameworkCore.Internal; @@ -8,7 +9,7 @@ namespace JsonApiDotNetCore.Extensions { public static class ModelStateExtensions { - public static ErrorCollection ConvertToErrorCollection(this ModelStateDictionary modelState, IResourceGraph resourceGraph) + public static ErrorCollection ConvertToErrorCollection(this ModelStateDictionary modelState, Type resourceType) { ErrorCollection collection = new ErrorCollection(); foreach (var entry in modelState) @@ -16,7 +17,8 @@ public static ErrorCollection ConvertToErrorCollection(this ModelStateDiction if (entry.Value.Errors.Any() == false) continue; - var attrName = resourceGraph.GetPublicAttributeName(entry.Key); + var targetedProperty = resourceType.GetProperty(entry.Key); + var attrName = targetedProperty.GetCustomAttribute().PublicAttributeName; foreach (var modelError in entry.Value.Errors) { diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs index 1dfe948a0e..8d93d7fa60 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs @@ -12,25 +12,6 @@ public interface IResourceGraph : IContextEntityProvider { RelationshipAttribute GetInverseRelationship(RelationshipAttribute relationship); - /// - /// Get the internal navigation property name for the specified public - /// relationship name. - /// - /// The public relationship name specified by a or - /// - /// - /// _graph.GetRelationshipName<TodoItem>("achieved-date"); - /// // returns "AchievedDate" - /// - /// - string GetRelationshipName(string relationshipName); - - /// - /// Get the public attribute name for a type based on the internal attribute name. - /// - /// The internal attribute name for a . - string GetPublicAttributeName(string internalAttributeName); - /// /// Was built against an EntityFrameworkCore DbContext ? /// diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index f268caaddb..c2cc543cf8 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -52,25 +52,6 @@ internal ResourceGraph(List entities, bool usesDbContext, List public bool UsesDbContext { get; } - /// - public string GetRelationshipName(string relationshipName) - { - var entityType = typeof(TParent); - return Entities - .SingleOrDefault(e => e.EntityType == entityType) - ?.Relationships -  .SingleOrDefault(r => r.Is(relationshipName)) - ?.InternalRelationshipName; - } - - public string GetPublicAttributeName(string internalAttributeName) - { - return GetContextEntity(typeof(TParent)) - .Attributes - .SingleOrDefault(a => a.InternalAttributeName == internalAttributeName)? - .PublicAttributeName; - } - public RelationshipAttribute GetInverseRelationship(RelationshipAttribute relationship) { if (relationship.InverseNavigation == null) return null; diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasManyAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasManyAttribute.cs index cb1218987e..7a05dd8fe4 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasManyAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasManyAttribute.cs @@ -35,7 +35,6 @@ public HasManyAttribute(string publicName = null, Link relationshipLinks = Link. /// Gets the value of the navigation property, defined by the relationshipName, /// on the provided instance. /// - public override object GetValue(object entity) { return entity?.GetType()? diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs index 2ba9fbcff5..74d9e670ef 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs @@ -92,7 +92,7 @@ public async Task Respond_404_If_EntityDoesNotExist() } [Fact] - public async Task Respond_400_If_IdNotInAttributeList() + public async Task Respond_422_If_IdNotInAttributeList() { // Arrange var maxPersonId = _context.TodoItems.LastOrDefault()?.Id ?? 0; diff --git a/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs b/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs index 14bf06e3d5..557dc12207 100644 --- a/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs +++ b/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs @@ -8,8 +8,7 @@ using JsonApiDotNetCore.Internal; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using JsonApiDotNetCore.Internal.Contracts; -using System.IO; + namespace UnitTests { @@ -19,22 +18,20 @@ public class Resource : Identifiable { [Attr("test-attribute")] public string TestAttribute { get; set; } } - private Mock _resourceGraph = new Mock(); - private Mock _resourceGraphMock = new Mock(); [Fact] public async Task GetAsync_Calls_Service() { // arrange var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, getAll: serviceMock.Object); + var controller = new BaseJsonApiController(new Mock().Object, getAll: serviceMock.Object); // act await controller.GetAsync(); // assert serviceMock.Verify(m => m.GetAsync(), Times.Once); - + } [Fact] @@ -42,7 +39,7 @@ public async Task GetAsync_Throws_405_If_No_Service() { // arrange var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, null); + var controller = new BaseJsonApiController(new Mock().Object, null); // act var exception = await Assert.ThrowsAsync(() => controller.GetAsync()); @@ -57,14 +54,14 @@ public async Task GetAsyncById_Calls_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, getById: serviceMock.Object); + var controller = new BaseJsonApiController(new Mock().Object, getById: serviceMock.Object); // act await controller.GetAsync(id); // assert serviceMock.Verify(m => m.GetAsync(id), Times.Once); - + } [Fact] @@ -73,7 +70,7 @@ public async Task GetAsyncById_Throws_405_If_No_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, getById: null); + var controller = new BaseJsonApiController(new Mock().Object, getById: null); // act var exception = await Assert.ThrowsAsync(() => controller.GetAsync(id)); @@ -88,7 +85,7 @@ public async Task GetRelationshipsAsync_Calls_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, getRelationships: serviceMock.Object); + var controller = new BaseJsonApiController(new Mock().Object, getRelationships: serviceMock.Object); // act await controller.GetRelationshipsAsync(id, string.Empty); @@ -103,7 +100,7 @@ public async Task GetRelationshipsAsync_Throws_405_If_No_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, getRelationships: null); + var controller = new BaseJsonApiController(new Mock().Object, getRelationships: null); // act var exception = await Assert.ThrowsAsync(() => controller.GetRelationshipsAsync(id, string.Empty)); @@ -118,7 +115,7 @@ public async Task GetRelationshipAsync_Calls_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, getRelationship: serviceMock.Object); + var controller = new BaseJsonApiController(new Mock().Object, getRelationship: serviceMock.Object); // act await controller.GetRelationshipAsync(id, string.Empty); @@ -133,7 +130,7 @@ public async Task GetRelationshipAsync_Throws_405_If_No_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, getRelationship: null); + var controller = new BaseJsonApiController(new Mock().Object, getRelationship: null); // act var exception = await Assert.ThrowsAsync(() => controller.GetRelationshipAsync(id, string.Empty)); @@ -150,9 +147,9 @@ public async Task PatchAsync_Calls_Service() var resource = new Resource(); var serviceMock = new Mock>(); //_resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - - var controller = new BaseJsonApiController(new JsonApiOptions(), _resourceGraph.Object, update: serviceMock.Object); + + var controller = new BaseJsonApiController(new JsonApiOptions(), update: serviceMock.Object); // act await controller.PatchAsync(id, resource); @@ -170,7 +167,7 @@ public async Task PatchAsync_ModelStateInvalid_ValidateModelStateDisbled() var serviceMock = new Mock>(); //_resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - var controller = new BaseJsonApiController(new JsonApiOptions(), _resourceGraph.Object, update: serviceMock.Object); + var controller = new BaseJsonApiController(new JsonApiOptions(), update: serviceMock.Object); // act var response = await controller.PatchAsync(id, resource); @@ -187,10 +184,8 @@ public async Task PatchAsync_ModelStateInvalid_ValidateModelStateEnabled() const int id = 0; var resource = new Resource(); var serviceMock = new Mock>(); - _resourceGraphMock.Setup(a => a.GetPublicAttributeName("TestAttribute")).Returns("test-attribute"); -// _resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = true }, _resourceGraph.Object, update: serviceMock.Object); + var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = true }, update: serviceMock.Object); controller.ModelState.AddModelError("TestAttribute", "Failed Validation"); // act @@ -199,7 +194,7 @@ public async Task PatchAsync_ModelStateInvalid_ValidateModelStateEnabled() // assert serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny()), Times.Never); Assert.IsType(response); - Assert.IsType(((UnprocessableEntityObjectResult) response).Value); + Assert.IsType(((UnprocessableEntityObjectResult)response).Value); } [Fact] @@ -208,7 +203,7 @@ public async Task PatchAsync_Throws_405_If_No_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, update: null); + var controller = new BaseJsonApiController(new Mock().Object, update: null); // act var exception = await Assert.ThrowsAsync(() => controller.PatchAsync(id, It.IsAny())); @@ -223,11 +218,11 @@ public async Task PostAsync_Calls_Service() // arrange var resource = new Resource(); var serviceMock = new Mock>(); -// _resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); + // _resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - var controller = new BaseJsonApiController(new JsonApiOptions(), _resourceGraph.Object, create: serviceMock.Object); + var controller = new BaseJsonApiController(new JsonApiOptions(), create: serviceMock.Object); serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); - controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext {HttpContext = new DefaultHttpContext()}; + controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext { HttpContext = new DefaultHttpContext() }; // act await controller.PostAsync(resource); @@ -242,7 +237,7 @@ public async Task PostAsync_ModelStateInvalid_ValidateModelStateDisabled() // arrange var resource = new Resource(); var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = false }, _resourceGraph.Object, create: serviceMock.Object); + var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = false }, create: serviceMock.Object); controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext { HttpContext = new DefaultHttpContext() }; serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); @@ -261,9 +256,8 @@ public async Task PostAsync_ModelStateInvalid_ValidateModelStateEnabled() // arrange var resource = new Resource(); var serviceMock = new Mock>(); - _resourceGraphMock.Setup(a => a.GetPublicAttributeName("TestAttribute")).Returns("test-attribute"); - var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = true }, _resourceGraph.Object, create: serviceMock.Object); - controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext { HttpContext = new DefaultHttpContext() }; + var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = true }, create: serviceMock.Object); + controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }; controller.ModelState.AddModelError("TestAttribute", "Failed Validation"); serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); @@ -284,7 +278,7 @@ public async Task PatchRelationshipsAsync_Calls_Service() const int id = 0; var resource = new Resource(); var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, updateRelationships: serviceMock.Object); + var controller = new BaseJsonApiController(new Mock().Object, updateRelationships: serviceMock.Object); // act await controller.PatchRelationshipsAsync(id, string.Empty, null); @@ -299,7 +293,7 @@ public async Task PatchRelationshipsAsync_Throws_405_If_No_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, updateRelationships: null); + var controller = new BaseJsonApiController(new Mock().Object, updateRelationships: null); // act var exception = await Assert.ThrowsAsync(() => controller.PatchRelationshipsAsync(id, string.Empty, null)); @@ -315,7 +309,7 @@ public async Task DeleteAsync_Calls_Service() const int id = 0; var resource = new Resource(); var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, _resourceGraph.Object, delete: serviceMock.Object); + var controller = new BaseJsonApiController(new Mock().Object, delete: serviceMock.Object); // Act await controller.DeleteAsync(id); @@ -330,9 +324,9 @@ public async Task DeleteAsync_Throws_405_If_No_Service() // arrange const int id = 0; var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, - - _resourceGraph.Object, delete: null); + var controller = new BaseJsonApiController(new Mock().Object, + + delete: null); // act var exception = await Assert.ThrowsAsync(() => controller.DeleteAsync(id)); diff --git a/wiki/v4/content/using JsonApiDotNetCore.Controllers; b/wiki/v4/content/using JsonApiDotNetCore.Controllers; new file mode 100644 index 0000000000..58fbcc748a --- /dev/null +++ b/wiki/v4/content/using JsonApiDotNetCore.Controllers; @@ -0,0 +1,346 @@ +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using Moq; +using Xunit; +using System.Threading.Tasks; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Internal; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using JsonApiDotNetCore.Internal.Contracts; +using System.IO; + +namespace UnitTests +{ + public class BaseJsonApiController_Tests + { + public class Resource : Identifiable + { + [Attr("test-attribute")] public string TestAttribute { get; set; } + } + private Mock _resourceGraph = new Mock(); + private Mock _resourceGraphMock = new Mock(); + + [Fact] + public async Task GetAsync_Calls_Service() + { + // arrange + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, getAll: serviceMock.Object); + + // act + await controller.GetAsync(); + + // assert + serviceMock.Verify(m => m.GetAsync(), Times.Once); + + } + + [Fact] + public async Task GetAsync_Throws_405_If_No_Service() + { + // arrange + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, null); + + // act + var exception = await Assert.ThrowsAsync(() => controller.GetAsync()); + + // assert + Assert.Equal(405, exception.GetStatusCode()); + } + + [Fact] + public async Task GetAsyncById_Calls_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, getById: serviceMock.Object); + + // act + await controller.GetAsync(id); + + // assert + serviceMock.Verify(m => m.GetAsync(id), Times.Once); + + } + + [Fact] + public async Task GetAsyncById_Throws_405_If_No_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, getById: null); + + // act + var exception = await Assert.ThrowsAsync(() => controller.GetAsync(id)); + + // assert + Assert.Equal(405, exception.GetStatusCode()); + } + + [Fact] + public async Task GetRelationshipsAsync_Calls_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, getRelationships: serviceMock.Object); + + // act + await controller.GetRelationshipsAsync(id, string.Empty); + + // assert + serviceMock.Verify(m => m.GetRelationshipsAsync(id, string.Empty), Times.Once); + } + + [Fact] + public async Task GetRelationshipsAsync_Throws_405_If_No_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, getRelationships: null); + + // act + var exception = await Assert.ThrowsAsync(() => controller.GetRelationshipsAsync(id, string.Empty)); + + // assert + Assert.Equal(405, exception.GetStatusCode()); + } + + [Fact] + public async Task GetRelationshipAsync_Calls_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, getRelationship: serviceMock.Object); + + // act + await controller.GetRelationshipAsync(id, string.Empty); + + // assert + serviceMock.Verify(m => m.GetRelationshipAsync(id, string.Empty), Times.Once); + } + + [Fact] + public async Task GetRelationshipAsync_Throws_405_If_No_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, getRelationship: null); + + // act + var exception = await Assert.ThrowsAsync(() => controller.GetRelationshipAsync(id, string.Empty)); + + // assert + Assert.Equal(405, exception.GetStatusCode()); + } + + [Fact] + public async Task PatchAsync_Calls_Service() + { + // arrange + const int id = 0; + var resource = new Resource(); + var serviceMock = new Mock>(); + //_resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); + + + var controller = new BaseJsonApiController(new JsonApiOptions(), update: serviceMock.Object); + + // act + await controller.PatchAsync(id, resource); + + // assert + serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny()), Times.Once); + } + + [Fact] + public async Task PatchAsync_ModelStateInvalid_ValidateModelStateDisbled() + { + // arrange + const int id = 0; + var resource = new Resource(); + var serviceMock = new Mock>(); + //_resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); + + var controller = new BaseJsonApiController(new JsonApiOptions(), update: serviceMock.Object); + + // act + var response = await controller.PatchAsync(id, resource); + + // assert + serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny()), Times.Once); + Assert.IsNotType(response); + } + + [Fact] + public async Task PatchAsync_ModelStateInvalid_ValidateModelStateEnabled() + { + // arrange + const int id = 0; + var resource = new Resource(); + var serviceMock = new Mock>(); + _resourceGraphMock.Setup(a => a.GetPublicAttributeName("TestAttribute")).Returns("test-attribute"); +// _resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); + + var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = true }, update: serviceMock.Object); + controller.ModelState.AddModelError("TestAttribute", "Failed Validation"); + + // act + var response = await controller.PatchAsync(id, resource); + + // assert + serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny()), Times.Never); + Assert.IsType(response); + Assert.IsType(((UnprocessableEntityObjectResult) response).Value); + } + + [Fact] + public async Task PatchAsync_Throws_405_If_No_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, update: null); + + // act + var exception = await Assert.ThrowsAsync(() => controller.PatchAsync(id, It.IsAny())); + + // assert + Assert.Equal(405, exception.GetStatusCode()); + } + + [Fact] + public async Task PostAsync_Calls_Service() + { + // arrange + var resource = new Resource(); + var serviceMock = new Mock>(); +// _resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); + + var controller = new BaseJsonApiController(new JsonApiOptions(), create: serviceMock.Object); + serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); + controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext {HttpContext = new DefaultHttpContext()}; + + // act + await controller.PostAsync(resource); + + // assert + serviceMock.Verify(m => m.CreateAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task PostAsync_ModelStateInvalid_ValidateModelStateDisabled() + { + // arrange + var resource = new Resource(); + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = false }, create: serviceMock.Object); + controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext { HttpContext = new DefaultHttpContext() }; + serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); + + + // act + var response = await controller.PostAsync(resource); + + // assert + serviceMock.Verify(m => m.CreateAsync(It.IsAny()), Times.Once); + Assert.IsNotType(response); + } + + [Fact] + public async Task PostAsync_ModelStateInvalid_ValidateModelStateEnabled() + { + // arrange + var resource = new Resource(); + var serviceMock = new Mock>(); + _resourceGraphMock.Setup(a => a.GetPublicAttributeName("TestAttribute")).Returns("test-attribute"); + var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = true }, create: serviceMock.Object); + controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext { HttpContext = new DefaultHttpContext() }; + controller.ModelState.AddModelError("TestAttribute", "Failed Validation"); + serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); + + + // act + var response = await controller.PostAsync(resource); + + // assert + serviceMock.Verify(m => m.CreateAsync(It.IsAny()), Times.Never); + Assert.IsType(response); + Assert.IsType(((UnprocessableEntityObjectResult)response).Value); + } + + [Fact] + public async Task PatchRelationshipsAsync_Calls_Service() + { + // arrange + const int id = 0; + var resource = new Resource(); + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, updateRelationships: serviceMock.Object); + + // act + await controller.PatchRelationshipsAsync(id, string.Empty, null); + + // assert + serviceMock.Verify(m => m.UpdateRelationshipsAsync(id, string.Empty, null), Times.Once); + } + + [Fact] + public async Task PatchRelationshipsAsync_Throws_405_If_No_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, updateRelationships: null); + + // act + var exception = await Assert.ThrowsAsync(() => controller.PatchRelationshipsAsync(id, string.Empty, null)); + + // assert + Assert.Equal(405, exception.GetStatusCode()); + } + + [Fact] + public async Task DeleteAsync_Calls_Service() + { + // Arrange + const int id = 0; + var resource = new Resource(); + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, delete: serviceMock.Object); + + // Act + await controller.DeleteAsync(id); + + // Assert + serviceMock.Verify(m => m.DeleteAsync(id), Times.Once); + } + + [Fact] + public async Task DeleteAsync_Throws_405_If_No_Service() + { + // arrange + const int id = 0; + var serviceMock = new Mock>(); + var controller = new BaseJsonApiController(new Mock().Object, + + delete: null); + + // act + var exception = await Assert.ThrowsAsync(() => controller.DeleteAsync(id)); + + // assert + Assert.Equal(405, exception.GetStatusCode()); + } + + + } +} From e2c5be09af30ac84709a90498750c3678e569044 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 13:56:46 +0200 Subject: [PATCH 03/29] style: spacing in BaseJsonApiController<,> --- .../Controllers/BaseJsonApiController.cs | 9 +++------ src/JsonApiDotNetCore/Internal/ResourceGraph.cs | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index 931a73eaa8..1b2477dc79 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -115,18 +115,15 @@ public virtual async Task GetAsync(TId id) if (_getById == null) throw Exceptions.UnSupportedRequestMethod; var entity = await _getById.GetAsync(id); if (entity == null) - { return NotFound(); - } + return Ok(entity); } public virtual async Task GetRelationshipsAsync(TId id, string relationshipName) { if (_getRelationships == null) - { throw Exceptions.UnSupportedRequestMethod; - } var relationship = await _getRelationships.GetRelationshipsAsync(id, relationshipName); if (relationship == null) return NotFound(); @@ -153,7 +150,7 @@ public virtual async Task PostAsync([FromBody] T entity) return Forbidden(); if (_jsonApiOptions.ValidateModelState && !ModelState.IsValid) - return UnprocessableEntity(ModelStateExtensions.ConvertToErrorCollection(ModelState, GetAssociatedResource())); + return UnprocessableEntity(ModelState.ConvertToErrorCollection(GetAssociatedResource())); entity = await _create.CreateAsync(entity); @@ -167,7 +164,7 @@ public virtual async Task PatchAsync(TId id, [FromBody] T entity) return UnprocessableEntity(); if (_jsonApiOptions.ValidateModelState && !ModelState.IsValid) - return UnprocessableEntity(ModelStateExtensions.ConvertToErrorCollection(ModelState, GetAssociatedResource())); + return UnprocessableEntity(ModelState.ConvertToErrorCollection(GetAssociatedResource())); var updatedEntity = await _update.UpdateAsync(id, entity); diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index c2cc543cf8..d06f998997 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using JsonApiDotNetCore.Internal.Contracts; From baa77eedb158ce099f18a357c8ef38a1ad74c02e Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 15:24:57 +0200 Subject: [PATCH 04/29] feat: remove GetEntityFromControllerName from ResourceGraph --- .../Controllers/TodoItemsCustomController.cs | 19 +++++--- .../IServiceCollectionExtensions.cs | 4 +- .../Internal/Contracts/IResourceGraph.cs | 2 - .../Internal/DefaultRoutingConvention.cs | 45 ++++++++++++++++--- .../Internal/IJsonApiRoutingConvention.cs | 8 +++- .../Internal/ResourceGraph.cs | 21 --------- .../Middleware/RequestMiddleware.cs | 22 ++++----- .../BaseJsonApiController_Tests.cs | 5 --- 8 files changed, 72 insertions(+), 54 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs index bc174f102a..c4913689e4 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; @@ -13,9 +14,10 @@ namespace JsonApiDotNetCoreExample.Controllers public class TodoItemsCustomController : CustomJsonApiController { public TodoItemsCustomController( + IJsonApiOptions options, IResourceService resourceService, ILoggerFactory loggerFactory) - : base(resourceService, loggerFactory) + : base(options, resourceService, loggerFactory) { } } @@ -23,16 +25,19 @@ public class CustomJsonApiController : CustomJsonApiController where T : class, IIdentifiable { public CustomJsonApiController( + IJsonApiOptions options, IResourceService resourceService, ILoggerFactory loggerFactory) - : base(resourceService, loggerFactory) - { } + : base(options, resourceService, loggerFactory) + { + } } public class CustomJsonApiController : ControllerBase where T : class, IIdentifiable { private readonly ILogger _logger; + private readonly IJsonApiOptions _options; private readonly IResourceService _resourceService; protected IActionResult Forbidden() @@ -41,11 +46,13 @@ protected IActionResult Forbidden() } public CustomJsonApiController( + IJsonApiOptions options, IResourceService resourceService, ILoggerFactory loggerFactory) { + _options = options; _resourceService = resourceService; - _logger = loggerFactory.CreateLogger>(); + _logger = loggerFactory.CreateLogger>(); } public CustomJsonApiController( @@ -95,8 +102,8 @@ public virtual async Task PostAsync([FromBody] T entity) if (entity == null) return UnprocessableEntity(); - //if (!_jsonApiContext.Options.AllowClientGeneratedIds && !string.IsNullOrEmpty(entity.StringId)) - // return Forbidden(); + if (_options.AllowClientGeneratedIds && !string.IsNullOrEmpty(entity.StringId)) + return Forbidden(); entity = await _resourceService.CreateAsync(entity); diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index c85176c7e4..d65a690124 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -121,7 +121,9 @@ private static void ConfigureMvc(IServiceCollection services, IMvcCoreBuilder mv // register services that allow user to override behaviour that is configured on startup, like routing conventions AddStartupConfigurationServices(services, options); var intermediateProvider = services.BuildServiceProvider(); - mvcBuilder.AddMvcOptions(opt => opt.Conventions.Insert(0, intermediateProvider.GetRequiredService())); + var routingConvention = intermediateProvider.GetRequiredService(); + mvcBuilder.AddMvcOptions(opt => opt.Conventions.Insert(0, routingConvention)); + services.AddSingleton(routingConvention); } private static void AddMvcOptions(MvcOptions options, JsonApiOptions config) diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs index 8d93d7fa60..427ca11366 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs @@ -16,7 +16,5 @@ public interface IResourceGraph : IContextEntityProvider /// Was built against an EntityFrameworkCore DbContext ? /// bool UsesDbContext { get; } - - ContextEntity GetEntityFromControllerName(string pathParsed); } } diff --git a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs index 3335ca87aa..e61f0d3134 100644 --- a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs @@ -6,7 +6,10 @@ using System.Reflection; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Graph; +using JsonApiDotNetCore.Models; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; namespace JsonApiDotNetCore.Internal @@ -37,17 +40,29 @@ public class DefaultRoutingConvention : IJsonApiRoutingConvention private readonly string _namespace; private readonly IResourceNameFormatter _formatter; private readonly HashSet _registeredTemplates = new HashSet(); + private readonly Dictionary _registeredResources = new Dictionary(); public DefaultRoutingConvention(IJsonApiOptions options, IResourceNameFormatter formatter) { _namespace = options.Namespace; _formatter = formatter; } + /// + public Type GetAssociatedResource(string controllerName) + { + _registeredResources.TryGetValue(controllerName, out Type type); + return type; + } + /// public void Apply(ApplicationModel application) { foreach (var controller in application.Controllers) { + var resourceType = GetResourceTypeFromController(controller.ControllerType); + if (resourceType != null) + _registeredResources.Add(controller.ControllerName, resourceType); + if (RoutingConventionDisabled(controller) == false) continue; @@ -74,12 +89,12 @@ private bool RoutingConventionDisabled(ControllerModel controller) /// private string TemplateFromResource(ControllerModel model) { - var resourceType = GetResourceTypeFromController(model.ControllerType); - if (resourceType != null) + if (_registeredResources.TryGetValue(model.ControllerName, out Type resourceType)) { var template = $"{_namespace}/{_formatter.FormatResourceName(resourceType)}"; - if (_registeredTemplates.Add(template)) + if (_registeredTemplates.Add(template)) return template; + } return null; } @@ -100,12 +115,30 @@ private string TemplateFromController(ControllerModel model) /// private Type GetResourceTypeFromController(Type type) { + var controllerBase = typeof(ControllerBase); + var jsonApiMixin = typeof(JsonApiControllerMixin); var target = typeof(BaseJsonApiController<,>); - var currentBaseType = type.BaseType; + var identifiable = typeof(IIdentifiable); + var currentBaseType = type; + if (type.Name.Contains("TodoItemsCustom")) + { + var x = 123; + } + while (!currentBaseType.IsGenericType || currentBaseType.GetGenericTypeDefinition() != target) { - currentBaseType = currentBaseType.BaseType; - if (currentBaseType == null) break; + var nextBaseType = currentBaseType.BaseType; + + if ( (nextBaseType == controllerBase || nextBaseType == jsonApiMixin) && currentBaseType.IsGenericType) + { + var potentialResource = currentBaseType.GetGenericArguments().FirstOrDefault(t => t.Inherits(identifiable)); + if (potentialResource != null) + return potentialResource; + } + + currentBaseType = nextBaseType; + if (nextBaseType == null) + break; } return currentBaseType?.GetGenericArguments().First(); } diff --git a/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs index aba03b806b..223310e247 100644 --- a/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc.ApplicationModels; +using System; +using Microsoft.AspNetCore.Mvc.ApplicationModels; namespace JsonApiDotNetCore.Internal { @@ -6,5 +7,8 @@ namespace JsonApiDotNetCore.Internal /// Service for specifying which routing convention to use. This can be overriden to customize /// the relation between controllers and mapped routes. /// - public interface IJsonApiRoutingConvention : IApplicationModelConvention { } + public interface IJsonApiRoutingConvention : IApplicationModelConvention + { + Type GetAssociatedResource(string controllerName); + } } diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index d06f998997..7f1f61c4db 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -35,10 +35,6 @@ public ResourceGraph(List entities, bool usesDbContext) Instance = this; } - // eventually, this is the planned public constructor - // to avoid breaking changes, we will be leaving the original constructor in place - // until the context graph validation process is completed - // you can track progress on this issue here: https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/170 internal ResourceGraph(List entities, bool usesDbContext, List validationResults, List controllerContexts) { ControllerResourceMap = controllerContexts; @@ -57,23 +53,6 @@ public RelationshipAttribute GetInverseRelationship(RelationshipAttribute relati return GetContextEntity(relationship.DependentType).Relationships.SingleOrDefault(r => r.InternalRelationshipName == relationship.InverseNavigation); } - public ContextEntity GetEntityFromControllerName(string controllerName) - { - - if (ControllerResourceMap.Any()) - { - // Autodiscovery was used, so there is a well defined mapping between exposed resources and their associated controllers - var resourceType = ControllerResourceMap.FirstOrDefault(cm => cm.ControllerName == controllerName)?.Resource; - if (resourceType == null) return null; - return Entities.First(e => e.EntityType == resourceType); - - } else - { - // No autodiscovery: try to guess contextentity from controller name. - return Entities.FirstOrDefault(e => e.EntityName.ToLower().Replace("-", "") == controllerName.ToLower()); - } - } - /// public ContextEntity GetContextEntity(string entityName) => Entities.SingleOrDefault(e => string.Equals(e.EntityName, entityName, StringComparison.OrdinalIgnoreCase)); diff --git a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs index 53ace47531..510627cc23 100644 --- a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs @@ -21,8 +21,9 @@ public class CurrentRequestMiddleware private readonly RequestDelegate _next; private HttpContext _httpContext; private ICurrentRequest _currentRequest; - private IResourceGraph _resourceGraph; + private IContextEntityProvider _contextEntityProvider; private IJsonApiOptions _options; + private IJsonApiRoutingConvention _routingConvention; public CurrentRequestMiddleware(RequestDelegate next) { @@ -30,13 +31,15 @@ public CurrentRequestMiddleware(RequestDelegate next) } public async Task Invoke(HttpContext httpContext, + IJsonApiRoutingConvention routingConvention, IJsonApiOptions options, ICurrentRequest currentRequest, - IResourceGraph resourceGraph) + IContextEntityProvider contextEntityProvider) { _httpContext = httpContext; _currentRequest = currentRequest; - _resourceGraph = resourceGraph; + _routingConvention = routingConvention; + _contextEntityProvider = contextEntityProvider; _options = options; var requestResource = GetCurrentEntity(); if (requestResource != null) @@ -60,10 +63,7 @@ private string GetBasePath(string entityName) { return GetNamespaceFromPath(r.Path, entityName); } - else - { - return $"{r.Scheme}://{r.Host}{GetNamespaceFromPath(r.Path, entityName)}"; - } + return $"{r.Scheme}://{r.Host}{GetNamespaceFromPath(r.Path, entityName)}"; } internal static string GetNamespaceFromPath(string path, string entityName) { @@ -162,15 +162,15 @@ private void FlushResponse(HttpContext context, int statusCode) /// /// Gets the current entity that we need for serialization and deserialization. /// - /// - /// /// private ContextEntity GetCurrentEntity() { var controllerName = (string)_httpContext.GetRouteData().Values["controller"]; + var resourceType = _routingConvention.GetAssociatedResource(controllerName); + var requestResource = _contextEntityProvider.GetContextEntity(resourceType); + if (requestResource == null) + return requestResource; var rd = _httpContext.GetRouteData().Values; - var requestResource = _resourceGraph.GetEntityFromControllerName(controllerName); - if (rd.TryGetValue("relationshipName", out object relationshipName)) _currentRequest.RequestRelationship = requestResource.Relationships.Single(r => r.PublicRelationshipName == (string)relationshipName); return requestResource; diff --git a/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs b/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs index 557dc12207..f33ae0aa30 100644 --- a/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs +++ b/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs @@ -146,8 +146,6 @@ public async Task PatchAsync_Calls_Service() const int id = 0; var resource = new Resource(); var serviceMock = new Mock>(); - //_resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - var controller = new BaseJsonApiController(new JsonApiOptions(), update: serviceMock.Object); @@ -165,8 +163,6 @@ public async Task PatchAsync_ModelStateInvalid_ValidateModelStateDisbled() const int id = 0; var resource = new Resource(); var serviceMock = new Mock>(); - //_resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - var controller = new BaseJsonApiController(new JsonApiOptions(), update: serviceMock.Object); // act @@ -218,7 +214,6 @@ public async Task PostAsync_Calls_Service() // arrange var resource = new Resource(); var serviceMock = new Mock>(); - // _resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); var controller = new BaseJsonApiController(new JsonApiOptions(), create: serviceMock.Object); serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); From 85ee717c9b4d4d5956480bd5177bfdd039a76e03 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 15:38:05 +0200 Subject: [PATCH 05/29] feat: remove GetInverseRelationship from ResourceGraph --- .../Hooks/Execution/HookExecutorHelper.cs | 9 ++---- .../Hooks/ResourceHookExecutor.cs | 10 +++---- .../Contracts/IContextEntityProvider.cs | 5 ++++ .../Internal/Contracts/IResourceGraph.cs | 5 ---- .../Internal/InverseRelationships.cs | 27 +++++++++++++---- .../Internal/ResourceGraph.cs | 7 ++--- .../ResourceHooks/ResourceHooksTestsSetup.cs | 29 +++++++++---------- 7 files changed, 50 insertions(+), 42 deletions(-) diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index ce8d1dd138..b92619b691 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -22,20 +22,15 @@ internal class HookExecutorHelper : IHookExecutorHelper private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); private readonly IJsonApiOptions _options; protected readonly IGenericProcessorFactory _genericProcessorFactory; - protected readonly IResourceGraph _graph; protected readonly Dictionary _hookContainers; protected readonly Dictionary _hookDiscoveries; protected readonly List _targetedHooksForRelatedEntities; - public HookExecutorHelper( - IGenericProcessorFactory genericProcessorFactory, - IResourceGraph graph, - IJsonApiOptions options - ) + public HookExecutorHelper(IGenericProcessorFactory genericProcessorFactory, + IJsonApiOptions options) { _options = options; _genericProcessorFactory = genericProcessorFactory; - _graph = graph; _hookContainers = new Dictionary(); _hookDiscoveries = new Dictionary(); _targetedHooksForRelatedEntities = new List(); diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index dda22e0762..da53d991ef 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -21,19 +21,19 @@ internal class ResourceHookExecutor : IResourceHookExecutor private readonly ITraversalHelper _traversalHelper; private readonly IIncludeService _includeService; private readonly ITargetedFields _targetedFields; - private readonly IResourceGraph _graph; + private readonly IInverseRelationships _inverseRelationships; public ResourceHookExecutor( IHookExecutorHelper executorHelper, ITraversalHelper traversalHelper, ITargetedFields targetedFields, IIncludeService includedRelationships, - IResourceGraph resourceGraph) + IInverseRelationships inverseRelationships) { _executorHelper = executorHelper; _traversalHelper = traversalHelper; _targetedFields = targetedFields; _includeService = includedRelationships; - _graph = resourceGraph; + _inverseRelationships = inverseRelationships; } /// @@ -324,7 +324,7 @@ Dictionary ReplaceKeysWithInverseRelationshi /// If it isn't, JADNC currently knows nothing about this relationship pointing back, and it /// currently cannot fire hooks for entities resolved through inverse relationships. var inversableRelationshipAttributes = entitiesByRelationship.Where(kvp => kvp.Key.InverseNavigation != null); - return inversableRelationshipAttributes.ToDictionary(kvp => _graph.GetInverseRelationship(kvp.Key), kvp => kvp.Value); + return inversableRelationshipAttributes.ToDictionary(kvp => _inverseRelationships.GetInverse(kvp.Key), kvp => kvp.Value); } /// @@ -337,7 +337,7 @@ void FireForAffectedImplicits(Type entityTypeToInclude, Dictionary _graph.GetInverseRelationship(kvp.Key), kvp => kvp.Value); + var inverse = implicitAffected.ToDictionary(kvp => _inverseRelationships.GetInverse(kvp.Key), kvp => kvp.Value); var resourcesByRelationship = CreateRelationshipHelper(entityTypeToInclude, inverse); CallHook(container, ResourceHook.BeforeImplicitUpdateRelationship, new object[] { resourcesByRelationship, pipeline, }); } diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs index 46782d8d19..fe88db72b3 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs @@ -8,6 +8,11 @@ namespace JsonApiDotNetCore.Internal.Contracts /// public interface IContextEntityProvider { + /// + /// Gets all registered context entities + /// + ContextEntity[] GetContextEntities(); + /// /// Get the resource metadata by the DbSet property name /// diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs index 427ca11366..e07a856b0b 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs @@ -4,14 +4,9 @@ namespace JsonApiDotNetCore.Internal.Contracts { /// /// A cache for the models in entity core - /// TODO: separate context entity getting part from relationship resolving part. - /// These are two deviating responsibilities that often do not need to be exposed - /// at the same time. /// public interface IResourceGraph : IContextEntityProvider { - RelationshipAttribute GetInverseRelationship(RelationshipAttribute relationship); - /// /// Was built against an EntityFrameworkCore DbContext ? /// diff --git a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs index fbfd4b9da7..560fd50d0d 100644 --- a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs +++ b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; @@ -24,20 +25,37 @@ public interface IInverseRelationships /// deal with resolving the inverse relationships. /// void Resolve(); + + + /// + /// Traverses the resource graph for the inverse relationship of the provided + /// ; + /// + /// + RelationshipAttribute GetInverse(RelationshipAttribute relationship); } /// public class InverseRelationships : IInverseRelationships { - private readonly ResourceGraph _graph; + private readonly IContextEntityProvider _provider; private readonly IDbContextResolver _resolver; - public InverseRelationships(IResourceGraph graph, IDbContextResolver resolver = null) + public InverseRelationships(IContextEntityProvider provider, IDbContextResolver resolver = null) { - _graph = (ResourceGraph)graph; + _provider = (ResourceGraph)provider; _resolver = resolver; } + /// + public RelationshipAttribute GetInverse(RelationshipAttribute relationship) + { + if (relationship.InverseNavigation == null) return null; + return _provider.GetContextEntity(relationship.DependentType) + .Relationships + .SingleOrDefault(r => r.InternalRelationshipName == relationship.InverseNavigation); + } + /// public void Resolve() { @@ -45,7 +63,7 @@ public void Resolve() { DbContext context = _resolver.GetContext(); - foreach (ContextEntity ce in _graph.Entities) + foreach (ContextEntity ce in _provider.GetContextEntities()) { IEntityType meta = context.Model.FindEntityType(ce.EntityType); if (meta == null) continue; @@ -63,7 +81,6 @@ public void Resolve() /// If EF Core is not being used, we're expecting the resolver to not be registered. /// /// true, if entity framework core was enabled, false otherwise. - /// Resolver. private bool EntityFrameworkCoreIsEnabled() { return _resolver != null; diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index 7f1f61c4db..c29c27dd01 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -47,11 +47,8 @@ internal ResourceGraph(List entities, bool usesDbContext, List public bool UsesDbContext { get; } - public RelationshipAttribute GetInverseRelationship(RelationshipAttribute relationship) - { - if (relationship.InverseNavigation == null) return null; - return GetContextEntity(relationship.DependentType).Relationships.SingleOrDefault(r => r.InternalRelationshipName == relationship.InverseNavigation); - } + /// + public ContextEntity[] GetContextEntities() => Entities.ToArray(); /// public ContextEntity GetContextEntity(string entityName) diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 8fe8cfe346..cba0435a60 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -142,14 +142,13 @@ protected List CreateTodoWithOwner() public class HooksTestsSetup : HooksDummyData { - (IResourceGraph, Mock, Mock, Mock, IJsonApiOptions) CreateMocks() + (IInverseRelationships, Mock, Mock, Mock, IJsonApiOptions) CreateMocks() { var pfMock = new Mock(); - var graph = _graph; var ufMock = new Mock(); var iqsMock = new Mock(); var optionsMock = new JsonApiOptions { LoaDatabaseValues = false }; - return (graph, ufMock, iqsMock, pfMock, optionsMock); + return (new InverseRelationships(_graph), ufMock, iqsMock, pfMock, optionsMock); } internal (Mock, ResourceHookExecutor, Mock>) CreateTestObjects(IHooksDiscovery mainDiscovery = null) @@ -159,13 +158,13 @@ public class HooksTestsSetup : HooksDummyData var mainResource = CreateResourceDefinition(mainDiscovery); // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. - var (graph, ufMock, iqMock, gpfMock, options) = CreateMocks(); + var (inverse, ufMock, iqMock, gpfMock, options) = CreateMocks(); SetupProcessorFactoryForResourceDefinition(gpfMock, mainResource.Object, mainDiscovery, null); - var execHelper = new HookExecutorHelper(gpfMock.Object, graph, options); - var traversalHelper = new TraversalHelper(graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, graph); + var execHelper = new HookExecutorHelper(gpfMock.Object, options); + var traversalHelper = new TraversalHelper(_graph, ufMock.Object); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, inverse); return (iqMock, hookExecutor, mainResource); } @@ -184,16 +183,16 @@ public class HooksTestsSetup : HooksDummyData var nestedResource = CreateResourceDefinition(nestedDiscovery); // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. - var (graph, ufMock, iqMock, gpfMock, options) = CreateMocks(); + var (inverse, ufMock, iqMock, gpfMock, options) = CreateMocks(); var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; SetupProcessorFactoryForResourceDefinition(gpfMock, mainResource.Object, mainDiscovery, dbContext); SetupProcessorFactoryForResourceDefinition(gpfMock, nestedResource.Object, nestedDiscovery, dbContext); - var execHelper = new HookExecutorHelper(gpfMock.Object, graph, options); - var traversalHelper = new TraversalHelper(graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, graph); + var execHelper = new HookExecutorHelper(gpfMock.Object, options); + var traversalHelper = new TraversalHelper(_graph, ufMock.Object); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, inverse); return (iqMock, ufMock, hookExecutor, mainResource, nestedResource); } @@ -215,7 +214,7 @@ public class HooksTestsSetup : HooksDummyData var secondNestedResource = CreateResourceDefinition(secondNestedDiscovery); // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. - var (graph, ufMock, iqMock, gpfMock, options) = CreateMocks(); + var (inverse, ufMock, iqMock, gpfMock, options) = CreateMocks(); var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; @@ -223,9 +222,9 @@ public class HooksTestsSetup : HooksDummyData SetupProcessorFactoryForResourceDefinition(gpfMock, firstNestedResource.Object, firstNestedDiscovery, dbContext); SetupProcessorFactoryForResourceDefinition(gpfMock, secondNestedResource.Object, secondNestedDiscovery, dbContext); - var execHelper = new HookExecutorHelper(gpfMock.Object, graph, options); - var traversalHelper = new TraversalHelper(graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, graph); + var execHelper = new HookExecutorHelper(gpfMock.Object, options); + var traversalHelper = new TraversalHelper(_graph, ufMock.Object); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, inverse); return (iqMock, hookExecutor, mainResource, firstNestedResource, secondNestedResource); } From a352f0893ccf0719be1a51103ddecca2d2dacb87 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 18:23:26 +0200 Subject: [PATCH 06/29] feat: decouples UsesDbContext from ResourceGraph and introduces decoupled application instantation --- .../JsonApiDotNetCoreExample/Startup.cs | 18 +- .../NoEntityFrameworkExample/Startup.cs | 11 +- .../Builders/IResourceGraphBuilder.cs | 11 +- .../Builders/JsonApiApplicationBuilder.cs | 207 ++++++++++++++ .../Builders/ResourceGraphBuilder.cs | 41 +-- .../Configuration/IJsonApiOptions.cs | 1 - .../Configuration/JsonApiOptions.cs | 27 -- .../IServiceCollectionExtensions.cs | 266 +++--------------- .../Graph/IServiceDiscoveryFacade.cs | 10 + .../Graph/ServiceDiscoveryFacade.cs | 35 +-- .../Middleware/JsonApiExceptionFilter.cs | 17 +- .../Middleware/TypeMatchFilter.cs | 19 +- .../Startups/ClientGeneratedIdsStartup.cs | 2 +- .../Builders/ContextGraphBuilder_Tests.cs | 8 +- .../IServiceCollectionExtensionsTests.cs | 6 +- 15 files changed, 310 insertions(+), 369 deletions(-) mode change 100755 => 100644 src/Examples/NoEntityFrameworkExample/Startup.cs create mode 100644 src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs create mode 100644 src/JsonApiDotNetCore/Graph/IServiceDiscoveryFacade.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startup.cs index a784de13f6..2963a33ffa 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startup.cs @@ -32,14 +32,16 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services) services .AddSingleton(loggerFactory) .AddDbContext(options => options.UseNpgsql(GetDbConnectionString()), ServiceLifetime.Transient) - .AddJsonApi(options => { - options.Namespace = "api/v1"; - options.DefaultPageSize = 5; - options.IncludeTotalRecordCount = true; - options.EnableResourceHooks = true; - options.LoaDatabaseValues = true; - }, - discovery => discovery.AddCurrentAssembly()); + .AddJsonApi( + options => + { + options.Namespace = "api/v1"; + options.DefaultPageSize = 5; + options.IncludeTotalRecordCount = true; + options.EnableResourceHooks = true; + options.LoaDatabaseValues = true; + }, + discovery => discovery.AddCurrentAssembly()); return services.BuildServiceProvider(); } diff --git a/src/Examples/NoEntityFrameworkExample/Startup.cs b/src/Examples/NoEntityFrameworkExample/Startup.cs old mode 100755 new mode 100644 index d42c44fa42..b83084e87a --- a/src/Examples/NoEntityFrameworkExample/Startup.cs +++ b/src/Examples/NoEntityFrameworkExample/Startup.cs @@ -33,12 +33,11 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services) // Add framework services. var mvcBuilder = services.AddMvcCore(); - services.AddJsonApi(options => { - options.Namespace = "api/v1"; - options.BuildResourceGraph((builder) => { - builder.AddResource("custom-todo-items"); - }); - }, mvcBuilder); + services.AddJsonApi( + options => options.Namespace = "api/v1", + resources: resources => resources.AddResource("custom-todo-items"), + mvcBuilder: mvcBuilder + ); services.AddScoped, TodoItemService>(); diff --git a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs index d03d4d3eab..116cd387bd 100644 --- a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs @@ -26,7 +26,6 @@ public interface IResourceGraphBuilder /// IResourceGraphBuilder AddResource(string pluralizedTypeName = null) where TResource : class, IIdentifiable; - IResourceGraphBuilder AddControllerPairing(Type controller, Type model = null); /// /// Add a json:api resource @@ -57,14 +56,6 @@ public interface IResourceGraphBuilder /// that also implement /// /// The implementation type. - IResourceGraphBuilder AddDbContext() where T : DbContext; - - /// - /// Specify the used to format resource names. - /// - /// Formatter used to define exposed resource names by convention. - IResourceGraphBuilder UseNameFormatter(IResourceNameFormatter resourceNameFormatter); - - + IResourceGraphBuilder AddDbContext() where T : DbContext; } } diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs new file mode 100644 index 0000000000..3291f4921b --- /dev/null +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Data; +using JsonApiDotNetCore.Formatters; +using JsonApiDotNetCore.Graph; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Generics; +using JsonApiDotNetCore.Managers; +using JsonApiDotNetCore.Managers.Contracts; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Serialization; +using JsonApiDotNetCore.Hooks; +using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Query; +using JsonApiDotNetCore.Serialization.Server.Builders; +using JsonApiDotNetCore.Serialization.Server; +using JsonApiDotNetCore.Serialization.Client; +using Microsoft.Extensions.DependencyInjection.Extensions; +using JsonApiDotNetCore.Builders; + +namespace JsonApiDotNetCore.Builders +{ + public class JsonApiApplicationBuilder + { + public readonly JsonApiOptions JsonApiOptions = new JsonApiOptions(); + private IResourceGraphBuilder _resourceGraphBuilder; + private IServiceDiscoveryFacade _serviceDiscoveryFacade; + private bool _usesDbContext; + private readonly IServiceCollection _services; + private readonly IMvcCoreBuilder _mvcBuilder; + + public JsonApiApplicationBuilder(IServiceCollection services, IMvcCoreBuilder mvcBuilder) + { + _services = services; + _mvcBuilder = mvcBuilder; + } + + + public void ConfigureJsonApiOptions(Action configureOptions) => configureOptions(JsonApiOptions); + + public void ConfigureMvc() + { + RegisterJsonApiStartupServices(); + + var intermediateProvider = _services.BuildServiceProvider(); + _resourceGraphBuilder = intermediateProvider.GetRequiredService(); + _serviceDiscoveryFacade = intermediateProvider.GetRequiredService(); + var exceptionFilterProvider = intermediateProvider.GetRequiredService(); + var typeMatchFilterProvider = intermediateProvider.GetRequiredService(); + + _mvcBuilder.AddMvcOptions(mvcOptions => + { + mvcOptions.Filters.Add(exceptionFilterProvider.Get()); + mvcOptions.Filters.Add(typeMatchFilterProvider.Get()); + mvcOptions.InputFormatters.Insert(0, new JsonApiInputFormatter()); + mvcOptions.OutputFormatters.Insert(0, new JsonApiOutputFormatter()); + }); + + var routingConvention = intermediateProvider.GetRequiredService(); + _mvcBuilder.AddMvcOptions(opt => opt.Conventions.Insert(0, routingConvention)); + _services.AddSingleton(routingConvention); // <--- why is this needed? + } + + public void AutoDiscover(Action autoDiscover) + { + autoDiscover(_serviceDiscoveryFacade); + } + + public void ConfigureResources(Action resourceGraphBuilder) + { + resourceGraphBuilder(_resourceGraphBuilder); + } + + public void ConfigureResources(Action resourceGraphBuilder) where TContext : DbContext + { + _resourceGraphBuilder.AddDbContext(); + _usesDbContext = true; + _services.AddScoped>(); + resourceGraphBuilder?.Invoke(_resourceGraphBuilder); + } + + private void RegisterJsonApiStartupServices() + { + _services.AddSingleton(JsonApiOptions); + _services.TryAddSingleton(new KebabCaseFormatter()); + _services.TryAddSingleton(); + _services.TryAddSingleton(); + _services.TryAddSingleton(sp => new ServiceDiscoveryFacade(_services, sp.GetRequiredService())); + _services.TryAddScoped(); + _services.TryAddScoped(); + } + + public void ConfigureServices() + { + var graph = _resourceGraphBuilder.Build(); + + if (!_usesDbContext) + { + _services.AddScoped(); + _services.AddSingleton(new DbContextOptionsBuilder().Options); + } + + _services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>)); + _services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>)); + + _services.AddScoped(typeof(IEntityReadRepository<,>), typeof(DefaultEntityRepository<,>)); + _services.AddScoped(typeof(IEntityWriteRepository<,>), typeof(DefaultEntityRepository<,>)); + + _services.AddScoped(typeof(ICreateService<>), typeof(EntityResourceService<>)); + _services.AddScoped(typeof(ICreateService<,>), typeof(EntityResourceService<,>)); + + _services.AddScoped(typeof(IGetAllService<>), typeof(EntityResourceService<>)); + _services.AddScoped(typeof(IGetAllService<,>), typeof(EntityResourceService<,>)); + + _services.AddScoped(typeof(IGetByIdService<>), typeof(EntityResourceService<>)); + _services.AddScoped(typeof(IGetByIdService<,>), typeof(EntityResourceService<,>)); + + _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<>)); + _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<,>)); + + _services.AddScoped(typeof(IUpdateService<>), typeof(EntityResourceService<>)); + _services.AddScoped(typeof(IUpdateService<,>), typeof(EntityResourceService<,>)); + + _services.AddScoped(typeof(IDeleteService<>), typeof(EntityResourceService<>)); + _services.AddScoped(typeof(IDeleteService<,>), typeof(EntityResourceService<,>)); + + _services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>)); + _services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>)); + + _services.AddSingleton(JsonApiOptions); + _services.AddSingleton(graph); + _services.AddSingleton(); + _services.AddSingleton(graph); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(typeof(GenericProcessor<>)); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + + AddServerSerialization(); + AddQueryParameterServices(); + if (JsonApiOptions.EnableResourceHooks) + AddResourceHooks(); + + _services.AddScoped(); + } + + + private void AddQueryParameterServices() + { + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + + _services.AddScoped(sp => sp.GetService()); + _services.AddScoped(sp => sp.GetService()); + _services.AddScoped(sp => sp.GetService()); + _services.AddScoped(sp => sp.GetService()); + _services.AddScoped(sp => sp.GetService()); + _services.AddScoped(sp => sp.GetService()); + _services.AddScoped(sp => sp.GetService()); + } + + + private void AddResourceHooks() + { + _services.AddSingleton(typeof(IHooksDiscovery<>), typeof(HooksDiscovery<>)); + _services.AddScoped(typeof(IResourceHookContainer<>), typeof(ResourceDefinition<>)); + _services.AddTransient(typeof(IResourceHookExecutor), typeof(ResourceHookExecutor)); + _services.AddTransient(); + _services.AddTransient(); + } + + private void AddServerSerialization() + { + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(); + _services.AddScoped(typeof(IMetaBuilder<>), typeof(MetaBuilder<>)); + _services.AddScoped(typeof(ResponseSerializer<>)); + _services.AddScoped(sp => sp.GetRequiredService().GetSerializer()); + _services.AddScoped(); + } + } +} diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index 21b3058233..1878599d03 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -17,16 +17,18 @@ namespace JsonApiDotNetCore.Builders { public class ResourceGraphBuilder : IResourceGraphBuilder { - private List _entities = new List(); - private List _validationResults = new List(); - private Dictionary> _controllerMapper = new Dictionary>() { }; - private List _undefinedMapper = new List() { }; + private readonly List _entities = new List(); + private readonly List _validationResults = new List(); + private readonly Dictionary> _controllerMapper = new Dictionary>() { }; + private readonly List _undefinedMapper = new List() { }; private bool _usesDbContext; - private IResourceNameFormatter _resourceNameFormatter; + private readonly IResourceNameFormatter _resourceNameFormatter = new KebabCaseFormatter(); - public ResourceGraphBuilder(IResourceNameFormatter formatter = null) + public ResourceGraphBuilder() { } + + public ResourceGraphBuilder(IResourceNameFormatter formatter) { - _resourceNameFormatter = formatter ?? new KebabCaseFormatter(); + _resourceNameFormatter = formatter; } /// @@ -263,30 +265,5 @@ private void AssertEntityIsNotAlreadyDefined(Type entityType) if (_entities.Any(e => e.EntityType == entityType)) throw new InvalidOperationException($"Cannot add entity type {entityType} to context graph, there is already an entity of that type configured."); } - - /// - public IResourceGraphBuilder UseNameFormatter(IResourceNameFormatter resourceNameFormatter) - { - _resourceNameFormatter = resourceNameFormatter; - return this; - } - - public IResourceGraphBuilder AddControllerPairing(Type controller, Type model = null) - { - if (model == null) - { - _undefinedMapper.Add(controller); - return this; - } - if (_controllerMapper.Keys.Contains(model)) - { - _controllerMapper[model].Add(controller); - } - else - { - _controllerMapper.Add(model, new List() { controller }); - } - return this; - } } } diff --git a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs index 2fd5984118..661341b25a 100644 --- a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs @@ -23,7 +23,6 @@ public interface IJsonApiOptions : ILinksConfiguration, ISerializerOptions int DefaultPageSize { get; } bool ValidateModelState { get; } bool AllowClientGeneratedIds { get; } - IResourceGraph ResourceGraph { get; set; } bool AllowCustomQueryParameters { get; set; } string Namespace { get; set; } } diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index 631f7e2b63..2a78c6f5b0 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -1,11 +1,8 @@ -using System; using System.Collections.Generic; using JsonApiDotNetCore.Builders; using JsonApiDotNetCore.Graph; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Models.Links; -using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; namespace JsonApiDotNetCore.Configuration @@ -95,12 +92,6 @@ public class JsonApiOptions : IJsonApiOptions /// public bool AllowClientGeneratedIds { get; set; } - /// - /// The graph of all resources exposed by this application. - /// - [Obsolete("Use the standalone resourcegraph")] - public IResourceGraph ResourceGraph { get; set; } - /// /// Whether or not to allow all custom query parameters. /// @@ -139,24 +130,6 @@ public class JsonApiOptions : IJsonApiOptions NullValueHandling = NullValueHandling.Ignore }; - public void BuildResourceGraph(Action builder) where TContext : DbContext - { - BuildResourceGraph(builder); - - ResourceGraphBuilder.AddDbContext(); - - ResourceGraph = ResourceGraphBuilder.Build(); - } - - public void BuildResourceGraph(Action builder) - { - if (builder == null) return; - - builder(ResourceGraphBuilder); - - ResourceGraph = ResourceGraphBuilder.Build(); - } - public void EnableExtension(JsonApiExtension extension) => EnabledExtensions.Add(extension); diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index d65a690124..1f93ada5f1 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -4,269 +4,77 @@ using System.Reflection; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Data; -using JsonApiDotNetCore.Formatters; using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Generics; -using JsonApiDotNetCore.Managers; -using JsonApiDotNetCore.Managers.Contracts; -using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Serialization; -using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCore.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Serialization.Client; +using JsonApiDotNetCore.Serialization; using JsonApiDotNetCore.Internal.Contracts; -using JsonApiDotNetCore.Query; -using JsonApiDotNetCore.Serialization.Server.Builders; using JsonApiDotNetCore.Serialization.Server; -using JsonApiDotNetCore.Serialization.Client; -using Microsoft.Extensions.DependencyInjection.Extensions; +using JsonApiDotNetCore.Services; namespace JsonApiDotNetCore.Extensions { // ReSharper disable once InconsistentNaming public static class IServiceCollectionExtensions { - static private readonly Action _noopConfig = opt => { }; - public static IServiceCollection AddJsonApi(this IServiceCollection services, - IMvcCoreBuilder mvcBuilder = null) - where TContext : DbContext - { - return AddJsonApi(services, _noopConfig, mvcBuilder); - } - /// /// Enabling JsonApiDotNetCore using the EF Core DbContext to build the ResourceGraph. /// /// /// - /// + /// + /// /// - public static IServiceCollection AddJsonApi(this IServiceCollection services, - Action configureOptions, + public static IServiceCollection AddJsonApi(this IServiceCollection services, + Action options = null, + Action resources = null, IMvcCoreBuilder mvcBuilder = null) - where TContext : DbContext - { - var options = new JsonApiOptions(); - // add basic Mvc functionality - mvcBuilder = mvcBuilder ?? services.AddMvcCore(); - // configures JsonApiOptions; - configureOptions(options); - // ResourceGraphBuilder should not be exposed on JsonApiOptions. - // Instead, ResourceGraphBuilder should consume JsonApiOptions - // build the resource graph using ef core DbContext - options.BuildResourceGraph(builder => builder.AddDbContext()); - ConfigureMvc(services, mvcBuilder, options); - // register services - AddJsonApiInternals(services, options); + where TEfCoreDbContext : DbContext + { + var application = new JsonApiApplicationBuilder(services, mvcBuilder ?? services.AddMvcCore()); + if (options != null) + application.ConfigureJsonApiOptions(options); + application.ConfigureMvc(); + application.ConfigureResources(resources); + application.ConfigureServices(); return services; } - /// /// Enabling JsonApiDotNetCore using manual declaration to build the ResourceGraph. - /// - /// - /// - /// - public static IServiceCollection AddJsonApi(this IServiceCollection services, - Action configureOptions, - IMvcCoreBuilder mvcBuilder = null) - { - var options = new JsonApiOptions(); - // add basic Mvc functionality - mvcBuilder = mvcBuilder ?? services.AddMvcCore(); - // configures JsonApiOptions; - configureOptions(options); - ConfigureMvc(services, mvcBuilder, options); - // register services - AddJsonApiInternals(services, options); - return services; - } - - /// - /// Enabling JsonApiDotNetCore using the EF Core DbContext to build the ResourceGraph. - /// + /// z /// - /// - /// + /// + /// /// public static IServiceCollection AddJsonApi(this IServiceCollection services, - Action configureOptions, - Action autoDiscover, + Action options = null, + Action discovery = null, + Action resources = null, IMvcCoreBuilder mvcBuilder = null) { - var options = new JsonApiOptions(); - // add basic Mvc functionality - mvcBuilder = mvcBuilder ?? services.AddMvcCore(); - // configures JsonApiOptions; - configureOptions(options); - // build the resource graph using auto discovery. - var facade = new ServiceDiscoveryFacade(services, options.ResourceGraphBuilder); - autoDiscover(facade); - ConfigureMvc(services, mvcBuilder, options); - // register services - AddJsonApiInternals(services, options); + var application = new JsonApiApplicationBuilder(services, mvcBuilder ?? services.AddMvcCore()); + if (options != null) + application.ConfigureJsonApiOptions(options); + application.ConfigureMvc(); + if (discovery != null) + application.AutoDiscover(discovery); + if (resources != null) + application.ConfigureResources(resources); + application.ConfigureServices(); return services; } - private static void ConfigureMvc(IServiceCollection services, IMvcCoreBuilder mvcBuilder, JsonApiOptions options) - { - // add JsonApi filters and serializers - mvcBuilder.AddMvcOptions(opt => AddMvcOptions(opt, options)); - // register services that allow user to override behaviour that is configured on startup, like routing conventions - AddStartupConfigurationServices(services, options); - var intermediateProvider = services.BuildServiceProvider(); - var routingConvention = intermediateProvider.GetRequiredService(); - mvcBuilder.AddMvcOptions(opt => opt.Conventions.Insert(0, routingConvention)); - services.AddSingleton(routingConvention); - } - - private static void AddMvcOptions(MvcOptions options, JsonApiOptions config) - { - options.Filters.Add(typeof(JsonApiExceptionFilter)); - options.Filters.Add(typeof(TypeMatchFilter)); - options.SerializeAsJsonApi(config); - } - - public static void AddJsonApiInternals( - this IServiceCollection services, - JsonApiOptions jsonApiOptions) where TContext : DbContext - { - if (jsonApiOptions.ResourceGraph == null) - jsonApiOptions.BuildResourceGraph(null); - - services.AddScoped>(); - - AddJsonApiInternals(services, jsonApiOptions); - } - - /// - /// Adds services to the container that need to be retrieved with an intermediate provider during Startup. - /// - private static void AddStartupConfigurationServices(this IServiceCollection services, JsonApiOptions jsonApiOptions) - { - services.AddSingleton(jsonApiOptions); - services.TryAddSingleton(new KebabCaseFormatter()); - services.TryAddSingleton(); - } - - public static void AddJsonApiInternals( - this IServiceCollection services, - JsonApiOptions jsonApiOptions) - { - var graph = jsonApiOptions.ResourceGraph ?? jsonApiOptions.ResourceGraphBuilder.Build(); - - if (graph.UsesDbContext == false) - { - services.AddScoped(); - services.AddSingleton(new DbContextOptionsBuilder().Options); - } - - services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>)); - services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>)); - - services.AddScoped(typeof(IEntityReadRepository<,>), typeof(DefaultEntityRepository<,>)); - services.AddScoped(typeof(IEntityWriteRepository<,>), typeof(DefaultEntityRepository<,>)); - - services.AddScoped(typeof(ICreateService<>), typeof(EntityResourceService<>)); - services.AddScoped(typeof(ICreateService<,>), typeof(EntityResourceService<,>)); - - services.AddScoped(typeof(IGetAllService<>), typeof(EntityResourceService<>)); - services.AddScoped(typeof(IGetAllService<,>), typeof(EntityResourceService<,>)); - - services.AddScoped(typeof(IGetByIdService<>), typeof(EntityResourceService<>)); - services.AddScoped(typeof(IGetByIdService<,>), typeof(EntityResourceService<,>)); - - services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<>)); - services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<,>)); - - services.AddScoped(typeof(IUpdateService<>), typeof(EntityResourceService<>)); - services.AddScoped(typeof(IUpdateService<,>), typeof(EntityResourceService<,>)); - - services.AddScoped(typeof(IDeleteService<>), typeof(EntityResourceService<>)); - services.AddScoped(typeof(IDeleteService<,>), typeof(EntityResourceService<,>)); - - services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>)); - services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>)); - - services.AddSingleton(jsonApiOptions); - services.AddSingleton(graph); - services.AddSingleton(); - services.AddSingleton(graph); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(typeof(GenericProcessor<>)); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - AddServerSerialization(services); - AddQueryParameterServices(services); - if (jsonApiOptions.EnableResourceHooks) - AddResourceHooks(services); - - services.AddScoped(); - } - - private static void AddQueryParameterServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - services.AddScoped(sp => sp.GetService()); - services.AddScoped(sp => sp.GetService()); - services.AddScoped(sp => sp.GetService()); - services.AddScoped(sp => sp.GetService()); - services.AddScoped(sp => sp.GetService()); - services.AddScoped(sp => sp.GetService()); - services.AddScoped(sp => sp.GetService()); - } - - - private static void AddResourceHooks(IServiceCollection services) - { - services.AddSingleton(typeof(IHooksDiscovery<>), typeof(HooksDiscovery<>)); - services.AddScoped(typeof(IResourceHookContainer<>), typeof(ResourceDefinition<>)); - services.AddTransient(typeof(IResourceHookExecutor), typeof(ResourceHookExecutor)); - services.AddTransient(); - services.AddTransient(); - } - - private static void AddServerSerialization(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(typeof(IMetaBuilder<>), typeof(MetaBuilder<>)); - services.AddScoped(typeof(ResponseSerializer<>)); - services.AddScoped(sp => sp.GetRequiredService().GetSerializer()); - services.AddScoped(); - } /// /// Enables client serializers for sending requests and receiving responses /// in json:api format. Internally only used for testing. /// Will be extended in the future to be part of a JsonApiClientDotNetCore package. /// - public static void AddClientSerialization(this IServiceCollection services) + public static IServiceCollection AddClientSerialization(this IServiceCollection services) { services.AddScoped(); @@ -275,13 +83,7 @@ public static void AddClientSerialization(this IServiceCollection services) var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService(), sp.GetService(), sp.GetService().Get()); return new RequestSerializer(sp.GetService(), sp.GetService(), resourceObjectBuilder); }); - - } - - public static void SerializeAsJsonApi(this MvcOptions options, JsonApiOptions jsonApiOptions) - { - options.InputFormatters.Insert(0, new JsonApiInputFormatter()); - options.OutputFormatters.Insert(0, new JsonApiOutputFormatter()); + return services; } /// diff --git a/src/JsonApiDotNetCore/Graph/IServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/IServiceDiscoveryFacade.cs new file mode 100644 index 0000000000..53e4e80cc5 --- /dev/null +++ b/src/JsonApiDotNetCore/Graph/IServiceDiscoveryFacade.cs @@ -0,0 +1,10 @@ +using System.Reflection; + +namespace JsonApiDotNetCore.Graph +{ + public interface IServiceDiscoveryFacade + { + ServiceDiscoveryFacade AddAssembly(Assembly assembly); + ServiceDiscoveryFacade AddCurrentAssembly(); + } +} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs index 468537ec1a..deb980f56a 100644 --- a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs +++ b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs @@ -15,7 +15,7 @@ namespace JsonApiDotNetCore.Graph { - public class ServiceDiscoveryFacade + public class ServiceDiscoveryFacade : IServiceDiscoveryFacade { internal static HashSet ServiceInterfaces = new HashSet { typeof(IResourceService<>), @@ -52,9 +52,7 @@ public class ServiceDiscoveryFacade private readonly IResourceGraphBuilder _graphBuilder; private readonly List _identifiables = new List(); - public ServiceDiscoveryFacade( - IServiceCollection services, - IResourceGraphBuilder graphBuilder) + public ServiceDiscoveryFacade(IServiceCollection services, IResourceGraphBuilder graphBuilder) { _services = services; _graphBuilder = graphBuilder; @@ -80,38 +78,9 @@ public ServiceDiscoveryFacade AddAssembly(Assembly assembly) AddServices(assembly, resourceDescriptor); AddRepositories(assembly, resourceDescriptor); } - - ScanControllers(assembly); - return this; } - private void ScanControllers(Assembly assembly) - { - var baseTypes = new List() { typeof(ControllerBase), typeof(JsonApiControllerMixin), typeof(JsonApiController<>), typeof(BaseJsonApiController<>) }; - List baseTypesSeen = new List() { }; - var types = assembly.GetTypes().ToList(); - //types.ForEach(t => baseTypesSeen.Add(t.BaseType.Name)); - - var controllerMapper = new Dictionary>() { }; - var undefinedMapper = new List() { }; - var sdf = assembly.GetTypes() - .Where(type => typeof(ControllerBase).IsAssignableFrom(type) & !type.IsGenericType).ToList(); - foreach (var controllerType in sdf) - { - // get generic parameter - var genericParameters = controllerType.BaseType.GetGenericArguments(); - if (genericParameters.Count() > 0) - { - - _graphBuilder.AddControllerPairing(controllerType, genericParameters[0]); - } - else - { - _graphBuilder.AddControllerPairing(controllerType); - } - } - } public IEnumerable FindDerivedTypes(Type baseType) { diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiExceptionFilter.cs b/src/JsonApiDotNetCore/Middleware/JsonApiExceptionFilter.cs index dda1fd3b89..386cbd8945 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiExceptionFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiExceptionFilter.cs @@ -1,3 +1,4 @@ +using System; using JsonApiDotNetCore.Internal; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; @@ -5,13 +6,23 @@ namespace JsonApiDotNetCore.Middleware { - public class JsonApiExceptionFilter : ActionFilterAttribute, IExceptionFilter + public interface IJsonApiExceptionFilterProvider + { + Type Get(); + } + + public class JsonApiExceptionFilterProvider : IJsonApiExceptionFilterProvider + { + public Type Get() => typeof(DefaultExceptionFilter); + } + + public class DefaultExceptionFilter : ActionFilterAttribute, IExceptionFilter { private readonly ILogger _logger; - public JsonApiExceptionFilter(ILoggerFactory loggerFactory) + public DefaultExceptionFilter(ILoggerFactory loggerFactory) { - _logger = loggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); } public void OnException(ExceptionContext context) diff --git a/src/JsonApiDotNetCore/Middleware/TypeMatchFilter.cs b/src/JsonApiDotNetCore/Middleware/TypeMatchFilter.cs index e3e474740e..542d03f4cd 100644 --- a/src/JsonApiDotNetCore/Middleware/TypeMatchFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/TypeMatchFilter.cs @@ -2,19 +2,28 @@ using System.Linq; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; -using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; namespace JsonApiDotNetCore.Middleware { + public interface IJsonApiTypeMatchFilterProvider + { + Type Get(); + } + + public class JsonApiTypeMatchFilterProvider : IJsonApiTypeMatchFilterProvider + { + public Type Get() => typeof(TypeMatchFilter); + } + public class TypeMatchFilter : IActionFilter { - private readonly IResourceGraph _resourceGraph; + private readonly IContextEntityProvider _provider; - public TypeMatchFilter(IResourceGraph resourceGraph) + public TypeMatchFilter(IContextEntityProvider provider) { - _resourceGraph = resourceGraph; + _provider = provider; } /// @@ -30,7 +39,7 @@ public void OnActionExecuting(ActionExecutingContext context) if (deserializedType != null && targetType != null && deserializedType != targetType) { - var expectedJsonApiResource = _resourceGraph.GetContextEntity(targetType); + var expectedJsonApiResource = _provider.GetContextEntity(targetType); throw new JsonApiException(409, $"Cannot '{context.HttpContext.Request.Method}' type '{deserializedType.Name}' " diff --git a/test/JsonApiDotNetCoreExampleTests/Helpers/Startups/ClientGeneratedIdsStartup.cs b/test/JsonApiDotNetCoreExampleTests/Helpers/Startups/ClientGeneratedIdsStartup.cs index 8048738417..fb8920816b 100644 --- a/test/JsonApiDotNetCoreExampleTests/Helpers/Startups/ClientGeneratedIdsStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Helpers/Startups/ClientGeneratedIdsStartup.cs @@ -33,7 +33,7 @@ public override IServiceProvider ConfigureServices(IServiceCollection services) options.AllowClientGeneratedIds = true; }, discovery => discovery.AddAssembly(Assembly.Load(nameof(JsonApiDotNetCoreExample))), - mvcBuilder); + mvcBuilder: mvcBuilder); return services.BuildServiceProvider(); diff --git a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs index bb2e2173fc..5b7e6b2ca3 100644 --- a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs @@ -28,13 +28,7 @@ public void Can_Build_ResourceGraph_Using_Builder() { // arrange var services = new ServiceCollection(); - services.AddJsonApi(opt => - { - opt.BuildResourceGraph(b => - { - b.AddResource("non-db-resources"); - }); - }); + services.AddJsonApi(resources: builder => builder.AddResource("non-db-resources")); // act var container = services.BuildServiceProvider(); diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index c0bfc9aee0..40e53ed706 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -29,13 +29,11 @@ public void AddJsonApiInternals_Adds_All_Required_Services() { // arrange var services = new ServiceCollection(); - var jsonApiOptions = new JsonApiOptions(); - services.AddSingleton(jsonApiOptions); services.AddDbContext(options => options.UseInMemoryDatabase("UnitTestDb"), ServiceLifetime.Transient); - services.AddScoped>(); + services.AddJsonApi(); + // act - services.AddJsonApiInternals(jsonApiOptions); // this is required because the DbContextResolver requires access to the current HttpContext // to get the request scoped DbContext instance services.AddScoped(); From b5c38d495f469bc1adbf089dd1ee8c2e839c1c03 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 18:41:32 +0200 Subject: [PATCH 07/29] chore: remove redundant injections of IResourceGraph --- .../Controllers/ArticlesController.cs | 3 +- .../Controllers/PeopleController.cs | 3 +- .../ModelDefinition.cs | 2 +- .../ModelsController.cs | 3 +- .../Resources/ArticleResource.cs | 2 +- .../Resources/LockableResource.cs | 2 +- .../Resources/PassportResource.cs | 2 +- .../Resources/PersonResource.cs | 2 +- .../Resources/TagResource.cs | 2 +- .../Resources/TodoResource.cs | 2 +- .../Resources/UserResource.cs | 2 +- .../Services/CustomArticleService.cs | 4 +- .../Controllers/ReportsController.cs | 3 +- .../Builders/IResourceGraphBuilder.cs | 2 +- .../Builders/ResourceGraphBuilder.cs | 21 +--------- .../Configuration/JsonApiOptions.cs | 1 - .../Data/DefaultEntityRepository.cs | 8 ++-- .../IApplicationBuilderExtensions.cs | 2 +- .../IServiceCollectionExtensions.cs | 4 +- .../Internal/Contracts/IResourceGraph.cs | 5 +-- .../Internal/ResourceGraph.cs | 39 +++++-------------- .../Internal/ValidationResults.cs | 2 +- .../Models/ResourceDefinition.cs | 4 +- .../Common/ResourceObjectBuilder.cs | 4 +- .../Builders/IncludedResourceObjectBuilder.cs | 3 +- .../Builders/ResponseResourceObjectBuilder.cs | 3 +- .../Server/RequestDeserializer.cs | 4 +- .../Services/EntityResourceService.cs | 10 ++--- .../ServiceDiscoveryFacadeTests.cs | 4 +- .../Builders/ContextGraphBuilder_Tests.cs | 2 +- .../IServiceCollectionExtensionsTests.cs | 8 ++-- .../Internal/ContextGraphBuilder_Tests.cs | 2 +- .../Models/ResourceDefinitionTests.cs | 2 +- .../QueryParametersUnitTestCollection.cs | 2 +- .../UnitTests/ResourceHooks/DiscoveryTests.cs | 2 +- .../ResourceHooks/ResourceHooksTestsSetup.cs | 4 +- .../Client/RequestSerializerTests.cs | 2 +- .../Common/ResourceObjectBuilderTests.cs | 2 +- .../Serialization/DeserializerTestsSetup.cs | 2 +- .../SerializationTestsSetupBase.cs | 4 +- .../Serialization/SerializerTestsSetup.cs | 6 +-- .../IncludedResourceObjectBuilderTests.cs | 2 +- .../Services/EntityResourceService_Tests.cs | 2 +- 43 files changed, 69 insertions(+), 121 deletions(-) diff --git a/src/Examples/GettingStarted/Controllers/ArticlesController.cs b/src/Examples/GettingStarted/Controllers/ArticlesController.cs index 2d1567659b..135391e9eb 100644 --- a/src/Examples/GettingStarted/Controllers/ArticlesController.cs +++ b/src/Examples/GettingStarted/Controllers/ArticlesController.cs @@ -10,9 +10,8 @@ public class ArticlesController : JsonApiController
{ public ArticlesController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService
resourceService) - : base(jsonApiOptions, resourceGraph, resourceService) + : base(jsonApiOptions, resourceService) { } } } diff --git a/src/Examples/GettingStarted/Controllers/PeopleController.cs b/src/Examples/GettingStarted/Controllers/PeopleController.cs index 8d8ee58a2a..0725bbebaa 100644 --- a/src/Examples/GettingStarted/Controllers/PeopleController.cs +++ b/src/Examples/GettingStarted/Controllers/PeopleController.cs @@ -10,9 +10,8 @@ public class PeopleController : JsonApiController { public PeopleController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService) - : base(jsonApiOptions, resourceGraph, resourceService) + : base(jsonApiOptions, resourceService) { } } } diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs index d31458250c..aa19ac3f71 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs +++ b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs @@ -6,7 +6,7 @@ namespace GettingStarted.ResourceDefinitionExample { public class ModelDefinition : ResourceDefinition { - public ModelDefinition(IResourceGraph graph) : base(graph) + public ModelDefinition(IContextEntityProvider provider) : base(provider) { } diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs index 1c066c28c9..6a92b4f8f3 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs +++ b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs @@ -9,9 +9,8 @@ public class ModelsController : JsonApiController { public ModelsController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IResourceService resourceService) - : base(jsonApiOptions, resourceGraph, resourceService) + : base(jsonApiOptions, resourceService) { } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs index 1e3230c759..ad11dcccbe 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class ArticleResource : ResourceDefinition
{ - public ArticleResource(IResourceGraph graph) : base(graph) { } + public ArticleResource(IContextEntityProvider provider) : base(provider) { } public override IEnumerable
OnReturn(HashSet
entities, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs index 96585a9458..bac860e452 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public abstract class LockableResource : ResourceDefinition where T : class, IIsLockable, IIdentifiable { - protected LockableResource(IResourceGraph graph) : base(graph) { } + protected LockableResource(IContextEntityProvider provider) : base(provider) { } protected void DisallowLocked(IEnumerable entities) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs index ba8b722277..d8955665f5 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class PassportResource : ResourceDefinition { - public PassportResource(IResourceGraph graph) : base(graph) + public PassportResource(IContextEntityProvider provider) : base(provider) { } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs index 445a990520..c42770fd81 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class PersonResource : LockableResource, IHasMeta { - public PersonResource(IResourceGraph graph) : base(graph) { } + public PersonResource(IContextEntityProvider provider) : base(provider) { } public override IEnumerable BeforeUpdate(IDiffableEntityHashSet entities, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index cd266303f3..c9cbd39079 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class TagResource : ResourceDefinition { - public TagResource(IResourceGraph graph) : base(graph) { } + public TagResource(IContextEntityProvider provider) : base(provider) { } public override IEnumerable BeforeCreate(IEntityHashSet affected, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs index 1c9f89a7fc..24c717ee7c 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class TodoResource : LockableResource { - public TodoResource(IResourceGraph graph) : base(graph) { } + public TodoResource(IContextEntityProvider provider) : base(provider) { } public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index 52497df9a0..289556e14d 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class UserResource : ResourceDefinition { - public UserResource(IResourceGraph graph, IFieldsExplorer fieldExplorer) : base(fieldExplorer, graph) + public UserResource(IContextEntityProvider provider, IFieldsExplorer fieldExplorer) : base(fieldExplorer, provider) { HideFields(u => u.Password); } diff --git a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs index e3b30c79b9..56f923aadc 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs @@ -20,11 +20,11 @@ public CustomArticleService(ISortService sortService, IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageService pageService, - IResourceGraph resourceGraph, + IContextEntityProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) : base(sortService, filterService, repository, options, includeService, sparseFieldsService, - pageService, resourceGraph, hookExecutor, loggerFactory) + pageService, provider, hookExecutor, loggerFactory) { } diff --git a/src/Examples/ReportsExample/Controllers/ReportsController.cs b/src/Examples/ReportsExample/Controllers/ReportsController.cs index e421477c20..77099fe380 100644 --- a/src/Examples/ReportsExample/Controllers/ReportsController.cs +++ b/src/Examples/ReportsExample/Controllers/ReportsController.cs @@ -12,9 +12,8 @@ public class ReportsController : BaseJsonApiController { public ReportsController( IJsonApiOptions jsonApiOptions, - IResourceGraph resourceGraph, IGetAllService getAll) - : base(jsonApiOptions, resourceGraph, getAll: getAll) + : base(jsonApiOptions, getAll: getAll) { } [HttpGet] diff --git a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs index 116cd387bd..367455503d 100644 --- a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs @@ -13,7 +13,7 @@ public interface IResourceGraphBuilder /// /// Construct the /// - IResourceGraph Build(); + IContextEntityProvider Build(); /// /// Add a json:api resource diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index 1878599d03..ed365a05a9 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -32,27 +32,10 @@ public ResourceGraphBuilder(IResourceNameFormatter formatter) } /// - public IResourceGraph Build() + public IContextEntityProvider Build() { _entities.ForEach(SetResourceLinksOptions); - - List controllerContexts = new List() { }; - foreach (var cm in _controllerMapper) - { - var model = cm.Key; - foreach (var controller in cm.Value) - { - var controllerName = controller.Name.Replace("Controller", ""); - - controllerContexts.Add(new ControllerResourceMap - { - Resource = model, - ControllerName = controllerName, - }); - - } - } - var graph = new ResourceGraph(_entities, _usesDbContext, _validationResults, controllerContexts); + var graph = new ResourceGraph(_entities, _validationResults); return graph; } diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index 2a78c6f5b0..dfa0591e18 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -133,7 +133,6 @@ public class JsonApiOptions : IJsonApiOptions public void EnableExtension(JsonApiExtension extension) => EnabledExtensions.Add(extension); - internal IResourceGraphBuilder ResourceGraphBuilder { get; } = new ResourceGraphBuilder(); internal List EnabledExtensions { get; set; } = new List(); } } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index f06e933038..7ad9d98720 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -419,18 +419,18 @@ public class DefaultEntityRepository : DefaultEntityRepository)) as ILogger; - var resourceGraph = app.ApplicationServices.GetService(typeof(IResourceGraph)) as ResourceGraph; + var resourceGraph = app.ApplicationServices.GetService(typeof(IContextEntityProvider)) as ResourceGraph; if (logger != null && resourceGraph != null) { diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index 1f93ada5f1..ae1de65fd9 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -80,8 +80,8 @@ public static IServiceCollection AddClientSerialization(this IServiceCollection services.AddScoped(sp => { - var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService(), sp.GetService(), sp.GetService().Get()); - return new RequestSerializer(sp.GetService(), sp.GetService(), resourceObjectBuilder); + var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService(), sp.GetService().Get()); + return new RequestSerializer(sp.GetService(), sp.GetService(), resourceObjectBuilder); }); return services; } diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs index e07a856b0b..244eb91f3a 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs @@ -7,9 +7,6 @@ namespace JsonApiDotNetCore.Internal.Contracts /// public interface IResourceGraph : IContextEntityProvider { - /// - /// Was built against an EntityFrameworkCore DbContext ? - /// - bool UsesDbContext { get; } + } } diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index c29c27dd01..ea547ee8d1 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -6,57 +6,36 @@ namespace JsonApiDotNetCore.Internal { - public class ControllerResourceMap - { - public string ControllerName { get; set; } - public Type Resource { get; set; } - } - /// /// keeps track of all the models/resources defined in JADNC /// - public class ResourceGraph : IResourceGraph + public class ResourceGraph : IContextEntityProvider { - internal List Entities { get; } internal List ValidationResults { get; } + private List _entities { get; } - public List ControllerResourceMap { get; internal set; } - - [Obsolete("please instantiate properly, dont use the static constructor")] - internal static IResourceGraph Instance { get; set; } - - public ResourceGraph() { } - [Obsolete("Use new one")] - public ResourceGraph(List entities, bool usesDbContext) + public ResourceGraph(List entities) { - Entities = entities; - UsesDbContext = usesDbContext; + _entities = entities; ValidationResults = new List(); - Instance = this; } - internal ResourceGraph(List entities, bool usesDbContext, List validationResults, List controllerContexts) + public ResourceGraph(List entities, List validationResults) { - ControllerResourceMap = controllerContexts; - Entities = entities; - UsesDbContext = usesDbContext; + _entities = entities; ValidationResults = validationResults; - Instance = this; } /// - public bool UsesDbContext { get; } - - /// - public ContextEntity[] GetContextEntities() => Entities.ToArray(); + public ContextEntity[] GetContextEntities() => _entities.ToArray(); /// public ContextEntity GetContextEntity(string entityName) - => Entities.SingleOrDefault(e => string.Equals(e.EntityName, entityName, StringComparison.OrdinalIgnoreCase)); + => _entities.SingleOrDefault(e => string.Equals(e.EntityName, entityName, StringComparison.OrdinalIgnoreCase)); /// public ContextEntity GetContextEntity(Type entityType) - => Entities.SingleOrDefault(e => e.EntityType == entityType); + => _entities.SingleOrDefault(e => e.EntityType == entityType); /// public ContextEntity GetContextEntity() where TResource : class, IIdentifiable => GetContextEntity(typeof(TResource)); diff --git a/src/JsonApiDotNetCore/Internal/ValidationResults.cs b/src/JsonApiDotNetCore/Internal/ValidationResults.cs index fbaa6eb462..93fa32c74b 100644 --- a/src/JsonApiDotNetCore/Internal/ValidationResults.cs +++ b/src/JsonApiDotNetCore/Internal/ValidationResults.cs @@ -2,7 +2,7 @@ namespace JsonApiDotNetCore.Internal { - internal class ValidationResult + public class ValidationResult { public ValidationResult(LogLevel logLevel, string message) { diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 9d1c65a4c9..c37b5542f4 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -32,7 +32,7 @@ public class ResourceDefinition : IResourceDefinition, IResourceHookC private readonly IFieldsExplorer _fieldExplorer; private List _allowedAttributes; private List _allowedRelationships; - public ResourceDefinition(IFieldsExplorer fieldExplorer, IResourceGraph graph) + public ResourceDefinition(IFieldsExplorer fieldExplorer, IContextEntityProvider graph) { _contextEntity = graph.GetContextEntity(typeof(TResource)); _allowedAttributes = _contextEntity.Attributes; @@ -40,7 +40,7 @@ public ResourceDefinition(IFieldsExplorer fieldExplorer, IResourceGraph graph) _fieldExplorer = fieldExplorer; } - public ResourceDefinition(IResourceGraph graph) + public ResourceDefinition(IContextEntityProvider graph) { _contextEntity = graph.GetContextEntity(typeof(TResource)); _allowedAttributes = _contextEntity.Attributes; diff --git a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs index d144020972..589d4ef4ac 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs @@ -12,14 +12,12 @@ namespace JsonApiDotNetCore.Serialization /// public class ResourceObjectBuilder : IResourceObjectBuilder { - protected readonly IResourceGraph _resourceGraph; protected readonly IContextEntityProvider _provider; private readonly ResourceObjectBuilderSettings _settings; private const string _identifiablePropertyName = nameof(Identifiable.Id); - public ResourceObjectBuilder(IResourceGraph resourceGraph, IContextEntityProvider provider, ResourceObjectBuilderSettings settings) + public ResourceObjectBuilder(IContextEntityProvider provider, ResourceObjectBuilderSettings settings) { - _resourceGraph = resourceGraph; _provider = provider; _settings = settings; } diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs index 295f8db1a8..c663b8ec5a 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs @@ -16,10 +16,9 @@ public class IncludedResourceObjectBuilder : ResourceObjectBuilder, IIncludedRes public IncludedResourceObjectBuilder(IFieldsToSerialize fieldsToSerialize, ILinkBuilder linkBuilder, - IResourceGraph resourceGraph, IContextEntityProvider provider, IResourceObjectBuilderSettingsProvider settingsProvider) - : base(resourceGraph, provider, settingsProvider.Get()) + : base(provider, settingsProvider.Get()) { _included = new HashSet(new ResourceObjectComparer()); _fieldsToSerialize = fieldsToSerialize; diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs index 9256330fcb..c8e134d001 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs @@ -17,10 +17,9 @@ public class ResponseResourceObjectBuilder : ResourceObjectBuilder, IResourceObj public ResponseResourceObjectBuilder(ILinkBuilder linkBuilder, IIncludedResourceObjectBuilder includedBuilder, IIncludeService includeService, - IResourceGraph resourceGraph, IContextEntityProvider provider, IResourceObjectBuilderSettingsProvider settingsProvider) - : base(resourceGraph, provider, settingsProvider.Get()) + : base(provider, settingsProvider.Get()) { _linkBuilder = linkBuilder; _includedBuilder = includedBuilder; diff --git a/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs index 7c57556e7c..7283707b42 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs @@ -11,8 +11,8 @@ public class RequestDeserializer : BaseDocumentParser, IJsonApiDeserializer { private readonly ITargetedFields _targetedFields; - public RequestDeserializer(IResourceGraph resourceGraph, - ITargetedFields targetedFields) : base(resourceGraph) + public RequestDeserializer(IContextEntityProvider provider, + ITargetedFields targetedFields) : base(provider) { _targetedFields = targetedFields; } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 63eef18d5a..6b78308cbe 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -24,7 +24,6 @@ public class EntityResourceService : { private readonly IPageService _pageManager; private readonly IJsonApiOptions _options; - private readonly IResourceGraph _resourceGraph; private readonly IFilterService _filterService; private readonly ISortService _sortService; private readonly IEntityRepository _repository; @@ -42,7 +41,7 @@ public EntityResourceService( IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageService pageManager, - IResourceGraph resourceGraph, + IContextEntityProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) { @@ -50,13 +49,12 @@ public EntityResourceService( _sparseFieldsService = sparseFieldsService; _pageManager = pageManager; _options = options; - _resourceGraph = resourceGraph; _sortService = sortService; _filterService = filterService; _repository = repository; _hookExecutor = hookExecutor; _logger = loggerFactory?.CreateLogger>(); - _currentRequestResource = resourceGraph.GetContextEntity(); + _currentRequestResource = provider.GetContextEntity(); } public virtual async Task CreateAsync(TResource entity) @@ -327,9 +325,9 @@ public class EntityResourceService : EntityResourceService repository, IJsonApiOptions options,IIncludeService includeService, ISparseFieldsService sparseFieldsService, - IPageService pageManager, IResourceGraph resourceGraph, + IPageService pageManager, IContextEntityProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) - : base(sortService, filterService, repository, options, includeService, sparseFieldsService, pageManager, resourceGraph, hookExecutor, loggerFactory) + : base(sortService, filterService, repository, options, includeService, sparseFieldsService, pageManager, provider, hookExecutor, loggerFactory) { } } diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index 97dbc35eb8..c29ff6ff2b 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -71,7 +71,7 @@ public void AddCurrentAssembly_Adds_Services_To_Container() _services.AddScoped((_) => new Mock().Object); _services.AddScoped((_) => new Mock().Object); _services.AddScoped((_) => new Mock().Object); - _services.AddScoped((_) => new Mock().Object); + _services.AddScoped((_) => new Mock().Object); _facade.AddCurrentAssembly(); // assert @@ -103,7 +103,7 @@ public TestModelService( IJsonApiOptions options, IRequestContext currentRequest, IPageQueryService pageService, - IResourceGraph resourceGraph, + IContextEntityProvider resourceGraph, ILoggerFactory loggerFactory = null, IResourceHookExecutor hookExecutor = null) : base(repository, options, currentRequest, pageService, resourceGraph, loggerFactory, hookExecutor) { diff --git a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs index 5b7e6b2ca3..3a793bb6fc 100644 --- a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs @@ -34,7 +34,7 @@ public void Can_Build_ResourceGraph_Using_Builder() var container = services.BuildServiceProvider(); // assert - var resourceGraph = container.GetRequiredService(); + var resourceGraph = container.GetRequiredService(); var dbResource = resourceGraph.GetContextEntity("db-resources"); var nonDbResource = resourceGraph.GetContextEntity("non-db-resources"); Assert.Equal(typeof(DbResource), dbResource.EntityType); diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index 40e53ed706..834b469f0e 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -42,13 +42,13 @@ public void AddJsonApiInternals_Adds_All_Required_Services() // assert var currentRequest = provider.GetService(); Assert.NotNull(currentRequest); - var graph = provider.GetService(); + var graph = provider.GetService(); Assert.NotNull(graph); currentRequest.SetRequestResource(graph.GetContextEntity()); - Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService(typeof(IEntityRepository))); - Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService>()); Assert.NotNull(provider.GetService()); @@ -128,7 +128,7 @@ public void AddJsonApi_With_Context_Uses_DbSet_PropertyName_If_NoOtherSpecified( // assert var provider = services.BuildServiceProvider(); - var graph = provider.GetService(); + var graph = provider.GetService(); var resource = graph.GetContextEntity(typeof(IntResource)); Assert.Equal("resource", resource.EntityName); } diff --git a/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs b/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs index 122b2cd67b..d2f35dd454 100644 --- a/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs @@ -19,7 +19,7 @@ public void AddDbContext_Does_Not_Throw_If_Context_Contains_Members_That_DoNot_I var resourceGraph = resourceGraphBuilder.Build() as ResourceGraph; // assert - Assert.Empty(resourceGraph.Entities); + Assert.Empty(resourceGraph.GetContextEntities()); } [Fact] diff --git a/test/UnitTests/Models/ResourceDefinitionTests.cs b/test/UnitTests/Models/ResourceDefinitionTests.cs index 78ec7ff73e..d15563904d 100644 --- a/test/UnitTests/Models/ResourceDefinitionTests.cs +++ b/test/UnitTests/Models/ResourceDefinitionTests.cs @@ -10,7 +10,7 @@ namespace UnitTests.Models { public class ResourceDefinition_Scenario_Tests { - private readonly IResourceGraph _graph; + private readonly IContextEntityProvider _graph; public ResourceDefinition_Scenario_Tests() { diff --git a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs index 9cc6d80fc4..9cec8ed664 100644 --- a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs +++ b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs @@ -13,7 +13,7 @@ namespace UnitTests.QueryParameters public class QueryParametersUnitTestCollection { protected readonly ContextEntity _articleResourceContext; - protected readonly IResourceGraph _graph; + protected readonly IContextEntityProvider _graph; public QueryParametersUnitTestCollection() { diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs index fe6d798c37..16c4cc6e2f 100644 --- a/test/UnitTests/ResourceHooks/DiscoveryTests.cs +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -33,7 +33,7 @@ public void Hook_Discovery() public class AnotherDummy : Identifiable { } public abstract class ResourceDefintionBase : ResourceDefinition where T : class, IIdentifiable { - protected ResourceDefintionBase(IResourceGraph graph) : base(graph) { } + protected ResourceDefintionBase(IContextEntityProvider graph) : base(graph) { } public override IEnumerable BeforeDelete(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index cba0435a60..a53cc3684d 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -25,7 +25,7 @@ namespace UnitTests.ResourceHooks public class HooksDummyData { protected IFieldsExplorer _fieldExplorer; - protected IResourceGraph _graph; + protected IContextEntityProvider _graph; protected ResourceHook[] NoHooks = new ResourceHook[0]; protected ResourceHook[] EnableDbValues = { ResourceHook.BeforeUpdate, ResourceHook.BeforeUpdateRelationship }; protected ResourceHook[] DisableDbValues = new ResourceHook[0]; @@ -358,7 +358,7 @@ IDbContextResolver CreateTestDbResolver(AppDbContext dbContext) where TM void ResolveInverseRelationships(AppDbContext context) { - new InverseRelationships(ResourceGraph.Instance, new DbContextResolver(context)).Resolve(); + new InverseRelationships(_graph, new DbContextResolver(context)).Resolve(); } Mock> CreateResourceDefinition diff --git a/test/UnitTests/Serialization/Client/RequestSerializerTests.cs b/test/UnitTests/Serialization/Client/RequestSerializerTests.cs index bbb32cc04c..7d697b81eb 100644 --- a/test/UnitTests/Serialization/Client/RequestSerializerTests.cs +++ b/test/UnitTests/Serialization/Client/RequestSerializerTests.cs @@ -16,7 +16,7 @@ public class RequestSerializerTests : SerializerTestsSetup public RequestSerializerTests() { - var builder = new ResourceObjectBuilder(_resourceGraph, _resourceGraph, new ResourceObjectBuilderSettings()); + var builder = new ResourceObjectBuilder(_resourceGraph, new ResourceObjectBuilderSettings()); _serializer = new RequestSerializer(_fieldExplorer, _resourceGraph, builder); } diff --git a/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs index 708cf5055d..e461ff697c 100644 --- a/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs @@ -16,7 +16,7 @@ public class ResourceObjectBuilderTests : SerializerTestsSetup public ResourceObjectBuilderTests() { - _builder = new ResourceObjectBuilder(_resourceGraph, _resourceGraph, new ResourceObjectBuilderSettings()); + _builder = new ResourceObjectBuilder(_resourceGraph, new ResourceObjectBuilderSettings()); } [Fact] diff --git a/test/UnitTests/Serialization/DeserializerTestsSetup.cs b/test/UnitTests/Serialization/DeserializerTestsSetup.cs index dc9dff4aae..1941eace59 100644 --- a/test/UnitTests/Serialization/DeserializerTestsSetup.cs +++ b/test/UnitTests/Serialization/DeserializerTestsSetup.cs @@ -9,7 +9,7 @@ public class DeserializerTestsSetup : SerializationTestsSetupBase { protected class TestDocumentParser : BaseDocumentParser { - public TestDocumentParser(IResourceGraph resourceGraph) : base(resourceGraph) { } + public TestDocumentParser(IContextEntityProvider resourceGraph) : base(resourceGraph) { } public new object Deserialize(string body) { diff --git a/test/UnitTests/Serialization/SerializationTestsSetupBase.cs b/test/UnitTests/Serialization/SerializationTestsSetupBase.cs index a286781ffb..6c7d7fcbd8 100644 --- a/test/UnitTests/Serialization/SerializationTestsSetupBase.cs +++ b/test/UnitTests/Serialization/SerializationTestsSetupBase.cs @@ -8,7 +8,7 @@ namespace UnitTests.Serialization { public class SerializationTestsSetupBase { - protected IResourceGraph _resourceGraph; + protected IContextEntityProvider _resourceGraph; protected IContextEntityProvider _provider; protected readonly Faker _foodFaker; protected readonly Faker _songFaker; @@ -36,7 +36,7 @@ public SerializationTestsSetupBase() .RuleFor(f => f.Id, f => f.UniqueIndex + 1); } - protected IResourceGraph BuildGraph() + protected IContextEntityProvider BuildGraph() { var resourceGraphBuilder = new ResourceGraphBuilder(); resourceGraphBuilder.AddResource("test-resource"); diff --git a/test/UnitTests/Serialization/SerializerTestsSetup.cs b/test/UnitTests/Serialization/SerializerTestsSetup.cs index 54802bd5cb..52140acf62 100644 --- a/test/UnitTests/Serialization/SerializerTestsSetup.cs +++ b/test/UnitTests/Serialization/SerializerTestsSetup.cs @@ -52,7 +52,7 @@ protected ResponseSerializer GetResponseSerializer(List(meta, link, includedBuilder, fieldsToSerialize, resourceObjectBuilder, provider); } @@ -61,12 +61,12 @@ protected ResponseResourceObjectBuilder GetResponseResourceObjectBuilder(List
  • _crMock; private readonly Mock _pgsMock; private readonly Mock _ufMock; - private readonly IResourceGraph _resourceGraph; + private readonly IContextEntityProvider _resourceGraph; public EntityResourceService_Tests() { From be088673347de57b1211ef882ff9c939ecfb6f59 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 19:06:58 +0200 Subject: [PATCH 08/29] refactor: merge IContextEntityProvider and IFieldsExplorer into IResourceGraphExplorer --- .../Resources/ArticleResource.cs | 2 +- .../Resources/LockableResource.cs | 2 +- .../Resources/PassportResource.cs | 2 +- .../Resources/PersonResource.cs | 2 +- .../Resources/TagResource.cs | 2 +- .../Resources/TodoResource.cs | 2 +- .../Resources/UserResource.cs | 2 +- .../Builders/IResourceGraphBuilder.cs | 2 +- .../Builders/JsonApiApplicationBuilder.cs | 8 +- .../Builders/ResourceGraphBuilder.cs | 2 +- .../Data/DefaultEntityRepository.cs | 4 +- .../IApplicationBuilderExtensions.cs | 2 +- .../IServiceCollectionExtensions.cs | 4 +- .../Contracts/IContextEntityProvider.cs | 51 ++++++++ .../Internal/Contracts/IResourceGraph.cs | 2 +- .../Internal/ResourceGraph.cs | 114 +++++++++++++++-- .../Middleware/RequestMiddleware.cs | 8 +- .../Models/ResourceDefinition.cs | 16 +-- .../Common/QueryParameterService.cs | 4 +- .../QueryParameterServices/FilterService.cs | 2 +- .../QueryParameterServices/IncludeService.cs | 2 +- .../QueryParameterServices/SortService.cs | 2 +- .../SparseFieldsService.cs | 2 +- .../Serialization/Client/RequestSerializer.cs | 15 +-- .../Serialization/Server/FieldsToSerialize.cs | 15 +-- .../Services/Contract/IFieldExplorer.cs | 54 -------- .../Services/EntityResourceService.cs | 2 +- .../Services/ExposedFieldsExplorer.cs | 120 ------------------ .../Services/ResourceDefinitionProvider.cs | 4 +- .../ServiceDiscoveryFacadeTests.cs | 4 +- .../Acceptance/Spec/SparseFieldSetTests.cs | 7 +- .../Builders/ContextGraphBuilder_Tests.cs | 2 +- test/UnitTests/Builders/LinkBuilderTests.cs | 2 +- .../IServiceCollectionExtensionsTests.cs | 8 +- .../Models/ResourceDefinitionTests.cs | 4 +- .../QueryParametersUnitTestCollection.cs | 2 +- .../UnitTests/ResourceHooks/DiscoveryTests.cs | 2 +- .../Update/BeforeUpdate_WithDbValues_Tests.cs | 2 +- .../ResourceHooks/ResourceHooksTestsSetup.cs | 5 +- .../Client/RequestSerializerTests.cs | 4 +- .../Client/ResponseDeserializerTests.cs | 2 +- .../Common/DocumentBuilderTests.cs | 2 +- .../Common/DocumentParserTests.cs | 4 +- .../Common/ResourceObjectBuilderTests.cs | 16 +-- .../Serialization/DeserializerTestsSetup.cs | 2 +- .../SerializationTestsSetupBase.cs | 7 +- .../Serialization/SerializerTestsSetup.cs | 18 ++- .../IncludedResourceObjectBuilderTests.cs | 6 +- .../Server/RequestDeserializerTests.cs | 2 +- .../ResponseResourceObjectBuilderTests.cs | 2 +- .../Server/ResponseSerializerTests.cs | 16 +-- .../Services/EntityResourceService_Tests.cs | 2 +- 52 files changed, 263 insertions(+), 307 deletions(-) delete mode 100644 src/JsonApiDotNetCore/Services/Contract/IFieldExplorer.cs delete mode 100644 src/JsonApiDotNetCore/Services/ExposedFieldsExplorer.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs index ad11dcccbe..c8fa44cf13 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class ArticleResource : ResourceDefinition
    { - public ArticleResource(IContextEntityProvider provider) : base(provider) { } + public ArticleResource(IResourceGraphExplorer graph) : base(graph) { } public override IEnumerable
    OnReturn(HashSet
    entities, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs index bac860e452..026de2fe23 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public abstract class LockableResource : ResourceDefinition where T : class, IIsLockable, IIdentifiable { - protected LockableResource(IContextEntityProvider provider) : base(provider) { } + protected LockableResource(IResourceGraphExplorer graph) : base(graph) { } protected void DisallowLocked(IEnumerable entities) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs index d8955665f5..1fce52e991 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class PassportResource : ResourceDefinition { - public PassportResource(IContextEntityProvider provider) : base(provider) + public PassportResource(IResourceGraphExplorer graph) : base(graph) { } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs index c42770fd81..a9dfdc2bea 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class PersonResource : LockableResource, IHasMeta { - public PersonResource(IContextEntityProvider provider) : base(provider) { } + public PersonResource(IResourceGraphExplorer graph) : base(graph) { } public override IEnumerable BeforeUpdate(IDiffableEntityHashSet entities, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index c9cbd39079..b1faca828b 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class TagResource : ResourceDefinition { - public TagResource(IContextEntityProvider provider) : base(provider) { } + public TagResource(IResourceGraphExplorer graph) : base(graph) { } public override IEnumerable BeforeCreate(IEntityHashSet affected, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs index 24c717ee7c..696b3a25f6 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class TodoResource : LockableResource { - public TodoResource(IContextEntityProvider provider) : base(provider) { } + public TodoResource(IResourceGraphExplorer graph) : base(graph) { } public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index 289556e14d..3f402db3af 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class UserResource : ResourceDefinition { - public UserResource(IContextEntityProvider provider, IFieldsExplorer fieldExplorer) : base(fieldExplorer, provider) + public UserResource(IResourceGraphExplorer graph) : base(graph) { HideFields(u => u.Password); } diff --git a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs index 367455503d..d0cdc0978b 100644 --- a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs @@ -13,7 +13,7 @@ public interface IResourceGraphBuilder /// /// Construct the /// - IContextEntityProvider Build(); + IResourceGraphExplorer Build(); /// /// Add a json:api resource diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index 3291f4921b..4f2a56e294 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Formatters; @@ -16,16 +13,13 @@ using JsonApiDotNetCore.Hooks; using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Query; using JsonApiDotNetCore.Serialization.Server.Builders; using JsonApiDotNetCore.Serialization.Server; -using JsonApiDotNetCore.Serialization.Client; using Microsoft.Extensions.DependencyInjection.Extensions; -using JsonApiDotNetCore.Builders; namespace JsonApiDotNetCore.Builders { @@ -139,6 +133,7 @@ public void ConfigureServices() _services.AddSingleton(JsonApiOptions); _services.AddSingleton(graph); _services.AddSingleton(); + _services.AddSingleton(graph); _services.AddSingleton(graph); _services.AddScoped(); _services.AddScoped(); @@ -148,7 +143,6 @@ public void ConfigureServices() _services.AddScoped(typeof(GenericProcessor<>)); _services.AddScoped(); _services.AddScoped(); - _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index ed365a05a9..f8c871affd 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -32,7 +32,7 @@ public ResourceGraphBuilder(IResourceNameFormatter formatter) } /// - public IContextEntityProvider Build() + public IResourceGraphExplorer Build() { _entities.ForEach(SetResourceLinksOptions); var graph = new ResourceGraph(_entities, _validationResults); diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 7ad9d98720..30fa685058 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -419,7 +419,7 @@ public class DefaultEntityRepository : DefaultEntityRepository)) as ILogger; - var resourceGraph = app.ApplicationServices.GetService(typeof(IContextEntityProvider)) as ResourceGraph; + var resourceGraph = app.ApplicationServices.GetService(typeof(IResourceGraphExplorer)) as ResourceGraph; if (logger != null && resourceGraph != null) { diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index ae1de65fd9..cb7abbb0aa 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -80,8 +80,8 @@ public static IServiceCollection AddClientSerialization(this IServiceCollection services.AddScoped(sp => { - var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService(), sp.GetService().Get()); - return new RequestSerializer(sp.GetService(), sp.GetService(), resourceObjectBuilder); + var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService(), sp.GetService().Get()); + return new RequestSerializer(sp.GetService(), resourceObjectBuilder); }); return services; } diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs index fe88db72b3..ae1fecd181 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs @@ -1,8 +1,59 @@ using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Internal.Contracts { + + /// + /// Responsible for retrieving the exposed resource fields (attributes and + /// relationships) of registered resources in the resource graph. + /// + public interface IResourceGraphExplorer : IContextEntityProvider + { + /// + /// Gets all fields (attributes and relationships) for + /// that are targeted by the selector. If no selector is provided, all + /// exposed fields are returned. + /// + /// The resource for which to retrieve fields + /// Should be of the form: (TResource e) => new { e.Field1, e.Field2 } + List GetFields(Expression> selector = null) where TResource : IIdentifiable; + /// + /// Gets all attributes for + /// that are targeted by the selector. If no selector is provided, all + /// exposed fields are returned. + /// + /// The resource for which to retrieve attributes + /// Should be of the form: (TResource e) => new { e.Attribute1, e.Arttribute2 } + List GetAttributes(Expression> selector = null) where TResource : IIdentifiable; + /// + /// Gets all relationships for + /// that are targeted by the selector. If no selector is provided, all + /// exposed fields are returned. + /// + /// The resource for which to retrieve relationships + /// Should be of the form: (TResource e) => new { e.Relationship1, e.Relationship2 } + List GetRelationships(Expression> selector = null) where TResource : IIdentifiable; + /// + /// Gets all exposed fields (attributes and relationships) for type + /// + /// The resource type. Must extend IIdentifiable. + List GetFields(Type type); + /// + /// Gets all exposed attributes for type + /// + /// The resource type. Must extend IIdentifiable. + List GetAttributes(Type type); + /// + /// Gets all exposed relationships for type + /// + /// The resource type. Must extend IIdentifiable. + List GetRelationships(Type type); + } + /// /// Responsible for getting s from the . /// diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs index 244eb91f3a..fb5166f21b 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs @@ -5,7 +5,7 @@ namespace JsonApiDotNetCore.Internal.Contracts /// /// A cache for the models in entity core /// - public interface IResourceGraph : IContextEntityProvider + public interface IResourceGraph : IResourceGraphExplorer { } diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index ea547ee8d1..81a1bcba8e 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; @@ -9,18 +10,12 @@ namespace JsonApiDotNetCore.Internal /// /// keeps track of all the models/resources defined in JADNC /// - public class ResourceGraph : IContextEntityProvider + public class ResourceGraph : IResourceGraphExplorer { internal List ValidationResults { get; } private List _entities { get; } - public ResourceGraph(List entities) - { - _entities = entities; - ValidationResults = new List(); - } - - public ResourceGraph(List entities, List validationResults) + public ResourceGraph(List entities, List validationResults = null) { _entities = entities; ValidationResults = validationResults; @@ -39,5 +34,108 @@ public ContextEntity GetContextEntity(Type entityType) /// public ContextEntity GetContextEntity() where TResource : class, IIdentifiable => GetContextEntity(typeof(TResource)); + + /// + public List GetFields(Expression> selector = null) where T : IIdentifiable + { + return Getter(selector).ToList(); + } + /// + public List GetAttributes(Expression> selector = null) where T : IIdentifiable + { + return Getter(selector, FieldFilterType.Attribute).Cast().ToList(); + } + /// + public List GetRelationships(Expression> selector = null) where T : IIdentifiable + { + return Getter(selector, FieldFilterType.Relationship).Cast().ToList(); + } + /// + public List GetFields(Type type) + { + return GetContextEntity(type).Fields.ToList(); + } + /// + public List GetAttributes(Type type) + { + return GetContextEntity(type).Attributes.ToList(); + } + /// + public List GetRelationships(Type type) + { + return GetContextEntity(type).Relationships.ToList(); + } + + private IEnumerable Getter(Expression> selector = null, FieldFilterType type = FieldFilterType.None) where T : IIdentifiable + { + IEnumerable available; + if (type == FieldFilterType.Attribute) + available = GetContextEntity(typeof(T)).Attributes.Cast(); + else if (type == FieldFilterType.Relationship) + available = GetContextEntity(typeof(T)).Relationships.Cast(); + else + available = GetContextEntity(typeof(T)).Fields; + + if (selector == null) + return available; + + var targeted = new List(); + + if (selector.Body is MemberExpression memberExpression) + { // model => model.Field1 + try + { + targeted.Add(available.Single(f => f.ExposedInternalMemberName == memberExpression.Member.Name)); + return targeted; + } + catch (Exception ex) + { + ThrowNotExposedError(memberExpression.Member.Name, type); + } + } + + + if (selector.Body is NewExpression newExpression) + { // model => new { model.Field1, model.Field2 } + string memberName = null; + try + { + if (newExpression.Members == null) + return targeted; + + foreach (var member in newExpression.Members) + { + memberName = member.Name; + targeted.Add(available.Single(f => f.ExposedInternalMemberName == memberName)); + } + return targeted; + } + catch (Exception ex) + { + ThrowNotExposedError(memberName, type); + } + } + + throw new ArgumentException($"The expression returned by '{selector}' for '{GetType()}' is of type {selector.Body.GetType()}" + + " and cannot be used to select resource attributes. The type must be a NewExpression.Example: article => new { article.Author };"); + + } + + private void ThrowNotExposedError(string memberName, FieldFilterType type) + { + throw new ArgumentException($"{memberName} is not an json:api exposed {type.ToString("g")}."); + } + + /// + /// internally used only by . + /// + private enum FieldFilterType + { + None, + Attribute, + Relationship + } + + } } diff --git a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs index 510627cc23..1e5f25b454 100644 --- a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs @@ -21,7 +21,7 @@ public class CurrentRequestMiddleware private readonly RequestDelegate _next; private HttpContext _httpContext; private ICurrentRequest _currentRequest; - private IContextEntityProvider _contextEntityProvider; + private IResourceGraphExplorer _contextEntityProvider; private IJsonApiOptions _options; private IJsonApiRoutingConvention _routingConvention; @@ -34,8 +34,8 @@ public async Task Invoke(HttpContext httpContext, IJsonApiRoutingConvention routingConvention, IJsonApiOptions options, ICurrentRequest currentRequest, - IContextEntityProvider contextEntityProvider) - { + IResourceGraphExplorer contextEntityProvider) + { _httpContext = httpContext; _currentRequest = currentRequest; _routingConvention = routingConvention; @@ -47,7 +47,7 @@ public async Task Invoke(HttpContext httpContext, _currentRequest.SetRequestResource(GetCurrentEntity()); _currentRequest.IsRelationshipPath = PathIsRelationship(); _currentRequest.BasePath = GetBasePath(_currentRequest.GetRequestResource().EntityName); - } + } if (IsValid()) { diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index c37b5542f4..c445c15a97 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -29,23 +29,17 @@ public interface IResourceDefinition public class ResourceDefinition : IResourceDefinition, IResourceHookContainer where TResource : class, IIdentifiable { private readonly ContextEntity _contextEntity; - private readonly IFieldsExplorer _fieldExplorer; + private readonly IResourceGraphExplorer _graph; private List _allowedAttributes; private List _allowedRelationships; - public ResourceDefinition(IFieldsExplorer fieldExplorer, IContextEntityProvider graph) + public ResourceDefinition(IResourceGraphExplorer graph) { _contextEntity = graph.GetContextEntity(typeof(TResource)); _allowedAttributes = _contextEntity.Attributes; _allowedRelationships = _contextEntity.Relationships; - _fieldExplorer = fieldExplorer; + _graph = graph; } - public ResourceDefinition(IContextEntityProvider graph) - { - _contextEntity = graph.GetContextEntity(typeof(TResource)); - _allowedAttributes = _contextEntity.Attributes; - _allowedRelationships = _contextEntity.Relationships; - } public List GetAllowedRelationships() => _allowedRelationships; public List GetAllowedAttributes() => _allowedAttributes; @@ -57,7 +51,7 @@ public ResourceDefinition(IContextEntityProvider graph) /// Should be of the form: (TResource e) => new { e.Attribute1, e.Arttribute2, e.Relationship1, e.Relationship2 } public void HideFields(Expression> selector) { - var fieldsToHide = _fieldExplorer.GetFields(selector); + var fieldsToHide = _graph.GetFields(selector); _allowedAttributes = _allowedAttributes.Except(fieldsToHide.Where(f => f is AttrAttribute)).Cast().ToList(); _allowedRelationships = _allowedRelationships.Except(fieldsToHide.Where(f => f is RelationshipAttribute)).Cast().ToList(); } @@ -164,7 +158,7 @@ public class QueryFilters : Dictionary, Filte { var order = new List<(AttrAttribute, SortDirection)>(); foreach (var sortProp in defaultSortOrder) - order.Add((_fieldExplorer.GetAttributes(sortProp.Item1).Single(), sortProp.Item2)); + order.Add((_graph.GetAttributes(sortProp.Item1).Single(), sortProp.Item2)); return order; } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs index 9673271a94..a5e89c6d8c 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs @@ -14,10 +14,10 @@ namespace JsonApiDotNetCore.Query /// public abstract class QueryParameterService { - protected readonly IContextEntityProvider _contextEntityProvider; + protected readonly IResourceGraphExplorer _contextEntityProvider; protected readonly ContextEntity _requestResource; - protected QueryParameterService(IContextEntityProvider contextEntityProvider, ICurrentRequest currentRequest) + protected QueryParameterService(IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) { _contextEntityProvider = contextEntityProvider; _requestResource = currentRequest.GetRequestResource(); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs index 4c23ee3820..576e7e045f 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs @@ -16,7 +16,7 @@ public class FilterService : QueryParameterService, IFilterService private readonly List _filters; private IResourceDefinition _requestResourceDefinition; - public FilterService(IResourceDefinitionProvider resourceDefinitionProvider, IContextEntityProvider contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public FilterService(IResourceDefinitionProvider resourceDefinitionProvider, IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) { _requestResourceDefinition = resourceDefinitionProvider.Get(_requestResource.EntityType); _filters = new List(); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs index c68c6f4634..1d3405ccb1 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs @@ -14,7 +14,7 @@ public class IncludeService : QueryParameterService, IIncludeService /// todo: use read-only lists. private readonly List> _includedChains; - public IncludeService(IContextEntityProvider contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public IncludeService(IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) { _includedChains = new List>(); } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs index da2dbd7a34..8a3d46d35f 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs @@ -17,7 +17,7 @@ public class SortService : QueryParameterService, ISortService private bool _isProcessed; public SortService(IResourceDefinitionProvider resourceDefinitionProvider, - IContextEntityProvider contextEntityProvider, + IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) { diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index e6518bc7e2..202790d7d6 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -24,7 +24,7 @@ public class SparseFieldsService : QueryParameterService, ISparseFieldsService public override string Name => "fields"; - public SparseFieldsService(IContextEntityProvider contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public SparseFieldsService(IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) { _selectedFields = new List(); _selectedRelationshipFields = new Dictionary>(); diff --git a/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs b/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs index a27009f456..a2b3127085 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs @@ -17,13 +17,12 @@ public class RequestSerializer : BaseDocumentBuilder, IRequestSerializer private readonly Dictionary> _attributesToSerializeCache; private readonly Dictionary> _relationshipsToSerializeCache; private Type _currentTargetedResource; - private readonly IFieldsExplorer _fieldExplorer; - public RequestSerializer(IFieldsExplorer fieldExplorer, - IContextEntityProvider provider, + private readonly IResourceGraphExplorer _graph; + public RequestSerializer(IResourceGraphExplorer graph, IResourceObjectBuilder resourceObjectBuilder) - : base(resourceObjectBuilder, provider) + : base(resourceObjectBuilder, graph) { - _fieldExplorer = fieldExplorer; + _graph = graph; _attributesToSerializeCache = new Dictionary>(); _relationshipsToSerializeCache = new Dictionary>(); } @@ -64,7 +63,7 @@ public string Serialize(IEnumerable entities) public void SetAttributesToSerialize(Expression> filter) where TResource : class, IIdentifiable { - var allowedAttributes = _fieldExplorer.GetAttributes(filter); + var allowedAttributes = _graph.GetAttributes(filter); _attributesToSerializeCache[typeof(TResource)] = allowedAttributes; } @@ -72,7 +71,7 @@ public void SetAttributesToSerialize(Expression(Expression> filter) where TResource : class, IIdentifiable { - var allowedRelationships = _fieldExplorer.GetRelationships(filter); + var allowedRelationships = _graph.GetRelationships(filter); _relationshipsToSerializeCache[typeof(TResource)] = allowedRelationships; } @@ -90,7 +89,7 @@ private List GetAttributesToSerialize(IIdentifiable entity) return new List(); if (!_attributesToSerializeCache.TryGetValue(resourceType, out var attributes)) - return _fieldExplorer.GetAttributes(resourceType); + return _graph.GetAttributes(resourceType); return attributes; } diff --git a/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs b/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs index 0f020600cb..c8ea508bf5 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs @@ -14,19 +14,16 @@ namespace JsonApiDotNetCore.Serialization.Server /// for documents with many resource objects. public class FieldsToSerialize : IFieldsToSerialize { - private readonly IContextEntityProvider _resourceContextProvider; + private readonly IResourceGraphExplorer _graph; private readonly ISparseFieldsService _sparseFieldsService ; private readonly IServiceProvider _provider; private readonly Dictionary _resourceDefinitionCache = new Dictionary(); - private readonly IFieldsExplorer _fieldExplorer; - public FieldsToSerialize(IFieldsExplorer fieldExplorer, - IContextEntityProvider resourceContextProvider, + public FieldsToSerialize(IResourceGraphExplorer graph, ISparseFieldsService sparseFieldsService, IServiceProvider provider) { - _fieldExplorer = fieldExplorer; - _resourceContextProvider = resourceContextProvider; + _graph = graph; _sparseFieldsService = sparseFieldsService; _provider = provider; } @@ -34,7 +31,7 @@ public FieldsToSerialize(IFieldsExplorer fieldExplorer, /// public List GetAllowedAttributes(Type type, RelationshipAttribute relationship = null) { // get the list of all exposed atttributes for the given type. - var allowed = _fieldExplorer.GetAttributes(type); + var allowed = _graph.GetAttributes(type); var resourceDefinition = GetResourceDefinition(type); if (resourceDefinition != null) @@ -64,7 +61,7 @@ public List GetAllowedRelationships(Type type) return resourceDefinition.GetAllowedRelationships(); // The set of allowed attribrutes to be exposed was NOT defined on the resource definition: return all - return _fieldExplorer.GetRelationships(type); + return _graph.GetRelationships(type); } @@ -72,7 +69,7 @@ public List GetAllowedRelationships(Type type) private IResourceDefinition GetResourceDefinition(Type resourceType) { - var resourceDefinitionType = _resourceContextProvider.GetContextEntity(resourceType).ResourceType; + var resourceDefinitionType = _graph.GetContextEntity(resourceType).ResourceType; if (!_resourceDefinitionCache.TryGetValue(resourceDefinitionType, out IResourceDefinition resourceDefinition)) { resourceDefinition = _provider.GetService(resourceDefinitionType) as IResourceDefinition; diff --git a/src/JsonApiDotNetCore/Services/Contract/IFieldExplorer.cs b/src/JsonApiDotNetCore/Services/Contract/IFieldExplorer.cs deleted file mode 100644 index a5db41cd44..0000000000 --- a/src/JsonApiDotNetCore/Services/Contract/IFieldExplorer.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Services -{ - /// - /// Responsible for retrieving the exposed resource fields (attributes and - /// relationships) of registered resources. - /// - public interface IFieldsExplorer - { - /// - /// Gets all fields (attributes and relationships) for - /// that are targeted by the selector. If no selector is provided, all - /// exposed fields are returned. - /// - /// The resource for which to retrieve fields - /// Should be of the form: (TResource e) => new { e.Field1, e.Field2 } - List GetFields(Expression> selector = null) where TResource : IIdentifiable; - /// - /// Gets all attributes for - /// that are targeted by the selector. If no selector is provided, all - /// exposed fields are returned. - /// - /// The resource for which to retrieve attributes - /// Should be of the form: (TResource e) => new { e.Attribute1, e.Arttribute2 } - List GetAttributes(Expression> selector = null) where TResource : IIdentifiable; - /// - /// Gets all relationships for - /// that are targeted by the selector. If no selector is provided, all - /// exposed fields are returned. - /// - /// The resource for which to retrieve relationships - /// Should be of the form: (TResource e) => new { e.Relationship1, e.Relationship2 } - List GetRelationships(Expression> selector = null) where TResource : IIdentifiable; - /// - /// Gets all exposed fields (attributes and relationships) for type - /// - /// The resource type. Must extend IIdentifiable. - List GetFields(Type type); - /// - /// Gets all exposed attributes for type - /// - /// The resource type. Must extend IIdentifiable. - List GetAttributes(Type type); - /// - /// Gets all exposed relationships for type - /// - /// The resource type. Must extend IIdentifiable. - List GetRelationships(Type type); - } -} diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs index 6b78308cbe..58d7ad1b3d 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs @@ -324,7 +324,7 @@ public class EntityResourceService : EntityResourceService { public EntityResourceService(ISortService sortService, IFilterService filterService, IEntityRepository repository, - IJsonApiOptions options,IIncludeService includeService, ISparseFieldsService sparseFieldsService, + IJsonApiOptions options, IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageService pageManager, IContextEntityProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) : base(sortService, filterService, repository, options, includeService, sparseFieldsService, pageManager, provider, hookExecutor, loggerFactory) diff --git a/src/JsonApiDotNetCore/Services/ExposedFieldsExplorer.cs b/src/JsonApiDotNetCore/Services/ExposedFieldsExplorer.cs deleted file mode 100644 index 4db35cd1e2..0000000000 --- a/src/JsonApiDotNetCore/Services/ExposedFieldsExplorer.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using JsonApiDotNetCore.Internal.Contracts; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Services -{ - /// - public class FieldsExplorer : IFieldsExplorer - { - private readonly IContextEntityProvider _provider; - - public FieldsExplorer(IContextEntityProvider provider) - { - _provider = provider; - } - /// - public List GetFields(Expression> selector = null) where T : IIdentifiable - { - return Getter(selector).ToList(); - } - /// - public List GetAttributes(Expression> selector = null) where T : IIdentifiable - { - return Getter(selector, FieldFilterType.Attribute).Cast().ToList(); - } - /// - public List GetRelationships(Expression> selector = null) where T : IIdentifiable - { - return Getter(selector, FieldFilterType.Relationship).Cast().ToList(); - } - /// - public List GetFields(Type type) - { - return _provider.GetContextEntity(type).Fields.ToList(); - } - /// - public List GetAttributes(Type type) - { - return _provider.GetContextEntity(type).Attributes.ToList(); - } - /// - public List GetRelationships(Type type) - { - return _provider.GetContextEntity(type).Relationships.ToList(); - } - - private IEnumerable Getter(Expression> selector = null, FieldFilterType type = FieldFilterType.None) where T : IIdentifiable - { - IEnumerable available; - if (type == FieldFilterType.Attribute) - available = _provider.GetContextEntity(typeof(T)).Attributes.Cast(); - else if (type == FieldFilterType.Relationship) - available = _provider.GetContextEntity(typeof(T)).Relationships.Cast(); - else - available = _provider.GetContextEntity(typeof(T)).Fields; - - if (selector == null) - return available; - - var targeted = new List(); - - if (selector.Body is MemberExpression memberExpression) - { // model => model.Field1 - try - { - targeted.Add(available.Single(f => f.ExposedInternalMemberName == memberExpression.Member.Name)); - return targeted; - } - catch (Exception ex) - { - ThrowNotExposedError(memberExpression.Member.Name, type); - } - } - - - if (selector.Body is NewExpression newExpression) - { // model => new { model.Field1, model.Field2 } - string memberName = null; - try - { - if (newExpression.Members == null) - return targeted; - - foreach (var member in newExpression.Members) - { - memberName = member.Name; - targeted.Add(available.Single(f => f.ExposedInternalMemberName == memberName)); - } - return targeted; - } - catch (Exception ex) - { - ThrowNotExposedError(memberName, type); - } - } - - throw new ArgumentException($"The expression returned by '{selector}' for '{GetType()}' is of type {selector.Body.GetType()}" - + " and cannot be used to select resource attributes. The type must be a NewExpression.Example: article => new { article.Author };"); - - } - - private void ThrowNotExposedError(string memberName, FieldFilterType type) - { - throw new ArgumentException($"{memberName} is not an json:api exposed {type.ToString("g")}."); - } - - /// - /// internally used only by . - /// - private enum FieldFilterType - { - None, - Attribute, - Relationship - } - } -} diff --git a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs index 61234125f5..f4d7c4be16 100644 --- a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs +++ b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs @@ -8,10 +8,10 @@ namespace JsonApiDotNetCore.Query /// internal class ResourceDefinitionProvider : IResourceDefinitionProvider { - private readonly IContextEntityProvider _resourceContextProvider; + private readonly IResourceGraphExplorer _resourceContextProvider; private readonly IScopedServiceProvider _serviceProvider; - public ResourceDefinitionProvider(IContextEntityProvider resourceContextProvider, IScopedServiceProvider serviceProvider) + public ResourceDefinitionProvider(IResourceGraphExplorer resourceContextProvider, IScopedServiceProvider serviceProvider) { _resourceContextProvider = resourceContextProvider; _serviceProvider = serviceProvider; diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index c29ff6ff2b..4ea4f0a651 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -71,7 +71,7 @@ public void AddCurrentAssembly_Adds_Services_To_Container() _services.AddScoped((_) => new Mock().Object); _services.AddScoped((_) => new Mock().Object); _services.AddScoped((_) => new Mock().Object); - _services.AddScoped((_) => new Mock().Object); + _services.AddScoped((_) => new Mock().Object); _facade.AddCurrentAssembly(); // assert @@ -103,7 +103,7 @@ public TestModelService( IJsonApiOptions options, IRequestContext currentRequest, IPageQueryService pageService, - IContextEntityProvider resourceGraph, + IResourceGraphExplorer resourceGraph, ILoggerFactory loggerFactory = null, IResourceHookExecutor hookExecutor = null) : base(repository, options, currentRequest, pageService, resourceGraph, loggerFactory, hookExecutor) { diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs index feba7e7ce5..687b7c2299 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs @@ -22,6 +22,7 @@ using JsonApiDotNetCore.Builders; using JsonApiDotNetCoreExampleTests.Helpers.Models; using JsonApiDotNetCore.Services; +using JsonApiDotNetCore.Internal.Contracts; namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec { @@ -30,7 +31,7 @@ public class SparseFieldSetTests { private TestFixture _fixture; private readonly AppDbContext _dbContext; - private IFieldsExplorer _explorer; + private IResourceGraphExplorer _graph; private Faker _personFaker; private Faker _todoItemFaker; @@ -38,7 +39,7 @@ public SparseFieldSetTests(TestFixture fixture) { _fixture = fixture; _dbContext = fixture.GetService(); - _explorer = fixture.GetService(); + _graph = fixture.GetService(); _personFaker = new Faker() .RuleFor(p => p.FirstName, f => f.Name.FirstName()) .RuleFor(p => p.LastName, f => f.Name.LastName()) @@ -72,7 +73,7 @@ public async Task Can_Select_Sparse_Fieldsets() var query = _dbContext .TodoItems .Where(t => t.Id == todoItem.Id) - .Select(_explorer.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToList()); + .Select(_graph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToList()); var resultSql = StringExtensions.Normalize(query.ToSql()); var result = await query.FirstAsync(); diff --git a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs index 3a793bb6fc..99d002e818 100644 --- a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs @@ -34,7 +34,7 @@ public void Can_Build_ResourceGraph_Using_Builder() var container = services.BuildServiceProvider(); // assert - var resourceGraph = container.GetRequiredService(); + var resourceGraph = container.GetRequiredService(); var dbResource = resourceGraph.GetContextEntity("db-resources"); var nonDbResource = resourceGraph.GetContextEntity("non-db-resources"); Assert.Equal(typeof(DbResource), dbResource.EntityType); diff --git a/test/UnitTests/Builders/LinkBuilderTests.cs b/test/UnitTests/Builders/LinkBuilderTests.cs index 4dc2ec44a8..f0916e1987 100644 --- a/test/UnitTests/Builders/LinkBuilderTests.cs +++ b/test/UnitTests/Builders/LinkBuilderTests.cs @@ -16,7 +16,7 @@ namespace UnitTests public class LinkBuilderTests { private readonly IPageService _pageService; - private readonly Mock _provider = new Mock(); + private readonly Mock _provider = new Mock(); private const string _host = "http://www.example.com"; private const string _topSelf = "http://www.example.com/articles"; private const string _resourceSelf = "http://www.example.com/articles/123"; diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index 834b469f0e..05cccab6a9 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -42,13 +42,13 @@ public void AddJsonApiInternals_Adds_All_Required_Services() // assert var currentRequest = provider.GetService(); Assert.NotNull(currentRequest); - var graph = provider.GetService(); + var graph = provider.GetService(); Assert.NotNull(graph); currentRequest.SetRequestResource(graph.GetContextEntity()); - Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService(typeof(IEntityRepository))); - Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService>()); Assert.NotNull(provider.GetService()); @@ -128,7 +128,7 @@ public void AddJsonApi_With_Context_Uses_DbSet_PropertyName_If_NoOtherSpecified( // assert var provider = services.BuildServiceProvider(); - var graph = provider.GetService(); + var graph = provider.GetService(); var resource = graph.GetContextEntity(typeof(IntResource)); Assert.Equal("resource", resource.EntityName); } diff --git a/test/UnitTests/Models/ResourceDefinitionTests.cs b/test/UnitTests/Models/ResourceDefinitionTests.cs index d15563904d..a69b339604 100644 --- a/test/UnitTests/Models/ResourceDefinitionTests.cs +++ b/test/UnitTests/Models/ResourceDefinitionTests.cs @@ -10,7 +10,7 @@ namespace UnitTests.Models { public class ResourceDefinition_Scenario_Tests { - private readonly IContextEntityProvider _graph; + private readonly IResourceGraphExplorer _graph; public ResourceDefinition_Scenario_Tests() { @@ -58,7 +58,7 @@ public class RequestFilteredResource : ResourceDefinition { // this constructor will be resolved from the container // that means you can take on any dependency that is also defined in the container - public RequestFilteredResource(bool isAdmin) : base(new FieldsExplorer(new ResourceGraphBuilder().AddResource().Build()), new ResourceGraphBuilder().AddResource().Build()) + public RequestFilteredResource(bool isAdmin) : base(new ResourceGraphBuilder().AddResource().Build()) { if (isAdmin) HideFields(m => m.AlwaysExcluded); diff --git a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs index 9cec8ed664..c114e42f65 100644 --- a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs +++ b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs @@ -13,7 +13,7 @@ namespace UnitTests.QueryParameters public class QueryParametersUnitTestCollection { protected readonly ContextEntity _articleResourceContext; - protected readonly IContextEntityProvider _graph; + protected readonly IResourceGraphExplorer _graph; public QueryParametersUnitTestCollection() { diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs index 16c4cc6e2f..4c87de9ed8 100644 --- a/test/UnitTests/ResourceHooks/DiscoveryTests.cs +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -33,7 +33,7 @@ public void Hook_Discovery() public class AnotherDummy : Identifiable { } public abstract class ResourceDefintionBase : ResourceDefinition where T : class, IIdentifiable { - protected ResourceDefintionBase(IContextEntityProvider graph) : base(graph) { } + protected ResourceDefintionBase(IResourceGraphExplorer graph) : base(graph) { } public override IEnumerable BeforeDelete(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs index a778e9ac3a..0eb6fb5173 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs @@ -82,7 +82,7 @@ public void BeforeUpdate_Deleting_Relationship() var personDiscovery = SetDiscoverableHooks(targetHooks, EnableDbValues); var (_, ufMock, hookExecutor, todoResourceMock, ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery, repoDbContextOptions: options); - ufMock.Setup(c => c.Relationships).Returns(_fieldExplorer.GetRelationships((TodoItem t) => t.ToOnePerson)); + ufMock.Setup(c => c.Relationships).Returns(_graph.GetRelationships((TodoItem t) => t.ToOnePerson)); // act var _todoList = new List() { new TodoItem { Id = this.todoList[0].Id } }; diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index a53cc3684d..141272d7cd 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -24,8 +24,7 @@ namespace UnitTests.ResourceHooks { public class HooksDummyData { - protected IFieldsExplorer _fieldExplorer; - protected IContextEntityProvider _graph; + protected IResourceGraphExplorer _graph; protected ResourceHook[] NoHooks = new ResourceHook[0]; protected ResourceHook[] EnableDbValues = { ResourceHook.BeforeUpdate, ResourceHook.BeforeUpdateRelationship }; protected ResourceHook[] DisableDbValues = new ResourceHook[0]; @@ -48,7 +47,7 @@ public HooksDummyData() .AddResource() .Build(); - _fieldExplorer = new FieldsExplorer(_graph); + _todoFaker = new Faker().Rules((f, i) => i.Id = f.UniqueIndex + 1); _personFaker = new Faker().Rules((f, i) => i.Id = f.UniqueIndex + 1); diff --git a/test/UnitTests/Serialization/Client/RequestSerializerTests.cs b/test/UnitTests/Serialization/Client/RequestSerializerTests.cs index 7d697b81eb..68d44bf0b7 100644 --- a/test/UnitTests/Serialization/Client/RequestSerializerTests.cs +++ b/test/UnitTests/Serialization/Client/RequestSerializerTests.cs @@ -16,8 +16,8 @@ public class RequestSerializerTests : SerializerTestsSetup public RequestSerializerTests() { - var builder = new ResourceObjectBuilder(_resourceGraph, new ResourceObjectBuilderSettings()); - _serializer = new RequestSerializer(_fieldExplorer, _resourceGraph, builder); + var builder = new ResourceObjectBuilder(_graph, new ResourceObjectBuilderSettings()); + _serializer = new RequestSerializer(_graph, builder); } [Fact] diff --git a/test/UnitTests/Serialization/Client/ResponseDeserializerTests.cs b/test/UnitTests/Serialization/Client/ResponseDeserializerTests.cs index 5aeef42e3d..02bedf2170 100644 --- a/test/UnitTests/Serialization/Client/ResponseDeserializerTests.cs +++ b/test/UnitTests/Serialization/Client/ResponseDeserializerTests.cs @@ -16,7 +16,7 @@ public class ResponseDeserializerTests : DeserializerTestsSetup public ResponseDeserializerTests() { - _deserializer = new ResponseDeserializer(_resourceGraph); + _deserializer = new ResponseDeserializer(_graph); _linkValues.Add("self", "http://example.com/articles"); _linkValues.Add("next", "http://example.com/articles?page[offset]=2"); _linkValues.Add("last", "http://example.com/articles?page[offset]=10"); diff --git a/test/UnitTests/Serialization/Common/DocumentBuilderTests.cs b/test/UnitTests/Serialization/Common/DocumentBuilderTests.cs index e7bff25cc6..f89667feb0 100644 --- a/test/UnitTests/Serialization/Common/DocumentBuilderTests.cs +++ b/test/UnitTests/Serialization/Common/DocumentBuilderTests.cs @@ -18,7 +18,7 @@ public BaseDocumentBuilderTests() { var mock = new Mock(); mock.Setup(m => m.Build(It.IsAny(), It.IsAny>(), It.IsAny>())).Returns(new ResourceObject()); - _builder = new TestDocumentBuilder(mock.Object, _resourceGraph); + _builder = new TestDocumentBuilder(mock.Object, _graph); } diff --git a/test/UnitTests/Serialization/Common/DocumentParserTests.cs b/test/UnitTests/Serialization/Common/DocumentParserTests.cs index c16daf36c7..27fd20c790 100644 --- a/test/UnitTests/Serialization/Common/DocumentParserTests.cs +++ b/test/UnitTests/Serialization/Common/DocumentParserTests.cs @@ -16,7 +16,7 @@ public class BaseDocumentParserTests : DeserializerTestsSetup public BaseDocumentParserTests() { - _deserializer = new TestDocumentParser(_resourceGraph); + _deserializer = new TestDocumentParser(_graph); } [Fact] @@ -132,7 +132,7 @@ public void DeserializeAttributes_VariousDataTypes_CanDeserialize(string member, var entity = (TestResource)_deserializer.Deserialize(body); // assert - var pi = _resourceGraph.GetContextEntity("test-resource").Attributes.Single(attr => attr.PublicAttributeName == member).PropertyInfo; + var pi = _graph.GetContextEntity("test-resource").Attributes.Single(attr => attr.PublicAttributeName == member).PropertyInfo; var deserializedValue = pi.GetValue(entity); if (member == "int-field") diff --git a/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs index e461ff697c..c0b707d02c 100644 --- a/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs @@ -16,7 +16,7 @@ public class ResourceObjectBuilderTests : SerializerTestsSetup public ResourceObjectBuilderTests() { - _builder = new ResourceObjectBuilder(_resourceGraph, new ResourceObjectBuilderSettings()); + _builder = new ResourceObjectBuilder(_graph, new ResourceObjectBuilderSettings()); } [Fact] @@ -58,7 +58,7 @@ public void EntityToResourceObject_ResourceWithIncludedAttrs_CanBuild(string str { // arrange var entity = new TestResource() { StringField = stringFieldValue, NullableIntField = intFieldValue }; - var attrs = _fieldExplorer.GetAttributes(tr => new { tr.StringField, tr.NullableIntField }); + var attrs = _graph.GetAttributes(tr => new { tr.StringField, tr.NullableIntField }); // act var resourceObject = _builder.Build(entity, attrs); @@ -114,7 +114,7 @@ public void EntityWithRelationshipsToResourceObject_WithIncludedRelationshipsAtt PopulatedToOne = new OneToOneDependent { Id = 10 }, PopulatedToManies = new List { new OneToManyDependent { Id = 20 } } }; - var relationships = _fieldExplorer.GetRelationships(tr => new { tr.PopulatedToManies, tr.PopulatedToOne, tr.EmptyToOne, tr.EmptyToManies }); + var relationships = _graph.GetRelationships(tr => new { tr.PopulatedToManies, tr.PopulatedToOne, tr.EmptyToOne, tr.EmptyToManies }); // act var resourceObject = _builder.Build(entity, relationships: relationships); @@ -138,7 +138,7 @@ public void EntityWithRelationshipsToResourceObject_DeviatingForeignKeyWhileRela { // arrange var entity = new OneToOneDependent { Principal = new OneToOnePrincipal { Id = 10 }, PrincipalId = 123 }; - var relationships = _fieldExplorer.GetRelationships(tr => tr.Principal); + var relationships = _graph.GetRelationships(tr => tr.Principal); // act var resourceObject = _builder.Build(entity, relationships: relationships); @@ -155,7 +155,7 @@ public void EntityWithRelationshipsToResourceObject_DeviatingForeignKeyAndNoNavi { // arrange var entity = new OneToOneDependent { Principal = null, PrincipalId = 123 }; - var relationships = _fieldExplorer.GetRelationships(tr => tr.Principal); + var relationships = _graph.GetRelationships(tr => tr.Principal); // act var resourceObject = _builder.Build(entity, relationships: relationships); @@ -169,7 +169,7 @@ public void EntityWithRequiredRelationshipsToResourceObject_DeviatingForeignKeyW { // arrange var entity = new OneToOneRequiredDependent { Principal = new OneToOnePrincipal { Id = 10 }, PrincipalId = 123 }; - var relationships = _fieldExplorer.GetRelationships(tr => tr.Principal); + var relationships = _graph.GetRelationships(tr => tr.Principal); // act var resourceObject = _builder.Build(entity, relationships: relationships); @@ -186,7 +186,7 @@ public void EntityWithRequiredRelationshipsToResourceObject_DeviatingForeignKeyA { // arrange var entity = new OneToOneRequiredDependent { Principal = null, PrincipalId = 123 }; - var relationships = _fieldExplorer.GetRelationships(tr => tr.Principal); + var relationships = _graph.GetRelationships(tr => tr.Principal); // act & assert Assert.ThrowsAny(() => _builder.Build(entity, relationships: relationships)); @@ -197,7 +197,7 @@ public void EntityWithRequiredRelationshipsToResourceObject_EmptyResourceWhileRe { // arrange var entity = new OneToOneRequiredDependent(); - var relationships = _fieldExplorer.GetRelationships(tr => tr.Principal); + var relationships = _graph.GetRelationships(tr => tr.Principal); // act & assert Assert.ThrowsAny(() => _builder.Build(entity, relationships: relationships)); diff --git a/test/UnitTests/Serialization/DeserializerTestsSetup.cs b/test/UnitTests/Serialization/DeserializerTestsSetup.cs index 1941eace59..b336f4e3d6 100644 --- a/test/UnitTests/Serialization/DeserializerTestsSetup.cs +++ b/test/UnitTests/Serialization/DeserializerTestsSetup.cs @@ -9,7 +9,7 @@ public class DeserializerTestsSetup : SerializationTestsSetupBase { protected class TestDocumentParser : BaseDocumentParser { - public TestDocumentParser(IContextEntityProvider resourceGraph) : base(resourceGraph) { } + public TestDocumentParser(IResourceGraphExplorer resourceGraph) : base(resourceGraph) { } public new object Deserialize(string body) { diff --git a/test/UnitTests/Serialization/SerializationTestsSetupBase.cs b/test/UnitTests/Serialization/SerializationTestsSetupBase.cs index 6c7d7fcbd8..3e7cce56fb 100644 --- a/test/UnitTests/Serialization/SerializationTestsSetupBase.cs +++ b/test/UnitTests/Serialization/SerializationTestsSetupBase.cs @@ -8,8 +8,7 @@ namespace UnitTests.Serialization { public class SerializationTestsSetupBase { - protected IContextEntityProvider _resourceGraph; - protected IContextEntityProvider _provider; + protected IResourceGraphExplorer _graph; protected readonly Faker _foodFaker; protected readonly Faker _songFaker; protected readonly Faker
    _articleFaker; @@ -18,7 +17,7 @@ public class SerializationTestsSetupBase public SerializationTestsSetupBase() { - _resourceGraph = BuildGraph(); + _graph = BuildGraph(); _articleFaker = new Faker
    () .RuleFor(f => f.Title, f => f.Hacker.Phrase()) .RuleFor(f => f.Id, f => f.UniqueIndex + 1); @@ -36,7 +35,7 @@ public SerializationTestsSetupBase() .RuleFor(f => f.Id, f => f.UniqueIndex + 1); } - protected IContextEntityProvider BuildGraph() + protected IResourceGraphExplorer BuildGraph() { var resourceGraphBuilder = new ResourceGraphBuilder(); resourceGraphBuilder.AddResource("test-resource"); diff --git a/test/UnitTests/Serialization/SerializerTestsSetup.cs b/test/UnitTests/Serialization/SerializerTestsSetup.cs index 52140acf62..c0a7c395ad 100644 --- a/test/UnitTests/Serialization/SerializerTestsSetup.cs +++ b/test/UnitTests/Serialization/SerializerTestsSetup.cs @@ -18,13 +18,11 @@ namespace UnitTests.Serialization { public class SerializerTestsSetup : SerializationTestsSetupBase { - protected readonly IFieldsExplorer _fieldExplorer; protected readonly TopLevelLinks _dummyToplevelLinks; protected readonly ResourceLinks _dummyResourceLinks; protected readonly RelationshipLinks _dummyRelationshipLinks; public SerializerTestsSetup() { - _fieldExplorer = new FieldsExplorer(_resourceGraph); _dummyToplevelLinks = new TopLevelLinks { Self = "http://www.dummy.com/dummy-self-link", @@ -52,7 +50,7 @@ protected ResponseSerializer GetResponseSerializer(List(meta, link, includedBuilder, fieldsToSerialize, resourceObjectBuilder, provider); } @@ -61,12 +59,12 @@ protected ResponseResourceObjectBuilder GetResponseResourceObjectBuilder(List
  • GetMetaBuilder(Dictionary meta = null) where T : class, IIdentifiable @@ -91,7 +89,7 @@ protected IMetaBuilder GetMetaBuilder(Dictionary meta = nu protected ICurrentRequest GetRequestManager() where T : class, IIdentifiable { var mock = new Mock(); - mock.Setup(m => m.GetRequestResource()).Returns(_resourceGraph.GetContextEntity()); + mock.Setup(m => m.GetRequestResource()).Returns(_graph.GetContextEntity()); return mock.Object; } @@ -113,8 +111,8 @@ protected ISparseFieldsService GetFieldsQuery() protected IFieldsToSerialize GetSerializableFields() { var mock = new Mock(); - mock.Setup(m => m.GetAllowedAttributes(It.IsAny(), It.IsAny())).Returns((t, r) => _resourceGraph.GetContextEntity(t).Attributes); - mock.Setup(m => m.GetAllowedRelationships(It.IsAny())).Returns(t => _resourceGraph.GetContextEntity(t).Relationships); + mock.Setup(m => m.GetAllowedAttributes(It.IsAny(), It.IsAny())).Returns((t, r) => _graph.GetContextEntity(t).Attributes); + mock.Setup(m => m.GetAllowedRelationships(It.IsAny())).Returns(t => _graph.GetContextEntity(t).Relationships); return mock.Object; } diff --git a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs index 13e079c927..5fe64a9ba4 100644 --- a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs @@ -157,13 +157,13 @@ public void BuildIncluded_DuplicateChildrenMultipleChains_OnceInOutput() private List GetIncludedRelationshipsChain(string chain) { var parsedChain = new List(); - var resourceContext = _resourceGraph.GetContextEntity
    (); + var resourceContext = _graph.GetContextEntity
    (); var splittedPath = chain.Split(QueryConstants.DOT); foreach (var requestedRelationship in splittedPath) { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); + resourceContext = _graph.GetContextEntity(relationship.DependentType); } return parsedChain; } @@ -172,7 +172,7 @@ private IncludedResourceObjectBuilder GetBuilder() { var fields = GetSerializableFields(); var links = GetLinkBuilder(); - return new IncludedResourceObjectBuilder(fields, links, _resourceGraph, GetSerializerSettingsProvider()); + return new IncludedResourceObjectBuilder(fields, links, _graph, GetSerializerSettingsProvider()); } } diff --git a/test/UnitTests/Serialization/Server/RequestDeserializerTests.cs b/test/UnitTests/Serialization/Server/RequestDeserializerTests.cs index 2e4fa17e2e..35815a2d32 100644 --- a/test/UnitTests/Serialization/Server/RequestDeserializerTests.cs +++ b/test/UnitTests/Serialization/Server/RequestDeserializerTests.cs @@ -18,7 +18,7 @@ public class RequestDeserializerTests : DeserializerTestsSetup private readonly Mock _fieldsManagerMock = new Mock(); public RequestDeserializerTests() : base() { - _deserializer = new RequestDeserializer(_resourceGraph, _fieldsManagerMock.Object); + _deserializer = new RequestDeserializer(_graph, _fieldsManagerMock.Object); } [Fact] diff --git a/test/UnitTests/Serialization/Server/ResponseResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Server/ResponseResourceObjectBuilderTests.cs index 59912ae952..8d48195921 100644 --- a/test/UnitTests/Serialization/Server/ResponseResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Server/ResponseResourceObjectBuilderTests.cs @@ -14,7 +14,7 @@ public class ResponseResourceObjectBuilderTests : SerializerTestsSetup public ResponseResourceObjectBuilderTests() { - _relationshipsForBuild = _fieldExplorer.GetRelationships(e => new { e.Dependents }); + _relationshipsForBuild = _graph.GetRelationships(e => new { e.Dependents }); } [Fact] diff --git a/test/UnitTests/Serialization/Server/ResponseSerializerTests.cs b/test/UnitTests/Serialization/Server/ResponseSerializerTests.cs index 7218c5ff50..56b252ea52 100644 --- a/test/UnitTests/Serialization/Server/ResponseSerializerTests.cs +++ b/test/UnitTests/Serialization/Server/ResponseSerializerTests.cs @@ -89,7 +89,7 @@ public void SerializeSingle_ResourceWithIncludedRelationships_CanSerialize() PopulatedToOne = new OneToOneDependent { Id = 10 }, PopulatedToManies = new List { new OneToManyDependent { Id = 20 } } }; - var chain = _fieldExplorer.GetRelationships().Select(r => new List { r }).ToList(); + var chain = _graph.GetRelationships().Select(r => new List { r }).ToList(); var serializer = GetResponseSerializer(inclusionChains: chain); // act @@ -152,13 +152,13 @@ public void SerializeSingle_ResourceWithDeeplyIncludedRelationships_CanSerialize PopulatedToManies = new List { includedEntity } }; - var chains = _fieldExplorer.GetRelationships() + var chains = _graph.GetRelationships() .Select(r => { var chain = new List { r }; if (r.PublicRelationshipName != "populated-to-manies") return new List { r }; - chain.AddRange(_fieldExplorer.GetRelationships()); + chain.AddRange(_graph.GetRelationships()); return chain; }).ToList(); @@ -365,7 +365,7 @@ public void SerializeSingleWithRequestRelationship_NullToOneRelationship_CanSeri // arrange var entity = new OneToOnePrincipal() { Id = 2, Dependent = null }; var serializer = GetResponseSerializer(); - var requestRelationship = _fieldExplorer.GetRelationships((OneToOnePrincipal t) => t.Dependent).First(); + var requestRelationship = _graph.GetRelationships((OneToOnePrincipal t) => t.Dependent).First(); serializer.RequestRelationship = requestRelationship; // act @@ -383,7 +383,7 @@ public void SerializeSingleWithRequestRelationship_PopulatedToOneRelationship_Ca // arrange var entity = new OneToOnePrincipal() { Id = 2, Dependent = new OneToOneDependent { Id = 1 } }; var serializer = GetResponseSerializer(); - var requestRelationship = _fieldExplorer.GetRelationships((OneToOnePrincipal t) => t.Dependent).First(); + var requestRelationship = _graph.GetRelationships((OneToOnePrincipal t) => t.Dependent).First(); serializer.RequestRelationship = requestRelationship; @@ -410,7 +410,7 @@ public void SerializeSingleWithRequestRelationship_EmptyToManyRelationship_CanSe // arrange var entity = new OneToManyPrincipal() { Id = 2, Dependents = new List() }; var serializer = GetResponseSerializer(); - var requestRelationship = _fieldExplorer.GetRelationships((OneToManyPrincipal t) => t.Dependents).First(); + var requestRelationship = _graph.GetRelationships((OneToManyPrincipal t) => t.Dependents).First(); serializer.RequestRelationship = requestRelationship; @@ -427,9 +427,9 @@ public void SerializeSingleWithRequestRelationship_EmptyToManyRelationship_CanSe public void SerializeSingleWithRequestRelationship_PopulatedToManyRelationship_CanSerialize() { // arrange - var entity = new OneToManyPrincipal() { Id = 2, Dependents = new List { new OneToManyDependent { Id = 1 } } }; + var entity = new OneToManyPrincipal() { Id = 2, Dependents = new List { new OneToManyDependent { Id = 1 } } }; var serializer = GetResponseSerializer(); - var requestRelationship = _fieldExplorer.GetRelationships((OneToManyPrincipal t) => t.Dependents).First(); + var requestRelationship = _graph.GetRelationships((OneToManyPrincipal t) => t.Dependents).First(); serializer.RequestRelationship = requestRelationship; diff --git a/test/UnitTests/Services/EntityResourceService_Tests.cs b/test/UnitTests/Services/EntityResourceService_Tests.cs index aef33122d3..b9e46e5a45 100644 --- a/test/UnitTests/Services/EntityResourceService_Tests.cs +++ b/test/UnitTests/Services/EntityResourceService_Tests.cs @@ -25,7 +25,7 @@ public class EntityResourceService_Tests private readonly Mock _crMock; private readonly Mock _pgsMock; private readonly Mock _ufMock; - private readonly IContextEntityProvider _resourceGraph; + private readonly IResourceGraphExplorer _resourceGraph; public EntityResourceService_Tests() { From 76cf8965ca6d14059dc2e4170eb1a1c25e5c74dd Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 19:14:55 +0200 Subject: [PATCH 09/29] style: cleanup of variable names --- .../Builders/IResourceGraphBuilder.cs | 1 - .../Builders/JsonApiApplicationBuilder.cs | 1 - .../Builders/ResourceGraphBuilder.cs | 11 --------- .../Hooks/ResourceHookExecutor.cs | 6 ++--- .../Contracts/IContextEntityProvider.cs | 7 ++++++ .../Internal/InverseRelationships.cs | 23 +------------------ .../Internal/ResourceGraph.cs | 9 ++++++++ .../ResourceHooks/ResourceHooksTestsSetup.cs | 16 ++++++------- 8 files changed, 28 insertions(+), 46 deletions(-) diff --git a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs index d0cdc0978b..6368de1ddb 100644 --- a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs @@ -1,6 +1,5 @@ using System; using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index 4f2a56e294..10a89a7b12 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -38,7 +38,6 @@ public JsonApiApplicationBuilder(IServiceCollection services, IMvcCoreBuilder mv _mvcBuilder = mvcBuilder; } - public void ConfigureJsonApiOptions(Action configureOptions) => configureOptions(JsonApiOptions); public void ConfigureMvc() diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index f8c871affd..2661592503 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -19,9 +19,6 @@ public class ResourceGraphBuilder : IResourceGraphBuilder { private readonly List _entities = new List(); private readonly List _validationResults = new List(); - private readonly Dictionary> _controllerMapper = new Dictionary>() { }; - private readonly List _undefinedMapper = new List() { }; - private bool _usesDbContext; private readonly IResourceNameFormatter _resourceNameFormatter = new KebabCaseFormatter(); public ResourceGraphBuilder() { } @@ -184,25 +181,17 @@ protected virtual Type GetRelationshipType(RelationshipAttribute relation, Prope /// public IResourceGraphBuilder AddDbContext() where T : DbContext { - _usesDbContext = true; - var contextType = typeof(T); - var contextProperties = contextType.GetProperties(); - foreach (var property in contextProperties) { var dbSetType = property.PropertyType; - if (dbSetType.GetTypeInfo().IsGenericType && dbSetType.GetGenericTypeDefinition() == typeof(DbSet<>)) { var entityType = dbSetType.GetGenericArguments()[0]; - AssertEntityIsNotAlreadyDefined(entityType); - var (isJsonApiResource, idType) = GetIdType(entityType); - if (isJsonApiResource) _entities.Add(GetEntity(GetResourceNameFromDbSetProperty(property, entityType), entityType, idType)); } diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index da53d991ef..9146056ab2 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -21,19 +21,19 @@ internal class ResourceHookExecutor : IResourceHookExecutor private readonly ITraversalHelper _traversalHelper; private readonly IIncludeService _includeService; private readonly ITargetedFields _targetedFields; - private readonly IInverseRelationships _inverseRelationships; + private readonly IResourceGraphExplorer _inverseRelationships; public ResourceHookExecutor( IHookExecutorHelper executorHelper, ITraversalHelper traversalHelper, ITargetedFields targetedFields, IIncludeService includedRelationships, - IInverseRelationships inverseRelationships) + IResourceGraphExplorer graph) { _executorHelper = executorHelper; _traversalHelper = traversalHelper; _targetedFields = targetedFields; _includeService = includedRelationships; - _inverseRelationships = inverseRelationships; + _inverseRelationships = graph; } /// diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs index ae1fecd181..30c7134702 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs @@ -52,6 +52,13 @@ public interface IResourceGraphExplorer : IContextEntityProvider ///
  • /// The resource type. Must extend IIdentifiable. List GetRelationships(Type type); + + /// + /// Traverses the resource graph for the inverse relationship of the provided + /// ; + /// + /// + RelationshipAttribute GetInverse(RelationshipAttribute relationship); } /// diff --git a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs index 560fd50d0d..91434f50ce 100644 --- a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs +++ b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; @@ -26,13 +24,6 @@ public interface IInverseRelationships /// void Resolve(); - - /// - /// Traverses the resource graph for the inverse relationship of the provided - /// ; - /// - /// - RelationshipAttribute GetInverse(RelationshipAttribute relationship); } /// @@ -47,15 +38,6 @@ public InverseRelationships(IContextEntityProvider provider, IDbContextResolver _resolver = resolver; } - /// - public RelationshipAttribute GetInverse(RelationshipAttribute relationship) - { - if (relationship.InverseNavigation == null) return null; - return _provider.GetContextEntity(relationship.DependentType) - .Relationships - .SingleOrDefault(r => r.InternalRelationshipName == relationship.InverseNavigation); - } - /// public void Resolve() { @@ -81,9 +63,6 @@ public void Resolve() /// If EF Core is not being used, we're expecting the resolver to not be registered. ///
    /// true, if entity framework core was enabled, false otherwise. - private bool EntityFrameworkCoreIsEnabled() - { - return _resolver != null; - } + private bool EntityFrameworkCoreIsEnabled() => _resolver != null; } } diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index 81a1bcba8e..e4ac4e590b 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -66,6 +66,15 @@ public List GetRelationships(Type type) return GetContextEntity(type).Relationships.ToList(); } + /// + public RelationshipAttribute GetInverse(RelationshipAttribute relationship) + { + if (relationship.InverseNavigation == null) return null; + return GetContextEntity(relationship.DependentType) + .Relationships + .SingleOrDefault(r => r.InternalRelationshipName == relationship.InverseNavigation); + } + private IEnumerable Getter(Expression> selector = null, FieldFilterType type = FieldFilterType.None) where T : IIdentifiable { IEnumerable available; diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 141272d7cd..6cd495a5c4 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -141,13 +141,13 @@ protected List CreateTodoWithOwner() public class HooksTestsSetup : HooksDummyData { - (IInverseRelationships, Mock, Mock, Mock, IJsonApiOptions) CreateMocks() + (Mock, Mock, Mock, IJsonApiOptions) CreateMocks() { var pfMock = new Mock(); var ufMock = new Mock(); var iqsMock = new Mock(); var optionsMock = new JsonApiOptions { LoaDatabaseValues = false }; - return (new InverseRelationships(_graph), ufMock, iqsMock, pfMock, optionsMock); + return (ufMock, iqsMock, pfMock, optionsMock); } internal (Mock, ResourceHookExecutor, Mock>) CreateTestObjects(IHooksDiscovery mainDiscovery = null) @@ -157,13 +157,13 @@ public class HooksTestsSetup : HooksDummyData var mainResource = CreateResourceDefinition(mainDiscovery); // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. - var (inverse, ufMock, iqMock, gpfMock, options) = CreateMocks(); + var (ufMock, iqMock, gpfMock, options) = CreateMocks(); SetupProcessorFactoryForResourceDefinition(gpfMock, mainResource.Object, mainDiscovery, null); var execHelper = new HookExecutorHelper(gpfMock.Object, options); var traversalHelper = new TraversalHelper(_graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, inverse); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _graph); return (iqMock, hookExecutor, mainResource); } @@ -182,7 +182,7 @@ public class HooksTestsSetup : HooksDummyData var nestedResource = CreateResourceDefinition(nestedDiscovery); // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. - var (inverse, ufMock, iqMock, gpfMock, options) = CreateMocks(); + var (ufMock, iqMock, gpfMock, options) = CreateMocks(); var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; @@ -191,7 +191,7 @@ public class HooksTestsSetup : HooksDummyData var execHelper = new HookExecutorHelper(gpfMock.Object, options); var traversalHelper = new TraversalHelper(_graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, inverse); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _graph); return (iqMock, ufMock, hookExecutor, mainResource, nestedResource); } @@ -213,7 +213,7 @@ public class HooksTestsSetup : HooksDummyData var secondNestedResource = CreateResourceDefinition(secondNestedDiscovery); // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. - var (inverse, ufMock, iqMock, gpfMock, options) = CreateMocks(); + var (ufMock, iqMock, gpfMock, options) = CreateMocks(); var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; @@ -223,7 +223,7 @@ public class HooksTestsSetup : HooksDummyData var execHelper = new HookExecutorHelper(gpfMock.Object, options); var traversalHelper = new TraversalHelper(_graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, inverse); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _graph); return (iqMock, hookExecutor, mainResource, firstNestedResource, secondNestedResource); } From 95d870755c3cdbf588501d909ce68246d753d3dc Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 21 Oct 2019 19:31:44 +0200 Subject: [PATCH 10/29] chore: various fixes for PR review, mostly style and variable naming --- .../Internal/Contracts/IResourceGraph.cs | 12 - .../Contracts/IResourceGraphExplorer.cs | 61 +++ .../Internal/IDefaultRoutingConvention.cs | 11 + .../using JsonApiDotNetCore.Controllers; | 346 ------------------ 4 files changed, 72 insertions(+), 358 deletions(-) delete mode 100644 src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs create mode 100644 src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs create mode 100644 src/JsonApiDotNetCore/Internal/IDefaultRoutingConvention.cs delete mode 100644 wiki/v4/content/using JsonApiDotNetCore.Controllers; diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs deleted file mode 100644 index fb5166f21b..0000000000 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraph.cs +++ /dev/null @@ -1,12 +0,0 @@ -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Internal.Contracts -{ - /// - /// A cache for the models in entity core - /// - public interface IResourceGraph : IResourceGraphExplorer - { - - } -} diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs new file mode 100644 index 0000000000..30f9a4b044 --- /dev/null +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Internal.Contracts +{ + /// + /// Responsible for retrieving the exposed resource fields (attributes and + /// relationships) of registered resources in the resource graph. + /// + public interface IResourceGraphExplorer : IContextEntityProvider + { + /// + /// Gets all fields (attributes and relationships) for + /// that are targeted by the selector. If no selector is provided, all + /// exposed fields are returned. + /// + /// The resource for which to retrieve fields + /// Should be of the form: (TResource e) => new { e.Field1, e.Field2 } + List GetFields(Expression> selector = null) where TResource : IIdentifiable; + /// + /// Gets all attributes for + /// that are targeted by the selector. If no selector is provided, all + /// exposed fields are returned. + /// + /// The resource for which to retrieve attributes + /// Should be of the form: (TResource e) => new { e.Attribute1, e.Arttribute2 } + List GetAttributes(Expression> selector = null) where TResource : IIdentifiable; + /// + /// Gets all relationships for + /// that are targeted by the selector. If no selector is provided, all + /// exposed fields are returned. + /// + /// The resource for which to retrieve relationships + /// Should be of the form: (TResource e) => new { e.Relationship1, e.Relationship2 } + List GetRelationships(Expression> selector = null) where TResource : IIdentifiable; + /// + /// Gets all exposed fields (attributes and relationships) for type + /// + /// The resource type. Must extend IIdentifiable. + List GetFields(Type type); + /// + /// Gets all exposed attributes for type + /// + /// The resource type. Must extend IIdentifiable. + List GetAttributes(Type type); + /// + /// Gets all exposed relationships for type + /// + /// The resource type. Must extend IIdentifiable. + List GetRelationships(Type type); + + /// + /// Traverses the resource graph for the inverse relationship of the provided + /// ; + /// + /// + RelationshipAttribute GetInverse(RelationshipAttribute relationship); + } +} diff --git a/src/JsonApiDotNetCore/Internal/IDefaultRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/IDefaultRoutingConvention.cs new file mode 100644 index 0000000000..d6a48e221a --- /dev/null +++ b/src/JsonApiDotNetCore/Internal/IDefaultRoutingConvention.cs @@ -0,0 +1,11 @@ +// REF: https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.CustomRoutingConvention/NameSpaceRoutingConvention.cs +// REF: https://github.com/aspnet/Mvc/issues/5691 +using System; + +namespace JsonApiDotNetCore.Internal +{ + public interface IControllerResourceMapping + { + Type GetAssociatedResource(string controllerName); + } +} \ No newline at end of file diff --git a/wiki/v4/content/using JsonApiDotNetCore.Controllers; b/wiki/v4/content/using JsonApiDotNetCore.Controllers; deleted file mode 100644 index 58fbcc748a..0000000000 --- a/wiki/v4/content/using JsonApiDotNetCore.Controllers; +++ /dev/null @@ -1,346 +0,0 @@ -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Services; -using Moq; -using Xunit; -using System.Threading.Tasks; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Internal; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using JsonApiDotNetCore.Internal.Contracts; -using System.IO; - -namespace UnitTests -{ - public class BaseJsonApiController_Tests - { - public class Resource : Identifiable - { - [Attr("test-attribute")] public string TestAttribute { get; set; } - } - private Mock _resourceGraph = new Mock(); - private Mock _resourceGraphMock = new Mock(); - - [Fact] - public async Task GetAsync_Calls_Service() - { - // arrange - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, getAll: serviceMock.Object); - - // act - await controller.GetAsync(); - - // assert - serviceMock.Verify(m => m.GetAsync(), Times.Once); - - } - - [Fact] - public async Task GetAsync_Throws_405_If_No_Service() - { - // arrange - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, null); - - // act - var exception = await Assert.ThrowsAsync(() => controller.GetAsync()); - - // assert - Assert.Equal(405, exception.GetStatusCode()); - } - - [Fact] - public async Task GetAsyncById_Calls_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, getById: serviceMock.Object); - - // act - await controller.GetAsync(id); - - // assert - serviceMock.Verify(m => m.GetAsync(id), Times.Once); - - } - - [Fact] - public async Task GetAsyncById_Throws_405_If_No_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, getById: null); - - // act - var exception = await Assert.ThrowsAsync(() => controller.GetAsync(id)); - - // assert - Assert.Equal(405, exception.GetStatusCode()); - } - - [Fact] - public async Task GetRelationshipsAsync_Calls_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, getRelationships: serviceMock.Object); - - // act - await controller.GetRelationshipsAsync(id, string.Empty); - - // assert - serviceMock.Verify(m => m.GetRelationshipsAsync(id, string.Empty), Times.Once); - } - - [Fact] - public async Task GetRelationshipsAsync_Throws_405_If_No_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, getRelationships: null); - - // act - var exception = await Assert.ThrowsAsync(() => controller.GetRelationshipsAsync(id, string.Empty)); - - // assert - Assert.Equal(405, exception.GetStatusCode()); - } - - [Fact] - public async Task GetRelationshipAsync_Calls_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, getRelationship: serviceMock.Object); - - // act - await controller.GetRelationshipAsync(id, string.Empty); - - // assert - serviceMock.Verify(m => m.GetRelationshipAsync(id, string.Empty), Times.Once); - } - - [Fact] - public async Task GetRelationshipAsync_Throws_405_If_No_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, getRelationship: null); - - // act - var exception = await Assert.ThrowsAsync(() => controller.GetRelationshipAsync(id, string.Empty)); - - // assert - Assert.Equal(405, exception.GetStatusCode()); - } - - [Fact] - public async Task PatchAsync_Calls_Service() - { - // arrange - const int id = 0; - var resource = new Resource(); - var serviceMock = new Mock>(); - //_resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - - - var controller = new BaseJsonApiController(new JsonApiOptions(), update: serviceMock.Object); - - // act - await controller.PatchAsync(id, resource); - - // assert - serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny()), Times.Once); - } - - [Fact] - public async Task PatchAsync_ModelStateInvalid_ValidateModelStateDisbled() - { - // arrange - const int id = 0; - var resource = new Resource(); - var serviceMock = new Mock>(); - //_resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - - var controller = new BaseJsonApiController(new JsonApiOptions(), update: serviceMock.Object); - - // act - var response = await controller.PatchAsync(id, resource); - - // assert - serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny()), Times.Once); - Assert.IsNotType(response); - } - - [Fact] - public async Task PatchAsync_ModelStateInvalid_ValidateModelStateEnabled() - { - // arrange - const int id = 0; - var resource = new Resource(); - var serviceMock = new Mock>(); - _resourceGraphMock.Setup(a => a.GetPublicAttributeName("TestAttribute")).Returns("test-attribute"); -// _resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - - var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = true }, update: serviceMock.Object); - controller.ModelState.AddModelError("TestAttribute", "Failed Validation"); - - // act - var response = await controller.PatchAsync(id, resource); - - // assert - serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny()), Times.Never); - Assert.IsType(response); - Assert.IsType(((UnprocessableEntityObjectResult) response).Value); - } - - [Fact] - public async Task PatchAsync_Throws_405_If_No_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, update: null); - - // act - var exception = await Assert.ThrowsAsync(() => controller.PatchAsync(id, It.IsAny())); - - // assert - Assert.Equal(405, exception.GetStatusCode()); - } - - [Fact] - public async Task PostAsync_Calls_Service() - { - // arrange - var resource = new Resource(); - var serviceMock = new Mock>(); -// _resourceGraph.Setup(a => a.ApplyContext(It.IsAny>())).Returns(_resourceGraph.Object); - - var controller = new BaseJsonApiController(new JsonApiOptions(), create: serviceMock.Object); - serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); - controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext {HttpContext = new DefaultHttpContext()}; - - // act - await controller.PostAsync(resource); - - // assert - serviceMock.Verify(m => m.CreateAsync(It.IsAny()), Times.Once); - } - - [Fact] - public async Task PostAsync_ModelStateInvalid_ValidateModelStateDisabled() - { - // arrange - var resource = new Resource(); - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = false }, create: serviceMock.Object); - controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext { HttpContext = new DefaultHttpContext() }; - serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); - - - // act - var response = await controller.PostAsync(resource); - - // assert - serviceMock.Verify(m => m.CreateAsync(It.IsAny()), Times.Once); - Assert.IsNotType(response); - } - - [Fact] - public async Task PostAsync_ModelStateInvalid_ValidateModelStateEnabled() - { - // arrange - var resource = new Resource(); - var serviceMock = new Mock>(); - _resourceGraphMock.Setup(a => a.GetPublicAttributeName("TestAttribute")).Returns("test-attribute"); - var controller = new BaseJsonApiController(new JsonApiOptions { ValidateModelState = true }, create: serviceMock.Object); - controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext { HttpContext = new DefaultHttpContext() }; - controller.ModelState.AddModelError("TestAttribute", "Failed Validation"); - serviceMock.Setup(m => m.CreateAsync(It.IsAny())).ReturnsAsync(resource); - - - // act - var response = await controller.PostAsync(resource); - - // assert - serviceMock.Verify(m => m.CreateAsync(It.IsAny()), Times.Never); - Assert.IsType(response); - Assert.IsType(((UnprocessableEntityObjectResult)response).Value); - } - - [Fact] - public async Task PatchRelationshipsAsync_Calls_Service() - { - // arrange - const int id = 0; - var resource = new Resource(); - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, updateRelationships: serviceMock.Object); - - // act - await controller.PatchRelationshipsAsync(id, string.Empty, null); - - // assert - serviceMock.Verify(m => m.UpdateRelationshipsAsync(id, string.Empty, null), Times.Once); - } - - [Fact] - public async Task PatchRelationshipsAsync_Throws_405_If_No_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, updateRelationships: null); - - // act - var exception = await Assert.ThrowsAsync(() => controller.PatchRelationshipsAsync(id, string.Empty, null)); - - // assert - Assert.Equal(405, exception.GetStatusCode()); - } - - [Fact] - public async Task DeleteAsync_Calls_Service() - { - // Arrange - const int id = 0; - var resource = new Resource(); - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, delete: serviceMock.Object); - - // Act - await controller.DeleteAsync(id); - - // Assert - serviceMock.Verify(m => m.DeleteAsync(id), Times.Once); - } - - [Fact] - public async Task DeleteAsync_Throws_405_If_No_Service() - { - // arrange - const int id = 0; - var serviceMock = new Mock>(); - var controller = new BaseJsonApiController(new Mock().Object, - - delete: null); - - // act - var exception = await Assert.ThrowsAsync(() => controller.DeleteAsync(id)); - - // assert - Assert.Equal(405, exception.GetStatusCode()); - } - - - } -} From a7618f6fb44f9cdae9e42c1faebde2e3d7a792e8 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 09:55:22 +0200 Subject: [PATCH 11/29] style: rename IResourceGraphExplorer back to IResourceGraph, consitent usage of IContextEntityProvider --- .../Resources/ArticleResource.cs | 2 +- .../Resources/LockableResource.cs | 2 +- .../Resources/PassportResource.cs | 2 +- .../Resources/PersonResource.cs | 2 +- .../Resources/TagResource.cs | 2 +- .../Resources/TodoResource.cs | 2 +- .../Resources/UserResource.cs | 2 +- src/Examples/ReportsExample/Startup.cs | 2 +- .../Builders/IResourceGraphBuilder.cs | 2 +- .../Builders/JsonApiApplicationBuilder.cs | 8 +-- .../Builders/ResourceGraphBuilder.cs | 8 +-- .../Data/DefaultEntityRepository.cs | 16 +++--- .../IApplicationBuilderExtensions.cs | 2 +- .../IServiceCollectionExtensions.cs | 4 +- .../Graph/ServiceDiscoveryFacade.cs | 10 ++-- .../Hooks/ResourceHookExecutor.cs | 6 +- .../Hooks/Traversal/TraversalHelper.cs | 11 ++-- .../Contracts/IContextEntityProvider.cs | 55 ------------------- .../Contracts/IResourceGraphExplorer.cs | 6 +- .../Internal/InverseRelationships.cs | 2 +- .../Internal/ResourceGraph.cs | 2 +- .../Middleware/RequestMiddleware.cs | 4 +- .../Models/ResourceDefinition.cs | 12 ++-- .../Common/QueryParameterService.cs | 4 +- .../QueryParameterServices/FilterService.cs | 2 +- .../QueryParameterServices/IncludeService.cs | 2 +- .../QueryParameterServices/SortService.cs | 2 +- .../SparseFieldsService.cs | 2 +- .../Serialization/Client/RequestSerializer.cs | 14 ++--- .../Serialization/Server/FieldsToSerialize.cs | 36 +++--------- .../Services/ResourceDefinitionProvider.cs | 4 +- .../ServiceDiscoveryFacadeTests.cs | 20 +++---- .../Acceptance/Spec/CreatingDataTests.cs | 4 +- .../Spec/DeeplyNestedInclusionTests.cs | 4 +- .../Acceptance/Spec/SparseFieldSetTests.cs | 10 ++-- .../Acceptance/TestFixture.cs | 4 +- .../Builders/ContextGraphBuilder_Tests.cs | 22 ++++---- test/UnitTests/Builders/LinkBuilderTests.cs | 2 +- .../Data/DefaultEntityRepository_Tests.cs | 4 +- .../IServiceCollectionExtensionsTests.cs | 14 ++--- .../Models/ResourceDefinitionTests.cs | 4 +- .../QueryParameters/FilterServiceTests.cs | 2 +- .../QueryParameters/IncludeServiceTests.cs | 4 +- .../QueryParametersUnitTestCollection.cs | 6 +- .../QueryParameters/SortServiceTests.cs | 2 +- .../SparseFieldsServiceTests.cs | 2 +- .../UnitTests/ResourceHooks/DiscoveryTests.cs | 2 +- .../Update/BeforeUpdate_WithDbValues_Tests.cs | 2 +- .../ResourceHooks/ResourceHooksTestsSetup.cs | 22 ++++---- .../Client/RequestSerializerTests.cs | 4 +- .../Client/ResponseDeserializerTests.cs | 2 +- .../Common/DocumentBuilderTests.cs | 2 +- .../Common/DocumentParserTests.cs | 4 +- .../Common/ResourceObjectBuilderTests.cs | 16 +++--- .../Serialization/DeserializerTestsSetup.cs | 2 +- .../SerializationTestsSetupBase.cs | 6 +- .../Serialization/SerializerTestsSetup.cs | 16 +++--- .../IncludedResourceObjectBuilderTests.cs | 6 +- .../Server/RequestDeserializerTests.cs | 2 +- .../ResponseResourceObjectBuilderTests.cs | 2 +- .../Server/ResponseSerializerTests.cs | 14 ++--- .../Services/EntityResourceService_Tests.cs | 2 +- 62 files changed, 182 insertions(+), 256 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs index c8fa44cf13..9a36eb27dc 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class ArticleResource : ResourceDefinition
    { - public ArticleResource(IResourceGraphExplorer graph) : base(graph) { } + public ArticleResource(IResourceGraph resourceGraph) : base(resourceGraph) { } public override IEnumerable
    OnReturn(HashSet
    entities, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs index 026de2fe23..c757191304 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public abstract class LockableResource : ResourceDefinition where T : class, IIsLockable, IIdentifiable { - protected LockableResource(IResourceGraphExplorer graph) : base(graph) { } + protected LockableResource(IResourceGraph resourceGraph) : base(resourceGraph) { } protected void DisallowLocked(IEnumerable entities) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs index 1fce52e991..25cc4afb72 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class PassportResource : ResourceDefinition { - public PassportResource(IResourceGraphExplorer graph) : base(graph) + public PassportResource(IResourceGraph resourceGraph) : base(resourceGraph) { } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs index a9dfdc2bea..da8fc957b7 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class PersonResource : LockableResource, IHasMeta { - public PersonResource(IResourceGraphExplorer graph) : base(graph) { } + public PersonResource(IResourceGraph resourceGraph) : base(resourceGraph) { } public override IEnumerable BeforeUpdate(IDiffableEntityHashSet entities, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs index b1faca828b..1999936e34 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class TagResource : ResourceDefinition { - public TagResource(IResourceGraphExplorer graph) : base(graph) { } + public TagResource(IResourceGraph resourceGraph) : base(resourceGraph) { } public override IEnumerable BeforeCreate(IEntityHashSet affected, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs index 696b3a25f6..26f6c69c64 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class TodoResource : LockableResource { - public TodoResource(IResourceGraphExplorer graph) : base(graph) { } + public TodoResource(IResourceGraph resourceGraph) : base(resourceGraph) { } public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs index 3f402db3af..a8d6f039e4 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreExample.Resources { public class UserResource : ResourceDefinition { - public UserResource(IResourceGraphExplorer graph) : base(graph) + public UserResource(IResourceGraph resourceGraph) : base(resourceGraph) { HideFields(u => u.Password); } diff --git a/src/Examples/ReportsExample/Startup.cs b/src/Examples/ReportsExample/Startup.cs index 4f49e87db6..609847fa04 100644 --- a/src/Examples/ReportsExample/Startup.cs +++ b/src/Examples/ReportsExample/Startup.cs @@ -27,7 +27,7 @@ public virtual void ConfigureServices(IServiceCollection services) var mvcBuilder = services.AddMvcCore(); services.AddJsonApi( opt => opt.Namespace = "api", - discovery => discovery.AddCurrentAssembly(), mvcBuilder); + discovery => discovery.AddCurrentAssembly(), mvcBuilder: mvcBuilder); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) diff --git a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs index 6368de1ddb..6a3e7f2ae3 100644 --- a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs @@ -12,7 +12,7 @@ public interface IResourceGraphBuilder /// /// Construct the /// - IResourceGraphExplorer Build(); + IResourceGraph Build(); /// /// Add a json:api resource diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index 10a89a7b12..c8dec0985d 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -94,7 +94,7 @@ private void RegisterJsonApiStartupServices() public void ConfigureServices() { - var graph = _resourceGraphBuilder.Build(); + var resourceGraph = _resourceGraphBuilder.Build(); if (!_usesDbContext) { @@ -130,10 +130,10 @@ public void ConfigureServices() _services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>)); _services.AddSingleton(JsonApiOptions); - _services.AddSingleton(graph); + _services.AddSingleton(resourceGraph); _services.AddSingleton(); - _services.AddSingleton(graph); - _services.AddSingleton(graph); + _services.AddSingleton(resourceGraph); + _services.AddSingleton(resourceGraph); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index 2661592503..14144d3ae8 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -29,11 +29,11 @@ public ResourceGraphBuilder(IResourceNameFormatter formatter) } /// - public IResourceGraphExplorer Build() + public IResourceGraph Build() { _entities.ForEach(SetResourceLinksOptions); - var graph = new ResourceGraph(_entities, _validationResults); - return graph; + var resourceGraph = new ResourceGraph(_entities, _validationResults); + return resourceGraph; } private void SetResourceLinksOptions(ContextEntity resourceContext) @@ -235,7 +235,7 @@ private string GetResourceNameFromDbSetProperty(PropertyInfo property, Type reso private void AssertEntityIsNotAlreadyDefined(Type entityType) { if (_entities.Any(e => e.EntityType == entityType)) - throw new InvalidOperationException($"Cannot add entity type {entityType} to context graph, there is already an entity of that type configured."); + throw new InvalidOperationException($"Cannot add entity type {entityType} to context resourceGraph, there is already an entity of that type configured."); } } } diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index 30fa685058..47c6bca132 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -24,26 +24,26 @@ public class DefaultEntityRepository : IEntityRepository _dbSet; - private readonly IContextEntityProvider _provider; + private readonly IResourceGraph _resourceGraph; private readonly IGenericProcessorFactory _genericProcessorFactory; public DefaultEntityRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, - IContextEntityProvider provider, + IResourceGraph resourceGraph, IGenericProcessorFactory genericProcessorFactory) - : this(targetedFields, contextResolver, provider, genericProcessorFactory, null) + : this(targetedFields, contextResolver, resourceGraph, genericProcessorFactory, null) { } public DefaultEntityRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, - IContextEntityProvider provider, + IResourceGraph resourceGraph, IGenericProcessorFactory genericProcessorFactory, ILoggerFactory loggerFactory = null) { _targetedFields = targetedFields; - _provider = provider; + _resourceGraph = resourceGraph; _genericProcessorFactory = genericProcessorFactory; _context = contextResolver.GetContext(); _dbSet = _context.Set(); @@ -137,7 +137,7 @@ private void LoadInverseRelationships(object trackedRelationshipValue, Relations private bool IsHasOneRelationship(string internalRelationshipName, Type type) { - var relationshipAttr = _provider.GetContextEntity(type).Relationships.FirstOrDefault(r => r.InternalRelationshipName == internalRelationshipName); + var relationshipAttr = _resourceGraph.GetRelationships(type).FirstOrDefault(r => r.InternalRelationshipName == internalRelationshipName); if (relationshipAttr != null) { if (relationshipAttr is HasOneAttribute) @@ -419,7 +419,7 @@ public class DefaultEntityRepository : DefaultEntityRepository)) as ILogger; - var resourceGraph = app.ApplicationServices.GetService(typeof(IResourceGraphExplorer)) as ResourceGraph; + var resourceGraph = app.ApplicationServices.GetService(typeof(IResourceGraph)) as ResourceGraph; if (logger != null && resourceGraph != null) { diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index cb7abbb0aa..db13d3ae28 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -80,8 +80,8 @@ public static IServiceCollection AddClientSerialization(this IServiceCollection services.AddScoped(sp => { - var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService(), sp.GetService().Get()); - return new RequestSerializer(sp.GetService(), resourceObjectBuilder); + var resourceObjectBuilder = new ResourceObjectBuilder(sp.GetService(), sp.GetService().Get()); + return new RequestSerializer(sp.GetService(), resourceObjectBuilder); }); return services; } diff --git a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs index deb980f56a..d197d55ecc 100644 --- a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs +++ b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs @@ -49,13 +49,13 @@ public class ServiceDiscoveryFacade : IServiceDiscoveryFacade typeof(IEntityReadRepository<,>) }; private readonly IServiceCollection _services; - private readonly IResourceGraphBuilder _graphBuilder; + private readonly IResourceGraphBuilder _resourceGraphBuilder; private readonly List _identifiables = new List(); - public ServiceDiscoveryFacade(IServiceCollection services, IResourceGraphBuilder graphBuilder) + public ServiceDiscoveryFacade(IServiceCollection services, IResourceGraphBuilder resourceGraphBuilder) { _services = services; - _graphBuilder = graphBuilder; + _resourceGraphBuilder = resourceGraphBuilder; } /// @@ -107,7 +107,7 @@ private void AddDbContextResolvers(Assembly assembly) } /// - /// Adds resources to the graph and registers types on the container. + /// Adds resources to the resourceGraph and registers types on the container. /// /// The assembly to search for resources in. public ServiceDiscoveryFacade AddResources(Assembly assembly) @@ -144,7 +144,7 @@ private void RegisterResourceDefinition(Assembly assembly, ResourceDescriptor id private void AddResourceToGraph(ResourceDescriptor identifiable) { var resourceName = FormatResourceName(identifiable.ResourceType); - _graphBuilder.AddResource(identifiable.ResourceType, identifiable.IdType, resourceName); + _resourceGraphBuilder.AddResource(identifiable.ResourceType, identifiable.IdType, resourceName); } private string FormatResourceName(Type resourceType) diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 9146056ab2..8d4123fa8d 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -21,19 +21,19 @@ internal class ResourceHookExecutor : IResourceHookExecutor private readonly ITraversalHelper _traversalHelper; private readonly IIncludeService _includeService; private readonly ITargetedFields _targetedFields; - private readonly IResourceGraphExplorer _inverseRelationships; + private readonly IResourceGraph _inverseRelationships; public ResourceHookExecutor( IHookExecutorHelper executorHelper, ITraversalHelper traversalHelper, ITargetedFields targetedFields, IIncludeService includedRelationships, - IResourceGraphExplorer graph) + IResourceGraph resourceGraph) { _executorHelper = executorHelper; _traversalHelper = traversalHelper; _targetedFields = targetedFields; _includeService = includedRelationships; - _inverseRelationships = graph; + _inverseRelationships = resourceGraph; } /// diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs index b023870066..80c6d797ca 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs @@ -25,7 +25,7 @@ namespace JsonApiDotNetCore.Hooks internal class TraversalHelper : ITraversalHelper { private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); - private readonly IContextEntityProvider _provider; + private readonly IResourceGraph _resourceGraph; private readonly ITargetedFields _targetedFields; /// /// Keeps track of which entities has already been traversed through, to prevent @@ -38,11 +38,11 @@ internal class TraversalHelper : ITraversalHelper /// private readonly Dictionary RelationshipProxies = new Dictionary(); public TraversalHelper( - IContextEntityProvider provider, + IResourceGraph resourceGraph, ITargetedFields targetedFields) { _targetedFields = targetedFields; - _provider = provider; + _resourceGraph = resourceGraph; } /// @@ -196,13 +196,12 @@ HashSet ProcessEntities(IEnumerable incomingEntities) /// /// Parses all relationships from to - /// other models in the resource graphs by constructing RelationshipProxies . + /// other models in the resource resourceGraphs by constructing RelationshipProxies . /// /// The type to parse void RegisterRelationshipProxies(DependentType type) { - var contextEntity = _provider.GetContextEntity(type); - foreach (RelationshipAttribute attr in contextEntity.Relationships) + foreach (RelationshipAttribute attr in _resourceGraph.GetRelationships(type)) { if (!attr.CanInclude) continue; if (!RelationshipProxies.TryGetValue(attr, out RelationshipProxy proxies)) diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs index 30c7134702..c5bf26cc7a 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs @@ -6,61 +6,6 @@ namespace JsonApiDotNetCore.Internal.Contracts { - - /// - /// Responsible for retrieving the exposed resource fields (attributes and - /// relationships) of registered resources in the resource graph. - /// - public interface IResourceGraphExplorer : IContextEntityProvider - { - /// - /// Gets all fields (attributes and relationships) for - /// that are targeted by the selector. If no selector is provided, all - /// exposed fields are returned. - /// - /// The resource for which to retrieve fields - /// Should be of the form: (TResource e) => new { e.Field1, e.Field2 } - List GetFields(Expression> selector = null) where TResource : IIdentifiable; - /// - /// Gets all attributes for - /// that are targeted by the selector. If no selector is provided, all - /// exposed fields are returned. - /// - /// The resource for which to retrieve attributes - /// Should be of the form: (TResource e) => new { e.Attribute1, e.Arttribute2 } - List GetAttributes(Expression> selector = null) where TResource : IIdentifiable; - /// - /// Gets all relationships for - /// that are targeted by the selector. If no selector is provided, all - /// exposed fields are returned. - /// - /// The resource for which to retrieve relationships - /// Should be of the form: (TResource e) => new { e.Relationship1, e.Relationship2 } - List GetRelationships(Expression> selector = null) where TResource : IIdentifiable; - /// - /// Gets all exposed fields (attributes and relationships) for type - /// - /// The resource type. Must extend IIdentifiable. - List GetFields(Type type); - /// - /// Gets all exposed attributes for type - /// - /// The resource type. Must extend IIdentifiable. - List GetAttributes(Type type); - /// - /// Gets all exposed relationships for type - /// - /// The resource type. Must extend IIdentifiable. - List GetRelationships(Type type); - - /// - /// Traverses the resource graph for the inverse relationship of the provided - /// ; - /// - /// - RelationshipAttribute GetInverse(RelationshipAttribute relationship); - } - /// /// Responsible for getting s from the . /// diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs index 30f9a4b044..550f351e8f 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs @@ -7,9 +7,9 @@ namespace JsonApiDotNetCore.Internal.Contracts { /// /// Responsible for retrieving the exposed resource fields (attributes and - /// relationships) of registered resources in the resource graph. + /// relationships) of registered resources in the resource resourceGraph. /// - public interface IResourceGraphExplorer : IContextEntityProvider + public interface IResourceGraph : IContextEntityProvider { /// /// Gets all fields (attributes and relationships) for @@ -52,7 +52,7 @@ public interface IResourceGraphExplorer : IContextEntityProvider List GetRelationships(Type type); /// - /// Traverses the resource graph for the inverse relationship of the provided + /// Traverses the resource resourceGraph for the inverse relationship of the provided /// ; /// /// diff --git a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs index 91434f50ce..ee6b9aa249 100644 --- a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs +++ b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs @@ -34,7 +34,7 @@ public class InverseRelationships : IInverseRelationships public InverseRelationships(IContextEntityProvider provider, IDbContextResolver resolver = null) { - _provider = (ResourceGraph)provider; + _provider = provider; _resolver = resolver; } diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index e4ac4e590b..dab6833fb1 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.Internal /// /// keeps track of all the models/resources defined in JADNC /// - public class ResourceGraph : IResourceGraphExplorer + public class ResourceGraph : IResourceGraph { internal List ValidationResults { get; } private List _entities { get; } diff --git a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs index 1e5f25b454..f8c0d399fd 100644 --- a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs @@ -21,7 +21,7 @@ public class CurrentRequestMiddleware private readonly RequestDelegate _next; private HttpContext _httpContext; private ICurrentRequest _currentRequest; - private IResourceGraphExplorer _contextEntityProvider; + private IResourceGraph _contextEntityProvider; private IJsonApiOptions _options; private IJsonApiRoutingConvention _routingConvention; @@ -34,7 +34,7 @@ public async Task Invoke(HttpContext httpContext, IJsonApiRoutingConvention routingConvention, IJsonApiOptions options, ICurrentRequest currentRequest, - IResourceGraphExplorer contextEntityProvider) + IResourceGraph contextEntityProvider) { _httpContext = httpContext; _currentRequest = currentRequest; diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index c445c15a97..d23df72e30 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -29,15 +29,15 @@ public interface IResourceDefinition public class ResourceDefinition : IResourceDefinition, IResourceHookContainer where TResource : class, IIdentifiable { private readonly ContextEntity _contextEntity; - private readonly IResourceGraphExplorer _graph; + private readonly IResourceGraph _resourceGraph; private List _allowedAttributes; private List _allowedRelationships; - public ResourceDefinition(IResourceGraphExplorer graph) + public ResourceDefinition(IResourceGraph resourceGraph) { - _contextEntity = graph.GetContextEntity(typeof(TResource)); + _contextEntity = resourceGraph.GetContextEntity(typeof(TResource)); _allowedAttributes = _contextEntity.Attributes; _allowedRelationships = _contextEntity.Relationships; - _graph = graph; + _resourceGraph = resourceGraph; } @@ -51,7 +51,7 @@ public ResourceDefinition(IResourceGraphExplorer graph) /// Should be of the form: (TResource e) => new { e.Attribute1, e.Arttribute2, e.Relationship1, e.Relationship2 } public void HideFields(Expression> selector) { - var fieldsToHide = _graph.GetFields(selector); + var fieldsToHide = _resourceGraph.GetFields(selector); _allowedAttributes = _allowedAttributes.Except(fieldsToHide.Where(f => f is AttrAttribute)).Cast().ToList(); _allowedRelationships = _allowedRelationships.Except(fieldsToHide.Where(f => f is RelationshipAttribute)).Cast().ToList(); } @@ -158,7 +158,7 @@ public class QueryFilters : Dictionary, Filte { var order = new List<(AttrAttribute, SortDirection)>(); foreach (var sortProp in defaultSortOrder) - order.Add((_graph.GetAttributes(sortProp.Item1).Single(), sortProp.Item2)); + order.Add((_resourceGraph.GetAttributes(sortProp.Item1).Single(), sortProp.Item2)); return order; } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs index a5e89c6d8c..60371007f0 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs @@ -14,10 +14,10 @@ namespace JsonApiDotNetCore.Query /// public abstract class QueryParameterService { - protected readonly IResourceGraphExplorer _contextEntityProvider; + protected readonly IResourceGraph _contextEntityProvider; protected readonly ContextEntity _requestResource; - protected QueryParameterService(IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) + protected QueryParameterService(IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) { _contextEntityProvider = contextEntityProvider; _requestResource = currentRequest.GetRequestResource(); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs index 576e7e045f..211cb996c2 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs @@ -16,7 +16,7 @@ public class FilterService : QueryParameterService, IFilterService private readonly List _filters; private IResourceDefinition _requestResourceDefinition; - public FilterService(IResourceDefinitionProvider resourceDefinitionProvider, IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public FilterService(IResourceDefinitionProvider resourceDefinitionProvider, IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) { _requestResourceDefinition = resourceDefinitionProvider.Get(_requestResource.EntityType); _filters = new List(); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs index 1d3405ccb1..d5edb3993d 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs @@ -14,7 +14,7 @@ public class IncludeService : QueryParameterService, IIncludeService /// todo: use read-only lists. private readonly List> _includedChains; - public IncludeService(IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public IncludeService(IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) { _includedChains = new List>(); } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs index 8a3d46d35f..a3ceae7a0d 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs @@ -17,7 +17,7 @@ public class SortService : QueryParameterService, ISortService private bool _isProcessed; public SortService(IResourceDefinitionProvider resourceDefinitionProvider, - IResourceGraphExplorer contextEntityProvider, + IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) { diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index 202790d7d6..d23a4abf36 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -24,7 +24,7 @@ public class SparseFieldsService : QueryParameterService, ISparseFieldsService public override string Name => "fields"; - public SparseFieldsService(IResourceGraphExplorer contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public SparseFieldsService(IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) { _selectedFields = new List(); _selectedRelationshipFields = new Dictionary>(); diff --git a/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs b/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs index a2b3127085..7f5d827dbe 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs @@ -17,12 +17,12 @@ public class RequestSerializer : BaseDocumentBuilder, IRequestSerializer private readonly Dictionary> _attributesToSerializeCache; private readonly Dictionary> _relationshipsToSerializeCache; private Type _currentTargetedResource; - private readonly IResourceGraphExplorer _graph; - public RequestSerializer(IResourceGraphExplorer graph, + private readonly IResourceGraph _resourceGraph; + public RequestSerializer(IResourceGraph resourceGraph, IResourceObjectBuilder resourceObjectBuilder) - : base(resourceObjectBuilder, graph) + : base(resourceObjectBuilder, resourceGraph) { - _graph = graph; + _resourceGraph = resourceGraph; _attributesToSerializeCache = new Dictionary>(); _relationshipsToSerializeCache = new Dictionary>(); } @@ -63,7 +63,7 @@ public string Serialize(IEnumerable entities) public void SetAttributesToSerialize(Expression> filter) where TResource : class, IIdentifiable { - var allowedAttributes = _graph.GetAttributes(filter); + var allowedAttributes = _resourceGraph.GetAttributes(filter); _attributesToSerializeCache[typeof(TResource)] = allowedAttributes; } @@ -71,7 +71,7 @@ public void SetAttributesToSerialize(Expression(Expression> filter) where TResource : class, IIdentifiable { - var allowedRelationships = _graph.GetRelationships(filter); + var allowedRelationships = _resourceGraph.GetRelationships(filter); _relationshipsToSerializeCache[typeof(TResource)] = allowedRelationships; } @@ -89,7 +89,7 @@ private List GetAttributesToSerialize(IIdentifiable entity) return new List(); if (!_attributesToSerializeCache.TryGetValue(resourceType, out var attributes)) - return _graph.GetAttributes(resourceType); + return _resourceGraph.GetAttributes(resourceType); return attributes; } diff --git a/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs b/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs index c8ea508bf5..43dbcd6417 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs @@ -9,21 +9,17 @@ namespace JsonApiDotNetCore.Serialization.Server { /// - /// TODO: explore option out caching so we don't have to recalculate the list - /// of allowed attributes and relationships all the time. This is more efficient - /// for documents with many resource objects. public class FieldsToSerialize : IFieldsToSerialize { - private readonly IResourceGraphExplorer _graph; + private readonly IResourceGraph _resourceGraph; private readonly ISparseFieldsService _sparseFieldsService ; - private readonly IServiceProvider _provider; - private readonly Dictionary _resourceDefinitionCache = new Dictionary(); + private readonly IResourceDefinitionProvider _provider; - public FieldsToSerialize(IResourceGraphExplorer graph, + public FieldsToSerialize(IResourceGraph resourceGraph, ISparseFieldsService sparseFieldsService, - IServiceProvider provider) + IResourceDefinitionProvider provider) { - _graph = graph; + _resourceGraph = resourceGraph; _sparseFieldsService = sparseFieldsService; _provider = provider; } @@ -31,9 +27,9 @@ public FieldsToSerialize(IResourceGraphExplorer graph, /// public List GetAllowedAttributes(Type type, RelationshipAttribute relationship = null) { // get the list of all exposed atttributes for the given type. - var allowed = _graph.GetAttributes(type); + var allowed = _resourceGraph.GetAttributes(type); - var resourceDefinition = GetResourceDefinition(type); + var resourceDefinition = _provider.Get(type); if (resourceDefinition != null) // The set of allowed attribrutes to be exposed was defined on the resource definition allowed = allowed.Intersect(resourceDefinition.GetAllowedAttributes()).ToList(); @@ -55,27 +51,13 @@ public List GetAllowedAttributes(Type type, RelationshipAttribute /// public List GetAllowedRelationships(Type type) { - var resourceDefinition = GetResourceDefinition(type); + var resourceDefinition = _provider.Get(type); if (resourceDefinition != null) // The set of allowed attribrutes to be exposed was defined on the resource definition return resourceDefinition.GetAllowedRelationships(); // The set of allowed attribrutes to be exposed was NOT defined on the resource definition: return all - return _graph.GetRelationships(type); - } - - - /// consider to implement and inject a `ResourceDefinitionProvider` service. - private IResourceDefinition GetResourceDefinition(Type resourceType) - { - - var resourceDefinitionType = _graph.GetContextEntity(resourceType).ResourceType; - if (!_resourceDefinitionCache.TryGetValue(resourceDefinitionType, out IResourceDefinition resourceDefinition)) - { - resourceDefinition = _provider.GetService(resourceDefinitionType) as IResourceDefinition; - _resourceDefinitionCache.Add(resourceDefinitionType, resourceDefinition); - } - return resourceDefinition; + return _resourceGraph.GetRelationships(type); } } } diff --git a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs index f4d7c4be16..32ddcaf37c 100644 --- a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs +++ b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs @@ -8,10 +8,10 @@ namespace JsonApiDotNetCore.Query /// internal class ResourceDefinitionProvider : IResourceDefinitionProvider { - private readonly IResourceGraphExplorer _resourceContextProvider; + private readonly IResourceGraph _resourceContextProvider; private readonly IScopedServiceProvider _serviceProvider; - public ResourceDefinitionProvider(IResourceGraphExplorer resourceContextProvider, IScopedServiceProvider serviceProvider) + public ResourceDefinitionProvider(IResourceGraph resourceContextProvider, IScopedServiceProvider serviceProvider) { _resourceContextProvider = resourceContextProvider; _serviceProvider = serviceProvider; diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index 4ea4f0a651..c4cec73a15 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -21,7 +21,7 @@ namespace DiscoveryTests public class ServiceDiscoveryFacadeTests { private readonly IServiceCollection _services = new ServiceCollection(); - private readonly ResourceGraphBuilder _graphBuilder = new ResourceGraphBuilder(); + private readonly ResourceGraphBuilder _resourceGraphBuilder = new ResourceGraphBuilder(); public ServiceDiscoveryFacadeTests() { @@ -31,7 +31,7 @@ public ServiceDiscoveryFacadeTests() TestModelRepository._dbContextResolver = dbResolverMock.Object; } - private ServiceDiscoveryFacade _facade => new ServiceDiscoveryFacade(_services, _graphBuilder); + private ServiceDiscoveryFacade _facade => new ServiceDiscoveryFacade(_services, _resourceGraphBuilder); [Fact] public void AddAssembly_Adds_All_Resources_To_Graph() @@ -40,10 +40,10 @@ public void AddAssembly_Adds_All_Resources_To_Graph() _facade.AddAssembly(typeof(Person).Assembly); // assert - var graph = _graphBuilder.Build(); - var personResource = graph.GetContextEntity(typeof(Person)); - var articleResource = graph.GetContextEntity(typeof(Article)); - var modelResource = graph.GetContextEntity(typeof(Model)); + var resourceGraph = _resourceGraphBuilder.Build(); + var personResource = resourceGraph.GetContextEntity(typeof(Person)); + var articleResource = resourceGraph.GetContextEntity(typeof(Article)); + var modelResource = resourceGraph.GetContextEntity(typeof(Model)); Assert.NotNull(personResource); Assert.NotNull(articleResource); @@ -57,8 +57,8 @@ public void AddCurrentAssembly_Adds_Resources_To_Graph() _facade.AddCurrentAssembly(); // assert - var graph = _graphBuilder.Build(); - var testModelResource = graph.GetContextEntity(typeof(TestModel)); + var resourceGraph = _resourceGraphBuilder.Build(); + var testModelResource = resourceGraph.GetContextEntity(typeof(TestModel)); Assert.NotNull(testModelResource); } @@ -71,7 +71,7 @@ public void AddCurrentAssembly_Adds_Services_To_Container() _services.AddScoped((_) => new Mock().Object); _services.AddScoped((_) => new Mock().Object); _services.AddScoped((_) => new Mock().Object); - _services.AddScoped((_) => new Mock().Object); + _services.AddScoped((_) => new Mock().Object); _facade.AddCurrentAssembly(); // assert @@ -103,7 +103,7 @@ public TestModelService( IJsonApiOptions options, IRequestContext currentRequest, IPageQueryService pageService, - IResourceGraphExplorer resourceGraph, + IResourceGraph resourceGraph, ILoggerFactory loggerFactory = null, IResourceHookExecutor hookExecutor = null) : base(repository, options, currentRequest, pageService, resourceGraph, loggerFactory, hookExecutor) { diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs index 3b6865e8c6..f2b4b68596 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs @@ -273,8 +273,8 @@ public async Task CreateResource_EntityTypeMismatch_IsConflict() // arrange var dbContext = PrepareTest(); var serializer = GetSerializer(e => new { }, e => new { e.Owner }); - var graph = new ResourceGraphBuilder().AddResource("todo-items").AddResource().AddResource().Build(); - var _deserializer = new ResponseDeserializer(graph); + var resourceGraph = new ResourceGraphBuilder().AddResource("todo-items").AddResource().AddResource().Build(); + var _deserializer = new ResponseDeserializer(resourceGraph); var content = serializer.Serialize(_todoItemFaker.Generate()).Replace("todo-items", "people"); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeeplyNestedInclusionTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeeplyNestedInclusionTests.cs index 57aec660cb..3490f6e949 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeeplyNestedInclusionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeeplyNestedInclusionTests.cs @@ -38,8 +38,8 @@ public async Task Can_Include_Nested_Relationships() { // arrange const string route = "/api/v1/todo-items?include=collection.owner"; - var graph = new ResourceGraphBuilder().AddResource("todo-items").AddResource().AddResource().Build(); - var deserializer = new ResponseDeserializer(graph); + var resourceGraph = new ResourceGraphBuilder().AddResource("todo-items").AddResource().AddResource().Build(); + var deserializer = new ResponseDeserializer(resourceGraph); var todoItem = new TodoItem { Collection = new TodoItemCollection diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs index 687b7c2299..8971e937ae 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs @@ -31,7 +31,7 @@ public class SparseFieldSetTests { private TestFixture _fixture; private readonly AppDbContext _dbContext; - private IResourceGraphExplorer _graph; + private IResourceGraph _resourceGraph; private Faker _personFaker; private Faker _todoItemFaker; @@ -39,7 +39,7 @@ public SparseFieldSetTests(TestFixture fixture) { _fixture = fixture; _dbContext = fixture.GetService(); - _graph = fixture.GetService(); + _resourceGraph = fixture.GetService(); _personFaker = new Faker() .RuleFor(p => p.FirstName, f => f.Name.FirstName()) .RuleFor(p => p.LastName, f => f.Name.LastName()) @@ -73,7 +73,7 @@ public async Task Can_Select_Sparse_Fieldsets() var query = _dbContext .TodoItems .Where(t => t.Id == todoItem.Id) - .Select(_graph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToList()); + .Select(_resourceGraph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToList()); var resultSql = StringExtensions.Normalize(query.ToSql()); var result = await query.FirstAsync(); @@ -145,8 +145,8 @@ public async Task Fields_Query_Selects_All_Fieldset_With_HasOne() var route = $"/api/v1/todo-items?include=owner&fields[owner]=first-name,age"; var request = new HttpRequestMessage(httpMethod, route); - var graph = new ResourceGraphBuilder().AddResource().AddResource("todo-items").Build(); - var deserializer = new ResponseDeserializer(graph); + var resourceGraph = new ResourceGraphBuilder().AddResource().AddResource("todo-items").Build(); + var deserializer = new ResponseDeserializer(resourceGraph); // act var response = await client.SendAsync(request); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs index e15ae242d8..a0def3595c 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs @@ -44,7 +44,7 @@ public IRequestSerializer GetSerializer(Expression() .AddResource
    () .AddResource() @@ -55,7 +55,7 @@ public IResponseDeserializer GetDeserializer() .AddResource() .AddResource("todo-items") .AddResource().Build(); - return new ResponseDeserializer(graph); + return new ResponseDeserializer(resourceGraph); } public T GetService() => (T)_services.GetService(typeof(T)); diff --git a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs index 99d002e818..a2904bc8b7 100644 --- a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs @@ -34,7 +34,7 @@ public void Can_Build_ResourceGraph_Using_Builder() var container = services.BuildServiceProvider(); // assert - var resourceGraph = container.GetRequiredService(); + var resourceGraph = container.GetRequiredService(); var dbResource = resourceGraph.GetContextEntity("db-resources"); var nonDbResource = resourceGraph.GetContextEntity("non-db-resources"); Assert.Equal(typeof(DbResource), dbResource.EntityType); @@ -50,10 +50,10 @@ public void Resources_Without_Names_Specified_Will_Use_Default_Formatter() builder.AddResource(); // act - var graph = builder.Build(); + var resourceGraph = builder.Build(); // assert - var resource = graph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetContextEntity(typeof(TestResource)); Assert.Equal("test-resources", resource.EntityName); } @@ -65,10 +65,10 @@ public void Resources_Without_Names_Specified_Will_Use_Configured_Formatter() builder.AddResource(); // act - var graph = builder.Build(); + var resourceGraph = builder.Build(); // assert - var resource = graph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetContextEntity(typeof(TestResource)); Assert.Equal("testResources", resource.EntityName); } @@ -80,10 +80,10 @@ public void Attrs_Without_Names_Specified_Will_Use_Default_Formatter() builder.AddResource(); // act - var graph = builder.Build(); + var resourceGraph = builder.Build(); // assert - var resource = graph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetContextEntity(typeof(TestResource)); Assert.Contains(resource.Attributes, (i) => i.PublicAttributeName == "compound-attribute"); } @@ -95,10 +95,10 @@ public void Attrs_Without_Names_Specified_Will_Use_Configured_Formatter() builder.AddResource(); // act - var graph = builder.Build(); + var resourceGraph = builder.Build(); // assert - var resource = graph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetContextEntity(typeof(TestResource)); Assert.Contains(resource.Attributes, (i) => i.PublicAttributeName == "compoundAttribute"); } @@ -110,10 +110,10 @@ public void Relationships_Without_Names_Specified_Will_Use_Default_Formatter() builder.AddResource(); // act - var graph = builder.Build(); + var resourceGraph = builder.Build(); // assert - var resource = graph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetContextEntity(typeof(TestResource)); Assert.Equal("related-resource", resource.Relationships.Single(r => r.IsHasOne).PublicRelationshipName); Assert.Equal("related-resources", resource.Relationships.Single(r => r.IsHasMany).PublicRelationshipName); } diff --git a/test/UnitTests/Builders/LinkBuilderTests.cs b/test/UnitTests/Builders/LinkBuilderTests.cs index f0916e1987..661ea73ba7 100644 --- a/test/UnitTests/Builders/LinkBuilderTests.cs +++ b/test/UnitTests/Builders/LinkBuilderTests.cs @@ -16,7 +16,7 @@ namespace UnitTests public class LinkBuilderTests { private readonly IPageService _pageService; - private readonly Mock _provider = new Mock(); + private readonly Mock _provider = new Mock(); private const string _host = "http://www.example.com"; private const string _topSelf = "http://www.example.com/articles"; private const string _resourceSelf = "http://www.example.com/articles/123"; diff --git a/test/UnitTests/Data/DefaultEntityRepository_Tests.cs b/test/UnitTests/Data/DefaultEntityRepository_Tests.cs index 4260618e26..6c0ce780c1 100644 --- a/test/UnitTests/Data/DefaultEntityRepository_Tests.cs +++ b/test/UnitTests/Data/DefaultEntityRepository_Tests.cs @@ -77,13 +77,13 @@ private DefaultEntityRepository GetRepository() .Setup(m => m.GetContext()) .Returns(_contextMock.Object); - var graph = new ResourceGraphBuilder().AddResource().Build(); + var resourceGraph = new ResourceGraphBuilder().AddResource().Build(); return new DefaultEntityRepository( _targetedFieldsMock.Object, _contextResolverMock.Object, - graph, null, null); + resourceGraph, null, null); } [Theory] diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index 05cccab6a9..0f2ce10f96 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -42,13 +42,13 @@ public void AddJsonApiInternals_Adds_All_Required_Services() // assert var currentRequest = provider.GetService(); Assert.NotNull(currentRequest); - var graph = provider.GetService(); - Assert.NotNull(graph); - currentRequest.SetRequestResource(graph.GetContextEntity()); - Assert.NotNull(provider.GetService()); + var resourceGraph = provider.GetService(); + Assert.NotNull(resourceGraph); + currentRequest.SetRequestResource(resourceGraph.GetContextEntity()); + Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService(typeof(IEntityRepository))); - Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService>()); Assert.NotNull(provider.GetService()); @@ -128,8 +128,8 @@ public void AddJsonApi_With_Context_Uses_DbSet_PropertyName_If_NoOtherSpecified( // assert var provider = services.BuildServiceProvider(); - var graph = provider.GetService(); - var resource = graph.GetContextEntity(typeof(IntResource)); + var resourceGraph = provider.GetService(); + var resource = resourceGraph.GetContextEntity(typeof(IntResource)); Assert.Equal("resource", resource.EntityName); } diff --git a/test/UnitTests/Models/ResourceDefinitionTests.cs b/test/UnitTests/Models/ResourceDefinitionTests.cs index a69b339604..e863e55465 100644 --- a/test/UnitTests/Models/ResourceDefinitionTests.cs +++ b/test/UnitTests/Models/ResourceDefinitionTests.cs @@ -10,11 +10,11 @@ namespace UnitTests.Models { public class ResourceDefinition_Scenario_Tests { - private readonly IResourceGraphExplorer _graph; + private readonly IResourceGraph _resourceGraph; public ResourceDefinition_Scenario_Tests() { - _graph = new ResourceGraphBuilder() + _resourceGraph = new ResourceGraphBuilder() .AddResource("models") .Build(); } diff --git a/test/UnitTests/QueryParameters/FilterServiceTests.cs b/test/UnitTests/QueryParameters/FilterServiceTests.cs index ab3b5d13ae..7e0e4d4462 100644 --- a/test/UnitTests/QueryParameters/FilterServiceTests.cs +++ b/test/UnitTests/QueryParameters/FilterServiceTests.cs @@ -11,7 +11,7 @@ public class FilterServiceTests : QueryParametersUnitTestCollection { public FilterService GetService() { - return new FilterService(MockResourceDefinitionProvider(), _graph, MockCurrentRequest(_articleResourceContext)); + return new FilterService(MockResourceDefinitionProvider(), _resourceGraph, MockCurrentRequest(_articleResourceContext)); } [Fact] diff --git a/test/UnitTests/QueryParameters/IncludeServiceTests.cs b/test/UnitTests/QueryParameters/IncludeServiceTests.cs index 8ed7b0c034..439dc98288 100644 --- a/test/UnitTests/QueryParameters/IncludeServiceTests.cs +++ b/test/UnitTests/QueryParameters/IncludeServiceTests.cs @@ -14,7 +14,7 @@ public class IncludeServiceTests : QueryParametersUnitTestCollection public IncludeService GetService(ContextEntity resourceContext = null) { - return new IncludeService(_graph, MockCurrentRequest(resourceContext ?? _articleResourceContext)); + return new IncludeService(_resourceGraph, MockCurrentRequest(resourceContext ?? _articleResourceContext)); } [Fact] @@ -58,7 +58,7 @@ public void Parse_ChainsOnWrongMainResource_ThrowsJsonApiException() // arrange const string chain = "author.blogs.reviewer.favorite-food,reviewer.blogs.author.favorite-song"; var query = new KeyValuePair("include", new StringValues(chain)); - var service = GetService(_graph.GetContextEntity()); + var service = GetService(_resourceGraph.GetContextEntity()); // act, assert var exception = Assert.Throws( () => service.Parse(query)); diff --git a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs index c114e42f65..43bcf2e28f 100644 --- a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs +++ b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs @@ -13,7 +13,7 @@ namespace UnitTests.QueryParameters public class QueryParametersUnitTestCollection { protected readonly ContextEntity _articleResourceContext; - protected readonly IResourceGraphExplorer _graph; + protected readonly IResourceGraph _resourceGraph; public QueryParametersUnitTestCollection() { @@ -23,8 +23,8 @@ public QueryParametersUnitTestCollection() builder.AddResource(); builder.AddResource(); builder.AddResource(); - _graph = builder.Build(); - _articleResourceContext = _graph.GetContextEntity
    (); + _resourceGraph = builder.Build(); + _articleResourceContext = _resourceGraph.GetContextEntity
    (); } public ICurrentRequest MockCurrentRequest(ContextEntity requestResource = null) diff --git a/test/UnitTests/QueryParameters/SortServiceTests.cs b/test/UnitTests/QueryParameters/SortServiceTests.cs index 1ca38d192e..278812fa5e 100644 --- a/test/UnitTests/QueryParameters/SortServiceTests.cs +++ b/test/UnitTests/QueryParameters/SortServiceTests.cs @@ -10,7 +10,7 @@ public class SortServiceTests : QueryParametersUnitTestCollection { public SortService GetService() { - return new SortService(MockResourceDefinitionProvider(), _graph, MockCurrentRequest(_articleResourceContext)); + return new SortService(MockResourceDefinitionProvider(), _resourceGraph, MockCurrentRequest(_articleResourceContext)); } [Fact] diff --git a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs index eeb0648110..c0c5d19207 100644 --- a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs +++ b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs @@ -12,7 +12,7 @@ public class SparseFieldsServiceTests : QueryParametersUnitTestCollection { public SparseFieldsService GetService(ContextEntity contextEntity = null) { - return new SparseFieldsService(_graph, MockCurrentRequest(contextEntity ?? _articleResourceContext)); + return new SparseFieldsService(_resourceGraph, MockCurrentRequest(contextEntity ?? _articleResourceContext)); } [Fact] diff --git a/test/UnitTests/ResourceHooks/DiscoveryTests.cs b/test/UnitTests/ResourceHooks/DiscoveryTests.cs index 4c87de9ed8..b48adb1af6 100644 --- a/test/UnitTests/ResourceHooks/DiscoveryTests.cs +++ b/test/UnitTests/ResourceHooks/DiscoveryTests.cs @@ -33,7 +33,7 @@ public void Hook_Discovery() public class AnotherDummy : Identifiable { } public abstract class ResourceDefintionBase : ResourceDefinition where T : class, IIdentifiable { - protected ResourceDefintionBase(IResourceGraphExplorer graph) : base(graph) { } + protected ResourceDefintionBase(IResourceGraph resourceGraph) : base(resourceGraph) { } public override IEnumerable BeforeDelete(IEntityHashSet affected, ResourcePipeline pipeline) { return affected; } public override void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded) { } diff --git a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs index 0eb6fb5173..f697411b19 100644 --- a/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs +++ b/test/UnitTests/ResourceHooks/ResourceHookExecutor/Update/BeforeUpdate_WithDbValues_Tests.cs @@ -82,7 +82,7 @@ public void BeforeUpdate_Deleting_Relationship() var personDiscovery = SetDiscoverableHooks(targetHooks, EnableDbValues); var (_, ufMock, hookExecutor, todoResourceMock, ownerResourceMock) = CreateTestObjects(todoDiscovery, personDiscovery, repoDbContextOptions: options); - ufMock.Setup(c => c.Relationships).Returns(_graph.GetRelationships((TodoItem t) => t.ToOnePerson)); + ufMock.Setup(c => c.Relationships).Returns(_resourceGraph.GetRelationships((TodoItem t) => t.ToOnePerson)); // act var _todoList = new List() { new TodoItem { Id = this.todoList[0].Id } }; diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 6cd495a5c4..0e5d84b08e 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -24,7 +24,7 @@ namespace UnitTests.ResourceHooks { public class HooksDummyData { - protected IResourceGraphExplorer _graph; + protected IResourceGraph _resourceGraph; protected ResourceHook[] NoHooks = new ResourceHook[0]; protected ResourceHook[] EnableDbValues = { ResourceHook.BeforeUpdate, ResourceHook.BeforeUpdateRelationship }; protected ResourceHook[] DisableDbValues = new ResourceHook[0]; @@ -37,7 +37,7 @@ public class HooksDummyData protected readonly Faker _passportFaker; public HooksDummyData() { - _graph = new ResourceGraphBuilder() + _resourceGraph = new ResourceGraphBuilder() .AddResource() .AddResource() .AddResource() @@ -162,8 +162,8 @@ public class HooksTestsSetup : HooksDummyData SetupProcessorFactoryForResourceDefinition(gpfMock, mainResource.Object, mainDiscovery, null); var execHelper = new HookExecutorHelper(gpfMock.Object, options); - var traversalHelper = new TraversalHelper(_graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _graph); + var traversalHelper = new TraversalHelper(_resourceGraph, ufMock.Object); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _resourceGraph); return (iqMock, hookExecutor, mainResource); } @@ -190,8 +190,8 @@ public class HooksTestsSetup : HooksDummyData SetupProcessorFactoryForResourceDefinition(gpfMock, nestedResource.Object, nestedDiscovery, dbContext); var execHelper = new HookExecutorHelper(gpfMock.Object, options); - var traversalHelper = new TraversalHelper(_graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _graph); + var traversalHelper = new TraversalHelper(_resourceGraph, ufMock.Object); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _resourceGraph); return (iqMock, ufMock, hookExecutor, mainResource, nestedResource); } @@ -222,8 +222,8 @@ public class HooksTestsSetup : HooksDummyData SetupProcessorFactoryForResourceDefinition(gpfMock, secondNestedResource.Object, secondNestedDiscovery, dbContext); var execHelper = new HookExecutorHelper(gpfMock.Object, options); - var traversalHelper = new TraversalHelper(_graph, ufMock.Object); - var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _graph); + var traversalHelper = new TraversalHelper(_resourceGraph, ufMock.Object); + var hookExecutor = new ResourceHookExecutor(execHelper, traversalHelper, ufMock.Object, iqMock.Object, _resourceGraph); return (iqMock, hookExecutor, mainResource, firstNestedResource, secondNestedResource); } @@ -357,7 +357,7 @@ IDbContextResolver CreateTestDbResolver(AppDbContext dbContext) where TM void ResolveInverseRelationships(AppDbContext context) { - new InverseRelationships(_graph, new DbContextResolver(context)).Resolve(); + new InverseRelationships(_resourceGraph, new DbContextResolver(context)).Resolve(); } Mock> CreateResourceDefinition @@ -383,13 +383,13 @@ protected List> GetIncludedRelationshipsChains(param protected List GetIncludedRelationshipsChain(string chain) { var parsedChain = new List(); - var resourceContext = _graph.GetContextEntity(); + var resourceContext = _resourceGraph.GetContextEntity(); var splittedPath = chain.Split(QueryConstants.DOT); foreach (var requestedRelationship in splittedPath) { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _graph.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); } return parsedChain; } diff --git a/test/UnitTests/Serialization/Client/RequestSerializerTests.cs b/test/UnitTests/Serialization/Client/RequestSerializerTests.cs index 68d44bf0b7..5c405b09ef 100644 --- a/test/UnitTests/Serialization/Client/RequestSerializerTests.cs +++ b/test/UnitTests/Serialization/Client/RequestSerializerTests.cs @@ -16,8 +16,8 @@ public class RequestSerializerTests : SerializerTestsSetup public RequestSerializerTests() { - var builder = new ResourceObjectBuilder(_graph, new ResourceObjectBuilderSettings()); - _serializer = new RequestSerializer(_graph, builder); + var builder = new ResourceObjectBuilder(_resourceGraph, new ResourceObjectBuilderSettings()); + _serializer = new RequestSerializer(_resourceGraph, builder); } [Fact] diff --git a/test/UnitTests/Serialization/Client/ResponseDeserializerTests.cs b/test/UnitTests/Serialization/Client/ResponseDeserializerTests.cs index 02bedf2170..5aeef42e3d 100644 --- a/test/UnitTests/Serialization/Client/ResponseDeserializerTests.cs +++ b/test/UnitTests/Serialization/Client/ResponseDeserializerTests.cs @@ -16,7 +16,7 @@ public class ResponseDeserializerTests : DeserializerTestsSetup public ResponseDeserializerTests() { - _deserializer = new ResponseDeserializer(_graph); + _deserializer = new ResponseDeserializer(_resourceGraph); _linkValues.Add("self", "http://example.com/articles"); _linkValues.Add("next", "http://example.com/articles?page[offset]=2"); _linkValues.Add("last", "http://example.com/articles?page[offset]=10"); diff --git a/test/UnitTests/Serialization/Common/DocumentBuilderTests.cs b/test/UnitTests/Serialization/Common/DocumentBuilderTests.cs index f89667feb0..e7bff25cc6 100644 --- a/test/UnitTests/Serialization/Common/DocumentBuilderTests.cs +++ b/test/UnitTests/Serialization/Common/DocumentBuilderTests.cs @@ -18,7 +18,7 @@ public BaseDocumentBuilderTests() { var mock = new Mock(); mock.Setup(m => m.Build(It.IsAny(), It.IsAny>(), It.IsAny>())).Returns(new ResourceObject()); - _builder = new TestDocumentBuilder(mock.Object, _graph); + _builder = new TestDocumentBuilder(mock.Object, _resourceGraph); } diff --git a/test/UnitTests/Serialization/Common/DocumentParserTests.cs b/test/UnitTests/Serialization/Common/DocumentParserTests.cs index 27fd20c790..c16daf36c7 100644 --- a/test/UnitTests/Serialization/Common/DocumentParserTests.cs +++ b/test/UnitTests/Serialization/Common/DocumentParserTests.cs @@ -16,7 +16,7 @@ public class BaseDocumentParserTests : DeserializerTestsSetup public BaseDocumentParserTests() { - _deserializer = new TestDocumentParser(_graph); + _deserializer = new TestDocumentParser(_resourceGraph); } [Fact] @@ -132,7 +132,7 @@ public void DeserializeAttributes_VariousDataTypes_CanDeserialize(string member, var entity = (TestResource)_deserializer.Deserialize(body); // assert - var pi = _graph.GetContextEntity("test-resource").Attributes.Single(attr => attr.PublicAttributeName == member).PropertyInfo; + var pi = _resourceGraph.GetContextEntity("test-resource").Attributes.Single(attr => attr.PublicAttributeName == member).PropertyInfo; var deserializedValue = pi.GetValue(entity); if (member == "int-field") diff --git a/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs index c0b707d02c..06df8ab1e2 100644 --- a/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs @@ -16,7 +16,7 @@ public class ResourceObjectBuilderTests : SerializerTestsSetup public ResourceObjectBuilderTests() { - _builder = new ResourceObjectBuilder(_graph, new ResourceObjectBuilderSettings()); + _builder = new ResourceObjectBuilder(_resourceGraph, new ResourceObjectBuilderSettings()); } [Fact] @@ -58,7 +58,7 @@ public void EntityToResourceObject_ResourceWithIncludedAttrs_CanBuild(string str { // arrange var entity = new TestResource() { StringField = stringFieldValue, NullableIntField = intFieldValue }; - var attrs = _graph.GetAttributes(tr => new { tr.StringField, tr.NullableIntField }); + var attrs = _resourceGraph.GetAttributes(tr => new { tr.StringField, tr.NullableIntField }); // act var resourceObject = _builder.Build(entity, attrs); @@ -114,7 +114,7 @@ public void EntityWithRelationshipsToResourceObject_WithIncludedRelationshipsAtt PopulatedToOne = new OneToOneDependent { Id = 10 }, PopulatedToManies = new List { new OneToManyDependent { Id = 20 } } }; - var relationships = _graph.GetRelationships(tr => new { tr.PopulatedToManies, tr.PopulatedToOne, tr.EmptyToOne, tr.EmptyToManies }); + var relationships = _resourceGraph.GetRelationships(tr => new { tr.PopulatedToManies, tr.PopulatedToOne, tr.EmptyToOne, tr.EmptyToManies }); // act var resourceObject = _builder.Build(entity, relationships: relationships); @@ -138,7 +138,7 @@ public void EntityWithRelationshipsToResourceObject_DeviatingForeignKeyWhileRela { // arrange var entity = new OneToOneDependent { Principal = new OneToOnePrincipal { Id = 10 }, PrincipalId = 123 }; - var relationships = _graph.GetRelationships(tr => tr.Principal); + var relationships = _resourceGraph.GetRelationships(tr => tr.Principal); // act var resourceObject = _builder.Build(entity, relationships: relationships); @@ -155,7 +155,7 @@ public void EntityWithRelationshipsToResourceObject_DeviatingForeignKeyAndNoNavi { // arrange var entity = new OneToOneDependent { Principal = null, PrincipalId = 123 }; - var relationships = _graph.GetRelationships(tr => tr.Principal); + var relationships = _resourceGraph.GetRelationships(tr => tr.Principal); // act var resourceObject = _builder.Build(entity, relationships: relationships); @@ -169,7 +169,7 @@ public void EntityWithRequiredRelationshipsToResourceObject_DeviatingForeignKeyW { // arrange var entity = new OneToOneRequiredDependent { Principal = new OneToOnePrincipal { Id = 10 }, PrincipalId = 123 }; - var relationships = _graph.GetRelationships(tr => tr.Principal); + var relationships = _resourceGraph.GetRelationships(tr => tr.Principal); // act var resourceObject = _builder.Build(entity, relationships: relationships); @@ -186,7 +186,7 @@ public void EntityWithRequiredRelationshipsToResourceObject_DeviatingForeignKeyA { // arrange var entity = new OneToOneRequiredDependent { Principal = null, PrincipalId = 123 }; - var relationships = _graph.GetRelationships(tr => tr.Principal); + var relationships = _resourceGraph.GetRelationships(tr => tr.Principal); // act & assert Assert.ThrowsAny(() => _builder.Build(entity, relationships: relationships)); @@ -197,7 +197,7 @@ public void EntityWithRequiredRelationshipsToResourceObject_EmptyResourceWhileRe { // arrange var entity = new OneToOneRequiredDependent(); - var relationships = _graph.GetRelationships(tr => tr.Principal); + var relationships = _resourceGraph.GetRelationships(tr => tr.Principal); // act & assert Assert.ThrowsAny(() => _builder.Build(entity, relationships: relationships)); diff --git a/test/UnitTests/Serialization/DeserializerTestsSetup.cs b/test/UnitTests/Serialization/DeserializerTestsSetup.cs index b336f4e3d6..dc9dff4aae 100644 --- a/test/UnitTests/Serialization/DeserializerTestsSetup.cs +++ b/test/UnitTests/Serialization/DeserializerTestsSetup.cs @@ -9,7 +9,7 @@ public class DeserializerTestsSetup : SerializationTestsSetupBase { protected class TestDocumentParser : BaseDocumentParser { - public TestDocumentParser(IResourceGraphExplorer resourceGraph) : base(resourceGraph) { } + public TestDocumentParser(IResourceGraph resourceGraph) : base(resourceGraph) { } public new object Deserialize(string body) { diff --git a/test/UnitTests/Serialization/SerializationTestsSetupBase.cs b/test/UnitTests/Serialization/SerializationTestsSetupBase.cs index 3e7cce56fb..c56e4a532e 100644 --- a/test/UnitTests/Serialization/SerializationTestsSetupBase.cs +++ b/test/UnitTests/Serialization/SerializationTestsSetupBase.cs @@ -8,7 +8,7 @@ namespace UnitTests.Serialization { public class SerializationTestsSetupBase { - protected IResourceGraphExplorer _graph; + protected IResourceGraph _resourceGraph; protected readonly Faker _foodFaker; protected readonly Faker _songFaker; protected readonly Faker
    _articleFaker; @@ -17,7 +17,7 @@ public class SerializationTestsSetupBase public SerializationTestsSetupBase() { - _graph = BuildGraph(); + _resourceGraph = BuildGraph(); _articleFaker = new Faker
    () .RuleFor(f => f.Title, f => f.Hacker.Phrase()) .RuleFor(f => f.Id, f => f.UniqueIndex + 1); @@ -35,7 +35,7 @@ public SerializationTestsSetupBase() .RuleFor(f => f.Id, f => f.UniqueIndex + 1); } - protected IResourceGraphExplorer BuildGraph() + protected IResourceGraph BuildGraph() { var resourceGraphBuilder = new ResourceGraphBuilder(); resourceGraphBuilder.AddResource("test-resource"); diff --git a/test/UnitTests/Serialization/SerializerTestsSetup.cs b/test/UnitTests/Serialization/SerializerTestsSetup.cs index c0a7c395ad..d359d46a2c 100644 --- a/test/UnitTests/Serialization/SerializerTestsSetup.cs +++ b/test/UnitTests/Serialization/SerializerTestsSetup.cs @@ -50,7 +50,7 @@ protected ResponseSerializer GetResponseSerializer(List(meta, link, includedBuilder, fieldsToSerialize, resourceObjectBuilder, provider); } @@ -59,12 +59,12 @@ protected ResponseResourceObjectBuilder GetResponseResourceObjectBuilder(List
  • GetMetaBuilder(Dictionary meta = null) where T : class, IIdentifiable @@ -89,7 +89,7 @@ protected IMetaBuilder GetMetaBuilder(Dictionary meta = nu protected ICurrentRequest GetRequestManager() where T : class, IIdentifiable { var mock = new Mock(); - mock.Setup(m => m.GetRequestResource()).Returns(_graph.GetContextEntity()); + mock.Setup(m => m.GetRequestResource()).Returns(_resourceGraph.GetContextEntity()); return mock.Object; } @@ -111,8 +111,8 @@ protected ISparseFieldsService GetFieldsQuery() protected IFieldsToSerialize GetSerializableFields() { var mock = new Mock(); - mock.Setup(m => m.GetAllowedAttributes(It.IsAny(), It.IsAny())).Returns((t, r) => _graph.GetContextEntity(t).Attributes); - mock.Setup(m => m.GetAllowedRelationships(It.IsAny())).Returns(t => _graph.GetContextEntity(t).Relationships); + mock.Setup(m => m.GetAllowedAttributes(It.IsAny(), It.IsAny())).Returns((t, r) => _resourceGraph.GetContextEntity(t).Attributes); + mock.Setup(m => m.GetAllowedRelationships(It.IsAny())).Returns(t => _resourceGraph.GetContextEntity(t).Relationships); return mock.Object; } diff --git a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs index 5fe64a9ba4..13e079c927 100644 --- a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs @@ -157,13 +157,13 @@ public void BuildIncluded_DuplicateChildrenMultipleChains_OnceInOutput() private List GetIncludedRelationshipsChain(string chain) { var parsedChain = new List(); - var resourceContext = _graph.GetContextEntity
    (); + var resourceContext = _resourceGraph.GetContextEntity
    (); var splittedPath = chain.Split(QueryConstants.DOT); foreach (var requestedRelationship in splittedPath) { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _graph.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); } return parsedChain; } @@ -172,7 +172,7 @@ private IncludedResourceObjectBuilder GetBuilder() { var fields = GetSerializableFields(); var links = GetLinkBuilder(); - return new IncludedResourceObjectBuilder(fields, links, _graph, GetSerializerSettingsProvider()); + return new IncludedResourceObjectBuilder(fields, links, _resourceGraph, GetSerializerSettingsProvider()); } } diff --git a/test/UnitTests/Serialization/Server/RequestDeserializerTests.cs b/test/UnitTests/Serialization/Server/RequestDeserializerTests.cs index 35815a2d32..2e4fa17e2e 100644 --- a/test/UnitTests/Serialization/Server/RequestDeserializerTests.cs +++ b/test/UnitTests/Serialization/Server/RequestDeserializerTests.cs @@ -18,7 +18,7 @@ public class RequestDeserializerTests : DeserializerTestsSetup private readonly Mock _fieldsManagerMock = new Mock(); public RequestDeserializerTests() : base() { - _deserializer = new RequestDeserializer(_graph, _fieldsManagerMock.Object); + _deserializer = new RequestDeserializer(_resourceGraph, _fieldsManagerMock.Object); } [Fact] diff --git a/test/UnitTests/Serialization/Server/ResponseResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Server/ResponseResourceObjectBuilderTests.cs index 8d48195921..cade4043e5 100644 --- a/test/UnitTests/Serialization/Server/ResponseResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Server/ResponseResourceObjectBuilderTests.cs @@ -14,7 +14,7 @@ public class ResponseResourceObjectBuilderTests : SerializerTestsSetup public ResponseResourceObjectBuilderTests() { - _relationshipsForBuild = _graph.GetRelationships(e => new { e.Dependents }); + _relationshipsForBuild = _resourceGraph.GetRelationships(e => new { e.Dependents }); } [Fact] diff --git a/test/UnitTests/Serialization/Server/ResponseSerializerTests.cs b/test/UnitTests/Serialization/Server/ResponseSerializerTests.cs index 56b252ea52..bd806ab61b 100644 --- a/test/UnitTests/Serialization/Server/ResponseSerializerTests.cs +++ b/test/UnitTests/Serialization/Server/ResponseSerializerTests.cs @@ -89,7 +89,7 @@ public void SerializeSingle_ResourceWithIncludedRelationships_CanSerialize() PopulatedToOne = new OneToOneDependent { Id = 10 }, PopulatedToManies = new List { new OneToManyDependent { Id = 20 } } }; - var chain = _graph.GetRelationships().Select(r => new List { r }).ToList(); + var chain = _resourceGraph.GetRelationships().Select(r => new List { r }).ToList(); var serializer = GetResponseSerializer(inclusionChains: chain); // act @@ -152,13 +152,13 @@ public void SerializeSingle_ResourceWithDeeplyIncludedRelationships_CanSerialize PopulatedToManies = new List { includedEntity } }; - var chains = _graph.GetRelationships() + var chains = _resourceGraph.GetRelationships() .Select(r => { var chain = new List { r }; if (r.PublicRelationshipName != "populated-to-manies") return new List { r }; - chain.AddRange(_graph.GetRelationships()); + chain.AddRange(_resourceGraph.GetRelationships()); return chain; }).ToList(); @@ -365,7 +365,7 @@ public void SerializeSingleWithRequestRelationship_NullToOneRelationship_CanSeri // arrange var entity = new OneToOnePrincipal() { Id = 2, Dependent = null }; var serializer = GetResponseSerializer(); - var requestRelationship = _graph.GetRelationships((OneToOnePrincipal t) => t.Dependent).First(); + var requestRelationship = _resourceGraph.GetRelationships((OneToOnePrincipal t) => t.Dependent).First(); serializer.RequestRelationship = requestRelationship; // act @@ -383,7 +383,7 @@ public void SerializeSingleWithRequestRelationship_PopulatedToOneRelationship_Ca // arrange var entity = new OneToOnePrincipal() { Id = 2, Dependent = new OneToOneDependent { Id = 1 } }; var serializer = GetResponseSerializer(); - var requestRelationship = _graph.GetRelationships((OneToOnePrincipal t) => t.Dependent).First(); + var requestRelationship = _resourceGraph.GetRelationships((OneToOnePrincipal t) => t.Dependent).First(); serializer.RequestRelationship = requestRelationship; @@ -410,7 +410,7 @@ public void SerializeSingleWithRequestRelationship_EmptyToManyRelationship_CanSe // arrange var entity = new OneToManyPrincipal() { Id = 2, Dependents = new List() }; var serializer = GetResponseSerializer(); - var requestRelationship = _graph.GetRelationships((OneToManyPrincipal t) => t.Dependents).First(); + var requestRelationship = _resourceGraph.GetRelationships((OneToManyPrincipal t) => t.Dependents).First(); serializer.RequestRelationship = requestRelationship; @@ -429,7 +429,7 @@ public void SerializeSingleWithRequestRelationship_PopulatedToManyRelationship_C // arrange var entity = new OneToManyPrincipal() { Id = 2, Dependents = new List { new OneToManyDependent { Id = 1 } } }; var serializer = GetResponseSerializer(); - var requestRelationship = _graph.GetRelationships((OneToManyPrincipal t) => t.Dependents).First(); + var requestRelationship = _resourceGraph.GetRelationships((OneToManyPrincipal t) => t.Dependents).First(); serializer.RequestRelationship = requestRelationship; diff --git a/test/UnitTests/Services/EntityResourceService_Tests.cs b/test/UnitTests/Services/EntityResourceService_Tests.cs index b9e46e5a45..2ff9f83cb6 100644 --- a/test/UnitTests/Services/EntityResourceService_Tests.cs +++ b/test/UnitTests/Services/EntityResourceService_Tests.cs @@ -25,7 +25,7 @@ public class EntityResourceService_Tests private readonly Mock _crMock; private readonly Mock _pgsMock; private readonly Mock _ufMock; - private readonly IResourceGraphExplorer _resourceGraph; + private readonly IResourceGraph _resourceGraph; public EntityResourceService_Tests() { From 0230b6c956a78fa8f08c69e07488579b651a0b92 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 10:22:02 +0200 Subject: [PATCH 12/29] docs: comments for DefaultActionFilter and DefaultTypeFilter --- ...ionFilter.cs => DefaultExceptionFilter.cs} | 16 ++++---------- ...tchFilter.cs => DefaultTypeMatchFilter.cs} | 22 +++++-------------- .../IJsonApiExceptionFilterProvider.cs | 20 +++++++++++++++++ .../IJsonApiTypeMatchFilterProvider.cs | 20 +++++++++++++++++ 4 files changed, 50 insertions(+), 28 deletions(-) rename src/JsonApiDotNetCore/Middleware/{JsonApiExceptionFilter.cs => DefaultExceptionFilter.cs} (77%) rename src/JsonApiDotNetCore/Middleware/{TypeMatchFilter.cs => DefaultTypeMatchFilter.cs} (77%) create mode 100644 src/JsonApiDotNetCore/Middleware/IJsonApiExceptionFilterProvider.cs create mode 100644 src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilterProvider.cs diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiExceptionFilter.cs b/src/JsonApiDotNetCore/Middleware/DefaultExceptionFilter.cs similarity index 77% rename from src/JsonApiDotNetCore/Middleware/JsonApiExceptionFilter.cs rename to src/JsonApiDotNetCore/Middleware/DefaultExceptionFilter.cs index 386cbd8945..b6c82b27e3 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiExceptionFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/DefaultExceptionFilter.cs @@ -1,21 +1,13 @@ -using System; -using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; namespace JsonApiDotNetCore.Middleware { - public interface IJsonApiExceptionFilterProvider - { - Type Get(); - } - - public class JsonApiExceptionFilterProvider : IJsonApiExceptionFilterProvider - { - public Type Get() => typeof(DefaultExceptionFilter); - } - + /// + /// Global exception filter that wraps any thrown error with a JsonApiException. + /// public class DefaultExceptionFilter : ActionFilterAttribute, IExceptionFilter { private readonly ILogger _logger; diff --git a/src/JsonApiDotNetCore/Middleware/TypeMatchFilter.cs b/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs similarity index 77% rename from src/JsonApiDotNetCore/Middleware/TypeMatchFilter.cs rename to src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs index 542d03f4cd..0465dd00b9 100644 --- a/src/JsonApiDotNetCore/Middleware/TypeMatchFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; @@ -7,28 +7,18 @@ namespace JsonApiDotNetCore.Middleware { - public interface IJsonApiTypeMatchFilterProvider - { - Type Get(); - } - - public class JsonApiTypeMatchFilterProvider : IJsonApiTypeMatchFilterProvider - { - public Type Get() => typeof(TypeMatchFilter); - } - - public class TypeMatchFilter : IActionFilter + /// + /// Action filter used to verify the incoming type matches the target type, else return a 409 + /// + public class DefaultTypeMatchFilter : IActionFilter { private readonly IContextEntityProvider _provider; - public TypeMatchFilter(IContextEntityProvider provider) + public DefaultTypeMatchFilter(IContextEntityProvider provider) { _provider = provider; } - /// - /// Used to verify the incoming type matches the target type, else return a 409 - /// public void OnActionExecuting(ActionExecutingContext context) { var request = context.HttpContext.Request; diff --git a/src/JsonApiDotNetCore/Middleware/IJsonApiExceptionFilterProvider.cs b/src/JsonApiDotNetCore/Middleware/IJsonApiExceptionFilterProvider.cs new file mode 100644 index 0000000000..6400fa3a50 --- /dev/null +++ b/src/JsonApiDotNetCore/Middleware/IJsonApiExceptionFilterProvider.cs @@ -0,0 +1,20 @@ +using System; + +namespace JsonApiDotNetCore.Middleware +{ + /// + /// Provides the type of the global exception filter that is configured in MVC during startup. + /// This can be overridden to let JADNC use your own exception filter. The default exception filter used + /// is + /// + public interface IJsonApiExceptionFilterProvider + { + Type Get(); + } + + /// + public class JsonApiExceptionFilterProvider : IJsonApiExceptionFilterProvider + { + public Type Get() => typeof(DefaultExceptionFilter); + } +} diff --git a/src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilterProvider.cs b/src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilterProvider.cs new file mode 100644 index 0000000000..26ce36e1f7 --- /dev/null +++ b/src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilterProvider.cs @@ -0,0 +1,20 @@ +using System; + +namespace JsonApiDotNetCore.Middleware +{ + /// + /// Provides the type of the global action filter that is configured in MVC during startup. + /// This can be overridden to let JADNC use your own exception filter. The default exception filter used + /// is + /// + public interface IJsonApiTypeMatchFilterProvider + { + Type Get(); + } + + /// + public class JsonApiTypeMatchFilterProvider : IJsonApiTypeMatchFilterProvider + { + public Type Get() => typeof(DefaultTypeMatchFilter); + } +} From 1ad32fa05a830d57897dc40215bf860223daff5b Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 10:30:01 +0200 Subject: [PATCH 13/29] docs: comments JsonApiApplicationBuilder --- .../Builders/JsonApiApplicationBuilder.cs | 51 ++++++++++++++----- .../IJsonApiTypeMatchFilterProvider.cs | 2 +- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index c8dec0985d..c2a555eda6 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -23,6 +23,10 @@ namespace JsonApiDotNetCore.Builders { + /// + /// A utility class that builds a JsonApi application. It registers all required services + /// and allows the user to override parts of the startup configuration. + /// public class JsonApiApplicationBuilder { public readonly JsonApiOptions JsonApiOptions = new JsonApiOptions(); @@ -38,8 +42,17 @@ public JsonApiApplicationBuilder(IServiceCollection services, IMvcCoreBuilder mv _mvcBuilder = mvcBuilder; } + /// + /// Executes the action provided by the user to configure + /// public void ConfigureJsonApiOptions(Action configureOptions) => configureOptions(JsonApiOptions); + /// + /// Configures built-in .net core MVC (things like middleware, routing). Most of this configuration can be adjusted for the developers need. + /// Before calling .AddJsonApi(), a developer can register their own implementation of the following services to customize startup: + /// , , , + /// , and . + /// public void ConfigureMvc() { RegisterJsonApiStartupServices(); @@ -63,16 +76,27 @@ public void ConfigureMvc() _services.AddSingleton(routingConvention); // <--- why is this needed? } + /// + /// Executes autodiscovery of JADNC services. + /// public void AutoDiscover(Action autoDiscover) { autoDiscover(_serviceDiscoveryFacade); } + /// + /// Executes the action provided by the user to configure the resources using + /// + /// public void ConfigureResources(Action resourceGraphBuilder) { resourceGraphBuilder(_resourceGraphBuilder); } + /// + /// Executes the action provided by the user to configure the resources using . + /// Additionally, inspects the EF core database context for models that implement IIdentifiable. + /// public void ConfigureResources(Action resourceGraphBuilder) where TContext : DbContext { _resourceGraphBuilder.AddDbContext(); @@ -81,17 +105,9 @@ public void ConfigureResources(Action resourceG resourceGraphBuilder?.Invoke(_resourceGraphBuilder); } - private void RegisterJsonApiStartupServices() - { - _services.AddSingleton(JsonApiOptions); - _services.TryAddSingleton(new KebabCaseFormatter()); - _services.TryAddSingleton(); - _services.TryAddSingleton(); - _services.TryAddSingleton(sp => new ServiceDiscoveryFacade(_services, sp.GetRequiredService())); - _services.TryAddScoped(); - _services.TryAddScoped(); - } - + /// + /// Registers the remaining internals. + /// public void ConfigureServices() { var resourceGraph = _resourceGraphBuilder.Build(); @@ -154,7 +170,6 @@ public void ConfigureServices() _services.AddScoped(); } - private void AddQueryParameterServices() { _services.AddScoped(); @@ -174,7 +189,6 @@ private void AddQueryParameterServices() _services.AddScoped(sp => sp.GetService()); } - private void AddResourceHooks() { _services.AddSingleton(typeof(IHooksDiscovery<>), typeof(HooksDiscovery<>)); @@ -196,5 +210,16 @@ private void AddServerSerialization() _services.AddScoped(sp => sp.GetRequiredService().GetSerializer()); _services.AddScoped(); } + + private void RegisterJsonApiStartupServices() + { + _services.AddSingleton(JsonApiOptions); + _services.TryAddSingleton(new KebabCaseFormatter()); + _services.TryAddSingleton(); + _services.TryAddSingleton(); + _services.TryAddSingleton(sp => new ServiceDiscoveryFacade(_services, sp.GetRequiredService())); + _services.TryAddScoped(); + _services.TryAddScoped(); + } } } diff --git a/src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilterProvider.cs b/src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilterProvider.cs index 26ce36e1f7..50d2476890 100644 --- a/src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilterProvider.cs +++ b/src/JsonApiDotNetCore/Middleware/IJsonApiTypeMatchFilterProvider.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Middleware { /// /// Provides the type of the global action filter that is configured in MVC during startup. - /// This can be overridden to let JADNC use your own exception filter. The default exception filter used + /// This can be overridden to let JADNC use your own action filter. The default action filter used /// is /// public interface IJsonApiTypeMatchFilterProvider From c2d64176cc20e05f0b794f01571ed68062e34e8a Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 10:45:17 +0200 Subject: [PATCH 14/29] style: consistent variable naming| --- .../Hooks/ResourceHookExecutor.cs | 8 ++++---- .../Internal/DefaultRoutingConvention.cs | 7 +------ .../Internal/IControllerResourceMapping.cs | 15 +++++++++++++++ .../Internal/IDefaultRoutingConvention.cs | 11 ----------- .../Internal/IJsonApiRoutingConvention.cs | 8 ++------ .../Middleware/RequestMiddleware.cs | 18 +++++++++--------- .../Annotation/HasManyThroughAttribute.cs | 1 - .../Common/QueryParameterService.cs | 14 ++++---------- .../QueryParameterServices/FilterService.cs | 2 +- .../QueryParameterServices/IncludeService.cs | 4 ++-- .../QueryParameterServices/SortService.cs | 4 ++-- .../SparseFieldsService.cs | 10 +++++----- 12 files changed, 45 insertions(+), 57 deletions(-) create mode 100644 src/JsonApiDotNetCore/Internal/IControllerResourceMapping.cs delete mode 100644 src/JsonApiDotNetCore/Internal/IDefaultRoutingConvention.cs diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 8d4123fa8d..b351a2a92d 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -21,7 +21,7 @@ internal class ResourceHookExecutor : IResourceHookExecutor private readonly ITraversalHelper _traversalHelper; private readonly IIncludeService _includeService; private readonly ITargetedFields _targetedFields; - private readonly IResourceGraph _inverseRelationships; + private readonly IResourceGraph _resourceGraph; public ResourceHookExecutor( IHookExecutorHelper executorHelper, ITraversalHelper traversalHelper, @@ -33,7 +33,7 @@ public ResourceHookExecutor( _traversalHelper = traversalHelper; _targetedFields = targetedFields; _includeService = includedRelationships; - _inverseRelationships = resourceGraph; + _resourceGraph = resourceGraph; } /// @@ -324,7 +324,7 @@ Dictionary ReplaceKeysWithInverseRelationshi /// If it isn't, JADNC currently knows nothing about this relationship pointing back, and it /// currently cannot fire hooks for entities resolved through inverse relationships. var inversableRelationshipAttributes = entitiesByRelationship.Where(kvp => kvp.Key.InverseNavigation != null); - return inversableRelationshipAttributes.ToDictionary(kvp => _inverseRelationships.GetInverse(kvp.Key), kvp => kvp.Value); + return inversableRelationshipAttributes.ToDictionary(kvp => _resourceGraph.GetInverse(kvp.Key), kvp => kvp.Value); } /// @@ -337,7 +337,7 @@ void FireForAffectedImplicits(Type entityTypeToInclude, Dictionary _inverseRelationships.GetInverse(kvp.Key), kvp => kvp.Value); + var inverse = implicitAffected.ToDictionary(kvp => _resourceGraph.GetInverse(kvp.Key), kvp => kvp.Value); var resourcesByRelationship = CreateRelationshipHelper(entityTypeToInclude, inverse); CallHook(container, ResourceHook.BeforeImplicitUpdateRelationship, new object[] { resourcesByRelationship, pipeline, }); } diff --git a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs index e61f0d3134..ca54f13f6c 100644 --- a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs @@ -35,7 +35,7 @@ namespace JsonApiDotNetCore.Internal /// public class SomeVeryCustomController{SomeResource} : JsonApiMixin { } /// // => /some-very-customs/relationship/related-resource /// - public class DefaultRoutingConvention : IJsonApiRoutingConvention + public class DefaultRoutingConvention : IJsonApiRoutingConvention, IControllerResourceMapping { private readonly string _namespace; private readonly IResourceNameFormatter _formatter; @@ -120,11 +120,6 @@ private Type GetResourceTypeFromController(Type type) var target = typeof(BaseJsonApiController<,>); var identifiable = typeof(IIdentifiable); var currentBaseType = type; - if (type.Name.Contains("TodoItemsCustom")) - { - var x = 123; - } - while (!currentBaseType.IsGenericType || currentBaseType.GetGenericTypeDefinition() != target) { var nextBaseType = currentBaseType.BaseType; diff --git a/src/JsonApiDotNetCore/Internal/IControllerResourceMapping.cs b/src/JsonApiDotNetCore/Internal/IControllerResourceMapping.cs new file mode 100644 index 0000000000..347a85650c --- /dev/null +++ b/src/JsonApiDotNetCore/Internal/IControllerResourceMapping.cs @@ -0,0 +1,15 @@ +using System; + +namespace JsonApiDotNetCore.Internal +{ + /// + /// Registery of which resource is associated with which controller. + /// + public interface IControllerResourceMapping + { + /// + /// Get the associated resource with the controller with the provided controller name + /// + Type GetAssociatedResource(string controllerName); + } +} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Internal/IDefaultRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/IDefaultRoutingConvention.cs deleted file mode 100644 index d6a48e221a..0000000000 --- a/src/JsonApiDotNetCore/Internal/IDefaultRoutingConvention.cs +++ /dev/null @@ -1,11 +0,0 @@ -// REF: https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.CustomRoutingConvention/NameSpaceRoutingConvention.cs -// REF: https://github.com/aspnet/Mvc/issues/5691 -using System; - -namespace JsonApiDotNetCore.Internal -{ - public interface IControllerResourceMapping - { - Type GetAssociatedResource(string controllerName); - } -} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs index 223310e247..aba03b806b 100644 --- a/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.ApplicationModels; namespace JsonApiDotNetCore.Internal { @@ -7,8 +6,5 @@ namespace JsonApiDotNetCore.Internal /// Service for specifying which routing convention to use. This can be overriden to customize /// the relation between controllers and mapped routes. /// - public interface IJsonApiRoutingConvention : IApplicationModelConvention - { - Type GetAssociatedResource(string controllerName); - } + public interface IJsonApiRoutingConvention : IApplicationModelConvention { } } diff --git a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs index f8c0d399fd..13bffd4da0 100644 --- a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs @@ -21,9 +21,9 @@ public class CurrentRequestMiddleware private readonly RequestDelegate _next; private HttpContext _httpContext; private ICurrentRequest _currentRequest; - private IResourceGraph _contextEntityProvider; + private IResourceGraph _resourceGraph; private IJsonApiOptions _options; - private IJsonApiRoutingConvention _routingConvention; + private IControllerResourceMapping _controllerResourceMapping; public CurrentRequestMiddleware(RequestDelegate next) { @@ -31,15 +31,15 @@ public CurrentRequestMiddleware(RequestDelegate next) } public async Task Invoke(HttpContext httpContext, - IJsonApiRoutingConvention routingConvention, - IJsonApiOptions options, + IControllerResourceMapping controllerResourceMapping, + IJsonApiOptions options, ICurrentRequest currentRequest, - IResourceGraph contextEntityProvider) + IResourceGraph resourceGraph) { _httpContext = httpContext; _currentRequest = currentRequest; - _routingConvention = routingConvention; - _contextEntityProvider = contextEntityProvider; + _controllerResourceMapping = controllerResourceMapping; + _resourceGraph = resourceGraph; _options = options; var requestResource = GetCurrentEntity(); if (requestResource != null) @@ -166,8 +166,8 @@ private void FlushResponse(HttpContext context, int statusCode) private ContextEntity GetCurrentEntity() { var controllerName = (string)_httpContext.GetRouteData().Values["controller"]; - var resourceType = _routingConvention.GetAssociatedResource(controllerName); - var requestResource = _contextEntityProvider.GetContextEntity(resourceType); + var resourceType = _controllerResourceMapping.GetAssociatedResource(controllerName); + var requestResource = _resourceGraph.GetContextEntity(resourceType); if (requestResource == null) return requestResource; var rd = _httpContext.GetRouteData().Values; diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs index a019e97ae5..6d60f4b660 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs @@ -103,7 +103,6 @@ public override object GetValue(object entity) /// The new property value public override void SetValue(object entity, object newValue) { - var propertyInfo = entity .GetType() .GetProperty(InternalRelationshipName); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs index 60371007f0..fa57e1850f 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs @@ -14,12 +14,12 @@ namespace JsonApiDotNetCore.Query ///
  • public abstract class QueryParameterService { - protected readonly IResourceGraph _contextEntityProvider; + protected readonly IResourceGraph _resourceGraph; protected readonly ContextEntity _requestResource; - protected QueryParameterService(IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) + protected QueryParameterService(IResourceGraph resourceGraph, ICurrentRequest currentRequest) { - _contextEntityProvider = contextEntityProvider; + _resourceGraph = resourceGraph; _requestResource = currentRequest.GetRequestResource(); } @@ -48,15 +48,9 @@ protected AttrAttribute GetAttribute(string target, RelationshipAttribute relati { AttrAttribute attribute; if (relationship != null) - { - var relatedContextEntity = _contextEntityProvider.GetContextEntity(relationship.DependentType); - attribute = relatedContextEntity.Attributes - .FirstOrDefault(a => a.Is(target)); - } + attribute = _resourceGraph.GetAttributes(relationship.DependentType).FirstOrDefault(a => a.Is(target)); else - { attribute = _requestResource.Attributes.FirstOrDefault(attr => attr.Is(target)); - } if (attribute == null) throw new JsonApiException(400, $"'{target}' is not a valid attribute."); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs index 211cb996c2..2984139fe7 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs @@ -16,7 +16,7 @@ public class FilterService : QueryParameterService, IFilterService private readonly List _filters; private IResourceDefinition _requestResourceDefinition; - public FilterService(IResourceDefinitionProvider resourceDefinitionProvider, IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public FilterService(IResourceDefinitionProvider resourceDefinitionProvider, IResourceGraph resourceGraph, ICurrentRequest currentRequest) : base(resourceGraph, currentRequest) { _requestResourceDefinition = resourceDefinitionProvider.Get(_requestResource.EntityType); _filters = new List(); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs index d5edb3993d..816033eab1 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs @@ -14,7 +14,7 @@ public class IncludeService : QueryParameterService, IIncludeService /// todo: use read-only lists. private readonly List> _includedChains; - public IncludeService(IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public IncludeService(IResourceGraph resourceGraph, ICurrentRequest currentRequest) : base(resourceGraph, currentRequest) { _includedChains = new List>(); } @@ -52,7 +52,7 @@ private void ParseChain(string chain) throw CannotIncludeError(resourceContext, relationshipName); parsedChain.Add(relationship); - resourceContext = _contextEntityProvider.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); } _includedChains.Add(parsedChain); } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs index a3ceae7a0d..45a8fca0bb 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs @@ -17,9 +17,9 @@ public class SortService : QueryParameterService, ISortService private bool _isProcessed; public SortService(IResourceDefinitionProvider resourceDefinitionProvider, - IResourceGraph contextEntityProvider, + IResourceGraph resourceGraph, ICurrentRequest currentRequest) - : base(contextEntityProvider, currentRequest) + : base(resourceGraph, currentRequest) { _resourceDefinitionProvider = resourceDefinitionProvider; _queries = new List(); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index d23a4abf36..a8a72fa62a 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -24,7 +24,7 @@ public class SparseFieldsService : QueryParameterService, ISparseFieldsService public override string Name => "fields"; - public SparseFieldsService(IResourceGraph contextEntityProvider, ICurrentRequest currentRequest) : base(contextEntityProvider, currentRequest) + public SparseFieldsService(IResourceGraph resourceGraph, ICurrentRequest currentRequest) : base(resourceGraph, currentRequest) { _selectedFields = new List(); _selectedRelationshipFields = new Dictionary>(); @@ -47,7 +47,7 @@ public virtual void Parse(KeyValuePair queryParameter) var typeName = queryParameter.Key.Split(QueryConstants.OPEN_BRACKET, QueryConstants.CLOSE_BRACKET)[1]; var fields = new List { nameof(Identifiable.Id) }; - var relationship = _requestResource.Relationships.SingleOrDefault(a => a.Is(typeName)); + var relationship = _requestResource.Relationships.FirstOrDefault(a => a.Is(typeName)); if (relationship == null && string.Equals(typeName, _requestResource.EntityName, StringComparison.OrdinalIgnoreCase) == false) throw new JsonApiException(400, $"fields[{typeName}] is invalid"); @@ -56,8 +56,8 @@ public virtual void Parse(KeyValuePair queryParameter) { if (relationship != default) { - var relationProperty = _contextEntityProvider.GetContextEntity(relationship.DependentType); - var attr = relationProperty.Attributes.SingleOrDefault(a => a.Is(field)); + var relationProperty = _resourceGraph.GetContextEntity(relationship.DependentType); + var attr = relationProperty.Attributes.FirstOrDefault(a => a.Is(field)); if (attr == null) throw new JsonApiException(400, $"'{relationship.DependentType.Name}' does not contain '{field}'."); @@ -67,7 +67,7 @@ public virtual void Parse(KeyValuePair queryParameter) } else { - var attr = _requestResource.Attributes.SingleOrDefault(a => a.Is(field)); + var attr = _requestResource.Attributes.FirstOrDefault(a => a.Is(field)); if (attr == null) throw new JsonApiException(400, $"'{_requestResource.EntityName}' does not contain '{field}'."); From b4baf91eee9af046921f88275854d297164c93dc Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 11:50:48 +0200 Subject: [PATCH 15/29] chore: wire up IControllerResourceMapping --- src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index c2a555eda6..a5db20f4a5 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -73,7 +73,7 @@ public void ConfigureMvc() var routingConvention = intermediateProvider.GetRequiredService(); _mvcBuilder.AddMvcOptions(opt => opt.Conventions.Insert(0, routingConvention)); - _services.AddSingleton(routingConvention); // <--- why is this needed? + _services.AddSingleton((IControllerResourceMapping)routingConvention); } /// From f22d932dd7f3fcbf7ee7563bd54a5c5f4855452d Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 11:51:51 +0200 Subject: [PATCH 16/29] refactor: routing convention implements controller mapping interface --- src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs | 2 +- src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index a5db20f4a5..71928fddd4 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -73,7 +73,7 @@ public void ConfigureMvc() var routingConvention = intermediateProvider.GetRequiredService(); _mvcBuilder.AddMvcOptions(opt => opt.Conventions.Insert(0, routingConvention)); - _services.AddSingleton((IControllerResourceMapping)routingConvention); + _services.AddSingleton(routingConvention); } /// diff --git a/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs index aba03b806b..00eed0b4c0 100644 --- a/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Internal/IJsonApiRoutingConvention.cs @@ -6,5 +6,5 @@ namespace JsonApiDotNetCore.Internal /// Service for specifying which routing convention to use. This can be overriden to customize /// the relation between controllers and mapped routes. /// - public interface IJsonApiRoutingConvention : IApplicationModelConvention { } + public interface IJsonApiRoutingConvention : IApplicationModelConvention, IControllerResourceMapping { } } From 833da6abe3781400e9899b8e2d9eddb61a6602b3 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 12:19:19 +0200 Subject: [PATCH 17/29] style: DefaultResourceRepository and DefaultEntityService as consistent class names, with equivalent interfaces --- .../Services/CustomArticleService.cs | 4 +- .../Builders/JsonApiApplicationBuilder.cs | 38 +++++++++---------- .../Controllers/BaseJsonApiController.cs | 13 +------ .../Controllers/JsonApiController.cs | 7 +--- ...sitory.cs => DefaultResourceRepository.cs} | 12 +++--- .../Data/IEntityRepository.cs | 18 --------- ...pository.cs => IResourceReadRepository.cs} | 7 ++-- .../Data/IResourceRepository.cs | 15 ++++++++ ...ository.cs => IResourceWriteRepository.cs} | 6 +-- .../Graph/ServiceDiscoveryFacade.cs | 10 ++--- .../Hooks/Execution/HookExecutorHelper.cs | 4 +- .../Hooks/IResourceHookContainer.cs | 24 ++++++------ .../Hooks/IResourceHookExecutor.cs | 8 ++-- .../SparseFieldsService.cs | 1 - ...ceService.cs => DefaultResourceService.cs} | 18 ++++----- .../ServiceDiscoveryFacadeTests.cs | 10 ++--- .../Data/DefaultEntityRepository_Tests.cs | 8 ++-- .../IServiceCollectionExtensionsTests.cs | 2 +- .../ResourceHooks/ResourceHooksTestsSetup.cs | 8 ++-- .../Services/EntityResourceService_Tests.cs | 6 +-- 20 files changed, 100 insertions(+), 119 deletions(-) rename src/JsonApiDotNetCore/Data/{DefaultEntityRepository.cs => DefaultResourceRepository.cs} (97%) delete mode 100644 src/JsonApiDotNetCore/Data/IEntityRepository.cs rename src/JsonApiDotNetCore/Data/{IEntityReadRepository.cs => IResourceReadRepository.cs} (93%) create mode 100644 src/JsonApiDotNetCore/Data/IResourceRepository.cs rename src/JsonApiDotNetCore/Data/{IEntityWriteRepository.cs => IResourceWriteRepository.cs} (76%) rename src/JsonApiDotNetCore/Services/{EntityResourceService.cs => DefaultResourceService.cs} (95%) diff --git a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs index 56f923aadc..c348df457c 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs @@ -11,11 +11,11 @@ namespace JsonApiDotNetCoreExample.Services { - public class CustomArticleService : EntityResourceService
    + public class CustomArticleService : DefaultResourceService
    { public CustomArticleService(ISortService sortService, IFilterService filterService, - IEntityRepository repository, + IResourceRepository repository, IJsonApiOptions options, IIncludeService includeService, ISparseFieldsService sparseFieldsService, diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index 71928fddd4..2f378a4895 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -73,7 +73,7 @@ public void ConfigureMvc() var routingConvention = intermediateProvider.GetRequiredService(); _mvcBuilder.AddMvcOptions(opt => opt.Conventions.Insert(0, routingConvention)); - _services.AddSingleton(routingConvention); + _services.AddSingleton(routingConvention); } /// @@ -118,32 +118,32 @@ public void ConfigureServices() _services.AddSingleton(new DbContextOptionsBuilder().Options); } - _services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>)); - _services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>)); + _services.AddScoped(typeof(IResourceRepository<>), typeof(DefaultResourceRepository<>)); + _services.AddScoped(typeof(IResourceRepository<,>), typeof(DefaultResourceRepository<,>)); - _services.AddScoped(typeof(IEntityReadRepository<,>), typeof(DefaultEntityRepository<,>)); - _services.AddScoped(typeof(IEntityWriteRepository<,>), typeof(DefaultEntityRepository<,>)); + _services.AddScoped(typeof(IResourceReadRepository<,>), typeof(DefaultResourceRepository<,>)); + _services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(DefaultResourceRepository<,>)); - _services.AddScoped(typeof(ICreateService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(ICreateService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(ICreateService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(ICreateService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IGetAllService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IGetAllService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IGetAllService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IGetAllService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IGetByIdService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IGetByIdService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IGetByIdService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IGetByIdService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IGetRelationshipService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IUpdateService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IUpdateService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IUpdateService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IUpdateService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IDeleteService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IDeleteService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IDeleteService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IDeleteService<,>), typeof(DefaultResourceService<,>)); - _services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>)); - _services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>)); + _services.AddScoped(typeof(IResourceService<>), typeof(DefaultResourceService<>)); + _services.AddScoped(typeof(IResourceService<,>), typeof(DefaultResourceService<,>)); _services.AddSingleton(JsonApiOptions); _services.AddSingleton(resourceGraph); diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index 1b2477dc79..e0b781b283 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -4,7 +4,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Mvc; @@ -32,14 +31,11 @@ public BaseJsonApiController( IResourceService resourceService, ILoggerFactory loggerFactory) { - if(loggerFactory != null) - { + if (loggerFactory != null) _logger = loggerFactory.CreateLogger>(); - } else - { _logger = new Logger>(new LoggerFactory()); - } + _jsonApiOptions = jsonApiOptions; _getAll = resourceService; _getById = resourceService; @@ -51,7 +47,6 @@ public BaseJsonApiController( _delete = resourceService; } - public BaseJsonApiController( IJsonApiOptions jsonApiOptions, IResourceQueryService queryService = null, @@ -68,11 +63,7 @@ public BaseJsonApiController( _delete = cmdService; } - /// - /// Base constructor - /// /// - /// /// /// /// diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs index 058ef5c313..6ed4ccca04 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; using System.Threading.Tasks; using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Mvc; @@ -11,11 +9,8 @@ namespace JsonApiDotNetCore.Controllers { public class JsonApiController : BaseJsonApiController where T : class, IIdentifiable { - /// - /// - /// + /// - /// /// /// public JsonApiController( diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs similarity index 97% rename from src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs rename to src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs index 47c6bca132..1c9b4d15b0 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs @@ -18,7 +18,7 @@ namespace JsonApiDotNetCore.Data /// Provides a default repository implementation and is responsible for /// abstracting any EF Core APIs away from the service layer. /// - public class DefaultEntityRepository : IEntityRepository + public class DefaultResourceRepository : IResourceRepository where TEntity : class, IIdentifiable { private readonly ITargetedFields _targetedFields; @@ -27,7 +27,7 @@ public class DefaultEntityRepository : IEntityRepository - public class DefaultEntityRepository : DefaultEntityRepository, IEntityRepository + public class DefaultResourceRepository : DefaultResourceRepository, IResourceRepository where TEntity : class, IIdentifiable { - public DefaultEntityRepository(ITargetedFields targetedFields, + public DefaultResourceRepository(ITargetedFields targetedFields, IDbContextResolver contextResolver, IResourceGraph contextEntityProvider, IGenericProcessorFactory genericProcessorFactory) @@ -425,7 +425,7 @@ public DefaultEntityRepository(ITargetedFields targetedFields, { } - public DefaultEntityRepository(ITargetedFields targetedFields, + public DefaultResourceRepository(ITargetedFields targetedFields, IDbContextResolver contextResolver, IResourceGraph contextEntityProvider, IGenericProcessorFactory genericProcessorFactory, diff --git a/src/JsonApiDotNetCore/Data/IEntityRepository.cs b/src/JsonApiDotNetCore/Data/IEntityRepository.cs deleted file mode 100644 index 2c65b0a76a..0000000000 --- a/src/JsonApiDotNetCore/Data/IEntityRepository.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using JsonApiDotNetCore.Models; - -namespace JsonApiDotNetCore.Data -{ - public interface IEntityRepository - : IEntityRepository - where TEntity : class, IIdentifiable - { } - - public interface IEntityRepository - : IEntityReadRepository, - IEntityWriteRepository - where TEntity : class, IIdentifiable - { } -} - - diff --git a/src/JsonApiDotNetCore/Data/IEntityReadRepository.cs b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs similarity index 93% rename from src/JsonApiDotNetCore/Data/IEntityReadRepository.cs rename to src/JsonApiDotNetCore/Data/IResourceReadRepository.cs index 605a07257d..34af4ca868 100644 --- a/src/JsonApiDotNetCore/Data/IEntityReadRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -7,12 +6,12 @@ namespace JsonApiDotNetCore.Data { - public interface IEntityReadRepository - : IEntityReadRepository + public interface IResourceReadRepository + : IResourceReadRepository where TEntity : class, IIdentifiable { } - public interface IEntityReadRepository + public interface IResourceReadRepository where TEntity : class, IIdentifiable { /// diff --git a/src/JsonApiDotNetCore/Data/IResourceRepository.cs b/src/JsonApiDotNetCore/Data/IResourceRepository.cs new file mode 100644 index 0000000000..f176c187e9 --- /dev/null +++ b/src/JsonApiDotNetCore/Data/IResourceRepository.cs @@ -0,0 +1,15 @@ +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCore.Data +{ + public interface IResourceRepository + : IResourceRepository + where TEntity : class, IIdentifiable + { } + + public interface IResourceRepository + : IResourceReadRepository, + IResourceWriteRepository + where TEntity : class, IIdentifiable + { } +} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs b/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs similarity index 76% rename from src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs rename to src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs index 60c632f6a3..50b9f5aed7 100644 --- a/src/JsonApiDotNetCore/Data/IEntityWriteRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs @@ -5,12 +5,12 @@ namespace JsonApiDotNetCore.Data { - public interface IEntityWriteRepository - : IEntityWriteRepository + public interface IResourceWriteRepository + : IResourceWriteRepository where TEntity : class, IIdentifiable { } - public interface IEntityWriteRepository + public interface IResourceWriteRepository where TEntity : class, IIdentifiable { Task CreateAsync(TEntity entity); diff --git a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs index d197d55ecc..7e6f049b53 100644 --- a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs +++ b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs @@ -41,12 +41,12 @@ public class ServiceDiscoveryFacade : IServiceDiscoveryFacade }; internal static HashSet RepositoryInterfaces = new HashSet { - typeof(IEntityRepository<>), - typeof(IEntityRepository<,>), + typeof(IResourceRepository<>), + typeof(IResourceRepository<,>), typeof(IEntityWriteRepository<>), - typeof(IEntityWriteRepository<,>), + typeof(IResourceWriteRepository<,>), typeof(IEntityReadRepository<>), - typeof(IEntityReadRepository<,>) + typeof(IResourceReadRepository<,>) }; private readonly IServiceCollection _services; private readonly IResourceGraphBuilder _resourceGraphBuilder; @@ -173,7 +173,7 @@ private void AddServices(Assembly assembly, ResourceDescriptor resourceDescripto } /// - /// Add implementations to container. + /// Add implementations to container. /// /// The assembly to search for resources in. public ServiceDiscoveryFacade AddRepositories(Assembly assembly) diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index b92619b691..c41dcd5d89 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -147,9 +147,9 @@ IEnumerable GetWhereAndInclude(IEnumerable ids, Rela return repo.Include(query, inclusionChain).ToList(); } - IEntityReadRepository GetRepository() where TEntity : class, IIdentifiable + IResourceReadRepository GetRepository() where TEntity : class, IIdentifiable { - return _genericProcessorFactory.GetProcessor>(typeof(IEntityReadRepository<,>), typeof(TEntity), typeof(TId)); + return _genericProcessorFactory.GetProcessor>(typeof(IResourceReadRepository<,>), typeof(TEntity), typeof(TId)); } diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index f0f45ab276..9ab0f5c2ec 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -20,7 +20,7 @@ public interface IResourceHookContainer : IBeforeHooks, IA public interface IBeforeHooks where TResource : class, IIdentifiable { /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just before creation of entities of type . /// /// For the pipeline, @@ -41,7 +41,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// An enum indicating from where the hook was triggered. IEnumerable BeforeCreate(IEntityHashSet entities, ResourcePipeline pipeline); /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just before reading entities of type . /// /// An enum indicating from where the hook was triggered. @@ -49,7 +49,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// The string id of the requested entity, in the case of void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null); /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just before updating entities of type . /// /// For the pipeline, the @@ -77,7 +77,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable IEnumerable BeforeUpdate(IDiffableEntityHashSet entities, ResourcePipeline pipeline); /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just before deleting entities of type . /// /// For the pipeline, @@ -97,7 +97,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// An enum indicating from where the hook was triggered. IEnumerable BeforeDelete(IEntityHashSet entities, ResourcePipeline pipeline); /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just before updating relationships to entities of type . /// /// This hook is fired when a relationship is created to entities of type @@ -117,7 +117,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// A helper that groups the entities by the affected relationship IEnumerable BeforeUpdateRelationship(HashSet ids, IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just before implicitly updating relationships to entities of type . /// /// This hook is fired when a relationship to entities of type @@ -143,7 +143,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable public interface IAfterHooks where TResource : class, IIdentifiable { /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just after creation of entities of type . /// /// If relationships were created with the created entities, this will @@ -156,7 +156,7 @@ public interface IAfterHooks where TResource : class, IIdentifiable /// An enum indicating from where the hook was triggered. void AfterCreate(HashSet entities, ResourcePipeline pipeline); /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just after reading entities of type . /// /// The unique set of affected entities. @@ -165,7 +165,7 @@ public interface IAfterHooks where TResource : class, IIdentifiable /// or if they were included as a relationship void AfterRead(HashSet entities, ResourcePipeline pipeline, bool isIncluded = false); /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just after updating entities of type . /// /// If relationships were updated with the updated entities, this will @@ -177,7 +177,7 @@ public interface IAfterHooks where TResource : class, IIdentifiable /// An enum indicating from where the hook was triggered. void AfterUpdate(HashSet entities, ResourcePipeline pipeline); /// - /// Implement this hook to run custom logic in the + /// Implement this hook to run custom logic in the /// layer just after deletion of entities of type . /// /// The unique set of affected entities. @@ -185,7 +185,7 @@ public interface IAfterHooks where TResource : class, IIdentifiable /// If set to true if the deletion was succeeded in the repository layer. void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded); /// - /// Implement this hook to run custom logic in the layer + /// Implement this hook to run custom logic in the layer /// just after a relationship was updated. /// /// Relationship helper. @@ -201,7 +201,7 @@ public interface IOnHooks where TResource : class, IIdentifiable /// /// Implement this hook to transform the result data just before returning /// the entities of type from the - /// layer + /// layer /// /// The returned may be a subset /// of and may contain changes in properties diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs index e642e6b9a4..accadc029b 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs @@ -23,7 +23,7 @@ public interface IBeforeExecutor { /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// The returned set will be used in the actual operation in . + /// The returned set will be used in the actual operation in . /// /// Fires the /// hook where T = for values in parameter . @@ -45,12 +45,12 @@ public interface IBeforeExecutor /// /// An enum indicating from where the hook was triggered. /// StringId of the requested entity in the case of - /// . + /// . /// The type of the request entity void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TResource : class, IIdentifiable; /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// The returned set will be used in the actual operation in . + /// The returned set will be used in the actual operation in . /// /// Fires the /// hook where T = for values in parameter . @@ -71,7 +71,7 @@ public interface IBeforeExecutor IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// The returned set will be used in the actual operation in . + /// The returned set will be used in the actual operation in . /// /// Fires the /// hook where T = for values in parameter . diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index 07e9cfb46e..e6b0fcf56b 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -75,7 +75,6 @@ public virtual void Parse(KeyValuePair queryParameter) foreach (var field in fields) RegisterRelatedResourceField(field, relationship); - } } diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs similarity index 95% rename from src/JsonApiDotNetCore/Services/EntityResourceService.cs rename to src/JsonApiDotNetCore/Services/DefaultResourceService.cs index 58d7ad1b3d..2f27db62d7 100644 --- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs +++ b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs @@ -18,7 +18,7 @@ namespace JsonApiDotNetCore.Services /// /// /// - public class EntityResourceService : + public class DefaultResourceService : IResourceService where TResource : class, IIdentifiable { @@ -26,21 +26,21 @@ public class EntityResourceService : private readonly IJsonApiOptions _options; private readonly IFilterService _filterService; private readonly ISortService _sortService; - private readonly IEntityRepository _repository; + private readonly IResourceRepository _repository; private readonly ILogger _logger; private readonly IResourceHookExecutor _hookExecutor; private readonly IIncludeService _includeService; private readonly ISparseFieldsService _sparseFieldsService; private readonly ContextEntity _currentRequestResource; - public EntityResourceService( + public DefaultResourceService( ISortService sortService, IFilterService filterService, - IEntityRepository repository, IJsonApiOptions options, IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageService pageManager, + IResourceRepository repository, IContextEntityProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) @@ -53,7 +53,7 @@ public EntityResourceService( _filterService = filterService; _repository = repository; _hookExecutor = hookExecutor; - _logger = loggerFactory?.CreateLogger>(); + _logger = loggerFactory?.CreateLogger>(); _currentRequestResource = provider.GetContextEntity(); } @@ -283,7 +283,7 @@ private async Task GetWithRelationshipsAsync(TId id) TResource value; // https://github.com/aspnet/EntityFrameworkCore/issues/6573 - if (sparseFieldset.Count() > 0) + if (sparseFieldset.Any()) value = query.FirstOrDefault(); else value = await _repository.FirstOrDefaultAsync(query); @@ -319,15 +319,15 @@ private List AsList(TResource entity) /// No mapping with integer as default /// /// - public class EntityResourceService : EntityResourceService, + public class DefaultResourceService : DefaultResourceService, IResourceService where TResource : class, IIdentifiable { - public EntityResourceService(ISortService sortService, IFilterService filterService, IEntityRepository repository, + public DefaultResourceService(ISortService sortService, IFilterService filterService, IResourceRepository repository, IJsonApiOptions options, IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageService pageManager, IContextEntityProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) - : base(sortService, filterService, repository, options, includeService, sparseFieldsService, pageManager, provider, hookExecutor, loggerFactory) + : base(sortService, filterService, options, includeService, sparseFieldsService, pageManager, repository, provider, hookExecutor, loggerFactory) { } } diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index c4cec73a15..8aec2dc980 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -88,18 +88,18 @@ public void AddCurrentAssembly_Adds_Repositories_To_Container() // assert var services = _services.BuildServiceProvider(); - Assert.IsType(services.GetService>()); + Assert.IsType(services.GetService>()); } public class TestModel : Identifiable { } - public class TestModelService : EntityResourceService + public class TestModelService : DefaultResourceService { - private static IEntityRepository _repo = new Mock>().Object; + private static IResourceRepository _repo = new Mock>().Object; private static IJsonApiContext _jsonApiContext = new Mock().Object; public TestModelService( - IEntityRepository repository, + IResourceRepository repository, IJsonApiOptions options, IRequestContext currentRequest, IPageQueryService pageService, @@ -110,7 +110,7 @@ public TestModelService( } } - public class TestModelRepository : DefaultEntityRepository + public class TestModelRepository : DefaultResourceRepository { internal static IDbContextResolver _dbContextResolver; private static IJsonApiContext _jsonApiContext = new Mock().Object; diff --git a/test/UnitTests/Data/DefaultEntityRepository_Tests.cs b/test/UnitTests/Data/DefaultEntityRepository_Tests.cs index 6c0ce780c1..8b2410dbbb 100644 --- a/test/UnitTests/Data/DefaultEntityRepository_Tests.cs +++ b/test/UnitTests/Data/DefaultEntityRepository_Tests.cs @@ -16,7 +16,7 @@ namespace UnitTests.Data { - public class DefaultEntityRepository_Tests : JsonApiControllerMixin + public class DefaultResourceRepository_Tests : JsonApiControllerMixin { private readonly Mock _currentRequestMock; private readonly Mock> _dbSetMock; @@ -25,7 +25,7 @@ public class DefaultEntityRepository_Tests : JsonApiControllerMixin private readonly Mock _contextResolverMock; private readonly TodoItem _todoItem; - public DefaultEntityRepository_Tests() + public DefaultResourceRepository_Tests() { _todoItem = new TodoItem { @@ -66,7 +66,7 @@ public async Task UpdateAsync_Updates_Attributes_In_AttributesToUpdate() Assert.Equal(todoItemUpdates.Description, updatedItem.Description); } - private DefaultEntityRepository GetRepository() + private DefaultResourceRepository GetRepository() { _contextMock @@ -80,7 +80,7 @@ private DefaultEntityRepository GetRepository() var resourceGraph = new ResourceGraphBuilder().AddResource().Build(); - return new DefaultEntityRepository( + return new DefaultResourceRepository( _targetedFieldsMock.Object, _contextResolverMock.Object, resourceGraph, null, null); diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index 0f2ce10f96..456046f141 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -47,7 +47,7 @@ public void AddJsonApiInternals_Adds_All_Required_Services() currentRequest.SetRequestResource(resourceGraph.GetContextEntity()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); - Assert.NotNull(provider.GetService(typeof(IEntityRepository))); + Assert.NotNull(provider.GetService(typeof(IResourceRepository))); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService>()); diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 0e5d84b08e..0da9b338c2 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -329,8 +329,8 @@ void SetupProcessorFactoryForResourceDefinition( var idType = TypeHelper.GetIdentifierType(); if (idType == typeof(int)) { - IEntityReadRepository repo = CreateTestRepository(dbContext); - processorFactory.Setup(c => c.GetProcessor>(typeof(IEntityReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); + IResourceReadRepository repo = CreateTestRepository(dbContext); + processorFactory.Setup(c => c.GetProcessor>(typeof(IResourceReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); } else { @@ -340,12 +340,12 @@ void SetupProcessorFactoryForResourceDefinition( } } - IEntityReadRepository CreateTestRepository( + IResourceReadRepository CreateTestRepository( AppDbContext dbContext ) where TModel : class, IIdentifiable { IDbContextResolver resolver = CreateTestDbResolver(dbContext); - return new DefaultEntityRepository(null, resolver, null, null, null); + return new DefaultResourceRepository(null, resolver, null, null, null); } IDbContextResolver CreateTestDbResolver(AppDbContext dbContext) where TModel : class, IIdentifiable diff --git a/test/UnitTests/Services/EntityResourceService_Tests.cs b/test/UnitTests/Services/EntityResourceService_Tests.cs index 2ff9f83cb6..19bbe41689 100644 --- a/test/UnitTests/Services/EntityResourceService_Tests.cs +++ b/test/UnitTests/Services/EntityResourceService_Tests.cs @@ -20,7 +20,7 @@ namespace UnitTests.Services { public class EntityResourceService_Tests { - private readonly Mock> _repositoryMock = new Mock>(); + private readonly Mock> _repositoryMock = new Mock>(); private readonly ILoggerFactory _loggerFactory = new Mock().Object; private readonly Mock _crMock; private readonly Mock _pgsMock; @@ -95,9 +95,9 @@ public async Task GetRelationshipAsync_Returns_Relationship_Value() Assert.Equal(todoItem.Collection.Id, collection.Id); } - private EntityResourceService GetService() + private DefaultResourceService GetService() { - return new EntityResourceService(null, null, _repositoryMock.Object, new JsonApiOptions(), null, null, _pgsMock.Object, _resourceGraph); + return new DefaultResourceService(null, null, _repositoryMock.Object, new JsonApiOptions(), null, null, _pgsMock.Object, _resourceGraph); } } } From 519513d0d6bb8c1f23e5893d81f337580b77357f Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 12:22:59 +0200 Subject: [PATCH 18/29] style: rename old repository interfaces --- src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs index 7e6f049b53..f03f2ae890 100644 --- a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs +++ b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs @@ -43,9 +43,9 @@ public class ServiceDiscoveryFacade : IServiceDiscoveryFacade internal static HashSet RepositoryInterfaces = new HashSet { typeof(IResourceRepository<>), typeof(IResourceRepository<,>), - typeof(IEntityWriteRepository<>), + typeof(IResourceWriteRepository<>), typeof(IResourceWriteRepository<,>), - typeof(IEntityReadRepository<>), + typeof(IResourceReadRepository<>), typeof(IResourceReadRepository<,>) }; private readonly IServiceCollection _services; From d03858287ad73f69c6cb8a0013fdec98e9ed2a18 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 12:23:36 +0200 Subject: [PATCH 19/29] style: ContextEntity --> ResourceContext --- benchmarks/Query/QueryParser_Benchmarks.cs | 2 +- .../ModelDefinition.cs | 2 +- .../Services/CustomArticleService.cs | 2 +- .../Builders/JsonApiApplicationBuilder.cs | 2 +- .../Builders/ResourceGraphBuilder.cs | 6 ++-- .../Internal/ContextEntity.cs | 2 +- .../Contracts/IContextEntityProvider.cs | 12 ++++---- .../Contracts/IResourceGraphExplorer.cs | 2 +- .../Internal/InverseRelationships.cs | 6 ++-- .../Internal/ResourceGraph.cs | 28 +++++++++---------- .../Middleware/DefaultTypeMatchFilter.cs | 6 ++-- .../Middleware/RequestMiddleware.cs | 4 +-- .../Models/Annotation/HasOneAttribute.cs | 2 +- .../Models/ResourceDefinition.cs | 4 +-- .../Common/QueryParameterService.cs | 2 +- .../QueryParameterServices/IncludeService.cs | 6 ++-- .../SparseFieldsService.cs | 2 +- .../Contracts/ICurrentRequest.cs | 4 +-- .../RequestServices/CurrentRequest.cs | 6 ++-- .../Client/ResponseDeserializer.cs | 4 +-- .../Common/BaseDocumentParser.cs | 6 ++-- .../Serialization/Common/DocumentBuilder.cs | 4 +-- .../Common/ResourceObjectBuilder.cs | 8 +++--- .../Builders/IncludedResourceObjectBuilder.cs | 4 +-- .../Server/Builders/LinkBuilder.cs | 26 ++++++++--------- .../Builders/ResponseResourceObjectBuilder.cs | 2 +- .../Server/Contracts/ILinkBuilder.cs | 2 +- .../Server/RequestDeserializer.cs | 2 +- .../Server/ResponseSerializer.cs | 4 +-- .../Server/ResponseSerializerFactory.cs | 2 +- .../Services/DefaultResourceService.cs | 8 +++--- .../Services/ResourceDefinitionProvider.cs | 2 +- .../ServiceDiscoveryFacadeTests.cs | 8 +++--- .../Builders/ContextGraphBuilder_Tests.cs | 14 +++++----- test/UnitTests/Builders/LinkBuilderTests.cs | 18 ++++++------ .../IServiceCollectionExtensionsTests.cs | 4 +-- .../QueryParameters/IncludeServiceTests.cs | 4 +-- .../QueryParametersUnitTestCollection.cs | 6 ++-- .../SparseFieldsServiceTests.cs | 10 +++---- .../ResourceHooks/ResourceHooksTestsSetup.cs | 4 +-- .../Common/DocumentParserTests.cs | 2 +- .../Serialization/SerializerTestsSetup.cs | 14 +++++----- .../IncludedResourceObjectBuilderTests.cs | 4 +-- 43 files changed, 131 insertions(+), 131 deletions(-) diff --git a/benchmarks/Query/QueryParser_Benchmarks.cs b/benchmarks/Query/QueryParser_Benchmarks.cs index 6cfe71843d..68c08e8e79 100644 --- a/benchmarks/Query/QueryParser_Benchmarks.cs +++ b/benchmarks/Query/QueryParser_Benchmarks.cs @@ -23,7 +23,7 @@ public class QueryParser_Benchmarks { public QueryParser_Benchmarks() { var requestMock = new Mock(); - requestMock.Setup(m => m.GetRequestResource()).Returns(new ContextEntity { + requestMock.Setup(m => m.GetRequestResource()).Returns(new ResourceContext { Attributes = new List { new AttrAttribute(ATTRIBUTE, ATTRIBUTE) } diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs index aa19ac3f71..4e82b45763 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs +++ b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs @@ -6,7 +6,7 @@ namespace GettingStarted.ResourceDefinitionExample { public class ModelDefinition : ResourceDefinition { - public ModelDefinition(IContextEntityProvider provider) : base(provider) + public ModelDefinition(IResourceContextProvider provider) : base(provider) { } diff --git a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs index c348df457c..549feaaa66 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs @@ -20,7 +20,7 @@ public CustomArticleService(ISortService sortService, IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageService pageService, - IContextEntityProvider provider, + IResourceContextProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) : base(sortService, filterService, repository, options, includeService, sparseFieldsService, diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index 2f378a4895..6b3e02390f 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -149,7 +149,7 @@ public void ConfigureServices() _services.AddSingleton(resourceGraph); _services.AddSingleton(); _services.AddSingleton(resourceGraph); - _services.AddSingleton(resourceGraph); + _services.AddSingleton(resourceGraph); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index 14144d3ae8..bfc0dc25e6 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -17,7 +17,7 @@ namespace JsonApiDotNetCore.Builders { public class ResourceGraphBuilder : IResourceGraphBuilder { - private readonly List _entities = new List(); + private readonly List _entities = new List(); private readonly List _validationResults = new List(); private readonly IResourceNameFormatter _resourceNameFormatter = new KebabCaseFormatter(); @@ -36,7 +36,7 @@ public IResourceGraph Build() return resourceGraph; } - private void SetResourceLinksOptions(ContextEntity resourceContext) + private void SetResourceLinksOptions(ResourceContext resourceContext) { var attribute = (LinksAttribute)resourceContext.EntityType.GetCustomAttribute(typeof(LinksAttribute)); if (attribute != null) @@ -67,7 +67,7 @@ public IResourceGraphBuilder AddResource(Type entityType, Type idType, string pl return this; } - private ContextEntity GetEntity(string pluralizedTypeName, Type entityType, Type idType) => new ContextEntity + private ResourceContext GetEntity(string pluralizedTypeName, Type entityType, Type idType) => new ResourceContext { EntityName = pluralizedTypeName, EntityType = entityType, diff --git a/src/JsonApiDotNetCore/Internal/ContextEntity.cs b/src/JsonApiDotNetCore/Internal/ContextEntity.cs index 67e7d01026..84b66979d1 100644 --- a/src/JsonApiDotNetCore/Internal/ContextEntity.cs +++ b/src/JsonApiDotNetCore/Internal/ContextEntity.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCore.Internal { - public class ContextEntity + public class ResourceContext { /// /// The exposed resource name diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs index c5bf26cc7a..f967679cbe 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs @@ -7,28 +7,28 @@ namespace JsonApiDotNetCore.Internal.Contracts { /// - /// Responsible for getting s from the . + /// Responsible for getting s from the . /// - public interface IContextEntityProvider + public interface IResourceContextProvider { /// /// Gets all registered context entities /// - ContextEntity[] GetContextEntities(); + ResourceContext[] GetContextEntities(); /// /// Get the resource metadata by the DbSet property name /// - ContextEntity GetContextEntity(string exposedResourceName); + ResourceContext GetResourceContext(string exposedResourceName); /// /// Get the resource metadata by the resource type /// - ContextEntity GetContextEntity(Type resourceType); + ResourceContext GetResourceContext(Type resourceType); /// /// Get the resource metadata by the resource type /// - ContextEntity GetContextEntity() where TResource : class, IIdentifiable; + ResourceContext GetResourceContext() where TResource : class, IIdentifiable; } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs index 550f351e8f..b69ac2a6d1 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceGraphExplorer.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCore.Internal.Contracts /// Responsible for retrieving the exposed resource fields (attributes and /// relationships) of registered resources in the resource resourceGraph. /// - public interface IResourceGraph : IContextEntityProvider + public interface IResourceGraph : IResourceContextProvider { /// /// Gets all fields (attributes and relationships) for diff --git a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs index ee6b9aa249..887069ed1d 100644 --- a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs +++ b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs @@ -29,10 +29,10 @@ public interface IInverseRelationships /// public class InverseRelationships : IInverseRelationships { - private readonly IContextEntityProvider _provider; + private readonly IResourceContextProvider _provider; private readonly IDbContextResolver _resolver; - public InverseRelationships(IContextEntityProvider provider, IDbContextResolver resolver = null) + public InverseRelationships(IResourceContextProvider provider, IDbContextResolver resolver = null) { _provider = provider; _resolver = resolver; @@ -45,7 +45,7 @@ public void Resolve() { DbContext context = _resolver.GetContext(); - foreach (ContextEntity ce in _provider.GetContextEntities()) + foreach (ResourceContext ce in _provider.GetContextEntities()) { IEntityType meta = context.Model.FindEntityType(ce.EntityType); if (meta == null) continue; diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index dab6833fb1..ecf23b89f9 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -13,27 +13,27 @@ namespace JsonApiDotNetCore.Internal public class ResourceGraph : IResourceGraph { internal List ValidationResults { get; } - private List _entities { get; } + private List _entities { get; } - public ResourceGraph(List entities, List validationResults = null) + public ResourceGraph(List entities, List validationResults = null) { _entities = entities; ValidationResults = validationResults; } /// - public ContextEntity[] GetContextEntities() => _entities.ToArray(); + public ResourceContext[] GetContextEntities() => _entities.ToArray(); /// - public ContextEntity GetContextEntity(string entityName) + public ResourceContext GetResourceContext(string entityName) => _entities.SingleOrDefault(e => string.Equals(e.EntityName, entityName, StringComparison.OrdinalIgnoreCase)); /// - public ContextEntity GetContextEntity(Type entityType) + public ResourceContext GetResourceContext(Type entityType) => _entities.SingleOrDefault(e => e.EntityType == entityType); /// - public ContextEntity GetContextEntity() where TResource : class, IIdentifiable - => GetContextEntity(typeof(TResource)); + public ResourceContext GetResourceContext() where TResource : class, IIdentifiable + => GetResourceContext(typeof(TResource)); /// public List GetFields(Expression> selector = null) where T : IIdentifiable @@ -53,24 +53,24 @@ public List GetRelationships(Expression public List GetFields(Type type) { - return GetContextEntity(type).Fields.ToList(); + return GetResourceContext(type).Fields.ToList(); } /// public List GetAttributes(Type type) { - return GetContextEntity(type).Attributes.ToList(); + return GetResourceContext(type).Attributes.ToList(); } /// public List GetRelationships(Type type) { - return GetContextEntity(type).Relationships.ToList(); + return GetResourceContext(type).Relationships.ToList(); } /// public RelationshipAttribute GetInverse(RelationshipAttribute relationship) { if (relationship.InverseNavigation == null) return null; - return GetContextEntity(relationship.DependentType) + return GetResourceContext(relationship.DependentType) .Relationships .SingleOrDefault(r => r.InternalRelationshipName == relationship.InverseNavigation); } @@ -79,11 +79,11 @@ private IEnumerable Getter(Expression> selec { IEnumerable available; if (type == FieldFilterType.Attribute) - available = GetContextEntity(typeof(T)).Attributes.Cast(); + available = GetResourceContext(typeof(T)).Attributes.Cast(); else if (type == FieldFilterType.Relationship) - available = GetContextEntity(typeof(T)).Relationships.Cast(); + available = GetResourceContext(typeof(T)).Relationships.Cast(); else - available = GetContextEntity(typeof(T)).Fields; + available = GetResourceContext(typeof(T)).Fields; if (selector == null) return available; diff --git a/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs b/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs index 0465dd00b9..a880d35213 100644 --- a/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs @@ -12,9 +12,9 @@ namespace JsonApiDotNetCore.Middleware /// public class DefaultTypeMatchFilter : IActionFilter { - private readonly IContextEntityProvider _provider; + private readonly IResourceContextProvider _provider; - public DefaultTypeMatchFilter(IContextEntityProvider provider) + public DefaultTypeMatchFilter(IResourceContextProvider provider) { _provider = provider; } @@ -29,7 +29,7 @@ public void OnActionExecuting(ActionExecutingContext context) if (deserializedType != null && targetType != null && deserializedType != targetType) { - var expectedJsonApiResource = _provider.GetContextEntity(targetType); + var expectedJsonApiResource = _provider.GetResourceContext(targetType); throw new JsonApiException(409, $"Cannot '{context.HttpContext.Request.Method}' type '{deserializedType.Name}' " diff --git a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs index 13bffd4da0..45ac99cfb7 100644 --- a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs @@ -163,11 +163,11 @@ private void FlushResponse(HttpContext context, int statusCode) /// Gets the current entity that we need for serialization and deserialization. /// /// - private ContextEntity GetCurrentEntity() + private ResourceContext GetCurrentEntity() { var controllerName = (string)_httpContext.GetRouteData().Values["controller"]; var resourceType = _controllerResourceMapping.GetAssociatedResource(controllerName); - var requestResource = _resourceGraph.GetContextEntity(resourceType); + var requestResource = _resourceGraph.GetResourceContext(resourceType); if (requestResource == null) return requestResource; var rd = _httpContext.GetRouteData().Values; diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs index 1037aa9882..c599ef4eec 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs @@ -12,7 +12,7 @@ public class HasOneAttribute : RelationshipAttribute /// /// The relationship name as exposed by the API /// Enum to set which links should be outputted for this relationship. Defaults to which means that the configuration in - /// or is used. + /// or is used. /// Whether or not this relationship can be included using the ?include=public-name query string /// The foreign key property name. Defaults to "{RelationshipName}Id" /// The name of the entity mapped property, defaults to null diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index d23df72e30..56f8a1f50e 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -28,13 +28,13 @@ public interface IResourceDefinition /// The resource type public class ResourceDefinition : IResourceDefinition, IResourceHookContainer where TResource : class, IIdentifiable { - private readonly ContextEntity _contextEntity; + private readonly ResourceContext _contextEntity; private readonly IResourceGraph _resourceGraph; private List _allowedAttributes; private List _allowedRelationships; public ResourceDefinition(IResourceGraph resourceGraph) { - _contextEntity = resourceGraph.GetContextEntity(typeof(TResource)); + _contextEntity = resourceGraph.GetResourceContext(typeof(TResource)); _allowedAttributes = _contextEntity.Attributes; _allowedRelationships = _contextEntity.Relationships; _resourceGraph = resourceGraph; diff --git a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs index fa57e1850f..e9930a3a9e 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs @@ -15,7 +15,7 @@ namespace JsonApiDotNetCore.Query public abstract class QueryParameterService { protected readonly IResourceGraph _resourceGraph; - protected readonly ContextEntity _requestResource; + protected readonly ResourceContext _requestResource; protected QueryParameterService(IResourceGraph resourceGraph, ICurrentRequest currentRequest) { diff --git a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs index 816033eab1..cc04552cfa 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs @@ -52,17 +52,17 @@ private void ParseChain(string chain) throw CannotIncludeError(resourceContext, relationshipName); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.DependentType); } _includedChains.Add(parsedChain); } - private JsonApiException CannotIncludeError(ContextEntity resourceContext, string requestedRelationship) + private JsonApiException CannotIncludeError(ResourceContext resourceContext, string requestedRelationship) { return new JsonApiException(400, $"Including the relationship {requestedRelationship} on {resourceContext.EntityName} is not allowed"); } - private JsonApiException InvalidRelationshipError(ContextEntity resourceContext, string requestedRelationship) + private JsonApiException InvalidRelationshipError(ResourceContext resourceContext, string requestedRelationship) { return new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {resourceContext.EntityName}", $"{resourceContext.EntityName} does not have a relationship named {requestedRelationship}"); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index e6b0fcf56b..db50c46ad0 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -83,7 +83,7 @@ public virtual void Parse(KeyValuePair queryParameter) /// private void RegisterRelatedResourceField(string field, RelationshipAttribute relationship) { - var relationProperty = _resourceGraph.GetContextEntity(relationship.DependentType); + var relationProperty = _resourceGraph.GetResourceContext(relationship.DependentType); var attr = relationProperty.Attributes.SingleOrDefault(a => a.Is(field)); if (attr == null) throw new JsonApiException(400, $"'{relationship.DependentType.Name}' does not contain '{field}'."); diff --git a/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs b/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs index ac65ffbc2c..3b796e033d 100644 --- a/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs +++ b/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs @@ -36,8 +36,8 @@ public interface ICurrentRequest /// Sets the current context entity for this entire request /// /// - void SetRequestResource(ContextEntity contextEntityCurrent); + void SetRequestResource(ResourceContext contextEntityCurrent); - ContextEntity GetRequestResource(); + ResourceContext GetRequestResource(); } } diff --git a/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs b/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs index bbaf8c037c..9648e06e6b 100644 --- a/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs +++ b/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCore.Managers { class CurrentRequest : ICurrentRequest { - private ContextEntity _contextEntity; + private ResourceContext _contextEntity; public string BasePath { get; set; } public bool IsRelationshipPath { get; set; } public RelationshipAttribute RequestRelationship { get; set; } @@ -15,12 +15,12 @@ class CurrentRequest : ICurrentRequest /// The main resource of the request. /// /// - public ContextEntity GetRequestResource() + public ResourceContext GetRequestResource() { return _contextEntity; } - public void SetRequestResource(ContextEntity primaryResource) + public void SetRequestResource(ResourceContext primaryResource) { _contextEntity = primaryResource; } diff --git a/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs b/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs index 31a3c78d1d..c8d990ccd1 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs @@ -13,7 +13,7 @@ namespace JsonApiDotNetCore.Serialization.Client /// public class ResponseDeserializer : BaseDocumentParser, IResponseDeserializer { - public ResponseDeserializer(IContextEntityProvider provider) : base(provider) { } + public ResponseDeserializer(IResourceContextProvider provider) : base(provider) { } /// public DeserializedSingleResponse DeserializeSingle(string body) where TResource : class, IIdentifiable @@ -91,7 +91,7 @@ private IIdentifiable ParseIncludedRelationship(RelationshipAttribute relationsh if (includedResource == null) return relatedInstance; - var contextEntity = _provider.GetContextEntity(relatedResourceIdentifier.Type); + var contextEntity = _provider.GetResourceContext(relatedResourceIdentifier.Type); if (contextEntity == null) throw new InvalidOperationException($"Included type '{relationshipAttr.DependentType}' is not a registered json:api resource."); diff --git a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs index 845a711146..651a88fbed 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs @@ -18,10 +18,10 @@ namespace JsonApiDotNetCore.Serialization /// public abstract class BaseDocumentParser { - protected readonly IContextEntityProvider _provider; + protected readonly IResourceContextProvider _provider; protected Document _document; - protected BaseDocumentParser(IContextEntityProvider provider) + protected BaseDocumentParser(IResourceContextProvider provider) { _provider = provider; } @@ -128,7 +128,7 @@ private JToken LoadJToken(string body) /// The parsed entity private IIdentifiable ParseResourceObject(ResourceObject data) { - var contextEntity = _provider.GetContextEntity(data.Type); + var contextEntity = _provider.GetResourceContext(data.Type); if (contextEntity == null) { throw new JsonApiException(400, diff --git a/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs b/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs index 4fab07117a..54e4066ecf 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs @@ -11,9 +11,9 @@ namespace JsonApiDotNetCore.Serialization /// public abstract class BaseDocumentBuilder { - protected readonly IContextEntityProvider _provider; + protected readonly IResourceContextProvider _provider; protected readonly IResourceObjectBuilder _resourceObjectBuilder; - protected BaseDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IContextEntityProvider provider) + protected BaseDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IResourceContextProvider provider) { _resourceObjectBuilder = resourceObjectBuilder; _provider = provider; diff --git a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs index 589d4ef4ac..d9582a84d6 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs @@ -12,11 +12,11 @@ namespace JsonApiDotNetCore.Serialization /// public class ResourceObjectBuilder : IResourceObjectBuilder { - protected readonly IContextEntityProvider _provider; + protected readonly IResourceContextProvider _provider; private readonly ResourceObjectBuilderSettings _settings; private const string _identifiablePropertyName = nameof(Identifiable.Id); - public ResourceObjectBuilder(IContextEntityProvider provider, ResourceObjectBuilderSettings settings) + public ResourceObjectBuilder(IResourceContextProvider provider, ResourceObjectBuilderSettings settings) { _provider = provider; _settings = settings; @@ -25,7 +25,7 @@ public ResourceObjectBuilder(IContextEntityProvider provider, ResourceObjectBuil /// public ResourceObject Build(IIdentifiable entity, IEnumerable attributes = null, IEnumerable relationships = null) { - var resourceContext = _provider.GetContextEntity(entity.GetType()); + var resourceContext = _provider.GetResourceContext(entity.GetType()); // populating the top-level "type" and "id" members. var ro = new ResourceObject { Type = resourceContext.EntityName, Id = entity.StringId.NullIfEmpty() }; @@ -98,7 +98,7 @@ private List GetRelatedResourceLinkage(HasManyAttribut /// private ResourceIdentifierObject GetResourceIdentifier(IIdentifiable entity) { - var resourceName = _provider.GetContextEntity(entity.GetType()).EntityName; + var resourceName = _provider.GetResourceContext(entity.GetType()).EntityName; return new ResourceIdentifierObject { Type = resourceName, diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs index c663b8ec5a..6ca1091216 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs @@ -16,7 +16,7 @@ public class IncludedResourceObjectBuilder : ResourceObjectBuilder, IIncludedRes public IncludedResourceObjectBuilder(IFieldsToSerialize fieldsToSerialize, ILinkBuilder linkBuilder, - IContextEntityProvider provider, + IResourceContextProvider provider, IResourceObjectBuilderSettingsProvider settingsProvider) : base(provider, settingsProvider.Get()) { @@ -121,7 +121,7 @@ protected override RelationshipEntry GetRelationshipData(RelationshipAttribute r private ResourceObject GetOrBuildResourceObject(IIdentifiable parent, RelationshipAttribute relationship) { var type = parent.GetType(); - var resourceName = _provider.GetContextEntity(type).EntityName; + var resourceName = _provider.GetResourceContext(type).EntityName; var entry = _included.SingleOrDefault(ro => ro.Type == resourceName && ro.Id == parent.StringId); if (entry == null) { diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs index fe2badc807..959799c790 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.Serialization.Server.Builders { public class LinkBuilder : ILinkBuilder { - private readonly IContextEntityProvider _provider; + private readonly IResourceContextProvider _provider; private readonly ILinksConfiguration _options; private readonly ICurrentRequest _currentRequest; private readonly IPageService _pageService; @@ -18,7 +18,7 @@ public class LinkBuilder : ILinkBuilder public LinkBuilder(ILinksConfiguration options, ICurrentRequest currentRequest, IPageService pageService, - IContextEntityProvider provider) + IResourceContextProvider provider) { _options = options; _currentRequest = currentRequest; @@ -27,7 +27,7 @@ public LinkBuilder(ILinksConfiguration options, } /// - public TopLevelLinks GetTopLevelLinks(ContextEntity primaryResource) + public TopLevelLinks GetTopLevelLinks(ResourceContext primaryResource) { TopLevelLinks topLevelLinks = null; if (ShouldAddTopLevelLink(primaryResource, Link.Self)) @@ -41,18 +41,18 @@ public TopLevelLinks GetTopLevelLinks(ContextEntity primaryResource) /// /// Checks if the top-level should be added by first checking - /// configuration on the , and if not configured, by checking with the + /// configuration on the , and if not configured, by checking with the /// global configuration in . /// /// - private bool ShouldAddTopLevelLink(ContextEntity primaryResource, Link link) + private bool ShouldAddTopLevelLink(ResourceContext primaryResource, Link link) { if (primaryResource.TopLevelLinks != Link.NotConfigured) return primaryResource.TopLevelLinks.HasFlag(link); return _options.TopLevelLinks.HasFlag(link); } - private void SetPageLinks(ContextEntity primaryResource, ref TopLevelLinks links) + private void SetPageLinks(ResourceContext primaryResource, ref TopLevelLinks links) { if (!_pageService.ShouldPaginate()) return; @@ -78,7 +78,7 @@ private string GetSelfTopLevelLink(string resourceName) return $"{GetBasePath()}/{resourceName}"; } - private string GetPageLink(ContextEntity primaryResource, int pageOffset, int pageSize) + private string GetPageLink(ResourceContext primaryResource, int pageOffset, int pageSize) { return $"{GetBasePath()}/{primaryResource.EntityName}?page[size]={pageSize}&page[number]={pageOffset}"; } @@ -87,7 +87,7 @@ private string GetPageLink(ContextEntity primaryResource, int pageOffset, int pa /// public ResourceLinks GetResourceLinks(string resourceName, string id) { - var resourceContext = _provider.GetContextEntity(resourceName); + var resourceContext = _provider.GetResourceContext(resourceName); if (ShouldAddResourceLink(resourceContext, Link.Self)) return new ResourceLinks { Self = GetSelfResourceLink(resourceName, id) }; @@ -97,7 +97,7 @@ public ResourceLinks GetResourceLinks(string resourceName, string id) /// public RelationshipLinks GetRelationshipLinks(RelationshipAttribute relationship, IIdentifiable parent) { - var parentResourceContext = _provider.GetContextEntity(parent.GetType()); + var parentResourceContext = _provider.GetResourceContext(parent.GetType()); var childNavigation = relationship.PublicRelationshipName; RelationshipLinks links = null; if (ShouldAddRelationshipLink(parentResourceContext, relationship, Link.Related)) @@ -130,11 +130,11 @@ private string GetRelatedRelationshipLink(string parent, string parentId, string /// /// Checks if the resource object level should be added by first checking - /// configuration on the , and if not configured, by checking with the + /// configuration on the , and if not configured, by checking with the /// global configuration in . /// /// - private bool ShouldAddResourceLink(ContextEntity resourceContext, Link link) + private bool ShouldAddResourceLink(ResourceContext resourceContext, Link link) { if (resourceContext.ResourceLinks != Link.NotConfigured) return resourceContext.ResourceLinks.HasFlag(link); @@ -144,11 +144,11 @@ private bool ShouldAddResourceLink(ContextEntity resourceContext, Link link) /// /// Checks if the resource object level should be added by first checking /// configuration on the attribute, if not configured by checking - /// the , and if not configured by checking with the + /// the , and if not configured by checking with the /// global configuration in . /// /// - private bool ShouldAddRelationshipLink(ContextEntity resourceContext, RelationshipAttribute relationship, Link link) + private bool ShouldAddRelationshipLink(ResourceContext resourceContext, RelationshipAttribute relationship, Link link) { if (relationship.RelationshipLinks != Link.NotConfigured) return relationship.RelationshipLinks.HasFlag(link); diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs index c8e134d001..fadc819581 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/ResponseResourceObjectBuilder.cs @@ -17,7 +17,7 @@ public class ResponseResourceObjectBuilder : ResourceObjectBuilder, IResourceObj public ResponseResourceObjectBuilder(ILinkBuilder linkBuilder, IIncludedResourceObjectBuilder includedBuilder, IIncludeService includeService, - IContextEntityProvider provider, + IResourceContextProvider provider, IResourceObjectBuilderSettingsProvider settingsProvider) : base(provider, settingsProvider.Get()) { diff --git a/src/JsonApiDotNetCore/Serialization/Server/Contracts/ILinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Contracts/ILinkBuilder.cs index 4a6ae113bf..a4bd87195a 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Contracts/ILinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Contracts/ILinkBuilder.cs @@ -13,7 +13,7 @@ public interface ILinkBuilder /// Builds the links object that is included in the top-level of the document. /// /// The primary resource of the response body - TopLevelLinks GetTopLevelLinks(ContextEntity primaryResource); + TopLevelLinks GetTopLevelLinks(ResourceContext primaryResource); /// /// Builds the links object for resources in the primary data. /// diff --git a/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs index 7283707b42..441f873bd1 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/RequestDeserializer.cs @@ -11,7 +11,7 @@ public class RequestDeserializer : BaseDocumentParser, IJsonApiDeserializer { private readonly ITargetedFields _targetedFields; - public RequestDeserializer(IContextEntityProvider provider, + public RequestDeserializer(IResourceContextProvider provider, ITargetedFields targetedFields) : base(provider) { _targetedFields = targetedFields; diff --git a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs index 62867eae57..dd138d3697 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs @@ -41,7 +41,7 @@ public ResponseSerializer(IMetaBuilder metaBuilder, IIncludedResourceObjectBuilder includedBuilder, IFieldsToSerialize fieldsToSerialize, IResourceObjectBuilder resourceObjectBuilder, - IContextEntityProvider provider) : + IResourceContextProvider provider) : base(resourceObjectBuilder, provider) { _fieldsToSerialize = fieldsToSerialize; @@ -158,7 +158,7 @@ private List GetRelationshipsToSerialize(Type resourceTyp ///
    private void AddTopLevelObjects(Document document) { - document.Links = _linkBuilder.GetTopLevelLinks(_provider.GetContextEntity()); + document.Links = _linkBuilder.GetTopLevelLinks(_provider.GetResourceContext()); document.Meta = _metaBuilder.GetMeta(); document.Included = _includedBuilder.Build(); } diff --git a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs index 7554e27fe8..4a447c30ae 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs @@ -24,7 +24,7 @@ public ResponseSerializerFactory(ICurrentRequest currentRequest, IScopedServiceP } /// - /// Initializes the server serializer using the + /// Initializes the server serializer using the /// associated with the current request. /// public IJsonApiSerializer GetSerializer() diff --git a/src/JsonApiDotNetCore/Services/DefaultResourceService.cs b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs index 2f27db62d7..4b1eb44050 100644 --- a/src/JsonApiDotNetCore/Services/DefaultResourceService.cs +++ b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs @@ -31,7 +31,7 @@ public class DefaultResourceService : private readonly IResourceHookExecutor _hookExecutor; private readonly IIncludeService _includeService; private readonly ISparseFieldsService _sparseFieldsService; - private readonly ContextEntity _currentRequestResource; + private readonly ResourceContext _currentRequestResource; public DefaultResourceService( ISortService sortService, @@ -41,7 +41,7 @@ public DefaultResourceService( ISparseFieldsService sparseFieldsService, IPageService pageManager, IResourceRepository repository, - IContextEntityProvider provider, + IResourceContextProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) { @@ -54,7 +54,7 @@ public DefaultResourceService( _repository = repository; _hookExecutor = hookExecutor; _logger = loggerFactory?.CreateLogger>(); - _currentRequestResource = provider.GetContextEntity(); + _currentRequestResource = provider.GetResourceContext(); } public virtual async Task CreateAsync(TResource entity) @@ -325,7 +325,7 @@ public class DefaultResourceService : DefaultResourceService repository, IJsonApiOptions options, IIncludeService includeService, ISparseFieldsService sparseFieldsService, - IPageService pageManager, IContextEntityProvider provider, + IPageService pageManager, IResourceContextProvider provider, IResourceHookExecutor hookExecutor = null, ILoggerFactory loggerFactory = null) : base(sortService, filterService, options, includeService, sparseFieldsService, pageManager, repository, provider, hookExecutor, loggerFactory) { diff --git a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs index 32ddcaf37c..77083e9e5d 100644 --- a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs +++ b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs @@ -20,7 +20,7 @@ public ResourceDefinitionProvider(IResourceGraph resourceContextProvider, IScope /// public IResourceDefinition Get(Type resourceType) { - return (IResourceDefinition)_serviceProvider.GetService(_resourceContextProvider.GetContextEntity(resourceType).ResourceType); + return (IResourceDefinition)_serviceProvider.GetService(_resourceContextProvider.GetResourceContext(resourceType).ResourceType); } } } diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index 8aec2dc980..2bd0e9bdbd 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -41,9 +41,9 @@ public void AddAssembly_Adds_All_Resources_To_Graph() // assert var resourceGraph = _resourceGraphBuilder.Build(); - var personResource = resourceGraph.GetContextEntity(typeof(Person)); - var articleResource = resourceGraph.GetContextEntity(typeof(Article)); - var modelResource = resourceGraph.GetContextEntity(typeof(Model)); + var personResource = resourceGraph.GetResourceContext(typeof(Person)); + var articleResource = resourceGraph.GetResourceContext(typeof(Article)); + var modelResource = resourceGraph.GetResourceContext(typeof(Model)); Assert.NotNull(personResource); Assert.NotNull(articleResource); @@ -58,7 +58,7 @@ public void AddCurrentAssembly_Adds_Resources_To_Graph() // assert var resourceGraph = _resourceGraphBuilder.Build(); - var testModelResource = resourceGraph.GetContextEntity(typeof(TestModel)); + var testModelResource = resourceGraph.GetResourceContext(typeof(TestModel)); Assert.NotNull(testModelResource); } diff --git a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs index a2904bc8b7..b5c335a32b 100644 --- a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs @@ -35,8 +35,8 @@ public void Can_Build_ResourceGraph_Using_Builder() // assert var resourceGraph = container.GetRequiredService(); - var dbResource = resourceGraph.GetContextEntity("db-resources"); - var nonDbResource = resourceGraph.GetContextEntity("non-db-resources"); + var dbResource = resourceGraph.GetResourceContext("db-resources"); + var nonDbResource = resourceGraph.GetResourceContext("non-db-resources"); Assert.Equal(typeof(DbResource), dbResource.EntityType); Assert.Equal(typeof(NonDbResource), nonDbResource.EntityType); Assert.Equal(typeof(ResourceDefinition), nonDbResource.ResourceType); @@ -53,7 +53,7 @@ public void Resources_Without_Names_Specified_Will_Use_Default_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); Assert.Equal("test-resources", resource.EntityName); } @@ -68,7 +68,7 @@ public void Resources_Without_Names_Specified_Will_Use_Configured_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); Assert.Equal("testResources", resource.EntityName); } @@ -83,7 +83,7 @@ public void Attrs_Without_Names_Specified_Will_Use_Default_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); Assert.Contains(resource.Attributes, (i) => i.PublicAttributeName == "compound-attribute"); } @@ -98,7 +98,7 @@ public void Attrs_Without_Names_Specified_Will_Use_Configured_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); Assert.Contains(resource.Attributes, (i) => i.PublicAttributeName == "compoundAttribute"); } @@ -113,7 +113,7 @@ public void Relationships_Without_Names_Specified_Will_Use_Default_Formatter() var resourceGraph = builder.Build(); // assert - var resource = resourceGraph.GetContextEntity(typeof(TestResource)); + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); Assert.Equal("related-resource", resource.Relationships.Single(r => r.IsHasOne).PublicRelationshipName); Assert.Equal("related-resources", resource.Relationships.Single(r => r.IsHasMany).PublicRelationshipName); } diff --git a/test/UnitTests/Builders/LinkBuilderTests.cs b/test/UnitTests/Builders/LinkBuilderTests.cs index 661ea73ba7..0cfb498e53 100644 --- a/test/UnitTests/Builders/LinkBuilderTests.cs +++ b/test/UnitTests/Builders/LinkBuilderTests.cs @@ -42,8 +42,8 @@ public void BuildResourceLinks_GlobalAndResourceConfiguration_ExpectedResult(Lin { // arrange var config = GetConfiguration(resourceLinks: global); - var primaryResource = GetContextEntity
    (resourceLinks: resource); - _provider.Setup(m => m.GetContextEntity("articles")).Returns(primaryResource); + var primaryResource = GetResourceContext
    (resourceLinks: resource); + _provider.Setup(m => m.GetResourceContext("articles")).Returns(primaryResource); var builder = new LinkBuilder(config, GetRequestManager(), null, _provider.Object); // act @@ -90,8 +90,8 @@ public void BuildRelationshipLinks_GlobalResourceAndAttrConfiguration_ExpectedLi { // arrange var config = GetConfiguration(relationshipLinks: global); - var primaryResource = GetContextEntity
    (relationshipLinks: resource); - _provider.Setup(m => m.GetContextEntity(typeof(Article))).Returns(primaryResource); + var primaryResource = GetResourceContext
    (relationshipLinks: resource); + _provider.Setup(m => m.GetResourceContext(typeof(Article))).Returns(primaryResource); var builder = new LinkBuilder(config, GetRequestManager(), null, _provider.Object); var attr = new HasOneAttribute(links: relationship) { DependentType = typeof(Author), PublicRelationshipName = "author" }; @@ -138,8 +138,8 @@ public void BuildTopLevelLinks_GlobalAndResourceConfiguration_ExpectedLinks(Link { // arrange var config = GetConfiguration(topLevelLinks: global); - var primaryResource = GetContextEntity
    (topLevelLinks: resource); - _provider.Setup(m => m.GetContextEntity
    ()).Returns(primaryResource); + var primaryResource = GetResourceContext
    (topLevelLinks: resource); + _provider.Setup(m => m.GetResourceContext
    ()).Returns(primaryResource); var builder = new LinkBuilder(config, GetRequestManager(), _pageService, _provider.Object); @@ -170,7 +170,7 @@ private bool CheckPages(TopLevelLinks links, bool pages) return links.First == null && links.Prev == null && links.Next == null && links.Last == null; } - private ICurrentRequest GetRequestManager(ContextEntity resourceContext = null) + private ICurrentRequest GetRequestManager(ResourceContext resourceContext = null) { var mock = new Mock(); mock.Setup(m => m.BasePath).Returns(_host); @@ -202,11 +202,11 @@ private IPageService GetPageManager() - private ContextEntity GetContextEntity(Link resourceLinks = Link.NotConfigured, + private ResourceContext GetResourceContext(Link resourceLinks = Link.NotConfigured, Link topLevelLinks = Link.NotConfigured, Link relationshipLinks = Link.NotConfigured) where TResource : class, IIdentifiable { - return new ContextEntity + return new ResourceContext { ResourceLinks = resourceLinks, TopLevelLinks = topLevelLinks, diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index 456046f141..e83feaa3d6 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -44,7 +44,7 @@ public void AddJsonApiInternals_Adds_All_Required_Services() Assert.NotNull(currentRequest); var resourceGraph = provider.GetService(); Assert.NotNull(resourceGraph); - currentRequest.SetRequestResource(resourceGraph.GetContextEntity()); + currentRequest.SetRequestResource(resourceGraph.GetResourceContext()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService(typeof(IResourceRepository))); @@ -129,7 +129,7 @@ public void AddJsonApi_With_Context_Uses_DbSet_PropertyName_If_NoOtherSpecified( // assert var provider = services.BuildServiceProvider(); var resourceGraph = provider.GetService(); - var resource = resourceGraph.GetContextEntity(typeof(IntResource)); + var resource = resourceGraph.GetResourceContext(typeof(IntResource)); Assert.Equal("resource", resource.EntityName); } diff --git a/test/UnitTests/QueryParameters/IncludeServiceTests.cs b/test/UnitTests/QueryParameters/IncludeServiceTests.cs index 439dc98288..6771d53c7d 100644 --- a/test/UnitTests/QueryParameters/IncludeServiceTests.cs +++ b/test/UnitTests/QueryParameters/IncludeServiceTests.cs @@ -12,7 +12,7 @@ namespace UnitTests.QueryParameters public class IncludeServiceTests : QueryParametersUnitTestCollection { - public IncludeService GetService(ContextEntity resourceContext = null) + public IncludeService GetService(ResourceContext resourceContext = null) { return new IncludeService(_resourceGraph, MockCurrentRequest(resourceContext ?? _articleResourceContext)); } @@ -58,7 +58,7 @@ public void Parse_ChainsOnWrongMainResource_ThrowsJsonApiException() // arrange const string chain = "author.blogs.reviewer.favorite-food,reviewer.blogs.author.favorite-song"; var query = new KeyValuePair("include", new StringValues(chain)); - var service = GetService(_resourceGraph.GetContextEntity()); + var service = GetService(_resourceGraph.GetResourceContext()); // act, assert var exception = Assert.Throws( () => service.Parse(query)); diff --git a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs index 43bcf2e28f..24136f87d8 100644 --- a/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs +++ b/test/UnitTests/QueryParameters/QueryParametersUnitTestCollection.cs @@ -12,7 +12,7 @@ namespace UnitTests.QueryParameters { public class QueryParametersUnitTestCollection { - protected readonly ContextEntity _articleResourceContext; + protected readonly ResourceContext _articleResourceContext; protected readonly IResourceGraph _resourceGraph; public QueryParametersUnitTestCollection() @@ -24,10 +24,10 @@ public QueryParametersUnitTestCollection() builder.AddResource(); builder.AddResource(); _resourceGraph = builder.Build(); - _articleResourceContext = _resourceGraph.GetContextEntity
    (); + _articleResourceContext = _resourceGraph.GetResourceContext
    (); } - public ICurrentRequest MockCurrentRequest(ContextEntity requestResource = null) + public ICurrentRequest MockCurrentRequest(ResourceContext requestResource = null) { var mock = new Mock(); diff --git a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs index 89e1b76c9c..ef77f99aec 100644 --- a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs +++ b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs @@ -10,7 +10,7 @@ namespace UnitTests.QueryParameters { public class SparseFieldsServiceTests : QueryParametersUnitTestCollection { - public SparseFieldsService GetService(ContextEntity contextEntity = null) + public SparseFieldsService GetService(ResourceContext contextEntity = null) { return new SparseFieldsService(_resourceGraph, MockCurrentRequest(contextEntity ?? _articleResourceContext)); } @@ -40,7 +40,7 @@ public void Parse_ValidSelection_CanParse() var query = new KeyValuePair($"fields", new StringValues(attrName)); - var contextEntity = new ContextEntity + var contextEntity = new ResourceContext { EntityName = type, Attributes = new List { attribute, idAttribute }, @@ -70,7 +70,7 @@ public void Parse_TypeNameAsNavigation_ThrowsJsonApiException() var query = new KeyValuePair($"fields[{type}]", new StringValues(attrName)); - var contextEntity = new ContextEntity + var contextEntity = new ResourceContext { EntityName = type, Attributes = new List { attribute, idAttribute }, @@ -96,7 +96,7 @@ public void Parse_DeeplyNestedSelection_ThrowsJsonApiException() var query = new KeyValuePair($"fields[{relationship}]", new StringValues(attrName)); - var contextEntity = new ContextEntity + var contextEntity = new ResourceContext { EntityName = type, Attributes = new List { attribute, idAttribute }, @@ -118,7 +118,7 @@ public void Parse_InvalidField_ThrowsJsonApiException() var query = new KeyValuePair($"fields[{type}]", new StringValues(attrName)); - var contextEntity = new ContextEntity + var contextEntity = new ResourceContext { EntityName = type, Attributes = new List(), diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 0da9b338c2..874b7eba2f 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -383,13 +383,13 @@ protected List> GetIncludedRelationshipsChains(param protected List GetIncludedRelationshipsChain(string chain) { var parsedChain = new List(); - var resourceContext = _resourceGraph.GetContextEntity(); + var resourceContext = _resourceGraph.GetResourceContext(); var splittedPath = chain.Split(QueryConstants.DOT); foreach (var requestedRelationship in splittedPath) { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.DependentType); } return parsedChain; } diff --git a/test/UnitTests/Serialization/Common/DocumentParserTests.cs b/test/UnitTests/Serialization/Common/DocumentParserTests.cs index c16daf36c7..fa6e10ce01 100644 --- a/test/UnitTests/Serialization/Common/DocumentParserTests.cs +++ b/test/UnitTests/Serialization/Common/DocumentParserTests.cs @@ -132,7 +132,7 @@ public void DeserializeAttributes_VariousDataTypes_CanDeserialize(string member, var entity = (TestResource)_deserializer.Deserialize(body); // assert - var pi = _resourceGraph.GetContextEntity("test-resource").Attributes.Single(attr => attr.PublicAttributeName == member).PropertyInfo; + var pi = _resourceGraph.GetResourceContext("test-resource").Attributes.Single(attr => attr.PublicAttributeName == member).PropertyInfo; var deserializedValue = pi.GetValue(entity); if (member == "int-field") diff --git a/test/UnitTests/Serialization/SerializerTestsSetup.cs b/test/UnitTests/Serialization/SerializerTestsSetup.cs index d359d46a2c..133482a5d1 100644 --- a/test/UnitTests/Serialization/SerializerTestsSetup.cs +++ b/test/UnitTests/Serialization/SerializerTestsSetup.cs @@ -49,7 +49,7 @@ protected ResponseSerializer GetResponseSerializer(List(meta, link, includedBuilder, fieldsToSerialize, resourceObjectBuilder, provider); } @@ -74,7 +74,7 @@ protected IResourceObjectBuilderSettingsProvider GetSerializerSettingsProvider() return mock.Object; } - private IResourceGraph GetContextEntityProvider() + private IResourceGraph GetResourceContextProvider() { return _resourceGraph; } @@ -89,14 +89,14 @@ protected IMetaBuilder GetMetaBuilder(Dictionary meta = nu protected ICurrentRequest GetRequestManager() where T : class, IIdentifiable { var mock = new Mock(); - mock.Setup(m => m.GetRequestResource()).Returns(_resourceGraph.GetContextEntity()); + mock.Setup(m => m.GetRequestResource()).Returns(_resourceGraph.GetResourceContext()); return mock.Object; } protected ILinkBuilder GetLinkBuilder(TopLevelLinks top = null, ResourceLinks resource = null, RelationshipLinks relationship = null) { var mock = new Mock(); - mock.Setup(m => m.GetTopLevelLinks(It.IsAny())).Returns(top); + mock.Setup(m => m.GetTopLevelLinks(It.IsAny())).Returns(top); mock.Setup(m => m.GetResourceLinks(It.IsAny(), It.IsAny())).Returns(resource); mock.Setup(m => m.GetRelationshipLinks(It.IsAny(), It.IsAny())).Returns(relationship); return mock.Object; @@ -111,8 +111,8 @@ protected ISparseFieldsService GetFieldsQuery() protected IFieldsToSerialize GetSerializableFields() { var mock = new Mock(); - mock.Setup(m => m.GetAllowedAttributes(It.IsAny(), It.IsAny())).Returns((t, r) => _resourceGraph.GetContextEntity(t).Attributes); - mock.Setup(m => m.GetAllowedRelationships(It.IsAny())).Returns(t => _resourceGraph.GetContextEntity(t).Relationships); + mock.Setup(m => m.GetAllowedAttributes(It.IsAny(), It.IsAny())).Returns((t, r) => _resourceGraph.GetResourceContext(t).Attributes); + mock.Setup(m => m.GetAllowedRelationships(It.IsAny())).Returns(t => _resourceGraph.GetResourceContext(t).Relationships); return mock.Object; } @@ -131,7 +131,7 @@ protected IIncludeService GetIncludedRelationships(List protected class TestDocumentBuilder : BaseDocumentBuilder { - public TestDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IContextEntityProvider provider) : base(resourceObjectBuilder, provider) { } + public TestDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IResourceContextProvider provider) : base(resourceObjectBuilder, provider) { } public new Document Build(IIdentifiable entity, List attributes = null, List relationships = null) { diff --git a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs index 13e079c927..200020f8d8 100644 --- a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs @@ -157,13 +157,13 @@ public void BuildIncluded_DuplicateChildrenMultipleChains_OnceInOutput() private List GetIncludedRelationshipsChain(string chain) { var parsedChain = new List(); - var resourceContext = _resourceGraph.GetContextEntity
    (); + var resourceContext = _resourceGraph.GetResourceContext
    (); var splittedPath = chain.Split(QueryConstants.DOT); foreach (var requestedRelationship in splittedPath) { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetContextEntity(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.DependentType); } return parsedChain; } From f2e411c905bd676ace9e792c65e4ab6272f5a90c Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 12:28:11 +0200 Subject: [PATCH 20/29] style: rename Entity to Resource --- .../Builders/ResourceGraphBuilder.cs | 10 +++++----- .../Hooks/Execution/HookExecutorHelper.cs | 11 +---------- ...tyProvider.cs => IResourceContextProvider.cs} | 5 +---- .../Internal/InverseRelationships.cs | 4 ++-- .../{ContextEntity.cs => ResourceContext.cs} | 6 +++--- src/JsonApiDotNetCore/Internal/ResourceGraph.cs | 16 +++++----------- .../Middleware/DefaultTypeMatchFilter.cs | 2 +- .../Middleware/RequestMiddleware.cs | 2 +- .../Common/QueryParameterService.cs | 2 +- .../QueryParameterServices/FilterService.cs | 2 +- .../QueryParameterServices/IncludeService.cs | 6 +++--- .../QueryParameterServices/SortService.cs | 2 +- .../SparseFieldsService.cs | 6 +++--- .../Serialization/Common/BaseDocumentParser.cs | 2 +- .../Common/ResourceObjectBuilder.cs | 4 ++-- .../Builders/IncludedResourceObjectBuilder.cs | 2 +- .../Serialization/Server/Builders/LinkBuilder.cs | 8 ++++---- .../Server/ResponseSerializerFactory.cs | 2 +- .../Services/ResourceDefinitionProvider.cs | 2 +- .../Builders/ContextGraphBuilder_Tests.cs | 10 +++++----- test/UnitTests/Builders/LinkBuilderTests.cs | 2 +- .../IServiceCollectionExtensionsTests.cs | 2 +- .../Internal/ContextGraphBuilder_Tests.cs | 2 +- .../QueryParameters/SparseFieldsServiceTests.cs | 8 ++++---- 24 files changed, 50 insertions(+), 68 deletions(-) rename src/JsonApiDotNetCore/Internal/Contracts/{IContextEntityProvider.cs => IResourceContextProvider.cs} (86%) rename src/JsonApiDotNetCore/Internal/{ContextEntity.cs => ResourceContext.cs} (96%) diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index bfc0dc25e6..0a99207ec8 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -38,7 +38,7 @@ public IResourceGraph Build() private void SetResourceLinksOptions(ResourceContext resourceContext) { - var attribute = (LinksAttribute)resourceContext.EntityType.GetCustomAttribute(typeof(LinksAttribute)); + var attribute = (LinksAttribute)resourceContext.ResourceType.GetCustomAttribute(typeof(LinksAttribute)); if (attribute != null) { resourceContext.RelationshipLinks = attribute.RelationshipLinks; @@ -69,12 +69,12 @@ public IResourceGraphBuilder AddResource(Type entityType, Type idType, string pl private ResourceContext GetEntity(string pluralizedTypeName, Type entityType, Type idType) => new ResourceContext { - EntityName = pluralizedTypeName, - EntityType = entityType, + ResourceName = pluralizedTypeName, + ResourceType = entityType, IdentityType = idType, Attributes = GetAttributes(entityType), Relationships = GetRelationships(entityType), - ResourceType = GetResourceDefinitionType(entityType) + ResourceDefinitionType = GetResourceDefinitionType(entityType) }; @@ -234,7 +234,7 @@ private string GetResourceNameFromDbSetProperty(PropertyInfo property, Type reso private void AssertEntityIsNotAlreadyDefined(Type entityType) { - if (_entities.Any(e => e.EntityType == entityType)) + if (_entities.Any(e => e.ResourceType == entityType)) throw new InvalidOperationException($"Cannot add entity type {entityType} to context resourceGraph, there is already an entity of that type configured."); } } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index c41dcd5d89..0dc09fd948 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -97,23 +97,14 @@ public HashSet LoadDbValues(IEnumerable entities, Res return new HashSet(dbValues); } - public bool ShouldLoadDbValues(Type entityType, ResourceHook hook) { var discovery = GetHookDiscovery(entityType); - if (discovery.DatabaseValuesDisabledHooks.Contains(hook)) - { return false; - } if (discovery.DatabaseValuesEnabledHooks.Contains(hook)) - { return true; - } - else - { - return _options.LoaDatabaseValues; - } + return _options.LoaDatabaseValues; } bool ShouldExecuteHook(DependentType entityType, ResourceHook hook) diff --git a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs b/src/JsonApiDotNetCore/Internal/Contracts/IResourceContextProvider.cs similarity index 86% rename from src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs rename to src/JsonApiDotNetCore/Internal/Contracts/IResourceContextProvider.cs index f967679cbe..c975211b6d 100644 --- a/src/JsonApiDotNetCore/Internal/Contracts/IContextEntityProvider.cs +++ b/src/JsonApiDotNetCore/Internal/Contracts/IResourceContextProvider.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Internal.Contracts @@ -14,7 +11,7 @@ public interface IResourceContextProvider /// /// Gets all registered context entities /// - ResourceContext[] GetContextEntities(); + ResourceContext[] GetResourceContexts(); /// /// Get the resource metadata by the DbSet property name diff --git a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs index 887069ed1d..3aa999f9bb 100644 --- a/src/JsonApiDotNetCore/Internal/InverseRelationships.cs +++ b/src/JsonApiDotNetCore/Internal/InverseRelationships.cs @@ -45,9 +45,9 @@ public void Resolve() { DbContext context = _resolver.GetContext(); - foreach (ResourceContext ce in _provider.GetContextEntities()) + foreach (ResourceContext ce in _provider.GetResourceContexts()) { - IEntityType meta = context.Model.FindEntityType(ce.EntityType); + IEntityType meta = context.Model.FindEntityType(ce.ResourceType); if (meta == null) continue; foreach (var attr in ce.Relationships) { diff --git a/src/JsonApiDotNetCore/Internal/ContextEntity.cs b/src/JsonApiDotNetCore/Internal/ResourceContext.cs similarity index 96% rename from src/JsonApiDotNetCore/Internal/ContextEntity.cs rename to src/JsonApiDotNetCore/Internal/ResourceContext.cs index 84b66979d1..91934ca070 100644 --- a/src/JsonApiDotNetCore/Internal/ContextEntity.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceContext.cs @@ -11,12 +11,12 @@ public class ResourceContext /// /// The exposed resource name /// - public string EntityName { get; set; } + public string ResourceName { get; set; } /// /// The data model type /// - public Type EntityType { get; set; } + public Type ResourceType { get; set; } /// /// The identity member type @@ -27,7 +27,7 @@ public class ResourceContext /// The concrete type. /// We store this so that we don't need to re-compute the generic type. /// - public Type ResourceType { get; set; } + public Type ResourceDefinitionType { get; set; } /// /// Exposed resource attributes. diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index ecf23b89f9..885d65bd09 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -13,28 +13,25 @@ namespace JsonApiDotNetCore.Internal public class ResourceGraph : IResourceGraph { internal List ValidationResults { get; } - private List _entities { get; } + private List _resources { get; } public ResourceGraph(List entities, List validationResults = null) { - _entities = entities; + _resources = entities; ValidationResults = validationResults; } /// - public ResourceContext[] GetContextEntities() => _entities.ToArray(); - + public ResourceContext[] GetResourceContexts() => _resources.ToArray(); /// public ResourceContext GetResourceContext(string entityName) - => _entities.SingleOrDefault(e => string.Equals(e.EntityName, entityName, StringComparison.OrdinalIgnoreCase)); - + => _resources.SingleOrDefault(e => string.Equals(e.ResourceName, entityName, StringComparison.OrdinalIgnoreCase)); /// public ResourceContext GetResourceContext(Type entityType) - => _entities.SingleOrDefault(e => e.EntityType == entityType); + => _resources.SingleOrDefault(e => e.ResourceType == entityType); /// public ResourceContext GetResourceContext() where TResource : class, IIdentifiable => GetResourceContext(typeof(TResource)); - /// public List GetFields(Expression> selector = null) where T : IIdentifiable { @@ -65,7 +62,6 @@ public List GetRelationships(Type type) { return GetResourceContext(type).Relationships.ToList(); } - /// public RelationshipAttribute GetInverse(RelationshipAttribute relationship) { @@ -144,7 +140,5 @@ private enum FieldFilterType Attribute, Relationship } - - } } diff --git a/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs b/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs index a880d35213..8ba43dbc26 100644 --- a/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/DefaultTypeMatchFilter.cs @@ -33,7 +33,7 @@ public void OnActionExecuting(ActionExecutingContext context) throw new JsonApiException(409, $"Cannot '{context.HttpContext.Request.Method}' type '{deserializedType.Name}' " - + $"to '{expectedJsonApiResource?.EntityName}' endpoint.", + + $"to '{expectedJsonApiResource?.ResourceName}' endpoint.", detail: "Check that the request payload type matches the type expected by this endpoint."); } } diff --git a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs index 45ac99cfb7..0d36826b7c 100644 --- a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs @@ -46,7 +46,7 @@ public async Task Invoke(HttpContext httpContext, { _currentRequest.SetRequestResource(GetCurrentEntity()); _currentRequest.IsRelationshipPath = PathIsRelationship(); - _currentRequest.BasePath = GetBasePath(_currentRequest.GetRequestResource().EntityName); + _currentRequest.BasePath = GetBasePath(_currentRequest.GetRequestResource().ResourceName); } if (IsValid()) diff --git a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs index e9930a3a9e..d36accf355 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs @@ -66,7 +66,7 @@ protected RelationshipAttribute GetRelationship(string propertyName) if (propertyName == null) return null; var relationship = _requestResource.Relationships.FirstOrDefault(r => r.Is(propertyName)); if (relationship == null) - throw new JsonApiException(400, $"{propertyName} is not a valid relationship on {_requestResource.EntityName}."); + throw new JsonApiException(400, $"{propertyName} is not a valid relationship on {_requestResource.ResourceName}."); return relationship; } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs index 2984139fe7..feed8c49fe 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/FilterService.cs @@ -18,7 +18,7 @@ public class FilterService : QueryParameterService, IFilterService public FilterService(IResourceDefinitionProvider resourceDefinitionProvider, IResourceGraph resourceGraph, ICurrentRequest currentRequest) : base(resourceGraph, currentRequest) { - _requestResourceDefinition = resourceDefinitionProvider.Get(_requestResource.EntityType); + _requestResourceDefinition = resourceDefinitionProvider.Get(_requestResource.ResourceType); _filters = new List(); } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs index cc04552cfa..5fb586cc56 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs @@ -59,13 +59,13 @@ private void ParseChain(string chain) private JsonApiException CannotIncludeError(ResourceContext resourceContext, string requestedRelationship) { - return new JsonApiException(400, $"Including the relationship {requestedRelationship} on {resourceContext.EntityName} is not allowed"); + return new JsonApiException(400, $"Including the relationship {requestedRelationship} on {resourceContext.ResourceName} is not allowed"); } private JsonApiException InvalidRelationshipError(ResourceContext resourceContext, string requestedRelationship) { - return new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {resourceContext.EntityName}", - $"{resourceContext.EntityName} does not have a relationship named {requestedRelationship}"); + return new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {resourceContext.ResourceName}", + $"{resourceContext.ResourceName} does not have a relationship named {requestedRelationship}"); } } } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs index 45a8fca0bb..cd5a283e07 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs @@ -39,7 +39,7 @@ public List Get() { if (_queries == null) { - var requestResourceDefinition = _resourceDefinitionProvider.Get(_requestResource.EntityType); + var requestResourceDefinition = _resourceDefinitionProvider.Get(_requestResource.ResourceType); if (requestResourceDefinition != null) return requestResourceDefinition.DefaultSort()?.Select(d => BuildQueryContext(new SortQuery(d.Item1.PublicAttributeName, d.Item2))).ToList(); } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index db50c46ad0..05a676afd4 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -61,7 +61,7 @@ public virtual void Parse(KeyValuePair queryParameter) // it is possible that the request resource has a relationship // that is equal to the resource name, like with self-referering data types (eg directory structures) // if not, no longer support this type of sparse field selection. - if (navigation == _requestResource.EntityName && !_requestResource.Relationships.Any(a => a.Is(navigation))) + if (navigation == _requestResource.ResourceName && !_requestResource.Relationships.Any(a => a.Is(navigation))) throw new JsonApiException(400, $"Use \"?fields=...\" instead of \"fields[{navigation}]\":" + $" the square bracket navigations is now reserved " + $"for relationships only. See https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/555#issuecomment-543100865"); @@ -71,7 +71,7 @@ public virtual void Parse(KeyValuePair queryParameter) var relationship = _requestResource.Relationships.SingleOrDefault(a => a.Is(navigation)); if (relationship == null) - throw new JsonApiException(400, $"\"{navigation}\" in \"fields[{navigation}]\" is not a valid relationship of {_requestResource.EntityName}"); + throw new JsonApiException(400, $"\"{navigation}\" in \"fields[{navigation}]\" is not a valid relationship of {_requestResource.ResourceName}"); foreach (var field in fields) RegisterRelatedResourceField(field, relationship); @@ -100,7 +100,7 @@ private void RegisterRequestResourceField(string field) { var attr = _requestResource.Attributes.SingleOrDefault(a => a.Is(field)); if (attr == null) - throw new JsonApiException(400, $"'{_requestResource.EntityName}' does not contain '{field}'."); + throw new JsonApiException(400, $"'{_requestResource.ResourceName}' does not contain '{field}'."); (_selectedFields = _selectedFields ?? new List()).Add(attr); } diff --git a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs index 651a88fbed..fa0d70699f 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs @@ -138,7 +138,7 @@ private IIdentifiable ParseResourceObject(ResourceObject data) + "If you have manually registered the resource, check that the call to AddResource correctly sets the public name."); } - var entity = (IIdentifiable)Activator.CreateInstance(contextEntity.EntityType); + var entity = (IIdentifiable)Activator.CreateInstance(contextEntity.ResourceType); entity = SetAttributes(entity, data.Attributes, contextEntity.Attributes); entity = SetRelationships(entity, data.Relationships, contextEntity.Relationships); diff --git a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs index d9582a84d6..6e0dde1fb0 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs @@ -28,7 +28,7 @@ public ResourceObject Build(IIdentifiable entity, IEnumerable att var resourceContext = _provider.GetResourceContext(entity.GetType()); // populating the top-level "type" and "id" members. - var ro = new ResourceObject { Type = resourceContext.EntityName, Id = entity.StringId.NullIfEmpty() }; + var ro = new ResourceObject { Type = resourceContext.ResourceName, Id = entity.StringId.NullIfEmpty() }; // populating the top-level "attribute" member of a resource object. never include "id" as an attribute if (attributes != null && (attributes = attributes.Where(attr => attr.InternalAttributeName != _identifiablePropertyName)).Any()) @@ -98,7 +98,7 @@ private List GetRelatedResourceLinkage(HasManyAttribut /// private ResourceIdentifierObject GetResourceIdentifier(IIdentifiable entity) { - var resourceName = _provider.GetResourceContext(entity.GetType()).EntityName; + var resourceName = _provider.GetResourceContext(entity.GetType()).ResourceName; return new ResourceIdentifierObject { Type = resourceName, diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs index 6ca1091216..4ce2aa5f94 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/IncludedResourceObjectBuilder.cs @@ -121,7 +121,7 @@ protected override RelationshipEntry GetRelationshipData(RelationshipAttribute r private ResourceObject GetOrBuildResourceObject(IIdentifiable parent, RelationshipAttribute relationship) { var type = parent.GetType(); - var resourceName = _provider.GetResourceContext(type).EntityName; + var resourceName = _provider.GetResourceContext(type).ResourceName; var entry = _included.SingleOrDefault(ro => ro.Type == resourceName && ro.Id == parent.StringId); if (entry == null) { diff --git a/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs index 959799c790..9b00cdc23a 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/Builders/LinkBuilder.cs @@ -31,7 +31,7 @@ public TopLevelLinks GetTopLevelLinks(ResourceContext primaryResource) { TopLevelLinks topLevelLinks = null; if (ShouldAddTopLevelLink(primaryResource, Link.Self)) - topLevelLinks = new TopLevelLinks { Self = GetSelfTopLevelLink(primaryResource.EntityName) }; + topLevelLinks = new TopLevelLinks { Self = GetSelfTopLevelLink(primaryResource.ResourceName) }; if (ShouldAddTopLevelLink(primaryResource, Link.Paging)) SetPageLinks(primaryResource, ref topLevelLinks); @@ -80,7 +80,7 @@ private string GetSelfTopLevelLink(string resourceName) private string GetPageLink(ResourceContext primaryResource, int pageOffset, int pageSize) { - return $"{GetBasePath()}/{primaryResource.EntityName}?page[size]={pageSize}&page[number]={pageOffset}"; + return $"{GetBasePath()}/{primaryResource.ResourceName}?page[size]={pageSize}&page[number]={pageOffset}"; } @@ -101,12 +101,12 @@ public RelationshipLinks GetRelationshipLinks(RelationshipAttribute relationship var childNavigation = relationship.PublicRelationshipName; RelationshipLinks links = null; if (ShouldAddRelationshipLink(parentResourceContext, relationship, Link.Related)) - links = new RelationshipLinks { Related = GetRelatedRelationshipLink(parentResourceContext.EntityName, parent.StringId, childNavigation) }; + links = new RelationshipLinks { Related = GetRelatedRelationshipLink(parentResourceContext.ResourceName, parent.StringId, childNavigation) }; if (ShouldAddRelationshipLink(parentResourceContext, relationship, Link.Self)) { links = links ?? new RelationshipLinks(); - links.Self = GetSelfRelationshipLink(parentResourceContext.EntityName, parent.StringId, childNavigation); + links.Self = GetSelfRelationshipLink(parentResourceContext.ResourceName, parent.StringId, childNavigation); } return links; diff --git a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs index 4a447c30ae..951d915eee 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs @@ -46,7 +46,7 @@ private Type GetDocumentPrimaryType() if (_currentRequest.RequestRelationship != null && !_currentRequest.IsRelationshipPath) return _currentRequest.RequestRelationship.DependentType; - return _currentRequest.GetRequestResource()?.EntityType; + return _currentRequest.GetRequestResource()?.ResourceType; } } } diff --git a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs index 77083e9e5d..5a9e4b655c 100644 --- a/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs +++ b/src/JsonApiDotNetCore/Services/ResourceDefinitionProvider.cs @@ -20,7 +20,7 @@ public ResourceDefinitionProvider(IResourceGraph resourceContextProvider, IScope /// public IResourceDefinition Get(Type resourceType) { - return (IResourceDefinition)_serviceProvider.GetService(_resourceContextProvider.GetResourceContext(resourceType).ResourceType); + return (IResourceDefinition)_serviceProvider.GetService(_resourceContextProvider.GetResourceContext(resourceType).ResourceDefinitionType); } } } diff --git a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs index b5c335a32b..f066b20fd8 100644 --- a/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Builders/ContextGraphBuilder_Tests.cs @@ -37,9 +37,9 @@ public void Can_Build_ResourceGraph_Using_Builder() var resourceGraph = container.GetRequiredService(); var dbResource = resourceGraph.GetResourceContext("db-resources"); var nonDbResource = resourceGraph.GetResourceContext("non-db-resources"); - Assert.Equal(typeof(DbResource), dbResource.EntityType); - Assert.Equal(typeof(NonDbResource), nonDbResource.EntityType); - Assert.Equal(typeof(ResourceDefinition), nonDbResource.ResourceType); + Assert.Equal(typeof(DbResource), dbResource.ResourceType); + Assert.Equal(typeof(NonDbResource), nonDbResource.ResourceType); + Assert.Equal(typeof(ResourceDefinition), nonDbResource.ResourceDefinitionType); } [Fact] @@ -54,7 +54,7 @@ public void Resources_Without_Names_Specified_Will_Use_Default_Formatter() // assert var resource = resourceGraph.GetResourceContext(typeof(TestResource)); - Assert.Equal("test-resources", resource.EntityName); + Assert.Equal("test-resources", resource.ResourceName); } [Fact] @@ -69,7 +69,7 @@ public void Resources_Without_Names_Specified_Will_Use_Configured_Formatter() // assert var resource = resourceGraph.GetResourceContext(typeof(TestResource)); - Assert.Equal("testResources", resource.EntityName); + Assert.Equal("testResources", resource.ResourceName); } [Fact] diff --git a/test/UnitTests/Builders/LinkBuilderTests.cs b/test/UnitTests/Builders/LinkBuilderTests.cs index 0cfb498e53..e7e2371558 100644 --- a/test/UnitTests/Builders/LinkBuilderTests.cs +++ b/test/UnitTests/Builders/LinkBuilderTests.cs @@ -211,7 +211,7 @@ private ResourceContext GetResourceContext(Link resourceLinks = Link. ResourceLinks = resourceLinks, TopLevelLinks = topLevelLinks, RelationshipLinks = relationshipLinks, - EntityName = typeof(TResource).Name.Dasherize() + "s" + ResourceName = typeof(TResource).Name.Dasherize() + "s" }; } } diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index e83feaa3d6..0ccb6649c3 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -130,7 +130,7 @@ public void AddJsonApi_With_Context_Uses_DbSet_PropertyName_If_NoOtherSpecified( var provider = services.BuildServiceProvider(); var resourceGraph = provider.GetService(); var resource = resourceGraph.GetResourceContext(typeof(IntResource)); - Assert.Equal("resource", resource.EntityName); + Assert.Equal("resource", resource.ResourceName); } public class IntResource : Identifiable { } diff --git a/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs b/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs index d2f35dd454..bb61a42da7 100644 --- a/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs +++ b/test/UnitTests/Internal/ContextGraphBuilder_Tests.cs @@ -19,7 +19,7 @@ public void AddDbContext_Does_Not_Throw_If_Context_Contains_Members_That_DoNot_I var resourceGraph = resourceGraphBuilder.Build() as ResourceGraph; // assert - Assert.Empty(resourceGraph.GetContextEntities()); + Assert.Empty(resourceGraph.GetResourceContexts()); } [Fact] diff --git a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs index ef77f99aec..6a405ea2bf 100644 --- a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs +++ b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs @@ -42,7 +42,7 @@ public void Parse_ValidSelection_CanParse() var contextEntity = new ResourceContext { - EntityName = type, + ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; @@ -72,7 +72,7 @@ public void Parse_TypeNameAsNavigation_ThrowsJsonApiException() var contextEntity = new ResourceContext { - EntityName = type, + ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; @@ -98,7 +98,7 @@ public void Parse_DeeplyNestedSelection_ThrowsJsonApiException() var contextEntity = new ResourceContext { - EntityName = type, + ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; @@ -120,7 +120,7 @@ public void Parse_InvalidField_ThrowsJsonApiException() var contextEntity = new ResourceContext { - EntityName = type, + ResourceName = type, Attributes = new List(), Relationships = new List() }; From 3af6f9b1af482d6698650201916a946d0b2183d4 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 12:29:25 +0200 Subject: [PATCH 21/29] style: TEntity --> TResource --- .../Data/DbContextResolver.cs | 2 +- .../Data/DefaultResourceRepository.cs | 52 ++++++++--------- .../Data/IDbContextResolver.cs | 6 +- .../Data/IResourceReadRepository.cs | 30 +++++----- .../Data/IResourceRepository.cs | 14 ++--- .../Data/IResourceWriteRepository.cs | 14 ++--- .../Extensions/TypeExtensions.cs | 2 +- .../Hooks/Discovery/HooksDiscovery.cs | 6 +- .../Hooks/Discovery/IHooksDiscovery.cs | 6 +- .../Hooks/Execution/IHookExecutorHelper.cs | 2 +- .../Hooks/IResourceHookContainer.cs | 12 ++-- .../Hooks/ResourceHookExecutor.cs | 58 +++++++++---------- .../Hooks/Traversal/ChildNode.cs | 18 +++--- .../Hooks/Traversal/ITraversalHelper.cs | 4 +- .../Hooks/Traversal/RootNode.cs | 18 +++--- .../Hooks/Traversal/TraversalHelper.cs | 26 ++++----- .../Extensions/IQueryableExtensions.cs | 8 +-- test/UnitTests/DbSetMock.cs | 4 +- .../ResourceHooks/ResourceHooksTestsSetup.cs | 6 +- 19 files changed, 144 insertions(+), 144 deletions(-) diff --git a/src/JsonApiDotNetCore/Data/DbContextResolver.cs b/src/JsonApiDotNetCore/Data/DbContextResolver.cs index dc30159205..3c9e20b9da 100644 --- a/src/JsonApiDotNetCore/Data/DbContextResolver.cs +++ b/src/JsonApiDotNetCore/Data/DbContextResolver.cs @@ -14,7 +14,7 @@ public DbContextResolver(TContext context) public DbContext GetContext() => _context; - public DbSet GetDbSet() where TEntity : class => null; + public DbSet GetDbSet() where TResource : class => null; } } diff --git a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs index 1c9b4d15b0..50f0efdfd3 100644 --- a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs @@ -18,12 +18,12 @@ namespace JsonApiDotNetCore.Data /// Provides a default repository implementation and is responsible for /// abstracting any EF Core APIs away from the service layer. /// - public class DefaultResourceRepository : IResourceRepository - where TEntity : class, IIdentifiable + public class DefaultResourceRepository : IResourceRepository + where TResource : class, IIdentifiable { private readonly ITargetedFields _targetedFields; private readonly DbContext _context; - private readonly DbSet _dbSet; + private readonly DbSet _dbSet; private readonly IResourceGraph _resourceGraph; private readonly IGenericProcessorFactory _genericProcessorFactory; @@ -46,16 +46,16 @@ public DefaultResourceRepository( _resourceGraph = resourceGraph; _genericProcessorFactory = genericProcessorFactory; _context = contextResolver.GetContext(); - _dbSet = _context.Set(); + _dbSet = _context.Set(); } /// - public virtual IQueryable Get() => _dbSet; + public virtual IQueryable Get() => _dbSet; /// - public virtual IQueryable Get(TId id) => _dbSet.Where(e => e.Id.Equals(id)); + public virtual IQueryable Get(TId id) => _dbSet.Where(e => e.Id.Equals(id)); /// - public virtual IQueryable Select(IQueryable entities, List fields) + public virtual IQueryable Select(IQueryable entities, List fields) { if (fields?.Count > 0) return entities.Select(fields); @@ -64,24 +64,24 @@ public virtual IQueryable Select(IQueryable entities, List - public virtual IQueryable Filter(IQueryable entities, FilterQueryContext filterQueryContext) + public virtual IQueryable Filter(IQueryable entities, FilterQueryContext filterQueryContext) { if (filterQueryContext.IsCustom) { - var query = (Func, FilterQuery, IQueryable>)filterQueryContext.CustomQuery; + var query = (Func, FilterQuery, IQueryable>)filterQueryContext.CustomQuery; return query(entities, filterQueryContext.Query); } return entities.Filter(filterQueryContext); } /// - public virtual IQueryable Sort(IQueryable entities, SortQueryContext sortQueryContext) + public virtual IQueryable Sort(IQueryable entities, SortQueryContext sortQueryContext) { return entities.Sort(sortQueryContext); } /// - public virtual async Task CreateAsync(TEntity entity) + public virtual async Task CreateAsync(TResource entity) { foreach (var relationshipAttr in _targetedFields.Relationships) { @@ -150,7 +150,7 @@ private bool IsHasOneRelationship(string internalRelationshipName, Type type) return !type.GetProperty(internalRelationshipName).PropertyType.Inherits(typeof(IEnumerable)); } - private void DetachRelationships(TEntity entity) + private void DetachRelationships(TResource entity) { foreach (var relationshipAttr in _targetedFields.Relationships) { @@ -176,7 +176,7 @@ private void DetachRelationships(TEntity entity) } /// - public virtual async Task UpdateAsync(TEntity updatedEntity) + public virtual async Task UpdateAsync(TResource updatedEntity) { var databaseEntity = await Get(updatedEntity.Id).FirstOrDefaultAsync(); if (databaseEntity == null) @@ -212,7 +212,7 @@ public virtual async Task UpdateAsync(TEntity updatedEntity) /// to the change tracker. It does so by checking if there already are /// instances of the to-be-attached entities in the change tracker. ///
    - private object GetTrackedRelationshipValue(RelationshipAttribute relationshipAttr, TEntity entity, out bool wasAlreadyAttached) + private object GetTrackedRelationshipValue(RelationshipAttribute relationshipAttr, TResource entity, out bool wasAlreadyAttached) { wasAlreadyAttached = false; if (relationshipAttr is HasOneAttribute hasOneAttr) @@ -278,7 +278,7 @@ public virtual async Task DeleteAsync(TId id) return true; } - public virtual IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain) + public virtual IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain) { if (!inclusionChain.Any()) return entities; @@ -293,7 +293,7 @@ public virtual IQueryable Include(IQueryable entities, params } /// - public virtual async Task> PageAsync(IQueryable entities, int pageSize, int pageNumber) + public virtual async Task> PageAsync(IQueryable entities, int pageSize, int pageNumber) { if (pageNumber >= 0) { @@ -315,25 +315,25 @@ public virtual async Task> PageAsync(IQueryable en } /// - public async Task CountAsync(IQueryable entities) + public async Task CountAsync(IQueryable entities) { - return (entities is IAsyncEnumerable) + return (entities is IAsyncEnumerable) ? await entities.CountAsync() : entities.Count(); } /// - public async Task FirstOrDefaultAsync(IQueryable entities) + public async Task FirstOrDefaultAsync(IQueryable entities) { - return (entities is IAsyncEnumerable) + return (entities is IAsyncEnumerable) ? await entities.FirstOrDefaultAsync() : entities.FirstOrDefault(); } /// - public async Task> ToListAsync(IQueryable entities) + public async Task> ToListAsync(IQueryable entities) { - return (entities is IAsyncEnumerable) + return (entities is IAsyncEnumerable) ? await entities.ToListAsync() : entities.ToList(); } @@ -351,7 +351,7 @@ public async Task> ToListAsync(IQueryable entiti /// after which the reassignment `p1.todoItems = [t3, t4]` will actually /// make EF Core perform a complete replace. This method does the loading of `[t1, t2]`. ///
    - protected void LoadCurrentRelationships(TEntity oldEntity, RelationshipAttribute relationshipAttribute) + protected void LoadCurrentRelationships(TResource oldEntity, RelationshipAttribute relationshipAttribute) { if (relationshipAttribute is HasManyThroughAttribute throughAttribute) { @@ -369,7 +369,7 @@ protected void LoadCurrentRelationships(TEntity oldEntity, RelationshipAttribute /// use the join table (ArticleTags). This methods assigns the relationship value to entity /// by taking care of that /// - private void AssignHasManyThrough(TEntity entity, HasManyThroughAttribute hasManyThrough, IList relationshipValue) + private void AssignHasManyThrough(TResource entity, HasManyThroughAttribute hasManyThrough, IList relationshipValue) { var pointers = relationshipValue.Cast(); var throughRelationshipCollection = Activator.CreateInstance(hasManyThrough.ThroughProperty.PropertyType) as IList; @@ -414,8 +414,8 @@ private IIdentifiable AttachOrGetTracked(IIdentifiable relationshipValue) } /// - public class DefaultResourceRepository : DefaultResourceRepository, IResourceRepository - where TEntity : class, IIdentifiable + public class DefaultResourceRepository : DefaultResourceRepository, IResourceRepository + where TResource : class, IIdentifiable { public DefaultResourceRepository(ITargetedFields targetedFields, IDbContextResolver contextResolver, diff --git a/src/JsonApiDotNetCore/Data/IDbContextResolver.cs b/src/JsonApiDotNetCore/Data/IDbContextResolver.cs index 4915d788c4..cb5372c29d 100644 --- a/src/JsonApiDotNetCore/Data/IDbContextResolver.cs +++ b/src/JsonApiDotNetCore/Data/IDbContextResolver.cs @@ -7,8 +7,8 @@ public interface IDbContextResolver { DbContext GetContext(); - [Obsolete("Use DbContext.Set() instead", error: true)] - DbSet GetDbSet() - where TEntity : class; + [Obsolete("Use DbContext.Set() instead", error: true)] + DbSet GetDbSet() + where TResource : class; } } diff --git a/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs index 34af4ca868..4e02306eea 100644 --- a/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs @@ -6,27 +6,27 @@ namespace JsonApiDotNetCore.Data { - public interface IResourceReadRepository - : IResourceReadRepository - where TEntity : class, IIdentifiable + public interface IResourceReadRepository + : IResourceReadRepository + where TResource : class, IIdentifiable { } - public interface IResourceReadRepository - where TEntity : class, IIdentifiable + public interface IResourceReadRepository + where TResource : class, IIdentifiable { /// /// The base GET query. This is a good place to apply rules that should affect all reads, /// such as authorization of resources. /// - IQueryable Get(); + IQueryable Get(); /// /// Get the entity by id /// - IQueryable Get(TId id); + IQueryable Get(TId id); /// /// Apply fields to the provided queryable /// - IQueryable Select(IQueryable entities, List fields); + IQueryable Select(IQueryable entities, List fields); /// /// Include a relationship in the query /// @@ -35,30 +35,30 @@ public interface IResourceReadRepository /// _todoItemsRepository.GetAndIncludeAsync(1, "achieved-date"); /// /// - IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain); + IQueryable Include(IQueryable entities, params RelationshipAttribute[] inclusionChain); /// /// Apply a filter to the provided queryable /// - IQueryable Filter(IQueryable entities, FilterQueryContext filterQuery); + IQueryable Filter(IQueryable entities, FilterQueryContext filterQuery); /// /// Apply a sort to the provided queryable /// - IQueryable Sort(IQueryable entities, SortQueryContext sortQueries); + IQueryable Sort(IQueryable entities, SortQueryContext sortQueries); /// /// Paginate the provided queryable /// - Task> PageAsync(IQueryable entities, int pageSize, int pageNumber); + Task> PageAsync(IQueryable entities, int pageSize, int pageNumber); /// /// Count the total number of records /// - Task CountAsync(IQueryable entities); + Task CountAsync(IQueryable entities); /// /// Get the first element in the collection, return the default value if collection is empty /// - Task FirstOrDefaultAsync(IQueryable entities); + Task FirstOrDefaultAsync(IQueryable entities); /// /// Convert the collection to a materialized list /// - Task> ToListAsync(IQueryable entities); + Task> ToListAsync(IQueryable entities); } } diff --git a/src/JsonApiDotNetCore/Data/IResourceRepository.cs b/src/JsonApiDotNetCore/Data/IResourceRepository.cs index f176c187e9..100ea63961 100644 --- a/src/JsonApiDotNetCore/Data/IResourceRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceRepository.cs @@ -2,14 +2,14 @@ namespace JsonApiDotNetCore.Data { - public interface IResourceRepository - : IResourceRepository - where TEntity : class, IIdentifiable + public interface IResourceRepository + : IResourceRepository + where TResource : class, IIdentifiable { } - public interface IResourceRepository - : IResourceReadRepository, - IResourceWriteRepository - where TEntity : class, IIdentifiable + public interface IResourceRepository + : IResourceReadRepository, + IResourceWriteRepository + where TResource : class, IIdentifiable { } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs b/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs index 50b9f5aed7..af25b64e3a 100644 --- a/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs @@ -5,17 +5,17 @@ namespace JsonApiDotNetCore.Data { - public interface IResourceWriteRepository - : IResourceWriteRepository - where TEntity : class, IIdentifiable + public interface IResourceWriteRepository + : IResourceWriteRepository + where TResource : class, IIdentifiable { } - public interface IResourceWriteRepository - where TEntity : class, IIdentifiable + public interface IResourceWriteRepository + where TResource : class, IIdentifiable { - Task CreateAsync(TEntity entity); + Task CreateAsync(TResource entity); - Task UpdateAsync(TEntity entity); + Task UpdateAsync(TResource entity); Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds); diff --git a/src/JsonApiDotNetCore/Extensions/TypeExtensions.cs b/src/JsonApiDotNetCore/Extensions/TypeExtensions.cs index ee79174529..ea0f7ee4ea 100644 --- a/src/JsonApiDotNetCore/Extensions/TypeExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/TypeExtensions.cs @@ -33,7 +33,7 @@ public static void AddRange(this IList list, IEnumerable items) /// /// Extension to use the LINQ cast method in a non-generic way: /// - /// Type targetType = typeof(TEntity) + /// Type targetType = typeof(TResource) /// ((IList)myList).Cast(targetType). /// /// diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs index 3d5490e42f..9b95e24562 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/HooksDiscovery.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.Hooks /// /// The default implementation for IHooksDiscovery /// - public class HooksDiscovery : IHooksDiscovery where TEntity : class, IIdentifiable + public class HooksDiscovery : IHooksDiscovery where TResource : class, IIdentifiable { private readonly ResourceHook[] _allHooks; private readonly ResourceHook[] _databaseValuesAttributeAllowed = @@ -40,8 +40,8 @@ public HooksDiscovery() /// The implemented hooks for model. void DiscoverImplementedHooksForModel() { - Type parameterizedResourceDefinition = typeof(ResourceDefinition); - var derivedTypes = TypeLocator.GetDerivedTypes(typeof(TEntity).Assembly, parameterizedResourceDefinition).ToList(); + Type parameterizedResourceDefinition = typeof(ResourceDefinition); + var derivedTypes = TypeLocator.GetDerivedTypes(typeof(TResource).Assembly, parameterizedResourceDefinition).ToList(); var implementedHooks = new List(); diff --git a/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs b/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs index 709b30900e..9db0d2a0ca 100644 --- a/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs +++ b/src/JsonApiDotNetCore/Hooks/Discovery/IHooksDiscovery.cs @@ -4,11 +4,11 @@ namespace JsonApiDotNetCore.Hooks { /// - /// A singleton service for a particular TEntity that stores a field of + /// A singleton service for a particular TResource that stores a field of /// enums that represents which resource hooks have been implemented for that /// particular entity. /// - public interface IHooksDiscovery : IHooksDiscovery where TEntity : class, IIdentifiable + public interface IHooksDiscovery : IHooksDiscovery where TResource : class, IIdentifiable { } @@ -17,7 +17,7 @@ public interface IHooksDiscovery : IHooksDiscovery where TEntity : clas public interface IHooksDiscovery { /// - /// A list of the implemented hooks for resource TEntity + /// A list of the implemented hooks for resource TResource /// /// The implemented hooks. ResourceHook[] ImplementedHooks { get; } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs index 98de544f0a..23460b9fa6 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs @@ -31,7 +31,7 @@ internal interface IHookExecutorHelper /// Also caches the retrieves containers so we don't need to reflectively /// instantiate them multiple times. /// - IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable; + IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TResource : class, IIdentifiable; /// /// Load the implicitly affected entities from the database for a given set of target target entities and involved relationships diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index 9ab0f5c2ec..52c8cb36d0 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -26,7 +26,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// For the pipeline, /// will typically contain one entry. /// - /// The returned may be a subset + /// The returned may be a subset /// of , in which case the operation of the /// pipeline will not be executed for the omitted entities. The returned /// set may also contain custom changes of the properties on the entities. @@ -55,8 +55,8 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// For the pipeline, the /// will typically contain one entity. /// - /// The returned may be a subset - /// of the property in parameter , + /// The returned may be a subset + /// of the property in parameter , /// in which case the operation of the pipeline will not be executed /// for the omitted entities. The returned set may also contain custom /// changes of the properties on the entities. @@ -83,7 +83,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// For the pipeline, /// will typically contain one entity. /// - /// The returned may be a subset + /// The returned may be a subset /// of , in which case the operation of the /// pipeline will not be executed for the omitted entities. /// @@ -106,7 +106,7 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// and its author relationship was set to an existing Person, this hook will be fired /// for that particular Person. /// - /// The returned may be a subset + /// The returned may be a subset /// of , in which case the operation of the /// pipeline will not be executed for any entity whose id was omitted /// @@ -203,7 +203,7 @@ public interface IOnHooks where TResource : class, IIdentifiable /// the entities of type from the /// layer /// - /// The returned may be a subset + /// The returned may be a subset /// of and may contain changes in properties /// of the encapsulated entities. /// diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index b351a2a92d..4d312a0d81 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -37,24 +37,24 @@ public ResourceHookExecutor( } /// - public virtual void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TEntity : class, IIdentifiable + public virtual void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TResource : class, IIdentifiable { - var hookContainer = _executorHelper.GetResourceHookContainer(ResourceHook.BeforeRead); + var hookContainer = _executorHelper.GetResourceHookContainer(ResourceHook.BeforeRead); hookContainer?.BeforeRead(pipeline, false, stringId); - var calledContainers = new List() { typeof(TEntity) }; + var calledContainers = new List() { typeof(TResource) }; foreach (var chain in _includeService.Get()) RecursiveBeforeRead(chain, pipeline, calledContainers); } /// - public virtual IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.BeforeUpdate, entities, out var container, out var node)) { var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray(); - var dbValues = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, relationships); - var diff = new DiffableEntityHashSet(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer(), _targetedFields); - IEnumerable updated = container.BeforeUpdate(diff, pipeline); + var dbValues = LoadDbValues(typeof(TResource), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, relationships); + var diff = new DiffableEntityHashSet(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer(), _targetedFields); + IEnumerable updated = container.BeforeUpdate(diff, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -64,12 +64,12 @@ public virtual IEnumerable BeforeUpdate(IEnumerable e } /// - public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.BeforeCreate, entities, out var container, out var node)) { - var affected = new EntityHashSet((HashSet)node.UniqueEntities, node.PrincipalsToNextLayer()); - IEnumerable updated = container.BeforeCreate(affected, pipeline); + var affected = new EntityHashSet((HashSet)node.UniqueEntities, node.PrincipalsToNextLayer()); + IEnumerable updated = container.BeforeCreate(affected, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -78,15 +78,15 @@ public virtual IEnumerable BeforeCreate(IEnumerable e } /// - public virtual IEnumerable BeforeDelete(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual IEnumerable BeforeDelete(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.BeforeDelete, entities, out var container, out var node)) { var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray(); - var targetEntities = LoadDbValues(typeof(TEntity), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, relationships) ?? node.UniqueEntities; - var affected = new EntityHashSet(targetEntities, node.PrincipalsToNextLayer()); + var targetEntities = LoadDbValues(typeof(TResource), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, relationships) ?? node.UniqueEntities; + var affected = new EntityHashSet(targetEntities, node.PrincipalsToNextLayer()); - IEnumerable updated = container.BeforeDelete(affected, pipeline); + IEnumerable updated = container.BeforeDelete(affected, pipeline); node.UpdateUnique(updated); node.Reassign(entities); } @@ -105,11 +105,11 @@ public virtual IEnumerable BeforeDelete(IEnumerable e } /// - public virtual IEnumerable OnReturn(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual IEnumerable OnReturn(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.OnReturn, entities, out var container, out var node) && pipeline != ResourcePipeline.GetRelationship) { - IEnumerable updated = container.OnReturn((HashSet)node.UniqueEntities, pipeline); + IEnumerable updated = container.OnReturn((HashSet)node.UniqueEntities, pipeline); ValidateHookResponse(updated); node.UpdateUnique(updated); node.Reassign(entities); @@ -125,11 +125,11 @@ public virtual IEnumerable OnReturn(IEnumerable entit } /// - public virtual void AfterRead(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual void AfterRead(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterRead, entities, out var container, out var node)) { - container.AfterRead((HashSet)node.UniqueEntities, pipeline); + container.AfterRead((HashSet)node.UniqueEntities, pipeline); } Traverse(_traversalHelper.CreateNextLayer(node), ResourceHook.AfterRead, (nextContainer, nextNode) => @@ -139,11 +139,11 @@ public virtual void AfterRead(IEnumerable entities, ResourcePi } /// - public virtual void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterCreate, entities, out var container, out var node)) { - container.AfterCreate((HashSet)node.UniqueEntities, pipeline); + container.AfterCreate((HashSet)node.UniqueEntities, pipeline); } Traverse(_traversalHelper.CreateNextLayer(node), @@ -152,11 +152,11 @@ public virtual void AfterCreate(IEnumerable entities, Resource } /// - public virtual void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TEntity : class, IIdentifiable + public virtual void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterUpdate, entities, out var container, out var node)) { - container.AfterUpdate((HashSet)node.UniqueEntities, pipeline); + container.AfterUpdate((HashSet)node.UniqueEntities, pipeline); } Traverse(_traversalHelper.CreateNextLayer(node), @@ -165,28 +165,28 @@ public virtual void AfterUpdate(IEnumerable entities, Resource } /// - public virtual void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TEntity : class, IIdentifiable + public virtual void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterDelete, entities, out var container, out var node)) { - container.AfterDelete((HashSet)node.UniqueEntities, pipeline, succeeded); + container.AfterDelete((HashSet)node.UniqueEntities, pipeline, succeeded); } } /// /// For a given target and for a given type - /// , gets the hook container if the target + /// , gets the hook container if the target /// hook was implemented and should be executed. /// /// Along the way, creates a traversable node from the root entity set. /// /// true, if hook was implemented, false otherwise. - bool GetHook(ResourceHook target, IEnumerable entities, - out IResourceHookContainer container, - out RootNode node) where TEntity : class, IIdentifiable + bool GetHook(ResourceHook target, IEnumerable entities, + out IResourceHookContainer container, + out RootNode node) where TResource : class, IIdentifiable { node = _traversalHelper.CreateRootNode(entities); - container = _executorHelper.GetResourceHookContainer(target); + container = _executorHelper.GetResourceHookContainer(target); return container != null; } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs index 8a29d6c539..ddebdbf8ee 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs @@ -11,8 +11,8 @@ namespace JsonApiDotNetCore.Hooks /// /// Child node in the tree /// - /// - internal class ChildNode : INode where TEntity : class, IIdentifiable + /// + internal class ChildNode : INode where TResource : class, IIdentifiable { private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); /// @@ -24,7 +24,7 @@ public IEnumerable UniqueEntities { get { - return new HashSet(_relationshipsFromPreviousLayer.SelectMany(rfpl => rfpl.DependentEntities)); + return new HashSet(_relationshipsFromPreviousLayer.SelectMany(rfpl => rfpl.DependentEntities)); } } @@ -37,11 +37,11 @@ public IRelationshipsFromPreviousLayer RelationshipsFromPreviousLayer } } - private readonly RelationshipsFromPreviousLayer _relationshipsFromPreviousLayer; + private readonly RelationshipsFromPreviousLayer _relationshipsFromPreviousLayer; - public ChildNode(RelationshipProxy[] nextLayerRelationships, RelationshipsFromPreviousLayer prevLayerRelationships) + public ChildNode(RelationshipProxy[] nextLayerRelationships, RelationshipsFromPreviousLayer prevLayerRelationships) { - EntityType = typeof(TEntity); + EntityType = typeof(TResource); RelationshipsToNextLayer = nextLayerRelationships; _relationshipsFromPreviousLayer = prevLayerRelationships; } @@ -49,10 +49,10 @@ public ChildNode(RelationshipProxy[] nextLayerRelationships, RelationshipsFromPr /// public void UpdateUnique(IEnumerable updated) { - List casted = updated.Cast().ToList(); + List casted = updated.Cast().ToList(); foreach (var rpfl in _relationshipsFromPreviousLayer) { - rpfl.DependentEntities = new HashSet(rpfl.DependentEntities.Intersect(casted, _comparer).Cast()); + rpfl.DependentEntities = new HashSet(rpfl.DependentEntities.Intersect(casted, _comparer).Cast()); } } @@ -62,7 +62,7 @@ public void UpdateUnique(IEnumerable updated) /// public void Reassign(IEnumerable updated = null) { - var unique = (HashSet)UniqueEntities; + var unique = (HashSet)UniqueEntities; foreach (var rfpl in _relationshipsFromPreviousLayer) { var proxy = rfpl.Proxy; diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/ITraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/ITraversalHelper.cs index f0449b9756..2a93cbb4b0 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/ITraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/ITraversalHelper.cs @@ -24,7 +24,7 @@ internal interface ITraversalHelper /// /// The root node. /// Root entities. - /// The 1st type parameter. - RootNode CreateRootNode(IEnumerable rootEntities) where TEntity : class, IIdentifiable; + /// The 1st type parameter. + RootNode CreateRootNode(IEnumerable rootEntities) where TResource : class, IIdentifiable; } } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs index 1c4d4d6c1a..47f7a8ce6b 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs @@ -12,11 +12,11 @@ namespace JsonApiDotNetCore.Hooks /// The root node class of the breadth-first-traversal of entity data structures /// as performed by the /// - internal class RootNode : INode where TEntity : class, IIdentifiable + internal class RootNode : INode where TResource : class, IIdentifiable { private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); private readonly RelationshipProxy[] _allRelationshipsToNextLayer; - private HashSet _uniqueEntities; + private HashSet _uniqueEntities; public Type EntityType { get; internal set; } public IEnumerable UniqueEntities { get { return _uniqueEntities; } } public RelationshipProxy[] RelationshipsToNextLayer { get; } @@ -41,10 +41,10 @@ public Dictionary PrincipalsToNextLayer() /// public IRelationshipsFromPreviousLayer RelationshipsFromPreviousLayer { get { return null; } } - public RootNode(IEnumerable uniqueEntities, RelationshipProxy[] poplatedRelationships, RelationshipProxy[] allRelationships) + public RootNode(IEnumerable uniqueEntities, RelationshipProxy[] poplatedRelationships, RelationshipProxy[] allRelationships) { - EntityType = typeof(TEntity); - _uniqueEntities = new HashSet(uniqueEntities); + EntityType = typeof(TResource); + _uniqueEntities = new HashSet(uniqueEntities); RelationshipsToNextLayer = poplatedRelationships; _allRelationshipsToNextLayer = allRelationships; } @@ -55,15 +55,15 @@ public RootNode(IEnumerable uniqueEntities, RelationshipProxy[] poplate /// Updated. public void UpdateUnique(IEnumerable updated) { - var casted = updated.Cast().ToList(); - var intersected = _uniqueEntities.Intersect(casted, _comparer).Cast(); - _uniqueEntities = new HashSet(intersected); + var casted = updated.Cast().ToList(); + var intersected = _uniqueEntities.Intersect(casted, _comparer).Cast(); + _uniqueEntities = new HashSet(intersected); } public void Reassign(IEnumerable source = null) { var ids = _uniqueEntities.Select(ue => ue.StringId); - ((List)source).RemoveAll(se => !ids.Contains(se.StringId)); + ((List)source).RemoveAll(se => !ids.Contains(se.StringId)); } } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs index 80c6d797ca..a6f8c757d2 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs @@ -52,15 +52,15 @@ public TraversalHelper( /// /// The root node. /// Root entities. - /// The 1st type parameter. - public RootNode CreateRootNode(IEnumerable rootEntities) where TEntity : class, IIdentifiable + /// The 1st type parameter. + public RootNode CreateRootNode(IEnumerable rootEntities) where TResource : class, IIdentifiable { _processedEntities = new Dictionary>(); - RegisterRelationshipProxies(typeof(TEntity)); + RegisterRelationshipProxies(typeof(TResource)); var uniqueEntities = ProcessEntities(rootEntities); - var populatedRelationshipsToNextLayer = GetPopulatedRelationships(typeof(TEntity), uniqueEntities.Cast()); - var allRelationshipsFromType = RelationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.PrincipalType == typeof(TEntity)).ToArray(); - return new RootNode(uniqueEntities, populatedRelationshipsToNextLayer, allRelationshipsFromType); + var populatedRelationshipsToNextLayer = GetPopulatedRelationships(typeof(TResource), uniqueEntities.Cast()); + var allRelationshipsFromType = RelationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.PrincipalType == typeof(TResource)).ToArray(); + return new RootNode(uniqueEntities, populatedRelationshipsToNextLayer, allRelationshipsFromType); } /// @@ -185,10 +185,10 @@ RelationshipProxy[] GetPopulatedRelationships(PrincipalType principalType, IEnum /// /// The entities. /// Incoming entities. - /// The 1st type parameter. - HashSet ProcessEntities(IEnumerable incomingEntities) where TEntity : class, IIdentifiable + /// The 1st type parameter. + HashSet ProcessEntities(IEnumerable incomingEntities) where TResource : class, IIdentifiable { - Type type = typeof(TEntity); + Type type = typeof(TResource); var newEntities = UniqueInTree(incomingEntities, type); RegisterProcessedEntities(newEntities, type); return newEntities; @@ -250,10 +250,10 @@ HashSet GetProcessedEntities(Type entityType) /// The in tree. /// Entities. /// Entity type. - HashSet UniqueInTree(IEnumerable entities, Type entityType) where TEntity : class, IIdentifiable + HashSet UniqueInTree(IEnumerable entities, Type entityType) where TResource : class, IIdentifiable { - var newEntities = entities.Except(GetProcessedEntities(entityType), _comparer).Cast(); - return new HashSet(newEntities); + var newEntities = entities.Except(GetProcessedEntities(entityType), _comparer).Cast(); + return new HashSet(newEntities); } /// @@ -284,7 +284,7 @@ void AddToRelationshipGroup(Dictionary> t } /// - /// Reflective helper method to create an instance of ; + /// Reflective helper method to create an instance of ; /// INode CreateNodeInstance(DependentType nodeType, RelationshipProxy[] relationshipsToNext, IEnumerable relationshipsFromPrev) { diff --git a/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/IQueryableExtensions.cs b/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/IQueryableExtensions.cs index 9298d93a05..f9558e9e86 100644 --- a/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/IQueryableExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/IQueryableExtensions.cs @@ -21,10 +21,10 @@ public static class IQueryableExtensions private static readonly PropertyInfo DependenciesProperty = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies"); - public static string ToSql(this IQueryable queryable) - where TEntity : class + public static string ToSql(this IQueryable queryable) + where TResource : class { - if (!(queryable is EntityQueryable) && !(queryable is InternalDbSet)) + if (!(queryable is EntityQueryable) && !(queryable is InternalDbSet)) throw new ArgumentException(); var queryCompiler = (IQueryCompiler)QueryCompilerField.GetValue(queryable.Provider); @@ -34,7 +34,7 @@ public static string ToSql(this IQueryable queryable) var queryCompilationContextFactory = ((DatabaseDependencies)DependenciesProperty.GetValue(database)).QueryCompilationContextFactory; var queryCompilationContext = queryCompilationContextFactory.Create(false); var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor(); - modelVisitor.CreateQueryExecutor(queryModel); + modelVisitor.CreateQueryExecutor(queryModel); return modelVisitor.Queries.Join(Environment.NewLine + Environment.NewLine); } } diff --git a/test/UnitTests/DbSetMock.cs b/test/UnitTests/DbSetMock.cs index f56a3b1f01..fc87757815 100644 --- a/test/UnitTests/DbSetMock.cs +++ b/test/UnitTests/DbSetMock.cs @@ -32,7 +32,7 @@ public static Mock> AsDbSetMock(this List list) where T : class } } -internal class TestAsyncQueryProvider : IAsyncQueryProvider +internal class TestAsyncQueryProvider : IAsyncQueryProvider { private readonly IQueryProvider _inner; @@ -43,7 +43,7 @@ internal TestAsyncQueryProvider(IQueryProvider inner) public IQueryable CreateQuery(Expression expression) { - return new TestAsyncEnumerable(expression); + return new TestAsyncEnumerable(expression); } public IQueryable CreateQuery(Expression expression) diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 874b7eba2f..4860daafbd 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -228,10 +228,10 @@ public class HooksTestsSetup : HooksDummyData return (iqMock, hookExecutor, mainResource, firstNestedResource, secondNestedResource); } - protected IHooksDiscovery SetDiscoverableHooks(ResourceHook[] implementedHooks, params ResourceHook[] enableDbValuesHooks) - where TEntity : class, IIdentifiable + protected IHooksDiscovery SetDiscoverableHooks(ResourceHook[] implementedHooks, params ResourceHook[] enableDbValuesHooks) + where TResource : class, IIdentifiable { - var mock = new Mock>(); + var mock = new Mock>(); mock.Setup(discovery => discovery.ImplementedHooks) .Returns(implementedHooks); From ce08b4fc81e2daee60e632205c8479627b165157 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 12:31:15 +0200 Subject: [PATCH 22/29] style: DependentType, PrincipalType --> RightType, LeftType --- .../Builders/ResourceGraphBuilder.cs | 10 ++++----- .../Extensions/IQueryableExtensions.cs | 4 ++-- .../Hooks/Execution/HookExecutorHelper.cs | 20 +++++++++--------- .../Execution/RelationshipsDictionary.cs | 2 +- .../Hooks/ResourceHookExecutor.cs | 12 +++++------ .../Hooks/Traversal/RelationshipProxy.cs | 2 +- .../Hooks/Traversal/TraversalHelper.cs | 2 +- .../Annotation/HasManyThroughAttribute.cs | 4 ++-- .../Annotation/RelationshipAttribute.cs | 21 ++++--------------- .../Common/QueryParameterService.cs | 2 +- .../QueryParameterServices/IncludeService.cs | 2 +- .../SparseFieldsService.cs | 6 +++--- .../Client/ResponseDeserializer.cs | 6 +++--- .../Common/BaseDocumentParser.cs | 6 +++--- .../Server/ResponseSerializerFactory.cs | 2 +- test/UnitTests/Builders/LinkBuilderTests.cs | 2 +- .../AffectedEntitiesHelperTests.cs | 12 +++++------ .../ResourceHooks/ResourceHooksTestsSetup.cs | 2 +- .../IncludedResourceObjectBuilderTests.cs | 2 +- 19 files changed, 53 insertions(+), 66 deletions(-) diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index 0a99207ec8..405fe64936 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -125,8 +125,8 @@ protected virtual List GetRelationships(Type entityType) attribute.PublicRelationshipName = attribute.PublicRelationshipName ?? _resourceNameFormatter.FormatPropertyName(prop); attribute.InternalRelationshipName = prop.Name; - attribute.DependentType = GetRelationshipType(attribute, prop); - attribute.PrincipalType = entityType; + attribute.RightType = GetRelationshipType(attribute, prop); + attribute.LeftType = entityType; attributes.Add(attribute); if (attribute is HasManyThroughAttribute hasManyThroughAttribute) @@ -160,13 +160,13 @@ protected virtual List GetRelationships(Type entityType) ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a relationship id property to type {entityType} with name {leftIdPropertyName}"); // Article → ArticleTag.Tag - hasManyThroughAttribute.RightProperty = throughProperties.SingleOrDefault(x => x.PropertyType == hasManyThroughAttribute.DependentType) - ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a navigation property to type {hasManyThroughAttribute.DependentType}"); + hasManyThroughAttribute.RightProperty = throughProperties.SingleOrDefault(x => x.PropertyType == hasManyThroughAttribute.RightType) + ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a navigation property to type {hasManyThroughAttribute.RightType}"); // ArticleTag.TagId var rightIdPropertyName = JsonApiOptions.RelatedIdMapper.GetRelatedIdPropertyName(hasManyThroughAttribute.RightProperty.Name); hasManyThroughAttribute.RightIdProperty = throughProperties.SingleOrDefault(x => x.Name == rightIdPropertyName) - ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a relationship id property to type {hasManyThroughAttribute.DependentType} with name {rightIdPropertyName}"); + ?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a relationship id property to type {hasManyThroughAttribute.RightType} with name {rightIdPropertyName}"); } } diff --git a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs index 387243fbaa..441d3aee9f 100644 --- a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs @@ -124,7 +124,7 @@ private static IOrderedQueryable CallGenericOrderMethod(IQuery return (IOrderedQueryable)result; } - private static Expression GetFilterExpressionLambda(Expression left, Expression right, FilterOperation operation) + private static Expression GetFilterExpressionLambda(Expression left, Expression right, FilterOperation operation) { Expression body; switch (operation) @@ -259,7 +259,7 @@ private static IQueryable CallGenericWhereMethod(IQueryable - public IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TEntity : class, IIdentifiable + public IResourceHookContainer GetResourceHookContainer(ResourceHook hook = ResourceHook.None) where TResource : class, IIdentifiable { - return (IResourceHookContainer)GetResourceHookContainer(typeof(TEntity), hook); + return (IResourceHookContainer)GetResourceHookContainer(typeof(TResource), hook); } public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] inclusionChain) @@ -89,12 +89,12 @@ public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerab return (IEnumerable)Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(entityTypeForRepository), values.Cast(entityTypeForRepository)); } - public HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships) where TEntity : class, IIdentifiable + public HashSet LoadDbValues(IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] relationships) where TResource : class, IIdentifiable { - var entityType = typeof(TEntity); - var dbValues = LoadDbValues(entityType, entities, hook, relationships)?.Cast(); + var entityType = typeof(TResource); + var dbValues = LoadDbValues(entityType, entities, hook, relationships)?.Cast(); if (dbValues == null) return null; - return new HashSet(dbValues); + return new HashSet(dbValues); } public bool ShouldLoadDbValues(Type entityType, ResourceHook hook) @@ -131,16 +131,16 @@ IHooksDiscovery GetHookDiscovery(Type entityType) return discovery; } - IEnumerable GetWhereAndInclude(IEnumerable ids, RelationshipAttribute[] inclusionChain) where TEntity : class, IIdentifiable + IEnumerable GetWhereAndInclude(IEnumerable ids, RelationshipAttribute[] inclusionChain) where TResource : class, IIdentifiable { - var repo = GetRepository(); + var repo = GetRepository(); var query = repo.Get().Where(e => ids.Contains(e.Id)); return repo.Include(query, inclusionChain).ToList(); } - IResourceReadRepository GetRepository() where TEntity : class, IIdentifiable + IResourceReadRepository GetRepository() where TResource : class, IIdentifiable { - return _genericProcessorFactory.GetProcessor>(typeof(IResourceReadRepository<,>), typeof(TEntity), typeof(TId)); + return _genericProcessorFactory.GetProcessor>(typeof(IResourceReadRepository<,>), typeof(TResource), typeof(TId)); } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs b/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs index b2f1a8fbec..02fab415b4 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs @@ -87,7 +87,7 @@ public Dictionary> GetByRelationship public Dictionary> GetByRelationship(Type relatedType) { - return this.Where(p => p.Key.DependentType == relatedType).ToDictionary(p => p.Key, p => p.Value); + return this.Where(p => p.Key.RightType == relatedType).ToDictionary(p => p.Key, p => p.Value); } /// diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index 4d312a0d81..f2ca5ee138 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -215,10 +215,10 @@ void Traverse(NodeLayer currentLayer, ResourceHook target, Action relationshipChain, ResourcePipeline pipeline, List calledContainers) { var relationship = relationshipChain.First(); - if (!calledContainers.Contains(relationship.DependentType)) + if (!calledContainers.Contains(relationship.RightType)) { - calledContainers.Add(relationship.DependentType); - var container = _executorHelper.GetResourceHookContainer(relationship.DependentType, ResourceHook.BeforeRead); + calledContainers.Add(relationship.RightType); + var container = _executorHelper.GetResourceHookContainer(relationship.RightType, ResourceHook.BeforeRead); if (container != null) CallHook(container, ResourceHook.BeforeRead, new object[] { pipeline, true, null }); } @@ -306,7 +306,7 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) /// the root layer is ALWAYS homogenous, so we safely assume /// that for every relationship to the previous layer, the /// principal type is the same. - PrincipalType principalEntityType = currenEntitiesGrouped.First().Key.PrincipalType; + PrincipalType principalEntityType = currenEntitiesGrouped.First().Key.LeftType; FireForAffectedImplicits(principalEntityType, currentEntitiesGroupedInverse, pipeline); } } @@ -403,8 +403,8 @@ Dictionary ReplaceWithDbValues(Dictionary().Select(entity => dbValues.Single(dbEntity => dbEntity.StringId == entity.StringId)).Cast(key.PrincipalType); - prevLayerRelationships[key] = TypeHelper.CreateHashSetFor(key.PrincipalType, replaced); + var replaced = prevLayerRelationships[key].Cast().Select(entity => dbValues.Single(dbEntity => dbEntity.StringId == entity.StringId)).Cast(key.LeftType); + prevLayerRelationships[key] = TypeHelper.CreateHashSetFor(key.LeftType, replaced); } return prevLayerRelationships; } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs index 6e5d90ecc8..6cb24fc58b 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs @@ -31,7 +31,7 @@ public class RelationshipProxy /// Identifiable) or it is the righthand side (when the jointable is not identifiable) /// public Type DependentType { get; private set; } - public Type PrincipalType { get { return Attribute.PrincipalType; } } + public Type PrincipalType { get { return Attribute.LeftType; } } public bool IsContextRelation { get; private set; } public RelationshipAttribute Attribute { get; set; } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs index a6f8c757d2..0a8c503a40 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs @@ -270,7 +270,7 @@ DependentType GetDependentTypeFromRelationship(RelationshipAttribute attr) { return throughAttr.ThroughType; } - return attr.DependentType; + return attr.RightType; } void AddToRelationshipGroup(Dictionary> target, RelationshipProxy proxy, IEnumerable newEntities) diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs index 6d60f4b660..853a414b8f 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasManyThroughAttribute.cs @@ -85,14 +85,14 @@ public override object GetValue(object entity) if (throughEntities == null) // return an empty list for the right-type of the property. - return TypeHelper.CreateListFor(DependentType); + return TypeHelper.CreateListFor(RightType); // the right entities are included on the navigation/through entities. Extract and return them. var rightEntities = new List(); foreach (var rightEntity in (IList)throughEntities) rightEntities.Add((IIdentifiable)RightProperty.GetValue(rightEntity)); - return rightEntities.Cast(DependentType); + return rightEntities.Cast(RightType); } diff --git a/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs index 1a5bd5dea2..30f3271a17 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs @@ -23,19 +23,6 @@ protected RelationshipAttribute(string publicName, Link relationshipLinks, bool public string InternalRelationshipName { get; internal set; } public string InverseNavigation { get; internal set; } - /// - /// The related entity type. This does not necessarily match the navigation property type. - /// In the case of a HasMany relationship, this value will be the generic argument type. - /// - /// - /// - /// - /// public List<Tag> Tags { get; sit; } // Type => Tag - /// - /// - [Obsolete("Use property DependentType")] - public Type Type { get { return DependentType; } internal set { DependentType = value; } } - /// /// The related entity type. This does not necessarily match the navigation property type. /// In the case of a HasMany relationship, this value will be the generic argument type. @@ -48,12 +35,12 @@ protected RelationshipAttribute(string publicName, Link relationshipLinks, bool /// public List<Tag> Tags { get; set; } // Type => Tag /// /// - public Type DependentType { get; internal set; } + public Type RightType { get; internal set; } /// /// The parent entity type. The technical language as used in EF Core is used here (dependent vs principal). /// - public Type PrincipalType { get; internal set; } + public Type LeftType { get; internal set; } public bool IsHasMany => GetType() == typeof(HasManyAttribute) || GetType().Inherits(typeof(HasManyAttribute)); public bool IsHasOne => GetType() == typeof(HasOneAttribute); @@ -84,9 +71,9 @@ public override bool Equals(object obj) bool equalRelationshipName = PublicRelationshipName.Equals(attr.PublicRelationshipName); bool equalPrincipalType = true; - if (PrincipalType != null) + if (LeftType != null) { - equalPrincipalType = PrincipalType.Equals(attr.PrincipalType); + equalPrincipalType = LeftType.Equals(attr.LeftType); } return IsHasMany == attr.IsHasMany && equalRelationshipName && equalPrincipalType; } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs index d36accf355..cadd91f6fd 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/Common/QueryParameterService.cs @@ -48,7 +48,7 @@ protected AttrAttribute GetAttribute(string target, RelationshipAttribute relati { AttrAttribute attribute; if (relationship != null) - attribute = _resourceGraph.GetAttributes(relationship.DependentType).FirstOrDefault(a => a.Is(target)); + attribute = _resourceGraph.GetAttributes(relationship.RightType).FirstOrDefault(a => a.Is(target)); else attribute = _requestResource.Attributes.FirstOrDefault(attr => attr.Is(target)); diff --git a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs index 5fb586cc56..4032da4c53 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/IncludeService.cs @@ -52,7 +52,7 @@ private void ParseChain(string chain) throw CannotIncludeError(resourceContext, relationshipName); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetResourceContext(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.RightType); } _includedChains.Add(parsedChain); } diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs index 05a676afd4..f20542562d 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs @@ -39,7 +39,7 @@ public List Get(RelationshipAttribute relationship = null) _selectedRelationshipFields.TryGetValue(relationship, out var fields); return fields; } - + /// public virtual void Parse(KeyValuePair queryParameter) { // expected: articles?fields=prop1,prop2 @@ -83,10 +83,10 @@ public virtual void Parse(KeyValuePair queryParameter) /// private void RegisterRelatedResourceField(string field, RelationshipAttribute relationship) { - var relationProperty = _resourceGraph.GetResourceContext(relationship.DependentType); + var relationProperty = _resourceGraph.GetResourceContext(relationship.RightType); var attr = relationProperty.Attributes.SingleOrDefault(a => a.Is(field)); if (attr == null) - throw new JsonApiException(400, $"'{relationship.DependentType.Name}' does not contain '{field}'."); + throw new JsonApiException(400, $"'{relationship.RightType.Name}' does not contain '{field}'."); if (!_selectedRelationshipFields.TryGetValue(relationship, out var registeredFields)) _selectedRelationshipFields.Add(relationship, registeredFields = new List()); diff --git a/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs b/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs index c8d990ccd1..b0fa4f562c 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs @@ -71,7 +71,7 @@ protected override void AfterProcessField(IIdentifiable entity, IResourceField f } else if (field is HasManyAttribute hasManyAttr) { // add attributes and relationships of a parsed HasMany relationship - var values = TypeHelper.CreateListFor(hasManyAttr.DependentType); + var values = TypeHelper.CreateListFor(hasManyAttr.RightType); foreach (var rio in data.ManyData) values.Add(ParseIncludedRelationship(hasManyAttr, rio)); @@ -84,7 +84,7 @@ protected override void AfterProcessField(IIdentifiable entity, IResourceField f /// private IIdentifiable ParseIncludedRelationship(RelationshipAttribute relationshipAttr, ResourceIdentifierObject relatedResourceIdentifier) { - var relatedInstance = relationshipAttr.DependentType.New(); + var relatedInstance = relationshipAttr.RightType.New(); relatedInstance.StringId = relatedResourceIdentifier.Id; var includedResource = GetLinkedResource(relatedResourceIdentifier); @@ -93,7 +93,7 @@ private IIdentifiable ParseIncludedRelationship(RelationshipAttribute relationsh var contextEntity = _provider.GetResourceContext(relatedResourceIdentifier.Type); if (contextEntity == null) - throw new InvalidOperationException($"Included type '{relationshipAttr.DependentType}' is not a registered json:api resource."); + throw new InvalidOperationException($"Included type '{relationshipAttr.RightType}' is not a registered json:api resource."); SetAttributes(relatedInstance, includedResource.Attributes, contextEntity.Attributes); SetRelationships(relatedInstance, includedResource.Relationships, contextEntity.Relationships); diff --git a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs index fa0d70699f..383c6bd29d 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs @@ -213,7 +213,7 @@ private void SetNavigation(IIdentifiable entity, HasOneAttribute attr, string re } else { - var relatedInstance = attr.DependentType.New(); + var relatedInstance = attr.RightType.New(); relatedInstance.StringId = relatedId; attr.SetValue(entity, relatedInstance); } @@ -230,11 +230,11 @@ private object SetHasManyRelationship(IIdentifiable entity, { // if the relationship is set to null, no need to set the navigation property to null: this is the default value. var relatedResources = relationshipData.ManyData.Select(rio => { - var relatedInstance = attr.DependentType.New(); + var relatedInstance = attr.RightType.New(); relatedInstance.StringId = rio.Id; return relatedInstance; }); - var convertedCollection = TypeHelper.ConvertCollection(relatedResources, attr.DependentType); + var convertedCollection = TypeHelper.ConvertCollection(relatedResources, attr.RightType); attr.SetValue(entity, convertedCollection); } diff --git a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs index 951d915eee..af0997d736 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs @@ -44,7 +44,7 @@ public IJsonApiSerializer GetSerializer() private Type GetDocumentPrimaryType() { if (_currentRequest.RequestRelationship != null && !_currentRequest.IsRelationshipPath) - return _currentRequest.RequestRelationship.DependentType; + return _currentRequest.RequestRelationship.RightType; return _currentRequest.GetRequestResource()?.ResourceType; } diff --git a/test/UnitTests/Builders/LinkBuilderTests.cs b/test/UnitTests/Builders/LinkBuilderTests.cs index e7e2371558..b7c5f1a3fe 100644 --- a/test/UnitTests/Builders/LinkBuilderTests.cs +++ b/test/UnitTests/Builders/LinkBuilderTests.cs @@ -93,7 +93,7 @@ public void BuildRelationshipLinks_GlobalResourceAndAttrConfiguration_ExpectedLi var primaryResource = GetResourceContext
    (relationshipLinks: resource); _provider.Setup(m => m.GetResourceContext(typeof(Article))).Returns(primaryResource); var builder = new LinkBuilder(config, GetRequestManager(), null, _provider.Object); - var attr = new HasOneAttribute(links: relationship) { DependentType = typeof(Author), PublicRelationshipName = "author" }; + var attr = new HasOneAttribute(links: relationship) { RightType = typeof(Author), PublicRelationshipName = "author" }; // act var links = builder.GetRelationshipLinks(attr, new Article { Id = 123 }); diff --git a/test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs b/test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs index d4f579d121..bf81006448 100644 --- a/test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs +++ b/test/UnitTests/ResourceHooks/AffectedEntitiesHelperTests.cs @@ -40,20 +40,20 @@ public RelationshipDictionaryTests() { FirstToOneAttr = new HasOneAttribute("first-to-one") { - PrincipalType = typeof(Dummy), - DependentType = typeof(ToOne), + LeftType = typeof(Dummy), + RightType = typeof(ToOne), InternalRelationshipName = "FirstToOne" }; SecondToOneAttr = new HasOneAttribute("second-to-one") { - PrincipalType = typeof(Dummy), - DependentType = typeof(ToOne), + LeftType = typeof(Dummy), + RightType = typeof(ToOne), InternalRelationshipName = "SecondToOne" }; ToManyAttr = new HasManyAttribute("to-manies") { - PrincipalType = typeof(Dummy), - DependentType = typeof(ToMany), + LeftType = typeof(Dummy), + RightType = typeof(ToMany), InternalRelationshipName = "ToManies" }; Relationships.Add(FirstToOneAttr, FirstToOnesEntities); diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 4860daafbd..a42b3484c6 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -389,7 +389,7 @@ protected List GetIncludedRelationshipsChain(string chain { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetResourceContext(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.RightType); } return parsedChain; } diff --git a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs index 200020f8d8..e3b7798974 100644 --- a/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Server/IncludedResourceObjectBuilderTests.cs @@ -163,7 +163,7 @@ private List GetIncludedRelationshipsChain(string chain) { var relationship = resourceContext.Relationships.Single(r => r.PublicRelationshipName == requestedRelationship); parsedChain.Add(relationship); - resourceContext = _resourceGraph.GetResourceContext(relationship.DependentType); + resourceContext = _resourceGraph.GetResourceContext(relationship.RightType); } return parsedChain; } From b1625199e475dbe356bf81b1ffc0736ce5aedc34 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 12:52:27 +0200 Subject: [PATCH 23/29] style: replaced principal with left and dependent with right whenever it referred to relationships --- .../Data/DefaultResourceRepository.cs | 8 +- .../Hooks/Execution/EntityHashSet.cs | 8 +- .../Hooks/Execution/HookExecutorHelper.cs | 60 ++- .../Hooks/Execution/IHookExecutorHelper.cs | 2 +- .../Execution/RelationshipsDictionary.cs | 36 +- .../Hooks/ResourceHookExecutor.cs | 52 +-- .../Hooks/Traversal/ChildNode.cs | 22 +- .../Hooks/Traversal/IEntityNode.cs | 4 +- .../Hooks/Traversal/RelationshipGroup.cs | 14 +- .../Hooks/Traversal/RelationshipProxy.cs | 58 ++- .../RelationshipsFromPreviousLayer.cs | 28 +- .../Hooks/Traversal/RootNode.cs | 11 +- .../Hooks/Traversal/TraversalHelper.cs | 104 +++--- .../Internal/ResourceGraph.cs | 2 +- .../Annotation/RelationshipAttribute.cs | 12 +- wiki/v4/content/using System; | 344 ++++++++++++++++++ 16 files changed, 544 insertions(+), 221 deletions(-) create mode 100644 wiki/v4/content/using System; diff --git a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs index 50f0efdfd3..9a23624d67 100644 --- a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs @@ -239,8 +239,8 @@ private IList GetTrackedManyRelationshipValue(IEnumerable relatio { // convert each element in the value list to relationshipAttr.DependentType. var tracked = AttachOrGetTracked(pointer); if (tracked != null) _wasAlreadyAttached = true; - return Convert.ChangeType(tracked ?? pointer, relationshipAttr.DependentType); - }).ToList().Cast(relationshipAttr.DependentType); + return Convert.ChangeType(tracked ?? pointer, relationshipAttr.RightType); + }).ToList().Cast(relationshipAttr.RightType); if (_wasAlreadyAttached) wasAlreadyAttached = true; return (IList)trackedPointerCollection; } @@ -262,7 +262,7 @@ public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute // of the property... var typeToUpdate = (relationship is HasManyThroughAttribute hasManyThrough) ? hasManyThrough.ThroughType - : relationship.DependentType; + : relationship.RightType; var genericProcessor = _genericProcessorFactory.GetProcessor(typeof(GenericProcessor<>), typeToUpdate); await genericProcessor.UpdateRelationshipsAsync(parent, relationship, relationshipIds); @@ -365,7 +365,7 @@ protected void LoadCurrentRelationships(TResource oldEntity, RelationshipAttribu /// /// The relationshipValue parameter contains the dependent side of the relationship (Tags). - /// We can't directly add them to the principal entity (Article): we need to + /// We can't directly add them to the left entity (Article): we need to /// use the join table (ArticleTags). This methods assigns the relationship value to entity /// by taking care of that /// diff --git a/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs b/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs index 25df58b79b..93ee588a7a 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/EntityHashSet.cs @@ -44,15 +44,15 @@ internal EntityHashSet(IEnumerable entities, /// - public Dictionary> GetByRelationship(Type principalType) + public Dictionary> GetByRelationship(Type leftType) { - return _relationships.GetByRelationship(principalType); + return _relationships.GetByRelationship(leftType); } /// - public Dictionary> GetByRelationship() where TRelatedResource : class, IIdentifiable + public Dictionary> GetByRelationship() where TRightResource : class, IIdentifiable { - return GetByRelationship(typeof(TRelatedResource)); + return GetByRelationship(typeof(TRightResource)); } /// diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index 027d53de63..cc37be321e 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -7,11 +7,9 @@ using JsonApiDotNetCore.Internal.Generics; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Extensions; -using PrincipalType = System.Type; -using DependentType = System.Type; -using Microsoft.EntityFrameworkCore; +using LeftType = System.Type; +using RightType = System.Type; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Configuration; namespace JsonApiDotNetCore.Hooks @@ -22,8 +20,8 @@ internal class HookExecutorHelper : IHookExecutorHelper private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); private readonly IJsonApiOptions _options; protected readonly IGenericProcessorFactory _genericProcessorFactory; - protected readonly Dictionary _hookContainers; - protected readonly Dictionary _hookDiscoveries; + protected readonly Dictionary _hookContainers; + protected readonly Dictionary _hookDiscoveries; protected readonly List _targetedHooksForRelatedEntities; public HookExecutorHelper(IGenericProcessorFactory genericProcessorFactory, @@ -31,22 +29,22 @@ public HookExecutorHelper(IGenericProcessorFactory genericProcessorFactory, { _options = options; _genericProcessorFactory = genericProcessorFactory; - _hookContainers = new Dictionary(); - _hookDiscoveries = new Dictionary(); + _hookContainers = new Dictionary(); + _hookDiscoveries = new Dictionary(); _targetedHooksForRelatedEntities = new List(); } /// - public IResourceHookContainer GetResourceHookContainer(DependentType dependentType, ResourceHook hook = ResourceHook.None) + public IResourceHookContainer GetResourceHookContainer(RightType rightType, ResourceHook hook = ResourceHook.None) { /// checking the cache if we have a reference for the requested container, /// regardless of the hook we will use it for. If the value is null, /// it means there was no implementation IResourceHookContainer at all, /// so we need not even bother. - if (!_hookContainers.TryGetValue(dependentType, out IResourceHookContainer container)) + if (!_hookContainers.TryGetValue(rightType, out IResourceHookContainer container)) { - container = (_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), dependentType)); - _hookContainers[dependentType] = container; + container = (_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), rightType)); + _hookContainers[rightType] = container; } if (container == null) return container; @@ -65,7 +63,7 @@ public IResourceHookContainer GetResourceHookContainer(DependentType dependentTy foreach (ResourceHook targetHook in targetHooks) { - if (ShouldExecuteHook(dependentType, targetHook)) return container; + if (ShouldExecuteHook(rightType, targetHook)) return container; } return null; } @@ -76,7 +74,7 @@ public IResourceHookContainer GetResourceHookContainer(Res return (IResourceHookContainer)GetResourceHookContainer(typeof(TResource), hook); } - public IEnumerable LoadDbValues(PrincipalType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] inclusionChain) + public IEnumerable LoadDbValues(LeftType entityTypeForRepository, IEnumerable entities, ResourceHook hook, params RelationshipAttribute[] inclusionChain) { var idType = TypeHelper.GetIdentifierType(entityTypeForRepository); var parameterizedGetWhere = GetType() @@ -107,7 +105,7 @@ public bool ShouldLoadDbValues(Type entityType, ResourceHook hook) return _options.LoaDatabaseValues; } - bool ShouldExecuteHook(DependentType entityType, ResourceHook hook) + bool ShouldExecuteHook(RightType entityType, ResourceHook hook) { var discovery = GetHookDiscovery(entityType); return discovery.ImplementedHooks.Contains(hook); @@ -145,46 +143,46 @@ IResourceReadRepository GetRepository() where TR public Dictionary LoadImplicitlyAffected( - Dictionary principalEntitiesByRelation, - IEnumerable existingDependentEntities = null) + Dictionary leftEntitiesByRelation, + IEnumerable existingRightEntities = null) { var implicitlyAffected = new Dictionary(); - foreach (var kvp in principalEntitiesByRelation) + foreach (var kvp in leftEntitiesByRelation) { - if (IsHasManyThrough(kvp, out var principals, out var relationship)) continue; + if (IsHasManyThrough(kvp, out var lefts, out var relationship)) continue; // note that we dont't have to check if BeforeImplicitUpdate hook is implemented. If not, it wont ever get here. - var includedPrincipals = LoadDbValues(relationship.PrincipalType, principals, ResourceHook.BeforeImplicitUpdateRelationship, relationship); + var includedLefts = LoadDbValues(relationship.LeftType, lefts, ResourceHook.BeforeImplicitUpdateRelationship, relationship); - foreach (IIdentifiable ip in includedPrincipals) + foreach (IIdentifiable ip in includedLefts) { - IList dbDependentEntityList = null; + IList dbRightEntityList = null; var relationshipValue = relationship.GetValue(ip); if (!(relationshipValue is IEnumerable)) { - dbDependentEntityList = TypeHelper.CreateListFor(relationship.DependentType); - if (relationshipValue != null) dbDependentEntityList.Add(relationshipValue); + dbRightEntityList = TypeHelper.CreateListFor(relationship.RightType); + if (relationshipValue != null) dbRightEntityList.Add(relationshipValue); } else { - dbDependentEntityList = (IList)relationshipValue; + dbRightEntityList = (IList)relationshipValue; } - var dbDependentEntityListCasted = dbDependentEntityList.Cast().ToList(); - if (existingDependentEntities != null) dbDependentEntityListCasted = dbDependentEntityListCasted.Except(existingDependentEntities.Cast(), _comparer).ToList(); + var dbRightEntityListCasted = dbRightEntityList.Cast().ToList(); + if (existingRightEntities != null) dbRightEntityListCasted = dbRightEntityListCasted.Except(existingRightEntities.Cast(), _comparer).ToList(); - if (dbDependentEntityListCasted.Any()) + if (dbRightEntityListCasted.Any()) { if (!implicitlyAffected.TryGetValue(relationship, out IEnumerable affected)) { - affected = TypeHelper.CreateListFor(relationship.DependentType); + affected = TypeHelper.CreateListFor(relationship.RightType); implicitlyAffected[relationship] = affected; } - ((IList)affected).AddRange(dbDependentEntityListCasted); + ((IList)affected).AddRange(dbRightEntityListCasted); } } } - return implicitlyAffected.ToDictionary(kvp => kvp.Key, kvp => TypeHelper.CreateHashSetFor(kvp.Key.DependentType, kvp.Value)); + return implicitlyAffected.ToDictionary(kvp => kvp.Key, kvp => TypeHelper.CreateHashSetFor(kvp.Key.RightType, kvp.Value)); } diff --git a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs index 23460b9fa6..214f1b000d 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/IHookExecutorHelper.cs @@ -37,7 +37,7 @@ internal interface IHookExecutorHelper /// Load the implicitly affected entities from the database for a given set of target target entities and involved relationships /// /// The implicitly affected entities by relationship - Dictionary LoadImplicitlyAffected(Dictionary principalEntities, IEnumerable existingDependentEntities = null); + Dictionary LoadImplicitlyAffected(Dictionary leftEntities, IEnumerable existingRightEntities = null); /// /// For a set of entities, loads current values from the database diff --git a/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs b/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs index 02fab415b4..45b6cd0eca 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/RelationshipsDictionary.cs @@ -16,51 +16,51 @@ public interface IRelationshipsDictionary { } /// /// An interface that is implemented to expose a relationship dictionary on another class. /// - public interface IByAffectedRelationships : - IRelationshipGetters where TDependentResource : class, IIdentifiable + public interface IByAffectedRelationships : + IRelationshipGetters where TRightResource : class, IIdentifiable { /// /// Gets a dictionary of affected resources grouped by affected relationships. /// - Dictionary> AffectedRelationships { get; } + Dictionary> AffectedRelationships { get; } } /// /// A helper class that provides insights in which relationships have been updated for which entities. /// - public interface IRelationshipsDictionary : - IRelationshipGetters, - IReadOnlyDictionary>, - IRelationshipsDictionary where TDependentResource : class, IIdentifiable + public interface IRelationshipsDictionary : + IRelationshipGetters, + IReadOnlyDictionary>, + IRelationshipsDictionary where TRightResource : class, IIdentifiable { } /// /// A helper class that provides insights in which relationships have been updated for which entities. /// - public interface IRelationshipGetters where TResource : class, IIdentifiable + public interface IRelationshipGetters where TLeftResource : class, IIdentifiable { /// - /// Gets a dictionary of all entities that have an affected relationship to type + /// Gets a dictionary of all entities that have an affected relationship to type /// - Dictionary> GetByRelationship() where TRelatedResource : class, IIdentifiable; + Dictionary> GetByRelationship() where TRightResource : class, IIdentifiable; /// - /// Gets a dictionary of all entities that have an affected relationship to type + /// Gets a dictionary of all entities that have an affected relationship to type /// - Dictionary> GetByRelationship(Type relatedResourceType); + Dictionary> GetByRelationship(Type relatedResourceType); /// - /// Gets a collection of all the entities for the property within + /// Gets a collection of all the entities for the property within /// has been affected by the request /// - /// - HashSet GetAffected(Expression> NavigationAction); + /// + HashSet GetAffected(Expression> navigationAction); } /// - /// Implementation of IAffectedRelationships{TDependentResource} + /// Implementation of IAffectedRelationships{TRightResource} /// - /// It is practically a ReadOnlyDictionary{RelationshipAttribute, HashSet{TDependentResource}} dictionary - /// with the two helper methods defined on IAffectedRelationships{TDependentResource}. + /// It is practically a ReadOnlyDictionary{RelationshipAttribute, HashSet{TRightResource}} dictionary + /// with the two helper methods defined on IAffectedRelationships{TRightResource}. /// public class RelationshipsDictionary : Dictionary>, diff --git a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs index f2ca5ee138..f8e4dad3ac 100644 --- a/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/ResourceHookExecutor.cs @@ -5,8 +5,8 @@ using System.Reflection; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; -using PrincipalType = System.Type; -using DependentType = System.Type; +using LeftType = System.Type; +using RightType = System.Type; using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Serialization; @@ -41,7 +41,7 @@ public virtual void BeforeRead(ResourcePipeline pipeline, string stri { var hookContainer = _executorHelper.GetResourceHookContainer(ResourceHook.BeforeRead); hookContainer?.BeforeRead(pipeline, false, stringId); - var calledContainers = new List() { typeof(TResource) }; + var calledContainers = new List() { typeof(TResource) }; foreach (var chain in _includeService.Get()) RecursiveBeforeRead(chain, pipeline, calledContainers); } @@ -53,7 +53,7 @@ public virtual IEnumerable BeforeUpdate(IEnumerable p.Attribute).ToArray(); var dbValues = LoadDbValues(typeof(TResource), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeUpdate, relationships); - var diff = new DiffableEntityHashSet(node.UniqueEntities, dbValues, node.PrincipalsToNextLayer(), _targetedFields); + var diff = new DiffableEntityHashSet(node.UniqueEntities, dbValues, node.LeftsToNextLayer(), _targetedFields); IEnumerable updated = container.BeforeUpdate(diff, pipeline); node.UpdateUnique(updated); node.Reassign(entities); @@ -68,7 +68,7 @@ public virtual IEnumerable BeforeCreate(IEnumerable((HashSet)node.UniqueEntities, node.PrincipalsToNextLayer()); + var affected = new EntityHashSet((HashSet)node.UniqueEntities, node.LeftsToNextLayer()); IEnumerable updated = container.BeforeCreate(affected, pipeline); node.UpdateUnique(updated); node.Reassign(entities); @@ -84,7 +84,7 @@ public virtual IEnumerable BeforeDelete(IEnumerable p.Attribute).ToArray(); var targetEntities = LoadDbValues(typeof(TResource), (IEnumerable)node.UniqueEntities, ResourceHook.BeforeDelete, relationships) ?? node.UniqueEntities; - var affected = new EntityHashSet(targetEntities, node.PrincipalsToNextLayer()); + var affected = new EntityHashSet(targetEntities, node.LeftsToNextLayer()); IEnumerable updated = container.BeforeDelete(affected, pipeline); node.UpdateUnique(updated); @@ -95,11 +95,11 @@ public virtual IEnumerable BeforeDelete(IEnumerable - void RecursiveBeforeRead(List relationshipChain, ResourcePipeline pipeline, List calledContainers) + void RecursiveBeforeRead(List relationshipChain, ResourcePipeline pipeline, List calledContainers) { var relationship = relationshipChain.First(); if (!calledContainers.Contains(relationship.RightType)) @@ -241,9 +241,9 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) { foreach (INode node in layer) { - var nestedHookcontainer = _executorHelper.GetResourceHookContainer(node.EntityType, ResourceHook.BeforeUpdateRelationship); + var nestedHookcontainer = _executorHelper.GetResourceHookContainer(node.ResourceType, ResourceHook.BeforeUpdateRelationship); IEnumerable uniqueEntities = node.UniqueEntities; - DependentType entityType = node.EntityType; + RightType entityType = node.ResourceType; Dictionary currenEntitiesGrouped; Dictionary currentEntitiesGroupedInverse; @@ -262,7 +262,7 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) /// we want want inverse relationship attribute: /// we now have the one pointing from article -> person, ] /// but we require the the one that points from person -> article - currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetDependentEntities(); + currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetRightEntities(); currentEntitiesGroupedInverse = ReplaceKeysWithInverseRelationships(currenEntitiesGrouped); var resourcesByRelationship = CreateRelationshipHelper(entityType, currentEntitiesGroupedInverse, dbValues); @@ -281,22 +281,22 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) /// To fire a hook for owner_old, we need to first get a reference to it. /// For this, we need to query the database for the HasOneAttribute:owner /// relationship of article1, which is referred to as the - /// principal side of the HasOneAttribute:owner relationship. - var principalEntities = node.RelationshipsFromPreviousLayer.GetPrincipalEntities(); - if (principalEntities.Any()) + /// left side of the HasOneAttribute:owner relationship. + var leftEntities = node.RelationshipsFromPreviousLayer.GetLeftEntities(); + if (leftEntities.Any()) { /// owner_old is loaded, which is an "implicitly affected entity" - FireForAffectedImplicits(entityType, principalEntities, pipeline, uniqueEntities); + FireForAffectedImplicits(entityType, leftEntities, pipeline, uniqueEntities); } } /// Fire the BeforeImplicitUpdateRelationship hook for article2 /// For this, we need to query the database for the current owner /// relationship value of owner_new. - currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetDependentEntities(); + currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetRightEntities(); if (currenEntitiesGrouped.Any()) { - /// dependentEntities is grouped by relationships from previous + /// rightEntities is grouped by relationships from previous /// layer, ie { HasOneAttribute:owner => owner_new }. But /// to load article2 onto owner_new, we need to have the /// RelationshipAttribute from owner to article, which is the @@ -305,9 +305,9 @@ void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) /// Note that currently in the JADNC implementation of hooks, /// the root layer is ALWAYS homogenous, so we safely assume /// that for every relationship to the previous layer, the - /// principal type is the same. - PrincipalType principalEntityType = currenEntitiesGrouped.First().Key.LeftType; - FireForAffectedImplicits(principalEntityType, currentEntitiesGroupedInverse, pipeline); + /// left type is the same. + LeftType leftType = currenEntitiesGrouped.First().Key.LeftType; + FireForAffectedImplicits(leftType, currentEntitiesGroupedInverse, pipeline); } } } @@ -389,7 +389,7 @@ object ThrowJsonApiExceptionOnError(Func action) /// If are included, the values of the entries in need to be replaced with these values. /// /// The relationship helper. - IRelationshipsDictionary CreateRelationshipHelper(DependentType entityType, Dictionary prevLayerRelationships, IEnumerable dbValues = null) + IRelationshipsDictionary CreateRelationshipHelper(RightType entityType, Dictionary prevLayerRelationships, IEnumerable dbValues = null) { if (dbValues != null) prevLayerRelationships = ReplaceWithDbValues(prevLayerRelationships, dbValues.Cast()); return (IRelationshipsDictionary)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipsDictionary<>), entityType, true, prevLayerRelationships); @@ -442,12 +442,12 @@ IEnumerable LoadDbValues(Type entityType, IEnumerable uniqueEntities, ResourceHo void FireAfterUpdateRelationship(IResourceHookContainer container, INode node, ResourcePipeline pipeline) { - Dictionary currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetDependentEntities(); + Dictionary currenEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetRightEntities(); /// the relationships attributes in currenEntitiesGrouped will be pointing from a /// resource in the previouslayer to a resource in the current (nested) layer. /// For the nested hook we need to replace these attributes with their inverse. /// See the FireNestedBeforeUpdateHooks method for a more detailed example. - var resourcesByRelationship = CreateRelationshipHelper(node.EntityType, ReplaceKeysWithInverseRelationships(currenEntitiesGrouped)); + var resourcesByRelationship = CreateRelationshipHelper(node.ResourceType, ReplaceKeysWithInverseRelationships(currenEntitiesGrouped)); CallHook(container, ResourceHook.AfterUpdateRelationship, new object[] { resourcesByRelationship, pipeline }); } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs index ddebdbf8ee..00fec56681 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/ChildNode.cs @@ -4,7 +4,7 @@ using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; -using DependentType = System.Type; +using RightType = System.Type; namespace JsonApiDotNetCore.Hooks { @@ -16,7 +16,7 @@ internal class ChildNode : INode where TResource : class, IIdentifiab { private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); /// - public DependentType EntityType { get; private set; } + public RightType ResourceType { get; private set; } /// public RelationshipProxy[] RelationshipsToNextLayer { get; set; } /// @@ -24,7 +24,7 @@ public IEnumerable UniqueEntities { get { - return new HashSet(_relationshipsFromPreviousLayer.SelectMany(rfpl => rfpl.DependentEntities)); + return new HashSet(_relationshipsFromPreviousLayer.SelectMany(rfpl => rfpl.RightEntities)); } } @@ -41,7 +41,7 @@ public IRelationshipsFromPreviousLayer RelationshipsFromPreviousLayer public ChildNode(RelationshipProxy[] nextLayerRelationships, RelationshipsFromPreviousLayer prevLayerRelationships) { - EntityType = typeof(TResource); + ResourceType = typeof(TResource); RelationshipsToNextLayer = nextLayerRelationships; _relationshipsFromPreviousLayer = prevLayerRelationships; } @@ -52,7 +52,7 @@ public void UpdateUnique(IEnumerable updated) List casted = updated.Cast().ToList(); foreach (var rpfl in _relationshipsFromPreviousLayer) { - rpfl.DependentEntities = new HashSet(rpfl.DependentEntities.Intersect(casted, _comparer).Cast()); + rpfl.RightEntities = new HashSet(rpfl.RightEntities.Intersect(casted, _comparer).Cast()); } } @@ -66,22 +66,22 @@ public void Reassign(IEnumerable updated = null) foreach (var rfpl in _relationshipsFromPreviousLayer) { var proxy = rfpl.Proxy; - var principalEntities = rfpl.PrincipalEntities; + var leftEntities = rfpl.LeftEntities; - foreach (IIdentifiable principal in principalEntities) + foreach (IIdentifiable left in leftEntities) { - var currentValue = proxy.GetValue(principal); + var currentValue = proxy.GetValue(left); if (currentValue is IEnumerable relationshipCollection) { - var newValue = relationshipCollection.Intersect(unique, _comparer).Cast(proxy.DependentType); - proxy.SetValue(principal, newValue); + var newValue = relationshipCollection.Intersect(unique, _comparer).Cast(proxy.RightType); + proxy.SetValue(left, newValue); } else if (currentValue is IIdentifiable relationshipSingle) { if (!unique.Intersect(new HashSet() { relationshipSingle }, _comparer).Any()) { - proxy.SetValue(principal, null); + proxy.SetValue(left, null); } } } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/IEntityNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/IEntityNode.cs index 88c664a744..2519449325 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/IEntityNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/IEntityNode.cs @@ -1,5 +1,5 @@ using System.Collections; -using DependentType = System.Type; +using RightType = System.Type; namespace JsonApiDotNetCore.Hooks { @@ -11,7 +11,7 @@ internal interface INode /// /// Each node representes the entities of a given type throughout a particular layer. /// - DependentType EntityType { get; } + RightType ResourceType { get; } /// /// The unique set of entities in this node. Note that these are all of the same type. /// diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipGroup.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipGroup.cs index 1ed52419c7..6f34f1fb7b 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipGroup.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipGroup.cs @@ -6,19 +6,19 @@ namespace JsonApiDotNetCore.Hooks internal interface IRelationshipGroup { RelationshipProxy Proxy { get; } - HashSet PrincipalEntities { get; } + HashSet LeftEntities { get; } } - internal class RelationshipGroup : IRelationshipGroup where TDependent : class, IIdentifiable + internal class RelationshipGroup : IRelationshipGroup where TRight : class, IIdentifiable { public RelationshipProxy Proxy { get; } - public HashSet PrincipalEntities { get; } - public HashSet DependentEntities { get; internal set; } - public RelationshipGroup(RelationshipProxy proxy, HashSet principalEntities, HashSet dependentEntities) + public HashSet LeftEntities { get; } + public HashSet RightEntities { get; internal set; } + public RelationshipGroup(RelationshipProxy proxy, HashSet leftEntities, HashSet rightEntities) { Proxy = proxy; - PrincipalEntities = principalEntities; - DependentEntities = dependentEntities; + LeftEntities = leftEntities; + RightEntities = rightEntities; } } } \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs index 6cb24fc58b..427dbaf2eb 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipProxy.cs @@ -16,8 +16,6 @@ namespace JsonApiDotNetCore.Hooks /// (eg ArticleTags) is identifiable (in which case we will traverse through /// it and fire hooks for it, if defined) or not (in which case we skip /// ArticleTags and go directly to Tags. - /// - /// TODO: We can consider moving fields like DependentType and PrincipalType /// public class RelationshipProxy { @@ -30,20 +28,20 @@ public class RelationshipProxy /// For HasManyThrough it is either the ThroughProperty (when the jointable is /// Identifiable) or it is the righthand side (when the jointable is not identifiable) /// - public Type DependentType { get; private set; } - public Type PrincipalType { get { return Attribute.LeftType; } } + public Type RightType { get; private set; } + public Type LeftType { get { return Attribute.LeftType; } } public bool IsContextRelation { get; private set; } public RelationshipAttribute Attribute { get; set; } public RelationshipProxy(RelationshipAttribute attr, Type relatedType, bool isContextRelation) { - DependentType = relatedType; + RightType = relatedType; Attribute = attr; IsContextRelation = isContextRelation; if (attr is HasManyThroughAttribute throughAttr) { _isHasManyThrough = true; - _skipJoinTable |= DependentType != throughAttr.ThroughType; + _skipJoinTable |= RightType != throughAttr.ThroughType; } } @@ -63,22 +61,17 @@ public object GetValue(IIdentifiable entity) { return throughAttr.ThroughProperty.GetValue(entity); } - else - { - var collection = new List(); - var joinEntities = (IList)throughAttr.ThroughProperty.GetValue(entity); - if (joinEntities == null) return null; - - foreach (var joinEntity in joinEntities) - { - var rightEntity = (IIdentifiable)throughAttr.RightProperty.GetValue(joinEntity); - if (rightEntity == null) continue; - collection.Add(rightEntity); - } - return collection; + var collection = new List(); + var joinEntities = (IList)throughAttr.ThroughProperty.GetValue(entity); + if (joinEntities == null) return null; + foreach (var joinEntity in joinEntities) + { + var rightEntity = (IIdentifiable)throughAttr.RightProperty.GetValue(joinEntity); + if (rightEntity == null) continue; + collection.Add(rightEntity); } - + return collection; } return Attribute.GetValue(entity); } @@ -97,27 +90,24 @@ public void SetValue(IIdentifiable entity, object value) if (!_skipJoinTable) { var list = (IEnumerable)value; - ((HasManyThroughAttribute)Attribute).ThroughProperty.SetValue(entity, list.Cast(DependentType)); + ((HasManyThroughAttribute)Attribute).ThroughProperty.SetValue(entity, list.Cast(RightType)); return; } - else + var throughAttr = (HasManyThroughAttribute)Attribute; + var joinEntities = (IEnumerable)throughAttr.ThroughProperty.GetValue(entity); + + var filteredList = new List(); + var rightEntities = ((IEnumerable)value).Cast(RightType); + foreach (var je in joinEntities) { - var throughAttr = (HasManyThroughAttribute)Attribute; - var joinEntities = (IEnumerable)throughAttr.ThroughProperty.GetValue(entity); - var filteredList = new List(); - var rightEntities = ((IEnumerable)value).Cast(DependentType); - foreach (var je in joinEntities) + if (((IList)rightEntities).Contains(throughAttr.RightProperty.GetValue(je))) { - - if (((IList)rightEntities).Contains(throughAttr.RightProperty.GetValue(je))) - { - filteredList.Add(je); - } + filteredList.Add(je); } - throughAttr.ThroughProperty.SetValue(entity, filteredList.Cast(throughAttr.ThroughType)); - return; } + throughAttr.ThroughProperty.SetValue(entity, filteredList.Cast(throughAttr.ThroughType)); + return; } Attribute.SetValue(entity, value); } diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs index cb4185d1fd..2068fa8652 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RelationshipsFromPreviousLayer.cs @@ -13,37 +13,39 @@ internal interface IRelationshipsFromPreviousLayer /// /// Grouped by relationship to the previous layer, gets all the entities of the current layer /// - /// The dependent entities. - Dictionary GetDependentEntities(); + /// The right side entities. + Dictionary GetRightEntities(); /// /// Grouped by relationship to the previous layer, gets all the entities of the previous layer /// - /// The dependent entities. - Dictionary GetPrincipalEntities(); + /// The right side entities. + Dictionary GetLeftEntities(); } - internal class RelationshipsFromPreviousLayer : IRelationshipsFromPreviousLayer, IEnumerable> where TDependent : class, IIdentifiable + internal class RelationshipsFromPreviousLayer : IRelationshipsFromPreviousLayer, IEnumerable> where TRightResource : class, IIdentifiable { - readonly IEnumerable> _collection; + readonly IEnumerable> _collection; - public RelationshipsFromPreviousLayer(IEnumerable> collection) + public RelationshipsFromPreviousLayer(IEnumerable> collection) { _collection = collection; } - public Dictionary GetDependentEntities() + /// + public Dictionary GetRightEntities() { - return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.DependentEntities); + return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.RightEntities); } - public Dictionary GetPrincipalEntities() + /// + public Dictionary GetLeftEntities() { - return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.PrincipalEntities); + return _collection.ToDictionary(rg => rg.Proxy.Attribute, rg => (IEnumerable)rg.LeftEntities); } - public IEnumerator> GetEnumerator() + public IEnumerator> GetEnumerator() { - return _collection.Cast>().GetEnumerator(); + return _collection.Cast>().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs b/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs index 47f7a8ce6b..23fc32c8bb 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/RootNode.cs @@ -7,7 +7,6 @@ namespace JsonApiDotNetCore.Hooks { - /// /// The root node class of the breadth-first-traversal of entity data structures /// as performed by the @@ -17,21 +16,21 @@ internal class RootNode : INode where TResource : class, IIdentifiabl private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); private readonly RelationshipProxy[] _allRelationshipsToNextLayer; private HashSet _uniqueEntities; - public Type EntityType { get; internal set; } + public Type ResourceType { get; internal set; } public IEnumerable UniqueEntities { get { return _uniqueEntities; } } public RelationshipProxy[] RelationshipsToNextLayer { get; } - public Dictionary> PrincipalsToNextLayerByRelationships() + public Dictionary> LeftsToNextLayerByRelationships() { return _allRelationshipsToNextLayer - .GroupBy(proxy => proxy.DependentType) + .GroupBy(proxy => proxy.RightType) .ToDictionary(gdc => gdc.Key, gdc => gdc.ToDictionary(p => p.Attribute, p => UniqueEntities)); } /// /// The current layer entities grouped by affected relationship to the next layer /// - public Dictionary PrincipalsToNextLayer() + public Dictionary LeftsToNextLayer() { return RelationshipsToNextLayer.ToDictionary(p => p.Attribute, p => UniqueEntities); } @@ -43,7 +42,7 @@ public Dictionary PrincipalsToNextLayer() public RootNode(IEnumerable uniqueEntities, RelationshipProxy[] poplatedRelationships, RelationshipProxy[] allRelationships) { - EntityType = typeof(TResource); + ResourceType = typeof(TResource); _uniqueEntities = new HashSet(uniqueEntities); RelationshipsToNextLayer = poplatedRelationships; _allRelationshipsToNextLayer = allRelationships; diff --git a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs index 0a8c503a40..1b8fb9c935 100644 --- a/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Traversal/TraversalHelper.cs @@ -6,11 +6,10 @@ using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; -using JsonApiDotNetCore.Managers.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Serialization; -using DependentType = System.Type; -using PrincipalType = System.Type; +using RightType = System.Type; +using LeftType = System.Type; namespace JsonApiDotNetCore.Hooks { @@ -31,12 +30,12 @@ internal class TraversalHelper : ITraversalHelper /// Keeps track of which entities has already been traversed through, to prevent /// infinite loops in eg cyclic data structures. /// - private Dictionary> _processedEntities; + private Dictionary> _processedEntities; /// /// A mapper from to . /// See the latter for more details. /// - private readonly Dictionary RelationshipProxies = new Dictionary(); + private readonly Dictionary _relationshipProxies = new Dictionary(); public TraversalHelper( IResourceGraph resourceGraph, ITargetedFields targetedFields) @@ -55,11 +54,11 @@ public TraversalHelper( /// The 1st type parameter. public RootNode CreateRootNode(IEnumerable rootEntities) where TResource : class, IIdentifiable { - _processedEntities = new Dictionary>(); + _processedEntities = new Dictionary>(); RegisterRelationshipProxies(typeof(TResource)); var uniqueEntities = ProcessEntities(rootEntities); var populatedRelationshipsToNextLayer = GetPopulatedRelationships(typeof(TResource), uniqueEntities.Cast()); - var allRelationshipsFromType = RelationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.PrincipalType == typeof(TResource)).ToArray(); + var allRelationshipsFromType = _relationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.LeftType == typeof(TResource)).ToArray(); return new RootNode(uniqueEntities, populatedRelationshipsToNextLayer, allRelationshipsFromType); } @@ -82,15 +81,15 @@ public NodeLayer CreateNextLayer(IEnumerable nodes) { /// first extract entities by parsing populated relationships in the entities /// of previous layer - (var principals, var dependents) = ExtractEntities(nodes); + (var lefts, var rights) = ExtractEntities(nodes); /// group them conveniently so we can make ChildNodes of them: - /// there might be several relationship attributes in dependents dictionary - /// that point to the same dependent type. - var principalsGrouped = GroupByDependentTypeOfRelationship(principals); + /// there might be several relationship attributes in rights dictionary + /// that point to the same right type. + var leftsGrouped = GroupByRightTypeOfRelationship(lefts); /// convert the groups into child nodes - var nextNodes = principalsGrouped.Select(entry => + var nextNodes = leftsGrouped.Select(entry => { var nextNodeType = entry.Key; RegisterRelationshipProxies(nextNodeType); @@ -99,8 +98,8 @@ public NodeLayer CreateNextLayer(IEnumerable nodes) var relationshipsToPreviousLayer = entry.Value.Select(grouped => { var proxy = grouped.Key; - populatedRelationships.AddRange(GetPopulatedRelationships(nextNodeType, dependents[proxy])); - return CreateRelationshipGroupInstance(nextNodeType, proxy, grouped.Value, dependents[proxy]); + populatedRelationships.AddRange(GetPopulatedRelationships(nextNodeType, rights[proxy])); + return CreateRelationshipGroupInstance(nextNodeType, proxy, grouped.Value, rights[proxy]); }).ToList(); return CreateNodeInstance(nextNodeType, populatedRelationships.ToArray(), relationshipsToPreviousLayer); @@ -112,72 +111,72 @@ public NodeLayer CreateNextLayer(IEnumerable nodes) /// /// iterates throug the dictinary and groups the values - /// by matching dependent type of the keys (which are relationshipattributes) + /// by matching right type of the keys (which are relationshipattributes) /// - Dictionary>>> GroupByDependentTypeOfRelationship(Dictionary> relationships) + Dictionary>>> GroupByRightTypeOfRelationship(Dictionary> relationships) { - return relationships.GroupBy(kvp => kvp.Key.DependentType).ToDictionary(gdc => gdc.Key, gdc => gdc.ToList()); + return relationships.GroupBy(kvp => kvp.Key.RightType).ToDictionary(gdc => gdc.Key, gdc => gdc.ToList()); } /// /// Extracts the entities for the current layer by going through all populated relationships - /// of the (principal entities of the previous layer. + /// of the (left entities of the previous layer. /// - (Dictionary>, Dictionary>) ExtractEntities(IEnumerable principalNodes) + (Dictionary>, Dictionary>) ExtractEntities(IEnumerable leftNodes) { - var principalsEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => prevLayerEntities - var dependentsEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => currentLayerEntities + var leftEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => prevLayerEntities + var rightEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => currentLayerEntities - foreach (var node in principalNodes) + foreach (var node in leftNodes) { - var principalEntities = node.UniqueEntities; + var leftEntities = node.UniqueEntities; var relationships = node.RelationshipsToNextLayer; - foreach (IIdentifiable principalEntity in principalEntities) + foreach (IIdentifiable leftEntity in leftEntities) { foreach (var proxy in relationships) { - var relationshipValue = proxy.GetValue(principalEntity); + var relationshipValue = proxy.GetValue(leftEntity); // skip this relationship if it's not populated if (!proxy.IsContextRelation && relationshipValue == null) continue; - if (!(relationshipValue is IEnumerable dependentEntities)) + if (!(relationshipValue is IEnumerable rightEntities)) { // in the case of a to-one relationship, the assigned value // will not be a list. We therefore first wrap it in a list. - var list = TypeHelper.CreateListFor(proxy.DependentType); + var list = TypeHelper.CreateListFor(proxy.RightType); if (relationshipValue != null) list.Add(relationshipValue); - dependentEntities = list; + rightEntities = list; } - var uniqueDependentEntities = UniqueInTree(dependentEntities.Cast(), proxy.DependentType); - if (proxy.IsContextRelation || uniqueDependentEntities.Any()) + var uniqueRightEntities = UniqueInTree(rightEntities.Cast(), proxy.RightType); + if (proxy.IsContextRelation || uniqueRightEntities.Any()) { - AddToRelationshipGroup(dependentsEntitiesGrouped, proxy, uniqueDependentEntities); - AddToRelationshipGroup(principalsEntitiesGrouped, proxy, new IIdentifiable[] { principalEntity }); + AddToRelationshipGroup(rightEntitiesGrouped, proxy, uniqueRightEntities); + AddToRelationshipGroup(leftEntitiesGrouped, proxy, new IIdentifiable[] { leftEntity }); } } } } var processEntitiesMethod = GetType().GetMethod(nameof(ProcessEntities), BindingFlags.NonPublic | BindingFlags.Instance); - foreach (var kvp in dependentsEntitiesGrouped) + foreach (var kvp in rightEntitiesGrouped) { - var type = kvp.Key.DependentType; + var type = kvp.Key.RightType; var list = kvp.Value.Cast(type); processEntitiesMethod.MakeGenericMethod(type).Invoke(this, new object[] { list }); } - return (principalsEntitiesGrouped, dependentsEntitiesGrouped); + return (leftEntitiesGrouped, rightEntitiesGrouped); } /// /// Get all populated relationships known in the current tree traversal from a - /// principal type to any dependent type + /// left type to any right type /// /// The relationships. - RelationshipProxy[] GetPopulatedRelationships(PrincipalType principalType, IEnumerable principals) + RelationshipProxy[] GetPopulatedRelationships(LeftType leftType, IEnumerable lefts) { - var relationshipsFromPrincipalToDependent = RelationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.PrincipalType == principalType); - return relationshipsFromPrincipalToDependent.Where(proxy => proxy.IsContextRelation || principals.Any(p => proxy.GetValue(p) != null)).ToArray(); + var relationshipsFromLeftToRight = _relationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.LeftType == leftType); + return relationshipsFromLeftToRight.Where(proxy => proxy.IsContextRelation || lefts.Any(p => proxy.GetValue(p) != null)).ToArray(); } /// @@ -199,24 +198,23 @@ HashSet ProcessEntities(IEnumerable incomingEnt /// other models in the resource resourceGraphs by constructing RelationshipProxies . /// /// The type to parse - void RegisterRelationshipProxies(DependentType type) + void RegisterRelationshipProxies(RightType type) { foreach (RelationshipAttribute attr in _resourceGraph.GetRelationships(type)) { if (!attr.CanInclude) continue; - if (!RelationshipProxies.TryGetValue(attr, out RelationshipProxy proxies)) + if (!_relationshipProxies.TryGetValue(attr, out RelationshipProxy proxies)) { - DependentType dependentType = GetDependentTypeFromRelationship(attr); + RightType rightType = GetRightTypeFromRelationship(attr); bool isContextRelation = false; var relationshipsToUpdate = _targetedFields.Relationships; if (relationshipsToUpdate != null) isContextRelation = relationshipsToUpdate.Contains(attr); - var proxy = new RelationshipProxy(attr, dependentType, isContextRelation); - RelationshipProxies[attr] = proxy; + var proxy = new RelationshipProxy(attr, rightType, isContextRelation); + _relationshipProxies[attr] = proxy; } } } - /// /// Registers the processed entities in the dictionary grouped by type /// @@ -264,7 +262,7 @@ HashSet UniqueInTree(IEnumerable entities, Type /// /// The target type for traversal /// Relationship attribute - DependentType GetDependentTypeFromRelationship(RelationshipAttribute attr) + RightType GetRightTypeFromRelationship(RelationshipAttribute attr) { if (attr is HasManyThroughAttribute throughAttr && throughAttr.ThroughType.Inherits(typeof(IIdentifiable))) { @@ -286,30 +284,30 @@ void AddToRelationshipGroup(Dictionary> t /// /// Reflective helper method to create an instance of ; /// - INode CreateNodeInstance(DependentType nodeType, RelationshipProxy[] relationshipsToNext, IEnumerable relationshipsFromPrev) + INode CreateNodeInstance(RightType nodeType, RelationshipProxy[] relationshipsToNext, IEnumerable relationshipsFromPrev) { IRelationshipsFromPreviousLayer prev = CreateRelationshipsFromInstance(nodeType, relationshipsFromPrev); return (INode)TypeHelper.CreateInstanceOfOpenType(typeof(ChildNode<>), nodeType, new object[] { relationshipsToNext, prev }); } /// - /// Reflective helper method to create an instance of ; + /// Reflective helper method to create an instance of ; /// - IRelationshipsFromPreviousLayer CreateRelationshipsFromInstance(DependentType nodeType, IEnumerable relationshipsFromPrev) + IRelationshipsFromPreviousLayer CreateRelationshipsFromInstance(RightType nodeType, IEnumerable relationshipsFromPrev) { var casted = relationshipsFromPrev.Cast(relationshipsFromPrev.First().GetType()); return (IRelationshipsFromPreviousLayer)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipsFromPreviousLayer<>), nodeType, new object[] { casted }); } /// - /// Reflective helper method to create an instance of ; + /// Reflective helper method to create an instance of ; /// - IRelationshipGroup CreateRelationshipGroupInstance(Type thisLayerType, RelationshipProxy proxy, List principalEntities, List dependentEntities) + IRelationshipGroup CreateRelationshipGroupInstance(Type thisLayerType, RelationshipProxy proxy, List leftEntities, List rightEntities) { - var dependentEntitiesHashed = TypeHelper.CreateInstanceOfOpenType(typeof(HashSet<>), thisLayerType, dependentEntities.Cast(thisLayerType)); + var rightEntitiesHashed = TypeHelper.CreateInstanceOfOpenType(typeof(HashSet<>), thisLayerType, rightEntities.Cast(thisLayerType)); return (IRelationshipGroup)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipGroup<>), thisLayerType, - new object[] { proxy, new HashSet(principalEntities), dependentEntitiesHashed }); + new object[] { proxy, new HashSet(leftEntities), rightEntitiesHashed }); } } diff --git a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs index 885d65bd09..c9cfcd2b5d 100644 --- a/src/JsonApiDotNetCore/Internal/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Internal/ResourceGraph.cs @@ -66,7 +66,7 @@ public List GetRelationships(Type type) public RelationshipAttribute GetInverse(RelationshipAttribute relationship) { if (relationship.InverseNavigation == null) return null; - return GetResourceContext(relationship.DependentType) + return GetResourceContext(relationship.RightType) .Relationships .SingleOrDefault(r => r.InternalRelationshipName == relationship.InverseNavigation); } diff --git a/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs index 30f3271a17..0e92e5ea84 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs @@ -26,8 +26,6 @@ protected RelationshipAttribute(string publicName, Link relationshipLinks, bool /// /// The related entity type. This does not necessarily match the navigation property type. /// In the case of a HasMany relationship, this value will be the generic argument type. - /// - /// The technical language as used in EF Core is used here (dependent vs principal). /// /// /// @@ -38,7 +36,7 @@ protected RelationshipAttribute(string publicName, Link relationshipLinks, bool public Type RightType { get; internal set; } /// - /// The parent entity type. The technical language as used in EF Core is used here (dependent vs principal). + /// The parent entity type. This is the type of the class in which this attribute was used. /// public Type LeftType { get; internal set; } @@ -70,12 +68,7 @@ public override bool Equals(object obj) } bool equalRelationshipName = PublicRelationshipName.Equals(attr.PublicRelationshipName); - bool equalPrincipalType = true; - if (LeftType != null) - { - equalPrincipalType = LeftType.Equals(attr.LeftType); - } - return IsHasMany == attr.IsHasMany && equalRelationshipName && equalPrincipalType; + return IsHasMany == attr.IsHasMany && equalRelationshipName; } /// @@ -91,6 +84,5 @@ public virtual bool Is(string publicRelationshipName) /// In all cases except the HasManyThrough relationships, this will just be the . /// public virtual string RelationshipPath => InternalRelationshipName; - } } diff --git a/wiki/v4/content/using System; b/wiki/v4/content/using System; new file mode 100644 index 0000000000..58eb324083 --- /dev/null +++ b/wiki/v4/content/using System; @@ -0,0 +1,344 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using JsonApiDotNetCore.Extensions; +using JsonApiDotNetCore.Internal; +using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Serialization; +using RightType = System.Type; +using LeftType = System.Type; + +namespace JsonApiDotNetCore.Hooks +{ + /// + /// A helper class used by the to traverse through + /// entity data structures (trees), allowing for a breadth-first-traversal + /// + /// It creates nodes for each layer. + /// Typically, the first layer is homogeneous (all entities have the same type), + /// and further nodes can be mixed. + /// + internal class TraversalHelper : ITraversalHelper + { + private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); + private readonly IResourceGraph _resourceGraph; + private readonly ITargetedFields _targetedFields; + /// + /// Keeps track of which entities has already been traversed through, to prevent + /// infinite loops in eg cyclic data structures. + /// + private Dictionary> _processedEntities; + /// + /// A mapper from to . + /// See the latter for more details. + /// + private readonly Dictionary _relationshipProxies = new Dictionary(); + public TraversalHelper( + IResourceGraph resourceGraph, + ITargetedFields targetedFields) + { + _targetedFields = targetedFields; + _resourceGraph = resourceGraph; + } + + /// + /// Creates a root node for breadth-first-traversal. Note that typically, in + /// JADNC, the root layer will be homogeneous. Also, because it is the first layer, + /// there can be no relationships to previous layers, only to next layers. + /// + /// The root node. + /// Root entities. + /// The 1st type parameter. + public RootNode CreateRootNode(IEnumerable rootEntities) where TResource : class, IIdentifiable + { + _processedEntities = new Dictionary>(); + RegisterRelationshipProxies(typeof(TResource)); + var uniqueEntities = ProcessEntities(rootEntities); + var populatedRelationshipsToNextLayer = GetPopulatedRelationships(typeof(TResource), uniqueEntities.Cast()); + var allRelationshipsFromType = _relationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.LeftType == typeof(TResource)).ToArray(); + return new RootNode(uniqueEntities, populatedRelationshipsToNextLayer, allRelationshipsFromType); + } + + /// + /// Create the first layer after the root layer (based on the root node) + /// + /// The next layer. + /// Root node. + public NodeLayer CreateNextLayer(INode rootNode) + { + return CreateNextLayer(new INode[] { rootNode }); + } + + /// + /// Create a next layer from any previous layer + /// + /// The next layer. + /// Nodes. + public NodeLayer CreateNextLayer(IEnumerable nodes) + { + /// first extract entities by parsing populated relationships in the entities + /// of previous layer + (var lefts, var rights) = ExtractEntities(nodes); + + /// group them conveniently so we can make ChildNodes of them: + /// there might be several relationship attributes in rights dictionary + /// that point to the same right type. + var leftsGrouped = GroupByRightTypeOfRelationship(lefts); + + /// convert the groups into child nodes + var nextNodes = leftsGrouped.Select(entry => + { + var nextNodeType = entry.Key; + RegisterRelationshipProxies(nextNodeType); + + var populatedRelationships = new List(); + var relationshipsToPreviousLayer = entry.Value.Select(grouped => + { + var proxy = grouped.Key; + populatedRelationships.AddRange(GetPopulatedRelationships(nextNodeType, rights[proxy])); + return CreateRelationshipGroupInstance(nextNodeType, proxy, grouped.Value, rights[proxy]); + }).ToList(); + + return CreateNodeInstance(nextNodeType, populatedRelationships.ToArray(), relationshipsToPreviousLayer); + }).ToList(); + + /// wrap the child nodes in a EntityChildLayer + return new NodeLayer(nextNodes); + } + + /// + /// iterates throug the dictinary and groups the values + /// by matching right type of the keys (which are relationshipattributes) + /// + Dictionary>>> GroupByRightTypeOfRelationship(Dictionary> relationships) + { + return relationships.GroupBy(kvp => kvp.Key.RightType).ToDictionary(gdc => gdc.Key, gdc => gdc.ToList()); + } + + /// + /// Extracts the entities for the current layer by going through all populated relationships + /// of the (left entities of the previous layer. + /// + (Dictionary>, Dictionary>) ExtractEntities(IEnumerable leftNodes) + { + var leftEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => prevLayerEntities + var rightEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => currentLayerEntities + + foreach (var node in leftNodes) + { + var leftEntities = node.UniqueEntities; + var relationships = node.RelationshipsToNextLayer; + foreach (IIdentifiable leftEntity in leftEntities) + { + foreach (var proxy in relationships) + { + var relationshipValue = proxy.GetValue(leftEntity); + // skip this relationship if it's not populated + if (!proxy.IsContextRelation && relationshipValue == null) continue; + if (!(relationshipValue is IEnumerable rightEntities)) + { + // in the case of a to-one relationship, the assigned value + // will not be a list. We therefore first wrap it in a list. + var list = TypeHelper.CreateListFor(proxy.RightType); + if (relationshipValue != null) list.Add(relationshipValue); + rightEntities = list; + } + + var uniqueRightEntities = UniqueInTree(rightEntities.Cast(), proxy.RightType); + if (proxy.IsContextRelation || uniqueRightEntities.Any()) + { + AddToRelationshipGroup(rightEntitiesGrouped, proxy, uniqueRightEntities); + AddToRelationshipGroup(leftEntitiesGrouped, proxy, new IIdentifiable[] { leftEntity }); + } + } + } + } + + var processEntitiesMethod = GetType().GetMethod(nameof(ProcessEntities), BindingFlags.NonPublic | BindingFlags.Instance); + foreach (var kvp in rightEntitiesGrouped) + { + var type = kvp.Key.RightType; + var list = kvp.Value.Cast(type); + processEntitiesMethod.MakeGenericMethod(type).Invoke(this, new object[] { list }); + } + + return (leftEntitiesGrouped, rightEntitiesGrouped); + } + + /// + /// Get all populated relationships known in the current tree traversal from a + /// left type to any right type + /// + /// The relationships. + RelationshipProxy[] GetPopulatedRelationships(LeftType leftType, IEnumerable lefts) + { + var relationshipsFromLeftToRight = _relationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.LeftType == leftType); + return relationshipsFromLeftToRight.Where(proxy => proxy.IsContextRelation || lefts.Any(p => proxy.GetValue(p) != null)).ToArray(); + } + + /// + /// Registers the entities as "seen" in the tree traversal, extracts any new s from it. + /// + /// The entities. + /// Incoming entities. + /// The 1st type parameter. + HashSet ProcessEntities(IEnumerable incomingEntities) where TResource : class, IIdentifiable + { + Type type = typeof(TResource); + var newEntities = UniqueInTree(incomingEntities, type); + RegisterProcessedEntities(newEntities, type); + return newEntities; + } + + /// + /// Parses all relationships from to + /// other models in the resource resourceGraphs by constructing RelationshipProxies . + /// + /// The type to parse + void RegisterRelationshipProxies(RightType type) + { + foreach (RelationshipAttribute attr in _resourceGraph.GetRelationships(type)) + { + if (!attr.CanInclude) continue; + if (!_relationshipProxies.TryGetValue(attr, out RelationshipProxy proxies)) + { + RightType rightType = GetRightTypeFromRelationship(attr); + bool isContextRelation = false; + var relationshipsToUpdate = _targetedFields.Relationships; + if (relationshipsToUpdate != null) isContextRelation = relationshipsToUpdate.Contains(attr); + var proxy = new RelationshipProxy(attr, rightType, isContextRelation); + _relationshipProxies[attr] = proxy; + } + } + } + + + /// + /// Registers the processed entities in the dictionary grouped by type + /// + /// Entities to register + /// Entity type. + void RegisterProcessedEntities(IEnumerable entities, Type entityType) + { + var processedEntities = GetProcessedEntities(entityType); + processedEntities.UnionWith(new HashSet(entities)); + } + + /// + /// Gets the processed entities for a given type, instantiates the collection if new. + /// + /// The processed entities. + /// Entity type. + HashSet GetProcessedEntities(Type entityType) + { + if (!_processedEntities.TryGetValue(entityType, out HashSet processedEntities)) + { + processedEntities = new HashSet(); + _processedEntities[entityType] = processedEntities; + } + return processedEntities; + } + + /// + /// Using the register of processed entities, determines the unique and new + /// entities with respect to previous iterations. + /// + /// The in tree. + /// Entities. + /// Entity type. + HashSet UniqueInTree(IEnumerable entities, Type entityType) where TResource : class, IIdentifiable + { + var newEntities = entities.Except(GetProcessedEntities(entityType), _comparer).Cast(); + return new HashSet(newEntities); + } + + /// + /// Gets the type from relationship attribute. If the attribute is + /// HasManyThrough, and the jointable entity is identifiable, then the target + /// type is the joinentity instead of the righthand side, because hooks might be + /// implemented for the jointable entity. + /// + /// The target type for traversal + /// Relationship attribute + RightType GetRightTypeFromRelationship(RelationshipAttribute attr) + { + if (attr is HasManyThroughAttribute throughAttr && throughAttr.ThroughType.Inherits(typeof(IIdentifiable))) + { + return throughAttr.ThroughType; + } + return attr.RightType; + } + + void AddToRelationshipGroup(Dictionary> target, RelationshipProxy proxy, IEnumerable newEntities) + { + if (!target.TryGetValue(proxy, out List entities)) + { + entities = new List(); + target[proxy] = entities; + } + entities.AddRange(newEntities); + } + + /// + /// Reflective helper method to create an instance of ; + /// + INode CreateNodeInstance(RightType nodeType, RelationshipProxy[] relationshipsToNext, IEnumerable relationshipsFromPrev) + { + IRelationshipsFromPreviousLayer prev = CreateRelationshipsFromInstance(nodeType, relationshipsFromPrev); + return (INode)TypeHelper.CreateInstanceOfOpenType(typeof(ChildNode<>), nodeType, new object[] { relationshipsToNext, prev }); + } + + /// + /// Reflective helper method to create an instance of ; + /// + IRelationshipsFromPreviousLayer CreateRelationshipsFromInstance(RightType nodeType, IEnumerable relationshipsFromPrev) + { + var casted = relationshipsFromPrev.Cast(relationshipsFromPrev.First().GetType()); + return (IRelationshipsFromPreviousLayer)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipsFromPreviousLayer<>), nodeType, new object[] { casted }); + } + + /// + /// Reflective helper method to create an instance of ; + /// + IRelationshipGroup CreateRelationshipGroupInstance(Type thisLayerType, RelationshipProxy proxy, List leftEntities, List rightEntities) + { + var rightEntitiesHashed = TypeHelper.CreateInstanceOfOpenType(typeof(HashSet<>), thisLayerType, rightEntities.Cast(thisLayerType)); + return (IRelationshipGroup)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipGroup<>), + thisLayerType, + new object[] { proxy, new HashSet(leftEntities), rightEntitiesHashed }); + } + } + + /// + /// A helper class that represents all entities in the current layer that + /// are being traversed for which hooks will be executed (see IResourceHookExecutor) + /// + internal class NodeLayer : IEnumerable + { + readonly List _collection; + + public bool AnyEntities() + { + return _collection.Any(n => n.UniqueEntities.Cast().Any()); + } + + public NodeLayer(List nodes) + { + _collection = nodes; + } + + public IEnumerator GetEnumerator() + { + return _collection.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} + From e509956f1aca5fef3b36c5cc07aec2c385f720e2 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 13:03:24 +0200 Subject: [PATCH 24/29] chore: remove redundant bindings throughout entire codebase --- .../Builders/IResourceGraphBuilder.cs | 8 +- .../Configuration/IJsonApiOptions.cs | 2 - .../Configuration/JsonApiOptions.cs | 1 - .../Controllers/JsonApiCmdController.cs | 1 - .../Data/IResourceWriteRepository.cs | 3 +- .../Extensions/IQueryableExtensions.cs | 1 - .../IServiceCollectionExtensions.cs | 2 - .../Formatters/JsonApiReader.cs | 1 - .../Graph/ServiceDiscoveryFacade.cs | 3 - .../Internal/DefaultRoutingConvention.cs | 2 - .../Exceptions/JsonApiRouteHandler.cs | 83 ------------------- .../Internal/Generics/GenericProcessor.cs | 3 - .../Internal/IdentifiableComparer.cs | 2 - .../Internal/Query/FilterQuery.cs | 4 - .../Models/Annotation/HasOneAttribute.cs | 1 - src/JsonApiDotNetCore/Models/IHasMeta.cs | 1 - .../Models/ResourceDefinition.cs | 2 - .../Contracts/ICurrentRequest.cs | 2 - .../Server/ResponseSerializerFactory.cs | 3 - 19 files changed, 5 insertions(+), 120 deletions(-) delete mode 100644 src/JsonApiDotNetCore/Internal/Exceptions/JsonApiRouteHandler.cs diff --git a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs index 6a3e7f2ae3..a9a74cf9a7 100644 --- a/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs @@ -1,5 +1,5 @@ using System; -using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; @@ -21,7 +21,7 @@ public interface IResourceGraphBuilder /// /// The pluralized name that should be exposed by the API. /// If nothing is specified, the configured name formatter will be used. - /// See . + /// See . /// IResourceGraphBuilder AddResource(string pluralizedTypeName = null) where TResource : class, IIdentifiable; @@ -34,7 +34,7 @@ public interface IResourceGraphBuilder /// /// The pluralized name that should be exposed by the API. /// If nothing is specified, the configured name formatter will be used. - /// See . + /// See . /// IResourceGraphBuilder AddResource(string pluralizedTypeName = null) where TResource : class, IIdentifiable; @@ -46,7 +46,7 @@ public interface IResourceGraphBuilder /// /// The pluralized name that should be exposed by the API. /// If nothing is specified, the configured name formatter will be used. - /// See . + /// See . /// IResourceGraphBuilder AddResource(Type entityType, Type idType, string pluralizedTypeName = null); diff --git a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs index 661341b25a..a969a4dbf0 100644 --- a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs @@ -1,5 +1,3 @@ -using JsonApiDotNetCore.Internal.Contracts; - namespace JsonApiDotNetCore.Configuration { public interface IJsonApiOptions : ILinksConfiguration, ISerializerOptions diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index dfa0591e18..fe41af6602 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using JsonApiDotNetCore.Builders; using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Models.Links; diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs index 4887c8955d..d88ee0272d 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Threading.Tasks; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Models; diff --git a/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs b/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs index af25b64e3a..ea70d70fa8 100644 --- a/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceWriteRepository.cs @@ -1,11 +1,10 @@ -using System; using System.Collections.Generic; using System.Threading.Tasks; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Data { - public interface IResourceWriteRepository + public interface IResourceWriteRepository : IResourceWriteRepository where TResource : class, IIdentifiable { } diff --git a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs index 441d3aee9f..a838202253 100644 --- a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; using System.Reflection; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Internal.Query; using JsonApiDotNetCore.Models; diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index db13d3ae28..5e37f416cb 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Reflection; using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Internal; using Microsoft.EntityFrameworkCore; @@ -13,7 +12,6 @@ using JsonApiDotNetCore.Serialization; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Serialization.Server; -using JsonApiDotNetCore.Services; namespace JsonApiDotNetCore.Extensions { diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs index c51af2c5b6..d00f47526e 100644 --- a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs +++ b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading.Tasks; using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Managers.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Serialization.Server; using Microsoft.AspNetCore.Mvc.Formatters; diff --git a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs index f03f2ae890..074914faa3 100644 --- a/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs +++ b/src/JsonApiDotNetCore/Graph/ServiceDiscoveryFacade.cs @@ -1,11 +1,8 @@ using JsonApiDotNetCore.Builders; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; -using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; diff --git a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs index ca54f13f6c..cf25305ca8 100644 --- a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs @@ -1,5 +1,3 @@ -// REF: https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.CustomRoutingConvention/NameSpaceRoutingConvention.cs -// REF: https://github.com/aspnet/Mvc/issues/5691 using System; using System.Collections.Generic; using System.Linq; diff --git a/src/JsonApiDotNetCore/Internal/Exceptions/JsonApiRouteHandler.cs b/src/JsonApiDotNetCore/Internal/Exceptions/JsonApiRouteHandler.cs deleted file mode 100644 index 132f8d2042..0000000000 --- a/src/JsonApiDotNetCore/Internal/Exceptions/JsonApiRouteHandler.cs +++ /dev/null @@ -1,83 +0,0 @@ -// REF: https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcRouteHandler.cs -using System; -using System.Threading.Tasks; -using JsonApiDotNetCore.Extensions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.AspNetCore.Routing; - -namespace JsonApiDotNetCore.Internal -{ - public class JsonApiRouteHandler : IRouter - { - private readonly IActionContextAccessor _actionContextAccessor; - private readonly IActionInvokerFactory _actionInvokerFactory; - private readonly IActionSelector _actionSelector; - - public JsonApiRouteHandler( - IActionInvokerFactory actionInvokerFactory, - IActionSelector actionSelector) - : this(actionInvokerFactory, actionSelector, actionContextAccessor: null) - { - } - - public JsonApiRouteHandler( - IActionInvokerFactory actionInvokerFactory, - IActionSelector actionSelector, - IActionContextAccessor actionContextAccessor) - { - // The IActionContextAccessor is optional. We want to avoid the overhead of using CallContext - // if possible. - _actionContextAccessor = actionContextAccessor; - _actionInvokerFactory = actionInvokerFactory; - _actionSelector = actionSelector; - } - - public VirtualPathData GetVirtualPath(VirtualPathContext context) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - - // We return null here because we're not responsible for generating the url, the route is. - return null; - } - - public Task RouteAsync(RouteContext context) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - - var candidates = _actionSelector.SelectCandidates(context); - if (candidates == null || candidates.Count == 0) - { - return Task.CompletedTask; - } - - var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates); - if (actionDescriptor == null) - { - return Task.CompletedTask; - } - - context.Handler = (c) => - { - var routeData = c.GetRouteData(); - - foreach(var routeValue in routeData.Values) - routeData.Values[routeValue.Key] = routeValue.Value.ToString().ToProperCase(); - - var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor); - if (_actionContextAccessor != null) - { - _actionContextAccessor.ActionContext = actionContext; - } - - var invoker = _actionInvokerFactory.CreateInvoker(actionContext); - - return invoker.InvokeAsync(); - }; - - return Task.CompletedTask; - } - } -} diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs index 5145621058..00292ec625 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs @@ -6,10 +6,7 @@ using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; namespace JsonApiDotNetCore.Internal.Generics { diff --git a/src/JsonApiDotNetCore/Internal/IdentifiableComparer.cs b/src/JsonApiDotNetCore/Internal/IdentifiableComparer.cs index 273f5f5d51..60793829b8 100644 --- a/src/JsonApiDotNetCore/Internal/IdentifiableComparer.cs +++ b/src/JsonApiDotNetCore/Internal/IdentifiableComparer.cs @@ -1,7 +1,5 @@ using JsonApiDotNetCore.Models; -using System; using System.Collections.Generic; -using System.Text; namespace JsonApiDotNetCore.Internal { diff --git a/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs b/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs index e3d2075d36..40588e672b 100644 --- a/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs +++ b/src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs @@ -1,7 +1,3 @@ -using System; -using JsonApiDotNetCore.Extensions; -using JsonApiDotNetCore.Models; - namespace JsonApiDotNetCore.Internal.Query { /// diff --git a/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs b/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs index c599ef4eec..772eb32457 100644 --- a/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs +++ b/src/JsonApiDotNetCore/Models/Annotation/HasOneAttribute.cs @@ -1,4 +1,3 @@ -using System; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Models.Links; diff --git a/src/JsonApiDotNetCore/Models/IHasMeta.cs b/src/JsonApiDotNetCore/Models/IHasMeta.cs index 34efffdabd..f1605bf790 100644 --- a/src/JsonApiDotNetCore/Models/IHasMeta.cs +++ b/src/JsonApiDotNetCore/Models/IHasMeta.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using JsonApiDotNetCore.Services; namespace JsonApiDotNetCore.Models { diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index 56f8a1f50e..a27eb2ca6c 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using JsonApiDotNetCore.Services; -using System.Collections; namespace JsonApiDotNetCore.Models { diff --git a/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs b/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs index 3b796e033d..b6e2171c1a 100644 --- a/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs +++ b/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs @@ -1,11 +1,9 @@ -using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCore.Managers.Contracts { /// - /// This is the former RequestManager. TODO: not done. /// Metadata associated to the current json:api request. /// public interface ICurrentRequest diff --git a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs index af0997d736..9b39778d45 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializerFactory.cs @@ -1,10 +1,7 @@ using System; -using JsonApiDotNetCore.Extensions; using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Managers.Contracts; -using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; -using Microsoft.Extensions.DependencyInjection; namespace JsonApiDotNetCore.Serialization.Server { From 48495d3b1fc91095e28ce959ff8ac40d579462af Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 13:27:13 +0200 Subject: [PATCH 25/29] refactor: regroupe hook container and executors --- .../Hooks/IResourceHookContainer.cs | 152 ++++++++++-------- .../Hooks/IResourceHookExecutor.cs | 106 ++++++------ 2 files changed, 144 insertions(+), 114 deletions(-) diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs index 52c8cb36d0..cffc1371c9 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookContainer.cs @@ -12,12 +12,39 @@ public interface IResourceHookContainer { } /// /// Implement this interface to implement business logic hooks on . /// - public interface IResourceHookContainer : IBeforeHooks, IAfterHooks, IOnHooks, IResourceHookContainer where TResource : class, IIdentifiable { } + public interface IResourceHookContainer + : IReadHookContainer, IDeleteHookContainer, ICreateHookContainer, + IUpdateHookContainer, IOnReturnHookContainer, IResourceHookContainer + where TResource : class, IIdentifiable { } /// - /// Wrapper interface for all Before hooks. + /// Read hooks container /// - public interface IBeforeHooks where TResource : class, IIdentifiable + public interface IReadHookContainer where TResource : class, IIdentifiable + { + /// + /// Implement this hook to run custom logic in the + /// layer just before reading entities of type . + /// + /// An enum indicating from where the hook was triggered. + /// Indicates whether the to be queried entities are the main request entities or if they were included + /// The string id of the requested entity, in the case of + void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null); + /// + /// Implement this hook to run custom logic in the + /// layer just after reading entities of type . + /// + /// The unique set of affected entities. + /// An enum indicating from where the hook was triggered. + /// A boolean to indicate whether the entities in this hook execution are the main entities of the request, + /// or if they were included as a relationship + void AfterRead(HashSet entities, ResourcePipeline pipeline, bool isIncluded = false); + } + + /// + /// Create hooks container + /// + public interface ICreateHookContainer where TResource : class, IIdentifiable { /// /// Implement this hook to run custom logic in the @@ -40,14 +67,27 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. IEnumerable BeforeCreate(IEntityHashSet entities, ResourcePipeline pipeline); + /// /// Implement this hook to run custom logic in the - /// layer just before reading entities of type . + /// layer just after creation of entities of type . + /// + /// If relationships were created with the created entities, this will + /// be reflected by the corresponding NavigationProperty being set. + /// For each of these relationships, the + /// hook is fired after the execution of this hook. /// + /// The transformed entity set + /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. - /// Indicates whether the to be queried entities are the main request entities or if they were included - /// The string id of the requested entity, in the case of - void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null); + void AfterCreate(HashSet entities, ResourcePipeline pipeline); + } + + /// + /// update hooks container + /// + public interface IUpdateHookContainer where TResource : class, IIdentifiable + { /// /// Implement this hook to run custom logic in the /// layer just before updating entities of type . @@ -76,26 +116,6 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// An enum indicating from where the hook was triggered. IEnumerable BeforeUpdate(IDiffableEntityHashSet entities, ResourcePipeline pipeline); - /// - /// Implement this hook to run custom logic in the - /// layer just before deleting entities of type . - /// - /// For the pipeline, - /// will typically contain one entity. - /// - /// The returned may be a subset - /// of , in which case the operation of the - /// pipeline will not be executed for the omitted entities. - /// - /// If by the deletion of these entities any other entities are affected - /// implicitly by the removal of their relationships (eg - /// in the case of an one-to-one relationship), the - /// hook is fired for these entities. - /// - /// The transformed entity set - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - IEnumerable BeforeDelete(IEntityHashSet entities, ResourcePipeline pipeline); /// /// Implement this hook to run custom logic in the /// layer just before updating relationships to entities of type . @@ -116,6 +136,28 @@ public interface IBeforeHooks where TResource : class, IIdentifiable /// An enum indicating from where the hook was triggered. /// A helper that groups the entities by the affected relationship IEnumerable BeforeUpdateRelationship(HashSet ids, IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); + + /// + /// Implement this hook to run custom logic in the + /// layer just after updating entities of type . + /// + /// If relationships were updated with the updated entities, this will + /// be reflected by the corresponding NavigationProperty being set. + /// For each of these relationships, the + /// hook is fired after the execution of this hook. + /// + /// The unique set of affected entities. + /// An enum indicating from where the hook was triggered. + void AfterUpdate(HashSet entities, ResourcePipeline pipeline); + + /// + /// Implement this hook to run custom logic in the layer + /// just after a relationship was updated. + /// + /// Relationship helper. + /// An enum indicating from where the hook was triggered. + void AfterUpdateRelationship(IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); + /// /// Implement this hook to run custom logic in the /// layer just before implicitly updating relationships to entities of type . @@ -138,44 +180,31 @@ public interface IBeforeHooks where TResource : class, IIdentifiable } /// - /// Wrapper interface for all After hooks. + /// Delete hooks container /// - public interface IAfterHooks where TResource : class, IIdentifiable + public interface IDeleteHookContainer where TResource : class, IIdentifiable { /// /// Implement this hook to run custom logic in the - /// layer just after creation of entities of type . + /// layer just before deleting entities of type . /// - /// If relationships were created with the created entities, this will - /// be reflected by the corresponding NavigationProperty being set. - /// For each of these relationships, the - /// hook is fired after the execution of this hook. - /// - /// The transformed entity set - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - void AfterCreate(HashSet entities, ResourcePipeline pipeline); - /// - /// Implement this hook to run custom logic in the - /// layer just after reading entities of type . - /// - /// The unique set of affected entities. - /// An enum indicating from where the hook was triggered. - /// A boolean to indicate whether the entities in this hook execution are the main entities of the request, - /// or if they were included as a relationship - void AfterRead(HashSet entities, ResourcePipeline pipeline, bool isIncluded = false); - /// - /// Implement this hook to run custom logic in the - /// layer just after updating entities of type . + /// For the pipeline, + /// will typically contain one entity. /// - /// If relationships were updated with the updated entities, this will - /// be reflected by the corresponding NavigationProperty being set. - /// For each of these relationships, the - /// hook is fired after the execution of this hook. + /// The returned may be a subset + /// of , in which case the operation of the + /// pipeline will not be executed for the omitted entities. + /// + /// If by the deletion of these entities any other entities are affected + /// implicitly by the removal of their relationships (eg + /// in the case of an one-to-one relationship), the + /// hook is fired for these entities. /// + /// The transformed entity set /// The unique set of affected entities. /// An enum indicating from where the hook was triggered. - void AfterUpdate(HashSet entities, ResourcePipeline pipeline); + IEnumerable BeforeDelete(IEntityHashSet entities, ResourcePipeline pipeline); + /// /// Implement this hook to run custom logic in the /// layer just after deletion of entities of type . @@ -184,19 +213,12 @@ public interface IAfterHooks where TResource : class, IIdentifiable /// An enum indicating from where the hook was triggered. /// If set to true if the deletion was succeeded in the repository layer. void AfterDelete(HashSet entities, ResourcePipeline pipeline, bool succeeded); - /// - /// Implement this hook to run custom logic in the layer - /// just after a relationship was updated. - /// - /// Relationship helper. - /// An enum indicating from where the hook was triggered. - void AfterUpdateRelationship(IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline); } /// - /// Wrapper interface for all on hooks. + /// On return hook container /// - public interface IOnHooks where TResource : class, IIdentifiable + public interface IOnReturnHookContainer where TResource : class, IIdentifiable { /// /// Implement this hook to transform the result data just before returning diff --git a/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs b/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs index accadc029b..44ab2929bf 100644 --- a/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs +++ b/src/JsonApiDotNetCore/Hooks/IResourceHookExecutor.cs @@ -7,19 +7,16 @@ namespace JsonApiDotNetCore.Hooks /// /// Transient service responsible for executing Resource Hooks as defined /// in . see methods in - /// , and - /// for more information. + /// , and + /// for more information. /// /// Uses for traversal of nested entity data structures. /// Uses for retrieving meta data about hooks, /// fetching database values and performing other recurring internal operations. /// - public interface IResourceHookExecutor : IBeforeExecutor, IAfterExecutor, IOnExecutor { } + public interface IResourceHookExecutor : IReadHookExecutor, IUpdateHookExecutor, ICreateHookExecutor, IDeleteHookExecutor, IOnReturnHookExecutor { } - /// - /// Wrapper interface for all Before execution methods. - /// - public interface IBeforeExecutor + public interface ICreateHookExecutor { /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. @@ -37,38 +34,22 @@ public interface IBeforeExecutor /// The type of the root entities IEnumerable BeforeCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// - /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// - /// Fires the - /// hook where T = for the requested - /// entities as well as any related relationship. - /// - /// An enum indicating from where the hook was triggered. - /// StringId of the requested entity in the case of - /// . - /// The type of the request entity - void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TResource : class, IIdentifiable; - /// - /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. - /// The returned set will be used in the actual operation in . + /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// - /// Fires the + /// Fires the /// hook where T = for values in parameter . /// - /// Fires the + /// Fires the /// hook for any related (nested) entity for values within parameter - /// - /// Fires the - /// hook for any entities that are indirectly (implicitly) affected by this operation. - /// Eg: when updating a one-to-one relationship of an entity which already - /// had this relationship populated, then this update will indirectly affect - /// the existing relationship value. /// - /// The transformed set /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. /// The type of the root entities - IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + } + + public interface IDeleteHookExecutor + { /// /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. /// The returned set will be used in the actual operation in . @@ -86,26 +67,35 @@ public interface IBeforeExecutor /// An enum indicating from where the hook was triggered. /// The type of the root entities IEnumerable BeforeDelete(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + /// + /// Executes the After Cycle by firing the appropiate hooks if they are implemented. + /// + /// Fires the + /// hook where T = for values in parameter . + /// + /// Target entities for the Before cycle. + /// An enum indicating from where the hook was triggered. + /// The type of the root entities + void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TResource : class, IIdentifiable; } /// - /// Wrapper interface for all After execution methods. + /// Wrapper interface for all Before execution methods. /// - public interface IAfterExecutor + public interface IReadHookExecutor { /// - /// Executes the After Cycle by firing the appropiate hooks if they are implemented. - /// - /// Fires the - /// hook where T = for values in parameter . + /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. /// - /// Fires the - /// hook for any related (nested) entity for values within parameter + /// Fires the + /// hook where T = for the requested + /// entities as well as any related relationship. /// - /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. - /// The type of the root entities - void AfterCreate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + /// StringId of the requested entity in the case of + /// . + /// The type of the request entity + void BeforeRead(ResourcePipeline pipeline, string stringId = null) where TResource : class, IIdentifiable; /// /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// @@ -116,35 +106,53 @@ public interface IAfterExecutor /// An enum indicating from where the hook was triggered. /// The type of the root entities void AfterRead(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + } + + /// + /// Wrapper interface for all After execution methods. + /// + public interface IUpdateHookExecutor + { /// - /// Executes the After Cycle by firing the appropiate hooks if they are implemented. + /// Executes the Before Cycle by firing the appropiate hooks if they are implemented. + /// The returned set will be used in the actual operation in . /// - /// Fires the + /// Fires the /// hook where T = for values in parameter . /// - /// Fires the + /// Fires the /// hook for any related (nested) entity for values within parameter + /// + /// Fires the + /// hook for any entities that are indirectly (implicitly) affected by this operation. + /// Eg: when updating a one-to-one relationship of an entity which already + /// had this relationship populated, then this update will indirectly affect + /// the existing relationship value. /// + /// The transformed set /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. /// The type of the root entities - void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; + IEnumerable BeforeUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; /// /// Executes the After Cycle by firing the appropiate hooks if they are implemented. /// - /// Fires the + /// Fires the /// hook where T = for values in parameter . + /// + /// Fires the + /// hook for any related (nested) entity for values within parameter /// /// Target entities for the Before cycle. /// An enum indicating from where the hook was triggered. /// The type of the root entities - void AfterDelete(IEnumerable entities, ResourcePipeline pipeline, bool succeeded) where TResource : class, IIdentifiable; + void AfterUpdate(IEnumerable entities, ResourcePipeline pipeline) where TResource : class, IIdentifiable; } /// /// Wrapper interface for all On execution methods. /// - public interface IOnExecutor + public interface IOnReturnHookExecutor { /// /// Executes the On Cycle by firing the appropiate hooks if they are implemented. From f19e60603fbb29f8bba692059f8d9b7ad586c9c4 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 14:59:12 +0200 Subject: [PATCH 26/29] style: IGenericProcessor --> IHasManyThroughUpdateHelper --- .../Builders/JsonApiApplicationBuilder.cs | 2 +- .../Data/DefaultResourceRepository.cs | 28 +- .../Data/IResourceReadRepository.cs | 2 +- .../Extensions/DbContextExtensions.cs | 4 - .../Extensions/IQueryableExtensions.cs | 2 +- .../Internal/Generics/GenericProcessor.cs | 105 ------ .../Generics/HasManyThroughUpdateHelper.cs | 80 ++++ .../Serialization/Server/FieldsToSerialize.cs | 1 - .../Services/DefaultResourceService.cs | 4 +- .../Acceptance/Spec/SparseFieldSetTests.cs | 2 +- .../IServiceCollectionExtensionsTests.cs | 2 +- wiki/v4/content/using System; | 353 +----------------- 12 files changed, 112 insertions(+), 473 deletions(-) delete mode 100644 src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs create mode 100644 src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index 6b3e02390f..b56acb0677 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -155,7 +155,7 @@ public void ConfigureServices() _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); - _services.AddScoped(typeof(GenericProcessor<>)); + _services.AddScoped(typeof(HasManyThroughUpdateHelper<>)); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); diff --git a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs index 9a23624d67..21dc977fff 100644 --- a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs @@ -55,9 +55,9 @@ public DefaultResourceRepository( public virtual IQueryable Get(TId id) => _dbSet.Where(e => e.Id.Equals(id)); /// - public virtual IQueryable Select(IQueryable entities, List fields) + public virtual IQueryable Select(IQueryable entities, params AttrAttribute[] fields) { - if (fields?.Count > 0) + if (fields.Any()) return entities.Select(fields); return entities; @@ -256,16 +256,20 @@ private IIdentifiable GetTrackedHasOneRelationshipValue(IIdentifiable relationsh /// public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds) { - // TODO: it would be better to let this be determined within the relationship attribute... - // need to think about the right way to do that since HasMany doesn't need to think about this - // and setting the HasManyThrough.Type to the join type (ArticleTag instead of Tag) for this changes the semantics - // of the property... - var typeToUpdate = (relationship is HasManyThroughAttribute hasManyThrough) - ? hasManyThrough.ThroughType - : relationship.RightType; - - var genericProcessor = _genericProcessorFactory.GetProcessor(typeof(GenericProcessor<>), typeToUpdate); - await genericProcessor.UpdateRelationshipsAsync(parent, relationship, relationshipIds); + if (relationship is HasManyThroughAttribute hasManyThrough) + { + var helper = _genericProcessorFactory.GetProcessor(typeof(HasManyThroughUpdateHelper<>), hasManyThrough.ThroughType); + await helper.UpdateAsync((IIdentifiable)parent, hasManyThrough, relationshipIds); + return; + } + + var context = _context.Set(relationship.RightType); + var updatedValue = relationship is HasManyAttribute + ? context.Where(e => relationshipIds.Contains(((IIdentifiable)e).StringId)).Cast(relationship.RightType) + : context.FirstOrDefault(e => relationshipIds.First() == ((IIdentifiable)e).StringId); + + relationship.SetValue(parent, updatedValue); + await _context.SaveChangesAsync(); } /// diff --git a/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs index 4e02306eea..6480131c7e 100644 --- a/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs +++ b/src/JsonApiDotNetCore/Data/IResourceReadRepository.cs @@ -26,7 +26,7 @@ public interface IResourceReadRepository /// /// Apply fields to the provided queryable /// - IQueryable Select(IQueryable entities, List fields); + IQueryable Select(IQueryable entities, params AttrAttribute[] fields); /// /// Include a relationship in the query /// diff --git a/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs b/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs index 984b396243..ebe89815f8 100644 --- a/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs @@ -10,10 +10,6 @@ namespace JsonApiDotNetCore.Extensions { public static class DbContextExtensions { - [Obsolete("This is no longer required since the introduction of context.Set", error: false)] - public static DbSet GetDbSet(this DbContext context) where T : class - => context.Set(); - /// /// Get the DbSet when the model type is unknown until runtime /// diff --git a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs index a838202253..bb00eef3f3 100644 --- a/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs @@ -64,7 +64,7 @@ public static IQueryable Filter(this IQueryable sourc return CallGenericWhereMethod(source, filterQuery); } - public static IQueryable Select(this IQueryable source, List columns) + public static IQueryable Select(this IQueryable source, AttrAttribute[] columns) => CallGenericSelectMethod(source, columns.Select(attr => attr.InternalAttributeName).ToList()); public static IOrderedQueryable Sort(this IQueryable source, SortQueryContext sortQuery) diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs deleted file mode 100644 index 00292ec625..0000000000 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using JsonApiDotNetCore.Data; -using JsonApiDotNetCore.Extensions; -using JsonApiDotNetCore.Models; -using Microsoft.EntityFrameworkCore; - -namespace JsonApiDotNetCore.Internal.Generics -{ - // TODO: consider renaming to PatchRelationshipService (or something) - public interface IGenericProcessor - { - Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds); - } - - /// - /// A special processor that gets instantiated for a generic type (<T>) - /// when the actual type is not known until runtime. Specifically, this is used for updating - /// relationships. - /// - public class GenericProcessor : IGenericProcessor where T : class - { - private readonly DbContext _context; - public GenericProcessor(IDbContextResolver contextResolver) - { - _context = contextResolver.GetContext(); - } - - public virtual async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds) - { - if (relationship is HasManyThroughAttribute hasManyThrough && parent is IIdentifiable identifiableParent) - { - await SetHasManyThroughRelationshipAsync(identifiableParent, hasManyThrough, relationshipIds); - } - else - { - await SetRelationshipsAsync(parent, relationship, relationshipIds); - } - } - - private async Task SetHasManyThroughRelationshipAsync(IIdentifiable identifiableParent, HasManyThroughAttribute hasManyThrough, IEnumerable relationshipIds) - { - // we need to create a transaction for the HasManyThrough case so we can get and remove any existing - // join entities and only commit if all operations are successful - using(var transaction = await _context.GetCurrentOrCreateTransactionAsync()) - { - // ArticleTag - ParameterExpression parameter = Expression.Parameter(hasManyThrough.ThroughType); - - // ArticleTag.ArticleId - Expression property = Expression.Property(parameter, hasManyThrough.LeftIdProperty); - - // article.Id - var parentId = TypeHelper.ConvertType(identifiableParent.StringId, hasManyThrough.LeftIdProperty.PropertyType); - Expression target = Expression.Constant(parentId); - - // ArticleTag.ArticleId.Equals(article.Id) - Expression equals = Expression.Call(property, "Equals", null, target); - - var lambda = Expression.Lambda>(equals, parameter); - - // TODO: we shouldn't need to do this instead we should try updating the existing? - // the challenge here is if a composite key is used, then we will fail to - // create due to a unique key violation - var oldLinks = _context - .Set() - .Where(lambda.Compile()) - .ToList(); - - _context.RemoveRange(oldLinks); - - var newLinks = relationshipIds.Select(x => { - var link = Activator.CreateInstance(hasManyThrough.ThroughType); - hasManyThrough.LeftIdProperty.SetValue(link, TypeHelper.ConvertType(parentId, hasManyThrough.LeftIdProperty.PropertyType)); - hasManyThrough.RightIdProperty.SetValue(link, TypeHelper.ConvertType(x, hasManyThrough.RightIdProperty.PropertyType)); - return link; - }); - - _context.AddRange(newLinks); - await _context.SaveChangesAsync(); - - transaction.Commit(); - } - } - - private async Task SetRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds) - { - if (relationship.IsHasMany) - { - var entities = _context.Set().Where(x => relationshipIds.Contains(((IIdentifiable)x).StringId)).ToList(); - relationship.SetValue(parent, entities); - } - else - { - var entity = _context.Set().SingleOrDefault(x => relationshipIds.First() == ((IIdentifiable)x).StringId); - relationship.SetValue(parent, entity); - } - - await _context.SaveChangesAsync(); - } - } -} diff --git a/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs b/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs new file mode 100644 index 0000000000..ee878b4831 --- /dev/null +++ b/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using JsonApiDotNetCore.Data; +using JsonApiDotNetCore.Extensions; +using JsonApiDotNetCore.Models; +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCore.Internal.Generics +{ + /// + /// A special helper service that gets instantiated for the right-type of a many-to-many relationship and is responsible for + /// processing updates for that relationships + /// + public interface IHasManyThroughUpdateHelper + { + Task UpdateAsync(IIdentifiable parent, HasManyThroughAttribute relationship, IEnumerable relationshipIds); + } + + /// + /// A special processor that gets instantiated for a generic type (<T>) + /// when the actual type is not known until runtime. Specifically, this is used for updating + /// relationships. + /// + public class HasManyThroughUpdateHelper : IHasManyThroughUpdateHelper where T : class + { + private readonly DbContext _context; + public HasManyThroughUpdateHelper(IDbContextResolver contextResolver) + { + _context = contextResolver.GetContext(); + } + + public virtual async Task UpdateAsync(IIdentifiable parent, HasManyThroughAttribute relationship, IEnumerable relationshipIds) + { + // we need to create a transaction for the HasManyThrough case so we can get and remove any existing + // join entities and only commit if all operations are successful + using (var transaction = await _context.GetCurrentOrCreateTransactionAsync()) + { + // ArticleTag + ParameterExpression parameter = Expression.Parameter(relationship.ThroughType); + + // ArticleTag.ArticleId + Expression property = Expression.Property(parameter, relationship.LeftIdProperty); + + // article.Id + var parentId = TypeHelper.ConvertType(parent.StringId, relationship.LeftIdProperty.PropertyType); + Expression target = Expression.Constant(parentId); + + // ArticleTag.ArticleId.Equals(article.Id) + Expression equals = Expression.Call(property, "Equals", null, target); + + var lambda = Expression.Lambda>(equals, parameter); + + // TODO: we shouldn't need to do this instead we should try updating the existing? + // the challenge here is if a composite key is used, then we will fail to + // create due to a unique key violation + var oldLinks = _context + .Set() + .Where(lambda.Compile()) + .ToList(); + + _context.RemoveRange(oldLinks); + + var newLinks = relationshipIds.Select(x => { + var link = Activator.CreateInstance(relationship.ThroughType); + relationship.LeftIdProperty.SetValue(link, TypeHelper.ConvertType(parentId, relationship.LeftIdProperty.PropertyType)); + relationship.RightIdProperty.SetValue(link, TypeHelper.ConvertType(x, relationship.RightIdProperty.PropertyType)); + return link; + }); + + _context.AddRange(newLinks); + await _context.SaveChangesAsync(); + + transaction.Commit(); + } + } + } +} diff --git a/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs b/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs index 43dbcd6417..4bdf99654a 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/FieldsToSerialize.cs @@ -1,7 +1,6 @@ using JsonApiDotNetCore.Internal.Contracts; using System; using System.Collections.Generic; -using JsonApiDotNetCore.Services; using JsonApiDotNetCore.Query; using System.Linq; using JsonApiDotNetCore.Models; diff --git a/src/JsonApiDotNetCore/Services/DefaultResourceService.cs b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs index 4b1eb44050..5c79f0dc5c 100644 --- a/src/JsonApiDotNetCore/Services/DefaultResourceService.cs +++ b/src/JsonApiDotNetCore/Services/DefaultResourceService.cs @@ -263,7 +263,7 @@ protected virtual IQueryable ApplySelect(IQueryable entiti { var fields = _sparseFieldsService.Get(); if (fields != null && fields.Any()) - entities = _repository.Select(entities, fields); + entities = _repository.Select(entities, fields.ToArray()); return entities; } @@ -276,7 +276,7 @@ protected virtual IQueryable ApplySelect(IQueryable entiti private async Task GetWithRelationshipsAsync(TId id) { var sparseFieldset = _sparseFieldsService.Get(); - var query = _repository.Select(_repository.Get(id), sparseFieldset); + var query = _repository.Select(_repository.Get(id), sparseFieldset.ToArray()); foreach (var chain in _includeService.Get()) query = _repository.Include(query, chain.ToArray()); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs index 67ec9b53ba..16c83ad13a 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs @@ -73,7 +73,7 @@ public async Task Can_Select_Sparse_Fieldsets() var query = _dbContext .TodoItems .Where(t => t.Id == todoItem.Id) - .Select(_resourceGraph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToList()); + .Select(_resourceGraph.GetAttributes(e => new { e.Id, e.Description, e.CreatedDate, e.AchievedDate } ).ToArray()); var resultSql = StringExtensions.Normalize(query.ToSql()); var result = await query.FirstAsync(); diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index 0ccb6649c3..7522fbe13b 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -56,7 +56,7 @@ public void AddJsonApiInternals_Adds_All_Required_Services() Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); - Assert.NotNull(provider.GetService(typeof(GenericProcessor))); + Assert.NotNull(provider.GetService(typeof(HasManyThroughUpdateHelper))); } [Fact] diff --git a/wiki/v4/content/using System; b/wiki/v4/content/using System; index 58eb324083..9f941645af 100644 --- a/wiki/v4/content/using System; +++ b/wiki/v4/content/using System; @@ -1,344 +1,9 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using JsonApiDotNetCore.Extensions; -using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Internal.Contracts; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Serialization; -using RightType = System.Type; -using LeftType = System.Type; - -namespace JsonApiDotNetCore.Hooks -{ - /// - /// A helper class used by the to traverse through - /// entity data structures (trees), allowing for a breadth-first-traversal - /// - /// It creates nodes for each layer. - /// Typically, the first layer is homogeneous (all entities have the same type), - /// and further nodes can be mixed. - /// - internal class TraversalHelper : ITraversalHelper - { - private readonly IdentifiableComparer _comparer = new IdentifiableComparer(); - private readonly IResourceGraph _resourceGraph; - private readonly ITargetedFields _targetedFields; - /// - /// Keeps track of which entities has already been traversed through, to prevent - /// infinite loops in eg cyclic data structures. - /// - private Dictionary> _processedEntities; - /// - /// A mapper from to . - /// See the latter for more details. - /// - private readonly Dictionary _relationshipProxies = new Dictionary(); - public TraversalHelper( - IResourceGraph resourceGraph, - ITargetedFields targetedFields) - { - _targetedFields = targetedFields; - _resourceGraph = resourceGraph; - } - - /// - /// Creates a root node for breadth-first-traversal. Note that typically, in - /// JADNC, the root layer will be homogeneous. Also, because it is the first layer, - /// there can be no relationships to previous layers, only to next layers. - /// - /// The root node. - /// Root entities. - /// The 1st type parameter. - public RootNode CreateRootNode(IEnumerable rootEntities) where TResource : class, IIdentifiable - { - _processedEntities = new Dictionary>(); - RegisterRelationshipProxies(typeof(TResource)); - var uniqueEntities = ProcessEntities(rootEntities); - var populatedRelationshipsToNextLayer = GetPopulatedRelationships(typeof(TResource), uniqueEntities.Cast()); - var allRelationshipsFromType = _relationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.LeftType == typeof(TResource)).ToArray(); - return new RootNode(uniqueEntities, populatedRelationshipsToNextLayer, allRelationshipsFromType); - } - - /// - /// Create the first layer after the root layer (based on the root node) - /// - /// The next layer. - /// Root node. - public NodeLayer CreateNextLayer(INode rootNode) - { - return CreateNextLayer(new INode[] { rootNode }); - } - - /// - /// Create a next layer from any previous layer - /// - /// The next layer. - /// Nodes. - public NodeLayer CreateNextLayer(IEnumerable nodes) - { - /// first extract entities by parsing populated relationships in the entities - /// of previous layer - (var lefts, var rights) = ExtractEntities(nodes); - - /// group them conveniently so we can make ChildNodes of them: - /// there might be several relationship attributes in rights dictionary - /// that point to the same right type. - var leftsGrouped = GroupByRightTypeOfRelationship(lefts); - - /// convert the groups into child nodes - var nextNodes = leftsGrouped.Select(entry => - { - var nextNodeType = entry.Key; - RegisterRelationshipProxies(nextNodeType); - - var populatedRelationships = new List(); - var relationshipsToPreviousLayer = entry.Value.Select(grouped => - { - var proxy = grouped.Key; - populatedRelationships.AddRange(GetPopulatedRelationships(nextNodeType, rights[proxy])); - return CreateRelationshipGroupInstance(nextNodeType, proxy, grouped.Value, rights[proxy]); - }).ToList(); - - return CreateNodeInstance(nextNodeType, populatedRelationships.ToArray(), relationshipsToPreviousLayer); - }).ToList(); - - /// wrap the child nodes in a EntityChildLayer - return new NodeLayer(nextNodes); - } - - /// - /// iterates throug the dictinary and groups the values - /// by matching right type of the keys (which are relationshipattributes) - /// - Dictionary>>> GroupByRightTypeOfRelationship(Dictionary> relationships) - { - return relationships.GroupBy(kvp => kvp.Key.RightType).ToDictionary(gdc => gdc.Key, gdc => gdc.ToList()); - } - - /// - /// Extracts the entities for the current layer by going through all populated relationships - /// of the (left entities of the previous layer. - /// - (Dictionary>, Dictionary>) ExtractEntities(IEnumerable leftNodes) - { - var leftEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => prevLayerEntities - var rightEntitiesGrouped = new Dictionary>(); // RelationshipAttr_prevlayer->currentlayer => currentLayerEntities - - foreach (var node in leftNodes) - { - var leftEntities = node.UniqueEntities; - var relationships = node.RelationshipsToNextLayer; - foreach (IIdentifiable leftEntity in leftEntities) - { - foreach (var proxy in relationships) - { - var relationshipValue = proxy.GetValue(leftEntity); - // skip this relationship if it's not populated - if (!proxy.IsContextRelation && relationshipValue == null) continue; - if (!(relationshipValue is IEnumerable rightEntities)) - { - // in the case of a to-one relationship, the assigned value - // will not be a list. We therefore first wrap it in a list. - var list = TypeHelper.CreateListFor(proxy.RightType); - if (relationshipValue != null) list.Add(relationshipValue); - rightEntities = list; - } - - var uniqueRightEntities = UniqueInTree(rightEntities.Cast(), proxy.RightType); - if (proxy.IsContextRelation || uniqueRightEntities.Any()) - { - AddToRelationshipGroup(rightEntitiesGrouped, proxy, uniqueRightEntities); - AddToRelationshipGroup(leftEntitiesGrouped, proxy, new IIdentifiable[] { leftEntity }); - } - } - } - } - - var processEntitiesMethod = GetType().GetMethod(nameof(ProcessEntities), BindingFlags.NonPublic | BindingFlags.Instance); - foreach (var kvp in rightEntitiesGrouped) - { - var type = kvp.Key.RightType; - var list = kvp.Value.Cast(type); - processEntitiesMethod.MakeGenericMethod(type).Invoke(this, new object[] { list }); - } - - return (leftEntitiesGrouped, rightEntitiesGrouped); - } - - /// - /// Get all populated relationships known in the current tree traversal from a - /// left type to any right type - /// - /// The relationships. - RelationshipProxy[] GetPopulatedRelationships(LeftType leftType, IEnumerable lefts) - { - var relationshipsFromLeftToRight = _relationshipProxies.Select(entry => entry.Value).Where(proxy => proxy.LeftType == leftType); - return relationshipsFromLeftToRight.Where(proxy => proxy.IsContextRelation || lefts.Any(p => proxy.GetValue(p) != null)).ToArray(); - } - - /// - /// Registers the entities as "seen" in the tree traversal, extracts any new s from it. - /// - /// The entities. - /// Incoming entities. - /// The 1st type parameter. - HashSet ProcessEntities(IEnumerable incomingEntities) where TResource : class, IIdentifiable - { - Type type = typeof(TResource); - var newEntities = UniqueInTree(incomingEntities, type); - RegisterProcessedEntities(newEntities, type); - return newEntities; - } - - /// - /// Parses all relationships from to - /// other models in the resource resourceGraphs by constructing RelationshipProxies . - /// - /// The type to parse - void RegisterRelationshipProxies(RightType type) - { - foreach (RelationshipAttribute attr in _resourceGraph.GetRelationships(type)) - { - if (!attr.CanInclude) continue; - if (!_relationshipProxies.TryGetValue(attr, out RelationshipProxy proxies)) - { - RightType rightType = GetRightTypeFromRelationship(attr); - bool isContextRelation = false; - var relationshipsToUpdate = _targetedFields.Relationships; - if (relationshipsToUpdate != null) isContextRelation = relationshipsToUpdate.Contains(attr); - var proxy = new RelationshipProxy(attr, rightType, isContextRelation); - _relationshipProxies[attr] = proxy; - } - } - } - - - /// - /// Registers the processed entities in the dictionary grouped by type - /// - /// Entities to register - /// Entity type. - void RegisterProcessedEntities(IEnumerable entities, Type entityType) - { - var processedEntities = GetProcessedEntities(entityType); - processedEntities.UnionWith(new HashSet(entities)); - } - - /// - /// Gets the processed entities for a given type, instantiates the collection if new. - /// - /// The processed entities. - /// Entity type. - HashSet GetProcessedEntities(Type entityType) - { - if (!_processedEntities.TryGetValue(entityType, out HashSet processedEntities)) - { - processedEntities = new HashSet(); - _processedEntities[entityType] = processedEntities; - } - return processedEntities; - } - - /// - /// Using the register of processed entities, determines the unique and new - /// entities with respect to previous iterations. - /// - /// The in tree. - /// Entities. - /// Entity type. - HashSet UniqueInTree(IEnumerable entities, Type entityType) where TResource : class, IIdentifiable - { - var newEntities = entities.Except(GetProcessedEntities(entityType), _comparer).Cast(); - return new HashSet(newEntities); - } - - /// - /// Gets the type from relationship attribute. If the attribute is - /// HasManyThrough, and the jointable entity is identifiable, then the target - /// type is the joinentity instead of the righthand side, because hooks might be - /// implemented for the jointable entity. - /// - /// The target type for traversal - /// Relationship attribute - RightType GetRightTypeFromRelationship(RelationshipAttribute attr) - { - if (attr is HasManyThroughAttribute throughAttr && throughAttr.ThroughType.Inherits(typeof(IIdentifiable))) - { - return throughAttr.ThroughType; - } - return attr.RightType; - } - - void AddToRelationshipGroup(Dictionary> target, RelationshipProxy proxy, IEnumerable newEntities) - { - if (!target.TryGetValue(proxy, out List entities)) - { - entities = new List(); - target[proxy] = entities; - } - entities.AddRange(newEntities); - } - - /// - /// Reflective helper method to create an instance of ; - /// - INode CreateNodeInstance(RightType nodeType, RelationshipProxy[] relationshipsToNext, IEnumerable relationshipsFromPrev) - { - IRelationshipsFromPreviousLayer prev = CreateRelationshipsFromInstance(nodeType, relationshipsFromPrev); - return (INode)TypeHelper.CreateInstanceOfOpenType(typeof(ChildNode<>), nodeType, new object[] { relationshipsToNext, prev }); - } - - /// - /// Reflective helper method to create an instance of ; - /// - IRelationshipsFromPreviousLayer CreateRelationshipsFromInstance(RightType nodeType, IEnumerable relationshipsFromPrev) - { - var casted = relationshipsFromPrev.Cast(relationshipsFromPrev.First().GetType()); - return (IRelationshipsFromPreviousLayer)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipsFromPreviousLayer<>), nodeType, new object[] { casted }); - } - - /// - /// Reflective helper method to create an instance of ; - /// - IRelationshipGroup CreateRelationshipGroupInstance(Type thisLayerType, RelationshipProxy proxy, List leftEntities, List rightEntities) - { - var rightEntitiesHashed = TypeHelper.CreateInstanceOfOpenType(typeof(HashSet<>), thisLayerType, rightEntities.Cast(thisLayerType)); - return (IRelationshipGroup)TypeHelper.CreateInstanceOfOpenType(typeof(RelationshipGroup<>), - thisLayerType, - new object[] { proxy, new HashSet(leftEntities), rightEntitiesHashed }); - } - } - - /// - /// A helper class that represents all entities in the current layer that - /// are being traversed for which hooks will be executed (see IResourceHookExecutor) - /// - internal class NodeLayer : IEnumerable - { - readonly List _collection; - - public bool AnyEntities() - { - return _collection.Any(n => n.UniqueEntities.Cast().Any()); - } - - public NodeLayer(List nodes) - { - _collection = nodes; - } - - public IEnumerator GetEnumerator() - { - return _collection.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} - +style: DefaultResourceRepository and DefaultEntityService as consiste… … +style: rename old repository interfaces +style: ContextEntity --> ResourceContext +style: rename Entity to Resource +style: TEntity --> TResource +style: DependentType, PrincipalType --> RightType, LeftType +style: replaced principal with left and dependent with right whenever… … +chore: remove redundant bindings throughout entire codebase +refactor: regroupe hook container and executors \ No newline at end of file From 7d235ab756c0100d81b3b716a20798b246f92f66 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 14:59:34 +0200 Subject: [PATCH 27/29] chore: delete rogue file --- wiki/v4/content/using System; | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 wiki/v4/content/using System; diff --git a/wiki/v4/content/using System; b/wiki/v4/content/using System; deleted file mode 100644 index 9f941645af..0000000000 --- a/wiki/v4/content/using System; +++ /dev/null @@ -1,9 +0,0 @@ -style: DefaultResourceRepository and DefaultEntityService as consiste… … -style: rename old repository interfaces -style: ContextEntity --> ResourceContext -style: rename Entity to Resource -style: TEntity --> TResource -style: DependentType, PrincipalType --> RightType, LeftType -style: replaced principal with left and dependent with right whenever… … -chore: remove redundant bindings throughout entire codebase -refactor: regroupe hook container and executors \ No newline at end of file From 6a186a5d9535229a851e4ec247638c26090b3eee Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 15:11:46 +0200 Subject: [PATCH 28/29] style: rename GenericProcessorFactory --> GenericServiceFactory --- .../JsonApiSerializer_Benchmarks.cs | 2 +- .../Builders/JsonApiApplicationBuilder.cs | 2 +- .../Data/DefaultResourceRepository.cs | 20 +++++++++---------- ...sorFactory.cs => GenericServiceFactory.cs} | 20 +++++++++---------- .../Generics/HasManyThroughUpdateHelper.cs | 12 +++++------ .../ResourceHooks/ResourceHooksTestsSetup.cs | 18 ++++++++--------- 6 files changed, 37 insertions(+), 37 deletions(-) rename src/JsonApiDotNetCore/Internal/Generics/{GenericProcessorFactory.cs => GenericServiceFactory.cs} (61%) diff --git a/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs b/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs index b174da9cb9..aa04e3cc26 100644 --- a/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs +++ b/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs @@ -34,7 +34,7 @@ // jsonApiOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions); -// var genericProcessorFactoryMock = new Mock(); +// var genericServiceFactoryMock = new Mock(); // var documentBuilder = new BaseDocumentBuilder(jsonApiContextMock.Object); // _jsonApiSerializer = new JsonApiSerializer(jsonApiContextMock.Object, documentBuilder); diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index b56acb0677..bd27dc9c46 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -154,7 +154,7 @@ public void ConfigureServices() _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); - _services.AddScoped(); + _services.AddScoped(); _services.AddScoped(typeof(HasManyThroughUpdateHelper<>)); _services.AddScoped(); _services.AddScoped(); diff --git a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs index 21dc977fff..49c5b6a0ae 100644 --- a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs @@ -25,26 +25,26 @@ public class DefaultResourceRepository : IResourceRepository _dbSet; private readonly IResourceGraph _resourceGraph; - private readonly IGenericProcessorFactory _genericProcessorFactory; + private readonly IGenericServiceFactory _genericServiceFactory; public DefaultResourceRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, IResourceGraph resourceGraph, - IGenericProcessorFactory genericProcessorFactory) - : this(targetedFields, contextResolver, resourceGraph, genericProcessorFactory, null) + IGenericServiceFactory genericServiceFactory) + : this(targetedFields, contextResolver, resourceGraph, genericServiceFactory, null) { } public DefaultResourceRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, IResourceGraph resourceGraph, - IGenericProcessorFactory genericProcessorFactory, + IGenericServiceFactory genericServiceFactory, ILoggerFactory loggerFactory = null) { _targetedFields = targetedFields; _resourceGraph = resourceGraph; - _genericProcessorFactory = genericProcessorFactory; + _genericServiceFactory = genericServiceFactory; _context = contextResolver.GetContext(); _dbSet = _context.Set(); } @@ -258,7 +258,7 @@ public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute { if (relationship is HasManyThroughAttribute hasManyThrough) { - var helper = _genericProcessorFactory.GetProcessor(typeof(HasManyThroughUpdateHelper<>), hasManyThrough.ThroughType); + var helper = _genericServiceFactory.Get(typeof(HasManyThroughUpdateHelper<>), hasManyThrough.ThroughType); await helper.UpdateAsync((IIdentifiable)parent, hasManyThrough, relationshipIds); return; } @@ -424,17 +424,17 @@ public class DefaultResourceRepository : DefaultResourceRepository - public interface IGenericProcessorFactory + public interface IGenericServiceFactory { /// /// Constructs the generic type and locates the service, then casts to TInterface @@ -18,7 +18,7 @@ public interface IGenericProcessorFactory /// GetProcessor<IGenericProcessor>(typeof(GenericProcessor<>), typeof(TResource)); /// /// - TInterface GetProcessor(Type openGenericType, Type resourceType); + TInterface Get(Type openGenericType, Type resourceType); /// /// Constructs the generic type and locates the service, then casts to TInterface @@ -28,25 +28,25 @@ public interface IGenericProcessorFactory /// GetProcessor<IGenericProcessor>(typeof(GenericProcessor<,>), typeof(TResource), typeof(TId)); /// /// - TInterface GetProcessor(Type openGenericType, Type resourceType, Type keyType); + TInterface Get(Type openGenericType, Type resourceType, Type keyType); } - public class GenericProcessorFactory : IGenericProcessorFactory + public class GenericServiceFactory : IGenericServiceFactory { private readonly IServiceProvider _serviceProvider; - public GenericProcessorFactory(IScopedServiceProvider serviceProvider) + public GenericServiceFactory(IScopedServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } - public TInterface GetProcessor(Type openGenericType, Type resourceType) - => GetProcessorInternal(openGenericType, resourceType); + public TInterface Get(Type openGenericType, Type resourceType) + => GetInternal(openGenericType, resourceType); - public TInterface GetProcessor(Type openGenericType, Type resourceType, Type keyType) - => GetProcessorInternal(openGenericType, resourceType, keyType); + public TInterface Get(Type openGenericType, Type resourceType, Type keyType) + => GetInternal(openGenericType, resourceType, keyType); - private TInterface GetProcessorInternal(Type openGenericType, params Type[] types) + private TInterface GetInternal(Type openGenericType, params Type[] types) { var concreteType = openGenericType.MakeGenericType(types); diff --git a/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs b/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs index ee878b4831..c98560015e 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/HasManyThroughUpdateHelper.cs @@ -12,18 +12,17 @@ namespace JsonApiDotNetCore.Internal.Generics { /// /// A special helper service that gets instantiated for the right-type of a many-to-many relationship and is responsible for - /// processing updates for that relationships + /// processing updates for that relationships. /// public interface IHasManyThroughUpdateHelper { + /// + /// Processes updates of has many through relationship. + /// Task UpdateAsync(IIdentifiable parent, HasManyThroughAttribute relationship, IEnumerable relationshipIds); } - /// - /// A special processor that gets instantiated for a generic type (<T>) - /// when the actual type is not known until runtime. Specifically, this is used for updating - /// relationships. - /// + /// public class HasManyThroughUpdateHelper : IHasManyThroughUpdateHelper where T : class { private readonly DbContext _context; @@ -32,6 +31,7 @@ public HasManyThroughUpdateHelper(IDbContextResolver contextResolver) _context = contextResolver.GetContext(); } + /// public virtual async Task UpdateAsync(IIdentifiable parent, HasManyThroughAttribute relationship, IEnumerable relationshipIds) { // we need to create a transaction for the HasManyThrough case so we can get and remove any existing diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index a42b3484c6..c0ece81984 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -141,9 +141,9 @@ protected List CreateTodoWithOwner() public class HooksTestsSetup : HooksDummyData { - (Mock, Mock, Mock, IJsonApiOptions) CreateMocks() + (Mock, Mock, Mock, IJsonApiOptions) CreateMocks() { - var pfMock = new Mock(); + var pfMock = new Mock(); var ufMock = new Mock(); var iqsMock = new Mock(); var optionsMock = new JsonApiOptions { LoaDatabaseValues = false }; @@ -156,7 +156,7 @@ public class HooksTestsSetup : HooksDummyData // creates the resource definition mock and corresponding ImplementedHooks discovery instance var mainResource = CreateResourceDefinition(mainDiscovery); - // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + // mocking the genericServiceFactory and JsonApiContext and wiring them up. var (ufMock, iqMock, gpfMock, options) = CreateMocks(); SetupProcessorFactoryForResourceDefinition(gpfMock, mainResource.Object, mainDiscovery, null); @@ -181,7 +181,7 @@ public class HooksTestsSetup : HooksDummyData var mainResource = CreateResourceDefinition(mainDiscovery); var nestedResource = CreateResourceDefinition(nestedDiscovery); - // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + // mocking the genericServiceFactory and JsonApiContext and wiring them up. var (ufMock, iqMock, gpfMock, options) = CreateMocks(); var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; @@ -212,7 +212,7 @@ public class HooksTestsSetup : HooksDummyData var firstNestedResource = CreateResourceDefinition(firstNestedDiscovery); var secondNestedResource = CreateResourceDefinition(secondNestedDiscovery); - // mocking the GenericProcessorFactory and JsonApiContext and wiring them up. + // mocking the genericServiceFactory and JsonApiContext and wiring them up. var (ufMock, iqMock, gpfMock, options) = CreateMocks(); var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; @@ -311,17 +311,17 @@ void MockHooks(Mock> resourceDefinition) } void SetupProcessorFactoryForResourceDefinition( - Mock processorFactory, + Mock processorFactory, IResourceHookContainer modelResource, IHooksDiscovery discovery, AppDbContext dbContext = null ) where TModel : class, IIdentifiable { - processorFactory.Setup(c => c.GetProcessor(typeof(ResourceDefinition<>), typeof(TModel))) + processorFactory.Setup(c => c.Gett<(typeof(ResourceDefinition<>), typeof(TModel))) .Returns(modelResource); - processorFactory.Setup(c => c.GetProcessor(typeof(IHooksDiscovery<>), typeof(TModel))) + processorFactory.Setup(c => c.Gett<(typeof(IHooksDiscovery<>), typeof(TModel))) .Returns(discovery); if (dbContext != null) @@ -330,7 +330,7 @@ void SetupProcessorFactoryForResourceDefinition( if (idType == typeof(int)) { IResourceReadRepository repo = CreateTestRepository(dbContext); - processorFactory.Setup(c => c.GetProcessor>(typeof(IResourceReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); + processorFactory.Setup(c => c.Gett<>(typeof(IResourceReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); } else { From 354a1be763dda9ca020e2fb13148d51e3420662d Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 22 Oct 2019 16:15:18 +0200 Subject: [PATCH 29/29] chore: fix NoEntityFrameworkExample project --- .../Controllers/ArticlesController.cs | 1 - .../Controllers/PeopleController.cs | 1 - .../GettingStarted/Data/SampleDbContext.cs | 5 +-- src/Examples/GettingStarted/Models/Person.cs | 1 - .../ModelDefinition.cs | 16 ++++---- .../ModelsController.cs | 1 - src/Examples/GettingStarted/Startup.cs | 9 +---- .../Builders/JsonApiApplicationBuilder.cs | 2 +- .../Data/DefaultResourceRepository.cs | 8 ++-- .../Hooks/Execution/HookExecutorHelper.cs | 10 ++--- .../Generics/GenericServiceFactory.cs | 4 +- ...dleware.cs => CurrentRequestMiddleware.cs} | 2 - .../Models/ResourceDefinition.cs | 8 ++-- .../Contracts/ICurrentRequest.cs | 4 +- .../RequestServices/CurrentRequest.cs | 6 +-- .../Client/ResponseDeserializer.cs | 8 ++-- .../Common/BaseDocumentParser.cs | 10 ++--- .../ServiceDiscoveryFacadeTests.cs | 2 - .../Acceptance/TestFixture.cs | 1 - .../Extensibility/NoEntityFrameworkTests.cs | 9 ++--- test/NoEntityFrameworkTests/TestFixture.cs | 37 ++++++++++++++++++- .../IServiceCollectionExtensionsTests.cs | 2 +- .../SparseFieldsServiceTests.cs | 20 +++++----- .../ResourceHooks/ResourceHooksTestsSetup.cs | 6 +-- 24 files changed, 93 insertions(+), 80 deletions(-) rename src/JsonApiDotNetCore/Middleware/{RequestMiddleware.cs => CurrentRequestMiddleware.cs} (99%) diff --git a/src/Examples/GettingStarted/Controllers/ArticlesController.cs b/src/Examples/GettingStarted/Controllers/ArticlesController.cs index 135391e9eb..2bc928a46f 100644 --- a/src/Examples/GettingStarted/Controllers/ArticlesController.cs +++ b/src/Examples/GettingStarted/Controllers/ArticlesController.cs @@ -1,7 +1,6 @@ using GettingStarted.Models; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; namespace GettingStarted diff --git a/src/Examples/GettingStarted/Controllers/PeopleController.cs b/src/Examples/GettingStarted/Controllers/PeopleController.cs index 0725bbebaa..95eac64346 100644 --- a/src/Examples/GettingStarted/Controllers/PeopleController.cs +++ b/src/Examples/GettingStarted/Controllers/PeopleController.cs @@ -1,7 +1,6 @@ using GettingStarted.Models; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; namespace GettingStarted diff --git a/src/Examples/GettingStarted/Data/SampleDbContext.cs b/src/Examples/GettingStarted/Data/SampleDbContext.cs index 2f8fefb405..ede5e02baf 100644 --- a/src/Examples/GettingStarted/Data/SampleDbContext.cs +++ b/src/Examples/GettingStarted/Data/SampleDbContext.cs @@ -6,10 +6,7 @@ namespace GettingStarted { public class SampleDbContext : DbContext { - public SampleDbContext(DbContextOptions options) - : base(options) - { } - + public SampleDbContext(DbContextOptions options) : base(options) { } public DbSet
    Articles { get; set; } public DbSet People { get; set; } public DbSet Models { get; set; } diff --git a/src/Examples/GettingStarted/Models/Person.cs b/src/Examples/GettingStarted/Models/Person.cs index 625cf26ab6..39b59a44bb 100644 --- a/src/Examples/GettingStarted/Models/Person.cs +++ b/src/Examples/GettingStarted/Models/Person.cs @@ -7,7 +7,6 @@ public class Person : Identifiable { [Attr] public string Name { get; set; } - [HasMany] public List
    Articles { get; set; } } diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs index 4e82b45763..6fd6a131f4 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs +++ b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs @@ -6,16 +6,14 @@ namespace GettingStarted.ResourceDefinitionExample { public class ModelDefinition : ResourceDefinition { - public ModelDefinition(IResourceContextProvider provider) : base(provider) + public ModelDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { + // this allows POST / PATCH requests to set the value of a + // property, but we don't include this value in the response + // this might be used if the incoming value gets hashed or + // encrypted prior to being persisted and this value should + // never be sent back to the client + HideFields(model => model.DontExpose); } - - // this allows POST / PATCH requests to set the value of a - // property, but we don't include this value in the response - // this might be used if the incoming value gets hashed or - // encrypted prior to being persisted and this value should - // never be sent back to the client - protected override List OutputAttrs() - => Remove(model => model.DontExpose); } } diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs index 6a92b4f8f3..1b488ed383 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs +++ b/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Services; namespace GettingStarted.ResourceDefinitionExample diff --git a/src/Examples/GettingStarted/Startup.cs b/src/Examples/GettingStarted/Startup.cs index d5c805282b..f3e98948c8 100644 --- a/src/Examples/GettingStarted/Startup.cs +++ b/src/Examples/GettingStarted/Startup.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using JsonApiDotNetCore.Extensions; @@ -23,7 +18,7 @@ public void ConfigureServices(IServiceCollection services) var mvcBuilder = services.AddMvcCore(); services.AddJsonApi( options => options.Namespace = "api", - discover => discover.AddCurrentAssembly(), mvcBuilder); + discover => discover.AddCurrentAssembly(), mvcBuilder: mvcBuilder); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, SampleDbContext context) diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index bd27dc9c46..7942e3774c 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -154,7 +154,7 @@ public void ConfigureServices() _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); - _services.AddScoped(); + _services.AddScoped(); _services.AddScoped(typeof(HasManyThroughUpdateHelper<>)); _services.AddScoped(); _services.AddScoped(); diff --git a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs index 49c5b6a0ae..117fb450e3 100644 --- a/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultResourceRepository.cs @@ -423,18 +423,18 @@ public class DefaultResourceRepository : DefaultResourceRepository _hookContainers; protected readonly Dictionary _hookDiscoveries; protected readonly List _targetedHooksForRelatedEntities; - public HookExecutorHelper(IGenericProcessorFactory genericProcessorFactory, + public HookExecutorHelper(IGenericServiceFactory genericProcessorFactory, IJsonApiOptions options) { _options = options; @@ -43,7 +43,7 @@ public IResourceHookContainer GetResourceHookContainer(RightType rightType, Reso /// so we need not even bother. if (!_hookContainers.TryGetValue(rightType, out IResourceHookContainer container)) { - container = (_genericProcessorFactory.GetProcessor(typeof(ResourceDefinition<>), rightType)); + container = (_genericProcessorFactory.Get(typeof(ResourceDefinition<>), rightType)); _hookContainers[rightType] = container; } if (container == null) return container; @@ -123,7 +123,7 @@ IHooksDiscovery GetHookDiscovery(Type entityType) { if (!_hookDiscoveries.TryGetValue(entityType, out IHooksDiscovery discovery)) { - discovery = _genericProcessorFactory.GetProcessor(typeof(IHooksDiscovery<>), entityType); + discovery = _genericProcessorFactory.Get(typeof(IHooksDiscovery<>), entityType); _hookDiscoveries[entityType] = discovery; } return discovery; @@ -138,7 +138,7 @@ IEnumerable GetWhereAndInclude(IEnumerable ids, IResourceReadRepository GetRepository() where TResource : class, IIdentifiable { - return _genericProcessorFactory.GetProcessor>(typeof(IResourceReadRepository<,>), typeof(TResource), typeof(TId)); + return _genericProcessorFactory.Get>(typeof(IResourceReadRepository<,>), typeof(TResource), typeof(TId)); } diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericServiceFactory.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericServiceFactory.cs index a0b67e742f..bc834f4733 100644 --- a/src/JsonApiDotNetCore/Internal/Generics/GenericServiceFactory.cs +++ b/src/JsonApiDotNetCore/Internal/Generics/GenericServiceFactory.cs @@ -15,7 +15,7 @@ public interface IGenericServiceFactory ///
    /// /// - /// GetProcessor<IGenericProcessor>(typeof(GenericProcessor<>), typeof(TResource)); + /// Get<IGenericProcessor>(typeof(GenericProcessor<>), typeof(TResource)); /// /// TInterface Get(Type openGenericType, Type resourceType); @@ -25,7 +25,7 @@ public interface IGenericServiceFactory ///
    /// /// - /// GetProcessor<IGenericProcessor>(typeof(GenericProcessor<,>), typeof(TResource), typeof(TId)); + /// Get<IGenericProcessor>(typeof(GenericProcessor<,>), typeof(TResource), typeof(TId)); /// /// TInterface Get(Type openGenericType, Type resourceType, Type keyType); diff --git a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs b/src/JsonApiDotNetCore/Middleware/CurrentRequestMiddleware.cs similarity index 99% rename from src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs rename to src/JsonApiDotNetCore/Middleware/CurrentRequestMiddleware.cs index 0d36826b7c..8b8027120a 100644 --- a/src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/CurrentRequestMiddleware.cs @@ -12,8 +12,6 @@ namespace JsonApiDotNetCore.Middleware { /// - /// Can be overwritten to help you out during testing - /// /// This sets all necessary parameters relating to the HttpContext for JADNC /// public class CurrentRequestMiddleware diff --git a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs index a27eb2ca6c..6f7798e484 100644 --- a/src/JsonApiDotNetCore/Models/ResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Models/ResourceDefinition.cs @@ -26,15 +26,15 @@ public interface IResourceDefinition /// The resource type public class ResourceDefinition : IResourceDefinition, IResourceHookContainer where TResource : class, IIdentifiable { - private readonly ResourceContext _contextEntity; + private readonly ResourceContext _resourceContext; private readonly IResourceGraph _resourceGraph; private List _allowedAttributes; private List _allowedRelationships; public ResourceDefinition(IResourceGraph resourceGraph) { - _contextEntity = resourceGraph.GetResourceContext(typeof(TResource)); - _allowedAttributes = _contextEntity.Attributes; - _allowedRelationships = _contextEntity.Relationships; + _resourceContext = resourceGraph.GetResourceContext(typeof(TResource)); + _allowedAttributes = _resourceContext.Attributes; + _allowedRelationships = _resourceContext.Relationships; _resourceGraph = resourceGraph; } diff --git a/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs b/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs index b6e2171c1a..0260db8c91 100644 --- a/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs +++ b/src/JsonApiDotNetCore/RequestServices/Contracts/ICurrentRequest.cs @@ -33,8 +33,8 @@ public interface ICurrentRequest /// /// Sets the current context entity for this entire request /// - /// - void SetRequestResource(ResourceContext contextEntityCurrent); + /// + void SetRequestResource(ResourceContext currentResourceContext); ResourceContext GetRequestResource(); } diff --git a/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs b/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs index 9648e06e6b..abb1b5863a 100644 --- a/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs +++ b/src/JsonApiDotNetCore/RequestServices/CurrentRequest.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCore.Managers { class CurrentRequest : ICurrentRequest { - private ResourceContext _contextEntity; + private ResourceContext _resourceContext; public string BasePath { get; set; } public bool IsRelationshipPath { get; set; } public RelationshipAttribute RequestRelationship { get; set; } @@ -17,12 +17,12 @@ class CurrentRequest : ICurrentRequest /// public ResourceContext GetRequestResource() { - return _contextEntity; + return _resourceContext; } public void SetRequestResource(ResourceContext primaryResource) { - _contextEntity = primaryResource; + _resourceContext = primaryResource; } } } diff --git a/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs b/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs index b0fa4f562c..b5446325ad 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs @@ -91,12 +91,12 @@ private IIdentifiable ParseIncludedRelationship(RelationshipAttribute relationsh if (includedResource == null) return relatedInstance; - var contextEntity = _provider.GetResourceContext(relatedResourceIdentifier.Type); - if (contextEntity == null) + var resourceContext = _provider.GetResourceContext(relatedResourceIdentifier.Type); + if (resourceContext == null) throw new InvalidOperationException($"Included type '{relationshipAttr.RightType}' is not a registered json:api resource."); - SetAttributes(relatedInstance, includedResource.Attributes, contextEntity.Attributes); - SetRelationships(relatedInstance, includedResource.Relationships, contextEntity.Relationships); + SetAttributes(relatedInstance, includedResource.Attributes, resourceContext.Attributes); + SetRelationships(relatedInstance, includedResource.Relationships, resourceContext.Relationships); return relatedInstance; } diff --git a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs index 383c6bd29d..ede8418eed 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs @@ -128,8 +128,8 @@ private JToken LoadJToken(string body) /// The parsed entity private IIdentifiable ParseResourceObject(ResourceObject data) { - var contextEntity = _provider.GetResourceContext(data.Type); - if (contextEntity == null) + var resourceContext = _provider.GetResourceContext(data.Type); + if (resourceContext == null) { throw new JsonApiException(400, message: $"This API does not contain a json:api resource named '{data.Type}'.", @@ -138,10 +138,10 @@ private IIdentifiable ParseResourceObject(ResourceObject data) + "If you have manually registered the resource, check that the call to AddResource correctly sets the public name."); } - var entity = (IIdentifiable)Activator.CreateInstance(contextEntity.ResourceType); + var entity = (IIdentifiable)Activator.CreateInstance(resourceContext.ResourceType); - entity = SetAttributes(entity, data.Attributes, contextEntity.Attributes); - entity = SetRelationships(entity, data.Relationships, contextEntity.Relationships); + entity = SetAttributes(entity, data.Attributes, resourceContext.Attributes); + entity = SetRelationships(entity, data.Relationships, resourceContext.Relationships); if (data.Id != null) entity.StringId = data.Id?.ToString(); diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index 2bd0e9bdbd..f7cbb834bd 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -5,9 +5,7 @@ using JsonApiDotNetCore.Data; using JsonApiDotNetCore.Graph; using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Internal.Contracts; -using JsonApiDotNetCore.Managers.Contracts; using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs index a0def3595c..f4db2a89b1 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs @@ -18,7 +18,6 @@ public class TestFixture : IDisposable where TStartup : class { private readonly TestServer _server; private IServiceProvider _services; - public TestFixture() { var builder = new WebHostBuilder() diff --git a/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs b/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs index e964da8d1e..4ff89428f2 100644 --- a/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs +++ b/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs @@ -35,8 +35,7 @@ public async Task Can_Get_TodoItems() // act var response = await client.SendAsync(request); var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = _fixture.Server.GetDeserializer() - .DeserializeList(responseBody); + var deserializedBody = _fixture.GetDeserializer().DeserializeList(responseBody).Data; // assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -62,8 +61,7 @@ public async Task Can_Get_TodoItems_By_Id() // act var response = await client.SendAsync(request); var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = (TodoItem)_fixture.Server.GetDeserializer() - .Deserialize(responseBody); + var deserializedBody = _fixture.GetDeserializer().DeserializeSingle(responseBody).Data; // assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -99,8 +97,7 @@ public async Task Can_Create_TodoItems() // act var response = await client.SendAsync(request); var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = (TodoItem)_fixture.Server.GetDeserializer() - .Deserialize(responseBody); + var deserializedBody = _fixture.GetDeserializer().DeserializeSingle(responseBody).Data; // assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); diff --git a/test/NoEntityFrameworkTests/TestFixture.cs b/test/NoEntityFrameworkTests/TestFixture.cs index 3a317e03cd..4903306613 100644 --- a/test/NoEntityFrameworkTests/TestFixture.cs +++ b/test/NoEntityFrameworkTests/TestFixture.cs @@ -1,8 +1,14 @@ +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Serialization.Client; using JsonApiDotNetCoreExample.Data; +using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCoreExampleTests.Helpers.Extensions; +using JsonApiDotNetCoreExampleTests.Helpers.Models; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using System; +using System.Linq.Expressions; namespace NoEntityFrameworkTests { @@ -10,14 +16,43 @@ public class TestFixture : IDisposable { public AppDbContext Context { get; private set; } public TestServer Server { get; private set; } - + private IServiceProvider _services; public TestFixture() { var builder = new WebHostBuilder().UseStartup(); Server = new TestServer(builder); Context = Server.GetService(); Context.Database.EnsureCreated(); + _services = Server.Host.Services; + } + + public IRequestSerializer GetSerializer(Expression> attributes = null, Expression> relationships = null) where TResource : class, IIdentifiable + { + var serializer = GetService(); + if (attributes != null) + serializer.SetAttributesToSerialize(attributes); + if (relationships != null) + serializer.SetRelationshipsToSerialize(relationships); + return serializer; } + public IResponseDeserializer GetDeserializer() + { + var resourceGraph = new ResourceGraphBuilder() + .AddResource() + .AddResource
    () + .AddResource() + .AddResource() + .AddResource() + .AddResource() + .AddResource() + .AddResource() + .AddResource("todo-items") + .AddResource().Build(); + return new ResponseDeserializer(resourceGraph); + } + + public T GetService() => (T)_services.GetService(typeof(T)); + public void Dispose() { diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index 7522fbe13b..bdcdb002d0 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -55,7 +55,7 @@ public void AddJsonApiInternals_Adds_All_Required_Services() Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService()); - Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); Assert.NotNull(provider.GetService(typeof(HasManyThroughUpdateHelper))); } diff --git a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs index 6a405ea2bf..17c59083c6 100644 --- a/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs +++ b/test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs @@ -10,9 +10,9 @@ namespace UnitTests.QueryParameters { public class SparseFieldsServiceTests : QueryParametersUnitTestCollection { - public SparseFieldsService GetService(ResourceContext contextEntity = null) + public SparseFieldsService GetService(ResourceContext resourceContext = null) { - return new SparseFieldsService(_resourceGraph, MockCurrentRequest(contextEntity ?? _articleResourceContext)); + return new SparseFieldsService(_resourceGraph, MockCurrentRequest(resourceContext ?? _articleResourceContext)); } [Fact] @@ -40,13 +40,13 @@ public void Parse_ValidSelection_CanParse() var query = new KeyValuePair($"fields", new StringValues(attrName)); - var contextEntity = new ResourceContext + var resourceContext = new ResourceContext { ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; - var service = GetService(contextEntity); + var service = GetService(resourceContext); // act service.Parse(query); @@ -70,13 +70,13 @@ public void Parse_TypeNameAsNavigation_ThrowsJsonApiException() var query = new KeyValuePair($"fields[{type}]", new StringValues(attrName)); - var contextEntity = new ResourceContext + var resourceContext = new ResourceContext { ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; - var service = GetService(contextEntity); + var service = GetService(resourceContext); // act, assert var ex = Assert.Throws(() => service.Parse(query)); @@ -96,13 +96,13 @@ public void Parse_DeeplyNestedSelection_ThrowsJsonApiException() var query = new KeyValuePair($"fields[{relationship}]", new StringValues(attrName)); - var contextEntity = new ResourceContext + var resourceContext = new ResourceContext { ResourceName = type, Attributes = new List { attribute, idAttribute }, Relationships = new List() }; - var service = GetService(contextEntity); + var service = GetService(resourceContext); // act, assert var ex = Assert.Throws(() => service.Parse(query)); @@ -118,14 +118,14 @@ public void Parse_InvalidField_ThrowsJsonApiException() var query = new KeyValuePair($"fields[{type}]", new StringValues(attrName)); - var contextEntity = new ResourceContext + var resourceContext = new ResourceContext { ResourceName = type, Attributes = new List(), Relationships = new List() }; - var service = GetService(contextEntity); + var service = GetService(resourceContext); // act , assert var ex = Assert.Throws(() => service.Parse(query)); diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index c0ece81984..0e2d136535 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -318,10 +318,10 @@ void SetupProcessorFactoryForResourceDefinition( ) where TModel : class, IIdentifiable { - processorFactory.Setup(c => c.Gett<(typeof(ResourceDefinition<>), typeof(TModel))) + processorFactory.Setup(c => c.Get(typeof(ResourceDefinition<>), typeof(TModel))) .Returns(modelResource); - processorFactory.Setup(c => c.Gett<(typeof(IHooksDiscovery<>), typeof(TModel))) + processorFactory.Setup(c => c.Get(typeof(IHooksDiscovery<>), typeof(TModel))) .Returns(discovery); if (dbContext != null) @@ -330,7 +330,7 @@ void SetupProcessorFactoryForResourceDefinition( if (idType == typeof(int)) { IResourceReadRepository repo = CreateTestRepository(dbContext); - processorFactory.Setup(c => c.Gett<>(typeof(IResourceReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); + processorFactory.Setup(c => c.Get>(typeof(IResourceReadRepository<,>), typeof(TModel), typeof(int))).Returns(repo); } else {