-
Notifications
You must be signed in to change notification settings - Fork 225
Union response type not working #850
I have a mutation
In the documentation, I only see The GraphQL request: mutation resetPassword { ResetPassword ( input: { token: "PasswordResetRequest-011eaf33-a93a-4f72-9509-e2b739741535" newPassword: "MyPassword01!+qw121124" repeatNewPassword: "MyPassword01!+qw12112" } ) { ... on ResetPasswordSuccessfulResponse { token newPassword } ... on ResetPasswordFailedResponse { errors } } } The mutation: ResetPassword: type: ResetPasswordResponse resolve: '@=mutation("reset_password", args["input"]["token"], args["input"]["newPassword"], args["input"]["repeatNewPassword"])' args: input: type: ResetPasswordInput! The resolver: class ResetPasswordMutation implements MutationInterface, AliasedInterface { public function __construct( private MessageBusInterface $messageBus, ) {} public function resetPassword( string $token, string $newPassword, string $repeatNewPassword ): ResetPasswordMutationResponse { try { $this->messageBus->dispatch( new ResetPassword( $token, $newPassword, $repeatNewPassword ) ); return new ResetPasswordMutationSuccessfulResponse($token, $newPassword); } catch (ValidationFailedException $exception) { $violations = []; /** @var ConstraintViolationInterface $violation */ foreach ($exception->getViolations() as $violation) { $violation->getMessage(); } return new ResetPasswordMutationFailedResponse($violations); } } public static function getAliases(): array { return [ 'resetPassword' => 'reset_password', ]; } } YAML types config: ResetPasswordResponse: type: union config: types: [ResetPasswordSuccessfulResponse, ResetPasswordFailedResponse] description: 'Reset password succeeded or failed.' ResetPasswordSuccessfulResponse: type: object config: fields: token: type: 'String!' newPassword: type: 'String!' ResetPasswordFailedResponse: type: object config: fields: errors: type: '[String]' |
All reactions
@WouterCypers you should use union types exactly like interfaces. You can check the documentation for interfaces for more details.
In your case you should define for your abstract type (ResetPasswordResponse) a type resolver (resolveType):
ResetPasswordResponse: type: union config: types: [ResetPasswordSuccessfulResponse, ResetPasswordFailedResponse] description: 'Reset password succeeded or failed.' resolveType: "@=mutation('map_response_type', value, typeResolver)"
And modify your ResetPasswordMutation class:
use GraphQL\Type\Definition\ObjectType; use Overblog\GraphQLBundle\Resolver\TypeResolver; use Overblog\GraphQLBundle\Resolver\UnresolvableException; class ResetPas...
Replies: 1 comment 2 replies
@WouterCypers you should use union types exactly like interfaces. You can check the documentation for interfaces for more details.
In your case you should define for your abstract type (ResetPasswordResponse) a type resolver (resolveType):
ResetPasswordResponse: type: union config: types: [ResetPasswordSuccessfulResponse, ResetPasswordFailedResponse] description: 'Reset password succeeded or failed.' resolveType: "@=mutation('map_response_type', value, typeResolver)"
And modify your ResetPasswordMutation class:
use GraphQL\Type\Definition\ObjectType; use Overblog\GraphQLBundle\Resolver\TypeResolver; use Overblog\GraphQLBundle\Resolver\UnresolvableException; class ResetPasswordMutation implements MutationInterface, AliasedInterface { public function __construct( private MessageBusInterface $messageBus, ) {} public function resetPassword( string $token, string $newPassword, string $repeatNewPassword ): ResetPasswordMutationResponse { try { $this->messageBus->dispatch( new ResetPassword( $token, $newPassword, $repeatNewPassword ) ); return new ResetPasswordMutationSuccessfulResponse($token, $newPassword); } catch (ValidationFailedException $exception) { $violations = []; /** @var ConstraintViolationInterface $violation */ foreach ($exception->getViolations() as $violation) { $violation->getMessage(); } return new ResetPasswordMutationFailedResponse($violations); } } public function mapResponseType($value, TypeResolver $typeResolver): ObjectType { if ($value instanceof ResetPasswordMutationSuccessfulResponse) { return $typeResolver->resolve('ResetPasswordSuccessfulResponse'); } if ($value instanceof ResetPasswordMutationFailedResponse) { return $typeResolver->resolve('ResetPasswordFailedResponse'); } throw new UnresolvableException("Couldn't resolve type for union 'ResetPasswordResponse'"); } public static function getAliases(): array { return [ 'resetPassword' => 'reset_password', 'mapResponseType' => 'map_response_type' ]; } }
All reactions
-
🎉 1 -
❤️ 1
@murtukov Thank you for your speedy reply!
I have two remarks and one question.
Remarks:
- In the YAML config you provided, it should say
@=mutationinstead of@=query(I had aUnknown resolver with alias [..]error) - In my case, the
TypeResolverinstance returns aGraphQL\Type\Definition\Typeinstead of aGraphQL\Type\Definition\ObjectType
Question:
Would it also be possible to use isTypeOf on ResetPasswordMutationFailedResponse and ResetPasswordMutationSuccesfulResponse instead of the mapResponseType on the mutation? If yes, do you have a reference on how to implement this?
All reactions
Would it also be possible to use
isTypeOf
Yes, it is possible, but not recommended for performance reasons. For more info: https://webonyx.github.io/graphql-php/type-definitions/interfaces/#interface-role-in-data-fetching
In the YAML config you provided, it should say @=mutation instead of @=query (I had a Unknown resolver with alias [..] error)
Yes, good catch, updated the answer. It should be @=mutation because your resolver is located in a class implementing MutationInterface.
In my case, the
TypeResolverinstance returns aGraphQL\Type\Definition\Typeinstead of aGraphQL\Type\Definition\ObjectType.
This is not possible, because your GraphQL types ResetPasswordSuccessfulResponse and ResetPasswordFailedResponse are of type object and thus it generates 2 PHP classes that extend ObjectType. And ObjectType extends Type.
So even though the return type-hint of the method TypeResolver::resolve is Type, in your case it actually returns an ObjectType instance.
P. S. The qualifiers mutation and query are likely to be changed in the future. You are currently using the 1.0 version, which is in development and there are many architectural changes planned. While it's ok to use 1.0 for learning I strongly discourage you to use it in production.
All reactions
-
👍 1