diff --git a/src/Application.php b/src/Application.php index b6c6ea9..f506732 100644 --- a/src/Application.php +++ b/src/Application.php @@ -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()); diff --git a/src/Command/OneTimeTaskListCommand.php b/src/Command/OneTimeTaskListCommand.php new file mode 100644 index 0000000..13f989e --- /dev/null +++ b/src/Command/OneTimeTaskListCommand.php @@ -0,0 +1,38 @@ +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; + } +} diff --git a/src/Command/OneTimeTaskMarkCommand.php b/src/Command/OneTimeTaskMarkCommand.php new file mode 100644 index 0000000..785ea24 --- /dev/null +++ b/src/Command/OneTimeTaskMarkCommand.php @@ -0,0 +1,45 @@ +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; + } +} diff --git a/src/Command/OneTimeTaskUnmarkCommand.php b/src/Command/OneTimeTaskUnmarkCommand.php new file mode 100644 index 0000000..00bc3df --- /dev/null +++ b/src/Command/OneTimeTaskUnmarkCommand.php @@ -0,0 +1,39 @@ +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; + } +} diff --git a/src/Services/OneTimeTasks.php b/src/Services/OneTimeTasks.php index f059727..f0434f2 100644 --- a/src/Services/OneTimeTasks.php +++ b/src/Services/OneTimeTasks.php @@ -35,7 +35,7 @@ public function execute(OutputInterface $output): void /** * @return array[] */ - private function getExecutedTasks(): array + public function getExecutedTasks(): array { try { return $this->connection->fetchAllAssociativeIndexed('SELECT id, created_at FROM one_time_tasks'); @@ -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, + ]); + } } diff --git a/tests/Command/OneTimeTaskListCommandTest.php b/tests/Command/OneTimeTaskListCommandTest.php new file mode 100644 index 0000000..34a6e52 --- /dev/null +++ b/tests/Command/OneTimeTaskListCommandTest.php @@ -0,0 +1,29 @@ +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()); + } +} diff --git a/tests/Command/OneTimeTaskMarkCommandTest.php b/tests/Command/OneTimeTaskMarkCommandTest.php new file mode 100644 index 0000000..6f6391d --- /dev/null +++ b/tests/Command/OneTimeTaskMarkCommandTest.php @@ -0,0 +1,45 @@ +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()); + } +} diff --git a/tests/Command/OneTimeTaskUnmarkCommandTest.php b/tests/Command/OneTimeTaskUnmarkCommandTest.php new file mode 100644 index 0000000..92b0ed6 --- /dev/null +++ b/tests/Command/OneTimeTaskUnmarkCommandTest.php @@ -0,0 +1,28 @@ +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(); + } +} diff --git a/tests/Services/OneTimeTasksTest.php b/tests/Services/OneTimeTasksTest.php index 4078766..c17e544 100644 --- a/tests/Services/OneTimeTasksTest.php +++ b/tests/Services/OneTimeTasksTest.php @@ -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'); + } }