Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.30% covered (success)
96.30%
26 / 27
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
NotificationDeliveryStatusService
96.30% covered (success)
96.30%
26 / 27
50.00% covered (danger)
50.00%
1 / 2
12
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 status
96.15% covered (success)
96.15%
25 / 26
0.00% covered (danger)
0.00%
0 / 1
11
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service\Notification;
10
11use OCA\Libresign\Db\NotificationDeliveryMapper;
12use OCA\Libresign\Db\SignRequestMapper;
13use OCA\Libresign\Enum\NotificationDeliveryState;
14use OCA\Libresign\Exception\LibresignException;
15use OCA\Libresign\Service\Validation\FileInputValidator;
16use OCA\Libresign\Service\Validation\SigningRequestValidator;
17use OCP\IUserSession;
18
19/**
20 * Read the state of one manual notification event.
21 *
22 * No recipient, message body, event key or provider error is returned.
23 * Authorization is checked before any delivery record is read.
24 */
25class NotificationDeliveryStatusService {
26    public function __construct(
27        private SigningRequestValidator $validator,
28        private FileInputValidator $fileInputValidator,
29        private IUserSession $userSession,
30        private SignRequestMapper $signRequests,
31        private NotificationDeliveryMapper $deliveries,
32        private NotificationDeliveryKeyFactory $keys,
33    ) {
34    }
35
36    /** @return array{status: string, complete: bool, channels: list<array{channel: string, state: string}>} */
37    public function status(int $fileId, int $signRequestId, string $requestId): array {
38        $this->validator->canRequestSign($this->userSession->getUser());
39        $this->fileInputValidator->validateLibreSignFileId($fileId);
40        $this->validator->iRequestedSignThisFile($this->userSession->getUser(), $fileId);
41        $this->signRequests->getByFileIdAndSignRequestId($fileId, $signRequestId);
42        if (!preg_match('/\A[a-f0-9]{32}\z/D', $requestId)) {
43            throw new LibresignException('Invalid notification request ID');
44        }
45
46        $correlationId = $this->keys->correlationId('manual:' . $requestId, 'sign_request', $fileId, $signRequestId);
47        $channels = [];
48        $processing = false;
49        $attention = false;
50        foreach ($this->deliveries->findForEvent($correlationId) as $delivery) {
51            if ($delivery->getFileId() !== $fileId
52                || $delivery->getSignRequestId() !== $signRequestId
53                || $delivery->getEventType() !== 'sign_request') {
54                continue;
55            }
56            $state = $delivery->getState();
57            $channels[] = ['channel' => $delivery->getChannel(), 'state' => $state];
58            if (in_array($state, [NotificationDeliveryState::PENDING->value, NotificationDeliveryState::SENDING->value], true)) {
59                $processing = true;
60            } elseif ($state !== NotificationDeliveryState::ACCEPTED->value) {
61                $attention = true;
62            }
63        }
64
65        return [
66            'status' => $processing ? 'processing' : ($attention ? 'attention_required' : ($channels === [] ? 'not_found' : 'completed')),
67            'complete' => !$processing,
68            'channels' => $channels,
69        ];
70    }
71}