diff --git a/src/HotChocolate/Core/src/Types/InternalsVisibleTo.cs b/src/HotChocolate/Core/src/Types/InternalsVisibleTo.cs index 4e87382dc42..9eee17d3f3a 100644 --- a/src/HotChocolate/Core/src/Types/InternalsVisibleTo.cs +++ b/src/HotChocolate/Core/src/Types/InternalsVisibleTo.cs @@ -13,3 +13,4 @@ [assembly: InternalsVisibleTo("HotChocolate.AspNetCore.Tests")] [assembly: InternalsVisibleTo("HotChocolate.Data.Filters.Tests")] [assembly: InternalsVisibleTo("HotChocolate.Data.Sorting.Tests")] +[assembly: InternalsVisibleTo("HotChocolate.Data.Projections.Tests")] diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/Convention.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/Convention.cs index ad369000367..614ad2cad42 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/Convention.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/Convention.cs @@ -33,7 +33,7 @@ protected internal virtual void Initialize(IConventionContext context) MarkInitialized(); } - protected internal virtual void OnComplete(IConventionContext context) + protected internal virtual void Complete(IConventionContext context) { } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/ConventionExtension.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/ConventionExtension.cs index 5c44449582d..e9d19fc9475 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/ConventionExtension.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/ConventionExtension.cs @@ -6,9 +6,9 @@ public abstract class ConventionExtension { public abstract void Merge(IConventionContext context, Convention convention); - protected internal sealed override void OnComplete(IConventionContext context) + protected internal sealed override void Complete(IConventionContext context) { - base.OnComplete(context); + base.Complete(context); } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/Convention`1.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/Convention`1.cs index ee8e70b85b1..49a8a2cedab 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/Convention`1.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/Convention`1.cs @@ -27,7 +27,7 @@ protected internal sealed override void Initialize(IConventionContext context) MarkInitialized(); } - protected internal override void OnComplete(IConventionContext context) + protected internal override void Complete(IConventionContext context) { _definition = null; } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs index ad59a9139d0..c5994d8c814 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs @@ -117,7 +117,7 @@ public ITypeInspector TypeInspector init.Initialize(conventionContext); MergeExtensions(conventionContext, init, extensions); - init.OnComplete(conventionContext); + init.Complete(conventionContext); } if (createdConvention is T createdConventionOfT) @@ -176,7 +176,7 @@ public ITypeInspector TypeInspector { extensionConvention.Initialize(context); extensions[m].Merge(context, convention); - extensionConvention.OnComplete(context); + extensionConvention.Complete(context); } } } diff --git a/src/HotChocolate/Core/test/Types.Tests/SchemaBuilderTests.cs b/src/HotChocolate/Core/test/Types.Tests/SchemaBuilderTests.cs index ad90baf5c11..8c5d625e7c4 100644 --- a/src/HotChocolate/Core/test/Types.Tests/SchemaBuilderTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/SchemaBuilderTests.cs @@ -2169,10 +2169,10 @@ protected override MockConventionDefinition CreateDefinition(IConventionContext return new MockConventionDefinition(); } - protected internal override void OnComplete(IConventionContext context) + protected internal override void Complete(IConventionContext context) { IsExtended = Definition.IsExtended; - base.OnComplete(context); + base.Complete(context); } } diff --git a/src/HotChocolate/Data/HotChocolate.Data.sln b/src/HotChocolate/Data/HotChocolate.Data.sln index 2bd5d6c03b2..4cfc5f33a16 100644 --- a/src/HotChocolate/Data/HotChocolate.Data.sln +++ b/src/HotChocolate/Data/HotChocolate.Data.sln @@ -80,6 +80,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Data.Tests", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.StarWars", "..\Core\test\StarWars\HotChocolate.StarWars.csproj", "{6B8B8B28-EE74-49A9-BC68-C42CB217BB54}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Data.Projections.Tests", "test\Data.Projections.Tests\HotChocolate.Data.Projections.Tests.csproj", "{5B36B9E9-BC55-4A4D-B58F-9311581C008B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -123,6 +125,7 @@ Global {16FB6511-AE94-46C4-9652-2665C6816546} = {4EE990B2-C327-46DA-8FE8-F95AC228E47F} {256DC401-40F1-4632-BB68-A336D1C518D9} = {4EE990B2-C327-46DA-8FE8-F95AC228E47F} {6B8B8B28-EE74-49A9-BC68-C42CB217BB54} = {882EC02D-5E1D-41F5-AD9F-AA06E31D133A} + {5B36B9E9-BC55-4A4D-B58F-9311581C008B} = {4EE990B2-C327-46DA-8FE8-F95AC228E47F} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D68A0AB9-871A-487B-8D12-1A7544D81B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -475,5 +478,17 @@ Global {6B8B8B28-EE74-49A9-BC68-C42CB217BB54}.Release|x64.Build.0 = Release|Any CPU {6B8B8B28-EE74-49A9-BC68-C42CB217BB54}.Release|x86.ActiveCfg = Release|Any CPU {6B8B8B28-EE74-49A9-BC68-C42CB217BB54}.Release|x86.Build.0 = Release|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Debug|x64.ActiveCfg = Debug|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Debug|x64.Build.0 = Debug|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Debug|x86.ActiveCfg = Debug|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Debug|x86.Build.0 = Debug|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Release|Any CPU.Build.0 = Release|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Release|x64.ActiveCfg = Release|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Release|x64.Build.0 = Release|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Release|x86.ActiveCfg = Release|Any CPU + {5B36B9E9-BC55-4A4D-B58F-9311581C008B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs b/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs index 6895a978b73..85b2d0bc039 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs @@ -68,7 +68,7 @@ protected virtual void Configure(IFilterConventionDescriptor descriptor) { } - protected override void OnComplete(IConventionContext context) + protected override void Complete(IConventionContext context) { if (Definition?.Provider is null) { @@ -100,14 +100,14 @@ protected override void OnComplete(IConventionContext context) CollectExtensions(context.Services, Definition); init.Initialize(context, this); MergeExtensions(context, init, extensions); - init.OnComplete(context); + init.Complete(context); } _typeInspector = context.DescriptorContext.TypeInspector; // It is important to always call base to continue the cleanup and the disposal of the // definition - base.OnComplete(context); + base.Complete(context); } @@ -295,7 +295,7 @@ public NameString GetOperationName(int operation) { extensionConvention.Initialize(context, this); extensions[m].Merge(context, providerConvention); - extensionConvention.OnComplete(context); + extensionConvention.Complete(context); } } } diff --git a/src/HotChocolate/Data/src/Data/Filters/Visitor/FilterProvider.cs b/src/HotChocolate/Data/src/Data/Filters/Visitor/FilterProvider.cs index 7f8ee8269d3..e09df25721f 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Visitor/FilterProvider.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Visitor/FilterProvider.cs @@ -60,12 +60,12 @@ protected override FilterProviderDefinition CreateDefinition(IConventionContext base.Initialize(context); } - void IFilterProviderConvention.OnComplete(IConventionContext context) + void IFilterProviderConvention.Complete(IConventionContext context) { - OnComplete(context); + Complete(context); } - protected override void OnComplete(IConventionContext context) + protected override void Complete(IConventionContext context) { if (Definition.Handlers.Count == 0) { diff --git a/src/HotChocolate/Data/src/Data/Filters/Visitor/FilterProviderExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Visitor/FilterProviderExtensions.cs index fe297092799..654f1ce89a9 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Visitor/FilterProviderExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Visitor/FilterProviderExtensions.cs @@ -30,9 +30,9 @@ public FilterProviderExtensions(Action> conf base.Initialize(context); } - void IFilterProviderConvention.OnComplete(IConventionContext context) + void IFilterProviderConvention.Complete(IConventionContext context) { - OnComplete(context); + Complete(context); } protected override FilterProviderDefinition CreateDefinition(IConventionContext context) diff --git a/src/HotChocolate/Data/src/Data/Filters/Visitor/IFilterProviderConvention.cs b/src/HotChocolate/Data/src/Data/Filters/Visitor/IFilterProviderConvention.cs index 210f0ddbdd7..8cd94513abd 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Visitor/IFilterProviderConvention.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Visitor/IFilterProviderConvention.cs @@ -6,6 +6,6 @@ internal interface IFilterProviderConvention { internal void Initialize(IConventionContext context, IFilterConvention convention); - internal void OnComplete(IConventionContext context); + internal void Complete(IConventionContext context); } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionConventionDescriptor.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionConventionDescriptor.cs index 503c62d119a..bb50ee6efc8 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionConventionDescriptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionConventionDescriptor.cs @@ -24,5 +24,22 @@ IProjectionConventionDescriptor Provider(TProvider provider) /// /// The projection provider type. IProjectionConventionDescriptor Provider(Type provider); + + /// + /// Add a extensions that is applied to + /// + /// The filter provider extension type. + IProjectionConventionDescriptor AddProviderExtension() + where TExtension : class, IProjectionProviderExtension; + + /// + /// Add a extensions that is applied to + /// + /// + /// The concrete filter provider extension that shall be used. + /// + /// The filter provider extension type. + IProjectionConventionDescriptor AddProviderExtension(TExtension provider) + where TExtension : class, IProjectionProviderExtension; } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionProviderConvention.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionProviderConvention.cs index d3f79f64717..03b1e72babd 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionProviderConvention.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionProviderConvention.cs @@ -6,6 +6,6 @@ internal interface IProjectionProviderConvention { internal void Initialize(IConventionContext context); - internal void OnComplete(IConventionContext context); + internal void Complete(IConventionContext context); } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionProviderExtension.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionProviderExtension.cs new file mode 100644 index 00000000000..1e1e3ee701b --- /dev/null +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionProviderExtension.cs @@ -0,0 +1,9 @@ +using HotChocolate.Types.Descriptors; + +namespace HotChocolate.Data.Projections +{ + public interface IProjectionProviderExtension + : IConventionExtension + { + } +} diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConvention.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConvention.cs index 6f786dd0ae4..fc38cf56606 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConvention.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConvention.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using HotChocolate.Execution.Processing; using HotChocolate.Resolvers; using HotChocolate.Types.Descriptors; @@ -28,6 +29,8 @@ public ProjectionConvention(Action configure) throw new ArgumentNullException(nameof(configure)); } + internal new ProjectionConventionDefinition? Definition => base.Definition; + protected override ProjectionConventionDefinition CreateDefinition( IConventionContext context) { @@ -51,7 +54,7 @@ protected virtual void Configure(IProjectionConventionDescriptor descriptor) { } - protected override void OnComplete(IConventionContext context) + protected override void Complete(IConventionContext context) { if (Definition.Provider is null) { @@ -62,7 +65,7 @@ protected override void OnComplete(IConventionContext context) { _provider = context.Services.GetOrCreateService(Definition.Provider) ?? - throw ProjectionConvention_NoProviderFound(GetType(), Definition.Scope); + throw ProjectionConvention_NoProviderFound(GetType(), Definition.Scope); } else { @@ -71,8 +74,11 @@ protected override void OnComplete(IConventionContext context) if (_provider is IProjectionProviderConvention init) { + IReadOnlyList extensions = + CollectExtensions(context.Services, Definition); init.Initialize(context); - init.OnComplete(context); + MergeExtensions(context, init, extensions); + init.Complete(context); } } @@ -81,5 +87,44 @@ protected override void OnComplete(IConventionContext context) public ISelectionOptimizer CreateOptimizer() => new ProjectionOptimizer(_provider); + + private static IReadOnlyList CollectExtensions( + IServiceProvider serviceProvider, + ProjectionConventionDefinition definition) + { + List extensions = new List(); + extensions.AddRange(definition.ProviderExtensions); + foreach (var extensionType in definition.ProviderExtensionsTypes) + { + if (serviceProvider.TryGetOrCreateService( + extensionType, + out var createdExtension)) + { + extensions.Add(createdExtension); + } + } + + return extensions; + } + + private static void MergeExtensions( + IConventionContext context, + IProjectionProviderConvention provider, + IReadOnlyList extensions) + { + if (provider is Convention providerConvention) + { + for (var m = 0; m < extensions.Count; m++) + { + if (extensions[m] is IProjectionProviderConvention extensionConvention) + { + extensionConvention.Initialize(context); + extensions[m].Merge(context, providerConvention); + extensionConvention.Complete(context); + } + } + } + } } } + diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionDefinition.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionDefinition.cs index d7cc644af08..4c5b9c03531 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionDefinition.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionDefinition.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using HotChocolate.Types; namespace HotChocolate.Data.Projections @@ -10,5 +11,10 @@ public class ProjectionConventionDefinition : IHasScope public Type? Provider { get; set; } public IProjectionProvider? ProviderInstance { get; set; } + + public List ProviderExtensions { get; } = + new List(); + + public List ProviderExtensionsTypes { get; } = new List(); } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionDescriptor.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionDescriptor.cs index eccb42f8ef8..69f3e6b311b 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionDescriptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionDescriptor.cs @@ -57,6 +57,22 @@ public IProjectionConventionDescriptor Provider(Type provider) return this; } + /// + public IProjectionConventionDescriptor AddProviderExtension() + where TExtension : class, IProjectionProviderExtension + { + Definition.ProviderExtensionsTypes.Add(typeof(TExtension)); + return this; + } + + /// + public IProjectionConventionDescriptor AddProviderExtension(TExtension provider) + where TExtension : class, IProjectionProviderExtension + { + Definition.ProviderExtensions.Add(provider); + return this; + } + /// /// Creates a new descriptor for /// diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionExtension.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionExtension.cs new file mode 100644 index 00000000000..706425669ab --- /dev/null +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConventionExtension.cs @@ -0,0 +1,75 @@ +using System; +using HotChocolate.Types.Descriptors; + +namespace HotChocolate.Data.Projections +{ + public class ProjectionConventionExtension + : ConventionExtension + { + private Action? _configure; + private IProjectionProvider _provider; + + protected ProjectionConventionExtension() + { + _configure = Configure; + } + + public ProjectionConventionExtension(Action configure) + { + _configure = configure ?? + throw new ArgumentNullException(nameof(configure)); + } + + protected override ProjectionConventionDefinition CreateDefinition( + IConventionContext context) + { + if (_configure is null) + { + throw new InvalidOperationException( + DataResources.ProjectionConvention_NoConfigurationSpecified); + } + + var descriptor = ProjectionConventionDescriptor.New( + context.DescriptorContext, + context.Scope); + + _configure(descriptor); + _configure = null; + + return descriptor.CreateDefinition(); + } + + protected internal new void Initialize(IConventionContext context) + { + base.Initialize(context); + } + + protected virtual void Configure(IProjectionConventionDescriptor descriptor) + { + } + + public override void Merge(IConventionContext context, Convention convention) + { + if (convention is ProjectionConvention projectionConvention && + Definition is not null && + projectionConvention.Definition is not null) + { + projectionConvention.Definition.ProviderExtensions.AddRange( + Definition.ProviderExtensions); + + projectionConvention.Definition.ProviderExtensionsTypes.AddRange( + Definition.ProviderExtensionsTypes); + + if (Definition.Provider is not null) + { + projectionConvention.Definition.Provider = Definition.Provider; + } + + if (Definition.ProviderInstance is not null) + { + projectionConvention.Definition.ProviderInstance = Definition.ProviderInstance; + } + } + } + } +} diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProvider.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProvider.cs index 8df01b843b2..345259cd678 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProvider.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProvider.cs @@ -41,6 +41,8 @@ public ProjectionProvider(Action configure) throw new ArgumentNullException(nameof(configure)); } + internal new ProjectionProviderDefinition? Definition => base.Definition; + protected override ProjectionProviderDefinition CreateDefinition( IConventionContext context) { @@ -63,12 +65,12 @@ protected virtual void Configure(IProjectionProviderDescriptor descriptor) { } - void IProjectionProviderConvention.OnComplete(IConventionContext context) + void IProjectionProviderConvention.Complete(IConventionContext context) { - OnComplete(context); + Complete(context); } - protected override void OnComplete(IConventionContext context) + protected override void Complete(IConventionContext context) { if (Definition.Handlers.Count == 0) { diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProviderExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProviderExtensions.cs new file mode 100644 index 00000000000..a197065428a --- /dev/null +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProviderExtensions.cs @@ -0,0 +1,70 @@ +using System; +using HotChocolate.Types.Descriptors; + +namespace HotChocolate.Data.Projections +{ + public abstract class ProjectionProviderExtensions + : ConventionExtension, + IProjectionProviderExtension, + IProjectionProviderConvention + { + private Action? _configure; + + protected ProjectionProviderExtensions() + { + _configure = Configure; + } + + public ProjectionProviderExtensions(Action configure) + { + _configure = configure ?? + throw new ArgumentNullException(nameof(configure)); + } + + void IProjectionProviderConvention.Initialize(IConventionContext context) + { + base.Initialize(context); + } + + void IProjectionProviderConvention.Complete(IConventionContext context) + { + Complete(context); + } + + protected override ProjectionProviderDefinition CreateDefinition(IConventionContext context) + { + if (_configure is null) + { + throw new InvalidOperationException( + DataResources.ProjectionProvider_NoConfigurationSpecified); + } + + var descriptor = ProjectionProviderDescriptor.New( + context.DescriptorContext, + context.Scope); + + _configure(descriptor); + _configure = null; + + return descriptor.CreateDefinition(); + } + + protected virtual void Configure(IProjectionProviderDescriptor descriptor) { } + + public override void Merge(IConventionContext context, Convention convention) + { + if (Definition is not null && + convention is ProjectionProvider conv && + conv.Definition is {} target) + { + // Provider extensions should be applied by default before the default handlers, as + // the interceptor picks up the first handler. A provider extension should adds more + // specific handlers than the default providers + for (var i = Definition.Handlers.Count - 1; i >= 0; i--) + { + target.Handlers.Insert(0, Definition.Handlers[i]); + } + } + } + } +} diff --git a/src/HotChocolate/Data/src/Data/Sorting/Convention/SortConvention.cs b/src/HotChocolate/Data/src/Data/Sorting/Convention/SortConvention.cs index 280583aa825..6947a2c56da 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Convention/SortConvention.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Convention/SortConvention.cs @@ -75,7 +75,7 @@ protected virtual void Configure(ISortConventionDescriptor descriptor) { } - protected override void OnComplete(IConventionContext context) + protected override void Complete(IConventionContext context) { if (Definition?.Provider is null) { @@ -119,14 +119,14 @@ protected override void OnComplete(IConventionContext context) CollectExtensions(context.Services, Definition); init.Initialize(context); MergeExtensions(context, init, extensions); - init.OnComplete(context); + init.Complete(context); } _typeInspector = context.DescriptorContext.TypeInspector; // It is important to always call base to continue the cleanup and the disposal of the // definition - base.OnComplete(context); + base.Complete(context); } @@ -336,7 +336,7 @@ public NameString GetOperationName(int operation) { extensionConvention.Initialize(context); extensions[m].Merge(context, providerConvention); - extensionConvention.OnComplete(context); + extensionConvention.Complete(context); } } } diff --git a/src/HotChocolate/Data/src/Data/Sorting/Visitor/ISortProviderConvention.cs b/src/HotChocolate/Data/src/Data/Sorting/Visitor/ISortProviderConvention.cs index 8c2d90df2b7..3f352a76c6a 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Visitor/ISortProviderConvention.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Visitor/ISortProviderConvention.cs @@ -6,6 +6,6 @@ internal interface ISortProviderConvention { internal void Initialize(IConventionContext context); - internal void OnComplete(IConventionContext context); + internal void Complete(IConventionContext context); } } diff --git a/src/HotChocolate/Data/src/Data/Sorting/Visitor/SortProvider.cs b/src/HotChocolate/Data/src/Data/Sorting/Visitor/SortProvider.cs index 1beb1b49a71..fb233a42a99 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Visitor/SortProvider.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Visitor/SortProvider.cs @@ -66,12 +66,12 @@ void ISortProviderConvention.Initialize(IConventionContext context) base.Initialize(context); } - void ISortProviderConvention.OnComplete(IConventionContext context) + void ISortProviderConvention.Complete(IConventionContext context) { - OnComplete(context); + Complete(context); } - protected override void OnComplete(IConventionContext context) + protected override void Complete(IConventionContext context) { if (Definition?.Handlers.Count == 0) { diff --git a/src/HotChocolate/Data/src/Data/Sorting/Visitor/SortProviderExtensions.cs b/src/HotChocolate/Data/src/Data/Sorting/Visitor/SortProviderExtensions.cs index 9eecef0ca7b..1ab07cdf64f 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Visitor/SortProviderExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Visitor/SortProviderExtensions.cs @@ -28,9 +28,9 @@ void ISortProviderConvention.Initialize(IConventionContext context) base.Initialize(context); } - void ISortProviderConvention.OnComplete(IConventionContext context) + void ISortProviderConvention.Complete(IConventionContext context) { - OnComplete(context); + Complete(context); } protected override SortProviderDefinition CreateDefinition(IConventionContext context) diff --git a/src/HotChocolate/Data/test/Data.Filters.Tests/Convention/FilterConventionTests.cs b/src/HotChocolate/Data/test/Data.Filters.Tests/Convention/FilterConventionTests.cs index ec0317761ca..a379f2ee23f 100644 --- a/src/HotChocolate/Data/test/Data.Filters.Tests/Convention/FilterConventionTests.cs +++ b/src/HotChocolate/Data/test/Data.Filters.Tests/Convention/FilterConventionTests.cs @@ -366,7 +366,7 @@ public void FilterProvider_Throws_Exception_When_NotInitializedByConvention() // assert SchemaException exception = - Assert.Throws(() => provider.OnComplete(context)); + Assert.Throws(() => provider.Complete(context)); exception.Message.MatchSnapshot(); } diff --git a/src/HotChocolate/Data/test/Data.Projections.Tests/HotChocolate.Data.Projections.Tests.csproj b/src/HotChocolate/Data/test/Data.Projections.Tests/HotChocolate.Data.Projections.Tests.csproj new file mode 100644 index 00000000000..f48a9117d8f --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.Tests/HotChocolate.Data.Projections.Tests.csproj @@ -0,0 +1,17 @@ + + + + HotChocolate.Data.Projections.Tests + HotChocolate.Data + net5.0 + + + + + + + + + + + diff --git a/src/HotChocolate/Data/test/Data.Projections.Tests/ProjectionConventionExtensionsTests.cs b/src/HotChocolate/Data/test/Data.Projections.Tests/ProjectionConventionExtensionsTests.cs new file mode 100644 index 00000000000..62f7a342826 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.Tests/ProjectionConventionExtensionsTests.cs @@ -0,0 +1,145 @@ +using System; +using HotChocolate.Data.Projections; +using HotChocolate.Data.Projections.Expressions; +using HotChocolate.Execution.Processing; +using HotChocolate.Resolvers; +using HotChocolate.Types.Descriptors; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace HotChocolate.Data +{ + public class ProjectionConventionExtensionsTests + { + [Fact] + public void Merge_Should_Merge_Provider() + { + // arrange + var convention = + new MockProjectionConvention(x => x.Provider()); + var extension = new ProjectionConventionExtension(x => x.Provider()); + var context = new ConventionContext( + "Scope", + new ServiceCollection().BuildServiceProvider(), + DescriptorContext.Create()); + + convention.Initialize(context); + extension.Initialize(context); + + // act + extension.Merge(context, convention); + + // assert + Assert.Equal(typeof(MockProvider), convention.DefinitionAccessor?.Provider); + } + + [Fact] + public void Merge_Should_Merge_ProviderInstance() + { + // arrange + var providerInstance = new MockProvider(); + var convention = new MockProjectionConvention( + x => x.Provider(new QueryableProjectionProvider())); + var extension = new ProjectionConventionExtension( + x => x.Provider(providerInstance)); + var context = new ConventionContext( + "Scope", + new ServiceCollection().BuildServiceProvider(), + DescriptorContext.Create()); + + convention.Initialize(context); + extension.Initialize(context); + + // act + extension.Merge(context, convention); + + // assert + Assert.Equal(providerInstance, convention.DefinitionAccessor?.ProviderInstance); + } + + [Fact] + public void Merge_Should_Merge_ProviderExtensionsTypes() + { + // arrange + var convention = + new MockProjectionConvention(x => x.AddProviderExtension()); + var extension = + new ProjectionConventionExtension( + x => x.AddProviderExtension()); + var context = new ConventionContext( + "Scope", + new ServiceCollection().BuildServiceProvider(), + DescriptorContext.Create()); + + convention.Initialize(context); + extension.Initialize(context); + + // act + extension.Merge(context, convention); + + // assert + Assert.NotNull(convention.DefinitionAccessor); + Assert.Equal(2, convention.DefinitionAccessor!.ProviderExtensionsTypes.Count); + } + + [Fact] + public void Merge_Should_Merge_ProviderExtensions() + { + // arrange + var provider1 = new MockProviderExtensions(); + var convention = new MockProjectionConvention(x => x.AddProviderExtension(provider1)); + var provider2 = new MockProviderExtensions(); + var extension = + new ProjectionConventionExtension(x => x.AddProviderExtension(provider2)); + var context = new ConventionContext( + "Scope", + new ServiceCollection().BuildServiceProvider(), + DescriptorContext.Create()); + + convention.Initialize(context); + extension.Initialize(context); + + // act + extension.Merge(context, convention); + + // assert + Assert.NotNull(convention.DefinitionAccessor); + Assert.Collection( + convention.DefinitionAccessor!.ProviderExtensions, + x => Assert.Equal(provider1, x), + x => Assert.Equal(provider2, x)); + } + + private class MockProviderExtensions : ProjectionProviderExtensions + { + } + + private class MockProvider : IProjectionProvider + { + public string? Scope { get; } + + public FieldMiddleware CreateExecutor() + { + throw new NotImplementedException(); + } + + public Selection RewriteSelection( + SelectionOptimizerContext context, + Selection selection) + { + throw new NotImplementedException(); + } + } + + private class MockProjectionConvention : ProjectionConvention + { + public MockProjectionConvention( + Action configure) + : base(configure) + { + } + + public ProjectionConventionDefinition? DefinitionAccessor => base.Definition; + } + } +}