Merge pull request #59271 from nextcloud/feat/occ-group-add-user-batch

enh(occ): make it possible to add an arbitrary number of users to a g…
This commit is contained in:
Arthur Schiwon 2026-05-20 11:01:20 +02:00 committed by GitHub
commit 49021973e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 225 additions and 43 deletions

View file

@ -35,8 +35,8 @@ class AddUser extends Base {
'group to add the user to'
)->addArgument(
'user',
InputArgument::REQUIRED,
'user to add to the group'
InputArgument::REQUIRED + InputArgument::IS_ARRAY,
'users to add to the group',
);
}
@ -45,15 +45,35 @@ class AddUser extends Base {
$group = $this->groupManager->get($input->getArgument('group'));
if (is_null($group)) {
$output->writeln('<error>group not found</error>');
return 1;
return Base::FAILURE;
}
$user = $this->userManager->get($input->getArgument('user'));
if (is_null($user)) {
$output->writeln('<error>user not found</error>');
return 1;
$allUsersFound = true;
$noUserFound = true;
$users = (array)$input->getArgument('user');
foreach ($users as $userId) {
$user = $this->userManager->get($userId);
if (is_null($user)) {
$output->writeln('<error>user ' . $userId . ' not found</error>');
$allUsersFound = false;
continue;
}
$noUserFound = false;
$group->addUser($user);
unset($user);
$output->writeln('<info>user ' . $userId . ' added</info>');
}
$group->addUser($user);
return 0;
if (!$allUsersFound && !$noUserFound) {
$output->writeln('<error>Some users were not found, all others where added to the group.</error>');
return Base::FAILURE;
}
if ($noUserFound) {
return Base::FAILURE;
}
return Base::SUCCESS;
}
/**

View file

@ -35,8 +35,8 @@ class RemoveUser extends Base {
'group to remove the user from'
)->addArgument(
'user',
InputArgument::REQUIRED,
'user to remove from the group'
InputArgument::REQUIRED + InputArgument::IS_ARRAY,
'users to remove from the group'
);
}
@ -45,15 +45,35 @@ class RemoveUser extends Base {
$group = $this->groupManager->get($input->getArgument('group'));
if (is_null($group)) {
$output->writeln('<error>group not found</error>');
return 1;
return Base::FAILURE;
}
$user = $this->userManager->get($input->getArgument('user'));
if (is_null($user)) {
$output->writeln('<error>user not found</error>');
return 1;
$allUsersFound = true;
$noUserFound = true;
$users = (array)$input->getArgument('user');
foreach ($users as $userId) {
$user = $this->userManager->get($userId);
if (is_null($user)) {
$output->writeln('<error>user ' . $userId . ' not found</error>');
$allUsersFound = false;
continue;
}
$noUserFound = false;
$group->removeUser($user);
unset($user);
$output->writeln('<info>user ' . $userId . ' removed</info>');
}
$group->removeUser($user);
return 0;
if (!$allUsersFound && !$noUserFound) {
$output->writeln('<error>Some users were not found, all others where removed from the group.</error>');
return Base::FAILURE;
}
if ($noUserFound) {
return Base::FAILURE;
}
return Base::SUCCESS;
}
/**

View file

@ -39,21 +39,26 @@ class AddUserTest extends TestCase {
$this->groupManager = $this->createMock(IGroupManager::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->command = new AddUser($this->userManager, $this->groupManager);
$this->input = $this->createMock(InputInterface::class);
$this->input->method('getArgument')
->willReturnCallback(function ($arg) {
if ($arg === 'group') {
return 'myGroup';
} elseif ($arg === 'user') {
return 'myUser';
}
throw new \Exception();
});
$this->output = $this->createMock(OutputInterface::class);
}
protected function configureInput(array|string $returnGroup, array|string $returnUser): void {
$this->input = $this->createMock(InputInterface::class);
$this->input->method('getArgument')
->willReturnCallback(function ($arg) use ($returnGroup, $returnUser) {
if ($arg === 'group') {
return $returnGroup;
}
if ($arg === 'user') {
return $returnUser;
}
throw new \Exception();
});
}
public function testNoGroup(): void {
$this->configureInput('myGroup', 'myUser');
$this->groupManager->method('get')
->with('myGroup')
->willReturn(null);
@ -66,6 +71,8 @@ class AddUserTest extends TestCase {
}
public function testNoUser(): void {
$this->configureInput('myGroup', 'myUser');
$group = $this->createMock(IGroup::class);
$this->groupManager->method('get')
->with('myGroup')
@ -77,12 +84,14 @@ class AddUserTest extends TestCase {
$this->output->expects($this->once())
->method('writeln')
->with('<error>user not found</error>');
->with('<error>user myUser not found</error>');
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
}
public function testAdd(): void {
$this->configureInput('myGroup', 'myUser');
$group = $this->createMock(IGroup::class);
$this->groupManager->method('get')
->with('myGroup')
@ -99,4 +108,66 @@ class AddUserTest extends TestCase {
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
}
public function testAddMultiple(): void {
$this->configureInput('myGroup', ['myUser', 'myOtherUser']);
$group = $this->createMock(IGroup::class);
$this->groupManager->method('get')
->with('myGroup')
->willReturn($group);
$user1 = $this->createMock(IUser::class);
$user2 = $this->createMock(IUser::class);
$this->userManager->method('get')
->willReturnMap([
['myUser', $user1],
['myOtherUser', $user2],
]);
$group->expects($this->exactly(2))
->method('addUser')
->with($this->callback(static fn (IUser $user): bool => in_array($user, [$user1, $user2], true)));
$this->output->expects($this->exactly(2))
->method('writeln')
->with($this->callback(static fn (string $message): bool => in_array($message,
[
'<info>user myUser added</info>',
'<info>user myOtherUser added</info>',
], true)));
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
}
public function testAddMultiplePartialSuccess(): void {
$this->configureInput('myGroup', ['myUser', 'myOtherUser']);
$group = $this->createMock(IGroup::class);
$this->groupManager->method('get')
->with('myGroup')
->willReturn($group);
$user = $this->createMock(IUser::class);
$this->userManager->method('get')
->willReturnMap([
['myUser', $user],
['myOtherUser', null],
]);
$group->expects($this->once())
->method('addUser')
->with($user);
$this->output->expects($this->exactly(3))
->method('writeln')
->with($this->callback(static fn (string $message): bool => in_array($message,
[
'<info>user myUser added</info>',
'<error>user myOtherUser not found</error>',
'<error>Some users were not found, all others where added to the group.</error>',
], true)));
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
}
}

View file

@ -39,21 +39,26 @@ class RemoveUserTest extends TestCase {
$this->groupManager = $this->createMock(IGroupManager::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->command = new RemoveUser($this->userManager, $this->groupManager);
$this->input = $this->createMock(InputInterface::class);
$this->input->method('getArgument')
->willReturnCallback(function ($arg) {
if ($arg === 'group') {
return 'myGroup';
} elseif ($arg === 'user') {
return 'myUser';
}
throw new \Exception();
});
$this->output = $this->createMock(OutputInterface::class);
}
protected function configureInput(array|string $returnGroup, array|string $returnUser): void {
$this->input = $this->createMock(InputInterface::class);
$this->input->method('getArgument')
->willReturnCallback(function ($arg) use ($returnGroup, $returnUser) {
if ($arg === 'group') {
return $returnGroup;
}
if ($arg === 'user') {
return $returnUser;
}
throw new \Exception();
});
}
public function testNoGroup(): void {
$this->configureInput('myGroup', 'myUser');
$this->groupManager->method('get')
->with('myGroup')
->willReturn(null);
@ -66,6 +71,8 @@ class RemoveUserTest extends TestCase {
}
public function testNoUser(): void {
$this->configureInput('myGroup', 'myUser');
$group = $this->createMock(IGroup::class);
$this->groupManager->method('get')
->with('myGroup')
@ -77,12 +84,14 @@ class RemoveUserTest extends TestCase {
$this->output->expects($this->once())
->method('writeln')
->with('<error>user not found</error>');
->with('<error>user myUser not found</error>');
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
}
public function testAdd(): void {
public function testRemove(): void {
$this->configureInput('myGroup', 'myUser');
$group = $this->createMock(IGroup::class);
$this->groupManager->method('get')
->with('myGroup')
@ -99,4 +108,66 @@ class RemoveUserTest extends TestCase {
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
}
public function testRemoveMultiple(): void {
$this->configureInput('myGroup', ['myUser', 'myOtherUser']);
$group = $this->createMock(IGroup::class);
$this->groupManager->method('get')
->with('myGroup')
->willReturn($group);
$user1 = $this->createMock(IUser::class);
$user2 = $this->createMock(IUser::class);
$this->userManager->method('get')
->willReturnMap([
['myUser', $user1],
['myOtherUser', $user2],
]);
$group->expects($this->exactly(2))
->method('removeUser')
->with($this->callback(static fn (IUser $user): bool => in_array($user, [$user1, $user2], true)));
$this->output->expects($this->exactly(2))
->method('writeln')
->with($this->callback(static fn (string $message): bool => in_array($message,
[
'<info>user myUser removed</info>',
'<info>user myOtherUser removed</info>',
], true)));
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
}
public function testRemoveMultiplePartialSuccess(): void {
$this->configureInput('myGroup', ['myUser', 'myOtherUser']);
$group = $this->createMock(IGroup::class);
$this->groupManager->method('get')
->with('myGroup')
->willReturn($group);
$user = $this->createMock(IUser::class);
$this->userManager->method('get')
->willReturnMap([
['myUser', $user],
['myOtherUser', null],
]);
$group->expects($this->once())
->method('removeUser')
->with($user);
$this->output->expects($this->exactly(3))
->method('writeln')
->with($this->callback(static fn (string $message): bool => in_array($message,
[
'<info>user myUser removed</info>',
'<error>user myOtherUser not found</error>',
'<error>Some users were not found, all others where removed from the group.</error>',
], true)));
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
}
}