aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/graphql-go/graphql/executor.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/graphql-go/graphql/executor.go')
-rw-r--r--vendor/github.com/graphql-go/graphql/executor.go921
1 files changed, 921 insertions, 0 deletions
diff --git a/vendor/github.com/graphql-go/graphql/executor.go b/vendor/github.com/graphql-go/graphql/executor.go
new file mode 100644
index 00000000..a7a7535d
--- /dev/null
+++ b/vendor/github.com/graphql-go/graphql/executor.go
@@ -0,0 +1,921 @@
+package graphql
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/graphql-go/graphql/gqlerrors"
+ "github.com/graphql-go/graphql/language/ast"
+)
+
+type ExecuteParams struct {
+ Schema Schema
+ Root interface{}
+ AST *ast.Document
+ OperationName string
+ Args map[string]interface{}
+
+ // Context may be provided to pass application-specific per-request
+ // information to resolve functions.
+ Context context.Context
+}
+
+func Execute(p ExecuteParams) (result *Result) {
+ // Use background context if no context was provided
+ ctx := p.Context
+ if ctx == nil {
+ ctx = context.Background()
+ }
+
+ resultChannel := make(chan *Result)
+
+ go func(out chan<- *Result, done <-chan struct{}) {
+ 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))
+ select {
+ case out <- result:
+ case <-done:
+ }
+ 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
+ select {
+ case out <- result:
+ case <-done:
+ }
+ }
+ }()
+
+ result = executeOperation(executeOperationParams{
+ ExecutionContext: exeContext,
+ Root: p.Root,
+ Operation: exeContext.Operation,
+ })
+ select {
+ case out <- result:
+ case <-done:
+ }
+
+ }(resultChannel, ctx.Done())
+
+ select {
+ case <-ctx.Done():
+ result = &Result{}
+ result.Errors = append(result.Errors, gqlerrors.FormatError(ctx.Err()))
+ case r := <-resultChannel:
+ result = r
+ }
+ return
+}
+
+type buildExecutionCtxParams struct {
+ Schema Schema
+ Root interface{}
+ AST *ast.Document
+ OperationName string
+ Args map[string]interface{}
+ Errors []gqlerrors.FormattedError
+ Result *Result
+ Context context.Context
+}
+
+type executionContext struct {
+ Schema Schema
+ Fragments map[string]ast.Definition
+ Root interface{}
+ Operation ast.Definition
+ VariableValues map[string]interface{}
+ Errors []gqlerrors.FormattedError
+ Context context.Context
+}
+
+func buildExecutionContext(p buildExecutionCtxParams) (*executionContext, error) {
+ eCtx := &executionContext{}
+ var operation *ast.OperationDefinition
+ fragments := map[string]ast.Definition{}
+
+ for _, definition := range p.AST.Definitions {
+ switch definition := definition.(type) {
+ case *ast.OperationDefinition:
+ if (p.OperationName == "") && operation != nil {
+ return nil, errors.New("Must provide operation name if query contains multiple operations.")
+ }
+ if p.OperationName == "" || definition.GetName() != nil && definition.GetName().Value == p.OperationName {
+ operation = definition
+ }
+ case *ast.FragmentDefinition:
+ key := ""
+ if definition.GetName() != nil && definition.GetName().Value != "" {
+ key = definition.GetName().Value
+ }
+ fragments[key] = definition
+ default:
+ return nil, fmt.Errorf("GraphQL cannot execute a request containing a %v", definition.GetKind())
+ }
+ }
+
+ if operation == nil {
+ if p.OperationName != "" {
+ return nil, fmt.Errorf(`Unknown operation named "%v".`, p.OperationName)
+ }
+ return nil, fmt.Errorf(`Must provide an operation.`)
+ }
+
+ variableValues, err := getVariableValues(p.Schema, operation.GetVariableDefinitions(), p.Args)
+ if err != nil {
+ return nil, err
+ }
+
+ eCtx.Schema = p.Schema
+ eCtx.Fragments = fragments
+ eCtx.Root = p.Root
+ eCtx.Operation = operation
+ eCtx.VariableValues = variableValues
+ eCtx.Errors = p.Errors
+ eCtx.Context = p.Context
+ return eCtx, nil
+}
+
+type executeOperationParams struct {
+ ExecutionContext *executionContext
+ Root interface{}
+ Operation ast.Definition
+}
+
+func executeOperation(p executeOperationParams) *Result {
+ operationType, err := getOperationRootType(p.ExecutionContext.Schema, p.Operation)
+ if err != nil {
+ return &Result{Errors: gqlerrors.FormatErrors(err)}
+ }
+
+ fields := collectFields(collectFieldsParams{
+ ExeContext: p.ExecutionContext,
+ RuntimeType: operationType,
+ SelectionSet: p.Operation.GetSelectionSet(),
+ })
+
+ executeFieldsParams := executeFieldsParams{
+ ExecutionContext: p.ExecutionContext,
+ ParentType: operationType,
+ Source: p.Root,
+ Fields: fields,
+ }
+
+ if p.Operation.GetOperation() == ast.OperationTypeMutation {
+ return executeFieldsSerially(executeFieldsParams)
+ }
+ return executeFields(executeFieldsParams)
+
+}
+
+// Extracts the root type of the operation from the schema.
+func getOperationRootType(schema Schema, operation ast.Definition) (*Object, error) {
+ if operation == nil {
+ return nil, errors.New("Can only execute queries and mutations")
+ }
+
+ switch operation.GetOperation() {
+ case ast.OperationTypeQuery:
+ return schema.QueryType(), nil
+ case ast.OperationTypeMutation:
+ mutationType := schema.MutationType()
+ if mutationType.PrivateName == "" {
+ return nil, gqlerrors.NewError(
+ "Schema is not configured for mutations",
+ []ast.Node{operation},
+ "",
+ nil,
+ []int{},
+ nil,
+ )
+ }
+ return mutationType, nil
+ case ast.OperationTypeSubscription:
+ subscriptionType := schema.SubscriptionType()
+ if subscriptionType.PrivateName == "" {
+ return nil, gqlerrors.NewError(
+ "Schema is not configured for subscriptions",
+ []ast.Node{operation},
+ "",
+ nil,
+ []int{},
+ nil,
+ )
+ }
+ return subscriptionType, nil
+ default:
+ return nil, gqlerrors.NewError(
+ "Can only execute queries, mutations and subscription",
+ []ast.Node{operation},
+ "",
+ nil,
+ []int{},
+ nil,
+ )
+ }
+}
+
+type executeFieldsParams struct {
+ ExecutionContext *executionContext
+ ParentType *Object
+ Source interface{}
+ Fields map[string][]*ast.Field
+}
+
+// Implements the "Evaluating selection sets" section of the spec for "write" mode.
+func executeFieldsSerially(p executeFieldsParams) *Result {
+ if p.Source == nil {
+ p.Source = map[string]interface{}{}
+ }
+ if p.Fields == nil {
+ p.Fields = map[string][]*ast.Field{}
+ }
+
+ finalResults := map[string]interface{}{}
+ for responseName, fieldASTs := range p.Fields {
+ resolved, state := resolveField(p.ExecutionContext, p.ParentType, p.Source, fieldASTs)
+ if state.hasNoFieldDefs {
+ continue
+ }
+ finalResults[responseName] = resolved
+ }
+
+ return &Result{
+ Data: finalResults,
+ Errors: p.ExecutionContext.Errors,
+ }
+}
+
+// Implements the "Evaluating selection sets" section of the spec for "read" mode.
+func executeFields(p executeFieldsParams) *Result {
+ if p.Source == nil {
+ p.Source = map[string]interface{}{}
+ }
+ if p.Fields == nil {
+ p.Fields = map[string][]*ast.Field{}
+ }
+
+ finalResults := map[string]interface{}{}
+ for responseName, fieldASTs := range p.Fields {
+ resolved, state := resolveField(p.ExecutionContext, p.ParentType, p.Source, fieldASTs)
+ if state.hasNoFieldDefs {
+ continue
+ }
+ finalResults[responseName] = resolved
+ }
+
+ return &Result{
+ Data: finalResults,
+ Errors: p.ExecutionContext.Errors,
+ }
+}
+
+type collectFieldsParams struct {
+ ExeContext *executionContext
+ RuntimeType *Object // previously known as OperationType
+ SelectionSet *ast.SelectionSet
+ Fields map[string][]*ast.Field
+ VisitedFragmentNames map[string]bool
+}
+
+// Given a selectionSet, adds all of the fields in that selection to
+// the passed in map of fields, and returns it at the end.
+// CollectFields requires the "runtime type" of an object. For a field which
+// returns and Interface or Union type, the "runtime type" will be the actual
+// Object type returned by that field.
+func collectFields(p collectFieldsParams) map[string][]*ast.Field {
+
+ fields := p.Fields
+ if fields == nil {
+ fields = map[string][]*ast.Field{}
+ }
+ if p.VisitedFragmentNames == nil {
+ p.VisitedFragmentNames = map[string]bool{}
+ }
+ if p.SelectionSet == nil {
+ return fields
+ }
+ for _, iSelection := range p.SelectionSet.Selections {
+ switch selection := iSelection.(type) {
+ case *ast.Field:
+ if !shouldIncludeNode(p.ExeContext, selection.Directives) {
+ continue
+ }
+ name := getFieldEntryKey(selection)
+ if _, ok := fields[name]; !ok {
+ fields[name] = []*ast.Field{}
+ }
+ fields[name] = append(fields[name], selection)
+ case *ast.InlineFragment:
+
+ if !shouldIncludeNode(p.ExeContext, selection.Directives) ||
+ !doesFragmentConditionMatch(p.ExeContext, selection, p.RuntimeType) {
+ continue
+ }
+ innerParams := collectFieldsParams{
+ ExeContext: p.ExeContext,
+ RuntimeType: p.RuntimeType,
+ SelectionSet: selection.SelectionSet,
+ Fields: fields,
+ VisitedFragmentNames: p.VisitedFragmentNames,
+ }
+ collectFields(innerParams)
+ case *ast.FragmentSpread:
+ fragName := ""
+ if selection.Name != nil {
+ fragName = selection.Name.Value
+ }
+ if visited, ok := p.VisitedFragmentNames[fragName]; (ok && visited) ||
+ !shouldIncludeNode(p.ExeContext, selection.Directives) {
+ continue
+ }
+ p.VisitedFragmentNames[fragName] = true
+ fragment, hasFragment := p.ExeContext.Fragments[fragName]
+ if !hasFragment {
+ continue
+ }
+
+ if fragment, ok := fragment.(*ast.FragmentDefinition); ok {
+ if !doesFragmentConditionMatch(p.ExeContext, fragment, p.RuntimeType) {
+ continue
+ }
+ innerParams := collectFieldsParams{
+ ExeContext: p.ExeContext,
+ RuntimeType: p.RuntimeType,
+ SelectionSet: fragment.GetSelectionSet(),
+ Fields: fields,
+ VisitedFragmentNames: p.VisitedFragmentNames,
+ }
+ collectFields(innerParams)
+ }
+ }
+ }
+ return fields
+}
+
+// Determines if a field should be included based on the @include and @skip
+// directives, where @skip has higher precedence than @include.
+func shouldIncludeNode(eCtx *executionContext, directives []*ast.Directive) bool {
+
+ defaultReturnValue := true
+
+ var skipAST *ast.Directive
+ var includeAST *ast.Directive
+ for _, directive := range directives {
+ if directive == nil || directive.Name == nil {
+ continue
+ }
+ if directive.Name.Value == SkipDirective.Name {
+ skipAST = directive
+ break
+ }
+ }
+ if skipAST != nil {
+ argValues, err := getArgumentValues(
+ SkipDirective.Args,
+ skipAST.Arguments,
+ eCtx.VariableValues,
+ )
+ if err != nil {
+ return defaultReturnValue
+ }
+ if skipIf, ok := argValues["if"].(bool); ok {
+ if skipIf == true {
+ return false
+ }
+ }
+ }
+ for _, directive := range directives {
+ if directive == nil || directive.Name == nil {
+ continue
+ }
+ if directive.Name.Value == IncludeDirective.Name {
+ includeAST = directive
+ break
+ }
+ }
+ if includeAST != nil {
+ argValues, err := getArgumentValues(
+ IncludeDirective.Args,
+ includeAST.Arguments,
+ eCtx.VariableValues,
+ )
+ if err != nil {
+ return defaultReturnValue
+ }
+ if includeIf, ok := argValues["if"].(bool); ok {
+ if includeIf == false {
+ return false
+ }
+ }
+ }
+ return defaultReturnValue
+}
+
+// Determines if a fragment is applicable to the given type.
+func doesFragmentConditionMatch(eCtx *executionContext, fragment ast.Node, ttype *Object) bool {
+
+ switch fragment := fragment.(type) {
+ case *ast.FragmentDefinition:
+ typeConditionAST := fragment.TypeCondition
+ if typeConditionAST == nil {
+ return true
+ }
+ conditionalType, err := typeFromAST(eCtx.Schema, typeConditionAST)
+ if err != nil {
+ return false
+ }
+ if conditionalType == ttype {
+ return true
+ }
+ if conditionalType.Name() == ttype.Name() {
+ return true
+ }
+ if conditionalType, ok := conditionalType.(*Interface); ok {
+ return eCtx.Schema.IsPossibleType(conditionalType, ttype)
+ }
+ if conditionalType, ok := conditionalType.(*Union); ok {
+ return eCtx.Schema.IsPossibleType(conditionalType, ttype)
+ }
+ case *ast.InlineFragment:
+ typeConditionAST := fragment.TypeCondition
+ if typeConditionAST == nil {
+ return true
+ }
+ conditionalType, err := typeFromAST(eCtx.Schema, typeConditionAST)
+ if err != nil {
+ return false
+ }
+ if conditionalType == ttype {
+ return true
+ }
+ if conditionalType.Name() == ttype.Name() {
+ return true
+ }
+ if conditionalType, ok := conditionalType.(*Interface); ok {
+ return eCtx.Schema.IsPossibleType(conditionalType, ttype)
+ }
+ if conditionalType, ok := conditionalType.(*Union); ok {
+ return eCtx.Schema.IsPossibleType(conditionalType, ttype)
+ }
+ }
+
+ return false
+}
+
+// Implements the logic to compute the key of a given field’s entry
+func getFieldEntryKey(node *ast.Field) string {
+
+ if node.Alias != nil && node.Alias.Value != "" {
+ return node.Alias.Value
+ }
+ if node.Name != nil && node.Name.Value != "" {
+ return node.Name.Value
+ }
+ return ""
+}
+
+// Internal resolveField state
+type resolveFieldResultState struct {
+ hasNoFieldDefs bool
+}
+
+// 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
+}
+
+func completeValueCatchingError(eCtx *executionContext, returnType Type, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) (completed interface{}) {
+ // catch panic
+ defer func() interface{} {
+ if r := recover(); r != nil {
+ //send panic upstream
+ if _, ok := returnType.(*NonNull); ok {
+ panic(r)
+ }
+ if err, ok := r.(gqlerrors.FormattedError); ok {
+ eCtx.Errors = append(eCtx.Errors, err)
+ }
+ return completed
+ }
+ return completed
+ }()
+
+ if returnType, ok := returnType.(*NonNull); ok {
+ completed := completeValue(eCtx, returnType, fieldASTs, info, result)
+ return completed
+ }
+ completed = completeValue(eCtx, returnType, fieldASTs, info, result)
+ return completed
+}
+
+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 := invariantf(false,
+ `Cannot complete value of unexpected type "%v."`, returnType)
+
+ if err != nil {
+ panic(gqlerrors.FormatError(err))
+ }
+ return nil
+}
+
+// completeAbstractValue completes value of an Abstract type (Union / Interface) by determining the runtime type
+// of that value, then completing based on that type.
+func completeAbstractValue(eCtx *executionContext, returnType Abstract, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) interface{} {
+
+ var runtimeType *Object
+
+ resolveTypeParams := ResolveTypeParams{
+ Value: result,
+ Info: info,
+ Context: eCtx.Context,
+ }
+ if unionReturnType, ok := returnType.(*Union); ok && unionReturnType.ResolveType != nil {
+ runtimeType = unionReturnType.ResolveType(resolveTypeParams)
+ } else if interfaceReturnType, ok := returnType.(*Interface); ok && interfaceReturnType.ResolveType != nil {
+ runtimeType = interfaceReturnType.ResolveType(resolveTypeParams)
+ } else {
+ runtimeType = defaultResolveTypeFn(resolveTypeParams, returnType)
+ }
+
+ err := invariant(runtimeType != nil,
+ fmt.Sprintf(`Abstract type %v must resolve to an Object type at runtime `+
+ `for field %v.%v with value "%v", received "%v".`,
+ returnType, info.ParentType, info.FieldName, result, runtimeType),
+ )
+ if err != nil {
+ panic(err)
+ }
+
+ if !eCtx.Schema.IsPossibleType(returnType, runtimeType) {
+ panic(gqlerrors.NewFormattedError(
+ fmt.Sprintf(`Runtime Object type "%v" is not a possible type `+
+ `for "%v".`, runtimeType, returnType),
+ ))
+ }
+
+ return completeObjectValue(eCtx, runtimeType, fieldASTs, info, result)
+}
+
+// completeObjectValue complete an Object value by executing all sub-selections.
+func completeObjectValue(eCtx *executionContext, returnType *Object, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) interface{} {
+
+ // 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 returnType.IsTypeOf != nil {
+ p := IsTypeOfParams{
+ Value: result,
+ Info: info,
+ Context: eCtx.Context,
+ }
+ if !returnType.IsTypeOf(p) {
+ panic(gqlerrors.NewFormattedError(
+ fmt.Sprintf(`Expected value of type "%v" but got: %T.`, returnType, 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,
+ RuntimeType: returnType,
+ SelectionSet: selectionSet,
+ Fields: subFieldASTs,
+ VisitedFragmentNames: visitedFragmentNames,
+ }
+ subFieldASTs = collectFields(innerParams)
+ }
+ }
+ executeFieldsParams := executeFieldsParams{
+ ExecutionContext: eCtx,
+ ParentType: returnType,
+ Source: result,
+ Fields: subFieldASTs,
+ }
+ results := executeFields(executeFieldsParams)
+
+ return results.Data
+
+}
+
+// completeLeafValue complete a leaf value (Scalar / Enum) by serializing to a valid value, returning nil if serialization is not possible.
+func completeLeafValue(returnType Leaf, result interface{}) interface{} {
+ serializedResult := returnType.Serialize(result)
+ if isNullish(serializedResult) {
+ return nil
+ }
+ return serializedResult
+}
+
+// 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 := invariantf(
+ resultVal.IsValid() && resultVal.Type().Kind() == reflect.Slice,
+ "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
+}
+
+// defaultResolveTypeFn If a resolveType function is not given, then a default resolve behavior is
+// used which tests each possible type for the abstract type by calling
+// isTypeOf for the object being coerced, returning the first type that matches.
+func defaultResolveTypeFn(p ResolveTypeParams, abstractType Abstract) *Object {
+ possibleTypes := p.Info.Schema.PossibleTypes(abstractType)
+ for _, possibleType := range possibleTypes {
+ if possibleType.IsTypeOf == nil {
+ continue
+ }
+ isTypeOfParams := IsTypeOfParams{
+ Value: p.Value,
+ Info: p.Info,
+ Context: p.Context,
+ }
+ if res := possibleType.IsTypeOf(isTypeOfParams); res {
+ return possibleType
+ }
+ }
+ return nil
+}
+
+// FieldResolver is used in DefaultResolveFn when the the source value implements this interface.
+type FieldResolver interface {
+ // Resolve resolves the value for the given ResolveParams. It has the same semantics as FieldResolveFn.
+ Resolve(p ResolveParams) (interface{}, error)
+}
+
+// defaultResolveFn If a resolve function is not given, then a default resolve behavior is used
+// which takes the property of the source object of the same name as the field
+// and returns it as the result, or if it's a function, returns the result
+// of calling that function.
+func DefaultResolveFn(p ResolveParams) (interface{}, error) {
+ // try to resolve p.Source as a struct first
+ sourceVal := reflect.ValueOf(p.Source)
+ if sourceVal.IsValid() && sourceVal.Type().Kind() == reflect.Ptr {
+ sourceVal = sourceVal.Elem()
+ }
+ if !sourceVal.IsValid() {
+ return nil, nil
+ }
+
+ // Check if value implements 'Resolver' interface
+ if resolver, ok := sourceVal.Interface().(FieldResolver); ok {
+ return resolver.Resolve(p)
+ }
+
+ if sourceVal.Type().Kind() == reflect.Struct {
+ for i := 0; i < sourceVal.NumField(); i++ {
+ valueField := sourceVal.Field(i)
+ typeField := sourceVal.Type().Field(i)
+ // try matching the field name first
+ if typeField.Name == p.Info.FieldName {
+ return valueField.Interface(), nil
+ }
+ tag := typeField.Tag
+ checkTag := func(tagName string) bool {
+ t := tag.Get(tagName)
+ tOptions := strings.Split(t, ",")
+ if len(tOptions) == 0 {
+ return false
+ }
+ if tOptions[0] != p.Info.FieldName {
+ return false
+ }
+ return true
+ }
+ if checkTag("json") || checkTag("graphql") {
+ return valueField.Interface(), nil
+ } else {
+ continue
+ }
+ }
+ return nil, nil
+ }
+
+ // try p.Source as a map[string]interface
+ if sourceMap, ok := p.Source.(map[string]interface{}); ok {
+ property := sourceMap[p.Info.FieldName]
+ val := reflect.ValueOf(property)
+ if val.IsValid() && val.Type().Kind() == reflect.Func {
+ // try type casting the func to the most basic func signature
+ // for more complex signatures, user have to define ResolveFn
+ if propertyFn, ok := property.(func() interface{}); ok {
+ return propertyFn(), nil
+ }
+ }
+ return property, nil
+ }
+
+ // last resort, return nil
+ return nil, nil
+}
+
+// This method looks up the field on the given type definition.
+// 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 Schema, parentType *Object, fieldName string) *FieldDefinition {
+
+ if parentType == nil {
+ return nil
+ }
+
+ if fieldName == SchemaMetaFieldDef.Name &&
+ schema.QueryType() == parentType {
+ return SchemaMetaFieldDef
+ }
+ if fieldName == TypeMetaFieldDef.Name &&
+ schema.QueryType() == parentType {
+ return TypeMetaFieldDef
+ }
+ if fieldName == TypeNameMetaFieldDef.Name {
+ return TypeNameMetaFieldDef
+ }
+ return parentType.Fields()[fieldName]
+}