Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
68.29% |
28 / 41 |
|
66.67% |
4 / 6 |
CRAP | |
0.00% |
0 / 1 |
| NotificationAttemptMapper | |
68.29% |
28 / 41 |
|
66.67% |
4 / 6 |
10.04 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| begin | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
| finish | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
2.00 | |||
| findForDelivery | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
| finishInterrupted | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
| deleteOlderThan | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | /** |
| 5 | * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors |
| 6 | * SPDX-License-Identifier: AGPL-3.0-or-later |
| 7 | */ |
| 8 | |
| 9 | namespace OCA\Libresign\Db; |
| 10 | |
| 11 | use OCA\Libresign\Enum\NotificationDeliveryState; |
| 12 | use OCP\AppFramework\Db\QBMapper; |
| 13 | use OCP\DB\QueryBuilder\IQueryBuilder; |
| 14 | use OCP\IDBConnection; |
| 15 | |
| 16 | /** @template-extends QBMapper<NotificationAttempt> */ |
| 17 | class NotificationAttemptMapper extends QBMapper { |
| 18 | public function __construct(IDBConnection $db) { |
| 19 | parent::__construct($db, 'libresign_notification_attempt', NotificationAttempt::class); |
| 20 | } |
| 21 | |
| 22 | public function begin(int $deliveryId): NotificationAttempt { |
| 23 | $attempt = new NotificationAttempt(); |
| 24 | $attempt->setDeliveryId($deliveryId); |
| 25 | $attempt->setState(NotificationDeliveryState::SENDING->value); |
| 26 | $attempt->setStartedAt(new \DateTime('now', new \DateTimeZone('UTC'))); |
| 27 | return $this->insert($attempt); |
| 28 | } |
| 29 | |
| 30 | public function finish(int $attemptId, NotificationDeliveryState $state, ?string $reasonCode): void { |
| 31 | $qb = $this->db->getQueryBuilder(); |
| 32 | $updated = $qb->update($this->getTableName()) |
| 33 | ->set('state', $qb->createNamedParameter($state->value)) |
| 34 | ->set('reason_code', $qb->createNamedParameter($reasonCode)) |
| 35 | ->set('finished_at', $qb->createNamedParameter(new \DateTime('now', new \DateTimeZone('UTC')), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
| 36 | ->where($qb->expr()->eq('id', $qb->createNamedParameter($attemptId, IQueryBuilder::PARAM_INT))) |
| 37 | ->andWhere($qb->expr()->eq('state', $qb->createNamedParameter(NotificationDeliveryState::SENDING->value))) |
| 38 | ->executeStatement(); |
| 39 | if ($updated !== 1) { |
| 40 | throw new \LogicException('Notification attempt state changed during dispatch'); |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | /** @return list<NotificationAttempt> */ |
| 45 | public function findForDelivery(int $deliveryId): array { |
| 46 | $qb = $this->db->getQueryBuilder(); |
| 47 | $qb->select('*')->from($this->getTableName()) |
| 48 | ->where($qb->expr()->eq('delivery_id', $qb->createNamedParameter($deliveryId, IQueryBuilder::PARAM_INT))) |
| 49 | ->orderBy('started_at', 'ASC'); |
| 50 | return $this->findEntities($qb); |
| 51 | } |
| 52 | |
| 53 | /** Close interrupted submissions without marking them as safe to retry. */ |
| 54 | public function finishInterrupted(int $deliveryId): int { |
| 55 | $qb = $this->db->getQueryBuilder(); |
| 56 | return $qb->update($this->getTableName()) |
| 57 | ->set('state', $qb->createNamedParameter(NotificationDeliveryState::UNKNOWN->value)) |
| 58 | ->set('reason_code', $qb->createNamedParameter('submission_interrupted')) |
| 59 | ->set('finished_at', $qb->createNamedParameter(new \DateTime('now', new \DateTimeZone('UTC')), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
| 60 | ->where($qb->expr()->eq('delivery_id', $qb->createNamedParameter($deliveryId, IQueryBuilder::PARAM_INT))) |
| 61 | ->andWhere($qb->expr()->eq('state', $qb->createNamedParameter(NotificationDeliveryState::SENDING->value))) |
| 62 | ->executeStatement(); |
| 63 | } |
| 64 | |
| 65 | public function deleteOlderThan(\DateTime $before, int $limit = 500): int { |
| 66 | $qb = $this->db->getQueryBuilder(); |
| 67 | $rows = $qb->select('id')->from($this->getTableName()) |
| 68 | ->where($qb->expr()->lt('finished_at', $qb->createNamedParameter($before, IQueryBuilder::PARAM_DATETIME_MUTABLE))) |
| 69 | ->setMaxResults($limit) |
| 70 | ->executeQuery()->fetchAllAssociative(); |
| 71 | if ($rows === []) { |
| 72 | return 0; |
| 73 | } |
| 74 | $ids = array_map(static fn (array $row): int => (int)$row['id'], $rows); |
| 75 | $delete = $this->db->getQueryBuilder(); |
| 76 | return $delete->delete($this->getTableName()) |
| 77 | ->where($delete->expr()->in('id', $delete->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
| 78 | ->executeStatement(); |
| 79 | } |
| 80 | } |