From 866411a2c170d06ec3160e9331d65a8193ea6166 Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Fri, 1 Dec 2023 10:46:16 +0100 Subject: [PATCH] fix(dav): Make current ooo info time-dependent * If there is an out of office absence info and it happens now -> return data * Else: return no data Signed-off-by: Christoph Wurst --- apps/dav/appinfo/routes.php | 3 +- .../lib/Controller/OutOfOfficeController.php | 39 +++- apps/dav/lib/ResponseDefinitions.php | 17 +- apps/dav/lib/Service/AbsenceService.php | 16 ++ apps/dav/openapi.json | 182 ++++++++++++++++-- lib/private/User/OutOfOfficeData.php | 11 ++ lib/public/User/IOutOfOfficeData.php | 19 +- 7 files changed, 260 insertions(+), 27 deletions(-) diff --git a/apps/dav/appinfo/routes.php b/apps/dav/appinfo/routes.php index a6874aeb57da7..529bd0781fa8b 100644 --- a/apps/dav/appinfo/routes.php +++ b/apps/dav/appinfo/routes.php @@ -33,7 +33,8 @@ ], '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#getCurrentOutOfOfficeData', 'url' => '/api/v1/outOfOffice/{userId}/now', 'verb' => 'GET'], + ['name' => 'out_of_office#getOutOfOffice', '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 ffac1247a6cec..a2e7378f32de1 100644 --- a/apps/dav/lib/Controller/OutOfOfficeController.php +++ b/apps/dav/lib/Controller/OutOfOfficeController.php @@ -27,7 +27,6 @@ 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; @@ -36,18 +35,20 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; +use OCP\IUserManager; use OCP\IUserSession; use OCP\User\IAvailabilityCoordinator; /** * @psalm-import-type DAVOutOfOfficeData from ResponseDefinitions + * @psalm-import-type DAVCurrentOutOfOfficeData from ResponseDefinitions */ class OutOfOfficeController extends OCSController { public function __construct( string $appName, IRequest $request, - private AbsenceMapper $absenceMapper, + private IUserManager $userManager, private ?IUserSession $userSession, private AbsenceService $absenceService, private IAvailabilityCoordinator $coordinator, @@ -59,15 +60,45 @@ public function __construct( * Get the currently configured out-of-office data of a user. * * @param string $userId The user id to get out-of-office data for. - * @return DataResponse|DataResponse + * @return DataResponse|DataResponse * * 200: Out-of-office data * 404: No out-of-office data was found */ #[NoAdminRequired] public function getCurrentOutOfOfficeData(string $userId): DataResponse { + $user = $this->userManager->get($userId); + if ($user === null) { + return new DataResponse(null, Http::STATUS_NOT_FOUND); + } + try { + $data = $this->absenceService->getCurrentAbsence($user); + if ($data === null) { + return new DataResponse(null, Http::STATUS_NOT_FOUND); + } + } catch (DoesNotExistException) { + return new DataResponse(null, Http::STATUS_NOT_FOUND); + } + + return new DataResponse($data->jsonSerialize()); + } + + /** + * Get the configured out-of-office data of a user. + * + * @param string $userId The user id to get out-of-office data for. + * @return DataResponse|DataResponse + * + * 200: Out-of-office data + * 404: No out-of-office data was found + */ + #[NoAdminRequired] + public function getOutOfOffice(string $userId): DataResponse { try { - $data = $this->absenceMapper->findByUserId($userId); + $data = $this->absenceService->getAbsence($userId); + if ($data === null) { + return new DataResponse(null, Http::STATUS_NOT_FOUND); + } } catch (DoesNotExistException) { return new DataResponse(null, Http::STATUS_NOT_FOUND); } diff --git a/apps/dav/lib/ResponseDefinitions.php b/apps/dav/lib/ResponseDefinitions.php index 97bd8e9efe907..e6de3d5a65cbf 100644 --- a/apps/dav/lib/ResponseDefinitions.php +++ b/apps/dav/lib/ResponseDefinitions.php @@ -27,13 +27,24 @@ namespace OCA\DAV; /** - * @psalm-type DAVOutOfOfficeData = array{ + * @psalm-type DAVOutOfOfficeDataCommon = array{ + * userId: string, + * message: string, + * } + * + * @psalm-type DAVOutOfOfficeData = DAVOutOfOfficeDataCommon&array{ * id: int, - * userId: string, * firstDay: string, * lastDay: string, * status: string, - * message: string, + * } + * + * @todo this is a copy of \OCP\User\IOutOfOfficeData + * @psalm-type DAVCurrentOutOfOfficeData = DAVOutOfOfficeDataCommon&array{ + * id: string, + * startDate: int, + * endDate: int, + * shortMessage: string, * } */ class ResponseDefinitions { diff --git a/apps/dav/lib/Service/AbsenceService.php b/apps/dav/lib/Service/AbsenceService.php index 7c0d6eec0824b..3e2a218d52b47 100644 --- a/apps/dav/lib/Service/AbsenceService.php +++ b/apps/dav/lib/Service/AbsenceService.php @@ -145,6 +145,22 @@ public function getAbsence(string $userId): ?Absence { } } + public function getCurrentAbsence(IUser $user): ?IOutOfOfficeData { + try { + $absence = $this->absenceMapper->findByUserId($user->getUID()); + $oooData = $absence->toOutOufOfficeData( + $user, + $this->timezoneService->getUserTimezone($user->getUID()) ?? $this->timezoneService->getDefaultTimezone(), + ); + if ($this->isInEffect($oooData)) { + return $oooData; + } + } catch (DoesNotExistException) { + // Nothing there to process + } + return null; + } + public function isInEffect(IOutOfOfficeData $absence): bool { $now = $this->timeFactory->getTime(); return $absence->getStartDate() <= $now && $absence->getEndDate() >= $now; diff --git a/apps/dav/openapi.json b/apps/dav/openapi.json index a235e3bff1d6b..a0df1cedd16fd 100644 --- a/apps/dav/openapi.json +++ b/apps/dav/openapi.json @@ -42,6 +42,38 @@ } } }, + "CurrentOutOfOfficeData": { + "allOf": [ + { + "$ref": "#/components/schemas/OutOfOfficeDataCommon" + }, + { + "type": "object", + "required": [ + "id", + "startDate", + "endDate", + "shortMessage" + ], + "properties": { + "id": { + "type": "string" + }, + "startDate": { + "type": "integer", + "format": "int64" + }, + "endDate": { + "type": "integer", + "format": "int64" + }, + "shortMessage": { + "type": "string" + } + } + } + ] + }, "OCSMeta": { "type": "object", "required": [ @@ -67,32 +99,46 @@ } }, "OutOfOfficeData": { + "allOf": [ + { + "$ref": "#/components/schemas/OutOfOfficeDataCommon" + }, + { + "type": "object", + "required": [ + "id", + "firstDay", + "lastDay", + "status" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "firstDay": { + "type": "string" + }, + "lastDay": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } + ] + }, + "OutOfOfficeDataCommon": { "type": "object", "required": [ - "id", "userId", - "firstDay", - "lastDay", - "status", "message" ], "properties": { - "id": { - "type": "integer", - "format": "int64" - }, "userId": { "type": "string" }, - "firstDay": { - "type": "string" - }, - "lastDay": { - "type": "string" - }, - "status": { - "type": "string" - }, "message": { "type": "string" } @@ -219,7 +265,7 @@ } } }, - "/ocs/v2.php/apps/dav/api/v1/outOfOffice/{userId}": { + "/ocs/v2.php/apps/dav/api/v1/outOfOffice/{userId}/now": { "get": { "operationId": "out_of_office-get-current-out-of-office-data", "summary": "Get the currently configured out-of-office data of a user.", @@ -255,6 +301,106 @@ } } ], + "responses": { + "200": { + "description": "Out-of-office 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/CurrentOutOfOfficeData" + } + } + } + } + } + } + } + }, + "404": { + "description": "No out-of-office data was found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "nullable": true + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/dav/api/v1/outOfOffice/{userId}": { + "get": { + "operationId": "out_of_office-get-out-of-office", + "summary": "Get the configured out-of-office data of a user.", + "tags": [ + "out_of_office" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "userId", + "in": "path", + "description": "The user id to get out-of-office data for.", + "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": "Out-of-office data", diff --git a/lib/private/User/OutOfOfficeData.php b/lib/private/User/OutOfOfficeData.php index 12b7e03a0ae84..72e42afab6ae6 100644 --- a/lib/private/User/OutOfOfficeData.php +++ b/lib/private/User/OutOfOfficeData.php @@ -60,4 +60,15 @@ public function getShortMessage(): string { public function getMessage(): string { return $this->message; } + + public function jsonSerialize(): array { + return [ + 'id' => $this->getId(), + 'userId' => $this->getUser()->getUID(), + 'startDate' => $this->getStartDate(), + 'endDate' => $this->getEndDate(), + 'shortMessage' => $this->getShortMessage(), + 'message' => $this->getMessage(), + ]; + } } diff --git a/lib/public/User/IOutOfOfficeData.php b/lib/public/User/IOutOfOfficeData.php index 03444449d58f9..31281104382c2 100644 --- a/lib/public/User/IOutOfOfficeData.php +++ b/lib/public/User/IOutOfOfficeData.php @@ -25,14 +25,24 @@ namespace OCP\User; +use JsonSerializable; use OCP\IUser; /** * DTO to hold out-of-office information of a user * + * @psalm-type OutOfOfficeData = array{ + * id: string, + * userId: string, + * startDate: int, + * endDate: int, + * shortMessage: string, + * message: string, + * } + * * @since 28.0.0 */ -interface IOutOfOfficeData { +interface IOutOfOfficeData extends JsonSerializable { /** * Get the unique token assigned to the current out-of-office event * @@ -74,4 +84,11 @@ public function getShortMessage(): string; * @since 28.0.0 */ public function getMessage(): string; + + /** + * @return OutOfOfficeData + * + * @since 28.0.0 + */ + public function jsonSerialize(): array; }