diff --git a/zero.Core/Entities/IRoutedEntity.cs b/zero.Core/Entities/IRoutedEntity.cs new file mode 100644 index 00000000..ff877525 --- /dev/null +++ b/zero.Core/Entities/IRoutedEntity.cs @@ -0,0 +1,10 @@ +namespace zero.Core.Entities +{ + public interface IRoutedEntity + { + /// + /// Url for this entity + /// + public string Url { get; set; } + } +} diff --git a/zero.Core/Entities/Pages/Page.cs b/zero.Core/Entities/Pages/Page.cs index 68a20038..7fef0c86 100644 --- a/zero.Core/Entities/Pages/Page.cs +++ b/zero.Core/Entities/Pages/Page.cs @@ -10,6 +10,9 @@ namespace zero.Core.Entities /// public class Page : ZeroEntity, IPage { + /// + public string Url { get; set; } + /// public string ParentId { get; set; } @@ -25,7 +28,7 @@ namespace zero.Core.Entities [Collection("Pages")] - public interface IPage : IZeroEntity, IAppAwareEntity, IZeroDbConventions + public interface IPage : IZeroEntity, IAppAwareEntity, IZeroDbConventions, IRoutedEntity { /// /// Id of the parent page diff --git a/zero.Core/Routing/IPageUrlResolver.cs b/zero.Core/Routing/IPageUrlResolver.cs index 6ebca55f..5a298f62 100644 --- a/zero.Core/Routing/IPageUrlResolver.cs +++ b/zero.Core/Routing/IPageUrlResolver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading.Tasks; using zero.Core.Api; using zero.Core.Entities; @@ -9,6 +10,11 @@ namespace zero.Core.Routing { public interface IPageUrlResolver { + /// + /// Get URL for a page + /// + Task GetUrl(IPage page); + /// /// Get URL for a page /// diff --git a/zero.Core/Routing/PageUrlProvider-old.cs b/zero.Core/Routing/PageUrlProvider-old.cs new file mode 100644 index 00000000..a8d8f0af --- /dev/null +++ b/zero.Core/Routing/PageUrlProvider-old.cs @@ -0,0 +1,136 @@ +using Raven.Client.Documents; +using Raven.Client.Documents.Linq; +using Raven.Client.Documents.Session; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using zero.Core.Api; +using zero.Core.Entities; +using zero.Core.Entities.Messages; +using zero.Core.Extensions; +using zero.Core.Messages; +using zero.Core.Utils; + +namespace zero.Core.Routing +{ + public class PageUrlProviderOld : IUrlProvider + { + public string Alias => "zero.pages"; + + public const string PAGE_COLLECTION = "page"; + + public const string PAGE_TYPE = "pageType"; + + protected IDocumentStore Raven { get; private set; } + + protected IMessageAggregator Messages { get; private set; } + + protected IPageUrlResolver PageUrlResolver { get; set; } + + + protected IApplicationContext Context { get; private set; } + + public PageUrlProviderOld(IDocumentStore raven, IMessageAggregator messages, IPageUrlResolver pageUrlResolver, IApplicationContext context) + { + Raven = raven; + Messages = messages; + PageUrlResolver = pageUrlResolver; + Context = context; + } + + + /// + public async Task Resolve(IRoute route) + { + PageRoute resolved = new PageRoute(route); + + List ids = new List(); + RouteReference reference = route.References.SingleOrDefault(x => x.Collection == PAGE_COLLECTION); + + ids.Add(reference.Id); + ids.AddRange(route.Dependencies); + + using IAsyncDocumentSession session = Raven.OpenAsyncSession(); + + Dictionary pages = await session.LoadAsync(ids); + + if (!pages.TryGetValue(reference.Id, out IPage page) || page.AppId != route.AppId) + { + return null; + } + + resolved.Page = page; + resolved.Parents = pages.Where(x => x.Key != reference.Id).Select(x => x.Value).ToList(); + + return resolved; + } + + + + + /// + public async Task> Run() + { + using IAsyncDocumentSession session = Raven.OpenAsyncSession(); + + List allRoutes = new List(); + IList pages = await session.Query().ToListAsync(); + IEnumerable> groupedPages = pages.GroupBy(x => x.AppId); + + foreach (var group in groupedPages) + { + IApplicationContext context = await Context.ForId(group.Key); + + IList routes = TraversePageChildren(context, null, new List() { }, group); + allRoutes.AddRange(routes); + } + + return allRoutes; + } + + + protected IRoute BuildRoute(IApplicationContext context, IPage page, IEnumerable parents) + { + UrlInfo info = PageUrlResolver.GetUrl(context, page, parents); + + IRoute route = new Route() + { + Id = String.Concat("routes.", IdGenerator.Create()), + AppId = context.AppId, + Url = info.IsUrl ? info.Text.EnsureStartsWith('/') : null, + ProviderAlias = Alias + }; + + route.Params.Add(PAGE_TYPE, page.PageTypeAlias); + route.References.Add(new RouteReference(page.Id, PAGE_COLLECTION)); + + if (parents != null) + { + foreach (IPage parent in parents) + { + route.Dependencies.Add(parent.Id); + } + } + + return route; + } + + + IList TraversePageChildren(IApplicationContext context, IPage parent, IEnumerable parents, IEnumerable allPages) + { + List routes = new List(); + IEnumerable currentPages = allPages.Where(x => x.ParentId == parent?.Id); + + foreach (IPage page in currentPages) + { + IRoute route = BuildRoute(context, page, parents); + routes.Add(route); + routes.AddRange(TraversePageChildren(context, page, parents.Union(new List() { page }), allPages)); + } + + return routes; + } + } +} diff --git a/zero.Core/Routing/PageUrlProvider-test.cs b/zero.Core/Routing/PageUrlProvider-test.cs new file mode 100644 index 00000000..9424d81b --- /dev/null +++ b/zero.Core/Routing/PageUrlProvider-test.cs @@ -0,0 +1,105 @@ +using Raven.Client.Documents; +using Raven.Client.Documents.Linq; +using Raven.Client.Documents.Session; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using zero.Core.Api; +using zero.Core.Database.Indexes; +using zero.Core.Entities; +using zero.Core.Entities.Messages; +using zero.Core.Extensions; +using zero.Core.Messages; +using zero.Core.Utils; + +namespace zero.Core.Routing +{ + public class PageUrlProviderTest + { + public string Alias => "zero.pages"; + + public const string PAGE_COLLECTION = "page"; + + public const string PAGE_TYPE = "pageType"; + + protected IDocumentStore Raven { get; private set; } + + protected IMessageAggregator Messages { get; private set; } + + protected IPageUrlResolver PageUrlResolver { get; set; } + + protected IApplicationContext Context { get; private set; } + + + public PageUrlProviderTest(IDocumentStore raven, IMessageAggregator messages, IPageUrlResolver pageUrlResolver, IApplicationContext context) + { + Raven = raven; + Messages = messages; + PageUrlResolver = pageUrlResolver; + Context = context; + } + + + /// + public bool UrlChanged(IPage previous, IPage current) + { + return previous == null || !previous.Alias.Equals(current.Alias) || !previous.ParentId.Equals(current.ParentId); + } + + + /// + public Task GetDependencies(IPage model) + { + // TODO get all pages (and other routes) which have this page as a dependency and update them + throw new NotImplementedException(); + } + + + /// + public async Task CreateRoute(IPage model) + { + using IAsyncDocumentSession session = Raven.OpenAsyncSession(); + + Pages_ByHierarchy.Result result = await session.Query() + .ProjectInto() + .Include(x => x.Path.Select(p => p.Id)) + .FirstOrDefaultAsync(x => x.Id == model.Id); + + IList parents = (await session.LoadAsync(result.Path.Select(x => x.Id))).Select(x => x.Value).ToList(); + + return new Route() + { + Id = String.Concat("routes.", IdGenerator.Create()), + AppId = model.AppId, + Url = GetUrl(model, parents), + ProviderAlias = Alias + }; + } + + + /// + /// Create url for page + /// + protected string GetUrl(IPage page, IEnumerable parents) + { + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.Append('/'); + + if (parents != null) + { + foreach (IPage parent in parents) + { + stringBuilder.Append(parent.Alias); + stringBuilder.Append('/'); + } + } + + stringBuilder.Append(page.Alias); + + return stringBuilder.ToString(); + } + } +} diff --git a/zero.Core/Routing/PageUrlResolver.cs b/zero.Core/Routing/PageUrlResolver.cs index d1d4a7b5..65a29141 100644 --- a/zero.Core/Routing/PageUrlResolver.cs +++ b/zero.Core/Routing/PageUrlResolver.cs @@ -1,8 +1,12 @@ -using System; +using Raven.Client.Documents; +using Raven.Client.Documents.Session; +using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading.Tasks; using zero.Core.Api; +using zero.Core.Database.Indexes; using zero.Core.Entities; namespace zero.Core.Routing @@ -13,6 +17,49 @@ namespace zero.Core.Routing const bool TRAILING_SLASH = false; + protected IDocumentStore Raven { get; private set; } + + + public PageUrlResolver(IDocumentStore raven) + { + Raven = raven; + } + + + /// + public async Task GetUrl(IPage page) + { + using IAsyncDocumentSession session = Raven.OpenAsyncSession(); + + Pages_ByHierarchy.Result result = await session.Query() + .ProjectInto() + .Include(x => x.Path.Select(p => p.Id)) + .FirstOrDefaultAsync(x => x.Id == page.Id); + + IList parents = (await session.LoadAsync(result.Path.Select(x => x.Id))).Select(x => x.Value).ToList(); + + StringBuilder stringBuilder = new StringBuilder(); + + if (parents != null) + { + foreach (IPage parent in parents) + { + stringBuilder.Append(parent.Alias); + stringBuilder.Append(PATH_SEPARATOR); + } + } + + stringBuilder.Append(page.Alias); + stringBuilder.Append(PATH_SEPARATOR); + + if (!TRAILING_SLASH) + { + stringBuilder.Remove(stringBuilder.Length - 1, 1); + } + + return stringBuilder.ToString(); + } + /// public UrlInfo GetUrl(IApplicationContext context, IPage page, IEnumerable parents)