Skip to content

Commit e54e3e8

Browse files
authored
Merge pull request #1 from keithmattix/context-selected-fields
Expose selected fields in context
2 parents f33e813 + 607cac1 commit e54e3e8

File tree

4 files changed

+54
-3
lines changed

4 files changed

+54
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ When using `UseFieldResolvers` schema option, a struct field will be used *only*
7676

7777
The method has up to two arguments:
7878

79-
- Optional `context.Context` argument.
79+
- Optional `context.Context` argument. If the graphql query had nested subfields, then use the `SelctedFieldsFromContext(ctx context.Context)` getter method
8080
- 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.
8181

8282
The method has up to two results:

graphql.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/graph-gophers/graphql-go/internal/validation"
1717
"github.com/graph-gophers/graphql-go/introspection"
1818
"github.com/graph-gophers/graphql-go/log"
19+
"github.com/graph-gophers/graphql-go/selection"
1920
"github.com/graph-gophers/graphql-go/trace"
2021
)
2122

@@ -144,6 +145,12 @@ type Response struct {
144145
Extensions map[string]interface{} `json:"extensions,omitempty"`
145146
}
146147

148+
// SelectedFieldsFromContext retrieves the selected fields passed via the context during the request
149+
// execution
150+
func SelectedFieldsFromContext(ctx context.Context) []*selection.SelectedField {
151+
return exec.SelectedFieldsFromContext(ctx)
152+
}
153+
147154
// Validate validates the given query with the schema.
148155
func (s *Schema) Validate(queryString string) []*errors.QueryError {
149156
doc, qErr := query.Parse(queryString)
@@ -184,11 +191,11 @@ func (s *Schema) exec(ctx context.Context, queryString string, operationName str
184191

185192
// Subscriptions are not valid in Exec. Use schema.Subscribe() instead.
186193
if op.Type == query.Subscription {
187-
return &Response{Errors: []*errors.QueryError{&errors.QueryError{ Message: "graphql-ws protocol header is missing" }}}
194+
return &Response{Errors: []*errors.QueryError{&errors.QueryError{Message: "graphql-ws protocol header is missing"}}}
188195
}
189196
if op.Type == query.Mutation {
190197
if _, ok := s.schema.EntryPoints["mutation"]; !ok {
191-
return &Response{Errors: []*errors.QueryError{{ Message: "no mutations are offered by the schema" }}}
198+
return &Response{Errors: []*errors.QueryError{{Message: "no mutations are offered by the schema"}}}
192199
}
193200
}
194201

internal/exec/exec.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,16 @@ import (
1515
"github.com/graph-gophers/graphql-go/internal/query"
1616
"github.com/graph-gophers/graphql-go/internal/schema"
1717
"github.com/graph-gophers/graphql-go/log"
18+
"github.com/graph-gophers/graphql-go/selection"
1819
"github.com/graph-gophers/graphql-go/trace"
1920
)
2021

22+
type ctxKey string
23+
24+
const (
25+
selectedFieldsKey ctxKey = "selectedFields"
26+
)
27+
2128
type Request struct {
2229
selected.Request
2330
Limiter chan struct{}
@@ -160,6 +167,32 @@ func typeOf(tf *selected.TypenameField, resolver reflect.Value) string {
160167
return ""
161168
}
162169

170+
func selectionToSelectedFields(internalSelection []selected.Selection) []*selection.SelectedField {
171+
fieldSelection := []*selection.SelectedField{}
172+
for _, element := range internalSelection {
173+
if field, ok := element.(*selected.SchemaField); ok {
174+
nestedSelections := selectionToSelectedFields(field.Sels)
175+
fieldSelection = append(fieldSelection, &selection.SelectedField{
176+
Name: field.Name,
177+
SelectedFields: nestedSelections,
178+
})
179+
}
180+
}
181+
return fieldSelection
182+
}
183+
184+
// SelectedFieldsFromContext exposes the fields selected in the GraphQL request
185+
// using the public-facing selection.SelectedField struct
186+
func SelectedFieldsFromContext(ctx context.Context) []*selection.SelectedField {
187+
selection := ctx.Value(selectedFieldsKey).([]selected.Selection)
188+
selectedFields := selectionToSelectedFields(selection)
189+
return selectedFields
190+
}
191+
192+
func contextWithSelectedFields(parentContext context.Context, selection []selected.Selection) context.Context {
193+
return context.WithValue(parentContext, selectedFieldsKey, selection)
194+
}
195+
163196
func execFieldSelection(ctx context.Context, r *Request, s *resolvable.Schema, f *fieldToExec, path *pathSegment, applyLimiter bool) {
164197
if applyLimiter {
165198
r.Limiter <- struct{}{}
@@ -195,6 +228,9 @@ func execFieldSelection(ctx context.Context, r *Request, s *resolvable.Schema, f
195228
if f.field.UseMethodResolver() {
196229
var in []reflect.Value
197230
if f.field.HasContext {
231+
if len(f.sels) != 0 {
232+
traceCtx = contextWithSelectedFields(traceCtx, f.sels)
233+
}
198234
in = append(in, reflect.ValueOf(traceCtx))
199235
}
200236
if f.field.ArgsPacker != nil {

selection/selected_field.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package selection
2+
3+
// SelectedField is the public representation of a field selection
4+
// during a graphql query
5+
type SelectedField struct {
6+
Name string
7+
SelectedFields []*SelectedField
8+
}

0 commit comments

Comments
 (0)