From 09c9a95fcf4b0e8356a12f48d3b6b7c3c06f7f7d Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Wed, 21 Apr 2021 15:56:17 +0200 Subject: [PATCH] added activity events for plugins (#2516) --- src/Activity/ActivityService.php | 104 +++++++++++++ src/Controller/ActivityController.php | 19 ++- src/Customer/CustomerService.php | 3 + src/Event/AbstractActivityEvent.php | 34 +++++ src/Event/ActivityCreateEvent.php | 17 +++ src/Event/ActivityCreatePostEvent.php | 17 +++ src/Event/ActivityCreatePreEvent.php | 17 +++ src/Event/ActivityUpdatePostEvent.php | 17 +++ src/Event/ActivityUpdatePreEvent.php | 17 +++ src/Project/ProjectService.php | 3 + tests/Activity/ActivityServiceTest.php | 157 ++++++++++++++++++++ tests/Event/AbstractActivityEventTest.php | 29 ++++ tests/Event/ActivityCreateEventTest.php | 26 ++++ tests/Event/ActivityCreatePostEventTest.php | 26 ++++ tests/Event/ActivityCreatePreEventTest.php | 26 ++++ tests/Event/ActivityUpdatePostEventTest.php | 26 ++++ tests/Event/ActivityUpdatePreEventTest.php | 26 ++++ tests/Event/ReportingEventTest.php | 35 +++++ 18 files changed, 591 insertions(+), 8 deletions(-) create mode 100644 src/Activity/ActivityService.php create mode 100644 src/Event/AbstractActivityEvent.php create mode 100644 src/Event/ActivityCreateEvent.php create mode 100644 src/Event/ActivityCreatePostEvent.php create mode 100644 src/Event/ActivityCreatePreEvent.php create mode 100644 src/Event/ActivityUpdatePostEvent.php create mode 100644 src/Event/ActivityUpdatePreEvent.php create mode 100644 tests/Activity/ActivityServiceTest.php create mode 100644 tests/Event/AbstractActivityEventTest.php create mode 100644 tests/Event/ActivityCreateEventTest.php create mode 100644 tests/Event/ActivityCreatePostEventTest.php create mode 100644 tests/Event/ActivityCreatePreEventTest.php create mode 100644 tests/Event/ActivityUpdatePostEventTest.php create mode 100644 tests/Event/ActivityUpdatePreEventTest.php create mode 100644 tests/Event/ReportingEventTest.php diff --git a/src/Activity/ActivityService.php b/src/Activity/ActivityService.php new file mode 100644 index 00000000..09c3b434 --- /dev/null +++ b/src/Activity/ActivityService.php @@ -0,0 +1,104 @@ +repository = $activityRepository; + $this->dispatcher = $dispatcher; + $this->validator = $validator; + } + + public function createNewActivity(?Project $project = null): Activity + { + $activity = new Activity(); + + if ($project !== null) { + $activity->setProject($project); + } + + $this->dispatcher->dispatch(new ActivityMetaDefinitionEvent($activity)); + $this->dispatcher->dispatch(new ActivityCreateEvent($activity)); + + return $activity; + } + + public function saveNewActivity(Activity $activity): Activity + { + if (null !== $activity->getId()) { + throw new InvalidArgumentException('Cannot create activity, already persisted'); + } + + $this->validateActivity($activity); + + $this->dispatcher->dispatch(new ActivityCreatePreEvent($activity)); + $this->repository->saveActivity($activity); + $this->dispatcher->dispatch(new ActivityCreatePostEvent($activity)); + + return $activity; + } + + /** + * @param Activity $activity + * @param string[] $groups + * @throws ValidationFailedException + */ + private function validateActivity(Activity $activity, array $groups = []): void + { + $errors = $this->validator->validate($activity, null, $groups); + + if ($errors->count() > 0) { + throw new ValidationFailedException($errors, 'Validation Failed'); + } + } + + public function updateActivity(Activity $activity): Activity + { + $this->validateActivity($activity); + + $this->dispatcher->dispatch(new ActivityUpdatePreEvent($activity)); + $this->repository->saveActivity($activity); + $this->dispatcher->dispatch(new ActivityUpdatePostEvent($activity)); + + return $activity; + } +} diff --git a/src/Controller/ActivityController.php b/src/Controller/ActivityController.php index b35ddc07..f2ab91fd 100644 --- a/src/Controller/ActivityController.php +++ b/src/Controller/ActivityController.php @@ -9,6 +9,7 @@ namespace App\Controller; +use App\Activity\ActivityService; use App\Configuration\SystemConfiguration; use App\Entity\Activity; use App\Entity\ActivityRate; @@ -57,12 +58,17 @@ final class ActivityController extends AbstractController * @var EventDispatcherInterface */ private $dispatcher; + /** + * @var ActivityService + */ + private $activityService; - public function __construct(ActivityRepository $repository, SystemConfiguration $configuration, EventDispatcherInterface $dispatcher) + public function __construct(ActivityRepository $repository, SystemConfiguration $configuration, EventDispatcherInterface $dispatcher, ActivityService $activityService) { $this->repository = $repository; $this->configuration = $configuration; $this->dispatcher = $dispatcher; + $this->activityService = $activityService; } /** @@ -183,10 +189,7 @@ final class ActivityController extends AbstractController */ public function createAction(Request $request, ?Project $project = null) { - $activity = new Activity(); - if (null !== $project) { - $activity->setProject($project); - } + $activity = $this->activityService->createNewActivity($project); $event = new ActivityMetaDefinitionEvent($activity); $this->dispatcher->dispatch($event); @@ -196,7 +199,7 @@ final class ActivityController extends AbstractController if ($editForm->isSubmitted() && $editForm->isValid()) { try { - $this->repository->saveActivity($activity); + $this->activityService->saveNewActivity($activity); $this->flashSuccess('action.update.success'); return $this->redirectToRoute('admin_activity'); @@ -226,7 +229,7 @@ final class ActivityController extends AbstractController if ($form->isSubmitted() && $form->isValid()) { try { - $this->repository->saveActivity($activity); + $this->activityService->updateActivity($activity); $this->flashSuccess('action.update.success'); if ($this->isGranted('view', $activity)) { @@ -286,7 +289,7 @@ final class ActivityController extends AbstractController if ($editForm->isSubmitted() && $editForm->isValid()) { try { - $this->repository->saveActivity($activity); + $this->activityService->updateActivity($activity); $this->flashSuccess('action.update.success'); return $this->redirectToRoute('activity_details', ['id' => $activity->getId()]); diff --git a/src/Customer/CustomerService.php b/src/Customer/CustomerService.php index e704fefc..2de3960c 100644 --- a/src/Customer/CustomerService.php +++ b/src/Customer/CustomerService.php @@ -23,6 +23,9 @@ use InvalidArgumentException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; +/** + * @final + */ class CustomerService { private $repository; diff --git a/src/Event/AbstractActivityEvent.php b/src/Event/AbstractActivityEvent.php new file mode 100644 index 00000000..7b02665c --- /dev/null +++ b/src/Event/AbstractActivityEvent.php @@ -0,0 +1,34 @@ +activity = $activity; + } + + public function getActivity(): Activity + { + return $this->activity; + } +} diff --git a/src/Event/ActivityCreateEvent.php b/src/Event/ActivityCreateEvent.php new file mode 100644 index 00000000..20d49bc7 --- /dev/null +++ b/src/Event/ActivityCreateEvent.php @@ -0,0 +1,17 @@ +createMock(ActivityRepository::class); + } + + if ($dispatcher === null) { + $dispatcher = $this->createMock(EventDispatcherInterface::class); + } + + if ($validator === null) { + $validator = $this->createMock(ValidatorInterface::class); + $validator->method('validate')->willReturn(new ConstraintViolationList()); + } + + $service = new ActivityService($repository, $dispatcher, $validator); + + return $service; + } + + public function testCannotSavePersistedProjectAsNew() + { + $project = $this->createMock(Activity::class); + $project->expects($this->once())->method('getId')->willReturn(1); + + $sut = $this->getSut(); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Cannot create activity, already persisted'); + + $sut->saveNewActivity($project); + } + + public function testsaveNewActivityHasValidationError() + { + $constraints = new ConstraintViolationList(); + $constraints->add(new ConstraintViolation('toooo many tests', 'abc.def', [], '$root', 'begin', 4, null, null, null, '$cause')); + + $validator = $this->createMock(ValidatorInterface::class); + $validator->method('validate')->willReturn($constraints); + + $sut = $this->getSut(null, $validator); + + $this->expectException(ValidationFailedException::class); + $this->expectExceptionMessage('Validation Failed'); + + $sut->saveNewActivity(new Activity()); + } + + public function testUpdateDispatchesEvents() + { + $project = $this->createMock(Activity::class); + $project->method('getId')->willReturn(1); + + $dispatcher = $this->createMock(EventDispatcherInterface::class); + $dispatcher->expects($this->exactly(2))->method('dispatch')->willReturnCallback(function ($event) use ($project) { + if ($event instanceof ActivityUpdatePostEvent) { + self::assertSame($project, $event->getActivity()); + } elseif ($event instanceof ActivityUpdatePreEvent) { + self::assertSame($project, $event->getActivity()); + } else { + $this->fail('Invalid event received'); + } + }); + + $sut = $this->getSut($dispatcher); + + $sut->updateActivity($project); + } + + public function testcreateNewActivityDispatchesEvents() + { + $dispatcher = $this->createMock(EventDispatcherInterface::class); + $dispatcher->expects($this->exactly(2))->method('dispatch')->willReturnCallback(function ($event) { + if ($event instanceof ActivityMetaDefinitionEvent) { + self::assertInstanceOf(Activity::class, $event->getEntity()); + } elseif ($event instanceof ActivityCreateEvent) { + self::assertInstanceOf(Activity::class, $event->getActivity()); + } else { + $this->fail('Invalid event received'); + } + }); + + $sut = $this->getSut($dispatcher); + + $project = new Project(); + $activity = $sut->createNewActivity($project); + + self::assertSame($project, $activity->getProject()); + } + + public function testsaveNewActivityDispatchesEvents() + { + $dispatcher = $this->createMock(EventDispatcherInterface::class); + $dispatcher->expects($this->exactly(2))->method('dispatch')->willReturnCallback(function ($event) { + if ($event instanceof ActivityCreatePreEvent) { + self::assertInstanceOf(Activity::class, $event->getActivity()); + } elseif ($event instanceof ActivityCreatePostEvent) { + self::assertInstanceOf(Activity::class, $event->getActivity()); + } else { + $this->fail('Invalid event received'); + } + }); + + $sut = $this->getSut($dispatcher); + + $activity = new Activity(); + $sut->saveNewActivity($activity); + } + + public function testcreateNewActivityWithoutCustomer() + { + $sut = $this->getSut(); + + $project = $sut->createNewActivity(); + self::assertNull($project->getProject()); + + $project = $sut->createNewActivity(); + self::assertNull($project->getProject()); + } +} diff --git a/tests/Event/AbstractActivityEventTest.php b/tests/Event/AbstractActivityEventTest.php new file mode 100644 index 00000000..9af6fc56 --- /dev/null +++ b/tests/Event/AbstractActivityEventTest.php @@ -0,0 +1,29 @@ +createActivityEvent($activity); + + self::assertInstanceOf(Event::class, $sut); + self::assertSame($activity, $sut->getActivity()); + } +} diff --git a/tests/Event/ActivityCreateEventTest.php b/tests/Event/ActivityCreateEventTest.php new file mode 100644 index 00000000..3d712928 --- /dev/null +++ b/tests/Event/ActivityCreateEventTest.php @@ -0,0 +1,26 @@ +getUser()); + self::assertEquals([], $sut->getReports()); + + $report = new Report('id', 'route', 'label', 'icon'); + $sut->addReport($report); + self::assertSame([$report], $sut->getReports()); + } +}