diff --git a/lib/Command/ExApp/Register.php b/lib/Command/ExApp/Register.php index 3d762d49..014030e7 100644 --- a/lib/Command/ExApp/Register.php +++ b/lib/Command/ExApp/Register.php @@ -11,7 +11,6 @@ use OCA\AppAPI\Service\AppAPIService; use OCA\AppAPI\Service\DaemonConfigService; use OCA\AppAPI\Service\ExAppApiScopeService; -use OCA\AppAPI\Service\ExAppScopesService; use OCA\AppAPI\Service\ExAppService; use OCP\IConfig; @@ -30,7 +29,6 @@ class Register extends Command { public function __construct( private readonly AppAPIService $service, private readonly DaemonConfigService $daemonConfigService, - private readonly ExAppScopesService $exAppScopesService, private readonly ExAppApiScopeService $exAppApiScopeService, private readonly DockerActions $dockerActions, private readonly ManualActions $manualActions, @@ -111,9 +109,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 2; } - $forceScopes = (bool) $input->getOption('force-scopes'); - $confirmRequiredScopes = $forceScopes; - if (!$forceScopes && $input->isInteractive()) { + $confirmRequiredScopes = (bool) $input->getOption('force-scopes'); + if (!$confirmRequiredScopes && $input->isInteractive()) { /** @var QuestionHelper $helper */ $helper = $this->getHelper('question'); @@ -147,14 +144,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 3; } if (count($appInfo['external-app']['scopes']) > 0) { - if (!$this->exAppScopesService->registerExAppScopes($exApp, $this->exAppApiScopeService->mapScopeGroupsToNumbers($appInfo['external-app']['scopes']))) { - $this->logger->error(sprintf('Error while registering API scopes for %s.', $appId)); - if ($outputConsole) { - $output->writeln(sprintf('Error while registering API scopes for %s.', $appId)); - } - $this->_unregisterExApp($appId, $isTestDeployMode); - return 1; - } $this->logger->info( sprintf('ExApp %s scope groups successfully set: %s', $exApp->getAppid(), implode(', ', $appInfo['external-app']['scopes'])) ); diff --git a/lib/Command/ExApp/Scopes/ListScopes.php b/lib/Command/ExApp/Scopes/ListScopes.php index e6168b9b..3a10dbe0 100644 --- a/lib/Command/ExApp/Scopes/ListScopes.php +++ b/lib/Command/ExApp/Scopes/ListScopes.php @@ -4,9 +4,7 @@ namespace OCA\AppAPI\Command\ExApp\Scopes; -use OCA\AppAPI\Db\ExAppScope; use OCA\AppAPI\Service\ExAppApiScopeService; -use OCA\AppAPI\Service\ExAppScopesService; use OCA\AppAPI\Service\ExAppService; use Symfony\Component\Console\Command\Command; @@ -18,7 +16,6 @@ class ListScopes extends Command { public function __construct( private readonly ExAppService $service, - private readonly ExAppScopesService $exAppScopeService, private readonly ExAppApiScopeService $exAppApiScopeService, ) { parent::__construct(); @@ -39,16 +36,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 2; } - $scopes = $this->exAppScopeService->getExAppScopes($exApp); + $scopes = $exApp->getApiScopes(); if (empty($scopes)) { $output->writeln(sprintf('No scopes granted for ExApp %s', $appId)); return 0; } $output->writeln(sprintf('ExApp %s scopes:', $exApp->getAppid())); - $mappedScopes = array_unique($this->exAppApiScopeService->mapScopeGroupsToNames(array_map(function (ExAppScope $scope) { - return intval($scope->getScopeGroup()); - }, $scopes))); + $mappedScopes = array_unique($this->exAppApiScopeService->mapScopeGroupsToNames($scopes)); $output->writeln(join(', ', $mappedScopes)); return 0; } diff --git a/lib/Command/ExApp/Update.php b/lib/Command/ExApp/Update.php index faf3279a..c9496b1a 100644 --- a/lib/Command/ExApp/Update.php +++ b/lib/Command/ExApp/Update.php @@ -4,7 +4,6 @@ namespace OCA\AppAPI\Command\ExApp; -use OCA\AppAPI\Db\ExAppScope; use OCA\AppAPI\DeployActions\DockerActions; use OCA\AppAPI\DeployActions\ManualActions; use OCA\AppAPI\Fetcher\ExAppArchiveFetcher; @@ -12,7 +11,6 @@ use OCA\AppAPI\Service\AppAPIService; use OCA\AppAPI\Service\DaemonConfigService; use OCA\AppAPI\Service\ExAppApiScopeService; -use OCA\AppAPI\Service\ExAppScopesService; use OCA\AppAPI\Service\ExAppService; use Psr\Log\LoggerInterface; @@ -29,7 +27,6 @@ class Update extends Command { public function __construct( private readonly AppAPIService $service, private readonly ExAppService $exAppService, - private readonly ExAppScopesService $exAppScopeService, private readonly ExAppApiScopeService $exAppApiScopeService, private readonly DaemonConfigService $daemonConfigService, private readonly DockerActions $dockerActions, @@ -141,6 +138,30 @@ private function updateExApp(InputInterface $input, OutputInterface $output, str return 0; } + // Default scopes approval process (compare new ExApp scopes) + $currentExAppScopes = $exApp->getApiScopes(); + // Prepare for prompt of newly requested ExApp scopes + $requiredScopes = array_values(array_diff($this->exAppApiScopeService->mapScopeGroupsToNumbers($appInfo['external-app']['scopes']), $currentExAppScopes)); + + $confirmScopes = (bool) $input->getOption('force-scopes'); + if (!$confirmScopes && $input->isInteractive()) { + /** @var QuestionHelper $helper */ + $helper = $this->getHelper('question'); + + if (count($requiredScopes) > 0) { + $output->writeln(sprintf('ExApp %s requested scopes: %s', $appId, implode(', ', + $this->exAppApiScopeService->mapScopeGroupsToNames($requiredScopes)))); + $question = new ConfirmationQuestion('Do you want to approve it? [y/N] ', false); + $confirmScopes = $helper->ask($input, $output, $question); + } else { + $confirmScopes = true; + } + } + if (!$confirmScopes && count($requiredScopes) > 0) { + $output->writeln(sprintf('ExApp %s required scopes not approved. Failed to finish ExApp update.', $appId)); + return 1; + } + $status = $exApp->getStatus(); $status['type'] = 'update'; $status['error'] = ''; @@ -239,46 +260,6 @@ private function updateExApp(InputInterface $input, OutputInterface $output, str $output->writeln(sprintf('ExApp %s update successfully deployed.', $appId)); } - // Default scopes approval process (compare new ExApp scopes) - $currentExAppScopes = array_map(function (ExAppScope $exAppScope) { - return $exAppScope->getScopeGroup(); - }, $this->exAppScopeService->getExAppScopes($exApp)); - // Prepare for prompt of newly requested ExApp scopes - $requiredScopes = array_values(array_diff($this->exAppApiScopeService->mapScopeGroupsToNumbers($appInfo['external-app']['scopes']), $currentExAppScopes)); - - $forceScopes = (bool) $input->getOption('force-scopes'); - $confirmScopes = $forceScopes; - - if (!$forceScopes && $input->isInteractive()) { - /** @var QuestionHelper $helper */ - $helper = $this->getHelper('question'); - - if (count($requiredScopes) > 0) { - $output->writeln(sprintf('ExApp %s requested scopes: %s', $appId, implode(', ', - $this->exAppApiScopeService->mapScopeGroupsToNames($requiredScopes)))); - $question = new ConfirmationQuestion('Do you want to approve it? [y/N] ', false); - $confirmScopes = $helper->ask($input, $output, $question); - } else { - $confirmScopes = true; - } - } - - if (!$confirmScopes && count($requiredScopes) > 0) { - $output->writeln(sprintf('ExApp %s required scopes not approved. Failed to finish ExApp update.', $appId)); - return 1; - } - - if (!$this->exAppScopeService->registerExAppScopes( - $exApp, $this->exAppApiScopeService->mapScopeGroupsToNumbers($appInfo['external-app']['scopes'])) - ) { - $this->logger->error(sprintf('Failed to update ExApp %s scopes.', $appId)); - if ($outputConsole) { - $output->writeln(sprintf('Failed to update ExApp %s scopes.', $appId)); - } - $this->exAppService->setStatusError($exApp, 'Failed to update scopes'); - return 1; - } - $this->service->dispatchExAppInitInternal($exApp); if ($input->getOption('wait-finish')) { $error = $this->exAppService->waitInitStepFinish($appId); diff --git a/lib/Controller/ExAppsPageController.php b/lib/Controller/ExAppsPageController.php index 64c7022b..4d45e09a 100644 --- a/lib/Controller/ExAppsPageController.php +++ b/lib/Controller/ExAppsPageController.php @@ -12,13 +12,11 @@ use OC\App\Platform; use OC_App; use OCA\AppAPI\AppInfo\Application; -use OCA\AppAPI\Db\ExAppScope; use OCA\AppAPI\DeployActions\DockerActions; use OCA\AppAPI\Fetcher\ExAppFetcher; use OCA\AppAPI\Service\AppAPIService; use OCA\AppAPI\Service\DaemonConfigService; use OCA\AppAPI\Service\ExAppApiScopeService; -use OCA\AppAPI\Service\ExAppScopesService; use OCA\AppAPI\Service\ExAppService; use OCA\AppAPI\Service\ExAppUsersService; use OCP\App\IAppManager; @@ -45,7 +43,6 @@ class ExAppsPageController extends Controller { private IConfig $config; private AppAPIService $service; private DaemonConfigService $daemonConfigService; - private ExAppScopesService $exAppScopeService; private DockerActions $dockerActions; private CategoryFetcher $categoryFetcher; private IFactory $l10nFactory; @@ -62,7 +59,6 @@ public function __construct( IInitialState $initialStateService, AppAPIService $service, DaemonConfigService $daemonConfigService, - ExAppScopesService $exAppScopeService, ExAppApiScopeService $exAppApiScopeService, ExAppUsersService $exAppUsersService, DockerActions $dockerActions, @@ -80,7 +76,6 @@ public function __construct( $this->config = $config; $this->service = $service; $this->daemonConfigService = $daemonConfigService; - $this->exAppScopeService = $exAppScopeService; $this->exAppApiScopeService = $exAppApiScopeService; $this->dockerActions = $dockerActions; $this->categoryFetcher = $categoryFetcher; @@ -205,9 +200,7 @@ private function getAppsForCategory(string $requestedCategory = ''): array { $daemon = null; if ($exApp !== null) { - $scopes = $this->exAppApiScopeService->mapScopeGroupsToNames(array_map(function (ExAppScope $exAppScope) { - return $exAppScope->getScopeGroup(); - }, $this->exAppScopeService->getExAppScopes($exApp))); + $scopes = $this->exAppApiScopeService->mapScopeGroupsToNames($exApp->getApiScopes()); $daemon = $this->daemonConfigService->getDaemonConfigByName($exApp->getDaemonConfigName()); } @@ -342,9 +335,7 @@ private function buildLocalAppsList(array $apps, array $exApps): array { if (!in_array($app['id'], $registeredAppsIds)) { $exApp = $this->exAppService->getExApp($app['id']); $daemon = $this->daemonConfigService->getDaemonConfigByName($exApp->getDaemonConfigName()); - $scopes = $this->exAppApiScopeService->mapScopeGroupsToNames(array_map(function (ExAppScope $exAppScope) { - return $exAppScope->getScopeGroup(); - }, $this->exAppScopeService->getExAppScopes($exApp))); + $scopes = $this->exAppApiScopeService->mapScopeGroupsToNames($exApp->getApiScopes()); $formattedLocalApps[] = [ 'id' => $app['id'], diff --git a/lib/Db/ExAppScope.php b/lib/Db/ExAppScope.php deleted file mode 100644 index d6635a08..00000000 --- a/lib/Db/ExAppScope.php +++ /dev/null @@ -1,49 +0,0 @@ -addType('appid', 'string'); - $this->addType('scopeGroup', 'int'); - - if (isset($params['id'])) { - $this->setId($params['id']); - } - if (isset($params['appid'])) { - $this->setAppid($params['appid']); - } - if (isset($params['scope_group'])) { - $this->setScopeGroup($params['scope_group']); - } - } - - public function jsonSerialize(): array { - return [ - 'id' => $this->getId(), - 'appid' => $this->getAppid(), - 'scope_group' => $this->getScopeGroup(), - ]; - } -} diff --git a/lib/Db/ExAppScopeMapper.php b/lib/Db/ExAppScopeMapper.php deleted file mode 100644 index 0780aefa..00000000 --- a/lib/Db/ExAppScopeMapper.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ -class ExAppScopeMapper extends QBMapper { - public function __construct(IDBConnection $db) { - parent::__construct($db, 'ex_apps_scopes'); - } - - /** - * @param string $appId - * - * @throws Exception - * @return ExAppScope[] - */ - public function findByAppid(string $appId): array { - $qb = $this->db->getQueryBuilder(); - return $this->findEntities($qb->select('*') - ->from($this->tableName) - ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) - ); - } - - /** - * @param string $appId - * @param int $scopeGroup - * - * @throws DoesNotExistException if not found - * @throws Exception - * @throws MultipleObjectsReturnedException if more than one result - * @return ExAppScope|null - */ - public function findByAppidScope(string $appId, int $scopeGroup): ?ExAppScope { - $qb = $this->db->getQueryBuilder(); - return $this->findEntity($qb->select('*') - ->from($this->tableName) - ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) - ->andWhere($qb->expr()->eq('scope_group', $qb->createNamedParameter($scopeGroup), IQueryBuilder::PARAM_INT)) - ); - } - - /** - * @param string $appId - * - * @throws Exception - * @return int - */ - public function deleteByAppid(string $appId): int { - $qb = $this->db->getQueryBuilder(); - return $qb->delete($this->tableName) - ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) - ->executeStatement(); - } -} diff --git a/lib/Migration/Version2207Date20240502145029.php b/lib/Migration/Version2207Date20240502145029.php new file mode 100644 index 00000000..a829cd52 --- /dev/null +++ b/lib/Migration/Version2207Date20240502145029.php @@ -0,0 +1,30 @@ +hasTable('ex_apps_scopes')) { + $schema->dropTable('ex_apps_scopes'); + } + + return $schema; + } +} diff --git a/lib/Service/AppAPIService.php b/lib/Service/AppAPIService.php index 206b6d92..01f7feaf 100644 --- a/lib/Service/AppAPIService.php +++ b/lib/Service/AppAPIService.php @@ -38,16 +38,15 @@ public function __construct( private readonly IUserSession $userSession, private readonly ISession $session, private readonly IUserManager $userManager, - private readonly IFactory $l10nFactory, + private readonly IFactory $l10nFactory, private readonly ExNotificationsManager $exNotificationsManager, - private readonly ExAppService $exAppService, + private readonly ExAppService $exAppService, private readonly ExAppUsersService $exAppUsersService, private readonly ExAppApiScopeService $exAppApiScopeService, - private readonly ExAppScopesService $exAppScopesService, private readonly ExAppConfigService $exAppConfigService, - private readonly DockerActions $dockerActions, - private readonly ManualActions $manualActions, - private readonly AppAPICommonService $commonService, + private readonly DockerActions $dockerActions, + private readonly ManualActions $manualActions, + private readonly AppAPICommonService $commonService, ) { $this->client = $clientService->newClient(); } @@ -275,7 +274,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) $path = '/dav/'; } - $allScopesFlag = (bool)$this->exAppScopesService->getByScope($exApp, ExAppApiScopeService::ALL_API_SCOPE); + $allScopesFlag = (bool)$this->getByScope($exApp, ExAppApiScopeService::ALL_API_SCOPE); $apiScope = $this->exAppApiScopeService->getApiScopeByRoute($path); if (!$allScopesFlag) { @@ -286,7 +285,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) // BASIC ApiScope is granted to all ExApps (all API routes with BASIC scope group). if ($apiScope['scope_group'] !== ExAppApiScopeService::BASIC_API_SCOPE) { - if (!$this->exAppScopesService->passesScopeCheck($exApp, $apiScope['scope_group'])) { + if (!$this->passesScopeCheck($exApp, $apiScope['scope_group'])) { $this->logger->error(sprintf('ExApp %s not passed scope group check %s', $exApp->getAppid(), $path)); return false; } @@ -347,6 +346,22 @@ private function finalizeRequestToNC(string $userId, IRequest $request, int $isS return true; } + public function getByScope(ExApp $exApp, int $apiScope): ?int { + foreach ($exApp->getApiScopes() as $scope) { + if ($scope === $apiScope) { + return $scope; + } + } + return null; + } + + public function passesScopeCheck(ExApp $exApp, int $apiScope): bool { + if (in_array($apiScope, $exApp->getApiScopes(), true)) { + return true; + } + return false; + } + private function buildRequestInfo(IRequest $request): array { $headers = []; $aeHeadersList = [ diff --git a/lib/Service/ExAppScopesService.php b/lib/Service/ExAppScopesService.php deleted file mode 100644 index 55133579..00000000 --- a/lib/Service/ExAppScopesService.php +++ /dev/null @@ -1,137 +0,0 @@ -cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_apps_scopes'); - } - - public function getExAppScopes(ExApp $exApp): array { - try { - $cacheKey = '/ex_app_scopes_' . $exApp->getAppid(); - $cached = $this->cache->get($cacheKey); - if ($cached !== null) { - return array_map(function ($cachedEntry) { - return $cachedEntry instanceof ExAppScope ? $cachedEntry : new ExAppScope($cachedEntry); - }, $cached); - } - - $exAppScopes = $this->mapper->findByAppid($exApp->getAppid()); - $this->cache->set($cacheKey, $exAppScopes); - return $exAppScopes; - } catch (Exception $e) { - $this->logger->error(sprintf('Failed to get all api scopes. Error: %s', $e->getMessage()), ['exception' => $e]); - return []; - } - } - - public function setExAppScopeGroup(ExApp $exApp, int $scopeGroup): ?ExAppScope { - $exAppScope = $this->getByScope($exApp, $scopeGroup); - if ($exAppScope instanceof ExAppScope) { - return $exAppScope; - } - - $exAppScope = new ExAppScope([ - 'appid' => $exApp->getAppid(), - 'scope_group' => $scopeGroup, - ]); - try { - return $this->mapper->insert($exAppScope); - } catch (Exception $e) { - $this->logger->error(sprintf('Error while setting ExApp scope group: %s', $e->getMessage()), ['exception' => $e]); - return null; - } - } - - public function getByScope(ExApp $exApp, int $apiScope): ?ExAppScope { - try { - $cacheKey = '/ex_app_scopes_' . $exApp->getAppid() . '_' . $apiScope; - $cached = $this->cache->get($cacheKey); - if ($cached !== null) { - return $cached instanceof ExAppScope ? $cached : new ExAppScope($cached); - } - - $exAppScope = $this->mapper->findByAppidScope($exApp->getAppid(), $apiScope); - $this->cache->set($cacheKey, $exAppScope); - return $exAppScope; - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - return null; - } - } - - public function passesScopeCheck(ExApp $exApp, int $apiScope): bool { - $exAppScope = $this->getByScope($exApp, $apiScope); - return $exAppScope instanceof ExAppScope; - } - - public function removeExAppScopes(string $appId): bool { - try { - $result = $this->mapper->deleteByAppid($appId) > 0; - if ($result) { - $this->cache->clear('/ex_app_scopes_' . $appId); - } - return $result; - } catch (Exception $e) { - $this->logger->error(sprintf('Failed to delete all ExApp %s scopes. Error: %s', $appId, $e->getMessage()), ['exception' => $e]); - return false; - } - } - - public function removeExAppScope(ExApp $exApp, int $apiScope): bool { - $exAppScope = $this->getByScope($exApp, $apiScope); - if ($exAppScope === null) { - return false; - } - - try { - return $this->mapper->delete($exAppScope) instanceof ExAppScope; - } catch (Exception $e) { - $this->logger->error(sprintf('Failed to delete ExApp %s scope %s', $exApp->getAppid(), $apiScope), ['exception' => $e]); - return false; - } - } - - public function registerExAppScopes(ExApp $exApp, array $newExAppScopes): bool { - $currentExAppScopes = array_map(function (ExAppScope $exAppScope) { - return $exAppScope->getScopeGroup(); - }, $this->getExAppScopes($exApp)); - $newScopes = array_values(array_diff($newExAppScopes, $currentExAppScopes)); - $removedScopes = array_values(array_diff($currentExAppScopes, $newExAppScopes)); - - foreach ($newScopes as $newScope) { - if ($this->setExAppScopeGroup($exApp, $newScope) === null) { - return false; - } - } - - foreach ($removedScopes as $removedScope) { - if (!$this->removeExAppScope($exApp, $removedScope)) { - return false; - } - } - - $this->cache->clear('/ex_app_scopes_' . $exApp->getAppid()); - return true; - } -} diff --git a/lib/Service/ExAppService.php b/lib/Service/ExAppService.php index 739a8895..6616c91d 100644 --- a/lib/Service/ExAppService.php +++ b/lib/Service/ExAppService.php @@ -7,7 +7,6 @@ use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Db\ExApp; use OCA\AppAPI\Db\ExAppMapper; -use OCA\AppAPI\Db\ExAppScope; use OCA\AppAPI\Fetcher\ExAppArchiveFetcher; use OCA\AppAPI\Fetcher\ExAppFetcher; use OCA\AppAPI\Service\ProvidersAI\SpeechToTextService; @@ -33,25 +32,24 @@ class ExAppService { private ICache $cache; public function __construct( - private readonly LoggerInterface $logger, - ICacheFactory $cacheFactory, - private readonly IUserManager $userManager, - private readonly ExAppFetcher $exAppFetcher, - private readonly ExAppArchiveFetcher $exAppArchiveFetcher, - private readonly ExAppMapper $exAppMapper, - private readonly ExAppUsersService $exAppUsersService, - private readonly ExAppScopesService $exAppScopesService, - private readonly ExAppApiScopeService $exAppApiScopeService, - private readonly TopMenuService $topMenuService, - private readonly InitialStateService $initialStateService, - private readonly ScriptsService $scriptsService, - private readonly StylesService $stylesService, - private readonly FilesActionsMenuService $filesActionsMenuService, - private readonly SpeechToTextService $speechToTextService, - private readonly TextProcessingService $textProcessingService, - private readonly TranslationService $translationService, - private readonly TalkBotsService $talkBotsService, - private readonly SettingsService $settingsService, + private readonly LoggerInterface $logger, + ICacheFactory $cacheFactory, + private readonly IUserManager $userManager, + private readonly ExAppFetcher $exAppFetcher, + private readonly ExAppArchiveFetcher $exAppArchiveFetcher, + private readonly ExAppMapper $exAppMapper, + private readonly ExAppUsersService $exAppUsersService, + private readonly ExAppApiScopeService $exAppApiScopeService, + private readonly TopMenuService $topMenuService, + private readonly InitialStateService $initialStateService, + private readonly ScriptsService $scriptsService, + private readonly StylesService $stylesService, + private readonly FilesActionsMenuService $filesActionsMenuService, + private readonly SpeechToTextService $speechToTextService, + private readonly TextProcessingService $textProcessingService, + private readonly TranslationService $translationService, + private readonly TalkBotsService $talkBotsService, + private readonly SettingsService $settingsService, private readonly ExAppEventsListenerService $eventsListenerService, private readonly ExAppOccService $occService, ) { @@ -98,7 +96,6 @@ public function unregisterExApp(string $appId): bool { if ($exApp === null) { return false; } - $this->exAppScopesService->removeExAppScopes($appId); $this->exAppUsersService->removeExAppUsers($appId); $this->talkBotsService->unregisterExAppTalkBots($exApp); // TODO: Think about internal Events for clean and flexible unregister ExApp callbacks $this->filesActionsMenuService->unregisterExAppFileActions($appId); @@ -184,9 +181,7 @@ public function formatExAppInfo(ExApp $exApp): array { 'last_check_time' => $exApp->getLastCheckTime(), 'system' => $exApp->getIsSystem(), 'status' => $exApp->getStatus(), - 'scopes' => $this->exAppApiScopeService->mapScopeGroupsToNames(array_map(function (ExAppScope $exAppScope) { - return $exAppScope->getScopeGroup(); - }, $this->exAppScopesService->getExAppScopes($exApp))), + 'scopes' => $this->exAppApiScopeService->mapScopeGroupsToNames($exApp->getApiScopes()), ]; }