diff --git a/src/bundle/Resources/config/routing.yml b/src/bundle/Resources/config/routing.yml index 848fdba8..ba95b558 100644 --- a/src/bundle/Resources/config/routing.yml +++ b/src/bundle/Resources/config/routing.yml @@ -583,6 +583,14 @@ ezpublish_rest_loadContentTypeFieldDefinition: requirements: contentTypeId: \d+ fieldDefinitionId: \d+ + +ibexa.rest.load_content_type_field_definition_by_identifier: + path: /content/types/{contentTypeId}/fieldDefinition/{fieldDefinitionIdentifier} + controller: ezpublish_rest.controller.content_type:loadContentTypeFieldDefinitionByIdentifier + methods: [GET] + requirements: + contentTypeId: \d+ + fieldDefinitionIdentifier: \w+ ezpublish_rest_loadContentTypeDraft: path: /content/types/{contentTypeId}/draft diff --git a/src/lib/Server/Controller/ContentType.php b/src/lib/Server/Controller/ContentType.php index edccc0fc..791f4eb5 100644 --- a/src/lib/Server/Controller/ContentType.php +++ b/src/lib/Server/Controller/ContentType.php @@ -560,6 +560,38 @@ public function loadContentTypeFieldDefinition($contentTypeId, $fieldDefinitionI throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); } + /** + * @throws \EzSystems\EzPlatformRest\Exceptions\NotFoundException + * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + */ + public function loadContentTypeFieldDefinitionByIdentifier( + int $contentTypeId, + string $fieldDefinitionIdentifier, + Request $request + ): Values\RestFieldDefinition { + $contentType = $this->contentTypeService->loadContentType($contentTypeId); + $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); + $path = $this->router->generate( + 'ibexa.rest.load_content_type_field_definition_by_identifier', + [ + 'contentTypeId' => $contentType->id, + 'fieldDefinitionIdentifier' => $fieldDefinitionIdentifier, + ] + ); + + if ($fieldDefinition === null) { + throw new Exceptions\NotFoundException( + sprintf("Field definition not found: '%s'.", $request->getPathInfo()) + ); + } + + return new Values\RestFieldDefinition( + $contentType, + $fieldDefinition, + $path + ); + } + /** * Loads field definitions for a given content type draft. * diff --git a/src/lib/Server/Output/ValueObjectVisitor/RestFieldDefinition.php b/src/lib/Server/Output/ValueObjectVisitor/RestFieldDefinition.php index 3c24d580..cde94d8b 100644 --- a/src/lib/Server/Output/ValueObjectVisitor/RestFieldDefinition.php +++ b/src/lib/Server/Output/ValueObjectVisitor/RestFieldDefinition.php @@ -53,17 +53,19 @@ public function visit(Visitor $visitor, Generator $generator, $data) $visitor->setHeader('Accept-Patch', $generator->getMediaType('FieldDefinitionUpdate')); } - $generator->startAttribute( - 'href', - $this->router->generate( + if ($data->path === null) { + $href = $this->router->generate( "ezpublish_rest_loadContentType{$urlTypeSuffix}FieldDefinition", [ 'contentTypeId' => $contentType->id, 'fieldDefinitionId' => $fieldDefinition->id, ] - ) - ); - $generator->endAttribute('href'); + ); + } else { + $href = $data->path; + } + + $generator->attribute('href', $href); $generator->startValueElement('id', $fieldDefinition->id); $generator->endValueElement('id'); diff --git a/src/lib/Server/Values/RestFieldDefinition.php b/src/lib/Server/Values/RestFieldDefinition.php index 4e5203e2..a05a07ea 100644 --- a/src/lib/Server/Values/RestFieldDefinition.php +++ b/src/lib/Server/Values/RestFieldDefinition.php @@ -30,14 +30,16 @@ class RestFieldDefinition extends RestValue public $fieldDefinition; /** - * Construct. + * Path which is used to fetch the list of field definitions. * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition + * @var string|null */ - public function __construct(ContentType $contentType, FieldDefinition $fieldDefinition) + public $path; + + public function __construct(ContentType $contentType, FieldDefinition $fieldDefinition, ?string $path = null) { $this->contentType = $contentType; $this->fieldDefinition = $fieldDefinition; + $this->path = $path; } } diff --git a/tests/bundle/Functional/ContentTypeTest.php b/tests/bundle/Functional/ContentTypeTest.php index 62e69644..af16a7f3 100644 --- a/tests/bundle/Functional/ContentTypeTest.php +++ b/tests/bundle/Functional/ContentTypeTest.php @@ -446,6 +446,29 @@ public function testLoadContentTypeFieldDefinition(string $fieldDefinitionHref) self::assertHttpResponseCodeEquals($response, 200); } + /** + * Covers GET /content/types/{contentTypeId}/fieldDefinition/{fieldDefinitionIdentifier}. + * + * @depends testCreateContentType + * + * @throws \Psr\Http\Client\ClientException + */ + public function testLoadContentTypeFieldDefinitionByIdentifier(string $contentTypeHref): void + { + $url = sprintf('%s/fieldDefinition/title', $contentTypeHref); + + $response = $this->sendHttpRequest( + $this->createHttpRequest('GET', $url, '', 'FieldDefinition+json') + ); + + self::assertHttpResponseCodeEquals($response, 200); + + $data = json_decode($response->getBody(), true); + + self::assertEquals($url, $data['FieldDefinition']['_href']); + self::assertEquals('title', $data['FieldDefinition']['identifier']); + } + /** * @depends testAddContentTypeDraftFieldDefinition * Covers PATCH /content/types//fieldDefinitions/ diff --git a/tests/lib/Server/Output/ValueObjectVisitor/RestFieldDefinitionTest.php b/tests/lib/Server/Output/ValueObjectVisitor/RestFieldDefinitionTest.php index e25c3358..9ef2723e 100644 --- a/tests/lib/Server/Output/ValueObjectVisitor/RestFieldDefinitionTest.php +++ b/tests/lib/Server/Output/ValueObjectVisitor/RestFieldDefinitionTest.php @@ -21,17 +21,24 @@ public function setUp(): void $this->fieldTypeSerializerMock = $this->createMock(FieldTypeSerializer::class); } - /** - * @return \DOMDocument - */ - public function testVisitRestFieldDefinition() + public function testVisitRestFieldDefinition(): \DOMDocument + { + return $this->generateDomDocument(); + } + + public function testVisitRestFieldDefinitionWithPath(): \DOMDocument + { + return $this->generateDomDocument('/content/types/contentTypeId/fieldDefinition/title'); + } + + protected function generateDomDocument(?string $path = null): \DOMDocument { $visitor = $this->getVisitor(); $generator = $this->getGenerator(); $generator->startDocument(null); - $restFieldDefinition = $this->getBasicRestFieldDefinition(); + $restFieldDefinition = $this->getBasicRestFieldDefinition($path); $this->fieldTypeSerializerMock->expects($this->once()) ->method('serializeFieldDefaultValue') @@ -43,14 +50,16 @@ public function testVisitRestFieldDefinition() ) ); - $this->addRouteExpectation( - 'ezpublish_rest_loadContentTypeFieldDefinition', - [ - 'contentTypeId' => $restFieldDefinition->contentType->id, - 'fieldDefinitionId' => $restFieldDefinition->fieldDefinition->id, - ], - "/content/types/{$restFieldDefinition->contentType->id}/fieldDefinitions/{$restFieldDefinition->fieldDefinition->id}" - ); + if ($path === null) { + $this->addRouteExpectation( + 'ezpublish_rest_loadContentTypeFieldDefinition', + [ + 'contentTypeId' => $restFieldDefinition->contentType->id, + 'fieldDefinitionId' => $restFieldDefinition->fieldDefinition->id, + ], + "/content/types/{$restFieldDefinition->contentType->id}/fieldDefinitions/{$restFieldDefinition->fieldDefinition->id}" + ); + } $visitor->visit( $this->getVisitorMock(), @@ -68,7 +77,7 @@ public function testVisitRestFieldDefinition() return $dom; } - protected function getBasicRestFieldDefinition() + protected function getBasicRestFieldDefinition(?string $path = null): Server\Values\RestFieldDefinition { return new Server\Values\RestFieldDefinition( new Values\ContentType\ContentType( @@ -95,14 +104,40 @@ protected function getBasicRestFieldDefinition() 'names' => ['eng-US' => 'Sindelfingen'], 'descriptions' => ['eng-GB' => 'Bielefeld'], ] - ) + ), + $path + ); + } + + public function provideXpathAssertions(): array + { + $xpathAssertions = $this->getXpathAssertions(); + $xpathAssertions[] = '/FieldDefinition[@href="/content/types/contentTypeId/fieldDefinitions/fieldDefinitionId_23"]'; + + return $this->prepareXPathAssertions($xpathAssertions); + } + + public function provideXpathAssertionsPath(): array + { + $xpathAssertions = $this->getXpathAssertions(); + $xpathAssertions[] = '/FieldDefinition[@href="/content/types/contentTypeId/fieldDefinition/title"]'; + + return $this->prepareXPathAssertions($xpathAssertions); + } + + protected function prepareXPathAssertions(array $xpathAssertions): array + { + return array_map( + static function (string $xpath): array { + return [$xpath]; + }, + $xpathAssertions ); } - public function provideXpathAssertions() + protected function getXpathAssertions(): array { - $xpathAssertions = [ - '/FieldDefinition[@href="/content/types/contentTypeId/fieldDefinitions/fieldDefinitionId_23"]', + return [ '/FieldDefinition[@media-type="application/vnd.ez.api.FieldDefinition+xml"]', '/FieldDefinition/id[text()="fieldDefinitionId_23"]', '/FieldDefinition/identifier[text()="title"]', @@ -117,33 +152,30 @@ public function provideXpathAssertions() '/FieldDefinition/names/value[@languageCode="eng-US" and text()="Sindelfingen"]', '/FieldDefinition/descriptions/value[@languageCode="eng-GB" and text()="Bielefeld"]', ]; - - return array_map( - function ($xpath) { - return [$xpath]; - }, - $xpathAssertions - ); } /** - * @param string $xpath - * @param \DOMDocument $dom - * * @depends testVisitRestFieldDefinition * @dataProvider provideXpathAssertions */ - public function testGeneratedXml($xpath, \DOMDocument $dom) + public function testGeneratedXml(string $xpath, \DOMDocument $dom): void + { + $this->assertXPath($dom, $xpath); + } + + /** + * @depends testVisitRestFieldDefinitionWithPath + * @dataProvider provideXpathAssertionsPath + */ + public function testGeneratedXmlPath(string $xpath, \DOMDocument $dom): void { $this->assertXPath($dom, $xpath); } /** * Get the Content visitor. - * - * @return \EzSystems\EzPlatformRest\Server\Output\ValueObjectVisitor\RestFieldDefinition */ - protected function internalGetVisitor() + protected function internalGetVisitor(): ValueObjectVisitor\RestFieldDefinition { return new ValueObjectVisitor\RestFieldDefinition($this->fieldTypeSerializerMock); }