From 4a8a47ae27dc546d2f673a0243c981675dd3396b Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 25 Mar 2025 10:35:00 +0100 Subject: [PATCH 01/34] routes_rest.yaml: Fix greet controller --- code_samples/api/rest_api/config/routes_rest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/config/routes_rest.yaml b/code_samples/api/rest_api/config/routes_rest.yaml index 967abc848a..f6ae852e68 100644 --- a/code_samples/api/rest_api/config/routes_rest.yaml +++ b/code_samples/api/rest_api/config/routes_rest.yaml @@ -1,6 +1,6 @@ app.rest.greeting: path: '/greet' - controller: App\Rest\Controller\DefaultController::helloWorld + controller: App\Rest\Controller\DefaultController::greet methods: [GET,POST] defaults: csrf_protection: false From d6a064fedf569b83cadb6873745ce2b04f58d2a5 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:40:53 +0100 Subject: [PATCH 02/34] Start rewrite custom rest example --- .../src/Rest/Controller/DefaultController.php | 31 ++++++++---- .../src/Rest/InputParser/GreetingInput.php | 20 -------- .../Output/ValueObjectVisitorDispatcher.php | 49 ------------------- .../Rest/Serializer/GreetingNormalizer.php | 34 +++++++++++++ .../src/Rest/ValueObjectVisitor/Greeting.php | 21 -------- .../Rest/ValueObjectVisitor/RestLocation.php | 45 ----------------- 6 files changed, 55 insertions(+), 145 deletions(-) delete mode 100644 code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php delete mode 100644 code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php create mode 100644 code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php delete mode 100644 code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php delete mode 100644 code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index 0cfa960a63..52d5c70180 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -2,24 +2,35 @@ namespace App\Rest\Controller; +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; use App\Rest\Values\Greeting; -use Ibexa\Rest\Message; use Ibexa\Rest\Server\Controller; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +#[Get( + uriTemplate: '/greet', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'TODO: Greet', + description: 'TODO', + tags: [ + 'User', + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'TODO', + ], + ], + ), +)] class DefaultController extends Controller { public function greet(Request $request): Greeting { - if ('POST' === $request->getMethod()) { - return $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - } - return new Greeting(); + //return new Response('TEST'); } } diff --git a/code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php b/code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php deleted file mode 100644 index b3936d50e6..0000000000 --- a/code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php +++ /dev/null @@ -1,20 +0,0 @@ -visitors = []; - foreach ($visitors as $type => $visitor) { - $this->visitors[$type] = $visitor; - } - $this->valueObjectVisitorDispatcher = $valueObjectVisitorDispatcher; - } - - public function setOutputVisitor(Visitor $outputVisitor): void - { - $this->outputVisitor = $outputVisitor; - $this->valueObjectVisitorDispatcher->setOutputVisitor($outputVisitor); - } - - public function setOutputGenerator(Generator $outputGenerator): void - { - $this->outputGenerator = $outputGenerator; - $this->valueObjectVisitorDispatcher->setOutputGenerator($outputGenerator); - } - - public function visit($data) - { - $className = get_class($data); - if (isset($this->visitors[$className])) { - return $this->visitors[$className]->visit($this->outputVisitor, $this->outputGenerator, $data); - } - - return $this->valueObjectVisitorDispatcher->visit($data); - } -} diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php new file mode 100644 index 0000000000..d85caf7460 --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -0,0 +1,34 @@ +normalizer->normalize(['greeting' => [ + 'salutation' => $object->salutation, + 'recipient' => $object->recipient, + 'sentence' => "{$object->salutation} {$object->recipient}", + ]], $format, $context); + //var_dump($object);die('TODO: Implement normalize() method.'); + } +} diff --git a/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php b/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php deleted file mode 100644 index f909993490..0000000000 --- a/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php +++ /dev/null @@ -1,21 +0,0 @@ -setHeader('Content-Type', $generator->getMediaType('Greeting')); - $generator->startObjectElement('Greeting'); - $generator->attribute('href', $this->router->generate('app.rest.greeting')); - $generator->valueElement('Salutation', $data->salutation); - $generator->valueElement('Recipient', $data->recipient); - $generator->valueElement('Sentence', "{$data->salutation} {$data->recipient}"); - $generator->endObjectElement('Greeting'); - } -} diff --git a/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php b/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php deleted file mode 100644 index 46c08c52fd..0000000000 --- a/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php +++ /dev/null @@ -1,45 +0,0 @@ -urlAliasService = $urlAliasService; - } - - public function visit(Visitor $visitor, Generator $generator, $data) - { - // Not using $generator->startObjectElement to not have the XML Generator adding its own media-type attribute with the default vendor - $generator->startHashElement('Location'); - $generator->attribute( - 'media-type', - 'application/app.api.Location+' . strtolower((new \ReflectionClass($generator))->getShortName()) - ); - $generator->attribute( - 'href', - $this->router->generate( - 'ibexa.rest.load_location', - ['locationPath' => trim($data->location->pathString, '/')] - ) - ); - parent::visit($visitor, $generator, $data); - $visitor->visitValueObject(new URLAliasRefList(array_merge( - $this->urlAliasService->listLocationAliases($data->location, false), - $this->urlAliasService->listLocationAliases($data->location, true) - ), $this->router->generate( - 'ibexa.rest.list_location_url_aliases', - ['locationPath' => trim($data->location->pathString, '/')] - ))); - $generator->endHashElement('Location'); - } -} From 3b616c48c887bcf16ac79424ffb070cb423390a3 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:42:20 +0100 Subject: [PATCH 03/34] Start rewrite custom rest example --- code_samples/api/rest_api/config/services.yaml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/code_samples/api/rest_api/config/services.yaml b/code_samples/api/rest_api/config/services.yaml index e8c592606f..ec48b6bcdd 100644 --- a/code_samples/api/rest_api/config/services.yaml +++ b/code_samples/api/rest_api/config/services.yaml @@ -41,12 +41,7 @@ services: autoconfigure: true tags: [ 'controller.service_arguments' ] - App\Rest\ValueObjectVisitor\Greeting: - parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor - tags: - - { name: ibexa.rest.output.value_object.visitor, type: App\Rest\Values\Greeting } - App\Rest\InputParser\GreetingInput: - parent: Ibexa\Rest\Server\Common\Parser - tags: - - { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.GreetingInput } + App\Rest\Serializer\: + resource: '../src/Rest/Serializer/' + tags: ['ibexa.rest.serializer.normalizer'] From 296ca50d1c25aca2f8c1465ea8f306e43f6039c5 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Thu, 27 Mar 2025 09:28:21 +0100 Subject: [PATCH 04/34] clean-up --- .../api/rest_api/src/Rest/Controller/DefaultController.php | 1 - .../api/rest_api/src/Rest/Serializer/GreetingNormalizer.php | 1 - 2 files changed, 2 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index 52d5c70180..d0becb1649 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -31,6 +31,5 @@ class DefaultController extends Controller public function greet(Request $request): Greeting { return new Greeting(); - //return new Response('TEST'); } } diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index d85caf7460..3c05503f45 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -29,6 +29,5 @@ public function normalize(mixed $object, ?string $format = null, array $context 'recipient' => $object->recipient, 'sentence' => "{$object->salutation} {$object->recipient}", ]], $format, $context); - //var_dump($object);die('TODO: Implement normalize() method.'); } } From 2ce1141c899f2374f9e9a56d64ae3daa6e09c063 Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Thu, 27 Mar 2025 08:37:20 +0000 Subject: [PATCH 05/34] PHP & JS CS Fixes --- .../api/rest_api/src/Rest/Serializer/GreetingNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index 3c05503f45..7f8cef3042 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -20,7 +20,7 @@ public function supportsNormalization(mixed $data, ?string $format = null) } /** - * @param Greeting $object + * @param \App\Rest\Values\Greeting $object */ public function normalize(mixed $object, ?string $format = null, array $context = []): array { From 685ad5db68b1404383a8582f866106d45090ab8c Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 28 Mar 2025 11:19:00 +0100 Subject: [PATCH 06/34] Enhance output --- .../src/Rest/Controller/DefaultController.php | 29 +++++++++++++++++-- .../Rest/Serializer/GreetingNormalizer.php | 9 ++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index d0becb1649..e64742f994 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -9,6 +9,8 @@ use Ibexa\Rest\Server\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Serializer\Encoder\XmlEncoder; +use Symfony\Component\Serializer\SerializerInterface; #[Get( uriTemplate: '/greet', @@ -28,8 +30,31 @@ )] class DefaultController extends Controller { - public function greet(Request $request): Greeting + const DEFAULT_FORMAT = 'xml'; + + const AVAILABLE_FORMATS = ['json', 'xml']; + + public function __construct(private SerializerInterface $serializer) + { + } + + public function greet(Request $request): Response//Greeting { - return new Greeting(); + //$this->serializer->deserialize($request->getContent()) + + //return new Greeting(); + + $accept = $request->headers->get('Accept', 'application/' . self::DEFAULT_FORMAT); + preg_match('@.*[/+](?P[^/+]+)@', $accept, $matches); + $format = empty($matches['format']) ? self::DEFAULT_FORMAT : $matches['format']; + if (!in_array($format, self::AVAILABLE_FORMATS)) { + $format = self::DEFAULT_FORMAT; + } + + $serialized = $this->serializer->serialize(new Greeting('Salut', 'Monde'), $format, [ + XmlEncoder::ROOT_NODE_NAME => 'Greeting', + ]); + + return new Response($serialized, Response::HTTP_OK); } } diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index 7f8cef3042..666d7ac647 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -24,10 +24,15 @@ public function supportsNormalization(mixed $data, ?string $format = null) */ public function normalize(mixed $object, ?string $format = null, array $context = []): array { - return $this->normalizer->normalize(['greeting' => [ + $data = [ 'salutation' => $object->salutation, 'recipient' => $object->recipient, 'sentence' => "{$object->salutation} {$object->recipient}", - ]], $format, $context); + ]; + if ('json' === $format) { + $data = ['greeting' => $data]; + } + + return $this->normalizer->normalize($data, $format, $context); } } From 4d8252e4e027aadf65d31d12e2a10ed24bf89d0c Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Fri, 28 Mar 2025 10:29:59 +0000 Subject: [PATCH 07/34] PHP & JS CS Fixes --- .../api/rest_api/src/Rest/Controller/DefaultController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index e64742f994..4227d914ea 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -30,9 +30,9 @@ )] class DefaultController extends Controller { - const DEFAULT_FORMAT = 'xml'; + public const DEFAULT_FORMAT = 'xml'; - const AVAILABLE_FORMATS = ['json', 'xml']; + public const AVAILABLE_FORMATS = ['json', 'xml']; public function __construct(private SerializerInterface $serializer) { From 0b04132c8cc6d1513ba68f6682e9c782910517e4 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 31 Mar 2025 08:57:39 +0200 Subject: [PATCH 08/34] Enhance output --- .../src/Rest/Controller/DefaultController.php | 12 ++++++++---- .../src/Rest/Serializer/GreetingNormalizer.php | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index 4227d914ea..543eb1cf56 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -41,8 +41,8 @@ public function __construct(private SerializerInterface $serializer) public function greet(Request $request): Response//Greeting { //$this->serializer->deserialize($request->getContent()) - - //return new Greeting(); + $salutation = 'Salut'; + $recipient = 'Monde'; $accept = $request->headers->get('Accept', 'application/' . self::DEFAULT_FORMAT); preg_match('@.*[/+](?P[^/+]+)@', $accept, $matches); @@ -51,8 +51,12 @@ public function greet(Request $request): Response//Greeting $format = self::DEFAULT_FORMAT; } - $serialized = $this->serializer->serialize(new Greeting('Salut', 'Monde'), $format, [ - XmlEncoder::ROOT_NODE_NAME => 'Greeting', + $greeting = new Greeting($salutation, $recipient); + + //return $greeting; + + $serialized = $this->serializer->serialize($greeting, $format, [ + XmlEncoder::ROOT_NODE_NAME => substr(strrchr(get_class($greeting), '\\'), 1), ]); return new Response($serialized, Response::HTTP_OK); diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index 666d7ac647..14f03c0f13 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -20,7 +20,7 @@ public function supportsNormalization(mixed $data, ?string $format = null) } /** - * @param \App\Rest\Values\Greeting $object + * @param Greeting $object */ public function normalize(mixed $object, ?string $format = null, array $context = []): array { @@ -30,7 +30,7 @@ public function normalize(mixed $object, ?string $format = null, array $context 'sentence' => "{$object->salutation} {$object->recipient}", ]; if ('json' === $format) { - $data = ['greeting' => $data]; + $data = ['Greeting' => $data]; } return $this->normalizer->normalize($data, $format, $context); From 6300ecefe7a205e5be2690db5b510fe5cc64f1e6 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:07:11 +0200 Subject: [PATCH 09/34] Denormalize input --- .../src/Rest/Controller/DefaultController.php | 16 +++++--- .../Serializer/GreetingInputDenormalizer.php | 37 +++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index 543eb1cf56..f3f051fd08 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -40,9 +40,15 @@ public function __construct(private SerializerInterface $serializer) public function greet(Request $request): Response//Greeting { - //$this->serializer->deserialize($request->getContent()) - $salutation = 'Salut'; - $recipient = 'Monde'; + $contentType = $request->headers->get('Content-Type'); + if ($contentType) { + preg_match('@.*[/+](?P[^/+]+)@', $contentType, $matches); + $format = empty($matches['format']) ? self::DEFAULT_FORMAT : $matches['format']; + $input = $request->getContent(); + $greeting = $this->serializer->deserialize($input, Greeting::class, $format); + } else { + $greeting = new Greeting(); + } $accept = $request->headers->get('Accept', 'application/' . self::DEFAULT_FORMAT); preg_match('@.*[/+](?P[^/+]+)@', $accept, $matches); @@ -51,14 +57,12 @@ public function greet(Request $request): Response//Greeting $format = self::DEFAULT_FORMAT; } - $greeting = new Greeting($salutation, $recipient); - //return $greeting; $serialized = $this->serializer->serialize($greeting, $format, [ XmlEncoder::ROOT_NODE_NAME => substr(strrchr(get_class($greeting), '\\'), 1), ]); - return new Response($serialized, Response::HTTP_OK); + return new Response($serialized, Response::HTTP_OK, ['Content-Type' => "application/vnd.ibexa.api.Greeting+$format"]); } } diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php new file mode 100644 index 0000000000..65ad9d7c10 --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php @@ -0,0 +1,37 @@ + Date: Mon, 31 Mar 2025 08:20:03 +0000 Subject: [PATCH 10/34] PHP & JS CS Fixes --- .../rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php | 2 +- .../api/rest_api/src/Rest/Serializer/GreetingNormalizer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php index 65ad9d7c10..053db6135d 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php @@ -1,4 +1,4 @@ - Date: Mon, 31 Mar 2025 12:49:39 +0200 Subject: [PATCH 11/34] Add REST controller to doc --- .../api/rest_api/config/services.yaml | 2 +- .../src/Rest/Controller/DefaultController.php | 139 +++++++++++++++++- 2 files changed, 135 insertions(+), 6 deletions(-) diff --git a/code_samples/api/rest_api/config/services.yaml b/code_samples/api/rest_api/config/services.yaml index ec48b6bcdd..8cc7741c89 100644 --- a/code_samples/api/rest_api/config/services.yaml +++ b/code_samples/api/rest_api/config/services.yaml @@ -39,7 +39,7 @@ services: parent: Ibexa\Rest\Server\Controller autowire: true autoconfigure: true - tags: [ 'controller.service_arguments' ] + tags: [ 'controller.service_arguments', 'ibexa.api_platform.resource' ] App\Rest\Serializer\: diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index f3f051fd08..e085965add 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -3,6 +3,7 @@ namespace App\Rest\Controller; use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Post; use ApiPlatform\OpenApi\Factory\OpenApiFactory; use ApiPlatform\OpenApi\Model; use App\Rest\Values\Greeting; @@ -12,18 +13,146 @@ use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\SerializerInterface; -#[Get( +#[Post( uriTemplate: '/greet', extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], openapi: new Model\Operation( - summary: 'TODO: Greet', - description: 'TODO', + summary: 'Greet', + description: 'Greets a recipient with a salutation', tags: [ - 'User', + 'App', ], + parameters: [ + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: false, + description: 'The greeting input schema encoded in XML or JSON.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the greeting is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + required: false, + content: new \ArrayObject([ + 'application/vnd.ibexa.api.GreetingInput+xml' => [ + 'schema' => [ + 'type' => 'object', + 'xml' => [ + 'name' => 'GreetingInput', + 'wrapped' => false, + ], + 'properties' => [ + 'salutation' => [ + 'type' => 'string', + 'required' => false, + ], + 'recipient' => [ + 'type' => 'string', + 'required' => false, + ], + ], + ], + 'example' => [ + 'salutation' => 'Good morning', + ], + ], + 'application/vnd.ibexa.api.GreetingInput+json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'GreetingInput' => [ + 'type' => 'object', + 'properties' => [ + 'salutation' => [ + 'type' => 'string', + 'required' => false, + ], + 'recipient' => [ + 'type' => 'string', + 'required' => false, + ], + ], + ], + ], + ], + 'example' => [ + 'GreetingInput' => [ + 'salutation' => 'Good day', + 'recipient' => 'Earth', + ], + ], + ], + ]), + ), responses: [ Response::HTTP_OK => [ - 'description' => 'TODO', + 'description' => 'OK - Return a greeting', + 'content' => [ + 'application/vnd.ibexa.api.Greeting+xml' => [ + 'schema' => [ + 'xml' => [ + 'name' => 'Greeting', + 'wrapped' => false, + ], + 'properties' => [ + 'salutation' => [ + 'type' => 'string', + ], + 'recipient' => [ + 'type' => 'string', + ], + 'sentence' => [ + 'type' => 'string', + 'description' => 'Composed sentence using salutation and recipient.' + ] + ], + ], + 'example' => [ + 'salutation' => 'Good morning', + 'recipient' => 'World', + 'sentence' => 'Good Morning World', + ], + ], + 'application/vnd.ibexa.api.Greeting+json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'Greeting' => [ + 'type' => 'object', + 'properties' => [ + 'salutation' => [ + 'type' => 'string', + ], + 'recipient' => [ + 'type' => 'string', + ], + 'sentence' => [ + 'type' => 'string', + ], + ], + ], + ], + ], + 'example' => [ + 'Greeting' => [ + 'salutation' => 'Good day', + 'recipient' => 'Earth', + 'sentence' => 'Good day Earth', + ], + ], + ], + ], ], ], ), From 72c0614d5a14febcc1e4d0d688f22b6efd2a02ff Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Mon, 31 Mar 2025 10:59:27 +0000 Subject: [PATCH 12/34] PHP & JS CS Fixes --- .../api/rest_api/src/Rest/Controller/DefaultController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index e085965add..11e52823f9 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -2,7 +2,6 @@ namespace App\Rest\Controller; -use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\Post; use ApiPlatform\OpenApi\Factory\OpenApiFactory; use ApiPlatform\OpenApi\Model; @@ -114,8 +113,8 @@ ], 'sentence' => [ 'type' => 'string', - 'description' => 'Composed sentence using salutation and recipient.' - ] + 'description' => 'Composed sentence using salutation and recipient.', + ], ], ], 'example' => [ From 8d101f7b8c25b4a5e5eca81b39c680b522276c12 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 31 Mar 2025 13:45:19 +0200 Subject: [PATCH 13/34] Add REST controller to doc --- .../src/Rest/Controller/DefaultController.php | 89 ++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index 11e52823f9..8244ae2915 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -12,6 +12,90 @@ use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\SerializerInterface; +#[Get( + uriTemplate: '/greet', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Greet', + description: 'Greets a recipient with a salutation', + tags: [ + 'App', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: false, + description: 'If set, the greeting is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + example: 'application/vnd.ibexa.api.Greeting+json', + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Return a greeting', + 'content' => [ + 'application/vnd.ibexa.api.Greeting+xml' => [ + 'schema' => [ + 'xml' => [ + 'name' => 'Greeting', + 'wrapped' => false, + ], + 'properties' => [ + 'salutation' => [ + 'type' => 'string', + ], + 'recipient' => [ + 'type' => 'string', + ], + 'sentence' => [ + 'type' => 'string', + 'description' => 'Composed sentence using salutation and recipient.' + ] + ], + ], + 'example' => [ + 'salutation' => 'Hello', + 'recipient' => 'World', + 'sentence' => 'Hello World', + ], + ], + 'application/vnd.ibexa.api.Greeting+json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'Greeting' => [ + 'type' => 'object', + 'properties' => [ + 'salutation' => [ + 'type' => 'string', + ], + 'recipient' => [ + 'type' => 'string', + ], + 'sentence' => [ + 'type' => 'string', + 'description' => 'Composed sentence using salutation and recipient.' + ], + ], + ], + ], + ], + 'example' => [ + 'Greeting' => [ + 'salutation' => 'Hello', + 'recipient' => 'World', + 'sentence' => 'Hello World', + ], + ], + ], + ], + ], + ], + ), +)] #[Post( uriTemplate: '/greet', extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], @@ -30,15 +114,17 @@ schema: [ 'type' => 'string', ], + example: 'application/vnd.ibexa.api.GreetingInput+json', ), new Model\Parameter( name: 'Accept', in: 'header', - required: true, + required: false, description: 'If set, the greeting is returned in XML or JSON format.', schema: [ 'type' => 'string', ], + example: 'application/vnd.ibexa.api.Greeting+json', ), ], requestBody: new Model\RequestBody( @@ -138,6 +224,7 @@ ], 'sentence' => [ 'type' => 'string', + 'description' => 'Composed sentence using salutation and recipient.' ], ], ], From 749ee2ee84ee8703c15b9addeed3aea13fd90c27 Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Mon, 31 Mar 2025 11:55:04 +0000 Subject: [PATCH 14/34] PHP & JS CS Fixes --- .../rest_api/src/Rest/Controller/DefaultController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index 8244ae2915..eccf59dc8b 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -52,8 +52,8 @@ ], 'sentence' => [ 'type' => 'string', - 'description' => 'Composed sentence using salutation and recipient.' - ] + 'description' => 'Composed sentence using salutation and recipient.', + ], ], ], 'example' => [ @@ -77,7 +77,7 @@ ], 'sentence' => [ 'type' => 'string', - 'description' => 'Composed sentence using salutation and recipient.' + 'description' => 'Composed sentence using salutation and recipient.', ], ], ], @@ -224,7 +224,7 @@ ], 'sentence' => [ 'type' => 'string', - 'description' => 'Composed sentence using salutation and recipient.' + 'description' => 'Composed sentence using salutation and recipient.', ], ], ], From 8746a4100e0c66bf1d1b54fc0360137daa77e421 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:15:10 +0200 Subject: [PATCH 15/34] Start to update creating_new_rest_resource.md --- .../src/Rest/Controller/DefaultController.php | 1 + .../Rest/Serializer/GreetingNormalizer.php | 6 +- .../creating_new_rest_resource.md | 96 ++++++++++--------- 3 files changed, 53 insertions(+), 50 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index eccf59dc8b..4e08832bbf 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -2,6 +2,7 @@ namespace App\Rest\Controller; +use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\Post; use ApiPlatform\OpenApi\Factory\OpenApiFactory; use ApiPlatform\OpenApi\Model; diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index b14eaa14d9..16caaa2776 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -25,9 +25,9 @@ public function supportsNormalization(mixed $data, ?string $format = null) public function normalize(mixed $object, ?string $format = null, array $context = []): array { $data = [ - 'salutation' => $object->salutation, - 'recipient' => $object->recipient, - 'sentence' => "{$object->salutation} {$object->recipient}", + 'Salutation' => $object->salutation, + 'Recipient' => $object->recipient, + 'Sentence' => "{$object->salutation} {$object->recipient}", ]; if ('json' === $format) { $data = ['Greeting' => $data]; diff --git a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md index d057cb05ad..176d93578c 100644 --- a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md +++ b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md @@ -8,8 +8,8 @@ To create a new REST resource, you need to prepare: - the REST route leading to a controller action - the controller and its action -- one or several `InputParser` objects if the controller needs to receive a payload to treat, one or several value classes to represent this payload and potentially one or several new media types to type this payload in the `Content-Type` header (optional) -- one or several new value classes to represent the controller action result, their `ValueObjectVisitor` to help the generator to turn this into XML or JSON and potentially one or several new media types to claim in the `Accept` header the desired value (optional) +- one or several input denormalizers if the controller needs to receive a payload to treat, one or several value classes to represent this payload, and potentially one or several new media types to type this payload in the `Content-Type` header (optional) +- one or several new value classes to represent the controller action result, their normalizers to help the generator to turn this into XML or JSON, and potentially one or several new media types to claim in the `Accept` header the desired value (optional) - the addition of this resource route to the REST root (optional) In the following example, you add a greeting resource to the REST API. @@ -51,19 +51,26 @@ services: [[= include_file('code_samples/api/rest_api/config/services.yaml', 36, 42) =]] ``` -Having the REST controllers set as services enables using features such as the `InputDispatcher` service in the [Controller action](#controller-action). +Having the REST controllers set as services enables using features such as + +- TODO: `controller.service_arguments`?? +- TODO: `ibexa.api_platform.resource` tag is needed to have the route available in live doc (/api/ibexa/v2/doc#/App/api_greet_get) ### Controller action A REST controller should: -- return a value object and have a `Generator` and `ValueObjectVisitor`s producing the XML or JSON output -- extend `Ibexa\Rest\Server\Controller` to inherit utils methods and properties like `InputDispatcher` or `RequestParser` +- return an object (passed automatically to a normaliser) or a `Response` (to customize it further) +- TODO: extend `Ibexa\Rest\Server\Controller` to inherit utils methods and properties like `InputDispatcher` or `RequestParser` ``` php -[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php') =]] +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 14) =]] +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 246) =]] ``` +
+TODO + If the returned value was depending on a location, it could have been wrapped in a `CachedValue` to be cached by the reverse proxy (like Varnish) for future calls. `CachedValue` is used in the following way: @@ -75,52 +82,34 @@ return new CachedValue( ); ``` -## Value and ValueObjectVisitor - -``` php -[[= include_file('code_samples/api/rest_api/src/Rest/Values/Greeting.php') =]] -``` - -A `ValueObjectVisitor` must implement the `visit` method. +
-| Argument | Description | -|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| -| `$visitor` | The output visitor.
Can be used to set custom response headers (`setHeader`), HTTP status code ( `setStatus`) | -| `$generator` | The actual response generator. It provides you with a DOM-like API. | -| `$data` | The visited data. The exact object that you returned from the controller.
It can't have a type declaration because the method signature is shared. | +## Value and Normalizer ``` php -[[= include_file('code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php') =]] +[[= include_file('code_samples/api/rest_api/src/Rest/Values/Greeting.php') =]] ``` -The `Values/Greeting` class is linked to its `ValueObjectVisitor` through the service tag. - ``` yaml services: #… [[= include_file('code_samples/api/rest_api/config/services.yaml', 43, 48) =]] ``` -Here, the media type is `application/vnd.ibexa.api.Greeting` plus a format. -To have a different vendor than the default, you could create a new `Output\Generator` or hard-code it in the `ValueObjectVisitor` like in the [`RestLocation` example](adding_custom_media_type.md#new-restlocation-valueobjectvisitor). - -## InputParser - -A REST resource could use route parameters to handle input, but this example illustrates the usage of an input parser. - -For this example, the structure is a `GreetingInput` root node with two leaf nodes, `Salutation` and `Recipient`. +A normalizer must implement the `supportsNormalization` and `normalize` methods. ``` php -[[= include_file('code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php') =]] +[[= include_file('code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php') =]] ``` -Here, this `InputParser` directly returns the right value object. -In other cases, it could return whatever object is needed to represent the input for the controller to perform its action, like arguments to use with a Repository service. +## Input denormalizer -``` yaml -services: - #… -[[= include_file('code_samples/api/rest_api/config/services.yaml', 48, 53) =]] +A REST resource could use route parameters to handle input, but this example illustrates the usage of denormalized payload. + +For this example, the structure is a `GreetingInput` root node with two leaf nodes, `salutation` and `recipient`. + +``` php +[[= include_file('code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php') =]] ``` ## Testing the new resource @@ -138,25 +127,25 @@ curl https://api.example.com/api/ibexa/v2/greet --include --request POST \ --header 'Accept: application/vnd.ibexa.api.Greeting+json'; ``` -``` +```http HTTP/1.1 200 OK -Content-Type: application/vnd.ibexa.api.greeting+xml +Content-Type: application/vnd.ibexa.api.Greeting+xml - + Hello World Hello World HTTP/1.1 200 OK -Content-Type: application/vnd.ibexa.api.greeting+xml +Content-Type: application/vnd.ibexa.api.Greeting+xml - - - Good morning - World - Good morning World + + + Good morning + World + Good morning World HTTP/1.1 200 OK @@ -164,8 +153,6 @@ Content-Type: application/vnd.ibexa.api.greeting+json { "Greeting": { - "_media-type": "application\/vnd.ibexa.api.Greeting+json", - "_href": "\/api\/ibexa\/v2\/greet", "Salutation": "Good day", "Recipient": "Earth", "Sentence": "Good day Earth" @@ -173,8 +160,21 @@ Content-Type: application/vnd.ibexa.api.greeting+json } ``` +## Describe resource in OpenAPI schema + +TODO + +```php +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 246) =]] +``` + +
+ + ## Registering resources in REST root + + You can add the new resource to the [root resource](rest_api_usage.md#rest-root) through a configuration with the following pattern: ```yaml @@ -211,3 +211,5 @@ The above example adds the following entry to the root XML output: ```xml ``` + +
From b3f0c11292711c128a1e6fd3af35f4b85b761823 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:09:47 +0200 Subject: [PATCH 16/34] Restore RestLocation.php out of scope --- .../Rest/ValueObjectVisitor/RestLocation.php | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php diff --git a/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php b/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php new file mode 100644 index 0000000000..46c08c52fd --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php @@ -0,0 +1,45 @@ +urlAliasService = $urlAliasService; + } + + public function visit(Visitor $visitor, Generator $generator, $data) + { + // Not using $generator->startObjectElement to not have the XML Generator adding its own media-type attribute with the default vendor + $generator->startHashElement('Location'); + $generator->attribute( + 'media-type', + 'application/app.api.Location+' . strtolower((new \ReflectionClass($generator))->getShortName()) + ); + $generator->attribute( + 'href', + $this->router->generate( + 'ibexa.rest.load_location', + ['locationPath' => trim($data->location->pathString, '/')] + ) + ); + parent::visit($visitor, $generator, $data); + $visitor->visitValueObject(new URLAliasRefList(array_merge( + $this->urlAliasService->listLocationAliases($data->location, false), + $this->urlAliasService->listLocationAliases($data->location, true) + ), $this->router->generate( + 'ibexa.rest.list_location_url_aliases', + ['locationPath' => trim($data->location->pathString, '/')] + ))); + $generator->endHashElement('Location'); + } +} From 97d32aea0d2a9848c2c448abc78ecfd542166ace Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:12:03 +0200 Subject: [PATCH 17/34] Restore ValueObjectVisitorDispatcher.php out of scope --- .../Output/ValueObjectVisitorDispatcher.php | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php diff --git a/code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php b/code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php new file mode 100644 index 0000000000..39782164fc --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php @@ -0,0 +1,49 @@ +visitors = []; + foreach ($visitors as $type => $visitor) { + $this->visitors[$type] = $visitor; + } + $this->valueObjectVisitorDispatcher = $valueObjectVisitorDispatcher; + } + + public function setOutputVisitor(Visitor $outputVisitor): void + { + $this->outputVisitor = $outputVisitor; + $this->valueObjectVisitorDispatcher->setOutputVisitor($outputVisitor); + } + + public function setOutputGenerator(Generator $outputGenerator): void + { + $this->outputGenerator = $outputGenerator; + $this->valueObjectVisitorDispatcher->setOutputGenerator($outputGenerator); + } + + public function visit($data) + { + $className = get_class($data); + if (isset($this->visitors[$className])) { + return $this->visitors[$className]->visit($this->outputVisitor, $this->outputGenerator, $data); + } + + return $this->valueObjectVisitorDispatcher->visit($data); + } +} From 14a1c7881abdb5396c84bb5c44f7af6f9889266f Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:16:56 +0200 Subject: [PATCH 18/34] phpstan-baseline.neon: rm ValueObjectVisitor\Greeting err --- phpstan-baseline.neon | 6 ------ 1 file changed, 6 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ab3610cd9e..454283cda3 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -204,12 +204,6 @@ parameters: count: 1 path: code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php - - - message: '#^Method App\\Rest\\ValueObjectVisitor\\Greeting\:\:visit\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php - - message: '#^Method App\\Rest\\ValueObjectVisitor\\RestLocation\:\:visit\(\) has no return type specified\.$#' identifier: missingType.return From cd9c883866a46afb5c9fcfceb5037eb11cac7e5b Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:37:50 +0200 Subject: [PATCH 19/34] DefaultController: Hard code root node name Fix the following for low coast Parameter #1 $string of function substr expects string, string|false given. --- .../rest_api/src/Rest/Controller/DefaultController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index 4e08832bbf..9542de3fbe 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -254,7 +254,7 @@ public function __construct(private SerializerInterface $serializer) { } - public function greet(Request $request): Response//Greeting + public function greet(Request $request): Response|Greeting { $contentType = $request->headers->get('Content-Type'); if ($contentType) { @@ -266,6 +266,8 @@ public function greet(Request $request): Response//Greeting $greeting = new Greeting(); } + //return $greeting; + $accept = $request->headers->get('Accept', 'application/' . self::DEFAULT_FORMAT); preg_match('@.*[/+](?P[^/+]+)@', $accept, $matches); $format = empty($matches['format']) ? self::DEFAULT_FORMAT : $matches['format']; @@ -273,10 +275,8 @@ public function greet(Request $request): Response//Greeting $format = self::DEFAULT_FORMAT; } - //return $greeting; - $serialized = $this->serializer->serialize($greeting, $format, [ - XmlEncoder::ROOT_NODE_NAME => substr(strrchr(get_class($greeting), '\\'), 1), + XmlEncoder::ROOT_NODE_NAME => 'Greeting', ]); return new Response($serialized, Response::HTTP_OK, ['Content-Type' => "application/vnd.ibexa.api.Greeting+$format"]); From 530d80aa3c2acb4068b1fefe95bf0c98f5bc0eef Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:40:31 +0200 Subject: [PATCH 20/34] GreetingInputDenormalizer.php Try to avoid "Call to function is_array() with array will always evaluate to true." --- .../src/Rest/Serializer/GreetingInputDenormalizer.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php index 053db6135d..4d97736890 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php @@ -26,12 +26,16 @@ public function denormalize(mixed $data, string $type, ?string $format = null, a public function supportsDenormalization(mixed $data, string $type, ?string $format = null): bool { + if (!is_array($data)) { + return false; + } + if ('json' === $format) { $data = $data[array_key_first($data)]; } $data = array_change_key_case($data); - return Greeting::class === $type && is_array($data) && + return Greeting::class === $type && (array_key_exists('salutation', $data) || array_key_exists('recipient', $data)); } } From 379a4f1b6326c16551557466aad7b10de4347e15 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 2 Apr 2025 10:00:32 +0200 Subject: [PATCH 21/34] GreetingNormalizer: Fix type hint & PHPDoc --- .../rest_api/src/Rest/Serializer/GreetingNormalizer.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index 16caaa2776..8a2a9f0422 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -7,9 +7,6 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -/** - * @method array getSupportedTypes(?string $format) - */ class GreetingNormalizer implements NormalizerInterface, NormalizerAwareInterface { use NormalizerAwareTrait; @@ -19,10 +16,8 @@ public function supportsNormalization(mixed $data, ?string $format = null) return $data instanceof Greeting; } - /** - * @param \App\Rest\Values\Greeting $object - */ - public function normalize(mixed $object, ?string $format = null, array $context = []): array + /** @param Greeting $object */ + public function normalize(mixed $object, ?string $format = null, array $context = []): mixed { $data = [ 'Salutation' => $object->salutation, From 5a80a7923ffd64d9fd83042f7f15b4aadffd40a5 Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Wed, 2 Apr 2025 08:09:52 +0000 Subject: [PATCH 22/34] PHP & JS CS Fixes --- .../api/rest_api/src/Rest/Serializer/GreetingNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index 8a2a9f0422..812abba131 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -16,7 +16,7 @@ public function supportsNormalization(mixed $data, ?string $format = null) return $data instanceof Greeting; } - /** @param Greeting $object */ + /** @param \App\Rest\Values\Greeting $object */ public function normalize(mixed $object, ?string $format = null, array $context = []): mixed { $data = [ From bbc851f0d61b060b3da5678d817ef9d6e26f0ff1 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:59:07 +0200 Subject: [PATCH 23/34] Fix GreetingInputDenormalizer::denormalize `Compile Error: Declaration of App\Rest\Serializer\GreetingInputDenormalizer::denormalize(mixed $data, string $type, ?string $format = null, array $context = []) must be compatible with Symfony\Component\Serializer\Normalizer\DenormalizerInterface::denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed` --- .../rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php index 4d97736890..b38c74052a 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php @@ -11,7 +11,7 @@ class GreetingInputDenormalizer implements DenormalizerInterface, DenormalizerAw { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed { if ('json' === $format) { $data = $data[array_key_first($data)]; From a64de31ac397ba3da35c91edc533626e0e7ae084 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:01:03 +0200 Subject: [PATCH 24/34] Fix GreetingInputDenormalizer::supportsDenormalization `Compile Error: Declaration of App\Rest\Serializer\GreetingInputDenormalizer::supportsDenormalization(mixed $data, string $type, ?string $format = null): bool must be compatible with Symfony\Component\Serializer\Normalizer\DenormalizerInterface::supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool` --- .../rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php index b38c74052a..ec587ef890 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php @@ -24,7 +24,7 @@ public function denormalize(mixed $data, string $type, ?string $format = null, a return new Greeting($salutation, $recipient); } - public function supportsDenormalization(mixed $data, string $type, ?string $format = null): bool + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool { if (!is_array($data)) { return false; From c75a1abecb9ec3721ac6c27cadb61fc1c14a73ad Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:42:45 +0200 Subject: [PATCH 25/34] Fix GreetingInputDenormalizer::getSupportedTypes `Error: Class App\Rest\Serializer\GreetingInputDenormalizer contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Symfony\Component\Serializer\Normalizer\DenormalizerInterface::getSupportedTypes)` --- .../src/Rest/Serializer/GreetingInputDenormalizer.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php index ec587ef890..da2d1d2e9d 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingInputDenormalizer.php @@ -35,7 +35,14 @@ public function supportsDenormalization(mixed $data, string $type, ?string $form } $data = array_change_key_case($data); - return Greeting::class === $type && + return in_array($type, $this->getSupportedTypes($format), true) && (array_key_exists('salutation', $data) || array_key_exists('recipient', $data)); } + + public function getSupportedTypes(?string $format): array + { + return [ + Greeting::class => true, + ]; + } } From 0e82f5f44f8fa10328fe30fa7ac90c01c98803a5 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:44:40 +0200 Subject: [PATCH 26/34] Fix GreetingNormalizer::normalize `Compile Error: Declaration of App\Rest\Serializer\GreetingNormalizer::normalize(mixed $object, ?string $format = null, array $context = []): mixed must be compatible with Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize(mixed $data, ?string $format = null, array $context = []): ArrayObject|array|string|int|float|bool|null` --- .../api/rest_api/src/Rest/Serializer/GreetingNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index 812abba131..b540b2433f 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -17,7 +17,7 @@ public function supportsNormalization(mixed $data, ?string $format = null) } /** @param \App\Rest\Values\Greeting $object */ - public function normalize(mixed $object, ?string $format = null, array $context = []): mixed + public function normalize(mixed $object, ?string $format = null, array $context = []): array|\ArrayObject|bool|float|int|null|string { $data = [ 'Salutation' => $object->salutation, From e826328ac0eac63b060e416e90566eaff2f0ba45 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:45:47 +0200 Subject: [PATCH 27/34] Fix GreetingNormalizer::supportsNormalization `Compile Error: Declaration of App\Rest\Serializer\GreetingNormalizer::supportsNormalization(mixed $data, ?string $format = null) must be compatible with Symfony\Component\Serializer\Normalizer\NormalizerInterface::supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool` --- .../api/rest_api/src/Rest/Serializer/GreetingNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index b540b2433f..772ed067cb 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -11,7 +11,7 @@ class GreetingNormalizer implements NormalizerInterface, NormalizerAwareInterfac { use NormalizerAwareTrait; - public function supportsNormalization(mixed $data, ?string $format = null) + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool { return $data instanceof Greeting; } From 4ef6d43b52a9d307174a6f90338e40f400e9d483 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:49:40 +0200 Subject: [PATCH 28/34] Fix GreetingNormalizer::getSupportedTypes `Error: Class App\Rest\Serializer\GreetingNormalizer contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Symfony\Component\Serializer\Normalizer\NormalizerInterface::getSupportedTypes)` --- .../rest_api/src/Rest/Serializer/GreetingNormalizer.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php index 772ed067cb..03d0eb9853 100644 --- a/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php +++ b/code_samples/api/rest_api/src/Rest/Serializer/GreetingNormalizer.php @@ -30,4 +30,11 @@ public function normalize(mixed $object, ?string $format = null, array $context return $this->normalizer->normalize($data, $format, $context); } + + public function getSupportedTypes(?string $format): array + { + return [ + Greeting::class => true, + ]; + } } From 3da7b1016d9abd7b87762a7950e5a9e2b32ee42c Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 22 Jul 2025 15:36:59 +0200 Subject: [PATCH 29/34] creating_new_rest_resource.md: Complete TODOs --- .../creating_new_rest_resource.md | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md index 176d93578c..48a8e625b6 100644 --- a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md +++ b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md @@ -51,17 +51,17 @@ services: [[= include_file('code_samples/api/rest_api/config/services.yaml', 36, 42) =]] ``` -Having the REST controllers set as services enables using features such as +The [`controller.service_arguments` tag]([[= symfony_doc =]]/controller/service.html#using-the-controller-service-arguments-service-tag) declares the controller as a service receiving injections. +It helps the autowiring of the serializer in the constructor. -- TODO: `controller.service_arguments`?? -- TODO: `ibexa.api_platform.resource` tag is needed to have the route available in live doc (/api/ibexa/v2/doc#/App/api_greet_get) +The `ibexa.api_platform.resource` tag declares the service as an API Platform resource. ### Controller action A REST controller should: -- return an object (passed automatically to a normaliser) or a `Response` (to customize it further) -- TODO: extend `Ibexa\Rest\Server\Controller` to inherit utils methods and properties like `InputDispatcher` or `RequestParser` +- return an object (passed automatically to a normalizer) or a `Response` (to customize it further) +- extend `Ibexa\Rest\Server\Controller` to inherit useful methods and properties like `InputDispatcher` or `RequestParser` ``` php [[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 14) =]] @@ -69,19 +69,19 @@ A REST controller should: ```
-TODO - -If the returned value was depending on a location, it could have been wrapped in a `CachedValue` to be cached by the reverse proxy (like Varnish) for future calls. - -`CachedValue` is used in the following way: - -```php -return new CachedValue( - new MyValue($args…), - ['locationId'=> $locationId] -); -``` - + HTTP Cache + + If the returned value was depending on a location, it could have been wrapped in a CachedValue + to be cached by the reverse proxy (like Varnish or Fastly) for future calls. + + CachedValue is used as following: + + ``` php + return new CachedValue( + new MyValue($args…), + ['locationId'=> $locationId] + ); + ```
## Value and Normalizer @@ -162,19 +162,17 @@ Content-Type: application/vnd.ibexa.api.greeting+json ## Describe resource in OpenAPI schema -TODO +Thanks to API Platform, you can document the OpenAPI resource directly from its controller through annotations. +The resource is added to the OpenAPI Description dumped with `ibexa:openapi` command. +In `dev` mode, the resource appears in the live documentation at `/api/ibexa/v2/doc#/App/api_greet_get`. -```php -[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 246) =]] +``` php hl_lines="5 6 16 100" +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 247) =]] +//… ``` -
- - ## Registering resources in REST root - - You can add the new resource to the [root resource](rest_api_usage.md#rest-root) through a configuration with the following pattern: ```yaml @@ -192,7 +190,7 @@ The parameter values can be a real value or a placeholder. For example, `'router.generate("ibexa.rest.load_location", {locationPath: "1/2"})'` results in `/api/ibexa/v2/content/locations/1/2` while `'router.generate("ibexa.rest.load_location", {locationPath: "{locationPath}"})'` gives `/api/ibexa/v2/content/locations/{locationPath}`. This syntax is based on Symfony's [expression language]([[= symfony_doc =]]/components/expression_language/index.html), an extensible component that allows limited/readable scripting to be used outside the code context. -In this example, `app.rest.greeting` is available in every SiteAccess (`default`): +In the following example, `app.rest.greeting` is available in every SiteAccess (`default`): ```yaml ibexa_rest: @@ -204,12 +202,12 @@ ibexa_rest: href: 'router.generate("app.rest.greeting")' ``` -You can place this configuration in any regular config file, like the existing `config/packages/ibexa.yaml`, or a new `config/packages/ibexa_rest.yaml` file. +You can place this configuration in any regular config file, +like the existing `config/packages/ibexa.yaml`, +or a new `config/packages/ibexa_rest.yaml` file. -The above example adds the following entry to the root XML output: +This example adds the following entry to the root XML output: ```xml ``` - -
From de3751f222eddcac137d02a634ea44121cd354bc Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:25:29 +0200 Subject: [PATCH 30/34] Apply suggestions from code review --- .../src/Rest/Controller/DefaultController.php | 42 ++----------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index 9542de3fbe..c67bb8dcdc 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -22,18 +22,7 @@ tags: [ 'App', ], - parameters: [ - new Model\Parameter( - name: 'Accept', - in: 'header', - required: false, - description: 'If set, the greeting is returned in XML or JSON format.', - schema: [ - 'type' => 'string', - ], - example: 'application/vnd.ibexa.api.Greeting+json', - ), - ], + parameters: [], responses: [ Response::HTTP_OK => [ 'description' => 'OK - Return a greeting', @@ -106,28 +95,7 @@ tags: [ 'App', ], - parameters: [ - new Model\Parameter( - name: 'Content-Type', - in: 'header', - required: false, - description: 'The greeting input schema encoded in XML or JSON.', - schema: [ - 'type' => 'string', - ], - example: 'application/vnd.ibexa.api.GreetingInput+json', - ), - new Model\Parameter( - name: 'Accept', - in: 'header', - required: false, - description: 'If set, the greeting is returned in XML or JSON format.', - schema: [ - 'type' => 'string', - ], - example: 'application/vnd.ibexa.api.Greeting+json', - ), - ], + parameters: [], requestBody: new Model\RequestBody( required: false, content: new \ArrayObject([ @@ -138,14 +106,13 @@ 'name' => 'GreetingInput', 'wrapped' => false, ], + 'required' => [], 'properties' => [ 'salutation' => [ 'type' => 'string', - 'required' => false, ], 'recipient' => [ 'type' => 'string', - 'required' => false, ], ], ], @@ -159,14 +126,13 @@ 'properties' => [ 'GreetingInput' => [ 'type' => 'object', + 'required' => [], 'properties' => [ 'salutation' => [ 'type' => 'string', - 'required' => false, ], 'recipient' => [ 'type' => 'string', - 'required' => false, ], ], ], From 21aaea3c6dc4462c0a3bc17fda92fe37671c1aa0 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:56:21 +0200 Subject: [PATCH 31/34] creating_new_rest_resource.md: Update inclusion line numbers --- .../extending_rest_api/creating_new_rest_resource.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md index 48a8e625b6..54570ef4f2 100644 --- a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md +++ b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md @@ -65,7 +65,7 @@ A REST controller should: ``` php [[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 14) =]] -[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 246) =]] +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 212) =]] ```
@@ -166,8 +166,8 @@ Thanks to API Platform, you can document the OpenAPI resource directly from its The resource is added to the OpenAPI Description dumped with `ibexa:openapi` command. In `dev` mode, the resource appears in the live documentation at `/api/ibexa/v2/doc#/App/api_greet_get`. -``` php hl_lines="5 6 16 100" -[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 247) =]] +``` php hl_lines="5 6 16 89" +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 212) =]] //… ``` From 30e2d3c4acf6ff9b7388a3c2b7f864635713adb6 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 8 Aug 2025 12:18:17 +0200 Subject: [PATCH 32/34] creating_new_rest_resource.md: Detail OpenAPI doc --- .../src/Rest/Controller/DefaultController.php | 4 ++-- .../creating_new_rest_resource.md | 19 +++++++++++++++--- .../rest_api/img/rest-api-greeting-form.png | Bin 0 -> 109727 bytes 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 docs/api/rest_api/img/rest-api-greeting-form.png diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php index c67bb8dcdc..811591344e 100644 --- a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -97,6 +97,7 @@ ], parameters: [], requestBody: new Model\RequestBody( + description: 'Set salutation or recipient.', required: false, content: new \ArrayObject([ 'application/vnd.ibexa.api.GreetingInput+xml' => [ @@ -118,6 +119,7 @@ ], 'example' => [ 'salutation' => 'Good morning', + 'recipient' => 'Planet', ], ], 'application/vnd.ibexa.api.GreetingInput+json' => [ @@ -232,8 +234,6 @@ public function greet(Request $request): Response|Greeting $greeting = new Greeting(); } - //return $greeting; - $accept = $request->headers->get('Accept', 'application/' . self::DEFAULT_FORMAT); preg_match('@.*[/+](?P[^/+]+)@', $accept, $matches); $format = empty($matches['format']) ? self::DEFAULT_FORMAT : $matches['format']; diff --git a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md index 54570ef4f2..a6e46b62b4 100644 --- a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md +++ b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md @@ -65,7 +65,7 @@ A REST controller should: ``` php [[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 14) =]] -[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 212) =]] +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 214) =]] ```
@@ -167,10 +167,23 @@ The resource is added to the OpenAPI Description dumped with `ibexa:openapi` com In `dev` mode, the resource appears in the live documentation at `/api/ibexa/v2/doc#/App/api_greet_get`. ``` php hl_lines="5 6 16 89" -[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 212) =]] -//… +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 215) =]]//… ``` +The resource can be tested from this live documentation. + +For example, the `POST /greet` at `/api/ibexa/v2/doc#/App/api_greet_post` can be tested this way: + +- Click the **Try it out** button +- In **Request body** section, choose the Content-Type from the drop-down menu. For example, the JSON format `application/vnd.ibexa.api.GreetingInput+json` +- In the text area, edit the JSON to make your own test +- In the **Responses** section, choose the desired response format for successful `200` +- Click the **Execute** button + +![Example of test from live doc](../img/rest-api-greeting-form.png) + +Eventually, remove properties to see if default values work, inject parse errors, or empty the whole text area to see error handling. + ## Registering resources in REST root You can add the new resource to the [root resource](rest_api_usage.md#rest-root) through a configuration with the following pattern: diff --git a/docs/api/rest_api/img/rest-api-greeting-form.png b/docs/api/rest_api/img/rest-api-greeting-form.png new file mode 100644 index 0000000000000000000000000000000000000000..1e0ee216e7a2e93f0dbe8f0b630cdcf8ff0cf00e GIT binary patch literal 109727 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�Xe^ET(3C-8!-+tiy5uBWX`BAS zF9R~pldnr-8?>M{_SlUK{)}k;H=rpSGmfb*i@B45wRN+fP*^lN-xzc%l9ibBT&JAh z1Sv+z@4x}v@J~vOD~=<@`*zy$nKPF)cd!`D1sI?+{KD_ILVrw#BGAwodA$NM>niWd zZL@9VL_wKS@tph(TsIYOAJ>d3zcSV{iNKD2@qrnLcXIY>NI(vEufHFP1`6|uJKN6k@;K7pVb@p$)WA5*I2}rqvZ&wwcF68zbbRNV!x9Cr6b<&DJOjmGI;XmZH*7- z@^&0*cz5mE-f5x)Lt^cudMl+mkt0d`P@ZxLcGsnYQ3bJd99mYp~r-8;3R}F5+Vnt_>G(PF1i zZCQ{`Xy~m+(GnMsa#?p=d0t%9IOS+PjOsS520;xPqE|~?KdOSh0}1fXb{SNNuTO6? zko5)) za()^4RTg%+ln?q9UqIclayPL!%r99!v;FAHkZ2;!am|syG-z^wi63=w4=$W!NNEql zhxthc1aww2u+^A(bQ$#kS;hsOWQLr7S!g+rJgD4x9m=PtYX(m^w z?GX`_*YybOJSyv7K$f3WKsT|G>{vfQ>;s-<(x-iAn36v)Pyn)*uarRlhWUC!tJTRr zqztc4DT28S!Qim1ku>ct?VL`nIQjl?Y4$Ztr2I6%l{3u-)f?vq&Ew5LyW?+k&i6Bk zgdX!1I#BU+K{Q$buDVVURBJdGuhk*2%}I)+Arr9aJvtc0Rtd%{Gglw9``nN?Z&NOs z&3y`M)$e`9RS9qHHg2Oxm?*_w-NXq0B@0XFb-c=ap)^l^4z9O{yQ?V|V!rm{GTQN% zM7WksSm(!eo8Y$gRC<0GyWqLpHGH=274}4H>7?uNpS(0FA`fME7mR^1!{QOs#&5oJ z!rIjfL!K~Y1NwtTL;b7YQdHroY`nrqAqd~)c8+{+$!<@%tKUUh?Oqi;n9?YN=eo&_ z8%YkrJK&gFzSu9>O#0RbkKlaByUSanpSedVjLw zlr^NSIM+Cm?{`Xkn6lK<)o6YCf{0p(#@hXS^N%pd`a)3F)ab-BqK-X-OVsl!DvGC4WnVX3p@>5O;g~W63q{$sKKAm z^z}Ag;c05^*kW2KxdQ^RP#bO8dG4Rl+9M3FQ@l3&xH57QPL)Y@dQ6E96{CYo)Jt{f?LO0aTl*D%BH zt{i#xYf`q-g&TS=+?UHfB^fPJn7;};11cNOL$8!|(R&y$9B)X2XFesZ^b&HtxqTV~ zt#sD?i-h&X9F9cvti^B$MzzW~zdpKXD*nK&hYu^Y?33QhFj~Rd<6{nW(eRQZ8mOU{ zR|8>YiJah^=K3GI3}e|5O!t%Lhx6470?*-C;am-!!4X=n`TmRGp3t%48d-m1WgKl0 z&5nuX<_Cl2M?u4}<;+ZI7>JaCOkN4F${=aX)5O{FZq*?EdFJBjYtbDnogmfA4( zbSaJ5bWyab=P<`!#8`FT?;^XuX$_et`ehgvDOLQ`9*^{9Vby)w;?LYi)3SG~8C5bP zxl@JwV=lKXV!|)}mG{*^7D=>Zq%4E&3pnL7f4%au!A{xd?d0s9=U^&G7_2##;k&uq zXju;X>452Qb7ti!FLw6FJ!-a|T$7WIiSyEr4;9F9Z;3{cm+4K_3P0?Rv>ehk1zrJF ze4=$)!(uTRpJN_Qny08QL!hb0F=!2j5>;F7EX&?LTO zELZkaEEVDU1B>+7E>D_?vLF7wA1|8}Z=ga0M|>6mGodup^(fr(2+z~A8LUW9<)Y2P zJBEw!dWOw7l=1cX?SAH5x5qih`F!;Zi>NLw1twK(TE$EkQbHJRgGY-=XiO$UZ^0J@ za`{bo7w`+y@i^T2JLo-Z@2^u<{sfw=nW3Yywwie%iMIR$g-0~$R|h^Vxf2*4BU^55 ze*!K>JyAj(#6*wCr7FInuzQi2AQ4OS;dOJ_|9Ai?8BSZgA>n3 z!5qmn1M%m0t{JV8D#<0j**EFpgZ7Vgb009`G_eMvKbu{)u%kL+NkJSpL|;|$XuY$HkVxiSiai?vLR(?Cl_B4yT;A8r5gM*AIXnFsT+Cfk#RT8zFIRThYT9krsUMMwdIW1{X{$RE+Jy40* z6T8~m5!9HgG>Ue^8hGB(7BZ!NWYI6Z?}?V0A$wrb9U9dk9)kEEYpEu|gd2Fy=My>_%D|nIxsN z%1Eg9y=|dUnx|riv((fbJho3{;=@Tj6ZyU93NgmNI82ZHP@-~8J)QdnRJ2N7O<$Ik zcn+jnKN5fyMR-cQnQI%&-eR)z0jQW+W!bqm(Q-y2wd%RZfNMmlDW`kV{+|4#2t~lr zS4<)Xn~( zJiEs>AVWw1H>-)^;hpnygpuC(^60EmcyqFC)HnE%NN&^zHli<|zv9o1?GgFb_R)e1 zbrl3qM5S@z2-^IP0@K2sZvI?h+!6t~8TdIEQwZcbxK@8YWNv?gQ`YYtz6JM+#c`vi zCWBDIfN0tdhv<>o)aF`GV`3@0Zk!Jo8^wqZ<261ht{eu!cx>^Q^r-@!Et+qea5CHrg)PF+Y19fuOHDD`XeN1R{wB`^wKVt3h6#9A%}m1WEn%Jb8b z%SwEpH8--Pxh-atje}(xaWJC;>X?I4()Ng>b000QHjwQt$vL6}Iu4NS%b+Yd0G?${ z&+Dyk^miz>3<*#E2ooI^C*^%*eN2^H?2)zcVHurdYsWqaK%px7P{d9$x|Qz#g7PA2 zK>kVCecQT@phtYxf>=Xo2gsYDFXDTjL6zlTFshvUFSEdU*UQTd|;`qnyw<> z%Tt;2r#J;Eax~vhGDcp=UonE9bt06@=yXp{(}}l@hBqhSx5Nt{ge*n#kEz~F#kuT+ zr7#FDX-*d}WIZi|H0&Ald%t>22Ty52*J3|**u0+4X}LfH4VAG}5^)DQKS9Fz!O(

N3S&>HTkaTSFm>jMZ2y90N6& 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 b{0^;F3-iPLnzPCS;7>wCR=DJ&uJ8W?2oC{B literal 0 HcmV?d00001 From 49ebc7187bcbbcbdf3f0cfb4fdaf75a8ea5b9a54 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:34:05 +0200 Subject: [PATCH 33/34] creating_new_rest_resource.md: Update Controller inheritance usefulness --- .../extending_rest_api/creating_new_rest_resource.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md index a6e46b62b4..502a8132f7 100644 --- a/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md +++ b/docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md @@ -61,7 +61,9 @@ The `ibexa.api_platform.resource` tag declares the service as an API Platform re A REST controller should: - return an object (passed automatically to a normalizer) or a `Response` (to customize it further) -- extend `Ibexa\Rest\Server\Controller` to inherit useful methods and properties like `InputDispatcher` or `RequestParser` +- extend `Ibexa\Rest\Server\Controller` + - to inherit useful methods and properties like `repository` or `router` + - to be part of the [OpenAPI Description](#describe-resource-in-openapi-schema) ``` php [[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 14) =]] @@ -170,7 +172,7 @@ In `dev` mode, the resource appears in the live documentation at `/a [[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php', 0, 215) =]]//… ``` -The resource can be tested from this live documentation. +The resource can be tested from the live documentation. For example, the `POST /greet` at `/api/ibexa/v2/doc#/App/api_greet_post` can be tested this way: From 9d3ac78b485531aa4334da68606d6b708eacfd60 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:42:46 +0200 Subject: [PATCH 34/34] =?UTF-8?q?format=20rest=5Fapi/=E2=80=A6/services.ya?= =?UTF-8?q?ml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code_samples/api/rest_api/config/services.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/api/rest_api/config/services.yaml b/code_samples/api/rest_api/config/services.yaml index 8cc7741c89..f624c3684d 100644 --- a/code_samples/api/rest_api/config/services.yaml +++ b/code_samples/api/rest_api/config/services.yaml @@ -39,7 +39,7 @@ services: parent: Ibexa\Rest\Server\Controller autowire: true autoconfigure: true - tags: [ 'controller.service_arguments', 'ibexa.api_platform.resource' ] + tags: ['controller.service_arguments', 'ibexa.api_platform.resource'] App\Rest\Serializer\: