From 2449a1e8bfb8082576ea3066b5498c49e02ae097 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Wed, 12 Aug 2020 21:56:47 +0200 Subject: [PATCH] fix serialized dates from API (#1888) --- src/Entity/Activity.php | 2 +- src/Entity/Customer.php | 2 +- src/Entity/Project.php | 9 +++++ src/Entity/Timesheet.php | 6 +++ tests/API/ProjectControllerTest.php | 49 +++++++++++++++++++++++- tests/API/TimesheetControllerTest.php | 55 ++++++++++++++++++++++++++- 6 files changed, 118 insertions(+), 5 deletions(-) diff --git a/src/Entity/Activity.php b/src/Entity/Activity.php index 2522b105..f7c531cb 100644 --- a/src/Entity/Activity.php +++ b/src/Entity/Activity.php @@ -332,7 +332,7 @@ class Activity implements EntityWithMetaFields public function addTeam(Team $team) { if ($this->teams->contains($team)) { - return $this; + return; } $this->teams->add($team); diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php index 1e98d868..7cc62169 100644 --- a/src/Entity/Customer.php +++ b/src/Entity/Customer.php @@ -599,7 +599,7 @@ class Customer implements EntityWithMetaFields public function addTeam(Team $team) { if ($this->teams->contains($team)) { - return $this; + return; } $this->teams->add($team); diff --git a/src/Entity/Project.php b/src/Entity/Project.php index 85ddbf08..d121b03b 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -118,6 +118,9 @@ class Project implements EntityWithMetaFields * @Serializer\Expose() * @Serializer\Groups({"Project_Entity"}) * @Serializer\Type(name="DateTime") + * @Serializer\Accessor(getter="getOrderDate") + * + * Attention: Accessor MUST be used, otherwise date will be serialized in UTC. * * @Exporter\Expose(label="label.orderDate", type="datetime") * @@ -130,6 +133,9 @@ class Project implements EntityWithMetaFields * @Serializer\Expose() * @Serializer\Groups({"Project"}) * @Serializer\Type(name="DateTime") + * @Serializer\Accessor(getter="getStart") + * + * Attention: Accessor MUST be used, otherwise date will be serialized in UTC. * * @Exporter\Expose(label="label.project_start", type="datetime") * @@ -142,6 +148,9 @@ class Project implements EntityWithMetaFields * @Serializer\Expose() * @Serializer\Groups({"Project"}) * @Serializer\Type(name="DateTime") + * @Serializer\Accessor(getter="getEnd") + * + * Attention: Accessor MUST be used, otherwise date will be serialized in UTC. * * @Exporter\Expose(label="label.project_end", type="datetime") * diff --git a/src/Entity/Timesheet.php b/src/Entity/Timesheet.php index edf4816b..80e80a88 100644 --- a/src/Entity/Timesheet.php +++ b/src/Entity/Timesheet.php @@ -114,6 +114,9 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface * @Serializer\Expose() * @Serializer\Groups({"Default"}) * @Serializer\Type(name="DateTime") + * @Serializer\Accessor(getter="getBegin") + * + * Attention: Accessor MUST be used, otherwise date will be serialized in UTC. * * @ORM\Column(name="start_time", type="datetime", nullable=false) * @Assert\NotNull() @@ -125,6 +128,9 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface * @Serializer\Expose() * @Serializer\Groups({"Default"}) * @Serializer\Type(name="DateTime") + * @Serializer\Accessor(getter="getEnd") + * + * Attention: Accessor MUST be used, otherwise date will be serialized in UTC. * * @ORM\Column(name="end_time", type="datetime", nullable=true) */ diff --git a/tests/API/ProjectControllerTest.php b/tests/API/ProjectControllerTest.php index 8ad95ab2..5d07b836 100644 --- a/tests/API/ProjectControllerTest.php +++ b/tests/API/ProjectControllerTest.php @@ -184,11 +184,58 @@ class ProjectControllerTest extends APIControllerBaseTest public function testGetEntity() { $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); - $this->assertAccessIsGranted($client, '/api/projects/1'); + $em = $this->getEntityManager(); + + $customer = (new Customer()) + ->setName('first one') + ->setVisible(true) + ->setCountry('de') + ->setTimezone('Europe/Berlin') + ; + $em->persist($customer); + + $orderDate = new \DateTime('2019-11-29 14:35:17', new \DateTimeZone('Pacific/Tongatapu')); + $startDate = new \DateTime('2020-01-07 18:19:20', new \DateTimeZone('Pacific/Tongatapu')); + $endDate = new \DateTime('2021-03-23 00:00:01', new \DateTimeZone('Pacific/Tongatapu')); + + $project = (new Project()) + ->setName('first') + ->setVisible(true) + ->setCustomer($customer) + ->setOrderDate($orderDate) + ->setStart($startDate) + ->setEnd($endDate) + ; + $em->persist($project); + + $this->assertAccessIsGranted($client, '/api/projects/2'); $result = json_decode($client->getResponse()->getContent(), true); $this->assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); + + $expected = [ + 'parentTitle' => 'first one', + 'customer' => 2, + 'id' => 2, + 'name' => 'first', + 'orderNumber' => null, + // make sure the timezone is properly applied in serializer (see #1858) + 'orderDate' => '2019-11-29T14:35:17+1300', + 'start' => '2020-01-07T18:19:20+1300', + 'end' => '2021-03-23T00:00:01+1300', + 'comment' => null, + 'visible' => true, + 'budget' => 0.0, + 'timeBudget' => 0, + 'metaFields' => [], + 'teams' => [], + 'color' => null, + ]; + + foreach ($expected as $key => $value) { + self::assertEquals($value, $result[$key]); + } } public function testNotFound() diff --git a/tests/API/TimesheetControllerTest.php b/tests/API/TimesheetControllerTest.php index 16cd8e2b..181271fa 100644 --- a/tests/API/TimesheetControllerTest.php +++ b/tests/API/TimesheetControllerTest.php @@ -38,9 +38,10 @@ class TimesheetControllerTest extends APIControllerBaseTest ->setHourlyRate(true) ->setAmount(10) ->setUser($this->getUserByRole($role)) - ->setStartDate((new \DateTime('first day of this month'))->setTime(0, 0, 1)) ->setAllowEmptyDescriptions(false) + ->setStartDate((new \DateTime('first day of this month'))->setTime(0, 0, 1)) ; + $this->importFixture($fixture); } @@ -273,12 +274,62 @@ class TimesheetControllerTest extends APIControllerBaseTest public function testGetEntity() { $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); - $this->importFixtureForUser(User::ROLE_USER); + $em = $this->getEntityManager(); + + $startDate = new \DateTime('2020-03-27 14:35:59', new \DateTimeZone('Pacific/Tongatapu')); + $endDate = (clone $startDate)->modify('+ 46385 seconds'); + $project = $em->getRepository(Project::class)->find(1); + $activity = $em->getRepository(Activity::class)->find(1); + + $tag = new Tag(); + $tag->setName('test'); + $em->persist($tag); + + $timesheet = new Timesheet(); + $timesheet + ->setHourlyRate(137.21) + ->setInternalRate(64.96) + ->setBegin($startDate) + ->setEnd($endDate) + ->setExported(true) + ->setDescription('**foo**' . PHP_EOL . 'bar') + ->setUser($this->getUserByRole(User::ROLE_USER)) + ->setProject($project) + ->setActivity($activity) + ->addTag($tag) + ; + $em->persist($timesheet); + $this->assertAccessIsGranted($client, '/api/timesheets/1'); $result = json_decode($client->getResponse()->getContent(), true); $this->assertIsArray($result); self::assertApiResponseTypeStructure('TimesheetEntity', $result); + + $expected = [ + 'activity' => 1, + 'project' => 1, + 'user' => 2, + 'tags' => [ + 0 => 'test' + ], + 'id' => 1, + // make sure the timezone is properly applied in serializer (see #1858) + // minute and second are different from the above datetime object, because of applied default minute rounding + 'begin' => '2020-03-27T14:35:00+1300', + 'end' => '2020-03-28T03:30:00+1300', + 'description' => "**foo**\nbar", + 'duration' => 46500, + 'exported' => true, + 'metaFields' => [], + 'hourlyRate' => 137.21, + 'rate' => 1772.3, + 'internalRate' => 0.0, + ]; + + foreach ($expected as $key => $value) { + self::assertEquals($value, $result[$key]); + } } public function testGetEntityAccessDenied()