Skip to content

Commit b320b99

Browse files
mjmsqs
authored andcommitted
Filter interface fragment fields based on concrete type
1 parent e892575 commit b320b99

File tree

4 files changed

+79
-7
lines changed

4 files changed

+79
-7
lines changed

graphql_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,38 @@ func TestFragments(t *testing.T) {
11791179
}
11801180
`,
11811181
},
1182+
{
1183+
Schema: starwarsSchema,
1184+
Query: `
1185+
query {
1186+
human(id: "1000") {
1187+
id
1188+
mass
1189+
...characterInfo
1190+
}
1191+
}
1192+
1193+
fragment characterInfo on Character {
1194+
name
1195+
...on Droid {
1196+
primaryFunction
1197+
}
1198+
...on Human {
1199+
height
1200+
}
1201+
}
1202+
`,
1203+
ExpectedResult: `
1204+
{
1205+
"human": {
1206+
"id": "1000",
1207+
"mass": 77,
1208+
"name": "Luke Skywalker",
1209+
"height": 1.72
1210+
}
1211+
}
1212+
`,
1213+
},
11821214
})
11831215
}
11841216

internal/exec/resolvable/meta.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ func newMeta(s *types.Schema) *Meta {
2323
b := newBuilder(s)
2424

2525
metaSchema := s.Types["__Schema"].(*types.ObjectTypeDefinition)
26-
so, err := b.makeObjectExec(metaSchema.Name, metaSchema.Fields, nil, false, reflect.TypeOf(&introspection.Schema{}))
26+
so, err := b.makeObjectExec(metaSchema.Name, metaSchema.Fields, nil, nil, false, reflect.TypeOf(&introspection.Schema{}))
2727
if err != nil {
2828
panic(err)
2929
}
3030

3131
metaType := s.Types["__Type"].(*types.ObjectTypeDefinition)
32-
t, err := b.makeObjectExec(metaType.Name, metaType.Fields, nil, false, reflect.TypeOf(&introspection.Type{}))
32+
t, err := b.makeObjectExec(metaType.Name, metaType.Fields, nil, nil, false, reflect.TypeOf(&introspection.Type{}))
3333
if err != nil {
3434
panic(err)
3535
}
3636

3737
metaService := s.Types["_Service"].(*types.ObjectTypeDefinition)
38-
sv, err := b.makeObjectExec(metaService.Name, metaService.Fields, nil, false, reflect.TypeOf(&introspection.Service{}))
38+
sv, err := b.makeObjectExec(metaService.Name, metaService.Fields, nil, nil, false, reflect.TypeOf(&introspection.Service{}))
3939
if err != nil {
4040
panic(err)
4141
}

internal/exec/resolvable/resolvable.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Object struct {
2828
Name string
2929
Fields map[string]*Field
3030
TypeAssertions map[string]*TypeAssertion
31+
Interfaces map[string]struct{}
3132
}
3233

3334
type Field struct {
@@ -158,13 +159,13 @@ func (b *execBuilder) makeExec(t types.Type, resolverType reflect.Type) (Resolva
158159

159160
switch t := t.(type) {
160161
case *types.ObjectTypeDefinition:
161-
return b.makeObjectExec(t.Name, t.Fields, nil, nonNull, resolverType)
162+
return b.makeObjectExec(t.Name, t.Fields, nil, t.Interfaces, nonNull, resolverType)
162163

163164
case *types.InterfaceTypeDefinition:
164-
return b.makeObjectExec(t.Name, t.Fields, t.PossibleTypes, nonNull, resolverType)
165+
return b.makeObjectExec(t.Name, t.Fields, t.PossibleTypes, nil, nonNull, resolverType)
165166

166167
case *types.Union:
167-
return b.makeObjectExec(t.Name, nil, t.UnionMemberTypes, nonNull, resolverType)
168+
return b.makeObjectExec(t.Name, nil, t.UnionMemberTypes, nil, nonNull, resolverType)
168169
}
169170

170171
if !nonNull {
@@ -218,7 +219,7 @@ func makeScalarExec(t *types.ScalarTypeDefinition, resolverType reflect.Type) (R
218219
}
219220

220221
func (b *execBuilder) makeObjectExec(typeName string, fields types.FieldsDefinition, possibleTypes []*types.ObjectTypeDefinition,
221-
nonNull bool, resolverType reflect.Type) (*Object, error) {
222+
interfaces []*types.InterfaceTypeDefinition, nonNull bool, resolverType reflect.Type) (*Object, error) {
222223
if !nonNull {
223224
if resolverType.Kind() != reflect.Ptr && resolverType.Kind() != reflect.Interface {
224225
return nil, fmt.Errorf("%s is not a pointer or interface", resolverType)
@@ -297,11 +298,16 @@ func (b *execBuilder) makeObjectExec(typeName string, fields types.FieldsDefinit
297298
typeAssertions[impl.Name] = a
298299
}
299300
}
301+
ifaces := make(map[string]struct{})
302+
for _, iface := range interfaces {
303+
ifaces[iface.Name] = struct{}{}
304+
}
300305

301306
return &Object{
302307
Name: typeName,
303308
Fields: Fields,
304309
TypeAssertions: typeAssertions,
310+
Interfaces: ifaces,
305311
}, nil
306312
}
307313

internal/exec/selected/selected.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ func applySelectionSet(r *Request, s *resolvable.Schema, e *resolvable.Object, s
185185

186186
func applyFragment(r *Request, s *resolvable.Schema, e *resolvable.Object, frag *types.Fragment) []Selection {
187187
if frag.On.Name != e.Name {
188+
if _, ok := e.Interfaces[frag.On.Name]; ok {
189+
return applyInterfaceFragment(r, s, e, frag)
190+
}
191+
188192
t := r.Schema.Resolve(frag.On.Name)
189193
face, ok := t.(*types.InterfaceTypeDefinition)
190194
if !ok && frag.On.Name != "" {
@@ -221,6 +225,36 @@ func applyFragment(r *Request, s *resolvable.Schema, e *resolvable.Object, frag
221225
return applySelectionSet(r, s, e, frag.Selections)
222226
}
223227

228+
func applyInterfaceFragment(r *Request, s *resolvable.Schema, e *resolvable.Object, frag *types.Fragment) []Selection {
229+
// if the fragment is on an interface the object type implements, then filter out
230+
// selections for any fragments that don't match this type.
231+
var sels []types.Selection
232+
233+
for _, sel := range frag.Selections {
234+
switch sel := sel.(type) {
235+
case *types.Field:
236+
sels = append(sels, sel)
237+
case *types.InlineFragment:
238+
if sel.On.Name != e.Name {
239+
if _, ok := e.Interfaces[sel.On.Name]; !ok {
240+
continue
241+
}
242+
}
243+
sels = append(sels, sel)
244+
case *types.FragmentSpread:
245+
f := &r.Doc.Fragments.Get(sel.Name.Name).Fragment
246+
if f.On.Name != e.Name {
247+
if _, ok := e.Interfaces[f.On.Name]; !ok {
248+
continue
249+
}
250+
}
251+
sels = append(sels, sel)
252+
}
253+
}
254+
255+
return applySelectionSet(r, s, e, sels)
256+
}
257+
224258
func applyField(r *Request, s *resolvable.Schema, e resolvable.Resolvable, sels []types.Selection) []Selection {
225259
switch e := e.(type) {
226260
case *resolvable.Object:

0 commit comments

Comments
 (0)