part of the extensibility works now

This commit is contained in:
2020-05-22 21:19:49 +02:00
parent 410bfcc95c
commit 897b801cd4
19 changed files with 310 additions and 221 deletions
+10 -11
View File
@@ -1,4 +1,5 @@
using Raven.Client.Documents;
using FluentValidation;
using Raven.Client.Documents;
using Raven.Client.Documents.Session;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -8,17 +9,15 @@ using zero.Core.Validation;
namespace zero.Core.Api
{
//public class ApplicationsApi : ApplicationsApi<Application>
//{
// public ApplicationsApi(IBackofficeStore store) : base(store) { }
//}
//public interface IApplicationsApi : IApplicationsApi<IApplication> { }
public class ApplicationsApi<T> : BackofficeApi, IApplicationsApi<T> where T : IApplication
{
public ApplicationsApi(IBackofficeStore store) : base(store) { }
IValidator<T> Validator;
public ApplicationsApi(IBackofficeStore store, IValidator<T> validator) : base(store)
{
Validator = validator;
}
/// <inheritdoc />
@@ -57,7 +56,7 @@ namespace zero.Core.Api
/// <inheritdoc />
public async Task<EntityResult<T>> Save(T model)
{
return await Save(model, new ApplicationValidator());
return await Save(model, Validator);
}
-31
View File
@@ -1,31 +0,0 @@
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace zero.Core
{
public class ApiControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var ctrls = feature.Controllers;
//var type = typeof(ApplicationsController)
//var controllerType = typeof(GenericController<>).MakeGenericType(entityType.AsType()).GetTypeInfo();
//feature.Controllers.Add()
//var typeName = entityType.Name + "Controller";
//// Check to see if there is a "real" controller for this class
//if (!feature.Controllers.Any(t => t.Name == typeName))
//{
// // Create a generic controller for this type
// feature.Controllers.Add(controllerType);
//}
}
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using zero.Core.Attributes;
namespace zero.Core.Entities
{
+66
View File
@@ -0,0 +1,66 @@
using FluentValidation;
using System;
using System.Collections.Generic;
using System.Text;
using zero.Core.Renderer;
namespace zero.Core
{
public static class EntityMap
{
static Dictionary<Type, Type> Types = new Dictionary<Type, Type>();
public static void Use<TService, TImplementation>() where TImplementation : TService
{
Types[typeof(TService)] = typeof(TImplementation);
}
public static Type Get<T>() => Get(typeof(T));
public static Type Get(Type type)
{
if (!Types.ContainsKey(type))
{
return null;
}
return Types[type];
}
}
public class EntityDefinition<TService, TImplementation> where TImplementation : TService
{
public Type ServiceType => typeof(TService);
public Type ImplementationType => typeof(TImplementation);
public IList<IValidator> Validators { get; set; } = new List<IValidator>();
public AbstractRenderer<TImplementation> Renderer { get; set; }
}
public class EntityDefinition
{
public Type ServiceType { get; protected set; }
public Type ImplementationType { get; protected set; }
public IList<IValidator> Validators { get; set; } = new List<IValidator>();
public AbstractGenericRenderer Renderer { get; set; }
public static EntityDefinition Convert<TService, TImplementation>(EntityDefinition<TService, TImplementation> definition) where TImplementation : TService
{
return new EntityDefinition()
{
ServiceType = definition.ServiceType,
ImplementationType = definition.ImplementationType,
Validators = definition.Validators,
Renderer = definition.Renderer.ToGenericRenderer()
};
}
}
}
@@ -33,7 +33,7 @@ namespace zero.Core.Extensions
}
// use name from attribute if available
CollectionAttribute collection = type.GetCustomAttribute<CollectionAttribute>();
CollectionAttribute collection = type.GetCustomAttribute<CollectionAttribute>(true);
if (collection != null)
{
return collection.Name;
+62 -2
View File
@@ -1,4 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
using FluentValidation;
using Microsoft.Extensions.DependencyInjection;
using Scrutor;
using System;
using System.Linq;
using System.Reflection;
using zero.Core.Api;
namespace zero.Core.Entities
@@ -7,8 +12,63 @@ namespace zero.Core.Entities
{
public static void AddZeroCoreServices(this IServiceCollection services)
{
services.AddTransient<IApplication, Application>();
//services.Scan(scan => scan.FromAssemblyOf<IApplication>()
// .AddClasses(classes => classes.AssignableTo(typeof(IValidator<>)))
// .AsImplementedInterfaces()
// //.As(typeof(IValidator<>))
// .WithScopedLifetime()
//);
//Assembly[] assemblies = new Assembly[1] { typeof(IApplication).Assembly };
services.AddAllByInterfaceTransient(typeof(IValidator), typeof(IValidator<>), AppDomain.CurrentDomain.GetAssemblies());
services.AddTransient<IApplication, Application>();
services.AddTransient(typeof(IApplicationsApi<>), typeof(ApplicationsApi<>));
}
public static void AddAllByInterfaceTransient<TService, TImplementation>(this IServiceCollection services, Assembly[] assemblies)
{
services.AddAllByInterfaceTransient(typeof(TService), typeof(TImplementation), assemblies);
//Type searchFor = typeof(TService);
//string searchForName = nameof(TService); // + (isGeneric ? "`1" : String.Empty);
//foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
//{
// foreach (Type type in assembly.GetExportedTypes())
// {
// if (searchFor.IsAssignableFrom(type) && type.IsClass)
// {
// Type service = type.GetInterfaces().FirstOrDefault(x => x.IsInterface && x.Name == searchForName);
// if (service != null)
// {
// services.AddTransient(service, type);
// }
// }
// }
//}
}
public static void AddAllByInterfaceTransient(this IServiceCollection services, Type serviceType, Type implementingType, Assembly[] assemblies)
{
foreach (Assembly assembly in assemblies)
{
foreach (Type type in assembly.GetExportedTypes())
{
if (serviceType.IsAssignableFrom(type) && type.IsClass && !type.IsAbstract && !type.FullName.StartsWith("Fluent"))
{
Type service = type.GetInterfaces().FirstOrDefault(x => x.IsInterface && x.Name == implementingType.Name);
if (service != null)
{
services.AddTransient(service, type);
}
}
}
}
}
}
}
+1
View File
@@ -15,6 +15,7 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.3" />
<PackageReference Include="RavenDB.Client" Version="4.2.101" />
<PackageReference Include="Scrutor" Version="3.2.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-rc0001" />
</ItemGroup>
+34 -1
View File
@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -24,6 +26,37 @@ namespace zero.Debug.Controllers
}
[HttpGet]
public IActionResult Services()
{
List<object> items = new List<object>();
foreach (var service in Startup.Services)
{
if (!service.ServiceType.FullName.StartsWith("zero.") && (service.ImplementationType == null || !service.ImplementationType.FullName.StartsWith("zero.")))
{
continue;
}
items.Add(new
{
name = service.ServiceType.FullName,
lifetime = service.Lifetime,
instance = service.ImplementationType?.FullName
});
}
return Json(items);
}
[HttpGet]
public IActionResult Things([FromServices] IValidator<Application> appValidator)
{
return Ok();
}
[HttpGet]
public async Task<IActionResult> Index()
{
+15 -1
View File
@@ -1,9 +1,23 @@
using zero.Core.Entities;
using FluentValidation;
using zero.Core.Attributes;
using zero.Core.Entities;
using zero.Core.Validation;
namespace zero.Debug
{
[Collection("Applications")]
public class MyApplication : Application
{
public string Description { get; set; }
}
public class MyApplicationValidator : AbstractValidator<MyApplication>
{
public MyApplicationValidator()
{
Include(new ApplicationValidator());
RuleFor(x => x.Description).NotEmpty();
}
}
}
+3
View File
@@ -21,11 +21,14 @@ namespace zero.Debug
}
public static IServiceCollection Services;
// 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 https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
Services = services;
services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
+5
View File
@@ -1,7 +1,10 @@
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using zero.Core;
using zero.Core.Entities;
using zero.Core.Options;
using zero.Core.Plugins;
using zero.Debug;
using zero.Debug.TestData;
using zero.TestData.Lists;
@@ -37,6 +40,8 @@ namespace zero.TestData
public void ConfigureServices(IServiceCollection services)
{
EntityMap.Use<IApplication, MyApplication>();
services.AddTransient<ITestService, TestService>();
}
}
@@ -106,7 +106,7 @@
form.load(!this.id ? ApplicationsApi.getEmpty() : ApplicationsApi.getById(this.id)).then(response =>
{
this.disabled = !response.canEdit;
this.model = response;
this.model = response.entity;
});
ApplicationsApi.getAllFeatures().then(items =>
+25 -15
View File
@@ -2,33 +2,43 @@
using Microsoft.AspNetCore.Mvc.Controllers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using zero.Core.Entities;
using zero.Web.Controllers;
using zero.Core;
using zero.Web.Filters;
namespace zero.Web
{
public class ApiControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
public ApiControllerFeatureProvider()
{
}
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var ctrls = feature.Controllers;
Assembly currentAssembly = typeof(ApiControllerFeatureProvider).Assembly;
var type = typeof(ApplicationsController<>).MakeGenericType(typeof(Application)).GetTypeInfo();
IEnumerable<Type> candidates = currentAssembly.GetExportedTypes().Where(x => x.GetCustomAttributes<BackofficeGenericControllerAttribute>().Any() && x.ContainsGenericParameters);
feature.Controllers.Add(type);
//var controllerType = typeof(GenericController<>).MakeGenericType(entityType.AsType()).GetTypeInfo();
foreach (Type candidate in candidates)
{
Type genericType = candidate.GetGenericTypeDefinition();
Type[] arguments = genericType.GetGenericArguments();
Type desiredInterface = arguments.FirstOrDefault()?.GetInterfaces().FirstOrDefault();
//feature.Controllers.Add()
//var typeName = entityType.Name + "Controller";
if (desiredInterface == null)
{
continue;
}
//// Check to see if there is a "real" controller for this class
//if (!feature.Controllers.Any(t => t.Name == typeName))
//{
// // Create a generic controller for this type
// feature.Controllers.Add(controllerType);
//}
Type implementation = EntityMap.Get(desiredInterface);
TypeInfo type = candidate.MakeGenericType(implementation).GetTypeInfo();
feature.Controllers.Add(type);
}
}
}
}
+19
View File
@@ -0,0 +1,19 @@
using FluentValidation;
using zero.Core.Entities;
using zero.Core.Renderer;
namespace zero.Web
{
public class ApplicationRenderer : AbstractRenderer<Application>
{
public ApplicationRenderer()
{
Alias = "debug.contentpage";
LabelTemplate = "@_test.fields.{0}";
DescriptionTemplate = "@_test.fields.{0}_text";
Field(x => x.Name, required: true).Text();
}
}
}
@@ -1,73 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Identity;
using zero.Web.Filters;
using zero.Web.Models;
namespace zero.Web.Controllers
{
[ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Read)]
[BackofficeGenericController]
public class ApplicationsController<T> : BackofficeController where T : IApplication
{
IApplicationsApi<T> Api;
public ApplicationsController(IApplicationsApi<T> api)
{
Api = api;
}
/// <summary>
/// Get translation by id
/// </summary>
public IActionResult GetEmpty([FromServices] T app) => JsonEdit(app);
/// <summary>
/// Get translation by id
/// </summary>
public async Task<IActionResult> GetById([FromQuery] string id) => JsonEdit(await Api.GetById(id));
/// <summary>
/// Get all translations
/// </summary>
public async Task<IActionResult> GetAll([FromQuery] ListQuery<T> query = null)
{
if (query == null)
{
return Json(await Api.GetAll());
}
return Json(await Api.GetByQuery(query));
}
/// <summary>
/// Get all available features to select
/// </summary>
public IActionResult GetAllFeatures() => Json(Options.Features.GetAllItems());
/// <summary>
/// Save translation
/// </summary>
[ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Write)]
public async Task<IActionResult> Save([FromBody] EditModel<T> model)
{
return Ok();
//Application entity = await Mapper.Map(model, await Api.GetById(null));
//return await As<IApplication, ApplicationEditModel>(await Api.Save(entity));
}
/// <summary>
/// Deletes a translation
/// </summary>
[ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Write)]
public async Task<IActionResult> Delete([FromQuery] string id) => JsonEdit(await Api.Delete(id));
}
}
+50 -78
View File
@@ -1,93 +1,65 @@
//using Microsoft.AspNetCore.Mvc;
//using System.Threading.Tasks;
//using zero.Core.Api;
//using zero.Core.Entities;
//using zero.Core.Identity;
//using zero.Core.Options;
//using zero.Web.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Identity;
//namespace zero.Web.Controllers
//{
// [ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Read)]
// public class ApplicationsController : BackofficeController
// {
// IApplicationsApi<Application> Api;
namespace zero.Web.Controllers
{
[ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Read)]
public class ApplicationsController<T> : BackofficeController where T : IApplication
{
IApplicationsApi<T> Api;
// public ApplicationsController(IApplicationsApi<Application> api)
// {
// Api = api;
// }
public ApplicationsController(IApplicationsApi<T> api)
{
Api = api;
}
// /// <summary>
// /// Get translation by id
// /// </summary>
// public IActionResult GetEmpty([FromServices] IApplication app)
// {
// return JsonEdit(app);
// }
/// <summary>
/// Get translation by id
/// </summary>
public IActionResult GetEmpty([FromServices] T app) => JsonEdit(app);
// /// <summary>
// /// Get translation by id
// /// </summary>
// public async Task<IActionResult> GetById([FromQuery] string id)
// {
// return JsonEdit(await Api.GetById(id));
// }
/// <summary>
/// Get translation by id
/// </summary>
public async Task<IActionResult> GetById([FromQuery] string id) => JsonEdit(await Api.GetById(id));
// /// <summary>
// /// Get all translations
// /// </summary>
// public async Task<IActionResult> GetAll([FromQuery] ListQuery<Application> query = null)
// {
// if (query == null)
// {
// return Json(await Api.GetAll());
// }
/// <summary>
/// Get all translations
/// </summary>
public async Task<IActionResult> GetAll([FromQuery] ListQuery<T> query = null)
{
if (query == null)
{
return Json(await Api.GetAll());
}
// return Json(await Api.GetByQuery(query));
// }
return Json(await Api.GetByQuery(query));
}
// /// <summary>
// /// Get all available features to select
// /// </summary>
// public IActionResult GetAllFeatures()
// {
// return Json(Options.Features.GetAllItems());
// }
/// <summary>
/// Get all available features to select
/// </summary>
public IActionResult GetAllFeatures() => Json(Options.Features.GetAllItems());
// /// <summary>
// /// Save translation
// /// </summary>
// [ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Write)]
// public async Task<IActionResult> Save([FromBody] EditModel<IApplication> model)
// {
// return Ok();
// //Application entity = await Mapper.Map(model, await Api.GetById(null));
// //return await As<IApplication, ApplicationEditModel>(await Api.Save(entity));
// }
/// <summary>
/// Save translation
/// </summary>
[ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Write)]
public async Task<IActionResult> Save([FromBody] T model) => Json(await Api.Save(model));
// /// <summary>
// /// Deletes a translation
// /// </summary>
// [ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Write)]
// public async Task<IActionResult> Delete([FromQuery] string id)
// {
// return JsonEdit(await Api.Delete(id));
// }
// /// <summary>
// /// Save translation
// /// </summary>
// //public async Task<IActionResult> Switch([FromQuery] string id)
// //{
// // return await As<Application, ApplicationEditModel>(await Api.Save(entity));
// //}
// }
//}
/// <summary>
/// Deletes a translation
/// </summary>
[ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Write)]
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
}
}
+3 -1
View File
@@ -13,6 +13,7 @@ using zero.Web.Models;
namespace zero.Web.Controllers
{
[ZeroAuthorize, CanEdit, AddToken]
[BackofficeGenericController]
public abstract class BackofficeController : Controller
{
IMapper _mapper;
@@ -42,7 +43,8 @@ namespace zero.Web.Controllers
{
return Json(new EditModel<T>()
{
Entity = data
Entity = data,
CanEdit = true
});
}
+14 -1
View File
@@ -137,7 +137,7 @@ namespace zero.Web
mvc.ConfigureApplicationPartManager(setup =>
{
setup.FeatureProviders.Add(new Web.ApiControllerFeatureProvider());
setup.FeatureProviders.Add(new ApiControllerFeatureProvider());
});
return mvc;
@@ -239,6 +239,19 @@ namespace zero.Web
}
private static EntityDefinition[] Entities = new EntityDefinition[] { };
//public ZeroPlugin Use<TService, TImplementation>(Action<EntityDefinition<TService, TImplementation>> configure) where TImplementation : TService
//{
// //configure()
//}
//public ZeroPlugin Use<TService, TImplementation>() where TImplementation : TService
//{
// //configure()
//}
/// <summary>
/// Adds a zero plugin
/// </summary>
-5
View File
@@ -36,11 +36,6 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "zero.Web.UI", "zero.Web.UI\
SlnRelativePath = "zero.Web.UI\"
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{08BF691F-450C-4C85-9953-D250E4DF7F41}"
ProjectSection(SolutionItems) = preProject
AssemblyInfo.cs = AssemblyInfo.cs
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU