]> source.dussan.org Git - nextcloud-server.git/commitdiff
fix: Make webhook event serialization opt-in with a new interface
authorCôme Chilliet <come.chilliet@nextcloud.com>
Thu, 6 Jun 2024 14:12:36 +0000 (16:12 +0200)
committerCôme Chilliet <91878298+come-nc@users.noreply.github.com>
Tue, 11 Jun 2024 12:10:29 +0000 (14:10 +0200)
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
apps/webhooks/lib/Db/WebhookListenerMapper.php
apps/webhooks/lib/Listener/WebhooksEventListener.php
lib/composer/composer/autoload_classmap.php
lib/composer/composer/autoload_static.php
lib/public/EventDispatcher/IWebhookCompatibleEvent.php [new file with mode: 0644]
lib/public/Files/Events/Node/AbstractNodeEvent.php
lib/public/Files/Events/Node/AbstractNodesEvent.php

index a4a43a882f5929d0c8e44badcd12e58bbe8435f6..85c167b0c922551d49bb0579bdd8b9c89dbcf4bc 100644 (file)
@@ -14,6 +14,7 @@ use OCP\AppFramework\Db\MultipleObjectsReturnedException;
 use OCP\AppFramework\Db\QBMapper;
 use OCP\DB\Exception;
 use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\EventDispatcher\IWebhookCompatibleEvent;
 use OCP\IDBConnection;
 
 /**
@@ -68,6 +69,9 @@ class WebhookListenerMapper extends QBMapper {
                AuthMethod $authMethod,
                ?array $authData,
        ): WebhookListener {
+               if (!class_exists($event) || !is_a($event, IWebhookCompatibleEvent::class, true)) {
+                       throw new \UnexpectedValueException("$event is not an event class compatible with webhooks");
+               }
                $webhookListener = WebhookListener::fromParams(
                        [
                                'appId' => $appId,
@@ -99,6 +103,9 @@ class WebhookListenerMapper extends QBMapper {
                AuthMethod $authMethod,
                ?array $authData,
        ): WebhookListener {
+               if (!class_exists($event) || !is_a($event, IWebhookCompatibleEvent::class, true)) {
+                       throw new \UnexpectedValueException("$event is not an event class compatible with webhooks");
+               }
                $webhookListener = WebhookListener::fromParams(
                        [
                                'id' => $id,
index afe53595e75dc9a94a975802bbfceb5efd2cf2f2..37d6863553a4885038cb6268bea7c4bd34996950 100644 (file)
@@ -15,13 +15,14 @@ use OCA\Webhooks\Service\PHPMongoQuery;
 use OCP\BackgroundJob\IJobList;
 use OCP\EventDispatcher\Event;
 use OCP\EventDispatcher\IEventListener;
+use OCP\EventDispatcher\IWebhookCompatibleEvent;
 use OCP\EventDispatcher\JsonSerializer;
 use OCP\IUserSession;
 use Psr\Log\LoggerInterface;
 
 /**
  * The class to handle the share events
- * @template-implements IEventListener<Event>
+ * @template-implements IEventListener<IWebhookCompatibleEvent>
  */
 class WebhooksEventListener implements IEventListener {
        public function __construct(
@@ -55,29 +56,10 @@ class WebhooksEventListener implements IEventListener {
                }
        }
 
-       private function serializeEvent(Event $event): array|\JsonSerializable {
-               if ($event instanceof \JsonSerializable) {
-                       return $event;
-               } else {
-                       /* Event is not serializable, we fallback to reflection to still send something */
-                       $data = ['class' => $event::class];
-                       $ref = new \ReflectionClass($event);
-                       foreach ($ref->getMethods() as $method) {
-                               if (str_starts_with($method->getName(), 'get')) {
-                                       $key = strtolower(substr($method->getName(), 3));
-                                       $value = $method->invoke($event);
-                                       if ($value instanceof \OCP\Files\FileInfo) {
-                                               $value = [
-                                                       'id' => $value->getId(),
-                                                       'path' => $value->getPath(),
-                                               ];
-                                       }
-                                       $data[$key] = $value;
-                               }
-                       }
-                       $this->logger->debug('Webhook had to use fallback to serialize event '.$event::class);
-                       return $data;
-               }
+       private function serializeEvent(IWebhookCompatibleEvent $event): array {
+               $data = $event->getWebhookSerializable();
+               $data['class'] = $event::class;
+               return $data;
        }
 
        private function filterMatch(array $filter, array $data): bool {
index 0fe1314644fe61f6d1f31e3d9bed908b6bb6bce1..dbd9ebc66ab5870ecd352d82b9083110a3fe7d40 100644 (file)
@@ -281,6 +281,7 @@ return array(
     'OCP\\EventDispatcher\\GenericEvent' => $baseDir . '/lib/public/EventDispatcher/GenericEvent.php',
     'OCP\\EventDispatcher\\IEventDispatcher' => $baseDir . '/lib/public/EventDispatcher/IEventDispatcher.php',
     'OCP\\EventDispatcher\\IEventListener' => $baseDir . '/lib/public/EventDispatcher/IEventListener.php',
+    'OCP\\EventDispatcher\\IWebhookCompatibleEvent' => $baseDir . '/lib/public/EventDispatcher/IWebhookCompatibleEvent.php',
     'OCP\\EventDispatcher\\JsonSerializer' => $baseDir . '/lib/public/EventDispatcher/JsonSerializer.php',
     'OCP\\Exceptions\\AbortedEventException' => $baseDir . '/lib/public/Exceptions/AbortedEventException.php',
     'OCP\\Exceptions\\AppConfigException' => $baseDir . '/lib/public/Exceptions/AppConfigException.php',
index cf52b9026f11a4e156ad1f84e23882592221bacb..9f3b289cdfc5a3040693bcba448c3261c3cc56f2 100644 (file)
@@ -322,6 +322,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
         'OCP\\EventDispatcher\\GenericEvent' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/GenericEvent.php',
         'OCP\\EventDispatcher\\IEventDispatcher' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IEventDispatcher.php',
         'OCP\\EventDispatcher\\IEventListener' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IEventListener.php',
+        'OCP\\EventDispatcher\\IWebhookCompatibleEvent' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IWebhookCompatibleEvent.php',
         'OCP\\EventDispatcher\\JsonSerializer' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/JsonSerializer.php',
         'OCP\\Exceptions\\AbortedEventException' => __DIR__ . '/../../..' . '/lib/public/Exceptions/AbortedEventException.php',
         'OCP\\Exceptions\\AppConfigException' => __DIR__ . '/../../..' . '/lib/public/Exceptions/AppConfigException.php',
diff --git a/lib/public/EventDispatcher/IWebhookCompatibleEvent.php b/lib/public/EventDispatcher/IWebhookCompatibleEvent.php
new file mode 100644 (file)
index 0000000..b13c35c
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\EventDispatcher;
+
+/**
+ * Interface for events which can be listened to by webhooks
+ *
+ * @since 30.0.0
+ */
+interface IWebhookCompatibleEvent {
+       /**
+        * Return data to be serialized and sent to the webhook. Will be serialized using json_encode.
+        *
+        * @since 30.0.0
+        */
+       public function getWebhookSerializable(): array;
+}
index cc27bdd8cfdb31eb017605d7177523d6bb86b403..64b0e3a3aa511ef051f0359014b374c89dab3020 100644 (file)
@@ -9,13 +9,14 @@ declare(strict_types=1);
 namespace OCP\Files\Events\Node;
 
 use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IWebhookCompatibleEvent;
 use OCP\EventDispatcher\JsonSerializer;
 use OCP\Files\Node;
 
 /**
  * @since 20.0.0
  */
-abstract class AbstractNodeEvent extends Event implements \JsonSerializable {
+abstract class AbstractNodeEvent extends Event implements IWebhookCompatibleEvent {
        /**
         * @since 20.0.0
         */
@@ -34,9 +35,8 @@ abstract class AbstractNodeEvent extends Event implements \JsonSerializable {
        /**
         * @since 30.0.0
         */
-       public function jsonSerialize(): array {
+       public function getWebhookSerializable(): array {
                return [
-                       'class' => static::class,
                        'node' => JsonSerializer::serializeFileInfo($this->node),
                ];
        }
index b5fd1b75898f8467c42f70c6331465c9d96c3cdf..7941a9e596a80531db27cb4166a208618687c018 100644 (file)
@@ -9,13 +9,14 @@ declare(strict_types=1);
 namespace OCP\Files\Events\Node;
 
 use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IWebhookCompatibleEvent;
 use OCP\EventDispatcher\JsonSerializer;
 use OCP\Files\Node;
 
 /**
  * @since 20.0.0
  */
-abstract class AbstractNodesEvent extends Event {
+abstract class AbstractNodesEvent extends Event implements IWebhookCompatibleEvent {
        /**
         * @since 20.0.0
         */
@@ -42,9 +43,8 @@ abstract class AbstractNodesEvent extends Event {
        /**
         * @since 30.0.0
         */
-       public function jsonSerialize(): array {
+       public function getWebhookSerializable(): array {
                return [
-                       'class' => static::class,
                        'source' => JsonSerializer::serializeFileInfo($this->source),
                        'target' => JsonSerializer::serializeFileInfo($this->target),
                ];