Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

granate (relay part) gets confused by similar types #21

Open
nicolaiskogheim opened this issue Aug 15, 2017 · 0 comments
Open

granate (relay part) gets confused by similar types #21

nicolaiskogheim opened this issue Aug 15, 2017 · 0 comments

Comments

@nicolaiskogheim
Copy link
Contributor

When graphql type A is a subset of B, then B can be "converted" to A, and look like an A in the graphql response to a query. This is because graphql types is represented as interfaces in go, and it is a feature that a type struct b {} can act as both an A and a B if it satisfies both those interfaces.

Let me explain.

Graphql schema:

type Admin extends Node {
	id: ID!
	name: String!
}

type User extends Node {
	id: ID!
	name: String!
	userSpecific: Bool
}

From this, granate generates interfaces:

// schema/adapters.go
type AdminInterface interface {
	IdField(context.Context) (*string, error)
	NameField(context.Context) (*string, error)
}

type UserInterface interface {
	IdField(context.Context) (*string, error)
	NameField(context.Context) (*string, error)
	UserSpecificField(context.Context) (*string, error)
}

The important thing here is that anything that satisfies UserInterface also satisfies the AdminInterface which tricks the following logic:

// schema/definitions.go
func init() {

	nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{
		IDFetcher: func(id string, info graphql.ResolveInfo, ctx context.Context) (interface{}, error) {
			// ...
		},
		TypeResolve: func(p graphql.ResolveTypeParams) *graphql.Object {
			switch p.Value.(type) {
			case AdminInterface: //< UserInterface satisfies this condition
				return adminDefinition

			case UserInterface:
				return userDefinition

			}

			return nil
		},
	})

This means that this GraphQL request

query get_user {
    node(id:"VXNlcjox") { # id: "User:1"
        id

        ... on User {
            name
            userSpecific
	}
	
	... on Admin {
	    name
	}

    }
}

returns this response

{
  "data": {
    "node": {
      "name": "Jon Jonsen",
      "id": "QWRtaW46MQ==" // id: "Admin:1"
      // no userSpecific field, because this is an Admin now
    }
  }
}

I circumvented the problem by adding a dummy field to the graphql types that fell victim to this .. well, it is a feature, but for our purposes it's a bug.

My new graphql schema:

type Admin extends Node {
	id: ID!
	name: String!
	# Do not consume this field /// Document that this field is not part of api
	isAdmin: Bool
}

type User extends Node {
	id: ID!
	name: String!
	userSpecific: Bool
	# Do not consume this field /// Also here
	isUser: Bool
}

Thanks to @noh4ck for helping me finding the cause and a workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant