Skip to content

Commit

Permalink
Merge pull request #1 from keithmattix/context-selected-fields
Browse files Browse the repository at this point in the history
Expose selected fields in context
  • Loading branch information
keithmattix authored Jan 2, 2021
2 parents f33e813 + 607cac1 commit e54e3e8
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 9 additions & 2 deletions graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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"
)

Expand Down Expand Up @@ -144,6 +145,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 {
doc, qErr := query.Parse(queryString)
Expand Down Expand Up @@ -184,11 +191,11 @@ func (s *Schema) exec(ctx context.Context, queryString string, operationName str

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

Expand Down
36 changes: 36 additions & 0 deletions internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,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{}
Expand Down Expand Up @@ -160,6 +167,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{}{}
Expand Down Expand Up @@ -195,6 +228,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 {
Expand Down
8 changes: 8 additions & 0 deletions selection/selected_field.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit e54e3e8

Please sign in to comment.