/// <summary>
        /// Return the specified variable's value for the document from the attached <see cref="Inputs"/> object.
        /// <br/><br/>
        /// Validates and parses the supplied input object according to the variable's type, and converts the object
        /// with <see cref="ScalarGraphType.ParseValue(object)"/> and
        /// <see cref="IInputObjectGraphType.ParseDictionary(IDictionary{string, object})"/> as applicable.
        /// <br/><br/>
        /// Since v3.3, returns null for variables set to null rather than the variable's default value.
        /// </summary>
        private object GetVariableValue(IGraphType graphType, VariableDefinition variableDef, object input, IVariableVisitor visitor)
        {
            return(ParseValue(graphType, variableDef, variableDef.Name, input, visitor));

            // Coerces a value depending on the graph type.
            object ParseValue(IGraphType type, VariableDefinition variableDef, VariableName variableName, object value, IVariableVisitor visitor)
            {
                if (type is IInputObjectGraphType inputObjectGraphType)
                {
                    var parsedValue = ParseValueObject(inputObjectGraphType, variableDef, variableName, value, visitor);
                    visitor?.VisitObject(this, variableDef, variableName, inputObjectGraphType, value, parsedValue);
                    return(parsedValue);
                }
                else if (type is NonNullGraphType nonNullGraphType)
                {
                    if (value == null)
                    {
                        throw new InvalidVariableError(this, variableDef, variableName, "Received a null input for a non-null variable.");
                    }

                    return(ParseValue(nonNullGraphType.ResolvedType, variableDef, variableName, value, visitor));
                }
                else if (type is ListGraphType listGraphType)
                {
                    var parsedValue = ParseValueList(listGraphType, variableDef, variableName, value, visitor);
                    visitor?.VisitList(this, variableDef, variableName, listGraphType, value, parsedValue);
                    return(parsedValue);
                }
                else if (type is ScalarGraphType scalarGraphType)
                {
                    var parsedValue = ParseValueScalar(scalarGraphType, variableDef, variableName, value);
                    visitor?.VisitScalar(this, variableDef, variableName, scalarGraphType, value, parsedValue);
                    return(parsedValue);
                }
                else
                {
                    throw new InvalidVariableError(this, variableDef, variableName, $"The graph type '{type.Name}' is not an input graph type.");
                }
            }

            // Coerces a scalar value
            object ParseValueScalar(ScalarGraphType scalarGraphType, VariableDefinition variableDef, VariableName variableName, object value)
            {
                try
                {
                    return(scalarGraphType.ParseValue(value));
                }
                catch (Exception ex)
                {
                    throw new InvalidVariableError(this, variableDef, variableName, $"Unable to convert '{value}' to '{scalarGraphType.Name}'", ex);
                }
            }

            IList <object> ParseValueList(ListGraphType listGraphType, VariableDefinition variableDef, VariableName variableName, object value, IVariableVisitor visitor)
            {
                if (value == null)
                {
                    return(null);
                }

                // note: a list can have a single child element which will automatically be interpreted as a list of 1 elements. (see below rule)
                // so, to prevent a string as being interpreted as a list of chars (which get converted to strings), we ignore considering a string as an IEnumerable
                if (value is IEnumerable values && !(value is string))
                {
                    // create a list containing the parsed elements in the input list
                    if (values is IList list)
                    {
                        var valueOutputs = new object[list.Count];
                        for (int index = 0; index < list.Count; index++)
                        {
                            // parse/validate values as required by graph type
                            valueOutputs[index] = ParseValue(listGraphType.ResolvedType, variableDef, new VariableName(variableName, index), list[index], visitor);
                        }
                        return(valueOutputs);
                    }
                    else
                    {
                        var valueOutputs = new List <object>(values is ICollection collection ? collection.Count : 0);
                        int index        = 0;
                        foreach (object val in values)
                        {
                            // parse/validate values as required by graph type
                            valueOutputs.Add(ParseValue(listGraphType.ResolvedType, variableDef, new VariableName(variableName, index++), val, visitor));
                        }
                        return(valueOutputs);
                    }
                }
        /// <summary>
        /// Returns all of the variable values defined for the operation from the attached <see cref="Inputs"/> object.
        /// </summary>
        public Variables GetVariableValues(ISchema schema, VariableDefinitions variableDefinitions, Inputs inputs, IVariableVisitor visitor = null)
        {
            if ((variableDefinitions?.List?.Count ?? 0) == 0)
            {
                return(Variables.None);
            }

            var variables = new Variables(variableDefinitions.List.Count);

            if (variableDefinitions != null)
            {
                foreach (var variableDef in variableDefinitions.List)
                {
                    // find the IGraphType instance for the variable type
                    var graphType = variableDef.Type.GraphTypeFromType(schema);

                    if (graphType == null)
                    {
                        ReportError(new InvalidVariableError(this, variableDef, variableDef.Name, $"Variable has unknown type '{variableDef.Type.Name()}'"));
                        continue;
                    }

                    // create a new variable object
                    var variable = new Variable(variableDef.Name);

                    // attempt to retrieve the variable value from the inputs
                    if (inputs.TryGetValue(variableDef.Name, out var variableValue))
                    {
                        // parse the variable via ParseValue (for scalars) and ParseDictionary (for objects) as applicable
                        try
                        {
                            variable.Value = GetVariableValue(graphType, variableDef, variableValue, visitor);
                        }
                        catch (ValidationError error)
                        {
                            ReportError(error);
                            continue;
                        }
                    }
                    else if (variableDef.DefaultValue != null)
                    {
                        // if the variable was not specified in the inputs, and a default literal value was specified, use the specified default variable value

                        // parse the variable literal via ParseLiteral (for scalars) and ParseDictionary (for objects) as applicable
                        try
                        {
                            variable.Value = ExecutionHelper.CoerceValue(graphType, variableDef.DefaultValue, variables, null).Value;
                        }
                        catch (Exception ex)
                        {
                            ReportError(new InvalidVariableError(this, variableDef, variableDef.Name, "Error coercing default value.", ex));
                            continue;
                        }
                        variable.IsDefault = true;
                    }
                    else if (graphType is NonNullGraphType)
                    {
                        ReportError(new InvalidVariableError(this, variableDef, variable.Name, "No value provided for a non-null variable."));
                        continue;
                    }

                    // if the variable was not specified and no default was specified, do not set variable.Value

                    // add the variable to the list of parsed variables defined for the operation
                    variables.Add(variable);
                }
            }

            // return the list of parsed variables defined for the operation
            return(variables);
        }
Ejemplo n.º 3
0
 public abstract T Visit <T>(IVariableVisitor <T> visitor);
Ejemplo n.º 4
0
 public override T Visit <T>(IVariableVisitor <T> visitor)
 {
     return(visitor.Visit(this));
 }
Ejemplo n.º 5
0
        /// <summary>
        /// Return the specified variable's value for the document from the attached <see cref="Inputs"/> object.
        /// <br/><br/>
        /// Validates and parses the supplied input object according to the variable's type, and converts the object
        /// with <see cref="ScalarGraphType.ParseValue(object)"/> and
        /// <see cref="IInputObjectGraphType.ParseDictionary(IDictionary{string, object})"/> as applicable.
        /// <br/><br/>
        /// Since v3.3, returns null for variables set to null rather than the variable's default value.
        /// </summary>
        private object GetVariableValue(IGraphType graphType, VariableDefinition variableDef, object input, IVariableVisitor visitor)
        {
            return(ParseValue(graphType, variableDef, variableDef.Name, input, visitor));

            // Coerces a value depending on the graph type.
            object ParseValue(IGraphType type, VariableDefinition variableDef, VariableName variableName, object value, IVariableVisitor visitor)
            {
                if (type is IInputObjectGraphType inputObjectGraphType)
                {
                    var parsedValue = ParseValueObject(inputObjectGraphType, variableDef, variableName, value, visitor);
                    visitor?.VisitObject(this, variableDef, variableName, inputObjectGraphType, value, parsedValue);
                    return(parsedValue);
                }
                else if (type is NonNullGraphType nonNullGraphType)
                {
                    if (value == null)
                    {
                        throw new InvalidVariableError(this, variableDef, variableName, "Received a null input for a non-null variable.");
                    }

                    return(ParseValue(nonNullGraphType.ResolvedType, variableDef, variableName, value, visitor));
                }
                else if (type is ListGraphType listGraphType)
                {
                    var parsedValue = ParseValueList(listGraphType, variableDef, variableName, value, visitor);
                    visitor?.VisitList(this, variableDef, variableName, listGraphType, value, parsedValue);
                    return(parsedValue);
                }
                else if (type is ScalarGraphType scalarGraphType)
                {
                    var parsedValue = ParseValueScalar(scalarGraphType, variableDef, variableName, value);
                    visitor?.VisitScalar(this, variableDef, variableName, scalarGraphType, value, parsedValue);
                    return(parsedValue);
                }
                else
                {
                    throw new InvalidVariableError(this, variableDef, variableName, $"The graph type '{type.Name}' is not an input graph type.");
                }
            }

            // Coerces a scalar value
            object ParseValueScalar(ScalarGraphType scalarGraphType, VariableDefinition variableDef, VariableName variableName, object value)
            {
                try
                {
                    //  Convert input scalar type into correct one.
                    //  example.: type is int. value is '10'.
                    //     This value can be parsed from string into int and solution is very practical for wide usage.
                    // Maybe best practice should be add new global switch which enable/disable this option.
                    if (SchemaTypes.BuiltInScalarMappings.Where(m => m.Value == scalarGraphType.GetType()).Any())
                    {
                        var type = SchemaTypes.BuiltInScalarMappings.Where(m => m.Value == scalarGraphType.GetType()).FirstOrDefault().Key;
                        if (type != null)
                        {
                            value = Convert.ChangeType(value, type);
                        }
                    }

                    return(scalarGraphType.ParseValue(value));
                }
                catch (Exception ex)
                {
                    throw new InvalidVariableError(this, variableDef, variableName, $"Unable to convert '{value}' to '{scalarGraphType.Name}'", ex);
                }
            }

            IList <object> ParseValueList(ListGraphType listGraphType, VariableDefinition variableDef, VariableName variableName, object value, IVariableVisitor visitor)
            {
                if (value == null)
                {
                    return(null);
                }

                // note: a list can have a single child element which will automatically be interpreted as a list of 1 elements. (see below rule)
                // so, to prevent a string as being interpreted as a list of chars (which get converted to strings), we ignore considering a string as an IEnumerable
                if (value is IEnumerable values && !(value is string))
                {
                    // create a list containing the parsed elements in the input list
                    if (values is IList list)
                    {
                        var valueOutputs = new object[list.Count];
                        for (int index = 0; index < list.Count; index++)
                        {
                            // parse/validate values as required by graph type
                            valueOutputs[index] = ParseValue(listGraphType.ResolvedType, variableDef, new VariableName(variableName, index), list[index], visitor);
                        }
                        return(valueOutputs);
                    }
                    else
                    {
                        var valueOutputs = new List <object>(values is ICollection collection ? collection.Count : 0);
                        int index        = 0;
                        foreach (object val in values)
                        {
                            // parse/validate values as required by graph type
                            valueOutputs.Add(ParseValue(listGraphType.ResolvedType, variableDef, new VariableName(variableName, index++), val, visitor));
                        }
                        return(valueOutputs);
                    }
                }