Skip to content

ResourceMetadataCollection->getOperation: wrong cache hit with empty/null operationName #5204

@usu

Description

@usu

API Platform version(s) affected: 3.0.4

Description
Calling getOperation with null or empty string as $operationName can lead to wrong cache hit.

How to reproduce

  1. Specify a resource User with default CRUD operations
  2. Run the following test code
class GetOperationTest extends ApiTestCase {
    public function test1GetOperationForGet() {
        $resourceMetadataCollection = $this->getResourceMetadataFactory()->create(User::class);

        $getOperation = $resourceMetadataCollection->getOperation(null, false, true);
        $this->assertInstanceOf(Get::class, $getOperation);
    }

    public function test2GetOperationForGetCollection() {
        $resourceMetadataCollection = $this->getResourceMetadataFactory()->create(User::class);

        $getCollectionOperation = $resourceMetadataCollection->getOperation(null, true, true);
        $this->assertInstanceOf(GetCollection::class, $getCollectionOperation);
    }

    public function test3GetOperationForBoth() {
        $resourceMetadataCollection = $this->getResourceMetadataFactory()->create(User::class);

        $getOperation = $resourceMetadataCollection->getOperation(null, false, true);
        $this->assertInstanceOf(Get::class, $getOperation);

        $getCollectionOperation = $resourceMetadataCollection->getOperation(null, true, true);
        $this->assertInstanceOf(GetCollection::class, $getCollectionOperation);
    }
}
  1. Expected all tests to succeed. While test1 and test3 are running fine, test3 however fails with
    Failed asserting that ApiPlatform\Metadata\Get Object (...) is an instance of class "ApiPlatform\Metadata\GetCollection".

Possible Solution
The problem in test3 is that the first call of getOperation populates operationCache with an empty string as key:
return $this->operationCache[self::HTTP_PREFIX.$operationName] = $operation; where $operationName is empty string.

The second call to getOperation will immediately return the cache hit.

Possible solution:
Store the operation in operationCache with actual name of the operation, not with $operationName:
return $this->operationCache[self::HTTP_PREFIX.$operation->getName()] = $operation;

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions