using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq.Expressions; using System.Reflection; using HopFrame.Core.Callbacks; namespace HopFrame.Core.Config; 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 Properties { get; } = new(); public TableConfig(DbContextConfig config, Type tableType, string propertyName, int nthTable) { TableType = tableType; PropertyName = propertyName; ContextConfig = config; DisplayName = PropertyName; Order = nthTable; 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; propConfig.Editable = false; } if (info.GetCustomAttributes(true).Any(a => a is KeyAttribute)) { propConfig.Editable = false; } Properties.Add(propConfig); } } } /// /// A helper class for editing the /// public sealed class TableConfigurator(TableConfig config) { /// /// The Internal property configuration that's modified by the helper functions /// public TableConfig InnerConfig { get; } = config; /// /// Determines if the table should be ignored in the admin ui /// public TableConfigurator Ignore(bool ignore) { InnerConfig.Ignored = ignore; return this; } /// /// Configures the property of the table /// /// Used for determining the property /// The configurator for the specified property /// public PropertyConfigurator Property(Expression> propertyExpression) { var info = GetPropertyInfo(propertyExpression); var prop = InnerConfig.Properties .Single(prop => prop.Info == info); return new PropertyConfigurator(prop); } /// /// Configures the property of the table using the provided configurator /// /// Used for determining the property /// Used for configuring the property /// public TableConfigurator Property(Expression> propertyExpression, Action> configurator) { var prop = Property(propertyExpression); configurator.Invoke(prop); return this; } /// /// Adds a virtual property to the table view (this property will not appear in the editor) /// /// The name of the virtual property /// The template used for generating the property value /// The configurator for the virtual property /// public VirtualPropertyConfigurator AddVirtualProperty(string name, Func template) { var prop = new VirtualPropertyConfig(InnerConfig, InnerConfig.Properties.Count) { Name = name, IsVirtualProperty = true, Formatter = (obj, provider) => Task.FromResult(template.Invoke((TModel)obj, provider)) }; InnerConfig.Properties.Add(prop); return new VirtualPropertyConfigurator(prop); } /// public VirtualPropertyConfigurator AddVirtualProperty(string name, Func> template) { var prop = new VirtualPropertyConfig(InnerConfig, InnerConfig.Properties.Count) { Name = name, IsVirtualProperty = true, Formatter = (obj, provider) => template.Invoke((TModel)obj, provider) }; InnerConfig.Properties.Add(prop); return new VirtualPropertyConfigurator(prop); } /// /// Adds a virtual property to the table view and configures it using the provided configurator (this property will not appear in the editor) /// /// The name of the virtual property /// The template used for generating the property value /// Used for configuring the virtual property /// public TableConfigurator AddVirtualProperty(string name, Func template, Action> configurator) { var prop = AddVirtualProperty(name, template); configurator.Invoke(prop); return this; } /// /// Determines the name for the table used in the admin ui and url for the table page /// public TableConfigurator SetDisplayName(string name) { InnerConfig.DisplayName = name; return this; } /// /// Determines the description displayed in the admin ui /// public TableConfigurator SetDescription(string description) { InnerConfig.Description = description; return this; } /// /// Determines the order index for the table in the admin ui /// /// public TableConfigurator SetOrderIndex(int index) { InnerConfig.Order = index; return this; } /// /// Determines the policy needed by a user in order to view the table /// public TableConfigurator SetViewPolicy(string policy) { InnerConfig.ViewPolicy = policy; return this; } /// /// Determines the policy needed by a user in order to edit the entries /// public TableConfigurator SetUpdatePolicy(string policy) { InnerConfig.UpdatePolicy = policy; return this; } /// /// Determines the policy needed by a user in order to create entries /// public TableConfigurator SetCreatePolicy(string policy) { InnerConfig.CreatePolicy = policy; return this; } /// /// Determines the policy needed by a user in order to delete entries /// public TableConfigurator SetDeletePolicy(string policy) { InnerConfig.DeletePolicy = policy; return this; } /// /// Adds a callback handler of the provided type /// /// The type of callback that triggers the handler /// The handler delegate public TableConfigurator AddCallbackHandler(CallbackType type, Func handler) { var eventName = CallbackTypes.ConstructCallbackName(type, InnerConfig); var handlerStore = new HopCallbackHandler(eventName, (o, provider) => handler.Invoke((TModel)o, provider)); InnerConfig.ContextConfig.ParentConfig.Handlers.Add(handlerStore); return this; } /// /// Adds a callback handler of the provided type /// /// The type of callback that triggers the handler /// The handler delegate public TableConfigurator AddCallbackHandler(CallbackType type, Action handler) { var eventName = CallbackTypes.ConstructCallbackName(type, InnerConfig); var handlerStore = new HopCallbackHandler(eventName, (o, provider) => { handler.Invoke((TModel)o, provider); return Task.CompletedTask; }); InnerConfig.ContextConfig.ParentConfig.Handlers.Add(handlerStore); return this; } internal static PropertyInfo GetPropertyInfo(Expression> propertyLambda) { if (propertyLambda.Body is not MemberExpression member) { throw new ArgumentException($"Expression '{propertyLambda}' refers to a method, not a property."); } if (member.Member is not PropertyInfo propInfo) { throw new ArgumentException($"Expression '{propertyLambda}' refers to a field, not a property."); } var type = typeof(TSource); if (propInfo.ReflectedType != null && type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType)) { throw new ArgumentException($"Expression '{propertyLambda}' refers to a property that is not from type {type}."); } if (propInfo.Name is null) throw new ArgumentException($"Expression '{propertyLambda}' refers a not existing property."); return propInfo; } }