Skip to content

Commit

Permalink
feat: add commands to list one time tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
shyim committed Apr 16, 2024
1 parent 3bcd7b3 commit e2e783b
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 8 deletions.
6 changes: 0 additions & 6 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ private function createContainer(): ContainerBuilder
$definition->addTag('console.command');
});

// @phpstan-ignore-next-line
$container->registerAttributeForAutoconfiguration(AsEventListener::class, static function (ChildDefinition $definition, AsEventListener $attribute, \ReflectionClass|\ReflectionMethod $reflector): void {
$tagAttributes = get_object_vars($attribute);
$definition->addTag('kernel.event_listener', $tagAttributes);
});

$container->addCompilerPass(new AddConsoleCommandPass());
$container->addCompilerPass(new RegisterListenersPass());

Expand Down
38 changes: 38 additions & 0 deletions src/Command/OneTimeTaskListCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php declare(strict_types=1);

namespace Shopware\Deployment\Command;

use Shopware\Deployment\Services\OneTimeTasks;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'one-time-task:list',
description: 'List all executed one-time tasks',
)]
class OneTimeTaskListCommand extends Command
{
public function __construct(private readonly OneTimeTasks $oneTimeTasks)
{
parent::__construct();
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$list = $this->oneTimeTasks->getExecutedTasks();

$table = new Table($output);
$table->setHeaders(['ID', 'Executed at']);

foreach ($list as $id => $data) {
$table->addRow([$id, $data['created_at']]);
}

$table->render();

return Command::SUCCESS;
}
}
45 changes: 45 additions & 0 deletions src/Command/OneTimeTaskMarkCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php declare(strict_types=1);

namespace Shopware\Deployment\Command;

use Shopware\Deployment\Services\OneTimeTasks;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
name: 'one-time-task:mark',
description: 'Mark a one-time task as run without executing it',
)]
class OneTimeTaskMarkCommand extends Command
{
public function __construct(private readonly OneTimeTasks $oneTimeTasks)
{
parent::__construct();
}

protected function configure(): void
{
$this->addArgument('id', InputArgument::REQUIRED, 'The ID of the one-time task');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

try {
$this->oneTimeTasks->markAsRun($input->getArgument('id'));
} catch (\Throwable) {
$io->error('Could not mark one-time task as run, as it has been marked as run before.');

return Command::FAILURE;
}

$io->success('One-time task marked as run');

return Command::SUCCESS;
}
}
39 changes: 39 additions & 0 deletions src/Command/OneTimeTaskUnmarkCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php declare(strict_types=1);

namespace Shopware\Deployment\Command;

use Shopware\Deployment\Services\OneTimeTasks;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
name: 'one-time-task:unmark',
description: 'Unmark a one-time task as run without executing it',
)]
class OneTimeTaskUnmarkCommand extends Command
{
public function __construct(private readonly OneTimeTasks $oneTimeTasks)
{
parent::__construct();
}

protected function configure(): void
{
$this->addArgument('id', InputArgument::REQUIRED, 'The ID of the one-time task');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$this->oneTimeTasks->remove($input->getArgument('id'));

$io->success('One-time task mark has been removed, this script will be executed on next deployment.');

return Command::SUCCESS;
}
}
11 changes: 9 additions & 2 deletions src/Services/OneTimeTasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function execute(OutputInterface $output): void
/**
* @return array<string, string>[]
*/
private function getExecutedTasks(): array
public function getExecutedTasks(): array
{
try {
return $this->connection->fetchAllAssociativeIndexed('SELECT id, created_at FROM one_time_tasks');
Expand All @@ -46,11 +46,18 @@ private function getExecutedTasks(): array
}
}

private function markAsRun(string $id): void
public function markAsRun(string $id): void
{
$this->connection->executeStatement('INSERT INTO one_time_tasks (id, created_at) VALUES (:id, :created_at)', [
'id' => $id,
'created_at' => (new \DateTime())->format('Y-m-d H:i:s'),
]);
}

public function remove(string $id): void
{
$this->connection->executeStatement('DELETE FROM one_time_tasks WHERE id = ?', [
$id,
]);
}
}
29 changes: 29 additions & 0 deletions tests/Command/OneTimeTaskListCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types=1);

namespace Shopware\Deployment\Tests\Command;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Shopware\Deployment\Command\OneTimeTaskListCommand;
use Shopware\Deployment\Services\OneTimeTasks;
use Symfony\Component\Console\Tester\CommandTester;

#[CoversClass(OneTimeTaskListCommand::class)]
class OneTimeTaskListCommandTest extends TestCase
{
public function testList(): void
{
$taskService = $this->createMock(OneTimeTasks::class);
$taskService->method('getExecutedTasks')->willReturn([
'test' => ['created_at' => '2021-01-01 00:00:00'],
]);

$cmd = new OneTimeTaskListCommand($taskService);
$tester = new CommandTester($cmd);
$tester->execute([]);

$tester->assertCommandIsSuccessful();
static::assertStringContainsString('test', $tester->getDisplay());
static::assertStringContainsString('2021-01-01 00:00:00', $tester->getDisplay());
}
}
45 changes: 45 additions & 0 deletions tests/Command/OneTimeTaskMarkCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php declare(strict_types=1);

namespace Shopware\Deployment\Tests\Command;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Shopware\Deployment\Command\OneTimeTaskMarkCommand;
use Shopware\Deployment\Services\OneTimeTasks;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;

#[CoversClass(OneTimeTaskMarkCommand::class)]
class OneTimeTaskMarkCommandTest extends TestCase
{
public function testMark(): void
{
$taskService = $this->createMock(OneTimeTasks::class);
$taskService
->expects(static::once())
->method('markAsRun')
->with('test');

$cmd = new OneTimeTaskMarkCommand($taskService);
$tester = new CommandTester($cmd);
$tester->execute(['id' => 'test']);

$tester->assertCommandIsSuccessful();
}

public function testMarkAgain(): void
{
$taskService = $this->createMock(OneTimeTasks::class);
$taskService
->expects(static::once())
->method('markAsRun')
->willThrowException(new \Exception('Task already marked as run'));

$cmd = new OneTimeTaskMarkCommand($taskService);

$tester = new CommandTester($cmd);
$tester->execute(['id' => 'test']);

static::assertSame(Command::FAILURE, $tester->getStatusCode());
}
}
28 changes: 28 additions & 0 deletions tests/Command/OneTimeTaskUnmarkCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types=1);

namespace Shopware\Deployment\Tests\Command;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Shopware\Deployment\Command\OneTimeTaskUnmarkCommand;
use Shopware\Deployment\Services\OneTimeTasks;
use Symfony\Component\Console\Tester\CommandTester;

#[CoversClass(OneTimeTaskUnmarkCommand::class)]
class OneTimeTaskUnmarkCommandTest extends TestCase
{
public function testUnmark(): void
{
$taskService = $this->createMock(OneTimeTasks::class);
$taskService
->expects(static::once())
->method('remove')
->with('test');

$cmd = new OneTimeTaskUnmarkCommand($taskService);
$tester = new CommandTester($cmd);
$tester->execute(['id' => 'test']);

$tester->assertCommandIsSuccessful();
}
}
11 changes: 11 additions & 0 deletions tests/Services/OneTimeTasksTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,15 @@ public function testTaskAlreadyExecuted(): void
$tasks = new OneTimeTasks($processHelper, $connection, $configuration);
$tasks->execute($output);
}

public function testRemove(): void
{
$connection = $this->createMock(Connection::class);
$connection->expects($this->once())->method('executeStatement')->with('DELETE FROM one_time_tasks WHERE id = ?', ['test']);

$processHelper = $this->createMock(ProcessHelper::class);

$tasks = new OneTimeTasks($processHelper, $connection, new ProjectConfiguration());
$tasks->remove('test');
}
}

0 comments on commit e2e783b

Please sign in to comment.