Skip to content

Commit

Permalink
IBX-6315: Added LocationArgumentResolver (#270)
Browse files Browse the repository at this point in the history
For more details see https://issues.ibexa.co/browse/IBX-6315 and #270

Key changes:

* Added ArgumentResolver for locationId passed as query parameter instead of route attribute

---------

Co-authored-by: Adam Wójs <[email protected]>
Co-authored-by: Andrew Longosz <[email protected]>
  • Loading branch information
3 people authored Oct 19, 2023
1 parent b40f6e7 commit 3a2be98
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 13 deletions.
14 changes: 2 additions & 12 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -15157,12 +15157,12 @@ parameters:

-
message: "#^Cannot access offset int\\|string on Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\.$#"
count: 3
count: 1
path: src/lib/Persistence/Legacy/Content/FieldHandler.php

-
message: "#^Cannot access offset string on Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\.$#"
count: 4
count: 2
path: src/lib/Persistence/Legacy/Content/FieldHandler.php

-
Expand Down Expand Up @@ -20400,11 +20400,6 @@ parameters:
count: 1
path: src/lib/Repository/Mapper/ContentDomainMapper.php

-
message: "#^Cannot access offset mixed on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\>\\.$#"
count: 1
path: src/lib/Repository/Mapper/ContentDomainMapper.php

-
message: "#^Cannot call method error\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#"
count: 1
Expand Down Expand Up @@ -58395,11 +58390,6 @@ parameters:
count: 3
path: tests/lib/Repository/Service/Mock/ContentTest.php

-
message: "#^Cannot access offset mixed on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#"
count: 1
path: tests/lib/Repository/Service/Mock/ContentTest.php

-
message: "#^Cannot access offset string on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#"
count: 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\Core\ControllerArgumentResolver;

use Ibexa\Contracts\Core\Exception\InvalidArgumentException;
use Ibexa\Contracts\Core\Repository\LocationService;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;

/**
* @internal
*/
final class LocationArgumentResolver implements ArgumentValueResolverInterface
{
private LocationService $locationService;

private const PARAMETER_LOCATION_ID = 'locationId';

public function __construct(LocationService $locationService)
{
$this->locationService = $locationService;
}

public function supports(Request $request, ArgumentMetadata $argument): bool
{
return
Location::class === $argument->getType()
&& !$request->attributes->has(self::PARAMETER_LOCATION_ID)
&& $request->query->has(self::PARAMETER_LOCATION_ID);
}

/**
* @return iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Location>
*
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
*/
public function resolve(Request $request, ArgumentMetadata $argument): iterable
{
$locationId = $request->query->get(self::PARAMETER_LOCATION_ID);
if (!is_numeric($locationId)) {
throw new InvalidArgumentException(
'locationId',
'Expected numeric type, ' . get_debug_type($locationId) . ' given.'
);
}

yield $this->locationService->loadLocation((int)$locationId);
}
}
8 changes: 7 additions & 1 deletion src/bundle/Core/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ services:
tags:
- { name: request.param_converter, priority: '%ibexa.param_converter.location.priority%', converter: ez_location_converter }

Ibexa\Bundle\Core\ControllerArgumentResolver\LocationArgumentResolver:
autowire: true
autoconfigure: true
tags:
- { name: controller.argument_value_resolver, priority: 50 }

Ibexa\Bundle\Core\EventListener\ExceptionListener:
class: Ibexa\Bundle\Core\EventListener\ExceptionListener
arguments: ["@translator"]
Expand Down Expand Up @@ -361,5 +367,5 @@ services:
$repositoryConfigurationProvider: '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider'
$defaultConnection: '%doctrine.default_connection%'
$entityManagers: '%doctrine.entity_managers%'

Ibexa\Bundle\Core\Translation\Policy\PolicyTranslationDefinitionProvider: ~
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\Bundle\Core\ControllerArgumentResolver;

use Generator;
use Ibexa\Bundle\Core\ControllerArgumentResolver\LocationArgumentResolver;
use Ibexa\Contracts\Core\Exception\InvalidArgumentException;
use Ibexa\Contracts\Core\Repository\LocationService;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;

/**
* @covers \Ibexa\Bundle\Core\Converter\LocationArgumentResolver
*/
final class LocationArgumentResolverTest extends TestCase
{
private const PARAMETER_LOCATION_ID = 'locationId';

private LocationArgumentResolver $locationArgumentResolver;

protected function setUp(): void
{
$locationService = $this->createMock(LocationService::class);
$this->locationArgumentResolver = new LocationArgumentResolver($locationService);
}

/**
* @dataProvider provideDataForTestSupports
*/
public function testSupports(
bool $expected,
Request $request,
ArgumentMetadata $argumentMetadata
): void {
self::assertSame(
$expected,
$this->locationArgumentResolver->supports(
$request,
$argumentMetadata
)
);
}

public function testResolveThrowsInvalidArgumentException(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Argument \'locationId\' is invalid: Expected numeric type, string given.');

$generator = $this->locationArgumentResolver->resolve(
new Request(
[
'locationId' => 'foo',
]
),
$this->createMock(ArgumentMetadata::class)
);

self::assertInstanceOf(Generator::class, $generator);

$generator->getReturn();
}

public function testResolve(): void
{
$resolvedArgumentsGenerator = $this->locationArgumentResolver->resolve(
$this->createRequest(true, false, 1),
$this->createMock(ArgumentMetadata::class)
);

self::assertInstanceOf(Generator::class, $resolvedArgumentsGenerator);
$resolvedArguments = iterator_to_array($resolvedArgumentsGenerator);

self::assertCount(1, $resolvedArguments);

$value = current($resolvedArguments);
self::assertInstanceOf(
Location::class,
$value
);
}

/**
* @return iterable<array{
* bool,
* \Symfony\Component\HttpFoundation\Request,
* \Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata
* }>
*/
public function provideDataForTestSupports(): iterable
{
$locationBasedArgumentMetadata = $this->createArgumentMetadata(Location::class);

yield 'Supported - locationId passed to request query' => [
true,
$this->createRequest(true, false, 1),
$locationBasedArgumentMetadata,
];

yield 'Not supported - type different than Ibexa\Contracts\Core\Repository\Values\Content\Location' => [
false,
$this->createRequest(true, false, 1),
$this->createArgumentMetadata('foo'),
];

yield 'Not supported - locationId passed to request attributes' => [
false,
$this->createRequest(false, true, 1),
$locationBasedArgumentMetadata,
];

yield 'Not supported - locationId passed to request attributes and query' => [
false,
$this->createRequest(true, true, 1),
$locationBasedArgumentMetadata,
];
}

private function createArgumentMetadata(string $type): ArgumentMetadata
{
$argumentMetadata = $this->createMock(ArgumentMetadata::class);
$argumentMetadata
->method('getType')
->willReturn($type);

return $argumentMetadata;
}

private function createRequest(
bool $addToQuery,
bool $addToAttributes,
?int $locationId = null
): Request {
$request = Request::create('/');

if ($addToQuery) {
$request->query->set(self::PARAMETER_LOCATION_ID, $locationId);
}

if ($addToAttributes) {
$request->attributes->set(self::PARAMETER_LOCATION_ID, $locationId);
}

return $request;
}
}

0 comments on commit 3a2be98

Please sign in to comment.