graphqlite icon indicating copy to clipboard operation
graphqlite copied to clipboard

GraphQLite is automatically making interface implementations when it shouldn't

Open MattBred opened this issue 2 years ago • 9 comments

Version: 5.0.1

According to https://graphqlite.thecodingmachine.io/docs/inheritance-interfaces, GraphQLite should only make an Impl type when it can't find another type that implements that interface:

If GraphQLite cannot find a proper GraphQL Object type implementing an interface, it will create an object type "on the fly".

However, it does it no matter what on my end:

/**
 * @GraphQL\Type
 */
interface MobileAppInterface
{
	/**
	 * @GraphQL\Field 
	 */
	public function getVersionCode(): ?int;
}

/**
 * @GraphQL\Type
 */
class AndroidApp implements MobileAppInterface
{
	public function getVersionCode(): ?int
	{
		// TODO: Implement getVersionCode() method.
	}
}

This generates the following schema:

interface MobileAppInterface {
  versionCode: Int
}

type MobileAppImpl implements MobileAppInterface {
  versionCode: Int
}

type AndroidApp implements MobileAppInterface {
  versionCode: Int
}

MattBred avatar Dec 13 '21 21:12 MattBred

@MattBred I've noticed the same thing. It's a bit harmless, as the interface isn't used, just generated in the schema. But, it is sloppy and confusing.

A PR on this would be awesome :)

oojacoboo avatar Dec 14 '21 12:12 oojacoboo

As you can see here: https://github.com/thecodingmachine/graphqlite/blob/3b97f9d020297658e32833e9b3d49e7b27c650dd/src/Mappers/RecursiveTypeMapper.php#L212-L223 Your type somehow is not yet part of the typeRegistry, might be a ordering issue in the recursive type mapper?

Lappihuan avatar Apr 22 '22 19:04 Lappihuan

It's probably related to the mapping cache in the RecursiveTypeMapper. Note that the Interface type names are different. The implementation around this is shaky. It mostly works, but an improved design is really needed.

oojacoboo avatar Apr 22 '22 20:04 oojacoboo

You are right, this seems quite naive. The interface type name is generated in this function called on line 214 above: https://github.com/thecodingmachine/graphqlite/blob/3b97f9d020297658e32833e9b3d49e7b27c650dd/src/NamingStrategy.php#L31-L34 It should actually check if there is a class in php implementing this interface. At least thats what the docs suggest it does but apparently not.

Lappihuan avatar Apr 22 '22 20:04 Lappihuan

There are two mappings as well, the actual FQCN and the GQL name. I don't find that these two mappings work well with each other either. IMO, we'd benefit from having a single mapping cache that's more robust.

oojacoboo avatar Apr 22 '22 21:04 oojacoboo

@oojacoboo what do you mean by two mappings?

Lappihuan avatar Apr 25 '22 20:04 Lappihuan

The RecursiveTypeMapper has:

  • mappedClasses
  • classToTypeCache
  • classToInputTypeCache
  • interfaceToClassNameMap

I found it frustrating to work with all of these different maps and think this responsibility would probably be better left to a mapping abstraction.

oojacoboo avatar Apr 25 '22 20:04 oojacoboo

I have ran into the same issue, this causes issues when generating types from schema, i have to handle the interface type even though i already know it would be one of N types

aszenz avatar Apr 24 '23 10:04 aszenz

A PR on this is certainly welcomed!

oojacoboo avatar Apr 24 '23 13:04 oojacoboo