private static CachedField GetFieldsAndFragmentNames( ValidationContext context, Dictionary <SelectionSet, CachedField> cachedFieldsAndFragmentNames, IGraphType?parentType, SelectionSet selectionSet) { cachedFieldsAndFragmentNames.TryGetValue(selectionSet, out CachedField cached); if (cached == null) { var nodeAndDef = new Dictionary <string, List <FieldDefPair> >(); var fragmentNames = new HashSet <string>(); CollectFieldsAndFragmentNames( context, parentType, selectionSet, nodeAndDef, fragmentNames); cached = new CachedField { NodeAndDef = nodeAndDef, Names = fragmentNames.ToList() }; cachedFieldsAndFragmentNames.Add(selectionSet, cached); } return(cached); }
/// <summary> /// Initializes an instance of <see cref="ExecutionNode"/> with the specified values /// </summary> /// <param name="parent">The parent node, or <see langword="null"/> if this is the root node</param> /// <param name="graphType">The graph type of this node, unwrapped if it is a <see cref="NonNullGraphType"/>. Array nodes will be a <see cref="ListGraphType"/> instance.</param> /// <param name="field">The AST field of this node</param> /// <param name="fieldDefinition">The graph's field type of this node</param> /// <param name="indexInParentNode">For child array item nodes of a <see cref="ListGraphType"/>, the index of this array item within the field; otherwise, <see langword="null"/></param> protected ExecutionNode(ExecutionNode?parent, IGraphType?graphType, Field?field, FieldType?fieldDefinition, int?indexInParentNode) { Parent = parent; GraphType = graphType; Field = field; FieldDefinition = fieldDefinition; IndexInParentNode = indexInParentNode; }
internal static (IGraphType?resolvedType, Type?type) GetNamedTypes(this IGraphType?type) { return(type switch { NonNullGraphType nonNull => nonNull.ResolvedType != null?GetNamedTypes(nonNull.ResolvedType) : (null, GetNamedType(nonNull.Type !)), ListGraphType list => list.ResolvedType != null?GetNamedTypes(list.ResolvedType) : (null, GetNamedType(list.Type !)), _ => (type, null) });
private static void CollectFieldsAndFragmentNames( ValidationContext context, IGraphType?parentType, SelectionSet selectionSet, Dictionary <string, List <FieldDefPair> > nodeAndDefs, HashSet <string> fragments) { for (int i = 0; i < selectionSet.Selections.Count; i++) { var selection = selectionSet.Selections[i]; if (selection is Field field) { var fieldName = field.Name; FieldType?fieldDef = null; if (isObjectType(parentType) || isInterfaceType(parentType)) { fieldDef = (parentType as IComplexGraphType) !.GetField(fieldName); } var responseName = !string.IsNullOrWhiteSpace(field.Alias) ? field.Alias ! : fieldName; if (!nodeAndDefs.ContainsKey(responseName)) { nodeAndDefs[responseName] = new List <FieldDefPair>(); } nodeAndDefs[responseName].Add(new FieldDefPair { ParentType = parentType, Field = selection, FieldDef = fieldDef }); } else if (selection is FragmentSpread fragmentSpread) { fragments.Add(fragmentSpread.Name); } else if (selection is InlineFragment inlineFragment) { var typeCondition = inlineFragment.Type; var inlineFragmentType = typeCondition != null ? typeCondition.GraphTypeFromType(context.Schema) : parentType; CollectFieldsAndFragmentNames( context, inlineFragmentType, inlineFragment.SelectionSet, nodeAndDefs, fragments); } } }
/// <summary> /// Returns the parent graph type of this node. /// </summary> public IObjectGraphType?GetParentType(ISchema schema) { IGraphType?parentType = Parent?.GraphType; if (parentType is IObjectGraphType objectType) { return(objectType); } if (parentType is IAbstractGraphType abstractType) { return(abstractType.GetObjectType(Parent !.Result !, schema)); } return(null); }
private static void Field(IGraphType?type, Field field, ValidationContext context) { if (type == null) { return; } if (type.IsLeafType()) { if (field.SelectionSet != null && field.SelectionSet.Selections.Count > 0) { context.ReportError(new ScalarLeafsError(context, field.SelectionSet, field, type)); } } else if (field.SelectionSet == null || field.SelectionSet.Selections.Count == 0) { context.ReportError(new ScalarLeafsError(context, field, type)); } }
public static object ToObject(this IDictionary <string, object?> source, Type type, IGraphType?mappedType = null) { // Given Field(x => x.FName).Name("FirstName") and key == "FirstName" returns "FName" string GetPropertyName(string key, out FieldType?field) { var complexType = mappedType?.GetNamedType() as IComplexGraphType; // type may not contain mapping information field = complexType?.GetField(key); return(field?.GetMetadata(ComplexGraphType <object> .ORIGINAL_EXPRESSION_PROPERTY_NAME, key) ?? key); } // Returns values (from source or defaults) that match constructor signature + used keys from source (List <object>?, List <string>?) GetValuesAndUsedKeys(ParameterInfo[] parameters) { // parameterless constructors are the most common use case if (parameters.Length == 0) { return(_emptyValues, null); } // otherwise we have to iterate over the parameters - worse performance but this is rather rare case List <object>?values = null; List <string>?keys = null; if (parameters.All(p => { // Source values take precedence if (source.Any(keyValue => { bool matched = string.Equals(GetPropertyName(keyValue.Key, out var _), p.Name, StringComparison.InvariantCultureIgnoreCase); if (matched) { (values ??= new List <object>()).Add(keyValue.Value); (keys ??= new List <string>()).Add(keyValue.Key); } return(matched); })) { return(true); } // Then check for default values if any if (p.HasDefaultValue) { (values ??= new List <object>()).Add(p.DefaultValue); return(true); } return(false); })) { return(values, keys); } return(null, null); } if (source == null) { throw new ArgumentNullException(nameof(source)); } // force sourceType to be IDictionary<string, object> if (ValueConverter.TryConvertTo(source, type, out object?result, typeof(IDictionary <string, object>))) { return(result !); } // attempt to use the most specific constructor sorting in decreasing order of parameters number var ctorCandidates = _types.GetOrAdd(type, t => t.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).OrderByDescending(ctor => ctor.GetParameters().Length).ToArray()); ConstructorInfo?targetCtor = null; ParameterInfo[]? ctorParameters = null; List <object>?values = null; List <string>?usedKeys = null; foreach (var ctor in ctorCandidates) { var parameters = ctor.GetParameters(); (values, usedKeys) = GetValuesAndUsedKeys(parameters); if (values != null) { targetCtor = ctor; ctorParameters = parameters; break; } } if (targetCtor == null || ctorParameters == null || values == null) { throw new ArgumentException($"Type '{type}' does not contain a constructor that could be used for current input arguments.", nameof(type)); } object?[] ctorArguments = ctorParameters.Length == 0 ? Array.Empty <object>() : new object[ctorParameters.Length]; for (int i = 0; i < ctorParameters.Length; ++i) { object?arg = GetPropertyValue(values[i], ctorParameters[i].ParameterType); ctorArguments[i] = arg; } object obj; try { obj = targetCtor.Invoke(ctorArguments); } catch (TargetInvocationException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); return(""); // never executed, necessary only for intellisense } foreach (var item in source) { // these parameters have already been used in the constructor, no need to set property if (usedKeys?.Any(k => k == item.Key) == true) { continue; } string propertyName = GetPropertyName(item.Key, out var field); PropertyInfo?propertyInfo = null; try { propertyInfo = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); } catch (AmbiguousMatchException) { propertyInfo = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); } if (propertyInfo != null && propertyInfo.CanWrite) { object?value = GetPropertyValue(item.Value, propertyInfo.PropertyType, field?.ResolvedType); propertyInfo.SetValue(obj, value, null); //issue: this works even if propertyInfo is ValueType and value is null } } return(obj); }
public static object?GetPropertyValue(this object?propertyValue, Type fieldType, IGraphType?mappedType = null) { // Short-circuit conversion if the property value already of the right type if (propertyValue == null || fieldType == typeof(object) || fieldType.IsInstanceOfType(propertyValue)) { return(propertyValue); } if (ValueConverter.TryConvertTo(propertyValue, fieldType, out object?result)) { return(result); } var enumerableInterface = fieldType.Name == "IEnumerable`1" ? fieldType : fieldType.GetInterface("IEnumerable`1"); if (fieldType != typeof(string) && enumerableInterface != null) { IList newCollection; var elementType = enumerableInterface.GetGenericArguments()[0]; var underlyingType = Nullable.GetUnderlyingType(elementType) ?? elementType; var fieldTypeImplementsIList = fieldType.GetInterface("IList") != null; var propertyValueAsIList = propertyValue as IList; // Custom container if (fieldTypeImplementsIList && !fieldType.IsArray) { newCollection = (IList)Activator.CreateInstance(fieldType); } // Array of known size is created immediately else if (fieldType.IsArray && propertyValueAsIList != null) { newCollection = Array.CreateInstance(elementType, propertyValueAsIList.Count); } // List<T> else { var genericListType = typeof(List <>).MakeGenericType(elementType); newCollection = (IList)Activator.CreateInstance(genericListType); } if (!(propertyValue is IEnumerable valueList)) { return(newCollection); } // Array of known size is populated in-place if (fieldType.IsArray && propertyValueAsIList != null) { for (int i = 0; i < propertyValueAsIList.Count; ++i) { var listItem = propertyValueAsIList[i]; newCollection[i] = listItem == null ? null : GetPropertyValue(listItem, underlyingType, mappedType); } } // Array of unknown size is created only after populating list else { foreach (var listItem in valueList) { newCollection.Add(listItem == null ? null : GetPropertyValue(listItem, underlyingType, mappedType)); } if (fieldType.IsArray) { newCollection = ((dynamic)newCollection).ToArray(); } } return(newCollection); } var value = propertyValue; var nullableFieldType = Nullable.GetUnderlyingType(fieldType); // if this is a nullable type and the value is null, return null if (nullableFieldType != null && value == null) { return(null); } if (nullableFieldType != null) { fieldType = nullableFieldType; } if (propertyValue is IDictionary <string, object?> objects) { return(ToObject(objects, fieldType, mappedType)); } if (fieldType.IsEnum) { if (value == null) { var enumNames = Enum.GetNames(fieldType); value = enumNames[0]; } if (!IsDefinedEnumValue(fieldType, value)) { throw new InvalidOperationException($"Unknown value '{value}' for enum '{fieldType.Name}'."); } string str = value.ToString(); value = Enum.Parse(fieldType, str, true); } return(ValueConverter.ConvertTo(value, fieldType)); }
public ObjectExecutionNode(ExecutionNode?parent, IGraphType?graphType, Field?field, FieldType?fieldDefinition, int?indexInParentNode) : base(parent, graphType, field, fieldDefinition, indexInParentNode) { }
/// <summary> /// Initializes a new instance for the specified inner graph type. /// </summary> public NonNullGraphType(IGraphType?type) { ResolvedType = type; }
private static bool isObjectType(IGraphType?parentType) { return(parentType is IObjectGraphType); }
private static bool isInterfaceType(IGraphType?parentType) { return(parentType is IInterfaceGraphType); }
public int Compare(IGraphType?x, IGraphType?y) => (x?.Name ?? "").CompareTo(y?.Name ?? "");
private static List <Conflict> FindConflictsWithinSelectionSet( ValidationContext context, Dictionary <SelectionSet, CachedField> cachedFieldsAndFragmentNames, PairSet comparedFragmentPairs, IGraphType?parentType, SelectionSet selectionSet) { var conflicts = new List <Conflict>(); CachedField cachedField = GetFieldsAndFragmentNames( context, cachedFieldsAndFragmentNames, parentType, selectionSet); var fieldMap = cachedField.NodeAndDef; var fragmentNames = cachedField.Names; CollectConflictsWithin( context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap); if (fragmentNames.Count != 0) { // (B) Then collect conflicts between these fields and those represented by // each spread fragment name found. var comparedFragments = new HashSet <string>(); for (int i = 0; i < fragmentNames.Count; i++) { CollectConflictsBetweenFieldsAndFragment( context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, false, fieldMap, fragmentNames[i]); // (C) Then compare this fragment with all other fragments found in this // selection set to collect conflicts between fragments spread together. // This compares each item in the list of fragment names to every other // item in that same list (except for itself). for (int j = i + 1; j < fragmentNames.Count; j++) { CollectConflictsBetweenFragments( context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, fragmentNames[i], fragmentNames[j]); } } } return(conflicts); }
private static List <Conflict> FindConflictsBetweenSubSelectionSets( ValidationContext context, Dictionary <SelectionSet, CachedField> cachedFieldsAndFragmentNames, PairSet comparedFragmentPairs, bool areMutuallyExclusive, IGraphType?parentType1, SelectionSet selectionSet1, IGraphType?parentType2, SelectionSet selectionSet2) { var conflicts = new List <Conflict>(); var cachedField1 = GetFieldsAndFragmentNames( context, cachedFieldsAndFragmentNames, parentType1, selectionSet1); var fieldMap1 = cachedField1.NodeAndDef; var fragmentNames1 = cachedField1.Names; var cachedField2 = GetFieldsAndFragmentNames( context, cachedFieldsAndFragmentNames, parentType2, selectionSet2); var fieldMap2 = cachedField2.NodeAndDef; var fragmentNames2 = cachedField2.Names; // (H) First, collect all conflicts between these two collections of field. CollectConflictsBetween( context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (I) Then collect conflicts between the first collection of fields and // those referenced by each fragment name associated with the second. if (fragmentNames2.Count != 0) { var comparedFragments = new HashSet <string>(); for (var j = 0; j < fragmentNames2.Count; j++) { CollectConflictsBetweenFieldsAndFragment( context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fragmentNames2[j]); } } // (I) Then collect conflicts between the second collection of fields and // those referenced by each fragment name associated with the first. if (fragmentNames1.Count != 0) { var comparedFragments = new HashSet <string>(); for (var i = 0; i < fragmentNames1.Count; i++) { CollectConflictsBetweenFieldsAndFragment( context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap2, fragmentNames1[i]); } } // (J) Also collect conflicts between any fragment names by the first and // fragment names by the second. This compares each item in the first set of // names to each item in the second set of names. for (var i = 0; i < fragmentNames1.Count; i++) { for (var j = 0; j < fragmentNames2.Count; j++) { CollectConflictsBetweenFragments( context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentNames1[i], fragmentNames2[j]); } } return(conflicts); }
public virtual void RegisterCommands(IEnumerable <Type> commandHandlerTypes) { foreach (var commandHandlerType in commandHandlerTypes) { var genericType = commandHandlerType.GetInterfaces().Single(x => x.GetTypeInfo().IsAssignableToGenericType(typeof(ICommandHandler <,>)) || x.GetTypeInfo().IsAssignableToGenericType(typeof(IAsyncCommandHandler <,>))); var genericArguments = genericType.GetGenericArguments(); var commandType = genericArguments[0]; var resultType = genericArguments[1]; var descriptionAttribute = commandType.GetCustomAttribute <DescriptionAttribute>(); var exposeAttribute = commandType.GetCustomAttribute <ExposeGraphQLAttribute>(); var authorizeAttribute = commandType.GetCustomAttribute <AuthorizeAttribute>(); if (exposeAttribute == null) { continue; } IInputObjectGraphType?inputGqlType = null; var properties = commandType.GetProperties(BindingFlags.Instance | BindingFlags.Public); if (properties.Any()) { var inputObjectType = typeof(InputObjectGraphType <>).MakeGenericType(commandType); inputGqlType = (IInputObjectGraphType)Activator.CreateInstance(inputObjectType); inputGqlType.Name = commandType.Name; inputGqlType.Description = descriptionAttribute?.Description; var addFieldMethod = inputGqlType.GetType().GetMethod("AddField"); foreach (var propertyInfo in properties) { addFieldMethod.Invoke(inputGqlType, new[] { new FieldType() { Name = propertyInfo.Name.ToCamelCase(), Type = propertyInfo.PropertyType.GetGraphTypeFromType(), Description = propertyInfo.GetCustomAttribute <DescriptionAttribute>()?.Description } }); } } IGraphType?resultGqlType = null; if (!GraphTypeTypeRegistry.Contains(resultType)) { var resultTypeName = resultType.Name; var returnObjectType = typeof(EntityGraphType <>).MakeGenericType(resultType); resultGqlType = (IGraphType)Activator.CreateInstance(returnObjectType, null); resultGqlType.Name = resultTypeName; if (authorizeAttribute != null) { var permissions = resultGqlType.GetMetadata <List <string> >(GraphQLExtensions.PermissionsKey); if (permissions == null) { permissions = new List <string>(); resultGqlType.Metadata[GraphQLExtensions.PermissionsKey] = permissions; } if (!string.IsNullOrWhiteSpace(authorizeAttribute.Permission)) { permissions.Add(authorizeAttribute.Permission); } } } var arguments = new List <QueryArgument>(); if (inputGqlType != null) { var argument = AllowNullCommand ? new QueryArgument(inputGqlType) : new QueryArgument(new NonNullGraphType(inputGqlType)); argument.Name = "command"; arguments.Add(argument); } var mutationName = exposeAttribute.IsFieldNameSet ? exposeAttribute.FieldName : new Regex("Command$").Replace(commandType.Name, ""); if (!Mutation.HasField(mutationName)) { var type = new FieldType { Type = resultGqlType == null?GraphTypeTypeRegistry.Get(resultType) : null, ResolvedType = resultGqlType, Resolver = new CommandResolver(_container, commandHandlerType, commandType, GetJsonSerializerSettings()), Name = GetNormalizedFieldName(mutationName), Description = descriptionAttribute?.Description, Arguments = new QueryArguments(arguments) }; Mutation.AddField(type); } } }
internal static bool IsGraphQLTypeReference(this IGraphType?type) { var(namedType, _) = type.GetNamedTypes(); return(namedType is GraphQLTypeReference); }
public ListGraphType(IGraphType?type) { ResolvedType = type; }
public virtual void RegisterQueries(IEnumerable <Type> queryHandlerTypes) { foreach (var queryHandlerType in queryHandlerTypes) { var genericType = queryHandlerType.GetInterfaces().Single(x => x.GetTypeInfo().IsAssignableToGenericType(typeof(IQueryHandler <,>)) || x.GetTypeInfo().IsAssignableToGenericType(typeof(IAsyncQueryHandler <,>))); var genericArguments = genericType.GetGenericArguments(); var queryType = genericArguments[0]; var resultType = genericArguments[1]; if (queryType.GetCustomAttribute <ExposeGraphQLAttribute>() == null) { continue; } var isCollection = resultType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(resultType); if (isCollection) { resultType = resultType.GetGenericArguments()[0]; } var descriptionAttribute = queryType.GetCustomAttribute <DescriptionAttribute>(); var exposeAttribute = queryType.GetCustomAttribute <ExposeGraphQLAttribute>(); var authorizeAttribute = queryType.GetCustomAttribute <AuthorizeAttribute>(); if (exposeAttribute == null) { continue; } var properties = queryType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(x => x.PropertyType != typeof(ResolveFieldContext)); IInputObjectGraphType?inputGqlType = null; if (properties.Any()) { var inputObjectType = typeof(AutoInputGraphType <>).MakeGenericType(queryType); inputGqlType = (IInputObjectGraphType)_container.GetInstance(inputObjectType); inputGqlType.Description = descriptionAttribute?.Description; var addFieldMethod = inputGqlType.GetType().GetMethod("AddField"); foreach (var propertyInfo in properties) { addFieldMethod.Invoke(inputGqlType, new[] { new FieldType() { Name = GetNormalizedFieldName(propertyInfo.Name), Type = propertyInfo.PropertyType.GetGraphTypeFromType(IsNullableProperty(propertyInfo)), Description = propertyInfo.GetCustomAttribute <DescriptionAttribute>()?.Description } }); } } IGraphType?resultGqlType = null; if (!GraphTypeTypeRegistry.Contains(resultType)) { var resultTypeName = GetTypeName(resultType); var returnObjectType = typeof(AutoObjectGraphType <>).MakeGenericType(resultType); resultGqlType = (IGraphType)_container.GetInstance(returnObjectType); resultGqlType.Name = resultTypeName; if (isCollection) { var name = resultGqlType.Name; var listGqlType = (ListGraphType)Activator.CreateInstance(typeof(ListGraphType <>).MakeGenericType(returnObjectType), null); listGqlType.ResolvedType = resultGqlType; resultGqlType = (IGraphType)listGqlType; resultGqlType.Name = "ListOf" + name; } if (authorizeAttribute != null) { var permissions = resultGqlType.GetMetadata <List <string> >(GraphQLExtensions.PermissionsKey); if (permissions == null) { permissions = new List <string>(); resultGqlType.Metadata[GraphQLExtensions.PermissionsKey] = permissions; } if (!string.IsNullOrWhiteSpace(authorizeAttribute.Permission)) { permissions.Add(authorizeAttribute.Permission); } } } var arguments = new List <QueryArgument>(); if (inputGqlType != null) { var argument = AllowNullQuery ? new QueryArgument(inputGqlType) : new QueryArgument(new NonNullGraphType(inputGqlType)); argument.Name = "query"; arguments.Add(argument); } var queryName = exposeAttribute.IsFieldNameSet ? exposeAttribute.FieldName : new Regex("Query").Replace(queryType.Name, ""); if (!Query.HasField(queryName)) { var type = new FieldType { Type = resultGqlType == null?GraphTypeTypeRegistry.Get(resultType) : null, ResolvedType = resultGqlType, Resolver = new QueryResolver(_container, queryHandlerType, queryType, GetJsonSerializerSettings()), Name = GetNormalizedFieldName(queryName), Description = descriptionAttribute?.Description, Arguments = new QueryArguments(arguments) }; Query.AddField(type); } } }