feat(mail) : commande app:mail:sync avec options --folder et --dry-run
This commit is contained in:
110
src/Command/MailSyncCommand.php
Normal file
110
src/Command/MailSyncCommand.php
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Command;
|
||||||
|
|
||||||
|
use App\Repository\MailConfigurationRepository;
|
||||||
|
use App\Repository\MailFolderRepository;
|
||||||
|
use App\Service\MailSyncService;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
|
#[AsCommand(
|
||||||
|
name: 'app:mail:sync',
|
||||||
|
description: 'Synchronise la boîte mail partagée OVH (IMAP) vers la base locale',
|
||||||
|
)]
|
||||||
|
final class MailSyncCommand extends Command
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly MailSyncService $mailSyncService,
|
||||||
|
private readonly MailConfigurationRepository $configRepository,
|
||||||
|
private readonly MailFolderRepository $folderRepository,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addOption(
|
||||||
|
'folder',
|
||||||
|
null,
|
||||||
|
InputOption::VALUE_OPTIONAL,
|
||||||
|
'Synchronise uniquement le dossier spécifié (ex: INBOX)',
|
||||||
|
)
|
||||||
|
->addOption(
|
||||||
|
'dry-run',
|
||||||
|
null,
|
||||||
|
InputOption::VALUE_NONE,
|
||||||
|
'Simule la synchronisation sans écrire en base (lecture IMAP uniquement)',
|
||||||
|
)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
|
||||||
|
$config = $this->configRepository->findSingleton();
|
||||||
|
|
||||||
|
if (null === $config || !$config->isEnabled()) {
|
||||||
|
$io->info('Mail config disabled, skipping.');
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$isDryRun = (bool) $input->getOption('dry-run');
|
||||||
|
$folderPath = $input->getOption('folder');
|
||||||
|
|
||||||
|
if ($isDryRun) {
|
||||||
|
$io->note('Mode --dry-run activé : aucune écriture en base.');
|
||||||
|
$io->success('Dry-run terminé — config IMAP active, aucune sync exécutée.');
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->text('Démarrage de la synchronisation mail...');
|
||||||
|
$startTime = microtime(true);
|
||||||
|
|
||||||
|
if (null !== $folderPath) {
|
||||||
|
$folder = $this->folderRepository->findByPath((string) $folderPath);
|
||||||
|
|
||||||
|
if (null === $folder) {
|
||||||
|
$io->error(sprintf('Dossier "%s" introuvable en base — lance une sync complète au moins une fois.', $folderPath));
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->text(sprintf('Synchronisation du dossier : %s', $folderPath));
|
||||||
|
$report = $this->mailSyncService->syncFolder($folder);
|
||||||
|
} else {
|
||||||
|
$report = $this->mailSyncService->syncAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
$elapsed = round(microtime(true) - $startTime, 2);
|
||||||
|
|
||||||
|
$io->success(sprintf(
|
||||||
|
'Sync terminée en %.1fs : %d créés, %d mis à jour, %d supprimés, %d dossiers scannés.',
|
||||||
|
$elapsed,
|
||||||
|
$report->createdCount,
|
||||||
|
$report->updatedCount,
|
||||||
|
$report->deletedCount,
|
||||||
|
$report->foldersScanned,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ([] !== $report->errors) {
|
||||||
|
$io->warning(sprintf('%d erreur(s) :', count($report->errors)));
|
||||||
|
|
||||||
|
foreach ($report->errors as $error) {
|
||||||
|
$io->text(' - '.$error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [] === $report->errors ? Command::SUCCESS : Command::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
tests/Functional/Command/MailSyncCommandTest.php
Normal file
40
tests/Functional/Command/MailSyncCommandTest.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Functional\Command;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class MailSyncCommandTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
public function testCommandExitsSuccessWhenMailDisabled(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$application = new Application(self::$kernel);
|
||||||
|
$command = $application->find('app:mail:sync');
|
||||||
|
$tester = new CommandTester($command);
|
||||||
|
|
||||||
|
$exitCode = $tester->execute([]);
|
||||||
|
|
||||||
|
self::assertSame(0, $exitCode);
|
||||||
|
self::assertStringContainsString('disabled', strtolower($tester->getDisplay()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCommandDryRunExitsSuccess(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$application = new Application(self::$kernel);
|
||||||
|
$command = $application->find('app:mail:sync');
|
||||||
|
$tester = new CommandTester($command);
|
||||||
|
|
||||||
|
$exitCode = $tester->execute(['--dry-run' => true]);
|
||||||
|
|
||||||
|
self::assertSame(0, $exitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user