Add EntityWorkflowSend and EntityWorkflowSendView entities

Introduced EntityWorkflowSend and EntityWorkflowSendView entities to enable tracking of workflow content sent to external parties. Updated EntityWorkflowStep to associate with these entities and added a corresponding database migration script.
This commit is contained in:
2024-10-03 11:55:58 +02:00
parent 9a9d14eb5a
commit 2213f6f429
4 changed files with 303 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Entity\Workflow;
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Random\Randomizer;
/**
* An entity which stores then sending of a workflow's content to
* some external entity.
*/
#[ORM\Entity]
#[ORM\Table(name: 'chill_main_workflow_entity_send')]
class EntityWorkflowSend implements TrackCreationInterface
{
use TrackCreationTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: Types::INTEGER)]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: ThirdParty::class)]
#[ORM\JoinColumn(nullable: true)]
private ?ThirdParty $destineeThirdParty = null;
#[ORM\Column(type: Types::TEXT, nullable: false, options: ['default' => ''])]
private string $destineeEmail = '';
#[ORM\Column(type: 'uuid', unique: true, nullable: false)]
private UuidInterface $uuid;
#[ORM\Column(type: Types::STRING, length: 255, nullable: false)]
private string $privateToken;
#[ORM\Column(type: Types::INTEGER, nullable: false, options: ['default' => 0])]
private int $numberOfErrorTrials = 0;
/**
* @var Collection<int, EntityWorkflowSendView>
*/
#[ORM\OneToMany(targetEntity: EntityWorkflowSendView::class, mappedBy: 'send')]
private Collection $views;
public function __construct(
#[ORM\ManyToOne(targetEntity: EntityWorkflowStep::class, inversedBy: 'sends')]
#[ORM\JoinColumn(nullable: false)]
private EntityWorkflowStep $entityWorkflowStep,
string|ThirdParty $destinee,
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: false)]
private \DateTimeImmutable $expireAt,
) {
$this->uuid = Uuid::uuid4();
$random = new Randomizer();
$this->privateToken = bin2hex($random->getBytes(48));
$this->entityWorkflowStep->addSend($this);
if ($destinee instanceof ThirdParty) {
$this->destineeThirdParty = $destinee;
} else {
$this->destineeEmail = $destinee;
}
$this->views = new ArrayCollection();
}
/**
* @internal use the @see{EntityWorkflowSendView}'s constructor instead
*/
public function addView(EntityWorkflowSendView $view): self
{
if (!$this->views->contains($view)) {
$this->views->add($view);
}
return $this;
}
public function getDestineeEmail(): string
{
return $this->destineeEmail;
}
public function getDestineeThirdParty(): ?ThirdParty
{
return $this->destineeThirdParty;
}
public function getId(): ?int
{
return $this->id;
}
public function getNumberOfErrorTrials(): int
{
return $this->numberOfErrorTrials;
}
public function getPrivateToken(): string
{
return $this->privateToken;
}
public function getUuid(): UuidInterface
{
return $this->uuid;
}
public function increaseErrorTrials(): void
{
$this->numberOfErrorTrials = $this->numberOfErrorTrials + 1;
}
public function getDestinee(): string|ThirdParty
{
if (null !== $this->getDestineeThirdParty()) {
return $this->getDestineeThirdParty();
}
return $this->getDestineeEmail();
}
/**
* Determines the kind of destinee based on whether the destinee is a thirdParty or an emailAddress.
*
* @return 'thirdParty'|'email' 'thirdParty' if the destinee is a third party, 'email' otherwise
*/
public function getDestineeKind(): string
{
if (null !== $this->getDestineeThirdParty()) {
return 'thirdParty';
}
return 'email';
}
public function isViewed(): bool
{
return $this->views->count() > 0;
}
public function isExpired(\DateTimeImmutable $now): bool
{
return $now >= $this->expireAt;
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Entity\Workflow;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
/**
* Register the viewing action from an external destinee.
*/
#[ORM\Entity(readOnly: true)]
#[ORM\Table(name: 'chill_main_workflow_entity_send_views')]
class EntityWorkflowSendView
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: Types::INTEGER)]
private ?int $id = null;
public function __construct(
#[ORM\ManyToOne(targetEntity: EntityWorkflowSend::class, inversedBy: 'views')]
#[ORM\JoinColumn(nullable: false)]
private EntityWorkflowSend $send,
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private \DateTimeInterface $viewAt,
#[ORM\Column(type: Types::TEXT)]
private string $remoteIp = '',
) {
$this->send->addView($this);
}
public function getId(): ?int
{
return $this->id;
}
public function getRemoteIp(): string
{
return $this->remoteIp;
}
public function getSend(): EntityWorkflowSend
{
return $this->send;
}
public function getViewAt(): \DateTimeInterface
{
return $this->viewAt;
}
}

View File

@@ -112,6 +112,11 @@ class EntityWorkflowStep
#[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepHold::class)]
private Collection $holdsOnStep;
/**
* @var Collection<int, EntityWorkflowSend>
*/
private Collection $sends;
public function __construct()
{
$this->ccUser = new ArrayCollection();
@@ -120,6 +125,7 @@ class EntityWorkflowStep
$this->destUserByAccessKey = new ArrayCollection();
$this->signatures = new ArrayCollection();
$this->holdsOnStep = new ArrayCollection();
$this->sends = new ArrayCollection();
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
}
@@ -190,6 +196,18 @@ class EntityWorkflowStep
return $this;
}
/**
* @internal use @see{EntityWorkflowSend}'s constructor instead
*/
public function addSend(EntityWorkflowSend $send): self
{
if (!$this->sends->contains($send)) {
$this->sends[] = $send;
}
return $this;
}
public function removeSignature(EntityWorkflowStepSignature $signature): self
{
if ($this->signatures->contains($signature)) {