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);
                });
            }
        }
Example #2
0
        /// <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);
        }
Example #4
0
        /// <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);
        }