/// <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));
 }
Пример #2
0
        /// <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);
        }
Пример #3
0
 /// <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.");
         }
     }
 }
Пример #6
0
 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);
            }
        }
Пример #8
0
        /// <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());
            }
        }
Пример #9
0
        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);
        }
Пример #10
0
 /// <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)
 {
 }
Пример #11
0
 /// <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);
Пример #12
0
 /// <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));
 }
Пример #14
0
            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);
        }