From 0d1748c8a8d08d189a56a48615468316b7e330f3 Mon Sep 17 00:00:00 2001 From: Luc Didry Date: Sat, 24 May 2025 10:03:32 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20=E2=80=94=20Add=20command=20to?= =?UTF-8?q?=20import=20URL=20for=20user=20from=20command-line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Command/Import/UrlCommand.php | 118 ++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/Command/Import/UrlCommand.php diff --git a/src/Command/Import/UrlCommand.php b/src/Command/Import/UrlCommand.php new file mode 100644 index 000000000..62a524d1c --- /dev/null +++ b/src/Command/Import/UrlCommand.php @@ -0,0 +1,118 @@ +setName('wallabag:import:url') + ->setDescription('Import a single URL') + ->addArgument('username', InputArgument::REQUIRED, 'User to add the URL to (value: username or id)') + ->addArgument('url', InputArgument::REQUIRED, 'URL to import') + ->addArgument('tags', InputArgument::OPTIONAL, 'Comma-separated list of tags to add') + ->addOption('markAsRead', null, InputOption::VALUE_OPTIONAL, 'Mark entry as read', false) + ->addOption('useUserId', null, InputOption::VALUE_NONE, 'Use user id instead of username to find account') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + // Turning off doctrine default logs queries for saving memory + $middlewares = $this->entityManager->getConnection()->getConfiguration()->getMiddlewares(); + $middlewaresWithoutLogging = array_filter($middlewares, fn (Middleware $middleware) => !$middleware instanceof LoggingMiddleware); + $this->entityManager->getConnection()->getConfiguration()->setMiddlewares($middlewaresWithoutLogging); + + if ($input->getOption('useUserId')) { + $entityUser = $this->userRepository->findOneById($input->getArgument('username')); + } else { + $entityUser = $this->userRepository->findOneByUsername($input->getArgument('username')); + } + + if (!\is_object($entityUser)) { + throw new Exception(\sprintf('User "%s" not found', $input->getArgument('username'))); + } + + // Authenticate user for paywalled websites + $token = new UsernamePasswordToken( + $entityUser, + 'main', + $entityUser->getRoles() + ); + + $this->tokenStorage->setToken($token); + $user = $this->tokenStorage->getToken()->getUser(); + \assert($user instanceof User); + + $url = $input->getArgument('url'); + + $existingEntry = $this->entityManager + ->getRepository(Entry::class) + ->findByUrlAndUserId($url, $user->getId()); + + if (false !== $existingEntry) { + $output->writeln(\sprintf('The URL %s is already in user’s entries.', $url)); + + return 1; + } + + $entry = new Entry($user); + + try { + $this->contentProxy->updateEntry($entry, $url); + } catch (\Exception $e) { + $output->writeln(\sprintf('Error trying to import the URL %s: %s.', $url, $e->getMessage())); + + return 1; + } + + if ($input->getOption('markAsRead')) { + $entry->updateArchived(true); + } + + $this->entityManager->persist($entry); + + $tags = explode(',', $input->getArgument('tags')); + if (count($tags) > 0) { + $this->tagsAssigner->assignTagsToEntry( + $entry, + $tags, + $this->entityManager->getUnitOfWork()->getScheduledEntityInsertions() + ); + } + + $this->entityManager->flush(); + + $output->writeln(\sprintf('URL %s successfully imported.', $url)); + + return 0; + } +} From 049d87e180ed4d3ce34359b6580ec543983ed902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Thu, 12 Jun 2025 21:08:04 +0200 Subject: [PATCH 2/2] Add tests --- src/Command/Import/UrlCommand.php | 2 +- tests/Command/Import/UrlCommandTest.php | 99 +++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tests/Command/Import/UrlCommandTest.php diff --git a/src/Command/Import/UrlCommand.php b/src/Command/Import/UrlCommand.php index 62a524d1c..9f40d7cde 100644 --- a/src/Command/Import/UrlCommand.php +++ b/src/Command/Import/UrlCommand.php @@ -101,7 +101,7 @@ class UrlCommand extends Command $this->entityManager->persist($entry); $tags = explode(',', $input->getArgument('tags')); - if (count($tags) > 0) { + if (\count($tags) > 1) { $this->tagsAssigner->assignTagsToEntry( $entry, $tags, diff --git a/tests/Command/Import/UrlCommandTest.php b/tests/Command/Import/UrlCommandTest.php new file mode 100644 index 000000000..c71c69ba7 --- /dev/null +++ b/tests/Command/Import/UrlCommandTest.php @@ -0,0 +1,99 @@ +expectException(RuntimeException::class); + $this->expectExceptionMessage('Not enough arguments'); + + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([]); + } + + public function testRunUrlCommandWithWrongUsername() + { + $this->expectException(NoResultException::class); + + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([ + 'username' => 'random', + 'url' => $this->url, + ]); + } + + public function testRunUrlCommand() + { + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([ + 'username' => 'admin', + 'url' => $this->url, + ]); + + $this->assertStringContainsString('successfully imported', $tester->getDisplay()); + } + + public function testRunUrlCommandWithTags() + { + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([ + 'username' => 'admin', + 'url' => $this->url, + 'tags' => 'sport, football', + ]); + + $this->assertStringContainsString('successfully imported', $tester->getDisplay()); + + $client = $this->getTestClient(); + $em = $client->getContainer()->get(EntityManagerInterface::class); + $entry = $em->getRepository(Entry::class)->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $this->assertContains('football', $entry->getTagsLabel()); + $this->assertNotContains('basketball', $entry->getTagsLabel()); + } + + public function testRunUrlCommandWithUserId() + { + $this->logInAs('admin'); + + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([ + 'username' => $this->getLoggedInUserId(), + 'url' => $this->url, + '--useUserId' => true, + ]); + + $this->assertStringContainsString('successfully imported', $tester->getDisplay()); + } +}