/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the TContext /// if it cannot process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified node; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(FieldValidationContext context) { return(context?.DataItem != null && context.DataItem.Status == FieldItemResolutionStatus.ResultAssigned && context.ResultData != null && !context.DataItem.TypeExpression.IsListOfItems); }
/// <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) { if (!context.DataItem.Status.IsFinalized()) { // any status other ResultAssigned or NotStarted indicates a non-processing such as complete or failed etc. // and should be left in tact and unedited. if (context.DataItem.Status == FieldItemResolutionStatus.ResultAssigned) { // if a data value was set ensure any potnetial children are processed if (context.DataItem.ResultData == null || context.DataItem.FieldContext.Field.IsLeaf) { context.DataItem.Complete(); } else { context.DataItem.RequireChildResolution(); } } else { // this rule can only run against field resolutions being validated // as complete. Something that hasnt been started yet has missed its chance context.DataItem.Fail(); } } return(true); }
/// <summary> /// Determines where the context is in a state such that it should continue processing its children, if any exist. /// Returning false will cease processing child items under the active item of this context. This can be useful /// if/when a situation in a parent disqualifies all other items in a processing tree. This step is always executed /// even if the primary execution is skipped or fails. /// </summary> /// <param name="context">The context.</param> /// <returns><c>true</c> if child rulesets should be executed, <c>false</c> otherwise.</returns> public override bool ShouldAllowChildContextsToExecute(FieldValidationContext context) { // if a context indicates an error when validating schema values, downstream children don't // matter. useful in preventing individual list item checks when the list itself // doesnt conform to required values. return(context?.DataItem != null && !context.DataItem.Status.IndicatesAnError()); }
/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the TContext /// if it cannot process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified node; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(FieldValidationContext context) { // this rule can only validate that a non-null result // is valid and "could" be used down stream // lists arent relevenat return(!context.DataItem.IsListField && context.ResultData != null); }
/// <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) { // during the execution of children to this context item // its possible that one of those fields (or child list items) resolved to null // either because the resolver returned null natively or because of an error // during processing. If this occurs and that child violates its type // expression requirements (handled in 6.4.3) then this item is also in // voliation of its type expression. // // note: each child is aware of its own type expression requirements // meaning that if this item in scope is a list of required items (e.g. [SomeType!]) // then each of those child items would indicate their own errors // if they were nulled since they understand that their type expression is SomeType! // we only need to worry about carrying the errors up the chain // to this item. var hasChildErrors = context .DataItem .Children .Any(x => x.Status.IndicatesAnError()); // 6.4.4 does not actaully raise an error message. Its a rule // of propegating down stream errors up the responsibility chain // as null values are resolved if (hasChildErrors) { context.DataItem.InvalidateResult(); } // a recorded validation error here doesnt necessarily indicate a complete // failure as an upstream field may successfully capture a null result // to allow some data to be resolved to the client return(true); }
public override FieldValidationResult Validate(object value, FieldValidationContext context) { var validationResult = base.Validate(value, context); if (validationResult == null) { return(null); } return(base.Validate(context.PreviousApplicationState.Applicants[0].Password, context)); }
internal override FieldValidationResult ValidateIfSpecified(string value, FieldValidationContext context) { if (value != context.Application.Applicants[0].Password) { return(new FieldValidationResult { ErrorMessage = "Passwords must match." }); } return(null); }
internal override FieldValidationResult ValidateIfSpecified(string value, FieldValidationContext context) { if (value.Length < MinimumLength || value.Length > MaximumLength) { return(new FieldValidationResult { ErrorMessage = $"The field must be between {MinimumLength} and {MaximumLength} characters." }); } return(null); }
/// <summary> /// Registers a validation error with the local message collection as a critical error. The validation /// message will automatically be appended with the appropriate message extensions to reference the error being validated. /// </summary> /// <param name="context">The validation context in scope.</param> /// <param name="message">The error message to apply.</param> /// <param name="exception">The exception to add to the message, if any.</param> protected void ValidationError( FieldValidationContext context, string message, Exception exception = null) { var graphMessage = GraphExecutionMessage.FromValidationRule( this, message, context.DataItem.FieldContext.Origin, exception); context.Messages.Add(graphMessage); }
/// <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; // did they forget to await a task and accidentally returned Task<T> from their resolver? if (dataObject is Task) { this.ValidationError( context, $"A field resolver for '{context.FieldPath}' yielded an invalid data object. See exception " + "for details. ", new GraphExecutionException( $"The field '{context.FieldPath}' yielded a {nameof(Task)} as its result but expected a value. " + "Did you forget to await an async method?")); context.DataItem.InvalidateResult(); } else { // is the concrete type of the data item not known to the schema? var strippedSourceType = GraphValidation.EliminateWrappersFromCoreType(dataObject.GetType()); var graphType = context.Schema.KnownTypes.FindGraphType(strippedSourceType); if (graphType == null) { this.ValidationError( context, $"A field resolver for '{context.Field.Route.Path}' generated a result " + "object type not known to the target schema. See exception for " + "details", new GraphExecutionException( $"The class '{strippedSourceType.FriendlyName()}' is not not mapped to a graph type " + "on the target schema.")); context.DataItem.InvalidateResult(); } } return(true); }
/// <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); }
/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the TContext /// if it cannot process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified node; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(FieldValidationContext context) { return(context?.DataItem != null && context.DataItem.Status == FieldItemResolutionStatus.ResultAssigned); }
/// <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; var dataItemTypeExpression = context.DataItem.TypeExpression; // did they forget to await a task and accidentally returned Task<T> from their resolver? // put in a special error message for better feed back if (dataObject is Task) { this.ValidationError( context, $"A field resolver for '{context.FieldPath}' yielded an invalid data object. See exception " + "for details. ", new GraphExecutionException( $"The field '{context.FieldPath}' yielded a {nameof(Task)} as its result but expected a value. " + "Did you forget to await an async method?")); 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); } // virtual graph types aren't real, they can be safely skipped if (expectedGraphType.IsVirtual) { return(true); } var rootSourceType = GraphValidation.EliminateWrappersFromCoreType(dataObject.GetType()); // Check that the actual .NET type of the result data IS (or can be cast to) the expected .NET // type for the graphtype of the field being checked var analysisResult = context.Schema.KnownTypes.AnalyzeRuntimeConcreteType(expectedGraphType, rootSourceType); if (!analysisResult.ExactMatchFound) { string foundTypeNames; if (!analysisResult.FoundTypes.Any()) { foundTypeNames = "~none~"; } else { foundTypeNames = string.Join(", ", analysisResult.FoundTypes.Select(x => x.FriendlyName())); } this.ValidationError( context, $"A field resolver for '{context.Field.Route.Path}' generated a result " + "object type not known to the target schema. See exception for " + "details", new GraphExecutionException( $"For target field of '{context.FieldPath}' (Graph Type: {expectedGraphType.Name}, Kind: {expectedGraphType.Kind}), a supplied object " + $"of class '{rootSourceType.FriendlyName()}' attempted to fill the request but graphql was not able to determine which " + $"of the matched concrete types to use and cannot resolve the field. Matched Types: [{foundTypeNames}]")); context.DataItem.InvalidateResult(); } else { // once the type is confirmed, confirm the actual value // for scenarios such as where a number may be masqurading as an enum but the enum doesn't define // a label for said number. var isValidValue = expectedGraphType.ValidateObject(dataObject); if (!isValidValue) { string actual = string.Empty; if (expectedGraphType is ObjectGraphType) { actual = dataObject.GetType().Name; } else { actual = dataObject.ToString(); } this.ValidationError( context, $"A resolved value for field '{context.FieldPath}' does not match the required graph type. " + $"Expected '{expectedGraphType.Name}' but got '{actual}'."); context.DataItem.InvalidateResult(); } } return(true); }
/// <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); }
/// <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) { // attempt to complete the item on the context context.DataItem.Complete(); return(true); }
public override FieldValidationResult Validate(object value, FieldValidationContext context) { return(null); }
/// <summary> /// Invoke the action item as an asyncronous operation. /// </summary> /// <param name="context">The context.</param> /// <param name="next">The next.</param> /// <param name="cancelToken">The cancel token.</param> /// <returns>Task.</returns> public async Task InvokeAsync(GraphFieldExecutionContext context, GraphMiddlewareInvocationDelegate <GraphFieldExecutionContext> next, CancellationToken cancelToken = default) { // create a set of validation contexts for every incoming source graph item // to capture and validate every item regardless of it being successfully resolved or failed var validationContexts = new List <FieldValidationContext>(context.Request.DataSource.Items.Count); foreach (var dataItem in context.Request.DataSource.Items) { var validationContext = new FieldValidationContext(_schema, dataItem, context.Messages); validationContexts.Add(validationContext); } // begin profiling of this single field of data context.Metrics?.BeginFieldResolution(context); bool fieldShouldBeCanceled = false; if (context.IsValid) { // build a collection of invokable parameters from the supplied context var executionArguments = context .InvocationContext .Arguments .Merge(context.VariableData) .WithSourceData(context.Request.DataSource.Value); // resolve the field var resolutionContext = new FieldResolutionContext(context, context.Request, executionArguments); context.Logger?.FieldResolutionStarted(resolutionContext); var task = context.Field?.Resolver?.Resolve(resolutionContext, cancelToken); await task.ConfigureAwait(false); context.Messages.AddRange(resolutionContext.Messages); this.AssignResults(context, resolutionContext); fieldShouldBeCanceled = resolutionContext.IsCancelled; context.Logger?.FieldResolutionCompleted(resolutionContext); } if (fieldShouldBeCanceled) { context.Cancel(); context.Request.DataSource.Items.ForEach(x => x.Cancel()); } // validate the resolution of the field in whatever manner that means for its current state var completionProcessor = new FieldCompletionRuleProcessor(); completionProcessor.Execute(validationContexts); // end profiling of this single field of data context.Metrics?.EndFieldResolution(context); await next(context, cancelToken).ConfigureAwait(false); // validate the final result after all downstream middleware execute // in the standard pipeline this generally means all child fields have resolved var validationProcessor = new FieldValidationRuleProcessor(); validationProcessor.Execute(validationContexts); }