func completeValue(eCtx *ExecutionContext, returnType types.GraphQLType, fieldASTs []*ast.Field, info types.GraphQLResolveInfo, result interface{}) interface{} { // TODO: explore resolving go-routines in completeValue resultVal := reflect.ValueOf(result) if resultVal.IsValid() && resultVal.Type().Kind() == reflect.Func { if propertyFn, ok := result.(func() interface{}); ok { return propertyFn() } err := graphqlerrors.NewGraphQLFormattedError("Error resolving func. Expected `func() interface{}` signature") panic(graphqlerrors.FormatError(err)) } if returnType, ok := returnType.(*types.GraphQLNonNull); ok { completed := completeValue(eCtx, returnType.OfType, fieldASTs, info, result) if completed == nil { err := graphqlerrors.NewLocatedError( fmt.Sprintf("Cannot return null for non-nullable field %v.%v.", info.ParentType, info.FieldName), graphqlerrors.FieldASTsToNodeASTs(fieldASTs), ) panic(graphqlerrors.FormatError(err)) } return completed } if isNullish(result) { return nil } // If field type is List, complete each item in the list with the inner type if returnType, ok := returnType.(*types.GraphQLList); ok { resultVal := reflect.ValueOf(result) err := invariant( resultVal.IsValid() && resultVal.Type().Kind() == reflect.Slice, "User Error: expected iterable, but did not find one.", ) if err != nil { panic(graphqlerrors.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 } // If field type is Scalar or Enum, serialize to a valid value, returning // null if serialization is not possible. if returnType, ok := returnType.(*types.GraphQLScalarType); ok { err := invariant(returnType.Serialize != nil, "Missing serialize method on type") if err != nil { panic(graphqlerrors.FormatError(err)) } serializedResult := returnType.Serialize(result) if isNullish(serializedResult) { return nil } return serializedResult } if returnType, ok := returnType.(*types.GraphQLEnumType); ok { err := invariant(returnType.Serialize != nil, "Missing serialize method on type") if err != nil { panic(graphqlerrors.FormatError(err)) } serializedResult := returnType.Serialize(result) if isNullish(serializedResult) { return nil } return serializedResult } // Field type must be Object, Interface or Union and expect sub-selections. var objectType *types.GraphQLObjectType switch returnType := returnType.(type) { case *types.GraphQLObjectType: objectType = returnType case types.GraphQLAbstractType: objectType = returnType.GetObjectType(result, info) if objectType != nil && !returnType.IsPossibleType(objectType) { panic(graphqlerrors.NewGraphQLFormattedError( fmt.Sprintf(`Runtime Object type "%v" is not a possible type `+ `for "%v".`, objectType, returnType), )) } } if objectType == nil { return nil } // If there is an isTypeOf predicate function, call it with the // current result. If isTypeOf returns false, then raise an error rather // than continuing execution. if objectType.IsTypeOf != nil && !objectType.IsTypeOf(result, info) { panic(graphqlerrors.NewGraphQLFormattedError( fmt.Sprintf(`Expected value of type "%v" but got: %T.`, objectType, result), )) } // Collect sub-fields to execute to complete this value. subFieldASTs := map[string][]*ast.Field{} visitedFragmentNames := map[string]bool{} for _, fieldAST := range fieldASTs { if fieldAST == nil { continue } selectionSet := fieldAST.SelectionSet if selectionSet != nil { innerParams := CollectFieldsParams{ ExeContext: eCtx, OperationType: objectType, SelectionSet: selectionSet, Fields: subFieldASTs, VisitedFragmentNames: visitedFragmentNames, } subFieldASTs = collectFields(innerParams) } } executeFieldsParams := ExecuteFieldsParams{ ExecutionContext: eCtx, ParentType: objectType, Source: result, Fields: subFieldASTs, } results := executeFields(executeFieldsParams) return results.Data }