func executeStep(
	ctx *ExecutionContext,
	plan *QueryPlan,
	step *QueryPlanStep,
	insertionPoint []string,
	resultLock *sync.Mutex,
	queryVariables map[string]interface{},
	resultCh chan *queryExecutionResult,
	errCh chan error,
	stepWg *sync.WaitGroup,
) {
	log.Debug("")
	log.Debug("Executing step to be inserted in ", step.ParentType, ". Insertion point: ", insertionPoint)
	log.Debug(step.SelectionSet)
	log.QueryPlanStep(step)
	variables := map[string]interface{}{}
	for variable := range step.Variables {
		if value, ok := queryVariables[variable]; ok {
			variables[variable] = value
		}
	}
	if len(insertionPoint) > 0 {
		head := insertionPoint[max(len(insertionPoint)-1, 0)]
		pointData, err := executorGetPointData(head)
		if err != nil {
			errCh <- err
			return
		}
		if pointData.ID == "" {
			errCh <- fmt.Errorf("Could not find id in path")
			return
		}
		variables["id"] = pointData.ID
	}
	if step.Queryer == nil {
		errCh <- errors.New(" could not find queryer for step")
		return
	}
	queryer := step.Queryer
	queryResult := map[string]interface{}{}
	if len(ctx.RequestMiddlewares) > 0 {
		if nQueryer, ok := queryer.(graphql.QueryerWithMiddlewares); ok {
			queryer = nQueryer.WithMiddlewares(ctx.RequestMiddlewares)
		}
	}
	operationName := ""
	if plan != nil && plan.Operation != nil {
		operationName = plan.Operation.Name
	}
	err := queryer.Query(ctx.RequestContext, &graphql.QueryInput{
		Query:         step.QueryString,
		QueryDocument: step.QueryDocument,
		Variables:     variables,
		OperationName: operationName,
	}, &queryResult)
	if err != nil {
		log.Warn("Network Error: ", err)
		errCh <- err
		return
	}
	stripNode := step.ParentType != "Query" && step.ParentType != "Subscription" && step.ParentType != "Mutation"
	if stripNode {
		log.Debug("Should strip node")
		extractedResult, err := executorExtractValue(queryResult, resultLock, []string{"node"})
		if err != nil {
			errCh <- err
			return
		}
		resultObj, ok := extractedResult.(map[string]interface{})
		if !ok {
			errCh <- fmt.Errorf("Query result of node query was not an object: %v", queryResult)
			return
		}
		queryResult = resultObj
	}
	type stepArgs struct {
		step           *QueryPlanStep
		insertionPoint []string
	}
	var dependentSteps []stepArgs
	defer func() {
		for _, sr := range dependentSteps {
			log.Info("Spawn ", sr.insertionPoint)
			go executeStep(ctx, plan, sr.step, sr.insertionPoint, resultLock, queryVariables, resultCh, errCh, stepWg)
		}
	}()
	if len(step.Then) > 0 {
		log.Debug("Kicking off child queries")
		for _, dependent := range step.Then {
			copiedInsertionPoint := make([]string, len(insertionPoint))
			copy(copiedInsertionPoint, insertionPoint)
			insertPoints, err := executorFindInsertionPoints(resultLock, dependent.InsertionPoint, step.SelectionSet, queryResult, [][]string{copiedInsertionPoint}, step.FragmentDefinitions)
			if err != nil {
				dependentSteps = nil
				errCh <- err
				return
			}
			for _, insertionPoint := range insertPoints {
				dependentSteps = append(dependentSteps, stepArgs{
					step:           dependent,
					insertionPoint: insertionPoint,
				})
			}
		}
	}
	stepWg.Add(len(dependentSteps))
	log.Debug("Pushing Result. Insertion point: ", insertionPoint, ". Value: ", queryResult)
	resultCh <- &queryExecutionResult{
		InsertionPoint: insertionPoint,
		Result:         queryResult,
	}
}
TODO: ugh... so... many... variables...
package gateway
import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"github.com/nautilus/graphql"
	"github.com/vektah/gqlparser/v2/ast"
)
type Executor interface {
	Execute(ctx *ExecutionContext) (map[string]interface{}, error)
}
type ParallelExecutor struct{}
type queryExecutionResult struct {
	InsertionPoint []string
	Result         map[string]interface{}
	StripNode      bool
}
type ExecutionContext struct {
	Plan               *QueryPlan
	Variables          map[string]interface{}
	RequestContext     context.Context
	RequestMiddlewares []graphql.NetworkMiddleware
}
func (executor *ParallelExecutor) Execute(ctx *ExecutionContext) (map[string]interface{}, error) {
	result := map[string]interface{}{}
	resultCh := make(chan *queryExecutionResult, 10)
	defer close(resultCh)
	stepWg := &sync.WaitGroup{}
	errMutex := &sync.Mutex{}
	errCh := make(chan error, 10)
	defer close(errCh)
	closeCh := make(chan bool)
	defer close(closeCh)
	resultLock := &sync.Mutex{}
	if len(ctx.Plan.RootStep.Then) == 0 {
		return nil, errors.New("was given empty plan")
	}
	for _, step := range ctx.Plan.RootStep.Then {
		stepWg.Add(1)
		go executeStep(ctx, ctx.Plan, step, []string{}, resultLock, ctx.Variables, resultCh, errCh, stepWg)
	}
	errs := graphql.ErrorList{}
	go func() {
		for {
			select {
			case payload := <-resultCh:
				if payload == nil {
					continue
				}
				log.Debug("Inserting result into ", payload.InsertionPoint)
				log.Debug("Result: ", payload.Result)
				err := executorInsertObject(result, resultLock, payload.InsertionPoint, payload.Result)
				if err != nil {
					errCh <- err
					continue
				}
				log.Debug("Done. ", result)
				stepWg.Done()
			case err := <-errCh:
				if err != nil {
					errMutex.Lock()
					if errList, ok := err.(graphql.ErrorList); ok {
						errs = append(errs, errList...)
					} else {
						errs = append(errs, err)
					}
					errMutex.Unlock()
					stepWg.Done()
				}
			case <-closeCh:
				return
			}
		}
	}()
	stepWg.Wait()
	errMutex.Lock()
	nErrs := len(errs)
	defer errMutex.Unlock()
	if nErrs > 0 {
		return result, errs
	}
	return result, nil
}