...

Source file src/github.com/vektah/gqlparser/v2/validator/vars.go

Documentation: github.com/vektah/gqlparser/v2/validator

     1  package validator
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/vektah/gqlparser/v2/ast"
    11  	"github.com/vektah/gqlparser/v2/gqlerror"
    12  )
    13  
    14  var ErrUnexpectedType = fmt.Errorf("Unexpected Type")
    15  
    16  // VariableValues coerces and validates variable values
    17  func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, error) {
    18  	coercedVars := map[string]interface{}{}
    19  
    20  	validator := varValidator{
    21  		path:   ast.Path{ast.PathName("variable")},
    22  		schema: schema,
    23  	}
    24  
    25  	for _, v := range op.VariableDefinitions {
    26  		validator.path = append(validator.path, ast.PathName(v.Variable))
    27  
    28  		if !v.Definition.IsInputType() {
    29  			return nil, gqlerror.ErrorPathf(validator.path, "must an input type")
    30  		}
    31  
    32  		val, hasValue := variables[v.Variable]
    33  
    34  		if !hasValue {
    35  			if v.DefaultValue != nil {
    36  				var err error
    37  				val, err = v.DefaultValue.Value(nil)
    38  				if err != nil {
    39  					return nil, gqlerror.WrapPath(validator.path, err)
    40  				}
    41  				hasValue = true
    42  			} else if v.Type.NonNull {
    43  				return nil, gqlerror.ErrorPathf(validator.path, "must be defined")
    44  			}
    45  		}
    46  
    47  		if hasValue {
    48  			if val == nil {
    49  				if v.Type.NonNull {
    50  					return nil, gqlerror.ErrorPathf(validator.path, "cannot be null")
    51  				}
    52  				coercedVars[v.Variable] = nil
    53  			} else {
    54  				rv := reflect.ValueOf(val)
    55  
    56  				jsonNumber, isJSONNumber := val.(json.Number)
    57  				if isJSONNumber {
    58  					if v.Type.NamedType == "Int" {
    59  						n, err := jsonNumber.Int64()
    60  						if err != nil {
    61  							return nil, gqlerror.ErrorPathf(validator.path, "cannot use value %d as %s", n, v.Type.NamedType)
    62  						}
    63  						rv = reflect.ValueOf(n)
    64  					} else if v.Type.NamedType == "Float" {
    65  						f, err := jsonNumber.Float64()
    66  						if err != nil {
    67  							return nil, gqlerror.ErrorPathf(validator.path, "cannot use value %f as %s", f, v.Type.NamedType)
    68  						}
    69  						rv = reflect.ValueOf(f)
    70  
    71  					}
    72  				}
    73  				if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
    74  					rv = rv.Elem()
    75  				}
    76  
    77  				rval, err := validator.validateVarType(v.Type, rv)
    78  				if err != nil {
    79  					return nil, err
    80  				}
    81  				coercedVars[v.Variable] = rval.Interface()
    82  			}
    83  		}
    84  
    85  		validator.path = validator.path[0 : len(validator.path)-1]
    86  	}
    87  	return coercedVars, nil
    88  }
    89  
    90  type varValidator struct {
    91  	path   ast.Path
    92  	schema *ast.Schema
    93  }
    94  
    95  func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflect.Value, *gqlerror.Error) {
    96  	currentPath := v.path
    97  	resetPath := func() {
    98  		v.path = currentPath
    99  	}
   100  	defer resetPath()
   101  	if typ.Elem != nil {
   102  		if val.Kind() != reflect.Slice {
   103  			// GraphQL spec says that non-null values should be coerced to an array when possible.
   104  			// Hence if the value is not a slice, we create a slice and add val to it.
   105  			slc := reflect.MakeSlice(reflect.SliceOf(val.Type()), 0, 0)
   106  			slc = reflect.Append(slc, val)
   107  			val = slc
   108  		}
   109  		for i := 0; i < val.Len(); i++ {
   110  			resetPath()
   111  			v.path = append(v.path, ast.PathIndex(i))
   112  			field := val.Index(i)
   113  			if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
   114  				if typ.Elem.NonNull && field.IsNil() {
   115  					return val, gqlerror.ErrorPathf(v.path, "cannot be null")
   116  				}
   117  				field = field.Elem()
   118  			}
   119  			_, err := v.validateVarType(typ.Elem, field)
   120  			if err != nil {
   121  				return val, err
   122  			}
   123  		}
   124  		return val, nil
   125  	}
   126  	def := v.schema.Types[typ.NamedType]
   127  	if def == nil {
   128  		panic(fmt.Errorf("missing def for %s", typ.NamedType))
   129  	}
   130  
   131  	if !typ.NonNull && !val.IsValid() {
   132  		// If the type is not null and we got a invalid value namely null/nil, then it's valid
   133  		return val, nil
   134  	}
   135  
   136  	switch def.Kind {
   137  	case ast.Enum:
   138  		kind := val.Type().Kind()
   139  		if kind != reflect.Int && kind != reflect.Int32 && kind != reflect.Int64 && kind != reflect.String {
   140  			return val, gqlerror.ErrorPathf(v.path, "enums must be ints or strings")
   141  		}
   142  		isValidEnum := false
   143  		for _, enumVal := range def.EnumValues {
   144  			if strings.EqualFold(val.String(), enumVal.Name) {
   145  				isValidEnum = true
   146  			}
   147  		}
   148  		if !isValidEnum {
   149  			return val, gqlerror.ErrorPathf(v.path, "%s is not a valid %s", val.String(), def.Name)
   150  		}
   151  		return val, nil
   152  	case ast.Scalar:
   153  		kind := val.Type().Kind()
   154  		switch typ.NamedType {
   155  		case "Int":
   156  			if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.Float32 || kind == reflect.Float64 || IsValidIntString(val, kind) {
   157  				return val, nil
   158  			}
   159  		case "Float":
   160  			if kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || IsValidFloatString(val, kind) {
   161  				return val, nil
   162  			}
   163  		case "String":
   164  			if kind == reflect.String {
   165  				return val, nil
   166  			}
   167  
   168  		case "Boolean":
   169  			if kind == reflect.Bool {
   170  				return val, nil
   171  			}
   172  
   173  		case "ID":
   174  			if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String {
   175  				return val, nil
   176  			}
   177  		default:
   178  			// assume custom scalars are ok
   179  			return val, nil
   180  		}
   181  		return val, gqlerror.ErrorPathf(v.path, "cannot use %s as %s", kind.String(), typ.NamedType)
   182  	case ast.InputObject:
   183  		if val.Kind() != reflect.Map {
   184  			return val, gqlerror.ErrorPathf(v.path, "must be a %s", def.Name)
   185  		}
   186  
   187  		// check for unknown fields
   188  		for _, name := range val.MapKeys() {
   189  			val.MapIndex(name)
   190  			fieldDef := def.Fields.ForName(name.String())
   191  			resetPath()
   192  			v.path = append(v.path, ast.PathName(name.String()))
   193  
   194  			switch {
   195  			case name.String() == "__typename":
   196  				continue
   197  			case fieldDef == nil:
   198  				return val, gqlerror.ErrorPathf(v.path, "unknown field")
   199  			}
   200  		}
   201  
   202  		for _, fieldDef := range def.Fields {
   203  			resetPath()
   204  			v.path = append(v.path, ast.PathName(fieldDef.Name))
   205  
   206  			field := val.MapIndex(reflect.ValueOf(fieldDef.Name))
   207  			if !field.IsValid() {
   208  				if fieldDef.Type.NonNull {
   209  					if fieldDef.DefaultValue != nil {
   210  						var err error
   211  						_, err = fieldDef.DefaultValue.Value(nil)
   212  						if err == nil {
   213  							continue
   214  						}
   215  					}
   216  					return val, gqlerror.ErrorPathf(v.path, "must be defined")
   217  				}
   218  				continue
   219  			}
   220  
   221  			if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
   222  				if fieldDef.Type.NonNull && field.IsNil() {
   223  					return val, gqlerror.ErrorPathf(v.path, "cannot be null")
   224  				}
   225  				//allow null object field and skip it
   226  				if !fieldDef.Type.NonNull && field.IsNil() {
   227  					continue
   228  				}
   229  				field = field.Elem()
   230  			}
   231  			cval, err := v.validateVarType(fieldDef.Type, field)
   232  			if err != nil {
   233  				return val, err
   234  			}
   235  			val.SetMapIndex(reflect.ValueOf(fieldDef.Name), cval)
   236  		}
   237  	default:
   238  		panic(fmt.Errorf("unsupported type %s", def.Kind))
   239  	}
   240  	return val, nil
   241  }
   242  
   243  func IsValidIntString(val reflect.Value, kind reflect.Kind) bool {
   244  	if kind != reflect.String {
   245  		return false
   246  	}
   247  	_, e := strconv.ParseInt(fmt.Sprintf("%v", val.Interface()), 10, 64)
   248  
   249  	return e == nil
   250  }
   251  
   252  func IsValidFloatString(val reflect.Value, kind reflect.Kind) bool {
   253  	if kind != reflect.String {
   254  		return false
   255  	}
   256  	_, e := strconv.ParseFloat(fmt.Sprintf("%v", val.Interface()), 64)
   257  	return e == nil
   258  }
   259  

View as plain text