Skip to content

Commit a300a61

Browse files
authored
Merge pull request graphql-go#622 from alex-lange/lange-add-union-thunk
Support thunks for Union.Types definitions
2 parents f02a1c9 + 0e40a4e commit a300a61

File tree

2 files changed

+99
-23
lines changed

2 files changed

+99
-23
lines changed

definition.go

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -796,15 +796,19 @@ type Union struct {
796796
PrivateDescription string `json:"description"`
797797
ResolveType ResolveTypeFn
798798

799-
typeConfig UnionConfig
800-
types []*Object
801-
possibleTypes map[string]bool
799+
typeConfig UnionConfig
800+
initalizedTypes bool
801+
types []*Object
802+
possibleTypes map[string]bool
802803

803804
err error
804805
}
806+
807+
type UnionTypesThunk func() []*Object
808+
805809
type UnionConfig struct {
806-
Name string `json:"name"`
807-
Types []*Object `json:"types"`
810+
Name string `json:"name"`
811+
Types interface{} `json:"types"`
808812
ResolveType ResolveTypeFn
809813
Description string `json:"description"`
810814
}
@@ -822,48 +826,80 @@ func NewUnion(config UnionConfig) *Union {
822826
objectType.PrivateDescription = config.Description
823827
objectType.ResolveType = config.ResolveType
824828

825-
if objectType.err = invariantf(
826-
len(config.Types) > 0,
827-
`Must provide Array of types for Union %v.`, config.Name,
828-
); objectType.err != nil {
829-
return objectType
829+
objectType.typeConfig = config
830+
831+
return objectType
832+
}
833+
834+
func (ut *Union) Types() []*Object {
835+
if ut.initalizedTypes {
836+
return ut.types
837+
}
838+
839+
var unionTypes []*Object
840+
switch utype := ut.typeConfig.Types.(type) {
841+
case UnionTypesThunk:
842+
unionTypes = utype()
843+
case []*Object:
844+
unionTypes = utype
845+
case nil:
846+
default:
847+
ut.err = fmt.Errorf("Unknown Union.Types type: %T", ut.typeConfig.Types)
848+
ut.initalizedTypes = true
849+
return nil
850+
}
851+
852+
ut.types, ut.err = defineUnionTypes(ut, unionTypes)
853+
ut.initalizedTypes = true
854+
return ut.types
855+
}
856+
857+
func defineUnionTypes(objectType *Union, unionTypes []*Object) ([]*Object, error) {
858+
definedUnionTypes := []*Object{}
859+
860+
if err := invariantf(
861+
len(unionTypes) > 0,
862+
`Must provide Array of types for Union %v.`, objectType.Name(),
863+
); err != nil {
864+
return definedUnionTypes, err
830865
}
831-
for _, ttype := range config.Types {
832-
if objectType.err = invariantf(
866+
867+
for _, ttype := range unionTypes {
868+
if err := invariantf(
833869
ttype != nil,
834870
`%v may only contain Object types, it cannot contain: %v.`, objectType, ttype,
835-
); objectType.err != nil {
836-
return objectType
871+
); err != nil {
872+
return definedUnionTypes, err
837873
}
838874
if objectType.ResolveType == nil {
839-
if objectType.err = invariantf(
875+
if err := invariantf(
840876
ttype.IsTypeOf != nil,
841877
`Union Type %v does not provide a "resolveType" function `+
842878
`and possible Type %v does not provide a "isTypeOf" `+
843879
`function. There is no way to resolve this possible type `+
844880
`during execution.`, objectType, ttype,
845-
); objectType.err != nil {
846-
return objectType
881+
); err != nil {
882+
return definedUnionTypes, err
847883
}
848884
}
885+
definedUnionTypes = append(definedUnionTypes, ttype)
849886
}
850-
objectType.types = config.Types
851-
objectType.typeConfig = config
852887

853-
return objectType
854-
}
855-
func (ut *Union) Types() []*Object {
856-
return ut.types
888+
return definedUnionTypes, nil
857889
}
890+
858891
func (ut *Union) String() string {
859892
return ut.PrivateName
860893
}
894+
861895
func (ut *Union) Name() string {
862896
return ut.PrivateName
863897
}
898+
864899
func (ut *Union) Description() string {
865900
return ut.PrivateDescription
866901
}
902+
867903
func (ut *Union) Error() error {
868904
return ut.err
869905
}

definition_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ func TestTypeSystem_DefinitionExample_ProhibitsNilTypeInUnions(t *testing.T) {
519519
Name: "BadUnion",
520520
Types: []*graphql.Object{nil},
521521
})
522+
ttype.Types()
522523
expected := `BadUnion may only contain Object types, it cannot contain: <nil>.`
523524
if ttype.Error().Error() != expected {
524525
t.Fatalf(`expected %v , got: %v`, expected, ttype.Error())
@@ -666,3 +667,42 @@ func TestTypeSystem_DefinitionExample_CanAddInputObjectField(t *testing.T) {
666667
t.Fatal("Unexpected result, inputObject should have a field named 'newValue'")
667668
}
668669
}
670+
671+
func TestTypeSystem_DefinitionExample_IncludesUnionTypesThunk(t *testing.T) {
672+
someObject := graphql.NewObject(graphql.ObjectConfig{
673+
Name: "SomeObject",
674+
Fields: graphql.Fields{
675+
"f": &graphql.Field{
676+
Type: graphql.Int,
677+
},
678+
},
679+
})
680+
681+
someOtherObject := graphql.NewObject(graphql.ObjectConfig{
682+
Name: "SomeOtherObject",
683+
Fields: graphql.Fields{
684+
"g": &graphql.Field{
685+
Type: graphql.Int,
686+
},
687+
},
688+
})
689+
690+
someUnion := graphql.NewUnion(graphql.UnionConfig{
691+
Name: "SomeUnion",
692+
Types: (graphql.UnionTypesThunk)(func() []*graphql.Object {
693+
return []*graphql.Object{someObject, someOtherObject}
694+
}),
695+
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
696+
return nil
697+
},
698+
})
699+
700+
unionTypes := someUnion.Types()
701+
702+
if someUnion.Error() != nil {
703+
t.Fatalf("unexpected error, got: %v", someUnion.Error().Error())
704+
}
705+
if len(unionTypes) != 2 {
706+
t.Fatalf("Unexpected result, someUnion should have two unionTypes, has %d", len(unionTypes))
707+
}
708+
}

0 commit comments

Comments
 (0)