Skip to content

Commit

Permalink
Fixes that type/feature mappings are not available during setup. (#16324
Browse files Browse the repository at this point in the history
)
  • Loading branch information
gvkries authored Jun 27, 2024
1 parent 9133c52 commit ba5e8c0
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using OrchardCore.Environment.Extensions.Features;
Expand All @@ -9,7 +8,6 @@ public interface IExtensionManager
{
IExtensionInfo GetExtension(string extensionId);
IEnumerable<IExtensionInfo> GetExtensions();
IEnumerable<Type> GetExportedExtensionTypes(IExtensionInfo extensionInfo);
Task<ExtensionEntry> LoadExtensionAsync(IExtensionInfo extensionInfo);

IEnumerable<IFeatureInfo> GetFeatures();
Expand Down
73 changes: 59 additions & 14 deletions src/OrchardCore/OrchardCore/Extensions/ExtensionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using OrchardCore.Environment.Extensions.Features;
Expand All @@ -24,6 +24,7 @@ public class ExtensionManager : IExtensionManager

private readonly IExtensionDependencyStrategy[] _extensionDependencyStrategies;
private readonly IExtensionPriorityStrategy[] _extensionPriorityStrategies;
private readonly ITypeFeatureProvider _typeFeatureProvider;
private readonly IFeaturesProvider _featuresProvider;

private FrozenDictionary<string, ExtensionEntry> _extensions;
Expand All @@ -41,12 +42,14 @@ public ExtensionManager(
IApplicationContext applicationContext,
IEnumerable<IExtensionDependencyStrategy> extensionDependencyStrategies,
IEnumerable<IExtensionPriorityStrategy> extensionPriorityStrategies,
ITypeFeatureProvider typeFeatureProvider,
IFeaturesProvider featuresProvider,
ILogger<ExtensionManager> logger)
{
_applicationContext = applicationContext;
_extensionDependencyStrategies = extensionDependencyStrategies as IExtensionDependencyStrategy[] ?? extensionDependencyStrategies.ToArray();
_extensionPriorityStrategies = extensionPriorityStrategies as IExtensionPriorityStrategy[] ?? extensionPriorityStrategies.ToArray();
_typeFeatureProvider = typeFeatureProvider;
_featuresProvider = featuresProvider;
L = logger;
}
Expand Down Expand Up @@ -89,18 +92,6 @@ public IEnumerable<IFeatureInfo> GetFeatures(string[] featureIdsToLoad)
}
}

public IEnumerable<Type> GetExportedExtensionTypes(IExtensionInfo extensionInfo)
{
EnsureInitialized();

if (_extensions.TryGetValue(extensionInfo.Id, out var extension))
{
return extension.ExportedTypes;
}

return [];
}

public Task<ExtensionEntry> LoadExtensionAsync(IExtensionInfo extensionInfo)
{
EnsureInitialized();
Expand All @@ -113,7 +104,7 @@ public Task<ExtensionEntry> LoadExtensionAsync(IExtensionInfo extensionInfo)
public Task<IEnumerable<IFeatureInfo>> LoadFeaturesAsync()
{
EnsureInitialized();

return Task.FromResult<IEnumerable<IFeatureInfo>>(_features.Values);
}

Expand Down Expand Up @@ -309,12 +300,49 @@ private void EnsureInitialized()

var loadedFeatures = new Dictionary<string, IFeatureInfo>();

// Get all types from all extension and add them to the type feature provider.
var allTypesByExtension = loadedExtensions
.SelectMany(extension =>
extension
.Value
.ExportedTypes
.Where(IsComponentType)
.Select(type => new
{
Extension = extension.Value,
Type = type
}));

var typesByFeature = allTypesByExtension
.GroupBy(typeByExtension => GetSourceFeatureNameForType(
typeByExtension.Type,
typeByExtension.Extension.ExtensionInfo.Id))
.ToDictionary(
group => group.Key,
group => group.Select(typesByExtension => typesByExtension.Type));

foreach (var loadedExtension in loadedExtensions)
{
var extension = loadedExtension.Value;

foreach (var feature in extension.ExtensionInfo.Features)
{
// Features can have no types.
if (typesByFeature.TryGetValue(feature.Id, out var featureTypes))
{
// This is adding the types to the main feature for backward compatibility.
// In the future we could stop doing it as we don't expect this to be necessary, and remove the FeatureTypeDiscovery attribute.
foreach (var type in featureTypes)
{
// If the attribute is present then we explicitly ignore the backward compatibility and skip the registration
// in the main feature.
if (!SkipExtensionFeatureRegistration(type))
{
_typeFeatureProvider.TryAdd(type, feature);
}
}
}

loadedFeatures.Add(feature.Id, feature);
}
}
Expand Down Expand Up @@ -366,5 +394,22 @@ private int GetPriority(IFeatureInfo feature)

return sum;
}

private static string GetSourceFeatureNameForType(Type type, string extensionId)
{
var attribute = type.GetCustomAttributes<FeatureAttribute>(false).FirstOrDefault();

return attribute?.FeatureName ?? extensionId;
}

private static bool IsComponentType(Type type)
{
return type.IsClass && !type.IsAbstract && type.IsPublic;
}

private static bool SkipExtensionFeatureRegistration(Type type)
{
return FeatureTypeDiscoveryAttribute.GetFeatureTypeDiscoveryForType(type)?.SkipExtension ?? false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,49 +152,6 @@ private void EnsureApplicationFeature()

private void PopulateTypeFeatureProvider(ITypeFeatureProvider typeFeatureProvider, FeatureAwareServiceCollection featureAwareServiceCollection)
{
// Get all types from all extension and add them to the type feature provider.
var extensions = _extensionManager.GetExtensions();

var allTypesByExtension = extensions
.SelectMany(extension =>
_extensionManager.GetExportedExtensionTypes(extension)
.Where(IsComponentType)
.Select(type => new
{
Extension = extension,
Type = type
}));

var typesByFeature = allTypesByExtension
.GroupBy(typeByExtension => GetSourceFeatureNameForType(
typeByExtension.Type,
typeByExtension.Extension.Id))
.ToDictionary(
group => group.Key,
group => group.Select(typesByExtension => typesByExtension.Type));

foreach (var extension in extensions)
{
foreach (var feature in extension.Features)
{
// Features can have no types.
if (typesByFeature.TryGetValue(feature.Id, out var featureTypes))
{
// This is adding the types to the main feature for backward compatibility.
// In the future we could stop doing it as we don't expect this to be necessary, and remove the FeatureTypeDiscovery attribute.
foreach (var type in featureTypes)
{
// If the attribute is present then we explicitly ignore the backward compatibility and skip the registration
// in the main feature.
if (!SkipExtensionFeatureRegistration(type))
{
typeFeatureProvider.TryAdd(type, feature);
}
}
}
}
}

// Register all DIed types in ITypeFeatureProvider.
foreach (var featureServiceCollection in featureAwareServiceCollection.FeatureCollections)
{
Expand Down Expand Up @@ -223,22 +180,5 @@ private void PopulateTypeFeatureProvider(ITypeFeatureProvider typeFeatureProvide
}
}
}

private static string GetSourceFeatureNameForType(Type type, string extensionId)
{
var attribute = type.GetCustomAttributes<FeatureAttribute>(false).FirstOrDefault();

return attribute?.FeatureName ?? extensionId;
}

private static bool IsComponentType(Type type)
{
return type.IsClass && !type.IsAbstract && type.IsPublic;
}

private static bool SkipExtensionFeatureRegistration(Type type)
{
return FeatureTypeDiscoveryAttribute.GetFeatureTypeDiscoveryForType(type)?.SkipExtension ?? false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,6 @@ public Task<IEnumerable<IFeatureInfo>> LoadFeaturesAsync(string[] featureIdsToLo
{
throw new NotImplementedException();
}

public IEnumerable<Type> GetExportedExtensionTypes(IExtensionInfo extensionInfo)
{
throw new NotImplementedException();
}
}

public class TestShapeProvider : IShapeTableProvider
Expand Down
3 changes: 3 additions & 0 deletions test/OrchardCore.Tests/Extensions/ExtensionManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public ExtensionManagerTests()
_applicationContext,
new[] { new ExtensionDependencyStrategy() },
new[] { new ExtensionPriorityStrategy() },
new TypeFeatureProvider(),
_moduleFeatureProvider,
new NullLogger<ExtensionManager>()
);
Expand All @@ -39,6 +40,7 @@ public ExtensionManagerTests()
_applicationContext,
new[] { new ExtensionDependencyStrategy() },
new[] { new ExtensionPriorityStrategy() },
new TypeFeatureProvider(),
_themeFeatureProvider,
new NullLogger<ExtensionManager>()
);
Expand All @@ -47,6 +49,7 @@ public ExtensionManagerTests()
_applicationContext,
new IExtensionDependencyStrategy[] { new ExtensionDependencyStrategy(), new ThemeExtensionDependencyStrategy() },
new[] { new ExtensionPriorityStrategy() },
new TypeFeatureProvider(),
_themeFeatureProvider,
new NullLogger<ExtensionManager>()
);
Expand Down
5 changes: 0 additions & 5 deletions test/OrchardCore.Tests/Stubs/StubExtensionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ public IEnumerable<IFeatureInfo> GetDependentFeatures(string featureId)
throw new NotImplementedException();
}

public IEnumerable<Type> GetExportedExtensionTypes(IExtensionInfo extensionInfo)
{
throw new NotImplementedException();
}

public IExtensionInfo GetExtension(string extensionId)
{
throw new NotImplementedException();
Expand Down

0 comments on commit ba5e8c0

Please sign in to comment.