Skip to content

Generic resource definitions #832

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 18, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions benchmarks/DependencyFactory.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Resources;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;

namespace Benchmarks
{
Expand All @@ -14,15 +11,5 @@ public static IResourceGraph CreateResourceGraph(IJsonApiOptions options)
builder.Add<BenchmarkResource>(BenchmarkResourcePublicNames.Type);
return builder.Build();
}

public static IResourceDefinitionProvider CreateResourceDefinitionProvider(IResourceGraph resourceGraph)
{
var resourceDefinition = new ResourceDefinition<BenchmarkResource>(resourceGraph);

var resourceDefinitionProviderMock = new Mock<IResourceDefinitionProvider>();
resourceDefinitionProviderMock.Setup(provider => provider.Get(It.IsAny<Type>())).Returns(resourceDefinition);

return resourceDefinitionProviderMock.Object;
}
}
}
5 changes: 3 additions & 2 deletions benchmarks/Serialization/JsonApiSerializerBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Queries;
using JsonApiDotNetCore.QueryStrings.Internal;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Serialization;
using JsonApiDotNetCore.Serialization.Building;
using Moq;
Expand Down Expand Up @@ -46,9 +47,9 @@ private static FieldsToSerialize CreateFieldsToSerialize(IResourceGraph resource
new SparseFieldSetQueryStringParameterReader(request, resourceGraph)
};

var resourceDefinitionProvider = DependencyFactory.CreateResourceDefinitionProvider(resourceGraph);
var accessor = new Mock<IResourceDefinitionAccessor>().Object;

return new FieldsToSerialize(resourceGraph, constraintProviders, resourceDefinitionProvider);
return new FieldsToSerialize(resourceGraph, constraintProviders, accessor);
}

[Benchmark]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

namespace JsonApiDotNetCoreExample.Definitions
{
public class ArticleDefinition : ResourceDefinition<Article>
public class ArticleHooksDefinition : ResourceHooksDefinition<Article>
{
public ArticleDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }
public ArticleHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }

public override IEnumerable<Article> OnReturn(HashSet<Article> resources, ResourcePipeline pipeline)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

namespace JsonApiDotNetCoreExample.Definitions
{
public abstract class LockableDefinition<T> : ResourceDefinition<T> where T : class, IIsLockable, IIdentifiable
public abstract class LockableHooksDefinition<T> : ResourceHooksDefinition<T> where T : class, IIsLockable, IIdentifiable
{
protected LockableDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }
protected LockableHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }

protected void DisallowLocked(IEnumerable<T> resources)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

namespace JsonApiDotNetCoreExample.Definitions
{
public class PassportDefinition : ResourceDefinition<Passport>
public class PassportHooksDefinition : ResourceHooksDefinition<Passport>
{
public PassportDefinition(IResourceGraph resourceGraph) : base(resourceGraph)
public PassportHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph)
{
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
using System.Collections.Generic;
using System.Linq;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Hooks.Internal.Execution;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCoreExample.Models;

namespace JsonApiDotNetCoreExample.Definitions
{
public class PersonDefinition : LockableDefinition<Person>, IHasMeta
public class PersonDefinition : JsonApiResourceDefinition<Person>
{
public PersonDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }

public override IEnumerable<string> BeforeUpdateRelationship(HashSet<string> ids, IRelationshipsDictionary<Person> resourcesByRelationship, ResourcePipeline pipeline)
{
BeforeImplicitUpdateRelationship(resourcesByRelationship, pipeline);
return ids;
}

public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary<Person> resourcesByRelationship, ResourcePipeline pipeline)
public PersonDefinition(IResourceGraph resourceGraph) : base(resourceGraph)
{
resourcesByRelationship.GetByRelationship<Passport>().ToList().ForEach(kvp => DisallowLocked(kvp.Value));
}

public IReadOnlyDictionary<string, object> GetMeta()
public override IReadOnlyDictionary<string, object> GetMeta()
{
return new Dictionary<string, object> {
{ "copyright", "Copyright 2015 Example Corp." },
{ "authors", new[] { "Jared Nance", "Maurits Moeys", "Harro van der Kroft" } }
return new Dictionary<string, object>
{
["license"] = "MIT",
["projectUrl"] = "https://github.com/json-api-dotnet/JsonApiDotNetCore/",
["versions"] = new[]
{
"v4.0.0",
"v3.1.0",
"v2.5.2",
"v1.3.1"
}
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Hooks.Internal.Execution;
using JsonApiDotNetCoreExample.Models;

namespace JsonApiDotNetCoreExample.Definitions
{
public class PersonHooksDefinition : LockableHooksDefinition<Person>
{
public PersonHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }

public override IEnumerable<string> BeforeUpdateRelationship(HashSet<string> ids, IRelationshipsDictionary<Person> resourcesByRelationship, ResourcePipeline pipeline)
{
BeforeImplicitUpdateRelationship(resourcesByRelationship, pipeline);
return ids;
}

public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary<Person> resourcesByRelationship, ResourcePipeline pipeline)
{
resourcesByRelationship.GetByRelationship<Passport>().ToList().ForEach(kvp => DisallowLocked(kvp.Value));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

namespace JsonApiDotNetCoreExample.Definitions
{
public class TagDefinition : ResourceDefinition<Tag>
public class TagHooksDefinition : ResourceHooksDefinition<Tag>
{
public TagDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }
public TagHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }

public override IEnumerable<Tag> BeforeCreate(IResourceHashSet<Tag> affected, ResourcePipeline pipeline)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

namespace JsonApiDotNetCoreExample.Definitions
{
public class TodoDefinition : LockableDefinition<TodoItem>
public class TodoHooksDefinition : LockableHooksDefinition<TodoItem>
{
public TodoDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }
public TodoHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }

public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null)
{
Expand Down
45 changes: 27 additions & 18 deletions src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public JsonApiApplicationBuilder(IServiceCollection services, IMvcCoreBuilder mv
var loggerFactory = _intermediateProvider.GetService<ILoggerFactory>();

_resourceGraphBuilder = new ResourceGraphBuilder(_options, loggerFactory);
_serviceDiscoveryFacade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, loggerFactory);
_serviceDiscoveryFacade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, loggerFactory);
}

/// <summary>
Expand Down Expand Up @@ -114,7 +114,7 @@ public void DiscoverInjectables()
/// <summary>
/// Registers the remaining internals.
/// </summary>
public void ConfigureServices(Type dbContextType)
public void ConfigureServiceContainer(Type dbContextType)
{
if (dbContextType != null)
{
Expand All @@ -127,28 +127,23 @@ public void ConfigureServices(Type dbContextType)
_services.AddSingleton(new DbContextOptionsBuilder().Options);
}

AddResourceLayer();
AddRepositoryLayer();
AddServiceLayer();
AddMiddlewareLayer();
AddSerializationLayer();
AddQueryStringLayer();

_services.AddSingleton<IResourceContextProvider>(sp => sp.GetRequiredService<IResourceGraph>());

_services.AddScoped<IGenericServiceFactory, GenericServiceFactory>();
_services.AddScoped(typeof(RepositoryRelationshipUpdateHelper<>));
_services.AddScoped<IResourceDefinitionProvider, ResourceDefinitionProvider>();
_services.AddScoped(typeof(IResourceChangeTracker<>), typeof(ResourceChangeTracker<>));
_services.AddScoped<IResourceFactory, ResourceFactory>();
_services.AddScoped<IPaginationContext, PaginationContext>();
_services.AddScoped<IQueryLayerComposer, QueryLayerComposer>();

AddServerSerialization();
AddQueryStringParameterServices();

if (_options.EnableResourceHooks)
{
AddResourceHooks();
}

_services.AddScoped<IGenericServiceFactory, GenericServiceFactory>();
_services.AddScoped(typeof(RepositoryRelationshipUpdateHelper<>));
_services.AddScoped(typeof(IResourceChangeTracker<>), typeof(ResourceChangeTracker<>));
_services.AddScoped<IPaginationContext, PaginationContext>();
_services.AddScoped<IQueryLayerComposer, QueryLayerComposer>();
_services.TryAddScoped<IInverseRelationships, InverseRelationships>();
}

Expand All @@ -173,6 +168,17 @@ private void AddMiddlewareLayer()
_services.AddScoped<IFieldsToSerialize, FieldsToSerialize>();
}

private void AddResourceLayer()
{
_services.AddScoped(typeof(IResourceDefinition<>), typeof(JsonApiResourceDefinition<>));
_services.AddScoped(typeof(IResourceDefinition<,>), typeof(JsonApiResourceDefinition<,>));
_services.AddScoped<IResourceDefinitionAccessor, ResourceDefinitionAccessor>();

_services.AddScoped<IResourceFactory, ResourceFactory>();

_services.AddSingleton<IResourceContextProvider>(sp => sp.GetRequiredService<IResourceGraph>());
}

private void AddRepositoryLayer()
{
_services.AddScoped(typeof(IResourceRepository<>), typeof(EntityFrameworkCoreRepository<>));
Expand Down Expand Up @@ -208,11 +214,14 @@ private void AddServiceLayer()
_services.AddScoped(typeof(IResourceService<>), typeof(JsonApiResourceService<>));
_services.AddScoped(typeof(IResourceService<,>), typeof(JsonApiResourceService<,>));

_services.AddScoped(typeof(IResourceQueryService<>), typeof(JsonApiResourceService<>));
_services.AddScoped(typeof(IResourceQueryService<,>), typeof(JsonApiResourceService<,>));

_services.AddScoped(typeof(IResourceCommandService<>), typeof(JsonApiResourceService<>));
_services.AddScoped(typeof(IResourceCommandService<,>), typeof(JsonApiResourceService<,>));
}

private void AddQueryStringParameterServices()
private void AddQueryStringLayer()
{
_services.AddScoped<IIncludeQueryStringParameterReader, IncludeQueryStringParameterReader>();
_services.AddScoped<IFilterQueryStringParameterReader, FilterQueryStringParameterReader>();
Expand Down Expand Up @@ -246,13 +255,13 @@ private void AddQueryStringParameterServices()
private void AddResourceHooks()
{
_services.AddSingleton(typeof(IHooksDiscovery<>), typeof(HooksDiscovery<>));
_services.AddScoped(typeof(IResourceHookContainer<>), typeof(ResourceDefinition<>));
_services.AddScoped(typeof(IResourceHookContainer<>), typeof(ResourceHooksDefinition<>));
_services.AddTransient(typeof(IResourceHookExecutor), typeof(ResourceHookExecutor));
_services.AddTransient<IHookExecutorHelper, HookExecutorHelper>();
_services.AddTransient<ITraversalHelper, TraversalHelper>();
}

private void AddServerSerialization()
private void AddSerializationLayer()
{
_services.AddScoped<IIncludedResourceObjectBuilder, IncludedResourceObjectBuilder>();
_services.AddScoped<IJsonApiDeserializer, RequestDeserializer>();
Expand Down
7 changes: 0 additions & 7 deletions src/JsonApiDotNetCore/Configuration/ResourceContext.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;

namespace JsonApiDotNetCore.Configuration
Expand All @@ -26,12 +25,6 @@ public class ResourceContext
/// </summary>
public Type IdentityType { get; set; }

/// <summary>
/// The concrete <see cref="ResourceDefinition{TResource}"/> type.
/// We store this so that we don't need to re-compute the generic type.
/// </summary>
public Type ResourceDefinitionType { get; set; }

/// <summary>
/// Exposed resource attributes.
/// See https://jsonapi.org/format/#document-resource-object-attributes.
Expand Down
3 changes: 0 additions & 3 deletions src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ public ResourceGraphBuilder Add(Type resourceType, Type idType = null, string pu
Attributes = GetAttributes(resourceType),
Relationships = GetRelationships(resourceType),
EagerLoads = GetEagerLoads(resourceType),
ResourceDefinitionType = GetResourceDefinitionType(resourceType)
};

private IReadOnlyCollection<AttrAttribute> GetAttributes(Type resourceType)
Expand Down Expand Up @@ -269,8 +268,6 @@ private Type TypeOrElementType(Type type)
return interfaces.Length == 1 ? interfaces.Single().GenericTypeArguments[0] : type;
}

private Type GetResourceDefinitionType(Type resourceType) => typeof(ResourceDefinition<>).MakeGenericType(resourceType);

private string FormatResourceName(Type resourceType)
{
var formatter = new ResourceNameFormatter(_options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using JsonApiDotNetCore.Serialization.Client.Internal;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace JsonApiDotNetCore.Configuration
{
Expand Down Expand Up @@ -57,7 +56,7 @@ private static void SetupApplicationBuilder(IServiceCollection services, Action<
applicationBuilder.AddResourceGraph(dbContextType, configureResourceGraph);
applicationBuilder.ConfigureMvc();
applicationBuilder.DiscoverInjectables();
applicationBuilder.ConfigureServices(dbContextType);
applicationBuilder.ConfigureServiceContainer(dbContextType);
}

/// <summary>
Expand Down
Loading