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

fix(metadata): fix Operations sort #6206

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Metadata/ApiResource.php
Expand Up @@ -1018,6 +1018,7 @@ public function withOperations(Operations $operations): self
{
$self = clone $this;
$self->operations = $operations;
$self->operations->sort();

return $self;
}
Expand Down
6 changes: 6 additions & 0 deletions src/Metadata/Operation.php
Expand Up @@ -794,6 +794,12 @@ public function __construct(
protected ?bool $serialize = null,
protected ?bool $fetchPartial = null,
protected ?bool $forceEager = null,
/**
* The priority helps with the order of operations when looping over a resource's operations.
* It can be usefull when we loop over operations to find a matching IRI, although most of the use cases
* should be covered by the HttpOperation::itemUriTemplate or the ApiProperty::uriTemplate functionalities.
* Sort is ascendant: a lower priority comes first in the list.
*/
protected ?int $priority = null,
protected ?string $name = null,
protected $provider = null,
Expand Down
Expand Up @@ -119,9 +119,11 @@ private function buildResourceOperations(array $attributes, string $resourceClas
}

[$key, $operation] = $this->getOperationWithDefaults($resources[$index], $operationAttribute);
$operation = $operation->withPriority(++$operationPriority);
if (null === $operation->getPriority()) {
$operation = $operation->withPriority(++$operationPriority);
}
$operations = $resources[$index]->getOperations() ?? new Operations();
$resources[$index] = $resources[$index]->withOperations($operations->add($key, $operation)->sort());
$resources[$index] = $resources[$index]->withOperations($operations->add($key, $operation));
}

// Loop again and set default operations if none where found
Expand Down
Expand Up @@ -52,7 +52,7 @@ public function create(string $resourceClass): ResourceMetadataCollection
}
}

$resource = $resource->withOperations($operations->sort());
$resource = $resource->withOperations($operations);
$resourceMetadataCollection[$i] = $resource;
}

Expand Down
Expand Up @@ -56,7 +56,7 @@ public function create(string $resourceClass): ResourceMetadataCollection
$operations->remove($operationName)->add($newOperationName, $operation->withName($newOperationName));
}

$resourceMetadataCollection[$i] = $resource->withOperations($operations->sort());
$resourceMetadataCollection[$i] = $resource->withOperations($operations);
}

return $resourceMetadataCollection;
Expand Down
Expand Up @@ -88,7 +88,7 @@ public function create(string $resourceClass): ResourceMetadataCollection
$operations->add($operation->getName(), $operation);
}

$resource = $resource->withOperations($operations->sort());
$resource = $resource->withOperations($operations);
$resourceMetadataCollection[$i] = $resource;
}

Expand Down
36 changes: 36 additions & 0 deletions src/Metadata/Tests/OperationsTest.php
@@ -0,0 +1,36 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Metadata\Tests;

use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Operations;
use PHPUnit\Framework\TestCase;

final class OperationsTest extends TestCase
{
public function testOperationsHaveNameIfNotSet(): void
{
$operations = new Operations([new Get(name: 'a'), new Get(name: 'b')]);

foreach ($operations as $name => $operation) {
$this->assertEquals($name, $operation->getName());
}
}

public function testOperationAreSorted(): void
{
$operations = new Operations(['a' => new Get(priority: 0), 'b' => new Get(priority: -1)]);
$this->assertEquals(['b', 'a'], array_keys(iterator_to_array($operations)));
}
}
33 changes: 33 additions & 0 deletions tests/Fixtures/TestBundle/ApiResource/OperationPriorities.php
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource;

use ApiPlatform\Metadata\Get;

#[Get(name: 'a', priority: 1, uriTemplate: 'operation_priority/{id}', provider: [self::class, 'shouldNotBeCalled'])]
#[Get(name: 'b', priority: -1, uriTemplate: 'operation_priority/{id}', provider: [self::class, 'shouldBeCalled'])]
class OperationPriorities
{
public int $id = 1;

public static function shouldBeCalled(): self
{
return new self();
}

public static function shouldNotBeCalled(): self
{
throw new \Exception('fail');
}
}
10 changes: 10 additions & 0 deletions tests/Symfony/Bundle/Test/ApiTestCaseTest.php
Expand Up @@ -267,6 +267,16 @@ public function testFindIriBy(): void
$this->assertNull(self::findIriBy($resource, ['name' => 'not-exist']));
}

public function testGetPrioritizedOperation(): void
{
$r = self::createClient()->request('GET', '/operation_priority/1', [
'headers' => [
'accept' => 'application/ld+json',
],
]);
$this->assertResponseIsSuccessful();
}

/**
* @group mercure
*/
Expand Down