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

Corrects the mapping of types to features in ITypeFeatureProvider #15793

Merged
merged 31 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0ca7aae
Update type mappings in TypeFeatureProvider when the service collecti…
gvkries Apr 19, 2024
f0834d1
Remove unnecessary FeatureEntry type.
gvkries Apr 19, 2024
7f5352d
Moves populating the `ITypeFeatureProvider` completely into the `Shel…
gvkries Apr 20, 2024
3202617
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries Apr 22, 2024
3350463
Merge branch 'gvkries/di-dependencies-15782' of https://github.com/gv…
gvkries Apr 22, 2024
b65b1e0
Fixes unit tests.
gvkries Apr 22, 2024
6e509a6
Changes the `ITypeFeatureProvider` to allow types that are used by mu…
gvkries Apr 23, 2024
4ed7ab8
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries Apr 23, 2024
1a74496
Added a basic unit test.
gvkries Apr 23, 2024
2a71dfe
Merge branch 'main' into gvkries/di-dependencies-15782
Piedone May 2, 2024
4b20596
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries May 6, 2024
8d78e67
Update src/OrchardCore/OrchardCore/Extensions/ExtensionManager.cs
gvkries May 6, 2024
18812a7
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries May 15, 2024
b93df1c
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries May 16, 2024
a4a3c85
Adds GetFeatureForDependency() back and also introduces GetExtensionF…
gvkries May 16, 2024
471c62a
Formatting.
gvkries May 16, 2024
b6ec58e
Apply suggestions from code review
gvkries May 17, 2024
ca4e21b
React to code review.
gvkries May 17, 2024
85b9421
Suggestions from code review applied.
gvkries May 17, 2024
2335466
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries May 17, 2024
58fe1e6
Removes the FeatureAttribute from classes that are now automatically …
gvkries May 17, 2024
1c0d8a3
Typo
gvkries May 17, 2024
c4e8a3e
Check if feature is enabled when displaying permissions registered fo…
gvkries May 17, 2024
b6a988a
Added FeatureAttribute back for some classes that should belong to th…
gvkries May 17, 2024
ed7f37e
Use the last enabled feature instead of the first one in the permissi…
gvkries May 19, 2024
3cb2504
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries May 19, 2024
4439345
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries May 23, 2024
e73cb94
Update AdminController.cs
sebastienros May 23, 2024
a6bd9cd
Update src/OrchardCore/OrchardCore.Abstractions/Extensions/Features/I…
sebastienros May 23, 2024
c8d9db7
Merge branch 'main' into gvkries/di-dependencies-15782
gvkries May 24, 2024
5037727
minor cleanup and add documentation
MikeAlhayek May 24, 2024
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
Expand Up @@ -54,7 +54,7 @@ private async Task UpdateRolesForInstalledFeatureAsync(IFeatureInfo feature)
_installedFeatures.Add(feature.Id);

var providers = _permissionProviders
.Where(provider => _typeFeatureProvider.GetFeatureForDependency(provider.GetType()).Id == feature.Id);
.Where(provider => _typeFeatureProvider.GetFeaturesForDependency(provider.GetType()).Any(p => p.Id == feature.Id));

if (!providers.Any())
{
Expand Down Expand Up @@ -98,7 +98,7 @@ private async Task UpdateRolesForEnabledFeatureAsync(IFeatureInfo feature)
}

var providers = _permissionProviders
.Where(provider => _typeFeatureProvider.GetFeatureForDependency(provider.GetType()).Id == feature.Id);
.Where(provider => _typeFeatureProvider.GetFeaturesForDependency(provider.GetType()).Any(p => p.Id == feature.Id));

if (!providers.Any())
{
Expand Down Expand Up @@ -144,8 +144,8 @@ private async Task UpdateRoleForInstalledFeaturesAsync(string roleName)

// And defining at least one 'IPermissionProvider'.
rolesDocument.MissingFeaturesByRole[roleName] = (await _extensionManager.LoadFeaturesAsync(missingFeatures))
.Where(entry => entry.ExportedTypes.Any(type => type.IsAssignableTo(typeof(IPermissionProvider))))
.Select(entry => entry.FeatureInfo.Id)
.Where(entry => _typeFeatureProvider.GetTypesForFeature(entry).Any(type => type.IsAssignableTo(typeof(IPermissionProvider))))
.Select(entry => entry.Id)
.ToList();

await _documentManager.UpdateAsync(rolesDocument);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using OrchardCore.Environment.Extensions.Features;

namespace OrchardCore.Environment.Extensions
Expand All @@ -9,7 +10,39 @@ namespace OrchardCore.Environment.Extensions
/// </summary>
public interface ITypeFeatureProvider
{
/// <summary>
/// Gets the extension for the specified dependent type.
Piedone marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="dependency"></param>
/// <returns></returns>
Piedone marked this conversation as resolved.
Show resolved Hide resolved
IExtensionInfo GetExtensionForDependency(Type dependency);

/// <summary>
/// Gets the first feature for the specified dependent type.
/// </summary>
/// <param name="dependency"></param>
/// <returns></returns>
IFeatureInfo GetFeatureForDependency(Type dependency);

/// <summary>
/// Gets all features for the specified dependent type.
/// </summary>
/// <param name="dependency"></param>
/// <returns></returns>
IEnumerable<IFeatureInfo> GetFeaturesForDependency(Type dependency);
Piedone marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Gets all dependent types for the specified feature.
/// </summary>
/// <param name="feature"></param>
/// <returns></returns>
IEnumerable<Type> GetTypesForFeature(IFeatureInfo feature);

/// <summary>
/// Adds a type to the specified feature.
/// </summary>
/// <param name="type"></param>
/// <param name="feature"></param>
void TryAdd(Type type, IFeatureInfo feature);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using OrchardCore.Environment.Extensions.Features;
Expand All @@ -8,13 +9,14 @@ public interface IExtensionManager
{
IExtensionInfo GetExtension(string extensionId);
IEnumerable<IExtensionInfo> GetExtensions();
IEnumerable<Type> GetExportedExtensionTypes(IExtensionInfo extensionInfo);
Task<ExtensionEntry> LoadExtensionAsync(IExtensionInfo extensionInfo);

IEnumerable<IFeatureInfo> GetFeatures();
IEnumerable<IFeatureInfo> GetFeatures(string[] featureIdsToLoad);
IEnumerable<IFeatureInfo> GetFeatureDependencies(string featureId);
IEnumerable<IFeatureInfo> GetDependentFeatures(string featureId);
Task<IEnumerable<FeatureEntry>> LoadFeaturesAsync();
Task<IEnumerable<FeatureEntry>> LoadFeaturesAsync(string[] featureIdsToLoad);
Task<IEnumerable<IFeatureInfo>> LoadFeaturesAsync();
Task<IEnumerable<IFeatureInfo>> LoadFeaturesAsync(string[] featureIdsToLoad);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ public class ShellBlueprint
public ShellSettings Settings { get; set; }
public ShellDescriptor Descriptor { get; set; }

public IDictionary<Type, FeatureEntry> Dependencies { get; set; }
public IDictionary<Type, IEnumerable<IFeatureInfo>> Dependencies { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ private static async Task<int> InvokeMethodAsync(MethodInfo method, IDataMigrati
private IDataMigration[] GetDataMigrations(string featureId)
{
var migrations = _dataMigrations
.Where(dm => _typeFeatureProvider.GetFeatureForDependency(dm.GetType()).Id == featureId)
.Where(dm => _typeFeatureProvider.GetFeaturesForDependency(dm.GetType()).Any(feature => feature.Id == featureId))
.ToArray();

return migrations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public async Task<ShellDescriptor> GetShellDescriptorAsync()
var featureIds = features.Select(sf => sf.Id).ToArray();

var missingDependencies = (await _extensionManager.LoadFeaturesAsync(featureIds))
.Select(entry => entry.FeatureInfo.Id)
.Select(entry => entry.Id)
.Except(featureIds)
.Select(id => new ShellFeature(id));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.Hosting;
using OrchardCore.Environment.Extensions;
Expand Down Expand Up @@ -42,25 +43,23 @@ public void OnProvidersExecuted(ApplicationModelProviderContext context)
foreach (var controller in context.Result.Controllers)
{
var controllerType = controller.ControllerType.AsType();
var blueprint = _typeFeatureProvider.GetFeatureForDependency(controllerType);

if (blueprint != null)
Piedone marked this conversation as resolved.
Show resolved Hide resolved
{
if (blueprint.Extension.Id == _hostingEnvironment.ApplicationName && !_shellSettings.IsRunning())
{
// Don't serve any action of the application'module which is enabled during a setup.
foreach (var action in controller.Actions)
{
action.Selectors.Clear();
}
var blueprint = _typeFeatureProvider.GetExtensionForDependency(controllerType);
gvkries marked this conversation as resolved.
Show resolved Hide resolved

controller.Selectors.Clear();
}
else
if (blueprint.Id == _hostingEnvironment.ApplicationName && !_shellSettings.IsRunning())
{
// Don't serve any action of the application'module which is enabled during a setup.
gvkries marked this conversation as resolved.
Show resolved Hide resolved
foreach (var action in controller.Actions)
{
// Add an "area" route value equal to the module id.
controller.RouteValues.Add("area", blueprint.Extension.Id);
action.Selectors.Clear();
}

controller.Selectors.Clear();
}
else
{
// Add an "area" route value equal to the module id.
controller.RouteValues.Add("area", blueprint.Id);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ public RecipeMigrator(

public async Task<string> ExecuteAsync(string recipeFileName, IDataMigration migration)
{
var featureInfo = _typeFeatureProvider.GetFeatureForDependency(migration.GetType());
var extensionInfo = _typeFeatureProvider.GetExtensionForDependency(migration.GetType());

var recipeBasePath = Path.Combine(featureInfo.Extension.SubPath, "Migrations").Replace('\\', '/');
var recipeBasePath = Path.Combine(extensionInfo.SubPath, "Migrations").Replace('\\', '/');
var recipeFilePath = Path.Combine(recipeBasePath, recipeFileName).Replace('\\', '/');
var recipeFileInfo = _hostingEnvironment.ContentRootFileProvider.GetFileInfo(recipeFilePath);
var recipeDescriptor = await _recipeReader.GetRecipeDescriptorAsync(recipeBasePath, recipeFileInfo, _hostingEnvironment.ContentRootFileProvider);
Expand Down
85 changes: 24 additions & 61 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.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using OrchardCore.Environment.Extensions.Features;
Expand All @@ -24,12 +24,11 @@ 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;
private List<IExtensionInfo> _extensionsInfos;
private Dictionary<string, FeatureEntry> _features;
private Dictionary<string, IFeatureInfo> _features;
private IFeatureInfo[] _featureInfos;

private readonly ConcurrentDictionary<string, Lazy<IEnumerable<IFeatureInfo>>> _featureDependencies = new();
Expand All @@ -42,14 +41,12 @@ 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 @@ -92,6 +89,18 @@ 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 @@ -101,22 +110,22 @@ public Task<ExtensionEntry> LoadExtensionAsync(IExtensionInfo extensionInfo)
return Task.FromResult(extension);
}

public Task<IEnumerable<FeatureEntry>> LoadFeaturesAsync()
public Task<IEnumerable<IFeatureInfo>> LoadFeaturesAsync()
{
EnsureInitialized();
return Task.FromResult<IEnumerable<FeatureEntry>>(_features.Values);
return Task.FromResult<IEnumerable<IFeatureInfo>>(_features.Values);
}

public Task<IEnumerable<FeatureEntry>> LoadFeaturesAsync(string[] featureIdsToLoad)
public Task<IEnumerable<IFeatureInfo>> LoadFeaturesAsync(string[] featureIdsToLoad)
{
EnsureInitialized();

var features = new HashSet<string>(GetFeatures(featureIdsToLoad).Select(f => f.Id));

var loadedFeatures = _features.Values
.Where(f => features.Contains(f.FeatureInfo.Id));
.Where(f => features.Contains(f.Id));

return Task.FromResult<IEnumerable<FeatureEntry>>(loadedFeatures);
return Task.FromResult<IEnumerable<IFeatureInfo>>(loadedFeatures);
}

public IEnumerable<IFeatureInfo> GetFeatureDependencies(string featureId)
Expand All @@ -130,9 +139,7 @@ public IEnumerable<IFeatureInfo> GetFeatureDependencies(string featureId)
return [];
}

var feature = entry.FeatureInfo;

return GetFeatureDependencies(feature, _featureInfos);
return GetFeatureDependencies(entry, _featureInfos);
})).Value;
}

Expand All @@ -147,9 +154,7 @@ public IEnumerable<IFeatureInfo> GetDependentFeatures(string featureId)
return [];
}

var feature = entry.FeatureInfo;

return GetDependentFeatures(feature, _featureInfos);
return GetDependentFeatures(entry, _featureInfos);
})).Value;
}

Expand Down Expand Up @@ -260,13 +265,6 @@ private static List<IFeatureInfo> GetFeatureDependenciesFunc(IFeatureInfo curren
return list;
}

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

return attribute?.FeatureName ?? extensionId;
}

private void EnsureInitialized()
{
if (_isInitialized)
Expand Down Expand Up @@ -308,50 +306,20 @@ private void EnsureInitialized()
loadedExtensions.TryAdd(module.Name, entry);
});

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

// Get all valid types from any extension
var allTypesByExtension = loadedExtensions.SelectMany(extension =>
extension.Value.ExportedTypes.Where(IsComponentType)
.Select(type => new
{
ExtensionEntry = extension.Value,
Type = type
})).ToArray();

var typesByFeature = allTypesByExtension
.GroupBy(typeByExtension => GetSourceFeatureNameForType(
typeByExtension.Type,
typeByExtension.ExtensionEntry.ExtensionInfo.Id))
.ToDictionary(
group => group.Key,
group => group.Select(typesByExtension => typesByExtension.Type).ToArray());
var loadedFeatures = new Dictionary<string, IFeatureInfo>();

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))
{
foreach (var type in featureTypes)
{
_typeFeatureProvider.TryAdd(type, feature);
}
}
else
{
featureTypes = [];
}

loadedFeatures.Add(feature.Id, new FeatureEntry(feature, featureTypes));
loadedFeatures.Add(feature.Id, feature);
}
}

// Feature infos and entries are ordered by priority and dependencies.
_featureInfos = Order(loadedFeatures.Values.Select(f => f.FeatureInfo));
_featureInfos = Order(loadedFeatures.Values);
_features = _featureInfos.ToDictionary(f => f.Id, f => loadedFeatures[f.Id]);

// Extensions are also ordered according to the weight of their first features.
Expand All @@ -366,11 +334,6 @@ private void EnsureInitialized()
}
}

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

private IFeatureInfo[] Order(IEnumerable<IFeatureInfo> featuresToOrder)
{
return featuresToOrder
Expand Down
Loading