aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Piskun <13381981+bigcat88@users.noreply.github.com>2024-07-12 23:29:55 +0300
committerAlexander Piskun <bigcat88@icloud.com>2024-07-16 19:36:43 +0300
commit696ece2f52f5c78987c48fa569b07ec0d4ac0660 (patch)
treed90479c8bfbe10a1fd8bc77b325340dc33b3fbf1
parenta2ded2005086df9fc8c05beb68452c2cee571203 (diff)
downloadnextcloud-server-696ece2f52f5c78987c48fa569b07ec0d4ac0660.tar.gz
nextcloud-server-696ece2f52f5c78987c48fa569b07ec0d4ac0660.zip
feat: webhooks_listeners app support for sending direct requests to ExApps using AppAPI.
Signed-off-by: Alexander Piskun <bigcat88@icloud.com>
-rw-r--r--apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php35
-rw-r--r--apps/webhook_listeners/lib/Db/WebhookListener.php5
-rw-r--r--build/stubs/app_api.php101
3 files changed, 126 insertions, 15 deletions
diff --git a/apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php b/apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php
index 9c9a4bb6dbe..f7c111f41e8 100644
--- a/apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php
+++ b/apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php
@@ -11,11 +11,15 @@ namespace OCA\WebhookListeners\BackgroundJobs;
use OCA\WebhookListeners\Db\AuthMethod;
use OCA\WebhookListeners\Db\WebhookListenerMapper;
+use OCP\App\IAppManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\QueuedJob;
use OCP\Http\Client\IClientService;
use OCP\ICertificateManager;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
+use RuntimeException;
class WebhookCall extends QueuedJob {
public function __construct(
@@ -23,6 +27,7 @@ class WebhookCall extends QueuedJob {
private ICertificateManager $certificateManager,
private WebhookListenerMapper $mapper,
private LoggerInterface $logger,
+ private IAppManager $appManager,
ITimeFactory $timeFactory,
) {
parent::__construct($timeFactory);
@@ -49,15 +54,39 @@ class WebhookCall extends QueuedJob {
$options['headers'] = array_merge($options['headers'], $authHeaders);
break;
}
- $response = $client->request($webhookListener->getHttpMethod(), $webhookListener->getUri(), $options);
+ $webhookUri = $webhookListener->getUri();
+ $exAppId = $webhookListener->getAppId();
+ if ($exAppId !== null && str_starts_with($webhookUri, "/")) {
+ // ExApp is awaiting a direct request to itself using AppAPI
+ if (!$this->appManager->isInstalled('app_api')) {
+ throw new RuntimeException('AppAPI is disabled or not installed.');
+ }
+ try {
+ $appApiFunctions = \OCP\Server::get(\OCA\AppAPI\PublicFunctions::class);
+ } catch (ContainerExceptionInterface | NotFoundExceptionInterface) {
+ throw new RuntimeException('Could not get AppAPI public functions.');
+ }
+ $exApp = $appApiFunctions->getExApp($exAppId);
+ if ($exApp === null) {
+ throw new RuntimeException('ExApp ' . $exAppId . ' is missing.');
+ } elseif (!$exApp['enabled']) {
+ throw new RuntimeException('ExApp ' . $exAppId . ' is disabled.');
+ }
+ $response = $appApiFunctions->exAppRequest($exAppId, $webhookUri, $webhookListener->getUserId(), $webhookListener->getHttpMethod(), [], $options);
+ if (is_array($response) && isset($response['error'])) {
+ throw new RuntimeException(sprintf('Error during request to ExApp(%s): %s', $exAppId, $response['error']));
+ }
+ } else {
+ $response = $client->request($webhookListener->getHttpMethod(), $webhookUri, $options);
+ }
$statusCode = $response->getStatusCode();
if ($statusCode >= 200 && $statusCode < 300) {
$this->logger->debug('Webhook returned status code '.$statusCode, ['body' => $response->getBody()]);
} else {
- $this->logger->warning('Webhook returned unexpected status code '.$statusCode, ['body' => $response->getBody()]);
+ $this->logger->warning('Webhook(' . $webhookId . ') returned unexpected status code '.$statusCode, ['body' => $response->getBody()]);
}
} catch (\Exception $e) {
- $this->logger->error('Webhook call failed: '.$e->getMessage(), ['exception' => $e]);
+ $this->logger->error('Webhook(' . $webhookId . ') call failed: '.$e->getMessage(), ['exception' => $e]);
}
}
}
diff --git a/apps/webhook_listeners/lib/Db/WebhookListener.php b/apps/webhook_listeners/lib/Db/WebhookListener.php
index a59549d0c4a..8053fc318dc 100644
--- a/apps/webhook_listeners/lib/Db/WebhookListener.php
+++ b/apps/webhook_listeners/lib/Db/WebhookListener.php
@@ -14,6 +14,7 @@ use OCP\Security\ICrypto;
/**
* @method void setUserId(string $userId)
+ * @method ?string getAppId()
* @method string getUserId()
* @method string getHttpMethod()
* @method string getUri()
@@ -139,4 +140,8 @@ class WebhookListener extends Entity implements \JsonSerializable {
)
);
}
+
+ public function getAppId(): ?string {
+ return $this->appId;
+ }
}
diff --git a/build/stubs/app_api.php b/build/stubs/app_api.php
index 1ab63499b77..04a6042dffd 100644
--- a/build/stubs/app_api.php
+++ b/build/stubs/app_api.php
@@ -1,15 +1,92 @@
<?php
-namespace OCA\AppAPI\Service;
-
-use OCP\IRequest;
-
-class AppAPIService {
- /**
- * @param IRequest $request
- * @param bool $isDav
- *
- * @return bool
- */
- public function validateExAppRequestToNC(IRequest $request, bool $isDav = false): bool {}
+namespace OCA\AppAPI\Service {
+ use OCP\IRequest;
+
+ class AppAPIService {
+ /**
+ * @param IRequest $request
+ * @param bool $isDav
+ *
+ * @return bool
+ */
+ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false): bool {}
+ }
+}
+
+namespace OCA\AppAPI {
+
+ use OCP\IRequest;
+ use OCP\Http\Client\IPromise;
+ use OCP\Http\Client\IResponse;
+
+ class PublicFunctions {
+
+ public function __construct(
+ private readonly ExAppService $exAppService,
+ private readonly AppAPIService $service,
+ ) {
+ }
+
+ /**
+ * Request to ExApp with AppAPI auth headers
+ */
+ public function exAppRequest(
+ string $appId,
+ string $route,
+ ?string $userId = null,
+ string $method = 'POST',
+ array $params = [],
+ array $options = [],
+ ?IRequest $request = null,
+ ): array|IResponse {
+ $exApp = $this->exAppService->getExApp($appId);
+ if ($exApp === null) {
+ return ['error' => sprintf('ExApp `%s` not found', $appId)];
+ }
+ return $this->service->requestToExApp($exApp, $route, $userId, $method, $params, $options, $request);
+ }
+
+ /**
+ * Async request to ExApp with AppAPI auth headers
+ *
+ * @throws \Exception if ExApp not found
+ */
+ public function asyncExAppRequest(
+ string $appId,
+ string $route,
+ ?string $userId = null,
+ string $method = 'POST',
+ array $params = [],
+ array $options = [],
+ ?IRequest $request = null,
+ ): IPromise {
+ $exApp = $this->exAppService->getExApp($appId);
+ if ($exApp === null) {
+ throw new \Exception(sprintf('ExApp `%s` not found', $appId));
+ }
+ return $this->service->requestToExAppAsync($exApp, $route, $userId, $method, $params, $options, $request);
+ }
+
+ /**
+ * Get basic ExApp info by appid
+ *
+ * @param string $appId
+ *
+ * @return array|null ExApp info (appid, version, name, enabled) or null if no ExApp found
+ */
+ public function getExApp(string $appId): ?array {
+ $exApp = $this->exAppService->getExApp($appId);
+ if ($exApp !== null) {
+ $info = $exApp->jsonSerialize();
+ return [
+ 'appid' => $info['appid'],
+ 'version' => $info['version'],
+ 'name' => $info['name'],
+ 'enabled' => $info['enabled'],
+ ];
+ }
+ return null;
+ }
+ }
}