Skip to content

Routing does not respect custom pluralized resource name #805

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 53 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0800bb2
feat: use resource graph in routing convention rather than formatter
maurei Aug 26, 2020
0d97cba
fix: discovery tests
maurei Aug 26, 2020
10efa97
fix: null ref exception
maurei Aug 26, 2020
6d314aa
fix: client generated id tests
maurei Aug 26, 2020
22d8700
fix: self review 1
maurei Aug 26, 2020
0476228
chore: rename to pluralizedResourceName
maurei Aug 26, 2020
af01214
fix: whitespace
maurei Aug 26, 2020
c2484e8
fix: enumeration exception
maurei Aug 26, 2020
9ac7456
fix: remove whitespace
maurei Aug 26, 2020
787002a
fix: review
maurei Aug 27, 2020
dc0c75a
fix: review
maurei Aug 27, 2020
05f0ad9
test: resource definition discovery test
maurei Aug 27, 2020
28e76fa
docs: update routing related docs
maurei Aug 27, 2020
978e9dd
docs: rephrase
maurei Aug 27, 2020
9d90952
chore: review
maurei Aug 27, 2020
d91bf76
Update routing.md
maurei Aug 27, 2020
08ab1ac
Update routing.md
maurei Aug 27, 2020
6496a7f
chore: trigger build
maurei Aug 27, 2020
aa3c122
fix: reduce amount of intermediate service providers
maurei Aug 28, 2020
681d2cc
feat: configure MvcOptions in phase two, removal of intermediate serv…
maurei Aug 28, 2020
de23e9f
fix: tests
maurei Aug 28, 2020
0b788b5
feat: remove intermediate service providers, consistent middleware co…
maurei Aug 28, 2020
9a7ebe0
fix: remove unnecessary parameter usejsonapi
maurei Aug 31, 2020
6ff5883
fix: review round 31/08
maurei Aug 31, 2020
501e0b7
fix: merge fix attempt 1
maurei Aug 31, 2020
a0d4cfc
fix: docs
maurei Aug 31, 2020
60b9640
fix: merge I
maurei Sep 1, 2020
526b0c6
fix: merge II
maurei Sep 1, 2020
21ed990
fix: docs, publicName
maurei Sep 1, 2020
f519342
fix: whitespace
maurei Sep 1, 2020
c51eea3
chore: review
maurei Sep 2, 2020
234cc86
chore: review 2
maurei Sep 2, 2020
21dfc9b
docs: simplify middleware extensibility docs
maurei Sep 3, 2020
4262410
Changed filters and formatters to async
Sep 3, 2020
e9e24eb
docs: simpify routing docs
maurei Sep 3, 2020
e016d37
Merge branch 'fix/786' of https://github.com/json-api-dotnet/JsonApiD…
maurei Sep 3, 2020
5c05503
chore: improve diff readability
maurei Sep 3, 2020
685eef8
chore: diff readability, fixes
maurei Sep 3, 2020
9f6a4f6
fix: more diff readability
maurei Sep 3, 2020
6696492
fix: nullcheck
maurei Sep 3, 2020
540bc8a
docs: resource graph builder inaccuracy
maurei Sep 3, 2020
4268be1
fix: broken links markdown
maurei Sep 3, 2020
234e0ec
fix: broken markdown links
maurei Sep 3, 2020
39cfdde
fix: restore whitespace
maurei Sep 3, 2020
d60ced3
fix: diff
maurei Sep 3, 2020
1cbd61f
fix: whitespace
maurei Sep 3, 2020
7c65d38
chore: review
maurei Sep 9, 2020
e53fe67
fix: typo in docs
maurei Sep 9, 2020
57eb82e
chore: discovery tests fix
maurei Sep 9, 2020
cd1222a
docs: improvements
maurei Sep 9, 2020
0b623be
chore: review
maurei Sep 10, 2020
948b72a
chore: casing convention -> naming convention
maurei Sep 10, 2020
911924c
fix: update example in docs
maurei Sep 10, 2020
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
3 changes: 2 additions & 1 deletion src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using JsonApiDotNetCoreExample.Data;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using JsonApiDotNetCore;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.QueryStrings;
Expand Down Expand Up @@ -39,7 +40,7 @@ public override void ConfigureServices(IServiceCollection services)
}, ServiceLifetime.Transient);

services.AddJsonApi<AppDbContext>(ConfigureJsonApiOptions, discovery => discovery.AddCurrentAssembly());

// once all tests have been moved to WebApplicationFactory format we can get rid of this line below
services.AddClientSerialization();
}
Expand Down
1 change: 1 addition & 0 deletions src/JsonApiDotNetCore/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("UnitTests")]
[assembly:InternalsVisibleTo("DiscoveryTests")]
[assembly:InternalsVisibleTo("JsonApiDotNetCoreExampleTests")]
[assembly:InternalsVisibleTo("NoEntityFrameworkTests")]
[assembly:InternalsVisibleTo("Benchmarks")]
Expand Down
12 changes: 6 additions & 6 deletions src/JsonApiDotNetCore/Builders/IResourceGraphBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,30 @@ public interface IResourceGraphBuilder
/// Add a json:api resource
/// </summary>
/// <typeparam name="TResource">The resource model type</typeparam>
/// <param name="pluralizedTypeName">
/// <param name="pluralizedResourceName">
/// The pluralized name that should be exposed by the API.
/// If nothing is specified, the configured name formatter will be used.
/// </param>
IResourceGraphBuilder AddResource<TResource>(string pluralizedTypeName = null) where TResource : class, IIdentifiable<int>;
IResourceGraphBuilder AddResource<TResource>(string pluralizedResourceName = null) where TResource : class, IIdentifiable<int>;
/// <summary>
/// Add a json:api resource
/// </summary>
/// <typeparam name="TResource">The resource model type</typeparam>
/// <typeparam name="TId">The resource model identifier type</typeparam>
/// <param name="pluralizedTypeName">
/// <param name="pluralizedResourceName">
/// The pluralized name that should be exposed by the API.
/// If nothing is specified, the configured name formatter will be used.
/// </param>
IResourceGraphBuilder AddResource<TResource, TId>(string pluralizedTypeName = null) where TResource : class, IIdentifiable<TId>;
IResourceGraphBuilder AddResource<TResource, TId>(string pluralizedResourceName = null) where TResource : class, IIdentifiable<TId>;
/// <summary>
/// Add a Json:Api resource
/// </summary>
/// <param name="resourceType">The resource model type</param>
/// <param name="idType">The resource model identifier type</param>
/// <param name="pluralizedTypeName">
/// <param name="pluralizedResourceName">
/// The pluralized name that should be exposed by the API.
/// If nothing is specified, the configured name formatter will be used.
/// </param>
IResourceGraphBuilder AddResource(Type resourceType, Type idType = null, string pluralizedTypeName = null);
IResourceGraphBuilder AddResource(Type resourceType, Type idType = null, string pluralizedResourceName = null);
}
}
118 changes: 70 additions & 48 deletions src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ internal sealed class JsonApiApplicationBuilder
private readonly JsonApiOptions _options = new JsonApiOptions();
private IResourceGraphBuilder _resourceGraphBuilder;
private Type _dbContextType;
private Action<IResourceGraphBuilder> _configureResources;
private Action<IServiceDiscoveryFacade> _configureAutoDiscovery;
private readonly IServiceCollection _services;
private IServiceDiscoveryFacade _serviceDiscoveryFacade;
private readonly IMvcCoreBuilder _mvcBuilder;
Expand All @@ -54,33 +56,42 @@ public void ConfigureJsonApiOptions(Action<JsonApiOptions> options)
options?.Invoke(_options);
}

public void RegisterResourceSources(Type dbContextType, Action<IServiceDiscoveryFacade> autoDiscovery,
Action<IResourceGraphBuilder> resources)
{
_dbContextType = dbContextType;
_configureAutoDiscovery = autoDiscovery;
_configureResources = resources;
}

/// <summary>
/// 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:
/// <see cref="IResourceGraphBuilder"/>, <see cref="IServiceDiscoveryFacade"/>, <see cref="IJsonApiExceptionFilterProvider"/>,
/// <see cref="IJsonApiTypeMatchFilterProvider"/> and <see cref="IJsonApiRoutingConvention"/>.
/// </summary>
public void ConfigureMvc(Type dbContextType)
public void ConfigureMvc()
{
RegisterJsonApiStartupServices();


using (var resourceConfigurationIntermediateProvider = _services.BuildServiceProvider())
{
_resourceGraphBuilder = resourceConfigurationIntermediateProvider.GetRequiredService<IResourceGraphBuilder>();
_serviceDiscoveryFacade = resourceConfigurationIntermediateProvider.GetRequiredService<IServiceDiscoveryFacade>();
_services.AddSingleton(BuildResourceGraph(resourceConfigurationIntermediateProvider));
}

IJsonApiExceptionFilterProvider exceptionFilterProvider;
IJsonApiTypeMatchFilterProvider typeMatchFilterProvider;
IJsonApiRoutingConvention routingConvention;

using (var intermediateProvider = _services.BuildServiceProvider())
using (var middlewareConfigurationIntermediateProvider = _services.BuildServiceProvider())
{
_resourceGraphBuilder = intermediateProvider.GetRequiredService<IResourceGraphBuilder>();
_serviceDiscoveryFacade = intermediateProvider.GetRequiredService<IServiceDiscoveryFacade>();
_dbContextType = dbContextType;

AddResourceTypesFromDbContext(intermediateProvider);

exceptionFilterProvider = intermediateProvider.GetRequiredService<IJsonApiExceptionFilterProvider>();
typeMatchFilterProvider = intermediateProvider.GetRequiredService<IJsonApiTypeMatchFilterProvider>();
routingConvention = intermediateProvider.GetRequiredService<IJsonApiRoutingConvention>();
exceptionFilterProvider = middlewareConfigurationIntermediateProvider.GetRequiredService<IJsonApiExceptionFilterProvider>();
typeMatchFilterProvider = middlewareConfigurationIntermediateProvider.GetRequiredService<IJsonApiTypeMatchFilterProvider>();
routingConvention = middlewareConfigurationIntermediateProvider.GetRequiredService<IJsonApiRoutingConvention>();
}

_mvcBuilder.AddMvcOptions(options =>
{
options.EnableEndpointRouting = true;
Expand All @@ -100,42 +111,13 @@ public void ConfigureMvc(Type dbContextType)
_services.AddSingleton<IControllerResourceMapping>(routingConvention);
}

private void AddResourceTypesFromDbContext(ServiceProvider intermediateProvider)
{
if (_dbContextType != null)
{
var dbContext = (DbContext) intermediateProvider.GetRequiredService(_dbContextType);

foreach (var entityType in dbContext.Model.GetEntityTypes())
{
_resourceGraphBuilder.AddResource(entityType.ClrType);
}
}
}

/// <summary>
/// Executes auto-discovery of JADNC services.
/// </summary>
public void AutoDiscover(Action<IServiceDiscoveryFacade> autoDiscover)
{
autoDiscover?.Invoke(_serviceDiscoveryFacade);
}

/// <summary>
/// Executes the action provided by the user to configure the resources using <see cref="IResourceGraphBuilder"/>
/// </summary>
public void ConfigureResources(Action<IResourceGraphBuilder> resources)
{
resources?.Invoke(_resourceGraphBuilder);
}

/// <summary>
/// Registers the remaining internals.
/// </summary>
public void ConfigureServices()
{
var resourceGraph = _resourceGraphBuilder.Build();

_serviceDiscoveryFacade.DiscoverServices();
if (_dbContextType != null)
{
var contextResolverType = typeof(DbContextResolver<>).MakeGenericType(_dbContextType);
Expand Down Expand Up @@ -179,10 +161,9 @@ public void ConfigureServices()

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

_services.AddSingleton(resourceGraph);

_services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
_services.AddSingleton<IResourceContextProvider>(resourceGraph);
_services.AddSingleton<IResourceContextProvider>(sp => sp.GetRequiredService<IResourceGraph>());
_services.AddSingleton<IExceptionHandler, ExceptionHandler>();

_services.AddScoped<ICurrentRequest, CurrentRequest>();
Expand All @@ -202,7 +183,9 @@ public void ConfigureServices()
AddServerSerialization();
AddQueryStringParameterServices();
if (_options.EnableResourceHooks)
{
AddResourceHooks();
}

_services.AddScoped<IInverseRelationships, InverseRelationships>();
}
Expand Down Expand Up @@ -270,5 +253,44 @@ private void RegisterJsonApiStartupServices()
_services.TryAddScoped<IJsonApiExceptionFilterProvider, JsonApiExceptionFilterProvider>();
_services.TryAddScoped<IJsonApiTypeMatchFilterProvider, JsonApiTypeMatchFilterProvider>();
}

private IResourceGraph BuildResourceGraph(ServiceProvider intermediateProvider)
{
AddResourceTypesFromDbContext(intermediateProvider);
AutoDiscoverResources();
ConfigureResources();

return _resourceGraphBuilder.Build();
}

private void AddResourceTypesFromDbContext(ServiceProvider intermediateProvider)
{
if (_dbContextType != null)
{
var dbContext = (DbContext) intermediateProvider.GetRequiredService(_dbContextType);

foreach (var entityType in dbContext.Model.GetEntityTypes())
{
_resourceGraphBuilder.AddResource(entityType.ClrType);
}
}
}

/// <summary>
/// Performs auto-discovery of JsonApiDotNetCore services.
/// </summary>
private void AutoDiscoverResources()
{
_configureAutoDiscovery?.Invoke(_serviceDiscoveryFacade);
_serviceDiscoveryFacade.DiscoverResources();
}

/// <summary>
/// Executes the action provided by the user to configure the resources using <see cref="IResourceGraphBuilder"/>
/// </summary>
private void ConfigureResources()
{
_configureResources?.Invoke(_resourceGraphBuilder);
}
}
}
18 changes: 9 additions & 9 deletions src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,23 @@ private void SetResourceLinksOptions(ResourceContext resourceContext)
}

/// <inheritdoc />
public IResourceGraphBuilder AddResource<TResource>(string pluralizedTypeName = null) where TResource : class, IIdentifiable<int>
=> AddResource<TResource, int>(pluralizedTypeName);
public IResourceGraphBuilder AddResource<TResource>(string pluralizedResourceName = null) where TResource : class, IIdentifiable<int>
=> AddResource<TResource, int>(pluralizedResourceName);

/// <inheritdoc />
public IResourceGraphBuilder AddResource<TResource, TId>(string pluralizedTypeName = null) where TResource : class, IIdentifiable<TId>
=> AddResource(typeof(TResource), typeof(TId), pluralizedTypeName);
public IResourceGraphBuilder AddResource<TResource, TId>(string pluralizedResourceName = null) where TResource : class, IIdentifiable<TId>
=> AddResource(typeof(TResource), typeof(TId), pluralizedResourceName);

/// <inheritdoc />
public IResourceGraphBuilder AddResource(Type resourceType, Type idType = null, string pluralizedTypeName = null)
public IResourceGraphBuilder AddResource(Type resourceType, Type idType = null, string pluralizedResourceName = null)
{
if (_resources.All(e => e.ResourceType != resourceType))
{
if (resourceType.IsOrImplementsInterface(typeof(IIdentifiable)))
{
pluralizedTypeName ??= FormatResourceName(resourceType);
pluralizedResourceName ??= FormatResourceName(resourceType);
idType ??= TypeLocator.GetIdType(resourceType);
var resourceContext = CreateResourceContext(pluralizedTypeName, resourceType, idType);
var resourceContext = CreateResourceContext(pluralizedResourceName, resourceType, idType);
_resources.Add(resourceContext);
}
else
Expand All @@ -72,9 +72,9 @@ public IResourceGraphBuilder AddResource(Type resourceType, Type idType = null,
return this;
}

private ResourceContext CreateResourceContext(string pluralizedTypeName, Type resourceType, Type idType) => new ResourceContext
private ResourceContext CreateResourceContext(string pluralizedResourceName, Type resourceType, Type idType) => new ResourceContext
{
ResourceName = pluralizedTypeName,
ResourceName = pluralizedResourceName,
ResourceType = resourceType,
IdentityType = idType,
Attributes = GetAttributes(resourceType),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,14 @@ public static IServiceCollection AddJsonApi<TDbContext>(this IServiceCollection
}

private static void SetupApplicationBuilder(IServiceCollection services, Action<JsonApiOptions> options,
Action<IServiceDiscoveryFacade> discovery,
Action<IServiceDiscoveryFacade> autoDiscovery,
Action<IResourceGraphBuilder> resources, IMvcCoreBuilder mvcBuilder, Type dbContextType)
{
var applicationBuilder = new JsonApiApplicationBuilder(services, mvcBuilder ?? services.AddMvcCore());

applicationBuilder.ConfigureJsonApiOptions(options);
applicationBuilder.ConfigureMvc(dbContextType);
applicationBuilder.AutoDiscover(discovery);
applicationBuilder.ConfigureResources(resources);
applicationBuilder.RegisterResourceSources(dbContextType, autoDiscovery, resources);
applicationBuilder.ConfigureMvc();
applicationBuilder.ConfigureServices();
}

Expand Down
18 changes: 17 additions & 1 deletion src/JsonApiDotNetCore/Graph/IServiceDiscoveryFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,23 @@ namespace JsonApiDotNetCore.Graph
{
public interface IServiceDiscoveryFacade
{
/// <summary>
/// Registers the designated assembly for discovery of JsonApiDotNetCore services and resources.
/// </summary>
ServiceDiscoveryFacade AddAssembly(Assembly assembly);
/// <summary>
/// Registers the current assembly for discovery of JsonApiDotNetCore services and resources.
/// </summary>
ServiceDiscoveryFacade AddCurrentAssembly();

/// <summary>
/// Discovers JsonApiDotNetCore services in the registered assemblies and adds them to the DI container.
/// </summary>
internal void DiscoverServices();

/// <summary>
/// Discovers JsonApiDotNetCore resources in the registered assemblies and adds them to the resource graph.
/// </summary>
internal void DiscoverResources();
}
}
}
Loading