From 2536c46fc7bb6b8688312849d71b3c6a2b23ee2e Mon Sep 17 00:00:00 2001 From: Tobias Klika Date: Thu, 2 Dec 2021 13:43:04 +0100 Subject: [PATCH] flavors work --- zero.Api/Endpoints/Pages/PageTreeService.cs | 8 +- .../Communication/Handlers/IHandlerHolder.cs | 8 ++ zero.Core/Localization/LocalizationModule.cs | 7 -- zero.Core/Media/Indexes/Media_ByChildren.cs | 2 +- zero.Core/Media/Indexes/Media_ByHierarchy.cs | 2 +- zero.Core/Media/MediaModule.cs | 6 ++ zero.Core/Models/ZeroEntity.cs | 5 +- zero.Core/Pages/IPageTypeHandler.cs | 2 +- zero.Core/Pages/Indexes/Pages_AsHistory.cs | 2 +- zero.Core/Pages/Indexes/Pages_ByHierarchy.cs | 2 +- zero.Core/Pages/Indexes/Pages_ByType.cs | 4 +- zero.Core/Pages/Indexes/Pages_WithChildren.cs | 2 +- zero.Core/Pages/Models/Page.cs | 5 -- zero.Core/Pages/Models/PageType.cs | 58 ------------- zero.Core/Pages/PageOptions.cs | 37 +------- zero.Core/Pages/PageTypeService.cs | 24 +++--- zero.Core/Pages/PagesModule.cs | 17 ++-- zero.Core/Pages/PagesStore.cs | 12 +-- ...rsistence.cs => ISupportsDbConventions.cs} | 2 +- zero.Core/Persistence/Tokens/ChangeToken.cs | 2 +- zero.Core/Persistence/Tokens/Token.cs | 2 +- .../ZeroDocumentConventionsBuilder.cs | 2 +- .../PageRouteProvider/PageLinkProvider.cs | 4 +- .../PageRouteProvider/PageRouteProvider.cs | 2 +- .../PageRouteProvider/PageUrlBuilder.cs | 2 +- zero.Core/Spaces/FlavorOptionsExtensions.cs | 48 +++++++++++ zero.Core/Spaces/Models/Space.cs | 5 +- zero.Core/Spaces/Models/SpaceType.cs | 12 +-- zero.Core/Spaces/SpaceOptions.cs | 77 ----------------- zero.Core/Spaces/SpaceService.cs | 29 +------ zero.Core/Spaces/SpaceStore.cs | 29 +------ zero.Core/Spaces/SpaceTypeService.cs | 13 ++- zero.Core/Spaces/SpacesModule.cs | 2 - zero.Core/Stores/EntityStore.cs | 18 +++- zero.Core/Stores/Flavors/FlavorOptions.cs | 86 ++++++++++++++----- zero.Core/Stores/StoreOperations.Empty.cs | 46 ++++++++++ zero.Core/Stores/StoreOperations.cs | 26 +++--- zero.Core/Stores/TreeEntityStore.cs | 2 +- 38 files changed, 275 insertions(+), 337 deletions(-) delete mode 100644 zero.Core/Pages/Models/PageType.cs rename zero.Core/Persistence/{ISupportsPersistence.cs => ISupportsDbConventions.cs} (74%) create mode 100644 zero.Core/Spaces/FlavorOptionsExtensions.cs delete mode 100644 zero.Core/Spaces/SpaceOptions.cs create mode 100644 zero.Core/Stores/StoreOperations.Empty.cs diff --git a/zero.Api/Endpoints/Pages/PageTreeService.cs b/zero.Api/Endpoints/Pages/PageTreeService.cs index c50c2d67..313a59cb 100644 --- a/zero.Api/Endpoints/Pages/PageTreeService.cs +++ b/zero.Api/Endpoints/Pages/PageTreeService.cs @@ -10,14 +10,14 @@ public class PageTreeService : IPageTreeService protected IRoutes Routes { get; set; } - protected PageOptions PageOptions { get; set; } + protected IPageTypeService PageTypes { get; set; } - public PageTreeService(IPagesStore pages, IZeroOptions options, IRoutes routes) + public PageTreeService(IPagesStore pages, IPageTypeService pageTypes, IRoutes routes) { Pages = pages; Routes = routes; - PageOptions = options.For(); + PageTypes = pageTypes; } @@ -94,7 +94,7 @@ public class PageTreeService : IPageTreeService // build tree foreach (Page page in pages.Items) { - PageType pageType = PageOptions.FirstOrDefault(x => x.Alias == page.PageTypeAlias); + FlavorConfig pageType = PageTypes.GetByAlias(page.Flavor); if (pageType == null) { diff --git a/zero.Core/Communication/Handlers/IHandlerHolder.cs b/zero.Core/Communication/Handlers/IHandlerHolder.cs index 16119a7e..096c88ef 100644 --- a/zero.Core/Communication/Handlers/IHandlerHolder.cs +++ b/zero.Core/Communication/Handlers/IHandlerHolder.cs @@ -16,9 +16,17 @@ public class HandlerHolder : IHandlerHolder { return Services.GetService(); } + + + public IEnumerable GetAll() where T : IHandler + { + return Services.GetService>(); + } } public interface IHandlerHolder { T Get() where T : IHandler; + + IEnumerable GetAll() where T : IHandler; } \ No newline at end of file diff --git a/zero.Core/Localization/LocalizationModule.cs b/zero.Core/Localization/LocalizationModule.cs index 6fc961e4..75a61557 100644 --- a/zero.Core/Localization/LocalizationModule.cs +++ b/zero.Core/Localization/LocalizationModule.cs @@ -13,12 +13,5 @@ public class LocalizationModule : ZeroModule services.AddScoped(); services.AddScoped(); services.AddScoped(); - - services.Configure(opts => - { - opts.Provide("zero.country", "Country", "Default country", "fth-flag"); - opts.Provide("zero.language", "Language", "Default language", "fth-flag"); - opts.Provide("zero.translation", "Translation", "Default translation", "fth-flag"); - }); } } \ No newline at end of file diff --git a/zero.Core/Media/Indexes/Media_ByChildren.cs b/zero.Core/Media/Indexes/Media_ByChildren.cs index 37c4e9cf..3c91dfa7 100644 --- a/zero.Core/Media/Indexes/Media_ByChildren.cs +++ b/zero.Core/Media/Indexes/Media_ByChildren.cs @@ -4,7 +4,7 @@ namespace zero.Media; public class Media_ByChildren : ZeroMultiMapIndex { - public class Result : ZeroIdEntity, ISupportsPersistence + public class Result : ZeroIdEntity, ISupportsDbConventions { public string ParentId { get; set; } diff --git a/zero.Core/Media/Indexes/Media_ByHierarchy.cs b/zero.Core/Media/Indexes/Media_ByHierarchy.cs index ad33cd33..565dff31 100644 --- a/zero.Core/Media/Indexes/Media_ByHierarchy.cs +++ b/zero.Core/Media/Indexes/Media_ByHierarchy.cs @@ -4,7 +4,7 @@ namespace zero.Media; public class Media_ByHierarchy : ZeroIndex { - public class Result : ZeroIdEntity, ISupportsPersistence + public class Result : ZeroIdEntity, ISupportsDbConventions { public string Name { get; set; } diff --git a/zero.Core/Media/MediaModule.cs b/zero.Core/Media/MediaModule.cs index 568b746d..b408b05b 100644 --- a/zero.Core/Media/MediaModule.cs +++ b/zero.Core/Media/MediaModule.cs @@ -41,5 +41,11 @@ public class MediaModule : ZeroModule raven.Indexes.Add(); raven.Indexes.Add(); }); + + //services.Configure(opts => + //{ + // // TODO should we use MediaType here? + // opts.Provide("zero.media", "@media.name", icon: "fth-image"); + //}); } } \ No newline at end of file diff --git a/zero.Core/Models/ZeroEntity.cs b/zero.Core/Models/ZeroEntity.cs index 34f60089..80d7c27f 100644 --- a/zero.Core/Models/ZeroEntity.cs +++ b/zero.Core/Models/ZeroEntity.cs @@ -4,7 +4,7 @@ using System.Diagnostics; namespace zero.Models; [DebuggerDisplay("Id = {Id,nq}, Name = {Name}, Alias = {Alias}")] -public class ZeroEntity : ZeroIdEntity, ISupportsPersistence, ISupportsRouting, ISupportsFlavors +public class ZeroEntity : ZeroIdEntity, ISupportsDbConventions, ISupportsRouting, ISupportsFlavors { /// /// Full name of the entity @@ -77,4 +77,5 @@ public class ZeroEntity : ZeroIdEntity, ISupportsPersistence, ISupportsRouting, /// [JsonIgnore] public string Url { get; set; } -} \ No newline at end of file +} + \ No newline at end of file diff --git a/zero.Core/Pages/IPageTypeHandler.cs b/zero.Core/Pages/IPageTypeHandler.cs index 7ed5a8a7..b77d44e8 100644 --- a/zero.Core/Pages/IPageTypeHandler.cs +++ b/zero.Core/Pages/IPageTypeHandler.cs @@ -2,5 +2,5 @@ public interface IPageTypeHandler : IHandler { - Task> GetAllowedPageTypes(Application application, IEnumerable registeredTypes, IEnumerable parents); + Task> GetAllowedPageTypes(Application application, IEnumerable registeredTypes, IEnumerable parents); } diff --git a/zero.Core/Pages/Indexes/Pages_AsHistory.cs b/zero.Core/Pages/Indexes/Pages_AsHistory.cs index 70f17d1d..7ba2bf8f 100644 --- a/zero.Core/Pages/Indexes/Pages_AsHistory.cs +++ b/zero.Core/Pages/Indexes/Pages_AsHistory.cs @@ -4,7 +4,7 @@ namespace zero.Pages; public class Pages_AsHistory : ZeroIndex { - public class Result : ZeroIdEntity, ISupportsPersistence + public class Result : ZeroIdEntity, ISupportsDbConventions { public DateTimeOffset LastModified { get; set; } } diff --git a/zero.Core/Pages/Indexes/Pages_ByHierarchy.cs b/zero.Core/Pages/Indexes/Pages_ByHierarchy.cs index cfc4b15a..b28e53ad 100644 --- a/zero.Core/Pages/Indexes/Pages_ByHierarchy.cs +++ b/zero.Core/Pages/Indexes/Pages_ByHierarchy.cs @@ -4,7 +4,7 @@ namespace zero.Pages; public class Pages_ByHierarchy : ZeroIndex { - public class Result : ZeroIdEntity, ISupportsPersistence + public class Result : ZeroIdEntity, ISupportsDbConventions { public string Name { get; set; } diff --git a/zero.Core/Pages/Indexes/Pages_ByType.cs b/zero.Core/Pages/Indexes/Pages_ByType.cs index fa6db8c7..9e52ed06 100644 --- a/zero.Core/Pages/Indexes/Pages_ByType.cs +++ b/zero.Core/Pages/Indexes/Pages_ByType.cs @@ -8,9 +8,9 @@ public class Pages_ByType : ZeroIndex { Map = items => items.Select(item => new { - PageTypeAlias = item.PageTypeAlias + PageTypeAlias = item.Flavor }); - Index(x => x.PageTypeAlias, FieldIndexing.Exact); + Index(x => x.Flavor, FieldIndexing.Exact); } } \ No newline at end of file diff --git a/zero.Core/Pages/Indexes/Pages_WithChildren.cs b/zero.Core/Pages/Indexes/Pages_WithChildren.cs index 170515eb..c34840cc 100644 --- a/zero.Core/Pages/Indexes/Pages_WithChildren.cs +++ b/zero.Core/Pages/Indexes/Pages_WithChildren.cs @@ -4,7 +4,7 @@ namespace zero.Pages; public class Pages_WithChildren : ZeroIndex { - public class Result : ZeroIdEntity, ISupportsPersistence + public class Result : ZeroIdEntity, ISupportsDbConventions { public string ParentId { get; set; } diff --git a/zero.Core/Pages/Models/Page.cs b/zero.Core/Pages/Models/Page.cs index 88bab4bd..eaa90da9 100644 --- a/zero.Core/Pages/Models/Page.cs +++ b/zero.Core/Pages/Models/Page.cs @@ -15,11 +15,6 @@ public class Page : ZeroEntity, ISupportsTrees /// public string ParentId { get; set; } - /// - /// Alias of the used page type - /// - public string PageTypeAlias { get; set; } - /// /// Date when the page is published /// diff --git a/zero.Core/Pages/Models/PageType.cs b/zero.Core/Pages/Models/PageType.cs deleted file mode 100644 index 23c991d0..00000000 --- a/zero.Core/Pages/Models/PageType.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace zero.Pages; - -/// -/// A page type holds information about a Page implementation -/// -public class PageType : PageType where T : Page, new() -{ - public PageType() : base(typeof(T)) { } -} - - -/// -/// A page type holds information about a Page implementation -/// -public class PageType -{ - /// - /// Type of the associated page class - /// - public Type ContentType { get; private set; } - - /// - /// Alias for querying - /// - public string Alias { get; set; } - - /// - /// Name of the page type - /// - public string Name { get; set; } - - /// - /// Optional description - /// - public string Description { get; set; } - - /// - /// Icon of the page type - /// - public string Icon { get; set; } - - - public PageType(Type type) - { - ContentType = type; - } - - public static PageType Convert(PageType model) where T : Page, new() - { - return new PageType(model.ContentType) - { - Alias = model.Alias, - Name = model.Name, - Description = model.Description, - Icon = model.Icon - }; - } -} diff --git a/zero.Core/Pages/PageOptions.cs b/zero.Core/Pages/PageOptions.cs index 6086671a..137d4f7b 100644 --- a/zero.Core/Pages/PageOptions.cs +++ b/zero.Core/Pages/PageOptions.cs @@ -1,41 +1,6 @@ namespace zero.Pages; -public class PageOptions : List +public class PageOptions { public string Root { get; set; } = Constants.Pages.DefaultRootPageTypeAlias; - - - public void Add(PageType pageType) where T : Page, new() - { - Add(PageType.Convert(pageType)); - } - - - public void Add(string alias, string name, string description, string icon) where T : Page, new() - { - Add(new PageType(typeof(T)) - { - Alias = alias, - Name = name, - Description = description, - Icon = icon - }); - } - - - public void Add(Type type, string alias, string name, string description, string icon) - { - Add(new PageType(type) - { - Alias = alias, - Name = name, - Description = description, - Icon = icon - }); - } - - public PageType GetByAlias(string alias) - { - return this.FirstOrDefault(x => x.Alias == alias); - } } diff --git a/zero.Core/Pages/PageTypeService.cs b/zero.Core/Pages/PageTypeService.cs index e44eee2f..65c9ad6b 100644 --- a/zero.Core/Pages/PageTypeService.cs +++ b/zero.Core/Pages/PageTypeService.cs @@ -9,33 +9,33 @@ public class PageTypeService : IPageTypeService protected IHandlerHolder Handler { get; private set; } - protected PageOptions PageOptions { get; set; } + protected FlavorOptions Flavors { get; set; } public PageTypeService(IZeroContext context, IZeroOptions options, IHandlerHolder handler) { Context = context; Handler = handler; - PageOptions = options.For(); + Flavors = options.For(); } /// - public IList GetPageTypes() + public IEnumerable GetAll() { - return PageOptions; + return Flavors.GetAll(); } /// - public PageType GetPageType(string pageTypeAlias) + public FlavorConfig GetByAlias(string flavorAlias) { - return PageOptions.FirstOrDefault(x => x.Alias == pageTypeAlias); + return Flavors.Get(flavorAlias); } /// - public async Task> GetAllowedPageTypes(string pageParentId = null) + public async Task> GetAllowedFlavors(string pageParentId = null) { List parents = new(); @@ -62,10 +62,10 @@ public class PageTypeService : IPageTypeService // if there is no registered handler we just allow all page types if (handler == null) { - return PageOptions; + return Flavors.GetAll(); } - return (await handler.GetAllowedPageTypes(Context.Application, PageOptions, parents))?.ToList() ?? new(); + return (await handler.GetAllowedPageTypes(Context.Application, Flavors.GetAll(), parents))?.ToList() ?? new(); } } @@ -75,15 +75,15 @@ public interface IPageTypeService /// /// Get all available page types /// - IList GetPageTypes(); + IEnumerable GetAll(); /// /// Get all page types which are allowed below a selected parent page /// - Task> GetAllowedPageTypes(string pageParentId = null); + Task> GetAllowedFlavors(string pageParentId = null); /// /// Get a specific page type by alias /// - PageType GetPageType(string pageTypeAlias); + FlavorConfig GetByAlias(string flavorAlias); } diff --git a/zero.Core/Pages/PagesModule.cs b/zero.Core/Pages/PagesModule.cs index a16899ce..13fc1029 100644 --- a/zero.Core/Pages/PagesModule.cs +++ b/zero.Core/Pages/PagesModule.cs @@ -14,15 +14,18 @@ public class PagesModule : ZeroModule services.AddOptions().Bind(configuration.GetSection("Zero:Pages")); services.AddOptions().Bind(configuration.GetSection("Zero:PageModules")); - services.Configure(opts => + services.Configure(opts => { - RavenOptions raven = opts.For(); - raven.Indexes.Add(); - raven.Indexes.Add(); - raven.Indexes.Add(); - raven.Indexes.Add(); + opts.Indexes.Add(); + opts.Indexes.Add(); + opts.Indexes.Add(); + opts.Indexes.Add(); + }); - opts.For().Add(Constants.Pages.FolderAlias, "@page.folder.name", "@page.folder.description", "fth-folder"); + services.Configure(opts => + { + opts.Provide(); + opts.Add(Constants.Pages.FolderAlias, "@page.folder.name", "@page.folder.description", "fth-folder"); }); } } \ No newline at end of file diff --git a/zero.Core/Pages/PagesStore.cs b/zero.Core/Pages/PagesStore.cs index 70f80ee0..3268866f 100644 --- a/zero.Core/Pages/PagesStore.cs +++ b/zero.Core/Pages/PagesStore.cs @@ -16,15 +16,15 @@ public class PagesStore : TreeEntityStore, IPagesStore /// public override async Task IsAllowedAsChild(Page model, string parentId) { - IList pageTypes = await PageTypes.GetAllowedPageTypes(parentId); - return pageTypes.Any(x => x.Alias == model.PageTypeAlias); + IEnumerable pageTypes = await PageTypes.GetAllowedFlavors(parentId); + return pageTypes.Any(x => x.Alias == model.Flavor); } /// - public async Task Empty(string pageType, string parentId = null) + public async Task Empty(string pageTypeAlias, string parentId = null) { - PageType type = PageTypes.GetPageType(pageType); + FlavorConfig type = PageTypes.GetByAlias(pageTypeAlias); if (type == null) { @@ -32,7 +32,7 @@ public class PagesStore : TreeEntityStore, IPagesStore } Page model = await Empty(); - model.PageTypeAlias = type.Alias; + model.Flavor = type.Alias; model.ParentId = parentId; if (!await IsAllowedAsChild(model, parentId)) @@ -48,7 +48,7 @@ public class PagesStore : TreeEntityStore, IPagesStore protected override void ValidationRules(ZeroValidator validator) { validator.RuleFor(x => x.Name).NotEmpty(); - validator.RuleFor(x => x.PageTypeAlias).NotEmpty(); + validator.RuleFor(x => x.Flavor).NotEmpty(); } } diff --git a/zero.Core/Persistence/ISupportsPersistence.cs b/zero.Core/Persistence/ISupportsDbConventions.cs similarity index 74% rename from zero.Core/Persistence/ISupportsPersistence.cs rename to zero.Core/Persistence/ISupportsDbConventions.cs index 68ad2af0..1ce4ec3b 100644 --- a/zero.Core/Persistence/ISupportsPersistence.cs +++ b/zero.Core/Persistence/ISupportsDbConventions.cs @@ -3,4 +3,4 @@ /// /// Triggers custom Raven conventions for database operations /// -public interface ISupportsPersistence { } \ No newline at end of file +public interface ISupportsDbConventions { } \ No newline at end of file diff --git a/zero.Core/Persistence/Tokens/ChangeToken.cs b/zero.Core/Persistence/Tokens/ChangeToken.cs index 06554343..ab5cb38d 100644 --- a/zero.Core/Persistence/Tokens/ChangeToken.cs +++ b/zero.Core/Persistence/Tokens/ChangeToken.cs @@ -4,7 +4,7 @@ /// A change token holds a reference to a database entity /// This is used to verify change requests for entities in the zero backoffice /// -public class ChangeToken : ISupportsPersistence +public class ChangeToken : ISupportsDbConventions { public string Id { get; set; } diff --git a/zero.Core/Persistence/Tokens/Token.cs b/zero.Core/Persistence/Tokens/Token.cs index 891d2cd6..866b85fd 100644 --- a/zero.Core/Persistence/Tokens/Token.cs +++ b/zero.Core/Persistence/Tokens/Token.cs @@ -1,7 +1,7 @@ namespace zero.Persistence; [RavenCollection("Tokens")] -public class SecurityToken : ISupportsPersistence +public class SecurityToken : ISupportsDbConventions { public string Id { get; set; } diff --git a/zero.Core/Persistence/ZeroDocumentConventionsBuilder.cs b/zero.Core/Persistence/ZeroDocumentConventionsBuilder.cs index c920df37..6bb7e4fc 100644 --- a/zero.Core/Persistence/ZeroDocumentConventionsBuilder.cs +++ b/zero.Core/Persistence/ZeroDocumentConventionsBuilder.cs @@ -9,7 +9,7 @@ public class ZeroDocumentConventionsBuilder : IZeroDocumentConventionsBuilder { protected HashSet PolymorphTypes { get; private set; } = new(); - protected Type AcceptsZeroConventionsType { get; set; } = typeof(ISupportsPersistence); + protected Type AcceptsZeroConventionsType { get; set; } = typeof(ISupportsDbConventions); protected char IdentityPartsSeparator { get; set; } = '.'; diff --git a/zero.Core/Routing/PageRouteProvider/PageLinkProvider.cs b/zero.Core/Routing/PageRouteProvider/PageLinkProvider.cs index 9f74ffcb..c2e223ff 100644 --- a/zero.Core/Routing/PageRouteProvider/PageLinkProvider.cs +++ b/zero.Core/Routing/PageRouteProvider/PageLinkProvider.cs @@ -63,9 +63,9 @@ public class PageLinkProvider : ILinkProvider return null; } - PageType pageType = PageTypeService.GetPageType(page.PageTypeAlias); + FlavorConfig pageType = PageTypeService.GetByAlias(page.Flavor); - string url = await Routes.GetUrl(page); + string url = await Routes.GetUrl(page); return new() { diff --git a/zero.Core/Routing/PageRouteProvider/PageRouteProvider.cs b/zero.Core/Routing/PageRouteProvider/PageRouteProvider.cs index 087a4f86..8a9fcd1d 100644 --- a/zero.Core/Routing/PageRouteProvider/PageRouteProvider.cs +++ b/zero.Core/Routing/PageRouteProvider/PageRouteProvider.cs @@ -37,7 +37,7 @@ public class PageRouteProvider : ZeroRouteProvider route.Url = UrlBuilder.GetUrl(model, parents); route.DependsOn(model.Id); route.DependsOn(parents.Select(x => x.Id).ToArray()); - route.Param(PAGE_TYPE_PARAM, model.PageTypeAlias); + route.Param(PAGE_TYPE_PARAM, model.Flavor); route.Param(PAGE_ID_PARAM, model.Id); route.Param(PAGE_IS_FOLDER, model is PageFolder); diff --git a/zero.Core/Routing/PageRouteProvider/PageUrlBuilder.cs b/zero.Core/Routing/PageRouteProvider/PageUrlBuilder.cs index 218d0581..973d0420 100644 --- a/zero.Core/Routing/PageRouteProvider/PageUrlBuilder.cs +++ b/zero.Core/Routing/PageRouteProvider/PageUrlBuilder.cs @@ -70,7 +70,7 @@ public class PageUrlBuilder : IPageUrlBuilder { alias = page.UrlAlias; } - else if (page.PageTypeAlias == Options.Root) + else if (page.Flavor == Options.Root) { return String.Empty; } diff --git a/zero.Core/Spaces/FlavorOptionsExtensions.cs b/zero.Core/Spaces/FlavorOptionsExtensions.cs new file mode 100644 index 00000000..0d36b35a --- /dev/null +++ b/zero.Core/Spaces/FlavorOptionsExtensions.cs @@ -0,0 +1,48 @@ +namespace zero.Spaces; + +public static class FlavorOptionsExtensions +{ + public static void AddSpaceList(this FlavorOptions options, string alias, string name, string description, string icon, string editorAlias = null) where T : Space, new() + { + options.Add(new SpaceType(typeof(T)) + { + Alias = alias, + EditorAlias = editorAlias.Or(alias), + View = SpaceView.List, + Construct = _ => new T(), + Name = name, + Description = description, + Icon = icon + }); + } + + + public static void AddSpaceEditor(this FlavorOptions options, string alias, string name, string description, string icon, string editorAlias = null) where T : Space, new() + { + options.Add(new SpaceType(typeof(T)) + { + Alias = alias, + EditorAlias = editorAlias.Or(alias), + View = SpaceView.Editor, + Construct = _ => new T(), + Name = name, + Description = description, + Icon = icon + }); + } + + public static void AddSpaceCustom(this FlavorOptions options, string componentPath, string alias, string name, string description, string icon, string editorAlias = null) where T : Space, new() + { + options.Add(new SpaceType(typeof(T)) + { + Alias = alias, + EditorAlias = editorAlias.Or(alias), + View = SpaceView.Custom, + Construct = _ => new T(), + ComponentPath = componentPath, + Name = name, + Description = description, + Icon = icon + }); + } +} diff --git a/zero.Core/Spaces/Models/Space.cs b/zero.Core/Spaces/Models/Space.cs index a4788abb..59f2b952 100644 --- a/zero.Core/Spaces/Models/Space.cs +++ b/zero.Core/Spaces/Models/Space.cs @@ -6,8 +6,5 @@ [RavenCollection("Spaces")] public class Space : ZeroEntity { - /// - /// Associated space - /// - public string SpaceTypeAlias { get; set; } + } \ No newline at end of file diff --git a/zero.Core/Spaces/Models/SpaceType.cs b/zero.Core/Spaces/Models/SpaceType.cs index afd4b479..4474f1f7 100644 --- a/zero.Core/Spaces/Models/SpaceType.cs +++ b/zero.Core/Spaces/Models/SpaceType.cs @@ -1,22 +1,14 @@ namespace zero.Spaces; -public class SpaceType +public class SpaceType : FlavorConfig { - public string Alias { get; set; } + public SpaceType(Type type) : base(type) { } public string EditorAlias { get; set; } - public Type Type { get; set; } - public SpaceView View { get; set; } public string ComponentPath { get; set; } - public string Name { get; set; } - - public string Description { get; set; } - - public string Icon { get; set; } - public bool LineBelow { get; set; } } \ No newline at end of file diff --git a/zero.Core/Spaces/SpaceOptions.cs b/zero.Core/Spaces/SpaceOptions.cs deleted file mode 100644 index d3d319d0..00000000 --- a/zero.Core/Spaces/SpaceOptions.cs +++ /dev/null @@ -1,77 +0,0 @@ -namespace zero.Spaces; - -public class SpaceOptions : List -{ - public void Add() where T : SpaceType, new() - { - Add(new T()); - } - - - public void AddList(string alias, string name, string description, string icon) where T : Space, new() - { - Add(new SpaceType() - { - Alias = alias, - View = SpaceView.List, - Name = name, - Description = description, - Icon = icon, - Type = typeof(T) - }); - } - - - public void AddEditor(string alias, string name, string description, string icon) where T : Space, new() - { - Add(new SpaceType() - { - Alias = alias, - View = SpaceView.Editor, - Name = name, - Description = description, - Icon = icon, - Type = typeof(T) - }); - } - - - public void AddSeparator() - { - SpaceType lastSpace = this.LastOrDefault(); - - if (lastSpace != null) - { - lastSpace.LineBelow = true; - } - } - - - public void AddCustom(string componentPath, string alias, string name, string description, string icon) where T : Space, new() - { - Add(new SpaceType() - { - Alias = alias, - View = SpaceView.Custom, - ComponentPath = componentPath, - Name = name, - Description = description, - Icon = icon, - Type = typeof(T) - }); - } - - - public void AddCustom(string componentPath, string alias, string name, string description, string icon) - { - Add(new SpaceType() - { - Alias = alias, - View = SpaceView.Custom, - ComponentPath = componentPath, - Name = name, - Description = description, - Icon = icon - }); - } -} diff --git a/zero.Core/Spaces/SpaceService.cs b/zero.Core/Spaces/SpaceService.cs index 9a501e65..a3bd8d7f 100644 --- a/zero.Core/Spaces/SpaceService.cs +++ b/zero.Core/Spaces/SpaceService.cs @@ -11,25 +11,9 @@ public class SpaceService : SpaceStore, ISpaceService public ISpaceTypeService Types { get; protected set; } - public SpaceService(IStoreContext store, ISpaceTypeService spaceTypeService) : base(store, spaceTypeService) + public SpaceService(IStoreContext store, ISpaceTypeService spaceTypeService) : base(store) { - Types = SpaceTypes; - } - - - /// - public Task Empty(string spaceType) where T : Space, new() - { - SpaceType type = SpaceTypes.GetByAlias(spaceType); - - if (type == null) - { - return Task.FromResult(null); - } - - T model = new(); - model.SpaceTypeAlias = type.Alias; - return Task.FromResult(model); + Types = spaceTypeService; } @@ -69,7 +53,7 @@ public class SpaceService : SpaceStore, ISpaceService /// public async Task> Load(string spaceType, int pageNumber, int pageSize, Func, IQueryable> expression = null) where T : Space, new() { - IRavenQueryable queryable = Session.Query().Where(x => x.SpaceTypeAlias == spaceType).Statistics(out QueryStatistics statistics); + IRavenQueryable queryable = Session.Query().Where(x => x.Flavor == spaceType).Statistics(out QueryStatistics statistics); expression ??= x => x; List result = await expression(queryable).Paging(pageNumber, pageSize).ToListAsync(); @@ -82,7 +66,7 @@ public class SpaceService : SpaceStore, ISpaceService where T : Space, new() where TIndex : AbstractCommonApiForIndexes, new() { - IRavenQueryable queryable = Session.Query().Where(x => x.SpaceTypeAlias == spaceType).Statistics(out QueryStatistics statistics); + IRavenQueryable queryable = Session.Query().Where(x => x.Flavor == spaceType).Statistics(out QueryStatistics statistics); expression ??= x => x; List result = await expression(queryable).Paging(pageNumber, pageSize).ToListAsync(); @@ -98,11 +82,6 @@ public interface ISpaceService : ISpaceStore /// ISpaceTypeService Types { get; } - /// - /// Get editor item for a space - /// - Task Empty(string spaceType) where T : Space, new(); - /// /// Get editor item for a space /// diff --git a/zero.Core/Spaces/SpaceStore.cs b/zero.Core/Spaces/SpaceStore.cs index c393a779..998ca1ee 100644 --- a/zero.Core/Spaces/SpaceStore.cs +++ b/zero.Core/Spaces/SpaceStore.cs @@ -2,37 +2,14 @@ public class SpaceStore : EntityStore, ISpaceStore { - protected ISpaceTypeService SpaceTypes { get; private set; } - - - public SpaceStore(IStoreContext context, ISpaceTypeService spaceTypes) : base(context) + public SpaceStore(IStoreContext context) : base(context) { - SpaceTypes = spaceTypes; - } - - - /// - public async Task Empty(string spaceType) - { - SpaceType type = SpaceTypes.GetByAlias(spaceType); - - if (type == null) - { - return null; - } - - Space model = await Empty(); - model.SpaceTypeAlias = type.Alias; - - return model; + } } public interface ISpaceStore : IEntityStore { - /// - /// Get new instance of an entity for a specific Space type - /// - Task Empty(string spaceType); + } \ No newline at end of file diff --git a/zero.Core/Spaces/SpaceTypeService.cs b/zero.Core/Spaces/SpaceTypeService.cs index 67241507..467c31f5 100644 --- a/zero.Core/Spaces/SpaceTypeService.cs +++ b/zero.Core/Spaces/SpaceTypeService.cs @@ -6,36 +6,35 @@ public class SpaceTypeService : ISpaceTypeService protected IHandlerHolder Handler { get; private set; } - protected SpaceOptions SpaceOptions { get; set; } + protected FlavorOptions Flavors { get; set; } public SpaceTypeService(IZeroContext context, IZeroOptions options, IHandlerHolder handler) { Context = context; Handler = handler; - SpaceOptions = options.For(); + Flavors = options.For(); } /// public IList GetAll() { - return SpaceOptions; + return Flavors.GetAll().Select(x => (SpaceType)x).ToList(); } /// - public SpaceType GetByAlias(string pageTypeAlias) + public SpaceType GetByAlias(string spaceTypeAlias) { - return SpaceOptions.FirstOrDefault(x => x.Alias == pageTypeAlias); + return Flavors.Get(spaceTypeAlias) as SpaceType; } /// public SpaceType GetByType() where T : Space { - Type type = typeof(T); - return SpaceOptions.FirstOrDefault(x => x.Type == type); + return Flavors.Get() as SpaceType; } } diff --git a/zero.Core/Spaces/SpacesModule.cs b/zero.Core/Spaces/SpacesModule.cs index 3b6c7a6b..96240eef 100644 --- a/zero.Core/Spaces/SpacesModule.cs +++ b/zero.Core/Spaces/SpacesModule.cs @@ -10,7 +10,5 @@ public class SpacesModule : ZeroModule services.AddScoped(); services.AddScoped(); services.AddScoped(); - - services.AddOptions().Bind(configuration.GetSection("Zero:Spaces")); } } \ No newline at end of file diff --git a/zero.Core/Stores/EntityStore.cs b/zero.Core/Stores/EntityStore.cs index 3e72e11e..31887968 100644 --- a/zero.Core/Stores/EntityStore.cs +++ b/zero.Core/Stores/EntityStore.cs @@ -4,7 +4,7 @@ using Raven.Client.Documents.Linq; namespace zero.Stores; -public abstract class EntityStore : IEntityStore where T : ZeroIdEntity, new() +public abstract class EntityStore : IEntityStore where T : ZeroIdEntity, ISupportsFlavors, new() { /// public Guid Guid { get; private set; } = Guid.NewGuid(); @@ -35,6 +35,12 @@ public abstract class EntityStore : IEntityStore where T : ZeroIdEntity, n /// public virtual Task Empty() => Operations.Empty(); + /// + public virtual Task Empty(string flavor) => Operations.Empty(flavor); + + /// + public virtual Task Empty(string flavor) where TFlavor : T => Operations.Empty(flavor); + /// public virtual Task Load(string id, string changeVector = null) => Operations.Load(id, changeVector); @@ -105,6 +111,16 @@ public interface IEntityStore where T : ZeroIdEntity, new() /// Task Empty(); + /// + /// Get new instance of an entity with a specific flavor + /// + Task Empty(string flavor); + + /// + /// Get new instance of an entity with a specific flavor + /// + Task Empty(string flavor) where TFlavor : T; + /// /// Get an entity by Id /// diff --git a/zero.Core/Stores/Flavors/FlavorOptions.cs b/zero.Core/Stores/Flavors/FlavorOptions.cs index 581472ce..83418021 100644 --- a/zero.Core/Stores/Flavors/FlavorOptions.cs +++ b/zero.Core/Stores/Flavors/FlavorOptions.cs @@ -7,29 +7,63 @@ public class FlavorOptions readonly ConcurrentDictionary> Flavors = new(); - public void Provide(string alias, string name, string description = null, string icon = null) - where TEntity : ISupportsFlavors, new() + public IEnumerable GetAll() { - Provide(_ => new TEntity(), alias, name, description, icon); + return GetAll(typeof(TEntity)); } - public void Provide(Func construct, string alias, string name, string description = null, string icon = null) + public IEnumerable GetAll(Type type) + { + return Flavors.GetValueOrDefault(type, new()); + } + + + public FlavorConfig Get() + { + return Get(typeof(TEntity), typeof(TFlavor)); + } + + + public FlavorConfig Get(string alias) + { + return Get(typeof(TEntity), alias); + } + + + public FlavorConfig Get(Type baseType, Type flavorType) + { + List flavors = Flavors.GetValueOrDefault(baseType, new()); + return flavors.FirstOrDefault(x => x.FlavorType == flavorType); + } + + + public FlavorConfig Get(Type baseType, string alias) + { + List flavors = Flavors.GetValueOrDefault(baseType, new()); + return flavors.FirstOrDefault(x => x.Alias == alias); + } + + + public void Provide() + where TEntity : ISupportsFlavors, new() + { + Provide(_ => new TEntity()); + } + + + public void Provide(Func construct) where TEntity : ISupportsFlavors { Type type = typeof(TEntity); if (Flavors.ContainsKey(type)) { - throw new KeyNotFoundException($"Already a provider for type '{type.Name}' registered."); + return; } FlavorProviderConfig config = new(type) { - Alias = alias, - Name = name, - Description = description, - Icon = icon, Construct = _ => construct(_) }; @@ -41,36 +75,46 @@ public class FlavorOptions public void Add(string alias, string name, string description = null, string icon = null) - where TEntity : ISupportsFlavors + where TEntity : ISupportsFlavors, new() where TFlavor : TEntity, new() { - Add(typeof(TEntity), typeof(TFlavor), _ => new TFlavor(), alias, name, description, icon); + Add(typeof(TFlavor), _ => new TFlavor(), alias, name, description, icon); } public void Add(Func construct, string alias, string name, string description = null, string icon = null) - where TEntity : ISupportsFlavors + where TEntity : ISupportsFlavors, new() where TFlavor : TEntity, new() { - Add(typeof(TEntity), typeof(TFlavor), construct, alias, name, description, icon); + Add(typeof(TFlavor), construct, alias, name, description, icon); } - void Add(Type baseEntityType, Type flavorType, Func construct, string alias, string name, string description = null, string icon = null) + public void Add(Type flavorType, Func construct, string alias, string name, string description = null, string icon = null) + where TEntity : ISupportsFlavors, new() + where TFlavor : TEntity, new() { - if (!Flavors.ContainsKey(baseEntityType)) - { - throw new KeyNotFoundException($"No provider for type '{baseEntityType.Name}' found. Use Provide<>() first."); - } - - FlavorConfig config = new(flavorType) + Add(new(flavorType) { Alias = alias, Name = name, Description = description, Icon = icon, Construct = _ => construct(_) - }; + }); + } + + + public void Add(FlavorConfig config) + where TEntity : ISupportsFlavors, new() + where TFlavor : TEntity, new() + { + Type baseEntityType = typeof(TEntity); + + if (!Flavors.ContainsKey(baseEntityType)) + { + Provide(); + } Flavors[baseEntityType].Add(config); } diff --git a/zero.Core/Stores/StoreOperations.Empty.cs b/zero.Core/Stores/StoreOperations.Empty.cs new file mode 100644 index 00000000..6dddf8ad --- /dev/null +++ b/zero.Core/Stores/StoreOperations.Empty.cs @@ -0,0 +1,46 @@ +namespace zero.Stores; + +public partial class StoreOperations : IStoreOperations +{ + /// + /// Get new instance of an entity + /// + public virtual Task Empty() where T : ZeroIdEntity, new() + { + return Task.FromResult(new T()); + } + + + /// + public virtual Task Empty(string flavor) where T : ZeroIdEntity, ISupportsFlavors, new() + { + FlavorConfig config = Flavors.Get(flavor); + T result = config?.Construct(config) as T; + + if (result == null) + { + return Task.FromResult(default); + } + + result.Flavor = flavor; + return Task.FromResult(result); + } + + + /// + public virtual Task Empty(string flavor) + where T : ZeroIdEntity, ISupportsFlavors, new() + where TFlavor : T + { + FlavorConfig config = Flavors.Get(); + TFlavor result = config?.Construct(config) as TFlavor; + + if (result == null) + { + return Task.FromResult(default); + } + + result.Flavor = flavor; + return Task.FromResult(result); + } +} \ No newline at end of file diff --git a/zero.Core/Stores/StoreOperations.cs b/zero.Core/Stores/StoreOperations.cs index e3fea465..4fc2c902 100644 --- a/zero.Core/Stores/StoreOperations.cs +++ b/zero.Core/Stores/StoreOperations.cs @@ -18,21 +18,15 @@ public partial class StoreOperations : IStoreOperations protected IInterceptors Interceptors { get; private set; } + protected FlavorOptions Flavors { get; private set; } - public StoreOperations(IZeroContext context, IInterceptors interceptors) + + public StoreOperations(IZeroContext context, IInterceptors interceptors, IZeroOptions options) { Context = context; Interceptors = interceptors; Options = new(true); - } - - - /// - /// Get new instance of an entity - /// - public virtual Task Empty() where T : ZeroIdEntity, new() - { - return Task.FromResult(new T()); + Flavors = options.For(); } @@ -122,6 +116,18 @@ public interface IStoreOperations /// Task Empty() where T : ZeroIdEntity, new(); + /// + /// Get new instance of an entity with a specific flavor + /// + Task Empty(string flavor) where T : ZeroIdEntity, ISupportsFlavors, new(); + + /// + /// Get new instance of an entity with a specific flavor + /// + Task Empty(string flavor) + where T : ZeroIdEntity, ISupportsFlavors, new() + where TFlavor : T; + /// /// Generate model Id by using configured document store conventions /// diff --git a/zero.Core/Stores/TreeEntityStore.cs b/zero.Core/Stores/TreeEntityStore.cs index 680807a9..c99d3bbb 100644 --- a/zero.Core/Stores/TreeEntityStore.cs +++ b/zero.Core/Stores/TreeEntityStore.cs @@ -3,7 +3,7 @@ using Raven.Client.Documents.Linq; namespace zero.Stores; -public abstract class TreeEntityStore : EntityStore, ITreeEntityStore where T : ZeroIdEntity, ISupportsTrees, new() +public abstract class TreeEntityStore : EntityStore, ITreeEntityStore where T : ZeroIdEntity, ISupportsTrees, ISupportsFlavors, new() { public TreeEntityStore(IStoreContext collectionContext) : base(collectionContext) { }