From 7a0c87b24103bb6204b660641846edc8c406cf45 Mon Sep 17 00:00:00 2001 From: Nick Randall Date: Tue, 21 Mar 2017 15:40:48 -0600 Subject: [PATCH] POC: allow users to get requested fields from context This POC could solve #17 --- graphql.go | 9 +++++++++ internal/exec/exec.go | 31 ++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/graphql.go b/graphql.go index e37c836f..0860670a 100644 --- a/graphql.go +++ b/graphql.go @@ -140,3 +140,12 @@ func (s *Schema) ToJSON() ([]byte, error) { } return json.MarshalIndent(result, "", "\t") } + +// GetFieldsFromContext can be used in resolvers to get the fields that were requested in the currenty query for the current type +func GetFieldsFromContext(ctx context.Context) exec.FieldList { + fields, ok := ctx.Value(exec.ResolverFields).(exec.FieldList) + if !ok { + panic("Could not get graphql fields from context") + } + return fields +} diff --git a/internal/exec/exec.go b/internal/exec/exec.go index 7fc23087..d283f6bd 100644 --- a/internal/exec/exec.go +++ b/internal/exec/exec.go @@ -25,6 +25,11 @@ const OpenTracingTagTrivial = "graphql.trivial" const OpenTracingTagArgsPrefix = "graphql.args." const OpenTracingTagError = "graphql.error" +type contextKey string +type FieldList map[string]FieldList + +const ResolverFields contextKey = "Resolver_Fields" + type Exec struct { queryExec iExec mutationExec iExec @@ -498,8 +503,9 @@ func (e *objectExec) execSelectionSet(ctx context.Context, r *request, selSet *q continue } + namedFields := resolveFieldNames(make(FieldList), sel.SelSet, r.doc) execSel(func() { - e.execField(ctx, r, sel, resolver, addResult) + e.execField(context.WithValue(ctx, ResolverFields, namedFields), r, sel, resolver, addResult) }) case *query.FragmentSpread: @@ -533,6 +539,29 @@ func (e *objectExec) execSelectionSet(ctx context.Context, r *request, selSet *q wg.Wait() } +func resolveFieldNames(namedFields FieldList, selSet *query.SelectionSet, doc *query.Document) FieldList { + if selSet != nil { + for _, selectionField := range selSet.Selections { + switch selectionField := selectionField.(type) { + case *query.Field: + namedFields[selectionField.Name] = make(FieldList) + resolveFieldNames(namedFields[selectionField.Name], selectionField.SelSet, doc) + case *query.FragmentSpread: + frag, ok := doc.Fragments[selectionField.Name] + if !ok { + panic(fmt.Errorf("fragment %q not found", selectionField.Name)) // TODO proper error handling + } + resolveFieldNames(namedFields, frag.SelSet, doc) + case *query.InlineFragment: + resolveFieldNames(namedFields, selectionField.SelSet, doc) + default: + panic("invalid type") + } + } + } + return namedFields +} + func (e *objectExec) execField(ctx context.Context, r *request, f *query.Field, resolver reflect.Value, addResult addResultFn) { switch f.Name { case "__typename":