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.Entities; using zero.Core.Options; namespace zero.Core.Routing { public abstract class AbtractRouteProvider : IRouteProvider { public virtual string Alias { get; protected set; } public virtual Type[] AffectedTypes { get; protected set; } public const string ID_PREFIX = "routes."; public const char SLASH = '/'; protected IZeroOptions Options { get; set; } public AbtractRouteProvider(string alias, IZeroOptions options) { Alias = alias; Options = options; AffectedTypes = new Type[1] { typeof(T) }; } /// /// Map a route to an MVC endpoint /// public virtual RouteProviderEndpoint MapEndpoint(IResolvedRoute route) { IEnumerable> resolvers = Options.Routing.EndpointResolvers.GetAll(route.GetType()); foreach (Func resolver in resolvers.Reverse()) { RouteProviderEndpoint endpoint = resolver(route); if (endpoint != null) { return endpoint; } } return Options.Routing.DefaultEndpoint; } /// public virtual async Task> GetRoutes(IAsyncDocumentSession session, IEnumerable models, object parameters = null) { Dictionary result = new(); Dictionary routeMap = new(); HashSet routeIds = new(); foreach (T model in models) { if (model == null) { continue; } string routeId = GetRouteId(model, parameters); routeIds.Add(routeId); routeMap.TryAdd(routeId, model); } Dictionary routes = await session.LoadAsync(routeIds); foreach ((string key, Route route) in routes) { if (routeMap.TryGetValue(key, out T model)) { result.TryAdd(model, route); } } return result; } /// public virtual async Task> GetRoutes(IAsyncDocumentSession session, IEnumerable models, object parameters = null) { return (await GetRoutes(session, models.Select(x => (T)x).ToArray(), parameters)).ToDictionary(x => (object)x.Key, x => x.Value); } /// public virtual async Task GetRoute(IAsyncDocumentSession session, T model, object parameters = null) { return await session.LoadAsync(GetRouteId(model, parameters)); } /// public virtual async Task GetRoute(IAsyncDocumentSession session, object model, object parameters = null) { if (!(model is T)) { return null; } return await GetRoute(session, (T)model, parameters); } /// public virtual Task ResolveRoute(IAsyncDocumentSession session, RouteResponse response) { DefaultResolvedRoute resolved = new DefaultResolvedRoute() { Route = response.Route }; return Task.FromResult((IResolvedRoute)resolved); } /// public abstract Task> GetAllRoutes(IAsyncDocumentSession session); /// public abstract string GetRouteId(T model, object parameters = null); /// public string GetRouteId(object model, object parameters = null) { if (!(model is T)) { throw new ArgumentException($"Parameter has to be of type {typeof(T)}", nameof(model)); } return GetRouteId((T)model, parameters); } protected async Task ResolvePage(IAsyncDocumentSession session) { IEnumerable>> resolvers = Options.Routing.PageResolvers.GetAll(typeof(T)); foreach (Expression> resolver in resolvers.Reverse()) { Page page = await session.Query().FirstOrDefaultAsync(resolver); if (page != null) { return page; } } return null; } protected async Task> ResolvePages(IAsyncDocumentSession session) { List pages = new(); IEnumerable>> resolvers = Options.Routing.PageResolvers.GetAll(typeof(T)); foreach (Expression> resolver in resolvers.Reverse()) { pages.AddRange(await session.Query().Where(resolver).ToListAsync()); } return pages; } protected async Task ResolvePageRoute(IAsyncDocumentSession session) { Page page = await ResolvePage(session); // 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(ID_PREFIX + page.Hash); } protected async Task> ResolvePageRoutes(IAsyncDocumentSession session) { List routes = new(); IEnumerable pages = await ResolvePages(session); string[] ids = pages.Select(x => ID_PREFIX + x.Hash).ToArray(); return ids.Length < 1 ? routes : (await session.LoadAsync(ids)).Select(x => x.Value); } } }