func Execute(p ExecuteParams) (result *Result) { result = &Result{} exeContext, err := buildExecutionContext(BuildExecutionCtxParams{ Schema: p.Schema, Root: p.Root, AST: p.AST, OperationName: p.OperationName, Args: p.Args, Errors: nil, Result: result, Context: p.Context, }) if err != nil { result.Errors = append(result.Errors, gqlerrors.FormatError(err)) return } defer func() { if r := recover(); r != nil { var err error if r, ok := r.(error); ok { err = gqlerrors.FormatError(r) } exeContext.Errors = append(exeContext.Errors, gqlerrors.FormatError(err)) result.Errors = exeContext.Errors } }() return executeOperation(ExecuteOperationParams{ ExecutionContext: exeContext, Root: p.Root, Operation: exeContext.Operation, }) }
// completeListValue complete a list value by completing each item in the list with the inner type func completeListValue(eCtx *ExecutionContext, returnType *List, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) interface{} { resultVal := reflect.ValueOf(result) parentTypeName := "" if info.ParentType != nil { parentTypeName = info.ParentType.Name() } err := invariant( resultVal.IsValid() && resultVal.Type().Kind() == reflect.Slice, fmt.Sprintf("User Error: expected iterable, but did not find one "+ "for field %v.%v.", parentTypeName, info.FieldName), ) if err != nil { panic(gqlerrors.FormatError(err)) } itemType := returnType.OfType completedResults := []interface{}{} for i := 0; i < resultVal.Len(); i++ { val := resultVal.Index(i).Interface() completedItem := completeValueCatchingError(eCtx, itemType, fieldASTs, info, val) completedResults = append(completedResults, completedItem) } return completedResults }
func (ctx *ValidationContext) ReportError(err error) { formattedErr := gqlerrors.FormatError(err) ctx.errors = append(ctx.errors, formattedErr) }
func completeValue(eCtx *ExecutionContext, returnType Type, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) interface{} { resultVal := reflect.ValueOf(result) if resultVal.IsValid() && resultVal.Type().Kind() == reflect.Func { if propertyFn, ok := result.(func() interface{}); ok { return propertyFn() } err := gqlerrors.NewFormattedError("Error resolving func. Expected `func() interface{}` signature") panic(gqlerrors.FormatError(err)) } // If field type is NonNull, complete for inner type, and throw field error // if result is null. if returnType, ok := returnType.(*NonNull); ok { completed := completeValue(eCtx, returnType.OfType, fieldASTs, info, result) if completed == nil { err := NewLocatedError( fmt.Sprintf("Cannot return null for non-nullable field %v.%v.", info.ParentType, info.FieldName), FieldASTsToNodeASTs(fieldASTs), ) panic(gqlerrors.FormatError(err)) } return completed } // If result value is null-ish (null, undefined, or NaN) then return null. if isNullish(result) { return nil } // If field type is List, complete each item in the list with the inner type if returnType, ok := returnType.(*List); ok { return completeListValue(eCtx, returnType, fieldASTs, info, result) } // If field type is a leaf type, Scalar or Enum, serialize to a valid value, // returning null if serialization is not possible. if returnType, ok := returnType.(*Scalar); ok { return completeLeafValue(returnType, result) } if returnType, ok := returnType.(*Enum); ok { return completeLeafValue(returnType, result) } // If field type is an abstract type, Interface or Union, determine the // runtime Object type and complete for that type. if returnType, ok := returnType.(*Union); ok { return completeAbstractValue(eCtx, returnType, fieldASTs, info, result) } if returnType, ok := returnType.(*Interface); ok { return completeAbstractValue(eCtx, returnType, fieldASTs, info, result) } // If field type is Object, execute and complete all sub-selections. if returnType, ok := returnType.(*Object); ok { return completeObjectValue(eCtx, returnType, fieldASTs, info, result) } // Not reachable. All possible output types have been considered. err := invariant(false, fmt.Sprintf(`Cannot complete value of unexpected type "%v."`, returnType), ) if err != nil { panic(gqlerrors.FormatError(err)) } return nil }
// Resolves the field on the given source object. In particular, this // figures out the value that the field returns by calling its resolve function, // then calls completeValue to complete promises, serialize scalars, or execute // the sub-selection-set for objects. func resolveField(eCtx *ExecutionContext, parentType *Object, source interface{}, fieldASTs []*ast.Field) (result interface{}, resultState resolveFieldResultState) { // catch panic from resolveFn var returnType Output defer func() (interface{}, resolveFieldResultState) { if r := recover(); r != nil { var err error if r, ok := r.(string); ok { err = NewLocatedError( fmt.Sprintf("%v", r), FieldASTsToNodeASTs(fieldASTs), ) } if r, ok := r.(error); ok { err = gqlerrors.FormatError(r) } // send panic upstream if _, ok := returnType.(*NonNull); ok { panic(gqlerrors.FormatError(err)) } eCtx.Errors = append(eCtx.Errors, gqlerrors.FormatError(err)) return result, resultState } return result, resultState }() fieldAST := fieldASTs[0] fieldName := "" if fieldAST.Name != nil { fieldName = fieldAST.Name.Value } fieldDef := getFieldDef(eCtx.Schema, parentType, fieldName) if fieldDef == nil { resultState.hasNoFieldDefs = true return nil, resultState } returnType = fieldDef.Type resolveFn := fieldDef.Resolve if resolveFn == nil { resolveFn = DefaultResolveFn } // Build a map of arguments from the field.arguments AST, using the // variables scope to fulfill any variable references. // TODO: find a way to memoize, in case this field is within a List type. args, _ := getArgumentValues(fieldDef.Args, fieldAST.Arguments, eCtx.VariableValues) info := ResolveInfo{ FieldName: fieldName, FieldASTs: fieldASTs, ReturnType: returnType, ParentType: parentType, Schema: eCtx.Schema, Fragments: eCtx.Fragments, RootValue: eCtx.Root, Operation: eCtx.Operation, VariableValues: eCtx.VariableValues, } var resolveFnError error result, resolveFnError = resolveFn(ResolveParams{ Source: source, Args: args, Info: info, Context: eCtx.Context, }) if resolveFnError != nil { panic(gqlerrors.FormatError(resolveFnError)) } completed := completeValueCatchingError(eCtx, returnType, fieldASTs, info, result) return completed, resultState }