From a667067e73303755201cb67c076d9d9a8bfaa191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 9 Jul 2024 12:17:51 +0200 Subject: [PATCH 1/9] feat(api): Add support for rich objects to API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/Controller/APIController.php | 50 ++++++++++++++++++++++++++--- lib/Notifier/AdminNotifications.php | 16 +++++++-- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/lib/Controller/APIController.php b/lib/Controller/APIController.php index cfb746eb4..21b1bb071 100644 --- a/lib/Controller/APIController.php +++ b/lib/Controller/APIController.php @@ -18,6 +18,9 @@ use OCP\IUser; use OCP\IUserManager; use OCP\Notification\IManager; +use OCP\Notification\InvalidValueException; +use OCP\RichObjectStrings\IValidator; +use OCP\RichObjectStrings\InvalidObjectExeption; class APIController extends OCSController { public function __construct( @@ -26,6 +29,7 @@ public function __construct( protected ITimeFactory $timeFactory, protected IUserManager $userManager, protected IManager $notificationManager, + protected IValidator $richValidator, ) { parent::__construct($appName, $request); } @@ -36,6 +40,10 @@ public function __construct( * @param string $userId ID of the user * @param string $shortMessage Subject of the notification * @param string $longMessage Message of the notification + * @param string $richSubject Subject of the notification with placeholders + * @param string $richSubjectParameters Rich objects to fill the subject placeholders, {@see \OCP\RichObjectStrings\Definitions} + * @param string $richMessage Message of the notification with placeholders + * @param string $richMessageParameters Rich objects to fill the message placeholders, {@see \OCP\RichObjectStrings\Definitions} * @return DataResponse, array{}>|DataResponse * * 200: Notification generated successfully @@ -43,14 +51,22 @@ public function __construct( * 404: User not found */ #[OpenAPI(scope: OpenAPI::SCOPE_ADMINISTRATION)] - public function generateNotification(string $userId, string $shortMessage, string $longMessage = ''): DataResponse { + public function generateNotification( + string $userId, + string $shortMessage = '', + string $longMessage = '', + string $richSubject = '', + array $richSubjectParameters = [], + string $richMessage = '', + array $richMessageParameters = [], + ): DataResponse { $user = $this->userManager->get($userId); if (!$user instanceof IUser) { return new DataResponse(null, Http::STATUS_NOT_FOUND); } - if ($shortMessage === '' || strlen($shortMessage) > 255) { + if (($shortMessage === '' && $richSubject === '') || strlen($shortMessage) > 255) { return new DataResponse(null, Http::STATUS_BAD_REQUEST); } @@ -62,17 +78,41 @@ public function generateNotification(string $userId, string $shortMessage, strin $datetime = $this->timeFactory->getDateTime(); try { + if ($richSubject !== '') { + $this->richValidator->validate($richSubject, $richSubjectParameters); + } + if ($richMessage !== '') { + $this->richValidator->validate($richMessage, $richMessageParameters); + } $notification->setApp('admin_notifications') ->setUser($user->getUID()) ->setDateTime($datetime) ->setObject('admin_notifications', dechex($datetime->getTimestamp())) - ->setSubject('ocs', [$shortMessage]); + ->setSubject( + 'ocs', + [ + 'parsed' => $shortMessage, + 'rich' => $richSubject, + 'parameters' => $richSubjectParameters, + ] + ); - if ($longMessage !== '') { - $notification->setMessage('ocs', [$longMessage]); + if ($longMessage !== '' || $richMessage !== '') { + $notification->setMessage( + 'ocs', + [ + 'parsed' => $longMessage, + 'rich' => $richMessage, + 'parameters' => $richMessageParameters, + ] + ); } $this->notificationManager->notify($notification); + } catch (InvalidObjectExeption $e) { + return new DataResponse('Invalid rich object: '.$e->getMessage(), Http::STATUS_BAD_REQUEST); + } catch (InvalidValueException $e) { + return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST); } catch (\InvalidArgumentException) { return new DataResponse(null, Http::STATUS_INTERNAL_SERVER_ERROR); } diff --git a/lib/Notifier/AdminNotifications.php b/lib/Notifier/AdminNotifications.php index 89696b098..aaf82e951 100644 --- a/lib/Notifier/AdminNotifications.php +++ b/lib/Notifier/AdminNotifications.php @@ -176,10 +176,20 @@ public function prepare(INotification $notification, string $languageCode): INot case 'cli': case 'ocs': $subjectParams = $notification->getSubjectParameters(); - $notification->setParsedSubject($subjectParams[0]); + if ($subjectParams['parsed'] !== '') { + $notification->setParsedSubject($subjectParams['parsed']); + } + if ($subjectParams['rich'] !== '') { + $notification->setRichSubject($subjectParams['rich'], $subjectParams['parameters']); + } $messageParams = $notification->getMessageParameters(); - if (isset($messageParams[0]) && $messageParams[0] !== '') { - $notification->setParsedMessage($messageParams[0]); + if (!empty($messageParams)) { + if ($messageParams['parsed'] !== '') { + $notification->setParsedMessage($messageParams['parsed']); + } + if ($messageParams['rich'] !== '') { + $notification->setRichMessage($messageParams['rich'], $messageParams['parameters']); + } } $notification->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('notifications', 'notifications-dark.svg'))); From 48b4c85fec6e3b75b66a61eaee16dd5058819efe Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 23 Jul 2024 08:42:43 +0200 Subject: [PATCH 2/9] ci(openapi): Update openapi-extractor to latest Signed-off-by: Joas Schilling --- vendor-bin/openapi-extractor/composer.lock | 61 ++++++++++++---------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/vendor-bin/openapi-extractor/composer.lock b/vendor-bin/openapi-extractor/composer.lock index 80ecc01c6..60ebb38b4 100644 --- a/vendor-bin/openapi-extractor/composer.lock +++ b/vendor-bin/openapi-extractor/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "adhocore/cli", - "version": "v1.6.2", + "version": "v1.7.1", "source": { "type": "git", "url": "https://github.com/adhocore/php-cli.git", - "reference": "34191315b0da20b9b4ecad783d91db992fa209a4" + "reference": "3fde60a838912e71c82ed0f48048685dc32dbc77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-cli/zipball/34191315b0da20b9b4ecad783d91db992fa209a4", - "reference": "34191315b0da20b9b4ecad783d91db992fa209a4", + "url": "https://api.github.com/repos/adhocore/php-cli/zipball/3fde60a838912e71c82ed0f48048685dc32dbc77", + "reference": "3fde60a838912e71c82ed0f48048685dc32dbc77", "shasum": "" }, "require": { @@ -62,7 +62,7 @@ ], "support": { "issues": "https://github.com/adhocore/php-cli/issues", - "source": "https://github.com/adhocore/php-cli/tree/v1.6.2" + "source": "https://github.com/adhocore/php-cli/tree/v1.7.1" }, "funding": [ { @@ -74,7 +74,7 @@ "type": "github" } ], - "time": "2024-01-22T22:37:23+00:00" + "time": "2024-03-28T08:30:12+00:00" }, { "name": "nextcloud/openapi-extractor", @@ -82,23 +82,24 @@ "source": { "type": "git", "url": "https://github.com/nextcloud/openapi-extractor.git", - "reference": "ecf189df4c930cd21acbef0984f5ca7bc3a7e5ab" + "reference": "944c6a64e428705eea138dcfce4e5b95b126448d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud/openapi-extractor/zipball/ecf189df4c930cd21acbef0984f5ca7bc3a7e5ab", - "reference": "ecf189df4c930cd21acbef0984f5ca7bc3a7e5ab", + "url": "https://api.github.com/repos/nextcloud/openapi-extractor/zipball/944c6a64e428705eea138dcfce4e5b95b126448d", + "reference": "944c6a64e428705eea138dcfce4e5b95b126448d", "shasum": "" }, "require": { - "adhocore/cli": "^v1.6", + "adhocore/cli": "^1.7", "ext-simplexml": "*", - "nikic/php-parser": "^4.16", + "nikic/php-parser": "^5.0", "php": "^8.1", - "phpstan/phpdoc-parser": "^1.23" + "phpstan/phpdoc-parser": "^1.28" }, "require-dev": { - "nextcloud/coding-standard": "^1.1" + "nextcloud/coding-standard": "^1.2", + "nextcloud/ocp": "dev-master" }, "default-branch": true, "bin": [ @@ -129,29 +130,31 @@ "source": "https://github.com/nextcloud/openapi-extractor/tree/main", "issues": "https://github.com/nextcloud/openapi-extractor/issues" }, - "time": "2024-04-08T14:19:17+00:00" + "time": "2024-07-06T04:06:45+00:00" }, { "name": "nikic/php-parser", - "version": "v4.18.0", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -159,7 +162,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -183,22 +186,22 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2023-12-10T21:03:43+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.25.0", + "version": "1.29.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240" + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240", - "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", "shasum": "" }, "require": { @@ -230,9 +233,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" }, - "time": "2024-01-04T17:06:16+00:00" + "time": "2024-05-31T08:52:43+00:00" } ], "packages-dev": [], From 650b703895919eec9852d3a3812000792b62d489 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 23 Jul 2024 08:43:22 +0200 Subject: [PATCH 3/9] fix(app): Allow to ge thte last inserted notification ID Signed-off-by: Joas Schilling --- lib/App.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/App.php b/lib/App.php index b6006469b..4207d0ead 100644 --- a/lib/App.php +++ b/lib/App.php @@ -17,6 +17,7 @@ use Symfony\Component\Console\Output\OutputInterface; class App implements IDeferrableApp { + protected ?int $lastInsertedId = null; public function __construct( protected Handler $handler, protected Push $push, @@ -33,15 +34,19 @@ public function setOutput(OutputInterface $output): void { * @since 8.2.0 */ public function notify(INotification $notification): void { - $notificationId = $this->handler->add($notification); + $this->lastInsertedId = $this->handler->add($notification); try { - $this->push->pushToDevice($notificationId, $notification); + $this->push->pushToDevice($this->lastInsertedId, $notification); } catch (NotificationNotFoundException $e) { $this->logger->error('Error while preparing push notification', ['exception' => $e]); } } + public function getLastInsertedId(): ?int { + return $this->lastInsertedId; + } + /** * @param INotification $notification * @return int From e855539a4f2be8d7a46168ee9aef3666395ace74 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 23 Jul 2024 08:44:00 +0200 Subject: [PATCH 4/9] feat(api): Add a V3 of the API allowing to give parameters Signed-off-by: Joas Schilling --- appinfo/routes.php | 1 + lib/Controller/APIController.php | 96 +++++++++++++++++++++----------- 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 4225f18e8..675b66231 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -17,6 +17,7 @@ ['name' => 'Endpoint#deleteAllNotifications', 'url' => '/api/{apiVersion}/notifications', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => '(v1|v2)']], ['name' => 'API#generateNotification', 'url' => '/api/{apiVersion}/admin_notifications/{userId}', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v1|v2)']], + ['name' => 'API#generateNotificationV3', 'url' => '/api/{apiVersion}/admin_notifications/{userId}', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v3)']], ['name' => 'Settings#personal', 'url' => '/api/{apiVersion}/settings', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#admin', 'url' => '/api/{apiVersion}/settings/admin', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], diff --git a/lib/Controller/APIController.php b/lib/Controller/APIController.php index 21b1bb071..ec1d8ffa5 100644 --- a/lib/Controller/APIController.php +++ b/lib/Controller/APIController.php @@ -9,6 +9,7 @@ namespace OCA\Notifications\Controller; +use OCA\Notifications\App; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\DataResponse; @@ -19,8 +20,9 @@ use OCP\IUserManager; use OCP\Notification\IManager; use OCP\Notification\InvalidValueException; -use OCP\RichObjectStrings\IValidator; use OCP\RichObjectStrings\InvalidObjectExeption; +use OCP\RichObjectStrings\IValidator; +use Psr\Log\LoggerInterface; class APIController extends OCSController { public function __construct( @@ -29,60 +31,88 @@ public function __construct( protected ITimeFactory $timeFactory, protected IUserManager $userManager, protected IManager $notificationManager, + protected App $notificationApp, protected IValidator $richValidator, + protected LoggerInterface $logger, ) { parent::__construct($appName, $request); } /** - * Generate a notification for a user + * Generate a notification for a user (deprecated, use v3 instead) * * @param string $userId ID of the user * @param string $shortMessage Subject of the notification * @param string $longMessage Message of the notification - * @param string $richSubject Subject of the notification with placeholders - * @param string $richSubjectParameters Rich objects to fill the subject placeholders, {@see \OCP\RichObjectStrings\Definitions} - * @param string $richMessage Message of the notification with placeholders - * @param string $richMessageParameters Rich objects to fill the message placeholders, {@see \OCP\RichObjectStrings\Definitions} * @return DataResponse, array{}>|DataResponse + * @deprecated 30.0.0 * * 200: Notification generated successfully * 400: Generating notification is not possible * 404: User not found */ #[OpenAPI(scope: OpenAPI::SCOPE_ADMINISTRATION)] - public function generateNotification( + public function generateNotification(string $userId, string $shortMessage, string $longMessage = ''): DataResponse { + $response = $this->generateNotificationV3($userId, $shortMessage, $longMessage); + if ($response->getStatus() === Http::STATUS_OK) { + return new DataResponse(); + } + + // Translate to old status code + $error = $response->getData()['error'] ?? null; + $code = match($error) { + 'user' => Http::STATUS_NOT_FOUND, + 'subject', + 'message' => Http::STATUS_BAD_REQUEST, + default => Http::STATUS_INTERNAL_SERVER_ERROR, + }; + return new DataResponse(null, $code); + } + + /** + * Generate a notification with rich object parameters for a user + * + * @param string $userId ID of the user + * @param string $subject Subject of the notification + * @param string $message Message of the notification + * @param array $subjectParameters Rich objects to fill the subject placeholders, {@see \OCP\RichObjectStrings\Definitions} + * @param array $messageParameters Rich objects to fill the message placeholders, {@see \OCP\RichObjectStrings\Definitions} + * @return DataResponse>|DataResponse> + * + * 200: Notification generated successfully, returned id is the notification ID for future delete requests + * 400: Provided data was invalid, check error field of the response of log file for details + */ + #[OpenAPI(scope: OpenAPI::SCOPE_ADMINISTRATION)] + public function generateNotificationV3( string $userId, - string $shortMessage = '', - string $longMessage = '', - string $richSubject = '', - array $richSubjectParameters = [], - string $richMessage = '', - array $richMessageParameters = [], + string $subject = '', + string $message = '', + array $subjectParameters = [], + array $messageParameters = [], ): DataResponse { $user = $this->userManager->get($userId); if (!$user instanceof IUser) { - return new DataResponse(null, Http::STATUS_NOT_FOUND); + return new DataResponse(['error' => 'user'], Http::STATUS_BAD_REQUEST); } - if (($shortMessage === '' && $richSubject === '') || strlen($shortMessage) > 255) { - return new DataResponse(null, Http::STATUS_BAD_REQUEST); + if ($subject === '' || strlen($subject) > 255) { + return new DataResponse(['error' => 'subject'], Http::STATUS_BAD_REQUEST); } - if ($longMessage !== '' && strlen($longMessage) > 4000) { - return new DataResponse(null, Http::STATUS_BAD_REQUEST); + if ($message !== '' && strlen($message) > 4000) { + return new DataResponse(['error' => 'message'], Http::STATUS_BAD_REQUEST); } $notification = $this->notificationManager->createNotification(); $datetime = $this->timeFactory->getDateTime(); try { - if ($richSubject !== '') { - $this->richValidator->validate($richSubject, $richSubjectParameters); + if (!empty($subjectParameters)) { + $this->richValidator->validate($subject, $subjectParameters); } - if ($richMessage !== '') { - $this->richValidator->validate($richMessage, $richMessageParameters); + if ($message !== '' && !empty($messageParameters)) { + $this->richValidator->validate($message, $messageParameters); } $notification->setApp('admin_notifications') ->setUser($user->getUID()) @@ -91,32 +121,30 @@ public function generateNotification( ->setSubject( 'ocs', [ - 'parsed' => $shortMessage, - 'rich' => $richSubject, - 'parameters' => $richSubjectParameters, + 'subject' => $subject, + 'parameters' => $subjectParameters, ] ); - if ($longMessage !== '' || $richMessage !== '') { + if ($message !== '') { $notification->setMessage( 'ocs', [ - 'parsed' => $longMessage, - 'rich' => $richMessage, - 'parameters' => $richMessageParameters, + 'message' => $message, + 'parameters' => $messageParameters, ] ); } $this->notificationManager->notify($notification); } catch (InvalidObjectExeption $e) { - return new DataResponse('Invalid rich object: '.$e->getMessage(), Http::STATUS_BAD_REQUEST); + $this->logger->error('Invalid rich object parameter provided: ' . $e->getMessage(), ['exception' => $e]); + return new DataResponse(['error' => 'parameters'], Http::STATUS_BAD_REQUEST); } catch (InvalidValueException $e) { - return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST); - } catch (\InvalidArgumentException) { - return new DataResponse(null, Http::STATUS_INTERNAL_SERVER_ERROR); + $this->logger->error('Invalid value for notification provided: ' . $e->getMessage(), ['exception' => $e]); + return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST); } - return new DataResponse(); + return new DataResponse(['id' => (int) $this->notificationApp->getLastInsertedId()]); } } From 06176e619cce407139edcb4543a98b531beb7284 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 23 Jul 2024 08:44:48 +0200 Subject: [PATCH 5/9] fix(notifications): Fix rendering of legacy notifications Signed-off-by: Joas Schilling --- lib/Notifier/AdminNotifications.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/Notifier/AdminNotifications.php b/lib/Notifier/AdminNotifications.php index aaf82e951..510f2dfd3 100644 --- a/lib/Notifier/AdminNotifications.php +++ b/lib/Notifier/AdminNotifications.php @@ -176,19 +176,21 @@ public function prepare(INotification $notification, string $languageCode): INot case 'cli': case 'ocs': $subjectParams = $notification->getSubjectParameters(); - if ($subjectParams['parsed'] !== '') { - $notification->setParsedSubject($subjectParams['parsed']); - } - if ($subjectParams['rich'] !== '') { - $notification->setRichSubject($subjectParams['rich'], $subjectParams['parameters']); + if (isset($subjectParams['subject'])) { + // Nextcloud 30+ + $notification->setRichSubject($subjectParams['subject'], $subjectParams['parameters']); + } else { + // Legacy before Nextcloud 30 (v3) + $notification->setParsedSubject($subjectParams[0]); } $messageParams = $notification->getMessageParameters(); if (!empty($messageParams)) { - if ($messageParams['parsed'] !== '') { - $notification->setParsedMessage($messageParams['parsed']); - } - if ($messageParams['rich'] !== '') { - $notification->setRichMessage($messageParams['rich'], $messageParams['parameters']); + if (!empty($messageParams['message'])) { + // Nextcloud 30+ + $notification->setRichMessage($messageParams['message'], $messageParams['parameters']); + } elseif (!empty($messageParams[0])) { + // Legacy before Nextcloud 30 (v3) + $notification->setParsedMessage($messageParams[0]); } } From 67fc2d4ae6e8ea7d6109959b55d742c3bbc64fc2 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 23 Jul 2024 09:05:54 +0200 Subject: [PATCH 6/9] fix(openapi): Define rich object parameters Signed-off-by: Joas Schilling --- lib/Controller/APIController.php | 10 +++++++--- lib/ResponseDefinitions.php | 34 ++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/Controller/APIController.php b/lib/Controller/APIController.php index ec1d8ffa5..a9ad176c4 100644 --- a/lib/Controller/APIController.php +++ b/lib/Controller/APIController.php @@ -10,6 +10,7 @@ namespace OCA\Notifications\Controller; use OCA\Notifications\App; +use OCA\Notifications\ResponseDefinitions; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\DataResponse; @@ -24,6 +25,9 @@ use OCP\RichObjectStrings\IValidator; use Psr\Log\LoggerInterface; +/** + * @psalm-import-type NotificationsRichObjectParameter from ResponseDefinitions + */ class APIController extends OCSController { public function __construct( string $appName, @@ -75,9 +79,9 @@ public function generateNotification(string $userId, string $shortMessage, strin * @param string $userId ID of the user * @param string $subject Subject of the notification * @param string $message Message of the notification - * @param array $subjectParameters Rich objects to fill the subject placeholders, {@see \OCP\RichObjectStrings\Definitions} - * @param array $messageParameters Rich objects to fill the message placeholders, {@see \OCP\RichObjectStrings\Definitions} - * @return DataResponse>|DataResponse> + * @param array $subjectParameters Rich objects to fill the subject placeholders, {@see \OCP\RichObjectStrings\Definitions} + * @param array $messageParameters Rich objects to fill the message placeholders, {@see \OCP\RichObjectStrings\Definitions} + * @return DataResponse|DataResponse * * 200: Notification generated successfully, returned id is the notification ID for future delete requests * 400: Provided data was invalid, check error field of the response of log file for details diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index 9da7f585a..e5cf665c1 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -10,6 +10,36 @@ namespace OCA\Notifications; /** + * @psalm-type NotificationsRichObjectParameter = array{ + * type: string, + * id: string, + * name: string, + * server?: string, + * link?: string, + * 'call-type'?: 'one2one'|'group'|'public', + * 'icon-url'?: string, + * 'message-id'?: string, + * boardname?: string, + * stackname?: string, + * size?: string, + * path?: string, + * mimetype?: string, + * 'preview-available'?: 'yes'|'no', + * mtime?: string, + * latitude?: string, + * longitude?: string, + * description?: string, + * thumb?: string, + * website?: string, + * visibility?: '0'|'1', + * assignable?: '0'|'1', + * conversation?: string, + * etag?: string, + * permissions?: string, + * width?: string, + * height?: string, + * } + * * @psalm-type NotificationsNotificationAction = array{ * label: string, * link: string, @@ -29,9 +59,9 @@ * link: string, * actions: NotificationsNotificationAction[], * subjectRich?: string, - * subjectRichParameters?: array, + * subjectRichParameters?: array, * messageRich?: string, - * messageRichParameters?: array, + * messageRichParameters?: array, * icon?: string, * shouldNotify?: bool, * } From d5c85afa35ebf5fcade5cafa0966ece40f101ee8 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 23 Jul 2024 09:06:26 +0200 Subject: [PATCH 7/9] fix(openapi): Unify the route name Signed-off-by: Joas Schilling --- appinfo/routes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 675b66231..46e5a8c88 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -17,7 +17,7 @@ ['name' => 'Endpoint#deleteAllNotifications', 'url' => '/api/{apiVersion}/notifications', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => '(v1|v2)']], ['name' => 'API#generateNotification', 'url' => '/api/{apiVersion}/admin_notifications/{userId}', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v1|v2)']], - ['name' => 'API#generateNotificationV3', 'url' => '/api/{apiVersion}/admin_notifications/{userId}', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v3)']], + ['name' => 'API#generateNotificationV3', 'url' => '/api/{apiVersion3}/admin_notifications/{userId}', 'verb' => 'POST', 'requirements' => ['apiVersion3' => '(v3)']], ['name' => 'Settings#personal', 'url' => '/api/{apiVersion}/settings', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#admin', 'url' => '/api/{apiVersion}/settings/admin', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], From 555568bf8643c7af09368e3488cc117750282d14 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 23 Jul 2024 09:07:29 +0200 Subject: [PATCH 8/9] chore(assets): Recompile assets Signed-off-by: Joas Schilling --- openapi-administration.json | 246 +++++++++++++++--- openapi-full.json | 506 ++++++++++++++++++++++++++++-------- openapi-push.json | 58 +++-- openapi.json | 206 ++++++++++++--- 4 files changed, 804 insertions(+), 212 deletions(-) diff --git a/openapi-administration.json b/openapi-administration.json index 437bbc1a6..262c588cb 100644 --- a/openapi-administration.json +++ b/openapi-administration.json @@ -86,8 +86,9 @@ "/ocs/v2.php/apps/notifications/api/{apiVersion}/admin_notifications/{userId}": { "post": { "operationId": "api-generate-notification", - "summary": "Generate a notification for a user", + "summary": "Generate a notification for a user (deprecated, use v3 instead)", "description": "This endpoint requires admin access", + "deprecated": true, "tags": [ "api" ], @@ -99,25 +100,31 @@ "basic_auth": [] } ], - "parameters": [ - { - "name": "shortMessage", - "in": "query", - "description": "Subject of the notification", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "longMessage", - "in": "query", - "description": "Message of the notification", - "schema": { - "type": "string", - "default": "" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "shortMessage" + ], + "properties": { + "shortMessage": { + "type": "string", + "description": "Subject of the notification" + }, + "longMessage": { + "type": "string", + "default": "", + "description": "Message of the notification" + } + } + } } - }, + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -273,13 +280,13 @@ } } }, - "/ocs/v2.php/apps/notifications/api/{apiVersion}/settings/admin": { + "/ocs/v2.php/apps/notifications/api/{apiVersion3}/admin_notifications/{userId}": { "post": { - "operationId": "settings-admin", - "summary": "Update default notification settings for new users", + "operationId": "api-generate-notification-v3", + "summary": "Generate a notification with rich object parameters for a user", "description": "This endpoint requires admin access", "tags": [ - "settings" + "api" ], "security": [ { @@ -289,35 +296,202 @@ "basic_auth": [] } ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "subject": { + "type": "string", + "default": "", + "description": "Subject of the notification" + }, + "message": { + "type": "string", + "default": "", + "description": "Message of the notification" + }, + "subjectParameters": { + "type": "object", + "default": [], + "description": "Rich objects to fill the subject placeholders, {@see \\OCP\\RichObjectStrings\\Definitions}", + "additionalProperties": { + "$ref": "#/components/schemas/RichObjectParameter" + } + }, + "messageParameters": { + "type": "object", + "default": [], + "description": "Rich objects to fill the message placeholders, {@see \\OCP\\RichObjectStrings\\Definitions}", + "additionalProperties": { + "$ref": "#/components/schemas/RichObjectParameter" + } + } + } + } + } + } + }, "parameters": [ { - "name": "batchSetting", - "in": "query", - "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)", + "name": "apiVersion3", + "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int64" + "type": "string", + "pattern": "^(v3)$" } }, { - "name": "soundNotification", - "in": "query", - "description": "Enable sound for notifications ('yes' or 'no')", + "name": "userId", + "in": "path", + "description": "ID of the user", "required": true, "schema": { "type": "string" } }, { - "name": "soundTalk", - "in": "query", - "description": "Enable sound for Talk notifications ('yes' or 'no')", + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", "required": true, "schema": { - "type": "string" + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Notification generated successfully, returned id is the notification ID for future delete requests", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + } + } + } + } + } + } + } + } } }, + "400": { + "description": "Provided data was invalid, check error field of the response of log file for details", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/settings/admin": { + "post": { + "operationId": "settings-admin", + "summary": "Update default notification settings for new users", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "batchSetting", + "soundNotification", + "soundTalk" + ], + "properties": { + "batchSetting": { + "type": "integer", + "format": "int64", + "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)" + }, + "soundNotification": { + "type": "string", + "description": "Enable sound for notifications ('yes' or 'no')" + }, + "soundTalk": { + "type": "string", + "description": "Enable sound for Talk notifications ('yes' or 'no')" + } + } + } + } + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -375,4 +549,4 @@ } }, "tags": [] -} \ No newline at end of file +} diff --git a/openapi-full.json b/openapi-full.json index c214b870e..3067c4f9c 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -111,7 +111,7 @@ "subjectRichParameters": { "type": "object", "additionalProperties": { - "type": "object" + "$ref": "#/components/schemas/RichObjectParameter" } }, "messageRich": { @@ -120,7 +120,7 @@ "messageRichParameters": { "type": "object", "additionalProperties": { - "type": "object" + "$ref": "#/components/schemas/RichObjectParameter" } }, "icon": { @@ -196,6 +196,114 @@ "type": "string" } } + }, + "RichObjectParameter": { + "type": "object", + "required": [ + "type", + "id", + "name" + ], + "properties": { + "type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "server": { + "type": "string" + }, + "link": { + "type": "string" + }, + "call-type": { + "type": "string", + "enum": [ + "one2one", + "group", + "public" + ] + }, + "icon-url": { + "type": "string" + }, + "message-id": { + "type": "string" + }, + "boardname": { + "type": "string" + }, + "stackname": { + "type": "string" + }, + "size": { + "type": "string" + }, + "path": { + "type": "string" + }, + "mimetype": { + "type": "string" + }, + "preview-available": { + "type": "string", + "enum": [ + "yes", + "no" + ] + }, + "mtime": { + "type": "string" + }, + "latitude": { + "type": "string" + }, + "longitude": { + "type": "string" + }, + "description": { + "type": "string" + }, + "thumb": { + "type": "string" + }, + "website": { + "type": "string" + }, + "visibility": { + "type": "string", + "enum": [ + "0", + "1" + ] + }, + "assignable": { + "type": "string", + "enum": [ + "0", + "1" + ] + }, + "conversation": { + "type": "string" + }, + "etag": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "width": { + "type": "string" + }, + "height": { + "type": "string" + } + } } } }, @@ -665,20 +773,30 @@ "basic_auth": [] } ], - "parameters": [ - { - "name": "ids[]", - "in": "query", - "description": "IDs of the notifications to check", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ids" + ], + "properties": { + "ids": { + "type": "array", + "description": "IDs of the notifications to check", + "items": { + "type": "integer", + "format": "int64" + } + } + } } } - }, + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -791,35 +909,37 @@ "basic_auth": [] } ], - "parameters": [ - { - "name": "batchSetting", - "in": "query", - "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - }, - { - "name": "soundNotification", - "in": "query", - "description": "Enable sound for notifications ('yes' or 'no')", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "soundTalk", - "in": "query", - "description": "Enable sound for Talk notifications ('yes' or 'no')", - "required": true, - "schema": { - "type": "string" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "batchSetting", + "soundNotification", + "soundTalk" + ], + "properties": { + "batchSetting": { + "type": "integer", + "format": "int64", + "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)" + }, + "soundNotification": { + "type": "string", + "description": "Enable sound for notifications ('yes' or 'no')" + }, + "soundTalk": { + "type": "string", + "description": "Enable sound for Talk notifications ('yes' or 'no')" + } + } + } } - }, + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -878,8 +998,9 @@ "/ocs/v2.php/apps/notifications/api/{apiVersion}/admin_notifications/{userId}": { "post": { "operationId": "api-generate-notification", - "summary": "Generate a notification for a user", + "summary": "Generate a notification for a user (deprecated, use v3 instead)", "description": "This endpoint requires admin access", + "deprecated": true, "tags": [ "api" ], @@ -891,25 +1012,31 @@ "basic_auth": [] } ], - "parameters": [ - { - "name": "shortMessage", - "in": "query", - "description": "Subject of the notification", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "longMessage", - "in": "query", - "description": "Message of the notification", - "schema": { - "type": "string", - "default": "" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "shortMessage" + ], + "properties": { + "shortMessage": { + "type": "string", + "description": "Subject of the notification" + }, + "longMessage": { + "type": "string", + "default": "", + "description": "Message of the notification" + } + } + } } - }, + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -1065,13 +1192,13 @@ } } }, - "/ocs/v2.php/apps/notifications/api/{apiVersion}/settings/admin": { + "/ocs/v2.php/apps/notifications/api/{apiVersion3}/admin_notifications/{userId}": { "post": { - "operationId": "settings-admin", - "summary": "Update default notification settings for new users", + "operationId": "api-generate-notification-v3", + "summary": "Generate a notification with rich object parameters for a user", "description": "This endpoint requires admin access", "tags": [ - "settings" + "api" ], "security": [ { @@ -1081,35 +1208,202 @@ "basic_auth": [] } ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "subject": { + "type": "string", + "default": "", + "description": "Subject of the notification" + }, + "message": { + "type": "string", + "default": "", + "description": "Message of the notification" + }, + "subjectParameters": { + "type": "object", + "default": [], + "description": "Rich objects to fill the subject placeholders, {@see \\OCP\\RichObjectStrings\\Definitions}", + "additionalProperties": { + "$ref": "#/components/schemas/RichObjectParameter" + } + }, + "messageParameters": { + "type": "object", + "default": [], + "description": "Rich objects to fill the message placeholders, {@see \\OCP\\RichObjectStrings\\Definitions}", + "additionalProperties": { + "$ref": "#/components/schemas/RichObjectParameter" + } + } + } + } + } + } + }, "parameters": [ { - "name": "batchSetting", - "in": "query", - "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)", + "name": "apiVersion3", + "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int64" + "type": "string", + "pattern": "^(v3)$" } }, { - "name": "soundNotification", - "in": "query", - "description": "Enable sound for notifications ('yes' or 'no')", + "name": "userId", + "in": "path", + "description": "ID of the user", "required": true, "schema": { "type": "string" } }, { - "name": "soundTalk", - "in": "query", - "description": "Enable sound for Talk notifications ('yes' or 'no')", + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", "required": true, "schema": { - "type": "string" + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Notification generated successfully, returned id is the notification ID for future delete requests", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + } + } + } + } + } + } + } + } } }, + "400": { + "description": "Provided data was invalid, check error field of the response of log file for details", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/settings/admin": { + "post": { + "operationId": "settings-admin", + "summary": "Update default notification settings for new users", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "batchSetting", + "soundNotification", + "soundTalk" + ], + "properties": { + "batchSetting": { + "type": "integer", + "format": "int64", + "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)" + }, + "soundNotification": { + "type": "string", + "description": "Enable sound for notifications ('yes' or 'no')" + }, + "soundTalk": { + "type": "string", + "description": "Enable sound for Talk notifications ('yes' or 'no')" + } + } + } + } + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -1180,34 +1474,36 @@ "basic_auth": [] } ], - "parameters": [ - { - "name": "pushTokenHash", - "in": "query", - "description": "Hash of the push token", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "devicePublicKey", - "in": "query", - "description": "Public key of the device", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "proxyServer", - "in": "query", - "description": "Proxy server to be used", - "required": true, - "schema": { - "type": "string" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "pushTokenHash", + "devicePublicKey", + "proxyServer" + ], + "properties": { + "pushTokenHash": { + "type": "string", + "description": "Hash of the push token" + }, + "devicePublicKey": { + "type": "string", + "description": "Public key of the device" + }, + "proxyServer": { + "type": "string", + "description": "Proxy server to be used" + } + } + } } - }, + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -1526,4 +1822,4 @@ } }, "tags": [] -} \ No newline at end of file +} diff --git a/openapi-push.json b/openapi-push.json index a5f555bdd..87bdc9b55 100644 --- a/openapi-push.json +++ b/openapi-push.json @@ -117,34 +117,36 @@ "basic_auth": [] } ], - "parameters": [ - { - "name": "pushTokenHash", - "in": "query", - "description": "Hash of the push token", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "devicePublicKey", - "in": "query", - "description": "Public key of the device", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "proxyServer", - "in": "query", - "description": "Proxy server to be used", - "required": true, - "schema": { - "type": "string" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "pushTokenHash", + "devicePublicKey", + "proxyServer" + ], + "properties": { + "pushTokenHash": { + "type": "string", + "description": "Hash of the push token" + }, + "devicePublicKey": { + "type": "string", + "description": "Public key of the device" + }, + "proxyServer": { + "type": "string", + "description": "Proxy server to be used" + } + } + } } - }, + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -463,4 +465,4 @@ } }, "tags": [] -} \ No newline at end of file +} diff --git a/openapi.json b/openapi.json index 9ced3becf..d8bbee675 100644 --- a/openapi.json +++ b/openapi.json @@ -111,7 +111,7 @@ "subjectRichParameters": { "type": "object", "additionalProperties": { - "type": "object" + "$ref": "#/components/schemas/RichObjectParameter" } }, "messageRich": { @@ -120,7 +120,7 @@ "messageRichParameters": { "type": "object", "additionalProperties": { - "type": "object" + "$ref": "#/components/schemas/RichObjectParameter" } }, "icon": { @@ -177,6 +177,114 @@ "type": "string" } } + }, + "RichObjectParameter": { + "type": "object", + "required": [ + "type", + "id", + "name" + ], + "properties": { + "type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "server": { + "type": "string" + }, + "link": { + "type": "string" + }, + "call-type": { + "type": "string", + "enum": [ + "one2one", + "group", + "public" + ] + }, + "icon-url": { + "type": "string" + }, + "message-id": { + "type": "string" + }, + "boardname": { + "type": "string" + }, + "stackname": { + "type": "string" + }, + "size": { + "type": "string" + }, + "path": { + "type": "string" + }, + "mimetype": { + "type": "string" + }, + "preview-available": { + "type": "string", + "enum": [ + "yes", + "no" + ] + }, + "mtime": { + "type": "string" + }, + "latitude": { + "type": "string" + }, + "longitude": { + "type": "string" + }, + "description": { + "type": "string" + }, + "thumb": { + "type": "string" + }, + "website": { + "type": "string" + }, + "visibility": { + "type": "string", + "enum": [ + "0", + "1" + ] + }, + "assignable": { + "type": "string", + "enum": [ + "0", + "1" + ] + }, + "conversation": { + "type": "string" + }, + "etag": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "width": { + "type": "string" + }, + "height": { + "type": "string" + } + } } } }, @@ -646,20 +754,30 @@ "basic_auth": [] } ], - "parameters": [ - { - "name": "ids[]", - "in": "query", - "description": "IDs of the notifications to check", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ids" + ], + "properties": { + "ids": { + "type": "array", + "description": "IDs of the notifications to check", + "items": { + "type": "integer", + "format": "int64" + } + } + } } } - }, + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -772,35 +890,37 @@ "basic_auth": [] } ], - "parameters": [ - { - "name": "batchSetting", - "in": "query", - "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - }, - { - "name": "soundNotification", - "in": "query", - "description": "Enable sound for notifications ('yes' or 'no')", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "soundTalk", - "in": "query", - "description": "Enable sound for Talk notifications ('yes' or 'no')", - "required": true, - "schema": { - "type": "string" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "batchSetting", + "soundNotification", + "soundTalk" + ], + "properties": { + "batchSetting": { + "type": "integer", + "format": "int64", + "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)" + }, + "soundNotification": { + "type": "string", + "description": "Enable sound for notifications ('yes' or 'no')" + }, + "soundTalk": { + "type": "string", + "description": "Enable sound for Talk notifications ('yes' or 'no')" + } + } + } } - }, + } + }, + "parameters": [ { "name": "apiVersion", "in": "path", @@ -858,4 +978,4 @@ } }, "tags": [] -} \ No newline at end of file +} From 78be17b91d8985347a3bbaa9204af6346c0336ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 5 Aug 2024 16:22:24 +0200 Subject: [PATCH 9/9] fix(tests): Fix unit tests and error catching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/Controller/APIController.php | 3 +- tests/Unit/AppInfo/RoutesTest.php | 2 +- tests/Unit/Controller/APIControllerTest.php | 35 ++++++++++++++------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/lib/Controller/APIController.php b/lib/Controller/APIController.php index a9ad176c4..b75847606 100644 --- a/lib/Controller/APIController.php +++ b/lib/Controller/APIController.php @@ -20,6 +20,7 @@ use OCP\IUser; use OCP\IUserManager; use OCP\Notification\IManager; +use OCP\Notification\IncompleteNotificationException; use OCP\Notification\InvalidValueException; use OCP\RichObjectStrings\InvalidObjectExeption; use OCP\RichObjectStrings\IValidator; @@ -144,7 +145,7 @@ public function generateNotificationV3( } catch (InvalidObjectExeption $e) { $this->logger->error('Invalid rich object parameter provided: ' . $e->getMessage(), ['exception' => $e]); return new DataResponse(['error' => 'parameters'], Http::STATUS_BAD_REQUEST); - } catch (InvalidValueException $e) { + } catch (InvalidValueException|IncompleteNotificationException $e) { $this->logger->error('Invalid value for notification provided: ' . $e->getMessage(), ['exception' => $e]); return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST); } diff --git a/tests/Unit/AppInfo/RoutesTest.php b/tests/Unit/AppInfo/RoutesTest.php index 29c0e90f8..06776622e 100644 --- a/tests/Unit/AppInfo/RoutesTest.php +++ b/tests/Unit/AppInfo/RoutesTest.php @@ -21,6 +21,6 @@ public function testRoutes() { $this->assertCount(1, $routes); $this->assertArrayHasKey('ocs', $routes); $this->assertIsArray($routes['ocs']); - $this->assertCount(8, $routes['ocs']); + $this->assertCount(9, $routes['ocs']); } } diff --git a/tests/Unit/Controller/APIControllerTest.php b/tests/Unit/Controller/APIControllerTest.php index 711cb89bc..0c4a0b4a2 100644 --- a/tests/Unit/Controller/APIControllerTest.php +++ b/tests/Unit/Controller/APIControllerTest.php @@ -6,6 +6,7 @@ namespace OCA\Notifications\Tests\Unit\Controller; +use OCA\Notifications\App; use OCA\Notifications\Controller\APIController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -14,7 +15,11 @@ use OCP\IUser; use OCP\IUserManager; use OCP\Notification\IManager; +use OCP\Notification\IncompleteNotificationException; use OCP\Notification\INotification; +use OCP\RichObjectStrings\IValidator; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; /** * Class APIControllerTest @@ -23,14 +28,14 @@ * @group DB */ class APIControllerTest extends \Test\TestCase { - /** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $timeFactory; - /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ - protected $userManager; - /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */ - protected $notificationManager; - /** @var APIController */ - protected $controller; + protected ITimeFactory&MockObject $timeFactory; + protected IUserManager&MockObject $userManager; + protected IManager&MockObject $notificationManager; + protected App&MockObject $notificationApp; + protected IValidator&MockObject $richValidator; + protected LoggerInterface&MockObject $logger; + + protected APIController $controller; protected function setUp(): void { parent::setUp(); @@ -40,13 +45,19 @@ protected function setUp(): void { $this->timeFactory = $this->createMock(ITimeFactory::class); $this->userManager = $this->createMock(IUserManager::class); $this->notificationManager = $this->createMock(IManager::class); + $this->notificationApp = $this->createMock(App::class); + $this->richValidator = $this->createMock(IValidator::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->controller = new APIController( 'notifications', $request, $this->timeFactory, $this->userManager, - $this->notificationManager + $this->notificationManager, + $this->notificationApp, + $this->richValidator, + $this->logger, ); } @@ -114,12 +125,12 @@ public function testGenerateNotification($userId, $short, $long, $createNotifica ->willReturnSelf(); $n->expects($this->once()) ->method('setSubject') - ->with('ocs', [$short]) + ->with('ocs', ['subject' => $short, 'parameters' => []]) ->willReturnSelf(); if ($validLong) { $n->expects($this->once()) ->method('setMessage') - ->with('ocs', [$long]) + ->with('ocs', ['message' => $long, 'parameters' => []]) ->willReturnSelf(); } else { $n->expects($this->never()) @@ -140,7 +151,7 @@ public function testGenerateNotification($userId, $short, $long, $createNotifica } elseif ($notifyThrows === true) { $this->notificationManager->expects($this->once()) ->method('notify') - ->willThrowException(new \InvalidArgumentException()); + ->willThrowException(new IncompleteNotificationException()); } }