%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');
}
}