/** * This method looks up the field on the given type defintion. * It has special casing for the two introspection fields, __schema * and __typename. __typename is special because it can always be * queried as a field, even in situations where no other fields * are allowed, like on a Union. __schema could get automatically * added to the query type, but that would require mutating type * definitions, which would cause issues. */ func getFieldDef(schema types.GraphQLSchema, parentType *types.GraphQLObjectType, fieldName string) *types.GraphQLFieldDefinition { if parentType == nil { return nil } if fieldName == types.SchemaMetaFieldDef.Name && schema.GetQueryType() == parentType { return types.SchemaMetaFieldDef } if fieldName == types.TypeMetaFieldDef.Name && schema.GetQueryType() == parentType { return types.TypeMetaFieldDef } if fieldName == types.TypeNameMetaFieldDef.Name { return types.TypeNameMetaFieldDef } return parentType.GetFields()[fieldName] }
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 }