fix some routing problems

This commit is contained in:
2021-11-07 13:51:45 +01:00
parent a281b16a48
commit ca8cb0d856
8 changed files with 150 additions and 100 deletions
+15 -1
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace zero.Core.Extensions
{
@@ -22,5 +23,18 @@ namespace zero.Core.Extensions
object value = model.GetValueOrDefault(key);
return value == default || !(value is T) ? default : (T)value;
}
public static Dictionary<TKey, TElement> ToDistinctDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
{
Dictionary<TKey, TElement> result = new();
foreach (TSource sourceElement in source)
{
result.TryAdd(keySelector(sourceElement), elementSelector(sourceElement));
}
return result;
}
}
}
@@ -12,6 +12,8 @@ namespace zero.Core.Routing
{
public static string PAGE_TYPE_PARAM = "pageType";
public static string PAGE_ID_PARAM = "pageId";
protected IPageUrlBuilder UrlBuilder { get; set; }
@@ -47,6 +49,7 @@ namespace zero.Core.Routing
route.DependsOn(model.Id);
route.DependsOn(parents.Select(x => x.Id).ToArray());
route.Param(PAGE_TYPE_PARAM, model.PageTypeAlias);
route.Param(PAGE_ID_PARAM, model.Id);
return route;
}
@@ -20,6 +20,12 @@ namespace zero.Core.Routing
}
public async Task<Page> ResolvePageFor<T>(RoutingContext context, Func<Page, bool> predicate = null)
{
return (await ResolveAllPagesFor<T>(context, predicate)).FirstOrDefault();
}
public async Task<HashSet<Route>> ResolveAllFor<T>(RoutingContext context, Func<Page, bool> predicate = null)
{
string fullKey = typeof(T).Name.ToString().ToLower() + ":" + context.Context.AppId;
@@ -57,5 +63,44 @@ namespace zero.Core.Routing
return routes.Select(x => x.Item2).ToHashSet();
}
public async Task<HashSet<Page>> ResolveAllPagesFor<T>(RoutingContext context, Func<Page, bool> predicate = null)
{
string fullKey = typeof(T).Name.ToString().ToLower() + ":" + context.Context.AppId;
if (!PageRouteCache.TryGetValue(fullKey, out HashSet<(Page, Route)> routes))
{
List<Page> pages = new();
IEnumerable<Expression<Func<Page, bool>>> resolvers = context.Context.Options.Routing.PageResolvers.GetAll(typeof(T));
foreach (Expression<Func<Page, bool>> resolver in resolvers.Reverse())
{
pages.AddRange(await context.Session.Query<Page>().Where(resolver).ToListAsync());
}
Dictionary<string, Page> idToPage = pages.ToDictionary(x => context.Context.Options.Routing.PageRouteIdBuilder.Generate(x), x => x);
Dictionary<string, Route> idToRoute = await context.Session.LoadAsync<Route>(idToPage.Select(x => x.Key));
routes = new();
foreach ((string id, Page page) in idToPage)
{
if (idToRoute.TryGetValue(id, out Route route) && route != null)
{
routes.Add((page, route));
}
}
PageRouteCache.TryAdd(fullKey, routes);
}
if (predicate != null)
{
return routes.Where(x => predicate(x.Item1)).Select(x => x.Item1).ToHashSet();
}
return routes.Select(x => x.Item1).ToHashSet();
}
}
}
+1 -1
View File
@@ -45,7 +45,7 @@ namespace zero.Core.Routing
// delete previous route if the ID has changed,
// otherwise it will just be updated
if (previousModel != null && !route.Id.Equals(GetId(provider, previousModel)))
if (previousModel != null && !route.Id.Equals(provider.Id(previousModel)))
{
context.Session.Delete(previousModel);
}
+13 -32
View File
@@ -2,7 +2,6 @@
using Microsoft.Extensions.Logging;
using Raven.Client.Documents;
using Raven.Client.Documents.Linq;
using Raven.Client.Documents.Session;
using Raven.Client.Exceptions.Documents.Indexes;
using System.Collections.Generic;
using System.Linq;
@@ -15,24 +14,16 @@ using zero.Core.Options;
namespace zero.Core.Routing
{
public class RouteResolver : IRouteResolver
public class RouteResolver : Routes, IRouteResolver
{
public const char PATH_SEPERATOR = '/';
protected IZeroStore Store { get; set; }
protected ILogger<Routes> Logger { get; set; }
protected IEnumerable<IRouteProvider> Providers { get; set; }
protected IApplicationResolver AppResolver { get; set; }
protected IZeroOptions Options { get; set; }
public RouteResolver(IZeroStore store, ILogger<Routes> logger, IEnumerable<IRouteProvider> providers, IApplicationResolver appResolver, IZeroOptions options)
public RouteResolver(IZeroContext context, IZeroStore store, ILogger<Routes> logger, IEnumerable<IRouteProvider> providers, IApplicationResolver appResolver) : base(context, store, logger, providers)
{
Store = store;
Logger = logger;
Providers = providers;
AppResolver = appResolver;
Options = options;
Options = context.Options;
}
@@ -95,7 +86,7 @@ namespace zero.Core.Routing
return null;
}
return await ResolveRouteInternal(session, new RouteResponse()
return await ResolveRouteInternal(new RouteResponse()
{
Route = route,
App = application,
@@ -128,36 +119,26 @@ namespace zero.Core.Routing
/// <inheritdoc />
public RouteEndpoint MapEndpoint(IRouteModel route)
{
IRouteProvider routeProvider = FindProvider(route.Route.ProviderAlias);
if (TryGetProvider(route.Route.ProviderAlias, out IRouteProvider provider))
{
return provider.Map(GetContext(), route);
}
return null;
//return routeProvider?.MapEndpoint(route);
}
/// <summary>
/// Call the provider which can resolve the route
/// </summary>
Task<IRouteModel> ResolveRouteInternal(IAsyncDocumentSession session, RouteResponse response)
async Task<IRouteModel> ResolveRouteInternal(RouteResponse response)
{
IRouteProvider routeProvider = FindProvider(response.Route.ProviderAlias);
return Task.FromResult(default(IRouteModel));
//return await routeProvider?.ResolveRoute(session, response);
}
/// <summary>
/// Find registered route provider for the specified alias
/// </summary>
IRouteProvider FindProvider(string alias)
{
IRouteProvider routeProvider = Providers.FirstOrDefault(x => x.Alias == alias);
if (routeProvider == null)
if (TryGetProvider(response.Route.ProviderAlias, out IRouteProvider provider))
{
Logger.LogWarning("Could not locate URL provider {provider}", alias);
return await provider.Model(GetContext(), response.Route);
}
return routeProvider;
return null;
}
}
+29 -62
View File
@@ -6,7 +6,6 @@ using System.Linq;
using System.Threading.Tasks;
using zero.Core.Database;
using zero.Core.Entities;
using zero.Core.Extensions;
namespace zero.Core.Routing
@@ -31,47 +30,27 @@ namespace zero.Core.Routing
/// <inheritdoc />
public async Task<string> GetUrl<T>(T model, object parameters = null) where T : IZeroRouteEntity => (await GetRoute(model, parameters))?.Url;
public async Task<string> GetUrl<T>(T model) where T : IZeroRouteEntity => (await GetRoute(model))?.Url;
/// <inheritdoc />
public async Task<string> GetUrl<T>(string id, object parameters = null) where T : IZeroRouteEntity => (await GetRoute<T>(id, parameters))?.Url;
public async Task<string> GetUrl<T>(string id) where T : IZeroRouteEntity => (await GetRoute<T>(id))?.Url;
/// <inheritdoc />
public async Task<Dictionary<T, string>> GetUrls<T>(IEnumerable<T> models, object parameters = null) where T : IZeroRouteEntity => (await GetRoutes(models, parameters)).ToDictionary(x => x.Key, x => x.Value?.Url);
public async Task<Dictionary<T, string>> GetUrls<T>(params T[] models) where T : IZeroRouteEntity => (await GetRoutes(models)).ToDictionary(x => x.Key, x => x.Value?.Url);
/// <inheritdoc />
public async Task<Route> GetRoute<T>(string id, object parameters = null) where T : IZeroRouteEntity
public async Task<Route> GetRoute<T>(string id) where T : IZeroRouteEntity
{
if (id.IsNullOrEmpty())
{
return null;
}
Type type = typeof(T);
IRouteProvider routeProvider = Providers.FirstOrDefault(x => x.CanHandle(type));
if (routeProvider == null)
{
return null;
}
RoutingContext context = GetContext();
T model = await context.Session.LoadAsync<T>(id);
if (model == null)
{
return null;
}
return await FindRoute(routeProvider, context, model);
T model = await GetContext().Session.LoadAsync<T>(id);
return await GetRoute(model);
}
/// <inheritdoc />
public async Task<Route> GetRoute<T>(T model, object parameters = null) where T : IZeroRouteEntity
public async Task<Route> GetRoute<T>(T model) where T : IZeroRouteEntity
{
if (model == null)
{
@@ -89,9 +68,9 @@ namespace zero.Core.Routing
/// <inheritdoc />
public async Task<Dictionary<T, Route>> GetRoutes<T>(IEnumerable<T> models, object parameters = null) where T : IZeroRouteEntity
public async Task<Dictionary<T, Route>> GetRoutes<T>(params T[] models) where T : IZeroRouteEntity
{
if (!models.Any())
if (models.Length < 1)
{
return new();
}
@@ -109,16 +88,7 @@ namespace zero.Core.Routing
}
RoutingContext context = GetContext();
Dictionary<string, T> idMap = models.ToDictionary(x => GetId(routeProvider, x), x => x);
Dictionary<string, Route> routes = await context.Session.LoadAsync<Route>(idMap.Select(x => x.Key));
Dictionary<T, Route> result = new();
foreach ((string id, T model) in idMap)
{
result.Add(model, routes.GetValueOrDefault(id));
}
return result;
return (await routeProvider.Find(context, models.Select(x => (IZeroRouteEntity)x).ToArray())).ToDictionary(x => (T)x.Key, x => x.Value);
}
@@ -131,6 +101,14 @@ namespace zero.Core.Routing
}
/// <inheritdoc />
public virtual bool TryGetProvider(string alias, out IRouteProvider provider)
{
provider = Providers.FirstOrDefault(x => x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase));
return provider != null;
}
/// <summary>
/// Map a route to an MVC endpoint
/// </summary>
@@ -161,22 +139,6 @@ namespace zero.Core.Routing
}
/// <summary>
/// Get ID in collection for an entity
/// </summary>
protected virtual string GetId(IRouteProvider provider, IZeroRouteEntity model)
{
string key = provider.Key(model);
if (key.IsNullOrEmpty())
{
return null;
}
return "routes." + provider.Alias + "." + key;
}
/// <summary>
/// Build a new routing context
/// </summary>
@@ -191,36 +153,41 @@ namespace zero.Core.Routing
/// <summary>
/// Get the URL for an entity
/// </summary>
Task<string> GetUrl<T>(T model, object parameters = null) where T : IZeroRouteEntity;
Task<string> GetUrl<T>(T model) where T : IZeroRouteEntity;
/// <summary>
/// Get the URL for an entity
/// </summary>
Task<string> GetUrl<T>(string id, object parameters = null) where T : IZeroRouteEntity;
Task<string> GetUrl<T>(string id) where T : IZeroRouteEntity;
/// <summary>
/// Get URLs for multiple entities
/// </summary>
Task<Dictionary<T, string>> GetUrls<T>(IEnumerable<T> models, object parameters = null) where T : IZeroRouteEntity;
Task<Dictionary<T, string>> GetUrls<T>(params T[] models) where T : IZeroRouteEntity;
/// <summary>
/// Get the route object for an entity
/// </summary>
Task<Route> GetRoute<T>(T model, object parameters = null) where T : IZeroRouteEntity;
Task<Route> GetRoute<T>(T model) where T : IZeroRouteEntity;
/// <summary>
/// Get the route object for an entity
/// </summary>
Task<Route> GetRoute<T>(string id, object parameters = null) where T : IZeroRouteEntity;
Task<Route> GetRoute<T>(string id) where T : IZeroRouteEntity;
/// <summary>
/// Get routes for multiple entities
/// </summary>
Task<Dictionary<T, Route>> GetRoutes<T>(IEnumerable<T> models, object parameters = null) where T : IZeroRouteEntity;
Task<Dictionary<T, Route>> GetRoutes<T>(params T[] models) where T : IZeroRouteEntity;
/// <summary>
/// Find a provider for a certain entity
/// </summary>
bool TryGetProvider<T>(T model, out IRouteProvider provider) where T : IZeroRouteEntity;
/// <summary>
/// Find a provider by alias
/// </summary>
bool TryGetProvider(string alias, out IRouteProvider provider);
}
}
+43 -3
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using zero.Core.Entities;
using zero.Core.Extensions;
namespace zero.Core.Routing
{
@@ -42,6 +43,27 @@ namespace zero.Core.Routing
/// <inheritdoc />
public sealed override Task<Route> Find(RoutingContext context, IZeroRouteEntity model) => Find(context, (T)model);
/// <inheritdoc />
public virtual async Task<Dictionary<T, Route>> Find(RoutingContext context, params T[] models)
{
Dictionary<string, T> idMap = models.ToDistinctDictionary(x => Id(x), x => x);
Dictionary<string, Route> routes = await context.Session.LoadAsync<Route>(idMap.Select(x => x.Key));
Dictionary<T, Route> result = new();
foreach ((string id, T model) in idMap)
{
result.Add(model, routes.GetValueOrDefault(id));
}
return result;
}
/// <inheritdoc />
public sealed override async Task<Dictionary<IZeroRouteEntity, Route>> Find(RoutingContext context, params IZeroRouteEntity[] models)
{
return (await Find(context, models.Select(x => (T)x).ToArray())).ToDictionary(x => (IZeroRouteEntity)x.Key, x => x.Value);
}
}
@@ -97,10 +119,23 @@ namespace zero.Core.Routing
return context.Context.Options.Routing.DefaultEndpoint;
}
/// <summary>
/// Find a persisted route for an entity
/// </summary>
/// <inheritdoc />
public virtual async Task<Route> Find(RoutingContext context, IZeroRouteEntity model) => await context.Session.LoadAsync<Route>(Id(model));
/// <inheritdoc />
public virtual async Task<Dictionary<IZeroRouteEntity, Route>> Find(RoutingContext context, params IZeroRouteEntity[] models)
{
Dictionary<string, IZeroRouteEntity> idMap = models.ToDistinctDictionary(x => Id(x), x => x);
Dictionary<string, Route> routes = await context.Session.LoadAsync<Route>(idMap.Select(x => x.Key));
Dictionary<IZeroRouteEntity, Route> result = new();
foreach ((string id, IZeroRouteEntity model) in idMap)
{
result.Add(model, routes.GetValueOrDefault(id));
}
return result;
}
}
@@ -186,6 +221,11 @@ namespace zero.Core.Routing
/// Find a persisted route for an entity
/// </summary>
Task<Route> Find(RoutingContext context, IZeroRouteEntity model);
/// <summary>
/// Find persisted routes for entities
/// </summary>
Task<Dictionary<IZeroRouteEntity, Route>> Find(RoutingContext context, params IZeroRouteEntity[] models);
}
}
+1 -1
View File
@@ -38,7 +38,7 @@ namespace zero.Web.Controllers
{
IReadOnlyCollection<PageType> pageTypes = Options.Pages.GetAllItems();
Dictionary<string, Page> pages = await Collection.GetByIds(ids.ToArray());
Dictionary<Page, Route> routes = await Routes.GetRoutes(pages.Where(x => x.Value != null).Select(x => x.Value));
Dictionary<Page, Route> routes = await Routes.GetRoutes(pages.Where(x => x.Value != null).Select(x => x.Value).ToArray());
return Previews(pages, item =>
{