Skip to content

Commit

Permalink
IBX-8823: Added CLI command to update user (#86)
Browse files Browse the repository at this point in the history
* IBX-8823: Added CLI command to update user

* cr remarks

* cr remarks vol1

* cr remarks vol2

* added test coverage

* cr remark vol3
  • Loading branch information
konradoboza authored Sep 6, 2024
1 parent b41ec1e commit cbdb8eb
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 1 deletion.
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ parameters:
path: src/bundle/Controller/PasswordResetController.php

-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Form\\\\FormInterface\\:\\:getClickedButton\\(\\)\\.$#"
message: "#^Call to an undefined method Symfony\\\\Component\\\\Form\\\\FormInterface\\<mixed\\>\\:\\:getClickedButton\\(\\)\\.$#"
count: 4
path: src/bundle/Controller/UserRegisterController.php

Expand Down
135 changes: 135 additions & 0 deletions src/bundle/Command/UpdateUserCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\User\Command;

use Ibexa\Contracts\Core\Repository\Repository;
use Ibexa\Contracts\Core\Repository\UserService;
use Ibexa\Contracts\Core\Repository\Values\User\User;
use Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct;
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\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(name: 'ibexa:user:update-user', description: 'Updates basic user data.')]
final class UpdateUserCommand extends Command
{
public function __construct(
private readonly UserService $userService,
private readonly Repository $repository,
) {
parent::__construct();
}

protected function configure(): void
{
$this->addArgument(
'user',
InputArgument::REQUIRED,
'User login',
);
$this->addOption(
'password',
null,
InputOption::VALUE_OPTIONAL,
'New plaintext password (input will be in a "hidden" mode)',
false
);
$this->addOption(
'email',
null,
InputOption::VALUE_REQUIRED,
'New e-mail address',
);
$this->addOption(
'enable',
null,
InputOption::VALUE_NONE,
'Flag enabling the user being updated',
);
$this->addOption(
'disable',
null,
InputOption::VALUE_NONE,
'Flag disabling the user being updated',
);
}

protected function interact(InputInterface $input, OutputInterface $output): void
{
$io = new SymfonyStyle($input, $output);
$password = $input->getOption('password');

if ($password !== null) {
return;
}

$password = $io->askHidden('Password (your input will be hidden)');
$input->setOption('password', $password);
}

/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$userReference = $input->getArgument('user');
$password = $input->getOption('password');
$enable = $input->getOption('enable');
$disable = $input->getOption('disable');
$email = $input->getOption('email');

if (!$password && !$enable && !$disable && $email === null) {
$io->error('No new user data specified, exiting.');

return Command::FAILURE;
}

$user = $this->userService->loadUserByLogin($userReference);

if ($enable && $disable) {
$io->error('--enable and --disable options cannot be used simultaneously.');

return Command::FAILURE;
}

$userUpdateStruct = new UserUpdateStruct();
$userUpdateStruct->password = $password;
$userUpdateStruct->email = $email;
$userUpdateStruct->enabled = $this->resolveEnabledFlag($enable, $disable);

$this->repository->sudo(
function () use ($user, $userUpdateStruct): User {
return $this->userService->updateUser($user, $userUpdateStruct);
}
);

$io->success(sprintf(
'User "%s" was successfully updated.',
$user->getLogin(),
));

return Command::SUCCESS;
}

private function resolveEnabledFlag(bool $enable, bool $disable): ?bool
{
if (!$enable && !$disable) {
return null;
}

return $enable === true;
}
}
87 changes: 87 additions & 0 deletions tests/bundle/Command/UpdateUserCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\Bundle\User\Command;

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;

final class UpdateUserCommandTest extends KernelTestCase
{
private readonly CommandTester $commandTester;

protected function setUp(): void
{
self::bootKernel();

$application = new Application(self::$kernel);
$application->setAutoExit(false);

$command = $application->find('ibexa:user:update-user');
$this->commandTester = new CommandTester($command);
}

public function testExecuteWithoutOptionsReturnsFailure(): void
{
$this->commandTester->execute([
'user' => 'anonymous',
]);

self::assertStringContainsString(
'No new user data specified, exiting.',
$this->commandTester->getDisplay()
);

self::assertSame(
Command::FAILURE,
$this->commandTester->getStatusCode()
);
}

public function testExecuteWithEnableAndDisableOptionsReturnsFailure(): void
{
$this->commandTester->execute(
[
'user' => 'anonymous',
'--enable' => true,
'--disable' => true,
],
);

self::assertStringContainsString(
'--enable and --disable options cannot be used simultaneously.',
$this->commandTester->getDisplay()
);

self::assertSame(
Command::FAILURE,
$this->commandTester->getStatusCode()
);
}

public function testExecuteReturnsSuccess(): void
{
$this->commandTester->execute(
[
'user' => 'anonymous',
'--password' => true,
'--email' => 'foo@bar.com',
'--enable' => true,
],
);

self::assertStringContainsString(
'User "anonymous" was successfully updated.',
$this->commandTester->getDisplay()
);

$this->commandTester->assertCommandIsSuccessful();
}
}

0 comments on commit cbdb8eb

Please sign in to comment.