diff --git a/introspection.go b/introspection.go index a0238d6..582a4d7 100644 --- a/introspection.go +++ b/introspection.go @@ -211,7 +211,7 @@ func IntrospectAPI(queryer Queryer, opts ...*IntrospectOptions) (*ast.Schema, er // if we are looking at an enum if len(remoteType.PossibleTypes) > 0 { - // build up an empty list of types + // build up an empty list of union types storedType.Types = []string{} // each union value needs to be added to the list @@ -230,7 +230,9 @@ func IntrospectAPI(queryer Queryer, opts ...*IntrospectOptions) (*ast.Schema, er if possibleType.Name == storedType.Name { continue } - storedType.Types = append(storedType.Types, possibleType.Name) + if storedType.Kind == ast.Union { + storedType.Types = append(storedType.Types, possibleType.Name) + } // add the possible type to the schema schema.AddPossibleType(remoteType.Name, possibleTypeDef) diff --git a/introspection_test.go b/introspection_test.go index fcefd2f..870ec83 100644 --- a/introspection_test.go +++ b/introspection_test.go @@ -1,6 +1,7 @@ package graphql import ( + "bytes" "context" "encoding/json" "errors" @@ -10,7 +11,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/vektah/gqlparser/v2" "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/formatter" ) func TestIntrospectAPI(t *testing.T) { @@ -1231,3 +1234,134 @@ func TestIntrospectAPI_retry(t *testing.T) { }, schema) }) } + +// Tests fix for https://github.com/nautilus/graphql/issues/35 +func TestIntrospectAPI_valid_interface_implementation(t *testing.T) { + t.Parallel() + schema, err := IntrospectAPI(&mockJSONQueryer{ + JSONResult: `{ + "__schema": { + "queryType": { + "name": "Query" + }, + "types": [ + { + "description": null, + "enumValues": [], + "fields": [ + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Find a Node for the given ID. Use fragments to select additional fields.", + "isDeprecated": false, + "name": "node", + "type": { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + } + } + ], + "inputFields": [], + "interfaces": [], + "kind": "OBJECT", + "name": "Query", + "possibleTypes": [] + }, + { + "description": "A resource.", + "enumValues": [], + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "The ID of this resource.", + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + } + ], + "inputFields": [], + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + } + ], + "kind": "OBJECT", + "name": "Resource", + "possibleTypes": [] + }, + { + "description": "Fetches an object given its ID.", + "enumValues": [], + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "The globally unique object ID.", + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + } + ], + "inputFields": [], + "interfaces": [], + "kind": "INTERFACE", + "name": "Node", + "possibleTypes": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Resource", + "ofType": null + } + ] + } + ] + } + }`, + }) + assert.NoError(t, err) + + var schemaBuffer bytes.Buffer + formatter.NewFormatter(&schemaBuffer).FormatSchema(schema) + _, err = gqlparser.LoadSchema(&ast.Source{Name: "input.graphql", Input: schemaBuffer.String()}) + assert.Nil(t, err, "Type should implement an interface without formatting/re-parsing failures.") +}