start new route provider
This commit is contained in:
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user