aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/graphql-go/graphql/type_info.go
blob: bcdc84a8dc0bb9986fc620501da40862a3b8f7b7 (plain) (tree)



















































































































































































































































































                                                                                                                                             
package graphql

import (
	"github.com/graphql-go/graphql/language/ast"
	"github.com/graphql-go/graphql/language/kinds"
)

// TODO: can move TypeInfo to a utils package if there ever is one
/**
 * TypeInfo is a utility class which, given a GraphQL schema, can keep track
 * of the current field and type definitions at any point in a GraphQL document
 * AST during a recursive descent by calling `enter(node)` and `leave(node)`.
 */
type fieldDefFn func(schema *Schema, parentType Type, fieldAST *ast.Field) *FieldDefinition

type TypeInfo struct {
	schema          *Schema
	typeStack       []Output
	parentTypeStack []Composite
	inputTypeStack  []Input
	fieldDefStack   []*FieldDefinition
	directive       *Directive
	argument        *Argument
	getFieldDef     fieldDefFn
}

type TypeInfoConfig struct {
	Schema *Schema

	// NOTE: this experimental optional second parameter is only needed in order
	// to support non-spec-compliant codebases. You should never need to use it.
	// It may disappear in the future.
	FieldDefFn fieldDefFn
}

func NewTypeInfo(opts *TypeInfoConfig) *TypeInfo {
	getFieldDef := opts.FieldDefFn
	if getFieldDef == nil {
		getFieldDef = DefaultTypeInfoFieldDef
	}
	return &TypeInfo{
		schema:      opts.Schema,
		getFieldDef: getFieldDef,
	}
}

func (ti *TypeInfo) Type() Output {
	if len(ti.typeStack) > 0 {
		return ti.typeStack[len(ti.typeStack)-1]
	}
	return nil
}

func (ti *TypeInfo) ParentType() Composite {
	if len(ti.parentTypeStack) > 0 {
		return ti.parentTypeStack[len(ti.parentTypeStack)-1]
	}
	return nil
}

func (ti *TypeInfo) InputType() Input {
	if len(ti.inputTypeStack) > 0 {
		return ti.inputTypeStack[len(ti.inputTypeStack)-1]
	}
	return nil
}
func (ti *TypeInfo) FieldDef() *FieldDefinition {
	if len(ti.fieldDefStack) > 0 {
		return ti.fieldDefStack[len(ti.fieldDefStack)-1]
	}
	return nil
}

func (ti *TypeInfo) Directive() *Directive {
	return ti.directive
}

func (ti *TypeInfo) Argument() *Argument {
	return ti.argument
}

func (ti *TypeInfo) Enter(node ast.Node) {

	schema := ti.schema
	var ttype Type
	switch node := node.(type) {
	case *ast.SelectionSet:
		namedType := GetNamed(ti.Type())
		var compositeType Composite
		if IsCompositeType(namedType) {
			compositeType, _ = namedType.(Composite)
		}
		ti.parentTypeStack = append(ti.parentTypeStack, compositeType)
	case *ast.Field:
		parentType := ti.ParentType()
		var fieldDef *FieldDefinition
		if parentType != nil {
			fieldDef = ti.getFieldDef(schema, parentType.(Type), node)
		}
		ti.fieldDefStack = append(ti.fieldDefStack, fieldDef)
		if fieldDef != nil {
			ti.typeStack = append(ti.typeStack, fieldDef.Type)
		} else {
			ti.typeStack = append(ti.typeStack, nil)
		}
	case *ast.Directive:
		nameVal := ""
		if node.Name != nil {
			nameVal = node.Name.Value
		}
		ti.directive = schema.Directive(nameVal)
	case *ast.OperationDefinition:
		if node.Operation == ast.OperationTypeQuery {
			ttype = schema.QueryType()
		} else if node.Operation == ast.OperationTypeMutation {
			ttype = schema.MutationType()
		} else if node.Operation == ast.OperationTypeSubscription {
			ttype = schema.SubscriptionType()
		}
		ti.typeStack = append(ti.typeStack, ttype)
	case *ast.InlineFragment:
		typeConditionAST := node.TypeCondition
		if typeConditionAST != nil {
			ttype, _ = typeFromAST(*schema, node.TypeCondition)
			ti.typeStack = append(ti.typeStack, ttype)
		} else {
			ti.typeStack = append(ti.typeStack, ti.Type())
		}
	case *ast.FragmentDefinition:
		typeConditionAST := node.TypeCondition
		if typeConditionAST != nil {
			ttype, _ = typeFromAST(*schema, typeConditionAST)
			ti.typeStack = append(ti.typeStack, ttype)
		} else {
			ti.typeStack = append(ti.typeStack, ti.Type())
		}
	case *ast.VariableDefinition:
		ttype, _ = typeFromAST(*schema, node.Type)
		ti.inputTypeStack = append(ti.inputTypeStack, ttype)
	case *ast.Argument:
		nameVal := ""
		if node.Name != nil {
			nameVal = node.Name.Value
		}
		var argType Input
		var argDef *Argument
		directive := ti.Directive()
		fieldDef := ti.FieldDef()
		if directive != nil {
			for _, arg := range directive.Args {
				if arg.Name() == nameVal {
					argDef = arg
				}
			}
		} else if fieldDef != nil {
			for _, arg := range fieldDef.Args {
				if arg.Name() == nameVal {
					argDef = arg
				}
			}
		}
		if argDef != nil {
			argType = argDef.Type
		}
		ti.argument = argDef
		ti.inputTypeStack = append(ti.inputTypeStack, argType)
	case *ast.ListValue:
		listType := GetNullable(ti.InputType())
		if list, ok := listType.(*List); ok {
			ti.inputTypeStack = append(ti.inputTypeStack, list.OfType)
		} else {
			ti.inputTypeStack = append(ti.inputTypeStack, nil)
		}
	case *ast.ObjectField:
		var fieldType Input
		objectType := GetNamed(ti.InputType())

		if objectType, ok := objectType.(*InputObject); ok {
			nameVal := ""
			if node.Name != nil {
				nameVal = node.Name.Value
			}
			if inputField, ok := objectType.Fields()[nameVal]; ok {
				fieldType = inputField.Type
			}
		}
		ti.inputTypeStack = append(ti.inputTypeStack, fieldType)
	}
}
func (ti *TypeInfo) Leave(node ast.Node) {
	kind := node.GetKind()
	switch kind {
	case kinds.SelectionSet:
		// pop ti.parentTypeStack
		if len(ti.parentTypeStack) > 0 {
			_, ti.parentTypeStack = ti.parentTypeStack[len(ti.parentTypeStack)-1], ti.parentTypeStack[:len(ti.parentTypeStack)-1]
		}
	case kinds.Field:
		// pop ti.fieldDefStack
		if len(ti.fieldDefStack) > 0 {
			_, ti.fieldDefStack = ti.fieldDefStack[len(ti.fieldDefStack)-1], ti.fieldDefStack[:len(ti.fieldDefStack)-1]
		}
		// pop ti.typeStack
		if len(ti.typeStack) > 0 {
			_, ti.typeStack = ti.typeStack[len(ti.typeStack)-1], ti.typeStack[:len(ti.typeStack)-1]
		}
	case kinds.Directive:
		ti.directive = nil
	case kinds.OperationDefinition:
		fallthrough
	case kinds.InlineFragment:
		fallthrough
	case kinds.FragmentDefinition:
		// pop ti.typeStack
		if len(ti.typeStack) > 0 {
			_, ti.typeStack = ti.typeStack[len(ti.typeStack)-1], ti.typeStack[:len(ti.typeStack)-1]
		}
	case kinds.VariableDefinition:
		// pop ti.inputTypeStack
		if len(ti.inputTypeStack) > 0 {
			_, ti.inputTypeStack = ti.inputTypeStack[len(ti.inputTypeStack)-1], ti.inputTypeStack[:len(ti.inputTypeStack)-1]
		}
	case kinds.Argument:
		ti.argument = nil
		// pop ti.inputTypeStack
		if len(ti.inputTypeStack) > 0 {
			_, ti.inputTypeStack = ti.inputTypeStack[len(ti.inputTypeStack)-1], ti.inputTypeStack[:len(ti.inputTypeStack)-1]
		}
	case kinds.ListValue:
		fallthrough
	case kinds.ObjectField:
		// pop ti.inputTypeStack
		if len(ti.inputTypeStack) > 0 {
			_, ti.inputTypeStack = ti.inputTypeStack[len(ti.inputTypeStack)-1], ti.inputTypeStack[:len(ti.inputTypeStack)-1]
		}
	}
}

// DefaultTypeInfoFieldDef Not exactly the same as the executor's definition of FieldDef, in this
// statically evaluated environment we do not always have an Object type,
// and need to handle Interface and Union types.
func DefaultTypeInfoFieldDef(schema *Schema, parentType Type, fieldAST *ast.Field) *FieldDefinition {
	name := ""
	if fieldAST.Name != nil {
		name = fieldAST.Name.Value
	}
	if name == SchemaMetaFieldDef.Name &&
		schema.QueryType() == parentType {
		return SchemaMetaFieldDef
	}
	if name == TypeMetaFieldDef.Name &&
		schema.QueryType() == parentType {
		return TypeMetaFieldDef
	}
	if name == TypeNameMetaFieldDef.Name && parentType != nil {
		if t, ok := parentType.(*Object); ok && t != nil {
			return TypeNameMetaFieldDef
		}
		if t, ok := parentType.(*Interface); ok && t != nil {
			return TypeNameMetaFieldDef
		}
		if t, ok := parentType.(*Union); ok && t != nil {
			return TypeNameMetaFieldDef
		}
	}

	if parentType, ok := parentType.(*Object); ok && parentType != nil {
		field, _ := parentType.Fields()[name]
		return field
	}
	if parentType, ok := parentType.(*Interface); ok && parentType != nil {
		field, _ := parentType.Fields()[name]
		return field
	}
	return nil
}