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