feat: firsts endpoints and firsts tests

This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2025-02-05 16:54:13 +01:00
parent 89570ad951
commit 6bc3696190
20 changed files with 455 additions and 23 deletions

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Domain\Scraping\Infrastructure\ApiPlatform\Dto;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\Domain\Scraping\Infrastructure\ApiPlatform\State\Processor\ScrapeChapterStateProcessor;
use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource(
shortName: 'ScrapeChapter',
operations: [
new Post(
uriTemplate: '/scraping/chapters',
status: 202,
processor: ScrapeChapterStateProcessor::class
),
]
)]
readonly class ScrapeChapterRequest
{
public function __construct(
#[ApiProperty(description: 'ID du chapitre à scraper')]
#[Assert\NotBlank]
public string $chapterId,
#[ApiProperty(description: 'ID de la source à utiliser')]
#[Assert\NotBlank]
public string $sourceId,
#[ApiProperty(description: 'ID du manga')]
#[Assert\NotBlank]
public string $mangaId,
) {
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Domain\Scraping\Infrastructure\ApiPlatform\Dto;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use App\Domain\Scraping\Infrastructure\ApiPlatform\State\Provider\ScrapingStatusStateProvider;
use ApiPlatform\Metadata\Link;
use App\Domain\Scraping\Domain\Model\ScrapingJob;
#[ApiResource(
shortName: 'ScrapingStatus',
operations: [
new Get(
uriTemplate: '/scraping/jobs/{jobId}/status',
provider: ScrapingStatusStateProvider::class,
uriVariables: [
'jobId' => new Link(
fromProperty: 'jobId',
toProperty: 'id',
fromClass: ScrapingStatusResponse::class,
toClass: ScrapingJob::class
)
]
),
],
)]
readonly class ScrapingStatusResponse
{
public function __construct(
#[ApiProperty(identifier: true)]
public string $jobId,
#[ApiProperty]
public string $status,
#[ApiProperty]
public ?float $progress = null,
#[ApiProperty]
public ?string $error = null
) {
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Domain\Scraping\Infrastructure\ApiPlatform\State\Processor;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Domain\Scraping\Application\Command\ScrapeChapter;
use App\Domain\Scraping\Infrastructure\ApiPlatform\Dto\ScrapeChapterRequest;
use Symfony\Component\Messenger\MessageBusInterface;
final class ScrapeChapterStateProcessor implements ProcessorInterface
{
public function __construct(
private readonly MessageBusInterface $commandBus
) {}
/**
* @param ScrapeChapterRequest $data
*/
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
{
$this->commandBus->dispatch(
new ScrapeChapter(
$data->chapterId,
$data->sourceId,
$data->mangaId
)
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Domain\Scraping\Infrastructure\ApiPlatform\State\Provider;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Domain\Scraping\Domain\Contract\Repository\ScrapingJobRepositoryInterface;
use App\Domain\Scraping\Infrastructure\ApiPlatform\Dto\ScrapingStatusResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
final readonly class ScrapingStatusStateProvider implements ProviderInterface
{
public function __construct(
private ScrapingJobRepositoryInterface $scrapingJobRepository
) {
}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ScrapingStatusResponse
{
$job = $this->scrapingJobRepository->findById($uriVariables['jobId']);
if (!$job) {
throw new NotFoundHttpException('Job de scraping non trouvé');
}
$progress = 0;
if ($job->getTotalPages() > 0) {
$progress = (count($job->getPages()) / $job->getTotalPages()) * 100;
}
return new ScrapingStatusResponse(
jobId: $job->getId(),
status: $job->getStatus()->value,
progress: $progress
);
}
}

View File

@@ -2,17 +2,18 @@
namespace App\Domain\Scraping\Infrastructure\Persistence;
use App\Domain\Scraping\Domain\Contract\Repository\ScrapingJobRepositoryInterface;
use App\Domain\Scraping\Domain\Model\ScrapingJob;
use App\Domain\Scraping\Domain\Model\ScrapingStatus;
use App\Domain\Scraping\Domain\Repository\ScrapingJobRepositoryInterface;
use App\Domain\Scraping\Infrastructure\Persistence\Entity\ScrapingJobEntity;
use Doctrine\ORM\EntityManagerInterface;
class DoctrineScrapingJobRepository implements ScrapingJobRepositoryInterface
readonly class DoctrineScrapingJobRepository implements ScrapingJobRepositoryInterface
{
public function __construct(
private readonly EntityManagerInterface $entityManager
) {}
private EntityManagerInterface $entityManager
) {
}
public function save(ScrapingJob $job): void
{
@@ -46,7 +47,7 @@ class DoctrineScrapingJobRepository implements ScrapingJobRepositoryInterface
->getQuery()
->getResult();
return array_map(fn(ScrapingJobEntity $entity) => $entity->toDomain(), $entities);
return array_map(fn (ScrapingJobEntity $entity) => $entity->toDomain(), $entities);
}
public function findInProgressJobs(): array
@@ -58,6 +59,6 @@ class DoctrineScrapingJobRepository implements ScrapingJobRepositoryInterface
->getQuery()
->getResult();
return array_map(fn(ScrapingJobEntity $entity) => $entity->toDomain(), $entities);
return array_map(fn (ScrapingJobEntity $entity) => $entity->toDomain(), $entities);
}
}
}