Example #1
0
        /// <summary>
        /// Updates this instance with a piece of data recieved from the completion of a user resolver function.
        /// This method performs no validation or resolution of the data, it simply assigns it as a value that was recieved
        /// from a resolver function and attaches it to this instance.
        /// </summary>
        /// <param name="data">The data.</param>
        public virtual void AssignResult(object data)
        {
            _childListItems = null;
            _childFields    = null;

            this.ResultData = data;
            this.SetStatus(FieldItemResolutionStatus.ResultAssigned);

            // initialize the children of this instance from the assigned result
            if (this.IsListField)
            {
                if (this.ResultData == null)
                {
                    return;
                }

                if (!GraphValidation.IsValidListType(this.ResultData.GetType()))
                {
                    return;
                }

                if (!(this.ResultData is IEnumerable arraySource))
                {
                    return;
                }

                _childListItems = new List <GraphDataItem>();

                // this instances's type expression is a list (or else we wouldnt be rendering out children)
                // strip out that outer most list component
                // to represent what each element of said list should be so that the rule
                // processor can validate the item as its own entity
                var childTypeExpression = this.TypeExpression.UnWrapExpression(MetaGraphTypes.IsList);

                var path  = this.Path.Clone();
                int index = 0;

                foreach (var item in arraySource)
                {
                    var indexedPath = path.Clone();
                    indexedPath.AddArrayIndex(index++);
                    var childItem = new GraphDataItem(this.FieldContext, item, indexedPath)
                    {
                        TypeExpression = childTypeExpression.Clone(),
                    };

                    this.AddListItem(childItem);

                    // child items are immediately resolved
                    // using the enumerated source data from the resolver that supplied data
                    // to this parent item.
                    // that is to say that no child resolver is needed to be processed to
                    // retrieve data for each child individually.
                    childItem.AssignResult(item);
                    childItem.SetStatus(this.Status);
                }
            }
        }
Example #2
0
        /// <summary>
        /// Validates the completed field context to ensure it is "correct" against the specification before finalizing its reslts.
        /// </summary>
        /// <param name="context">The context containing the resolved field.</param>
        /// <returns><c>true</c> if the node is valid, <c>false</c> otherwise.</returns>
        public override bool Execute(FieldValidationContext context)
        {
            var typeExpression = context.TypeExpression;

            if (typeExpression.IsRequired && context.ResultData == null)
            {
                // 6.4.3 section 1c
                this.ValidationError(
                    context,
                    $"Field '{context.FieldPath}' expected a non-null result but received {{null}}.");

                context.DataItem.InvalidateResult();
            }

            // 6.4.3 section 2
            if (context.ResultData == null)
            {
                return(true);
            }

            // 6.4.3 section 3, ensure an IEnumerable for a type expression that is a list
            if (typeExpression.IsListOfItems && !GraphValidation.IsValidListType(context.ResultData.GetType()))
            {
                this.ValidationError(
                    context,
                    $"Field '{context.FieldPath}' was expected to return a list of items but instead returned a single item.");

                context.DataItem.InvalidateResult();
                return(true);
            }

            var graphType = context.Schema?.KnownTypes.FindGraphType(typeExpression?.TypeName);

            if (graphType == null)
            {
                this.ValidationError(
                    context,
                    $"The graph type for field '{context.FieldPath}' ({typeExpression?.TypeName}) does not exist on the target schema. The field" +
                    $"cannot be properly evaluated.");

                context.DataItem.InvalidateResult();
            }
            else if (!typeExpression.Matches(context.ResultData, graphType.ValidateObject))
            {
                // generate a valid, properly cased type expression reference for the data that was provided
                var actualExpression = GraphValidation.GenerateTypeExpression(context.ResultData.GetType());
                var coreType         = GraphValidation.EliminateWrappersFromCoreType(context.ResultData.GetType());
                var actualType       = context.Schema.KnownTypes.FindGraphType(coreType);
                if (actualType != null)
                {
                    actualExpression = actualExpression.CloneTo(actualType.Name);
                }

                // 6.4.3  section 4 & 5
                this.ValidationError(
                    context,
                    $"The resolved value for field '{context.FieldPath}' does not match the required type expression. " +
                    $"Expected {typeExpression} but got {actualExpression}.");

                context.DataItem.InvalidateResult();
            }

            return(true);
        }
        public void IsValidListType(Type inputType, bool isValidListType)
        {
            var result = GraphValidation.IsValidListType(inputType);

            Assert.AreEqual(isValidListType, result);
        }
Example #4
0
        /// <summary>
        /// Validates the completed field context to ensure it is "correct" against the specification before finalizing its reslts.
        /// </summary>
        /// <param name="context">The context containing the resolved field.</param>
        /// <returns><c>true</c> if the node is valid, <c>false</c> otherwise.</returns>
        public override bool Execute(FieldValidationContext context)
        {
            var dataObject = context.ResultData;

            // 6.4.3 section 1c
            // This is a quick short cut and customed error message for a common top-level null mismatch.
            var dataItemTypeExpression = context.DataItem.TypeExpression;

            if (dataItemTypeExpression.IsRequired && dataObject == null)
            {
                this.ValidationError(
                    context,
                    $"Field '{context.FieldPath}' expected a non-null result but received {{null}}.");

                context.DataItem.InvalidateResult();
            }

            // 6.4.3 section 2
            if (dataObject == null)
            {
                return(true);
            }

            // 6.4.3 section 3, ensure list type in the result object for a type expression that is a list.
            // This is a quick short cut and customed error message for a common top-level list mismatch.
            if (dataItemTypeExpression.IsListOfItems && !GraphValidation.IsValidListType(dataObject.GetType()))
            {
                this.ValidationError(
                    context,
                    $"Field '{context.FieldPath}' was expected to return a list of items but instead returned a single item.");

                context.DataItem.InvalidateResult();
                return(true);
            }

            var expectedGraphType = context.Schema?.KnownTypes.FindGraphType(context.Field);

            if (expectedGraphType == null)
            {
                this.ValidationError(
                    context,
                    $"The graph type for field '{context.FieldPath}' ({dataItemTypeExpression?.TypeName}) does not exist on the target schema. The field" +
                    "cannot be properly evaluated.");

                context.DataItem.InvalidateResult();
                return(true);
            }

            // Perform a deep check of the meta-type chain (list and nullability wrappers) against the result data.
            // For example, if the type expression is [[SomeType]] ensure the result object is List<List<T>> etc.)
            // however, use the type name of the actual data object, not the graph type itself
            // we only want to check the type expression wrappers in this step
            var rootSourceType        = GraphValidation.EliminateWrappersFromCoreType(dataObject.GetType());
            var mangledTypeExpression = dataItemTypeExpression.CloneTo(rootSourceType.Name);

            if (!mangledTypeExpression.Matches(dataObject))
            {
                // generate a valid, properly cased type expression reference for the data that was provided
                var actualExpression = GraphValidation.GenerateTypeExpression(context.ResultData.GetType());

                // Fake the type expression against the real graph type
                // this step only validates the meta graph types the actual type may be different (but castable to the concrete
                // type of the graphType). Don't confuse the user in this step.
                actualExpression = actualExpression.CloneTo(expectedGraphType.Name);

                // 6.4.3  section 4 & 5
                this.ValidationError(
                    context,
                    $"The resolved value for field '{context.FieldPath}' does not match the required type expression. " +
                    $"Expected {dataItemTypeExpression} but got {actualExpression}.");

                context.DataItem.InvalidateResult();
            }

            return(true);
        }