feat: ajout de la fonctionnalité de monitoring des mangas, incluant l'activation et la désactivation du suivi, la synchronisation des chapitres, et la mise à jour de l'API pour gérer ces nouvelles actions. Création de nouveaux composants Vue pour le rafraîchissement des chapitres et l'affichage des notifications. Intégration de tests unitaires pour valider le bon fonctionnement de ces fonctionnalités.
This commit is contained in:
parent
d9e78b5229
commit
00d63dffeb
@@ -20,6 +20,7 @@ readonly class MangaDetail
|
||||
public ?string $externalId,
|
||||
public ?string $imageUrl,
|
||||
public ?string $thumbnailUrl,
|
||||
public ?float $rating
|
||||
public ?float $rating,
|
||||
public bool $monitored
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
'properties' => [
|
||||
'mangaId' => [
|
||||
'type' => 'string',
|
||||
'format' => 'uuid',
|
||||
// 'format' => 'uuid',
|
||||
'description' => 'L\'identifiant unique du manga',
|
||||
'example' => '123e4567-e89b-12d3-a456-426614174000'
|
||||
// 'example' => '123e4567-e89b-12d3-a456-426614174000'
|
||||
]
|
||||
],
|
||||
'required' => ['mangaId']
|
||||
@@ -54,7 +54,7 @@ class FetchMangaChaptersResource
|
||||
{
|
||||
public function __construct(
|
||||
#[Assert\NotBlank(message: 'L\'identifiant du manga est obligatoire')]
|
||||
#[Assert\Uuid(message: 'L\'identifiant du manga doit être un UUID valide')]
|
||||
// #[Assert\Uuid(message: 'L\'identifiant du manga doit être un UUID valide')]
|
||||
public string $mangaId
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Infrastructure\ApiPlatform\Resource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor\RefreshMangaChaptersProcessor;
|
||||
|
||||
#[ApiResource(
|
||||
shortName: 'MangaRefresh',
|
||||
operations: [
|
||||
new Post(
|
||||
uriTemplate: '/manga/{mangaId}/chapters/refresh',
|
||||
processor: RefreshMangaChaptersProcessor::class,
|
||||
status: 202,
|
||||
description: 'Déclenche la synchronisation et le scraping des nouveaux chapitres d\'un manga',
|
||||
openapiContext: [
|
||||
'summary' => 'Rafraîchir les chapitres d\'un manga',
|
||||
'description' => 'Lance la synchronisation incrémentale avec scraping automatique des nouveaux chapitres',
|
||||
'parameters' => [
|
||||
[
|
||||
'name' => 'mangaId',
|
||||
'in' => 'path',
|
||||
'required' => true,
|
||||
'schema' => ['type' => 'string'],
|
||||
'description' => 'L\'identifiant unique du manga'
|
||||
]
|
||||
],
|
||||
'responses' => [
|
||||
'202' => [
|
||||
'description' => 'Demande de refresh acceptée et mise en file d\'attente'
|
||||
],
|
||||
'404' => [
|
||||
'description' => 'Manga non trouvé'
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
||||
]
|
||||
)]
|
||||
class RefreshMangaChaptersResource
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Infrastructure\ApiPlatform\Resource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor\ToggleMonitoringProcessor;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
shortName: 'MangaMonitoring',
|
||||
operations: [
|
||||
new Post(
|
||||
uriTemplate: '/manga/{mangaId}/monitoring/toggle',
|
||||
processor: ToggleMonitoringProcessor::class,
|
||||
read: false,
|
||||
status: 204,
|
||||
description: 'Active ou désactive le monitoring automatique d\'un manga',
|
||||
openapiContext: [
|
||||
'summary' => 'Activer/Désactiver le monitoring d\'un manga',
|
||||
'description' => 'Active ou désactive le monitoring automatique pour recevoir les nouveaux chapitres',
|
||||
'parameters' => [
|
||||
[
|
||||
'name' => 'mangaId',
|
||||
'in' => 'path',
|
||||
'required' => true,
|
||||
'schema' => ['type' => 'string'],
|
||||
'description' => 'L\'identifiant unique du manga'
|
||||
]
|
||||
],
|
||||
'requestBody' => [
|
||||
'description' => 'État du monitoring à appliquer',
|
||||
'required' => true,
|
||||
'content' => [
|
||||
'application/json' => [
|
||||
'schema' => [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'enabled' => [
|
||||
'type' => 'boolean',
|
||||
'description' => 'True pour activer le monitoring, false pour le désactiver',
|
||||
'example' => true
|
||||
]
|
||||
],
|
||||
'required' => ['enabled']
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'responses' => [
|
||||
'204' => [
|
||||
'description' => 'Monitoring modifié avec succès'
|
||||
],
|
||||
'404' => [
|
||||
'description' => 'Manga non trouvé'
|
||||
],
|
||||
'422' => [
|
||||
'description' => 'Données de validation invalides'
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
||||
]
|
||||
)]
|
||||
class ToggleMonitoringResource
|
||||
{
|
||||
#[Assert\NotNull(message: 'Le champ enabled est obligatoire')]
|
||||
#[Assert\Type(type: 'boolean', message: 'Cette valeur doit être de type bool.')]
|
||||
public mixed $enabled = null;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor;
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Domain\Manga\Application\Command\FetchMangaChapters;
|
||||
use App\Domain\Manga\Domain\Model\ValueObject\MangaId;
|
||||
use App\Domain\Manga\Infrastructure\ApiPlatform\Resource\FetchMangaChaptersResource;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
@@ -21,7 +22,7 @@ readonly class FetchMangaChaptersProcessor implements ProcessorInterface
|
||||
}
|
||||
|
||||
$this->messageBus->dispatch(
|
||||
new FetchMangaChapters($data->mangaId)
|
||||
new FetchMangaChapters(new MangaId($data->mangaId))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Domain\Manga\Application\Command\RefreshMangaChapters;
|
||||
use App\Domain\Manga\Domain\Contract\Repository\MangaRepositoryInterface;
|
||||
use App\Domain\Manga\Domain\Exception\MangaNotFoundException;
|
||||
use App\Domain\Manga\Domain\Model\ValueObject\MangaId;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
readonly class RefreshMangaChaptersProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private MessageBusInterface $commandBus,
|
||||
private MangaRepositoryInterface $mangaRepository
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
|
||||
{
|
||||
$mangaId = $uriVariables['mangaId'] ?? null;
|
||||
|
||||
if (!$mangaId) {
|
||||
throw new \InvalidArgumentException('Manga ID is required');
|
||||
}
|
||||
|
||||
// Vérifier que le manga existe
|
||||
$manga = $this->mangaRepository->findById($mangaId);
|
||||
if (!$manga) {
|
||||
throw new MangaNotFoundException($mangaId);
|
||||
}
|
||||
|
||||
$this->commandBus->dispatch(
|
||||
new RefreshMangaChapters(new MangaId($mangaId))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Domain\Manga\Application\Command\ToggleMangaMonitoring;
|
||||
use App\Domain\Manga\Application\CommandHandler\ToggleMangaMonitoringHandler;
|
||||
use App\Domain\Manga\Domain\Exception\MangaNotFoundException;
|
||||
use App\Domain\Manga\Domain\Model\ValueObject\MangaId;
|
||||
use App\Domain\Manga\Infrastructure\ApiPlatform\Resource\ToggleMonitoringResource;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
readonly class ToggleMonitoringProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ToggleMangaMonitoringHandler $handler
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
|
||||
{
|
||||
if (!$data instanceof ToggleMonitoringResource) {
|
||||
throw new \InvalidArgumentException('Invalid resource type');
|
||||
}
|
||||
|
||||
$mangaId = $uriVariables['mangaId'] ?? null;
|
||||
|
||||
if (!$mangaId) {
|
||||
throw new \InvalidArgumentException('Manga ID is required');
|
||||
}
|
||||
|
||||
// La validation Symfony s'assure que enabled est un booléen valide
|
||||
if ($data->enabled === null) {
|
||||
throw new \InvalidArgumentException('Enabled field is required');
|
||||
}
|
||||
|
||||
try {
|
||||
$command = new ToggleMangaMonitoring(
|
||||
new MangaId($mangaId),
|
||||
$data->enabled
|
||||
);
|
||||
|
||||
$this->handler->handle($command);
|
||||
} catch (MangaNotFoundException $e) {
|
||||
throw new NotFoundHttpException($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,8 @@ readonly class GetMangaStateProvider implements ProviderInterface
|
||||
externalId: $response->externalId,
|
||||
imageUrl: $response->imageUrl,
|
||||
thumbnailUrl: $response->thumbnailUrl,
|
||||
rating: $response->rating
|
||||
rating: $response->rating,
|
||||
monitored: $response->monitored
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user