refactor: add custom Exception class

This commit is contained in:
Maël Gangloff
2025-10-07 15:55:17 +02:00
parent 79589e63eb
commit ea1df9d97a
10 changed files with 109 additions and 37 deletions

View File

@@ -27,4 +27,17 @@ api_platform:
JWT:
name: Authorization
type: header
exception_to_status:
# The 4 following handlers are registered by default, keep those lines to prevent unexpected side effects
Symfony\Component\Serializer\Exception\ExceptionInterface: 400 # Use a raw status code (recommended)
ApiPlatform\Exception\InvalidArgumentException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
ApiPlatform\ParameterValidator\Exception\ValidationExceptionInterface: 400
Doctrine\ORM\OptimisticLockException: 409
# Validation exception
ApiPlatform\Validator\Exception\ValidationException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_UNPROCESSABLE_ENTITY
App\Exception\DomainNotFoundException: 404
App\Exception\MalformedDomainException: 403
App\Exception\TldNotSupportedException: 403
App\Exception\UnknownRdapServerException: 403

View File

@@ -60,6 +60,7 @@ class DomainRefreshController extends AbstractController
) {
$this->logger->info('It is not necessary to update the domain name', [
'ldhName' => $idnDomain,
'updatedAt' => $domain->getUpdatedAt()->format(\DateTimeInterface::ATOM),
]);
return $domain;

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Exception;
class DomainNotFoundException extends \Exception
{
public static function fromDomain(string $ldhName): DomainNotFoundException
{
return new DomainNotFoundException("The domain name $ldhName is not present in the WHOIS database");
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Exception;
class MalformedDomainException extends \Exception
{
public static function fromDomain(string $ldhName): MalformedDomainException
{
return new MalformedDomainException("Domain name ($ldhName) must contain at least one dot");
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Exception;
class TldNotSupportedException extends \Exception
{
public static function fromTld(string $tld): TldNotSupportedException
{
return new TldNotSupportedException("The requested TLD $tld is not yet supported, please try again with another one");
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Exception;
class UnknownRdapServerException extends \Exception
{
public static function fromTld(string $tld): UnknownRdapServerException
{
return new UnknownRdapServerException("TLD $tld: Unable to determine which RDAP server to contact");
}
}

View File

@@ -4,11 +4,14 @@ namespace App\MessageHandler;
use App\Entity\Domain;
use App\Entity\WatchList;
use App\Exception\DomainNotFoundException;
use App\Exception\TldNotSupportedException;
use App\Exception\UnknownRdapServerException;
use App\Message\OrderDomain;
use App\Message\SendDomainEventNotif;
use App\Message\UpdateDomainsFromWatchlist;
use App\Notifier\DomainDeletedNotification;
use App\Notifier\DomainUpdateErrorNotification;
use App\Repository\DomainRepository;
use App\Repository\WatchListRepository;
use App\Service\ChatNotificationService;
use App\Service\Connector\AbstractProvider;
@@ -17,7 +20,6 @@ use App\Service\RDAPService;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Symfony\Component\Messenger\MessageBusInterface;
@@ -39,7 +41,7 @@ final readonly class UpdateDomainsFromWatchlistHandler
private WatchListRepository $watchListRepository,
private LoggerInterface $logger,
#[Autowire(service: 'service_container')]
private ContainerInterface $locator,
private ContainerInterface $locator, private DomainRepository $domainRepository,
) {
$this->sender = new Address($mailerSenderEmail, $mailerSenderName);
}
@@ -95,6 +97,7 @@ final readonly class UpdateDomainsFromWatchlistHandler
foreach ($watchList->getDomains()->filter(fn ($domain) => $domain->isToBeUpdated(false, null !== $watchList->getConnector())) as $domain
) {
$updatedAt = $domain->getUpdatedAt();
$deleted = $domain->getDeleted();
try {
/*
@@ -103,8 +106,10 @@ final readonly class UpdateDomainsFromWatchlistHandler
*/
$this->RDAPService->registerDomain($domain->getLdhName());
$this->bus->dispatch(new SendDomainEventNotif($watchList->getToken(), $domain->getLdhName(), $updatedAt));
} catch (NotFoundHttpException) {
if (!$domain->getDeleted()) {
} catch (DomainNotFoundException) {
$newDomain = $this->domainRepository->findOneBy(['ldhName' => $domain->getLdhName()]);
if (!$deleted && null !== $newDomain && $newDomain->getDeleted()) {
$notification = new DomainDeletedNotification($this->sender, $domain);
$this->mailer->send($notification->asEmailMessage(new Recipient($watchList->getUser()->getEmail()))->getMessage());
$this->chatNotificationService->sendChatNotification($watchList, $notification);
@@ -117,20 +122,10 @@ final readonly class UpdateDomainsFromWatchlistHandler
*/
$this->bus->dispatch(new OrderDomain($watchList->getToken(), $domain->getLdhName()));
}
} catch (\Throwable $e) {
} catch (TldNotSupportedException|UnknownRdapServerException) {
/*
* In case of another unknown error,
* the owner of the Watchlist is informed that an error occurred in updating the domain name.
* In this case, the domain name can no longer be updated. Unfortunately, there is nothing more that can be done.
*/
$this->logger->error('Update error email is sent to user', [
'username' => $watchList->getUser()->getUserIdentifier(),
'error' => $e,
]);
$email = (new DomainUpdateErrorNotification($this->sender, $domain))
->asEmailMessage(new Recipient($watchList->getUser()->getEmail()));
$this->mailer->send($email->getMessage());
throw $e;
}
}
}

View File

@@ -19,6 +19,10 @@ use App\Entity\Nameserver;
use App\Entity\NameserverEntity;
use App\Entity\RdapServer;
use App\Entity\Tld;
use App\Exception\DomainNotFoundException;
use App\Exception\MalformedDomainException;
use App\Exception\TldNotSupportedException;
use App\Exception\UnknownRdapServerException;
use App\Repository\DomainEntityRepository;
use App\Repository\DomainEventRepository;
use App\Repository\DomainRepository;
@@ -31,17 +35,14 @@ use App\Repository\RdapServerRepository;
use App\Repository\TldRepository;
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\OptimisticLockException;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Yaml\Yaml;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
@@ -119,9 +120,16 @@ class RDAPService
}
/**
* @throws HttpExceptionInterface
* @throws TransportExceptionInterface
* @throws RedirectionExceptionInterface
* @throws DomainNotFoundException
* @throws DecodingExceptionInterface
* @throws TldNotSupportedException
* @throws ClientExceptionInterface
* @throws OptimisticLockException
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws MalformedDomainException
* @throws UnknownRdapServerException
*/
public function registerDomains(array $domains): void
{
@@ -131,11 +139,16 @@ class RDAPService
}
/**
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws DomainNotFoundException
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws TldNotSupportedException
* @throws ClientExceptionInterface
* @throws OptimisticLockException
* @throws TransportExceptionInterface
* @throws MalformedDomainException
* @throws ServerExceptionInterface
* @throws UnknownRdapServerException
* @throws \Exception
*/
public function registerDomain(string $fqdn): Domain
@@ -163,7 +176,7 @@ class RDAPService
$this->updateDomainStatus($domain, $rdapData);
if (in_array('free', $domain->getStatus())) {
throw new NotFoundHttpException("The domain name $idnDomain is not present in the WHOIS database.");
throw DomainNotFoundException::fromDomain($idnDomain);
}
$domain
@@ -185,13 +198,17 @@ class RDAPService
return $domain;
}
/**
* @throws TldNotSupportedException
* @throws MalformedDomainException
*/
public function getTld(string $domain): Tld
{
if (!str_contains($domain, '.')) {
$tldEntity = $this->tldRepository->findOneBy(['tld' => '.']);
if (null == $tldEntity) {
throw new NotFoundHttpException("The requested TLD $domain is not yet supported, please try again with another one");
throw TldNotSupportedException::fromTld($domain);
}
return $tldEntity;
@@ -200,14 +217,14 @@ class RDAPService
$lastDotPosition = strrpos($domain, '.');
if (false === $lastDotPosition) {
throw new BadRequestException('Domain must contain at least one dot');
throw MalformedDomainException::fromDomain($domain);
}
$tld = self::convertToIdn(substr($domain, $lastDotPosition + 1));
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
if (null === $tldEntity) {
throw new NotFoundHttpException("The requested TLD $tld is not yet supported, please try again with another one");
throw TldNotSupportedException::fromTld($domain);
}
return $tldEntity;
@@ -218,13 +235,16 @@ class RDAPService
return strtolower(idn_to_ascii($fqdn));
}
/**
* @throws UnknownRdapServerException
*/
private function fetchRdapServer(Tld $tld): RdapServer
{
$tldString = $tld->getTld();
$rdapServer = $this->rdapServerRepository->findOneBy(['tld' => $tldString], ['updatedAt' => 'DESC']);
if (null === $rdapServer) {
throw new NotFoundHttpException("TLD $tldString : Unable to determine which RDAP server to contact");
throw UnknownRdapServerException::fromTld($tldString);
}
return $rdapServer;
@@ -292,7 +312,7 @@ class RDAPService
$this->em->flush();
}
throw new NotFoundHttpException("The domain name $idnDomain is not present in the WHOIS database.");
throw DomainNotFoundException::fromDomain($idnDomain);
}
return $e;
@@ -771,7 +791,6 @@ class RDAPService
}
/**
* @throws ORMException
* @throws \Exception
*/
public function updateRDAPServersFromFile(string $fileName): void
@@ -890,8 +909,8 @@ class RDAPService
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws ClientExceptionInterface
* @throws \Exception
*/
public function updateGTldListICANN(): void

View File

@@ -5,12 +5,12 @@ namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Entity\Domain;
use App\Exception\DomainNotFoundException;
use App\Service\RDAPService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\RateLimiter\RateLimiterFactory;
@@ -49,7 +49,7 @@ readonly class AutoRegisterDomainProvider implements ProviderInterface
try {
$domain = $this->RDAPService->registerDomain($ldhName);
} catch (NotFoundHttpException) {
} catch (DomainNotFoundException) {
$domain = (new Domain())
->setLdhName($ldhName)
->setTld($this->RDAPService->getTld($ldhName))

View File

@@ -1,10 +1,10 @@
{% extends "../base.html.twig" %}
{% extends "base.html.twig" %}
{% set email_color = '#0056b3' %}
{% set title = 'Domain Watchdog Alert' %}
{% block content %}
<p>Hello, <br/>
We are pleased to inform you that a domain name in your watchlist has been detected as deleted from the WHOIS database.<br/>
This is to inform you that a domain name in your watchlist has been detected as deleted from the WHOIS database.<br/>
<strong>Domain name:</strong> {{ domain.ldhName }}<br/>
<strong>Detection Date:</strong> {{ domain.updatedAt | date("c") }}<br/>
<br/>