diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2022-09-29 20:46:24 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-29 20:46:24 +0300 |
commit | 59a51634ec083e467b7787328f821e513a37e428 (patch) | |
tree | a4a58e2948628659a4af34821dbde6c6c3e63f0c | |
parent | f9a24263faab7b4d9ab2adc0500fd30499a31476 (diff) | |
parent | d6a05a4ea81308f2319049e4fa96b341809666e7 (diff) |
Merge pull request #8048 from nextcloud/backport/7986/stable25
[stable25] Allow to count the participants per call
-rw-r--r-- | appinfo/info.xml | 4 | ||||
-rw-r--r-- | lib/Command/Monitor/Calls.php | 91 | ||||
-rw-r--r-- | lib/Command/Monitor/HasActiveCalls.php (renamed from lib/Command/ActiveCalls.php) | 7 | ||||
-rw-r--r-- | lib/Command/Monitor/Room.php | 139 | ||||
-rw-r--r-- | tests/integration/features/bootstrap/CommandLineTrait.php | 19 | ||||
-rw-r--r-- | tests/integration/features/command/active-calls.feature | 2 | ||||
-rw-r--r-- | tests/integration/features/command/monitor-calls.feature | 61 | ||||
-rw-r--r-- | tests/integration/features/command/monitor-room.feature | 37 | ||||
-rw-r--r-- | tests/psalm-baseline.xml | 27 |
9 files changed, 371 insertions, 16 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml index da07f9264..51a82fc5d 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -78,12 +78,14 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m </repair-steps> <commands> - <command>OCA\Talk\Command\ActiveCalls</command> <command>OCA\Talk\Command\Command\Add</command> <command>OCA\Talk\Command\Command\AddSamples</command> <command>OCA\Talk\Command\Command\Delete</command> <command>OCA\Talk\Command\Command\ListCommand</command> <command>OCA\Talk\Command\Command\Update</command> + <command>OCA\Talk\Command\Monitor\Calls</command> + <command>OCA\Talk\Command\Monitor\HasActiveCalls</command> + <command>OCA\Talk\Command\Monitor\Room</command> <command>OCA\Talk\Command\Stun\Add</command> <command>OCA\Talk\Command\Stun\Delete</command> <command>OCA\Talk\Command\Stun\ListCommand</command> diff --git a/lib/Command/Monitor/Calls.php b/lib/Command/Monitor/Calls.php new file mode 100644 index 000000000..25527d058 --- /dev/null +++ b/lib/Command/Monitor/Calls.php @@ -0,0 +1,91 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Talk\Command\Monitor; + +use OC\Core\Command\Base; +use OCA\Talk\Participant; +use OCP\IDBConnection; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Calls extends Base { + protected IDBConnection $connection; + + public function __construct(IDBConnection $connection) { + parent::__construct(); + + $this->connection = $connection; + } + + protected function configure(): void { + parent::configure(); + + $this + ->setName('talk:monitor:calls') + ->setDescription('Prints a list with conversations that have an active call as well as their participant count') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $query = $this->connection->getQueryBuilder(); + $subQuery = $this->connection->getQueryBuilder(); + $subQuery->select('attendee_id') + ->from('talk_sessions') + ->where($subQuery->expr()->gt('in_call', $query->createNamedParameter(Participant::FLAG_DISCONNECTED))) + ->andWhere($subQuery->expr()->gt('last_ping', $query->createNamedParameter(time() - 60))) + ->groupBy('attendee_id'); + + $query->select('r.token', $query->func()->count('*', 'num_attendees')) + ->from('talk_attendees', 'a') + ->leftJoin('a', 'talk_rooms', 'r', $query->expr()->eq('a.room_id', 'r.id')) + ->where($query->expr()->in('a.id', $query->createFunction('(' . $subQuery->getSQL() . ')'))) + ->groupBy('r.token'); + + $data = []; + $result = $query->executeQuery(); + while ($row = $result->fetch()) { + $key = (string) $row['token']; + if ($input->getOption('output') === Base::OUTPUT_FORMAT_PLAIN) { + $key = '"' . $key . '"'; + } + + $data[$key] = (int) $row['num_attendees']; + } + $result->closeCursor(); + + if ($input->getOption('output') === Base::OUTPUT_FORMAT_PLAIN) { + $numCalls = count($data); + $numParticipants = array_sum($data); + + if (empty($data)) { + $output->writeln('<info>No calls in progress</info>'); + } else { + $output->writeln(sprintf('<error>There are currently %1$d calls in progress with %2$d participants</error>', $numCalls, $numParticipants)); + } + } + + $this->writeArrayInOutputFormat($input, $output, $data); + return 0; + } +} diff --git a/lib/Command/ActiveCalls.php b/lib/Command/Monitor/HasActiveCalls.php index 48d75c15a..78a271d9d 100644 --- a/lib/Command/ActiveCalls.php +++ b/lib/Command/Monitor/HasActiveCalls.php @@ -21,7 +21,7 @@ declare(strict_types=1); * */ -namespace OCA\Talk\Command; +namespace OCA\Talk\Command\Monitor; use OC\Core\Command\Base; use OCA\Talk\Participant; @@ -29,7 +29,7 @@ use OCP\IDBConnection; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class ActiveCalls extends Base { +class HasActiveCalls extends Base { protected IDBConnection $connection; public function __construct(IDBConnection $connection) { @@ -43,7 +43,8 @@ class ActiveCalls extends Base { $this ->setName('talk:active-calls') - ->setDescription('Allows you to check if calls are currently in process') ; + ->setDescription('Allows you to check if calls are currently in process') + ; } protected function execute(InputInterface $input, OutputInterface $output): int { diff --git a/lib/Command/Monitor/Room.php b/lib/Command/Monitor/Room.php new file mode 100644 index 000000000..a8f677ba9 --- /dev/null +++ b/lib/Command/Monitor/Room.php @@ -0,0 +1,139 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Talk\Command\Monitor; + +use OC\Core\Command\Base; +use OCA\Talk\Participant; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class Room extends Base { + protected IDBConnection $connection; + + public function __construct(IDBConnection $connection) { + parent::__construct(); + + $this->connection = $connection; + } + + protected function configure(): void { + parent::configure(); + + $this + ->setName('talk:monitor:room') + ->setDescription('Prints a list with conversations that have an active call as well as their participant count') + ->addArgument( + 'token', + InputArgument::REQUIRED, + 'Token of the room to monitor' + ) + ->addOption( + 'separator', + null, + InputOption::VALUE_REQUIRED, + 'Separator for the CSV list when output=csv is used', + ',' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $token = $input->getArgument('token'); + + $query = $this->connection->getQueryBuilder(); + $query->select('id') + ->from('talk_rooms') + ->where($query->expr()->eq('token', $query->createNamedParameter($token))); + + $result = $query->executeQuery(); + $roomId = (int) $result->fetchOne(); + $result->closeCursor(); + + if ($roomId === 0) { + if ($input->getOption('output') === Base::OUTPUT_FORMAT_PLAIN) { + $output->writeln(sprintf('<error>Room with token %1$s not found</error>', $token)); + } + return 1; + } + + $query = $this->connection->getQueryBuilder(); + $query->select($query->func()->count('*', 'num_attendees')) + ->from('talk_attendees') + ->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT))); + + $result = $query->executeQuery(); + $numAttendees = (int) $result->fetchOne(); + $result->closeCursor(); + + $numSessions = $numSessionsInCall = 0; + $query = $this->connection->getQueryBuilder(); + $query->select($query->func()->count('s.id', 'num_sessions')) + ->from('talk_sessions', 's') + ->leftJoin('s', 'talk_attendees', 'a', $query->expr()->eq('a.id', 's.attendee_id')) + ->where($query->expr()->eq('a.room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->gt('s.last_ping', $query->createNamedParameter(time() - 60, IQueryBuilder::PARAM_INT))); + + $result = $query->executeQuery(); + $numSessions = (int) $result->fetchOne(); + $result->closeCursor(); + + $query = $this->connection->getQueryBuilder(); + $query->select($query->func()->count('s.id', 'num_sessions')) + ->from('talk_sessions', 's') + ->leftJoin('s', 'talk_attendees', 'a', $query->expr()->eq('a.id', 's.attendee_id')) + ->where($query->expr()->eq('a.room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->gt('s.in_call', $query->createNamedParameter(Participant::FLAG_DISCONNECTED, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->gt('s.last_ping', $query->createNamedParameter(time() - 60, IQueryBuilder::PARAM_INT))); + + $result = $query->executeQuery(); + $numSessionsInCall = (int) $result->fetchOne(); + $result->closeCursor(); + + if ($input->getOption('output') === Base::OUTPUT_FORMAT_PLAIN) { + $output->writeln(sprintf( + 'The conversation has %1$d attendees with %2$d sessions of which %3$d are in the call.', + $numAttendees, + $numSessions, + $numSessionsInCall + )); + return 0; + } + if ($input->getOption('output') === 'csv') { + $separator = $input->getOption('separator'); + $output->writeln($numAttendees . $separator . $numSessions . $separator . $numSessionsInCall); + return 0; + } + + $this->writeArrayInOutputFormat($input, $output, [ + 'attendees' => $numAttendees, + 'sessions' => $numSessions, + 'call' => $numSessionsInCall, + ]); + return 0; + } +} diff --git a/tests/integration/features/bootstrap/CommandLineTrait.php b/tests/integration/features/bootstrap/CommandLineTrait.php index 1f6eff9f7..1c9a3d30b 100644 --- a/tests/integration/features/bootstrap/CommandLineTrait.php +++ b/tests/integration/features/bootstrap/CommandLineTrait.php @@ -113,6 +113,8 @@ trait CommandLineTrait { public function theCommandWasSuccessful() { $exceptions = $this->findExceptions(); if ($this->lastCode !== 0) { + echo $this->lastStdErr; + $msg = 'The command was not successful, exit code was ' . $this->lastCode . '.'; if (!empty($exceptions)) { $msg .= ' Exceptions: ' . implode(', ', $exceptions); @@ -158,6 +160,23 @@ trait CommandLineTrait { } /** + * @Then /^the command output contains the list entry '([^']*)' with value '([^']*)'$/ + */ + public function theCommandOutputContainsTheListEntry(string $key, string $value): void { + if (preg_match('/^"ROOM\(([^"]+)\)"$/', $key, $matches)) { + $key = '"' . self::$identifierToToken[$matches[1]] . '"'; + } + $text = '- ' . $key . ': ' . $value; + + if ($this->lastStdOut === '' && $this->lastStdErr !== '') { + Assert::assertStringContainsString($text, $this->lastStdErr, 'The command did not output the expected text on stdout'); + Assert::assertTrue(false, 'The command did not output the expected text on stdout but stderr'); + } + + Assert::assertStringContainsString($text, $this->lastStdOut, 'The command did not output the expected text on stdout'); + } + + /** * @Then /^the command error output contains the text "([^"]*)"$/ */ public function theCommandErrorOutputContainsTheText($text) { diff --git a/tests/integration/features/command/active-calls.feature b/tests/integration/features/command/active-calls.feature index c7ac37f0b..920fa5e69 100644 --- a/tests/integration/features/command/active-calls.feature +++ b/tests/integration/features/command/active-calls.feature @@ -1,4 +1,4 @@ -Feature: create +Feature: command/active-calls Background: Given user "participant1" exists diff --git a/tests/integration/features/command/monitor-calls.feature b/tests/integration/features/command/monitor-calls.feature new file mode 100644 index 000000000..973a8e04d --- /dev/null +++ b/tests/integration/features/command/monitor-calls.feature @@ -0,0 +1,61 @@ +Feature: command/monitor-calls + + Background: + Given user "participant1" exists + Given user "participant2" exists + Given user "participant3" exists + + Scenario: No call in progress + Given invoking occ with "talk:monitor:calls" + Then the command was successful + And the command output contains the text "No calls in progress" + + Scenario: Users only chatting + When user "participant1" creates room "room" (v4) + | roomType | 3 | + | roomName | room | + Then user "participant1" joins room "room" with 200 (v4) + + Given invoking occ with "talk:monitor:calls" + Then the command was successful + And the command output contains the text "No calls in progress" + + Then user "participant1" leaves room "room" with 200 (v4) + + + Scenario: Calls in progress + When user "participant1" creates room "room" (v4) + | roomType | 3 | + | roomName | room | + + Then user "participant1" joins room "room" with 200 (v4) + And user "participant1" joins call "room" with 200 (v4) + + Given invoking occ with "talk:monitor:calls" + Then the command was successful + And the command output contains the text "There are currently 1 calls in progress with 1 participants" + And the command output contains the list entry '"ROOM(room)"' with value '1' + + When user "participant3" creates room "room2" (v4) + | roomType | 1 | + | invite | participant2 | + And user "participant2" gets room "room2" with 200 (v4) + + And user "participant2" joins room "room2" with 200 (v4) + And user "participant3" joins room "room2" with 200 (v4) + And user "participant2" joins call "room2" with 200 (v4) + And user "participant3" joins call "room2" with 200 (v4) + + Given invoking occ with "talk:monitor:calls" + Then the command was successful + And the command output contains the text "There are currently 2 calls in progress with 3 participants" + And the command output contains the list entry '"ROOM(room)"' with value '1' + And the command output contains the list entry '"ROOM(room2)"' with value '2' + + Then user "participant1" leaves call "room" with 200 (v4) + And user "participant1" leaves room "room" with 200 (v4) + + Given invoking occ with "talk:monitor:calls" + Then the command was successful + And the command output contains the text "There are currently 1 calls in progress with 2 participants" + And the command output contains the list entry '"ROOM(room2)"' with value '2' diff --git a/tests/integration/features/command/monitor-room.feature b/tests/integration/features/command/monitor-room.feature new file mode 100644 index 000000000..fa417dc20 --- /dev/null +++ b/tests/integration/features/command/monitor-room.feature @@ -0,0 +1,37 @@ +Feature: command/monitor-room + + Background: + Given user "participant1" exists + Given user "participant2" exists + + Scenario: No call in progress + Given invoking occ with "talk:monitor:room abcdef" + Then the command failed with exit code 1 + And the command output contains the text "Room with token abcdef not found" + + Scenario: From nothing to calling + When user "participant1" creates room "room" (v4) + | roomType | 3 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + And user "participant2" gets room "room" with 200 (v4) + + Given invoking occ with "talk:monitor:room room-name:room" + Then the command was successful + And the command output contains the text "The conversation has 2 attendees with 0 sessions of which 0 are in the call." + + Given user "participant1" joins room "room" with 200 (v4) + And invoking occ with "talk:monitor:room room-name:room" + Then the command was successful + And the command output contains the text "The conversation has 2 attendees with 1 sessions of which 0 are in the call." + + Given user "participant1" joins call "room" with 200 (v4) + And invoking occ with "talk:monitor:room room-name:room" + Then the command was successful + And the command output contains the text "The conversation has 2 attendees with 1 sessions of which 1 are in the call." + + Given user "participant2" joins room "room" with 200 (v4) + And user "participant2" joins call "room" with 200 (v4) + And invoking occ with "talk:monitor:room room-name:room" + Then the command was successful + And the command output contains the text "The conversation has 2 attendees with 2 sessions of which 2 are in the call." diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index 0b509c127..bbcd46119 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<files psalm-version="4.23.0@f1fe6ff483bf325c803df9f510d09a03fd796f88"> +<files psalm-version="4.27.0@faf106e717c37b8c81721845dba9de3d8deed8ff"> <file src="lib/AppInfo/Application.php"> <UndefinedClass occurrences="2"> <code>BeforeTemplateRenderedEvent</code> @@ -37,11 +37,6 @@ <code>getById</code> </UndefinedInterfaceMethod> </file> - <file src="lib/Command/ActiveCalls.php"> - <UndefinedClass occurrences="1"> - <code>Base</code> - </UndefinedClass> - </file> <file src="lib/Command/Command/Add.php"> <UndefinedClass occurrences="1"> <code>Base</code> @@ -67,6 +62,21 @@ <code>Base</code> </UndefinedClass> </file> + <file src="lib/Command/Monitor/Calls.php"> + <UndefinedClass occurrences="1"> + <code>Base</code> + </UndefinedClass> + </file> + <file src="lib/Command/Monitor/HasActiveCalls.php"> + <UndefinedClass occurrences="1"> + <code>Base</code> + </UndefinedClass> + </file> + <file src="lib/Command/Monitor/Room.php"> + <UndefinedClass occurrences="1"> + <code>Base</code> + </UndefinedClass> + </file> <file src="lib/Command/Room/Add.php"> <UndefinedClass occurrences="1"> <code>Base</code> @@ -178,11 +188,6 @@ <code>IRootFolder</code> </MissingDependency> </file> - <file src="lib/Controller/SignalingController.php"> - <UndefinedClass occurrences="1"> - <code>ConnectException</code> - </UndefinedClass> - </file> <file src="lib/Controller/TempAvatarController.php"> <UndefinedClass occurrences="1"> <code>Filesystem</code> |