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

[v3] Operation "_api_/xxx{._format}_get_collection\" not found for resource #5438

Closed
maks-rafalko opened this issue Mar 8, 2023 · 8 comments

Comments

@maks-rafalko
Copy link
Contributor

maks-rafalko commented Mar 8, 2023

API Platform version(s) affected: 3.1.3, 2.7.10 (3.1.2 works well). The bug was introduced here: 268c274#diff-d87efa25fbf52904daa06047002652f08645e3ad4a7f4985ffeec5a7c8e39c4fR118-R121

Description

After upgrading from 3.1.2 to 3.1.3 due to Security Vulnerability (GHSA-vr2x-7687-h6qv which is fixed in 5723d68), operations, declared on base class of Class Table Inheritance no longer work

How to reproduce
In order to reproduce the issue, we need just 2 classes from the Doctrine's example of Class Table Inheritance.

Base class Person (it is an API resource):

Person.php
<?php

declare(strict_types=1);

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity()]
#[ORM\InheritanceType('JOINED')]
#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
#[ORM\DiscriminatorMap(['employee' => Employee::class])]
#[ApiResource(
    operations: [
        new GetCollection(),
    ]
)]
abstract class Person 
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    private ?string $name = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}

and child class Employee which extends Person. This is also an API resource (it's important).

Employee.php
<?php

declare(strict_types=1);

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity()]
#[ApiResource(
    operations: [
        new Get(),
    ]
)]
class Employee extends Person
{
}

Since we have GetCollection on a base class, we can call /api/people endpoint (people is a plural of person) to get all children of Person class - in our case it's just an Employee.

To reproduce the bug, there should be 1 record in DB to run normalization process.

What is the cause introduced in 3.1.3 comparing o 3.1.2?

The cause are these lines:

if (isset($context['operation']) && $context['operation'] instanceof CollectionOperationInterface) {
unset($context['operation']);
unset($context['iri']);

If I remove them - everything works as expected, just like in 3.1.2.

Since $context['operation'] is removed in the lines above, later in the flow this if statements returns false and the code tries to find operation in ResourceMetadataCollection, which fails.

if (!$operation && $this->resourceMetadataCollectionFactory && $this->resourceClassResolver->isResourceClass($context['resource_class'])) {
$resourceClass = $this->resourceClassResolver->getResourceClass(null, $context['resource_class']); // fix for abstract classes and interfaces
$operation = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation($context['root_operation_name'] ?? $context['operation_name'] ?? null);
}

Warning
ResourceMetadataCollection for resource Employee (child) has no data for operation name "_api_/people{._format}_get_collection" of base class Person, this is why it fails.

Possible Solution

I have no experience to understand a good solution, but at least the cause is known. Should ResourceMetadataCollection know about base class operations?

Additional Context

image

@yassinehamouten
Copy link

Hello @maks-rafalko,
I'm facing the same issue.
Did you find something to solve this issue without modifying APIP core ?

@maks-rafalko
Copy link
Contributor Author

maks-rafalko commented Mar 8, 2023

Not really, we just downgraded back to 3.1.2 unless we have a proper fix (in 3.1.4 I guess).

But if you really need 3.1.3 - you can probably override this method (it's public) by your own normalizer and remove the lines I highlighted in my first message.

@yassinehamouten
Copy link

I think we will also downgrade to 3.1.2 :/

Thank you Maks

@CODEheures
Copy link

I have the same issue after upgrading from 2.7.9 to 2.7.10.

@soyuka
Copy link
Member

soyuka commented Mar 8, 2023

I'll check this asap, best is to provide a PR with a failing behat test case so that I can fix that in a jinx and release it by friday! Thanks everyone and sorry for breaking this!

@soyuka
Copy link
Member

soyuka commented Mar 9, 2023

Fix incoming, releasing tomorrow please @CODEheures upgrade to 3.x it's a pain to maintain 2.7 but I'll add the patch there also :D.

soyuka added a commit to soyuka/core that referenced this issue Mar 9, 2023
soyuka added a commit to soyuka/core that referenced this issue Mar 9, 2023
soyuka added a commit to soyuka/core that referenced this issue Mar 9, 2023
soyuka added a commit to soyuka/core that referenced this issue Mar 9, 2023
soyuka added a commit to soyuka/core that referenced this issue Mar 9, 2023
@soyuka
Copy link
Member

soyuka commented Mar 9, 2023

can you test this:
dd9499e#diff-d87efa25fbf52904daa06047002652f08645e3ad4a7f4985ffeec5a7c8e39c4f (obviously whenever you can no hurry on my end :)) ?

@maks-rafalko
Copy link
Contributor Author

can you test this: dd9499e#diff-d87efa25fbf52904daa06047002652f08645e3ad4a7f4985ffeec5a7c8e39c4f (obviously whenever you can no hurry on my end :)) ?

It works, our tests are green with this patch!

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

4 participants