diff --git a/README.md b/README.md index 342a55a4..0df0668b 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ When using `UseFieldResolvers` schema option, a struct field will be used *only* The method has up to two arguments: -- Optional `context.Context` argument. +- Optional `context.Context` argument. If the graphql query had nested subfields, then use the `SelctedFieldsFromContext(ctx context.Context)` getter method - Mandatory `*struct { ... }` argument if the corresponding GraphQL field has arguments. The names of the struct fields have to be [exported](https://golang.org/ref/spec#Exported_identifiers) and have to match the names of the GraphQL arguments in a non-case-sensitive way. The method has up to two results: diff --git a/graphql.go b/graphql.go index 61d0a4d9..83e0551d 100644 --- a/graphql.go +++ b/graphql.go @@ -17,6 +17,7 @@ import ( "github.com/graph-gophers/graphql-go/internal/validation" "github.com/graph-gophers/graphql-go/introspection" "github.com/graph-gophers/graphql-go/log" + "github.com/graph-gophers/graphql-go/selection" "github.com/graph-gophers/graphql-go/trace" ) @@ -155,6 +156,12 @@ type Response struct { Extensions map[string]interface{} `json:"extensions,omitempty"` } +// SelectedFieldsFromContext retrieves the selected fields passed via the context during the request +// execution +func SelectedFieldsFromContext(ctx context.Context) []*selection.SelectedField { + return exec.SelectedFieldsFromContext(ctx) +} + // Validate validates the given query with the schema. func (s *Schema) Validate(queryString string) []*errors.QueryError { return s.ValidateWithVariables(queryString, nil) diff --git a/internal/exec/exec.go b/internal/exec/exec.go index 1e409bb8..04601b23 100644 --- a/internal/exec/exec.go +++ b/internal/exec/exec.go @@ -16,9 +16,16 @@ import ( "github.com/graph-gophers/graphql-go/internal/query" "github.com/graph-gophers/graphql-go/internal/schema" "github.com/graph-gophers/graphql-go/log" + "github.com/graph-gophers/graphql-go/selection" "github.com/graph-gophers/graphql-go/trace" ) +type ctxKey string + +const ( + selectedFieldsKey ctxKey = "selectedFields" +) + type Request struct { selected.Request Limiter chan struct{} @@ -162,6 +169,32 @@ func typeOf(tf *selected.TypenameField, resolver reflect.Value) string { return "" } +func selectionToSelectedFields(internalSelection []selected.Selection) []*selection.SelectedField { + fieldSelection := []*selection.SelectedField{} + for _, element := range internalSelection { + if field, ok := element.(*selected.SchemaField); ok { + nestedSelections := selectionToSelectedFields(field.Sels) + fieldSelection = append(fieldSelection, &selection.SelectedField{ + Name: field.Name, + SelectedFields: nestedSelections, + }) + } + } + return fieldSelection +} + +// SelectedFieldsFromContext exposes the fields selected in the GraphQL request +// using the public-facing selection.SelectedField struct +func SelectedFieldsFromContext(ctx context.Context) []*selection.SelectedField { + selection := ctx.Value(selectedFieldsKey).([]selected.Selection) + selectedFields := selectionToSelectedFields(selection) + return selectedFields +} + +func contextWithSelectedFields(parentContext context.Context, selection []selected.Selection) context.Context { + return context.WithValue(parentContext, selectedFieldsKey, selection) +} + func execFieldSelection(ctx context.Context, r *Request, s *resolvable.Schema, f *fieldToExec, path *pathSegment, applyLimiter bool) { if applyLimiter { r.Limiter <- struct{}{} @@ -197,6 +230,9 @@ func execFieldSelection(ctx context.Context, r *Request, s *resolvable.Schema, f if f.field.UseMethodResolver() { var in []reflect.Value if f.field.HasContext { + if len(f.sels) != 0 { + traceCtx = contextWithSelectedFields(traceCtx, f.sels) + } in = append(in, reflect.ValueOf(traceCtx)) } if f.field.ArgsPacker != nil { diff --git a/selection/selected_field.go b/selection/selected_field.go new file mode 100644 index 00000000..7637564b --- /dev/null +++ b/selection/selected_field.go @@ -0,0 +1,8 @@ +package selection + +// SelectedField is the public representation of a field selection +// during a graphql query +type SelectedField struct { + Name string + SelectedFields []*SelectedField +}