start new route provider

This commit is contained in:
2021-10-05 15:50:12 +02:00
parent 53ac744be8
commit f861ccb7a2
9 changed files with 252 additions and 40 deletions
+15 -1
View File
@@ -5,7 +5,7 @@ using System.Diagnostics;
namespace zero.Core.Entities
{
[DebuggerDisplay("Id = {Id,nq}, Name = {Name}, Alias = {Alias}")]
public class ZeroEntity : ZeroIdEntity, IZeroDbConventions
public class ZeroEntity : ZeroIdEntity, IZeroDbConventions, IZeroRouteEntity
{
/// <summary>
/// Full name of the entity
@@ -83,4 +83,18 @@ namespace zero.Core.Entities
/// </summary>
public string Id { get; set; }
}
public interface IZeroRouteEntity
{
/// <summary>
/// Id of the entity
/// </summary>
string Id { get; set; }
/// <summary>
/// Unique hash for this entity (primarily used for routing)
/// </summary>
string Hash { get; set; }
}
}
+25
View File
@@ -0,0 +1,25 @@
using zero.Core.Routing;
namespace zero.Core.Extensions
{
public static class RouteExtensions
{
public static Route DependsOn(this Route route, string id)
{
route.Dependencies.Add(id);
return route;
}
public static Route Param(this Route route, string key, object value)
{
route.Params[key] = value;
return route;
}
public static Route Reference(this Route route, string id, string collection)
{
route.References.Add(new(id, collection));
return route;
}
}
}
+5
View File
@@ -57,6 +57,11 @@ namespace zero.Core.Routing
/// </summary>
Task<IList<Route>> GetAllRoutes(IAsyncDocumentSession session);
///// <summary>
///// Updates all affected routes on demand after an entity has been changed
///// </summary>
//Task<int> UpdateOnDemand(object model, object previous);
/// <summary>
/// Generate unique route ID for a model
/// </summary>
@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using zero.Core.Entities;
namespace zero.Core.Routing
{
public interface INewRouteProvider<T> where T : IZeroRouteEntity
{
string Alias { get; }
/// <summary>
/// Generate unique route ID for a model
/// </summary>
string Id(T model);
/// <summary>
/// Build URL for a model
/// </summary>
//string Url(T model);
/// <summary>
/// Create route entity from a model
/// </summary>
Task<Route> Create(T model);
/// <summary>
/// Get all models which should be provided and handled by this instance
/// </summary>
Task<IEnumerable<T>> All();
/// <summary>
/// Build a resolved route from a route response
/// </summary>
Task<IResolvedRoute> Resolve(RouteResponse response);
}
}
@@ -0,0 +1,105 @@
using Raven.Client.Documents;
using Raven.Client.Documents.Session;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using zero.Core.Database;
using zero.Core.Entities;
using zero.Core.Options;
namespace zero.Core.Routing
{
public abstract class NewAbtractRouteProvider<T> : INewRouteProvider<T> where T : IZeroRouteEntity
{
public virtual string Alias { get; protected set; }
public const string ID_PREFIX = "routes.";
public const char SLASH = '/';
protected IZeroDocumentSession Session { get; set; }
protected IZeroOptions Options { get; set; }
public NewAbtractRouteProvider(string alias, IZeroDocumentSession session, IZeroOptions options)
{
Session = session;
Alias = alias;
Options = options;
}
public virtual string Id(T model) => ID_PREFIX + model.Hash;
public abstract Task<Route> Create(T model);
public abstract Task<IEnumerable<T>> All();
public abstract Task<IResolvedRoute> Resolve(RouteResponse response);
protected async Task<Page> ResolvePage()
{
IEnumerable<Expression<Func<Page, bool>>> resolvers = Options.Routing.PageResolvers.GetAll(typeof(T));
foreach (Expression<Func<Page, bool>> resolver in resolvers.Reverse())
{
Page page = await Session.Query<Page>().FirstOrDefaultAsync(resolver);
if (page != null)
{
return page;
}
}
return null;
}
protected async Task<IEnumerable<Page>> ResolvePages()
{
List<Page> pages = new();
IEnumerable<Expression<Func<Page, bool>>> resolvers = Options.Routing.PageResolvers.GetAll(typeof(T));
foreach (Expression<Func<Page, bool>> resolver in resolvers.Reverse())
{
pages.AddRange(await Session.Query<Page>().Where(resolver).ToListAsync());
}
return pages;
}
protected async Task<Route> ResolvePageRoute()
{
Page page = await ResolvePage();
// WARNING: we are assuming that the route id is built from the page hash but this could be altered with PageRouteProvier.GetRouteId.
// we cannot use a dependency on this provider here as we are working from the abstract route provider which is the base of the PageRouteProvider itself,
// and therefore a circular dependency.
return page == null ? null : await Session.LoadAsync<Route>(ID_PREFIX + page.Hash);
}
protected async Task<IEnumerable<Route>> ResolvePageRoutes()
{
List<Route> routes = new();
IEnumerable<Page> pages = await ResolvePages();
string[] ids = pages.Select(x => ID_PREFIX + x.Hash).ToArray();
return ids.Length < 1 ? routes : (await Session.LoadAsync<Route>(ids)).Select(x => x.Value);
}
}
}
@@ -0,0 +1,51 @@
using Raven.Client.Documents;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using zero.Core.Database;
using zero.Core.Entities;
using zero.Core.Extensions;
using zero.Core.Options;
namespace zero.Core.Routing
{
public class NewRouteProvider : NewAbtractRouteProvider<Country>
{
protected Route PageRoute { get; set; }
public NewRouteProvider(IZeroDocumentSession session, IZeroOptions options) : base("new.test", session, options)
{
Options = options;
}
/// <inheritdoc />
public override async Task<Route> Create(Country model)
{
PageRoute ??= await ResolvePageRoute();
return new Route(Alias)
{
Id = Id(model),
Url = String.Join(SLASH, PageRoute.Url, "story", model.Alias)
}.DependsOn(model.Id);
}
/// <inheritdoc />
public override async Task<IResolvedRoute> Resolve(RouteResponse response)
{
PageRoute resolved = new(response.Route);
resolved.Page = await Session.LoadAsync<Page>(response.Route.Dependencies[0]);
return resolved.Page == null ? null : resolved;
}
/// <inheritdoc />
public override async Task<IEnumerable<Country>> All()
{
return await Session.Query<Country>().ToListAsync();
}
}
}
+1 -1
View File
@@ -106,7 +106,7 @@ namespace zero.Core.Routing
/// <summary>
/// Get the part of the URL (by querying UrlAlias and Alias) for this page
/// </summary>
protected virtual string GetUrlPart(Page page)
public virtual string GetUrlPart(Page page)
{
string alias;
+7
View File
@@ -7,6 +7,13 @@ namespace zero.Core.Routing
[Collection("Routes")]
public class Route : ZeroIdEntity
{
public Route() { }
public Route(string providerAlias)
{
ProviderAlias = providerAlias;
}
/// <summary>
/// Generated URL based on the URL provider
/// </summary>
+7 -38
View File
@@ -216,30 +216,6 @@ namespace zero.Core.Routing
}
///// <inheritdoc />
//public async Task<IResolvedRoute> ResolveRoute(Route route)
//{
// using IAsyncDocumentSession session = Store.OpenAsyncSession();
// return await ResolveRouteInternal(session, null); // TODO
//}
/// <inheritdoc />
public NotFoundRoute NotFound(HttpContext context)
{
if (!Options.SetupCompleted || context.IsBackofficeRequest(Options.BackofficePath) || Options.Routing.NotFoundEndpoint == null)
{
return null;
}
return new NotFoundRoute(context)
{
Controller = Options.Routing.NotFoundEndpoint.Controller,
Action = Options.Routing.NotFoundEndpoint.Action
};
}
/// <inheritdoc />
public async Task RebuildAllRoutes()
{
@@ -253,16 +229,19 @@ namespace zero.Core.Routing
using IAsyncDocumentSession session = Store.OpenAsyncSession(app.Database);
session.Advanced.MaxNumberOfRequestsPerSession = 1000;
// delete all routes
await Store.PurgeAsync<Route>(app.Database);
foreach (IRouteProvider provider in Providers)
{
// get all routes for this provider
IList<Route> routes = await provider.GetAllRoutes(session);
// delete all registered routes in the database for this provider
await Store.PurgeAsync<Route>(app.Database, $"where {nameof(Route.ProviderAlias)} = $alias", new Raven.Client.Parameters()
{
{ "alias", provider.Alias }
});
//await Store.PurgeAsync<Route>(app.Database, $"where {nameof(Route.ProviderAlias)} = $alias", new Raven.Client.Parameters()
//{
// { "alias", provider.Alias }
//});
// store new routes
using (BulkInsertOperation bulkInsert = Store.BulkInsert(app.Database))
@@ -355,16 +334,6 @@ namespace zero.Core.Routing
/// </summary>
Task<IResolvedRoute> ResolveUrl(HttpContext context);
/// <summary>
/// Resolve a route object by passing it to the specified provider
/// </summary>
//Task<IResolvedRoute> ResolveRoute(Route route);
/// <summary>
/// Returns the endpoint which maps 404 requests
/// </summary>
NotFoundRoute NotFound(HttpContext context);
/// <summary>
/// Purges all routes and rebuilds them by iterating over all registered providers
/// </summary>