Skip to content

GraphQL Query operation ignores custom ItemProvider #5805

Open
@bkosun

Description

@bkosun

API Platform version(s) affected: 3.1, 3.2

Description
GraphQL Query operation ignores custom ItemProvider if resource does not have Http Get operation

src/Entity/Account.php

namespace App\Entity;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GraphQl\Query;
use App\Repository\AccountRepository;
use App\State\AccountItemProvider;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Uid\Uuid;

#[ORM\Entity(repositoryClass: AccountRepository::class)]
#[ApiResource(
    operations: [],
    normalizationContext: ['groups' => ['account:read_normalization']],
)]
#[Get(provider:  AccountItemProvider::class)] //<--- !!!
#[Query(provider: AccountItemProvider::class)]
class Account
{
    #[ORM\Id]
    #[ORM\Column(type: UuidType::NAME, unique: true)]
    #[ApiProperty(identifier: true)]
    #[Groups(['account:read_normalization'])]
    private Uuid $id;

    #[ORM\Column(type: "json")]
    #[Groups(['account:read_normalization'])]
    private array $credentials = [];
}

src/State/AccountItemProvider.php

namespace App\State;

use ApiPlatform\Doctrine\Orm\State\ItemProvider;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

class AccountItemProvider implements ProviderInterface
{
    private ItemProvider $decoratedProvider;
    private AuthorizationCheckerInterface $authorizationChecker;

    public function __construct(ItemProvider $decoratedProvider, AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->decoratedProvider = $decoratedProvider;
        $this->authorizationChecker = $authorizationChecker;
    }

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object
    {
        $provider = $this->decoratedProvider->provide($operation, $uriVariables, $context);

        if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
            $provider->setCredentials([]);
        }

        return $provider;
    }
}

For example, I have an entry with ID 018a7bb5-4215-7363-82cf-b54c804c138e and a property credentials that contains the following data:

[
    {
        "key": "1234567890"
    }
]

GraphQL query:

query getAccount{
  account(id: "/accounts/018a7bb5-4215-7363-82cf-b54c804c138e"){
      id
      credentials
  }
}

The result will be as follows:

{
  "data": {
    "account": {
      "id": "/accounts/018a7bb5-4215-7363-82cf-b54c804c138e",
      "credentials": []
    }
  }
}

However, if you remove the HTTP Get operation attribute, the ApiPlatform\Doctrine\Orm\State\ItemProvider provider will be used instead of App\State\AccountProvider, resulting in the following result:

{
  "data": {
    "account": {
      "id": "/accounts/018a7bb5-4215-7363-82cf-b54c804c138e",
      "credentials": [
        {
          "key": "1234567890"
        }
      ]
    }
  }
}

How to reproduce
Implement your own ItemProvider and configure a GraphQL Query operation without an Http Get operation

Possible Solution
...

Additional Context

Perhaps I have to use QueryItemRessolver?

use ApiPlatform\GraphQl\Resolver\QueryItemResolverInterface;

class AccountItemResolver implements QueryItemResolverInterface
{
    private AuthorizationCheckerInterface $authorizationChecker;

    public function __construct(AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->authorizationChecker = $authorizationChecker;
    }

    public function __invoke($item, array $context): object
    {
        if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
            $item->setCredentials([]);
        }

        return $item;
    }
}
#[Query(resolver: AccountItemResolver::class)]

This works, but, note that there is no similar problem if I use the GraphQL QueryCollection operation with custom CollectionProvider without the HTTP GetCollection operation

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions