/// <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); } } }
/// <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); }
/// <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); }