/// <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);
 }
Esempio n. 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)
        {
            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);
        }
Esempio n. 3
0
 /// <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());
 }
Esempio n. 4
0
 /// <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);
 }
Esempio n. 5
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)
        {
            // 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);
        }
Esempio n. 6
0
        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));
        }
Esempio n. 7
0
        internal override FieldValidationResult ValidateIfSpecified(string value, FieldValidationContext context)
        {
            if (value != context.Application.Applicants[0].Password)
            {
                return(new FieldValidationResult
                {
                    ErrorMessage = "Passwords must match."
                });
            }

            return(null);
        }
Esempio n. 8
0
        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);
        }
Esempio n. 9
0
        /// <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);
        }
Esempio n. 10
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;

            // 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);
        }
Esempio n. 11
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);
        }
Esempio n. 12
0
 /// <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);
        }
Esempio n. 14
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);
        }
 /// <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);
 }
Esempio n. 16
0
 public override FieldValidationResult Validate(object value, FieldValidationContext context)
 {
     return(null);
 }
Esempio n. 17
0
        /// <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);
        }