feat: add differential tracking of domain EPP status codes

This commit is contained in:
Maël Gangloff
2024-12-12 22:56:35 +01:00
parent 13562511be
commit 37740845e8
5 changed files with 222 additions and 1 deletions

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241212214741 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SEQUENCE domain_status_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE domain_status (id INT NOT NULL, domain_id VARCHAR(255) NOT NULL, date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, add_status TEXT NOT NULL, delete_status TEXT NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_146369D5115F0EE5 ON domain_status (domain_id)');
$this->addSql('COMMENT ON COLUMN domain_status.date IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('COMMENT ON COLUMN domain_status.add_status IS \'(DC2Type:simple_array)\'');
$this->addSql('COMMENT ON COLUMN domain_status.delete_status IS \'(DC2Type:simple_array)\'');
$this->addSql('ALTER TABLE domain_status ADD CONSTRAINT FK_146369D5115F0EE5 FOREIGN KEY (domain_id) REFERENCES domain (ldh_name) NOT DEFERRABLE INITIALLY IMMEDIATE');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP SEQUENCE domain_status_id_seq CASCADE');
$this->addSql('ALTER TABLE domain_status DROP CONSTRAINT FK_146369D5115F0EE5');
$this->addSql('DROP TABLE domain_status');
}
}

View File

@@ -106,6 +106,12 @@ class Domain
#[Groups(['domain:item', 'domain:list'])]
private ?bool $deleted;
/**
* @var Collection<int, DomainStatus>
*/
#[ORM\OneToMany(targetEntity: DomainStatus::class, mappedBy: 'domain', orphanRemoval: true)]
private Collection $domainStatuses;
private const IMPORTANT_EVENTS = [EventAction::Deletion->value, EventAction::Expiration->value];
private const IMPORTANT_STATUS = [
'redemption period',
@@ -128,8 +134,8 @@ class Domain
$this->nameservers = new ArrayCollection();
$this->createdAt = new \DateTimeImmutable('now');
$this->updatedAt = new \DateTimeImmutable('now');
$this->deleted = false;
$this->domainStatuses = new ArrayCollection();
}
public function getLdhName(): ?string
@@ -376,4 +382,34 @@ class Domain
|| (count(array_intersect($this->getStatus(), ['auto renew period', 'client hold', 'server hold'])) > 0
&& $this->getUpdatedAt()->diff(new \DateTimeImmutable())->days >= 1);
}
/**
* @return Collection<int, DomainStatus>
*/
public function getDomainStatuses(): Collection
{
return $this->domainStatuses;
}
public function addDomainStatus(DomainStatus $domainStatus): static
{
if (!$this->domainStatuses->contains($domainStatus)) {
$this->domainStatuses->add($domainStatus);
$domainStatus->setDomain($this);
}
return $this;
}
public function removeDomainStatus(DomainStatus $domainStatus): static
{
if ($this->domainStatuses->removeElement($domainStatus)) {
// set the owning side to null (unless already changed)
if ($domainStatus->getDomain() === $this) {
$domainStatus->setDomain(null);
}
}
return $this;
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace App\Entity;
use App\Repository\DomainStatusRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: DomainStatusRepository::class)]
class DomainStatus
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'domainStatuses')]
#[ORM\JoinColumn(referencedColumnName: 'ldh_name', nullable: false)]
private ?Domain $domain = null;
#[ORM\Column]
private ?\DateTimeImmutable $date = null;
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
private array $addStatus = [];
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
private array $deleteStatus = [];
public function __construct()
{
$this->date = new \DateTimeImmutable('now');
}
public function getId(): ?int
{
return $this->id;
}
public function getDomain(): ?Domain
{
return $this->domain;
}
public function setDomain(?Domain $domain): static
{
$this->domain = $domain;
return $this;
}
public function getDate(): ?\DateTimeImmutable
{
return $this->date;
}
public function setDate(\DateTimeImmutable $date): static
{
$this->date = $date;
return $this;
}
public function getAddStatus(): array
{
return $this->addStatus;
}
public function setAddStatus(array $addStatus): static
{
$this->addStatus = $addStatus;
return $this;
}
public function getDeleteStatus(): array
{
return $this->deleteStatus;
}
public function setDeleteStatus(array $deleteStatus): static
{
$this->deleteStatus = $deleteStatus;
return $this;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Repository;
use App\Entity\DomainStatus;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<DomainStatus>
*/
class DomainStatusRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, DomainStatus::class);
}
// /**
// * @return DomainStatus[] Returns an array of DomainStatus objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('d')
// ->andWhere('d.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('d.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?DomainStatus
// {
// return $this->createQueryBuilder('d')
// ->andWhere('d.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@@ -7,6 +7,7 @@ use App\Config\TldType;
use App\Entity\Domain;
use App\Entity\DomainEntity;
use App\Entity\DomainEvent;
use App\Entity\DomainStatus;
use App\Entity\Entity;
use App\Entity\EntityEvent;
use App\Entity\Nameserver;
@@ -182,7 +183,22 @@ readonly class RDAPService
$domain->setTld($tld)->setLdhName($idnDomain)->setDeleted(false);
if (array_key_exists('status', $res)) {
$addedStatus = array_diff($res['status'], $domain->getStatus());
$deletedStatus = array_diff($domain->getStatus(), $res['status']);
$domain->setStatus($res['status']);
if (count($addedStatus) > 0 || count($deletedStatus) > 0) {
$this->em->persist($domain);
$this->em->flush();
$domainStatus = (new DomainStatus())
->setDomain($domain)
->setDate($domain->getUpdatedAt())
->setAddStatus($addedStatus)
->setDeleteStatus($deletedStatus);
$this->em->persist($domainStatus);
}
} else {
$this->logger->warning('The domain name {idnDomain} has no WHOIS status.', [
'idnDomain' => $idnDomain,