Made implementation api cleaner

This commit is contained in:
Baumgartner Marcel 2023-11-15 10:25:28 +01:00
parent d55490dd51
commit e8706cad1c
20 changed files with 139 additions and 131 deletions

View file

@ -1,5 +1,6 @@
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Models.Abstractions;
using Moonlight.App.Models.Abstractions.Services;
namespace Moonlight.App.Actions.Dummy;

View file

@ -1,6 +1,4 @@
using System.ComponentModel;
using Moonlight.App.Database.Entities;
using Moonlight.App.Extensions.Attributes;
namespace Moonlight.App.Actions.Dummy;

View file

@ -0,0 +1,25 @@
using Moonlight.App.Actions.Dummy.Layouts;
using Moonlight.App.Actions.Dummy.Pages;
using Moonlight.App.Helpers;
using Moonlight.App.Models.Abstractions.Services;
namespace Moonlight.App.Actions.Dummy;
public class DummyServiceDefinition : ServiceDefinition
{
public override ServiceActions Actions => new DummyActions();
public override Type ConfigType => typeof(DummyConfig);
public override async Task BuildUserView(ServiceViewContext context)
{
context.Layout = ComponentHelper.FromType<DummyUser>();
await context.AddPage<DummyPage>("Demo", "/demo");
}
public override Task BuildAdminView(ServiceViewContext context)
{
context.Layout = ComponentHelper.FromType<DummyAdmin>();
return Task.CompletedTask;
}
}

View file

@ -1,34 +0,0 @@
using Microsoft.AspNetCore.Components;
using Moonlight.App.Actions.Dummy.Layouts;
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Helpers;
using Moonlight.App.Models.Abstractions;
namespace Moonlight.App.Actions.Dummy;
public class DummyServiceImplementation : ServiceImplementation
{
public override ServiceActions Actions { get; } = new DummyActions();
public override Type ConfigType { get; } = typeof(DummyConfig);
public override RenderFragment GetAdminLayout()
{
return ComponentHelper.FromType(typeof(DummyAdmin));
}
public override RenderFragment GetUserLayout()
{
return ComponentHelper.FromType(typeof(DummyUser));
}
public override ServiceUiPage[] GetUserPages(Service service, User user)
{
return Array.Empty<ServiceUiPage>();
}
public override ServiceUiPage[] GetAdminPages(Service service, User user)
{
return Array.Empty<ServiceUiPage>();
}
}

View file

@ -17,4 +17,7 @@ public static class ComponentHelper
builder.CloseComponent();
};
public static RenderFragment FromType<T>(Action<Dictionary<string, object>>? buildAttributes = null) where T : ComponentBase =>
FromType(typeof(T), buildAttributes);
}

View file

@ -1,10 +0,0 @@
using Moonlight.App.Database.Entities.Store;
namespace Moonlight.App.Models.Abstractions;
public abstract class ServiceActions
{
public abstract Task Create(IServiceProvider provider, Service service);
public abstract Task Update(IServiceProvider provider, Service service);
public abstract Task Delete(IServiceProvider provider, Service service);
}

View file

@ -1,18 +0,0 @@
using Microsoft.AspNetCore.Components;
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Store;
namespace Moonlight.App.Models.Abstractions;
public abstract class ServiceImplementation
{
public abstract ServiceActions Actions { get; }
public abstract Type ConfigType { get; }
public abstract RenderFragment GetAdminLayout();
public abstract RenderFragment GetUserLayout();
// The service and user parameter can be used to only show certain pages to admins or other
public abstract ServiceUiPage[] GetUserPages(Service service, User user);
public abstract ServiceUiPage[] GetAdminPages(Service service, User user);
}

View file

@ -0,0 +1,8 @@
namespace Moonlight.App.Models.Abstractions.Services;
public abstract class ServiceActions
{
public abstract Task Create(IServiceProvider provider, Database.Entities.Store.Service service);
public abstract Task Update(IServiceProvider provider, Database.Entities.Store.Service service);
public abstract Task Delete(IServiceProvider provider, Database.Entities.Store.Service service);
}

View file

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Components;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Models.Abstractions.Services;
public abstract class ServiceDefinition
{
// Config
public abstract ServiceActions Actions { get; }
public abstract Type ConfigType { get; }
// Methods
public abstract Task BuildUserView(ServiceViewContext context);
public abstract Task BuildAdminView(ServiceViewContext context);
}

View file

@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Components;
namespace Moonlight.App.Models.Abstractions;
namespace Moonlight.App.Models.Abstractions.Services;
public class ServiceUiPage
{
public string Name { get; set; }
public string Route { get; set; }
public string Icon { get; set; }
public ComponentBase Component { get; set; }
public RenderFragment Component { get; set; }
}

View file

@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Components;
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Helpers;
namespace Moonlight.App.Models.Abstractions.Services;
public class ServiceViewContext
{
// Meta
public Service Service { get; set; }
public User User { get; set; }
public Product Product { get; set; }
// Content
public List<ServiceUiPage> Pages { get; set; } = new();
public RenderFragment Layout { get; set; }
public Task AddPage<T>(string name, string route, string icon = "") where T : ComponentBase
{
Pages.Add(new()
{
Name = name,
Route = route,
Icon = icon,
Component = ComponentHelper.FromType<T>()
});
return Task.CompletedTask;
}
}

View file

@ -1,6 +1,4 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Models.Abstractions;
using Moonlight.App.Models.Abstractions.Services;
namespace Moonlight.App.Plugins.Contexts;
@ -13,5 +11,6 @@ public class PluginContext
public WebApplication WebApplication { get; set; }
public List<Action> PreInitTasks = new();
public List<Action> PostInitTasks = new();
public Action<List<ServiceUiPage>, ServiceManageContext>? BuildServiceUiPages = null;
public Action<ServiceViewContext>? BuildUserServiceView { get; set; } = null;
public Action<ServiceViewContext>? BuildAdminServiceView { get; set; } = null;
}

View file

@ -1,11 +0,0 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Store;
namespace Moonlight.App.Plugins.Contexts;
public class ServiceManageContext
{
public Service Service { get; set; }
public User User { get; set; }
public Product Product { get; set; }
}

View file

@ -3,6 +3,7 @@ using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Helpers;
using Moonlight.App.Models.Abstractions;
using Moonlight.App.Models.Abstractions.Services;
using Moonlight.App.Plugins;
using Moonlight.App.Plugins.Contexts;
@ -108,18 +109,24 @@ public class PluginService
}
}
public Task<ServiceUiPage[]> BuildServiceUiPages(ServiceUiPage[] pages, ServiceManageContext context)
public Task BuildUserServiceView(ServiceViewContext context)
{
var list = pages.ToList();
foreach (var plugin in Plugins)
{
// Only build if the plugin adds a page
if(plugin.Context.BuildServiceUiPages != null)
plugin.Context.BuildServiceUiPages.Invoke(list, context);
plugin.Context.BuildUserServiceView?.Invoke(context);
}
return Task.FromResult(list.ToArray());
return Task.CompletedTask;
}
public Task BuildAdminServiceView(ServiceViewContext context)
{
foreach (var plugin in Plugins)
{
plugin.Context.BuildAdminServiceView?.Invoke(context);
}
return Task.CompletedTask;
}
private string[] FindFiles(string dir)

View file

@ -9,17 +9,17 @@ namespace Moonlight.App.Services.ServiceManage;
public class ServiceAdminService
{
private readonly IServiceScopeFactory ServiceScopeFactory;
private readonly ServiceTypeService ServiceTypeService;
private readonly ServiceDefinitionService ServiceDefinitionService;
public ServiceAdminService(IServiceScopeFactory serviceScopeFactory, ServiceTypeService serviceTypeService)
public ServiceAdminService(IServiceScopeFactory serviceScopeFactory, ServiceDefinitionService serviceDefinitionService)
{
ServiceScopeFactory = serviceScopeFactory;
ServiceTypeService = serviceTypeService;
ServiceDefinitionService = serviceDefinitionService;
}
public async Task<Service> Create(User u, Product p, Action<Service>? modifyService = null)
{
var impl = ServiceTypeService.Get(p);
var impl = ServiceDefinitionService.Get(p);
// Load models in new scope
using var scope = ServiceScopeFactory.CreateScope();
@ -66,7 +66,7 @@ public class ServiceAdminService
if (service == null)
throw new DisplayException("Service does not exist anymore");
var impl = ServiceTypeService.Get(service);
var impl = ServiceDefinitionService.Get(service);
await impl.Actions.Delete(scope.ServiceProvider, service);

View file

@ -2,24 +2,25 @@
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Database.Enums;
using Moonlight.App.Models.Abstractions;
using Moonlight.App.Models.Abstractions.Services;
using Moonlight.App.Repositories;
namespace Moonlight.App.Services.ServiceManage;
public class ServiceTypeService
public class ServiceDefinitionService
{
private readonly Dictionary<ServiceType, ServiceImplementation> ServiceImplementations = new();
private readonly Dictionary<ServiceType, ServiceDefinition> ServiceImplementations = new();
private readonly IServiceScopeFactory ServiceScopeFactory;
public ServiceTypeService(IServiceScopeFactory serviceScopeFactory)
public ServiceDefinitionService(IServiceScopeFactory serviceScopeFactory)
{
ServiceScopeFactory = serviceScopeFactory;
}
public void Register<T>(ServiceType type) where T : ServiceImplementation
public void Register<T>(ServiceType type) where T : ServiceDefinition
{
var impl = Activator.CreateInstance<T>() as ServiceImplementation;
var impl = Activator.CreateInstance<T>() as ServiceDefinition;
if (impl == null)
throw new ArgumentException("The provided type is not an service implementation");
@ -30,7 +31,7 @@ public class ServiceTypeService
ServiceImplementations.Add(type, impl);
}
public ServiceImplementation Get(Service s)
public ServiceDefinition Get(Service s)
{
using var scope = ServiceScopeFactory.CreateScope();
var serviceRepo = scope.ServiceProvider.GetRequiredService<Repository<Service>>();
@ -43,9 +44,9 @@ public class ServiceTypeService
return Get(service.Product);
}
public ServiceImplementation Get(Product p) => Get(p.Type);
public ServiceDefinition Get(Product p) => Get(p.Type);
public ServiceImplementation Get(ServiceType type)
public ServiceDefinition Get(ServiceType type)
{
if (!ServiceImplementations.ContainsKey(type))
throw new ArgumentException($"No service implementation found for {type}");

View file

@ -13,7 +13,7 @@ public class ServiceService // This service is used for managing services and cr
private readonly Repository<User> UserRepository;
public ServiceAdminService Admin => ServiceProvider.GetRequiredService<ServiceAdminService>();
public ServiceTypeService Type => ServiceProvider.GetRequiredService<ServiceTypeService>();
public ServiceDefinitionService Definition => ServiceProvider.GetRequiredService<ServiceDefinitionService>();
public ServiceManageService Manage => ServiceProvider.GetRequiredService<ServiceManageService>();
public ServiceService(IServiceProvider serviceProvider, Repository<Service> serviceRepository, Repository<User> userRepository)

View file

@ -108,7 +108,7 @@ public class StoreAdminService
{
try
{
var impl = ServiceService.Type.Get(type);
var impl = ServiceService.Definition.Get(type);
return impl.ConfigType;
}
catch (ArgumentException)
@ -123,7 +123,7 @@ public class StoreAdminService
}
public object GetProductConfig(Product product)
{
var impl = ServiceService.Type.Get(product.Type);
var impl = ServiceService.Definition.Get(product.Type);
return JsonConvert.DeserializeObject(product.ConfigJson, impl.ConfigType) ??
CreateNewProductConfig(product.Type);

View file

@ -80,7 +80,7 @@ builder.Services.AddSingleton<AutoMailSendService>();
// Services / ServiceManage
builder.Services.AddScoped<ServiceService>();
builder.Services.AddSingleton<ServiceAdminService>();
builder.Services.AddSingleton<ServiceTypeService>();
builder.Services.AddSingleton<ServiceDefinitionService>();
builder.Services.AddSingleton<ServiceManageService>();
// Services / Ticketing
@ -123,9 +123,9 @@ app.MapControllers();
// Auto start background services
app.Services.GetRequiredService<AutoMailSendService>();
var serviceService = app.Services.GetRequiredService<ServiceTypeService>();
var serviceService = app.Services.GetRequiredService<ServiceDefinitionService>();
serviceService.Register<DummyServiceImplementation>(ServiceType.Server);
serviceService.Register<DummyServiceDefinition>(ServiceType.Server);
await pluginService.RunPrePost(app);

View file

@ -5,6 +5,7 @@
@using Moonlight.App.Services.ServiceManage
@using Microsoft.EntityFrameworkCore
@using Moonlight.App.Models.Abstractions
@using Moonlight.App.Models.Abstractions.Services
@using Moonlight.App.Services
@inject Repository<Service> ServiceRepository
@ -20,10 +21,10 @@
else
{
<CascadingValue Name="Service" Value="Service">
<CascadingValue Name="Implementation" Value="Implementation">
<CascadingValue Name="Implementation" Value="Definition">
<CascadingValue Name="Route" Value="Route">
<CascadingValue Name="Pages" Value="ServiceUiPages">
@Implementation.GetUserLayout()
<CascadingValue Name="ViewContext" Value="ViewContext">
@ViewContext.Layout
</CascadingValue>
</CascadingValue>
</CascadingValue>
@ -40,8 +41,8 @@
public string? Route { get; set; }
private Service? Service;
private ServiceImplementation Implementation;
private ServiceUiPage[] ServiceUiPages;
private ServiceDefinition Definition;
private ServiceViewContext ViewContext;
private async Task Load(LazyLoader lazyLoader)
{
@ -64,29 +65,21 @@
if (Service == null)
return;
// Load implementation
await lazyLoader.SetText("Loading implementation");
Definition = ServiceService.Definition.Get(Service.Product.Type);
Implementation = ServiceService.Type.Get(Service.Product.Type);
// Build dynamic user interface
await lazyLoader.SetText("Building dynamic user interface");
await lazyLoader.SetText("Building ui");
// Build ui pages
List<ServiceUiPage> pagesWithoutPlugins = new();
// -- Add default here --
// Add implementation pages
pagesWithoutPlugins.AddRange(Implementation.GetUserPages(Service, IdentityService.CurrentUser));
// Modify pages through plugins
ServiceUiPages = await PluginService.BuildServiceUiPages(pagesWithoutPlugins.ToArray(), new()
ViewContext = new ServiceViewContext()
{
Product = Service.Product,
Service = Service,
Product = Service.Product,
User = IdentityService.CurrentUser
});
};
// Done :D
await Definition.BuildUserView(ViewContext);
await PluginService.BuildUserServiceView(ViewContext);
}
}