Skip to content

Commit 79f48da

Browse files
committed
Add GraphQLSchema types field
This is a rebased and updated version of @tgriesser's #199. I further extended it to completely remove the side-effectful mutation of Interface types rather than just deferring that mutation to schema creation time. This introduces a *breaking* change to the Type System API. Now, any individual Interface type does not have the required information to answer `getPossibleTypes` or `isPossibleType` without knowing the other types in the Schema. These methods were moved to the Schema API, accepting the abstract type as the first parameter. This also introduces a *breaking* change to the type comparator functions: `isTypeSubTypeOf` and `doTypesOverlap` which now require a Schema as a first argument. Commit: 6a1f23e1f9c1e6bf4cea837bc9bb6eae0fb5c382 [6a1f23e] Parents: a781b556ee Author: Lee Byron <[email protected]> Date: 23 March 2016 at 1:17:43 PM SGT Commit Date: 25 March 2016 at 6:35:39 AM SGT
1 parent 4cafaaf commit 79f48da

11 files changed

+175
-176
lines changed

abstract_test.go

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForInterface(t *testing.T) {
3636
})
3737

3838
// ie declare that Dog belongs to Pet interface
39-
_ = graphql.NewObject(graphql.ObjectConfig{
39+
dogType := graphql.NewObject(graphql.ObjectConfig{
4040
Name: "Dog",
4141
Interfaces: []*graphql.Interface{
4242
petType,
@@ -67,7 +67,7 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForInterface(t *testing.T) {
6767
},
6868
})
6969
// ie declare that Cat belongs to Pet interface
70-
_ = graphql.NewObject(graphql.ObjectConfig{
70+
catType := graphql.NewObject(graphql.ObjectConfig{
7171
Name: "Cat",
7272
Interfaces: []*graphql.Interface{
7373
petType,
@@ -112,6 +112,7 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForInterface(t *testing.T) {
112112
},
113113
},
114114
}),
115+
Types: []graphql.Type{catType, dogType},
115116
})
116117
if err != nil {
117118
t.Fatalf("Error in schema %v", err.Error())
@@ -288,12 +289,6 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
288289
Fields: graphql.Fields{
289290
"name": &graphql.Field{
290291
Type: graphql.String,
291-
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
292-
if human, ok := p.Source.(*testHuman); ok {
293-
return human.Name, nil
294-
}
295-
return nil, nil
296-
},
297292
},
298293
},
299294
})
@@ -302,28 +297,12 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
302297
Interfaces: []*graphql.Interface{
303298
petType,
304299
},
305-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
306-
_, ok := value.(*testDog)
307-
return ok
308-
},
309300
Fields: graphql.Fields{
310301
"name": &graphql.Field{
311302
Type: graphql.String,
312-
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
313-
if dog, ok := p.Source.(*testDog); ok {
314-
return dog.Name, nil
315-
}
316-
return nil, nil
317-
},
318303
},
319304
"woofs": &graphql.Field{
320305
Type: graphql.Boolean,
321-
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
322-
if dog, ok := p.Source.(*testDog); ok {
323-
return dog.Woofs, nil
324-
}
325-
return nil, nil
326-
},
327306
},
328307
},
329308
})
@@ -332,28 +311,12 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
332311
Interfaces: []*graphql.Interface{
333312
petType,
334313
},
335-
IsTypeOf: func(value interface{}, info graphql.ResolveInfo) bool {
336-
_, ok := value.(*testCat)
337-
return ok
338-
},
339314
Fields: graphql.Fields{
340315
"name": &graphql.Field{
341316
Type: graphql.String,
342-
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
343-
if cat, ok := p.Source.(*testCat); ok {
344-
return cat.Name, nil
345-
}
346-
return nil, nil
347-
},
348317
},
349318
"meows": &graphql.Field{
350319
Type: graphql.Boolean,
351-
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
352-
if cat, ok := p.Source.(*testCat); ok {
353-
return cat.Meows, nil
354-
}
355-
return nil, nil
356-
},
357320
},
358321
},
359322
})
@@ -373,6 +336,7 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
373336
},
374337
},
375338
}),
339+
Types: []graphql.Type{catType, dogType},
376340
})
377341
if err != nil {
378342
t.Fatalf("Error in schema %v", err.Error())

definition.go

Lines changed: 13 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,21 @@ func IsCompositeType(ttype interface{}) bool {
143143
// Abstract interface for types that may describe the parent context of a selection set.
144144
type Abstract interface {
145145
Name() string
146-
PossibleTypes() []*Object
147-
IsPossibleType(ttype *Object) bool
148146
}
149147

150148
var _ Abstract = (*Interface)(nil)
151149
var _ Abstract = (*Union)(nil)
152150

151+
func IsAbstractType(ttype interface{}) bool {
152+
if _, ok := ttype.(*Interface); ok {
153+
return true
154+
}
155+
if _, ok := ttype.(*Union); ok {
156+
return true
157+
}
158+
return false
159+
}
160+
153161
// Nullable interface for types that can accept null as a value.
154162
type Nullable interface {
155163
}
@@ -393,21 +401,6 @@ func NewObject(config ObjectConfig) *Object {
393401
objectType.IsTypeOf = config.IsTypeOf
394402
objectType.typeConfig = config
395403

396-
/*
397-
addImplementationToInterfaces()
398-
Update the interfaces to know about this implementation.
399-
This is an rare and unfortunate use of mutation in the type definition
400-
implementations, but avoids an expensive "getPossibleTypes"
401-
implementation for Interface
402-
*/
403-
interfaces := objectType.Interfaces()
404-
if interfaces == nil {
405-
return objectType
406-
}
407-
for _, iface := range interfaces {
408-
iface.implementations = append(iface.implementations, objectType)
409-
}
410-
411404
return objectType
412405
}
413406
func (gt *Object) AddFieldConfig(fieldName string, fieldConfig *Field) {
@@ -671,11 +664,8 @@ type Interface struct {
671664
PrivateDescription string `json:"description"`
672665
ResolveType ResolveTypeFn
673666

674-
typeConfig InterfaceConfig
675-
fields FieldDefinitionMap
676-
implementations []*Object
677-
possibleTypes map[string]bool
678-
667+
typeConfig InterfaceConfig
668+
fields FieldDefinitionMap
679669
err error
680670
}
681671
type InterfaceConfig struct {
@@ -704,7 +694,6 @@ func NewInterface(config InterfaceConfig) *Interface {
704694
it.PrivateDescription = config.Description
705695
it.ResolveType = config.ResolveType
706696
it.typeConfig = config
707-
it.implementations = []*Object{}
708697

709698
return it
710699
}
@@ -737,28 +726,6 @@ func (it *Interface) Fields() (fields FieldDefinitionMap) {
737726
it.fields = fields
738727
return it.fields
739728
}
740-
func (it *Interface) PossibleTypes() []*Object {
741-
return it.implementations
742-
}
743-
func (it *Interface) IsPossibleType(ttype *Object) bool {
744-
if ttype == nil {
745-
return false
746-
}
747-
if len(it.possibleTypes) == 0 {
748-
possibleTypes := map[string]bool{}
749-
for _, possibleType := range it.PossibleTypes() {
750-
if possibleType == nil {
751-
continue
752-
}
753-
possibleTypes[possibleType.PrivateName] = true
754-
}
755-
it.possibleTypes = possibleTypes
756-
}
757-
if val, ok := it.possibleTypes[ttype.PrivateName]; ok {
758-
return val
759-
}
760-
return false
761-
}
762729
func (it *Interface) String() string {
763730
return it.PrivateName
764731
}
@@ -857,30 +824,9 @@ func NewUnion(config UnionConfig) *Union {
857824

858825
return objectType
859826
}
860-
func (ut *Union) PossibleTypes() []*Object {
827+
func (ut *Union) Types() []*Object {
861828
return ut.types
862829
}
863-
func (ut *Union) IsPossibleType(ttype *Object) bool {
864-
865-
if ttype == nil {
866-
return false
867-
}
868-
if len(ut.possibleTypes) == 0 {
869-
possibleTypes := map[string]bool{}
870-
for _, possibleType := range ut.PossibleTypes() {
871-
if possibleType == nil {
872-
continue
873-
}
874-
possibleTypes[possibleType.PrivateName] = true
875-
}
876-
ut.possibleTypes = possibleTypes
877-
}
878-
879-
if val, ok := ut.possibleTypes[ttype.PrivateName]; ok {
880-
return val
881-
}
882-
return false
883-
}
884830
func (ut *Union) String() string {
885831
return ut.PrivateName
886832
}

definition_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ func TestTypeSystem_DefinitionExample_IncludesInterfacesSubTypesInTheTypeMap(t *
365365
},
366366
},
367367
}),
368+
Types: []graphql.Type{someSubType},
368369
})
369370
if err != nil {
370371
t.Fatalf("unexpected error, got: %v", err)
@@ -408,6 +409,7 @@ func TestTypeSystem_DefinitionExample_IncludesInterfacesThunkSubtypesInTheTypeMa
408409
},
409410
},
410411
}),
412+
Types: []graphql.Type{someSubType},
411413
})
412414
if err != nil {
413415
t.Fatalf("unexpected error, got: %v", err)

executor.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,11 @@ func doesFragmentConditionMatch(eCtx *ExecutionContext, fragment ast.Node, ttype
425425
if conditionalType.Name() == ttype.Name() {
426426
return true
427427
}
428-
if conditionalType, ok := conditionalType.(Abstract); ok {
429-
return conditionalType.IsPossibleType(ttype)
428+
if conditionalType, ok := conditionalType.(*Interface); ok {
429+
return eCtx.Schema.IsPossibleType(conditionalType, ttype)
430+
}
431+
if conditionalType, ok := conditionalType.(*Union); ok {
432+
return eCtx.Schema.IsPossibleType(conditionalType, ttype)
430433
}
431434
case *ast.InlineFragment:
432435
typeConditionAST := fragment.TypeCondition
@@ -443,8 +446,11 @@ func doesFragmentConditionMatch(eCtx *ExecutionContext, fragment ast.Node, ttype
443446
if conditionalType.Name() == ttype.Name() {
444447
return true
445448
}
446-
if conditionalType, ok := conditionalType.(Abstract); ok {
447-
return conditionalType.IsPossibleType(ttype)
449+
if conditionalType, ok := conditionalType.(*Interface); ok {
450+
return eCtx.Schema.IsPossibleType(conditionalType, ttype)
451+
}
452+
if conditionalType, ok := conditionalType.(*Union); ok {
453+
return eCtx.Schema.IsPossibleType(conditionalType, ttype)
448454
}
449455
}
450456

@@ -539,6 +545,7 @@ func resolveField(eCtx *ExecutionContext, parentType *Object, source interface{}
539545
result, resolveFnError = resolveFn(ResolveParams{
540546
Source: source,
541547
Args: args,
548+
Schema: eCtx.Schema,
542549
Info: info,
543550
Context: eCtx.Context,
544551
})
@@ -629,7 +636,10 @@ func completeValue(eCtx *ExecutionContext, returnType Type, fieldASTs []*ast.Fie
629636

630637
// If field type is an abstract type, Interface or Union, determine the
631638
// runtime Object type and complete for that type.
632-
if returnType, ok := returnType.(Abstract); ok {
639+
if returnType, ok := returnType.(*Union); ok {
640+
return completeAbstractValue(eCtx, returnType, fieldASTs, info, result)
641+
}
642+
if returnType, ok := returnType.(*Interface); ok {
633643
return completeAbstractValue(eCtx, returnType, fieldASTs, info, result)
634644
}
635645

@@ -666,7 +676,7 @@ func completeAbstractValue(eCtx *ExecutionContext, returnType Abstract, fieldAST
666676
return nil
667677
}
668678

669-
if runtimeType != nil && !returnType.IsPossibleType(runtimeType) {
679+
if runtimeType != nil && !eCtx.Schema.IsPossibleType(returnType, runtimeType) {
670680
panic(gqlerrors.NewFormattedError(
671681
fmt.Sprintf(`Runtime Object type "%v" is not a possible type `+
672682
`for "%v".`, runtimeType, returnType),
@@ -758,7 +768,7 @@ func completeListValue(eCtx *ExecutionContext, returnType *List, fieldASTs []*as
758768
// used which tests each possible type for the abstract type by calling
759769
// isTypeOf for the object being coerced, returning the first type that matches.
760770
func defaultResolveTypeFn(value interface{}, info ResolveInfo, abstractType Abstract) *Object {
761-
possibleTypes := abstractType.PossibleTypes()
771+
possibleTypes := info.Schema.PossibleTypes(abstractType)
762772
for _, possibleType := range possibleTypes {
763773
if possibleType.IsTypeOf == nil {
764774
continue

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 ttype.PossibleTypes(), nil
494+
return p.Schema.PossibleTypes(ttype), nil
495495
case *Union:
496-
return ttype.PossibleTypes(), nil
496+
return p.Schema.PossibleTypes(ttype), nil
497497
}
498498
return nil, nil
499499
},

0 commit comments

Comments
 (0)