%PDF- %PDF-
Direktori : /www/varak.net/nextcloud.varak.net/apps/app_api/lib/Service/ |
Current File : //www/varak.net/nextcloud.varak.net/apps/app_api/lib/Service/ExAppOccService.php |
<?php declare(strict_types=1); namespace OCA\AppAPI\Service; use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Db\Console\ExAppOccCommand; use OCA\AppAPI\Db\Console\ExAppOccCommandMapper; use OCA\AppAPI\PublicFunctions; use OCP\AppFramework\Http; use OCP\DB\Exception; use OCP\ICache; use OCP\ICacheFactory; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; 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; class ExAppOccService { private ?ICache $cache = null; public function __construct( private readonly LoggerInterface $logger, private readonly ExAppOccCommandMapper $mapper, ICacheFactory $cacheFactory, ) { if ($cacheFactory->isAvailable()) { $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_occ_commands'); } } public function registerCommand( string $appId, string $name, string $description, int $hidden, array $arguments, array $options, array $usages, string $executeHandler ): ?ExAppOccCommand { $occCommandEntry = $this->getOccCommand($appId, $name); try { $newOccCommandEntry = new ExAppOccCommand([ 'appid' => $appId, 'name' => $name, 'description' => $description, 'hidden' => $hidden, 'arguments' => $arguments, 'options' => $options, 'usages' => $usages, 'execute_handler' => ltrim($executeHandler, '/'), ]); if ($occCommandEntry !== null) { $newOccCommandEntry->setId($occCommandEntry->getId()); } $occCommandEntry = $this->mapper->insertOrUpdate($newOccCommandEntry); $this->resetCacheEnabled(); } catch (Exception $e) { $this->logger->error( sprintf('Failed to register ExApp OCC command for %s. Error: %s', $appId, $e->getMessage()), ['exception' => $e] ); return null; } return $occCommandEntry; } public function unregisterCommand(string $appId, string $name): bool { if (!$this->mapper->removeByAppIdOccName($appId, $name)) { return false; } $this->resetCacheEnabled(); return true; } public function getOccCommand(string $appId, string $name): ?ExAppOccCommand { foreach ($this->getOccCommands() as $occCommand) { if (($occCommand->getAppid() === $appId) && ($occCommand->getName() === $name)) { return $occCommand; } } return null; } /** * Get list of registered ExApp OCC Commands (only for enabled ExApps) * * @return ExAppOccCommand[] */ public function getOccCommands(): array { try { $cacheKey = '/ex_occ_commands'; $records = $this->cache?->get($cacheKey); if ($records === null) { $records = $this->mapper->findAllEnabled(); $this->cache?->set($cacheKey, $records); } return array_map(function ($record) { return new ExAppOccCommand($record); }, $records); } catch (Exception) { return []; } } public function buildCommand(ExAppOccCommand $occCommand, ContainerInterface $container): Command { return new class($occCommand, $container) extends Command { private LoggerInterface $logger; private PublicFunctions $service; public function __construct( private ExAppOccCommand $occCommand, private ContainerInterface $container ) { parent::__construct(); $this->logger = $this->container->get(LoggerInterface::class); $this->service = $this->container->get(PublicFunctions::class); } protected function configure() { $this->setName($this->occCommand->getName()); $this->setDescription($this->occCommand->getDescription()); $this->setHidden(filter_var($this->occCommand->getHidden(), FILTER_VALIDATE_BOOLEAN)); foreach ($this->occCommand->getArguments() as $argument) { $this->addArgument( $argument['name'], $this->buildArgumentMode($argument['mode']), $argument['description'], in_array($argument['mode'], ['optional', 'array']) ? $argument['default'] : null, ); } foreach ($this->occCommand->getOptions() as $option) { $this->addOption( $option['name'], $option['shortcut'] ?? null, $this->buildOptionMode($option['mode']), $option['description'], $this->buildOptionMode($option['mode']) !== InputOption::VALUE_NONE ? $option['default'] ?? null : null ); } foreach ($this->occCommand->getUsages() as $usage) { $this->addUsage($usage); } } protected function execute(InputInterface $input, OutputInterface $output): int { if (count($this->occCommand->getArguments()) > 0) { $arguments = []; foreach ($this->occCommand->getArguments() as $argument) { $arguments[$argument['name']] = $input->getArgument($argument['name']); } } else { $arguments = null; } if (count($this->occCommand->getOptions()) > 0) { $options = []; foreach ($this->occCommand->getOptions() as $option) { $options[$option['name']] = $input->getOption($option['name']); } } else { $options = null; } $executeHandler = $this->occCommand->getExecuteHandler(); $response = $this->service->exAppRequest($this->occCommand->getAppid(), $executeHandler, params: [ 'occ' => [ 'arguments' => $arguments, 'options' => $options, ], ], options: [ 'stream' => true, 'timeout' => 0, ] ); if (is_array($response) && isset($response['error'])) { $output->writeln(sprintf('[%s] command executeHandler failed. Error: %s', $this->occCommand->getName(), $response['error'])); $this->logger->error(sprintf('[%s] command executeHandler failed. Error: %s', $this->occCommand->getName(), $response['error']), [ 'app' => $this->occCommand->getAppid(), ]); return 1; } if ($response->getStatusCode() !== Http::STATUS_OK) { $output->writeln(sprintf('[%s] command executeHandler failed', $this->occCommand->getName())); $this->logger->error(sprintf('[%s] command executeHandler failed', $this->occCommand->getName()), [ 'app' => $this->occCommand->getAppid(), ]); return 1; } $body = $response->getBody(); if (is_resource($body)) { while (!feof($body)) { $output->write(fread($body, 1024)); } } return 0; } private function buildArgumentMode(string $mode): int { $modes = explode(',', $mode); $argumentMode = 0; foreach ($modes as $mode) { $argumentMode |= $this->_buildArgumentMode($mode); } return $argumentMode; } private function _buildArgumentMode(string $mode): int { if ($mode === 'required') { return InputArgument::REQUIRED; } if ($mode === 'optional') { return InputArgument::OPTIONAL; } if ($mode === 'array') { return InputArgument::IS_ARRAY; } return InputArgument::OPTIONAL; } private function buildOptionMode(string $mode): int { if ($mode === 'required') { return InputOption::VALUE_REQUIRED; } if ($mode === 'optional') { return InputOption::VALUE_OPTIONAL; } if ($mode === 'none') { return InputOption::VALUE_NONE; } if ($mode === 'array') { return InputOption::VALUE_IS_ARRAY; } if ($mode === 'negatable') { return InputOption::VALUE_NEGATABLE; } return InputOption::VALUE_NONE; } }; } public function unregisterExAppOccCommands(string $appId): int { try { $result = $this->mapper->removeAllByAppId($appId); } catch (Exception) { $result = -1; } $this->resetCacheEnabled(); return $result; } public function resetCacheEnabled(): void { $this->cache?->remove('/ex_occ_commands'); } }