diff --git a/zero.Api/ApiParameterTransformer.cs b/zero.Api/ApiParameterTransformer.cs
new file mode 100644
index 00000000..16fd80e9
--- /dev/null
+++ b/zero.Api/ApiParameterTransformer.cs
@@ -0,0 +1,11 @@
+using Microsoft.AspNetCore.Routing;
+
+namespace zero.Api;
+
+public class ApiParameterTransformer : IOutboundParameterTransformer
+{
+ public string TransformOutbound(object value)
+ {
+ return value == null ? null : Safenames.Alias(value);
+ }
+}
\ No newline at end of file
diff --git a/zero.Api/Filters/ActionFilters/ApiMetadataFilter.cs b/zero.Api/Filters/ActionFilters/ApiMetadataFilter.cs
new file mode 100644
index 00000000..ad3981ac
--- /dev/null
+++ b/zero.Api/Filters/ActionFilters/ApiMetadataFilter.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Mvc.Filters;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace zero.Api.Filters;
+
+public class ApiMetadataFilterAttribute : ActionFilterAttribute
+{
+ public override void OnActionExecuting(ActionExecutingContext context)
+ {
+ context.HttpContext.Items["zero.action.started"] = DateTimeOffset.Now;
+ }
+}
\ No newline at end of file
diff --git a/zero.Api/Filters/ResultFilters/ApiResponseFilter.cs b/zero.Api/Filters/ResultFilters/ApiResponseFilter.cs
new file mode 100644
index 00000000..0133df40
--- /dev/null
+++ b/zero.Api/Filters/ResultFilters/ApiResponseFilter.cs
@@ -0,0 +1,56 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace zero.Api.Filters;
+
+public class ApiResponseFilterAttribute : ResultFilterAttribute
+{
+ public override void OnResultExecuting(ResultExecutingContext context)
+ {
+ DateTimeOffset started = (DateTimeOffset)context.HttpContext.Items["zero.action.started"];
+ TimeSpan duration = DateTimeOffset.Now - started;
+
+ if (context.Result is ObjectResult result && result.StatusCode.HasValue)
+ {
+ if (typeof(ZeroIdEntity).IsAssignableFrom(result.DeclaredType))
+ {
+ result.Value = new ModelApiResponse()
+ {
+ Model = result.Value,
+ Success = true,
+ Status = result.StatusCode.Value,
+ Metadata = new ModelApiResponseMetadata()
+ {
+ RequestDate = started,
+ Duration = duration,
+ Token = IdGenerator.Create(32)
+ }
+ };
+ }
+ else if (typeof(Paged).IsAssignableFrom(result.DeclaredType))
+ {
+ Paged paged = result.Value as Paged;
+ result.Value = new PagedApiResponse()
+ {
+ Items = paged.GetItems(),
+ Paging = new()
+ {
+ Page = paged.Page,
+ PageSize = paged.PageSize,
+ TotalItems = paged.TotalItems,
+ TotalPages = paged.TotalPages,
+ HasMore = paged.HasMore
+ },
+ Success = true,
+ Status = result.StatusCode.Value,
+ Metadata = new ModelApiResponseMetadata()
+ {
+ RequestDate = started,
+ Duration = duration,
+ Token = IdGenerator.Create(32)
+ }
+ };
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/zero.Api/Models/ApiResponse.cs b/zero.Api/Models/ApiResponse.cs
index e0017c39..fb8a23c3 100644
--- a/zero.Api/Models/ApiResponse.cs
+++ b/zero.Api/Models/ApiResponse.cs
@@ -2,7 +2,12 @@
{
public class ApiResponse
{
- [JsonPropertyName("@metadata")]
+ [JsonPropertyOrder(-2)]
+ public bool Success { get; set; }
+
+ [JsonPropertyOrder(-1)]
+ public int Status { get; set; }
+
public ApiResponseMetadata Metadata { get; set; } = new();
}
}
diff --git a/zero.Api/Models/ApiResponseMetadata.cs b/zero.Api/Models/ApiResponseMetadata.cs
index 25c6cabf..492f79a8 100644
--- a/zero.Api/Models/ApiResponseMetadata.cs
+++ b/zero.Api/Models/ApiResponseMetadata.cs
@@ -3,4 +3,6 @@
public class ApiResponseMetadata
{
public DateTimeOffset RequestDate { get; set; }
+
+ public TimeSpan Duration { get; set; }
}
\ No newline at end of file
diff --git a/zero.Api/Models/ErrorApiResponse.cs b/zero.Api/Models/ErrorApiResponse.cs
new file mode 100644
index 00000000..f6558850
--- /dev/null
+++ b/zero.Api/Models/ErrorApiResponse.cs
@@ -0,0 +1,15 @@
+namespace zero.Api.Models;
+
+public class ErrorApiResponse : ApiResponse
+{
+ public ErrorApiResponseError Error { get; set; }
+}
+
+public class ErrorApiResponseError
+{
+ public string Code { get; set; }
+
+ public string Message { get; set; }
+
+ public string ApiPath { get; set; }
+}
\ No newline at end of file
diff --git a/zero.Api/Models/ModelApiResponse.cs b/zero.Api/Models/ModelApiResponse.cs
new file mode 100644
index 00000000..a24ff714
--- /dev/null
+++ b/zero.Api/Models/ModelApiResponse.cs
@@ -0,0 +1,6 @@
+namespace zero.Api.Models;
+
+public class ModelApiResponse : ApiResponse
+{
+ public object Model { get; set; }
+}
diff --git a/zero.Api/Models/ModelApiResponseMetadata.cs b/zero.Api/Models/ModelApiResponseMetadata.cs
new file mode 100644
index 00000000..d575dc4e
--- /dev/null
+++ b/zero.Api/Models/ModelApiResponseMetadata.cs
@@ -0,0 +1,44 @@
+namespace zero.Api.Models;
+
+public class ModelApiResponseMetadata : ApiResponseMetadata
+{
+ ///
+ /// Wehther this entity is application aware
+ ///
+ public bool IsAppAware { get; set; }
+
+ ///
+ /// Whether this entity can be shared across applications (only for IsAppAware=true)
+ ///
+ public bool CanBeShared { get; set; }
+
+ public bool IsShared { get; set; }
+
+ ///
+ /// The change token maps to a database entity which holds ID and collection of the model to edit
+ /// If these values do not match the entity on save it is rejected
+ /// // TODO expiration expiry session.Advanced.GetMetadataFor(user)[Raven.Client.Constants.Documents.Metadata.Expires] = DateTime.UtcNow.AddMinutes(60);
+ ///
+ public string Token { get; set; }
+
+
+ ///
+ /// Whether an entity of this type can be created
+ ///
+ public bool CanCreate { get; set; }
+
+ ///
+ /// Whether an entity of this type can be created in the shared app space
+ ///
+ public bool CanCreateShared { get; set; }
+
+ ///
+ /// Whether this entity can be edited or only viewed
+ ///
+ public bool CanEdit { get; set; }
+
+ ///
+ /// Whether this entity can be deleted
+ ///
+ public bool CanDelete { get; set; }
+}
diff --git a/zero.Api/Models/PagedApiResponse.cs b/zero.Api/Models/PagedApiResponse.cs
new file mode 100644
index 00000000..41d1294b
--- /dev/null
+++ b/zero.Api/Models/PagedApiResponse.cs
@@ -0,0 +1,21 @@
+namespace zero.Api.Models;
+
+public class PagedApiResponse : ApiResponse
+{
+ public object Items { get; set; }
+ public PagedApiResponsePaging Paging { get; set; }
+}
+
+
+public class PagedApiResponsePaging
+{
+ public long Page { get; set; }
+
+ public long PageSize { get; set; }
+
+ public long TotalPages { get; set; }
+
+ public long TotalItems { get; set; }
+
+ public bool HasMore { get; set; }
+}
\ No newline at end of file
diff --git a/zero.Api/Models/_new/BasicModel.cs b/zero.Api/Models/_new/BasicModel.cs
index cc9e6c6e..72d943f8 100644
--- a/zero.Api/Models/_new/BasicModel.cs
+++ b/zero.Api/Models/_new/BasicModel.cs
@@ -26,4 +26,10 @@ public abstract class BasicModel : ZeroIdEntity where T : ZeroEntity
/// Date of creation
///
public DateTimeOffset CreatedDate { get; set; }
+
+ ///
+ /// API link for this entity
+ ///
+ [JsonPropertyName("@link")]
+ public string Link { get; set; }
}
\ No newline at end of file
diff --git a/zero.Api/Models/_new/DisplayModel.cs b/zero.Api/Models/_new/DisplayModel.cs
index 971b9e3f..f362cf16 100644
--- a/zero.Api/Models/_new/DisplayModel.cs
+++ b/zero.Api/Models/_new/DisplayModel.cs
@@ -1,20 +1,7 @@
namespace zero.Api.Models;
-public abstract class DisplayModel : ZeroIdEntity
-{
- ///
- /// Meta data
- ///
- public DisplayModelConfiguration Configuration { get; set; } = new();
-
- ///
- /// Permissions for this entity
- ///
- public DisplayModelPermissions Permissions { get; set; } = new();
-}
-
-public abstract class DisplayModel : DisplayModel where T : ZeroEntity
+public abstract class DisplayModel : ZeroIdEntity where T : ZeroEntity
{
///
/// Full name of the entity
@@ -70,51 +57,4 @@ public abstract class DisplayModel : DisplayModel where T : ZeroEntity
/// Language of the entity
///
public string LanguageId { get; set; }
-}
-
-
-public class DisplayModelConfiguration
-{
- ///
- /// Wehther this entity is application aware
- ///
- public bool IsAppAware { get; set; }
-
- ///
- /// Whether this entity can be shared across applications (only for IsAppAware=true)
- ///
- public bool CanBeShared { get; set; }
-
- public bool IsShared { get; set; }
-
- ///
- /// The change token maps to a database entity which holds ID and collection of the model to edit
- /// If these values do not match the entity on save it is rejected
- /// // TODO expiration expiry session.Advanced.GetMetadataFor(user)[Raven.Client.Constants.Documents.Metadata.Expires] = DateTime.UtcNow.AddMinutes(60);
- ///
- public string Token { get; set; }
-}
-
-
-public class DisplayModelPermissions
-{
- ///
- /// Whether an entity of this type can be created
- ///
- public bool CanCreate { get; set; }
-
- ///
- /// Whether an entity of this type can be created in the shared app space
- ///
- public bool CanCreateShared { get; set; }
-
- ///
- /// Whether this entity can be edited or only viewed
- ///
- public bool CanEdit { get; set; }
-
- ///
- /// Whether this entity can be deleted
- ///
- public bool CanDelete { get; set; }
}
\ No newline at end of file
diff --git a/zero.Api/Modules/Countries/CountriesController.cs b/zero.Api/Modules/Countries/CountriesController.cs
index cb4ee4c0..1ac378e3 100644
--- a/zero.Api/Modules/Countries/CountriesController.cs
+++ b/zero.Api/Modules/Countries/CountriesController.cs
@@ -7,19 +7,15 @@ public class CountriesController : ZeroApiController
{
protected ICountryStore Store { get; set; }
- protected IPickerProvider Picker { get; set; }
-
-
- public CountriesController(ICountryStore store, IPickerProvider picker)
+ public CountriesController(ICountryStore store)
{
Store = store;
- Picker = picker;
}
[HttpGet("empty")]
[ZeroAuthorize(CountryPermissions.Create)]
- public virtual async Task> Empty()
+ public virtual async Task> Empty()
{
throw new NotImplementedException("TEST ZERO");
@@ -30,32 +26,13 @@ public class CountriesController : ZeroApiController
return NotFound();
}
- return Mapper.Map(model);
- }
-
-
- [HttpGet("pick")]
- [ZeroAuthorize(CountryPermissions.Read)]
- public virtual async Task> Pick([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);
- }
-
-
- [HttpGet("pickpreview")]
- [ZeroAuthorize(CountryPermissions.Read)]
- public virtual async Task> Pick([FromQuery] string[] ids)
- {
- Dictionary model = await Store.Load(ids);
- return Mapper.Map(model);
+ return Mapper.Map(model);
}
[HttpGet("{id}")]
[ZeroAuthorize(CountryPermissions.Read)]
- public virtual async Task> Get(string id, string changeVector = null)
+ public virtual async Task> Get(string id, string changeVector = null)
{
Country model = await Store.Load(id, changeVector);
@@ -64,7 +41,7 @@ public class CountriesController : ZeroApiController
return NotFound();
}
- return Mapper.Map(model);
+ return Mapper.Map(model);
}
@@ -74,7 +51,11 @@ public class CountriesController : ZeroApiController
{
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);
+
+ return Mapper.Map(result, (src, dest) =>
+ {
+ dest.Link = Url.Action(nameof(CountriesController.Get), new { id = dest.Id });
+ });
}
@@ -87,7 +68,7 @@ public class CountriesController : ZeroApiController
if (result.IsSuccess)
{
- Result mappedResult = Mapper.Map(result);
+ Result mappedResult = Mapper.Map(result);
return CreatedAtAction(nameof(CountriesController.Get), new { id = model.Id }, mappedResult);
}
@@ -96,7 +77,7 @@ public class CountriesController : ZeroApiController
return result.WithoutModel();
}
- return Mapper.Map(result);
+ return Mapper.Map(result);
}
@@ -126,7 +107,7 @@ public class CountriesController : ZeroApiController
}
// TODO add Preference-Applied header, see https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#76-standard-response-headers
- return Mapper.Map(result);
+ return Mapper.Map(result);
}
diff --git a/zero.Api/Modules/Countries/CountryPickerProvider.cs b/zero.Api/Modules/Countries/CountryPickerProvider.cs
deleted file mode 100644
index 962ad412..00000000
--- a/zero.Api/Modules/Countries/CountryPickerProvider.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace zero.Api.Modules.Countries;
-
-public class CountryPickerProvider : PickerProvider
-{
- public CountryPickerProvider(IStoreOperations ops) : base(ops) { }
-
-
- ///
- protected override PickerPreviewModel ConvertToPreview(Country source)
- {
- return new()
- {
- Id = source.Id,
- Name = source.Name,
- Icon = "flag-" + source.Code.ToLowerInvariant()
- };
- }
-}
\ No newline at end of file
diff --git a/zero.Api/Modules/Countries/Maps/CountryDisplay.cs b/zero.Api/Modules/Countries/Maps/CountryEdit.cs
similarity index 69%
rename from zero.Api/Modules/Countries/Maps/CountryDisplay.cs
rename to zero.Api/Modules/Countries/Maps/CountryEdit.cs
index 449419ea..d815de5d 100644
--- a/zero.Api/Modules/Countries/Maps/CountryDisplay.cs
+++ b/zero.Api/Modules/Countries/Maps/CountryEdit.cs
@@ -1,6 +1,6 @@
namespace zero.Api.Modules.Countries;
-public class CountryDisplay : DisplayModel
+public class CountryEdit : DisplayModel
{
public bool IsPreferred { get; set; }
diff --git a/zero.Api/Modules/Countries/Maps/CountryMapperProfile.cs b/zero.Api/Modules/Countries/Maps/CountryMapperProfile.cs
index c6c94902..b4db0acb 100644
--- a/zero.Api/Modules/Countries/Maps/CountryMapperProfile.cs
+++ b/zero.Api/Modules/Countries/Maps/CountryMapperProfile.cs
@@ -5,7 +5,7 @@ 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);
mapper.Define((source, ctx) => new(), Map);
@@ -19,7 +19,7 @@ public class CountryMapperProfile : ZeroMapperProfile
target.IsPreferred = source.IsPreferred;
}
- void Map(Country source, CountryDisplay target, IZeroMapperContext ctx)
+ void Map(Country source, CountryEdit target, IZeroMapperContext ctx)
{
this.MapDisplayData(source, target);
target.Code = source.Code;
diff --git a/zero.Api/Modules/Countries/ServiceCollectionExtensions.cs b/zero.Api/Modules/Countries/ServiceCollectionExtensions.cs
index 450ebe34..a5127e70 100644
--- a/zero.Api/Modules/Countries/ServiceCollectionExtensions.cs
+++ b/zero.Api/Modules/Countries/ServiceCollectionExtensions.cs
@@ -6,7 +6,6 @@ public static class ServiceCollectionExtensions
{
public static IServiceCollection AddZeroBackofficeCountries(this IServiceCollection services)
{
- services.AddScoped, CountryPickerProvider>();
services.AddSingleton();
services.AddSingleton();
diff --git a/zero.Api/Modules/Error/ErrorController.cs b/zero.Api/Modules/Error/ErrorController.cs
new file mode 100644
index 00000000..1fa27d8f
--- /dev/null
+++ b/zero.Api/Modules/Error/ErrorController.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Mvc;
+
+namespace zero.Api.Modules.Error;
+
+public class ErrorController : ZeroApiController
+{
+ [HttpGet("")]
+ public virtual ActionResult Index()
+ {
+ IExceptionHandlerFeature exception = HttpContext.Features.Get();
+
+ return new ErrorApiResponse()
+ {
+ Success = false,
+ Status = HttpContext.Response.StatusCode,
+ Error = new()
+ {
+ ApiPath = exception.Path,
+ Message = exception.Error.Message
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/zero.Api/Modules/Error/ServiceCollectionExtensions.cs b/zero.Api/Modules/Error/ServiceCollectionExtensions.cs
new file mode 100644
index 00000000..065d4cec
--- /dev/null
+++ b/zero.Api/Modules/Error/ServiceCollectionExtensions.cs
@@ -0,0 +1,11 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace zero.Api.Modules.Error;
+
+public static class ServiceCollectionExtensions
+{
+ public static IServiceCollection AddZeroBackofficeErrorHandler(this IServiceCollection services)
+ {
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/zero.Api/Plugin.cs b/zero.Api/Plugin.cs
index fea541a5..1704916b 100644
--- a/zero.Api/Plugin.cs
+++ b/zero.Api/Plugin.cs
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -33,6 +34,11 @@ public class ZeroApiPlugin : ZeroPlugin
services.AddZeroBackofficeModules(configuration);
+ services.AddControllers(opts =>
+ {
+ opts.Conventions.Add(new RouteTokenTransformerConvention(new ApiParameterTransformer()));
+ });
+
//services.AddTransient();
//services.AddTransient();
diff --git a/zero.Api/ZeroApiController.cs b/zero.Api/ZeroApiController.cs
index 2c73fa7c..c3a0e44f 100644
--- a/zero.Api/ZeroApiController.cs
+++ b/zero.Api/ZeroApiController.cs
@@ -1,11 +1,14 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
+using zero.Api.Filters;
namespace zero.Api.Controllers;
[ApiController]
[ZeroAuthorize]
[DisableBrowserCache]
+[ApiMetadataFilter]
+[ApiResponseFilter]
//[ServiceFilter(typeof(ModelStateValidationFilterAttribute))]
//[ServiceFilter(typeof(BackofficeFilterAttribute))]
public abstract class ZeroApiController : ControllerBase
diff --git a/zero.Api/ZeroBackofficeMvcOptions.cs b/zero.Api/ZeroBackofficeMvcOptions.cs
index 6f76eaf5..e645f6ac 100644
--- a/zero.Api/ZeroBackofficeMvcOptions.cs
+++ b/zero.Api/ZeroBackofficeMvcOptions.cs
@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.Options;
+using zero.Api.Filters;
namespace zero.Api;
@@ -15,5 +17,6 @@ internal class ZeroBackofficeMvcOptions : IConfigureOptions
public void Configure(MvcOptions options)
{
options.Conventions.Add(new ZeroBackofficeControllerModelConvention(Options.ZeroPath));
+ //options.Conventions.Add(new RouteTokenTransformerConvention(new ApiParameterTransformer()));
}
}
\ No newline at end of file
diff --git a/zero.Core/Applications/ApplicationModule.cs b/zero.Core/Applications/ApplicationModule.cs
new file mode 100644
index 00000000..744673d0
--- /dev/null
+++ b/zero.Core/Applications/ApplicationModule.cs
@@ -0,0 +1,14 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace zero.Applications;
+
+public class ApplicationModule : ZeroModule
+{
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddScoped();
+ services.AddScoped();
+ services.AddOptions().Bind(configuration.GetSection("Zero:Applications"));
+ }
+}
\ No newline at end of file
diff --git a/zero.Core/Applications/ServiceCollectionExtensions.cs b/zero.Core/Applications/ServiceCollectionExtensions.cs
deleted file mode 100644
index 98c26e8e..00000000
--- a/zero.Core/Applications/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace zero.Applications;
-
-internal static class ServiceCollectionExtensions
-{
- public static IServiceCollection AddZeroApplications(this IServiceCollection services, IConfiguration config)
- {
- services.AddScoped();
- services.AddScoped();
-
- services.AddOptions().Bind(config.GetSection("Zero:Applications"));
-
- return services;
- }
-}
\ No newline at end of file
diff --git a/zero.Core/Architecture/ServiceCollectionExtensions.cs b/zero.Core/Architecture/ArchitectureModule.cs
similarity index 54%
rename from zero.Core/Architecture/ServiceCollectionExtensions.cs
rename to zero.Core/Architecture/ArchitectureModule.cs
index 189d9a6e..a7aefb34 100644
--- a/zero.Core/Architecture/ServiceCollectionExtensions.cs
+++ b/zero.Core/Architecture/ArchitectureModule.cs
@@ -3,16 +3,13 @@ using Microsoft.Extensions.DependencyInjection;
namespace zero.Architecture;
-internal static class ServiceCollectionExtensions
+public class ArchitectureModule : ZeroModule
{
- public static IServiceCollection AddZeroBlueprints(this IServiceCollection services, IConfiguration config)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped();
services.AddScoped();
services.AddScoped();
-
- services.AddOptions().Bind(config.GetSection("Zero:Blueprints"));
-
- return services;
+ services.AddOptions().Bind(configuration.GetSection("Zero:Blueprints"));
}
}
\ No newline at end of file
diff --git a/zero.Core/Architecture/ZeroModule.cs b/zero.Core/Architecture/ZeroModule.cs
new file mode 100644
index 00000000..959cdea8
--- /dev/null
+++ b/zero.Core/Architecture/ZeroModule.cs
@@ -0,0 +1,50 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace zero.Architecture;
+
+public abstract class ZeroModule : IZeroModule
+{
+ ///
+ public virtual int Order { get; } = 0;
+
+ ///
+ public virtual int ConfigureOrder => Order;
+
+ ///
+ public virtual void ConfigureServices(IServiceCollection services, IConfiguration configuration) { }
+
+ ///
+ public virtual void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { }
+}
+
+
+public interface IZeroModule
+{
+ ///
+ /// Get the value to use to order startups to configure services. The default is 0.
+ ///
+ int Order { get; }
+
+ ///
+ /// Get the value to use to order startups to build the pipeline. The default is the 'Order' property.
+ ///
+ int ConfigureOrder { get; }
+
+ ///
+ /// This method gets called by the runtime. Use this method to add services to the container.
+ /// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
+ ///
+ /// The collection of service descriptors.
+ void ConfigureServices(IServiceCollection services, IConfiguration configuration);
+
+ ///
+ /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ ///
+ ///
+ ///
+ ///
+ void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider);
+}
\ No newline at end of file
diff --git a/zero.Core/Architecture/ZeroModuleCollection.cs b/zero.Core/Architecture/ZeroModuleCollection.cs
new file mode 100644
index 00000000..50997efe
--- /dev/null
+++ b/zero.Core/Architecture/ZeroModuleCollection.cs
@@ -0,0 +1,50 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using System.Collections.Concurrent;
+
+namespace zero.Architecture;
+
+internal class ZeroModuleCollection
+{
+ static ConcurrentDictionary _modules = new();
+
+
+ ///
+ /// Get all registered modules
+ ///
+ public static IEnumerable GetAll() => _modules.Values;
+
+
+ ///
+ /// Adds a zero module
+ ///
+ public static void AddModule(IServiceCollection services, IConfiguration configuration) where T : class, IZeroModule, new()
+ {
+ AddModule(services, configuration, new T());
+ }
+
+
+ ///
+ /// Adds a zero module
+ ///
+ public static void AddModule(IServiceCollection services, IConfiguration configuration, T moduleInstance) where T : IZeroModule
+ {
+ AddModule(typeof(T), services, configuration, moduleInstance);
+ }
+
+
+ ///
+ /// Adds a zero module
+ ///
+ public static void AddModule(Type moduleType, IServiceCollection services, IConfiguration configuration, IZeroModule moduleInstance)
+ {
+ if (_modules.ContainsKey(moduleType))
+ {
+ return;
+ }
+
+ _modules.TryAdd(moduleType, moduleInstance);
+
+ moduleInstance.ConfigureServices(services, configuration);
+ }
+}
\ No newline at end of file
diff --git a/zero.Core/Communication/ServiceCollectionExtensions.cs b/zero.Core/Communication/CommunicationModule.cs
similarity index 66%
rename from zero.Core/Communication/ServiceCollectionExtensions.cs
rename to zero.Core/Communication/CommunicationModule.cs
index 7f08b1a4..439f0059 100644
--- a/zero.Core/Communication/ServiceCollectionExtensions.cs
+++ b/zero.Core/Communication/CommunicationModule.cs
@@ -3,13 +3,12 @@ using Microsoft.Extensions.DependencyInjection;
namespace zero.Communication;
-internal static class ServiceCollectionExtensions
+public class CommunicationModule : ZeroModule
{
- public static IServiceCollection AddZeroCommunication(this IServiceCollection services)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped();
services.AddSingleton();
services.AddTransient();
- return services;
}
}
\ No newline at end of file
diff --git a/zero.Core/Configuration/ServiceCollectionExtensions.cs b/zero.Core/Configuration/ConfigurationModule.cs
similarity index 57%
rename from zero.Core/Configuration/ServiceCollectionExtensions.cs
rename to zero.Core/Configuration/ConfigurationModule.cs
index bc1f6341..fde5dd90 100644
--- a/zero.Core/Configuration/ServiceCollectionExtensions.cs
+++ b/zero.Core/Configuration/ConfigurationModule.cs
@@ -4,21 +4,20 @@ using Microsoft.Extensions.Options;
namespace zero.Configuration;
-internal static class ServiceCollectionExtensions
+public class ConfigurationModule : ZeroModule
{
- public static IServiceCollection AddZeroConfiguration(this IServiceCollection services, IConfiguration config)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddOptions().Configure((opts, svc) =>
{
opts.ServiceProvider = svc;
opts.Version = "0.0.1-alpha.1";
opts.ZeroPath = "/zero";
- opts.TokenExpiration = TimeSpan.FromHours(3);
- }).Bind(config.GetSection("Zero"));
+ opts.TokenExpiration = TimeSpan.FromHours(3);
+ }).Bind(configuration.GetSection("Zero"));
+
services.AddTransient(factory => factory.GetService>().Value);
- services.AddOptions().Bind(config.GetSection("Zero:Features"));
-
- return services;
+ services.AddOptions().Bind(configuration.GetSection("Zero:Features"));
}
}
\ No newline at end of file
diff --git a/zero.Core/Context/ContextModule.cs b/zero.Core/Context/ContextModule.cs
new file mode 100644
index 00000000..b9445b73
--- /dev/null
+++ b/zero.Core/Context/ContextModule.cs
@@ -0,0 +1,13 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace zero.Context;
+
+public class ContextModule : ZeroModule
+{
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddScoped();
+ services.AddHttpContextAccessor();
+ }
+}
\ No newline at end of file
diff --git a/zero.Core/Context/ServiceCollectionExtensions.cs b/zero.Core/Context/ServiceCollectionExtensions.cs
deleted file mode 100644
index ec97450b..00000000
--- a/zero.Core/Context/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace zero.Context;
-
-internal static class ServiceCollectionExtensions
-{
- public static IServiceCollection AddZeroContext(this IServiceCollection services)
- {
- services.AddScoped();
- services.AddHttpContextAccessor();
- return services;
- }
-}
\ No newline at end of file
diff --git a/zero.Core/FileStorage/ServiceCollectionExtensions.cs b/zero.Core/FileStorage/FileStorageModule.cs
similarity index 52%
rename from zero.Core/FileStorage/ServiceCollectionExtensions.cs
rename to zero.Core/FileStorage/FileStorageModule.cs
index 49bdfe0c..4ebca033 100644
--- a/zero.Core/FileStorage/ServiceCollectionExtensions.cs
+++ b/zero.Core/FileStorage/FileStorageModule.cs
@@ -4,17 +4,15 @@ using Microsoft.Extensions.DependencyInjection;
namespace zero.FileStorage;
-internal static class ServiceCollectionExtensions
+public class FileStorageModule : ZeroModule
{
- public static IServiceCollection AddZeroFileStorage(this IServiceCollection services, IConfiguration config)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped(factory => new Paths(factory.GetService()));
- services.AddOptions().Bind(config.GetSection("Zero:FileSystem")).Configure(opts =>
+ services.AddOptions().Bind(configuration.GetSection("Zero:FileSystem")).Configure(opts =>
{
opts.ZeroAssetsPath = "zero";
});
-
- return services;
}
}
\ No newline at end of file
diff --git a/zero.Core/Identity/ServiceCollectionExtensions.cs b/zero.Core/Identity/IdentityModule.cs
similarity index 85%
rename from zero.Core/Identity/ServiceCollectionExtensions.cs
rename to zero.Core/Identity/IdentityModule.cs
index f0019b4a..e9bb21cb 100644
--- a/zero.Core/Identity/ServiceCollectionExtensions.cs
+++ b/zero.Core/Identity/IdentityModule.cs
@@ -1,14 +1,15 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Identity;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
-namespace zero.Identity;
+namespace zero.Applications;
-internal static class ServiceCollectionExtensions
+public class IdentityModule : ZeroModule
{
- public static IServiceCollection AddZeroIdentity(this IServiceCollection services)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.TryAddEnumerable(ServiceDescriptor.Singleton, PostConfigureCookieAuthenticationOptions>());
@@ -26,6 +27,5 @@ internal static class ServiceCollectionExtensions
.AddZeroBackofficeCookie();
services.AddAuthorization();
- return services;
}
}
\ No newline at end of file
diff --git a/zero.Core/Localization/ServiceCollectionExtensions.cs b/zero.Core/Localization/LocalizationModule.cs
similarity index 61%
rename from zero.Core/Localization/ServiceCollectionExtensions.cs
rename to zero.Core/Localization/LocalizationModule.cs
index 2eada28f..75a61557 100644
--- a/zero.Core/Localization/ServiceCollectionExtensions.cs
+++ b/zero.Core/Localization/LocalizationModule.cs
@@ -1,10 +1,11 @@
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
namespace zero.Localization;
-internal static class ServiceCollectionExtensions
+public class LocalizationModule : ZeroModule
{
- public static IServiceCollection AddZeroLocalization(this IServiceCollection services)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped();
services.AddScoped();
@@ -12,6 +13,5 @@ internal static class ServiceCollectionExtensions
services.AddScoped();
services.AddScoped();
services.AddScoped();
- return services;
}
}
\ No newline at end of file
diff --git a/zero.Core/Mails/MailsModule.cs b/zero.Core/Mails/MailsModule.cs
new file mode 100644
index 00000000..eefad1ea
--- /dev/null
+++ b/zero.Core/Mails/MailsModule.cs
@@ -0,0 +1,14 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace zero.Mails;
+
+public class MailsModule : ZeroModule
+{
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/zero.Core/Mails/ServiceCollectionExtensions.cs b/zero.Core/Mails/ServiceCollectionExtensions.cs
deleted file mode 100644
index 3d89191a..00000000
--- a/zero.Core/Mails/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace zero.Mails;
-
-internal static class ServiceCollectionExtensions
-{
- public static IServiceCollection AddZeroMails(this IServiceCollection services)
- {
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- return services;
- }
-}
\ No newline at end of file
diff --git a/zero.Core/Mapper/MapperExtensions.cs b/zero.Core/Mapper/MapperExtensions.cs
index 66239b95..38df094e 100644
--- a/zero.Core/Mapper/MapperExtensions.cs
+++ b/zero.Core/Mapper/MapperExtensions.cs
@@ -24,9 +24,14 @@ public static class MapperExtensions
return mapper.Map(source, typeof(TSource), default);
}
- public static Paged Map(this IZeroMapper mapper, Paged source)
+ public static Paged Map(this IZeroMapper mapper, Paged source, Action modify = null)
{
- return source.MapTo(srcItem => mapper.Map(srcItem));
+ return source.MapTo(srcItem =>
+ {
+ TDestination destination = mapper.Map(srcItem);
+ modify?.Invoke(srcItem, destination);
+ return destination;
+ });
}
public static Result Map(this IZeroMapper mapper, Result source)
diff --git a/zero.Core/Mapper/MapperModule.cs b/zero.Core/Mapper/MapperModule.cs
new file mode 100644
index 00000000..14a25d74
--- /dev/null
+++ b/zero.Core/Mapper/MapperModule.cs
@@ -0,0 +1,12 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace zero.Mapper;
+
+public class MapperModule : ZeroModule
+{
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/zero.Core/Mapper/ServiceCollectionExtensions.cs b/zero.Core/Mapper/ServiceCollectionExtensions.cs
deleted file mode 100644
index 35ce62d9..00000000
--- a/zero.Core/Mapper/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace zero.Mapper;
-
-internal static class ServiceCollectionExtensions
-{
- public static IServiceCollection AddZeroMapper(this IServiceCollection services)
- {
- services.AddScoped();
- return services;
- }
-}
\ No newline at end of file
diff --git a/zero.Core/Media/ServiceCollectionExtensions.cs b/zero.Core/Media/MediaModule.cs
similarity index 84%
rename from zero.Core/Media/ServiceCollectionExtensions.cs
rename to zero.Core/Media/MediaModule.cs
index c340ae87..568b746d 100644
--- a/zero.Core/Media/ServiceCollectionExtensions.cs
+++ b/zero.Core/Media/MediaModule.cs
@@ -7,9 +7,9 @@ using System.IO;
namespace zero.Media;
-internal static class ServiceCollectionExtensions
+public class MediaModule : ZeroModule
{
- public static IServiceCollection AddZeroMedia(this IServiceCollection services, IConfiguration config)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton(svc =>
{
@@ -22,7 +22,7 @@ internal static class ServiceCollectionExtensions
services.AddScoped();
services.AddScoped();
- services.AddOptions().Bind(config.GetSection("Zero:Media")).Configure(opts =>
+ services.AddOptions().Bind(configuration.GetSection("Zero:Media")).Configure(opts =>
{
opts.FolderPath = "uploads";
opts.AllowedOtherFileExtensions = new() { ".pdf" };
@@ -41,6 +41,5 @@ internal static class ServiceCollectionExtensions
raven.Indexes.Add();
raven.Indexes.Add();
});
- return services;
}
}
\ No newline at end of file
diff --git a/zero.Core/Models/Results/Paged.cs b/zero.Core/Models/Results/Paged.cs
index 4ead0606..0505fb78 100644
--- a/zero.Core/Models/Results/Paged.cs
+++ b/zero.Core/Models/Results/Paged.cs
@@ -13,10 +13,15 @@ public class Paged : Paged
{
return new Paged(Items.Select(x => convertItem(x)).Where(x => x != null).ToList(), TotalItems, Page, PageSize);
}
+
+ public override IEnumerable GetItems()
+ {
+ return Items;
+ }
}
-public class Paged
+public abstract class Paged
{
public long Page { get; protected set; }
@@ -55,4 +60,6 @@ public class Paged
}
return 0;
}
+
+ public abstract IEnumerable GetItems();
}
\ No newline at end of file
diff --git a/zero.Core/Pages/ServiceCollectionExtensions.cs b/zero.Core/Pages/PagesModule.cs
similarity index 68%
rename from zero.Core/Pages/ServiceCollectionExtensions.cs
rename to zero.Core/Pages/PagesModule.cs
index 1298eb5d..a16899ce 100644
--- a/zero.Core/Pages/ServiceCollectionExtensions.cs
+++ b/zero.Core/Pages/PagesModule.cs
@@ -3,16 +3,16 @@ using Microsoft.Extensions.DependencyInjection;
namespace zero.Pages;
-internal static class ServiceCollectionExtensions
+public class PagesModule : ZeroModule
{
- public static IServiceCollection AddZeroPages(this IServiceCollection services, IConfiguration config)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddOptions().Bind(config.GetSection("Zero:Pages"));
- services.AddOptions().Bind(config.GetSection("Zero:PageModules"));
+ services.AddOptions().Bind(configuration.GetSection("Zero:Pages"));
+ services.AddOptions().Bind(configuration.GetSection("Zero:PageModules"));
services.Configure(opts =>
{
@@ -24,6 +24,5 @@ internal static class ServiceCollectionExtensions
opts.For().Add(Constants.Pages.FolderAlias, "@page.folder.name", "@page.folder.description", "fth-folder");
});
- return services;
}
}
\ No newline at end of file
diff --git a/zero.Core/Persistence/ServiceCollectionExtensions.cs b/zero.Core/Persistence/PersistenceModule.cs
similarity index 81%
rename from zero.Core/Persistence/ServiceCollectionExtensions.cs
rename to zero.Core/Persistence/PersistenceModule.cs
index 6067fc60..bc2a5179 100644
--- a/zero.Core/Persistence/ServiceCollectionExtensions.cs
+++ b/zero.Core/Persistence/PersistenceModule.cs
@@ -5,9 +5,9 @@ using Raven.Client.Http;
namespace zero.Persistence;
-internal static class ServiceCollectionExtensions
+public class PersistenceModule : ZeroModule
{
- public static IServiceCollection AddZeroPersistence(this IServiceCollection services, IConfiguration config)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton();
services.AddSingleton(CreateRavenStore);
@@ -15,16 +15,13 @@ internal static class ServiceCollectionExtensions
services.AddScoped();
services.AddScoped();
- services.AddOptions().Bind(config.GetSection("Zero:Raven"));
-
- return services;
+ services.AddOptions().Bind(configuration.GetSection("Zero:Raven"));
}
-
///
/// Creates and configures the raven store
///
- static ZeroDocumentStore CreateRavenStore(IServiceProvider services)
+ protected ZeroDocumentStore CreateRavenStore(IServiceProvider services)
{
IZeroOptions options = services.GetService();
IZeroDocumentConventionsBuilder conventionsBuilder = services.GetService();
diff --git a/zero.Core/Persistence/Safenames.cs b/zero.Core/Persistence/Safenames.cs
index 6821692a..7d41297d 100644
--- a/zero.Core/Persistence/Safenames.cs
+++ b/zero.Core/Persistence/Safenames.cs
@@ -58,7 +58,7 @@ public class Safenames
}
char previous = default;
- StringBuilder output = new StringBuilder();
+ StringBuilder output = new();
for (int i = 0; i < value.Length; i++)
{
diff --git a/zero.Core/Rendering/RenderingModule.cs b/zero.Core/Rendering/RenderingModule.cs
new file mode 100644
index 00000000..a4cc72dc
--- /dev/null
+++ b/zero.Core/Rendering/RenderingModule.cs
@@ -0,0 +1,12 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace zero.Rendering;
+
+public class RenderingModule : ZeroModule
+{
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddScoped();
+ }
+}
\ No newline at end of file
diff --git a/zero.Core/Rendering/ServiceCollectionExtensions.cs b/zero.Core/Rendering/ServiceCollectionExtensions.cs
deleted file mode 100644
index 114b5b90..00000000
--- a/zero.Core/Rendering/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace zero.Rendering;
-
-internal static class ServiceCollectionExtensions
-{
- public static IServiceCollection AddZeroRendering(this IServiceCollection services)
- {
- services.AddScoped();
- return services;
- }
-}
\ No newline at end of file
diff --git a/zero.Core/Routing/ServiceCollectionExtensions.cs b/zero.Core/Routing/RoutingModule.cs
similarity index 82%
rename from zero.Core/Routing/ServiceCollectionExtensions.cs
rename to zero.Core/Routing/RoutingModule.cs
index 3fd44bf2..5894a993 100644
--- a/zero.Core/Routing/ServiceCollectionExtensions.cs
+++ b/zero.Core/Routing/RoutingModule.cs
@@ -5,9 +5,9 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
namespace zero.Routing;
-internal static class ServiceCollectionExtensions
+public class RoutingModule : ZeroModule
{
- public static IServiceCollection AddZeroRouting(this IServiceCollection services, IConfiguration config)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped();
services.AddScoped();
@@ -22,7 +22,7 @@ internal static class ServiceCollectionExtensions
services.TryAddEnumerable(ServiceDescriptor.Singleton());
services.AddScoped();
- services.AddOptions().Bind(config.GetSection("Zero:Routing"));
+ services.AddOptions().Bind(configuration.GetSection("Zero:Routing"));
services.Configure(opts =>
{
@@ -31,7 +31,5 @@ internal static class ServiceCollectionExtensions
raven.Indexes.Add();
raven.Indexes.Add();
});
-
- return services;
}
}
\ No newline at end of file
diff --git a/zero.Core/Spaces/ServiceCollectionExtensions.cs b/zero.Core/Spaces/SpacesModule.cs
similarity index 53%
rename from zero.Core/Spaces/ServiceCollectionExtensions.cs
rename to zero.Core/Spaces/SpacesModule.cs
index 49a4a527..3b6c7a6b 100644
--- a/zero.Core/Spaces/ServiceCollectionExtensions.cs
+++ b/zero.Core/Spaces/SpacesModule.cs
@@ -3,16 +3,14 @@ using Microsoft.Extensions.DependencyInjection;
namespace zero.Spaces;
-internal static class ServiceCollectionExtensions
+public class SpacesModule : ZeroModule
{
- public static IServiceCollection AddZeroSpaces(this IServiceCollection services, IConfiguration config)
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddOptions().Bind(config.GetSection("Zero:Spaces"));
-
- return services;
+ services.AddOptions().Bind(configuration.GetSection("Zero:Spaces"));
}
}
\ No newline at end of file
diff --git a/zero.Core/Stores/ServiceCollectionExtensions.cs b/zero.Core/Stores/ServiceCollectionExtensions.cs
deleted file mode 100644
index c0ddfe20..00000000
--- a/zero.Core/Stores/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace zero.Stores;
-
-internal static class ServiceCollectionExtensions
-{
- public static IServiceCollection AddZeroStores(this IServiceCollection services)
- {
- services.AddScoped();
- services.AddScoped();
- services.AddSingleton();
- return services;
- }
-}
\ No newline at end of file
diff --git a/zero.Core/Stores/StoresModule.cs b/zero.Core/Stores/StoresModule.cs
new file mode 100644
index 00000000..3038640d
--- /dev/null
+++ b/zero.Core/Stores/StoresModule.cs
@@ -0,0 +1,14 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace zero.Stores;
+
+public class StoresModule : ZeroModule
+{
+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddScoped();
+ services.AddScoped();
+ services.AddSingleton();
+ }
+}
\ No newline at end of file
diff --git a/zero.Core/ZeroApplicationBuilder.cs b/zero.Core/ZeroApplicationBuilder.cs
index 008de8f0..cbe5fdc9 100644
--- a/zero.Core/ZeroApplicationBuilder.cs
+++ b/zero.Core/ZeroApplicationBuilder.cs
@@ -13,6 +13,7 @@ public class ZeroApplicationBuilder : IZeroApplicationBuilder
{
App = app;
App.UseStaticFiles();
+ App.UseExceptionHandler("/zero/api/error");
App.UseMiddleware();
App.UseRouting();
App.UseAuthentication();
diff --git a/zero.Core/ZeroBuilder.cs b/zero.Core/ZeroBuilder.cs
index 7ae1f161..944312ab 100644
--- a/zero.Core/ZeroBuilder.cs
+++ b/zero.Core/ZeroBuilder.cs
@@ -30,27 +30,27 @@ public class ZeroBuilder
services.AddControllers();
-
// adds and discovers additional and built-in assemblies
new AssemblyDiscovery(Mvc).Execute(StartupOptions.AssemblyDiscoveryRules);
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
- services.AddZeroApplications(Configuration);
- services.AddZeroBlueprints(Configuration);
- services.AddZeroCommunication();
- services.AddZeroConfiguration(Configuration);
- services.AddZeroContext();
- services.AddZeroFileStorage(Configuration);
- services.AddZeroIdentity();
- services.AddZeroLocalization();
- services.AddZeroMails();
- services.AddZeroPages(Configuration);
- services.AddZeroMedia(Configuration);
- services.AddZeroPersistence(Configuration);
- services.AddZeroRendering();
- services.AddZeroRouting(Configuration);
- services.AddZeroStores();
- services.AddZeroMapper();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
+ AddModule();
//if (Environment.GetEnvironmentVariable("DOTNET_WATCH") == "1")
//{
@@ -72,6 +72,13 @@ public class ZeroBuilder
}
+ public ZeroBuilder AddModule() where T : class, IZeroModule, new()
+ {
+ ZeroModuleCollection.AddModule(Services, Configuration);
+ return this;
+ }
+
+
///
/// Adds a zero plugin
///