func (p *MinQueriesPlanner) selectLocation(possibleLocations []string, config *extractSelectionConfig) string {
	if len(possibleLocations) == 1 {
		return possibleLocations[0]
	} else {
		priorities := make([]string, len(p.LocationPriorities), len(p.LocationPriorities)+2)
		copy(priorities, p.LocationPriorities)
		priorities = append(priorities, config.parentLocation, internalSchemaLocation)
		for _, priority := range priorities {
			for _, location := range possibleLocations {
				if location == priority {
					return priority
				}
			}
		}
		return possibleLocations[0]
	}
}
selects one location out of possibleLocations, prioritizing the parent's location and the internal schema
package gateway
import (
	"errors"
	"fmt"
	"sync"
	"github.com/vektah/gqlparser/v2"
	"github.com/vektah/gqlparser/v2/ast"
	"github.com/nautilus/graphql"
)
type QueryPlanStep struct {
	InsertionPoint []string
	Then           []*QueryPlanStep
	Queryer      graphql.Queryer
	ParentType   string
	ParentID     string
	SelectionSet ast.SelectionSet
	QueryDocument       *ast.QueryDocument
	QueryString         string
	FragmentDefinitions ast.FragmentDefinitionList
	Variables           Set
}
type QueryPlan struct {
	Operation           *ast.OperationDefinition
	RootStep            *QueryPlanStep
	FragmentDefinitions ast.FragmentDefinitionList
	FieldsToScrub       map[string][][]string
}
type newQueryPlanStepPayload struct {
	Plan           *QueryPlan
	Location       string
	SelectionSet   ast.SelectionSet
	ParentType     string
	Parent         *QueryPlanStep
	InsertionPoint []string
	Fragments      ast.FragmentDefinitionList
	Wrapper        ast.SelectionSet
}
type QueryPlanner interface {
	Plan(*PlanningContext) (QueryPlanList, error)
}
type PlannerWithQueryerFactory interface {
	WithQueryerFactory(*QueryerFactory) QueryPlanner
}
type PlannerWithLocationPriorities interface {
	WithLocationPriorities(priorities []string) QueryPlanner
}
type QueryerFactory func(ctx *PlanningContext, url string) graphql.Queryer
type Planner struct {
	QueryerFactory *QueryerFactory
	queryerCache   map[string]graphql.Queryer
}
type MinQueriesPlanner struct {
	Planner
	LocationPriorities []string
}
func (p *MinQueriesPlanner) WithQueryerFactory(factory *QueryerFactory) QueryPlanner {
	p.Planner.QueryerFactory = factory
	return p
}
func (p *MinQueriesPlanner) WithLocationPriorities(priorities []string) QueryPlanner {
	p.LocationPriorities = priorities
	return p
}
type PlanningContext struct {
	Query     string
	Schema    *ast.Schema
	Locations FieldURLMap
	Gateway   *Gateway
}
func (p *MinQueriesPlanner) Plan(ctx *PlanningContext) (QueryPlanList, error) {
	parsedQuery, e := gqlparser.LoadQuery(ctx.Schema, ctx.Query)
	if e != nil {
		return nil, e
	}
	plans, err := p.generatePlans(ctx, parsedQuery)
	if err != nil {
		return nil, err
	}
	flatSelection, err := graphql.ApplyFragments(parsedQuery.Operations[0].SelectionSet, parsedQuery.Fragments)
	if err != nil {
		return nil, err
	}
	err = p.generateScrubFields(plans, flatSelection)
	if err != nil {
		return nil, err
	}
	return plans, nil
}
func (p *MinQueriesPlanner) generatePlans(ctx *PlanningContext, query *ast.QueryDocument) (QueryPlanList, error) {
	plans := QueryPlanList{}
	for _, operation := range query.Operations {
		plan := &QueryPlan{
			Operation:           operation,
			FragmentDefinitions: query.Fragments,
		}
		plans = append(plans, plan)
		stepCh := make(chan *newQueryPlanStepPayload, 50)
		errCh := make(chan error)
		defer close(errCh)
		stepWg := &sync.WaitGroup{}
		operationType := "Query"
		switch operation.Operation {
		case ast.Mutation:
			operationType = "Mutation"
		case ast.Subscription:
			operationType = "Subscription"
		}
		stepWg.Add(1)
		stepCh <- &newQueryPlanStepPayload{
			Plan:           plan,
			SelectionSet:   operation.SelectionSet,
			ParentType:     operationType,
			Location:       "",
			InsertionPoint: []string{},
			Fragments:      ast.FragmentDefinitionList{},
			Wrapper:        ast.SelectionSet{},
		}
		go func(newSteps chan *newQueryPlanStepPayload) {
		SelectLoop:
			for {
				select {
				case payload, ok := <-newSteps:
					if !ok {
						return
					}
					step := &QueryPlanStep{
						Queryer:             p.GetQueryer(ctx, payload.Location),
						ParentType:          payload.ParentType,
						SelectionSet:        ast.SelectionSet{},
						InsertionPoint:      payload.InsertionPoint,
						Variables:           Set{},
						FragmentDefinitions: payload.Fragments,
					}
					if payload.Parent != nil {
						log.Debug(fmt.Sprintf("Adding step as dependency"))
						payload.Parent.Then = append(payload.Parent.Then, step)
					}
					if plan.RootStep == nil {
						plan.RootStep = step
					}
					log.Debug(fmt.Sprintf(
						"Encountered new step: \n"+
							"\tParentType: %v \n"+
							"\tInsertion Point: %v \n"+
							"\tSelectionSet: \n%s",
						step.ParentType,
						payload.InsertionPoint,
						graphql.FormatSelectionSet(payload.SelectionSet),
					))
					newSelection, err := p.extractSelection(&extractSelectionConfig{
						stepCh:         stepCh,
						stepWg:         stepWg,
						locations:      ctx.Locations,
						parentLocation: payload.Location,
						parentType:     step.ParentType,
						selection:      payload.SelectionSet,
						step:           step,
						insertionPoint: payload.InsertionPoint,
						plan:           payload.Plan,
						wrapper:        payload.Wrapper,
					})
					if err != nil {
						errCh <- err
						continue SelectLoop
					}
					if newSelection != nil {
						step.SelectionSet = newSelection
					}
					variableDefs := ast.VariableDefinitionList{}
					for variable := range step.Variables {
						variableDefs = append(variableDefs, plan.Operation.VariableDefinitions.ForName(variable))
					}
					step.QueryDocument = plannerBuildQuery(plan.Operation.Name, step.ParentType, variableDefs, step.SelectionSet, step.FragmentDefinitions)
					queryString, err := graphql.PrintQuery(step.QueryDocument)
					if err != nil {
						errCh <- err
						continue SelectLoop
					}
					step.QueryString = queryString
					stepWg.Done()
					log.Debug("")
				}
			}
		}(stepCh)
		doneCh := make(chan bool)
		defer close(doneCh)
		go func() {
			stepWg.Wait()
			doneCh <- true
		}()
		select {
		case err := <-errCh:
			return nil, err
		case <-doneCh:
			close(stepCh)
		}
	}
	return plans, nil
}
type extractSelectionConfig struct {
	stepCh chan *newQueryPlanStepPayload
	errCh  chan error
	stepWg *sync.WaitGroup
	locations      FieldURLMap
	parentLocation string
	parentType     string
	step           *QueryPlanStep
	plan           *QueryPlan
	selection      ast.SelectionSet
	insertionPoint []string
	wrapper        ast.SelectionSet
}
func (p *MinQueriesPlanner) extractSelection(config *extractSelectionConfig) (ast.SelectionSet, error) {
	log.Debug("")
	log.Debug("--- Extracting Selection ---")
	log.Debug("Parent location: ", config.parentLocation)
	locationFields, locationFragments, err := p.groupSelectionSet(config)
	if err != nil {
		return nil, err
	}
	log.Debug("Fields By Location: ", locationFields)
	checkForID := false
	for location, selectionSet := range locationFields {
		if location == config.parentLocation {
			continue
		}
		log.Debug(fmt.Sprintf(
			"Adding the new step"+
				"\n\tParent Type: %s"+
				"\n\tLocation: %v"+
				"\n\tInsertion point: %v",
			config.parentType, location, config.insertionPoint))
		checkForID = true
		if config.wrapper != nil && len(config.wrapper) > 0 {
			log.Debug("wrapping selection", config.wrapper)
			selectionSet, err = p.wrapSelectionSet(config, locationFragments, location, selectionSet)
			if err != nil {
				return nil, err
			}
		}
		config.stepWg.Add(1)
		config.stepCh <- &newQueryPlanStepPayload{
			Plan:           config.plan,
			Parent:         config.step,
			InsertionPoint: config.insertionPoint,
			Wrapper:        config.wrapper,
			ParentType:     config.parentType,
			Location:     location,
			SelectionSet: selectionSet,
			Fragments:    locationFragments[location],
		}
	}
	if checkForID {
		locationFields[config.parentLocation] = append(locationFields[config.parentLocation], &ast.Field{Name: "id"})
	}
	currentLocationFields, ok := locationFields[config.parentLocation]
	if !ok {
		return ast.SelectionSet{}, nil
	}
	finalSelection := ast.SelectionSet{}
	for _, selection := range currentLocationFields {
		switch selection := selection.(type) {
		case *ast.Field:
			if len(selection.SelectionSet) > 0 {
				insertionPoint := make([]string, len(config.insertionPoint))
				copy(insertionPoint, config.insertionPoint)
				insertionPoint = append(insertionPoint, selection.Alias)
				wrapper := ast.SelectionSet{}
				if len(config.wrapper) > 0 {
					wrapper = config.wrapper[:1]
					if _, ok := wrapper[0].(*ast.InlineFragment); ok {
						wrapper = ast.SelectionSet{}
					}
				}
				log.Debug("found a thing with a selection. extracting to ", insertionPoint, ". Parent insertion", config.insertionPoint)
				subSelection, err := p.extractSelection(&extractSelectionConfig{
					stepCh:         config.stepCh,
					stepWg:         config.stepWg,
					step:           config.step,
					locations:      config.locations,
					parentLocation: config.parentLocation,
					plan:           config.plan,
					parentType:     coreFieldType(selection).Name(),
					selection:      selection.SelectionSet,
					insertionPoint: insertionPoint,
					wrapper:        wrapper,
				})
				if err != nil {
					return nil, err
				}
				log.Debug(fmt.Sprintf("final selection for %s.%s: %v\n", config.parentType, selection.Name, subSelection))
				selection.SelectionSet = subSelection
			} else {
				log.Debug("found a scalar")
			}
			for _, variable := range graphql.ExtractVariables(selection.Arguments) {
				config.step.Variables.Add(variable)
			}
			finalSelection = append(finalSelection, selection)
		case *ast.FragmentSpread:
			finalSelection = append(finalSelection, selection)
			defn := config.step.FragmentDefinitions.ForName(selection.Name)
			addDefn := false
			if defn == nil {
				addDefn = true
				defn = config.plan.FragmentDefinitions.ForName(selection.Name)
			}
			subSelection, err := p.extractSelection(&extractSelectionConfig{
				stepCh:         config.stepCh,
				stepWg:         config.stepWg,
				step:           config.step,
				locations:      config.locations,
				parentLocation: config.parentLocation,
				insertionPoint: config.insertionPoint,
				plan:           config.plan,
				parentType: defn.TypeCondition,
				selection:  defn.SelectionSet,
				wrapper: ast.SelectionSet{selection},
			})
			if err != nil {
				return nil, err
			}
			if addDefn {
				config.step.FragmentDefinitions = append(config.step.FragmentDefinitions,
					&ast.FragmentDefinition{
						Name:          selection.Name,
						TypeCondition: defn.TypeCondition,
						Directives:    defn.Directives,
					},
				)
			}
			config.step.FragmentDefinitions.ForName(selection.Name).SelectionSet = subSelection
		case *ast.InlineFragment:
			log.Debug("found an inline fragment. extracting to ", config.insertionPoint, ". Parent insertion", config.insertionPoint)
			newWrapper := make(ast.SelectionSet, len(config.wrapper))
			copy(newWrapper, config.wrapper)
			newWrapper = append(newWrapper, selection)
			subSelection, err := p.extractSelection(&extractSelectionConfig{
				stepCh:         config.stepCh,
				stepWg:         config.stepWg,
				step:           config.step,
				locations:      config.locations,
				parentLocation: config.parentLocation,
				plan:           config.plan,
				insertionPoint: config.insertionPoint,
				parentType: selection.TypeCondition,
				selection:  selection.SelectionSet,
				wrapper:    newWrapper,
			})
			if err != nil {
				return nil, err
			}
			selection.SelectionSet = subSelection
			finalSelection = append(finalSelection, selection)
		}
	}
	return finalSelection, nil
}
func (p *MinQueriesPlanner) wrapSelectionSet(config *extractSelectionConfig, locationFragments map[string]ast.FragmentDefinitionList, location string, selectionSet ast.SelectionSet) (ast.SelectionSet, error) {
	log.Debug("wrapping selection", config.wrapper)
	var selection ast.Selection
	var innerSelection ast.Selection
	for _, wrap := range config.wrapper {
		var newSelection ast.Selection
		switch wrap := wrap.(type) {
		case *ast.InlineFragment:
			newSelection = &ast.InlineFragment{
				TypeCondition: wrap.TypeCondition,
				Directives:    wrap.Directives,
			}
		case *ast.FragmentSpread:
			newSelection = &ast.FragmentSpread{
				Name:       wrap.Name,
				Directives: wrap.Directives,
			}
			locationFragments[location] = append(locationFragments[location], &ast.FragmentDefinition{
				Name:          wrap.Name,
				TypeCondition: config.parentType,
			})
		}
		if selection == nil {
			selection = newSelection
		} else if sel, ok := innerSelection.(*ast.InlineFragment); ok {
			sel.SelectionSet = ast.SelectionSet{newSelection}
		} else if sel, ok := innerSelection.(*ast.FragmentSpread); ok {
			defn := locationFragments[location].ForName(sel.Name)
			defn.SelectionSet = ast.SelectionSet{newSelection}
		}
		innerSelection = newSelection
	}
	if sel, ok := innerSelection.(*ast.InlineFragment); ok {
		sel.SelectionSet = selectionSet
	} else if sel, ok := innerSelection.(*ast.FragmentSpread); ok {
		defn := locationFragments[location].ForName(sel.Name)
		if defn == nil {
			return nil, errors.New("Could not find defn")
		}
		defn.SelectionSet = selectionSet
	}
	return ast.SelectionSet{selection}, nil
}