From 47745dab35c0e6c09612477c5eb60ef75e6f8be6 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Fri, 22 Apr 2022 19:34:02 -0400 Subject: [PATCH] Update for GraphQL.NET v5 (#17) --- Sample/RequiredAttribute.cs | 9 + Sample/Sample.csproj | 8 +- Sample/Startup.cs | 40 +- src/GraphQL.DI/AutoInputObjectGraphType.cs | 122 ---- src/GraphQL.DI/AutoObjectGraphType.cs | 239 ------ src/GraphQL.DI/ConcurrentAttribute.cs | 33 - src/GraphQL.DI/DIDocumentExecuter.cs | 93 --- src/GraphQL.DI/DIExecutionStrategy.cs | 138 ---- src/GraphQL.DI/DIFieldType.cs | 13 - src/GraphQL.DI/DIGraphAttribute.cs | 21 +- src/GraphQL.DI/DIGraphTypeMappingProvider.cs | 19 + src/GraphQL.DI/DIObjectGraphBase.cs | 16 +- src/GraphQL.DI/DIObjectGraphType.cs | 270 ++----- src/GraphQL.DI/DISchemaTypes.cs | 65 -- src/GraphQL.DI/FromServicesAttribute.cs | 14 - src/GraphQL.DI/FromSourceAttribute.cs | 14 - src/GraphQL.DI/GraphQLBuilderExtensions.cs | 94 +-- src/GraphQL.DI/GraphTypeAttribute.cs | 25 - src/GraphQL.DI/GraphTypeHelper.cs | 557 -------------- src/GraphQL.DI/IdAttribute.cs | 20 - src/GraphQL.DI/IgnoreAttribute.cs | 18 - src/GraphQL.DI/MetadataAttribute.cs | 28 - src/GraphQL.DI/NameAttribute.cs | 24 - src/GraphQL.DI/Nullability.cs | 21 - .../NullabilityInterpretationException.cs | 25 - src/GraphQL.DI/OptionalAttribute.cs | 14 - src/GraphQL.DI/OptionalListAttribute.cs | 14 - src/GraphQL.DI/PropertyResolver.cs | 18 - src/GraphQL.DI/ReflectionExtensions.cs | 165 ----- src/GraphQL.DI/RequiredAttribute.cs | 14 - src/GraphQL.DI/RequiredListAttribute.cs | 14 - src/GraphQL.DI/Shane32.GraphQL.DI.csproj | 4 +- src/GraphQL.DI/TypeInformation.cs | 156 ---- .../AutoInputObjectGraphTypeTests/Graph.cs | 93 --- .../AutoInputObjectGraphTypeTests/Property.cs | 180 ----- .../AutoObjectGraphTypeTests/Argument.cs | 235 ------ .../AutoObjectGraphTypeTestBase.cs | 140 ---- src/Tests/AutoObjectGraphTypeTests/Field.cs | 683 ------------------ src/Tests/AutoObjectGraphTypeTests/Graph.cs | 76 -- .../AutoObjectGraphTypeTests/Property.cs | 179 ----- .../AutoObjectGraphTypeTests/Services.cs | 51 -- src/Tests/DIObjectGraphBaseTests.cs | 27 +- src/Tests/DIObjectGraphTypeTests/Argument.cs | 65 +- .../DIObjectGraphTypeTestBase.cs | 8 +- src/Tests/DIObjectGraphTypeTests/Field.cs | 270 ++----- src/Tests/DIObjectGraphTypeTests/Graph.cs | 15 +- src/Tests/DIObjectGraphTypeTests/Services.cs | 5 +- src/Tests/Execution/DIDocumentExecuter.cs | 115 --- src/Tests/Execution/DIDocumentStrategy.cs | 94 --- src/Tests/Execution/DISchemaTypes.cs | 107 --- src/Tests/Execution/GraphQLBuilderTests.cs | 65 +- src/Tests/NullableTests/ArgumentTests.cs | 99 --- src/Tests/NullableTests/MethodTests.cs | 252 ------- .../NullableTests/NullabilityTestClasses.cs | 200 ----- src/Tests/NullableTests/PropertyTests.cs | 61 -- src/Tests/NullableTests/TestGraphType.cs | 21 - src/Tests/NullableTests/VerifyTestClasses.cs | 234 ------ src/Tests/Tests.csproj | 4 +- 58 files changed, 253 insertions(+), 5351 deletions(-) create mode 100644 Sample/RequiredAttribute.cs delete mode 100644 src/GraphQL.DI/AutoInputObjectGraphType.cs delete mode 100644 src/GraphQL.DI/AutoObjectGraphType.cs delete mode 100644 src/GraphQL.DI/ConcurrentAttribute.cs delete mode 100644 src/GraphQL.DI/DIDocumentExecuter.cs delete mode 100644 src/GraphQL.DI/DIExecutionStrategy.cs delete mode 100644 src/GraphQL.DI/DIFieldType.cs create mode 100644 src/GraphQL.DI/DIGraphTypeMappingProvider.cs delete mode 100644 src/GraphQL.DI/DISchemaTypes.cs delete mode 100644 src/GraphQL.DI/FromServicesAttribute.cs delete mode 100644 src/GraphQL.DI/FromSourceAttribute.cs delete mode 100644 src/GraphQL.DI/GraphTypeAttribute.cs delete mode 100644 src/GraphQL.DI/GraphTypeHelper.cs delete mode 100644 src/GraphQL.DI/IdAttribute.cs delete mode 100644 src/GraphQL.DI/IgnoreAttribute.cs delete mode 100644 src/GraphQL.DI/MetadataAttribute.cs delete mode 100644 src/GraphQL.DI/NameAttribute.cs delete mode 100644 src/GraphQL.DI/Nullability.cs delete mode 100644 src/GraphQL.DI/NullabilityInterpretationException.cs delete mode 100644 src/GraphQL.DI/OptionalAttribute.cs delete mode 100644 src/GraphQL.DI/OptionalListAttribute.cs delete mode 100644 src/GraphQL.DI/PropertyResolver.cs delete mode 100644 src/GraphQL.DI/ReflectionExtensions.cs delete mode 100644 src/GraphQL.DI/RequiredAttribute.cs delete mode 100644 src/GraphQL.DI/RequiredListAttribute.cs delete mode 100644 src/GraphQL.DI/TypeInformation.cs delete mode 100644 src/Tests/AutoInputObjectGraphTypeTests/Graph.cs delete mode 100644 src/Tests/AutoInputObjectGraphTypeTests/Property.cs delete mode 100644 src/Tests/AutoObjectGraphTypeTests/Argument.cs delete mode 100644 src/Tests/AutoObjectGraphTypeTests/AutoObjectGraphTypeTestBase.cs delete mode 100644 src/Tests/AutoObjectGraphTypeTests/Field.cs delete mode 100644 src/Tests/AutoObjectGraphTypeTests/Graph.cs delete mode 100644 src/Tests/AutoObjectGraphTypeTests/Property.cs delete mode 100644 src/Tests/AutoObjectGraphTypeTests/Services.cs delete mode 100644 src/Tests/Execution/DIDocumentExecuter.cs delete mode 100644 src/Tests/Execution/DIDocumentStrategy.cs delete mode 100644 src/Tests/Execution/DISchemaTypes.cs delete mode 100644 src/Tests/NullableTests/ArgumentTests.cs delete mode 100644 src/Tests/NullableTests/MethodTests.cs delete mode 100644 src/Tests/NullableTests/NullabilityTestClasses.cs delete mode 100644 src/Tests/NullableTests/PropertyTests.cs delete mode 100644 src/Tests/NullableTests/TestGraphType.cs delete mode 100644 src/Tests/NullableTests/VerifyTestClasses.cs diff --git a/Sample/RequiredAttribute.cs b/Sample/RequiredAttribute.cs new file mode 100644 index 0000000..923dc88 --- /dev/null +++ b/Sample/RequiredAttribute.cs @@ -0,0 +1,9 @@ +using GraphQL.Types; + +namespace Sample +{ + public class RequiredAttribute : GraphQL.GraphQLAttribute + { + public override void Modify(TypeInformation typeInformation) => typeInformation.IsNullable = false; + } +} diff --git a/Sample/Sample.csproj b/Sample/Sample.csproj index 9790a9e..ca842a7 100644 --- a/Sample/Sample.csproj +++ b/Sample/Sample.csproj @@ -10,9 +10,11 @@ - - - + + + + + diff --git a/Sample/Startup.cs b/Sample/Startup.cs index 38d0db4..367a15c 100644 --- a/Sample/Startup.cs +++ b/Sample/Startup.cs @@ -1,6 +1,10 @@ using EfLocalDb; +using GraphQL; +using GraphQL.AspNetCore3; using GraphQL.DI; +using GraphQL.MicrosoftDI; using GraphQL.Server; +using GraphQL.SystemTextJson; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -29,25 +33,22 @@ public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); - services.AddGraphQL(opts => { - opts.UnhandledExceptionDelegate = e => { - Debug.WriteLine($"Unhandled exception:\n{e.Exception}\n"); - }; - }) + services.AddGraphQL(b => b + .ConfigureExecutionOptions(opts => opts.UnhandledExceptionDelegate = async e => Debug.WriteLine($"Unhandled exception:\n{e.Exception}\n")) .AddSystemTextJson() - .AddGraphTypes(); - services.AddSingleton(); + .AddDIGraphTypes() + .AddGraphTypes()); services.AddSingleton(); - foreach (var type in typeof(TodoSchema).Assembly.GetTypes().Where(x => x.IsClass && !x.IsAbstract && !x.IsGenericTypeDefinition)) { - var baseType = type.BaseType; - while (baseType != null) { - if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(DIObjectGraphBase<>)) { - services.AddScoped(type); - break; - } - baseType = baseType.BaseType; - } - } + //foreach (var type in typeof(TodoSchema).Assembly.GetTypes().Where(x => x.IsClass && !x.IsAbstract && !x.IsGenericTypeDefinition)) { + // var baseType = type.BaseType; + // while (baseType != null) { + // if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(DIObjectGraphBase<>)) { + // services.AddScoped(type); + // break; + // } + // baseType = baseType.BaseType; + // } + //} //construct temporary database with scoped dbcontext instances services.AddSingleton(_ => new SqlInstance(builder => new TodoDbContext(builder.Options))); @@ -76,11 +77,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseAuthorization(); - //GET calls to /graphql - var graphQlPath = new Microsoft.AspNetCore.Http.PathString("/graphql"); - app.MapWhen(context => context.Request.Method == "GET" && context.Request.Path.Equals(graphQlPath, StringComparison.OrdinalIgnoreCase), app2 => app2.UseGraphQLGraphiQL("/graphql")); - //POST calls to /graphql app.UseGraphQL(); + app.UseGraphQLGraphiQL(); app.UseEndpoints(endpoints => { diff --git a/src/GraphQL.DI/AutoInputObjectGraphType.cs b/src/GraphQL.DI/AutoInputObjectGraphType.cs deleted file mode 100644 index a618b34..0000000 --- a/src/GraphQL.DI/AutoInputObjectGraphType.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using GraphQL.Types; - -namespace GraphQL.DI -{ - /// - /// An automatically-generated graph type for public properties on the specified input model. - /// - public class AutoInputObjectGraphType : InputObjectGraphType - { - /// - /// Creates a GraphQL type from . - /// - public AutoInputObjectGraphType() - { - GraphTypeHelper.ConfigureGraph(this, GetDefaultName); - - GraphTypeHelper.AddFields(this, CreateFieldTypeList()); - } - - /// - /// Returns a list of instances representing the fields ready to be - /// added to the graph type. - /// Sorts the list if specified by . - /// - protected virtual IEnumerable CreateFieldTypeList() - { - //scan for public members - var properties = GetPropertiesToProcess(); - var fieldTypeList = new List(properties.Count()); - foreach (var property in properties) { - var fieldType = ProcessProperty(property); - if (fieldType != null) - fieldTypeList.Add(fieldType); - } - if (SortMembers) - return fieldTypeList.OrderBy(x => x.Name, StringComparer.InvariantCultureIgnoreCase); - return fieldTypeList; - } - - /// - /// Returns the default name assigned to the graph, or to leave the default setting set by the constructor. - /// - protected virtual string? GetDefaultName() - { - //if this class is inherited, do not set default name - if (GetType() != typeof(AutoInputObjectGraphType)) - return null; - - //without this code, the name would default to AutoInputObjectGraphType_1 - var name = typeof(TSourceType).Name.Replace('`', '_'); - if (name.EndsWith("Model", StringComparison.InvariantCulture)) - name = name.Substring(0, name.Length - "Model".Length); - return name; - } - - /// - /// Indicates that the fields and arguments should be added to the graph type alphabetically. - /// - public static bool SortMembers { get; set; } = true; - - /// - /// Returns a list of properties that should have fields created for them. - /// - protected virtual IEnumerable GetPropertiesToProcess() - { - var props = typeof(TSourceType).GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(x => x.CanWrite); - return props; - } - - /// - /// Processes the specified property and returns a - /// - /// - /// - protected virtual FieldType? ProcessProperty(PropertyInfo property) - { - var fieldType = GraphTypeHelper.CreateField( - property, - () => InferGraphType(ApplyAttributes(GetTypeInformation(property)))); - if (fieldType == null) - return null; - //load the default value - fieldType.DefaultValue = property.GetCustomAttribute()?.Value; - //return the field - return fieldType; - } - - /// - protected virtual IEnumerable<(Type Type, Nullability Nullable)> GetNullabilityInformation(PropertyInfo parameter) - => parameter.GetNullabilityInformation(); - - /// - /// Analyzes a property and returns a - /// struct containing type information necessary to select a graph type. - /// - protected virtual TypeInformation GetTypeInformation(PropertyInfo propertyInfo) - => GraphTypeHelper.GetTypeInformation(propertyInfo, true, GetNullabilityInformation); - - /// - /// Apply , , , - /// , and over - /// the supplied . - /// Override this method to enforce specific graph types for specific CLR types, or to implement custom - /// attributes to change graph type selection behavior. - /// - protected virtual TypeInformation ApplyAttributes(TypeInformation typeInformation) - => typeInformation.ApplyAttributes(typeInformation.MemberInfo); - - /// - /// Returns a GraphQL input type for a specified CLR type - /// - protected virtual Type InferGraphType(TypeInformation typeInformation) - => typeInformation.InferGraphType(); - - } -} diff --git a/src/GraphQL.DI/AutoObjectGraphType.cs b/src/GraphQL.DI/AutoObjectGraphType.cs deleted file mode 100644 index 3ee2e2b..0000000 --- a/src/GraphQL.DI/AutoObjectGraphType.cs +++ /dev/null @@ -1,239 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using GraphQL.Resolvers; -using GraphQL.Types; - -namespace GraphQL.DI -{ - /// - /// An automatically-generated graph type for public properties and methods on the specified data model. - /// - public class AutoObjectGraphType : ObjectGraphType - { - /// - /// Gets or sets whether fields added to this graph type will default to running concurrently. - /// - protected bool DefaultConcurrent { get; set; } = false; - - /// - /// Gets or sets whether concurrent fields added to this graph type will default to running in a dedicated service scope. - /// - protected bool DefaultCreateScope { get; set; } = false; - - /// - /// Creates a GraphQL type from . - /// - public AutoObjectGraphType() - { - GraphTypeHelper.ConfigureGraph(this, GetDefaultName); - - //check if there is a default concurrency setting - var concurrentAttribute = typeof(TSourceType).GetCustomAttribute(); - if (concurrentAttribute != null) { - DefaultConcurrent = true; - DefaultCreateScope = concurrentAttribute.CreateNewScope; - } - - GraphTypeHelper.AddFields(this, CreateFieldTypeList()); - } - - /// - /// Returns a list of instances representing the fields ready to be - /// added to the graph type. - /// Sorts the list if specified by . - /// - protected virtual IEnumerable CreateFieldTypeList() - { - //scan for public members - var properties = GetPropertiesToProcess(); - var methods = GetMethodsToProcess(); - var fieldTypeList = new List(properties.Count() + methods.Count()); - foreach (var property in properties) { - var fieldType = ProcessProperty(property); - if (fieldType != null) - fieldTypeList.Add(fieldType); - } - foreach (var method in methods) { - var fieldType = ProcessMethod(method); - if (fieldType != null) - fieldTypeList.Add(fieldType); - } - if (SortMembers) - return fieldTypeList.OrderBy(x => x.Name, StringComparer.InvariantCultureIgnoreCase); - return fieldTypeList; - } - - /// - /// Returns the default name assigned to the graph, or to leave the default setting set by the constructor. - /// - protected virtual string? GetDefaultName() - { - //if this class is inherited, do not set default name - if (GetType() != typeof(AutoObjectGraphType)) - return null; - - //without this code, the name would default to AutoObjectGraphType_1 - var name = typeof(TSourceType).Name.Replace('`', '_'); - if (name.EndsWith("Model", StringComparison.InvariantCulture)) - name = name.Substring(0, name.Length - "Model".Length); - return name; - } - - /// - /// Indicates that the fields should be added to the graph type alphabetically. - /// - public static bool SortMembers { get; set; } = true; - - /// - /// Returns a list of properties that should have fields created for them. - /// - protected virtual IEnumerable GetPropertiesToProcess() - { - var props = typeof(TSourceType).GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(x => x.CanRead); - return props; - } - - /// - /// Returns a list of methods ( instances) on the class to be - /// converted into field definitions. - /// - protected virtual IEnumerable GetMethodsToProcess() - { - var methods = typeof(TSourceType).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly) - .Where(x => !x.ContainsGenericParameters && !x.IsSpecialName); - return methods; - } - - /// - /// Processes the specified property and returns a - /// - /// - /// - protected virtual FieldType? ProcessProperty(PropertyInfo property) - { - var fieldType = GraphTypeHelper.CreateField( - property, - () => InferGraphType(ApplyAttributes(GetTypeInformation(property)))); - if (fieldType == null) - return null; - //create the field resolver - fieldType.Resolver = new PropertyResolver(property); - //return the field - return fieldType; - } - - /// - protected virtual IEnumerable<(Type Type, Nullability Nullable)> GetNullabilityInformation(PropertyInfo parameter) - => parameter.GetNullabilityInformation(); - - /// - /// Analyzes a property and returns a - /// struct containing type information necessary to select a graph type. - /// - protected virtual TypeInformation GetTypeInformation(PropertyInfo propertyInfo) - => GraphTypeHelper.GetTypeInformation(propertyInfo, false, GetNullabilityInformation); - - /// - /// Apply , , , - /// , and over - /// the supplied . - /// Override this method to enforce specific graph types for specific CLR types, or to implement custom - /// attributes to change graph type selection behavior. - /// - protected virtual TypeInformation ApplyAttributes(TypeInformation typeInformation) - => typeInformation.ApplyAttributes(typeInformation.MemberInfo is PropertyInfo propertyInfo ? propertyInfo : typeInformation.IsInputType ? (ICustomAttributeProvider)typeInformation.ParameterInfo! : typeInformation.MemberInfo); - - /// - /// Returns a GraphQL input type for a specified CLR type - /// - protected virtual Type InferGraphType(TypeInformation typeInformation) - => typeInformation.InferGraphType(); - - /// - /// Converts a specified method ( instance) into a field definition. - /// - protected virtual DIFieldType? ProcessMethod(MethodInfo method) - { - var field = GraphTypeHelper.CreateField( - method, - () => InferGraphType(ApplyAttributes(GetTypeInformation(method.ReturnParameter, false)))); - if (field == null) - return null; - - GraphTypeHelper.ProcessMethod( - method, - field, - (method, param, resolveFieldContextParameter) => { - var queryArgument = ProcessParameter(method, param, resolveFieldContextParameter, out var usesServices, out var expr); - return (queryArgument, usesServices, expr); - }, - resolveFieldContextParameter => GraphTypeHelper.GetSourceExpression(resolveFieldContextParameter, typeof(TSourceType)), - CreateUnscopedResolver, - CreateScopedResolver, - DefaultConcurrent, - DefaultCreateScope); - - return field; - } - - /// - /// Analyzes a method argument or return value and returns a - /// struct containing type information necessary to select a graph type. - /// - protected virtual TypeInformation GetTypeInformation(ParameterInfo parameterInfo, bool isInputArgument) - => GraphTypeHelper.GetTypeInformation(parameterInfo, isInputArgument, GetNullabilityInformation); - - /// - protected virtual IEnumerable<(Type Type, Nullability Nullable)> GetNullabilityInformation(ParameterInfo parameter) - => parameter.GetNullabilityInformation(); - - /// - /// Processes a parameter of a method, returning an expression based upon , and - /// optionally returns a query argument to be added to the field. can be set to - /// to indicate that the parameter uses the service provider and may need to run within - /// a scoped service provider for concurrent use. - /// - protected virtual QueryArgument? ProcessParameter(MethodInfo method, ParameterInfo param, ParameterExpression resolveFieldContextParameter, out bool usesServices, out Expression expr) - => GraphTypeHelper.ProcessParameter( - method, - param, - resolveFieldContextParameter, - GetServiceProviderExpression, - GetServiceExpression, - param => InferGraphType(ApplyAttributes(GetTypeInformation(param, true))), - out usesServices, - out expr); - - /// - /// Returns a field resolver for a specified delegate expression. Does not create a dedicated service scope for the delegate. - /// - protected virtual IFieldResolver CreateUnscopedResolver(Expression resolveExpression, ParameterExpression resolveFieldContextParameter) - => GraphTypeHelper.CreateUnscopedResolver(resolveExpression, resolveFieldContextParameter); - - /// - /// Returns a field resolver for a specified delegate expression. The field resolver creates a dedicated service scope for the delegate. - /// - protected virtual IFieldResolver CreateScopedResolver(Expression resolveExpression, ParameterExpression resolveFieldContextParameter) - => GraphTypeHelper.CreateScopedResolver(resolveExpression, resolveFieldContextParameter); - - /// - /// Returns an expression that returns an from a . - /// Defaults to the following: - /// context => context.RequestServices. - /// - protected virtual Expression GetServiceProviderExpression(ParameterExpression resolveFieldContextParameter) - => GraphTypeHelper.GetServiceProviderExpression(resolveFieldContextParameter); - - /// - /// Returns an expression that gets/creates an instance of the specified type from a . - /// Defaults to the following: - /// context => context.RequestServices.GetRequiredService<T>(); - /// - protected virtual Expression GetServiceExpression(ParameterExpression resolveFieldContextParameter, Type serviceType) - => GraphTypeHelper.GetServiceExpression(resolveFieldContextParameter, serviceType); - } -} diff --git a/src/GraphQL.DI/ConcurrentAttribute.cs b/src/GraphQL.DI/ConcurrentAttribute.cs deleted file mode 100644 index a4bf974..0000000 --- a/src/GraphQL.DI/ConcurrentAttribute.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; - -namespace GraphQL.DI -{ - /// - /// Marks this class or method as being able to run concurrently. May specify if a new service - /// scope is required. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public sealed class ConcurrentAttribute : Attribute - { - /// - /// Marks this class or method as being able to run concurrently without a dedicated service scope. - /// - public ConcurrentAttribute() { } - - /// - /// Marks this class or method as being able to run concurrently, with or without a dedicated service scope. - /// - public ConcurrentAttribute(bool createNewScope) { CreateNewScope = createNewScope; } - - /// - /// Indicates if this class or method is able to run concurrently. - /// - public bool Concurrent { get; set; } = true; - - /// - /// Indicates if this class or method requires a dedicated service scope to run concurrently. When set - /// for a class, a new service scope will be created for each method called. - /// - public bool CreateNewScope { get; set; } = false; - } -} diff --git a/src/GraphQL.DI/DIDocumentExecuter.cs b/src/GraphQL.DI/DIDocumentExecuter.cs deleted file mode 100644 index 26bb363..0000000 --- a/src/GraphQL.DI/DIDocumentExecuter.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using GraphQL.Types; -using GraphQL.Execution; -using GraphQL.Language.AST; -using GraphQL.Validation; -using GraphQL.Validation.Complexity; -using Microsoft.Extensions.DependencyInjection; -using System.Threading.Tasks; - -namespace GraphQL.DI -{ - /// - /// Processes an entire GraphQL request, given an input GraphQL request string. This - /// is intended to be called by user code to process a query. Uses a - /// implementation for and . Can be passed - /// a implementation for . - /// - public class DIDocumentExecuter : DocumentExecuter - { - /// - /// The instance of the execution strategy used for and . - /// - protected IExecutionStrategy DIExecutionStrategy = DI.DIExecutionStrategy.Instance; - /// - /// The instance of the execution strategy used for . - /// - protected IExecutionStrategy? SubscriptionExecutionStrategy = null; - - /// - /// Initializes a new instance without support for subscriptions. - /// - public DIDocumentExecuter() : base() - { - } - - /// - /// Initializes a new instance with the specified for . - /// - public DIDocumentExecuter(IExecutionStrategy? subscriptionExecutionStrategy) : base() - { - SubscriptionExecutionStrategy = subscriptionExecutionStrategy; - } - - /// - /// Initializes a new instance using the specified to pull optional - /// references to , and . - /// Does not support subscriptions. - /// - public DIDocumentExecuter( - IServiceProvider serviceProvider) : base( - (serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider))).GetService() ?? new GraphQLDocumentBuilder(), - serviceProvider.GetService() ?? new DocumentValidator(), - serviceProvider.GetService() ?? new ComplexityAnalyzer()) - { - } - - /// - /// Initializes a new instance using the specified to pull optional - /// references to , and . - /// Uses the specified for . - /// - public DIDocumentExecuter(IServiceProvider serviceProvider, IExecutionStrategy? subscriptionExecutionStrategy) : this(serviceProvider) - { - SubscriptionExecutionStrategy = subscriptionExecutionStrategy; - } - - /// - protected override IExecutionStrategy SelectExecutionStrategy(ExecutionContext context) - { - switch (context.Operation.OperationType) - { - case OperationType.Query: - return DIExecutionStrategy; - - case OperationType.Mutation: - return DIExecutionStrategy; - - case OperationType.Subscription: - return SubscriptionExecutionStrategy ?? base.SelectExecutionStrategy(context); - - default: - return base.SelectExecutionStrategy(context); - } - } - - /// - public override Task ExecuteAsync(ExecutionOptions options) { - if (options.RequestServices == null) - throw new ArgumentNullException($"{nameof(options)}.{nameof(ExecutionOptions.RequestServices)}"); - return base.ExecuteAsync(options); - } - } -} diff --git a/src/GraphQL.DI/DIExecutionStrategy.cs b/src/GraphQL.DI/DIExecutionStrategy.cs deleted file mode 100644 index cde3345..0000000 --- a/src/GraphQL.DI/DIExecutionStrategy.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using GraphQL.DataLoader; -using GraphQL.Execution; - -namespace GraphQL.DI -{ - /// - /// An implementation for use with field types. - /// Will respect to execute nodes synchronously or concurrently. - /// Does not call . - /// - public class DIExecutionStrategy : ExecutionStrategy - { - internal static DIExecutionStrategy Instance = new DIExecutionStrategy(); - - /// - protected override async Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode) - { - if (context.RequestServices == null) - throw new ArgumentNullException($"{nameof(context)}.{nameof(ExecutionContext.RequestServices)}"); - - Func> taskFunc = async (task, node) => { await task; return node; }; - - var nodes = new Stack(); //synchronous nodes to be executed - nodes.Push(rootNode); - var asyncNodes = new Stack(); //asynchronous nodes to be executed - var waitingTasks = new List>(); //nodes currently executing - var pendingNodes = new Queue(); //IDelayLoadedResult nodes pending completion - Task? waitingSyncTask = null; - int maxTasks = context.MaxParallelExecutionCount ?? int.MaxValue; - if (maxTasks < 1) - throw new InvalidOperationException("Invalid maximum number of tasks"); - - void DICompleteNode(ExecutionNode node) - { - //if the result of the node is an IDelayLoadedResult, then add this - // node to a list of nodes to be loaded once everything else possible - // has been loaded - if (node.Result is IDataLoaderResult) { - pendingNodes.Enqueue(node); - } else { - // Push any child nodes on top of the stack - if (node is IParentExecutionNode parentNode) { - // Add in reverse order so fields are executed in the correct order (for synchronous tasks) - foreach (var child in parentNode.GetChildNodes().Reverse()) { - //add node to async list or sync list, as appropriate - if (child.FieldDefinition is DIFieldType fieldType && fieldType.Concurrent) { - asyncNodes.Push(child); - } else { - nodes.Push(child); - } - } - } - } - } - - // Process each node in the queue - while (true) { - //start executing all asynchronous nodes - while (asyncNodes.Count > 0 && waitingTasks.Count < maxTasks) { - //grab an asynchronous node to execute - var node = asyncNodes.Pop(); - //execute it (asynchronously) - var task = ExecuteNodeAsync(context, node); - if (task.IsCompleted) { - await task; - DICompleteNode(node); - } else { - var taskWithNode = taskFunc(task, node); - //add this task to the list of tasks waiting to be completed - waitingTasks.Add(taskWithNode); - } - } - - //start executing one synchronous task, if none is yet waiting to be completed - while (nodes.Count > 0 && waitingSyncTask == null && waitingTasks.Count < maxTasks) { - //grab a synchronous node to execute - var node = nodes.Pop(); - //execute it (asynchronously) - var task = ExecuteNodeAsync(context, node); - if (task.IsCompleted) { - await task; - DICompleteNode(node); - } else { - var taskWithNode = taskFunc(task, node); - //notate the synchronous task that is currently executing - waitingSyncTask = taskWithNode; - //add this task to the list of tasks waiting to be completed - waitingTasks.Add(taskWithNode); - } - } - - //complete one or more asynchronously-executing tasks - if (waitingTasks.Count == 1) { - DICompleteNode(await waitingTasks[0]); - waitingTasks.Clear(); - waitingSyncTask = null; - } else if (waitingTasks.Count > 0) { - //wait for at least one task to complete - var completedTask = await Task.WhenAny(waitingTasks).ConfigureAwait(false); - //note: errors are not thrown here, but rather down at task.Result - waitingTasks.Remove(completedTask); - if (waitingSyncTask == completedTask) - waitingSyncTask = null; - - //if the request was canceled, quit out now - context.CancellationToken.ThrowIfCancellationRequested(); - - //process the completed task - DICompleteNode(await completedTask); - } - - //if there's no sync/async nodes being processed or waiting to be processed, - // then load any IDelayLoadedResult values - if (nodes.Count == 0 && asyncNodes.Count == 0 && waitingTasks.Count == 0) { - //if no data loader nodes either, we are all done - if (pendingNodes.Count == 0) - return; - - //must be synchronously, as all DelayLoaders will exist in the same scope - //however, once a single node is resolved, all the rest of the tasks from the same DelayLoader will already be completed - //also, must execute all these nodes at once, otherwise - // a child node might execute and queue a child dataloader node, - // which may try to execute before this 'level' of dataloaders have executed - while (pendingNodes.Count > 0) { - var pendingNode = pendingNodes.Dequeue(); - var task = CompleteDataLoaderNodeAsync(context, pendingNode); - await task.ConfigureAwait(false); //execute synchronously - DICompleteNode(pendingNode); - } - } - } - } - } -} diff --git a/src/GraphQL.DI/DIFieldType.cs b/src/GraphQL.DI/DIFieldType.cs deleted file mode 100644 index 71c0fa8..0000000 --- a/src/GraphQL.DI/DIFieldType.cs +++ /dev/null @@ -1,13 +0,0 @@ -using GraphQL.Types; - -namespace GraphQL.DI -{ - /// - public class DIFieldType : FieldType - { - /// - /// Indicates if the field resolver can run concurrently with other field resolvers. - /// - public bool Concurrent { get; set; } = false; - } -} diff --git a/src/GraphQL.DI/DIGraphAttribute.cs b/src/GraphQL.DI/DIGraphAttribute.cs index 9727b2b..c8003e8 100644 --- a/src/GraphQL.DI/DIGraphAttribute.cs +++ b/src/GraphQL.DI/DIGraphAttribute.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; +using System.Linq; +using GraphQL.Types; namespace GraphQL.DI { @@ -8,8 +8,8 @@ namespace GraphQL.DI /// Marks a method's return graph type to be a specified DI graph type. /// Useful when the return type cannot be inferred (often when it is of type ). /// - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - public class DIGraphAttribute : Attribute + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class DIGraphAttribute : GraphQLAttribute { /// /// Marks a method's return graph type to be a specified DI graph type. @@ -20,6 +20,19 @@ public DIGraphAttribute(Type graphBaseType) GraphBaseType = graphBaseType; } + /// + public override void Modify(TypeInformation typeInformation) + { + if (typeInformation.IsInputType) + return; + var iface = GraphBaseType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDIObjectGraphBase<>)); + if (iface != null) { + typeInformation.GraphType = typeof(DIObjectGraphType<,>).MakeGenericType(GraphBaseType, iface.GetGenericArguments()[0]); + } else { + throw new InvalidOperationException($"Type '{GraphBaseType.Name}' does not implement {nameof(IDIObjectGraphBase)}; check {nameof(DIGraphAttribute)} attribute marked on '{typeInformation.MemberInfo.DeclaringType.Name}.{typeInformation.MemberInfo.Name}'."); + } + } + /// /// The DI graph type that inherits . /// diff --git a/src/GraphQL.DI/DIGraphTypeMappingProvider.cs b/src/GraphQL.DI/DIGraphTypeMappingProvider.cs new file mode 100644 index 0000000..e0195df --- /dev/null +++ b/src/GraphQL.DI/DIGraphTypeMappingProvider.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using GraphQL.Types; + +namespace GraphQL.DI +{ + internal class DIGraphTypeMappingProvider : IGraphTypeMappingProvider + { + private readonly Dictionary _typeDictionary; + + public DIGraphTypeMappingProvider(Dictionary typeDictionary) + { + _typeDictionary = typeDictionary; + } + + public Type? GetGraphTypeFromClrType(Type clrType, bool isInputType, Type? preferredGraphType) + => !isInputType && _typeDictionary.TryGetValue(clrType, out var graphType) ? graphType : preferredGraphType; + } +} diff --git a/src/GraphQL.DI/DIObjectGraphBase.cs b/src/GraphQL.DI/DIObjectGraphBase.cs index cd61d07..cc757bf 100644 --- a/src/GraphQL.DI/DIObjectGraphBase.cs +++ b/src/GraphQL.DI/DIObjectGraphBase.cs @@ -3,8 +3,8 @@ using System.Threading; using GraphQL.Execution; using GraphQL.Instrumentation; -using GraphQL.Language.AST; using GraphQL.Types; +using GraphQLParser.AST; namespace GraphQL.DI { @@ -32,7 +32,7 @@ public abstract class DIObjectGraphBase : IDIObjectGraphBase, public Metrics Metrics => Context.Metrics; IResolveFieldContext IDIObjectGraphBase.Context { get => Context; set => Context = value; } - Field IResolveFieldContext.FieldAst => Context.FieldAst; + GraphQLField IResolveFieldContext.FieldAst => Context.FieldAst; FieldType IResolveFieldContext.FieldDefinition => Context.FieldDefinition; IObjectGraphType IResolveFieldContext.ParentType => Context.ParentType; IResolveFieldContext IResolveFieldContext.Parent => Context.Parent!; @@ -40,17 +40,19 @@ public abstract class DIObjectGraphBase : IDIObjectGraphBase, object? IResolveFieldContext.RootValue => Context.RootValue; object IResolveFieldContext.Source => Context.Source!; ISchema IResolveFieldContext.Schema => Context.Schema; - Document IResolveFieldContext.Document => Context.Document; - Operation IResolveFieldContext.Operation => Context.Operation; - Variables IResolveFieldContext.Variables => Context.Variables; + GraphQLDocument IResolveFieldContext.Document => Context.Document; + GraphQLOperationDefinition IResolveFieldContext.Operation => Context.Operation; + Validation.Variables IResolveFieldContext.Variables => Context.Variables; CancellationToken IResolveFieldContext.CancellationToken => Context.CancellationToken; ExecutionErrors IResolveFieldContext.Errors => Context.Errors; IEnumerable IResolveFieldContext.Path => Context.Path; IEnumerable IResolveFieldContext.ResponsePath => Context.ResponsePath; - Dictionary? IResolveFieldContext.SubFields => Context.SubFields; - IDictionary IResolveFieldContext.Extensions => Context.Extensions; IServiceProvider IResolveFieldContext.RequestServices => Context.RequestServices!; IExecutionArrayPool IResolveFieldContext.ArrayPool => Context.ArrayPool; + IDictionary? IResolveFieldContext.Directives => Context.Directives; + Dictionary? IResolveFieldContext.SubFields => Context.SubFields; + IReadOnlyDictionary IResolveFieldContext.InputExtensions => Context.InputExtensions; + IDictionary IResolveFieldContext.OutputExtensions => Context.OutputExtensions; } /// diff --git a/src/GraphQL.DI/DIObjectGraphType.cs b/src/GraphQL.DI/DIObjectGraphType.cs index e05baed..52bcc38 100644 --- a/src/GraphQL.DI/DIObjectGraphType.cs +++ b/src/GraphQL.DI/DIObjectGraphType.cs @@ -3,9 +3,8 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -using GraphQL.Resolvers; +using System.Threading.Tasks; using GraphQL.Types; -using GraphQL.Utilities; using Microsoft.Extensions.DependencyInjection; namespace GraphQL.DI @@ -16,236 +15,83 @@ namespace GraphQL.DI /// public class DIObjectGraphType : DIObjectGraphType where TDIGraph : IDIObjectGraphBase { - /// - protected override string? GetDefaultName() - { - //if this class is inherited, do not set default name - var thisType = GetType(); - if (thisType != typeof(DIObjectGraphType)) - return null; - - //without this code, the name would default to DIObjectGraphType_1 - var name = typeof(TDIGraph).Name.Replace('`', '_'); - if (name.EndsWith("Graph", StringComparison.InvariantCulture)) - name = name.Substring(0, name.Length - "Graph".Length); - return name; - } } /// /// Wraps a graph type for use with GraphQL. This class should be registered as a singleton /// within your dependency injection provider. /// - public class DIObjectGraphType : ObjectGraphType where TDIGraph : IDIObjectGraphBase + public class DIObjectGraphType : AutoRegisteringObjectGraphType + where TDIGraph : IDIObjectGraphBase { - /// - /// Initializes a new instance, configuring the , , - /// and properties. - /// - public DIObjectGraphType() - { - GraphTypeHelper.ConfigureGraph(this, GetDefaultName); - - var classType = typeof(TDIGraph); - - //check if there is a default concurrency setting - var concurrentAttribute = classType.GetCustomAttribute(); - if (concurrentAttribute != null) { - DefaultConcurrent = true; - DefaultCreateScope = concurrentAttribute.CreateNewScope; - } - - //give inherited classes a chance to mutate the field type list before they are added to the graph type list - GraphTypeHelper.AddFields(this, CreateFieldTypeList()); - } - - /// - /// Returns the default name assigned to the graph, or to leave the default setting set by the constructor. - /// - protected virtual string? GetDefaultName() - { - //if this class is inherited, do not set default name - var thisType = GetType(); - if (thisType != typeof(DIObjectGraphType)) - return null; - - //without this code, the name would default to DIObjectGraphType_1 - var name = typeof(TDIGraph).Name.Replace('`', '_'); - if (name.EndsWith("Graph", StringComparison.InvariantCulture)) - name = name.Substring(0, name.Length - "Graph".Length); - return name; - } - - /// - /// Gets or sets whether fields added to this graph type will default to running concurrently. - /// - protected bool DefaultConcurrent { get; set; } = false; - - /// - /// Gets or sets whether concurrent fields added to this graph type will default to running in a dedicated service scope. - /// - protected bool DefaultCreateScope { get; set; } = false; - - /// - /// Indicates that the fields should be added to the graph type alphabetically. - /// - public static bool SortMembers { get; set; } = true; - - /// - /// Returns a list of instances representing the fields ready to be - /// added to the graph type. - /// Sorts the list if specified by . - /// - protected virtual IEnumerable CreateFieldTypeList() + /// + protected override void ConfigureGraph() { - //scan for public members - var methods = GetMethodsToProcess(); - var fieldTypeList = new List(methods.Count()); - foreach (var method in methods) { - var fieldType = ProcessMethod(method); - if (fieldType != null) - fieldTypeList.Add(fieldType); + // do not configure attributes set on TSource + // base.ConfigureGraph(); + + // configure attributes set on DIObject instead + var name = typeof(TDIGraph).GraphQLName(); + if (name.EndsWith("Graph") && name.Length > 5) + name = name.Substring(0, name.Length - 5); + Name = name; + Description ??= typeof(TDIGraph).Description(); + DeprecationReason ??= typeof(TDIGraph).ObsoleteMessage(); + var attributes = typeof(TDIGraph).GetCustomAttributes(); + foreach (var attr in attributes) { + attr.Modify(this); } - if (SortMembers) - return fieldTypeList.OrderBy(x => x.Name, StringComparer.InvariantCultureIgnoreCase); - return fieldTypeList; } - /// - /// Returns a list of methods ( instances) on the class to be - /// converted into field definitions. - /// - protected virtual IEnumerable GetMethodsToProcess() + // only process methods declared directly on TDIGraph -- not anything declared on TSource + /// + protected override IEnumerable GetRegisteredMembers() { + // only methods are supported var methods = typeof(TDIGraph).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly) - .Where(x => !x.ContainsGenericParameters && !x.IsSpecialName); + .Where(x => + !x.ContainsGenericParameters && // exclude methods with open generics + !x.IsSpecialName && // exclude methods generated for properties + x.ReturnType != typeof(void) && // exclude methods which do not return a value + x.ReturnType != typeof(Task) && // exclude methods which do not return a value + x.GetBaseDefinition() == x && // exclude methods which override an inherited class' method (e.g. GetHashCode) + // exclude methods generated for record types: bool Equals(TSourceType) + !(x.Name == "Equals" && !x.IsStatic && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(TDIGraph) && x.ReturnType == typeof(bool)) && + x.Name != "$"); // exclude methods generated for record types return methods; } - /// - /// Converts a specified method ( instance) into a field definition. - /// - protected virtual DIFieldType? ProcessMethod(MethodInfo method) - { - var field = GraphTypeHelper.CreateField( - method, - () => InferGraphType(ApplyAttributes(GetTypeInformation(method.ReturnParameter, false)))); - if (field == null) - return null; - - GraphTypeHelper.ProcessMethod( - method, - field, - (method, param, resolveFieldContextParameter) => { - var queryArgument = ProcessParameter(method, param, resolveFieldContextParameter, out var usesServices, out var expr); - return (queryArgument, usesServices, expr); - }, - GetInstanceExpression, - CreateUnscopedResolver, - CreateScopedResolver, - DefaultConcurrent, - DefaultCreateScope); + // each field resolver will build a new instance of DIObject + /// + protected override LambdaExpression BuildMemberInstanceExpression(MemberInfo memberInfo) + => (Expression>)((IResolveFieldContext context) => MemberInstanceFunc(context)); - return field; + /// + private TDIGraph MemberInstanceFunc(IResolveFieldContext context) + { + // create a new instance of DIObject, filling in any constructor arguments from DI + var graph = ActivatorUtilities.GetServiceOrCreateInstance(context.RequestServices ?? throw new MissingRequestServicesException()); + // set the context + graph.Context = context; + // return the object + return graph; } - /// - /// Apply , , , - /// , and over - /// the supplied . - /// Override this method to enforce specific graph types for specific CLR types, or to implement custom - /// attributes to change graph type selection behavior. - /// - protected virtual TypeInformation ApplyAttributes(TypeInformation typeInformation) - => typeInformation.ApplyAttributes(typeInformation.IsInputType ? (ICustomAttributeProvider)typeInformation.ParameterInfo! : typeInformation.MemberInfo); - - /// - protected virtual IEnumerable<(Type Type, Nullability Nullable)> GetNullabilityInformation(ParameterInfo parameter) - => parameter.GetNullabilityInformation(); - - /// - /// Analyzes a method argument or return value and returns a - /// struct containing type information necessary to select a graph type. - /// - protected virtual TypeInformation GetTypeInformation(ParameterInfo parameterInfo, bool isInputArgument) - => GraphTypeHelper.GetTypeInformation(parameterInfo, isInputArgument, GetNullabilityInformation); - - /// - /// Returns a GraphQL input type for a specified CLR type - /// - protected virtual Type InferGraphType(TypeInformation typeInformation) - => typeInformation.InferGraphType(); - - //grab some methods via reflection for us to use later - private static readonly MethodInfo _getOrCreateServiceMethod = typeof(ActivatorUtilities).GetMethods(BindingFlags.Public | BindingFlags.Static).Single(x => x.Name == nameof(ActivatorUtilities.GetServiceOrCreateInstance) && !x.IsGenericMethod); - private static readonly MethodInfo _setContextMethod = typeof(IDIObjectGraphBase).GetProperty(nameof(IDIObjectGraphBase.Context)).GetSetMethod(); - - /// - /// Returns an expression that gets/creates an instance of from a . - /// Defaults to the following: - /// context => {
- /// var g = ActivatorUtilities.GetServiceOrCreateInstance<T>(context.RequestServices);
- /// ((IDIObjectGraphBase)g).Context = context;
- /// return g;
- /// }
- ///
- protected virtual Expression GetInstanceExpression(ParameterExpression resolveFieldContextParameter) + /// + protected override ArgumentInformation GetArgumentInformation(FieldType fieldType, ParameterInfo parameterInfo) { - var serviceType = typeof(TDIGraph); - var serviceProvider = GetServiceProviderExpression(resolveFieldContextParameter); - var type = Expression.Constant(serviceType ?? throw new ArgumentNullException(nameof(serviceType))); - var getService = Expression.Call(_getOrCreateServiceMethod, serviceProvider, type); - var cast = Expression.Convert(getService, serviceType); - var variable = Expression.Parameter(serviceType); - var setVariable = Expression.Assign(variable, cast); - var setContext = Expression.Call(variable, _setContextMethod, resolveFieldContextParameter); - var block = Expression.Block(serviceType, new ParameterExpression[] { variable }, new Expression[] { setVariable, setContext, variable }); - return block; + var typeInformation = GetTypeInformation(parameterInfo); + var argumentInfo = new ArgumentInformation(parameterInfo, typeof(TSource), fieldType, typeInformation); + if (argumentInfo.ParameterInfo.ParameterType == typeof(IServiceProvider)) + { + argumentInfo.SetDelegate(context => context.RequestServices ?? throw new MissingRequestServicesException()); + } + if (argumentInfo.ParameterInfo.Name == "source" && argumentInfo.ParameterInfo.ParameterType == typeof(TSource)) + { + argumentInfo.SetDelegate(context => (TSource)context.Source); + } + argumentInfo.ApplyAttributes(); + return argumentInfo; } - - /// - /// Returns an expression that returns an from a . - /// Defaults to the following: - /// context => context.RequestServices. - /// - protected virtual Expression GetServiceProviderExpression(ParameterExpression resolveFieldContextParameter) - => GraphTypeHelper.GetServiceProviderExpression(resolveFieldContextParameter); - - /// - /// Returns an expression that gets/creates an instance of the specified type from a . - /// Defaults to the following: - /// context => context.RequestServices.GetRequiredService<T>(); - /// - protected virtual Expression GetServiceExpression(ParameterExpression resolveFieldContextParameter, Type serviceType) - => GraphTypeHelper.GetServiceExpression(resolveFieldContextParameter, serviceType); - - /// - /// Returns a field resolver for a specified delegate expression. Does not create a dedicated service scope for the delegate. - /// - protected virtual IFieldResolver CreateUnscopedResolver(Expression resolveExpression, ParameterExpression resolveFieldContextParameter) - => GraphTypeHelper.CreateUnscopedResolver(resolveExpression, resolveFieldContextParameter); - - /// - /// Returns a field resolver for a specified delegate expression. The field resolver creates a dedicated service scope for the delegate. - /// - protected virtual IFieldResolver CreateScopedResolver(Expression resolveExpression, ParameterExpression resolveFieldContextParameter) - => GraphTypeHelper.CreateScopedResolver(resolveExpression, resolveFieldContextParameter); - - /// - /// Processes a parameter of a method, returning an expression based upon , and - /// optionally returns a query argument to be added to the field. can be set to - /// to indicate that the parameter uses the service provider and may need to run within - /// a scoped service provider for concurrent use. - /// - protected virtual QueryArgument? ProcessParameter(MethodInfo method, ParameterInfo param, ParameterExpression resolveFieldContextParameter, out bool usesServices, out Expression expr) - => GraphTypeHelper.ProcessParameter( - method, - param, - resolveFieldContextParameter, - GetServiceProviderExpression, - GetServiceExpression, - param => InferGraphType(ApplyAttributes(GetTypeInformation(param, true))), - out usesServices, - out expr); } } diff --git a/src/GraphQL.DI/DISchemaTypes.cs b/src/GraphQL.DI/DISchemaTypes.cs deleted file mode 100644 index afd83fb..0000000 --- a/src/GraphQL.DI/DISchemaTypes.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using GraphQL.Types; - -namespace GraphQL.DI -{ - /// - public class DISchemaTypes : SchemaTypes - { - private readonly bool _autoMapInputTypes = true; - private readonly bool _autoMapOutputTypes = true; - - /// - /// Initializes a new instance for the specified schema, and with the specified type resolver. - /// Input and output clr types not mapped to a graph type utilize - /// and respectively. - /// - public DISchemaTypes(ISchema schema, IServiceProvider serviceProvider) : base(schema, serviceProvider) - { - } - - /// - /// Initializes a new instance for the specified schema, and with the specified type resolver. - /// Input and output clr types not mapped to a graph type utilize - /// and respectively, if specified via - /// and . - /// - public DISchemaTypes(ISchema schema, IServiceProvider serviceProvider, bool autoMapInputTypes, bool autoMapOutputTypes) : base(schema, serviceProvider) - { - if (autoMapInputTypes == false || autoMapOutputTypes == false) - throw new NotSupportedException( - "This functionality is not yet supported due to a design constraint within GraphQL.NET. " + - $"Please override {nameof(AutoMapInputType)} and/or {nameof(AutoMapOutputType)} to disable auto mapping."); - _autoMapInputTypes = autoMapInputTypes; - _autoMapOutputTypes = autoMapOutputTypes; - } - - /// - protected override Type? GetGraphTypeFromClrType(Type clrType, bool isInputType, List<(Type ClrType, Type GraphType)> typeMappings) - { - var type = base.GetGraphTypeFromClrType(clrType, isInputType, typeMappings); - if (type == null && isInputType && clrType.IsClass && AutoMapInputType(clrType)) { - return typeof(AutoInputObjectGraphType<>).MakeGenericType(clrType); - } - if (type == null && !isInputType && clrType.IsClass && AutoMapOutputType(clrType)) { - return typeof(AutoObjectGraphType<>).MakeGenericType(clrType); - } - return type; - } - - /// - /// Called when no input graph type is registered for the specified CLR type. - /// Return true if the specified CLR type should be wrapped with . - /// Defaults value depends on the constructor arguments. - /// - protected virtual bool AutoMapInputType(Type clrType) => _autoMapInputTypes; - - /// - /// Called when no output graph type is registered for the specified CLR type. - /// Return true if the specified CLR type should be wrapped with . - /// Defaults value depends on the constructor arguments. - /// - protected virtual bool AutoMapOutputType(Type clrType) => _autoMapOutputTypes; - } -} diff --git a/src/GraphQL.DI/FromServicesAttribute.cs b/src/GraphQL.DI/FromServicesAttribute.cs deleted file mode 100644 index b5dba35..0000000 --- a/src/GraphQL.DI/FromServicesAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace GraphQL.DI -{ - /// - /// Marks an parameter (query argument) as being pulled from . - /// The type of this parameter must match the source type as specified in the generic type parameter of - /// . For , the type must be . - /// - [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] - public class FromServicesAttribute : Attribute - { - } -} diff --git a/src/GraphQL.DI/FromSourceAttribute.cs b/src/GraphQL.DI/FromSourceAttribute.cs deleted file mode 100644 index 9cc32a4..0000000 --- a/src/GraphQL.DI/FromSourceAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace GraphQL.DI -{ - /// - /// Marks an parameter (query argument) as being pulled via . - /// For concurrent fields specified to run in a separate scope, the service will be pulled from the dedicated - /// service scope created for that field resolver. - /// - [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] - public class FromSourceAttribute : Attribute - { - } -} diff --git a/src/GraphQL.DI/GraphQLBuilderExtensions.cs b/src/GraphQL.DI/GraphQLBuilderExtensions.cs index 17d9bae..5e72965 100644 --- a/src/GraphQL.DI/GraphQLBuilderExtensions.cs +++ b/src/GraphQL.DI/GraphQLBuilderExtensions.cs @@ -12,14 +12,13 @@ namespace GraphQL.DI public static class GraphQLBuilderExtensions { /// - /// Registers , and + /// Registers and /// as generic types. /// public static IGraphQLBuilder AddDIGraphTypes(this IGraphQLBuilder builder) { - builder.TryRegister(typeof(AutoInputObjectGraphType<>), typeof(AutoInputObjectGraphType<>), ServiceLifetime.Transient); - builder.TryRegister(typeof(DIObjectGraphType<>), typeof(DIObjectGraphType<>), ServiceLifetime.Transient); - builder.TryRegister(typeof(DIObjectGraphType<,>), typeof(DIObjectGraphType<,>), ServiceLifetime.Transient); + builder.Services.TryRegister(typeof(DIObjectGraphType<>), typeof(DIObjectGraphType<>), ServiceLifetime.Transient); + builder.Services.TryRegister(typeof(DIObjectGraphType<,>), typeof(DIObjectGraphType<,>), ServiceLifetime.Transient); return builder; } @@ -38,104 +37,27 @@ public static IGraphQLBuilder AddDIClrTypeMappings(this IGraphQLBuilder builder) /// Scans the specified assembly for classes that implement and /// registers clr type mappings on the schema between that /// (constructed from that class and its source type), and the source type. - /// Skips classes where the source type is , or where the class is marked with - /// the , or where another graph type would be automatically mapped - /// to the specified type, or where a graph type has already been registered to the specified clr type. + /// Skips classes where the source type is or where the class is marked with + /// the . /// public static IGraphQLBuilder AddDIClrTypeMappings(this IGraphQLBuilder builder, Assembly assembly) { - var typesAlreadyMapped = new HashSet( - assembly.GetDefaultClrTypeMappings() - .Where(x => x.GraphType.IsOutputType()) - .Select(x => x.ClrType)); - var types = assembly.GetTypes() .Where(x => x.IsClass && !x.IsAbstract && typeof(IDIObjectGraphBase).IsAssignableFrom(x)) .Select(x => { var iface = x.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDIObjectGraphBase<>)); return (x, iface?.GetGenericArguments()[0]); }) - .Where(x => x.SourceType != null && x.SourceType != typeof(object) && !x.DIGraphType.IsDefined(typeof(DoNotMapClrTypeAttribute)) && !typesAlreadyMapped.Contains(x.SourceType)) + .Where(x => x.SourceType != null && x.SourceType != typeof(object) && !x.DIGraphType.IsDefined(typeof(DoNotMapClrTypeAttribute))) .Select<(Type DIGraphType, Type? SourceType), (Type ClrType, Type GraphType)>(x => (x.SourceType!, typeof(DIObjectGraphType<,>).MakeGenericType(x.DIGraphType, x.SourceType!))) - .ToList(); + .ToDictionary(x => x.ClrType, x => x.GraphType); if (types.Count == 0) return builder; - builder.ConfigureSchema(schema => { - var existingMappings = new HashSet(schema.TypeMappings.Where(x => x.graphType.IsOutputType()).Select(x => x.clrType)); - foreach (var type in types) { - if (!existingMappings.Contains(type.ClrType)) - schema.RegisterTypeMapping(type.ClrType, type.GraphType); - } - }); + builder.AddGraphTypeMappingProvider(new DIGraphTypeMappingProvider(types)); return builder; } - - /// - /// Contains a list of types that are scanned for, from which a clr type mapping will be matched - /// - private static readonly Type[] _typesToRegister = new Type[] - { - typeof(ObjectGraphType<>), - typeof(InputObjectGraphType<>), - typeof(EnumerationGraphType<>), - }; - - /// - /// Scans the specified assembly for classes that inherit from , - /// , or , and - /// returns a list of mappings between matched classes and the source type or underlying enum type. - /// Skips classes where the source type is , or where the class is marked with - /// the . - /// - private static List<(Type ClrType, Type GraphType)> GetDefaultClrTypeMappings(this Assembly assembly) - { - //create a list of type mappings - var typeMappings = new List<(Type clrType, Type graphType)>(); - - //loop through each type in the specified assembly - foreach (var graphType in assembly.GetTypes()) { - //skip types that are not graph types - if (!typeof(IGraphType).IsAssignableFrom(graphType)) - continue; - - //skip abstract types and interfaces - if (graphType.IsAbstract || graphType.IsInterface) - continue; - - //skip types marked with the DoNotRegister attribute - if (graphType.GetCustomAttributes(false).Any(y => y.GetType() == typeof(DoNotMapClrTypeAttribute))) - continue; - - //start with the base type - var baseType = graphType.BaseType; - while (baseType != null) { - //skip types marked with the DoNotRegister attribute - if (baseType.GetCustomAttributes(false).Any(y => y.GetType() == typeof(DoNotMapClrTypeAttribute))) - break; - - //look for generic types that match our list above - if (baseType.IsConstructedGenericType && _typesToRegister.Contains(baseType.GetGenericTypeDefinition())) { - //get the base type - var clrType = baseType.GetGenericArguments()[0]; - - //as long as it's not of type 'object', register it - if (clrType != typeof(object)) - typeMappings.Add((clrType, graphType)); - - //skip to the next type - break; - } - - //look up the inheritance chain for a match - baseType = baseType.BaseType; - } - } - - //return the list of type mappings - return typeMappings; - } } } diff --git a/src/GraphQL.DI/GraphTypeAttribute.cs b/src/GraphQL.DI/GraphTypeAttribute.cs deleted file mode 100644 index 43989b1..0000000 --- a/src/GraphQL.DI/GraphTypeAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using GraphQL.Types; - -namespace GraphQL.DI -{ - /// - /// Marks a method's (field's) return value to be the specified GraphQL type, or - /// marks a parameter's (query argument's) input value to be the specified GraphQL type. - /// - //perhaps this should apply to ReturnValue rather than Method - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class GraphTypeAttribute : Attribute - { - /// - public GraphTypeAttribute(Type graphType) - { - Type = graphType; - } - - /// - /// Returns the graph type specified for the method (field) or parameter (query argument). - /// - public Type Type { get; } - } -} diff --git a/src/GraphQL.DI/GraphTypeHelper.cs b/src/GraphQL.DI/GraphTypeHelper.cs deleted file mode 100644 index a7e1011..0000000 --- a/src/GraphQL.DI/GraphTypeHelper.cs +++ /dev/null @@ -1,557 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using GraphQL.DataLoader; -using GraphQL.MicrosoftDI; -using GraphQL.Resolvers; -using GraphQL.Types; -using Microsoft.Extensions.DependencyInjection; - -namespace GraphQL.DI -{ - internal static class GraphTypeHelper - { - /// - /// Configures the Name, Description, DeprecationRreason and metadata for a graph - /// - public static void ConfigureGraph(IGraphType graphType, Func getDefaultNameFunc) - { - var classType = typeof(TSourceType); - - //allow default name / description / obsolete tags to remain if not overridden - var nameAttribute = classType.GetCustomAttribute(); - if (nameAttribute != null) - graphType.Name = nameAttribute.Name; - else { - var name = getDefaultNameFunc(); - if (name != null) - graphType.Name = name; - } - - var descriptionAttribute = classType.GetCustomAttribute(); - if (descriptionAttribute != null) - graphType.Description = descriptionAttribute.Description; - var obsoleteAttribute = classType.GetCustomAttribute(); - if (obsoleteAttribute != null) - graphType.DeprecationReason = obsoleteAttribute.Message; - - //pull metadata - foreach (var metadataAttribute in classType.GetCustomAttributes()) - graphType.Metadata.Add(metadataAttribute.Key, metadataAttribute.Value); - } - - /// - /// Adds the specified fields to the specified graph. - /// Skips null fields. - /// - public static void AddFields(IComplexGraphType graphType, IEnumerable? fieldTypes) - { - if (fieldTypes != null) - foreach (var fieldType in fieldTypes) - if (fieldType != null) - graphType.AddField(fieldType); - } - - /// - /// Returns a DIFieldType for the specified method, with the metadata set and Name, Type, - /// Description, and DeprecationReason properties initialized. - /// - public static DIFieldType? CreateField(MethodInfo method, Func inferGraphType) - { - //get the method name - string methodName = method.Name; - var methodNameAttribute = method.GetCustomAttribute(); - if (methodNameAttribute != null) { - methodName = methodNameAttribute.Name; - } else { - if (methodName.EndsWith("Async") && method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) { - methodName = methodName.Substring(0, methodName.Length - "Async".Length); - } - } - if (methodName == null) - return null; //ignore method if set to null - - //ignore method if it does not return a value - if (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task)) - return null; - - //determine the graphtype of the field - var graphTypeAttribute = method.GetCustomAttribute(); - Type? graphType = graphTypeAttribute?.Type; - //infer the graphtype if it is not specified - if (graphType == null) { - graphType = inferGraphType(); - } - //load the description - string? description = method.GetCustomAttribute()?.Description; - //load the deprecation reason - string? obsoleteDescription = method.GetCustomAttribute()?.Message; - - var fieldType = new DIFieldType { - Name = methodName, - Type = graphType, - Description = description, - DeprecationReason = obsoleteDescription, - }; - - //load the metadata - foreach (var metaAttribute in method.GetCustomAttributes()) - fieldType.WithMetadata(metaAttribute.Key, metaAttribute.Value); - - return fieldType; - } - - private const string ORIGINAL_EXPRESSION_PROPERTY_NAME = nameof(ORIGINAL_EXPRESSION_PROPERTY_NAME); - - /// - /// Returns a DIFieldType for the specified property, with the metadata set and Name, Type, - /// Description, and DeprecationReason properties initialized. - /// - public static DIFieldType? CreateField(PropertyInfo property, Func inferGraphType) - { - //get the field name - string fieldName = property.Name; - var fieldNameAttribute = property.GetCustomAttribute(); - if (fieldNameAttribute != null) { - fieldName = fieldNameAttribute.Name; - } - if (fieldName == null) - return null; //ignore field if set to null (or Ignore is specified) - - //determine the graphtype of the field - var graphTypeAttribute = property.GetCustomAttribute(); - Type? graphType = graphTypeAttribute?.Type; - //infer the graphtype if it is not specified - if (graphType == null) { - graphType = inferGraphType(); - } - //load the description - string? description = property.GetCustomAttribute()?.Description; - //load the deprecation reason - string? obsoleteDescription = property.GetCustomAttribute()?.Message; - //create the field - var fieldType = new DIFieldType { - Name = fieldName, - Type = graphType, - Description = description, - DeprecationReason = obsoleteDescription, - }; - - //set name of property - fieldType.WithMetadata(ORIGINAL_EXPRESSION_PROPERTY_NAME, property.Name); - //load the metadata - foreach (var metaAttribute in property.GetCustomAttributes()) - fieldType.WithMetadata(metaAttribute.Key, metaAttribute.Value); - //return the field - return fieldType; - } - - private static readonly Type[] _listTypes = new Type[] { - typeof(IEnumerable<>), - typeof(IList<>), - typeof(List<>), - typeof(ICollection<>), - typeof(IReadOnlyCollection<>), - typeof(IReadOnlyList<>), - typeof(HashSet<>), - typeof(ISet<>), - }; - - /// - /// Analyzes a property and returns a - /// struct containing type information necessary to select a graph type. - /// - public static TypeInformation GetTypeInformation(PropertyInfo propertyInfo, bool isInputProperty, Func> getNullabilityInformationFunc) - { - var isList = false; - var isNullableList = false; - var typeTree = getNullabilityInformationFunc(propertyInfo); - foreach (var type in typeTree) { - if (type.Type.IsArray) { - //unwrap type and mark as list - isList = true; - isNullableList = type.Nullable != Nullability.NonNullable; - continue; - } - if (type.Type.IsGenericType) { - var g = type.Type.GetGenericTypeDefinition(); - if (_listTypes.Contains(g)) { - //unwrap type and mark as list - isList = true; - isNullableList = type.Nullable != Nullability.NonNullable; - continue; - } - } - if (type.Type == typeof(IEnumerable) || type.Type == typeof(ICollection)) { - //assume list of nullable object - isList = true; - isNullableList = type.Nullable != Nullability.NonNullable; - break; - } - //found match - var nullable = type.Nullable != Nullability.NonNullable; - return new TypeInformation(propertyInfo, isInputProperty, type.Type, nullable, isList, isNullableList, null); - } - //unknown type - return new TypeInformation(propertyInfo, isInputProperty, typeof(object), true, isList, isNullableList, null); - } - - /// - /// Analyzes a method argument or return value and returns a - /// struct containing type information necessary to select a graph type. - /// - public static TypeInformation GetTypeInformation(ParameterInfo parameterInfo, bool isInputArgument, Func> getNullabilityInformationFunc) - { - var isOptionalParameter = parameterInfo.IsOptional; - var isList = false; - var isNullableList = false; - var typeTree = getNullabilityInformationFunc(parameterInfo); - foreach (var type in typeTree) { - if (type.Type == typeof(IDataLoaderResult)) - //assume type is nullable object - break; - if (type.Type.IsArray) { - //unwrap type and mark as list - isList = true; - isNullableList = type.Nullable != Nullability.NonNullable; - continue; - } - if (type.Type.IsGenericType) { - var g = type.Type.GetGenericTypeDefinition(); - if (g == typeof(IDataLoaderResult<>) || g == typeof(Task<>)) { - //unwrap type - continue; - } - if (_listTypes.Contains(g)) { - //unwrap type and mark as list - isList = true; - isNullableList = type.Nullable != Nullability.NonNullable; - continue; - } - } - if (type.Type == typeof(IEnumerable) || type.Type == typeof(ICollection)) { - //assume list of nullable object - isList = true; - isNullableList = type.Nullable != Nullability.NonNullable; - break; - } - //found match - var nullable = type.Nullable != Nullability.NonNullable; - if (isOptionalParameter) { - if (isList) - isNullableList = true; - else - nullable = true; - } - return new TypeInformation(parameterInfo, isInputArgument, type.Type, nullable, isList, isNullableList, null); - } - //unknown type - if (isOptionalParameter && isList) - isNullableList = true; - return new TypeInformation(parameterInfo, isInputArgument, typeof(object), true, isList, isNullableList, null); - } - - /// - /// Converts a specified method ( instance) into a field definition. - /// - public static void ProcessMethod( - MethodInfo method, - DIFieldType field, - Func processParameterFunc, - Func getInstanceExpressionFunc, - Func createUnscopedResolverFunc, - Func createScopedResolverFunc, - bool defaultConcurrent, - bool defaultCreateScope) - { - //scan the parameter list to create a list of arguments, and at the same time, generate the expressions to be used to call this method during the resolve function - IFieldResolver resolver; - var queryArguments = new List(); - bool concurrent = false; - bool anyParamsUseServices = false; - { - var resolveFieldContextParameter = Expression.Parameter(typeof(IResolveFieldContext)); - var executeParams = new List(); - foreach (var param in method.GetParameters()) { - var (queryArgument, isService, expr) = processParameterFunc(method, param, resolveFieldContextParameter); - anyParamsUseServices |= isService; - if (queryArgument != null) - queryArguments.Add(queryArgument); - //add the constructed expression to the list to be used for creating the resolve function - executeParams.Add(expr); - } - //check if this is an async method - var isAsync = typeof(Task).IsAssignableFrom(method.ReturnType); - //define the resolve expression - Expression exprResolve; - if (method.IsStatic) { - //for static methods, no need to pull an instance of the class from the service provider - //just call the static method with the executeParams as the parameters - exprResolve = Expression.Call(method, executeParams.ToArray()); - } else { - //for instance methods, pull an instance of the class from the service provider - Expression exprGetService = getInstanceExpressionFunc(resolveFieldContextParameter); - //then, call the method with the executeParams as the parameters - exprResolve = Expression.Call(exprGetService, method, executeParams.ToArray()); - } - - //determine if this should run concurrently with other resolvers - //if it's an async static method that does not pull from services, then it's safe to run concurrently - if (isAsync && method.IsStatic && !anyParamsUseServices) { - //mark this field as concurrent, so the execution strategy will run it asynchronously - concurrent = true; - //set the resolver to run the compiled resolve function - resolver = createUnscopedResolverFunc(exprResolve, resolveFieldContextParameter); - } else { - var methodConcurrent = method.GetCustomAttribute(); - concurrent = defaultConcurrent; - var scoped = defaultCreateScope; - if (methodConcurrent != null) { - concurrent = methodConcurrent.Concurrent; - scoped = methodConcurrent.CreateNewScope; - } - //for methods that return a Task and are marked with the Concurrent attribute, - if (isAsync && concurrent) { - //mark this field as concurrent, so the execution strategy will run it asynchronously - concurrent = true; - //determine if a new DI scope is required - if (scoped) { - //the resolve function needs to create a scope, - // then run the compiled resolve function (which creates an instance of the class), - // then release the scope once the task has been awaited - resolver = createScopedResolverFunc(exprResolve, resolveFieldContextParameter); - } else { - //just run the compiled resolve function, and count on the method to handle multithreading issues - resolver = createUnscopedResolverFunc(exprResolve, resolveFieldContextParameter); - } - } - //for non-async methods, and instance methods that are not marked with the Concurrent attribute - else { - concurrent = false; - //just run the compiled resolve function - resolver = createUnscopedResolverFunc(exprResolve, resolveFieldContextParameter); - } - } - } - - //return the field - field.Arguments = new QueryArguments(queryArguments); - field.Concurrent = concurrent; - field.Resolver = resolver; - } - - private static readonly PropertyInfo _cancellationTokenProperty = typeof(IResolveFieldContext).GetProperty(nameof(IResolveFieldContext.CancellationToken), BindingFlags.Public | BindingFlags.Instance); - private static readonly MethodInfo _asMethod = typeof(ResolveFieldContextExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public).Single(x => x.Name == nameof(ResolveFieldContextExtensions.As) && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(IResolveFieldContext)); - private static readonly PropertyInfo _sourceProperty = typeof(IResolveFieldContext).GetProperty(nameof(IResolveFieldContext.Source), BindingFlags.Instance | BindingFlags.Public); - private static readonly MethodInfo _getArgumentMethod = typeof(ResolveFieldContextExtensions).GetMethods(BindingFlags.Public | BindingFlags.Static).Single(x => x.Name == nameof(ResolveFieldContextExtensions.GetArgument) && x.IsGenericMethod); - - /// - /// Processes a parameter of a method, returning an expression based upon , and - /// optionally returns a query argument to be added to the field. can be set to - /// to indicate that the parameter uses the service provider and may need to run within - /// a scoped service provider for concurrent use. - /// - public static QueryArgument? ProcessParameter( - MethodInfo method, - ParameterInfo param, - ParameterExpression resolveFieldContextParameter, - Func getServiceProviderExpressionFunc, - Func getServiceExpressionFunc, - Func getGraphTypeFunc, - out bool usesServices, - out Expression expr) - { - usesServices = false; - - if (param.ParameterType == typeof(IResolveFieldContext)) { - //if they are requesting the IResolveFieldContext, just pass it in - //e.g. Func = (context) => context; - expr = resolveFieldContextParameter; - //and do not add it as a QueryArgument - return null; - } - if (param.ParameterType == typeof(CancellationToken)) { - //return the cancellation token from the IResolveFieldContext parameter - expr = Expression.MakeMemberAccess(resolveFieldContextParameter, _cancellationTokenProperty); - //and do not add it as a QueryArgument - return null; - } - if (param.ParameterType.IsConstructedGenericType && param.ParameterType.GetGenericTypeDefinition() == typeof(IResolveFieldContext<>)) { - //validate that constructed type matches TSource - var genericType = param.ParameterType.GetGenericArguments()[0]; - if (!genericType.IsAssignableFrom(typeof(TSource))) - throw new InvalidOperationException($"Invalid {nameof(IResolveFieldContext)}<> type for method {method.Name}"); - //convert the IResolveFieldContext to the specified ResolveFieldContext<> - var asMethodTyped = _asMethod.MakeGenericMethod(genericType); - //e.g. Func> = (context) => context.As(); - expr = Expression.Call(asMethodTyped, resolveFieldContextParameter); - //and do not add it as a QueryArgument - return null; - } - if (param.ParameterType == typeof(TSource) && typeof(TSource) != typeof(object) && param.Name == "source") { - //retrieve the value and cast it to the specified type - //e.g. Func = (context) => (TSource)context.Source; - expr = Expression.Convert(Expression.Property(resolveFieldContextParameter, _sourceProperty), param.ParameterType); - //and do not add it as a QueryArgument - return null; - } - if (param.GetCustomAttribute() != null) { - //validate that type matches TSource - if (!param.ParameterType.IsAssignableFrom(typeof(TSource))) - throw new InvalidOperationException($"Invalid {nameof(IResolveFieldContext)}<> type for method {method.Name}"); - //retrieve the value and cast it to the specified type - //e.g. Func = (context) => (TSource)context.Source; - expr = Expression.Convert(Expression.Property(resolveFieldContextParameter, _sourceProperty), param.ParameterType); - //and do not add it as a QueryArgument - return null; - } - if (param.ParameterType == typeof(IServiceProvider)) { - //if they want the service provider, just pass it in - //e.g. Func = (context) => context.RequestServices; - expr = getServiceProviderExpressionFunc(resolveFieldContextParameter); - //note that we have a parameter that pulls from the service provider - usesServices = true; - //and do not add it as a QueryArgument - return null; - } - if (param.GetCustomAttribute() != null) { - //if they are pulling from a service context, pull that in - //e.g. Func = (context) => (IMyService)AsyncServiceProvider.GetRequiredService(typeof(IMyService)); - expr = getServiceExpressionFunc(resolveFieldContextParameter, param.ParameterType); - //note that we have a parameter that pulls from the service provider - usesServices = true; - //and do not add it as a QueryArgument - return null; - } - //pull the name attribute - var nameAttribute = param.GetCustomAttribute(); - if (nameAttribute != null && nameAttribute.Name == null) { - //name is set to null, so just fill with the default for this parameter and don't create a query argument - //e.g. Func = (context) => default(int); - expr = Expression.Constant(param.ParameterType.IsValueType ? Activator.CreateInstance(param.ParameterType) : null, param.ParameterType); - //and do not add it as a QueryArgument - return null; - } - //otherwise, it's a query argument - //initialize the query argument parameters - - //load the specified graph type - var graphTypeAttribute = param.GetCustomAttribute(); - Type? graphType = graphTypeAttribute?.Type; - //if no specific graphtype set, check for the Id attribute, or pull from registered graph type list - if (graphType == null) { - graphType = getGraphTypeFunc(param); - } - - //construct the query argument - var argument = new QueryArgument(graphType) { - Name = nameAttribute?.Name ?? param.Name, - Description = param.GetCustomAttribute()?.Description, - }; - - foreach (var metaAttribute in param.GetCustomAttributes()) - argument.Metadata.Add(metaAttribute.Key, metaAttribute.Value); - - //pull/create the default value - object? defaultValue = null; - if (param.IsOptional) { - defaultValue = param.DefaultValue; - } else if (param.ParameterType.IsValueType) { - defaultValue = Activator.CreateInstance(param.ParameterType); - } - - //construct a call to ResolveFieldContextExtensions.GetArgument, passing in the appropriate default value - var getArgumentMethodTyped = _getArgumentMethod.MakeGenericMethod(param.ParameterType); - //e.g. Func = (context) => ResolveFieldContextExtensions.GetArgument(context, argument.Name, defaultValue); - expr = Expression.Call(getArgumentMethodTyped, resolveFieldContextParameter, Expression.Constant(argument.Name), Expression.Constant(defaultValue, param.ParameterType)); - - //return the query argument - return argument; - } - - private static readonly ConcurrentDictionary _funcFieldResolverConstructors = new ConcurrentDictionary(); - /// - /// Returns a field resolver for a specified delegate expression. Does not create a dedicated service scope for the delegate. - /// - public static IFieldResolver CreateUnscopedResolver(Expression resolveExpression, ParameterExpression resolveFieldContextParameter) - { - var constructorInfo = _funcFieldResolverConstructors.GetOrAdd(resolveExpression.Type, t => typeof(FuncFieldResolver<>).MakeGenericType(t).GetConstructors().Single()); - var lambda = Expression.Lambda(resolveExpression, resolveFieldContextParameter).Compile(); - return (IFieldResolver)constructorInfo.Invoke(new[] { lambda }); - } - - private static readonly ConcurrentDictionary _scopedFieldResolverConstructors = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary _scopedAsyncFieldResolverConstructors = new ConcurrentDictionary(); - /// - /// Returns a field resolver for a specified delegate expression. The field resolver creates a dedicated service scope for the delegate. - /// - public static IFieldResolver CreateScopedResolver(Expression resolveExpression, ParameterExpression resolveFieldContextParameter) - { - ConstructorInfo constructorInfo; - if (typeof(Task).IsAssignableFrom(resolveExpression.Type)) { - constructorInfo = _scopedAsyncFieldResolverConstructors.GetOrAdd(GetTaskType(resolveExpression.Type), t => typeof(ScopedAsyncFieldResolver<>).MakeGenericType(t).GetConstructors().Single()); - } else { - constructorInfo = _scopedFieldResolverConstructors.GetOrAdd(resolveExpression.Type, t => typeof(ScopedFieldResolver<>).MakeGenericType(t).GetConstructors().Single()); - } - var lambda = Expression.Lambda(resolveExpression, resolveFieldContextParameter).Compile(); - return (IFieldResolver)constructorInfo.Invoke(new[] { lambda }); - - static Type GetTaskType(Type t) - { - while (t != null) { - if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Task<>)) - return t.GenericTypeArguments[0]; - t = t.BaseType; - } - throw new ArgumentOutOfRangeException(nameof(t), "Type does not inherit from Task<>."); - } - } - - private static readonly PropertyInfo _requestServicesProperty = typeof(IResolveFieldContext).GetProperty(nameof(IResolveFieldContext.RequestServices), BindingFlags.Instance | BindingFlags.Public); - - /// - /// Returns an expression that returns an from a . - /// Defaults to the following: - /// context => context.RequestServices. - /// - public static Expression GetServiceProviderExpression(ParameterExpression resolveFieldContextParameter) - { - //returns: context.RequestServices - return Expression.Property(resolveFieldContextParameter, _requestServicesProperty); - } - - private static readonly MethodInfo _getRequiredServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethods(BindingFlags.Public | BindingFlags.Static).Single(x => x.Name == nameof(ServiceProviderServiceExtensions.GetRequiredService) && !x.IsGenericMethod); - - /// - /// Returns an expression that gets/creates an instance of the specified type from a . - /// Defaults to the following: - /// context => context.RequestServices.GetRequiredService<T>(); - /// - public static Expression GetServiceExpression(ParameterExpression resolveFieldContextParameter, Type serviceType) - { - //returns: (serviceType)(context.RequestServices.GetRequiredService(serviceType)) - var serviceProvider = GetServiceProviderExpression(resolveFieldContextParameter); - var type = Expression.Constant(serviceType ?? throw new ArgumentNullException(nameof(serviceType))); - var getService = Expression.Call(_getRequiredServiceMethod, serviceProvider, type); - var cast = Expression.Convert(getService, serviceType); - return cast; - } - - /// - /// Returns an expression that gets the source and casts it to the specified type. - /// - public static Expression GetSourceExpression(ParameterExpression resolveFieldContextParameter, Type sourceType) - { - //retrieve the value and cast it to the specified type - //e.g. Func = (context) => (TSourceType)context.Source; - return Expression.Convert(Expression.Property(resolveFieldContextParameter, _sourceProperty), sourceType); - } - } -} diff --git a/src/GraphQL.DI/IdAttribute.cs b/src/GraphQL.DI/IdAttribute.cs deleted file mode 100644 index 92a6586..0000000 --- a/src/GraphQL.DI/IdAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace GraphQL.DI -{ - /// - /// Marks a method's (field's) return value to be an ID graph type, or - /// marks a parameter's (query argument's) input value to be an ID graph type. - /// - //perhaps this should apply to ReturnValue rather than Method - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class IdAttribute : Attribute - { - /// - public IdAttribute() - { - } - } -} diff --git a/src/GraphQL.DI/IgnoreAttribute.cs b/src/GraphQL.DI/IgnoreAttribute.cs deleted file mode 100644 index 8969127..0000000 --- a/src/GraphQL.DI/IgnoreAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace GraphQL.DI -{ - /// - /// Marks a method (field) to be skipped when constructing the graph type. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class IgnoreAttribute : NameAttribute - { - /// - public IgnoreAttribute() : base(null!) - { - } - } -} diff --git a/src/GraphQL.DI/MetadataAttribute.cs b/src/GraphQL.DI/MetadataAttribute.cs deleted file mode 100644 index 6ca2300..0000000 --- a/src/GraphQL.DI/MetadataAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace GraphQL.DI -{ - /// - /// Marks a class (graph type), method (field) or parameter (query argument) with additional metadata. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = true, Inherited = false)] - public class MetadataAttribute : Attribute - { - /// - public MetadataAttribute(string key, object value) - { - Key = key; - Value = value; - } - - /// - /// Gets or sets the metadata key. - /// - public string Key { get; set; } - - /// - /// Gets or sets the metadata value. - /// - public object Value { get; set; } - } -} diff --git a/src/GraphQL.DI/NameAttribute.cs b/src/GraphQL.DI/NameAttribute.cs deleted file mode 100644 index 559effe..0000000 --- a/src/GraphQL.DI/NameAttribute.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using GraphQL.Conversion; - -namespace GraphQL.DI -{ - /// - /// Marks a class (graph type), method (field) or parameter (query argument) with a specified GraphQL name. - /// Note that the specified name will be translated by the schema's . - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class NameAttribute : Attribute - { - /// - public NameAttribute(string name) - { - Name = name; - } - - /// - /// Returns the GraphQL name of the class (graph type), method (field), or parameter (query argument). - /// - public string Name { get; } - } -} diff --git a/src/GraphQL.DI/Nullability.cs b/src/GraphQL.DI/Nullability.cs deleted file mode 100644 index 9b82dac..0000000 --- a/src/GraphQL.DI/Nullability.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace GraphQL.DI -{ - /// - /// Nullable reference annotation for a member. - /// - public enum Nullability : byte - { - /// - /// The member is a reference type not marked with nullable reference annotations. - /// - Unknown = 0, - /// - /// The member is a non-nullable value type or is a nullable refernce type marked as non-nullable. - /// - NonNullable = 1, - /// - /// The member is a nullable value type or is a nullable reference type marked as nullable. - /// - Nullable = 2, - } -} diff --git a/src/GraphQL.DI/NullabilityInterpretationException.cs b/src/GraphQL.DI/NullabilityInterpretationException.cs deleted file mode 100644 index 91b6275..0000000 --- a/src/GraphQL.DI/NullabilityInterpretationException.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Reflection; - -namespace GraphQL.DI -{ - /// - /// Exception when nullable reference annotations cannot be read from a parameter. - /// - public class NullabilityInterpretationException : Exception - { - /// - /// Initializes a new instance for the specified . - /// - public NullabilityInterpretationException(ParameterInfo parameter) : base($"Unable to interpret nullability attributes for argument '{parameter.Name}' on method '{parameter.Member.DeclaringType.Name}.{parameter.Member.Name}'.") - { - } - - /// - /// Initializes a new instance for the specified . - /// - public NullabilityInterpretationException(MemberInfo member) : base($"Unable to interpret nullability attributes for '{member.DeclaringType.Name}.{member.Name}'.") - { - } - } -} diff --git a/src/GraphQL.DI/OptionalAttribute.cs b/src/GraphQL.DI/OptionalAttribute.cs deleted file mode 100644 index e408431..0000000 --- a/src/GraphQL.DI/OptionalAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace GraphQL.DI -{ - /// - /// Marks a method's (field's) return value as nullable, or - /// marks a parameter's (query argument's) input value to be optional. - /// - //perhaps this should apply to ReturnValue instead of Method - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class OptionalAttribute : Attribute - { - } -} diff --git a/src/GraphQL.DI/OptionalListAttribute.cs b/src/GraphQL.DI/OptionalListAttribute.cs deleted file mode 100644 index 3acae28..0000000 --- a/src/GraphQL.DI/OptionalListAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace GraphQL.DI -{ - /// - /// Marks a method's (field's) return value as nullable, or - /// marks a parameter's (query argument's) input value to be optional. - /// - //perhaps this should apply to ReturnValue instead of Method - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class OptionalListAttribute : Attribute - { - } -} diff --git a/src/GraphQL.DI/PropertyResolver.cs b/src/GraphQL.DI/PropertyResolver.cs deleted file mode 100644 index b6b2921..0000000 --- a/src/GraphQL.DI/PropertyResolver.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using GraphQL.Resolvers; - -namespace GraphQL.DI -{ - internal class PropertyResolver : IFieldResolver - { - private readonly PropertyInfo _property; - public PropertyResolver(PropertyInfo property) - { - _property = property; - } - - public object? Resolve(IResolveFieldContext context) - => _property.GetValue(context.Source); - } - -} diff --git a/src/GraphQL.DI/ReflectionExtensions.cs b/src/GraphQL.DI/ReflectionExtensions.cs deleted file mode 100644 index 18705e5..0000000 --- a/src/GraphQL.DI/ReflectionExtensions.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace GraphQL.DI -{ - /// - /// Provides extension methods to and instances to read nullabile type reference annotations. - /// - public static class ReflectionExtensions - { - private static (Nullability, IList?) GetMethodParameterNullability(ParameterInfo parameter) - { - Nullability nullableContext = GetMethodDefaultNullability(parameter.Member); - - foreach (var attribute in parameter.CustomAttributes) { - if (attribute.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute" && attribute.ConstructorArguments.Count == 1) { - var argType = attribute.ConstructorArguments[0].ArgumentType; - if (argType == typeof(byte)) { - return ((Nullability)(byte)attribute.ConstructorArguments[0].Value, null); - } else if (argType == typeof(byte[])) { - return (nullableContext, (IList)attribute.ConstructorArguments[0].Value); - } else { - throw new NullabilityInterpretationException(parameter); - } - } - } - return (nullableContext, null); - } - - private static (Nullability, IList?) GetMemberNullability(MemberInfo member) - { - Nullability nullableContext = GetMethodDefaultNullability(member); - - foreach (var attribute in member.CustomAttributes) { - if (attribute.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute" && attribute.ConstructorArguments.Count == 1) { - var argType = attribute.ConstructorArguments[0].ArgumentType; - if (argType == typeof(byte)) { - return ((Nullability)(byte)attribute.ConstructorArguments[0].Value, null); - } else if (argType == typeof(byte[])) { - return (nullableContext, (IList)attribute.ConstructorArguments[0].Value); - } else { - throw new NullabilityInterpretationException(member); - } - } - } - return (nullableContext, null); - } - - /// - /// Examines the parameter type and walks the type definitions, for each returning the nullability information for the type. - /// - /// For instance, for Test<string, int>, first returns nullability for Test<string, int>, then string, then int. - /// - /// - /// See . - /// - /// - public static IEnumerable<(Type Type, Nullability Nullable)> GetNullabilityInformation(this ParameterInfo parameter) - { - if (parameter == null) - throw new ArgumentNullException(nameof(parameter)); - - var (nullableContext, nullabilityBytes) = GetMethodParameterNullability(parameter); - var list = new List<(Type, Nullability)>(); - var index = 0; - Consider(parameter.ParameterType, false, list, nullabilityBytes, nullableContext, ref index); - if (nullabilityBytes != null && nullabilityBytes.Count != index) - throw new NullabilityInterpretationException(parameter); - return list; - } - - /// - /// Examines the property type and walks the type definitions, for each returning the nullability information for the type. - /// - /// For instance, for Test<string, int>, first returns nullability for Test<string, int>, then string, then int. - /// - /// - /// See . - /// - /// - public static IEnumerable<(Type Type, Nullability Nullable)> GetNullabilityInformation(this PropertyInfo property) - { - if (property == null) - throw new ArgumentNullException(nameof(property)); - - var (nullableContext, nullabilityBytes) = GetMemberNullability(property); - var list = new List<(Type, Nullability)>(); - var index = 0; - Consider(property.PropertyType, false, list, nullabilityBytes, nullableContext, ref index); - if (nullabilityBytes != null && nullabilityBytes.Count != index) - throw new NullabilityInterpretationException(property); - return list; - } - - private static void Consider(Type t, bool nullableValueType, List<(Type, Nullability)> list, IList? nullabilityBytes, Nullability nullableContext, ref int index) - { - if (t.IsValueType) { - if (t.IsGenericType) { - var g = t.GetGenericTypeDefinition(); - if (g == typeof(Nullable<>)) { - //do not increment index for Nullable - //do not add Nullable to the list but rather just add underlying type - Consider(t.GenericTypeArguments[0], true, list, nullabilityBytes, nullableContext, ref index); - } else { - //generic structs that are not Nullable will contain a 0 in the array - index++; - list.Add((t, nullableValueType ? Nullability.Nullable : Nullability.NonNullable)); - for (int i = 0; i < t.GenericTypeArguments.Length; i++) { - Consider(t.GenericTypeArguments[i], false, list, nullabilityBytes, nullableContext, ref index); - } - } - } else { - //do not increment index for concrete value types - list.Add((t, nullableValueType ? Nullability.Nullable : Nullability.NonNullable)); - } - return; - } - //pull the nullability flag from the array and increment index - var nullable = nullabilityBytes != null ? (Nullability)(byte)nullabilityBytes[index++].Value : nullableContext; - list.Add((t, nullable)); - - if (t.IsArray) { - Consider(t.GetElementType(), false, list, nullabilityBytes, nullableContext, ref index); - } else if (t.IsGenericType) { - for (int i = 0; i < t.GenericTypeArguments.Length; i++) { - Consider(t.GenericTypeArguments[i], false, list, nullabilityBytes, nullableContext, ref index); - } - } - } - - private static Nullability GetMethodDefaultNullability(MemberInfo method) - { - var nullable = Nullability.Unknown; - - // check the parent type first to see if there's a nullable context attribute set for it - CheckDeclaringType(method.DeclaringType); - - // now check the method to see if there's a nullable context attribute set for it - var attribute = method.CustomAttributes.FirstOrDefault(x => - x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute" && - x.ConstructorArguments.Count == 1 && - x.ConstructorArguments[0].ArgumentType == typeof(byte)); - if (attribute != null) { - nullable = (Nullability)(byte)attribute.ConstructorArguments[0].Value; - } - - return nullable; - - void CheckDeclaringType(Type parentType) - { - if (parentType.DeclaringType != null) - CheckDeclaringType(parentType.DeclaringType); - var attribute = parentType.CustomAttributes.FirstOrDefault(x => - x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute" && - x.ConstructorArguments.Count == 1 && - x.ConstructorArguments[0].ArgumentType == typeof(byte)); - if (attribute != null) { - nullable = (Nullability)(byte)attribute.ConstructorArguments[0].Value; - } - } - } - } -} diff --git a/src/GraphQL.DI/RequiredAttribute.cs b/src/GraphQL.DI/RequiredAttribute.cs deleted file mode 100644 index 0f3310b..0000000 --- a/src/GraphQL.DI/RequiredAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace GraphQL.DI -{ - /// - /// Marks a method's (field's) return value as always non-null, or - /// marks a parameter's (query argument's) input value to be required. - /// - //perhaps this should apply to ReturnValue instead of Method - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class RequiredAttribute : Attribute - { - } -} diff --git a/src/GraphQL.DI/RequiredListAttribute.cs b/src/GraphQL.DI/RequiredListAttribute.cs deleted file mode 100644 index e8b7398..0000000 --- a/src/GraphQL.DI/RequiredListAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace GraphQL.DI -{ - /// - /// Marks a method's (field's) return value as always non-null, or - /// marks a parameter's (query argument's) input value to be required. - /// - //perhaps this should apply to ReturnValue instead of Method - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class RequiredListAttribute : Attribute - { - } -} diff --git a/src/GraphQL.DI/Shane32.GraphQL.DI.csproj b/src/GraphQL.DI/Shane32.GraphQL.DI.csproj index 96e54ef..4a5f6f2 100644 --- a/src/GraphQL.DI/Shane32.GraphQL.DI.csproj +++ b/src/GraphQL.DI/Shane32.GraphQL.DI.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/GraphQL.DI/TypeInformation.cs b/src/GraphQL.DI/TypeInformation.cs deleted file mode 100644 index 3ec7b92..0000000 --- a/src/GraphQL.DI/TypeInformation.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using GraphQL.Types; - -namespace GraphQL.DI -{ - /// - /// Contains type and nullability information for a method return type or argument type. - /// - public struct TypeInformation - { - /// - /// The argument or return parameter of the method being inspected. - /// - public ParameterInfo? ParameterInfo; - - /// - /// The member being inspected. - /// - public MemberInfo MemberInfo; - - /// - /// Indicates that this is an input type (an argument); false for output types. - /// - public bool IsInputType; - - /// - /// The underlying type represented. This might be the underlying type of a - /// or the underlying type of a . - /// - public Type Type; - - /// - /// Indicates if the underlying type is nullable. - /// - public bool IsNullable; - - /// - /// Indicates that this represents a list of elements. - /// - public bool IsList; - - /// - /// Indicates if the list is nullable. - /// - public bool ListIsNullable; - - /// - /// The graph type of the underlying CLR type. - /// - public Type? GraphType; - - /// - /// Initializes an instance with the specified properties. - /// - /// The argument or return parameter of the method being inspected. - /// Indicates that this is an input type (an argument); false for output types. - /// The underlying type. - /// Indicates that the underlying type is nullable. - /// Indicates that this represents a list of elements. - /// Indicates that the list is nullable. - /// The graph type of the underlying CLR type; null if not specified. - public TypeInformation(ParameterInfo parameterInfo, bool isInputType, Type type, bool isNullable, bool isList, bool listIsNullable, Type? graphType) - { - ParameterInfo = parameterInfo; - MemberInfo = parameterInfo.Member; - IsInputType = isInputType; - Type = type; - IsNullable = isNullable; - IsList = isList; - ListIsNullable = listIsNullable; - GraphType = graphType; - } - - /// - /// Initializes an instance with the specified properties. - /// - /// The member being inspected. - /// Indicates that this is an input type (an argument); false for output types. - /// The underlying type. - /// Indicates that the underlying type is nullable. - /// Indicates that this represents a list of elements. - /// Indicates that the list is nullable. - /// The graph type of the underlying CLR type; null if not specified. - public TypeInformation(MemberInfo memberInfo, bool isInputType, Type type, bool isNullable, bool isList, bool listIsNullable, Type? graphType) - { - ParameterInfo = null; - MemberInfo = memberInfo; - IsInputType = isInputType; - Type = type; - IsNullable = isNullable; - IsList = isList; - ListIsNullable = listIsNullable; - GraphType = graphType; - } - - internal Type InferGraphType() - { - var t = GraphType; - if (t != null) { - if (!IsNullable) - t = typeof(NonNullGraphType<>).MakeGenericType(t); - } else { - t = Type.GetGraphTypeFromType(IsNullable, IsInputType ? TypeMappingMode.InputType : TypeMappingMode.OutputType); - } - if (IsList) { - t = typeof(ListGraphType<>).MakeGenericType(t); - if (!ListIsNullable) - t = typeof(NonNullGraphType<>).MakeGenericType(t); - } - return t; - } - - /// - /// Returns a new instance with , , , - /// , and - /// applied as necessary. - /// - internal TypeInformation ApplyAttributes(ICustomAttributeProvider member) - { - var typeInformation = this; //copy struct - //var member = examineParent ? (ICustomAttributeProvider)typeInformation.ParameterInfo.Member : typeInformation.ParameterInfo; - if (typeInformation.IsNullable) { - if (member.IsDefined(typeof(RequiredAttribute), false)) - typeInformation.IsNullable = false; - if (member.IsDefined(typeof(System.ComponentModel.DataAnnotations.RequiredAttribute), false)) - typeInformation.IsNullable = false; - } else { - if (member.IsDefined(typeof(OptionalAttribute), false)) - typeInformation.IsNullable = true; - } - if (typeInformation.IsList) { - if (typeInformation.ListIsNullable) { - if (member.IsDefined(typeof(RequiredListAttribute), false)) - typeInformation.ListIsNullable = false; - } else { - if (member.IsDefined(typeof(OptionalListAttribute), false)) - typeInformation.ListIsNullable = true; - } - } - if (member.IsDefined(typeof(IdAttribute), false)) - typeInformation.GraphType = typeof(IdGraphType); - else if (member.GetCustomAttributes(typeof(DIGraphAttribute), false).SingleOrDefault() is DIGraphAttribute diGraphAttribute) { - var iface = diGraphAttribute.GraphBaseType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDIObjectGraphBase<>)); - if (iface == null) { - throw new InvalidOperationException($"Member '{typeInformation.MemberInfo.DeclaringType.Name}.{typeInformation.MemberInfo.Name}' is marked with [DIGraph] specifying type '{diGraphAttribute.GraphBaseType.Name}' which does not inherit {nameof(IDIObjectGraphBase)}."); - } - typeInformation.GraphType = typeof(DIObjectGraphType<,>).MakeGenericType(diGraphAttribute.GraphBaseType, iface.GetGenericArguments()[0]); - } - return typeInformation; - } - - } -} diff --git a/src/Tests/AutoInputObjectGraphTypeTests/Graph.cs b/src/Tests/AutoInputObjectGraphTypeTests/Graph.cs deleted file mode 100644 index bb447a7..0000000 --- a/src/Tests/AutoInputObjectGraphTypeTests/Graph.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Reflection; -using GraphQL.DI; -using GraphQL.Types; -using Shouldly; -using Xunit; - -namespace AutoInputObjectGraphTypeTests -{ - public class Graph - { - private IInputObjectGraphType _graphType; - - private IInputObjectGraphType Configure() - { - _graphType = new AutoInputObjectGraphType(); - return _graphType; - } - - [Fact] - public void GraphName() - { - Configure().Name.ShouldBe("TestGraphName"); - } - - [Name("TestGraphName")] - public class CGraphName { } - - [Fact] - public void DefaultGraphName() - { - Configure().Name.ShouldBe("CDefault"); - } - - public class CDefault { } - - [Fact] - public void DefaultGraphNameInputModel() - { - Configure().Name.ShouldBe("CDefaultInput"); - } - - public class CDefaultInputModel { } - - [Fact] - public void GraphDescription() - { - Configure().Description.ShouldBe("TestGraphDescription"); - } - - [Description("TestGraphDescription")] - public class CGraphDescription { } - - [Fact] - public void GraphObsolete() - { -#pragma warning disable CS0618 // Member is obsolete - Configure().DeprecationReason.ShouldBe("TestDeprecationReason"); -#pragma warning restore CS0618 // Member is obsolete - } - - [Obsolete("TestDeprecationReason")] - public class CGraphObsolete { } - - [Fact] - public void GraphMetadata() - { - Configure().GetMetadata("test").ShouldBe("value"); - } - - [Metadata("test", "value")] - public class CGraphMetadata { } - - [Fact] - public void CanOverrideMembers() - { - var test = new CCanOverrideMembersGraphType(); - test.Fields.Count.ShouldBe(0); - } - - public class CCanOverrideMembersGraphType : AutoInputObjectGraphType - { - protected override IEnumerable GetPropertiesToProcess() => Array.Empty(); - } - - public class CCanOverrideMembers - { - public static string Field1() => "hello"; - } - } -} diff --git a/src/Tests/AutoInputObjectGraphTypeTests/Property.cs b/src/Tests/AutoInputObjectGraphTypeTests/Property.cs deleted file mode 100644 index 9e465b4..0000000 --- a/src/Tests/AutoInputObjectGraphTypeTests/Property.cs +++ /dev/null @@ -1,180 +0,0 @@ -#nullable enable - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using GraphQL; -using GraphQL.DI; -using GraphQL.Types; -using Moq; -using Shouldly; -using Xunit; - -namespace AutoInputObjectGraphTypeTests -{ - public class Property - { - private readonly IInputObjectGraphType _graphType = new AutoInputObjectGraphType(); - - public class CDefault - { - public string? Field1 { get; set; } - public string Field2 { get; set; } = null!; - public int? Field3 { get; set; } - public int Field4 { get; set; } - public List Field5 { get; set; } = null!; - public List? Field6 { get; set; } - public string[] Field7 { get; set; } = null!; - [Required] - public string? Field8 { get; set; } - [Optional] - public string Field9 { get; set; } = null!; - [OptionalList] - public List Field10 { get; set; } = null!; - [RequiredList] - public List? Field11 { get; set; } - [GraphType(typeof(GuidGraphType))] - public object? Field12 { get; set; } - [Name("Field14")] - public string? Field13 { get; set; } - [Ignore] - public string? Field15 { get; set; } - [DefaultValue(5)] - public int Field16 { get; set; } - [Description("Example")] - public int Field17 { get; set; } - [Obsolete("Example")] - public int Field18 { get; set; } - [Id] - public int Field19 { get; set; } - public int Field20 => 3; - [Name(null!)] - public int Field21 { get; set; } - public int Field22() => 3; - [Metadata("test", 3)] - [Metadata("test2", 5)] - public int Field23 { get; set; } - public IEnumerable? Field24 { get; set; } - public ICollection Field25 { get; set; } = null!; - public Class1 Field26 { get; set; } = null!; - public int?[]? Field27 { get; set; } - } - - public class Class1 { } - - [Theory] - [InlineData("Field1", typeof(string), true)] - [InlineData("Field2", typeof(string), false)] - [InlineData("Field3", typeof(int), true)] - [InlineData("Field4", typeof(int), false)] - [InlineData("Field5", typeof(string), true, true, false)] - [InlineData("Field6", typeof(string), false, true, true)] - [InlineData("Field7", typeof(string), false, true, false)] - [InlineData("Field8", typeof(string), false)] - [InlineData("Field9", typeof(string), true)] - [InlineData("Field10", typeof(string), false, true, true)] - [InlineData("Field11", typeof(string), true, true, false)] - [InlineData("Field12", null, true, false, false, typeof(GuidGraphType))] - [InlineData("Field13", null, true)] - [InlineData("Field14", typeof(string), true)] - [InlineData("Field15", null, true)] - [InlineData("Field16", typeof(int), false)] - [InlineData("Field17", typeof(int), false)] - [InlineData("Field18", typeof(int), false)] - [InlineData("Field19", null, true, false, false, typeof(NonNullGraphType))] - [InlineData("Field20", null, true)] - [InlineData("Field21", null, true)] - [InlineData("Field22", null, true)] - [InlineData("Field23", typeof(int), false)] - [InlineData("Field24", typeof(object), true, true, true)] - [InlineData("Field25", typeof(object), true, true, false)] - [InlineData("Field26", typeof(Class1), false)] - [InlineData("Field27", typeof(int), true, true, true)] - public void Types(string name, Type? fieldType, bool isNullable, bool isList = false, bool listIsNullable = false, Type? graphType = null) - { - if (graphType == null && fieldType != null) { - graphType = fieldType.GetGraphTypeFromType(isNullable, TypeMappingMode.InputType); - if (isList) { - graphType = typeof(ListGraphType<>).MakeGenericType(graphType); - if (!listIsNullable) { - graphType = typeof(NonNullGraphType<>).MakeGenericType(graphType); - } - } - } - var field = _graphType.Fields.Find(name); - if (graphType == null) { - field.ShouldBeNull(); - return; - } - field.ShouldNotBeNull(); - field.Type.ShouldBe(graphType); - field.Resolver.ShouldBe(null); - } - - [Fact] - public void DefaultValue() - { - var field = _graphType.Fields.Find("Field16"); - field.ShouldNotBeNull(); - field.DefaultValue.ShouldBeOfType().ShouldBe(5); - } - - [Fact] - public void Description() - { - var field = _graphType.Fields.Find("Field17"); - field.ShouldNotBeNull(); - field.Description.ShouldBe("Example"); - } - - [Fact] - public void Obsolete() - { - var field = _graphType.Fields.Find("Field18"); - field.ShouldNotBeNull(); - field.DeprecationReason.ShouldBe("Example"); - } - - [Fact] - public void Metadata() - { - var field = _graphType.Fields.Find("Field23"); - field.ShouldNotBeNull(); - field.Metadata.ShouldNotBeNull(); - field.Metadata.ShouldContainKeyAndValue("test", 3); - field.Metadata.ShouldContainKeyAndValue("test2", 5); - } - - [Theory] - [InlineData("Field1", "Field1")] - [InlineData("Field14", "Field13")] - public void Expression(string graphFieldName, string classPropertyName) - { - var field = _graphType.Fields.Find(graphFieldName); - field.ShouldNotBeNull(); - field.Metadata.ShouldNotBeNull(); - field.Metadata.ShouldContainKey("ORIGINAL_EXPRESSION_PROPERTY_NAME"); - var actual = field.Metadata["ORIGINAL_EXPRESSION_PROPERTY_NAME"] - .ShouldBeOfType(); - actual.ShouldBe(classPropertyName); - } - - [Fact] - public void ExpressionCorrectlyResolves() - { - var graph = new AutoInputObjectGraphType(); - var dic = new Dictionary { - { "Field2", "value" } - }; - var actual = (Class2)graph.ParseDictionary(dic); - actual.Field1.ShouldBe("value"); - } - - public class Class2 - { - [Name("Field2")] - public string? Field1 { get; set; } - } - } -} diff --git a/src/Tests/AutoObjectGraphTypeTests/Argument.cs b/src/Tests/AutoObjectGraphTypeTests/Argument.cs deleted file mode 100644 index f9d1bb2..0000000 --- a/src/Tests/AutoObjectGraphTypeTests/Argument.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System.ComponentModel; -using System.Threading; -using GraphQL.DI; -using GraphQL.Types; -using Shouldly; -using Xunit; - -namespace AutoObjectGraphTypeTests -{ - public class Argument : AutoObjectGraphTypeTestBase - { - [Fact] - public void NonNullValue() - { - Configure(); - VerifyFieldArgument("Field1", "arg", false, 2); - VerifyField("Field1", false, false, 2); - Verify(false); - } - - public class CNonNullValue - { - public static int Field1(int arg) => arg; - } - - [Fact] - public void NullableValue() - { - Configure(); - VerifyFieldArgument("Field1", "arg", true, (int?)2); - VerifyField("Field1", true, false, 2); - Verify(false); - } - - public class CNullableValue - { - public static int? Field1(int? arg) => arg; - } - - [Fact] - public void NullableValueExplicit() - { - Configure(); - VerifyFieldArgument("Field1", "arg", true, (int?)2); - VerifyField("Field1", true, false, 2); - Verify(false); - } - - public class CNullableValueExplicit - { - public static int? Field1([Optional] int arg) => arg; - } - - [Fact] - public void NullableObject() - { - Configure(); - VerifyFieldArgument("Field1", "arg", true, "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CNullableObject - { - public static string Field1([Optional] string arg) => arg; - } - - [Fact] - public void NonNullableObject() - { - Configure(); - VerifyFieldArgument("Field1", "arg", false, "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CNonNullableObject - { - public static string Field1([Required] string arg) => arg; - } - - [Fact] - public void NonNullableObject2() - { - Configure(); - VerifyFieldArgument("Field1", "arg", false, "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CNonNullableObject2 - { - public static string Field1([System.ComponentModel.DataAnnotations.Required] string arg) => arg; - } - - [Fact] - public void Name() - { - Configure(); - VerifyFieldArgument("Field1", "AltName", true, "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CName - { - public static string Field1([Name("AltName")] string arg) => arg; - } - - [Fact] - public void Description() - { - Configure(); - var arg = VerifyFieldArgument("Field1", "arg", true, "hello"); - arg.Description.ShouldBe("TestDescription"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CDescription - { - public static string Field1([Description("TestDescription")] string arg) => arg; - } - - [Fact] - public void GraphType() - { - Configure(); - VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CGraphType - { - public static string Field1([GraphType(typeof(IdGraphType))] string arg) => arg; - } - - [Fact] - public void GraphTypeInherited() - { - Configure(); - VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CGraphTypeInherited - { - public static string Field1([MyIdGraphType] string arg) => arg; - } - - public class MyIdGraphTypeAttribute : GraphTypeAttribute - { - public MyIdGraphTypeAttribute() : base(typeof(IdGraphType)) { } - } - - [Fact] - public void Id() - { - Configure(); - VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CIdGraphType - { - public static string Field1([Id] string arg) => arg; - } - - [Fact] - public void IdNonNull() - { - Configure(); - VerifyFieldArgument("Field1", "arg", typeof(NonNullGraphType), "3"); - VerifyField("Field1", true, false, "3"); - Verify(false); - } - - public class CIdGraphType2 - { - public static string Field1([Id] int arg) => arg.ToString(); - } - - [Fact] - public void CancellationToken() - { - using var cts = new CancellationTokenSource(); - var token = cts.Token; - cts.Cancel(); - Configure(); - _contextMock.SetupGet(x => x.CancellationToken).Returns(token).Verifiable(); - VerifyField("Field1", true, false, "hello + True"); - Verify(false); - } - - public class CCancellationToken - { - public static string Field1(CancellationToken cancellationToken) => "hello + " + cancellationToken.IsCancellationRequested; - } - - [Fact] - public void NullNameArgument() - { - Configure(); - var field = VerifyField("Field1", true, false, "hello + 0"); - field.Arguments.Count.ShouldBe(0); - Verify(false); - } - - public class CNullNameArgument - { - public static string Field1([Name(null)] int arg) => "hello + " + arg; - } - - [Fact] - public void DefaultValue() - { - Configure(); - VerifyFieldArgument("Field1", "arg1", true, 2); - VerifyField("Field1", false, false, 2); - VerifyFieldArgument("Field2", "arg2", true); - VerifyField("Field2", false, false, 5); - Verify(false); - } - - public class CDefaultValue - { - public static int Field1(int arg1 = 4) => arg1; - public static int Field2(int arg2 = 5) => arg2; - } - - } -} diff --git a/src/Tests/AutoObjectGraphTypeTests/AutoObjectGraphTypeTestBase.cs b/src/Tests/AutoObjectGraphTypeTests/AutoObjectGraphTypeTestBase.cs deleted file mode 100644 index fc52909..0000000 --- a/src/Tests/AutoObjectGraphTypeTests/AutoObjectGraphTypeTestBase.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using GraphQL; -using GraphQL.DI; -using GraphQL.Execution; -using GraphQL.Types; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Shouldly; - -namespace AutoObjectGraphTypeTests -{ - public class AutoObjectGraphTypeTestBase - { - protected object _source; - protected IComplexGraphType _graphType; - protected readonly Mock _scopedServiceProviderMock; - protected readonly Mock _scopeMock; - protected readonly Mock _scopeFactoryMock; - protected readonly Mock _serviceProviderMock; - protected readonly Mock _contextMock; - protected readonly Dictionary _arguments = new(); - - public AutoObjectGraphTypeTestBase() : base() - { - _scopedServiceProviderMock = new Mock(MockBehavior.Strict); - _scopeMock = new Mock(MockBehavior.Strict); - _scopeMock.Setup(x => x.Dispose()); - _scopeMock.SetupGet(x => x.ServiceProvider).Returns(_scopedServiceProviderMock.Object); - _scopeFactoryMock = new Mock(MockBehavior.Strict); - _scopeFactoryMock.Setup(x => x.CreateScope()).Returns(_scopeMock.Object); - _serviceProviderMock = new Mock(MockBehavior.Strict); - _serviceProviderMock.Setup(x => x.GetService(typeof(IServiceScopeFactory))).Returns(_scopeFactoryMock.Object); - _contextMock = new Mock(MockBehavior.Strict); - _contextMock.SetupGet(x => x.RequestServices).Returns(_serviceProviderMock.Object); - _contextMock.SetupGet(x => x.Source).Returns(() => _source!); - _contextMock.SetupGet(x => x.ParentType).Returns(new ObjectGraphType()); - _contextMock.SetupGet(x => x.Arguments).Returns(() => _arguments); - _contextMock.SetupGet(x => x.Schema).Returns((ISchema)null); - } - - protected IComplexGraphType Configure() where TSource : new() - => Configure(new TSource()); - - protected IComplexGraphType Configure(TSource value) - { - _source = value; - _graphType = new AutoObjectGraphType(); - return _graphType; - } - - protected FieldType VerifyField(string fieldName, bool nullable, bool concurrent, T returnValue) - { - return VerifyField(fieldName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.OutputType), concurrent, returnValue); - } - - protected FieldType VerifyField(string fieldName, Type fieldGraphType, bool concurrent, T returnValue) - { - var context = _contextMock.Object; - _graphType.ShouldNotBeNull(); - _graphType.Fields.ShouldNotBeNull(); - var field = _graphType.Fields.Find(fieldName); - field.ShouldNotBeNull(); - field.Type.ShouldBe(fieldGraphType); - field.ShouldBeOfType().Concurrent.ShouldBe(concurrent); - field.Resolver.ShouldNotBeNull(); - _contextMock.Setup(x => x.FieldDefinition).Returns(field); - field.Resolver.Resolve(context).ShouldBe(returnValue); - _arguments.Clear(); - return field; - } - - protected async Task VerifyFieldAsync(string fieldName, bool nullable, bool concurrent, T returnValue) - { - var context = _contextMock.Object; - _graphType.ShouldNotBeNull(); - _graphType.Fields.ShouldNotBeNull(); - var field = _graphType.Fields.Find(fieldName); - field.ShouldNotBeNull(); - field.Type.ShouldBe(typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.OutputType)); - field.ShouldBeOfType().Concurrent.ShouldBe(concurrent); - field.Resolver.ShouldNotBeNull(); - _contextMock.Setup(x => x.FieldDefinition).Returns(field); - var ret = field.Resolver.Resolve(context); - var taskRet = ret.ShouldBeOfType>(); - var final = await taskRet; - final.ShouldBe(returnValue); - _arguments.Clear(); - return field; - } - - protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, bool nullable, T returnValue) - { - return VerifyFieldArgument(fieldName, argumentName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.InputType), returnValue); - } - - protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, bool nullable) - { - return VerifyFieldArgument(fieldName, argumentName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.InputType)); - } - - protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, Type graphType) - { - _graphType.ShouldNotBeNull(); - _graphType.Fields.ShouldNotBeNull(); - var field = _graphType.Fields.Find(fieldName); - field.ShouldNotBeNull(); - field.Arguments.ShouldNotBeNull(); - var argument = field.Arguments.Find(argumentName); - argument.ShouldNotBeNull(); - argument.Type.ShouldBe(graphType); - return argument; - } - - protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, Type graphType, T returnValue) - { - var argument = VerifyFieldArgument(fieldName, argumentName, graphType); - _arguments.Add(argumentName, new ArgumentValue(returnValue, ArgumentSource.FieldDefault)); - return argument; - } - - protected void Verify(bool scoped) - { - if (scoped) { - _contextMock.Verify(x => x.RequestServices, Times.Once); - _serviceProviderMock.Verify(x => x.GetService(typeof(IServiceScopeFactory)), Times.Once); - _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Once); - _scopeMock.Verify(x => x.ServiceProvider, Times.Once); - } else { - _serviceProviderMock.Verify(x => x.GetService(typeof(IServiceScopeFactory)), Times.Never); - } - _contextMock.Verify(); - _serviceProviderMock.Verify(); - _scopeMock.Verify(); - _scopeFactoryMock.Verify(); - _scopedServiceProviderMock.Verify(); - } - } -} diff --git a/src/Tests/AutoObjectGraphTypeTests/Field.cs b/src/Tests/AutoObjectGraphTypeTests/Field.cs deleted file mode 100644 index 4d85f88..0000000 --- a/src/Tests/AutoObjectGraphTypeTests/Field.cs +++ /dev/null @@ -1,683 +0,0 @@ -using System; -using System.ComponentModel; -using System.Reflection; -using System.Threading.Tasks; -using GraphQL; -using GraphQL.DataLoader; -using GraphQL.DI; -using GraphQL.Types; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Shouldly; -using Xunit; - -namespace AutoObjectGraphTypeTests -{ - public class Field : AutoObjectGraphTypeTestBase - { - [Fact] - public void StaticMethod() - { - Configure(); - VerifyField("Field1", nullable: true, concurrent: false, returnValue: "hello"); - Verify(false); - } - - public class CStaticMethod - { - public static string Field1() => "hello"; - } - - [Fact] - public void InstanceMethod() - { - Configure(); - VerifyField("Field1", nullable: true, concurrent: false, returnValue: "hello"); - Verify(false); - } - - public class CInstanceMethod - { - public string Field1() => "hello"; - } - - [Fact] - public async Task StaticAsyncMethod() - { - Configure(); - await VerifyFieldAsync("Field1", nullable: true, concurrent: true, returnValue: "hello"); - Verify(false); - } - - public class CStaticAsyncMethod - { - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public async Task InstanceAsyncMethod() - { - Configure(); - await VerifyFieldAsync("Field1", nullable: true, concurrent: false, returnValue: "hello"); - Verify(false); - } - - public class CInstanceAsyncMethod - { - public Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public void Name() - { - Configure(); - VerifyField("FieldTest", true, false, "hello"); - Verify(false); - } - - public class CName - { - [Name("FieldTest")] - public static string Field1() => "hello"; - } - - [Fact] - public void Description() - { - Configure(); - VerifyField("Field1", true, false, "hello").Description.ShouldBe("DescriptionTest"); - Verify(false); - } - - public class CDescription - { - [Description("DescriptionTest")] - public static string Field1() => "hello"; - } - - [Fact] - public void Obsolete() - { - Configure(); - VerifyField("Field1", true, false, "hello").DeprecationReason.ShouldBe("ObsoleteTest"); - Verify(false); - } - - public class CObsolete - { - [Obsolete("ObsoleteTest")] - public static string Field1() => "hello"; - } - - [Fact] - public void Context() - { - Configure(); - _source = "testSource"; - VerifyField("Field1", true, false, "testSource"); - _contextMock.Verify(x => x.Source, Times.Once); - Verify(false); - } - - public class CContext - { - public static string Field1(IResolveFieldContext context) => (string)context.Source; - } - - [Fact] - public void Context_Typed() - { - Configure(); - _source = "testSource"; - VerifyField("Field1", true, false, "testSource"); - _contextMock.Verify(x => x.Source, Times.AtLeastOnce); - Verify(false); - } - - public class CContext_Typed - { - public static string Field1(IResolveFieldContext context) => (string)context.Source; - } - - [Fact] - public void Context_WrongType() - { - Should.Throw(() => Configure()); - } - - public class CContext_WrongType - { - public static string Field1(IResolveFieldContext context) => context.Source; - } - - [Fact] - public void Source() - { - Configure(); - _source = new CSource() { Value = "testSource" }; - VerifyField("Field1", true, false, "testSource"); - _contextMock.Verify(x => x.Source, Times.Once); - Verify(false); - } - - public class CSource - { - public string Value; - public static string Field1([FromSource] CSource source) => source.Value; - } - - [Fact] - public void Source2() - { - Configure(); - _source = new CSource2() { Value = "testSource" }; - VerifyField("Field1", true, false, "testSource"); - _contextMock.Verify(x => x.Source, Times.Once); - Verify(false); - } - - public class CSource2 - { - public string Value; - public string Field1() => Value; - } - - [Fact] - public void Source_WrongType() - { - Should.Throw(() => Configure()); - } - - public class CSource_WrongType - { - public static string Field1([FromSource] string source) => source; - } - - [Fact] - public void ServiceProvider() - { - Configure(); - _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CServiceProvider - { - public static string Field1(IServiceProvider serviceProvider) => serviceProvider.GetRequiredService(); - } - - [Fact] - public async Task ServiceProviderForScoped() - { - Configure(); - _scopedServiceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(true); - } - - public class CServiceProviderForScoped - { - [Concurrent(true)] - public static Task Field1(IServiceProvider serviceProvider) => Task.FromResult(serviceProvider.GetRequiredService()); - } - - [Fact] - public void NullableValue() - { - Configure(); - VerifyField("Field1", true, false, (int?)null); - Verify(false); - } - - public class CNullableValue - { - public static int? Field1() => null; - } - - [Fact] - public void NullableValueExplicit() - { - Configure(); - VerifyField("Field1", true, false, (int?)1); - Verify(false); - } - - public class CNullableValueExplicit - { - [Optional] - public static int Field1() => 1; - } - - [Fact] - public void NonNullableValue() - { - Configure(); - VerifyField("Field1", false, false, 1); - Verify(false); - } - - public class CNonNullableValue - { - public static int Field1() => 1; - } - - [Fact] - public void Required() - { - Configure(); - VerifyField("Field1", false, false, "hello"); - Verify(false); - } - - public class CRequired - { - [Required] - public static string Field1() => "hello"; - } - - [Fact] - public void InheritedRequired() - { - Configure(); - VerifyField("Field1", false, false, "hello"); - Verify(false); - } - - public class CInheritedRequired - { - [MyRequired] - public static string Field1() => "hello"; - } - - public class MyRequiredAttribute : RequiredAttribute { } - - [Fact] - public async Task RequiredTask() - { - Configure(); - await VerifyFieldAsync("Field1", false, true, "hello"); - Verify(false); - } - - public class CRequiredTask - { - [Required] - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public void CustomType() - { - Configure(); - VerifyField("Field1", typeof(IdGraphType), false, "hello"); - Verify(false); - } - - public class CCustomType - { - [GraphType(typeof(IdGraphType))] - public static string Field1() => "hello"; - } - - [Fact] - public void InheritedCustomType() - { - Configure(); - VerifyField("Field1", typeof(IdGraphType), false, "hello"); - Verify(false); - } - - public class CInheritedCustomType - { - [MyIdGraphType] - public static string Field1() => "hello"; - } - - public class MyIdGraphTypeAttribute : GraphTypeAttribute - { - public MyIdGraphTypeAttribute() : base(typeof(IdGraphType)) { } - } - - [Fact] - public void IdType() - { - Configure(); - VerifyField("Field1", typeof(IdGraphType), false, "hello"); - Verify(false); - } - - public class CIdType - { - [Id] - public static string Field1() => "hello"; - } - - [Fact] - public void IdTypeNonNull() - { - Configure(); - VerifyField("Field1", typeof(NonNullGraphType), false, 2); - Verify(false); - } - - public class CIdTypeNonNull - { - [Id] - public static int Field1() => 2; - } - - [Fact] - public void IdListType() - { - Configure(); - VerifyField("Field1", typeof(ListGraphType), false, new[] { "hello" }); - Verify(false); - } - - public class CIdListType - { - [Id] - public static string[] Field1() => new[] { "hello" }; - } - - [Fact] - public void DIGraphType() - { - Configure(); - VerifyField("Field1", typeof(DIObjectGraphType), false, "hello"); - Verify(false); - } - - public class CDIGraphType2 : DIObjectGraphBase - { - public static string Field1() => "hello"; - } - - public class CDIGraphType - { - [DIGraph(typeof(CDIGraphType2))] - public static string Field1() => "hello"; - } - - [Fact] - public void DIGraphTypeNonNull() - { - Configure(); - VerifyField("Field1", typeof(NonNullGraphType>), false, 2); - Verify(false); - } - - public class CDIGraphTypeNonNull - { - [DIGraph(typeof(CDIGraphType2))] - public static int Field1() => 2; - } - - [Fact] - public void DIGraphListType() - { - Configure(); - VerifyField("Field1", typeof(ListGraphType>), false, new[] { "hello" }); - Verify(false); - } - - public class CDIGraphListType - { - [DIGraph(typeof(CDIGraphType2))] - public static string[] Field1() => new[] { "hello" }; - } - - [Fact] - public void DIGraphTypeInvalid() - { - Should.Throw(() => Configure()); - } - - public class CDIGraphTypeInvalid - { - [DIGraph(typeof(string))] - public static string Field1() => "hello"; - } - - [Fact] - public async Task Concurrent() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(false); - } - - public class CConcurrent - { - [Concurrent] - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public void ConcurrentIgnoredForSynchronousMethods() - { - Configure(); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CConcurrentIgnoredForSynchronousMethods - { - [Concurrent] - public static string Field1() => "hello"; - } - - [Fact] - public async Task AlwaysConcurrentForStatic() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(false); - } - - public class CAlwaysConcurrentForStatic - { - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public async Task NotScopedWhenNoServices() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(false); - } - - public class CNotScopedWhenNoServices - { - [Concurrent(true)] - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public async Task AlwaysConcurrentForStaticUnlessService() - { - Configure(); - _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - await VerifyFieldAsync("Field1", true, false, "hello"); - Verify(false); - } - - public class CAlwaysConcurrentForStaticUnlessService - { - public static Task Field1([FromServices] string value) => Task.FromResult(value); - } - - [Fact] - public async Task ScopedOnlyWhenSpecifiedWithServices() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(true); - } - - public class CScopedOnlyWhenSpecified - { - [Concurrent(true)] - public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); - } - - [Fact] - public async Task InheritsConcurrent() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(true); - } - - [Concurrent(true)] - public class CInheritsConcurrent - { - public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); - } - - [Fact] - public async Task InheritedConcurrentOverridable() - { - Configure(); - await VerifyFieldAsync("Field1", true, false, "hello"); - Verify(false); - } - - [Concurrent(true)] - public class CInheritedConcurrentOverridable - { - [Concurrent(Concurrent = false)] - public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); - } - - [Fact] - public async Task RemoveAsyncFromName() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(false); - } - - public class CRemoveAsyncFromName - { - public static Task Field1Async() => Task.FromResult("hello"); - } - - [Fact] - public void SkipVoidMembers() - { - Configure(); - _graphType.Fields.Find("Field1").ShouldBeNull(); - _graphType.Fields.Find("Field2").ShouldBeNull(); - _graphType.Fields.Find("Field3").ShouldNotBeNull(); - } - - public class CSkipVoidMembers - { - public static Task Field1() => Task.CompletedTask; - public static void Field2() { } - public static string Field3() => null!; - } - - [Fact] - public void SkipNullName() - { - Configure(); - _graphType.Fields.Find("Field1").ShouldBeNull(); - _graphType.Fields.Find("Field2").ShouldNotBeNull(); - } - - public class CSkipNullName - { - [Name(null)] - public static string Field1() => "hello"; - public static string Field2() => "hello"; - } - - [Fact] - public void Ignore() - { - Configure(); - _graphType.Fields.Find("Field1").ShouldBeNull(); - _graphType.Fields.Find("Field2").ShouldNotBeNull(); - } - - public class CIgnore - { - [Ignore] - public static string Field1() => "hello"; - public static string Field2() => "hello"; - } - - [Fact] - public void AddsMetadata() - { - Configure(); - var field = VerifyField("Field1", true, false, "hello"); - field.GetMetadata("test").ShouldBe("value"); - Verify(false); - } - - public class CAddsMetadata - { - [Metadata("test", "value")] - public static string Field1() => "hello"; - } - - [Fact] - public void AddsInheritedMetadata() - { - Configure(); - var field = VerifyField("Field1", true, false, "hello"); - field.GetMetadata("test").ShouldBe("value2"); - Verify(false); - } - - public class CAddsInheritedMetadata - { - [InheritedMetadata("value2")] - public static string Field1() => "hello"; - } - - private class InheritedMetadata : MetadataAttribute - { - public InheritedMetadata(string value) : base("test", value) { } - } - - [Theory] - [InlineData("Field1", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field2", typeof(NonNullGraphType>))] - [InlineData("Field3", typeof(NonNullGraphType>))] - [InlineData("Field4", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field5", typeof(GraphQLClrOutputTypeReference))] - //[InlineData("Field6", typeof(GraphQLClrOutputTypeReference)] //Need to fix graphql-dotnet bug 2441 first - [InlineData("Field7", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field8", typeof(NonNullGraphType>))] - [InlineData("Field9", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field10", typeof(GraphQLClrOutputTypeReference))] - public void SupportsDataLoader(string fieldName, Type graphType) - { - Configure(); - VerifyField(fieldName, graphType, false, null); - Verify(false); - } - - public class CSupportsDataLoader - { - public IDataLoaderResult Field1() => null; - [Required] - public IDataLoaderResult Field2() => null; - public IDataLoaderResult Field3() => null; - [Optional] - public IDataLoaderResult Field4() => null; - public IDataLoaderResult Field5() => null; - public IDataLoaderResult Field6() => null; - public Task> Field7() => null; - public Task> Field8() => null; - public Task> Field9() => null; - public Task>> Field10() => null; - } - } -} diff --git a/src/Tests/AutoObjectGraphTypeTests/Graph.cs b/src/Tests/AutoObjectGraphTypeTests/Graph.cs deleted file mode 100644 index 24c6968..0000000 --- a/src/Tests/AutoObjectGraphTypeTests/Graph.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Reflection; -using GraphQL.DI; -using Shouldly; -using Xunit; - -namespace AutoObjectGraphTypeTests -{ - public class Graph : AutoObjectGraphTypeTestBase - { - [Fact] - public void GraphName() - { - Configure().Name.ShouldBe("TestGraphName"); - } - - [Name("TestGraphName")] - public class CGraphName { } - - [Fact] - public void GraphDefaultName() - { - Configure().Name.ShouldBe("CGraphDefaultName"); - } - - public class CGraphDefaultName { } - - [Fact] - public void GraphDescription() - { - Configure().Description.ShouldBe("TestGraphDescription"); - } - - [Description("TestGraphDescription")] - public class CGraphDescription { } - - [Fact] - public void GraphObsolete() - { -#pragma warning disable CS0618 // Member is obsolete - Configure().DeprecationReason.ShouldBe("TestDeprecationReason"); -#pragma warning restore CS0618 // Member is obsolete - } - - [Obsolete("TestDeprecationReason")] - public class CGraphObsolete { } - - [Fact] - public void GraphMetadata() - { - Configure().GetMetadata("test").ShouldBe("value"); - } - - [Metadata("test", "value")] - public class CGraphMetadata { } - - [Fact] - public void CanOverrideMembers() - { - var test = new CCanOverrideMembersGraphType(); - test.Fields.Count.ShouldBe(0); - } - - public class CCanOverrideMembersGraphType : AutoObjectGraphType - { - protected override IEnumerable GetMethodsToProcess() => Array.Empty(); - } - - public class CCanOverrideMembers - { - public static string Field1() => "hello"; - } - } -} diff --git a/src/Tests/AutoObjectGraphTypeTests/Property.cs b/src/Tests/AutoObjectGraphTypeTests/Property.cs deleted file mode 100644 index 1ca70c5..0000000 --- a/src/Tests/AutoObjectGraphTypeTests/Property.cs +++ /dev/null @@ -1,179 +0,0 @@ -#nullable enable - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using GraphQL; -using GraphQL.DI; -using GraphQL.Types; -using Moq; -using Shouldly; -using Xunit; - -namespace AutoObjectGraphTypeTests -{ - public class Property - { - private readonly IObjectGraphType _graphType = new AutoObjectGraphType(); - - public class CDefault - { - public string? Field1 { get; set; } = "1"; - public string Field2 { get; set; } = "2"; - public int? Field3 { get; set; } = 3; - public int Field4 { get; set; } = 4; - public List Field5 { get; set; } = null!; - public List? Field6 { get; set; } - public string[] Field7 { get; set; } = null!; - [Required] - public string? Field8 { get; set; } = "8"; - [Optional] - public string Field9 { get; set; } = "9"; - [OptionalList] - public List Field10 { get; set; } = null!; - [RequiredList] - public List? Field11 { get; set; } - [GraphType(typeof(GuidGraphType))] - public object? Field12 { get; set; } = 12f; - [Name("Field14")] - public string? Field13 { get; set; } = "13"; - [Ignore] - public string? Field15 { get; set; } = "15"; - [DefaultValue(5)] - public int Field16 { get; set; } = 16; - [Description("Example")] - public int Field17 { get; set; } = 17; - [Obsolete("Example")] - public int Field18 { get; set; } = 18; - [Id] - public int Field19 { get; set; } = 19; - public int Field20 => 3; - [Name(null!)] - public int Field21 { get; set; } - public int Field22() => 3; - [Metadata("test", 3)] - [Metadata("test2", 5)] - public int Field23 { get; set; } - public IEnumerable? Field24 { get; set; } - public ICollection Field25 { get; set; } = null!; - public Class1 Field26 { get; set; } = null!; - public int?[]? Field27 { get; set; } - public int Field28 { set { } } - } - - public class Class1 { } - - [Theory] - [InlineData("Field1", typeof(string), "1", true)] - [InlineData("Field2", typeof(string), "2", false)] - [InlineData("Field3", typeof(int), 3, true)] - [InlineData("Field4", typeof(int), 4, false)] - [InlineData("Field5", typeof(string), null, true, true, false)] - [InlineData("Field6", typeof(string), null, false, true, true)] - [InlineData("Field7", typeof(string), null, false, true, false)] - [InlineData("Field8", typeof(string), "8", false)] - [InlineData("Field9", typeof(string), "9", true)] - [InlineData("Field10", typeof(string), null, false, true, true)] - [InlineData("Field11", typeof(string), null, true, true, false)] - [InlineData("Field12", null, 12f, true, false, false, typeof(GuidGraphType))] - [InlineData("Field13", null, null, true)] - [InlineData("Field14", typeof(string), "13", true)] - [InlineData("Field15", null, "15", true)] - [InlineData("Field16", typeof(int), 16, false)] - [InlineData("Field17", typeof(int), 17, false)] - [InlineData("Field18", typeof(int), 18, false)] - [InlineData("Field19", null, 19, true, false, false, typeof(NonNullGraphType))] - [InlineData("Field20", typeof(int), 3, false)] - [InlineData("Field21", null, 0, true)] - [InlineData("Field22", typeof(int), 3, false)] - [InlineData("Field23", typeof(int), 0, false)] - [InlineData("Field24", typeof(object), null, true, true, true)] - [InlineData("Field25", typeof(object), null, true, true, false)] - [InlineData("Field26", typeof(Class1), null, false)] - [InlineData("Field27", typeof(int), null, true, true, true)] - [InlineData("Field28", null, null, true)] - public void Types(string name, Type? fieldType, object expectedResult, bool isNullable, bool isList = false, bool listIsNullable = false, Type? graphType = null) - { - if (graphType == null && fieldType != null) { - graphType = fieldType.GetGraphTypeFromType(isNullable, TypeMappingMode.OutputType); - if (isList) { - graphType = typeof(ListGraphType<>).MakeGenericType(graphType); - if (!listIsNullable) { - graphType = typeof(NonNullGraphType<>).MakeGenericType(graphType); - } - } - } - var field = _graphType.Fields.Find(name); - if (graphType == null) { - field.ShouldBeNull(); - return; - } - field.ShouldNotBeNull(); - field.Type.ShouldBe(graphType); - var obj = new CDefault(); - var mockResolveFieldContext = new Mock(MockBehavior.Loose); - mockResolveFieldContext.Setup(x => x.Source).Returns(obj); - field.Resolver.ShouldNotBeNull(); - var ret = field.Resolver.Resolve(mockResolveFieldContext.Object); - ret.ShouldBe(expectedResult); - } - - [Fact] - public void Description() - { - var field = _graphType.Fields.Find("Field17"); - field.ShouldNotBeNull(); - field.Description.ShouldBe("Example"); - } - - [Fact] - public void Obsolete() - { - var field = _graphType.Fields.Find("Field18"); - field.ShouldNotBeNull(); - field.DeprecationReason.ShouldBe("Example"); - } - - [Fact] - public void Metadata() - { - var field = _graphType.Fields.Find("Field23"); - field.ShouldNotBeNull(); - field.Metadata.ShouldNotBeNull(); - field.Metadata.ShouldContainKeyAndValue("test", 3); - field.Metadata.ShouldContainKeyAndValue("test2", 5); - } - - [Theory] - [InlineData("Field1", "Field1")] - [InlineData("Field14", "Field13")] - public void Expression(string graphFieldName, string classPropertyName) - { - var field = _graphType.Fields.Find(graphFieldName); - field.ShouldNotBeNull(); - field.Metadata.ShouldNotBeNull(); - field.Metadata.ShouldContainKey("ORIGINAL_EXPRESSION_PROPERTY_NAME"); - var actual = field.Metadata["ORIGINAL_EXPRESSION_PROPERTY_NAME"] - .ShouldBeOfType(); - actual.ShouldBe(classPropertyName); - } - - [Fact] - public void ExpressionCorrectlyResolves() - { - var graph = new AutoInputObjectGraphType(); - var dic = new Dictionary { - { "Field2", "value" } - }; - var actual = (Class2)graph.ParseDictionary(dic); - actual.Field1.ShouldBe("value"); - } - - public class Class2 - { - [Name("Field2")] - public string? Field1 { get; set; } - } - } -} diff --git a/src/Tests/AutoObjectGraphTypeTests/Services.cs b/src/Tests/AutoObjectGraphTypeTests/Services.cs deleted file mode 100644 index 8664aae..0000000 --- a/src/Tests/AutoObjectGraphTypeTests/Services.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.ComponentModel; -using System.Threading.Tasks; -using GraphQL.DI; -using GraphQL.Types; -using Shouldly; -using Xunit; - -namespace AutoObjectGraphTypeTests -{ - public class Services : AutoObjectGraphTypeTestBase - { - [Fact] - public void Service() - { - Configure(); - _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CService - { - public static string Field1([FromServices] string arg) => arg; - } - - [Fact] - public void ServiceMissingThrows() - { - Configure(); - _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns((string)null!).Verifiable(); - Should.Throw(() => VerifyField("Field1", true, false, "hello")); - Verify(false); - } - - [Fact] - public async Task ScopedService() - { - Configure(); - _scopedServiceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(true); - } - - public class CScopedService - { - [Concurrent(true)] - public static Task Field1([FromServices] string arg) => Task.FromResult(arg); - } - } -} diff --git a/src/Tests/DIObjectGraphBaseTests.cs b/src/Tests/DIObjectGraphBaseTests.cs index a6a77ea..330d19e 100644 --- a/src/Tests/DIObjectGraphBaseTests.cs +++ b/src/Tests/DIObjectGraphBaseTests.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading; using GraphQL; using GraphQL.DI; using GraphQL.Execution; using GraphQL.Instrumentation; -using GraphQL.Language.AST; using GraphQL.Types; +using GraphQLParser.AST; using Moq; using Shouldly; using Xunit; @@ -77,7 +76,7 @@ public void SourceTyped() [Fact] public void RFC_FieldAst() { - var fieldAst = new Field(default, default); + var fieldAst = new GraphQLField(); _mockContext.Setup(x => x.FieldAst).Returns(fieldAst); _graphContext.FieldAst.ShouldBe(fieldAst); } @@ -141,7 +140,7 @@ public void RFC_Schema() [Fact] public void RFC_Document() { - var obj = new Document(); + var obj = new GraphQLDocument(); _mockContext.Setup(x => x.Document).Returns(obj); _graphContext.Document.ShouldBe(obj); } @@ -149,7 +148,7 @@ public void RFC_Document() [Fact] public void RFC_Operation() { - var obj = new Operation(default, new SelectionSet()); + var obj = new GraphQLOperationDefinition(); _mockContext.Setup(x => x.Operation).Returns(obj); _graphContext.Operation.ShouldBe(obj); } @@ -157,7 +156,7 @@ public void RFC_Operation() [Fact] public void RFC_Variables() { - var obj = new Variables(); + var obj = new GraphQL.Validation.Variables(); _mockContext.Setup(x => x.Variables).Returns(obj); _graphContext.Variables.ShouldBe(obj); } @@ -197,17 +196,25 @@ public void RFC_ResponsePath() [Fact] public void RFC_SubFields() { - var obj = new Dictionary(); + var obj = new Dictionary(); _mockContext.Setup(x => x.SubFields).Returns(obj); _graphContext.SubFields.ShouldBe(obj); } [Fact] - public void RFC_Extensions() + public void RFC_InputExtensions() + { + var obj = Mock.Of>(); + _mockContext.Setup(x => x.InputExtensions).Returns(obj); + _graphContext.InputExtensions.ShouldBe(obj); + } + + [Fact] + public void RFC_OutputExtensions() { var obj = Mock.Of>(); - _mockContext.Setup(x => x.Extensions).Returns(obj); - _graphContext.Extensions.ShouldBe(obj); + _mockContext.Setup(x => x.OutputExtensions).Returns(obj); + _graphContext.OutputExtensions.ShouldBe(obj); } [Fact] diff --git a/src/Tests/DIObjectGraphTypeTests/Argument.cs b/src/Tests/DIObjectGraphTypeTests/Argument.cs index 587fe90..0f2c930 100644 --- a/src/Tests/DIObjectGraphTypeTests/Argument.cs +++ b/src/Tests/DIObjectGraphTypeTests/Argument.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using System.Threading; +using GraphQL; using GraphQL.DI; using GraphQL.Types; using Shouldly; @@ -37,48 +38,6 @@ public class CNullableValue : DIObjectGraphBase public static int? Field1(int? arg) => arg; } - [Fact] - public void NullableValueExplicit() - { - Configure(); - VerifyFieldArgument("Field1", "arg", true, (int?)2); - VerifyField("Field1", true, false, 2); - Verify(false); - } - - public class CNullableValueExplicit : DIObjectGraphBase - { - public static int? Field1([Optional] int arg) => arg; - } - - [Fact] - public void NullableObject() - { - Configure(); - VerifyFieldArgument("Field1", "arg", true, "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CNullableObject : DIObjectGraphBase - { - public static string Field1([Optional] string arg) => arg; - } - - [Fact] - public void NonNullableObject() - { - Configure(); - VerifyFieldArgument("Field1", "arg", false, "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CNonNullableObject : DIObjectGraphBase - { - public static string Field1([Required] string arg) => arg; - } - [Fact] public void NonNullableObject2() { @@ -133,7 +92,7 @@ public void GraphType() public class CGraphType : DIObjectGraphBase { - public static string Field1([GraphType(typeof(IdGraphType))] string arg) => arg; + public static string Field1([InputType(typeof(IdGraphType))] string arg) => arg; } [Fact] @@ -150,7 +109,7 @@ public class CGraphTypeInherited : DIObjectGraphBase public static string Field1([MyIdGraphType] string arg) => arg; } - public class MyIdGraphTypeAttribute : GraphTypeAttribute + public class MyIdGraphTypeAttribute : InputTypeAttribute { public MyIdGraphTypeAttribute() : base(typeof(IdGraphType)) { } } @@ -200,27 +159,13 @@ public class CCancellationToken : DIObjectGraphBase public static string Field1(CancellationToken cancellationToken) => "hello + " + cancellationToken.IsCancellationRequested; } - [Fact] - public void NullNameArgument() - { - Configure(); - var field = VerifyField("Field1", true, false, "hello + 0"); - field.Arguments.Count.ShouldBe(0); - Verify(false); - } - - public class CNullNameArgument : DIObjectGraphBase - { - public static string Field1([Name(null)] int arg) => "hello + " + arg; - } - [Fact] public void DefaultValue() { Configure(); - VerifyFieldArgument("Field1", "arg1", true, 2); + VerifyFieldArgument("Field1", "arg1", false, 2); VerifyField("Field1", false, false, 2); - VerifyFieldArgument("Field2", "arg2", true); + VerifyFieldArgument("Field2", "arg2", false); VerifyField("Field2", false, false, 5); Verify(false); } diff --git a/src/Tests/DIObjectGraphTypeTests/DIObjectGraphTypeTestBase.cs b/src/Tests/DIObjectGraphTypeTests/DIObjectGraphTypeTestBase.cs index 56cf151..bc10721 100644 --- a/src/Tests/DIObjectGraphTypeTests/DIObjectGraphTypeTestBase.cs +++ b/src/Tests/DIObjectGraphTypeTests/DIObjectGraphTypeTestBase.cs @@ -66,10 +66,9 @@ protected FieldType VerifyField(string fieldName, Type fieldGraphType, bool c var field = _graphType.Fields.Find(fieldName); field.ShouldNotBeNull(); field.Type.ShouldBe(fieldGraphType); - field.ShouldBeOfType().Concurrent.ShouldBe(concurrent); field.Resolver.ShouldNotBeNull(); _contextMock.Setup(x => x.FieldDefinition).Returns(field); - field.Resolver.Resolve(context).ShouldBe(returnValue); + field.Resolver.ResolveAsync(context).Result.ShouldBe(returnValue); _arguments.Clear(); return field; } @@ -82,12 +81,9 @@ protected async Task VerifyFieldAsync(string fieldName, bool nulla var field = _graphType.Fields.Find(fieldName); field.ShouldNotBeNull(); field.Type.ShouldBe(typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.OutputType)); - field.ShouldBeOfType().Concurrent.ShouldBe(concurrent); field.Resolver.ShouldNotBeNull(); _contextMock.Setup(x => x.FieldDefinition).Returns(field); - var ret = field.Resolver.Resolve(context); - var taskRet = ret.ShouldBeOfType>(); - var final = await taskRet; + var final = await field.Resolver.ResolveAsync(context); final.ShouldBe(returnValue); _arguments.Clear(); return field; diff --git a/src/Tests/DIObjectGraphTypeTests/Field.cs b/src/Tests/DIObjectGraphTypeTests/Field.cs index 1e895b8..4066bcb 100644 --- a/src/Tests/DIObjectGraphTypeTests/Field.cs +++ b/src/Tests/DIObjectGraphTypeTests/Field.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.ComponentModel; using System.Reflection; @@ -27,7 +29,7 @@ public class CStaticMethod : DIObjectGraphBase { public CStaticMethod() => throw new Exception(); - public static string Field1() => "hello"; + public static string? Field1() => "hello"; } [Fact] @@ -40,14 +42,14 @@ public void InstanceMethod() public class CInstanceMethod : DIObjectGraphBase { - public string Field1() => "hello"; + public string? Field1() => "hello"; } [Fact] public void SetsContext() { Configure(true); - _graphType.Fields.Find("Field1").Resolver.Resolve(_contextMock.Object).ShouldBe(_contextMock.Object); + _graphType.Fields.Find("Field1")!.Resolver!.ResolveAsync(_contextMock.Object).Result.ShouldBe(_contextMock.Object); } public class CVerifyContext : DIObjectGraphBase @@ -67,7 +69,7 @@ public class CStaticAsyncMethod : DIObjectGraphBase { public CStaticAsyncMethod() => throw new Exception(); - public static Task Field1() => Task.FromResult("hello"); + public static Task Field1() => Task.FromResult("hello"); } [Fact] @@ -80,7 +82,7 @@ public async Task InstanceAsyncMethod() public class CInstanceAsyncMethod : DIObjectGraphBase { - public Task Field1() => Task.FromResult("hello"); + public Task Field1() => Task.FromResult("hello"); } [Fact] @@ -94,7 +96,7 @@ public void Name() public class CName : DIObjectGraphBase { [Name("FieldTest")] - public static string Field1() => "hello"; + public static string? Field1() => "hello"; } [Fact] @@ -108,7 +110,7 @@ public void Description() public class CDescription : DIObjectGraphBase { [Description("DescriptionTest")] - public static string Field1() => "hello"; + public static string? Field1() => "hello"; } [Fact] @@ -122,7 +124,22 @@ public void Obsolete() public class CObsolete : DIObjectGraphBase { [Obsolete("ObsoleteTest")] - public static string Field1() => "hello"; + public static string? Field1() => "hello"; + } + + [Fact] + public void ImplicitSource() + { + Configure(); + _source = "testSource"; + VerifyField("Field1", true, false, "testSource"); + _contextMock.Verify(x => x.Source, Times.AtLeastOnce); + Verify(false); + } + + public class CImplicitSource : DIObjectGraphBase + { + public static string? Field1(string source) => source; } [Fact] @@ -137,10 +154,10 @@ public void Context() public class CContext : DIObjectGraphBase { - public static string Field1(IResolveFieldContext context) => (string)context.Source; + public static string? Field1(IResolveFieldContext context) => (string)context.Source; } - [Fact] + [Fact(Skip = "Not supported in this version")] public void Context_Typed() { Configure(); @@ -152,10 +169,10 @@ public void Context_Typed() public class CContext_Typed : DIObjectGraphBase { - public static string Field1(IResolveFieldContext context) => (string)context.Source; + public static string? Field1(IResolveFieldContext context) => (string)context.Source; } - [Fact] + [Fact(Skip = "Not supported in this version")] public void Context_WrongType() { Should.Throw(() => Configure()); @@ -163,7 +180,7 @@ public void Context_WrongType() public class CContext_WrongType : DIObjectGraphBase { - public static string Field1(IResolveFieldContext context) => context.Source; + public static string? Field1(IResolveFieldContext context) => context.Source; } [Fact] @@ -178,7 +195,7 @@ public void Source() public class CSource : DIObjectGraphBase { - public static string Field1([FromSource] object source) => (string)source; + public static string? Field1([FromSource] object source) => (string)source; } [Fact] @@ -203,7 +220,7 @@ public void ServiceProvider() public class CServiceProvider : DIObjectGraphBase { - public static string Field1(IServiceProvider serviceProvider) => serviceProvider.GetRequiredService(); + public static string? Field1(IServiceProvider serviceProvider) => serviceProvider.GetRequiredService(); } [Fact] @@ -217,8 +234,8 @@ public async Task ServiceProviderForScoped() public class CServiceProviderForScoped : DIObjectGraphBase { - [Concurrent(true)] - public static Task Field1(IServiceProvider serviceProvider) => Task.FromResult(serviceProvider.GetRequiredService()); + [Scoped] + public static Task Field1(IServiceProvider serviceProvider) => Task.FromResult(serviceProvider.GetRequiredService()); } [Fact] @@ -234,20 +251,6 @@ public class CNullableValue : DIObjectGraphBase public static int? Field1() => null; } - [Fact] - public void NullableValueExplicit() - { - Configure(); - VerifyField("Field1", true, false, (int?)1); - Verify(false); - } - - public class CNullableValueExplicit : DIObjectGraphBase - { - [Optional] - public static int Field1() => 1; - } - [Fact] public void NonNullableValue() { @@ -261,50 +264,6 @@ public class CNonNullableValue : DIObjectGraphBase public static int Field1() => 1; } - [Fact] - public void Required() - { - Configure(); - VerifyField("Field1", false, false, "hello"); - Verify(false); - } - - public class CRequired : DIObjectGraphBase - { - [Required] - public static string Field1() => "hello"; - } - - [Fact] - public void InheritedRequired() - { - Configure(); - VerifyField("Field1", false, false, "hello"); - Verify(false); - } - - public class CInheritedRequired : DIObjectGraphBase - { - [MyRequired] - public static string Field1() => "hello"; - } - - public class MyRequiredAttribute : RequiredAttribute { } - - [Fact] - public async Task RequiredTask() - { - Configure(); - await VerifyFieldAsync("Field1", false, true, "hello"); - Verify(false); - } - - public class CRequiredTask : DIObjectGraphBase - { - [Required] - public static Task Field1() => Task.FromResult("hello"); - } - [Fact] public void CustomType() { @@ -315,7 +274,7 @@ public void CustomType() public class CCustomType : DIObjectGraphBase { - [GraphType(typeof(IdGraphType))] + [OutputType(typeof(IdGraphType))] public static string Field1() => "hello"; } @@ -333,7 +292,7 @@ public class CInheritedCustomType : DIObjectGraphBase public static string Field1() => "hello"; } - public class MyIdGraphTypeAttribute : GraphTypeAttribute + public class MyIdGraphTypeAttribute : OutputTypeAttribute { public MyIdGraphTypeAttribute() : base(typeof(IdGraphType)) { } } @@ -349,7 +308,7 @@ public void IdType() public class CIdType : DIObjectGraphBase { [Id] - public static string Field1() => "hello"; + public static string? Field1() => "hello"; } [Fact] @@ -377,7 +336,7 @@ public void IdListType() public class CIdListType : DIObjectGraphBase { [Id] - public static string[] Field1() => new[] { "hello" }; + public static string?[]? Field1() => new[] { "hello" }; } [Fact] @@ -390,13 +349,13 @@ public void DIGraphType() public class CDIGraphType2 : DIObjectGraphBase { - public static string Field1() => "hello"; + public static string? Field1() => "hello"; } public class CDIGraphType : DIObjectGraphBase { [DIGraph(typeof(CDIGraphType2))] - public static string Field1() => "hello"; + public static string? Field1() => "hello"; } [Fact] @@ -424,7 +383,7 @@ public void DIGraphListType() public class CDIGraphListType : DIObjectGraphBase { [DIGraph(typeof(CDIGraphType2))] - public static string[] Field1() => new[] { "hello" }; + public static string?[]? Field1() => new[] { "hello" }; } [Fact] @@ -439,75 +398,6 @@ public class CDIGraphTypeInvalid : DIObjectGraphBase public static string Field1() => "hello"; } - [Fact] - public async Task Concurrent() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(false); - } - - public class CConcurrent : DIObjectGraphBase - { - [Concurrent] - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public void ConcurrentIgnoredForSynchronousMethods() - { - Configure(); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CConcurrentIgnoredForSynchronousMethods : DIObjectGraphBase - { - [Concurrent] - public static string Field1() => "hello"; - } - - [Fact] - public async Task AlwaysConcurrentForStatic() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(false); - } - - public class CAlwaysConcurrentForStatic : DIObjectGraphBase - { - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public async Task NotScopedWhenNoServices() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(false); - } - - public class CNotScopedWhenNoServices : DIObjectGraphBase - { - [Concurrent(true)] - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public async Task AlwaysConcurrentForStaticUnlessService() - { - Configure(); - _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - await VerifyFieldAsync("Field1", true, false, "hello"); - Verify(false); - } - - public class CAlwaysConcurrentForStaticUnlessService : DIObjectGraphBase - { - public static Task Field1([FromServices] string value) => Task.FromResult(value); - } - [Fact] public async Task ScopedOnlyWhenSpecifiedWithServices() { @@ -518,37 +408,8 @@ public async Task ScopedOnlyWhenSpecifiedWithServices() public class CScopedOnlyWhenSpecified : DIObjectGraphBase { - [Concurrent(true)] - public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); - } - - [Fact] - public async Task InheritsConcurrent() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(true); - } - - [Concurrent(true)] - public class CInheritsConcurrent : DIObjectGraphBase - { - public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); - } - - [Fact] - public async Task InheritedConcurrentOverridable() - { - Configure(); - await VerifyFieldAsync("Field1", true, false, "hello"); - Verify(false); - } - - [Concurrent(true)] - public class CInheritedConcurrentOverridable : DIObjectGraphBase - { - [Concurrent(Concurrent = false)] - public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); + [Scoped] + public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); } [Fact] @@ -561,7 +422,7 @@ public async Task RemoveAsyncFromName() public class CRemoveAsyncFromName : DIObjectGraphBase { - public static Task Field1Async() => Task.FromResult("hello"); + public static Task Field1Async() => Task.FromResult("hello"); } [Fact] @@ -580,21 +441,6 @@ public static void Field2() { } public static string Field3() => null!; } - [Fact] - public void SkipNullName() - { - Configure(); - _graphType.Fields.Find("Field1").ShouldBeNull(); - _graphType.Fields.Find("Field2").ShouldNotBeNull(); - } - - public class CSkipNullName : DIObjectGraphBase - { - [Name(null)] - public static string Field1() => "hello"; - public static string Field2() => "hello"; - } - [Fact] public void Ignore() { @@ -622,7 +468,7 @@ public void AddsMetadata() public class CAddsMetadata : DIObjectGraphBase { [Metadata("test", "value")] - public static string Field1() => "hello"; + public static string? Field1() => "hello"; } [Fact] @@ -637,7 +483,7 @@ public void AddsInheritedMetadata() public class CAddsInheritedMetadata : DIObjectGraphBase { [InheritedMetadata("value2")] - public static string Field1() => "hello"; + public static string? Field1() => "hello"; } private class InheritedMetadata : MetadataAttribute @@ -647,11 +493,9 @@ public InheritedMetadata(string value) : base("test", value) { } [Theory] [InlineData("Field1", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field2", typeof(NonNullGraphType>))] [InlineData("Field3", typeof(NonNullGraphType>))] - [InlineData("Field4", typeof(GraphQLClrOutputTypeReference))] [InlineData("Field5", typeof(GraphQLClrOutputTypeReference))] - //[InlineData("Field6", typeof(GraphQLClrOutputTypeReference)] //Need to fix graphql-dotnet bug 2441 first + [InlineData("Field6", typeof(GraphQLClrOutputTypeReference))] //Need to fix graphql-dotnet bug 2441 first [InlineData("Field7", typeof(GraphQLClrOutputTypeReference))] [InlineData("Field8", typeof(NonNullGraphType>))] [InlineData("Field9", typeof(GraphQLClrOutputTypeReference))] @@ -665,18 +509,14 @@ public void SupportsDataLoader(string fieldName, Type graphType) public class CSupportsDataLoader : DIObjectGraphBase { - public IDataLoaderResult Field1() => null; - [Required] - public IDataLoaderResult Field2() => null; - public IDataLoaderResult Field3() => null; - [Optional] - public IDataLoaderResult Field4() => null; - public IDataLoaderResult Field5() => null; - public IDataLoaderResult Field6() => null; - public Task> Field7() => null; - public Task> Field8() => null; - public Task> Field9() => null; - public Task>> Field10() => null; + public IDataLoaderResult Field1() => null!; + public IDataLoaderResult Field3() => null!; + public IDataLoaderResult Field5() => null!; + public IDataLoaderResult Field6() => null!; + public Task> Field7() => Task.FromResult>(null); + public Task> Field8() => Task.FromResult>(null!); + public Task> Field9() => Task.FromResult>(null!); + public Task>> Field10() => Task.FromResult>>(null!); } } } diff --git a/src/Tests/DIObjectGraphTypeTests/Graph.cs b/src/Tests/DIObjectGraphTypeTests/Graph.cs index 9268798..4961c22 100644 --- a/src/Tests/DIObjectGraphTypeTests/Graph.cs +++ b/src/Tests/DIObjectGraphTypeTests/Graph.cs @@ -1,7 +1,6 @@ using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Reflection; +using GraphQL; using GraphQL.DI; using Shouldly; using Xunit; @@ -75,18 +74,6 @@ public void GraphMetadata() [Metadata("test", "value")] public class CGraphMetadata : DIObjectGraphBase { } - [Fact] - public void CanOverrideMembers() - { - var test = new CCanOverrideMembersGraphType(); - test.Fields.Count.ShouldBe(0); - } - - public class CCanOverrideMembersGraphType : DIObjectGraphType - { - protected override IEnumerable GetMethodsToProcess() => Array.Empty(); - } - public class CCanOverrideMembers : DIObjectGraphBase { public static string Field1() => "hello"; diff --git a/src/Tests/DIObjectGraphTypeTests/Services.cs b/src/Tests/DIObjectGraphTypeTests/Services.cs index c367217..49d27c1 100644 --- a/src/Tests/DIObjectGraphTypeTests/Services.cs +++ b/src/Tests/DIObjectGraphTypeTests/Services.cs @@ -1,8 +1,7 @@ using System; -using System.ComponentModel; using System.Threading.Tasks; +using GraphQL; using GraphQL.DI; -using GraphQL.Types; using Shouldly; using Xunit; @@ -44,7 +43,7 @@ public async Task ScopedService() public class CScopedService : DIObjectGraphBase { - [Concurrent(true)] + [Scoped] public static Task Field1([FromServices] string arg) => Task.FromResult(arg); } } diff --git a/src/Tests/Execution/DIDocumentExecuter.cs b/src/Tests/Execution/DIDocumentExecuter.cs deleted file mode 100644 index 6e8320d..0000000 --- a/src/Tests/Execution/DIDocumentExecuter.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Threading.Tasks; -using GraphQL; -using GraphQL.DI; -using GraphQL.Execution; -using GraphQL.Language.AST; -using GraphQL.Validation; -using GraphQL.Validation.Complexity; -using Moq; -using Shouldly; -using Xunit; - -namespace Execution -{ - public class DIDocumentExecuterTests - { - [Fact] - public async Task NullRequestServicesThrows() - { - var e = await Should.ThrowAsync(async () => await new DIDocumentExecuter().ExecuteAsync(new ExecutionOptions())); - e.ParamName.ShouldBe("options.RequestServices"); - } - - [Fact] - public void CreateWithDefaults() - { - var test = new MockDIDocumentExecuter(); - test.TestSelectExecutionStrategy(OperationType.Query).ShouldBeOfType(); - test.TestSelectExecutionStrategy(OperationType.Mutation).ShouldBeOfType(); - Should.Throw(() => test.TestSelectExecutionStrategy(OperationType.Subscription)); - } - - [Fact] - public void CreateWithSubscription() - { - var strategy = new ParallelExecutionStrategy(); - var test = new MockDIDocumentExecuter(strategy); - test.TestSelectExecutionStrategy(OperationType.Query).ShouldBeOfType(); - test.TestSelectExecutionStrategy(OperationType.Mutation).ShouldBeOfType(); - test.TestSelectExecutionStrategy(OperationType.Subscription).ShouldBe(strategy); - } - - [Fact] - public void CreateWithServiceProviderDefaults() - { - var serviceProviderMock = new Mock(MockBehavior.Strict); - serviceProviderMock.Setup(x => x.GetService(typeof(IDocumentBuilder))).Returns((IDocumentBuilder)null).Verifiable(); - serviceProviderMock.Setup(x => x.GetService(typeof(IDocumentValidator))).Returns((IDocumentValidator)null).Verifiable(); - serviceProviderMock.Setup(x => x.GetService(typeof(IComplexityAnalyzer))).Returns((IComplexityAnalyzer)null).Verifiable(); - var test = new MockDIDocumentExecuter(serviceProviderMock.Object); - test.TestSelectExecutionStrategy(OperationType.Query).ShouldBeOfType(); - test.TestSelectExecutionStrategy(OperationType.Mutation).ShouldBeOfType(); - Should.Throw(() => test.TestSelectExecutionStrategy(OperationType.Subscription)); - } - - [Fact] - public void CreateWithServiceProviderInstances() - { - var documentBuilder = new Mock(MockBehavior.Strict).Object; - var documentValidator = new Mock(MockBehavior.Strict).Object; - var complexityAnalyzer = new Mock(MockBehavior.Strict).Object; - var serviceProviderMock = new Mock(MockBehavior.Strict); - serviceProviderMock.Setup(x => x.GetService(typeof(IDocumentBuilder))).Returns(documentBuilder).Verifiable(); - serviceProviderMock.Setup(x => x.GetService(typeof(IDocumentValidator))).Returns(documentValidator).Verifiable(); - serviceProviderMock.Setup(x => x.GetService(typeof(IComplexityAnalyzer))).Returns(complexityAnalyzer).Verifiable(); - var test = new MockDIDocumentExecuter(serviceProviderMock.Object); - test.TestSelectExecutionStrategy(OperationType.Query).ShouldBeOfType(); - test.TestSelectExecutionStrategy(OperationType.Mutation).ShouldBeOfType(); - Should.Throw(() => test.TestSelectExecutionStrategy(OperationType.Subscription)); - } - - [Fact] - public void CreateWithServiceProviderDefaultsAndSubscription() - { - var serviceProviderMock = new Mock(MockBehavior.Strict); - serviceProviderMock.Setup(x => x.GetService(typeof(IDocumentBuilder))).Returns((IDocumentBuilder)null).Verifiable(); - serviceProviderMock.Setup(x => x.GetService(typeof(IDocumentValidator))).Returns((IDocumentValidator)null).Verifiable(); - serviceProviderMock.Setup(x => x.GetService(typeof(IComplexityAnalyzer))).Returns((IComplexityAnalyzer)null).Verifiable(); - var strategy = new ParallelExecutionStrategy(); - var test = new MockDIDocumentExecuter(serviceProviderMock.Object, strategy); - test.TestSelectExecutionStrategy(OperationType.Query).ShouldBeOfType(); - test.TestSelectExecutionStrategy(OperationType.Mutation).ShouldBeOfType(); - test.TestSelectExecutionStrategy(OperationType.Subscription).ShouldBe(strategy); - } - - [Fact] - public void UnknownOperationType() - { - var obj = new MockDIDocumentExecuter(); - Should.Throw(() => obj.TestSelectExecutionStrategy((OperationType)255)); - } - - [Fact] - public void NullServiceProviderThrows() - { - Should.Throw(() => new DIDocumentExecuter((IServiceProvider)null)).ParamName.ShouldBe("serviceProvider"); - } - - private class MockDIDocumentExecuter : DIDocumentExecuter - { - public MockDIDocumentExecuter() : base() { } - public MockDIDocumentExecuter(IExecutionStrategy executionStrategy) : base(executionStrategy) { } - public MockDIDocumentExecuter(IServiceProvider serviceProvider) : base(serviceProvider) { } - public MockDIDocumentExecuter(IServiceProvider serviceProvider, IExecutionStrategy executionStrategy) : base(serviceProvider, executionStrategy) { } - - public IExecutionStrategy TestSelectExecutionStrategy(OperationType operationType) - { - var context = new ExecutionContext(); - context.Operation = new Operation(new NameNode("test"), new SelectionSet()); - context.Operation.OperationType = operationType; - return base.SelectExecutionStrategy(context); - } - } - } -} diff --git a/src/Tests/Execution/DIDocumentStrategy.cs b/src/Tests/Execution/DIDocumentStrategy.cs deleted file mode 100644 index dc31e73..0000000 --- a/src/Tests/Execution/DIDocumentStrategy.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using GraphQL; -using GraphQL.DataLoader; -using GraphQL.DI; -using GraphQL.Execution; -using GraphQL.SystemTextJson; -using GraphQL.Types; -using Moq; -using Shouldly; -using Xunit; - -namespace Execution -{ - public class DIDocumentStrategyTests - { - private readonly IDocumentExecuter _executer = new DIDocumentExecuter(); - private readonly ISchema _schema = new MySchema(); - private readonly IDocumentWriter _writer = new DocumentWriter(false); - - [Fact] - public async Task ItRuns() - { - var actual = await RunQuery("{field1,field2,field3,field4,field5,field6,field7,field8,field9,field10,field11,field12,field13,field14}"); - actual.ShouldBe(@"{""data"":{""field1"":""ret1"",""field2"":""ret2"",""field3"":""ret3"",""field4"":""ret4"",""field5"":""ret5"",""field6"":""ret6"",""field7"":""ret7"",""field8"":""ret8"",""field9"":""ret9"",""field10"":""ret10"",""field11"":""ret11"",""field12"":""ret12"",""field13"":""ret13"",""field14"":""ret14""}}"); - } - - [Fact] - public async Task InvalidMaxTasksThrows() - { - await Should.ThrowAsync(async () => { - var de = new DIExecutionStrategy(); - var ret = await _executer.ExecuteAsync(new ExecutionOptions { - Query = "{field1}", - Schema = _schema, - MaxParallelExecutionCount = -1, - RequestServices = Mock.Of(), - ThrowOnUnhandledException = true, - }); - }); - } - - [Fact] - public async Task NullRequestServicesThrows() - { - var document = GraphQL.Language.CoreToVanillaConverter.Convert(GraphQLParser.Parser.Parse("{field1}")); - var e = await Should.ThrowAsync(async () => await new DIExecutionStrategy().ExecuteAsync(new ExecutionContext() { - Schema = _schema, - Document = document, - Operation = document.Operations.First(), - })); - e.ParamName.ShouldBe("context.RequestServices"); - } - - private async Task RunQuery(string query) - { - var result = await _executer.ExecuteAsync(new ExecutionOptions { - Query = query, - Schema = _schema, - MaxParallelExecutionCount = 2, - RequestServices = Mock.Of(), - }); - result.Errors.ShouldBeNull(); - return await _writer.WriteToStringAsync(result); - } - - private class MySchema : Schema - { - public MySchema() - { - Query = new DIObjectGraphType(); - } - } - - private class Graph1 : DIObjectGraphBase - { - public static string Field1() => "ret1"; - public static Task Field2() => Task.FromResult("ret2"); - public static Task Field3() => Task.FromResult("ret3"); - public static string Field4() => "ret4"; - public static async Task Field5() { await Task.Yield(); return "ret5"; } - public static async Task Field6() { await Task.Yield(); return "ret6"; } - public static async Task Field7() { await Task.Yield(); return "ret7"; } - public static IDataLoaderResult Field8() => new SimpleDataLoader(c => Task.FromResult("ret8")); - public string Field9() => "ret9"; - public string Field10() => "ret10"; - public async Task Field11() { await Task.Yield(); return "ret11"; } - public async Task Field12() { await Task.Yield(); return "ret12"; } - public async Task Field13() { await Task.Yield(); return "ret13"; } - public static async Task> Field14() { await Task.Yield(); return new SimpleDataLoader(c => Task.FromResult("ret14")); } - } - } -} diff --git a/src/Tests/Execution/DISchemaTypes.cs b/src/Tests/Execution/DISchemaTypes.cs deleted file mode 100644 index 4e4e875..0000000 --- a/src/Tests/Execution/DISchemaTypes.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using GraphQL; -using GraphQL.DI; -using GraphQL.Types; -using Moq; -using Shouldly; -using Xunit; - -namespace Execution -{ - public class DISchemaTypesTests - { - [Theory] - [InlineData(typeof(int), false, typeof(IntGraphType))] - [InlineData(typeof(int), true, typeof(IntGraphType))] - [InlineData(typeof(Class1), false, typeof(AutoObjectGraphType))] - [InlineData(typeof(Class1), true, typeof(AutoInputObjectGraphType))] - public void GetGraphTypeFromClrType(Type clrType, bool isInputType, Type graphType) - { - var mySchemaTypes = new MySchemaTypes(); - var mappedTypes = new List<(Type, Type)>(); - mySchemaTypes.GetGraphTypeFromClrType(clrType, isInputType, mappedTypes).ShouldBe(graphType); - } - - private class MySchemaTypes : DISchemaTypes - { - public MySchemaTypes() : base(new Schema(), Mock.Of()) { } - - public new Type GetGraphTypeFromClrType(Type clrType, bool isInputType, List<(Type ClrType, Type GraphType)> typeMappings) - => base.GetGraphTypeFromClrType(clrType, isInputType, typeMappings); - } - - [Theory] - [InlineData(typeof(int), false, typeof(IntGraphType))] - [InlineData(typeof(int), true, typeof(IntGraphType))] - [InlineData(typeof(Class1), false, null)] - [InlineData(typeof(Class1), true, typeof(AutoInputObjectGraphType))] - public void GetGraphTypeFromClrType_NoOutput(Type clrType, bool isInputType, Type graphType) - { - var mySchemaTypes2 = new MySchemaTypes2(); - var mappedTypes = new List<(Type, Type)>(); - mySchemaTypes2.GetGraphTypeFromClrType(clrType, isInputType, mappedTypes).ShouldBe(graphType); - } - - private class MySchemaTypes2 : DISchemaTypes - { - public MySchemaTypes2() : base(new Schema(), Mock.Of(), true, true) { } - - public new Type GetGraphTypeFromClrType(Type clrType, bool isInputType, List<(Type ClrType, Type GraphType)> typeMappings) - => base.GetGraphTypeFromClrType(clrType, isInputType, typeMappings); - - protected override bool AutoMapOutputType(Type clrType) => false; - } - - [Theory] - [InlineData(typeof(int), false, typeof(IntGraphType))] - [InlineData(typeof(int), true, typeof(IntGraphType))] - [InlineData(typeof(Class1), false, typeof(AutoObjectGraphType))] - [InlineData(typeof(Class1), true, null)] - public void GetGraphTypeFromClrType_NoInput(Type clrType, bool isInputType, Type graphType) - { - var mySchemaTypes3 = new MySchemaTypes3(); - var mappedTypes = new List<(Type, Type)>(); - mySchemaTypes3.GetGraphTypeFromClrType(clrType, isInputType, mappedTypes).ShouldBe(graphType); - } - - private class MySchemaTypes3 : DISchemaTypes - { - public MySchemaTypes3() : base(new Schema(), Mock.Of(), true, true) { } - - public new Type GetGraphTypeFromClrType(Type clrType, bool isInputType, List<(Type ClrType, Type GraphType)> typeMappings) - => base.GetGraphTypeFromClrType(clrType, isInputType, typeMappings); - - protected override bool AutoMapInputType(Type clrType) => false; - } - - private class Class1 { } - - [Fact] - public void InputOutputTypesWork() - { - var schema = new Schema(); - schema.Query = new ObjectGraphType(); - var newField = new FieldType { Name = "Test", Type = typeof(GraphQLClrOutputTypeReference) }; - newField.Arguments = new QueryArguments(new QueryArgument> { Name = "arg" }); - schema.Query.AddField(newField); - var schemaTypes = new DISchemaTypes(schema, new DefaultServiceProvider()); - var class1Type = schemaTypes["Class1"].ShouldBeAssignableTo(); - class1Type.Fields.Count.ShouldBe(1); - class1Type.Fields.Find("value").ShouldNotBeNull(); - var class2Type = schemaTypes["Class2Input"].ShouldBeAssignableTo(); - class2Type.Fields.Count.ShouldBe(1); - class2Type.Fields.Find("value").ShouldNotBeNull(); - } - - private class Class1Model - { - public int Value { get; set; } - } - - private class Class2InputModel - { - public int Value { get; set; } - } - } -} diff --git a/src/Tests/Execution/GraphQLBuilderTests.cs b/src/Tests/Execution/GraphQLBuilderTests.cs index 3511a65..0578887 100644 --- a/src/Tests/Execution/GraphQLBuilderTests.cs +++ b/src/Tests/Execution/GraphQLBuilderTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using GraphQL.DI; using GraphQL.Types; using Moq; @@ -10,15 +9,20 @@ namespace Execution { public class GraphQLBuilderTests { + private readonly Mock _mockServiceRegister = new Mock(MockBehavior.Strict); private readonly Mock _mockGraphQLBuilder = new Mock(MockBehavior.Strict); private IGraphQLBuilder _graphQLBuilder => _mockGraphQLBuilder.Object; + public GraphQLBuilderTests() + { + _mockGraphQLBuilder.Setup(x => x.Services).Returns(_mockServiceRegister.Object); + } + [Fact] public void AddDIGraphTypes() { - _mockGraphQLBuilder.Setup(x => x.TryRegister(typeof(AutoInputObjectGraphType<>), typeof(AutoInputObjectGraphType<>), ServiceLifetime.Transient)).Returns(_graphQLBuilder).Verifiable(); - _mockGraphQLBuilder.Setup(x => x.TryRegister(typeof(DIObjectGraphType<>), typeof(DIObjectGraphType<>), ServiceLifetime.Transient)).Returns(_graphQLBuilder).Verifiable(); - _mockGraphQLBuilder.Setup(x => x.TryRegister(typeof(DIObjectGraphType<,>), typeof(DIObjectGraphType<,>), ServiceLifetime.Transient)).Returns(_graphQLBuilder).Verifiable(); + _mockServiceRegister.Setup(x => x.TryRegister(typeof(DIObjectGraphType<>), typeof(DIObjectGraphType<>), ServiceLifetime.Transient, RegistrationCompareMode.ServiceType)).Returns(_mockServiceRegister.Object).Verifiable(); + _mockServiceRegister.Setup(x => x.TryRegister(typeof(DIObjectGraphType<,>), typeof(DIObjectGraphType<,>), ServiceLifetime.Transient, RegistrationCompareMode.ServiceType)).Returns(_mockServiceRegister.Object).Verifiable(); _graphQLBuilder.AddDIGraphTypes().ShouldBe(_graphQLBuilder); _mockGraphQLBuilder.Verify(); } @@ -26,52 +30,29 @@ public void AddDIGraphTypes() [Fact] public void AddDIClrTypeMappings() { - var actual = new List<(Type clrType, Type graphType)>(); - var existingMappings = new (Type clrType, Type graphType)[] { - (typeof(Class7), typeof(Graph7)), - (typeof(Class8), typeof(Graph8)) - }; - var mockSchema = new Mock(MockBehavior.Strict); - mockSchema.Setup(x => x.TypeMappings).Returns(existingMappings).Verifiable(); - mockSchema.Setup(x => x.RegisterTypeMapping(It.IsAny(), It.IsAny())) - .Callback((clrType, graphType) => actual.Add((clrType, graphType))); - _mockGraphQLBuilder.Setup(x => x.Register(typeof(IConfigureSchema), It.IsAny(), false)) - .Returns((_, configure, _) => { - configure.Configure(mockSchema.Object, null); - return _mockGraphQLBuilder.Object; - }) - .Verifiable(); + IGraphTypeMappingProvider mapper = null; + _mockServiceRegister.Setup(x => x.Register(typeof(IGraphTypeMappingProvider), It.IsAny(), false)) + .Returns((_, m, _) => { + mapper = m; + return _mockServiceRegister.Object; + }); _graphQLBuilder.AddDIClrTypeMappings(); - mockSchema.Verify(); - _mockGraphQLBuilder.Verify(); - var expected = new List<(Type clrType, Type graphType)> { - (typeof(Class2), typeof(DIObjectGraphType)), - (typeof(Class4), typeof(DIObjectGraphType)), - (typeof(Class8), typeof(DIObjectGraphType)), - }; - actual.ShouldBe(actual); - } + mapper.ShouldNotBeNull(); + + mapper.GetGraphTypeFromClrType(typeof(Class1), false, null).ShouldBe(typeof(DIObjectGraphType)); + mapper.GetGraphTypeFromClrType(typeof(Class2), false, null).ShouldBe(typeof(DIObjectGraphType)); + mapper.GetGraphTypeFromClrType(typeof(Class3), false, null).ShouldBeNull(); + mapper.GetGraphTypeFromClrType(typeof(Class1), false, typeof(Class4)).ShouldBe(typeof(DIObjectGraphType)); + mapper.GetGraphTypeFromClrType(typeof(Class2), false, typeof(Class4)).ShouldBe(typeof(DIObjectGraphType)); + mapper.GetGraphTypeFromClrType(typeof(Class3), false, typeof(Class4)).ShouldBe(typeof(Class4)); + } private class Class1 { } private class Class2 { } private class Class3 { } private class Class4 { } - private class Class7 { } - private class Class8 { } private class Base1 : DIObjectGraphBase { } //don't register because graph1 private class Base2 : DIObjectGraphBase { } //register because graph2 is input - private class Base3 : DIObjectGraphBase { } //don't register because graph3 - private class Base4 : DIObjectGraphBase { } //register because no conflict - private class Base5 : DIObjectGraphBase { } //don't register because object type - private class Base6 : DIObjectGraphBase { } //don't register because object type - private class Base7 : DIObjectGraphBase { } //don't register because graph7 was manually registered - private class Base8 : DIObjectGraphBase { } //register because graph8 is input - private class Graph1 : ObjectGraphType { } - private class Graph2 : InputObjectGraphType { } - private class Graph3 : DIObjectGraphType { } - private class Graph5 : DIObjectGraphType { } - private class Graph7 : ObjectGraphType { } - private class Graph8 : InputObjectGraphType { } } } diff --git a/src/Tests/NullableTests/ArgumentTests.cs b/src/Tests/NullableTests/ArgumentTests.cs deleted file mode 100644 index 13fe950..0000000 --- a/src/Tests/NullableTests/ArgumentTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using GraphQL.DI; -using Shouldly; -using Xunit; - -namespace NullableTests -{ - public class ArgumentTests - { - [Theory] - [InlineData(typeof(NullableClass9), "Field1", "arg1", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass9), "Field1", "arg2", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass10), "Field1", "arg1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass10), "Field1", "arg2", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass11), "Field2", "arg1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass11), "Field2", "arg2", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg2", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg3", typeof(int), Nullability.NonNullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg4", typeof(int), Nullability.Nullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg5", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg6", typeof(IEnumerable), Nullability.NonNullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg7", typeof(IEnumerable), Nullability.NonNullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg8", typeof(IEnumerable), Nullability.Nullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass13), "Field2", "arg9", typeof(IEnumerable), Nullability.Nullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg1", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg2", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg3", typeof(int), Nullability.NonNullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg4", typeof(int), Nullability.Nullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg5", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg6", typeof(IEnumerable), Nullability.NonNullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg7", typeof(IEnumerable), Nullability.NonNullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg8", typeof(IEnumerable), Nullability.Nullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass14), "Field2", "arg9", typeof(IEnumerable), Nullability.Nullable, typeof(string), Nullability.Nullable)] - public void Argument(Type type, string methodName, string argumentName, Type expectedType, Nullability expectedNullability, Type expectedType2 = null, Nullability? expectedNullability2 = null) - { - var method = type.GetMethod(methodName); - var argument = method.GetParameters().Single(x => x.Name == argumentName); - var actual = argument.GetNullabilityInformation().ToList(); - actual.Count.ShouldBe(expectedType2 == null ? 1 : 2); - actual[0].Type.ShouldBe(expectedType); - actual[0].Nullable.ShouldBe(expectedNullability); - if (expectedType2 != null) { - actual[1].Type.ShouldBe(expectedType2); - actual[1].Nullable.ShouldBe(expectedNullability2.Value); - } - } - - [Theory] - [InlineData(typeof(NullableClass9), "Field1", "arg1", typeof(string), true)] - [InlineData(typeof(NullableClass9), "Field1", "arg2", typeof(string), true)] - [InlineData(typeof(NullableClass10), "Field1", "arg1", typeof(string), false)] - [InlineData(typeof(NullableClass10), "Field1", "arg2", typeof(string), false)] - [InlineData(typeof(NullableClass11), "Field2", "arg1", typeof(string), false)] - [InlineData(typeof(NullableClass11), "Field2", "arg2", typeof(string), false)] - [InlineData(typeof(NullableClass13), "Field2", "arg1", typeof(string), false)] - [InlineData(typeof(NullableClass13), "Field2", "arg2", typeof(string), true)] - [InlineData(typeof(NullableClass13), "Field2", "arg3", typeof(int), false)] - [InlineData(typeof(NullableClass13), "Field2", "arg4", typeof(int), true)] - [InlineData(typeof(NullableClass13), "Field2", "arg5", typeof(string), false, false, false, true)] - [InlineData(typeof(NullableClass13), "Field2", "arg6", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass13), "Field2", "arg7", typeof(string), true, true, false)] - [InlineData(typeof(NullableClass13), "Field2", "arg8", typeof(string), false, true, true)] - [InlineData(typeof(NullableClass13), "Field2", "arg9", typeof(string), true, true, true)] - [InlineData(typeof(NullableClass14), "Field2", "arg1", typeof(string), true)] - [InlineData(typeof(NullableClass14), "Field2", "arg2", typeof(string), false)] - [InlineData(typeof(NullableClass14), "Field2", "arg3", typeof(int), false)] - [InlineData(typeof(NullableClass14), "Field2", "arg4", typeof(int), true)] - [InlineData(typeof(NullableClass14), "Field2", "arg5", typeof(string), true, false, false, false)] - [InlineData(typeof(NullableClass14), "Field2", "arg6", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass14), "Field2", "arg7", typeof(string), true, true, false)] - [InlineData(typeof(NullableClass14), "Field2", "arg8", typeof(string), false, true, true)] - [InlineData(typeof(NullableClass14), "Field2", "arg9", typeof(string), true, true, true)] - [InlineData(typeof(NullableClass19), "Field18", "arg1", typeof(string), true)] - [InlineData(typeof(NullableClass19), "Field18", "arg2", typeof(string), false, true, true)] - [InlineData(typeof(NullableClass19), "Field18", "arg3", typeof(object), true)] - [InlineData(typeof(NullableClass19), "Field18", "arg4", typeof(object), false, true, true)] - public void Argument_GetTypeInformation(Type type, string methodName, string argumentName, Type expectedType, bool isNullable = false, bool isList = false, bool isNullableList = false, bool? isNullableAfterAttributes = null, bool? isNullableListAfterAttributes = null) - { - var method = type.GetMethod(methodName); - var argument = method.GetParameters().Single(x => x.Name == argumentName); - var actual = TestGraphType.Instance.GetTypeInformation(argument, true); - actual.ParameterInfo.ShouldBe(argument); - actual.MemberInfo.ShouldBe(argument.Member); - actual.IsInputType.ShouldBeTrue(); - actual.GraphType.ShouldBeNull(); - actual.Type.ShouldBe(expectedType); - actual.IsNullable.ShouldBe(isNullable); - actual.IsList.ShouldBe(isList); - actual.ListIsNullable.ShouldBe(isNullableList); - actual = TestGraphType.Instance.ApplyAttributes(actual); - actual.IsNullable.ShouldBe(isNullableAfterAttributes ?? isNullable); - actual.ListIsNullable.ShouldBe(isNullableListAfterAttributes ?? isNullableList); - } - } -} diff --git a/src/Tests/NullableTests/MethodTests.cs b/src/Tests/NullableTests/MethodTests.cs deleted file mode 100644 index ba9612f..0000000 --- a/src/Tests/NullableTests/MethodTests.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using GraphQL.DataLoader; -using GraphQL.DI; -using GraphQL.Types; -using Shouldly; -using Xunit; - -namespace NullableTests -{ - public class MethodTests - { - [Theory] - [InlineData(typeof(NullableClass1), "Field1", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass1), "Field2", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass1), "Field3", typeof(int), Nullability.NonNullable)] - [InlineData(typeof(NullableClass1), "Field4", typeof(int), Nullability.NonNullable)] - [InlineData(typeof(NullableClass1), "Field5", typeof(int), Nullability.Nullable)] - [InlineData(typeof(NullableClass1), "Field6", typeof(int), Nullability.Nullable)] - [InlineData(typeof(NullableClass1), "Field7", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass1), "Field8", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass1), "Field9", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass1), "Field10", typeof(List), Nullability.NonNullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass2), "Field1", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass2), "Field2", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass2), "Field3", typeof(int), Nullability.NonNullable)] - [InlineData(typeof(NullableClass2), "Field4", typeof(int), Nullability.NonNullable)] - [InlineData(typeof(NullableClass2), "Field5", typeof(int), Nullability.Nullable)] - [InlineData(typeof(NullableClass2), "Field6", typeof(int), Nullability.Nullable)] - [InlineData(typeof(NullableClass2), "Field7", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass2), "Field8", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass2), "Field9", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass2), "Field10", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass2), "Field11", typeof(List), Nullability.Nullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass5), "Test", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass6), "Field1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass6), "Field2", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass7), "Field1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass7), "Field2", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass7), "Field3", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass8), "Field1", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass8), "Field2", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass8), "Field3", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass8), "Field4", typeof(int), Nullability.NonNullable)] - [InlineData(typeof(NullableClass9), "Field1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass10), "Field1", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass11), "Field1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass11), "Field2", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass12), "Field1", typeof(IDataLoaderResult), Nullability.NonNullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass12), "Field2", typeof(IDataLoaderResult), Nullability.NonNullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass12), "Field3", typeof(IDataLoaderResult), Nullability.Nullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass12), "Field4", typeof(IDataLoaderResult), Nullability.Nullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass13), "Field1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass13), "Field2", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass14), "Field1", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass14), "Field2", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass15), "Field1", typeof(Task), Nullability.NonNullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass15), "Field2", typeof(Task), Nullability.NonNullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass15), "Field3", typeof(Task), Nullability.Nullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass15), "Field4", typeof(Task), Nullability.Nullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass16), "Field1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass16), "Field2", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass16), "Field3", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field1", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field2", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field3", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass17), "Field1", typeof(Task), Nullability.NonNullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass17), "Field2", typeof(Task), Nullability.NonNullable, typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass17), "Field3", typeof(Task), Nullability.NonNullable, typeof(string), Nullability.Nullable)] - public void Method_GetNullability(Type type, string methodName, Type expectedType, Nullability expectedNullability, Type expectedType2 = null, Nullability? expectedNullability2 = null) - { - var method = type.GetMethod(methodName); - var actual = method.ReturnParameter.GetNullabilityInformation().ToList(); - actual.Count.ShouldBe(expectedType2 == null ? 1 : 2); - actual[0].Type.ShouldBe(expectedType); - actual[0].Nullable.ShouldBe(expectedNullability); - if (expectedType2 != null) { - actual[1].Type.ShouldBe(expectedType2); - actual[1].Nullable.ShouldBe(expectedNullability2.Value); - } - } - - public static IEnumerable Class18_GetNullability_TestCases() - { - return new object[][] { - new object[] { "Field1", new List<(Type, Nullability)>() { - (typeof(Tuple), Nullability.NonNullable), - (typeof(string), Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - } }, - new object[] { "Field2", new List<(Type, Nullability)>() { - (typeof(Tuple, string>), Nullability.NonNullable), - (typeof(Tuple), Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - (typeof(string), Nullability.Nullable), - (typeof(string), Nullability.NonNullable), - } }, - new object[] { "Field3", new List<(Type, Nullability)>() { - (typeof(Tuple), Nullability.NonNullable), - (typeof(int), Nullability.Nullable), - (typeof(string), Nullability.Nullable), - } }, - new object[] { "Field4", new List<(Type, Nullability)>() { - (typeof(Tuple), Nullability.NonNullable), - (typeof(Guid), Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - } }, - new object[] { "Field5", new List<(Type, Nullability)>() { - (typeof(Tuple), Nullability.NonNullable), - (typeof(string[]), Nullability.NonNullable), - (typeof(string), Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - } }, - new object[] { "Field6", new List<(Type, Nullability)>() { - (typeof(Tuple), Nullability.NonNullable), - (typeof(int[]), Nullability.NonNullable), - (typeof(int), Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - } }, - new object[] { "Field7", new List<(Type, Nullability)>() { - (typeof(Tuple<(int, string), string>), Nullability.NonNullable), - (typeof((int, string)), Nullability.NonNullable), - (typeof(int), Nullability.NonNullable), - (typeof(string), Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - } }, - new object[] { "Field8", new List<(Type, Nullability)>() { - (typeof(Tuple<(int, string), string>), Nullability.NonNullable), - (typeof((int, string)), Nullability.NonNullable), - (typeof(int), Nullability.NonNullable), - (typeof(string), Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - } }, - new object[] { "Field9", new List<(Type, Nullability)>() { - (typeof(Tuple, string>), Nullability.NonNullable), - (typeof(TestStruct), Nullability.NonNullable), - (typeof(Guid), Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - } }, - new object[] { "Field10", new List<(Type, Nullability)>() { - (typeof(Tuple<,>).MakeGenericType(typeof(NullableClass18<>).GetGenericArguments()[0], typeof(string)), Nullability.NonNullable), - (typeof(NullableClass18<>).GetGenericArguments()[0], Nullability.NonNullable), - (typeof(string), Nullability.Nullable), - } }, - }; - } - - [Theory] - [MemberData(nameof(Class18_GetNullability_TestCases))] - public void Method_GetNullability_Class18(string methodName, List<(Type, Nullability)> expected) - { - var method = typeof(NullableClass18<>).GetMethod(methodName).ShouldNotBeNull(); - var actual = method.ReturnParameter.GetNullabilityInformation().ToList(); - expected.ShouldBe(actual); - } - - [Theory] - [InlineData(typeof(NullableClass1), "Field1", typeof(string), true)] - [InlineData(typeof(NullableClass1), "Field2", typeof(string), true, false, false, false)] - [InlineData(typeof(NullableClass1), "Field3", typeof(int), false)] - [InlineData(typeof(NullableClass1), "Field4", typeof(int), false, false, false, true)] - [InlineData(typeof(NullableClass1), "Field5", typeof(int), true)] - [InlineData(typeof(NullableClass1), "Field6", typeof(int), true, false, false, false)] - [InlineData(typeof(NullableClass1), "Field7", typeof(string), false)] - [InlineData(typeof(NullableClass1), "Field8", typeof(string), false, false, false, true)] - [InlineData(typeof(NullableClass1), "Field9", typeof(string), false, false, false, false, false, typeof(IdGraphType))] - [InlineData(typeof(NullableClass1), "Field10", typeof(string), false, true, false, false, true)] - [InlineData(typeof(NullableClass2), "Field1", typeof(string), true)] - [InlineData(typeof(NullableClass2), "Field2", typeof(string), true, false, false, false)] - [InlineData(typeof(NullableClass2), "Field3", typeof(int), false)] - [InlineData(typeof(NullableClass2), "Field4", typeof(int), false, false, false, true)] - [InlineData(typeof(NullableClass2), "Field5", typeof(int), true)] - [InlineData(typeof(NullableClass2), "Field6", typeof(int), true, false, false, false)] - [InlineData(typeof(NullableClass2), "Field7", typeof(string), true)] - [InlineData(typeof(NullableClass2), "Field8", typeof(string), true)] - [InlineData(typeof(NullableClass2), "Field9", typeof(string), false)] - [InlineData(typeof(NullableClass2), "Field10", typeof(string), false, false, false, true)] - [InlineData(typeof(NullableClass2), "Field11", typeof(string), true, true, true, true, false)] - [InlineData(typeof(NullableClass5), "Test", typeof(string), false)] - [InlineData(typeof(NullableClass6), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass6), "Field2", typeof(string), true)] - [InlineData(typeof(NullableClass7), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass7), "Field2", typeof(string), false)] - [InlineData(typeof(NullableClass7), "Field3", typeof(string), true)] - [InlineData(typeof(NullableClass8), "Field1", typeof(string), true)] - [InlineData(typeof(NullableClass8), "Field2", typeof(string), true)] - [InlineData(typeof(NullableClass8), "Field3", typeof(string), false)] - [InlineData(typeof(NullableClass8), "Field4", typeof(int), false)] - [InlineData(typeof(NullableClass9), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass10), "Field1", typeof(string), true)] - [InlineData(typeof(NullableClass11), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass11), "Field2", typeof(string), true)] - [InlineData(typeof(NullableClass12), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass12), "Field2", typeof(string), true)] - [InlineData(typeof(NullableClass12), "Field3", typeof(string), false)] - [InlineData(typeof(NullableClass12), "Field4", typeof(string), true)] - [InlineData(typeof(NullableClass13), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass13), "Field2", typeof(string), false)] - [InlineData(typeof(NullableClass14), "Field1", typeof(string), true)] - [InlineData(typeof(NullableClass14), "Field2", typeof(string), true)] - [InlineData(typeof(NullableClass15), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass15), "Field2", typeof(string), true)] - [InlineData(typeof(NullableClass15), "Field3", typeof(string), false)] - [InlineData(typeof(NullableClass15), "Field4", typeof(string), true)] - [InlineData(typeof(NullableClass16), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass16), "Field2", typeof(string), false)] - [InlineData(typeof(NullableClass16), "Field3", typeof(string), true)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field2", typeof(string), false)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field3", typeof(string), true)] - [InlineData(typeof(NullableClass17), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass17), "Field2", typeof(string), false)] - [InlineData(typeof(NullableClass17), "Field3", typeof(string), true)] - [InlineData(typeof(NullableClass19), "Field1", typeof(string), false)] - [InlineData(typeof(NullableClass19), "Field2", typeof(string), false)] - [InlineData(typeof(NullableClass19), "Field3", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field4", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field5", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field6", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field7", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field8", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field9", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field10", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field11", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field12", typeof(object), true, true, false)] - [InlineData(typeof(NullableClass19), "Field13", typeof(object), true, true, false)] - [InlineData(typeof(NullableClass19), "Field14", typeof(string), false)] - [InlineData(typeof(NullableClass19), "Field15", typeof(object), true)] - [InlineData(typeof(NullableClass19), "Field16", typeof(string), false, true, false)] - [InlineData(typeof(NullableClass19), "Field17", typeof(object), false)] - public void Method_GetTypeInformation(Type type, string methodName, Type expectedType, bool isNullable = false, bool isList = false, bool isNullableList = false, bool? isNullableAfterAttributes = null, bool? isNullableListAfterAttributes = null, Type expectedGraphType = null) - { - var method = type.GetMethod(methodName); - var actual = TestGraphType.Instance.GetTypeInformation(method.ReturnParameter, false); - actual.ParameterInfo.ShouldBe(method.ReturnParameter); - actual.MemberInfo.ShouldBe(method); - actual.IsInputType.ShouldBeFalse(); - actual.GraphType.ShouldBeNull(); - actual.Type.ShouldBe(expectedType); - actual.IsNullable.ShouldBe(isNullable); - actual.IsList.ShouldBe(isList); - actual.ListIsNullable.ShouldBe(isNullableList); - actual = TestGraphType.Instance.ApplyAttributes(actual); - actual.IsNullable.ShouldBe(isNullableAfterAttributes ?? isNullable); - actual.ListIsNullable.ShouldBe(isNullableListAfterAttributes ?? isNullableList); - actual.GraphType.ShouldBe(expectedGraphType); - } - } -} diff --git a/src/Tests/NullableTests/NullabilityTestClasses.cs b/src/Tests/NullableTests/NullabilityTestClasses.cs deleted file mode 100644 index d870b2d..0000000 --- a/src/Tests/NullableTests/NullabilityTestClasses.cs +++ /dev/null @@ -1,200 +0,0 @@ -#nullable enable -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading.Tasks; -using GraphQL.DataLoader; -using GraphQL.DI; - -namespace NullableTests -{ - public class NullableClass1 - { - public static string? Field1() => "field1"; - [Required] - public static string? Field2() => "field2"; - public static int Field3() => 1; - [Optional] - public static int Field4() => 1; - public static int? Field5() => 1; - [Required] - public static int? Field6() => 1; - public static string Field7() => "field1"; - [Optional] - public static string Field8() => "field2"; - [Id] - public static string Field9() => "field9"; - [OptionalList] - public static List Field10() => null!; - } - - public class NullableClass2 - { - public static string? Field1() => "field1"; - [Required] - public static string? Field2() => "field2"; - public static int Field3() => 1; - [Optional] - public static int Field4() => 1; - public static int? Field5() => 1; - [Required] - public static int? Field6() => 1; - public static string? Field7() => "test"; - public static string? Field8() => "test"; - public static string Field9() => "field1"; - [Optional] - public static string Field10() => "field2"; - [RequiredList] - public static List? Field11() => null!; - } - - public class NullableClass5 - { - public static string Test() => "test"; - } - - public class NullableClass6 - { - public static string Field1() => "test"; - public static string? Field2() => "test"; - } - - public class NullableClass7 - { - public static string Field1() => "test"; - public static string Field2() => "test"; - public static string? Field3() => "test"; - } - - public class NullableClass8 - { - public static string? Field1() => "test"; - public static string? Field2() => "test"; - public static string Field3() => "test"; - public static int Field4() => 3; - } - - public class NullableClass9 - { - public static string Field1(string? arg1, string? arg2) => "test"; - } - - public class NullableClass10 - { - public static string? Field1(string arg1, string arg2) => "test"; - } - - public class NullableClass11 - { - public static string Field1() => "test"; - public static string? Field2(string arg1, string arg2) => "test"; - } - - public class NullableClass12 - { - public static IDataLoaderResult Field1() => null!; - public static IDataLoaderResult Field2() => null!; - public static IDataLoaderResult? Field3() => null!; - public static IDataLoaderResult? Field4() => null!; - } - - public class NullableClass13 - { - public static string Field1() => "test"; - public static string Field2(string arg1, string? arg2, int arg3, int? arg4, [Optional] string arg5, IEnumerable arg6, IEnumerable arg7, IEnumerable? arg8, IEnumerable? arg9) => "test"; - } - - public class NullableClass14 - { - public static string? Field1() => "test"; - public static string? Field2(string? arg1, string arg2, int arg3, int? arg4, [Required] string? arg5, IEnumerable arg6, IEnumerable arg7, IEnumerable? arg8, IEnumerable? arg9) => "test"; - } - - public class NullableClass15 - { - public static Task Field1() => null!; - public static Task Field2() => null!; - public static Task? Field3() => null!; - public static Task? Field4() => null!; - } - - public class NullableClass16 - { - public static string Field1() => "test"; - public static string Field2() => "test"; - public static string? Field3() => "test"; - - public class NestedClass1 - { - public static string Field1() => "test"; - public static string Field2() => "test"; - public static string? Field3() => "test"; - } - } - - public class NullableClass17 - { - public static Task Field1() => null!; - public static Task Field2() => null!; - public static Task Field3() => null!; - } - - public class NullableClass18 - { - //reference type - public static Tuple Field1() => null!; - //check ordering of nested types - public static Tuple, string> Field2() => null!; - //nullable value type - public static Tuple Field3() => null!; - //non-generic value type - public static Tuple Field4() => null!; - //array - public static Tuple Field5() => null!; - public static Tuple Field6() => null!; - //value tuple - public static Tuple<(int, string), string?> Field7() => null!; - public static Tuple, string?> Field8() => null!; - //generic value type - public static Tuple, string?> Field9() => null!; - //type reference - public static Tuple Field10() => null!; - } - - public struct TestStruct - { - public T Value; - } - - public class NullableClass19 - { - public static string Field1() => null!; - public static Task Field2() => null!; - public static string[] Field3() => null!; - public static IEnumerable Field4() => null!; - public static IList Field5() => null!; - public static List Field6() => null!; - public static ICollection Field7() => null!; - public static IReadOnlyCollection Field8() => null!; - public static IReadOnlyList Field9() => null!; - public static HashSet Field10() => null!; - public static ISet Field11() => null!; - public static ICollection Field12() => null!; - public static IEnumerable Field13() => null!; - public static IDataLoaderResult Field14() => null!; - public static IDataLoaderResult Field15() => null!; - public static Task> Field16() => null!; - public static object Field17() => null!; - public static string Field18(string arg1 = "test", List arg2 = null!, object arg3 = null!, object[] arg4 = null!) => null!; - } - - public class NullableClass20 - { - public static int Field1 => 0; - public static string Field2 => null!; - public static string? Field3 => null; - public static List Field4 => null!; - public static int? Field5 => null; - public static string Field6 => null!; - } -} diff --git a/src/Tests/NullableTests/PropertyTests.cs b/src/Tests/NullableTests/PropertyTests.cs deleted file mode 100644 index 6a09a48..0000000 --- a/src/Tests/NullableTests/PropertyTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using GraphQL.DataLoader; -using GraphQL.DI; -using GraphQL.Types; -using Shouldly; -using Xunit; - -namespace NullableTests -{ - public class PropertyTests - { - [Theory] - [InlineData(typeof(NullableClass20), "Field1", typeof(int), Nullability.NonNullable)] - [InlineData(typeof(NullableClass20), "Field2", typeof(string), Nullability.NonNullable)] - [InlineData(typeof(NullableClass20), "Field3", typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass20), "Field4", typeof(List), Nullability.NonNullable, typeof(string), Nullability.Nullable)] - [InlineData(typeof(NullableClass20), "Field5", typeof(int), Nullability.Nullable)] - [InlineData(typeof(NullableClass20), "Field6", typeof(string), Nullability.NonNullable)] - public void GetNullability(Type type, string propertyName, Type expectedType, Nullability expectedNullability, Type expectedType2 = null, Nullability? expectedNullability2 = null) - { - var property = type.GetProperty(propertyName); - var actual = property.GetNullabilityInformation().ToList(); - actual.Count.ShouldBe(expectedType2 == null ? 1 : 2); - actual[0].Type.ShouldBe(expectedType); - actual[0].Nullable.ShouldBe(expectedNullability); - if (expectedType2 != null) { - actual[1].Type.ShouldBe(expectedType2); - actual[1].Nullable.ShouldBe(expectedNullability2.Value); - } - } - - [Theory] - [InlineData(typeof(NullableClass20), "Field1", typeof(int), false)] - [InlineData(typeof(NullableClass20), "Field2", typeof(string), false)] - [InlineData(typeof(NullableClass20), "Field3", typeof(string), true)] - [InlineData(typeof(NullableClass20), "Field4", typeof(string), true, true, false)] - [InlineData(typeof(NullableClass20), "Field5", typeof(int), true)] - [InlineData(typeof(NullableClass20), "Field6", typeof(string), false)] - public void GetTypeInformation(Type type, string propertyName, Type expectedType, bool isNullable = false, bool isList = false, bool isNullableList = false, bool? isNullableAfterAttributes = null, bool? isNullableListAfterAttributes = null, Type expectedGraphType = null) - { - var property = type.GetProperty(propertyName); - var actual = TestInputGraphType.Instance.GetTypeInformation(property); - actual.ParameterInfo.ShouldBeNull(); - actual.MemberInfo.ShouldBe(property); - actual.IsInputType.ShouldBeTrue(); - actual.GraphType.ShouldBeNull(); - actual.Type.ShouldBe(expectedType); - actual.IsNullable.ShouldBe(isNullable); - actual.IsList.ShouldBe(isList); - actual.ListIsNullable.ShouldBe(isNullableList); - actual = TestInputGraphType.Instance.ApplyAttributes(actual); - actual.IsNullable.ShouldBe(isNullableAfterAttributes ?? isNullable); - actual.ListIsNullable.ShouldBe(isNullableListAfterAttributes ?? isNullableList); - actual.GraphType.ShouldBe(expectedGraphType); - } - } -} diff --git a/src/Tests/NullableTests/TestGraphType.cs b/src/Tests/NullableTests/TestGraphType.cs deleted file mode 100644 index eb1416c..0000000 --- a/src/Tests/NullableTests/TestGraphType.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; -using GraphQL.DI; - -namespace NullableTests -{ - public class TestGraphType : DIObjectGraphType - { - public static TestGraphType Instance = new(); - public new TypeInformation GetTypeInformation(ParameterInfo parameterInfo, bool isInputArgument) => base.GetTypeInformation(parameterInfo, isInputArgument); - public new TypeInformation ApplyAttributes(TypeInformation typeInformation) => base.ApplyAttributes(typeInformation); - } - - public class TestInputGraphType : AutoInputObjectGraphType - { - public static TestInputGraphType Instance = new(); - public new TypeInformation GetTypeInformation(PropertyInfo propertyInfo) => base.GetTypeInformation(propertyInfo); - public new TypeInformation ApplyAttributes(TypeInformation typeInformation) => base.ApplyAttributes(typeInformation); - } - - public class TestClass : DIObjectGraphBase { } -} diff --git a/src/Tests/NullableTests/VerifyTestClasses.cs b/src/Tests/NullableTests/VerifyTestClasses.cs deleted file mode 100644 index 79f7449..0000000 --- a/src/Tests/NullableTests/VerifyTestClasses.cs +++ /dev/null @@ -1,234 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using Shouldly; -using Xunit; - -namespace NullableTests -{ - public class VerifyTestClasses - { - //Verify... methods verify that .NET is building the classes - // with the expected attributes on them. Failure is not an - // error, but simply indicates that the Method and Argument - // tests may not be testing the anticipated scenarios - //Method and Argument should always pass - [Theory] - [InlineData(typeof(NullableClass1), 1, 0)] //default not nullable - [InlineData(typeof(NullableClass2), 2, 0)] //default nullable - [InlineData(typeof(NullableClass5), null, null)] - [InlineData(typeof(NullableClass6), null, null)] - [InlineData(typeof(NullableClass7), 1, 0)] //default not nullable - [InlineData(typeof(NullableClass8), 2, 0)] //default nullable - [InlineData(typeof(NullableClass9), null, null)] - [InlineData(typeof(NullableClass10), null, null)] - [InlineData(typeof(NullableClass11), 1, 0)] //default not nullable - [InlineData(typeof(NullableClass12), null, null)] - [InlineData(typeof(NullableClass13), 1, 0)] //default not nullable - [InlineData(typeof(NullableClass14), 2, 0)] //default nullable - [InlineData(typeof(NullableClass15), null, null)] - [InlineData(typeof(NullableClass16), 1, 0)] - [InlineData(typeof(NullableClass16.NestedClass1), null, 0)] - [InlineData(typeof(NullableClass17), 1, 0)] - [InlineData(typeof(NullableClass18<>), null, null)] - [InlineData(typeof(NullableClass19), 1, 0)] - [InlineData(typeof(NullableClass20), 1, 0)] - public void VerifyTestClass(Type type, int? nullableContext, int? nullable) - { - var actualHasNullableContext = type.CustomAttributes.FirstOrDefault( - x => x.AttributeType.Name == "NullableContextAttribute"); - if (nullableContext == null) { - actualHasNullableContext.ShouldBeNull(); - } else { - actualHasNullableContext.ShouldNotBeNull(); - actualHasNullableContext.ConstructorArguments[0].Value.ShouldBe(nullableContext); - } - - var actualHasNullable = type.CustomAttributes.FirstOrDefault( - x => x.AttributeType.Name == "NullableAttribute"); - if (nullable == null) { - actualHasNullable.ShouldBeNull(); - } else { - actualHasNullable.ShouldNotBeNull(); - actualHasNullable.ConstructorArguments[0].ArgumentType.ShouldBe(typeof(byte)); - actualHasNullable.ConstructorArguments[0].Value.ShouldBe(nullable); - } - } - - [Theory] - [InlineData(typeof(NullableClass1), "Field1", 2, null)] - [InlineData(typeof(NullableClass1), "Field2", 2, null)] - [InlineData(typeof(NullableClass1), "Field3", null, null)] - [InlineData(typeof(NullableClass1), "Field4", null, null)] - [InlineData(typeof(NullableClass1), "Field5", null, null)] - [InlineData(typeof(NullableClass1), "Field6", null, null)] - [InlineData(typeof(NullableClass1), "Field7", null, null)] - [InlineData(typeof(NullableClass1), "Field8", null, null)] - [InlineData(typeof(NullableClass1), "Field9", null, null)] - [InlineData(typeof(NullableClass1), "Field10", null, null)] - [InlineData(typeof(NullableClass2), "Field1", null, null)] - [InlineData(typeof(NullableClass2), "Field2", null, null)] - [InlineData(typeof(NullableClass2), "Field3", null, null)] - [InlineData(typeof(NullableClass2), "Field4", null, null)] - [InlineData(typeof(NullableClass2), "Field5", null, null)] - [InlineData(typeof(NullableClass2), "Field6", null, null)] - [InlineData(typeof(NullableClass2), "Field7", null, null)] - [InlineData(typeof(NullableClass2), "Field8", null, null)] - [InlineData(typeof(NullableClass2), "Field9", 1, null)] - [InlineData(typeof(NullableClass2), "Field10", 1, null)] - [InlineData(typeof(NullableClass2), "Field11", null, null)] - [InlineData(typeof(NullableClass5), "Test", 1, null)] - [InlineData(typeof(NullableClass6), "Field1", 1, null)] - [InlineData(typeof(NullableClass6), "Field2", 2, null)] - [InlineData(typeof(NullableClass7), "Field1", null, null)] - [InlineData(typeof(NullableClass7), "Field2", null, null)] - [InlineData(typeof(NullableClass7), "Field3", 2, null)] - [InlineData(typeof(NullableClass8), "Field1", null, null)] - [InlineData(typeof(NullableClass8), "Field2", null, null)] - [InlineData(typeof(NullableClass8), "Field3", 1, null)] - [InlineData(typeof(NullableClass8), "Field4", null, null)] - [InlineData(typeof(NullableClass9), "Field1", 2, "1")] - [InlineData(typeof(NullableClass10), "Field1", 1, "2")] - [InlineData(typeof(NullableClass11), "Field1", null, null)] - [InlineData(typeof(NullableClass11), "Field2", null, "2")] - [InlineData(typeof(NullableClass12), "Field1", 1, null)] - [InlineData(typeof(NullableClass12), "Field2", null, "12")] - [InlineData(typeof(NullableClass12), "Field3", null, "21")] - [InlineData(typeof(NullableClass12), "Field4", 2, null)] - [InlineData(typeof(NullableClass13), "Field1", null, null)] - [InlineData(typeof(NullableClass13), "Field2", null, null)] - [InlineData(typeof(NullableClass14), "Field1", null, null)] - [InlineData(typeof(NullableClass14), "Field2", null, null)] - [InlineData(typeof(NullableClass15), "Field1", 1, null)] - [InlineData(typeof(NullableClass15), "Field2", null, "12")] - [InlineData(typeof(NullableClass15), "Field3", null, "21")] - [InlineData(typeof(NullableClass15), "Field4", 2, null)] - [InlineData(typeof(NullableClass16), "Field1", null, null)] - [InlineData(typeof(NullableClass16), "Field2", null, null)] - [InlineData(typeof(NullableClass16), "Field3", 2, null)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field1", null, null)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field2", null, null)] - [InlineData(typeof(NullableClass16.NestedClass1), "Field3", 2, null)] - [InlineData(typeof(NullableClass17), "Field1", null, null)] - [InlineData(typeof(NullableClass17), "Field2", null, null)] - [InlineData(typeof(NullableClass17), "Field3", null, "12")] - [InlineData(typeof(NullableClass18<>), "Field1", null, "112")] - [InlineData(typeof(NullableClass18<>), "Field2", null, "11221")] - [InlineData(typeof(NullableClass18<>), "Field3", null, "12")] - [InlineData(typeof(NullableClass18<>), "Field4", null, "12")] - [InlineData(typeof(NullableClass18<>), "Field5", null, "1112")] - [InlineData(typeof(NullableClass18<>), "Field6", null, "112")] - [InlineData(typeof(NullableClass18<>), "Field7", null, "1012")] - [InlineData(typeof(NullableClass18<>), "Field8", null, "1012")] - [InlineData(typeof(NullableClass18<>), "Field9", null, "102")] - [InlineData(typeof(NullableClass18<>), "Field10", null, "112")] - public void VerifyTestMethod(Type type, string methodName, int? nullableContext, string nullableValues) - { - var method = type.GetMethod(methodName); - var methodNullableAttribute = method.CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == "NullableAttribute"); - methodNullableAttribute.ShouldBeNull(); //should not be possible to apply the attribute here - - var methodNullableContextAttribute = method.CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == "NullableContextAttribute"); - if (nullableContext.HasValue) { - methodNullableContextAttribute.ShouldNotBeNull(); - methodNullableContextAttribute.ConstructorArguments[0].ArgumentType.ShouldBe(typeof(byte)); - methodNullableContextAttribute.ConstructorArguments[0].Value.ShouldBeOfType().ShouldBe((byte)nullableContext.Value); - } else { - methodNullableContextAttribute.ShouldBeNull(); - } - - var parameterNullableAttribute = method.ReturnParameter.CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == "NullableAttribute"); - if (nullableValues != null) { - parameterNullableAttribute.ShouldNotBeNull(); - var expectedValues = nullableValues.Select(x => (byte)int.Parse(x.ToString())).ToArray(); - if (expectedValues.Length == 1) { - parameterNullableAttribute.ConstructorArguments[0].ArgumentType.ShouldBe(typeof(byte)); - var actualValue = parameterNullableAttribute.ConstructorArguments[0].Value.ShouldBeOfType().ToString(); - actualValue.ShouldBe(nullableValues); - } else { - parameterNullableAttribute.ConstructorArguments[0].ArgumentType.ShouldBe(typeof(byte[])); - var actualValues = string.Join("", parameterNullableAttribute.ConstructorArguments[0].Value.ShouldBeOfType>().Select(x => x.Value.ToString())); - actualValues.ShouldBe(nullableValues); - } - } else { - parameterNullableAttribute.ShouldBeNull(); - } - } - - [Theory] - [InlineData(typeof(NullableClass20), "Field1", null)] - [InlineData(typeof(NullableClass20), "Field2", null)] - [InlineData(typeof(NullableClass20), "Field3", "2")] - [InlineData(typeof(NullableClass20), "Field4", "12")] - [InlineData(typeof(NullableClass20), "Field5", null)] - [InlineData(typeof(NullableClass20), "Field6", null)] - public void VerifyTestProperty(Type type, string propertyName, string nullableValues) - { - var property = type.GetProperty(propertyName); - var parameterNullableAttribute = property.CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == "NullableAttribute"); - if (nullableValues != null) { - parameterNullableAttribute.ShouldNotBeNull(); - var expectedValues = nullableValues.Select(x => (byte)int.Parse(x.ToString())).ToArray(); - if (expectedValues.Length == 1) { - parameterNullableAttribute.ConstructorArguments[0].ArgumentType.ShouldBe(typeof(byte)); - var actualValue = parameterNullableAttribute.ConstructorArguments[0].Value.ShouldBeOfType().ToString(); - actualValue.ShouldBe(nullableValues); - } else { - parameterNullableAttribute.ConstructorArguments[0].ArgumentType.ShouldBe(typeof(byte[])); - var actualValues = string.Join("", parameterNullableAttribute.ConstructorArguments[0].Value.ShouldBeOfType>().Select(x => x.Value.ToString())); - actualValues.ShouldBe(nullableValues); - } - } else { - parameterNullableAttribute.ShouldBeNull(); - } - } - - [Theory] - [InlineData(typeof(NullableClass9), "Field1", "arg1", null)] - [InlineData(typeof(NullableClass9), "Field1", "arg2", null)] - [InlineData(typeof(NullableClass10), "Field1", "arg1", null)] - [InlineData(typeof(NullableClass10), "Field1", "arg2", null)] - [InlineData(typeof(NullableClass11), "Field2", "arg1", null)] - [InlineData(typeof(NullableClass11), "Field2", "arg2", null)] - [InlineData(typeof(NullableClass13), "Field2", "arg1", null)] - [InlineData(typeof(NullableClass13), "Field2", "arg2", "2")] - [InlineData(typeof(NullableClass13), "Field2", "arg3", null)] - [InlineData(typeof(NullableClass13), "Field2", "arg4", null)] - [InlineData(typeof(NullableClass13), "Field2", "arg5", null)] - [InlineData(typeof(NullableClass13), "Field2", "arg6", null)] - [InlineData(typeof(NullableClass13), "Field2", "arg7", "12")] - [InlineData(typeof(NullableClass13), "Field2", "arg8", "21")] - [InlineData(typeof(NullableClass13), "Field2", "arg9", "2")] - [InlineData(typeof(NullableClass14), "Field2", "arg1", null)] - [InlineData(typeof(NullableClass14), "Field2", "arg2", "1")] - [InlineData(typeof(NullableClass14), "Field2", "arg3", null)] - [InlineData(typeof(NullableClass14), "Field2", "arg4", null)] - [InlineData(typeof(NullableClass14), "Field2", "arg5", null)] - [InlineData(typeof(NullableClass14), "Field2", "arg6", "1")] - [InlineData(typeof(NullableClass14), "Field2", "arg7", "12")] - [InlineData(typeof(NullableClass14), "Field2", "arg8", "21")] - [InlineData(typeof(NullableClass14), "Field2", "arg9", null)] - public void VerifyTestArgument(Type type, string methodName, string argumentName, string nullableValues) - { - var method = type.GetMethod(methodName); - var argument = method.GetParameters().Single(x => x.Name == argumentName); - var parameterNullableAttribute = argument.CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == "NullableAttribute"); - if (nullableValues != null) { - parameterNullableAttribute.ShouldNotBeNull(); - var expectedValues = nullableValues.Select(x => (byte)int.Parse(x.ToString())).ToArray(); - if (expectedValues.Length == 1) { - parameterNullableAttribute.ConstructorArguments[0].ArgumentType.ShouldBe(typeof(byte)); - var actualValue = parameterNullableAttribute.ConstructorArguments[0].Value.ShouldBeOfType().ToString(); - actualValue.ShouldBe(nullableValues); - } else { - parameterNullableAttribute.ConstructorArguments[0].ArgumentType.ShouldBe(typeof(byte[])); - var actualValues = string.Join("", parameterNullableAttribute.ConstructorArguments[0].Value.ShouldBeOfType>().Select(x => x.Value.ToString())); - actualValues.ShouldBe(nullableValues); - } - } else { - parameterNullableAttribute.ShouldBeNull(); - } - } - } -} diff --git a/src/Tests/Tests.csproj b/src/Tests/Tests.csproj index 9867ab8..d225621 100644 --- a/src/Tests/Tests.csproj +++ b/src/Tests/Tests.csproj @@ -13,8 +13,8 @@ - - + +