diff --git a/CODING-CHALLENGE-6.md b/CODING-CHALLENGE-6.md new file mode 100644 index 0000000..336e40b --- /dev/null +++ b/CODING-CHALLENGE-6.md @@ -0,0 +1,16 @@ +# RESTful Webservices in Symfony + +## Coding Challenge 6 - POST vs. PUT + +### Tasks + +- implement controllers to create and update attendees and workshops +- both should be possible with JSON and XML + +### Solution + +- use an `ArgumentValueResolver` (or alternativly the new `#[MapRequestPayload]` attribute) to deserialize the request's content +- use the `*Repository`s to save the object into the database +- *CREATE:* use HTTP method `POST`, return an HTTP 201 (Created) status code and + set the `Location` header with the help of the `UrlGenerator` +- *UPDATE:* use HTTP method `PUT`, return an HTTP 204 (No Content) status code and leave the response body empty diff --git a/CODING-CHALLENGE-7.md b/CODING-CHALLENGE-7.md new file mode 100644 index 0000000..fe16d2d --- /dev/null +++ b/CODING-CHALLENGE-7.md @@ -0,0 +1,14 @@ +# RESTful Webservices in Symfony + +## Coding Challenge 7 - Validation + +### Tasks + +- introduce Symfony's Validator to validate the request content used to create and update workshops and attendees + +### Solution + +- require the Symfony Validator component: `composer require validator` +- add validation constraints to your model properties (e.g. NotBlank, Email) +- inject the `Validator` Service in your `ValueResolver`s +- for now throw an `UnprocessableEntityHttpException` on validation errors \ No newline at end of file diff --git a/composer.json b/composer.json index 031825a..23b8d21 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "symfony/runtime": "6.3.*", "symfony/serializer": "6.3.*", "symfony/yaml": "6.3.*", - "webmozart/assert": "^1.11" + "webmozart/assert": "^1.11", + "willdurand/negotiation": "^3.1" }, "config": { "allow-plugins": { diff --git a/composer.lock b/composer.lock index e49b66b..e5832d7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "81fadf3eb9b9ecfbe68ef98a2e841210", + "content-hash": "5596bf499dc7ce184640fa9df83b1cb1", "packages": [ { "name": "brick/math", @@ -333,16 +333,16 @@ }, { "name": "doctrine/dbal", - "version": "3.6.7", + "version": "3.7.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "8e0e268052b4a8974cb00215bb2892787021614f" + "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/8e0e268052b4a8974cb00215bb2892787021614f", - "reference": "8e0e268052b4a8974cb00215bb2892787021614f", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/00d03067f07482f025d41ab55e4ba0db5eca2cdf", + "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf", "shasum": "" }, "require": { @@ -358,9 +358,9 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.34", + "phpstan/phpstan": "1.10.35", "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.12", + "phpunit/phpunit": "9.6.13", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", "squizlabs/php_codesniffer": "3.7.2", @@ -426,7 +426,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.6.7" + "source": "https://github.com/doctrine/dbal/tree/3.7.0" }, "funding": [ { @@ -442,20 +442,20 @@ "type": "tidelift" } ], - "time": "2023-09-19T20:15:41+00:00" + "time": "2023-09-26T20:56:55+00:00" }, { "name": "doctrine/deprecations", - "version": "v1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { @@ -487,9 +487,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, - "time": "2023-06-03T09:27:29+00:00" + "time": "2023-09-27T20:04:15+00:00" }, { "name": "doctrine/doctrine-bundle", @@ -4746,6 +4746,62 @@ "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "willdurand/negotiation", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/willdurand/Negotiation.git", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Negotiation\\": "src/Negotiation" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "William Durand", + "email": "will+git@drnd.me" + } + ], + "description": "Content Negotiation tools for PHP provided as a standalone library.", + "homepage": "http://williamdurand.fr/Negotiation/", + "keywords": [ + "accept", + "content", + "format", + "header", + "negotiation" + ], + "support": { + "issues": "https://github.com/willdurand/Negotiation/issues", + "source": "https://github.com/willdurand/Negotiation/tree/3.1.0" + }, + "time": "2022-01-30T20:08:53+00:00" } ], "packages-dev": [ @@ -5119,16 +5175,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.29.0", + "version": "v3.30.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "a55c488e8bfdd9b315880095cd02fa0e49e884d8" + "reference": "95c64693b2f149966a2bc05a7a4981b0343ea52f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a55c488e8bfdd9b315880095cd02fa0e49e884d8", - "reference": "a55c488e8bfdd9b315880095cd02fa0e49e884d8", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/95c64693b2f149966a2bc05a7a4981b0343ea52f", + "reference": "95c64693b2f149966a2bc05a7a4981b0343ea52f", "shasum": "" }, "require": { @@ -5202,7 +5258,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.29.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.30.0" }, "funding": [ { @@ -5210,7 +5266,7 @@ "type": "github" } ], - "time": "2023-09-26T13:02:53+00:00" + "time": "2023-09-26T22:10:43+00:00" }, { "name": "hautelook/alice-bundle", @@ -6340,16 +6396,16 @@ }, { "name": "sebastian/complexity", - "version": "3.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "c70b73893e10757af9c6a48929fa6a333b56a97a" + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c70b73893e10757af9c6a48929fa6a333b56a97a", - "reference": "c70b73893e10757af9c6a48929fa6a333b56a97a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "shasum": "" }, "require": { @@ -6362,7 +6418,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.1-dev" } }, "autoload": { @@ -6386,7 +6442,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" }, "funding": [ { @@ -6394,7 +6450,7 @@ "type": "github" } ], - "time": "2023-08-31T09:55:53+00:00" + "time": "2023-09-28T11:50:59+00:00" }, { "name": "sebastian/diff", diff --git a/src/Controller/Attendee/ListController.php b/src/Controller/Attendee/ListController.php index 9cd970b..558a1cb 100644 --- a/src/Controller/Attendee/ListController.php +++ b/src/Controller/Attendee/ListController.php @@ -27,10 +27,8 @@ public function __invoke(Request $request): Response $request->query->getInt('size', 10) ); - $serializedAttendeeCollection = $this->serializer->serialize($attendeeCollection, 'json'); + $serializedAttendeeCollection = $this->serializer->serialize($attendeeCollection, $request->getRequestFormat()); - return new Response($serializedAttendeeCollection, Response::HTTP_OK, [ - 'Content-Type' => 'application/json', - ]); + return new Response($serializedAttendeeCollection, Response::HTTP_OK); } } diff --git a/src/Controller/Attendee/ReadController.php b/src/Controller/Attendee/ReadController.php index 5b12ac1..ed626de 100644 --- a/src/Controller/Attendee/ReadController.php +++ b/src/Controller/Attendee/ReadController.php @@ -5,6 +5,7 @@ namespace App\Controller\Attendee; use App\Entity\Attendee; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\SerializerInterface; @@ -17,12 +18,10 @@ public function __construct( ) { } - public function __invoke(Attendee $attendee): Response + public function __invoke(Request $request, Attendee $attendee): Response { - $serializedAttendee = $this->serializer->serialize($attendee, 'json'); + $serializedAttendee = $this->serializer->serialize($attendee, $request->getRequestFormat()); - return new Response($serializedAttendee, Response::HTTP_OK, [ - 'Content-Type' => 'application/json', - ]); + return new Response($serializedAttendee, Response::HTTP_OK); } } diff --git a/src/Controller/Workshop/ListController.php b/src/Controller/Workshop/ListController.php index f65ecd7..99235ff 100644 --- a/src/Controller/Workshop/ListController.php +++ b/src/Controller/Workshop/ListController.php @@ -27,10 +27,8 @@ public function __invoke(Request $request): Response $request->query->getInt('size', 10) ); - $serializedWorkshopCollection = $this->serializer->serialize($workshopCollection, 'json'); + $serializedWorkshopCollection = $this->serializer->serialize($workshopCollection, $request->getRequestFormat()); - return new Response($serializedWorkshopCollection, Response::HTTP_OK, [ - 'Content-Type' => 'application/json', - ]); + return new Response($serializedWorkshopCollection, Response::HTTP_OK); } } diff --git a/src/Controller/Workshop/ReadController.php b/src/Controller/Workshop/ReadController.php index f0feec1..3bed2ee 100644 --- a/src/Controller/Workshop/ReadController.php +++ b/src/Controller/Workshop/ReadController.php @@ -5,6 +5,7 @@ namespace App\Controller\Workshop; use App\Entity\Workshop; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\SerializerInterface; @@ -17,12 +18,10 @@ public function __construct( ) { } - public function __invoke(Workshop $workshop): Response + public function __invoke(Request $request, Workshop $workshop): Response { - $serializedWorkshop = $this->serializer->serialize($workshop, 'json'); + $serializedWorkshop = $this->serializer->serialize($workshop, $request->getRequestFormat()); - return new Response($serializedWorkshop, Response::HTTP_OK, [ - 'Content-Type' => 'application/json', - ]); + return new Response($serializedWorkshop, Response::HTTP_OK); } } diff --git a/src/EventListener/RequestFormatListener.php b/src/EventListener/RequestFormatListener.php new file mode 100644 index 0000000..069d445 --- /dev/null +++ b/src/EventListener/RequestFormatListener.php @@ -0,0 +1,51 @@ + MimeType::JSON, + RequestFormat::JSON_HAL => MimeType::JSON_HAL, + RequestFormat::XML => MimeType::XML, + ]; + + public function __construct( + private ContentNegotiator $contentNegotiator + ) { + } + + public function onKernelRequest(RequestEvent $event): void + { + $request = $event->getRequest(); + + $this->addRequestFormats($request, $this->formats); + + $request->setRequestFormat( + $this->contentNegotiator->getNegotiatedRequestFormat() + ); + } + + /** + * Adds the supported formats to the request. + * + * This is necessary for {@see Request::getMimeType} to work. + */ + private function addRequestFormats(Request $request, array $formats): void + { + foreach ($formats as $format => $mimeType) { + $request->setFormat($format, $mimeType); + } + } +} diff --git a/src/EventListener/ResponseContentTypeListener.php b/src/EventListener/ResponseContentTypeListener.php new file mode 100644 index 0000000..8c6f19d --- /dev/null +++ b/src/EventListener/ResponseContentTypeListener.php @@ -0,0 +1,24 @@ +getRequest(); + $response = $event->getResponse(); + + $response->headers->set( + 'Content-Type', + sprintf('%s; charset=utf-8', $request->getMimeType($request->getRequestFormat())) + ); + } +} diff --git a/src/Negotiation/ContentNegotiator.php b/src/Negotiation/ContentNegotiator.php new file mode 100644 index 0000000..bca48bf --- /dev/null +++ b/src/Negotiation/ContentNegotiator.php @@ -0,0 +1,77 @@ +negotiator = new Negotiator(); + } + + public function isNegotiatedRequestFormat(string $format): bool + { + return $format === $this->getNegotiatedRequestFormat(); + } + + // this is used to set the correct serialization format + public function getNegotiatedRequestFormat(): string + { + $acceptableContentTypes = $this->requestStack->getCurrentRequest()->getAcceptableContentTypes(); + + $mimeType = $this->getNegotiatedMimeType($acceptableContentTypes); + + if (!$mimeType || !in_array($mimeType, self::SUPPORTED_MIME_TYPES, true)) { + throw $this->getNotAcceptableHttpException($acceptableContentTypes, self::SUPPORTED_MIME_TYPES); + } + + return match ($mimeType) { + MimeType::JSON_HAL => RequestFormat::JSON_HAL, + MimeType::XML => RequestFormat::XML, + default => RequestFormat::JSON, + }; + } + + private function getNegotiatedMimeType(array $acceptableContentTypes): ?string + { + // default is application/json + if (empty($acceptableContentTypes)) { + return MimeType::JSON; + } + + // default is application/json + if (1 === count($acceptableContentTypes) && '*/*' === $acceptableContentTypes[0]) { + return MimeType::JSON; + } + + $acceptableContentTypesAsString = implode(',', $acceptableContentTypes); + + $acceptHeader = $this->negotiator->getBest($acceptableContentTypesAsString, self::SUPPORTED_MIME_TYPES); + + return $acceptHeader?->getType(); + } + + private function getNotAcceptableHttpException(array $accepts, array $mimeTypes): NotAcceptableHttpException + { + return new NotAcceptableHttpException(sprintf( + 'Requested format "%s" is not supported. Supported MIME types are "%s".', + implode('", "', $accepts), + implode('", "', $mimeTypes) + )); + } +} diff --git a/src/Negotiation/MimeType.php b/src/Negotiation/MimeType.php new file mode 100644 index 0000000..3468b1e --- /dev/null +++ b/src/Negotiation/MimeType.php @@ -0,0 +1,12 @@ +getIterator(), $total); + if (!$this->contentNegotiator->isNegotiatedRequestFormat(RequestFormat::JSON_HAL)) { + return $paginatedCollection; + } + $routeName = $this->getRouteName(); $paginatedCollection diff --git a/src/Pagination/WorkshopCollectionFactory.php b/src/Pagination/WorkshopCollectionFactory.php index d22442c..9301974 100644 --- a/src/Pagination/WorkshopCollectionFactory.php +++ b/src/Pagination/WorkshopCollectionFactory.php @@ -4,6 +4,7 @@ namespace App\Pagination; +use App\Negotiation\ContentNegotiator; use App\Repository\WorkshopRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -13,8 +14,9 @@ final class WorkshopCollectionFactory extends PaginatedCollectionFactory public function __construct( private readonly WorkshopRepository $workshopRepository, UrlGeneratorInterface $urlGenerator, + ContentNegotiator $contentNegotiator, ) { - parent::__construct($urlGenerator); + parent::__construct($urlGenerator, $contentNegotiator); } public function getRepository(): ServiceEntityRepositoryInterface diff --git a/src/Serializer/AttendeeNormalizer.php b/src/Serializer/AttendeeNormalizer.php index 29081a0..fe8117d 100644 --- a/src/Serializer/AttendeeNormalizer.php +++ b/src/Serializer/AttendeeNormalizer.php @@ -5,6 +5,7 @@ namespace App\Serializer; use App\Entity\Attendee; +use App\Negotiation\RequestFormat; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; @@ -41,14 +42,20 @@ public function normalize($object, string $format = null, array $context = []) $data = $this->normalizer->normalize($object, $format, $context); - if (\is_array($data)) { - $data['_links']['self']['href'] = $this->urlGenerator->generate('read_workshop', [ - 'identifier' => $object->getIdentifier(), - ]); + if (!\is_array($data)) { + return $data; + } - $data['_links']['collection']['href'] = $this->urlGenerator->generate('list_workshop'); + if (RequestFormat::JSON_HAL !== $format) { + return $data; } + $data['_links']['self']['href'] = $this->urlGenerator->generate('read_workshop', [ + 'identifier' => $object->getIdentifier(), + ]); + + $data['_links']['collection']['href'] = $this->urlGenerator->generate('list_workshop'); + return $data; } diff --git a/src/Serializer/Encoder/JsonHalEncoder.php b/src/Serializer/Encoder/JsonHalEncoder.php new file mode 100644 index 0000000..0293ed2 --- /dev/null +++ b/src/Serializer/Encoder/JsonHalEncoder.php @@ -0,0 +1,17 @@ +normalizer->normalize($object, $format, $context); - if (\is_array($data)) { - $data['_links']['self']['href'] = $this->urlGenerator->generate('read_attendee', [ - 'identifier' => $object->getIdentifier(), - ]); + if (!\is_array($data)) { + return $data; + } - $data['_links']['collection']['href'] = $this->urlGenerator->generate('list_attendee'); + if (RequestFormat::JSON_HAL !== $format) { + return $data; } + $data['_links']['self']['href'] = $this->urlGenerator->generate('read_attendee', [ + 'identifier' => $object->getIdentifier(), + ]); + + $data['_links']['collection']['href'] = $this->urlGenerator->generate('list_attendee'); + return $data; } diff --git a/tests/Controller/Attendee/ListControllerTest.php b/tests/Controller/Attendee/ListControllerTest.php index 5e590ac..fb53c32 100644 --- a/tests/Controller/Attendee/ListControllerTest.php +++ b/tests/Controller/Attendee/ListControllerTest.php @@ -8,15 +8,27 @@ class ListControllerTest extends ApiTestCase { - public function test_it_should_list_all_attendees(): void + public static function provideHttpAcceptHeaderValues(): \Generator + { + yield 'json' => ['application/json']; + yield 'hal+json' => ['application/hal+json']; + } + + /** + * @dataProvider provideHttpAcceptHeaderValues + */ + public function test_it_should_list_all_attendees(string $httpAcceptHeaderValue): void { $this->loadFixtures([ __DIR__.'/fixtures/list_attendee.yaml', ]); - $this->browser->request('GET', '/attendees'); + $this->browser->request('GET', '/attendees', [], [], [ + 'HTTP_ACCEPT' => $httpAcceptHeaderValue, + ]); static::assertResponseIsSuccessful(); + static::assertStringContainsString($httpAcceptHeaderValue, $this->browser->getResponse()->headers->get('Content-Type')); $this->assertMatchesJsonSnapshot($this->browser->getResponse()->getContent()); } diff --git a/tests/Controller/Attendee/ReadControllerTest.php b/tests/Controller/Attendee/ReadControllerTest.php index 3ac2e18..c00142c 100644 --- a/tests/Controller/Attendee/ReadControllerTest.php +++ b/tests/Controller/Attendee/ReadControllerTest.php @@ -8,15 +8,27 @@ class ReadControllerTest extends ApiTestCase { - public function test_it_should_show_requested_attendee(): void + public static function provideHttpAcceptHeaderValues(): \Generator + { + yield 'json' => ['application/json']; + yield 'hal+json' => ['application/hal+json']; + } + + /** + * @dataProvider provideHttpAcceptHeaderValues + */ + public function test_it_should_show_requested_attendee(string $httpAcceptHeaderValue): void { $this->loadFixtures([ __DIR__.'/fixtures/read_attendee.yaml', ]); - $this->browser->request('GET', '/attendees/17058af8-1b0f-4afe-910d-669b4bd0fd26'); + $this->browser->request('GET', '/attendees/17058af8-1b0f-4afe-910d-669b4bd0fd26', [], [], [ + 'HTTP_ACCEPT' => $httpAcceptHeaderValue, + ]); static::assertResponseIsSuccessful(); + static::assertStringContainsString($httpAcceptHeaderValue, $this->browser->getResponse()->headers->get('Content-Type')); $this->assertMatchesJsonSnapshot($this->browser->getResponse()->getContent()); } diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees with data set haljson__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees with data set haljson__1.json new file mode 100644 index 0000000..543aa13 --- /dev/null +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees with data set haljson__1.json @@ -0,0 +1,43 @@ +{ + "items": [ + { + "identifier": "803449f4-9a4c-4ecb-8ce4-cebc804fe70a", + "firstname": "Jan", + "lastname": "Sch\u00e4dlich", + "email": "schaedlich.jan@gmail.com", + "workshops": [ + { + "identifier": "abba667a-96ae-4f75-9b71-97819b682e8d", + "title": "RESTful Webservices in Symfony", + "workshop_date": "2022-06-14", + "attendees": [ + "Jan Sch\u00e4dlich" + ], + "_links": { + "self": { + "href": "\/attendees\/abba667a-96ae-4f75-9b71-97819b682e8d" + }, + "collection": { + "href": "\/attendees" + } + } + } + ], + "_links": { + "self": { + "href": "\/workshops\/803449f4-9a4c-4ecb-8ce4-cebc804fe70a" + }, + "collection": { + "href": "\/workshops" + } + } + } + ], + "total": 1, + "count": 1, + "_links": { + "self": { + "href": "\/attendees?page=1&size=10" + } + } +} diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees with data set json__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees with data set json__1.json new file mode 100644 index 0000000..aef2fdc --- /dev/null +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_list_all_attendees with data set json__1.json @@ -0,0 +1,22 @@ +{ + "items": [ + { + "identifier": "803449f4-9a4c-4ecb-8ce4-cebc804fe70a", + "firstname": "Jan", + "lastname": "Sch\u00e4dlich", + "email": "schaedlich.jan@gmail.com", + "workshops": [ + { + "identifier": "abba667a-96ae-4f75-9b71-97819b682e8d", + "title": "RESTful Webservices in Symfony", + "workshop_date": "2022-06-14", + "attendees": [ + "Jan Sch\u00e4dlich" + ] + } + ] + } + ], + "total": 1, + "count": 1 +} diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 3 items each__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 3 items each__1.json index d6dca91..c7a3368 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 3 items each__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 3 items each__1.json @@ -5,61 +5,23 @@ "firstname": "a", "lastname": "1", "email": "a1@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/4878f198-36ab-4fe3-8189-19662a9764fa" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] }, { "identifier": "e942ce16-27c2-494f-9d93-03412da980c5", "firstname": "b", "lastname": "2", "email": "b2@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/e942ce16-27c2-494f-9d93-03412da980c5" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] }, { "identifier": "4714fb8a-83d8-49af-abbf-7c68fc6c9656", "firstname": "c", "lastname": "3", "email": "c3@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/4714fb8a-83d8-49af-abbf-7c68fc6c9656" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] } ], "total": 5, - "count": 3, - "_links": { - "self": { - "href": "\/attendees?page=1&size=3" - }, - "first": { - "href": "\/attendees?page=1&size=3" - }, - "last": { - "href": "\/attendees?page=2&size=3" - }, - "next": { - "href": "\/attendees?page=2&size=3" - } - } + "count": 3 } diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 5 items each__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 5 items each__1.json index 894de6a..f6ffe29 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 5 items each__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 1st page, 5 items each__1.json @@ -5,82 +5,37 @@ "firstname": "a", "lastname": "1", "email": "a1@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/4878f198-36ab-4fe3-8189-19662a9764fa" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] }, { "identifier": "e942ce16-27c2-494f-9d93-03412da980c5", "firstname": "b", "lastname": "2", "email": "b2@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/e942ce16-27c2-494f-9d93-03412da980c5" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] }, { "identifier": "4714fb8a-83d8-49af-abbf-7c68fc6c9656", "firstname": "c", "lastname": "3", "email": "c3@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/4714fb8a-83d8-49af-abbf-7c68fc6c9656" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] }, { "identifier": "65445e8c-a6c6-4955-9eb2-5fb60d6a991e", "firstname": "d", "lastname": "4", "email": "d4@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/65445e8c-a6c6-4955-9eb2-5fb60d6a991e" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] }, { "identifier": "3aacd688-5b81-4aba-a5ea-ac7668ba95b6", "firstname": "e", "lastname": "5", "email": "e5@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/3aacd688-5b81-4aba-a5ea-ac7668ba95b6" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] } ], "total": 5, - "count": 5, - "_links": { - "self": { - "href": "\/attendees?page=1&size=5" - } - } + "count": 5 } diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 3 items each__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 3 items each__1.json index 4643d9e..dd26fbd 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 3 items each__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 3 items each__1.json @@ -5,46 +5,16 @@ "firstname": "d", "lastname": "4", "email": "d4@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/65445e8c-a6c6-4955-9eb2-5fb60d6a991e" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] }, { "identifier": "3aacd688-5b81-4aba-a5ea-ac7668ba95b6", "firstname": "e", "lastname": "5", "email": "e5@test.de", - "workshops": [], - "_links": { - "self": { - "href": "\/workshops\/3aacd688-5b81-4aba-a5ea-ac7668ba95b6" - }, - "collection": { - "href": "\/workshops" - } - } + "workshops": [] } ], "total": 5, - "count": 2, - "_links": { - "self": { - "href": "\/attendees?page=2&size=3" - }, - "first": { - "href": "\/attendees?page=1&size=3" - }, - "last": { - "href": "\/attendees?page=2&size=3" - }, - "prev": { - "href": "\/attendees?page=1&size=3" - } - } + "count": 2 } diff --git a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 5 items each__1.json b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 5 items each__1.json index e6c1a1f..b985846 100644 --- a/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 5 items each__1.json +++ b/tests/Controller/Attendee/__snapshots__/ListControllerTest__test_it_should_paginate_attendees with data set show 2nd page, 5 items each__1.json @@ -1,13 +1,5 @@ { "items": [], "total": 5, - "count": 0, - "_links": { - "self": { - "href": "\/attendees?page=2&size=5" - }, - "prev": { - "href": "\/attendees?page=1&size=5" - } - } + "count": 0 } diff --git a/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee with data set haljson__1.json b/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee with data set haljson__1.json new file mode 100644 index 0000000..2388fd4 --- /dev/null +++ b/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee with data set haljson__1.json @@ -0,0 +1,32 @@ +{ + "identifier": "17058af8-1b0f-4afe-910d-669b4bd0fd26", + "firstname": "Jan", + "lastname": "Sch\u00e4dlich", + "email": "schaedlich.jan@gmail.com", + "workshops": [ + { + "identifier": "12bc7c60-7f77-41e0-90eb-9996a64a3b14", + "title": "RESTful Webservices in Symfony", + "workshop_date": "2022-06-14", + "attendees": [ + "Jan Sch\u00e4dlich" + ], + "_links": { + "self": { + "href": "\/attendees\/12bc7c60-7f77-41e0-90eb-9996a64a3b14" + }, + "collection": { + "href": "\/attendees" + } + } + } + ], + "_links": { + "self": { + "href": "\/workshops\/17058af8-1b0f-4afe-910d-669b4bd0fd26" + }, + "collection": { + "href": "\/workshops" + } + } +} diff --git a/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee with data set json__1.json b/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee with data set json__1.json new file mode 100644 index 0000000..763a24e --- /dev/null +++ b/tests/Controller/Attendee/__snapshots__/ReadControllerTest__test_it_should_show_requested_attendee with data set json__1.json @@ -0,0 +1,16 @@ +{ + "identifier": "17058af8-1b0f-4afe-910d-669b4bd0fd26", + "firstname": "Jan", + "lastname": "Sch\u00e4dlich", + "email": "schaedlich.jan@gmail.com", + "workshops": [ + { + "identifier": "12bc7c60-7f77-41e0-90eb-9996a64a3b14", + "title": "RESTful Webservices in Symfony", + "workshop_date": "2022-06-14", + "attendees": [ + "Jan Sch\u00e4dlich" + ] + } + ] +} diff --git a/tests/Controller/Workshop/ListControllerTest.php b/tests/Controller/Workshop/ListControllerTest.php index f31a41d..6a9fea0 100644 --- a/tests/Controller/Workshop/ListControllerTest.php +++ b/tests/Controller/Workshop/ListControllerTest.php @@ -8,15 +8,27 @@ class ListControllerTest extends ApiTestCase { - public function test_it_should_list_all_workshops(): void + public static function provideHttpAcceptHeaderValues(): \Generator + { + yield 'json' => ['application/json']; + yield 'hal+json' => ['application/hal+json']; + } + + /** + * @dataProvider provideHttpAcceptHeaderValues + */ + public function test_it_should_list_all_workshops(string $httpAcceptHeaderValue): void { $this->loadFixtures([ __DIR__.'/fixtures/list_workshop.yaml', ]); - $this->browser->request('GET', '/workshops'); + $this->browser->request('GET', '/workshops', [], [], [ + 'HTTP_ACCEPT' => $httpAcceptHeaderValue, + ]); static::assertResponseIsSuccessful(); + static::assertStringContainsString($httpAcceptHeaderValue, $this->browser->getResponse()->headers->get('Content-Type')); $this->assertMatchesJsonSnapshot($this->browser->getResponse()->getContent()); } diff --git a/tests/Controller/Workshop/ReadControllerTest.php b/tests/Controller/Workshop/ReadControllerTest.php index 29a1352..70504e4 100644 --- a/tests/Controller/Workshop/ReadControllerTest.php +++ b/tests/Controller/Workshop/ReadControllerTest.php @@ -8,15 +8,27 @@ class ReadControllerTest extends ApiTestCase { - public function test_it_should_show_requested_workshop(): void + public static function provideHttpAcceptHeaderValues(): \Generator + { + yield 'json' => ['application/json']; + yield 'hal+json' => ['application/hal+json']; + } + + /** + * @dataProvider provideHttpAcceptHeaderValues + */ + public function test_it_should_show_requested_workshop(string $httpAcceptHeaderValue): void { $this->loadFixtures([ __DIR__.'/fixtures/read_workshop.yaml', ]); - $this->browser->request('GET', '/workshops/8acf8f2b-95c1-46e1-85a4-ea6ff88081ce'); + $this->browser->request('GET', '/workshops/8acf8f2b-95c1-46e1-85a4-ea6ff88081ce', [], [], [ + 'HTTP_ACCEPT' => $httpAcceptHeaderValue, + ]); static::assertResponseIsSuccessful(); + static::assertStringContainsString($httpAcceptHeaderValue, $this->browser->getResponse()->headers->get('Content-Type')); $this->assertMatchesJsonSnapshot($this->browser->getResponse()->getContent()); } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops with data set haljson__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops with data set haljson__1.json new file mode 100644 index 0000000..ce823b4 --- /dev/null +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops with data set haljson__1.json @@ -0,0 +1,43 @@ +{ + "items": [ + { + "identifier": "ef6e13ef-f601-4e74-bae8-bdf8f41cd8db", + "title": "RESTful Webservices in Symfony", + "workshop_date": "2022-06-14", + "attendees": [ + { + "identifier": "2a451d60-2fef-437b-838e-10edb2ade8eb", + "firstname": "Jan", + "lastname": "Sch\u00e4dlich", + "email": "schaedlich.jan@gmail.com", + "workshops": [ + "RESTful Webservices in Symfony" + ], + "_links": { + "self": { + "href": "\/workshops\/2a451d60-2fef-437b-838e-10edb2ade8eb" + }, + "collection": { + "href": "\/workshops" + } + } + } + ], + "_links": { + "self": { + "href": "\/attendees\/ef6e13ef-f601-4e74-bae8-bdf8f41cd8db" + }, + "collection": { + "href": "\/attendees" + } + } + } + ], + "total": 1, + "count": 1, + "_links": { + "self": { + "href": "\/workshops?page=1&size=10" + } + } +} diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops with data set json__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops with data set json__1.json new file mode 100644 index 0000000..46094b5 --- /dev/null +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_list_all_workshops with data set json__1.json @@ -0,0 +1,22 @@ +{ + "items": [ + { + "identifier": "ef6e13ef-f601-4e74-bae8-bdf8f41cd8db", + "title": "RESTful Webservices in Symfony", + "workshop_date": "2022-06-14", + "attendees": [ + { + "identifier": "2a451d60-2fef-437b-838e-10edb2ade8eb", + "firstname": "Jan", + "lastname": "Sch\u00e4dlich", + "email": "schaedlich.jan@gmail.com", + "workshops": [ + "RESTful Webservices in Symfony" + ] + } + ] + } + ], + "total": 1, + "count": 1 +} diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 3 items each__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 3 items each__1.json index db902d7..5fa4b11 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 3 items each__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 3 items each__1.json @@ -4,59 +4,21 @@ "identifier": "775ead00-7f50-466f-b89d-73150c9adfc8", "title": "a", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/775ead00-7f50-466f-b89d-73150c9adfc8" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] }, { "identifier": "8b84bf4a-a8e9-436a-85ce-2f5df8f8a3a2", "title": "b", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/8b84bf4a-a8e9-436a-85ce-2f5df8f8a3a2" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] }, { "identifier": "531b95c9-05c5-49ac-98ec-e0263f35d83e", "title": "c", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/531b95c9-05c5-49ac-98ec-e0263f35d83e" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] } ], "total": 5, - "count": 3, - "_links": { - "self": { - "href": "\/workshops?page=1&size=3" - }, - "first": { - "href": "\/workshops?page=1&size=3" - }, - "last": { - "href": "\/workshops?page=2&size=3" - }, - "next": { - "href": "\/workshops?page=2&size=3" - } - } + "count": 3 } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 5 items each__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 5 items each__1.json index 3c38d22..53fee32 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 5 items each__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 1st page, 5 items each__1.json @@ -4,78 +4,33 @@ "identifier": "775ead00-7f50-466f-b89d-73150c9adfc8", "title": "a", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/775ead00-7f50-466f-b89d-73150c9adfc8" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] }, { "identifier": "8b84bf4a-a8e9-436a-85ce-2f5df8f8a3a2", "title": "b", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/8b84bf4a-a8e9-436a-85ce-2f5df8f8a3a2" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] }, { "identifier": "531b95c9-05c5-49ac-98ec-e0263f35d83e", "title": "c", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/531b95c9-05c5-49ac-98ec-e0263f35d83e" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] }, { "identifier": "d0e2cbfd-eff0-4025-90fb-3350aada0939", "title": "d", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/d0e2cbfd-eff0-4025-90fb-3350aada0939" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] }, { "identifier": "9aa6e731-6058-4f2c-9081-aadeb15db35e", "title": "e", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/9aa6e731-6058-4f2c-9081-aadeb15db35e" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] } ], "total": 5, - "count": 5, - "_links": { - "self": { - "href": "\/workshops?page=1&size=5" - } - } + "count": 5 } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 3 items each__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 3 items each__1.json index 55e4f54..349d498 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 3 items each__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 3 items each__1.json @@ -4,45 +4,15 @@ "identifier": "d0e2cbfd-eff0-4025-90fb-3350aada0939", "title": "d", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/d0e2cbfd-eff0-4025-90fb-3350aada0939" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] }, { "identifier": "9aa6e731-6058-4f2c-9081-aadeb15db35e", "title": "e", "workshop_date": "2021-12-07", - "attendees": [], - "_links": { - "self": { - "href": "\/attendees\/9aa6e731-6058-4f2c-9081-aadeb15db35e" - }, - "collection": { - "href": "\/attendees" - } - } + "attendees": [] } ], "total": 5, - "count": 2, - "_links": { - "self": { - "href": "\/workshops?page=2&size=3" - }, - "first": { - "href": "\/workshops?page=1&size=3" - }, - "last": { - "href": "\/workshops?page=2&size=3" - }, - "prev": { - "href": "\/workshops?page=1&size=3" - } - } + "count": 2 } diff --git a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 5 items each__1.json b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 5 items each__1.json index eab1cbb..b985846 100644 --- a/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 5 items each__1.json +++ b/tests/Controller/Workshop/__snapshots__/ListControllerTest__test_it_should_paginate_workshops with data set show 2nd page, 5 items each__1.json @@ -1,13 +1,5 @@ { "items": [], "total": 5, - "count": 0, - "_links": { - "self": { - "href": "\/workshops?page=2&size=5" - }, - "prev": { - "href": "\/workshops?page=1&size=5" - } - } + "count": 0 } diff --git a/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop with data set haljson__1.json b/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop with data set haljson__1.json new file mode 100644 index 0000000..bd4a30c --- /dev/null +++ b/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop with data set haljson__1.json @@ -0,0 +1,32 @@ +{ + "identifier": "8acf8f2b-95c1-46e1-85a4-ea6ff88081ce", + "title": "RESTful Webservices in Symfony", + "workshop_date": "2022-06-14", + "attendees": [ + { + "identifier": "36ffcf19-7560-4ead-8d8e-3c40cf169784", + "firstname": "Jan", + "lastname": "Sch\u00e4dlich", + "email": "schaedlich.jan@gmail.com", + "workshops": [ + "RESTful Webservices in Symfony" + ], + "_links": { + "self": { + "href": "\/workshops\/36ffcf19-7560-4ead-8d8e-3c40cf169784" + }, + "collection": { + "href": "\/workshops" + } + } + } + ], + "_links": { + "self": { + "href": "\/attendees\/8acf8f2b-95c1-46e1-85a4-ea6ff88081ce" + }, + "collection": { + "href": "\/attendees" + } + } +} diff --git a/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop with data set json__1.json b/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop with data set json__1.json new file mode 100644 index 0000000..0bb7af6 --- /dev/null +++ b/tests/Controller/Workshop/__snapshots__/ReadControllerTest__test_it_should_show_requested_workshop with data set json__1.json @@ -0,0 +1,16 @@ +{ + "identifier": "8acf8f2b-95c1-46e1-85a4-ea6ff88081ce", + "title": "RESTful Webservices in Symfony", + "workshop_date": "2022-06-14", + "attendees": [ + { + "identifier": "36ffcf19-7560-4ead-8d8e-3c40cf169784", + "firstname": "Jan", + "lastname": "Sch\u00e4dlich", + "email": "schaedlich.jan@gmail.com", + "workshops": [ + "RESTful Webservices in Symfony" + ] + } + ] +}