func TestVisitor_AllowsSkippingASubTree(t *testing.T) { query := `{ a, b { x }, c }` astDoc := parse(t, query) visited := []interface{}{} expectedVisited := []interface{}{ []interface{}{"enter", "Document", nil}, []interface{}{"enter", "OperationDefinition", nil}, []interface{}{"enter", "SelectionSet", nil}, []interface{}{"enter", "Field", nil}, []interface{}{"enter", "Name", "a"}, []interface{}{"leave", "Name", "a"}, []interface{}{"leave", "Field", nil}, []interface{}{"enter", "Field", nil}, []interface{}{"enter", "Field", nil}, []interface{}{"enter", "Name", "c"}, []interface{}{"leave", "Name", "c"}, []interface{}{"leave", "Field", nil}, []interface{}{"leave", "SelectionSet", nil}, []interface{}{"leave", "OperationDefinition", nil}, []interface{}{"leave", "Document", nil}, } v := &visitor.VisitorOptions{ Enter: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case *ast.Name: visited = append(visited, []interface{}{"enter", node.Kind, node.Value}) case *ast.Field: visited = append(visited, []interface{}{"enter", node.Kind, nil}) if node.Name != nil && node.Name.Value == "b" { return visitor.ActionSkip, nil } case ast.Node: visited = append(visited, []interface{}{"enter", node.GetKind(), nil}) default: visited = append(visited, []interface{}{"enter", nil, nil}) } return visitor.ActionNoChange, nil }, Leave: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case *ast.Name: visited = append(visited, []interface{}{"leave", node.Kind, node.Value}) case ast.Node: visited = append(visited, []interface{}{"leave", node.GetKind(), nil}) default: visited = append(visited, []interface{}{"leave", nil, nil}) } return visitor.ActionNoChange, nil }, } _ = visitor.Visit(astDoc, v, nil) if !reflect.DeepEqual(visited, expectedVisited) { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedVisited, visited)) } }
func (ctx *ValidationContext) VariableUsages(node HasSelectionSet) []*VariableUsage { if usages, ok := ctx.variableUsages[node]; ok && usages != nil { return usages } usages := []*VariableUsage{} typeInfo := NewTypeInfo(&TypeInfoConfig{ Schema: ctx.schema, }) visitor.Visit(node, visitor.VisitWithTypeInfo(typeInfo, &visitor.VisitorOptions{ KindFuncMap: map[string]visitor.NamedVisitFuncs{ kinds.VariableDefinition: { Kind: func(p visitor.VisitFuncParams) (string, interface{}) { return visitor.ActionSkip, nil }, }, kinds.Variable: { Kind: func(p visitor.VisitFuncParams) (string, interface{}) { if node, ok := p.Node.(*ast.Variable); ok && node != nil { usages = append(usages, &VariableUsage{ Node: node, Type: typeInfo.InputType(), }) } return visitor.ActionNoChange, nil }, }, }, }), nil) ctx.variableUsages[node] = usages return usages }
func TestVisitor_AllowsForEditingOnEnter(t *testing.T) { query := `{ a, b, c { a, b, c } }` astDoc := parse(t, query) expectedQuery := `{ a, c { a, c } }` expectedAST := parse(t, expectedQuery) v := &visitor.VisitorOptions{ Enter: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case *ast.Field: if node.Name != nil && node.Name.Value == "b" { return visitor.ActionUpdate, nil } } return visitor.ActionNoChange, nil }, } editedAst := visitor.Visit(astDoc, v, nil) if !reflect.DeepEqual(expectedAST, editedAst) { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedAST, editedAst)) } }
func Print(astNode ast.Node) (printed interface{}) { defer func() interface{} { if r := recover(); r != nil { return fmt.Sprintf("%v", astNode) } return printed }() printed = visitor.Visit(astNode, &visitor.VisitorOptions{ LeaveKindMap: printDocASTReducer, }, nil) return printed }
func TestVisitor_AllowsANamedFunctionsVisitorAPI(t *testing.T) { query := `{ a, b { x }, c }` astDoc := parse(t, query) visited := []interface{}{} expectedVisited := []interface{}{ []interface{}{"enter", "SelectionSet", nil}, []interface{}{"enter", "Name", "a"}, []interface{}{"enter", "Name", "b"}, []interface{}{"enter", "SelectionSet", nil}, []interface{}{"enter", "Name", "x"}, []interface{}{"leave", "SelectionSet", nil}, []interface{}{"enter", "Name", "c"}, []interface{}{"leave", "SelectionSet", nil}, } v := &visitor.VisitorOptions{ KindFuncMap: map[string]visitor.NamedVisitFuncs{ "Name": visitor.NamedVisitFuncs{ Kind: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case *ast.Name: visited = append(visited, []interface{}{"enter", node.Kind, node.Value}) } return visitor.ActionNoChange, nil }, }, "SelectionSet": visitor.NamedVisitFuncs{ Enter: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case *ast.SelectionSet: visited = append(visited, []interface{}{"enter", node.Kind, nil}) } return visitor.ActionNoChange, nil }, Leave: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case *ast.SelectionSet: visited = append(visited, []interface{}{"leave", node.Kind, nil}) } return visitor.ActionNoChange, nil }, }, }, } _ = visitor.Visit(astDoc, v, nil) if !reflect.DeepEqual(visited, expectedVisited) { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedVisited, visited)) } }
func Print11(astNode ast.Node) (printed interface{}) { // defer func() interface{} { // if r := recover(); r != nil { // log.Println("Error: %v", r) // return printed // } // return printed // }() printed = visitor.Visit(astNode, &visitor.VisitorOptions{ LeaveKindMap: printDocASTReducer, }, nil) return printed }
// VisitUsingRules This uses a specialized visitor which runs multiple visitors in parallel, // while maintaining the visitor skip and break API. // // @internal // Had to expose it to unit test experimental customizable validation feature, // but not meant for public consumption func VisitUsingRules(schema *Schema, typeInfo *TypeInfo, astDoc *ast.Document, rules []ValidationRuleFn) []gqlerrors.FormattedError { context := NewValidationContext(schema, astDoc, typeInfo) visitors := []*visitor.VisitorOptions{} for _, rule := range rules { instance := rule(context) visitors = append(visitors, instance.VisitorOpts) } // Visit the whole document with each instance of all provided rules. visitor.Visit(astDoc, visitor.VisitWithTypeInfo(typeInfo, visitor.VisitInParallel(visitors...)), nil) return context.Errors() }
func TestVisitor_AllowsSkippingASubTree(t *testing.T) { query := `{ a, b { x }, c }` astDoc := parse(t, query) visited := []interface{}{} expectedVisited := []interface{}{ []interface{}{"enter", "Document", nil}, []interface{}{"enter", "OperationDefinition", nil}, []interface{}{"enter", "SelectionSet", nil}, []interface{}{"enter", "Field", nil}, []interface{}{"enter", "Name", "a"}, []interface{}{"leave", "Name", "a"}, []interface{}{"leave", "Field", nil}, []interface{}{"enter", "Field", nil}, []interface{}{"enter", "Field", nil}, []interface{}{"enter", "Name", "c"}, []interface{}{"leave", "Name", "c"}, []interface{}{"leave", "Field", nil}, []interface{}{"leave", "SelectionSet", nil}, []interface{}{"leave", "OperationDefinition", nil}, []interface{}{"leave", "Document", nil}, } v := &visitor.VisitorOptions{ Enter: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case map[string]interface{}: visited = append(visited, []interface{}{"enter", getMapValue(node, "Kind"), getMapValue(node, "Value")}) if getMapValueString(node, "Kind") == "Field" && getMapValueString(node, "Name.Value") == "b" { return visitor.ActionSkip, nil } } return visitor.ActionNoChange, nil }, Leave: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case map[string]interface{}: visited = append(visited, []interface{}{"leave", getMapValue(node, "Kind"), getMapValue(node, "Value")}) } return visitor.ActionNoChange, nil }, } _ = visitor.Visit(astDoc, v, nil) if !reflect.DeepEqual(visited, expectedVisited) { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedVisited, visited)) } }
func TestVisitor_ProducesHelpfulErrorMessages(t *testing.T) { defer func() { if r := recover(); r != nil { err := r.(string) expectedErr := `Invalid AST Node (4): map[random:Data]` if err != expectedErr { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(err, expectedErr)) } return } t.Fatalf("expected to panic") }() astDoc := map[string]interface{}{ "random": "Data", } _ = visitor.Visit(astDoc, nil, nil) }
func TestVisitor_VisitsEditedNode(t *testing.T) { query := `{ a { x } }` astDoc := parse(t, query) addedField := &ast.Field{ Kind: "Field", Name: &ast.Name{ Kind: "Name", Value: "__typename", }, } didVisitAddedField := false v := &visitor.VisitorOptions{ Enter: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case *ast.Field: if node.Name != nil && node.Name.Value == "a" { s := node.SelectionSet.Selections s = append(s, addedField) ss := node.SelectionSet ss.Selections = s return visitor.ActionUpdate, &ast.Field{ Kind: "Field", SelectionSet: ss, } } if reflect.DeepEqual(node, addedField) { didVisitAddedField = true } } return visitor.ActionNoChange, nil }, } _ = visitor.Visit(astDoc, v, nil) if didVisitAddedField == false { t.Fatalf("Unexpected result, expected didVisitAddedField == true") } }
func TestVisitor_VisitsEditedNode(t *testing.T) { query := `{ a { x } }` astDoc := parse(t, query) addedField := map[string]interface{}{ "Kind": "Field", "Name": map[string]interface{}{ "Kind": "Name", "Value": "__typename", }, } didVisitAddedField := false v := &visitor.VisitorOptions{ Enter: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case map[string]interface{}: if getMapValueString(node, "Kind") == "Field" && getMapValueString(node, "Name.Value") == "a" { s := getMapValue(node, "SelectionSet.Selections").([]interface{}) s = append(s, addedField) return visitor.ActionUpdate, map[string]interface{}{ "Kind": "Field", "SelectionSet": s, } } if reflect.DeepEqual(node, addedField) { didVisitAddedField = true } } return visitor.ActionNoChange, nil }, } _ = visitor.Visit(astDoc, v, nil) if didVisitAddedField == false { t.Fatalf("Unexpected result, expected didVisitAddedField == true") } }
func TestVisitor_AllowsForEditingOnLeave(t *testing.T) { query := `{ a, b, c { a, b, c } }` astDoc := parse(t, query) expectedQuery := `{ a, c { a, c } }` expectedAST := parse(t, expectedQuery) v := &visitor.VisitorOptions{ Leave: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case map[string]interface{}: if getMapValueString(node, "Kind") == "Field" && getMapValueString(node, "Name.Value") == "b" { return visitor.ActionUpdate, nil } } return visitor.ActionNoChange, nil }, } editedAst := visitor.Visit(astDoc, v, nil) if !reflect.DeepEqual(ASTToJSON(t, expectedAST), editedAst) { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedAST, editedAst)) } }
func visitUsingRules(schema *Schema, astDoc *ast.Document, rules []ValidationRuleFn) (errors []gqlerrors.FormattedError) { typeInfo := NewTypeInfo(schema) context := NewValidationContext(schema, astDoc, typeInfo) var visitInstance func(astNode ast.Node, instance *ValidationRuleInstance) visitInstance = func(astNode ast.Node, instance *ValidationRuleInstance) { visitor.Visit(astNode, &visitor.VisitorOptions{ Enter: func(p visitor.VisitFuncParams) (string, interface{}) { var action = visitor.ActionNoChange var result interface{} switch node := p.Node.(type) { case ast.Node: // Collect type information about the current position in the AST. typeInfo.Enter(node) // Do not visit top level fragment definitions if this instance will // visit those fragments inline because it // provided `visitSpreadFragments`. kind := node.GetKind() if kind == kinds.FragmentDefinition && p.Key != nil && instance.VisitSpreadFragments == true { return visitor.ActionSkip, nil } // Get the visitor function from the validation instance, and if it // exists, call it with the visitor arguments. enterFn := visitor.GetVisitFn(instance.VisitorOpts, false, kind) if enterFn != nil { action, result = enterFn(p) } // If the visitor returned an error, log it and do not visit any // deeper nodes. if err, ok := result.(error); ok && err != nil { errors = append(errors, gqlerrors.FormatError(err)) action = visitor.ActionSkip } if err, ok := result.([]error); ok && err != nil { errors = append(errors, gqlerrors.FormatErrors(err...)...) action = visitor.ActionSkip } // If any validation instances provide the flag `visitSpreadFragments` // and this node is a fragment spread, visit the fragment definition // from this point. if action == visitor.ActionNoChange && result == nil && instance.VisitSpreadFragments == true && kind == kinds.FragmentSpread { node, _ := node.(*ast.FragmentSpread) name := node.Name nameVal := "" if name != nil { nameVal = name.Value } fragment := context.Fragment(nameVal) if fragment != nil { visitInstance(fragment, instance) } } // If the result is "false" (ie action === Action.Skip), we're not visiting any descendent nodes, // but need to update typeInfo. if action == visitor.ActionSkip { typeInfo.Leave(node) } } return action, result }, Leave: func(p visitor.VisitFuncParams) (string, interface{}) { var action = visitor.ActionNoChange var result interface{} switch node := p.Node.(type) { case ast.Node: kind := node.GetKind() // Get the visitor function from the validation instance, and if it // exists, call it with the visitor arguments. leaveFn := visitor.GetVisitFn(instance.VisitorOpts, true, kind) if leaveFn != nil { action, result = leaveFn(p) } // If the visitor returned an error, log it and do not visit any // deeper nodes. if err, ok := result.(error); ok && err != nil { errors = append(errors, gqlerrors.FormatError(err)) action = visitor.ActionSkip } if err, ok := result.([]error); ok && err != nil { errors = append(errors, gqlerrors.FormatErrors(err...)...) action = visitor.ActionSkip } // Update typeInfo. typeInfo.Leave(node) } return action, result }, }, nil) } instances := []*ValidationRuleInstance{} for _, rule := range rules { instance := rule(context) instances = append(instances, instance) } for _, instance := range instances { visitInstance(astDoc, instance) } return errors }
func TestVisitor_VisitsKitchenSink(t *testing.T) { b, err := ioutil.ReadFile("../../kitchen-sink.graphql") if err != nil { t.Fatalf("unable to load kitchen-sink.graphql") } query := string(b) astDoc := parse(t, query) visited := []interface{}{} expectedVisited := []interface{}{ []interface{}{"enter", "Document", nil, nil}, []interface{}{"enter", "OperationDefinition", 0, nil}, []interface{}{"enter", "Name", "Name", "OperationDefinition"}, []interface{}{"leave", "Name", "Name", "OperationDefinition"}, []interface{}{"enter", "VariableDefinition", 0, nil}, []interface{}{"enter", "Variable", "Variable", "VariableDefinition"}, []interface{}{"enter", "Name", "Name", "Variable"}, []interface{}{"leave", "Name", "Name", "Variable"}, []interface{}{"leave", "Variable", "Variable", "VariableDefinition"}, []interface{}{"enter", "Named", "Type", "VariableDefinition"}, []interface{}{"enter", "Name", "Name", "Named"}, []interface{}{"leave", "Name", "Name", "Named"}, []interface{}{"leave", "Named", "Type", "VariableDefinition"}, []interface{}{"leave", "VariableDefinition", 0, nil}, []interface{}{"enter", "VariableDefinition", 1, nil}, []interface{}{"enter", "Variable", "Variable", "VariableDefinition"}, []interface{}{"enter", "Name", "Name", "Variable"}, []interface{}{"leave", "Name", "Name", "Variable"}, []interface{}{"leave", "Variable", "Variable", "VariableDefinition"}, []interface{}{"enter", "Named", "Type", "VariableDefinition"}, []interface{}{"enter", "Name", "Name", "Named"}, []interface{}{"leave", "Name", "Name", "Named"}, []interface{}{"leave", "Named", "Type", "VariableDefinition"}, []interface{}{"enter", "EnumValue", "DefaultValue", "VariableDefinition"}, []interface{}{"leave", "EnumValue", "DefaultValue", "VariableDefinition"}, []interface{}{"leave", "VariableDefinition", 1, nil}, []interface{}{"enter", "SelectionSet", "SelectionSet", "OperationDefinition"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Alias", "Field"}, []interface{}{"leave", "Name", "Alias", "Field"}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"enter", "Argument", 0, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "ListValue", "Value", "Argument"}, []interface{}{"enter", "IntValue", 0, nil}, []interface{}{"leave", "IntValue", 0, nil}, []interface{}{"enter", "IntValue", 1, nil}, []interface{}{"leave", "IntValue", 1, nil}, []interface{}{"leave", "ListValue", "Value", "Argument"}, []interface{}{"leave", "Argument", 0, nil}, []interface{}{"enter", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"enter", "InlineFragment", 1, nil}, []interface{}{"enter", "Named", "TypeCondition", "InlineFragment"}, []interface{}{"enter", "Name", "Name", "Named"}, []interface{}{"leave", "Name", "Name", "Named"}, []interface{}{"leave", "Named", "TypeCondition", "InlineFragment"}, []interface{}{"enter", "Directive", 0, nil}, []interface{}{"enter", "Name", "Name", "Directive"}, []interface{}{"leave", "Name", "Name", "Directive"}, []interface{}{"leave", "Directive", 0, nil}, []interface{}{"enter", "SelectionSet", "SelectionSet", "InlineFragment"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"enter", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"enter", "Field", 1, nil}, []interface{}{"enter", "Name", "Alias", "Field"}, []interface{}{"leave", "Name", "Alias", "Field"}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"enter", "Argument", 0, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "IntValue", "Value", "Argument"}, []interface{}{"leave", "IntValue", "Value", "Argument"}, []interface{}{"leave", "Argument", 0, nil}, []interface{}{"enter", "Argument", 1, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "Variable", "Value", "Argument"}, []interface{}{"enter", "Name", "Name", "Variable"}, []interface{}{"leave", "Name", "Name", "Variable"}, []interface{}{"leave", "Variable", "Value", "Argument"}, []interface{}{"leave", "Argument", 1, nil}, []interface{}{"enter", "Directive", 0, nil}, []interface{}{"enter", "Name", "Name", "Directive"}, []interface{}{"leave", "Name", "Name", "Directive"}, []interface{}{"enter", "Argument", 0, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "Variable", "Value", "Argument"}, []interface{}{"enter", "Name", "Name", "Variable"}, []interface{}{"leave", "Name", "Name", "Variable"}, []interface{}{"leave", "Variable", "Value", "Argument"}, []interface{}{"leave", "Argument", 0, nil}, []interface{}{"leave", "Directive", 0, nil}, []interface{}{"enter", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"enter", "FragmentSpread", 1, nil}, []interface{}{"enter", "Name", "Name", "FragmentSpread"}, []interface{}{"leave", "Name", "Name", "FragmentSpread"}, []interface{}{"leave", "FragmentSpread", 1, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"leave", "Field", 1, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "InlineFragment"}, []interface{}{"leave", "InlineFragment", 1, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "OperationDefinition"}, []interface{}{"leave", "OperationDefinition", 0, nil}, []interface{}{"enter", "OperationDefinition", 1, nil}, []interface{}{"enter", "Name", "Name", "OperationDefinition"}, []interface{}{"leave", "Name", "Name", "OperationDefinition"}, []interface{}{"enter", "SelectionSet", "SelectionSet", "OperationDefinition"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"enter", "Argument", 0, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "IntValue", "Value", "Argument"}, []interface{}{"leave", "IntValue", "Value", "Argument"}, []interface{}{"leave", "Argument", 0, nil}, []interface{}{"enter", "Directive", 0, nil}, []interface{}{"enter", "Name", "Name", "Directive"}, []interface{}{"leave", "Name", "Name", "Directive"}, []interface{}{"leave", "Directive", 0, nil}, []interface{}{"enter", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"enter", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "Field"}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "OperationDefinition"}, []interface{}{"leave", "OperationDefinition", 1, nil}, []interface{}{"enter", "FragmentDefinition", 2, nil}, []interface{}{"enter", "Name", "Name", "FragmentDefinition"}, []interface{}{"leave", "Name", "Name", "FragmentDefinition"}, []interface{}{"enter", "Named", "TypeCondition", "FragmentDefinition"}, []interface{}{"enter", "Name", "Name", "Named"}, []interface{}{"leave", "Name", "Name", "Named"}, []interface{}{"leave", "Named", "TypeCondition", "FragmentDefinition"}, []interface{}{"enter", "SelectionSet", "SelectionSet", "FragmentDefinition"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"enter", "Argument", 0, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "Variable", "Value", "Argument"}, []interface{}{"enter", "Name", "Name", "Variable"}, []interface{}{"leave", "Name", "Name", "Variable"}, []interface{}{"leave", "Variable", "Value", "Argument"}, []interface{}{"leave", "Argument", 0, nil}, []interface{}{"enter", "Argument", 1, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "Variable", "Value", "Argument"}, []interface{}{"enter", "Name", "Name", "Variable"}, []interface{}{"leave", "Name", "Name", "Variable"}, []interface{}{"leave", "Variable", "Value", "Argument"}, []interface{}{"leave", "Argument", 1, nil}, []interface{}{"enter", "Argument", 2, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "ObjectValue", "Value", "Argument"}, []interface{}{"enter", "ObjectField", 0, nil}, []interface{}{"enter", "Name", "Name", "ObjectField"}, []interface{}{"leave", "Name", "Name", "ObjectField"}, []interface{}{"enter", "StringValue", "Value", "ObjectField"}, []interface{}{"leave", "StringValue", "Value", "ObjectField"}, []interface{}{"leave", "ObjectField", 0, nil}, []interface{}{"leave", "ObjectValue", "Value", "Argument"}, []interface{}{"leave", "Argument", 2, nil}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "FragmentDefinition"}, []interface{}{"leave", "FragmentDefinition", 2, nil}, []interface{}{"enter", "OperationDefinition", 3, nil}, []interface{}{"enter", "SelectionSet", "SelectionSet", "OperationDefinition"}, []interface{}{"enter", "Field", 0, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"enter", "Argument", 0, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "BooleanValue", "Value", "Argument"}, []interface{}{"leave", "BooleanValue", "Value", "Argument"}, []interface{}{"leave", "Argument", 0, nil}, []interface{}{"enter", "Argument", 1, nil}, []interface{}{"enter", "Name", "Name", "Argument"}, []interface{}{"leave", "Name", "Name", "Argument"}, []interface{}{"enter", "BooleanValue", "Value", "Argument"}, []interface{}{"leave", "BooleanValue", "Value", "Argument"}, []interface{}{"leave", "Argument", 1, nil}, []interface{}{"leave", "Field", 0, nil}, []interface{}{"enter", "Field", 1, nil}, []interface{}{"enter", "Name", "Name", "Field"}, []interface{}{"leave", "Name", "Name", "Field"}, []interface{}{"leave", "Field", 1, nil}, []interface{}{"leave", "SelectionSet", "SelectionSet", "OperationDefinition"}, []interface{}{"leave", "OperationDefinition", 3, nil}, []interface{}{"leave", "Document", nil, nil}, } v := &visitor.VisitorOptions{ Enter: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case ast.Node: if p.Parent != nil { visited = append(visited, []interface{}{"enter", node.GetKind(), p.Key, p.Parent.GetKind()}) } else { visited = append(visited, []interface{}{"enter", node.GetKind(), p.Key, nil}) } } return visitor.ActionNoChange, nil }, Leave: func(p visitor.VisitFuncParams) (string, interface{}) { switch node := p.Node.(type) { case ast.Node: if p.Parent != nil { visited = append(visited, []interface{}{"leave", node.GetKind(), p.Key, p.Parent.GetKind()}) } else { visited = append(visited, []interface{}{"leave", node.GetKind(), p.Key, nil}) } } return visitor.ActionNoChange, nil }, } _ = visitor.Visit(astDoc, v, nil) if !reflect.DeepEqual(visited, expectedVisited) { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedVisited, visited)) } }