Skip to content

Commit 1e33c35

Browse files
committed
[RFC] Add explicit context arg to graphql execution
This **BREAKING** change introduces a new argument to the GraphQL execution API which is presented to resolution functions: `context`. Removed `Schema` from `ResolveParams`, already available in `ResolveParams.Info`. `IsTypeOf` and `ResolveType` params are now struct, similar to `FieldResolveFn`. `context` is now available for resolving types in `IsTypeOf` and `ResolveType`, similar to `FieldResolveFn`. Commit: d7cc6f9aed462588291bc821238650c98ad53580 [d7cc6f9] Parents: 576b6a15d1 Author: Lee Byron <[email protected]> Date: 23 March 2016 at 10:30:13 AM SGT Commit Date: 25 March 2016 at 8:20:10 AM SGT
1 parent 79f48da commit 1e33c35

11 files changed

+175
-107
lines changed

abstract_test.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForInterface(t *testing.T) {
4141
Interfaces: []*graphql.Interface{
4242
petType,
4343
},
44-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
45-
_, ok := value.(*testDog)
44+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
45+
_, ok := p.Value.(*testDog)
4646
return ok
4747
},
4848
Fields: graphql.Fields{
@@ -72,8 +72,8 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForInterface(t *testing.T) {
7272
Interfaces: []*graphql.Interface{
7373
petType,
7474
},
75-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
76-
_, ok := value.(*testCat)
75+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
76+
_, ok := p.Value.(*testCat)
7777
return ok
7878
},
7979
Fields: graphql.Fields{
@@ -162,8 +162,8 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForUnion(t *testing.T) {
162162

163163
dogType := graphql.NewObject(graphql.ObjectConfig{
164164
Name: "Dog",
165-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
166-
_, ok := value.(*testDog)
165+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
166+
_, ok := p.Value.(*testDog)
167167
return ok
168168
},
169169
Fields: graphql.Fields{
@@ -177,8 +177,8 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForUnion(t *testing.T) {
177177
})
178178
catType := graphql.NewObject(graphql.ObjectConfig{
179179
Name: "Cat",
180-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
181-
_, ok := value.(*testCat)
180+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
181+
_, ok := p.Value.(*testCat)
182182
return ok
183183
},
184184
Fields: graphql.Fields{
@@ -270,14 +270,14 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
270270
Type: graphql.String,
271271
},
272272
},
273-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
274-
if _, ok := value.(*testCat); ok {
273+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
274+
if _, ok := p.Value.(*testCat); ok {
275275
return catType
276276
}
277-
if _, ok := value.(*testDog); ok {
277+
if _, ok := p.Value.(*testDog); ok {
278278
return dogType
279279
}
280-
if _, ok := value.(*testHuman); ok {
280+
if _, ok := p.Value.(*testHuman); ok {
281281
return humanType
282282
}
283283
return nil
@@ -425,14 +425,14 @@ func TestResolveTypeOnUnionYieldsUsefulError(t *testing.T) {
425425
Types: []*graphql.Object{
426426
dogType, catType,
427427
},
428-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
429-
if _, ok := value.(*testCat); ok {
428+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
429+
if _, ok := p.Value.(*testCat); ok {
430430
return catType
431431
}
432-
if _, ok := value.(*testDog); ok {
432+
if _, ok := p.Value.(*testDog); ok {
433433
return dogType
434434
}
435-
if _, ok := value.(*testHuman); ok {
435+
if _, ok := p.Value.(*testHuman); ok {
436436
return humanType
437437
}
438438
return nil

definition.go

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,22 @@ type Object struct {
369369
err error
370370
}
371371

372-
type IsTypeOfFn func(value interface{}, info ResolveInfo) bool
372+
// IsTypeOfParams Params for IsTypeOfFn()
373+
type IsTypeOfParams struct {
374+
// Value that needs to be resolve.
375+
// Use this to decide which GraphQLObject this value maps to.
376+
Value interface{}
377+
378+
// Info is a collection of information about the current execution state.
379+
Info ResolveInfo
380+
381+
// Context argument is a context value that is provided to every resolve function within an execution.
382+
// It is commonly
383+
// used to represent an authenticated user, or request-specific caches.
384+
Context context.Context
385+
}
386+
387+
type IsTypeOfFn func(p IsTypeOfParams) bool
373388

374389
type InterfacesThunk func() []*Interface
375390

@@ -560,14 +575,19 @@ func defineFieldMap(ttype Named, fields Fields) (FieldDefinitionMap, error) {
560575
}
561576

562577
// ResolveParams Params for FieldResolveFn()
563-
// TODO: clean up GQLFRParams fields
564578
type ResolveParams struct {
579+
// Source is the source value
565580
Source interface{}
566-
Args map[string]interface{}
567-
Info ResolveInfo
568-
Schema Schema
569-
//This can be used to provide per-request state
570-
//from the application.
581+
582+
// Args is a map of arguments for current GraphQL request
583+
Args map[string]interface{}
584+
585+
// Info is a collection of information about the current execution state.
586+
Info ResolveInfo
587+
588+
// Context argument is a context value that is provided to every resolve function within an execution.
589+
// It is commonly
590+
// used to represent an authenticated user, or request-specific caches.
571591
Context context.Context
572592
}
573593

@@ -666,7 +686,7 @@ type Interface struct {
666686

667687
typeConfig InterfaceConfig
668688
fields FieldDefinitionMap
669-
err error
689+
err error
670690
}
671691
type InterfaceConfig struct {
672692
Name string `json:"name"`
@@ -675,7 +695,22 @@ type InterfaceConfig struct {
675695
Description string `json:"description"`
676696
}
677697

678-
type ResolveTypeFn func(value interface{}, info ResolveInfo) *Object
698+
// ResolveTypeParams Params for ResolveTypeFn()
699+
type ResolveTypeParams struct {
700+
// Value that needs to be resolve.
701+
// Use this to decide which GraphQLObject this value maps to.
702+
Value interface{}
703+
704+
// Info is a collection of information about the current execution state.
705+
Info ResolveInfo
706+
707+
// Context argument is a context value that is provided to every resolve function within an execution.
708+
// It is commonly
709+
// used to represent an authenticated user, or request-specific caches.
710+
Context context.Context
711+
}
712+
713+
type ResolveTypeFn func(p ResolveTypeParams) *Object
679714

680715
func NewInterface(config InterfaceConfig) *Interface {
681716
it := &Interface{}

definition_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ var blogSubscription = graphql.NewObject(graphql.ObjectConfig{
114114

115115
var objectType = graphql.NewObject(graphql.ObjectConfig{
116116
Name: "Object",
117-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
117+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
118118
return true
119119
},
120120
})
@@ -352,7 +352,7 @@ func TestTypeSystem_DefinitionExample_IncludesInterfacesSubTypesInTheTypeMap(t *
352352
},
353353
},
354354
Interfaces: []*graphql.Interface{someInterface},
355-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
355+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
356356
return true
357357
},
358358
})
@@ -396,7 +396,7 @@ func TestTypeSystem_DefinitionExample_IncludesInterfacesThunkSubtypesInTheTypeMa
396396
Interfaces: (graphql.InterfacesThunk)(func() []*graphql.Interface {
397397
return []*graphql.Interface{someInterface}
398398
}),
399-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
399+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
400400
return true
401401
},
402402
})

executor.go

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -526,8 +526,6 @@ func resolveField(eCtx *ExecutionContext, parentType *Object, source interface{}
526526
// TODO: find a way to memoize, in case this field is within a List type.
527527
args, _ := getArgumentValues(fieldDef.Args, fieldAST.Arguments, eCtx.VariableValues)
528528

529-
// The resolve function's optional third argument is a collection of
530-
// information about the current execution state.
531529
info := ResolveInfo{
532530
FieldName: fieldName,
533531
FieldASTs: fieldASTs,
@@ -545,7 +543,6 @@ func resolveField(eCtx *ExecutionContext, parentType *Object, source interface{}
545543
result, resolveFnError = resolveFn(ResolveParams{
546544
Source: source,
547545
Args: args,
548-
Schema: eCtx.Schema,
549546
Info: info,
550547
Context: eCtx.Context,
551548
})
@@ -664,12 +661,17 @@ func completeAbstractValue(eCtx *ExecutionContext, returnType Abstract, fieldAST
664661

665662
var runtimeType *Object
666663

664+
resolveTypeParams := ResolveTypeParams{
665+
Value: result,
666+
Info: info,
667+
Context: eCtx.Context,
668+
}
667669
if unionReturnType, ok := returnType.(*Union); ok && unionReturnType.ResolveType != nil {
668-
runtimeType = unionReturnType.ResolveType(result, info)
670+
runtimeType = unionReturnType.ResolveType(resolveTypeParams)
669671
} else if interfaceReturnType, ok := returnType.(*Interface); ok && interfaceReturnType.ResolveType != nil {
670-
runtimeType = interfaceReturnType.ResolveType(result, info)
672+
runtimeType = interfaceReturnType.ResolveType(resolveTypeParams)
671673
} else {
672-
runtimeType = defaultResolveTypeFn(result, info, returnType)
674+
runtimeType = defaultResolveTypeFn(resolveTypeParams, returnType)
673675
}
674676

675677
if runtimeType == nil {
@@ -692,10 +694,17 @@ func completeObjectValue(eCtx *ExecutionContext, returnType *Object, fieldASTs [
692694
// If there is an isTypeOf predicate function, call it with the
693695
// current result. If isTypeOf returns false, then raise an error rather
694696
// than continuing execution.
695-
if returnType.IsTypeOf != nil && !returnType.IsTypeOf(result, info) {
696-
panic(gqlerrors.NewFormattedError(
697-
fmt.Sprintf(`Expected value of type "%v" but got: %T.`, returnType, result),
698-
))
697+
if returnType.IsTypeOf != nil {
698+
p := IsTypeOfParams{
699+
Value: result,
700+
Info: info,
701+
Context: eCtx.Context,
702+
}
703+
if !returnType.IsTypeOf(p) {
704+
panic(gqlerrors.NewFormattedError(
705+
fmt.Sprintf(`Expected value of type "%v" but got: %T.`, returnType, result),
706+
))
707+
}
699708
}
700709

701710
// Collect sub-fields to execute to complete this value.
@@ -767,13 +776,18 @@ func completeListValue(eCtx *ExecutionContext, returnType *List, fieldASTs []*as
767776
// defaultResolveTypeFn If a resolveType function is not given, then a default resolve behavior is
768777
// used which tests each possible type for the abstract type by calling
769778
// isTypeOf for the object being coerced, returning the first type that matches.
770-
func defaultResolveTypeFn(value interface{}, info ResolveInfo, abstractType Abstract) *Object {
771-
possibleTypes := info.Schema.PossibleTypes(abstractType)
779+
func defaultResolveTypeFn(p ResolveTypeParams, abstractType Abstract) *Object {
780+
possibleTypes := p.Info.Schema.PossibleTypes(abstractType)
772781
for _, possibleType := range possibleTypes {
773782
if possibleType.IsTypeOf == nil {
774783
continue
775784
}
776-
if res := possibleType.IsTypeOf(value, info); res {
785+
isTypeOfParams := IsTypeOfParams{
786+
Value: p.Value,
787+
Info: p.Info,
788+
Context: p.Context,
789+
}
790+
if res := possibleType.IsTypeOf(isTypeOfParams); res {
777791
return possibleType
778792
}
779793
}

executor_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,8 +1261,8 @@ func TestFailsWhenAnIsTypeOfCheckIsNotMet(t *testing.T) {
12611261

12621262
specialType := graphql.NewObject(graphql.ObjectConfig{
12631263
Name: "SpecialType",
1264-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
1265-
if _, ok := value.(testSpecialType); ok {
1264+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
1265+
if _, ok := p.Value.(testSpecialType); ok {
12661266
return true
12671267
}
12681268
return false

introspection.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,9 +491,9 @@ func init() {
491491
Resolve: func(p ResolveParams) (interface{}, error) {
492492
switch ttype := p.Source.(type) {
493493
case *Interface:
494-
return p.Schema.PossibleTypes(ttype), nil
494+
return p.Info.Schema.PossibleTypes(ttype), nil
495495
case *Union:
496-
return p.Schema.PossibleTypes(ttype), nil
496+
return p.Info.Schema.PossibleTypes(ttype), nil
497497
}
498498
return nil, nil
499499
},

rules_overlapping_fields_can_be_merged_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ var schema graphql.Schema
291291
func init() {
292292
someBoxInterface = graphql.NewInterface(graphql.InterfaceConfig{
293293
Name: "SomeBox",
294-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
294+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
295295
return stringBoxObject
296296
},
297297
Fields: graphql.Fields{
@@ -330,7 +330,7 @@ func init() {
330330
})
331331
var nonNullStringBox1Interface = graphql.NewInterface(graphql.InterfaceConfig{
332332
Name: "NonNullStringBox1",
333-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
333+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
334334
return stringBoxObject
335335
},
336336
Fields: graphql.Fields{
@@ -355,7 +355,7 @@ func init() {
355355
})
356356
var nonNullStringBox2Interface = graphql.NewInterface(graphql.InterfaceConfig{
357357
Name: "NonNullStringBox2",
358-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
358+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
359359
return stringBoxObject
360360
},
361361
Fields: graphql.Fields{

testutil/rules_test_harness.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func init() {
7070
})
7171
var dogType = graphql.NewObject(graphql.ObjectConfig{
7272
Name: "Dog",
73-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
73+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
7474
return true
7575
},
7676
Fields: graphql.Fields{
@@ -146,7 +146,7 @@ func init() {
146146

147147
var catType = graphql.NewObject(graphql.ObjectConfig{
148148
Name: "Cat",
149-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
149+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
150150
return true
151151
},
152152
Fields: graphql.Fields{
@@ -182,7 +182,7 @@ func init() {
182182
dogType,
183183
catType,
184184
},
185-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
185+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
186186
// not used for validation
187187
return nil
188188
},
@@ -198,7 +198,7 @@ func init() {
198198

199199
var humanType = graphql.NewObject(graphql.ObjectConfig{
200200
Name: "Human",
201-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
201+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
202202
return true
203203
},
204204
Interfaces: []*graphql.Interface{
@@ -229,7 +229,7 @@ func init() {
229229

230230
var alienType = graphql.NewObject(graphql.ObjectConfig{
231231
Name: "Alien",
232-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
232+
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
233233
return true
234234
},
235235
Interfaces: []*graphql.Interface{
@@ -259,7 +259,7 @@ func init() {
259259
dogType,
260260
humanType,
261261
},
262-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
262+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
263263
// not used for validation
264264
return nil
265265
},
@@ -270,7 +270,7 @@ func init() {
270270
alienType,
271271
humanType,
272272
},
273-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
273+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
274274
// not used for validation
275275
return nil
276276
},

testutil/testutil.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ func init() {
133133
Description: "Which movies they appear in.",
134134
},
135135
},
136-
ResolveType: func(value interface{}, info graphql.ResolveInfo) *graphql.Object {
137-
if character, ok := value.(StarWarsChar); ok {
136+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
137+
if character, ok := p.Value.(StarWarsChar); ok {
138138
id, _ := strconv.Atoi(character.ID)
139139
human := GetHuman(id)
140140
if human.ID != "" {

0 commit comments

Comments
 (0)