Skip to content

Commit

Permalink
Merge pull request #1640 from nextcloud/feat/openapi
Browse files Browse the repository at this point in the history
Add OpenAPI spec
  • Loading branch information
provokateurin authored Sep 4, 2023
2 parents 4aac8ab + 710863d commit 9e4bfdc
Show file tree
Hide file tree
Showing 16 changed files with 2,101 additions and 42 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/openapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: OpenAPI

on:
pull_request:
push:
branches:
- master
- stable*

jobs:
openapi:
runs-on: ubuntu-latest

if: ${{ github.repository_owner != 'nextcloud-gmbh' }}

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up php
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: xml
coverage: none
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Composer install
run: composer i

- name: OpenAPI checker
run: |
composer exec generate-spec
if [ -n "$(git status --porcelain openapi.json)" ]; then
git diff
exit 1
fi
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@ nbproject
/npm-debug.log
/build
/vendor
/vendor-bin/*/vendor
20 changes: 10 additions & 10 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@

return [
'ocs' => [
['name' => 'Endpoint#listNotifications', 'url' => '/api/{apiVersion}/notifications', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v(1|2)']],
['name' => 'Endpoint#getNotification', 'url' => '/api/{apiVersion}/notifications/{id}', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v(1|2)', 'id' => '\d+']],
['name' => 'Endpoint#confirmIdsForUser', 'url' => '/api/{apiVersion}/notifications/exists', 'verb' => 'POST', 'requirements' => ['apiVersion' => 'v(1|2)']],
['name' => 'Endpoint#deleteNotification', 'url' => '/api/{apiVersion}/notifications/{id}', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => 'v(1|2)', 'id' => '\d+']],
['name' => 'Endpoint#deleteAllNotifications', 'url' => '/api/{apiVersion}/notifications', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => 'v(1|2)']],
['name' => 'Push#registerDevice', 'url' => '/api/{apiVersion}/push', 'verb' => 'POST', 'requirements' => ['apiVersion' => 'v2']],
['name' => 'Push#removeDevice', 'url' => '/api/{apiVersion}/push', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => 'v2']],
['name' => 'Endpoint#listNotifications', 'url' => '/api/{apiVersion}/notifications', 'verb' => 'GET', 'requirements' => ['apiVersion' => '(v1|v2)']],
['name' => 'Endpoint#getNotification', 'url' => '/api/{apiVersion}/notifications/{id}', 'verb' => 'GET', 'requirements' => ['apiVersion' => '(v1|v2)', 'id' => '\d+']],
['name' => 'Endpoint#confirmIdsForUser', 'url' => '/api/{apiVersion}/notifications/exists', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v1|v2)']],
['name' => 'Endpoint#deleteNotification', 'url' => '/api/{apiVersion}/notifications/{id}', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => '(v1|v2)', 'id' => '\d+']],
['name' => 'Endpoint#deleteAllNotifications', 'url' => '/api/{apiVersion}/notifications', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => '(v1|v2)']],
['name' => 'Push#registerDevice', 'url' => '/api/{apiVersion}/push', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']],
['name' => 'Push#removeDevice', 'url' => '/api/{apiVersion}/push', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => '(v2)']],

['name' => 'API#generateNotification', 'url' => '/api/{apiVersion}/admin_notifications/{userId}', 'verb' => 'POST', 'requirements' => ['apiVersion' => 'v(1|2)']],
['name' => 'API#generateNotification', 'url' => '/api/{apiVersion}/admin_notifications/{userId}', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v1|v2)']],

['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']],
['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)']],
],
];
16 changes: 14 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,29 @@
"nextcloud/coding-standard": "^1.1",
"nextcloud/ocp": "dev-master",
"phpunit/phpunit": "^9.6",
"vimeo/psalm": "^5.14"
"vimeo/psalm": "^5.14",
"bamarni/composer-bin-plugin": "^1.8"
},
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true,
"platform": {
"php": "8.0.2"
},
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"bamarni/composer-bin-plugin": true
}
},
"scripts": {
"post-install-cmd": [
"[ $COMPOSER_DEV_MODE -eq 0 ] || composer bin all install",
"composer dump-autoload"
],
"post-update-cmd": [
"[ $COMPOSER_DEV_MODE -eq 0 ] || composer bin all update --ansi",
"composer dump-autoload"
],
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
Expand Down
71 changes: 64 additions & 7 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Kate Döen <kate.doeen@nextcloud.com>
*
* @license AGPL-3.0
*
Expand Down Expand Up @@ -36,7 +37,13 @@ class Capabilities implements ICapability {
/**
* Return this classes capabilities
*
* @return array<string, array<string, array<string>>>
* @return array{
* notifications: array{
* ocs-endpoints: string[],
* push: string[],
* admin-notifications: string[],
* },
* }
*/
public function getCapabilities(): array {
return [
Expand Down
14 changes: 10 additions & 4 deletions lib/Controller/APIController.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,16 @@ public function __construct(
}

/**
* @param string $userId
* @param string $shortMessage
* @param string $longMessage
* @return DataResponse
* Generate a notification for a user
*
* @param string $userId ID of the user
* @param string $shortMessage Subject of the notification
* @param string $longMessage Message of the notification
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, null, array{}>
*
* 200: Notification generated successfully
* 400: Generating notification is not possible
* 404: User not found
*/
public function generateNotification(string $userId, string $shortMessage, string $longMessage = ''): DataResponse {
$user = $this->userManager->get($userId);
Expand Down
57 changes: 44 additions & 13 deletions lib/Controller/EndpointController.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use OCA\Notifications\Exceptions\NotificationNotFoundException;
use OCA\Notifications\Handler;
use OCA\Notifications\Push;
use OCA\Notifications\ResponseDefinitions;
use OCA\Notifications\Service\ClientService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
Expand All @@ -44,6 +45,10 @@
use OCP\UserStatus\IManager as IUserStatusManager;
use OCP\UserStatus\IUserStatus;

/**
* @psalm-import-type NotificationsNotification from ResponseDefinitions
* @psalm-import-type NotificationsNotificationAction from ResponseDefinitions
*/
class EndpointController extends OCSController {
public function __construct(
string $appName,
Expand All @@ -64,8 +69,13 @@ public function __construct(
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $apiVersion
* @return DataResponse
* Get all notifications
*
* @param string $apiVersion Version of the API to use
* @return DataResponse<Http::STATUS_OK, NotificationsNotification[], array{'X-Nextcloud-User-Status': string}>|DataResponse<Http::STATUS_NO_CONTENT, null, array{X-Nextcloud-User-Status: string}>
*
* 200: Notifications returned
* 204: No app uses notifications
*/
public function listNotifications(string $apiVersion): DataResponse {
$userStatus = $this->userStatusManager->getUserStatuses([
Expand Down Expand Up @@ -131,9 +141,14 @@ public function listNotifications(string $apiVersion): DataResponse {
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $apiVersion
* @param int $id
* @return DataResponse
* Get a notification
*
* @param string $apiVersion Version of the API to use
* @param int $id ID of the notification
* @return DataResponse<Http::STATUS_OK, NotificationsNotification, array{}>|DataResponse<Http::STATUS_NOT_FOUND, null, array{}>
*
* 200: Notification returned
* 404: Notification not found
*/
public function getNotification(string $apiVersion, int $id): DataResponse {
if (!$this->manager->hasNotifiers()) {
Expand Down Expand Up @@ -174,9 +189,14 @@ public function getNotification(string $apiVersion, int $id): DataResponse {
/**
* @NoAdminRequired
*
* @param string $apiVersion
* @param int[] $ids
* @return DataResponse
* Check if notification IDs exist
*
* @param string $apiVersion Version of the API to use
* @param int[] $ids IDs of the notifications to check
* @return DataResponse<Http::STATUS_OK|Http::STATUS_BAD_REQUEST, int[], array{}>
*
* 200: Existing nsotification IDs returned
* 400: Too many notification IDs requested
*/
public function confirmIdsForUser(string $apiVersion, array $ids): DataResponse {
if (!$this->manager->hasNotifiers()) {
Expand All @@ -203,8 +223,14 @@ public function confirmIdsForUser(string $apiVersion, array $ids): DataResponse
/**
* @NoAdminRequired
*
* @param int $id
* @return DataResponse
* Delete a notification
*
* @param int $id ID of the notification
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, null, array{}>
*
* 200: Notification deleted successfully
* 403: Deleting notification for impersonated user is not allowed
* 404: Notification not found
*/
public function deleteNotification(int $id): DataResponse {
if ($id === 0) {
Expand All @@ -231,7 +257,12 @@ public function deleteNotification(int $id): DataResponse {
/**
* @NoAdminRequired
*
* @return DataResponse
* Delete all notifications
*
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_FORBIDDEN, null, array{}>
*
* 200: All notifications deleted successfully
* 403: Deleting notification for impersonated user is not allowed
*/
public function deleteAllNotifications(): DataResponse {
if ($this->session->getImpersonatingUserID() !== null) {
Expand Down Expand Up @@ -267,7 +298,7 @@ protected function generateETag(array $notifications): string {
* @param INotification $notification
* @param string $apiVersion
* @param bool $hasActiveTalkDesktop
* @return array
* @return NotificationsNotification
*/
protected function notificationToArray(int $notificationId, INotification $notification, string $apiVersion, bool $hasActiveTalkDesktop = false): array {
$data = [
Expand Down Expand Up @@ -309,7 +340,7 @@ protected function notificationToArray(int $notificationId, INotification $notif

/**
* @param IAction $action
* @return array
* @return NotificationsNotificationAction
*/
protected function actionToArray(IAction $action): array {
return [
Expand Down
Loading

0 comments on commit 9e4bfdc

Please sign in to comment.