public void IsValidGraphType(Type inputType, bool isValidType) { var result = GraphValidation.IsValidGraphType(inputType); Assert.AreEqual(isValidType, result); if (!result) { Assert.Throws <GraphTypeDeclarationException>(() => { GraphValidation.IsValidGraphType(inputType, true); }); } }
/// <summary> /// Determines whether the given container could be used as a graph field either because it is /// explictly declared as such or that it conformed to the required parameters of being /// a field. /// </summary> /// <param name="memberInfo">The member information to check.</param> /// <returns> /// <c>true</c> if the info represents a possible graph field; otherwise, <c>false</c>.</returns> protected virtual bool CouldBeGraphField(MemberInfo memberInfo) { // always skip those marked as such regardless of anything else if (memberInfo.HasAttribute <GraphSkipAttribute>()) { return(false); } // when the member declares any known attribute in the library include it // and allow it to generate validation failures if its not properly constructed if (memberInfo.SingleAttributeOfTypeOrDefault <GraphFieldAttribute>() != null) { return(true); } switch (memberInfo) { case MethodInfo mi: if (!GraphValidation.IsValidGraphType(mi.ReturnType, false)) { return(false); } if (mi.GetParameters().Any(x => !GraphValidation.IsValidGraphType(x.ParameterType, false))) { return(false); } break; case PropertyInfo pi: if (pi.GetGetMethod() == null) { return(false); } if (pi.GetIndexParameters().Length > 0) { return(false); } if (!GraphValidation.IsValidGraphType(pi.PropertyType, false)) { return(false); } break; } return(true); }
/// <summary> /// Parses the provided type, extracting the metadata to used in type generation for the object graph. /// </summary> /// <param name="objectType">The type of the object to parse.</param> /// <param name="kind">The kind of graph type to parse for.</param> /// <returns>IGraphTypeTemplate.</returns> public IGraphTypeTemplate ParseType(Type objectType, TypeKind?kind = null) { Validation.ThrowIfNull(objectType, nameof(objectType)); var typeKind = GraphValidation.ResolveTypeKind(objectType, kind); var typeKey = Tuple.Create(typeKind, objectType); if (_knownObjects.TryGetValue(typeKey, out var template) && this.CacheTemplates) { return(template); } if (GraphQLProviders.ScalarProvider.IsScalar(objectType)) { throw new GraphTypeDeclarationException( $"The type '{objectType.FriendlyName()}' is a known scalar type. Scalars must be explicitly defined and cannot be templated.", objectType); } if (Validation.IsCastable <IGraphUnionProxy>(objectType)) { throw new GraphTypeDeclarationException( $"The union proxy '{objectType.FriendlyName()}' cannot be directly parsed as a graph type. Double check " + "your field attribute declarations'.", objectType); } GraphValidation.IsValidGraphType(objectType, true); template = this.MakeTemplate(objectType, typeKind); template.Parse(); template.ValidateOrThrow(); if (this.CacheTemplates) { _knownObjects.TryAdd(typeKey, template); } return(template); }
/// <summary> /// When overridden in a child class this method builds out the template according to its own individual requirements. /// </summary> protected override void ParseTemplateDefinition() { _fieldDeclaration = this.SingleAttributeOfTypeOrDefault <GraphFieldAttribute>(); // ------------------------------------ // Common Metadata // ------------------------------------ this.Route = this.GenerateFieldPath(); this.Mode = _fieldDeclaration?.ExecutionMode ?? FieldResolutionMode.PerSourceItem; this.Complexity = _fieldDeclaration?.Complexity; this.Description = this.SingleAttributeOfTypeOrDefault <DescriptionAttribute>()?.Description; var depreciated = this.SingleAttributeOfTypeOrDefault <DeprecatedAttribute>(); if (depreciated != null) { this.IsDeprecated = true; this.DeprecationReason = depreciated.Reason?.Trim(); } var objectType = GraphValidation.EliminateWrappersFromCoreType(this.DeclaredReturnType); var typeExpression = GraphValidation.GenerateTypeExpression(this.DeclaredReturnType, this); typeExpression = typeExpression.CloneTo(GraphTypeNames.ParseName(objectType, this.Parent.Kind)); // ------------------------------------ // Gather Possible Types and/or union definition // ------------------------------------ this.BuildUnionProxyInstance(); if (this.UnionProxy != null) { this.PossibleTypes = new List <Type>(this.UnionProxy.Types); } else { this.PossibleTypes = new List <Type>(); // the possible types attribte is optional but expects taht the concrete types are added // to the schema else where lest a runtime exception occurs of a missing graph type. var typesAttrib = this.SingleAttributeOfTypeOrDefault <PossibleTypesAttribute>(); if (typesAttrib != null) { foreach (var type in typesAttrib.PossibleTypes) { this.PossibleTypes.Add(type); } } // add any types decalred on the primary field declaration if (_fieldDeclaration != null) { foreach (var type in _fieldDeclaration.Types) { var strippedType = GraphValidation.EliminateWrappersFromCoreType(type); if (strippedType != null) { this.PossibleTypes.Add(strippedType); } } } if (objectType != null && !Validation.IsCastable <IGraphActionResult>(objectType) && GraphValidation.IsValidGraphType(objectType)) { this.PossibleTypes.Insert(0, objectType); } } this.PossibleTypes = this.PossibleTypes.Distinct().ToList(); // ------------------------------------ // Adjust the action result to the actual return type this field returns // ------------------------------------ if (Validation.IsCastable <IGraphActionResult>(objectType)) { if (this.UnionProxy != null) { // if a union was decalred preserve whatever modifer elements // were decalred but alter the return type to object (a known common element among all members of the union) objectType = typeof(object); } else if (_fieldDeclaration != null && _fieldDeclaration.Types.Count > 0) { objectType = _fieldDeclaration.Types[0]; typeExpression = GraphValidation.GenerateTypeExpression(objectType, this) .CloneTo(GraphTypeNames.ParseName(objectType, this.Parent.Kind)); objectType = GraphValidation.EliminateWrappersFromCoreType(objectType); } else if (this.PossibleTypes.Count > 0) { objectType = this.PossibleTypes[0]; typeExpression = GraphValidation.GenerateTypeExpression(objectType, this) .CloneTo(GraphTypeNames.ParseName(objectType, this.Parent.Kind)); objectType = GraphValidation.EliminateWrappersFromCoreType(objectType); } else { objectType = typeof(object); } } this.ObjectType = objectType; if (this.UnionProxy != null) { this.TypeExpression = typeExpression.CloneTo(this.UnionProxy.Name); } else { this.TypeExpression = typeExpression; } // ------------------------------------ // Async Requirements // ------------------------------------ this.IsAsyncField = Validation.IsCastable <Task>(this.DeclaredReturnType); // ------------------------------------ // Security Policies // ------------------------------------ _securityPolicies = FieldSecurityGroup.FromAttributeCollection(this.AttributeProvider); }