@@ -47,7 +47,7 @@ type Field struct {
47
47
HasContext bool
48
48
HasError bool
49
49
ArgsPacker * packer.StructPacker
50
- DirectivesPackers map [ string ] * packer. StructPacker
50
+ DirectiveVisitors []directives. ResolverInterceptor
51
51
ValueExec Resolvable
52
52
TraceLabel string
53
53
}
@@ -56,6 +56,69 @@ func (f *Field) UseMethodResolver() bool {
56
56
return len (f .FieldIndex ) == 0
57
57
}
58
58
59
+ func (f * Field ) Resolve (ctx context.Context , resolver reflect.Value , args interface {}) (output interface {}, err error ) {
60
+ // Short circuit case to avoid wrapping functions
61
+ // TODO: confirm performance / memory difference, is it needed?
62
+ if len (f .DirectiveVisitors ) == 0 {
63
+ return f .resolveInternal (ctx , resolver , args )
64
+ }
65
+
66
+ currResolver := func (ctx context.Context , args interface {}) (output interface {}, err error ) {
67
+ return f .resolveInternal (ctx , resolver , args )
68
+ }
69
+
70
+ for _ , pd := range f .DirectiveVisitors {
71
+ pd := pd // Needed to avoid passing only the last directive, since we're closing over this loop var pointer
72
+ innerResolver := currResolver
73
+
74
+ currResolver = func (ctx context.Context , args interface {}) (output interface {}, err error ) {
75
+ return pd .Resolve (ctx , args , resolverFunc (innerResolver ))
76
+ }
77
+ }
78
+
79
+ return currResolver (ctx , args )
80
+ }
81
+
82
+ func (f * Field ) resolveInternal (ctx context.Context , resolver reflect.Value , args interface {}) (output interface {}, err error ) {
83
+ if ! f .UseMethodResolver () {
84
+ res := resolver
85
+
86
+ // TODO extract out unwrapping ptr logic to a common place
87
+ if res .Kind () == reflect .Ptr {
88
+ res = res .Elem ()
89
+ }
90
+
91
+ return res .FieldByIndex (f .FieldIndex ).Interface (), nil
92
+ }
93
+
94
+ var in []reflect.Value
95
+ var callOut []reflect.Value
96
+
97
+ if f .HasContext {
98
+ in = append (in , reflect .ValueOf (ctx ))
99
+ }
100
+
101
+ if f .ArgsPacker != nil {
102
+ in = append (in , reflect .ValueOf (args ))
103
+ }
104
+
105
+ callOut = resolver .Method (f .MethodIndex ).Call (in )
106
+ result := callOut [0 ]
107
+
108
+ if f .HasError && ! callOut [1 ].IsNil () {
109
+ resolverErr := callOut [1 ].Interface ().(error )
110
+ return result .Interface (), resolverErr
111
+ }
112
+
113
+ return result .Interface (), nil
114
+ }
115
+
116
+ type resolverFunc func (ctx context.Context , args interface {}) (output interface {}, err error )
117
+
118
+ func (f resolverFunc ) Resolve (ctx context.Context , args interface {}) (output interface {}, err error ) {
119
+ return f (ctx , args )
120
+ }
121
+
59
122
type TypeAssertion struct {
60
123
MethodIndex int
61
124
TypeExec Resolvable
@@ -81,7 +144,12 @@ func ApplyResolver(s *types.Schema, resolver interface{}, dirVisitors []directiv
81
144
return nil , err
82
145
}
83
146
84
- b := newBuilder (s , ds )
147
+ directivePackers , err := buildDirectivePackers (s , ds )
148
+ if err != nil {
149
+ return nil , err
150
+ }
151
+
152
+ b := newBuilder (s , directivePackers )
85
153
86
154
var query , mutation , subscription Resolvable
87
155
@@ -158,6 +226,44 @@ func ApplyResolver(s *types.Schema, resolver interface{}, dirVisitors []directiv
158
226
}, nil
159
227
}
160
228
229
+ func buildDirectivePackers (s * types.Schema , visitors map [string ]directives.Directive ) (map [string ]* packer.StructPacker , error ) {
230
+ // Directive packers need to use a dedicated builder which is ready ('finish()' called) while
231
+ // schema fields (and their argument packers) are still being built
232
+ builder := packer .NewBuilder ()
233
+
234
+ packers := map [string ]* packer.StructPacker {}
235
+ for _ , d := range s .Directives {
236
+ n := d .Name
237
+
238
+ v , ok := visitors [n ]
239
+ if ! ok {
240
+ // Directives which need visitors have already been checked
241
+ // Anything without a visitor now is an in-built directive without a packer.
242
+ continue
243
+ }
244
+
245
+ if _ , ok := v .(directives.ResolverInterceptor ); ! ok {
246
+ // Directive doesn't apply at field resolution time, skip it
247
+ continue
248
+ }
249
+
250
+ r := reflect .TypeOf (v )
251
+
252
+ p , err := builder .MakeStructPacker (d .Arguments , r )
253
+ if err != nil {
254
+ return nil , err
255
+ }
256
+
257
+ packers [n ] = p
258
+ }
259
+
260
+ if err := builder .Finish (); err != nil {
261
+ return nil , err
262
+ }
263
+
264
+ return packers , nil
265
+ }
266
+
161
267
func applyDirectives (s * types.Schema , visitors []directives.Directive ) (map [string ]directives.Directive , error ) {
162
268
byName := make (map [string ]directives.Directive , len (s .Directives ))
163
269
@@ -207,10 +313,10 @@ func applyDirectives(s *types.Schema, visitors []directives.Directive) (map[stri
207
313
}
208
314
209
315
type execBuilder struct {
210
- schema * types.Schema
211
- resMap map [typePair ]* resMapEntry
212
- directives map [string ]directives. Directive
213
- packerBuilder * packer.Builder
316
+ schema * types.Schema
317
+ resMap map [typePair ]* resMapEntry
318
+ directivePackers map [string ]* packer. StructPacker
319
+ packerBuilder * packer.Builder
214
320
}
215
321
216
322
type typePair struct {
@@ -223,12 +329,12 @@ type resMapEntry struct {
223
329
targets []* Resolvable
224
330
}
225
331
226
- func newBuilder (s * types.Schema , directives map [string ]directives. Directive ) * execBuilder {
332
+ func newBuilder (s * types.Schema , directivePackers map [string ]* packer. StructPacker ) * execBuilder {
227
333
return & execBuilder {
228
- schema : s ,
229
- resMap : make (map [typePair ]* resMapEntry ),
230
- directives : directives ,
231
- packerBuilder : packer .NewBuilder (),
334
+ schema : s ,
335
+ resMap : make (map [typePair ]* resMapEntry ),
336
+ directivePackers : directivePackers ,
337
+ packerBuilder : packer .NewBuilder (),
232
338
}
233
339
}
234
340
@@ -469,39 +575,9 @@ func (b *execBuilder) makeFieldExec(typeName string, f *types.FieldDefinition, m
469
575
}
470
576
}
471
577
472
- directivesPackers := map [string ]* packer.StructPacker {}
473
- for _ , d := range f .Directives {
474
- n := d .Name .Name
475
-
476
- // skip special directives without packers
477
- if n == "deprecated" {
478
- continue
479
- }
480
-
481
- v , ok := b .directives [n ]
482
- if ! ok {
483
- return nil , fmt .Errorf ("directive %q on field %q does not have a visitor registered with the schema" , n , f .Name )
484
- }
485
-
486
- if _ , ok := v .(directives.ResolverInterceptor ); ! ok {
487
- // Directive doesn't apply at field resolution time, skip it
488
- continue
489
- }
490
-
491
- r := reflect .TypeOf (v )
492
-
493
- // The directive definition is needed here in order to get the arguments definition list.
494
- // d.Arguments wouldn't work in this case because it does not contain args type information.
495
- dd , ok := b .schema .Directives [n ]
496
- if ! ok {
497
- return nil , fmt .Errorf ("directive definition %q is not defined in the schema" , n )
498
- }
499
- p , err := b .packerBuilder .MakeStructPacker (dd .Arguments , r )
500
- if err != nil {
501
- return nil , err
502
- }
503
-
504
- directivesPackers [n ] = p
578
+ ds , err := packDirectives (f .Directives , b .directivePackers )
579
+ if err != nil {
580
+ return nil , err
505
581
}
506
582
507
583
fe := & Field {
@@ -511,7 +587,7 @@ func (b *execBuilder) makeFieldExec(typeName string, f *types.FieldDefinition, m
511
587
FieldIndex : fieldIndex ,
512
588
HasContext : hasContext ,
513
589
ArgsPacker : argsPacker ,
514
- DirectivesPackers : directivesPackers ,
590
+ DirectiveVisitors : ds ,
515
591
HasError : hasError ,
516
592
TraceLabel : fmt .Sprintf ("GraphQL field: %s.%s" , typeName , f .Name ),
517
593
}
@@ -533,6 +609,32 @@ func (b *execBuilder) makeFieldExec(typeName string, f *types.FieldDefinition, m
533
609
return fe , nil
534
610
}
535
611
612
+ func packDirectives (ds types.DirectiveList , packers map [string ]* packer.StructPacker ) ([]directives.ResolverInterceptor , error ) {
613
+ packed := make ([]directives.ResolverInterceptor , 0 , len (ds ))
614
+ for _ , d := range ds {
615
+ dp , ok := packers [d .Name .Name ]
616
+ if ! ok {
617
+ continue // skip directives without packers
618
+ }
619
+
620
+ args := make (map [string ]interface {})
621
+ for _ , arg := range d .Arguments {
622
+ args [arg .Name .Name ] = arg .Value .Deserialize (nil )
623
+ }
624
+
625
+ p , err := dp .Pack (args )
626
+ if err != nil {
627
+ return nil , err
628
+ }
629
+
630
+ v := p .Interface ().(directives.ResolverInterceptor )
631
+
632
+ packed = append (packed , v )
633
+ }
634
+
635
+ return packed , nil
636
+ }
637
+
536
638
func findMethod (t reflect.Type , name string ) int {
537
639
for i := 0 ; i < t .NumMethod (); i ++ {
538
640
if strings .EqualFold (stripUnderscore (name ), stripUnderscore (t .Method (i ).Name )) {
0 commit comments