Skip to content

Commit b9ca4c2

Browse files
authored
IBX-5119: Handled empty mainLocationId in Server\Output\ValueObjectVisitor\Location
For more details see https://issues.ibexa.co/browse/IBX-5119 and ezsystems/ezplatform-rest#100
1 parent 1ca02b0 commit b9ca4c2

File tree

2 files changed

+185
-14
lines changed

2 files changed

+185
-14
lines changed

src/lib/Server/Output/ValueObjectVisitor/Location.php

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
use eZ\Publish\API\Repository\Exceptions\UnauthorizedException;
1010
use eZ\Publish\API\Repository\LocationService;
1111
use eZ\Publish\API\Repository\ContentService;
12+
use eZ\Publish\API\Repository\Values\Content;
1213
use EzSystems\EzPlatformRest\Output\ValueObjectVisitor;
1314
use EzSystems\EzPlatformRest\Output\Generator;
1415
use EzSystems\EzPlatformRest\Output\Visitor;
1516
use EzSystems\EzPlatformRest\Server\Values\RestContent as RestContentValue;
16-
use eZ\Publish\API\Repository\Values\Content\Location as LocationValue;
1717

1818
/**
1919
* Location value object visitor.
@@ -26,8 +26,10 @@ class Location extends ValueObjectVisitor
2626
/** @var \eZ\Publish\API\Repository\ContentService */
2727
private $contentService;
2828

29-
public function __construct(LocationService $locationService, ContentService $contentService)
30-
{
29+
public function __construct(
30+
LocationService $locationService,
31+
ContentService $contentService
32+
) {
3133
$this->locationService = $locationService;
3234
$this->contentService = $contentService;
3335
}
@@ -48,8 +50,15 @@ public function visit(Visitor $visitor, Generator $generator, $location)
4850
$generator->endObjectElement('Location');
4951
}
5052

51-
protected function visitLocationAttributes(Visitor $visitor, Generator $generator, LocationValue $location)
52-
{
53+
/**
54+
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
55+
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
56+
*/
57+
protected function visitLocationAttributes(
58+
Visitor $visitor,
59+
Generator $generator,
60+
Content\Location $location
61+
) {
5362
$generator->startAttribute(
5463
'href',
5564
$this->router->generate(
@@ -153,15 +162,8 @@ protected function visitLocationAttributes(Visitor $visitor, Generator $generato
153162
$generator->endAttribute('href');
154163

155164
$content = $location->getContent();
156-
$contentInfo = $location->contentInfo;
157-
158-
try {
159-
$mainLocation = $contentInfo->mainLocationId === $location->id
160-
? $location
161-
: $this->locationService->loadLocation($contentInfo->mainLocationId);
162-
} catch (UnauthorizedException $e) {
163-
$mainLocation = null;
164-
}
165+
$contentInfo = $location->getContentInfo();
166+
$mainLocation = $this->resolveMainLocation($contentInfo, $location);
165167

166168
$visitor->visitValueObject(new RestContentValue(
167169
$contentInfo,
@@ -174,4 +176,27 @@ protected function visitLocationAttributes(Visitor $visitor, Generator $generato
174176

175177
$generator->endObjectElement('ContentInfo');
176178
}
179+
180+
/**
181+
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
182+
*/
183+
private function resolveMainLocation(
184+
Content\ContentInfo $contentInfo,
185+
Content\Location $location
186+
): ?Content\Location {
187+
$mainLocationId = $contentInfo->getMainLocationId();
188+
if ($mainLocationId === null) {
189+
return null;
190+
}
191+
192+
if ($mainLocationId === $location->id) {
193+
return $location;
194+
}
195+
196+
try {
197+
return $this->locationService->loadLocation($mainLocationId);
198+
} catch (UnauthorizedException $e) {
199+
return null;
200+
}
201+
}
177202
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
namespace Ibexa\Tests\Rest\Server\Output\ValueObjectVisitor;
8+
9+
use eZ\Publish\API\Repository\ContentService;
10+
use eZ\Publish\API\Repository\LocationService;
11+
use eZ\Publish\API\Repository\PermissionResolver;
12+
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
13+
use eZ\Publish\API\Repository\Values\Content\Location as ApiLocation;
14+
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
15+
use eZ\Publish\Core\Repository\Values\Content\Content;
16+
use eZ\Publish\Core\Repository\Values\Content\Location;
17+
use eZ\Publish\Core\Repository\Values\Content\VersionInfo;
18+
use eZ\Publish\Core\Repository\Values\ContentType\ContentType;
19+
use EzSystems\EzPlatformRest\Tests\Output\ValueObjectVisitorBaseTest;
20+
use EzSystems\EzPlatformRest\Server\Output\ValueObjectVisitor;
21+
use PHPUnit\Framework\MockObject\MockObject;
22+
23+
final class LocationTest extends ValueObjectVisitorBaseTest
24+
{
25+
private const MAIN_LOCATION_ID = 78;
26+
27+
private const UNAUTHORIZED_MAIN_LOCATION_ID = 111;
28+
29+
private const LOCATION_ID = 55;
30+
31+
/** @var \eZ\Publish\API\Repository\LocationService|MockObject */
32+
private $locationServiceMock;
33+
34+
/** @var ContentService|MockObject */
35+
private $contentServiceMock;
36+
37+
protected function setUp(): void
38+
{
39+
$this->permissionResolverMock = $this->createMock(PermissionResolver::class);
40+
$this->locationServiceMock = $this->createMock(LocationService::class);
41+
$this->contentServiceMock = $this->createMock(ContentService::class);
42+
43+
parent::setUp();
44+
}
45+
46+
/**
47+
* @dataProvider getDataForTestVisitLocationAttributesResolvesMainLocation
48+
*/
49+
public function testVisitLocationAttributesResolvesMainLocation(
50+
?int $mainLocationId,
51+
int $locationId
52+
): void {
53+
$visitor = $this->getVisitor();
54+
$generator = $this->getGenerator();
55+
56+
$generator->startDocument(null);
57+
58+
$contentId = 7;
59+
$versionInfo = new VersionInfo();
60+
61+
$location = new Location([
62+
'id' => $locationId,
63+
'path' => ['1', '25', '42'],
64+
'priority' => 1,
65+
'sortField' => ApiLocation::SORT_FIELD_DEPTH,
66+
'sortOrder' => ApiLocation::SORT_ORDER_ASC,
67+
'parentLocationId' => 42,
68+
'contentInfo' => new ContentInfo([
69+
'id' => $contentId,
70+
'mainLocationId' => $mainLocationId,
71+
]),
72+
'content' => new Content([
73+
'id' => $contentId,
74+
'contentType' => new ContentType(),
75+
'versionInfo' => $versionInfo,
76+
]),
77+
]);
78+
79+
$this->mockLoadLocation($location);
80+
81+
$this->contentServiceMock->expects(self::once())
82+
->method('loadRelations')
83+
->with($versionInfo)
84+
->willReturn([]);
85+
86+
$visitor->visit(
87+
$this->getVisitorMock(),
88+
$generator,
89+
$location
90+
);
91+
92+
$result = $generator->endDocument(null);
93+
94+
self::assertNotNull($result);
95+
96+
$this->assertXMLTag(
97+
[
98+
'tag' => 'Location',
99+
'content' => $location->id . 1 . 'false' . 'false',
100+
],
101+
$result,
102+
'Invalid <Location> element.',
103+
);
104+
}
105+
106+
private function mockLoadLocation(Location $location): void
107+
{
108+
$mainLocationId = $location->getContentInfo()->getMainLocationId();
109+
110+
switch ($mainLocationId) {
111+
case $location->id:
112+
case null:
113+
$this->locationServiceMock->expects(self::never())
114+
->method('loadLocation');
115+
break;
116+
case self::UNAUTHORIZED_MAIN_LOCATION_ID:
117+
$this->locationServiceMock->expects(self::once())
118+
->method('loadLocation')
119+
->with($mainLocationId)
120+
->willThrowException(new UnauthorizedException('', ''));
121+
break;
122+
default:
123+
$this->locationServiceMock->expects(self::once())
124+
->method('loadLocation')
125+
->with($mainLocationId)
126+
->willReturn(new Location(['id' => $mainLocationId]));
127+
break;
128+
}
129+
}
130+
131+
public function getDataForTestVisitLocationAttributesResolvesMainLocation(): iterable
132+
{
133+
yield 'same' => [self::MAIN_LOCATION_ID, self::MAIN_LOCATION_ID];
134+
135+
yield 'empty-main-location' => [null, self::LOCATION_ID];
136+
137+
yield 'different' => [999, self::LOCATION_ID];
138+
139+
yield 'unauthorized' => [self::UNAUTHORIZED_MAIN_LOCATION_ID, self::LOCATION_ID];
140+
}
141+
142+
protected function internalGetVisitor(): ValueObjectVisitor\Location
143+
{
144+
return new ValueObjectVisitor\Location($this->locationServiceMock, $this->contentServiceMock);
145+
}
146+
}

0 commit comments

Comments
 (0)