From 78d17767331a05b7ce0baf497c139ed0c0b7a659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 17 May 2024 13:14:26 +0200 Subject: [PATCH] Add functionality to find a caller by phone number Added a new method in PersonRepository to allow querying people by phone number. Also, a new REST API endpoint "/public/api/1.0/ticket/find-caller" was introduced and it can find a caller by their phone number. Accompanied this feature addition with corresponding test cases. --- .../Repository/PersonRepository.php | 39 ++++++ .../src/Controller/FindCallerController.php | 58 ++++++++ .../Controller/FindCallerControllerTest.php | 128 ++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 src/Bundle/ChillTicketBundle/src/Controller/FindCallerController.php create mode 100644 src/Bundle/ChillTicketBundle/tests/Controller/FindCallerControllerTest.php diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php index b7104a843..6b36ddd11 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php @@ -12,10 +12,12 @@ declare(strict_types=1); namespace Chill\PersonBundle\Repository; use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Entity\PersonPhone; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; +use libphonenumber\PhoneNumber; class PersonRepository implements ObjectRepository { @@ -29,6 +31,8 @@ class PersonRepository implements ObjectRepository /** * @throws \Doctrine\ORM\NoResultException * @throws \Doctrine\ORM\NonUniqueResultException + * + * @deprecated */ public function countByPhone( string $phonenumber, @@ -71,6 +75,8 @@ class PersonRepository implements ObjectRepository /** * @throws \Exception + * + * @deprecated Use @see{self::findByPhoneNumber} or use a dedicated method in PersonACLAwareRepository */ public function findByPhone( string $phonenumber, @@ -91,6 +97,25 @@ class PersonRepository implements ObjectRepository return $qb->getQuery()->getResult(); } + /** + * Find a person which is associated to the given phonenumber, without restrictions + * on any. + * + * @return listrepository->createQueryBuilder('p'); + $qb->select('p'); + + $this->searchByPhoneNumbers($qb, $phoneNumber); + + $qb->setFirstResult($firstResult) + ->setMaxResults($maxResults); + + return $qb->getQuery()->getResult(); + } + public function findOneBy(array $criteria) { return $this->repository->findOneBy($criteria); @@ -109,6 +134,20 @@ class PersonRepository implements ObjectRepository } } + private function searchByPhoneNumbers(QueryBuilder $qb, PhoneNumber $phoneNumber): void + { + $qb->setParameter('number', $phoneNumber, 'phone_number'); + + $orX = $qb->expr()->orX(); + $orX->add($qb->expr()->eq('p.mobilenumber', ':number')); + $orX->add($qb->expr()->eq('p.phonenumber', ':number')); + $orX->add( + $qb->expr()->exists('SELECT 1 FROM '.PersonPhone::class.' k WHERE k.phonenumber = :number AND k.person = p') + ); + + $qb->andWhere($orX); + } + /** * @throws \Exception */ diff --git a/src/Bundle/ChillTicketBundle/src/Controller/FindCallerController.php b/src/Bundle/ChillTicketBundle/src/Controller/FindCallerController.php new file mode 100644 index 000000000..94febedaa --- /dev/null +++ b/src/Bundle/ChillTicketBundle/src/Controller/FindCallerController.php @@ -0,0 +1,58 @@ +query->get('caller', ''); + + if ('' === $caller) { + throw new BadRequestHttpException('Missing "caller" query parameter'); + } + + try { + $phoneNumber = $this->phonenumberHelper->parse($caller); + } catch (NumberParseException $e) { + throw new BadRequestHttpException('Unable to parse number', $e); + } + + $persons = $this->personRepository->findByPhoneNumber($phoneNumber, 0, 2); + + $asArray = match (count($persons)) { + 0 => ['found' => false, 'name' => null], + 1 => ['found' => true, 'name' => $this->personRender->renderString($persons[0], ['addAge' => false])], + default => ['found' => true, 'name' => 'multiple'], + }; + + return new JsonResponse($asArray); + } +} diff --git a/src/Bundle/ChillTicketBundle/tests/Controller/FindCallerControllerTest.php b/src/Bundle/ChillTicketBundle/tests/Controller/FindCallerControllerTest.php new file mode 100644 index 000000000..301b8f4ac --- /dev/null +++ b/src/Bundle/ChillTicketBundle/tests/Controller/FindCallerControllerTest.php @@ -0,0 +1,128 @@ +buildController($persons); + + $request = new Request(query: ['caller' => $caller]); + + $response = $controller->findCaller($request); + + $actual = json_decode($response->getContent(), true); + + self::assertEqualsCanonicalizing($expected, $actual); + } + + public function testFindCallerWithoutCallerArgument(): void + { + self::expectException(BadRequestHttpException::class); + + $controller = $this->buildController([]); + + $request = new Request(query: []); + + $controller->findCaller($request); + } + + public function testFindCallerWithEmptyCallerArgument(): void + { + self::expectException(BadRequestHttpException::class); + + $controller = $this->buildController([]); + + $request = new Request(query: ['caller' => '']); + + $controller->findCaller($request); + } + + public function testFindCallerWithInvalidCaller(): void + { + self::expectException(BadRequestHttpException::class); + + $controller = $this->buildController([]); + + $request = new Request(query: ['caller' => 'abcde']); + + $controller->findCaller($request); + } + + public static function provideFindCaller(): iterable + { + yield [ + '32486540600', + [], + ['found' => false, 'name' => null], + ]; + + yield [ + '32486540600', + [new Person()], + ['found' => true, 'name' => 'pppp'], + ] + ; + yield [ + '32486540600', + [new Person(), new Person()], + ['found' => true, 'name' => 'multiple'], + ]; + } + + private function buildController(array $personsFound): FindCallerController + { + $phonenumberHelper = + $subject = new PhonenumberHelper( + new ArrayAdapter(), + new ParameterBag([ + 'chill_main.phone_helper' => [ + 'default_carrier_code' => 'BE', + ], + ]), + new NullLogger() + ); + + $personRepository = $this->prophesize(PersonRepository::class); + $personRepository->findByPhoneNumber(Argument::any(), Argument::type('int'), Argument::type('int'))->willReturn($personsFound); + + $personRender = $this->prophesize(PersonRenderInterface::class); + $personRender->renderString(Argument::type(Person::class), Argument::type('array'))->willReturn('pppp'); + + return new FindCallerController($phonenumberHelper, $personRepository->reveal(), $personRender->reveal()); + } +}