feat: activity page
This commit is contained in:
parent
f418b36167
commit
19395b4869
15
src/Domain/Shared/Application/Command/DeleteJob.php
Normal file
15
src/Domain/Shared/Application/Command/DeleteJob.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Application\Command;
|
||||
|
||||
use App\Domain\Shared\Domain\Contract\CommandInterface;
|
||||
|
||||
readonly class DeleteJob implements CommandInterface
|
||||
{
|
||||
public function __construct(
|
||||
public string $id
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Application\Command;
|
||||
|
||||
use App\Domain\Shared\Domain\Contract\CommandInterface;
|
||||
use App\Domain\Shared\Domain\Model\JobStatus;
|
||||
|
||||
readonly class DeleteJobsByCriteria implements CommandInterface
|
||||
{
|
||||
/**
|
||||
* @param JobStatus[]|null $statuses
|
||||
*/
|
||||
public function __construct(
|
||||
public ?array $statuses = null,
|
||||
public ?string $type = null,
|
||||
public ?\DateTimeImmutable $createdAfter = null,
|
||||
public ?\DateTimeImmutable $createdBefore = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Application\CommandHandler;
|
||||
|
||||
use App\Domain\Shared\Application\Command\DeleteJob;
|
||||
use App\Domain\Shared\Domain\Contract\CommandHandlerInterface;
|
||||
use App\Domain\Shared\Domain\Contract\CommandInterface;
|
||||
use App\Domain\Shared\Domain\Contract\JobRepositoryInterface;
|
||||
|
||||
readonly class DeleteJobHandler implements CommandHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private JobRepositoryInterface $jobRepository
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(CommandInterface $command): void
|
||||
{
|
||||
if (!$command instanceof DeleteJob) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Command must be instance of %s, %s given',
|
||||
DeleteJob::class,
|
||||
get_class($command)
|
||||
));
|
||||
}
|
||||
|
||||
$this->jobRepository->get($command->id);
|
||||
$this->jobRepository->delete($command->id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Application\CommandHandler;
|
||||
|
||||
use App\Domain\Shared\Application\Command\DeleteJobsByCriteria;
|
||||
use App\Domain\Shared\Domain\Contract\CommandHandlerInterface;
|
||||
use App\Domain\Shared\Domain\Contract\CommandInterface;
|
||||
use App\Domain\Shared\Domain\Contract\JobRepositoryInterface;
|
||||
|
||||
readonly class DeleteJobsByCriteriaHandler implements CommandHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private JobRepositoryInterface $jobRepository
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(CommandInterface $command): void
|
||||
{
|
||||
if (!$command instanceof DeleteJobsByCriteria) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Command must be instance of %s, %s given',
|
||||
DeleteJobsByCriteria::class,
|
||||
get_class($command)
|
||||
));
|
||||
}
|
||||
|
||||
$criteria = [];
|
||||
|
||||
if (null !== $command->statuses) {
|
||||
$criteria['statuses'] = $command->statuses;
|
||||
}
|
||||
|
||||
if (null !== $command->type) {
|
||||
$criteria['type'] = $command->type;
|
||||
}
|
||||
|
||||
if (null !== $command->createdAfter) {
|
||||
$criteria['createdAfter'] = $command->createdAfter;
|
||||
}
|
||||
|
||||
if (null !== $command->createdBefore) {
|
||||
$criteria['createdBefore'] = $command->createdBefore;
|
||||
}
|
||||
|
||||
$this->jobRepository->deleteByCriteria($criteria);
|
||||
}
|
||||
}
|
||||
15
src/Domain/Shared/Application/Query/GetJobById.php
Normal file
15
src/Domain/Shared/Application/Query/GetJobById.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Application\Query;
|
||||
|
||||
use App\Domain\Shared\Domain\Contract\QueryInterface;
|
||||
|
||||
readonly class GetJobById implements QueryInterface
|
||||
{
|
||||
public function __construct(
|
||||
public string $id
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Application\QueryHandler;
|
||||
|
||||
use App\Domain\Shared\Application\Query\GetJobById;
|
||||
use App\Domain\Shared\Application\Response\JobResponse;
|
||||
use App\Domain\Shared\Domain\Contract\QueryHandlerInterface;
|
||||
use App\Domain\Shared\Domain\Contract\QueryInterface;
|
||||
use App\Domain\Shared\Domain\Contract\ResponseInterface;
|
||||
use App\Domain\Shared\Domain\Contract\JobRepositoryInterface;
|
||||
|
||||
readonly class GetJobByIdHandler implements QueryHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private JobRepositoryInterface $jobRepository
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(QueryInterface $query): ResponseInterface
|
||||
{
|
||||
if (!$query instanceof GetJobById) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Query must be instance of %s, %s given',
|
||||
GetJobById::class,
|
||||
get_class($query)
|
||||
));
|
||||
}
|
||||
|
||||
$job = $this->jobRepository->get($query->id);
|
||||
|
||||
return JobResponse::fromJob($job);
|
||||
}
|
||||
}
|
||||
41
src/Domain/Shared/Application/Response/JobResponse.php
Normal file
41
src/Domain/Shared/Application/Response/JobResponse.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Application\Response;
|
||||
|
||||
use App\Domain\Shared\Domain\Contract\ResponseInterface;
|
||||
use App\Domain\Shared\Domain\Model\Job;
|
||||
|
||||
readonly class JobResponse implements ResponseInterface
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $type,
|
||||
public string $status,
|
||||
public \DateTimeImmutable $createdAt,
|
||||
public ?\DateTimeImmutable $startedAt,
|
||||
public ?\DateTimeImmutable $completedAt,
|
||||
public ?string $failureReason,
|
||||
public int $attempts,
|
||||
public int $maxAttempts,
|
||||
public array $context
|
||||
) {
|
||||
}
|
||||
|
||||
public static function fromJob(Job $job): self
|
||||
{
|
||||
return new self(
|
||||
id: $job->id,
|
||||
type: $job->type,
|
||||
status: $job->status->value,
|
||||
createdAt: $job->createdAt,
|
||||
startedAt: $job->startedAt,
|
||||
completedAt: $job->completedAt,
|
||||
failureReason: $job->failureReason,
|
||||
attempts: $job->attempts,
|
||||
maxAttempts: $job->maxAttempts,
|
||||
context: $job->context
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -41,4 +41,17 @@ interface JobRepositoryInterface
|
||||
* } $criteria
|
||||
*/
|
||||
public function countByCriteria(array $criteria): int;
|
||||
|
||||
public function delete(string $id): void;
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* statuses?: ?array<JobStatus>,
|
||||
* type?: ?string,
|
||||
* createdAfter?: ?\DateTimeImmutable,
|
||||
* createdBefore?: ?\DateTimeImmutable
|
||||
* } $criteria
|
||||
* @return int Nombre de jobs supprimés
|
||||
*/
|
||||
public function deleteByCriteria(array $criteria): int;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,16 @@ namespace App\Domain\Shared\Infrastructure\ApiPlatform\Resource;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
use ApiPlatform\Metadata\ApiProperty;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use App\Domain\Shared\Application\Response\JobResponse;
|
||||
use App\Domain\Shared\Domain\Model\JobStatus;
|
||||
use App\Domain\Shared\Infrastructure\ApiPlatform\State\Processor\DeleteJobProcessor;
|
||||
use App\Domain\Shared\Infrastructure\ApiPlatform\State\Processor\DeleteJobsByCriteriaProcessor;
|
||||
use App\Domain\Shared\Infrastructure\ApiPlatform\State\Provider\DeleteJobProvider;
|
||||
use App\Domain\Shared\Infrastructure\ApiPlatform\State\Provider\GetJobListStateProvider;
|
||||
use App\Domain\Shared\Infrastructure\ApiPlatform\State\Provider\GetJobStateProvider;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
@@ -105,7 +112,56 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
||||
),
|
||||
new Get(
|
||||
uriTemplate: '/jobs/{id}',
|
||||
provider: GetJobStateProvider::class,
|
||||
output: GetJobListResource::class,
|
||||
description: 'Récupère un job par son identifiant',
|
||||
),
|
||||
new Delete(
|
||||
uriTemplate: '/jobs/{id}',
|
||||
provider: DeleteJobProvider::class,
|
||||
processor: DeleteJobProcessor::class,
|
||||
description: 'Supprime un job par son identifiant',
|
||||
),
|
||||
new Delete(
|
||||
uriTemplate: '/jobs',
|
||||
processor: DeleteJobsByCriteriaProcessor::class,
|
||||
description: 'Supprime les jobs correspondant aux critères',
|
||||
openapiContext: [
|
||||
'parameters' => [
|
||||
[
|
||||
'name' => 'status',
|
||||
'in' => 'query',
|
||||
'description' => 'Filtrer par statut(s) (virgule-séparés ou tableau)',
|
||||
'required' => false,
|
||||
'schema' => ['type' => 'string'],
|
||||
],
|
||||
[
|
||||
'name' => 'type',
|
||||
'in' => 'query',
|
||||
'description' => 'Filtrer par type de job',
|
||||
'required' => false,
|
||||
'schema' => ['type' => 'string'],
|
||||
],
|
||||
[
|
||||
'name' => 'createdAfter',
|
||||
'in' => 'query',
|
||||
'description' => 'Date de création minimum (ISO8601)',
|
||||
'required' => false,
|
||||
'schema' => ['type' => 'string', 'format' => 'date-time'],
|
||||
],
|
||||
[
|
||||
'name' => 'createdBefore',
|
||||
'in' => 'query',
|
||||
'description' => 'Date de création maximum (ISO8601)',
|
||||
'required' => false,
|
||||
'schema' => ['type' => 'string', 'format' => 'date-time'],
|
||||
],
|
||||
]
|
||||
]
|
||||
),
|
||||
]
|
||||
)]
|
||||
class GetJobListResource
|
||||
@@ -157,4 +213,20 @@ class GetJobListResource
|
||||
context: $job->context
|
||||
);
|
||||
}
|
||||
|
||||
public static function fromJobResponse(JobResponse $response): self
|
||||
{
|
||||
return new self(
|
||||
id: $response->id,
|
||||
type: $response->type,
|
||||
status: $response->status,
|
||||
createdAt: $response->createdAt,
|
||||
startedAt: $response->startedAt,
|
||||
completedAt: $response->completedAt,
|
||||
failureReason: $response->failureReason,
|
||||
attempts: $response->attempts,
|
||||
maxAttempts: $response->maxAttempts,
|
||||
context: $response->context
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Domain\Shared\Application\Command\DeleteJob;
|
||||
use App\Domain\Shared\Application\CommandHandler\DeleteJobHandler;
|
||||
use App\Domain\Shared\Domain\Exception\JobNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
readonly class DeleteJobProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private DeleteJobHandler $handler
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
|
||||
{
|
||||
try {
|
||||
$this->handler->handle(new DeleteJob($uriVariables['id']));
|
||||
} catch (JobNotFoundException $e) {
|
||||
throw new NotFoundHttpException($e->getMessage(), $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Domain\Shared\Application\Command\DeleteJobsByCriteria;
|
||||
use App\Domain\Shared\Application\CommandHandler\DeleteJobsByCriteriaHandler;
|
||||
use App\Domain\Shared\Domain\Model\JobStatus;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
readonly class DeleteJobsByCriteriaProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private DeleteJobsByCriteriaHandler $handler,
|
||||
private RequestStack $requestStack
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
$filters = $request ? $request->query->all() : [];
|
||||
|
||||
$statuses = null;
|
||||
if (isset($filters['status'])) {
|
||||
$statusValues = is_array($filters['status'])
|
||||
? $filters['status']
|
||||
: explode(',', $filters['status']);
|
||||
|
||||
$statuses = [];
|
||||
foreach ($statusValues as $value) {
|
||||
try {
|
||||
$statuses[] = JobStatus::from($value);
|
||||
} catch (\ValueError) {
|
||||
// ignore invalid values
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($statuses)) {
|
||||
$statuses = null;
|
||||
}
|
||||
}
|
||||
|
||||
$this->handler->handle(new DeleteJobsByCriteria(
|
||||
statuses: $statuses,
|
||||
type: $filters['type'] ?? null,
|
||||
createdAfter: isset($filters['createdAfter']) ? new \DateTimeImmutable($filters['createdAfter']) : null,
|
||||
createdBefore: isset($filters['createdBefore']) ? new \DateTimeImmutable($filters['createdBefore']) : null,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Infrastructure\ApiPlatform\State\Provider;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Domain\Shared\Application\Query\GetJobById;
|
||||
use App\Domain\Shared\Application\QueryHandler\GetJobByIdHandler;
|
||||
use App\Domain\Shared\Domain\Exception\JobNotFoundException;
|
||||
use App\Domain\Shared\Infrastructure\ApiPlatform\Resource\GetJobListResource;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
readonly class DeleteJobProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private GetJobByIdHandler $handler
|
||||
) {
|
||||
}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): GetJobListResource
|
||||
{
|
||||
try {
|
||||
$response = $this->handler->handle(new GetJobById($uriVariables['id']));
|
||||
} catch (JobNotFoundException $e) {
|
||||
throw new NotFoundHttpException($e->getMessage(), $e);
|
||||
}
|
||||
|
||||
return GetJobListResource::fromJobResponse($response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Infrastructure\ApiPlatform\State\Provider;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Domain\Shared\Application\Query\GetJobById;
|
||||
use App\Domain\Shared\Application\QueryHandler\GetJobByIdHandler;
|
||||
use App\Domain\Shared\Domain\Exception\JobNotFoundException;
|
||||
use App\Domain\Shared\Infrastructure\ApiPlatform\Resource\GetJobListResource;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
readonly class GetJobStateProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private GetJobByIdHandler $handler
|
||||
) {
|
||||
}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): GetJobListResource
|
||||
{
|
||||
try {
|
||||
$response = $this->handler->handle(new GetJobById($uriVariables['id']));
|
||||
} catch (JobNotFoundException $e) {
|
||||
throw new NotFoundHttpException($e->getMessage(), $e);
|
||||
}
|
||||
|
||||
return GetJobListResource::fromJobResponse($response);
|
||||
}
|
||||
}
|
||||
@@ -188,4 +188,52 @@ readonly class DoctrineJobRepository implements JobRepositoryInterface
|
||||
|
||||
return (int) $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function delete(string $id): void
|
||||
{
|
||||
$entity = $this->entityManager->find(JobEntity::class, $id);
|
||||
|
||||
if (null === $entity) {
|
||||
throw JobNotFoundException::withId($id);
|
||||
}
|
||||
|
||||
$this->entityManager->remove($entity);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
public function deleteByCriteria(array $criteria): int
|
||||
{
|
||||
$qb = $this->entityManager->createQueryBuilder()
|
||||
->delete(JobEntity::class, 'j');
|
||||
|
||||
if (isset($criteria['statuses']) && is_array($criteria['statuses']) && !empty($criteria['statuses'])) {
|
||||
$expr = $qb->expr()->orX();
|
||||
foreach ($criteria['statuses'] as $key => $status) {
|
||||
$paramName = 'status' . $key;
|
||||
$expr->add($qb->expr()->eq('j.status', ':' . $paramName));
|
||||
$qb->setParameter($paramName, $status->value);
|
||||
}
|
||||
$qb->andWhere($expr);
|
||||
} elseif (isset($criteria['status'])) {
|
||||
$qb->andWhere('j.status = :status')
|
||||
->setParameter('status', $criteria['status']->value);
|
||||
}
|
||||
|
||||
if (isset($criteria['type'])) {
|
||||
$qb->andWhere('j.type = :type')
|
||||
->setParameter('type', $criteria['type']);
|
||||
}
|
||||
|
||||
if (isset($criteria['createdAfter'])) {
|
||||
$qb->andWhere('j.createdAt >= :createdAfter')
|
||||
->setParameter('createdAfter', $criteria['createdAfter']);
|
||||
}
|
||||
|
||||
if (isset($criteria['createdBefore'])) {
|
||||
$qb->andWhere('j.createdAt <= :createdBefore')
|
||||
->setParameter('createdBefore', $criteria['createdBefore']);
|
||||
}
|
||||
|
||||
return (int) $qb->getQuery()->execute();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user