diff options
Diffstat (limited to 'vendor/github.com/graphql-go/graphql/executor.go')
-rw-r--r-- | vendor/github.com/graphql-go/graphql/executor.go | 921 |
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] +} |