From 7bfeeaedee4873eda6cb534c1cec2ce4910cbb9b Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 1 Dec 2023 09:15:18 +0100 Subject: [PATCH] feat(out-of-office): Add OCS endpoint to set and clear absence Signed-off-by: Joas Schilling --- apps/dav/appinfo/routes.php | 2 + .../lib/Controller/OutOfOfficeController.php | 77 +++++ apps/dav/openapi.json | 271 ++++++++++++++++++ 3 files changed, 350 insertions(+) diff --git a/apps/dav/appinfo/routes.php b/apps/dav/appinfo/routes.php index 1b2fa0094bf..d4a9f4cbeeb 100644 --- a/apps/dav/appinfo/routes.php +++ b/apps/dav/appinfo/routes.php @@ -36,5 +36,7 @@ return [ 'ocs' => [ ['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'], ['name' => 'out_of_office#getCurrentOutOfOfficeData', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'GET'], + ['name' => 'out_of_office#setOutOfOffice', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'POST'], + ['name' => 'out_of_office#clearOutOfOffice', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'DELETE'], ], ]; diff --git a/apps/dav/lib/Controller/OutOfOfficeController.php b/apps/dav/lib/Controller/OutOfOfficeController.php index e86f116c3b1..ffac1247a6c 100644 --- a/apps/dav/lib/Controller/OutOfOfficeController.php +++ b/apps/dav/lib/Controller/OutOfOfficeController.php @@ -26,14 +26,18 @@ declare(strict_types=1); namespace OCA\DAV\Controller; +use DateTimeImmutable; use OCA\DAV\Db\AbsenceMapper; use OCA\DAV\ResponseDefinitions; +use OCA\DAV\Service\AbsenceService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; +use OCP\IUserSession; +use OCP\User\IAvailabilityCoordinator; /** * @psalm-import-type DAVOutOfOfficeData from ResponseDefinitions @@ -44,6 +48,9 @@ class OutOfOfficeController extends OCSController { string $appName, IRequest $request, private AbsenceMapper $absenceMapper, + private ?IUserSession $userSession, + private AbsenceService $absenceService, + private IAvailabilityCoordinator $coordinator, ) { parent::__construct($appName, $request); } @@ -74,4 +81,74 @@ class OutOfOfficeController extends OCSController { 'message' => $data->getMessage(), ]); } + + /** + * Set out-of-office absence + * + * @param string $firstDay First day of the absence in format `YYYY-MM-DD` + * @param string $lastDay Last day of the absence in format `YYYY-MM-DD` + * @param string $status Short text that is set as user status during the absence + * @param string $message Longer multiline message that is shown to others during the absence + * @return DataResponse|DataResponse|DataResponse + * + * 200: Absence data + * 400: When the first day is not before the last day + * 401: When the user is not logged in + */ + #[NoAdminRequired] + public function setOutOfOffice( + string $firstDay, + string $lastDay, + string $status, + string $message, + ): DataResponse { + $user = $this->userSession?->getUser(); + if ($user === null) { + return new DataResponse(null, Http::STATUS_UNAUTHORIZED); + } + + $parsedFirstDay = new DateTimeImmutable($firstDay); + $parsedLastDay = new DateTimeImmutable($lastDay); + if ($parsedFirstDay->getTimestamp() > $parsedLastDay->getTimestamp()) { + return new DataResponse(['error' => 'firstDay'], Http::STATUS_BAD_REQUEST); + } + + $data = $this->absenceService->createOrUpdateAbsence( + $user, + $firstDay, + $lastDay, + $status, + $message, + ); + $this->coordinator->clearCache($user->getUID()); + + return new DataResponse([ + 'id' => $data->getId(), + 'userId' => $data->getUserId(), + 'firstDay' => $data->getFirstDay(), + 'lastDay' => $data->getLastDay(), + 'status' => $data->getStatus(), + 'message' => $data->getMessage(), + ]); + } + + /** + * Clear the out-of-office + * + * @return DataResponse + * + * 200: When the absence was cleared successfully + * 401: When the user is not logged in + */ + #[NoAdminRequired] + public function clearOutOfOffice(): DataResponse { + $user = $this->userSession?->getUser(); + if ($user === null) { + return new DataResponse(null, Http::STATUS_UNAUTHORIZED); + } + + $this->absenceService->clearAbsence($user); + $this->coordinator->clearCache($user->getUID()); + return new DataResponse(null); + } } diff --git a/apps/dav/openapi.json b/apps/dav/openapi.json index c8ae5a96206..a235e3bff1d 100644 --- a/apps/dav/openapi.json +++ b/apps/dav/openapi.json @@ -317,6 +317,277 @@ } } } + }, + "post": { + "operationId": "out_of_office-set-out-of-office", + "summary": "Set out-of-office absence", + "tags": [ + "out_of_office" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "firstDay", + "in": "query", + "description": "First day of the absence in format `YYYY-MM-DD`", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "lastDay", + "in": "query", + "description": "Last day of the absence in format `YYYY-MM-DD`", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "status", + "in": "query", + "description": "Short text that is set as user status during the absence", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "message", + "in": "query", + "description": "Longer multiline message that is shown to others during the absence", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Absence data", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/OutOfOfficeData" + } + } + } + } + } + } + } + }, + "400": { + "description": "When the first day is not before the last day", + "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", + "enum": [ + "firstDay" + ] + } + } + } + } + } + } + } + } + } + }, + "401": { + "description": "When the user is not logged in", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "nullable": true + } + } + } + } + } + } + } + } + } + }, + "delete": { + "operationId": "out_of_office-clear-out-of-office", + "summary": "Clear the out-of-office", + "tags": [ + "out_of_office" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "When the absence was cleared successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "nullable": true + } + } + } + } + } + } + } + }, + "401": { + "description": "When the user is not logged in", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "nullable": true + } + } + } + } + } + } + } + } + } } } }, -- 2.39.5