From bf372f8586b0e36698e859a0dd03ccf4843f369a Mon Sep 17 00:00:00 2001 From: Mathieu MARCHOIS Date: Thu, 19 Sep 2024 16:09:54 +0200 Subject: [PATCH] Remove model visa (#966) --- .../Command/DeleteVisaModelCommand.php | 15 ++++ .../Command/DeleteVisaModelCommandHandler.php | 27 ++++++ .../VisaModelRepositoryInterface.php | 2 + .../User/DeleteOrganizationUserController.php | 3 +- .../VisaModel/DeleteVisaModelController.php | 60 +++++++++++++ .../VisaModel/VisaModelRepository.php | 5 ++ .../organization/visa_model/index.html.twig | 90 ++++++++++++------- .../DeleteVisaModelControllerTest.php | 74 +++++++++++++++ .../DeleteVisaModelCommandHandlerTest.php | 68 ++++++++++++++ translations/messages.fr.xlf | 8 ++ 10 files changed, 317 insertions(+), 35 deletions(-) create mode 100644 src/Application/VisaModel/Command/DeleteVisaModelCommand.php create mode 100644 src/Application/VisaModel/Command/DeleteVisaModelCommandHandler.php create mode 100644 src/Infrastructure/Controller/MyArea/Organization/VisaModel/DeleteVisaModelController.php create mode 100644 tests/Integration/Infrastructure/Controller/MyArea/Organization/VisaModel/DeleteVisaModelControllerTest.php create mode 100644 tests/Unit/Application/VisaModel/Command/DeleteVisaModelCommandHandlerTest.php diff --git a/src/Application/VisaModel/Command/DeleteVisaModelCommand.php b/src/Application/VisaModel/Command/DeleteVisaModelCommand.php new file mode 100644 index 000000000..cc3fa00e7 --- /dev/null +++ b/src/Application/VisaModel/Command/DeleteVisaModelCommand.php @@ -0,0 +1,15 @@ +visaModelRepository->findOneByUuid($command->uuid); + if (!$visaModel instanceof VisaModel) { + throw new VisaModelNotFoundException(); + } + + $this->visaModelRepository->remove($visaModel); + } +} diff --git a/src/Domain/VisaModel/Repository/VisaModelRepositoryInterface.php b/src/Domain/VisaModel/Repository/VisaModelRepositoryInterface.php index 370db5ec3..e0a09ffeb 100644 --- a/src/Domain/VisaModel/Repository/VisaModelRepositoryInterface.php +++ b/src/Domain/VisaModel/Repository/VisaModelRepositoryInterface.php @@ -13,4 +13,6 @@ public function findOneByUuid(string $uuid): ?VisaModel; public function findOrganizationVisaModels(string $organizationUuid): array; public function add(VisaModel $visaModel): VisaModel; + + public function remove(VisaModel $visaModel): void; } diff --git a/src/Infrastructure/Controller/MyArea/Organization/User/DeleteOrganizationUserController.php b/src/Infrastructure/Controller/MyArea/Organization/User/DeleteOrganizationUserController.php index bb6382600..e28127183 100644 --- a/src/Infrastructure/Controller/MyArea/Organization/User/DeleteOrganizationUserController.php +++ b/src/Infrastructure/Controller/MyArea/Organization/User/DeleteOrganizationUserController.php @@ -13,7 +13,6 @@ use App\Infrastructure\Security\Voter\OrganizationVoter; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -39,7 +38,7 @@ public function __construct( methods: ['DELETE'], )] #[IsCsrfTokenValid('delete-user')] - public function __invoke(Request $request, string $organizationUuid, string $uuid): RedirectResponse + public function __invoke(string $organizationUuid, string $uuid): RedirectResponse { try { $organizationUser = $this->queryBus->handle(new GetOrganizationUserQuery($organizationUuid, $uuid)); diff --git a/src/Infrastructure/Controller/MyArea/Organization/VisaModel/DeleteVisaModelController.php b/src/Infrastructure/Controller/MyArea/Organization/VisaModel/DeleteVisaModelController.php new file mode 100644 index 000000000..f4aaf89e7 --- /dev/null +++ b/src/Infrastructure/Controller/MyArea/Organization/VisaModel/DeleteVisaModelController.php @@ -0,0 +1,60 @@ + Requirement::UUID, 'uuid' => Requirement::UUID], + methods: ['DELETE'], + )] + #[IsCsrfTokenValid('delete-visa-model')] + public function __invoke(string $organizationUuid, string $uuid): RedirectResponse + { + $organization = $this->getOrganization($organizationUuid); + + if (!$this->security->isGranted(OrganizationVoter::EDIT, $organization)) { + throw new AccessDeniedHttpException(); + } + + try { + $this->commandBus->handle(new DeleteVisaModelCommand($uuid)); + } catch (VisaModelNotFoundException) { + throw new NotFoundHttpException(); + } + + return new RedirectResponse( + url: $this->router->generate('app_config_visa_models_list', ['uuid' => $organizationUuid]), + status: Response::HTTP_SEE_OTHER, + ); + } +} diff --git a/src/Infrastructure/Persistence/Doctrine/Repository/VisaModel/VisaModelRepository.php b/src/Infrastructure/Persistence/Doctrine/Repository/VisaModel/VisaModelRepository.php index 33e6e4a2d..baacbbb70 100644 --- a/src/Infrastructure/Persistence/Doctrine/Repository/VisaModel/VisaModelRepository.php +++ b/src/Infrastructure/Persistence/Doctrine/Repository/VisaModel/VisaModelRepository.php @@ -24,6 +24,11 @@ public function add(VisaModel $visaModel): VisaModel return $visaModel; } + public function remove(VisaModel $visaModel): void + { + $this->getEntityManager()->remove($visaModel); + } + public function findOneByUuid(string $uuid): ?VisaModel { return $this->createQueryBuilder('v') diff --git a/templates/my_area/organization/visa_model/index.html.twig b/templates/my_area/organization/visa_model/index.html.twig index 11c8a5e87..f811fa92b 100644 --- a/templates/my_area/organization/visa_model/index.html.twig +++ b/templates/my_area/organization/visa_model/index.html.twig @@ -4,6 +4,8 @@ {{'visa.list.title'|trans }} - {{ parent() }} {% endblock %} +{% set deleteCsrfToken = csrf_token('delete-visa-model') %} + {% block body %}
@@ -25,41 +27,63 @@ {% endif %}

{{ 'visa.list.help'|trans }}

-
-
-
-
- - - - - - - - - - {% for visa in visaModels %} - - - - - - {% endfor %} - -
{{ 'visa.name'|trans }}{{ 'visa.description'|trans }}{{ 'common.actions'|trans }}
{{ visa.name }}{{ visa.description }} - {% if is_granted(constant('App\\Infrastructure\\Security\\Voter\\OrganizationVoter::EDIT'), organization) %} - - {% endif %} -
-
-
-
+
+ + + + + + + + + + {% for visa in visaModels %} + + + + + + {% endfor %} + +
{{ 'visa.name'|trans }}{{ 'visa.description'|trans }}{{ 'common.actions'|trans }}
{{ visa.name }}{{ visa.description }} + {% if is_granted(constant('App\\Infrastructure\\Security\\Voter\\OrganizationVoter::EDIT'), organization) %} + + {% endif %} +
{% endblock %} + +{% block body_end %} + {{ parent() }} + {% include 'common/confirmation_modal.html.twig' with { + id: 'visa-delete-modal', + title: 'visa.delete_modal.title'|trans, + buttons: [ + { label: 'common.delete'|trans, attr: {type: 'submit', class: 'fr-btn'} }, + { label: 'common.do_not_delete'|trans, attr: {value: 'close', class: 'fr-btn fr-btn--secondary'} }, + ] + } only %} +{% endblock %} diff --git a/tests/Integration/Infrastructure/Controller/MyArea/Organization/VisaModel/DeleteVisaModelControllerTest.php b/tests/Integration/Infrastructure/Controller/MyArea/Organization/VisaModel/DeleteVisaModelControllerTest.php new file mode 100644 index 000000000..c562a2b0b --- /dev/null +++ b/tests/Integration/Infrastructure/Controller/MyArea/Organization/VisaModel/DeleteVisaModelControllerTest.php @@ -0,0 +1,74 @@ +login('mathieu.fernandez@beta.gouv.fr'); + $client->request('DELETE', '/mon-espace/organizations/' . OrganizationFixture::MAIN_ORG_ID . '/visa_models/7eca6579-c07e-4e8e-8f10-fda610d7ee73', [ + '_token' => $this->generateCsrfToken($client, 'delete-visa-model'), + ]); + + $this->assertResponseStatusCodeSame(303); + $client->followRedirect(); + + $this->assertResponseStatusCodeSame(200); + $this->assertRouteSame('app_config_visa_models_list'); + } + + public function testNotFound(): void + { + $client = $this->login('mathieu.fernandez@beta.gouv.fr'); + $client->request('DELETE', '/mon-espace/organizations/' . OrganizationFixture::MAIN_ORG_ID . '/visa_models/e18d61be-1797-4d6b-aa58-cd75e623a821', [ + '_token' => $this->generateCsrfToken($client, 'delete-visa-model'), + ]); + + $this->assertResponseStatusCodeSame(404); + } + + public function testOrganizationNotOwned(): void + { + $client = $this->login(); + $client->request('DELETE', '/mon-espace/organizations/' . OrganizationFixture::MAIN_ORG_ID . '/visa_models/7eca6579-c07e-4e8e-8f10-fda610d7ee73', [ + '_token' => $this->generateCsrfToken($client, 'delete-visa-model'), + ]); + + $this->assertResponseStatusCodeSame(403); + } + + public function testBadAccessToken(): void + { + $client = $this->login('mathieu.fernandez@beta.gouv.fr'); + $client->request('DELETE', '/mon-espace/organizations/' . OrganizationFixture::MAIN_ORG_ID . '/visa_models/7eca6579-c07e-4e8e-8f10-fda610d7ee73', [ + '_token' => 'abc', + ]); + + $this->assertResponseRedirects('http://localhost/login', 302); + } + + public function testOrganizationOrUserNotFound(): void + { + $client = $this->login(); + $client->request('DELETE', '/mon-espace/organizations/f5c1cea8-a61d-43a7-9b5d-4b8c9557c673/visa_models/7eca6579-c07e-4e8e-8f10-fda610d7ee73', [ + '_token' => $this->generateCsrfToken($client, 'delete-visa-model'), + ]); + $this->assertResponseStatusCodeSame(404); + } + + public function testWithoutAuthenticatedUser(): void + { + $client = static::createClient(); + $client->request('DELETE', '/mon-espace/organizations/' . OrganizationFixture::MAIN_ORG_ID . '/visa_models/7eca6579-c07e-4e8e-8f10-fda610d7ee73'); + $this->assertResponseRedirects('http://localhost/login', 302); + } +} diff --git a/tests/Unit/Application/VisaModel/Command/DeleteVisaModelCommandHandlerTest.php b/tests/Unit/Application/VisaModel/Command/DeleteVisaModelCommandHandlerTest.php new file mode 100644 index 000000000..949043105 --- /dev/null +++ b/tests/Unit/Application/VisaModel/Command/DeleteVisaModelCommandHandlerTest.php @@ -0,0 +1,68 @@ +visaModelRepository = $this->createMock(VisaModelRepositoryInterface::class); + } + + public function testRemove(): void + { + $visaModel = $this->createMock(VisaModel::class); + + $this->visaModelRepository + ->expects(self::once()) + ->method('findOneByUuid') + ->with('f8216679-5a0b-4dd5-9e2b-b382d298c3b4') + ->willReturn($visaModel); + + $this->visaModelRepository + ->expects(self::once()) + ->method('remove') + ->with($visaModel); + + $handler = new DeleteVisaModelCommandHandler( + $this->visaModelRepository, + ); + $command = new DeleteVisaModelCommand('f8216679-5a0b-4dd5-9e2b-b382d298c3b4'); + + $handler($command); + } + + public function testNotFound(): void + { + $this->expectException(VisaModelNotFoundException::class); + + $this->visaModelRepository + ->expects(self::once()) + ->method('findOneByUuid') + ->with('f8216679-5a0b-4dd5-9e2b-b382d298c3b4') + ->willReturn(null); + + $this->visaModelRepository + ->expects(self::never()) + ->method('remove'); + + $handler = new DeleteVisaModelCommandHandler( + $this->visaModelRepository, + ); + $command = new DeleteVisaModelCommand('f8216679-5a0b-4dd5-9e2b-b382d298c3b4'); + + $handler($command); + } +} diff --git a/translations/messages.fr.xlf b/translations/messages.fr.xlf index 177bf85e7..29f7d387b 100644 --- a/translations/messages.fr.xlf +++ b/translations/messages.fr.xlf @@ -2344,6 +2344,14 @@ myarea.title Mon espace + + visa.delete_modal.title + Supprimer ce modèle ? + + + visa.list.delete + Supprimer le modèle "%name%" de l'organisation +