From b3a7a608e43ee51bfbc05ee6afc4e1ea8b0ef08a Mon Sep 17 00:00:00 2001 From: jld3103 Date: Fri, 27 Jan 2023 13:11:25 +0100 Subject: [PATCH] user_status: Add OpenAPI spec Signed-off-by: jld3103 --- .../lib/Controller/HeartbeatController.php | 27 +- .../Controller/PredefinedStatusController.php | 15 +- .../lib/Controller/StatusesController.php | 18 +- .../lib/Controller/UserStatusController.php | 39 +- apps/user_status/lib/ResponseDefinitions.php | 62 ++ apps/user_status/openapi.json | 843 ++++++++++++++++++ 6 files changed, 966 insertions(+), 38 deletions(-) create mode 100644 apps/user_status/lib/ResponseDefinitions.php create mode 100644 apps/user_status/openapi.json diff --git a/apps/user_status/lib/Controller/HeartbeatController.php b/apps/user_status/lib/Controller/HeartbeatController.php index e0b735f044f7b..61b2551aae2b3 100644 --- a/apps/user_status/lib/Controller/HeartbeatController.php +++ b/apps/user_status/lib/Controller/HeartbeatController.php @@ -6,6 +6,7 @@ * @copyright Copyright (c) 2020, Georg Ehrke * * @author Georg Ehrke + * @author Kate Döen * * @license GNU AGPL version 3 or any later version * @@ -23,12 +24,12 @@ * along with this program. If not, see . * */ + namespace OCA\UserStatus\Controller; use OCA\UserStatus\Db\UserStatus; +use OCA\UserStatus\ResponseDefinitions; use OCA\UserStatus\Service\StatusService; -use OCP\AppFramework\Controller; -use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; @@ -53,12 +54,13 @@ class HeartbeatController extends OCSController { /** @var StatusService */ private $service; - public function __construct(string $appName, - IRequest $request, - IEventDispatcher $eventDispatcher, - IUserSession $userSession, - ITimeFactory $timeFactory, - StatusService $service) { + public function __construct( + string $appName, + IRequest $request, + IEventDispatcher $eventDispatcher, + IUserSession $userSession, + ITimeFactory $timeFactory, + StatusService $service) { parent::__construct($appName, $request); $this->eventDispatcher = $eventDispatcher; $this->userSession = $userSession; @@ -69,11 +71,14 @@ public function __construct(string $appName, /** * @NoAdminRequired * - * @param string $status - * @return DataResponse + * @param string $status online, away + * @psalm-import-type PrivateUserStatus from ResponseDefinitions + * @return DataResponse 200 Status successfully updated + * @return DataResponse 204 User has no status to update + * @return DataResponse 400 Invalid status to update */ public function heartbeat(string $status): DataResponse { - if (!\in_array($status, [IUserStatus::ONLINE, IUserStatus::AWAY], true)) { + if (!in_array($status, [IUserStatus::ONLINE, IUserStatus::AWAY], true)) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } diff --git a/apps/user_status/lib/Controller/PredefinedStatusController.php b/apps/user_status/lib/Controller/PredefinedStatusController.php index ea1ff5209b854..db34d029ff068 100644 --- a/apps/user_status/lib/Controller/PredefinedStatusController.php +++ b/apps/user_status/lib/Controller/PredefinedStatusController.php @@ -6,6 +6,7 @@ * @copyright Copyright (c) 2020, Georg Ehrke * * @author Georg Ehrke + * @author Kate Döen * * @license GNU AGPL version 3 or any later version * @@ -23,8 +24,10 @@ * along with this program. If not, see . * */ + namespace OCA\UserStatus\Controller; +use OCA\UserStatus\ResponseDefinitions; use OCA\UserStatus\Service\PredefinedStatusService; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; @@ -47,9 +50,10 @@ class PredefinedStatusController extends OCSController { * @param IRequest $request * @param PredefinedStatusService $predefinedStatusService */ - public function __construct(string $appName, - IRequest $request, - PredefinedStatusService $predefinedStatusService) { + public function __construct( + string $appName, + IRequest $request, + PredefinedStatusService $predefinedStatusService) { parent::__construct($appName, $request); $this->predefinedStatusService = $predefinedStatusService; } @@ -57,9 +61,10 @@ public function __construct(string $appName, /** * @NoAdminRequired * - * @return DataResponse + * @psalm-import-type PredefinedStatus from ResponseDefinitions + * @return DataResponse 200 */ - public function findAll():DataResponse { + public function findAll(): DataResponse { // Filtering out the invisible one, that should only be set by API return new DataResponse(array_filter($this->predefinedStatusService->getDefaultStatuses(), function (array $status) { return !array_key_exists('visible', $status) || $status['visible'] === true; diff --git a/apps/user_status/lib/Controller/StatusesController.php b/apps/user_status/lib/Controller/StatusesController.php index d30389e1716c6..6a100c5b4fa94 100644 --- a/apps/user_status/lib/Controller/StatusesController.php +++ b/apps/user_status/lib/Controller/StatusesController.php @@ -7,6 +7,7 @@ * * @author Christoph Wurst * @author Georg Ehrke + * @author Kate Döen * * @license GNU AGPL version 3 or any later version * @@ -24,9 +25,11 @@ * along with this program. If not, see . * */ + namespace OCA\UserStatus\Controller; use OCA\UserStatus\Db\UserStatus; +use OCA\UserStatus\ResponseDefinitions; use OCA\UserStatus\Service\StatusService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http\DataResponse; @@ -47,9 +50,10 @@ class StatusesController extends OCSController { * @param IRequest $request * @param StatusService $service */ - public function __construct(string $appName, - IRequest $request, - StatusService $service) { + public function __construct( + string $appName, + IRequest $request, + StatusService $service) { parent::__construct($appName, $request); $this->service = $service; } @@ -59,7 +63,8 @@ public function __construct(string $appName, * * @param int|null $limit * @param int|null $offset - * @return DataResponse + * @psalm-import-type PublicUserStatus from ResponseDefinitions + * @return DataResponse 200 */ public function findAll(?int $limit = null, ?int $offset = null): DataResponse { $allStatuses = $this->service->findAll($limit, $offset); @@ -73,7 +78,8 @@ public function findAll(?int $limit = null, ?int $offset = null): DataResponse { * @NoAdminRequired * * @param string $userId - * @return DataResponse + * @psalm-import-type PublicUserStatus from ResponseDefinitions + * @return DataResponse 200 * @throws OCSNotFoundException */ public function find(string $userId): DataResponse { @@ -88,7 +94,7 @@ public function find(string $userId): DataResponse { /** * @param UserStatus $status - * @return array{userId: string, message: string, icon: string, clearAt: int, status: string} + * @return array */ private function formatStatus(UserStatus $status): array { $visibleStatus = $status->getStatus(); diff --git a/apps/user_status/lib/Controller/UserStatusController.php b/apps/user_status/lib/Controller/UserStatusController.php index 214dc21f453d0..d58f53040b171 100644 --- a/apps/user_status/lib/Controller/UserStatusController.php +++ b/apps/user_status/lib/Controller/UserStatusController.php @@ -8,6 +8,7 @@ * @author Georg Ehrke * @author Joas Schilling * @author Simon Spannagel + * @author Kate Döen * * @license GNU AGPL version 3 or any later version * @@ -25,6 +26,7 @@ * along with this program. If not, see . * */ + namespace OCA\UserStatus\Controller; use OCA\UserStatus\Db\UserStatus; @@ -33,6 +35,7 @@ use OCA\UserStatus\Exception\InvalidStatusIconException; use OCA\UserStatus\Exception\InvalidStatusTypeException; use OCA\UserStatus\Exception\StatusMessageTooLongException; +use OCA\UserStatus\ResponseDefinitions; use OCA\UserStatus\Service\StatusService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http\DataResponse; @@ -59,14 +62,15 @@ class UserStatusController extends OCSController { * @param string $appName * @param IRequest $request * @param string $userId - * @param ILogger $logger; + * @param ILogger $logger * @param StatusService $service */ - public function __construct(string $appName, - IRequest $request, - string $userId, - ILogger $logger, - StatusService $service) { + public function __construct( + string $appName, + IRequest $request, + string $userId, + ILogger $logger, + StatusService $service) { parent::__construct($appName, $request); $this->userId = $userId; $this->logger = $logger; @@ -76,7 +80,8 @@ public function __construct(string $appName, /** * @NoAdminRequired * - * @return DataResponse + * @psalm-import-type PrivateUserStatus from ResponseDefinitions + * @return DataResponse 200 * @throws OCSNotFoundException */ public function getStatus(): DataResponse { @@ -93,7 +98,8 @@ public function getStatus(): DataResponse { * @NoAdminRequired * * @param string $statusType - * @return DataResponse + * @psalm-import-type PrivateUserStatus from ResponseDefinitions + * @return DataResponse 200 * @throws OCSBadRequestException */ public function setStatus(string $statusType): DataResponse { @@ -113,11 +119,11 @@ public function setStatus(string $statusType): DataResponse { * * @param string $messageId * @param int|null $clearAt - * @return DataResponse + * @psalm-import-type PrivateUserStatus from ResponseDefinitions + * @return DataResponse 200 * @throws OCSBadRequestException */ - public function setPredefinedMessage(string $messageId, - ?int $clearAt): DataResponse { + public function setPredefinedMessage(string $messageId, ?int $clearAt): DataResponse { try { $status = $this->service->setPredefinedMessage($this->userId, $messageId, $clearAt); $this->service->removeBackupUserStatus($this->userId); @@ -137,12 +143,13 @@ public function setPredefinedMessage(string $messageId, * @param string|null $statusIcon * @param string|null $message * @param int|null $clearAt - * @return DataResponse + * @psalm-import-type PrivateUserStatus from ResponseDefinitions + * @return DataResponse 200 * @throws OCSBadRequestException */ public function setCustomMessage(?string $statusIcon, - ?string $message, - ?int $clearAt): DataResponse { + ?string $message, + ?int $clearAt): DataResponse { try { if (($message !== null && $message !== '') || ($clearAt !== null && $clearAt !== 0)) { $status = $this->service->setCustomMessage($this->userId, $statusIcon, $message, $clearAt); @@ -167,7 +174,7 @@ public function setCustomMessage(?string $statusIcon, /** * @NoAdminRequired * - * @return DataResponse + * @return DataResponse 200 */ public function clearStatus(): DataResponse { $this->service->clearStatus($this->userId); @@ -177,7 +184,7 @@ public function clearStatus(): DataResponse { /** * @NoAdminRequired * - * @return DataResponse + * @return DataResponse 200 */ public function clearMessage(): DataResponse { $this->service->clearMessage($this->userId); diff --git a/apps/user_status/lib/ResponseDefinitions.php b/apps/user_status/lib/ResponseDefinitions.php new file mode 100644 index 0000000000000..37ecd6404e2c6 --- /dev/null +++ b/apps/user_status/lib/ResponseDefinitions.php @@ -0,0 +1,62 @@ + + * + * @author Kate Döen + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\UserStatus; + +/** + * @psalm-type ClearAt = array{ + * type: string, + * time: string|int, + * } + * + * @psalm-type PredefinedStatus = array{ + * id: string, + * icon: string, + * message: string, + * clearAt: ClearAt|null, + * visible: bool|null, + * } + * + * @psalm-type PublicUserStatus = array{ + * userId: string, + * message: string|null, + * icon: string|null, + * clearAt: int|ClearAt|null, + * status: string, + * } + * + * @psalm-type PrivateUserStatus = array{ + * clearAt: int|ClearAt|null, + * icon: string|null, + * message: string|null, + * messageId: string|null, + * messageIsPredefined: bool, + * status: string, + * statusIsUserDefined: bool, + * userId: string, + * } + */ +class ResponseDefinitions { +} diff --git a/apps/user_status/openapi.json b/apps/user_status/openapi.json new file mode 100644 index 0000000000000..5d4b26e810e3a --- /dev/null +++ b/apps/user_status/openapi.json @@ -0,0 +1,843 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "User status", + "description": "User status", + "license": { + "name": "agpl" + }, + "version": "1.6.0" + }, + "paths": { + "/ocs/v2.php/apps/user_status/api/v1/statuses": { + "get": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "statuses-find-all", + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer" + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicUserStatus" + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/user_status/api/v1/statuses/{userId}": { + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "get": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "find-statuses", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/PublicUserStatus" + } + } + } + } + } + } + } + }, + "404": { + "description": "", + "content": { + "text/plain": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/user_status/api/v1/user_status": { + "get": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "get-status", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/PrivateUserStatus" + } + } + } + } + } + } + } + }, + "404": { + "description": "", + "content": { + "text/plain": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/user_status/api/v1/user_status/status": { + "put": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "set-status", + "parameters": [ + { + "name": "statusType", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/PrivateUserStatus" + } + } + } + } + } + } + } + }, + "400": { + "description": "", + "content": { + "text/plain": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/user_status/api/v1/user_status/message/predefined": { + "put": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "set-predefined-message", + "parameters": [ + { + "name": "messageId", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "clearAt", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/PrivateUserStatus" + } + } + } + } + } + } + } + }, + "400": { + "description": "", + "content": { + "text/plain": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/user_status/api/v1/user_status/message/custom": { + "put": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "set-custom-message", + "parameters": [ + { + "name": "statusIcon", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "message", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "clearAt", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/PrivateUserStatus" + } + } + } + } + } + } + } + }, + "400": { + "description": "", + "content": { + "text/plain": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/user_status/api/v1/user_status/message": { + "delete": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "clear-message", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/user_status/api/v1/predefined_statuses": { + "get": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "predefined-status-find-all", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PredefinedStatus" + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/user_status/api/v1/heartbeat": { + "put": { + "tags": [ + "user_status" + ], + "description": "", + "operationId": "heartbeat", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "online, away", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Status successfully updated", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/PrivateUserStatus" + } + } + } + } + } + } + } + }, + "204": { + "description": "User has no status to update", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + }, + "400": { + "description": "Invalid status to update", + "content": { + "application/json": { + "schema": { + "required": [ + "ocs" + ], + "type": "object", + "properties": { + "ocs": { + "required": [ + "meta", + "data" + ], + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ClearAt": { + "required": [ + "type", + "time" + ], + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "time": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + } + }, + "OCSMeta": { + "type": "object", + "required": [ + "status", + "statuscode" + ], + "properties": { + "status": { + "type": "string" + }, + "statuscode": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "totalitems": { + "type": "string" + }, + "itemsperpage": { + "type": "string" + } + } + }, + "PredefinedStatus": { + "required": [ + "id", + "icon", + "message" + ], + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "message": { + "type": "string" + }, + "clearAt": { + "$ref": "#/components/schemas/ClearAt" + }, + "visible": { + "type": "boolean" + } + } + }, + "PrivateUserStatus": { + "required": [ + "messageIsPredefined", + "status", + "statusIsUserDefined", + "userId" + ], + "type": "object", + "properties": { + "clearAt": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/ClearAt" + } + ] + }, + "icon": { + "type": "string" + }, + "message": { + "type": "string" + }, + "messageId": { + "type": "string" + }, + "messageIsPredefined": { + "type": "boolean" + }, + "status": { + "type": "string" + }, + "statusIsUserDefined": { + "type": "boolean" + }, + "userId": { + "type": "string" + } + } + }, + "PublicUserStatus": { + "required": [ + "userId", + "status" + ], + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "message": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "clearAt": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/ClearAt" + } + ] + }, + "status": { + "type": "string" + } + } + } + }, + "securitySchemes": { + "basic_auth": { + "type": "http", + "scheme": "basic" + } + } + }, + "security": [ + { + "basic_auth": [] + } + ], + "tags": [ + { + "name": "user_status" + } + ] +} \ No newline at end of file