diff --git a/zero.Api/Configuration/ApiOptions.cs b/zero.Api/Configuration/ApiOptions.cs index da7670c2..5c06b3a4 100644 --- a/zero.Api/Configuration/ApiOptions.cs +++ b/zero.Api/Configuration/ApiOptions.cs @@ -1,4 +1,4 @@ -using zero.Api.Modules.Search; +using zero.Api.Endpoints.Search; namespace zero.Api.Configuration; diff --git a/zero.Api/Endpoints/Applications/ApplicationModule.cs b/zero.Api/Endpoints/Applications/ApplicationModule.cs new file mode 100644 index 00000000..30d508df --- /dev/null +++ b/zero.Api/Endpoints/Applications/ApplicationModule.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Api.Endpoints.Applications; + +public class ApplicationModule : ZeroModule +{ + public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Applications/ApplicationPermissions.cs b/zero.Api/Endpoints/Applications/ApplicationPermissions.cs new file mode 100644 index 00000000..4fcb9b71 --- /dev/null +++ b/zero.Api/Endpoints/Applications/ApplicationPermissions.cs @@ -0,0 +1,30 @@ +namespace zero.Api.Endpoints.Applications; + +public class ApplicationPermissions : PermissionProvider +{ + // TODO wrong + public const string Create = "zero.settings.country.create"; + public const string Read = "zero.settings.country.read"; + public const string Update = "zero.settings.country.update"; + public const string Delete = "zero.settings.country.delete"; + + + public override Task Configure(IPermissionContext context) + { + if (!context.TryGetGroup(Constants.Permissions.Groups.Settings, out PermissionGroup group)) + { + group.Permissions.Add(new Permission("zero.settings.country", "@settings.application.countries.name") + { + Children = new() + { + new(Create, "@permission.states.create"), + new(Read, "@permission.states.read"), + new(Update, "@permission.states.update"), + new(Delete, "@permission.states.delete") + } + }); + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Applications/ApplicationsController.cs b/zero.Api/Endpoints/Applications/ApplicationsController.cs new file mode 100644 index 00000000..af46b427 --- /dev/null +++ b/zero.Api/Endpoints/Applications/ApplicationsController.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Mvc; + +namespace zero.Api.Endpoints.Applications; + +[ZeroSystemApi] +public class ApplicationsController : ZeroApiEntityStoreController +{ + public ApplicationsController(IApplicationStore store) : base(store) + { + + } + + + [HttpGet("empty")] + [ZeroAuthorize(ApplicationPermissions.Create)] + public virtual Task> Empty() => EmptyModel(); + + + [HttpGet("{id}")] + [ZeroAuthorize(ApplicationPermissions.Read)] + public virtual Task> Get(string id, string changeVector = null) => GetModel(id, changeVector); + + + [HttpGet("")] + [ZeroAuthorize(ApplicationPermissions.Read)] + public virtual Task> Get([FromQuery] ListQuery query) => GetModels(query); + + + [HttpPost("")] + [ZeroAuthorize(ApplicationPermissions.Create)] + public virtual Task> Create(ApplicationSave saveModel) => CreateModel(saveModel); + + + [HttpPut("{id}")] + [ZeroAuthorize(ApplicationPermissions.Update)] + public virtual Task> Update(string id, ApplicationSave updateModel) => UpdateModel(id, updateModel); + + + [HttpDelete("{id}")] + [ZeroAuthorize(ApplicationPermissions.Delete)] + public virtual Task> Delete(string id) => DeleteModel(id); +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Applications/Maps/ApplicationBasic.cs b/zero.Api/Endpoints/Applications/Maps/ApplicationBasic.cs new file mode 100644 index 00000000..7d106e82 --- /dev/null +++ b/zero.Api/Endpoints/Applications/Maps/ApplicationBasic.cs @@ -0,0 +1,10 @@ +namespace zero.Api.Endpoints.Applications; + +public class ApplicationBasic : BasicModel +{ + public string FullName { get; set; } + + public string ImageId { get; set; } + + public Uri[] Domains { get; set; } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Applications/Maps/ApplicationEdit.cs b/zero.Api/Endpoints/Applications/Maps/ApplicationEdit.cs new file mode 100644 index 00000000..2d706db1 --- /dev/null +++ b/zero.Api/Endpoints/Applications/Maps/ApplicationEdit.cs @@ -0,0 +1,16 @@ +namespace zero.Api.Endpoints.Applications; + +public class ApplicationEdit : DisplayModel +{ + public string ImageId { get; set; } + + public string IconId { get; set; } + + public Uri[] Domains { get; set; } = Array.Empty(); + + public string FullName { get; set; } + + public string Email { get; set; } + + public List Features { get; set; } = new(); +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Applications/Maps/ApplicationMapperProfile.cs b/zero.Api/Endpoints/Applications/Maps/ApplicationMapperProfile.cs new file mode 100644 index 00000000..073c25e3 --- /dev/null +++ b/zero.Api/Endpoints/Applications/Maps/ApplicationMapperProfile.cs @@ -0,0 +1,42 @@ +namespace zero.Api.Endpoints.Applications; + +public class ApplicationMapperProfile : 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(Application source, ApplicationBasic target, IZeroMapperContext ctx) + { + this.MapBasicData(source, target); + target.ImageId = source.ImageId; + target.Domains = source.Domains; + target.FullName = source.FullName; + } + + protected virtual void Map(Application source, ApplicationEdit target, IZeroMapperContext ctx) + { + this.MapDisplayData(source, target); + target.ImageId = source.ImageId; + target.IconId = source.IconId; + target.Domains = source.Domains; + target.FullName = source.FullName; + target.Email = source.Email; + target.Features = source.Features; +} + + protected virtual void Map(ApplicationSave source, Application target, IZeroMapperContext ctx) + { + this.MapSaveData(source, target); + target.ImageId = source.ImageId; + target.IconId = source.IconId; + target.Domains = source.Domains; + target.FullName = source.FullName; + target.Email = source.Email; + target.Features = source.Features; + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Applications/Maps/ApplicationSave.cs b/zero.Api/Endpoints/Applications/Maps/ApplicationSave.cs new file mode 100644 index 00000000..00f9fb0f --- /dev/null +++ b/zero.Api/Endpoints/Applications/Maps/ApplicationSave.cs @@ -0,0 +1,16 @@ +namespace zero.Api.Endpoints.Applications; + +public class ApplicationSave : SaveModel +{ + public string ImageId { get; set; } + + public string IconId { get; set; } + + public Uri[] Domains { get; set; } = Array.Empty(); + + public string FullName { get; set; } + + public string Email { get; set; } + + public List Features { get; set; } = new(); +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Countries/CountriesController.cs b/zero.Api/Endpoints/Countries/CountriesController.cs new file mode 100644 index 00000000..ec2868d9 --- /dev/null +++ b/zero.Api/Endpoints/Countries/CountriesController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc; + +namespace zero.Api.Endpoints.Countries; + +public class CountriesController : ZeroApiEntityStoreController +{ + public CountriesController(ICountryStore store) : base(store) + { + + } + + + [HttpGet("empty")] + [ZeroAuthorize(CountryPermissions.Create)] + public virtual Task> Empty() => EmptyModel(); + + + [HttpGet("{id}")] + [ZeroAuthorize(CountryPermissions.Read)] + public virtual Task> Get(string id, string changeVector = null) => GetModel(id, changeVector); + + + [HttpGet("")] + [ZeroAuthorize(CountryPermissions.Read)] + public virtual Task> Get([FromQuery] ListQuery query) + { + query.OrderQuery = q => q.OrderByDescending(x => x.IsPreferred).ThenBy(x => x.Name); + return GetModels(query); + } + + + [HttpPost("")] + [ZeroAuthorize(CountryPermissions.Create)] + public virtual Task> Create(CountrySave saveModel) => CreateModel(saveModel); + + + [HttpPut("{id}")] + [ZeroAuthorize(CountryPermissions.Update)] + public virtual Task> Update(string id, CountrySave updateModel) => UpdateModel(id, updateModel); + + + [HttpDelete("{id}")] + [ZeroAuthorize(CountryPermissions.Delete)] + public virtual Task> Delete(string id) => DeleteModel(id); +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Countries/CountryModule.cs b/zero.Api/Endpoints/Countries/CountryModule.cs new file mode 100644 index 00000000..9296efdc --- /dev/null +++ b/zero.Api/Endpoints/Countries/CountryModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Api.Endpoints.Countries; + +public class CountryModule : 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/Modules/Countries/CountryPermissions.cs b/zero.Api/Endpoints/Countries/CountryPermissions.cs similarity index 95% rename from zero.Api/Modules/Countries/CountryPermissions.cs rename to zero.Api/Endpoints/Countries/CountryPermissions.cs index 0d815784..5c36ce84 100644 --- a/zero.Api/Modules/Countries/CountryPermissions.cs +++ b/zero.Api/Endpoints/Countries/CountryPermissions.cs @@ -1,4 +1,4 @@ -namespace zero.Api.Modules.Countries; +namespace zero.Api.Endpoints.Countries; public class CountryPermissions : PermissionProvider { diff --git a/zero.Api/Modules/Countries/Indexes/zero_Backoffice_Countries_Listing.cs b/zero.Api/Endpoints/Countries/Indexes/zero_Api_Countries_Listing.cs similarity index 70% rename from zero.Api/Modules/Countries/Indexes/zero_Backoffice_Countries_Listing.cs rename to zero.Api/Endpoints/Countries/Indexes/zero_Api_Countries_Listing.cs index 7288740a..94dba27b 100644 --- a/zero.Api/Modules/Countries/Indexes/zero_Backoffice_Countries_Listing.cs +++ b/zero.Api/Endpoints/Countries/Indexes/zero_Api_Countries_Listing.cs @@ -1,8 +1,8 @@ using Raven.Client.Documents.Indexes; -namespace zero.Api.Modules.Countries; +namespace zero.Api.Endpoints.Countries; -public class zero_Backoffice_Countries_Listing : ZeroIndex +public class zero_Api_Countries_Listing : ZeroIndex { protected override void Create() { diff --git a/zero.Api/Modules/Countries/Maps/CountryBasic.cs b/zero.Api/Endpoints/Countries/Maps/CountryBasic.cs similarity index 74% rename from zero.Api/Modules/Countries/Maps/CountryBasic.cs rename to zero.Api/Endpoints/Countries/Maps/CountryBasic.cs index e04243ac..8954f575 100644 --- a/zero.Api/Modules/Countries/Maps/CountryBasic.cs +++ b/zero.Api/Endpoints/Countries/Maps/CountryBasic.cs @@ -1,4 +1,4 @@ -namespace zero.Api.Modules.Countries; +namespace zero.Api.Endpoints.Countries; public class CountryBasic : BasicModel { diff --git a/zero.Api/Modules/Countries/Maps/CountryEdit.cs b/zero.Api/Endpoints/Countries/Maps/CountryEdit.cs similarity index 75% rename from zero.Api/Modules/Countries/Maps/CountryEdit.cs rename to zero.Api/Endpoints/Countries/Maps/CountryEdit.cs index d815de5d..b5500ea6 100644 --- a/zero.Api/Modules/Countries/Maps/CountryEdit.cs +++ b/zero.Api/Endpoints/Countries/Maps/CountryEdit.cs @@ -1,4 +1,4 @@ -namespace zero.Api.Modules.Countries; +namespace zero.Api.Endpoints.Countries; public class CountryEdit : DisplayModel { diff --git a/zero.Api/Endpoints/Countries/Maps/CountryMapperProfile.cs b/zero.Api/Endpoints/Countries/Maps/CountryMapperProfile.cs new file mode 100644 index 00000000..38defec4 --- /dev/null +++ b/zero.Api/Endpoints/Countries/Maps/CountryMapperProfile.cs @@ -0,0 +1,33 @@ +namespace zero.Api.Endpoints.Countries; + +public class CountryMapperProfile : 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(Country source, CountryBasic target, IZeroMapperContext ctx) + { + this.MapBasicData(source, target); + target.Code = source.Code; + target.IsPreferred = source.IsPreferred; + } + + protected virtual void Map(Country source, CountryEdit target, IZeroMapperContext ctx) + { + this.MapDisplayData(source, target); + target.Code = source.Code; + target.IsPreferred = source.IsPreferred; + } + + protected virtual void Map(CountrySave source, Country target, IZeroMapperContext ctx) + { + this.MapSaveData(source, target); + target.Code = source.Code; + target.IsPreferred = source.IsPreferred; + } +} \ No newline at end of file diff --git a/zero.Api/Modules/Countries/Maps/CountrySave.cs b/zero.Api/Endpoints/Countries/Maps/CountrySave.cs similarity index 74% rename from zero.Api/Modules/Countries/Maps/CountrySave.cs rename to zero.Api/Endpoints/Countries/Maps/CountrySave.cs index 68a264a4..101ae933 100644 --- a/zero.Api/Modules/Countries/Maps/CountrySave.cs +++ b/zero.Api/Endpoints/Countries/Maps/CountrySave.cs @@ -1,4 +1,4 @@ -namespace zero.Api.Modules.Countries; +namespace zero.Api.Endpoints.Countries; public class CountrySave : SaveModel { diff --git a/zero.Api/Endpoints/Cultures/_todo_CulturesController.cs b/zero.Api/Endpoints/Cultures/_todo_CulturesController.cs new file mode 100644 index 00000000..1587ab85 --- /dev/null +++ b/zero.Api/Endpoints/Cultures/_todo_CulturesController.cs @@ -0,0 +1,34 @@ +// TODO this is only for the backoffice +// not required for API + +//using Microsoft.AspNetCore.Mvc; + +//namespace zero.Api.Endpoints.Languages; + +//public class CulturesController : ZeroApiController +//{ +// protected ICultureService Service { get; set; } + +// protected IZeroOptions Options { get; set; } + + +// public CulturesController(ICultureService service, IZeroOptions options) +// { +// Service = service; +// Options = options; +// } + + +// [HttpGet("empty")] +// [ZeroAuthorize(LanguagePermissions.Create)] +// public List GetAllCultures() +// { +// return Service.GetAllCultures(); +// } + + +// public List GetSupportedCultures() +// { +// return Service.GetAllCultures(Options.For.SupportedLanguages); +// } +//} \ No newline at end of file diff --git a/zero.Api/Modules/Error/ErrorController.cs b/zero.Api/Endpoints/Error/ErrorController.cs similarity index 93% rename from zero.Api/Modules/Error/ErrorController.cs rename to zero.Api/Endpoints/Error/ErrorController.cs index 1fa27d8f..5033c7ca 100644 --- a/zero.Api/Modules/Error/ErrorController.cs +++ b/zero.Api/Endpoints/Error/ErrorController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Mvc; -namespace zero.Api.Modules.Error; +namespace zero.Api.Endpoints.Error; public class ErrorController : ZeroApiController { diff --git a/zero.Api/Modules/Error/ServiceCollectionExtensions.cs b/zero.Api/Endpoints/Error/ServiceCollectionExtensions.cs similarity index 86% rename from zero.Api/Modules/Error/ServiceCollectionExtensions.cs rename to zero.Api/Endpoints/Error/ServiceCollectionExtensions.cs index 065d4cec..63baea1e 100644 --- a/zero.Api/Modules/Error/ServiceCollectionExtensions.cs +++ b/zero.Api/Endpoints/Error/ServiceCollectionExtensions.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.DependencyInjection; -namespace zero.Api.Modules.Error; +namespace zero.Api.Endpoints.Error; public static class ServiceCollectionExtensions { diff --git a/zero.Api/Endpoints/Languages/Indexes/zero_Api_Languages_Listing.cs b/zero.Api/Endpoints/Languages/Indexes/zero_Api_Languages_Listing.cs new file mode 100644 index 00000000..093127c1 --- /dev/null +++ b/zero.Api/Endpoints/Languages/Indexes/zero_Api_Languages_Listing.cs @@ -0,0 +1,18 @@ +using Raven.Client.Documents.Indexes; + +namespace zero.Api.Endpoints.Languages; + +public class zero_Api_Languages_Listing : ZeroIndex +{ + protected override void Create() + { + Map = items => items.Select(item => new + { + Name = item.Name, + CreatedDate = item.CreatedDate, + IsDefault = item.IsDefault + }); + + Index(x => x.Name, FieldIndexing.Search); + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Languages/LanguageModule.cs b/zero.Api/Endpoints/Languages/LanguageModule.cs new file mode 100644 index 00000000..a951d5e2 --- /dev/null +++ b/zero.Api/Endpoints/Languages/LanguageModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Api.Endpoints.Languages; + +public class LanguageModule : 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/Languages/LanguagePermissions.cs b/zero.Api/Endpoints/Languages/LanguagePermissions.cs new file mode 100644 index 00000000..75237912 --- /dev/null +++ b/zero.Api/Endpoints/Languages/LanguagePermissions.cs @@ -0,0 +1,29 @@ +namespace zero.Api.Endpoints.Languages; + +public class LanguagePermissions : PermissionProvider +{ + public const string Create = "zero.settings.language.create"; + public const string Read = "zero.settings.language.read"; + public const string Update = "zero.settings.language.update"; + public const string Delete = "zero.settings.language.delete"; + + + public override Task Configure(IPermissionContext context) + { + if (!context.TryGetGroup(Constants.Permissions.Groups.Settings, out PermissionGroup group)) + { + group.Permissions.Add(new Permission("zero.settings.language", "@settings.application.languages.name") + { + Children = new() + { + new(Create, "@permission.states.create"), + new(Read, "@permission.states.read"), + new(Update, "@permission.states.update"), + new(Delete, "@permission.states.delete") + } + }); + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Languages/LanguagesController.cs b/zero.Api/Endpoints/Languages/LanguagesController.cs new file mode 100644 index 00000000..f359cded --- /dev/null +++ b/zero.Api/Endpoints/Languages/LanguagesController.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Mvc; + +namespace zero.Api.Endpoints.Languages; + +public class LanguagesController : ZeroApiEntityStoreController +{ + public LanguagesController(ILanguageStore store) : base(store) + { + + } + + + [HttpGet("empty")] + [ZeroAuthorize(LanguagePermissions.Create)] + public virtual Task> Empty() => EmptyModel(); + + + [HttpGet("{id}")] + [ZeroAuthorize(LanguagePermissions.Read)] + public virtual Task> Get(string id, string changeVector = null) => GetModel(id, changeVector); + + + [HttpGet("")] + [ZeroAuthorize(LanguagePermissions.Read)] + public virtual Task> Get([FromQuery] ListQuery query) => GetModels(query); + + + [HttpPost("")] + [ZeroAuthorize(LanguagePermissions.Create)] + public virtual Task> Create(LanguageSave saveModel) => CreateModel(saveModel); + + + [HttpPut("{id}")] + [ZeroAuthorize(LanguagePermissions.Update)] + public virtual Task> Update(string id, LanguageSave updateModel) => UpdateModel(id, updateModel); + + + [HttpDelete("{id}")] + [ZeroAuthorize(LanguagePermissions.Delete)] + public virtual Task> Delete(string id) => DeleteModel(id); +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Languages/Maps/LanguageBasic.cs b/zero.Api/Endpoints/Languages/Maps/LanguageBasic.cs new file mode 100644 index 00000000..a25cc28c --- /dev/null +++ b/zero.Api/Endpoints/Languages/Maps/LanguageBasic.cs @@ -0,0 +1,10 @@ +namespace zero.Api.Endpoints.Languages; + +public class LanguageBasic : BasicModel +{ + public string Code { get; set; } + + public bool IsDefault { get; set; } + + public string InheritedLanguageId { get; set; } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Languages/Maps/LanguageEdit.cs b/zero.Api/Endpoints/Languages/Maps/LanguageEdit.cs new file mode 100644 index 00000000..4a21661c --- /dev/null +++ b/zero.Api/Endpoints/Languages/Maps/LanguageEdit.cs @@ -0,0 +1,10 @@ +namespace zero.Api.Endpoints.Languages; + +public class LanguageEdit : DisplayModel +{ + public string Code { get; set; } + + public bool IsDefault { get; set; } + + public string InheritedLanguageId { get; set; } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Languages/Maps/LanguageMapperProfile.cs b/zero.Api/Endpoints/Languages/Maps/LanguageMapperProfile.cs new file mode 100644 index 00000000..4a999439 --- /dev/null +++ b/zero.Api/Endpoints/Languages/Maps/LanguageMapperProfile.cs @@ -0,0 +1,36 @@ +namespace zero.Api.Endpoints.Languages; + +public class LanguageMapperProfile : 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; + } + + protected virtual void Map(Language source, LanguageEdit target, IZeroMapperContext ctx) + { + this.MapDisplayData(source, target); + target.Code = source.Code; + target.IsDefault = source.IsDefault; + target.InheritedLanguageId = source.InheritedLanguageId; + } + + protected virtual void Map(LanguageSave source, Language target, IZeroMapperContext ctx) + { + this.MapSaveData(source, target); + target.Code = source.Code; + target.IsDefault = source.IsDefault; + target.InheritedLanguageId = source.InheritedLanguageId; + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Languages/Maps/LanguageSave.cs b/zero.Api/Endpoints/Languages/Maps/LanguageSave.cs new file mode 100644 index 00000000..e0417756 --- /dev/null +++ b/zero.Api/Endpoints/Languages/Maps/LanguageSave.cs @@ -0,0 +1,10 @@ +namespace zero.Api.Endpoints.Languages; + +public class LanguageSave : SaveModel +{ + public string Code { get; set; } + + public bool IsDefault { get; set; } + + public string InheritedLanguageId { get; set; } +} \ No newline at end of file diff --git a/zero.Api/Modules/Pages/PageTreeService.cs b/zero.Api/Endpoints/Pages/PageTreeService.cs similarity index 99% rename from zero.Api/Modules/Pages/PageTreeService.cs rename to zero.Api/Endpoints/Pages/PageTreeService.cs index 4ea7cc04..c50c2d67 100644 --- a/zero.Api/Modules/Pages/PageTreeService.cs +++ b/zero.Api/Endpoints/Pages/PageTreeService.cs @@ -2,7 +2,7 @@ using Raven.Client.Documents.Linq; using Raven.Client.Documents.Session; -namespace zero.Api.Modules.Pages; +namespace zero.Api.Endpoints.Pages; public class PageTreeService : IPageTreeService { diff --git a/zero.Api/Modules/Search/zero_Backoffice_Search.cs b/zero.Api/Endpoints/Search/Indexes/zero_Backoffice_Search.cs similarity index 93% rename from zero.Api/Modules/Search/zero_Backoffice_Search.cs rename to zero.Api/Endpoints/Search/Indexes/zero_Backoffice_Search.cs index f9fca171..b5bfbe4f 100644 --- a/zero.Api/Modules/Search/zero_Backoffice_Search.cs +++ b/zero.Api/Endpoints/Search/Indexes/zero_Backoffice_Search.cs @@ -1,6 +1,6 @@ using Raven.Client.Documents; -namespace zero.Api.Modules.Search; +namespace zero.Api.Endpoints.Search; public class zero_Backoffice_Search : ZeroJavascriptIndex { diff --git a/zero.Api/Modules/Search/SearchIndexMap.cs b/zero.Api/Endpoints/Search/Models/SearchIndexMap.cs similarity index 98% rename from zero.Api/Modules/Search/SearchIndexMap.cs rename to zero.Api/Endpoints/Search/Models/SearchIndexMap.cs index 0f5862fb..bc1f9c63 100644 --- a/zero.Api/Modules/Search/SearchIndexMap.cs +++ b/zero.Api/Endpoints/Search/Models/SearchIndexMap.cs @@ -1,6 +1,6 @@ using Raven.Client.Documents; -namespace zero.Api.Modules.Search; +namespace zero.Api.Endpoints.Search; public class SearchIndexMap { diff --git a/zero.Api/Modules/Search/SearchResult.cs b/zero.Api/Endpoints/Search/Models/SearchResult.cs similarity index 93% rename from zero.Api/Modules/Search/SearchResult.cs rename to zero.Api/Endpoints/Search/Models/SearchResult.cs index 8723b796..0536a3c4 100644 --- a/zero.Api/Modules/Search/SearchResult.cs +++ b/zero.Api/Endpoints/Search/Models/SearchResult.cs @@ -1,4 +1,4 @@ -namespace zero.Api.Modules.Search; +namespace zero.Api.Endpoints.Search; public class SearchResult { diff --git a/zero.Api/Endpoints/Search/SearchController.cs b/zero.Api/Endpoints/Search/SearchController.cs new file mode 100644 index 00000000..e57b6d51 --- /dev/null +++ b/zero.Api/Endpoints/Search/SearchController.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Mvc; +using zero.Api.Filters; + +namespace zero.Api.Endpoints.Search; + +public class SearchController : ZeroApiController +{ + protected ISearchService Service { get; set; } + + public SearchController(ISearchService service) + { + Service = service; + } + + + [HttpGet("")] + public async Task> Query([FromQuery] ListQuery query) + { + return await Service.Query(query.Search); + } +} \ No newline at end of file diff --git a/zero.Api/Endpoints/Search/SearchModule.cs b/zero.Api/Endpoints/Search/SearchModule.cs new file mode 100644 index 00000000..cb7d27ce --- /dev/null +++ b/zero.Api/Endpoints/Search/SearchModule.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Api.Endpoints.Search; + +public class SearchModule : ZeroModule +{ + public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(); + services.AddScoped(); + + //services.Configure(opts => + //{ + // opts.Indexes.Add(); + //}); + + services.AddOptions().Bind(configuration.GetSection("Zero:Search")); + } +} \ No newline at end of file diff --git a/zero.Api/Modules/Search/SearchOptions.cs b/zero.Api/Endpoints/Search/SearchOptions.cs similarity index 86% rename from zero.Api/Modules/Search/SearchOptions.cs rename to zero.Api/Endpoints/Search/SearchOptions.cs index 510d7375..7fb7098e 100644 --- a/zero.Api/Modules/Search/SearchOptions.cs +++ b/zero.Api/Endpoints/Search/SearchOptions.cs @@ -1,4 +1,4 @@ -namespace zero.Api.Modules.Search; +namespace zero.Api.Endpoints.Search; public class SearchOptions : List { diff --git a/zero.Api/Endpoints/Search/SearchPermissions.cs b/zero.Api/Endpoints/Search/SearchPermissions.cs new file mode 100644 index 00000000..7808aeab --- /dev/null +++ b/zero.Api/Endpoints/Search/SearchPermissions.cs @@ -0,0 +1,19 @@ +namespace zero.Api.Endpoints.Search; + +public class SearchPermissions : PermissionProvider +{ + public const string Group = "zero.search"; + + public const string Search = "zero.search.use"; + + + public override Task Configure(IPermissionContext context) + { + PermissionGroup group = new(Group, "@search.permissions.search_group"); + group.Permissions.Add(new Permission(Search, "@search.permissions.search")); + + context.AddGroup(group); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/zero.Api/Modules/Search/BackofficeSearchService.cs b/zero.Api/Endpoints/Search/SearchService.cs similarity index 89% rename from zero.Api/Modules/Search/BackofficeSearchService.cs rename to zero.Api/Endpoints/Search/SearchService.cs index 842525e0..ab211af3 100644 --- a/zero.Api/Modules/Search/BackofficeSearchService.cs +++ b/zero.Api/Endpoints/Search/SearchService.cs @@ -2,16 +2,16 @@ using Raven.Client.Documents.Queries; using Raven.Client.Documents.Session; -namespace zero.Api.Modules.Search; +namespace zero.Api.Endpoints.Search; -public class BackofficeSearchService : IBackofficeSearchService +public class SearchService : ISearchService { protected IZeroStore Store { get; private set; } protected IZeroOptions Options { get; private set; } - public BackofficeSearchService(IZeroStore store, IZeroOptions options) + public SearchService(IZeroStore store, IZeroOptions options) { Store = store; Options = options; @@ -67,7 +67,7 @@ public class BackofficeSearchService : IBackofficeSearchService } } -public interface IBackofficeSearchService +public interface ISearchService { Task> Query(string searchTerm); } \ No newline at end of file diff --git a/zero.Api/Filters/ZeroSystemApiAttribute.cs b/zero.Api/Filters/ZeroSystemApiAttribute.cs new file mode 100644 index 00000000..2b8cd7ba --- /dev/null +++ b/zero.Api/Filters/ZeroSystemApiAttribute.cs @@ -0,0 +1,10 @@ +namespace zero.Api.Filters; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] +internal class ZeroSystemApiAttribute : Attribute +{ + public ZeroSystemApiAttribute() : base() + { + + } +} \ No newline at end of file diff --git a/zero.Api/Modules/Countries/CountriesController.cs b/zero.Api/Modules/Countries/CountriesController.cs deleted file mode 100644 index 1ac378e3..00000000 --- a/zero.Api/Modules/Countries/CountriesController.cs +++ /dev/null @@ -1,129 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System.Collections; - -namespace zero.Api.Modules.Countries; - -public class CountriesController : ZeroApiController -{ - protected ICountryStore Store { get; set; } - - public CountriesController(ICountryStore store) - { - Store = store; - } - - - [HttpGet("empty")] - [ZeroAuthorize(CountryPermissions.Create)] - public virtual async Task> Empty() - { - throw new NotImplementedException("TEST ZERO"); - - Country model = await Store.Empty(); - - if (model == null) - { - return NotFound(); - } - - return Mapper.Map(model); - } - - - [HttpGet("{id}")] - [ZeroAuthorize(CountryPermissions.Read)] - public virtual async Task> Get(string id, string changeVector = null) - { - Country model = await Store.Load(id, changeVector); - - if (model == null) - { - return NotFound(); - } - - return Mapper.Map(model); - } - - - [HttpGet("")] - [ZeroAuthorize(CountryPermissions.Read)] - public virtual async Task> Get([FromQuery] ListQuery query) - { - query.OrderQuery = q => q.OrderByDescending(x => x.IsPreferred).ThenBy(x => x.Name); - Paged result = await Store.Load(query.Page, query.PageSize, q => q.Filter(query)); - - return Mapper.Map(result, (src, dest) => - { - dest.Link = Url.Action(nameof(CountriesController.Get), new { id = dest.Id }); - }); - } - - - [HttpPost("")] - [ZeroAuthorize(CountryPermissions.Create)] - public virtual async Task> Create(CountrySave saveModel) - { - Country model = Mapper.Map(saveModel); - Result result = await Store.Create(model); - - if (result.IsSuccess) - { - Result mappedResult = Mapper.Map(result); - return CreatedAtAction(nameof(CountriesController.Get), new { id = model.Id }, mappedResult); - } - - if (Hints.ResponsePreference == ApiResponsePreference.Minimal) - { - return result.WithoutModel(); - } - - return Mapper.Map(result); - } - - - [HttpPut("{id}")] - [ZeroAuthorize(CountryPermissions.Update)] - public virtual async Task> Update(string id, CountrySave updateModel) - { - if (id != updateModel.Id) - { - return BadRequest(BackofficeConstants.HttpErrors.NoIdMatchOnUpdate); - } - - Country model = await Store.Load(id); - - if (model == null) - { - return BadRequest(BackofficeConstants.HttpErrors.IdNotFound); - } - - Mapper.Map(updateModel, model); - - Result result = await Store.Update(model); - - if (Hints.ResponsePreference == ApiResponsePreference.Minimal) - { - return result.WithoutModel(); - } - - // TODO add Preference-Applied header, see https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#76-standard-response-headers - return Mapper.Map(result); - } - - - [HttpDelete("{id}")] - [ZeroAuthorize(CountryPermissions.Delete)] - public virtual async Task> Delete(string id) - { - Country model = await Store.Load(id); - - if (model == null) - { - return NotFound(); - } - - Result result = await Store.Delete(model); - - return result.WithoutModel(); - } -} \ No newline at end of file diff --git a/zero.Api/Modules/Countries/Maps/CountryMapperProfile.cs b/zero.Api/Modules/Countries/Maps/CountryMapperProfile.cs deleted file mode 100644 index b4db0acb..00000000 --- a/zero.Api/Modules/Countries/Maps/CountryMapperProfile.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace zero.Api.Modules.Countries; - -public class CountryMapperProfile : 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); - mapper.Define((source, ctx) => new(), Map); - mapper.Define((source, ctx) => new(), Map); - } - - - void Map(Country source, CountryBasic target, IZeroMapperContext ctx) - { - this.MapBasicData(source, target); - target.Code = source.Code; - target.IsPreferred = source.IsPreferred; - } - - void Map(Country source, CountryEdit target, IZeroMapperContext ctx) - { - this.MapDisplayData(source, target); - target.Code = source.Code; - target.IsPreferred = source.IsPreferred; - } - - void Map(CountrySave source, Country target, IZeroMapperContext ctx) - { - this.MapSaveData(source, target); - target.Code = source.Code; - target.IsPreferred = source.IsPreferred; - } - - void Map(Country source, PickerModel target, IZeroMapperContext ctx) - { - target.Id = source.Id; - target.Name = source.Name; - target.IsActive = source.IsActive; - } - - void Map(Country source, PickerPreviewModel target, IZeroMapperContext ctx) - { - target.Id = source.Id; - target.Name = source.Name; - target.Icon = "flag-" + source.Code.ToLowerInvariant(); - } -} \ No newline at end of file diff --git a/zero.Api/Modules/Countries/ServiceCollectionExtensions.cs b/zero.Api/Modules/Countries/ServiceCollectionExtensions.cs deleted file mode 100644 index a5127e70..00000000 --- a/zero.Api/Modules/Countries/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Api.Modules.Countries; - -public static class ServiceCollectionExtensions -{ - public static IServiceCollection AddZeroBackofficeCountries(this IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); - - services.Configure(opts => - { - opts.Indexes.Add(); - }); - - return services; - } -} \ No newline at end of file diff --git a/zero.Api/Modules/Search/SearchEndpoint.cs b/zero.Api/Modules/Search/SearchEndpoint.cs deleted file mode 100644 index 7cefada4..00000000 --- a/zero.Api/Modules/Search/SearchEndpoint.cs +++ /dev/null @@ -1,16 +0,0 @@ -//using Microsoft.AspNetCore.Mvc; - -//namespace zero.Api.Modules; - -//[ZeroAuthorize] -//public class SearchController : BackofficeController -//{ -// IBackofficeSearchService SearchService; - -// public SearchController(IBackofficeSearchService searchService) -// { -// SearchService = searchService; -// } - -// public async Task> Query([FromQuery] string query) => await SearchService.Query(query); -//} \ No newline at end of file diff --git a/zero.Api/Modules/ServiceCollectionExtensions.cs b/zero.Api/Modules/ServiceCollectionExtensions.cs deleted file mode 100644 index 8f9afd4f..00000000 --- a/zero.Api/Modules/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using zero.Api.Modules.Countries; -using zero.Api.Modules.Pages; -using zero.Api.Modules.Search; - -namespace zero.Api.Modules; - -public static class ServiceCollectionExtensions -{ - public static IServiceCollection AddZeroBackofficeModules(this IServiceCollection services, IConfiguration config) - { - services.AddZeroBackofficeCountries(); - - services.AddScoped(); - services.AddScoped(); - - //services.Configure(opts => - //{ - // opts.Indexes.Add(); - //}); - - return services; - } -} \ No newline at end of file diff --git a/zero.Api/Plugin.cs b/zero.Api/Plugin.cs index 283c2a35..fa80efab 100644 --- a/zero.Api/Plugin.cs +++ b/zero.Api/Plugin.cs @@ -5,6 +5,9 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using zero.Api.Endpoints.Countries; +using zero.Api.Endpoints.Languages; +using zero.Api.Endpoints.Search; namespace zero.Api; @@ -23,21 +26,13 @@ public class ZeroApiPlugin : ZeroPlugin { services.AddOptions().Bind(configuration.GetSection("Zero:Api")).Configure(ConfigureOptions); services.TryAddEnumerable(ServiceDescriptor.Transient, ZeroApiMvcOptions>()); - //Mvc.AddNewtonsoftJson(x => - //{ - // // TODO this shall only be configurated for backoffice controllers - // BackofficeJsonSerlializerSettings.Setup(x.SerializerSettings); - //}); services.AddScoped(); - services.AddZeroBackofficeModules(configuration); - - //services.AddTransient(); - //services.AddTransient(); - - //services.AddScoped(); - + 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 9e35c368..10963cc8 100644 --- a/zero.Api/Usings.cs +++ b/zero.Api/Usings.cs @@ -26,6 +26,7 @@ global using zero.Utils; global using zero.Api.Configuration; global using zero.Api.Controllers; global using zero.Api.Models; -global using zero.Api.Modules; +global using zero.Api.Endpoints; global using zero.Api.Abstractions; -global using zero.Api.Extensions; \ No newline at end of file +global using zero.Api.Extensions; +global using zero.Api.Filters; \ No newline at end of file diff --git a/zero.Api/ZeroApiControllerModelConvention.cs b/zero.Api/ZeroApiControllerModelConvention.cs index 8c917355..5d298033 100644 --- a/zero.Api/ZeroApiControllerModelConvention.cs +++ b/zero.Api/ZeroApiControllerModelConvention.cs @@ -1,30 +1,26 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; +using System.Reflection; using System.Text; namespace zero.Api.Controllers; public class ZeroApiControllerModelConvention : IControllerModelConvention { - readonly AttributeRouteModel RouteModel; + readonly AttributeRouteModel AppAwareRouteModel; + + readonly AttributeRouteModel AppUnawareRouteModel; readonly Type BaseClass = typeof(ZeroApiController); + readonly bool RuntimeIsAppAware = false; + public ZeroApiControllerModelConvention(string zeroPath, string apiPath = "api", bool isAppAware = false) { - StringBuilder path = new(); - path.Append(zeroPath.EnsureEndsWith('/')); - path.Append(apiPath.TrimStart('/').EnsureEndsWith('/')); - - if (isAppAware) - { - path.Append("{zero_app_slug}/"); - } - - path.Append("[controller]"); - - RouteModel = new AttributeRouteModel(new RouteAttribute(path.ToString())); + RuntimeIsAppAware = isAppAware; + AppAwareRouteModel = BuildRouteModel(zeroPath, apiPath, true); + AppUnawareRouteModel = BuildRouteModel(zeroPath, apiPath, false); } @@ -37,7 +33,26 @@ public class ZeroApiControllerModelConvention : IControllerModelConvention if (controller.ControllerType.IsSubclassOf(BaseClass)) { - controller.Selectors[0].AttributeRouteModel = RouteModel; + bool isAppAware = RuntimeIsAppAware && controller.ControllerType.GetCustomAttribute() == null; + + controller.Selectors[0].AttributeRouteModel = isAppAware ? AppAwareRouteModel : AppUnawareRouteModel; } } + + + protected AttributeRouteModel BuildRouteModel(string zeroPath, string apiPath, bool appAware = false) + { + StringBuilder path = new(); + path.Append(zeroPath.EnsureEndsWith('/')); + path.Append(apiPath.TrimStart('/').EnsureEndsWith('/')); + + if (appAware) + { + path.Append("{zero_app_slug}/"); + } + + path.Append("[controller]"); + + return new AttributeRouteModel(new RouteAttribute(path.ToString())); + } } \ No newline at end of file diff --git a/zero.Api/ZeroApiEntityStoreController.cs b/zero.Api/ZeroApiEntityStoreController.cs new file mode 100644 index 00000000..c25266ed --- /dev/null +++ b/zero.Api/ZeroApiEntityStoreController.cs @@ -0,0 +1,135 @@ +using Microsoft.AspNetCore.Mvc; +using Raven.Client.Documents.Indexes; + +namespace zero.Api.Controllers; + +public abstract class ZeroApiEntityStoreController : ZeroApiController where TModel : ZeroEntity, new() where TStore : IEntityStore +{ + protected TStore Store { get; set; } + + public ZeroApiEntityStoreController(TStore store) + { + Store = store; + } + + + protected string GetActionMethod { get; set; } = "Get"; + + protected virtual string GetAction(TModel model) + { + return Url.Action(GetActionMethod, new { id = model.Id }); + } + + + protected async Task> EmptyModel() where T : DisplayModel + { + TModel model = await Store.Empty(); + + if (model == null) + { + return NotFound(); + } + + return Mapper.Map(model); + } + + + protected async Task> GetModel(string id, string changeVector = null) where T : DisplayModel + { + TModel model = await Store.Load(id, changeVector); + + if (model == null) + { + return NotFound(); + } + + return Mapper.Map(model); + } + + + protected async Task> GetModels(ListQuery query) where T : BasicModel + { + query.OrderQuery ??= q => q.OrderByDescending(x => x.CreatedDate); + Paged result = await Store.Load(query.Page, query.PageSize, q => q.Filter(query)); + + return Mapper.Map(result, (src, dest) => + { + dest.Link = GetAction(src); + }); + } + + + protected async Task> GetModels(ListQuery query) where T : BasicModel where TIndex : AbstractCommonApiForIndexes, new() + { + query.OrderQuery ??= q => q.OrderByDescending(x => x.CreatedDate); + Paged result = await Store.Load(query.Page, query.PageSize, q => q.Filter(query)); + + return Mapper.Map(result, (src, dest) => + { + dest.Link = GetAction(src); + }); + } + + + protected async Task> CreateModel(T saveModel) where T : SaveModel where TEdit : DisplayModel + { + TModel model = Mapper.Map(saveModel); + Result result = await Store.Create(model); + + if (result.IsSuccess) + { + Result mappedResult = Mapper.Map(result); + return Created(GetAction(model), mappedResult); + } + + if (Hints.ResponsePreference == ApiResponsePreference.Minimal) + { + return result.WithoutModel(); + } + + return Mapper.Map(result); + } + + + protected async Task> UpdateModel(string id, T updateModel) where T : SaveModel where TEdit : DisplayModel + { + if (id != updateModel.Id) + { + return BadRequest(BackofficeConstants.HttpErrors.NoIdMatchOnUpdate); + } + + TModel model = await Store.Load(id); + + if (model == null) + { + return BadRequest(BackofficeConstants.HttpErrors.IdNotFound); + } + + Mapper.Map(updateModel, model); + + Result result = await Store.Update(model); + + if (Hints.ResponsePreference == ApiResponsePreference.Minimal) + { + return result.WithoutModel(); + } + + // TODO add Preference-Applied header, see https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#76-standard-response-headers + return Mapper.Map(result); + } + + + protected async Task> DeleteModel(string id) + { + TModel model = await Store.Load(id); + + if (model == null) + { + return NotFound(); + } + + Result result = await Store.Delete(model); + + return result.WithoutModel(); + } +} diff --git a/zero.Backoffice/Modules_legacy/Applications/ApplicationEditModel.cs b/zero.Backoffice/Modules_legacy/Applications/ApplicationEditModel.cs deleted file mode 100644 index cc8d6829..00000000 --- a/zero.Backoffice/Modules_legacy/Applications/ApplicationEditModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using zero.Core.Entities; - -namespace zero.Web.Models -{ - public class ApplicationEditModel : ObsoleteEditModel - { - public string Name { get; set; } - - public string ImageId { get; set; } - - public string IconId { get; set; } - - public string[] Domains { get; set; } = new string[] { }; - - public string FullName { get; set; } - - public string Email { get; set; } - - public List Features { get; set; } = new List(); - } -} diff --git a/zero.Backoffice/Modules_legacy/Applications/ApplicationListModel.cs b/zero.Backoffice/Modules_legacy/Applications/ApplicationListModel.cs deleted file mode 100644 index e681f72a..00000000 --- a/zero.Backoffice/Modules_legacy/Applications/ApplicationListModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace zero.Web.Models -{ - public class ApplicationListModel : ListModel - { - public string Name { get; set; } - - public string FullName { get; set; } - - public string ImageId { get; set; } - - public string[] Domains { get; set; } - - public bool IsActive { get; set; } - } -} diff --git a/zero.Backoffice/Modules_legacy/Applications/ApplicationsController.cs b/zero.Backoffice/Modules_legacy/Applications/ApplicationsController.cs deleted file mode 100644 index 64a3e2a5..00000000 --- a/zero.Backoffice/Modules_legacy/Applications/ApplicationsController.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -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(Permissions.Settings.Applications, PermissionsValue.Read)] - public class ApplicationsController : BackofficeController - { - IApplicationsApi Api; - - public ApplicationsController(IApplicationsApi api) - { - Api = api; - IsCoreDatabase = true; - } - - - public EditModel GetEmpty([FromServices] Application blueprint) => Edit(blueprint); - - - public async Task> GetById([FromQuery] string id) => Edit(await Api.GetById(id)); - - - public async Task> GetAll() => await Api.GetAll(); - - - public async Task> GetByQuery([FromQuery] ListQuery query) => await Api.GetByQuery(query); - - - public IReadOnlyCollection GetAllFeatures() => Options.Features.GetAllItems(); - - - [HttpPost] - [ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Update)] - public async Task> Save([FromBody] Application model) => await Api.Save(model); - - - [HttpDelete] - [ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Update)] - public async Task> Delete([FromQuery] string id) => await Api.Delete(id); - } -} diff --git a/zero.Backoffice/Modules_legacy/Languages/LanguageEditModel.cs b/zero.Backoffice/Modules_legacy/Languages/LanguageEditModel.cs deleted file mode 100644 index c5fc0d92..00000000 --- a/zero.Backoffice/Modules_legacy/Languages/LanguageEditModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace zero.Web.Models -{ - public class LanguageEditModel : ObsoleteEditModel - { - public string Name { get; set; } - - public string Code { get; set; } - - public bool IsDefault { get; set; } - - public string InheritedLanguageId { get; set; } - } -} diff --git a/zero.Backoffice/Modules_legacy/Languages/LanguageListModel.cs b/zero.Backoffice/Modules_legacy/Languages/LanguageListModel.cs deleted file mode 100644 index 0b44e928..00000000 --- a/zero.Backoffice/Modules_legacy/Languages/LanguageListModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace zero.Web.Models -{ - public class LanguageListModel : ListModel - { - public string Name { get; set; } - - public string Code { get; set; } - - public bool IsDefault { get; set; } - - public bool IsActive { get; set; } - - public string InheritedLanguageId { get; set; } - } -} diff --git a/zero.Backoffice/Modules_legacy/Languages/LanguagesController.cs b/zero.Backoffice/Modules_legacy/Languages/LanguagesController.cs deleted file mode 100644 index c6558ac1..00000000 --- a/zero.Backoffice/Modules_legacy/Languages/LanguagesController.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Raven.Client.Documents.Linq; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using zero.Core.Collections; -using zero.Core.Database.Indexes; -using zero.Core.Entities; -using zero.Core.Identity; - -namespace zero.Web.Controllers -{ - [ZeroAuthorize(Permissions.Settings.Languages, PermissionsValue.Read)] - public class LanguagesController : ZeroBackofficeCollectionController - { - public LanguagesController(ILanguagesStore collection) : base(collection) - { - PreviewTransform = (item, model) => model.Icon = "fth-globe"; - } - - - public List GetAllCultures() - { - return Collection.GetAllCultures(); - } - - - public List GetSupportedCultures() - { - return Collection.GetAllCultures(Options.SupportedLanguages); - } - - - public override async Task> GetByQuery([FromQuery] ListQuery query) - { - query.OrderQuery = q => q.OrderByDescending(x => x.CreatedDate); - return await Collection.Load(query); - } - } -} diff --git a/zero.Backoffice/Modules_legacy/Languages/zero_Languages.cs b/zero.Backoffice/Modules_legacy/Languages/zero_Languages.cs deleted file mode 100644 index 935558a1..00000000 --- a/zero.Backoffice/Modules_legacy/Languages/zero_Languages.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Raven.Client.Documents.Indexes; -using System.Linq; -using zero.Core.Entities; - -namespace zero.Core.Database.Indexes -{ - public class zero_Languages : ZeroIndex - { - protected override void Create() - { - Map = items => items.Select(item => new - { - Name = item.Name, - CreatedDate = item.CreatedDate, - IsDefault = item.IsDefault - }); - - Index(x => x.Name, FieldIndexing.Search); - } - } -} diff --git a/zero.Backoffice/Plugin.cs b/zero.Backoffice/Plugin.cs index 0680e9e1..32219644 100644 --- a/zero.Backoffice/Plugin.cs +++ b/zero.Backoffice/Plugin.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System.IO; @@ -23,12 +24,6 @@ public class ZeroBackofficePlugin : ZeroPlugin services.AddHostedService(); services.AddTransient(); - //Mvc.AddNewtonsoftJson(x => - //{ - // // TODO this shall only be configurated for backoffice controllers - // BackofficeJsonSerlializerSettings.Setup(x.SerializerSettings); - //}); - services.AddZeroBackofficeUIComposition(); //services.AddTransient(); diff --git a/zero.Backoffice/Resources/Localization/zero.en-us.json b/zero.Backoffice/Resources/Localization/zero.en-us.json index 455bfa76..fd64b3fc 100644 --- a/zero.Backoffice/Resources/Localization/zero.en-us.json +++ b/zero.Backoffice/Resources/Localization/zero.en-us.json @@ -700,6 +700,10 @@ }, "search": { + "permissions": { + "search_group": "Search", + "search": "Use search" + }, "collection": { "page": "Page", "mediaFolder": "Media folder" diff --git a/zero.Core/Applications/ApplicationResolver.cs b/zero.Core/Applications/ApplicationResolver.cs index f04a4c60..5355a9a3 100644 --- a/zero.Core/Applications/ApplicationResolver.cs +++ b/zero.Core/Applications/ApplicationResolver.cs @@ -109,6 +109,11 @@ public class ApplicationResolver : IApplicationResolver /// public async Task ResolveFromRequest(HttpContext context) { + if (!Options.For().EnableMultiple) + { + return (await GetApplications()).FirstOrDefault(); + } + IApplicationResolverHandler handler = Handler.Get(); Application app = handler?.Resolve(context.Request, await GetApplications()); diff --git a/zero.Core/Architecture/ZeroModuleCollection.cs b/zero.Core/Architecture/ZeroModuleCollection.cs index 50997efe..77169d6b 100644 --- a/zero.Core/Architecture/ZeroModuleCollection.cs +++ b/zero.Core/Architecture/ZeroModuleCollection.cs @@ -4,7 +4,7 @@ using System.Collections.Concurrent; namespace zero.Architecture; -internal class ZeroModuleCollection +public class ZeroModuleCollection { static ConcurrentDictionary _modules = new(); diff --git a/zero.Core/Mapper/ZeroMapper.cs b/zero.Core/Mapper/ZeroMapper.cs index 214950d5..51098e64 100644 --- a/zero.Core/Mapper/ZeroMapper.cs +++ b/zero.Core/Mapper/ZeroMapper.cs @@ -38,6 +38,27 @@ public class ZeroMapper : IZeroMapper } + /// + public void Override(Func ctor, Action> map) + { + Type sourceType = typeof(TSource); + Type destinationType = typeof(TDestination); + + var sourceMaps = MapDefinitions.GetValueOrDefault(sourceType); + var sourceCtors = ConstructorDefinitions.GetValueOrDefault(sourceType); + + if (sourceMaps == null || sourceCtors == null || !sourceMaps.ContainsKey(destinationType)) + { + return; + } + + var baseMap = sourceMaps[destinationType]; + + sourceCtors[destinationType] = (source, ctx) => ctor((TSource)source, ctx); + sourceMaps[destinationType] = (source, destination, ctx) => map((TSource)source, (TDestination)destination, ctx, (source, destination, ctx) => baseMap(source, destination, ctx)); + } + + /// public TDestination Map(object source, Type sourceType, TDestination destination = default) { @@ -159,6 +180,9 @@ public interface IZeroMapper /// void Define(Func ctor, Action map); + /// + void Override(Func ctor, Action> map); + /// /// Map a source type to the destination type /// diff --git a/zero.Core/ZeroApplicationBuilder.cs b/zero.Core/ZeroApplicationBuilder.cs index 67b4b8ed..087c2a0f 100644 --- a/zero.Core/ZeroApplicationBuilder.cs +++ b/zero.Core/ZeroApplicationBuilder.cs @@ -13,9 +13,9 @@ public class ZeroApplicationBuilder : IZeroApplicationBuilder { App = app; App.UseStaticFiles(); - App.UseExceptionHandler("/zero/api/error"); - App.UseRouting(); + //App.UseExceptionHandler("/zero/api/error"); App.UseMiddleware(); + App.UseRouting(); App.UseAuthentication(); App.UseAuthorization(); diff --git a/zero.Core/ZeroBuilder.cs b/zero.Core/ZeroBuilder.cs index 944312ab..4b3432cb 100644 --- a/zero.Core/ZeroBuilder.cs +++ b/zero.Core/ZeroBuilder.cs @@ -2,6 +2,9 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; namespace zero; @@ -23,6 +26,13 @@ public class ZeroBuilder Mvc = services.AddMvc(); Configuration = configuration; + //Mvc.AddNewtonsoftJson(x => + //{ + // x.SerializerSettings.Converters.Add(new StringEnumConverter(new CamelCaseNamingStrategy())); + // x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + // x.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects; + //}); + // create startup options StartupOptions = new ZeroStartupOptions(Mvc); StartupOptions.AssemblyDiscoveryRules.Add(new ZeroAssemblyDiscoveryRule()); diff --git a/zero.Core/zero.Core.csproj b/zero.Core/zero.Core.csproj index 77ae83ea..beca188c 100644 --- a/zero.Core/zero.Core.csproj +++ b/zero.Core/zero.Core.csproj @@ -21,6 +21,7 @@ +