Added policy validation, ordering and virtual listing properties

This commit is contained in:
2025-01-16 20:23:28 +01:00
parent 4908947217
commit e9f686cf19
17 changed files with 321 additions and 93 deletions

View File

@@ -4,7 +4,7 @@ namespace HopFrame.Core.Config;
public class DbContextConfig {
public Type ContextType { get; }
public List<TableConfig> Tables { get; init; } = new();
public List<TableConfig> Tables { get; } = new();
public DbContextConfig(Type context) {
ContextType = context;
@@ -15,7 +15,7 @@ public class DbContextConfig {
var setType = typeof(DbSet<>).MakeGenericType(innerType);
if (property.PropertyType != setType) continue;
var table = new TableConfig(this, innerType, property.Name);
var table = new TableConfig(this, innerType, property.Name, Tables.Count);
Tables.Add(table);
}
}

View File

@@ -3,7 +3,7 @@ using System.Reflection;
namespace HopFrame.Core.Config;
public class PropertyConfig(PropertyInfo info, TableConfig table) {
public class PropertyConfig(PropertyInfo info, TableConfig table, int nthProperty) {
public PropertyInfo Info { get; } = info;
public TableConfig Table { get; } = table;
public string Name { get; set; } = info.Name;
@@ -19,6 +19,8 @@ public class PropertyConfig(PropertyInfo info, TableConfig table) {
public bool DisplayValue { get; set; } = true;
public bool IsRelation { get; set; }
public bool IsRequired { get; set; }
public bool IsListingProperty { get; set; }
public int Order { get; set; } = nthProperty;
}
public class PropertyConfig<TProp>(PropertyConfig config) {
@@ -84,4 +86,9 @@ public class PropertyConfig<TProp>(PropertyConfig config) {
InnerConfig.Validator = obj => validator.Invoke((TProp?)obj);
return this;
}
public PropertyConfig<TProp> OrderIndex(int index) {
InnerConfig.Order = index;
return this;
}
}

View File

@@ -9,20 +9,30 @@ public class TableConfig {
public Type TableType { get; }
public string PropertyName { get; }
public string DisplayName { get; set; }
public string? Description { get; set; }
public DbContextConfig ContextConfig { get; }
public bool Ignored { get; set; }
public int Order { get; set; }
internal bool Seeded { get; set; }
public string? ViewPolicy { get; set; }
public string? CreatePolicy { get; set; }
public string? UpdatePolicy { get; set; }
public string? DeletePolicy { get; set; }
public List<PropertyConfig> Properties { get; } = new();
public TableConfig(DbContextConfig config, Type tableType, string propertyName) {
public TableConfig(DbContextConfig config, Type tableType, string propertyName, int nthTable) {
TableType = tableType;
PropertyName = propertyName;
ContextConfig = config;
DisplayName = PropertyName;
Order = nthTable;
foreach (var info in tableType.GetProperties()) {
var propConfig = new PropertyConfig(info, this);
var properties = tableType.GetProperties();
for (var i = 0; i < properties.Length; i++) {
var info = properties[i];
var propConfig = new PropertyConfig(info, this, i);
if (info.GetCustomAttributes(true).Any(a => a is DatabaseGeneratedAttribute)) {
propConfig.Creatable = false;
@@ -59,10 +69,55 @@ public class TableConfig<TModel>(TableConfig config) {
return this;
}
public PropertyConfig<string> AddListingProperty(string name, Func<TModel, string> template) {
var prop = new PropertyConfig(InnerConfig.Properties.First().Info, InnerConfig, InnerConfig.Properties.Count);
prop.Name = name;
prop.IsListingProperty = true;
prop.Formatter = obj => template.Invoke((TModel)obj);
InnerConfig.Properties.Add(prop);
return new PropertyConfig<string>(prop);
}
public TableConfig<TModel> AddListingProperty(string name, Func<TModel, string> template, Action<PropertyConfig<string>> configurator) {
var prop = AddListingProperty(name, template);
configurator.Invoke(prop);
return this;
}
public TableConfig<TModel> SetDisplayName(string name) {
InnerConfig.DisplayName = name;
return this;
}
public TableConfig<TModel> SetDescription(string description) {
InnerConfig.Description = description;
return this;
}
public TableConfig<TModel> OrderIndex(int index) {
InnerConfig.Order = index;
return this;
}
public TableConfig<TModel> SetViewPolicy(string policy) {
InnerConfig.ViewPolicy = policy;
return this;
}
public TableConfig<TModel> SetUpdatePolicy(string policy) {
InnerConfig.UpdatePolicy = policy;
return this;
}
public TableConfig<TModel> SetCreatePolicy(string policy) {
InnerConfig.CreatePolicy = policy;
return this;
}
public TableConfig<TModel> SetDeletePolicy(string policy) {
InnerConfig.DeletePolicy = policy;
return this;
}
internal static PropertyInfo GetPropertyInfo<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda) {
if (propertyLambda.Body is not MemberExpression member) {

View File

@@ -1,6 +1,7 @@
using HopFrame.Core.Services;
using HopFrame.Core.Services.Implementations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace HopFrame.Core;
@@ -8,6 +9,7 @@ public static class ServiceCollectionExtensions {
public static IServiceCollection AddHopFrameServices(this IServiceCollection services) {
services.AddTransient<IContextExplorer, ContextExplorer>();
services.TryAddTransient<IHopFrameAuthHandler, DefaultAuthHandler>();
return services;
}

View File

@@ -3,7 +3,7 @@
namespace HopFrame.Core.Services;
public interface IContextExplorer {
public IEnumerable<string> GetTableNames();
public IEnumerable<TableConfig> GetTables();
public TableConfig? GetTable(string tableDisplayName);
public TableConfig? GetTable(Type tableEntity);
public ITableManager? GetTableManager(string tablePropertyName);

View File

@@ -12,5 +12,5 @@ public interface ITableManager {
public Task AddItem(object item);
public Task RevertChanges(object item);
public string DisplayProperty(object? item, PropertyInfo info, TableConfig? tableConfig);
public string DisplayProperty(object? item, PropertyConfig prop, TableConfig? tableConfig);
}

View File

@@ -5,11 +5,11 @@ using Microsoft.Extensions.Logging;
namespace HopFrame.Core.Services.Implementations;
internal sealed class ContextExplorer(HopFrameConfig config, IServiceProvider provider, ILogger<ContextExplorer> logger) : IContextExplorer {
public IEnumerable<string> GetTableNames() {
public IEnumerable<TableConfig> GetTables() {
foreach (var context in config.Contexts) {
foreach (var table in context.Tables) {
if (table.Ignored) continue;
yield return table.DisplayName;
yield return table;
}
}
}
@@ -60,6 +60,7 @@ internal sealed class ContextExplorer(HopFrameConfig config, IServiceProvider pr
foreach (var key in entity.GetForeignKeys()) {
var propConfig = table.Properties
.Where(prop => !prop.IsListingProperty)
.SingleOrDefault(prop => prop.Info == key.DependentToPrincipal?.PropertyInfo);
if (propConfig is null) continue;
propConfig.IsRelation = true;
@@ -67,6 +68,7 @@ internal sealed class ContextExplorer(HopFrameConfig config, IServiceProvider pr
foreach (var property in entity.GetProperties()) {
var propConfig = table.Properties
.Where(prop => !prop.IsListingProperty)
.SingleOrDefault(prop => prop.Info == property.PropertyInfo);
if (propConfig is null) continue;
propConfig.IsRequired = !property.IsNullable;

View File

@@ -0,0 +1,10 @@
namespace HopFrame.Core.Services.Implementations;
internal sealed class DefaultAuthHandler : IHopFrameAuthHandler {
public Task<bool> IsAuthenticatedAsync(string? policy) {
return Task.FromResult(true);
}
public Task<string> GetCurrentUserDisplayNameAsync() {
return Task.FromResult(string.Empty);
}
}

View File

@@ -67,10 +67,11 @@ internal sealed class TableManager<TModel>(DbContext context, TableConfig config
return false;
}
public string DisplayProperty(object? item, PropertyInfo info, TableConfig? tableConfig) {
public string DisplayProperty(object? item, PropertyConfig prop, TableConfig? tableConfig) {
if (item is null) return string.Empty;
var prop = tableConfig?.Properties.Find(prop => prop.Info.Name == info.Name);
if (prop is null) return item.ToString() ?? string.Empty;
if (prop.IsListingProperty)
return prop.Formatter!.Invoke(item);
var propValue = prop.Info.GetValue(item);
if (propValue is null)
@@ -83,14 +84,17 @@ internal sealed class TableManager<TModel>(DbContext context, TableConfig config
if (prop.DisplayedProperty is null) {
var key = prop.Info.PropertyType
.GetProperties()
.Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute))
.FirstOrDefault();
.FirstOrDefault(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute));
return key?.GetValue(propValue)?.ToString() ?? propValue.ToString() ?? string.Empty;
}
var innerConfig = explorer.GetTable(propValue.GetType());
return DisplayProperty(propValue, prop.DisplayedProperty, innerConfig);
var innerProp = innerConfig!.Properties
.SingleOrDefault(p => p.Info == prop.DisplayedProperty && !p.IsListingProperty);
if (innerProp is null) return propValue.ToString() ?? string.Empty;
return DisplayProperty(propValue, innerProp, innerConfig);
}
private IQueryable<TModel> IncludeForgeinKeys(IQueryable<TModel> query) {