1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
package exec
import (
"context"
"fmt"
"github.com/graph-gophers/graphql-go/ast"
"github.com/graph-gophers/graphql-go/errors"
"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
"github.com/graph-gophers/graphql-go/internal/exec/selected"
)
func collectFieldsToValidate(sels []selected.Selection, s *resolvable.Schema, fields *[]*fieldToValidate, fieldByAlias map[string]*fieldToValidate) {
for _, sel := range sels {
switch sel := sel.(type) {
case *selected.SchemaField:
field, ok := fieldByAlias[sel.Alias]
if !ok { // validation already checked for conflict (TODO)
field = &fieldToValidate{field: sel}
fieldByAlias[sel.Alias] = field
*fields = append(*fields, field)
}
field.sels = append(field.sels, sel.Sels...)
case *selected.TypenameField:
// Ignore __typename, which has no directives
case *selected.TypeAssertion:
collectFieldsToValidate(sel.Sels, s, fields, fieldByAlias)
default:
panic(fmt.Sprintf("unexpected selection type %T", sel))
}
}
}
func validateFieldSelection(ctx context.Context, s *resolvable.Schema, f *fieldToValidate, path *pathSegment) []*errors.QueryError {
if f.field.FixedResult.IsValid() {
// Skip fixed result meta fields like __TypeName
return nil
}
return validateSelectionSet(ctx, f.sels, f.field.Type, path, s)
}
func validateSelectionSet(ctx context.Context, sels []selected.Selection, typ ast.Type, path *pathSegment, s *resolvable.Schema) []*errors.QueryError {
t, _ := unwrapNonNull(typ)
switch t.(type) {
case *ast.ObjectTypeDefinition, *ast.InterfaceTypeDefinition, *ast.Union:
return validateSelections(ctx, sels, path, s)
}
switch t := t.(type) {
case *ast.List:
return validateList(ctx, sels, t, path, s)
case *ast.ScalarTypeDefinition, *ast.EnumTypeDefinition:
// Field resolution already validated, don't need to check the value
default:
panic(fmt.Sprintf("unexpected type %T", t))
}
return nil
}
func validateSelections(ctx context.Context, sels []selected.Selection, path *pathSegment, s *resolvable.Schema) (errs []*errors.QueryError) {
var fields []*fieldToValidate
collectFieldsToValidate(sels, s, &fields, make(map[string]*fieldToValidate))
for _, f := range fields {
errs = append(errs, validateFieldSelection(ctx, s, f, &pathSegment{path, f.field.Alias})...)
}
return errs
}
func validateList(ctx context.Context, sels []selected.Selection, typ *ast.List, path *pathSegment, s *resolvable.Schema) []*errors.QueryError {
// For lists, we only need to apply validation once. Nothing has been evaluated, so we have no list, and need to use '0' as the path index
return validateSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, 0}, s)
}
|