Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes that type/feature mappings are not available during setup #16324

Merged
merged 5 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 class ExtensionManager : IExtensionManager
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(),
hishamco marked this conversation as resolved.
Show resolved Hide resolved
_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