/// <summary> /// If possible, creates a <see cref="TableDescriptor"/> from the /// given <see cref="Type"/>. A <see cref="Type"/> is considered to /// represent a table if the following are true: /// <para /> /// <list type="bullet"> /// <item> /// The <see cref="Type"/> is concrete. That is, the /// <see cref="Type"/> is not abstract, is not static, and /// is not an interface. /// </item> /// <item> /// The <see cref="Type"/> is decorated with the /// <see cref="TableAttribute"/> attribute. /// </item> /// </list> /// <para/> /// </summary> /// <param name="type"> /// The <see cref="Type"/> from which to extract a /// <see cref="TableDescriptor"/>. /// </param> /// <param name="tableConfigSerializer"> /// The serializer object that is used to deserialize the /// pre-built table configuration JSON files. /// </param> /// <param name="tableDescriptor"> /// If <paramref name="type"/> is a valid table type, then this is /// populated with the created <see cref="TableDescriptor"/>. /// Otherwise, this parameter will be set to <c>null</c>. /// </param> /// <returns> /// <c>true</c> if <paramref name="type"/> is a valid table, /// <c>false</c> otherwise. /// </returns> public static bool TryCreate( Type type, ISerializer tableConfigSerializer, out TableDescriptor tableDescriptor) { return(TryCreate(type, tableConfigSerializer, out bool internalTable, out tableDescriptor, out var buildTableAction)); }
/// <summary> /// When a custom data processor needs to generate tables dynamically based on data content, call this /// method to register the table with the runtime. /// </summary> /// <param name="tableDescriptor">Descriptor for the generated table.</param> /// <param name="buildAction">Action called to create the requested table.</param> protected void AddTableGeneratedFromDataProcessing( TableDescriptor tableDescriptor, Action <ITableBuilder, IDataExtensionRetrieval> buildAction) { // the buildAction may be null because the custom data processor may have a special way to handle // these data derived tables - and that can be done through BuildTableCore // Guard.NotNull(tableDescriptor, nameof(tableDescriptor)); if (this.dataDerivedTables.ContainsKey(tableDescriptor)) { throw new ArgumentException( $"The data derived table already exists in the processor: {tableDescriptor}", nameof(tableDescriptor)); } if (this.TableDescriptorToBuildAction.ContainsKey(tableDescriptor)) { throw new ArgumentException( $"The data derived table already exists in the processor as a static table: {tableDescriptor}", nameof(tableDescriptor)); } this.dataDerivedTables.Add(tableDescriptor, buildAction); }
/// <inheritdoc /> public void EnableTable(TableDescriptor tableDescriptor) { lock (this.enabledTables) { this.enabledTables.Add(tableDescriptor); OnTableEnabled(tableDescriptor); } }
/// <summary> /// If possible, creates a <see cref="TableDescriptor"/> from the /// given <see cref="Type"/>. A <see cref="Type"/> is considered to /// represent a table if the following are true: /// <para /> /// <list type="bullet"> /// <item> /// The <see cref="Type"/> is concrete. That is, the /// <see cref="Type"/> is not abstract, is not static, and /// is not an interface. /// </item> /// <item> /// The <see cref="Type"/> is decorated with the /// <see cref="TableAttribute"/> attribute. /// </item> /// </list> /// <para/> /// </summary> /// <param name="type"> /// The <see cref="Type"/> from which to extract a /// <see cref="TableDescriptor"/>. /// </param> /// <param name="tableConfigSerializer"> /// The serializer object that is used to deserialize the /// pre-built table configuration JSON files. /// </param> /// <param name="isInternalTable"> /// This table is private to the custom data source. /// </param> /// <param name="tableDescriptor"> /// If <paramref name="type"/> is a valid table type, then this is /// populated with the created <see cref="TableDescriptor"/>. /// Otherwise, this parameter will be set to <c>null</c>. /// </param> /// <param name="buildTableAction"> /// If the type contains a build table action, it is returned here. /// </param> /// <returns> /// <c>true</c> if <paramref name="type"/> is a valid table, /// <c>false</c> otherwise. /// </returns> public static bool TryCreate( Type type, ISerializer tableConfigSerializer, out bool isInternalTable, out TableDescriptor tableDescriptor, out Action <ITableBuilder, IDataExtensionRetrieval> buildTableAction) { return(TryCreate(type, tableConfigSerializer, out isInternalTable, out tableDescriptor, out buildTableAction, out var isDataAvailableFunc)); }
private void ProcessTableForExtensibility(TableDescriptor tableDescriptor) { if (tableDescriptor.RequiredDataCookers.Any() || tableDescriptor.RequiredDataProcessors.Any()) { if (!this.extensibilitySupport.AddTable(tableDescriptor)) { throw new InvalidOperationException($"Unable to process table {tableDescriptor.Name} for dependencies."); } } }
public override bool Equals(TableDescriptor x, TableDescriptor y) { if (x is null) { return(y is null); } else if (y is null) { return(false); } else { return(x.Guid == y.Guid); } }
/// <inheritdoc /> protected override void BuildTableCore( TableDescriptor tableDescriptor, Action <ITableBuilder, IDataExtensionRetrieval> createTable, ITableBuilder tableBuilder) { Guard.NotNull(tableDescriptor, nameof(tableDescriptor)); Guard.NotNull(createTable, nameof(createTable)); Guard.NotNull(tableBuilder, nameof(tableBuilder)); var dataRetrieval = GetDataExtensionRetrieval(tableDescriptor); if (dataRetrieval != null) { createTable(tableBuilder, dataRetrieval); } }
/// <inheritdoc /> public void BuildTable(TableDescriptor table, ITableBuilder tableBuilder) { Guard.NotNull(table, nameof(table)); Guard.NotNull(tableBuilder, nameof(tableBuilder)); if (this.TableDescriptorToBuildAction.TryGetValue(table, out var buildTable)) { this.BuildTableCore(table, buildTable, tableBuilder); } else if (this.dataDerivedTables.TryGetValue(table, out buildTable)) { this.BuildTableCore(table, buildTable, tableBuilder); } else { Debug.Assert(false, "A table was requested that is not known by the data source."); throw new InvalidOperationException( "Table " + table + " was requested to be built but is not supported by " + this.GetType()); } }
public virtual ITableService CreateTableService(TableDescriptor table) { if (table is null) { return(null); } if (table.ServiceInterface is null) { return(null); } if (table.ServiceInterface.IsPublic && table.ServiceInterface.IsInstantiatable() && table.ServiceInterface.Implements <ITableService>()) { return((ITableService)Activator.CreateInstance(table.ServiceInterface)); } return(null); }
/// <summary> /// This is called when a table has been enabled on this data processor. The default implementation does /// nothing. /// </summary> /// <param name="tableDescriptor">Table that was enabled.</param> protected virtual void OnTableEnabled(TableDescriptor tableDescriptor) { }
/// <summary> /// When overridden in a derived class, builds the requested /// table using the given <see cref="ITableBuilder"/>. /// </summary> /// <param name="tableDescriptor"> /// The descriptor of the requested table. /// </param> /// <param name="createTable"> /// Called to create the requested table. /// The object parameter of the Action may be used by the /// CustomDataProcessor to pass context. /// </param> /// <param name="tableBuilder"> /// The builder to use to build the table. /// </param> protected abstract void BuildTableCore( TableDescriptor tableDescriptor, Action <ITableBuilder, IDataExtensionRetrieval> createTable, ITableBuilder tableBuilder);
/// <inheritdoc /> public virtual bool DoesTableHaveData(TableDescriptor table) { return(true); }
/// <summary> /// This returns an instance of <see cref="IDataExtensionRetrieval"/> that is specific to a given /// <see cref="TableDescriptor"/>. /// </summary> /// <param name="tableDescriptor">Table descriptor</param> /// <returns>This can be used to retrieve data with which to build the given table.</returns> protected IDataExtensionRetrieval GetDataExtensionRetrieval(TableDescriptor tableDescriptor) { return(this.extensibilitySupport.GetDataExtensionRetrieval(tableDescriptor)); }
public override int GetHashCode(TableDescriptor obj) { Guard.NotNull(obj, nameof(obj)); return(obj.Guid.GetHashCode()); }
/// <inheritdoc /> /// <summary> /// Builds on top of the functionality provided by the base class by enabling the data cookers required /// by the given table descriptor. /// </summary> protected override void OnTableEnabled(TableDescriptor tableDescriptor) { ProcessTableForExtensibility(tableDescriptor); }
/// <summary> /// If possible, creates a <see cref="TableDescriptor"/> from the /// given <see cref="Type"/>. A <see cref="Type"/> is considered to /// represent a table if the following are true: /// <para /> /// <list type="bullet"> /// <item> /// The <see cref="Type"/> is concrete. That is, the /// <see cref="Type"/> is not abstract, is not static, and /// is not an interface. /// </item> /// <item> /// The <see cref="Type"/> is decorated with the /// <see cref="TableAttribute"/> attribute. /// </item> /// </list> /// <para/> /// </summary> /// <param name="type"> /// The <see cref="Type"/> from which to extract a /// <see cref="TableDescriptor"/>. /// </param> /// <param name="tableConfigSerializer"> /// The serializer object that is used to deserialize the /// pre-built table configuration JSON files. /// </param> /// <param name="logger"> /// Used to log any relevant messages. /// </param> /// <param name="isInternalTable"> /// This table is private to the custom data source. /// </param> /// <param name="tableDescriptor"> /// If <paramref name="type"/> is a valid table type, then this is /// populated with the created <see cref="TableDescriptor"/>. /// Otherwise, this parameter will be set to <c>null</c>. /// </param> /// <param name="buildTableAction"> /// If the type contains a build table action, it is returned here. /// </param> /// <param name="isDataAvailableFunc"> /// If the type contains a has data func, it is returned here. /// </param> /// <returns> /// <c>true</c> if <paramref name="type"/> is a valid table, /// <c>false</c> otherwise. /// </returns> public static bool TryCreate( Type type, ISerializer tableConfigSerializer, ILogger logger, out bool isInternalTable, out TableDescriptor tableDescriptor, out Action <ITableBuilder, IDataExtensionRetrieval> buildTableAction, out Func <IDataExtensionRetrieval, bool> isDataAvailableFunc) { tableDescriptor = null; buildTableAction = null; isDataAvailableFunc = null; isInternalTable = false; if (type is null) { return(false); } var tableAttribute = type.GetCustomAttribute <TableAttribute>(); if (tableAttribute == null) { return(false); } if (!type.IsStatic() && !type.IsConcrete()) { return(false); } isInternalTable = tableAttribute.InternalTable; buildTableAction = GetTableBuildAction(type, tableAttribute.BuildTableActionMethodName); isDataAvailableFunc = GetIsDataAvailableFunc(type, tableAttribute.IsDataAvailableMethodName); tableDescriptor = GetTableDescriptor( type, tableAttribute.TableDescriptorPropertyName, tableConfigSerializer, logger); if (tableDescriptor == null) { return(false); } // I'm looking at this for the first time in months, and the certain tables aren't showing up because they're not specifically marked as internal // and they don't have build actions on them either. So for now, I'm going to try marking any table that doesn't have a build action as internal // to see if that will fix the issue. // todo:revisit this if (buildTableAction == null) { isInternalTable = true; } if (tableDescriptor.IsMetadataTable) { isInternalTable = true; } var cookerAttributes = type.GetCustomAttributes <RequiresCookerAttribute>(); foreach (var cooker in cookerAttributes) { tableDescriptor.AddRequiredDataCooker(cooker.RequiredDataCookerPath); } // todo:should we make this optional, or does it makes sense to always include this? // we could add an "IncludeType" bool or something on the table attribute if we don't want // this to always be set. tableDescriptor.ExtendedData["Type"] = type; return(true); }
private static TableDescriptor GetTableDescriptor( Type type, string propertyName, ISerializer tableConfigSerializer, ILogger logger) { Guard.NotNull(type, nameof(type)); TableDescriptor tableDescriptor = null; IEnumerable <RequiresCookerAttribute> cookerAttributes = null; var tableDescriptorPropertyInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static); if (tableDescriptorPropertyInfo != null) { var getMethodInfo = tableDescriptorPropertyInfo.GetMethod; if (!getMethodInfo.IsPublic) { logger?.Warn( $"Type {type} appears to be a Table, but the " + $"{nameof(TableAttribute.TableDescriptorPropertyName)}.get method is not found as public."); return(null); } if (getMethodInfo.ReturnType != typeof(TableDescriptor)) { logger?.Warn( $"Type {type} appears to be a Table, but the " + $"{nameof(TableAttribute.TableDescriptorPropertyName)}.get method must return type " + $"{nameof(TableDescriptor)}."); return(null); } // Allow [RequiresCooker] attribute on the property that returns the TableDescriptor cookerAttributes = tableDescriptorPropertyInfo.GetCustomAttributes <RequiresCookerAttribute>(); tableDescriptor = (TableDescriptor)getMethodInfo.Invoke(null, null); } else { // The table descriptor property didn't exist, check for a field instead. var tableDescriptorFieldInfo = type.GetField(propertyName, BindingFlags.Public | BindingFlags.Static); if (tableDescriptorFieldInfo == null) { logger?.Warn( $"Type {type} appears to be a class, but the " + $"{nameof(TableAttribute.TableDescriptorPropertyName)} property is not found as public and " + "static."); return(null); } // Allow [RequiresCooker] attribute on the property that returns the TableDescriptor cookerAttributes = tableDescriptorFieldInfo.GetCustomAttributes <RequiresCookerAttribute>(); tableDescriptor = tableDescriptorFieldInfo.GetValue(null) as TableDescriptor; } if (tableDescriptor == null) { return(null); } tableDescriptor.Type = type; tableDescriptor.PrebuiltTableConfigurations = TableConfigurations.GetPrebuiltTableConfigurations( type, tableDescriptor.Guid, tableConfigSerializer, logger); foreach (var cooker in cookerAttributes) { tableDescriptor.AddRequiredDataCooker(cooker.RequiredDataCookerPath); } return(tableDescriptor); }