Skip to content

Commit

Permalink
feat(doctrine): uid search filter support
Browse files Browse the repository at this point in the history
  • Loading branch information
bpolaszek authored and soyuka committed Apr 5, 2024
1 parent 678eb4f commit 5a29594
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 2 deletions.
39 changes: 39 additions & 0 deletions features/doctrine/search_filter.feature
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,45 @@ Feature: Search filter on collections
}
"""

@createSchema
Scenario: Get collection by ulid 01H2ZS93NBKJW5W4Y01S8TZ43M
Given there is a UidBasedId resource with id "01H2ZS93NBKJW5W4Y01S8TZ43M"
When I send a "GET" request to "/uid_based_ids?id=/uid_based_ids/01H2ZS93NBKJW5W4Y01S8TZ43M"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be valid according to this schema:
"""
{
"type": "object",
"properties": {
"@context": {"pattern": "^/contexts/UidBasedId"},
"@id": {"pattern": "^/uid_based_ids"},
"@type": {"pattern": "^hydra:Collection$"},
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@id": {
"oneOf": [
{"pattern": "^/uid_based_ids/01H2ZS93NBKJW5W4Y01S8TZ43M"}
]
}
}
}
},
"hydra:view": {
"type": "object",
"properties": {
"@id": {"pattern": "^/uid_based_ids\\?id=%2Fuid_based_ids%2F01H2ZS93NBKJW5W4Y01S8TZ43M"},
"@type": {"pattern": "^hydra:PartialCollectionView$"}
}
}
}
}
"""

@createSchema
Scenario: Get collection ordered by a non valid properties
When I send a "GET" request to "/dummies?unknown=0"
Expand Down
18 changes: 16 additions & 2 deletions src/Doctrine/Common/Filter/SearchFilterTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use ApiPlatform\Metadata\IriConverterInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Uid\AbstractUid;

/**
* Trait for filtering the collection by given properties.
Expand Down Expand Up @@ -127,19 +128,32 @@ protected function getIdFromValue(string $value): mixed
$item = $iriConverter->getResourceFromIri($value, ['fetch_data' => false]);

if (null === $this->identifiersExtractor) {
return $this->getPropertyAccessor()->getValue($item, 'id');
$id = $this->getPropertyAccessor()->getValue($item, 'id');

Check warning on line 131 in src/Doctrine/Common/Filter/SearchFilterTrait.php

View check run for this annotation

Codecov / codecov/patch

src/Doctrine/Common/Filter/SearchFilterTrait.php#L131

Added line #L131 was not covered by tests

return $this->toUuidValue($id);

Check warning on line 133 in src/Doctrine/Common/Filter/SearchFilterTrait.php

View check run for this annotation

Codecov / codecov/patch

src/Doctrine/Common/Filter/SearchFilterTrait.php#L133

Added line #L133 was not covered by tests
}

$identifiers = $this->identifiersExtractor->getIdentifiersFromItem($item);

return 1 === \count($identifiers) ? array_pop($identifiers) : $identifiers;
if (1 === \count($identifiers)) {
$id = array_pop($identifiers);

Check warning on line 139 in src/Doctrine/Common/Filter/SearchFilterTrait.php

View check run for this annotation

Codecov / codecov/patch

src/Doctrine/Common/Filter/SearchFilterTrait.php#L138-L139

Added lines #L138 - L139 were not covered by tests

return $this->toUuidValue($id);

Check warning on line 141 in src/Doctrine/Common/Filter/SearchFilterTrait.php

View check run for this annotation

Codecov / codecov/patch

src/Doctrine/Common/Filter/SearchFilterTrait.php#L141

Added line #L141 was not covered by tests
}

return array_map([$this, 'toUuidValue'], $identifiers);

Check warning on line 144 in src/Doctrine/Common/Filter/SearchFilterTrait.php

View check run for this annotation

Codecov / codecov/patch

src/Doctrine/Common/Filter/SearchFilterTrait.php#L144

Added line #L144 was not covered by tests
} catch (InvalidArgumentException) {
// Do nothing, return the raw value
}

return $value;
}

private function toUuidValue(mixed $id): mixed

Check warning on line 152 in src/Doctrine/Common/Filter/SearchFilterTrait.php

View check run for this annotation

Codecov / codecov/patch

src/Doctrine/Common/Filter/SearchFilterTrait.php#L152

Added line #L152 was not covered by tests
{
return ($id instanceof AbstractUid) ? $id->toBinary() : $id;

Check warning on line 154 in src/Doctrine/Common/Filter/SearchFilterTrait.php

View check run for this annotation

Codecov / codecov/patch

src/Doctrine/Common/Filter/SearchFilterTrait.php#L154

Added line #L154 was not covered by tests
}

/**
* Normalize the values array.
*/
Expand Down
14 changes: 14 additions & 0 deletions tests/Behat/DoctrineContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
use ApiPlatform\Tests\Fixtures\TestBundle\Document\SoMany as SoManyDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\Taxon as TaxonDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\ThirdLevel as ThirdLevelDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\UidBasedId as UidBasedIdDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\UrlEncodedId as UrlEncodedIdDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\User as UserDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\VideoGame as VideoGameDocument;
Expand Down Expand Up @@ -198,6 +199,7 @@
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Taxon;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\ThirdLevel;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\TreeDummy;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UidBasedId;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UrlEncodedId;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\User;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UuidIdentifierDummy;
Expand All @@ -215,6 +217,7 @@
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Uid\Ulid;
use Symfony\Component\Uid\Uuid as SymfonyUuid;

/**
Expand Down Expand Up @@ -1426,6 +1429,17 @@ public function thereIsAUrlEncodedIdResource(): void
$this->manager->clear();
}

/**
* @Given there is a UidBasedId resource with id :id
*/
public function thereIsAUidBasedIdResource(string $id): void
{
$uidBasedIdResource = ($this->isOrm() ? new UidBasedId(Ulid::fromBase32($id)) : new UidBasedIdDocument(Ulid::fromBase32($id)));
$this->manager->persist($uidBasedIdResource);
$this->manager->flush();
$this->manager->clear();
}

/**
* @Given there is a Program
*/
Expand Down
42 changes: 42 additions & 0 deletions tests/Fixtures/TestBundle/Document/UidBasedId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?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\Document;

use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Symfony\Component\Uid\Ulid;

/**
* @author Beno!t POLASZEK <bpolaszek@gmail.com>
*
* Resource with an Uid-based ID
*/
#[ApiResource(operations: [new Get(), new Post(), new GetCollection()])]
#[ApiFilter(SearchFilter::class, properties: ['id' => 'exact'])]
#[ODM\Document]
class UidBasedId
{
#[ODM\Id(strategy: 'none')]
public Ulid $id;

public function __construct(?Ulid $id)
{
$this->id = $id ?? new Ulid();
}
}
44 changes: 44 additions & 0 deletions tests/Fixtures/TestBundle/Entity/UidBasedId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?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\Entity;

use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Ulid;

/**
* @author Beno!t POLASZEK <bpolaszek@gmail.com>
*
* Resource with an Uid-based ID
*/
#[ApiResource(operations: [new Get(), new Post(), new GetCollection()])]
#[ApiFilter(SearchFilter::class, properties: ['id' => 'exact'])]
#[ORM\Entity]
class UidBasedId
{
#[ORM\Column(type: 'ulid')]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'NONE')]
public Ulid $id;

public function __construct(?Ulid $id)
{
$this->id = $id ?? new Ulid();
}
}

0 comments on commit 5a29594

Please sign in to comment.