From 07e06927831b85e5b2fdb6b1f3b36e14cf30ac71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 6 May 2021 00:14:36 +0200 Subject: [PATCH] bootstrap api and apply on accompanying period --- .../CRUDControllerCompilerPass.php | 76 +++++++++++++ .../Controller/AbstractCRUDController.php | 19 +++- .../CRUD/Controller/ApiController.php | 19 +--- .../CRUD/Routing/CRUDRoutesLoader.php | 8 +- .../ChillMainBundle/ChillMainBundle.php | 2 +- .../ChillMainExtension.php | 91 ++-------------- .../CRUDControllerCompilerPass.php | 103 ------------------ .../AccompanyingCourseApiController.php | 47 ++++++-- .../DataFixtures/ORM/LoadPersonACL.php | 8 ++ .../ChillPersonExtension.php | 4 +- .../Entity/AccompanyingPeriod.php | 12 +- .../AccompanyingPeriodNormalizer.php | 2 +- .../Normalizer/PersonNormalizer.php | 2 +- ...> AccompanyingCourseApiControllerTest.php} | 58 ++++++++-- .../Tests/Entity/AccompanyingPeriodTest.php | 35 +++--- .../config/services/controller.yaml | 6 + 16 files changed, 235 insertions(+), 257 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/CRUD/CompilerPass/CRUDControllerCompilerPass.php delete mode 100644 src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/CRUDControllerCompilerPass.php rename src/Bundle/ChillPersonBundle/Tests/Controller/{AccompanyingCourseControllerTest.php => AccompanyingCourseApiControllerTest.php} (71%) diff --git a/src/Bundle/ChillMainBundle/CRUD/CompilerPass/CRUDControllerCompilerPass.php b/src/Bundle/ChillMainBundle/CRUD/CompilerPass/CRUDControllerCompilerPass.php new file mode 100644 index 000000000..43424d4f9 --- /dev/null +++ b/src/Bundle/ChillMainBundle/CRUD/CompilerPass/CRUDControllerCompilerPass.php @@ -0,0 +1,76 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\MainBundle\CRUD\CompilerPass; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; +use Chill\MainBundle\Routing\MenuComposer; +use Symfony\Component\DependencyInjection\Definition; + +/** + * + * + */ +class CRUDControllerCompilerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $crudConfig = $container->getParameter('chill_main_crud_route_loader_config'); + $apiConfig = $container->getParameter('chill_main_api_route_loader_config'); + + foreach ($crudConfig as $crudEntry) { + $this->configureCrudController($container, $crudEntry, 'crud'); + } + + foreach ($apiConfig as $crudEntry) { + $this->configureCrudController($container, $crudEntry, 'api'); + } + } + + /** + * Add a controller for each definition, and add a methodCall to inject crud configuration to controller + */ + private function configureCrudController(ContainerBuilder $container, array $crudEntry, string $apiOrCrud): void + { + $controllerClass = $crudEntry['controller']; + + $controllerServiceName = 'cs'.$apiOrCrud.'_'.$crudEntry['name'].'_controller'; + + if ($container->hasDefinition($controllerClass)) { + $controller = $container->getDefinition($controllerClass); + $container->removeDefinition($controllerClass); + $alreadyDefined = true; + } else { + $controller = new Definition($controllerClass); + $alreadyDefined = false; + } + + $controller->addTag('controller.service_arguments'); + if (FALSE === $alreadyDefined) { + $controller->setAutoconfigured(true); + } + + $param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name']; + $container->setParameter($param, $crudEntry); + $controller->addMethodCall('setCrudConfig', ['%'.$param.'%']); + + $container->setDefinition($controllerServiceName, $controller); + } + +} diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php index 363d65152..31141a857 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php @@ -33,6 +33,7 @@ class AbstractCRUDController extends AbstractController } /** + * Get the complete FQDN of the class * * @return string the complete fqdn of the class */ @@ -42,7 +43,7 @@ class AbstractCRUDController extends AbstractController } /** - * + * called on post fetch entity */ protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response { @@ -50,14 +51,13 @@ class AbstractCRUDController extends AbstractController } /** - * + * Called on post check ACL */ protected function onPostCheckACL(string $action, Request $request, $entity, $_format): ?Response { return null; } - /** * check the acl. Called by every action. * @@ -74,12 +74,20 @@ class AbstractCRUDController extends AbstractController $this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity); } + /** + * + * @return string the crud name + */ + protected function getCrudName(): string + { + return $this->crudConfig['name']; + } + protected function getActionConfig(string $action) { return $this->crudConfig['actions'][$action]; } - - + /** * Set the crud configuration * @@ -87,7 +95,6 @@ class AbstractCRUDController extends AbstractController */ public function setCrudConfig(array $config): void { - dump($config); $this->crudConfig = $config; } diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php index c12c8e2b7..7256720bb 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php @@ -5,6 +5,7 @@ namespace Chill\MainBundle\CRUD\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Serializer\SerializerInterface; class ApiController extends AbstractCRUDController { @@ -114,22 +115,6 @@ class ApiController extends AbstractCRUDController protected function getSerializer(): SerializerInterface { - return $this->get(SerializerInterface::class); + return $this->get('serializer'); } - - /** - * Defined the services necessary for this controller - * - * @return array - */ - public static function getSubscribedServices(): array - { - return \array_merge( - parent::getSubscribedServices(), - [ - SerializerInterface::class => SerializerInterface::class, - ] - ); - } - } diff --git a/src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php b/src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php index 69b9fca7e..b09bbc55b 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php +++ b/src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php @@ -106,8 +106,7 @@ class CRUDRoutesLoader extends Loader protected function loadCrudConfig($crudConfig): RouteCollection { $collection = new RouteCollection(); - $controller = $crudConfig['controller'] === CrudController::class ? - 'cscrud_'.$crudConfig['name'].'_controller' : $crudConfig['controller']; + $controller ='cscrud_'.$crudConfig['name'].'_controller'; foreach ($crudConfig['actions'] as $name => $action) { // defaults (controller name) @@ -144,8 +143,7 @@ class CRUDRoutesLoader extends Loader protected function loadApiSingle(array $crudConfig): RouteCollection { $collection = new RouteCollection(); - $controller = $crudConfig['controller'] === ApiController::class ? - 'cscrud_'.$crudConfig['name'].'_controller' : $crudConfig['controller']; + $controller ='csapi_'.$crudConfig['name'].'_controller'; foreach ($crudConfig['actions'] as $name => $action) { // filter only on single actions @@ -160,7 +158,7 @@ class CRUDRoutesLoader extends Loader // path are rewritten // if name === 'default', we rewrite it to nothing :-) - $localName = '_entity' === $name ? '' : $name; + $localName = '_entity' === $name ? '' : '/'.$name; $localPath = $action['path'] ?? '/{id}'.$localName.'.{_format}'; $path = $crudConfig['base_path'].$localPath; diff --git a/src/Bundle/ChillMainBundle/ChillMainBundle.php b/src/Bundle/ChillMainBundle/ChillMainBundle.php index fba4db2cf..51ef344f2 100644 --- a/src/Bundle/ChillMainBundle/ChillMainBundle.php +++ b/src/Bundle/ChillMainBundle/ChillMainBundle.php @@ -14,7 +14,7 @@ use Chill\MainBundle\DependencyInjection\CompilerPass\NotificationCounterCompile use Chill\MainBundle\DependencyInjection\CompilerPass\MenuCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\ACLFlagsCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\GroupingCenterCompilerPass; -use Chill\MainBundle\DependencyInjection\CompilerPass\CRUDControllerCompilerPass; +use Chill\MainBundle\CRUD\CompilerPass\CRUDControllerCompilerPass; use Chill\MainBundle\Templating\Entity\CompilerPass as RenderEntityCompilerPass; diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index da3f27d8b..00b164303 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -133,7 +133,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, $loader->load('services/search.yaml'); $loader->load('services/serializer.yaml'); - $this->configureCruds($container, $config['cruds'], $config['apis'], $loader); + $this->configureCruds($container, $config['cruds'], $config['apis'], $loader); } /** @@ -210,95 +210,24 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, } /** - * @param ContainerBuilder $container - * @param array $config the config under 'cruds' key - * @return null + * Load parameter for configuration and set parameters for api */ - protected function configureCruds(ContainerBuilder $container, $crudConfig, $apiConfig, Loader\YamlFileLoader $loader) + protected function configureCruds( + ContainerBuilder $container, + array $crudConfig, + array $apiConfig, + Loader\YamlFileLoader $loader + ): void { - if ((count($crudConfig) + count($apiConfig)) === 0) { + if (count($crudConfig) === 0) { return; } -// dump(array_keys($container->getDefinitions())); - $loader->load('services/crud.yaml'); $container->setParameter('chill_main_crud_route_loader_config', $crudConfig); $container->setParameter('chill_main_api_route_loader_config', $apiConfig); - return; -/* - $definition = new Definition(); - $definition - ->setClass(\Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader::class) - ->addArgument('%chill_main_crud_route_loader_config%') - ->addArgument('%chill_main_api_route_loader_config%') - ; - $container->setDefinition('chill_main_crud_route_loader', $definition); - */ - - $alreadyExistingNames = []; - - foreach ($crudConfig as $crudEntry) { - $this->configureCrudController($container, $crudEntry, 'crud'); - } - - foreach ($apiConfig as $crudEntry) { - $this->configureCrudController($container, $crudEntry, 'crud'); - } + // Note: the controller are loaded inside compiler pass } - - /** - * Add a controller for each definition, and add a methodCall to inject crud configuration to controller - */ - private function configureCrudController(ContainerBuilder $container, array $crudEntry, string $apiOrCrud): void - { - $controller = $crudEntry['controller']; - $controllerServiceName = 'cscrud_'.$crudEntry['name'].'_controller'; - $name = $crudEntry['name']; - - // check for existing crud names - /*if (\in_array($name, $alreadyExistingNames)) { - throw new LogicException(sprintf("the name %s is defined twice in CRUD", $name)); - }*/ - - if (!$container->has($controllerServiceName)) { - $controllerDefinition = new Definition($controller); - $controllerDefinition->addTag('controller.service_arguments'); - $controllerDefinition->setAutoconfigured(true); - $controllerDefinition->setClass($crudEntry['controller']); - $container->setDefinition($controllerServiceName, $controllerDefinition); - } - - $container->setParameter('chill_main_'.$apiOrCrud.'_config_'.$name, $crudEntry); - $container->getDefinition($controllerServiceName) - ->addMethodCall('setCrudConfig', ['%chill_main_'.$apiOrCrud.'_config_'.$name.'%']); -/* - dump($controllerClass); - - if ($container->hasDefinition($controllerClass)) { - dump('container has controller class'); - $controllerServiceName = $controllerClass; - $controller = $container->getDefinition($controllerServiceName); - $param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name']; - $container->setParameter($param, $crudEntry); - $controller->addMethodCall('setCrudConfig', ['%'.$param.'%']); - dump(__LINE__, $controller); - $controller->setDefinition($controllerServiceName, $controller); - } else { - $controllerServiceName = 'cs'.$apiOrCrud.'_'.$crudEntry['name'].'_controller'; - $controller = new Definition($controllerClass); - $controller->addTag('controller.service_arguments'); - $controller->setAutoconfigured(true); - $controller->setClass($controllerClass); - $param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name']; - $container->setParameter($param, $crudEntry); - $controller->addMethodCall('setCrudConfig', ['%'.$param.'%']); - dump(__LINE__, $controller); - $container->setDefinition($controllerServiceName, $controller); - }*/ - - } - } diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/CRUDControllerCompilerPass.php b/src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/CRUDControllerCompilerPass.php deleted file mode 100644 index 77ef023dd..000000000 --- a/src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/CRUDControllerCompilerPass.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -namespace Chill\MainBundle\DependencyInjection\CompilerPass; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Reference; -use Chill\MainBundle\Routing\MenuComposer; -use Symfony\Component\DependencyInjection\Definition; - -/** - * - * - */ -class CRUDControllerCompilerPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container) - { - $crudConfig = $container->getParameter('chill_main_crud_route_loader_config'); - $apiConfig = $container->getParameter('chill_main_api_route_loader_config'); - - - foreach ($crudConfig as $crudEntry) { - $this->configureCrudController($container, $crudEntry, 'crud'); - } - - foreach ($apiConfig as $crudEntry) { - $this->configureCrudController($container, $crudEntry, 'crud'); - } - } - - /** - * Add a controller for each definition, and add a methodCall to inject crud configuration to controller - */ - private function configureCrudController(ContainerBuilder $container, array $crudEntry, string $apiOrCrud): void - { - $controller = $crudEntry['controller']; - $controllerServiceName = 'cscrud_'.$crudEntry['name'].'_controller'; - $name = $crudEntry['name']; - - // check for existing crud names - /*if (\in_array($name, $alreadyExistingNames)) { - throw new LogicException(sprintf("the name %s is defined twice in CRUD", $name)); - } - - if (!$container->has($controllerServiceName)) { - $controllerDefinition = new Definition($controller); - $controllerDefinition->addTag('controller.service_arguments'); - $controllerDefinition->setAutoconfigured(true); - $controllerDefinition->setClass($crudEntry['controller']); - $container->setDefinition($controllerServiceName, $controllerDefinition); - } - - $container->setParameter('chill_main_'.$apiOrCrud.'_config_'.$name, $crudEntry); - $container->getDefinition($controllerServiceName) - ->addMethodCall('setCrudConfig', ['%chill_main_'.$apiOrCrud.'_config_'.$name.'%']); -/* - dump($controllerClass); - */ - - $controllerClass = $crudEntry['controller']; - dump('in_array', $controllerClass, \in_array($controllerClass, \array_keys($container->getDefinitions()))); - - if ($container->hasDefinition($controllerClass)) { - dump('container has controller class'); - $controllerServiceName = $controllerClass; - $controller = $container->getDefinition($controllerServiceName); - $param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name']; - $container->setParameter($param, $crudEntry); - $controller->addMethodCall('setCrudConfig', ['%'.$param.'%']); - dump(__LINE__, $controller); - $controller->setDefinition($controllerServiceName, $controller); - } else { - $controllerServiceName = 'cs'.$apiOrCrud.'_'.$crudEntry['name'].'_controller'; - $controller = new Definition($controllerClass); - $controller->addTag('controller.service_arguments'); - $controller->setAutoconfigured(true); - $controller->setClass($controllerClass); - $param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name']; - $container->setParameter($param, $crudEntry); - $controller->addMethodCall('setCrudConfig', ['%'.$param.'%']); - dump(__LINE__, $controller); - $container->setDefinition($controllerServiceName, $controller); - } - - } - -} diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php index a5f068c30..2716d1c1a 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php @@ -8,33 +8,51 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Symfony\Component\HttpFoundation\Exception\BadRequestException; - +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Validator\Validator\ValidatorInterface; +use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent; +use Chill\PersonBundle\Entity\Person; class AccompanyingCourseApiController extends ApiController { - public function participationApi($accompanyingPeriodId, Request $request) + protected EventDispatcherInterface $eventDispatcher; + + protected ValidatorInterface $validator; + + public function __construct(EventDispatcherInterface $eventDispatcher, $validator) + { + $this->eventDispatcher = $eventDispatcher; + $this->validator = $validator; + } + + public function participationApi($id, Request $request, $_format) { /** @var AccompanyingPeriod $accompanyingPeriod */ - $accompanyingPeriod = $this->getEntity($accompanyingPeriodId); - $person = $this->serializer->deserialize($request->getContent(), Person::class, $_format, []); + $accompanyingPeriod = $this->getEntity('participation', $id, $request); + $person = $this->getSerializer() + ->deserialize($request->getContent(), Person::class, $_format, []); if (NULL === $person) { throw new BadRequestException('person id not found'); } // TODO add acl + // + $this->onPostCheckACL('participation', $request, $accompanyingPeriod, $_format); + switch ($request->getMethod()) { case Request::METHOD_POST: - $participation = $accompanyingCours->addPerson($person); + $participation = $accompanyingPeriod->addPerson($person); break; case Request::METHOD_DELETE: - $participation = $accompanyingCours->removePerson($person); + $participation = $accompanyingPeriod->removePerson($person); + $participation->setEndDate(new \DateTimeImmutable('now')); break; default: throw new BadRequestException("This method is not supported"); } - $errors = $this->validator->validate($accompanyingCourse); + $errors = $this->validator->validate($accompanyingPeriod); if ($errors->count() > 0) { // only format accepted @@ -44,5 +62,18 @@ class AccompanyingCourseApiController extends ApiController $this->getDoctrine()->getManager()->flush(); return $this->json($participation); - } + } + + protected function onPostCheckACL(string $action, Request $request, $entity, $_format): ?Response + { + $this->eventDispatcher->dispatch( + AccompanyingPeriodPrivacyEvent::ACCOMPANYING_PERIOD_PRIVACY_EVENT, + new AccompanyingPeriodPrivacyEvent($entity, [ + 'action' => $action, + 'request' => $request->getMethod() + ]) + ); + + return null; + } } diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPersonACL.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPersonACL.php index a3f42ced9..c8593d1fd 100644 --- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPersonACL.php +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPersonACL.php @@ -25,6 +25,7 @@ use Doctrine\Persistence\ObjectManager; use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup; use Chill\MainBundle\Entity\RoleScope; use Chill\PersonBundle\Security\Authorization\PersonVoter; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; /** * Add a role CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE for all groups except administrative, @@ -44,6 +45,7 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface { foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) { $permissionsGroup = $this->getReference($permissionsGroupRef); + $scopeSocial = $this->getReference('scope_social'); //create permission group switch ($permissionsGroup->getName()) { @@ -51,6 +53,12 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface case 'direction': printf("Adding CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE to %s permission group \n", $permissionsGroup->getName()); + $permissionsGroup->addRoleScope( + (new RoleScope()) + ->setRole(AccompanyingPeriodVoter::SEE) + ->setScope($scopeSocial) + ); + $roleScopeUpdate = (new RoleScope()) ->setRole('CHILL_PERSON_UPDATE') ->setScope(null); diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index 085fedc79..909df750f 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -315,7 +315,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac [ 'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod::class, 'name' => 'accompanying_course', - 'base_path' => '/api/1.0/accompanying_course', + 'base_path' => '/api/1.0/person/accompanying-course', 'controller' => \Chill\PersonBundle\Controller\AccompanyingCourseApiController::class, 'actions' => [ '_entity' => [ @@ -323,7 +323,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE ] ], - '/participation' => [ + 'participation' => [ 'methods' => [ Request::METHOD_POST => true, Request::METHOD_DELETE => true, diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 46028f028..8b60888bd 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -118,7 +118,7 @@ class AccompanyingPeriod * * @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class, * mappedBy="accompanyingPeriod", - * cascade={"persist", "remove", "merge", "detach"}) + * cascade={"persist", "refresh", "remove", "merge", "detach"}) */ private $participations; @@ -348,7 +348,7 @@ class AccompanyingPeriod */ public function getParticipationsContainsPerson(Person $person): Collection { - return $this->getParticipations()->filter( + return $this->getParticipations($person)->filter( function(AccompanyingPeriodParticipation $participation) use ($person) { if ($person === $participation->getPerson()) { return $participation; @@ -361,11 +361,11 @@ class AccompanyingPeriod * * "Open" means that the closed date is NULL */ - public function getOpenParticipationsContainsPerson(Person $person): ?AccompanyingPeriodParticipation + public function getOpenParticipationContainsPerson(Person $person): ?AccompanyingPeriodParticipation { - $collection = $this->getParticipationsContainsPerson()->filter( + $collection = $this->getParticipationsContainsPerson($person)->filter( function(AccompanyingPeriodParticipation $participation) use ($person) { - if (NULL === $participation->getClosingDate()) { + if (NULL === $participation->getEndDate()) { return $participation; } }); @@ -380,7 +380,7 @@ class AccompanyingPeriod */ public function containsPerson(Person $person): bool { - return $this->participationsContainsPerson($person)->count() > 0; + return $this->getParticipationsContainsPerson($person)->count() > 0; } /** diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodNormalizer.php index 55a59700d..7cf2330cb 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodNormalizer.php @@ -38,7 +38,7 @@ class AccompanyingPeriodNormalizer implements NormalizerInterface, NormalizerAwa 'remark' => $period->getRemark(), 'participations' => $this->normalizer->normalize($period->getParticipations(), $format), 'closingMotive' => $this->normalizer->normalize($period->getClosingMotive(), $format), - 'user' => $period->getUser() ? $this->normalize($period->getUser(), $format) : null, + 'user' => $period->getUser() ? $this->normalizer->normalize($period->getUser(), $format) : null, 'step' => $period->getStep(), 'origin' => $this->normalizer->normalize($period->getOrigin(), $format), 'intensity' => $period->getIntensity(), diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php index 90a816ebc..d88d27ddc 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php @@ -55,7 +55,7 @@ class PersonNormalizer implements 'id' => $person->getId(), 'firstName' => $person->getFirstName(), 'lastName' => $person->getLastName(), - 'birthdate' => $person->getBirthdate(), + 'birthdate' => $this->normalizer->normalize($person->getBirthdate()), 'center' => $this->normalizer->normalize($person->getCenter()) ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php similarity index 71% rename from src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseControllerTest.php rename to src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php index c45cb93d9..61b5307a4 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseControllerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php @@ -36,7 +36,7 @@ use Symfony\Component\HttpFoundation\Request; /** * Test api for AccompanyingCourseControllerTest */ -class AccompanyingCourseControllerTest extends WebTestCase +class AccompanyingCourseApiControllerTest extends WebTestCase { protected static EntityManagerInterface $em; @@ -65,7 +65,7 @@ class AccompanyingCourseControllerTest extends WebTestCase */ public function testAccompanyingCourseShow(int $personId, AccompanyingPeriod $period) { - $this->client->request(Request::METHOD_GET, sprintf('/fr/person/api/1.0/accompanying-course/%d/show.json', $period->getId())); + $c = $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $period->getId())); $response = $this->client->getResponse(); $this->assertEquals(200, $response->getStatusCode(), "Test that the response of rest api has a status code ok (200)"); @@ -77,6 +77,14 @@ class AccompanyingCourseControllerTest extends WebTestCase $this->assertGreaterThan(0, $data->participations); } + public function testShow404() + { + $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', 99999)); + $response = $this->client->getResponse(); + + $this->assertEquals(404, $response->getStatusCode(), "Test that the response of rest api has a status code 'not found' (404)"); + } + /** * * @dataProvider dataGenerateRandomAccompanyingCourse @@ -85,26 +93,55 @@ class AccompanyingCourseControllerTest extends WebTestCase { $this->client->request( Request::METHOD_POST, - sprintf('/fr/person/api/1.0/accompanying-course/%d/participation.json', $period->getId()), + sprintf('/api/1.0/person/accompanying-course/%d/participation.json', $period->getId()), [], // parameters [], // files [], // server parameters \json_encode([ 'id' => $personId ]) ); $response = $this->client->getResponse(); + $data = \json_decode($response->getContent(), true); $this->assertEquals(200, $response->getStatusCode(), "Test that the response of rest api has a status code ok (200)"); - $this->client->request(Request::METHOD_GET, sprintf('/fr/person/api/1.0/accompanying-course/%d/show.json', $period->getId())); + $this->assertArrayHasKey('id', $data); + $this->assertArrayHasKey('startDate', $data); + $this->assertNotNull($data['startDate']); + + // check by deownloading the accompanying cours + + $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $period->getId())); $response = $this->client->getResponse(); $data = \json_decode($response->getContent()); + // check that the person id is contained $participationsPersonsIds = \array_map( function($participation) { return $participation->person->id; }, $data->participations); $this->assertContains($personId, $participationsPersonsIds); + // check removing the participation + $this->client->request( + Request::METHOD_DELETE, + sprintf('/api/1.0/person/accompanying-course/%d/participation.json', $period->getId()), + [], // parameters + [], // files + [], // server parameters + \json_encode([ 'id' => $personId ]) + ); + $response = $this->client->getResponse(); + $data = \json_decode($response->getContent(), true); + + $this->assertEquals(200, $response->getStatusCode(), "Test that the response of rest api has a status code ok (200)"); + $this->assertArrayHasKey('id', $data); + $this->assertArrayHasKey('startDate', $data); + $this->assertNotNull($data['startDate']); + $this->assertArrayHasKey('endDate', $data); + $this->assertNotNull($data['endDate']); + + + // set to variable for tear down $this->personId = $personId; $this->period = $period; } @@ -112,6 +149,7 @@ class AccompanyingCourseControllerTest extends WebTestCase protected function tearDown() { // remove participation created during test 'testAccompanyingCourseAddParticipation' + // and if the test could not remove it $testAddParticipationName = 'testAccompanyingCourseAddParticipation'; @@ -126,8 +164,10 @@ class AccompanyingCourseControllerTest extends WebTestCase ->findOneBy(['person' => $this->personId, 'accompanyingPeriod' => $this->period]) ; - $em->remove($participation); - $em->flush(); + if (NULL !== $participation) { + $em->remove($participation); + $em->flush(); + } } public function dataGenerateRandomAccompanyingCourse() @@ -139,9 +179,9 @@ class AccompanyingCourseControllerTest extends WebTestCase // * one for getting the person, which will in turn provide his accompanying period; // * one for getting the personId to populate to the data manager // - // Ensure to keep always $maxGenerated to the double of $maxResults - $maxGenerated = 1; - $maxResults = 15 * 8; + // Ensure to keep always $maxGenerated to the double of $maxResults. x8 is a good compromize :) + $maxGenerated = 3; + $maxResults = $maxGenerated * 8; static::bootKernel(); $em = static::$container->get(EntityManagerInterface::class); diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php index a31055a0d..a1d13892f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php @@ -27,7 +27,6 @@ use Chill\PersonBundle\Entity\Person; class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase { - public function testClosingIsAfterOpeningConsistency() { $datetime1 = new \DateTime('now'); @@ -77,22 +76,24 @@ class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase $this->assertFalse($period->isOpen()); } - public function testCanBeReOpened() + public function testPersonPeriod() { - $person = new Person(\DateTime::createFromFormat('Y-m-d', '2010-01-01')); - $person->close($person->getAccompanyingPeriods()[0] - ->setClosingDate(\DateTime::createFromFormat('Y-m-d', '2010-12-31'))); - - $firstAccompanygingPeriod = $person->getAccompanyingPeriodsOrdered()[0]; - - $this->assertTrue($firstAccompanygingPeriod->canBeReOpened()); - - $lastAccompanyingPeriod = (new AccompanyingPeriod(\DateTime::createFromFormat('Y-m-d', '2011-01-01'))) - ->setClosingDate(\DateTime::createFromFormat('Y-m-d', '2011-12-31')) - ; - $person->addAccompanyingPeriod($lastAccompanyingPeriod); - - $this->assertFalse($firstAccompanygingPeriod->canBeReOpened()); - } + $person = new Person(); + $period = new AccompanyingPeriod(new \DateTime()); + $period->addPerson($person); + + $this->assertEquals(1, $period->getParticipations()->count()); + $this->assertTrue($period->containsPerson($person)); + + $participation = $period->getOpenParticipationContainsPerson($person); + $participations = $period->getParticipationsContainsPerson($person); + $this->assertNotNull($participation); + $this->assertEquals(1, $participations->count()); + + $period->removePerson($person); + + $participation = $period->getOpenParticipationContainsPerson($person); + $this->assertNull($participation); + } } diff --git a/src/Bundle/ChillPersonBundle/config/services/controller.yaml b/src/Bundle/ChillPersonBundle/config/services/controller.yaml index 893b1cfd3..c2329e73a 100644 --- a/src/Bundle/ChillPersonBundle/config/services/controller.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/controller.yaml @@ -45,3 +45,9 @@ services: $dispatcher: '@Symfony\Contracts\EventDispatcher\EventDispatcherInterface' $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface' tags: ['controller.service_arguments'] + + Chill\PersonBundle\Controller\AccompanyingCourseApiController: + arguments: + $eventDispatcher: '@Symfony\Contracts\EventDispatcher\EventDispatcherInterface' + $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface' + tags: ['controller.service_arguments']