diff --git a/.gitignore b/.gitignore index e43b0f98..3d725761 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +.idea \ No newline at end of file diff --git a/abstract_test.go b/abstract_test.go index 2a356ea6..623b6cba 100644 --- a/abstract_test.go +++ b/abstract_test.go @@ -158,6 +158,147 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForInterface(t *testing.T) { } } + +func TestAppendTypeUsedToAddRuntimeCustomScalarTypeForInterface(t *testing.T) { + + petType := graphql.NewInterface(graphql.InterfaceConfig{ + Name: "Pet", + Fields: graphql.Fields{ + "name": &graphql.Field{ + Type: graphql.String, + }, + }, + }) + + // ie declare that Dog belongs to Pet interface + dogType := graphql.NewObject(graphql.ObjectConfig{ + Name: "Dog", + Interfaces: []*graphql.Interface{ + petType, + }, + IsTypeOf: func(p graphql.IsTypeOfParams) bool { + _, ok := p.Value.(*testDog) + return ok + }, + Fields: graphql.Fields{ + "name": &graphql.Field{ + Type: graphql.String, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + if dog, ok := p.Source.(*testDog); ok { + return dog.Name, nil + } + return nil, nil + }, + }, + "woofs": &graphql.Field{ + Type: graphql.Boolean, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + if dog, ok := p.Source.(*testDog); ok { + return dog.Woofs, nil + } + return nil, nil + }, + }, + }, + }) + // ie declare that Cat belongs to Pet interface + catType := graphql.NewObject(graphql.ObjectConfig{ + Name: "Cat", + Interfaces: []*graphql.Interface{ + petType, + }, + IsTypeOf: func(p graphql.IsTypeOfParams) bool { + _, ok := p.Value.(*testCat) + return ok + }, + Fields: graphql.Fields{ + "name": &graphql.Field{ + Type: graphql.String, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + if cat, ok := p.Source.(*testCat); ok { + return cat.Name, nil + } + return nil, nil + }, + }, + "meows": &graphql.Field{ + Type: graphql.Boolean, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + if cat, ok := p.Source.(*testCat); ok { + return cat.Meows, nil + } + return nil, nil + }, + }, + }, + }) + schema, err := graphql.NewSchema(graphql.SchemaConfig{ + Query: graphql.NewObject(graphql.ObjectConfig{ + Name: "Query", + Fields: graphql.Fields{ + "pets": &graphql.Field{ + Type: graphql.NewList(petType), + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + return []interface{}{ + &testDog{"Odie", true}, + &testCat{"Garfield", false}, + }, nil + }, + }, + }, + }), + + }) + if err != nil { + t.Fatalf("Error in schema %v", err.Error()) + } + + //Now add types catType and dogType at runtime. + schema.AppendType(catType) + schema.AppendType(dogType) + + query := `{ + pets { + name + ... on Dog { + woofs + } + ... on Cat { + meows + } + } + }` + + expected := &graphql.Result{ + Data: map[string]interface{}{ + "pets": []interface{}{ + map[string]interface{}{ + "name": "Odie", + "woofs": bool(true), + }, + map[string]interface{}{ + "name": "Garfield", + "meows": bool(false), + }, + }, + }, + Errors: nil, + } + + result := graphql.Do(graphql.Params{ + Schema: schema, + RequestString: query, + }) + if len(result.Errors) != 0 { + t.Fatalf("wrong result, unexpected errors: %v", result.Errors) + } + if !reflect.DeepEqual(expected, result) { + t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expected, result)) + } +} + + + func TestIsTypeOfUsedToResolveRuntimeTypeForUnion(t *testing.T) { dogType := graphql.NewObject(graphql.ObjectConfig{ diff --git a/schema.go b/schema.go index 420a8e60..b270be4e 100644 --- a/schema.go +++ b/schema.go @@ -144,6 +144,63 @@ func NewSchema(config SchemaConfig) (Schema, error) { return schema, nil } + + +//Added Check implementation of interfaces at runtime.. +//Add Implementations at Runtime.. +func (gq *Schema) AddImplementation() error{ + + // Keep track of all implementations by interface name. + if gq.implementations == nil { + gq.implementations = map[string][]*Object{} + } + for _, ttype := range gq.typeMap { + if ttype, ok := ttype.(*Object); ok { + for _, iface := range ttype.Interfaces() { + impls, ok := gq.implementations[iface.Name()] + if impls == nil || !ok { + impls = []*Object{} + } + impls = append(impls, ttype) + gq.implementations[iface.Name()] = impls + } + } + } + + // Enforce correct interface implementations + for _, ttype := range gq.typeMap { + if ttype, ok := ttype.(*Object); ok { + for _, iface := range ttype.Interfaces() { + err := assertObjectImplementsInterface(gq, ttype, iface) + if err != nil { + return err + } + } + } + } + + return nil +} + + +//Edited. To check add Types at RunTime.. +//Append Runtime schema to typeMap +func (gq *Schema)AppendType(objectType Type) error { + if objectType.Error() != nil { + return objectType.Error() + } + var err error + gq.typeMap, err = typeMapReducer(gq, gq.typeMap, objectType) + if err != nil { + return err + } + //Now Add interface implementation.. + return gq.AddImplementation() +} + + + + func (gq *Schema) QueryType() *Object { return gq.queryType }