diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php index 5cc055f26..b5c935c76 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php @@ -25,12 +25,19 @@ class AbstractCRUDController extends AbstractController * * @param string $id * @return object + * @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found */ - protected function getEntity($action, $id, Request $request): ?object + protected function getEntity($action, $id, Request $request): object { - return $this->getDoctrine() + $e = $this->getDoctrine() ->getRepository($this->getEntityClass()) ->find($id); + + if (NULL === $e) { + throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id)); + } + + return $e; } /** diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php index bbf2f399a..87c9a70df 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php @@ -12,6 +12,8 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent; use Chill\PersonBundle\Entity\Person; +use Chill\ThirdPartyBundle\Entity\ThirdParty; +use Symfony\Component\Serializer\Exception\RuntimeException; class AccompanyingCourseApiController extends ApiController { @@ -64,6 +66,50 @@ class AccompanyingCourseApiController extends ApiController return $this->json($participation); } + public function requestorApi($id, Request $request, string $_format): Response + { + /** @var AccompanyingPeriod $accompanyingPeriod */ + $action = 'requestor'; + $accompanyingPeriod = $this->getEntity($action, $id, $request); + // a requestor may be a person or a thirdParty + + $this->checkACL($action, $request, $_format, $accompanyingPeriod); + $this->onPostCheckACL($action, $request, $_format, $accompanyingPeriod); + + if (Request::METHOD_DELETE === $request->getMethod()) { + $accompanyingPeriod->setRequestor(NULL); + } elseif (Request::METHOD_POST === $request->getMethod()) { + $requestor = null; + $exceptions = []; + foreach ([Person::class, ThirdParty::class] as $class) { + try { + $requestor = $this->getSerializer() + ->deserialize($request->getContent(), $class, $_format, []); + } catch (RuntimeException $e) { + $exceptions[] = $e; + } + } + if ($requestor === null) { + throw new BadRequestException('Could not find any person or requestor', 0, $exceptions[0]); + } + + $accompanyingPeriod->setRequestor($requestor); + } else { + throw new BadRequestException('method not supported'); + } + + $errors = $this->validator->validate($accompanyingPeriod); + + if ($errors->count() > 0) { + // only format accepted + return $this->json($errors); + } + + $this->getDoctrine()->getManager()->flush(); + + return $this->json($accompanyingPeriod->getRequestor()); + } + protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response { $this->eventDispatcher->dispatch( diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index 84b8bb397..6fa58adac 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -334,6 +334,18 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE ] + ], + 'requestor' => [ + 'methods' => [ + Request::METHOD_POST => true, + Request::METHOD_DELETE => true, + Request::METHOD_GET => false, + Request::METHOD_HEAD => false, + ], + 'roles' => [ + Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, + Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE + ] ] ] diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 9eec8e6fb..1a6a3faae 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -515,9 +515,9 @@ class AccompanyingPeriod return $this->requestorPerson; } - public function setRequestorPerson(Person $requestorPerson): self + public function setRequestorPerson(Person $requestorPerson = null): self { - $this->requestorPerson = ($this->requestorThirdParty === null) ? $requestorPerson : null; + $this->requestorPerson = $requestorPerson; return $this; } @@ -527,9 +527,9 @@ class AccompanyingPeriod return $this->requestorThirdParty; } - public function setRequestorThirdParty(ThirdParty $requestorThirdParty): self + public function setRequestorThirdParty(ThirdParty $requestorThirdParty = null): self { - $this->requestorThirdParty = ($this->requestorPerson === null) ? $requestorThirdParty : null; + $this->requestorThirdParty = $requestorThirdParty; return $this; } @@ -542,6 +542,37 @@ class AccompanyingPeriod return $this->requestorPerson ?? $this->requestorThirdParty; } + + /** + * Set a requestor + * + * The requestor is either an instance of ThirdParty, or an + * instance of Person + * + * @param $requestor Person|ThirdParty + * @return self + * @throw UnexpectedValueException if the requestor is not a Person or ThirdParty + * + */ + public function setRequestor($requestor): self + { + if ($requestor instanceof Person) { + $this->setRequestorThirdParty(NULL); + $this->setRequestorPerson($requestor); + } elseif ($requestor instanceof ThirdParty) { + $this->setRequestorThirdParty($requestor); + $this->setRequestorPerson(NULL); + } elseif (NULL === $requestor) { + $this->setRequestorPerson(NULL); + $this->setRequestorThirdParty(NULL); + } else { + throw new \UnexpectedValueException("requestor is not an instance of Person or ThirdParty"); + } + + return $this; + } + + public function isRequestorAnonymous(): bool { return $this->requestorAnonymous; diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php index a315fb326..a0a46c37e 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php @@ -83,16 +83,16 @@ class PersonNormalizer implements public function denormalize($data, string $type, string $format = null, array $context = []): Person { if ($context[self::GET_PERSON] ?? true) { - $id = $data['id'] ?? null; + $id = $data['person_id'] ?? null; if (NULL === $id) { - throw new RuntimeException("missing id into person object"); + throw new RuntimeException("missing person_id into person object"); } } /** var Person $person */ $person = $this->repository->findOneById($id); if (NULL === $person) { - return UnexpectedValueException("person id not found"); + throw new UnexpectedValueException("person id not found"); } return $person; diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php index 03fddab41..7fa11d6e1 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php @@ -24,6 +24,7 @@ namespace Chill\PersonBundle\Tests\Entity; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; +use Chill\ThirdPartyBundle\Entity\ThirdParty; class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase { @@ -104,4 +105,30 @@ class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase $participation = $period->getOpenParticipationContainsPerson($person); $this->assertNull($participation); } + + public function testRequestor() + { + $period = new AccompanyingPeriod(new \DateTime()); + $person = new Person(); + $thirdParty = new ThirdParty(); + + $this->assertNull($period->getRequestorThirdParty()); + $this->assertNull($period->getRequestorPerson()); + $this->assertNull($period->getRequestor()); + + $period->setRequestor($person); + $this->assertNull($period->getRequestorThirdParty()); + $this->assertSame($person, $period->getRequestorPerson()); + $this->assertSame($person, $period->getRequestor()); + + $period->setRequestor($thirdParty); + $this->assertNull($period->getRequestorPerson()); + $this->assertSame($thirdParty, $period->getRequestorThirdParty()); + $this->assertSame($thirdParty, $period->getRequestor()); + + $period->setRequestor(NULL); + $this->assertNull($period->getRequestorThirdParty()); + $this->assertNull($period->getRequestorPerson()); + $this->assertNull($period->getRequestor()); + } } diff --git a/src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php b/src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php index c961b3601..65845b744 100644 --- a/src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php +++ b/src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php @@ -4,13 +4,18 @@ namespace Chill\ThirdPartyBundle\Repository; use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\Query; +use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\Persistence\ManagerRegistry; +use Chill\ThirdPartyBundle\Entity\ThirdParty; -/** - * ThirdPartyRepository - * - */ -class ThirdPartyRepository extends \Doctrine\ORM\EntityRepository + +class ThirdPartyRepository extends ServiceEntityRepository { + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, ThirdParty::class); + } + /** * count amongst parties associated to $centers, with $terms parameters * diff --git a/src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php b/src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php index b10ed4d7e..81b5e7f04 100644 --- a/src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php +++ b/src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php @@ -6,11 +6,24 @@ use Chill\ThirdPartyBundle\Entity\ThirdParty; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Exception\RuntimeException; +use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; +use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository; -class ThirdPartyNormalizer implements NormalizerInterface, NormalizerAwareInterface + +class ThirdPartyNormalizer implements NormalizerInterface, DenormalizerInterface, NormalizerAwareInterface { + private ThirdPartyRepository $repository; + use NormalizerAwareTrait; + public function __construct(ThirdPartyRepository $repository) + { + $this->repository= $repository; + } + public function normalize($thirdParty, string $format = null, array $context = []) { /** @var $thirdParty ThirdParty */ @@ -28,4 +41,30 @@ class ThirdPartyNormalizer implements NormalizerInterface, NormalizerAwareInterf { return $data instanceof ThirdParty; } + + public function denormalize($data, string $type, string $format = null, array $context = []) + { + $thirdParty = $context[AbstractNormalizer::OBJECT_TO_POPULATE] ?? NULL; + if (NULL === $thirdParty) { + $id = $data['thirdparty_id'] ?? NULL; + if (NULL === $id) { + throw new RuntimeException("mission thirdparty_id into body"); + } + + $thirdParty = $this->repository->findOneById($id); + + if (NULL === $thirdParty) { + throw new UnexpectedValueException("thirdparty not found"); + } + } + + return $thirdParty; + } + + public function supportsDenormalization($data, string $type, string $format = null) + { + return ThirdParty::class === $type; + } + + } diff --git a/src/Bundle/ChillThirdPartyBundle/config/services.yaml b/src/Bundle/ChillThirdPartyBundle/config/services.yaml index 4f179f43e..438ed3ff0 100644 --- a/src/Bundle/ChillThirdPartyBundle/config/services.yaml +++ b/src/Bundle/ChillThirdPartyBundle/config/services.yaml @@ -1,2 +1,13 @@ --- services: + Chill\ThirdPartyBundle\Serializer\Normalizer\: + autowire: true + resource: '../Serializer/Normalizer/' + tags: + - { name: 'serializer.normalizer', priority: 64 } + + Chill\ThirdPartyBundle\Repository\: + autowire: true + resource: '../Repository/' + tags: + - { name: 'doctrine.repository_service' } diff --git a/src/Bundle/ChillThirdPartyBundle/config/services/serializer.yaml b/src/Bundle/ChillThirdPartyBundle/config/services/serializer.yaml index 286bb74ec..0baad4766 100644 --- a/src/Bundle/ChillThirdPartyBundle/config/services/serializer.yaml +++ b/src/Bundle/ChillThirdPartyBundle/config/services/serializer.yaml @@ -1,5 +1 @@ services: - Chill\ThirdPartyBundle\Serializer\Normalizer\: - resource: '../../Serializer/Normalizer/' - tags: - - { name: 'serializer.normalizer', priority: 64 }