Skip to content

Commit c4b854a

Browse files
authored
IBX-8172: OpenAPI compatible location copy endpoint
#110
1 parent 49748a4 commit c4b854a

File tree

12 files changed

+168
-6
lines changed

12 files changed

+168
-6
lines changed

src/bundle/Resources/config/input_parsers.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,3 +890,11 @@ services:
890890
$validator: '@validator'
891891
tags:
892892
- { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.MoveUserGroupInput }
893+
894+
Ibexa\Rest\Server\Input\Parser\CopyLocationInput:
895+
parent: Ibexa\Rest\Server\Common\Parser
896+
arguments:
897+
$locationService: '@ibexa.api.service.location'
898+
$validator: '@validator'
899+
tags:
900+
- { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.CopyLocationInput }

src/bundle/Resources/config/routing.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,16 @@ ibexa.rest.languages.view:
432432

433433
# Locations
434434

435+
ibexa.rest.location.copy:
436+
path: /content/locations/{locationPath}
437+
controller: Ibexa\Rest\Server\Controller\Location::copy
438+
condition: 'ibexa_get_media_type(request) === "CopyLocationInput"'
439+
methods: [POST]
440+
options:
441+
options_route_suffix: 'CopyLocationInput'
442+
requirements:
443+
locationPath: "[0-9/]+"
444+
435445
ibexa.rest.trash_location:
436446
path: /content/locations/{locationPath}
437447
controller: Ibexa\Rest\Server\Controller\Trash::trashLocation

src/lib/Server/Controller/Location.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,34 @@ public function copySubtree($locationPath, Request $request)
212212
);
213213
}
214214

215+
/**
216+
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
217+
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
218+
*/
219+
public function copy(string $locationPath, Request $request): Values\ResourceCreated
220+
{
221+
$locationId = $this->extractLocationIdFromPath($locationPath);
222+
$location = $this->locationService->loadLocation($locationId);
223+
224+
$destinationLocation = $this->inputDispatcher->parse(
225+
new Message(
226+
['Content-Type' => $request->headers->get('Content-Type')],
227+
$request->getContent(),
228+
),
229+
);
230+
231+
$newLocation = $this->locationService->copySubtree($location, $destinationLocation);
232+
233+
return new Values\ResourceCreated(
234+
$this->router->generate(
235+
'ibexa.rest.load_location',
236+
[
237+
'locationPath' => trim($newLocation->pathString, '/'),
238+
],
239+
)
240+
);
241+
}
242+
215243
/**
216244
* Moves a subtree to a new location.
217245
*
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Ibexa\Rest\Server\Input\Parser;
10+
11+
use Ibexa\Rest\Server\Validation\Builder\Input\Parser\BaseInputParserValidatorBuilder;
12+
use Ibexa\Rest\Server\Validation\Builder\Input\Parser\CopyLocationInputValidatorBuilder;
13+
14+
final class CopyLocationInput extends AbstractDestinationLocationParser
15+
{
16+
protected const string PARSER = 'CopyLocationInput';
17+
18+
protected function getValidatorBuilder(): BaseInputParserValidatorBuilder
19+
{
20+
return new CopyLocationInputValidatorBuilder($this->validator);
21+
}
22+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Ibexa\Rest\Server\Validation\Builder\Input\Parser;
10+
11+
use Ibexa\Rest\Server\Input\Parser\CopyLocationInput;
12+
use Symfony\Component\Validator\Constraint;
13+
use Symfony\Component\Validator\Constraints as Assert;
14+
15+
final class CopyLocationInputValidatorBuilder extends BaseInputParserValidatorBuilder
16+
{
17+
protected function buildConstraint(): Constraint
18+
{
19+
return new Assert\Collection(
20+
[
21+
CopyLocationInput::DESTINATION_KEY => [
22+
new Assert\NotBlank(),
23+
new Assert\Type('string'),
24+
new Assert\Regex('/^(\/\d+)+$/'),
25+
],
26+
],
27+
);
28+
}
29+
}

tests/bundle/Functional/HttpOptionsTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public function providerForTestHttpOptions(): array
8080
['/content/objectstategroups/1/objectstates/1', ['GET', 'PATCH', 'DELETE']],
8181
['/content/objects/1/objectstates', ['GET', 'PATCH']],
8282
['/content/locations', ['GET']],
83+
['/content/locations/1/2', ['POST'], 'CopyLocationInput+json'],
8384
['/content/locations/1/2', ['POST'], 'MoveLocationInput+json'],
8485
['/content/locations/1/2', ['POST'], 'SwapLocationInput+json'],
8586
['/content/locations/1/2', ['GET', 'PATCH', 'DELETE', 'COPY', 'MOVE', 'SWAP']],

tests/bundle/Functional/LocationTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,23 @@ public function testSwap(string $locationHref): void
333333

334334
self::assertHttpResponseCodeEquals($response, 204);
335335
}
336+
337+
/**
338+
* @depends testMoveLocation
339+
*/
340+
public function testCopy(string $locationHref): void
341+
{
342+
$request = $this->createHttpRequest(
343+
'POST',
344+
$locationHref,
345+
'CopyLocationInput+json',
346+
'',
347+
json_encode(['CopyLocationInput' => ['destination' => '/1/2']]) ?: '',
348+
);
349+
350+
$response = $this->sendHttpRequest($request);
351+
352+
self::assertHttpResponseCodeEquals($response, 201);
353+
self::assertHttpResponseHasHeader($response, 'Location');
354+
}
336355
}

tests/bundle/Functional/SearchView/Criterion/IsContainerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function getCriteriaPayloads(): iterable
2828
'is container' => [
2929
'json',
3030
$this->buildJsonCriterionQuery('"IsContainerCriterion": true'),
31-
12,
31+
13,
3232
],
3333
'is not container' => [
3434
'json',

tests/bundle/Functional/SearchView/Criterion/IsUserBasedTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public function getCriteriaPayloads(): iterable
2323
'is not user based' => [
2424
'json',
2525
$this->buildJsonCriterionQuery('"IsUserBasedCriterion": false'),
26-
12,
26+
13,
2727
],
2828
];
2929
}

tests/bundle/Functional/SearchView/Criterion/ObjectStateIdentifierTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ public function getCriteriaPayloads(): iterable
1818
'identifier with target group' => [
1919
'json',
2020
$this->buildJsonCriterionQuery('"ObjectStateIdentifierCriterion": {"value": "not_locked", "target": "ez_lock"}'),
21-
14,
21+
15,
2222
],
2323
'identifier without target group' => [
2424
'json',
2525
$this->buildJsonCriterionQuery('"ObjectStateIdentifierCriterion": {"value": "not_locked", "target": null}'),
26-
14,
26+
15,
2727
],
2828
];
2929
}

0 commit comments

Comments
 (0)