From b58d739f2590d1509cd57331e9c11e49c7ef5afd Mon Sep 17 00:00:00 2001 From: Tobias Klika Date: Sat, 11 Dec 2021 17:34:42 +0100 Subject: [PATCH] first draft of Spaces API --- .../Spaces/Indexes/zero_Api_Spaces_Listing.cs | 19 +++ zero.Api/Endpoints/Spaces/SpaceModule.cs | 18 +++ zero.Api/Endpoints/Spaces/SpacePermissions.cs | 31 +++++ zero.Api/Endpoints/Spaces/SpacesController.cs | 77 +++++++++++ .../Models/ListQuery/ListQueryExtensions.cs | 2 +- .../RecycleBin/RecycleBinController.cs | 36 ----- .../Security/AuthenticationController.cs | 46 ------- .../Spaces/SpaceContentEditModel.cs | 13 -- .../Modules_legacy/Spaces/SpacePermissions.cs | 17 --- .../Modules_legacy/Spaces/SpacesController.cs | 129 ------------------ zero.Api/Modules_legacy/Spaces/zero_Spaces.cs | 22 --- zero.Api/Plugin.cs | 1 + zero.Api/Usings.cs | 1 + .../_notimplemented/RecycleBinController.cs | 36 +++++ ...ZeroBackofficeControllerModelConvention.cs | 2 +- zero.Core/Spaces/SpaceTypeService.cs | 6 +- zero.Demo/Mapper.cs | 23 ++++ zero.Demo/Program.cs | 7 + zero.Demo/TeamSpace.cs | 12 ++ 19 files changed, 230 insertions(+), 268 deletions(-) create mode 100644 zero.Api/Endpoints/Spaces/Indexes/zero_Api_Spaces_Listing.cs create mode 100644 zero.Api/Endpoints/Spaces/SpaceModule.cs create mode 100644 zero.Api/Endpoints/Spaces/SpacePermissions.cs create mode 100644 zero.Api/Endpoints/Spaces/SpacesController.cs delete mode 100644 zero.Api/Modules_legacy/RecycleBin/RecycleBinController.cs delete mode 100644 zero.Api/Modules_legacy/Security/AuthenticationController.cs delete mode 100644 zero.Api/Modules_legacy/Spaces/SpaceContentEditModel.cs delete mode 100644 zero.Api/Modules_legacy/Spaces/SpacePermissions.cs delete mode 100644 zero.Api/Modules_legacy/Spaces/SpacesController.cs delete mode 100644 zero.Api/Modules_legacy/Spaces/zero_Spaces.cs create mode 100644 zero.Api/_notimplemented/RecycleBinController.cs create mode 100644 zero.Demo/Mapper.cs create mode 100644 zero.Demo/TeamSpace.cs diff --git a/zero.Api/Endpoints/Spaces/Indexes/zero_Api_Spaces_Listing.cs b/zero.Api/Endpoints/Spaces/Indexes/zero_Api_Spaces_Listing.cs new file mode 100644 index 00000000..fc1b945b --- /dev/null +++ b/zero.Api/Endpoints/Spaces/Indexes/zero_Api_Spaces_Listing.cs @@ -0,0 +1,19 @@ +using Raven.Client.Documents.Indexes; + +namespace zero.Api.Endpoints.Spaces; + +public class zero_Api_Spaces_Listing : ZeroIndex +{ + protected override void Create() + { + Map = items => items.Select(item => new + { + Name = item.Name, + IsActive = item.IsActive, + Flavor = item.Flavor, + CreatedDate = item.CreatedDate + }); + + Index(x => x.Name, FieldIndexing.Search); + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Spaces/SpaceModule.cs b/zero.Api/Endpoints/Spaces/SpaceModule.cs new file mode 100644 index 00000000..d94da71d --- /dev/null +++ b/zero.Api/Endpoints/Spaces/SpaceModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Api.Endpoints.Spaces; + +public class SpaceModule : ZeroModule +{ + public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(); + //services.AddSingleton(); + + services.Configure(opts => + { + opts.Indexes.Add(); + }); + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Spaces/SpacePermissions.cs b/zero.Api/Endpoints/Spaces/SpacePermissions.cs new file mode 100644 index 00000000..5df46f86 --- /dev/null +++ b/zero.Api/Endpoints/Spaces/SpacePermissions.cs @@ -0,0 +1,31 @@ +namespace zero.Api.Endpoints.Spaces; + +public class SpacePermissions : PermissionProvider +{ + public const string Group = "zero.space"; + + public const string Create = "zero.space.create"; + public const string Read = "zero.space.read"; + public const string Update = "zero.space.update"; + public const string Delete = "zero.space.delete"; + + + public override Task Configure(IPermissionContext context) + { + PermissionGroup group = new(Group, "@space.list"); + group.Permissions.Add(new Permission("zero.space.defaults", "Default permissions") + { + Children = new() + { + new(Create, "@permission.states.create"), + new(Read, "@permission.states.read"), + new(Update, "@permission.states.update"), + new(Delete, "@permission.states.delete") + } + }); + + context.AddGroup(group); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Spaces/SpacesController.cs b/zero.Api/Endpoints/Spaces/SpacesController.cs new file mode 100644 index 00000000..91976767 --- /dev/null +++ b/zero.Api/Endpoints/Spaces/SpacesController.cs @@ -0,0 +1,77 @@ +using Microsoft.AspNetCore.Mvc; +using System.Collections; + +namespace zero.Api.Endpoints.Spaces; + +public class SpacesController : ZeroApiEntityStoreController +{ + ISpaceTypeService SpaceTypes; + + public SpacesController(ISpaceStore store, ISpaceTypeService spaceTypes) : base(store) + { + SpaceTypes = spaceTypes; + } + + + [HttpGet("types")] + [ZeroAuthorize(SpacePermissions.Read)] + public virtual ActionResult GetTypes() => Ok(SpaceTypes.GetAll()); + + [HttpGet("types/{alias}")] + [ZeroAuthorize(SpacePermissions.Read)] + public virtual ActionResult GetTypes(string alias) => Ok(SpaceTypes.GetByAlias(alias)); + + [HttpGet("{alias}")] + [ZeroAuthorize(SpacePermissions.Read)] + public virtual async Task> Get(string alias, [FromQuery] ListQuery query = null) + { + SpaceType space = SpaceTypes.GetByAlias(alias); + + if (space == null) + { + return NotFound(); + } + + if (space.View != SpaceView.Editor) + { + query.OrderQuery ??= q => q.OrderByDescending(x => x.CreatedDate); + query.SearchSelector ??= x => x.Name; + Paged result = await Store.Load(query.Page, query.PageSize, q => q.Where(x => x.Flavor == alias).Filter(query)); + return result; + } + + var stream = Store.Stream(q => q.Where(x => x.Flavor == alias)); + Space item = await stream.FirstOrDefaultAsync(); + return item; + } + + + [HttpGet("{alias}/empty")] + [ZeroAuthorize(SpacePermissions.Create)] + public virtual async Task> Empty(string alias) => await Store.Empty(alias); + + + //[HttpGet("{alias}/{id}")] + //[ZeroAuthorize(SpacePermissions.Read)] + //public virtual Task> Get(string alias, string id, string changeVector = null) => GetModel(id, changeVector); + + + //[HttpGet("")] + //[ZeroAuthorize(SpacePermissions.Read)] + //public virtual Task> Get([FromQuery] ListQuery query) => GetModels(query); + + + //[HttpPost("")] + //[ZeroAuthorize(SpacePermissions.Create)] + //public virtual Task> Create(LanguageSave saveModel) => CreateModel(saveModel); + + + //[HttpPut("{id}")] + //[ZeroAuthorize(SpacePermissions.Update)] + //public virtual Task> Update(string id, LanguageSave updateModel, [FromQuery] string changeToken = null) => UpdateModel(id, updateModel, changeToken); + + + //[HttpDelete("{id}")] + //[ZeroAuthorize(SpacePermissions.Delete)] + //public virtual Task> Delete(string id) => DeleteModel(id); +} \ No newline at end of file diff --git a/zero.Api/Models/ListQuery/ListQueryExtensions.cs b/zero.Api/Models/ListQuery/ListQueryExtensions.cs index e7c7949f..9f4ea021 100644 --- a/zero.Api/Models/ListQuery/ListQueryExtensions.cs +++ b/zero.Api/Models/ListQuery/ListQueryExtensions.cs @@ -16,7 +16,7 @@ public static class ListQueryExtensions return JsonConvert.DeserializeObject>(options); } - public static IQueryable Filter(this IRavenQueryable source, ListQuery listQuery) where T : ZeroIdEntity + public static IQueryable Filter(this IQueryable source, ListQuery listQuery) where T : ZeroIdEntity { if (listQuery == null) { diff --git a/zero.Api/Modules_legacy/RecycleBin/RecycleBinController.cs b/zero.Api/Modules_legacy/RecycleBin/RecycleBinController.cs deleted file mode 100644 index 294666af..00000000 --- a/zero.Api/Modules_legacy/RecycleBin/RecycleBinController.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System.Threading.Tasks; -using zero.Core.Api; -using zero.Core.Entities; - -namespace zero.Web.Controllers -{ - public class RecycleBinController : BackofficeController - { - IRecycleBinApi Api; - - public RecycleBinController(IRecycleBinApi api) - { - Api = api; - } - - - public async Task> GetByQuery([FromQuery] RecycleBinListQuery query) - { - query.IncludeInactive = true; - return await Api.GetByQuery(query); - } - - - public async Task GetCountByOperation([FromQuery] string operationId) => await Api.GetCountByOperation(operationId); - - - [HttpDelete] - public async Task> Delete([FromQuery] string id) => await Api.Delete(id); - - - [HttpDelete] - public async Task> DeleteByGroup([FromQuery] string group) => await Api.DeleteByGroup(group); - - } -} diff --git a/zero.Api/Modules_legacy/Security/AuthenticationController.cs b/zero.Api/Modules_legacy/Security/AuthenticationController.cs deleted file mode 100644 index dc3221b6..00000000 --- a/zero.Api/Modules_legacy/Security/AuthenticationController.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System.Threading.Tasks; -using zero.Core.Api; -using zero.Core.Entities; -using zero.Core.Identity; -using zero.Web.Models; - -namespace zero.Web.Controllers -{ - [ZeroAuthorize(false)] - public class AuthenticationController : BackofficeController - { - IAuthenticationService Api; - - - public AuthenticationController(IAuthenticationService api) - { - Api = api; - } - - - public async Task> GetUser() => Edit(await Api.GetUser()); - - - public Result IsLoggedIn() => Result.Maybe(Api.IsLoggedIn()); - - - [HttpPost] - public async Task LoginUser([FromBody] LoginModel model) => await Api.Login(model.Email, model.Password, model.IsPersistent); - - - [HttpPost, ZeroAuthorize] - public async Task LogoutUser() - { - await Api.Logout(); - return Result.Success(); - } - - - [HttpPost, ZeroAuthorize] - public async Task SwitchApp(string appId) - { - return Result.Maybe(await Api.TrySwitchApp(appId)); - } - } -} diff --git a/zero.Api/Modules_legacy/Spaces/SpaceContentEditModel.cs b/zero.Api/Modules_legacy/Spaces/SpaceContentEditModel.cs deleted file mode 100644 index 485f187e..00000000 --- a/zero.Api/Modules_legacy/Spaces/SpaceContentEditModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using zero.Core.Entities; - -namespace zero.Web.Models -{ - public class SpaceContentEditModel : ObsoleteEditModel - { - public string Alias { get; set; } - - public SpaceContent Model { get; set; } - } -} diff --git a/zero.Api/Modules_legacy/Spaces/SpacePermissions.cs b/zero.Api/Modules_legacy/Spaces/SpacePermissions.cs deleted file mode 100644 index aca8d17b..00000000 --- a/zero.Api/Modules_legacy/Spaces/SpacePermissions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using zero.Core; -using zero.Core.Identity; - -namespace zero.Web.Defaults -{ - public class SpacePermissions : PermissionCollection - { - public SpacePermissions() - { - Alias = Constants.PermissionCollections.Spaces; - Label = "@permission.collections.spaces"; - Description = "@permission.collections.spaces_description"; - - // Items get added at runtime - } - } -} diff --git a/zero.Api/Modules_legacy/Spaces/SpacesController.cs b/zero.Api/Modules_legacy/Spaces/SpacesController.cs deleted file mode 100644 index ad97d9d0..00000000 --- a/zero.Api/Modules_legacy/Spaces/SpacesController.cs +++ /dev/null @@ -1,129 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using zero.Core.Api; -using zero.Core.Entities; -using zero.Core.Extensions; -using zero.Core.Identity; - -namespace zero.Web.Controllers -{ - [ZeroAuthorize(Permissions.Sections.Spaces, PermissionsValue.True)] - public class SpacesController : BackofficeController - { - ISpacesApi Api; - - public SpacesController(ISpacesApi api) - { - Api = api; - } - - - public IActionResult GetByAlias([FromQuery] string alias) - { - if (!CanReadSpace(alias)) - { - return new StatusCodeResult(403); - } - - return Ok(Api.GetByAlias(alias)); - } - - - public List GetAll() => Api.GetAll().Where(space => CanReadSpace(space.Alias)).ToList(); - - - public async Task GetList([FromQuery] string alias, [FromQuery] ListQuery query = null) - { - if (!CanReadSpace(alias)) - { - return new StatusCodeResult(403); - } - - return Ok(await Api.GetListByQuery(alias, query)); - } - - - - public async Task GetContent([FromQuery] string alias, [FromQuery] string contentId = null) - { - if (!CanReadSpace(alias)) - { - return new StatusCodeResult(403); - } - - Space space = Api.GetByAlias(alias); - SpaceContent model = null; - - if (space == null) - { - return new StatusCodeResult(404); - } - - if (space.View != SpaceView.List || !contentId.IsNullOrEmpty()) - { - model = await Api.GetItem(alias, contentId); - } - - if (model == null && contentId.IsNullOrEmpty()) - { - model = Activator.CreateInstance(space.Type) as SpaceContent; - model.SpaceAlias = space.Alias; - } - - return Ok(Edit(model)); - } - - - /// - /// Save content item - /// - public async Task Save([FromBody] SpaceContent model) - { - if (!CanWriteSpace(model.SpaceAlias)) - { - return new StatusCodeResult(403); - } - - return Ok(await Api.Save(model)); - } - - - /// - /// Deletes a content item - /// - public async Task Delete([FromQuery] string alias, [FromQuery] string id) - { - if (!CanWriteSpace(alias)) - { - return new StatusCodeResult(403); - } - - return Ok(await Api.Delete(id)); - } - - - /// - /// Whether the current user can read a space - /// - bool CanReadSpace(string alias) - { - return true; - //Permission permission = AuthenticationApi.GetPermission(Permissions.Spaces.PREFIX + alias); - //return permission != null && permission.CanRead; - } - - - /// - /// Whether the current user can read a space - /// - bool CanWriteSpace(string alias) - { - return true; - //Permission permission = AuthenticationApi.GetPermission(Permissions.Spaces.PREFIX + alias); - //return permission != null && permission.CanWrite; - } - } -} diff --git a/zero.Api/Modules_legacy/Spaces/zero_Spaces.cs b/zero.Api/Modules_legacy/Spaces/zero_Spaces.cs deleted file mode 100644 index c7479d3b..00000000 --- a/zero.Api/Modules_legacy/Spaces/zero_Spaces.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Raven.Client.Documents.Indexes; -using System.Linq; -using zero.Core.Entities; - -namespace zero.Core.Database.Indexes -{ - public class zero_Spaces : ZeroIndex - { - protected override void Create() - { - Map = items => items.Select(item => new - { - Name = item.Name, - IsActive = item.IsActive, - SpaceAlias = item.SpaceAlias, - CreatedDate = item.CreatedDate - }); - - Index(x => x.Name, FieldIndexing.Search); - } - } -} diff --git a/zero.Api/Plugin.cs b/zero.Api/Plugin.cs index 09342287..6a152ea0 100644 --- a/zero.Api/Plugin.cs +++ b/zero.Api/Plugin.cs @@ -33,6 +33,7 @@ public class ZeroApiPlugin : ZeroPlugin ZeroModuleCollection.AddModule(services, configuration); ZeroModuleCollection.AddModule(services, configuration); ZeroModuleCollection.AddModule(services, configuration); + ZeroModuleCollection.AddModule(services, configuration); PostConfigureServices?.Invoke(services, configuration); } diff --git a/zero.Api/Usings.cs b/zero.Api/Usings.cs index f05e507a..13a7eeaf 100644 --- a/zero.Api/Usings.cs +++ b/zero.Api/Usings.cs @@ -22,6 +22,7 @@ global using zero.Models; global using zero.Pages; global using zero.Persistence; global using zero.Routing; +global using zero.Spaces; global using zero.Utils; global using zero.Api.Configuration; diff --git a/zero.Api/_notimplemented/RecycleBinController.cs b/zero.Api/_notimplemented/RecycleBinController.cs new file mode 100644 index 00000000..8f6e88e3 --- /dev/null +++ b/zero.Api/_notimplemented/RecycleBinController.cs @@ -0,0 +1,36 @@ +//using Microsoft.AspNetCore.Mvc; +//using System.Threading.Tasks; +//using zero.Core.Api; +//using zero.Core.Entities; + +//namespace zero.Web.Controllers +//{ +// public class RecycleBinController : BackofficeController +// { +// IRecycleBinApi Api; + +// public RecycleBinController(IRecycleBinApi api) +// { +// Api = api; +// } + + +// public async Task> GetByQuery([FromQuery] RecycleBinListQuery query) +// { +// query.IncludeInactive = true; +// return await Api.GetByQuery(query); +// } + + +// public async Task GetCountByOperation([FromQuery] string operationId) => await Api.GetCountByOperation(operationId); + + +// [HttpDelete] +// public async Task> Delete([FromQuery] string id) => await Api.Delete(id); + + +// [HttpDelete] +// public async Task> DeleteByGroup([FromQuery] string group) => await Api.DeleteByGroup(group); + +// } +//} diff --git a/zero.Backoffice/ZeroBackofficeControllerModelConvention.cs b/zero.Backoffice/ZeroBackofficeControllerModelConvention.cs index 0b22b2fd..c0e06d6c 100644 --- a/zero.Backoffice/ZeroBackofficeControllerModelConvention.cs +++ b/zero.Backoffice/ZeroBackofficeControllerModelConvention.cs @@ -16,7 +16,7 @@ public class ZeroBackofficeControllerModelConvention : ZeroApiControllerModelCon readonly bool RuntimeIsAppAware = false; - public ZeroBackofficeControllerModelConvention(string zeroPath, string backofficeApiPath = "api/backoffice", bool isAppAware = false) : base(zeroPath, backofficeApiPath, isAppAware) + public ZeroBackofficeControllerModelConvention(string zeroPath, string backofficeApiPath = "backoffice", bool isAppAware = false) : base(zeroPath, backofficeApiPath, isAppAware) { RuntimeIsAppAware = isAppAware; AppAwareRouteModel = BuildRouteModel(zeroPath, backofficeApiPath, true); diff --git a/zero.Core/Spaces/SpaceTypeService.cs b/zero.Core/Spaces/SpaceTypeService.cs index 467c31f5..456354c1 100644 --- a/zero.Core/Spaces/SpaceTypeService.cs +++ b/zero.Core/Spaces/SpaceTypeService.cs @@ -18,9 +18,9 @@ public class SpaceTypeService : ISpaceTypeService /// - public IList GetAll() + public IEnumerable GetAll() { - return Flavors.GetAll().Select(x => (SpaceType)x).ToList(); + return Flavors.GetAll().Select(x => (SpaceType)x); } @@ -44,7 +44,7 @@ public interface ISpaceTypeService /// /// Get all available space types /// - IList GetAll(); + IEnumerable GetAll(); /// /// Get a specific space type by alias diff --git a/zero.Demo/Mapper.cs b/zero.Demo/Mapper.cs new file mode 100644 index 00000000..7a186ec4 --- /dev/null +++ b/zero.Demo/Mapper.cs @@ -0,0 +1,23 @@ +//using zero.Mapper; +//using zero.Spaces; + +//namespace zero.Demo; + +//public class Mapper : ZeroMapperProfile +//{ +// public override void Configure(IZeroMapper mapper) +// { +// mapper.Define((source, ctx) => new(), Map); +// mapper.Define((source, ctx) => new(), Map); +// mapper.Define((source, ctx) => new(), Map); +// } + + +// protected virtual void Map(Language source, LanguageBasic target, IZeroMapperContext ctx) +// { +// this.MapBasicData(source, target); +// target.Code = source.Code; +// target.IsDefault = source.IsDefault; +// target.InheritedLanguageId = source.InheritedLanguageId; +// } +//} \ No newline at end of file diff --git a/zero.Demo/Program.cs b/zero.Demo/Program.cs index 3c8b67fb..7c72678b 100644 --- a/zero.Demo/Program.cs +++ b/zero.Demo/Program.cs @@ -5,6 +5,8 @@ using zero.Backoffice; using zero.Backoffice.DevServer; using zero.Demo; using zero.Routing; +using zero.Spaces; +using zero.Stores; var builder = WebApplication.CreateBuilder(args); @@ -24,6 +26,11 @@ builder.Services.Configure(opts => opts.Enabled = false; }); +builder.Services.Configure(opts => +{ + opts.AddSpaceList("team", "Team", "Members of our team", "fth-users", "spaces.team"); +}); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/zero.Demo/TeamSpace.cs b/zero.Demo/TeamSpace.cs new file mode 100644 index 00000000..996ae7c3 --- /dev/null +++ b/zero.Demo/TeamSpace.cs @@ -0,0 +1,12 @@ +using zero.Spaces; + +namespace zero.Demo; + +public class TeamMember : Space +{ + public string Position { get; set; } + + public string ImageId { get; set; } + + public string Email { get; set; } +} \ No newline at end of file