• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LibreSign / libresign / 21498844095

29 Jan 2026 11:44PM UTC coverage: 46.59%. First build
21498844095

Pull #6641

github

web-flow
Merge e7322a308 into f39fc2360
Pull Request #6641: refactor: centralize file status management

66 of 89 new or added lines in 12 files covered. (74.16%)

7897 of 16950 relevant lines covered (46.59%)

5.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.26
/lib/Service/SignRequest/ProgressService.php
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\Service\SignRequest;
10

11
use OCA\Libresign\Db\File as FileEntity;
12
use OCA\Libresign\Db\FileMapper;
13
use OCA\Libresign\Db\SignRequest as SignRequestEntity;
14
use OCA\Libresign\Db\SignRequestMapper;
15
use OCA\Libresign\Enum\FileStatus;
16
use OCA\Libresign\Enum\SignRequestStatus;
17
use OCP\AppFramework\Db\DoesNotExistException;
18
use OCP\ICache;
19
use OCP\ICacheFactory;
20

21
class ProgressService {
22
        private ICache $cache;
23
        public const ERROR_KEY_PREFIX = 'libresign_sign_request_error_';
24
        public const FILE_ERROR_KEY_PREFIX = 'libresign_file_error_';
25
        public const ERROR_CACHE_TTL = 300;
26
        /** @var array<string, array> */
27
        private array $signRequestErrors = [];
28
        /** @var array<string, array> */
29
        private array $fileErrors = [];
30

31
        public function __construct(
32
                private FileMapper $fileMapper,
33
                ICacheFactory $cacheFactory,
34
                private SignRequestMapper $signRequestMapper,
35
                private StatusCacheService $statusCacheService,
36
        ) {
37
                $this->cache = $cacheFactory->createDistributed('libresign_progress');
34✔
38
        }
39

40
        /**
41
         * Poll for status change of a sign request
42
         *
43
         * Waits up to the specified timeout for the status to change by checking cache
44
         *
45
         * @return int The current status (changed or original if timeout reached)
46
         */
47
        public function pollForStatusChange(string $uuid, int $initialStatus, int $timeout = 30, int $intervalSeconds = 1): int {
48
                return $this->pollForStatusChangeInternal($uuid, [], $initialStatus, $timeout, $intervalSeconds);
2✔
49
        }
50

51
        public function pollForStatusOrErrorChange(
52
                FileEntity $file,
53
                SignRequestEntity $signRequest,
54
                int $initialStatus,
55
                int $timeout = 30,
56
                int $intervalSeconds = 1,
57
        ): int {
58
                if ($file->getNodeType() !== 'envelope') {
5✔
59
                        return $this->pollForProgressChange(
1✔
60
                                $file,
1✔
61
                                $signRequest,
1✔
62
                                [$signRequest->getUuid()],
1✔
63
                                $initialStatus,
1✔
64
                                $timeout,
1✔
65
                                $intervalSeconds,
1✔
66
                        );
1✔
67
                }
68

69
                $signRequestUuids = [$signRequest->getUuid()];
4✔
70
                $childSignRequests = $this->signRequestMapper
4✔
71
                        ->getByEnvelopeChildrenAndIdentifyMethod($file->getId(), $signRequest->getId());
4✔
72
                foreach ($childSignRequests as $childSignRequest) {
4✔
73
                        $childUuid = $childSignRequest->getUuid();
1✔
74
                        if ($childUuid !== '') {
1✔
75
                                $signRequestUuids[] = $childUuid;
1✔
76
                        }
77
                }
78

79
                return $this->pollForProgressChange(
4✔
80
                        $file,
4✔
81
                        $signRequest,
4✔
82
                        $signRequestUuids,
4✔
83
                        $initialStatus,
4✔
84
                        $timeout,
4✔
85
                        $intervalSeconds,
4✔
86
                );
4✔
87
        }
88

89
        private function pollForProgressChange(
90
                FileEntity $file,
91
                SignRequestEntity $signRequest,
92
                array $errorUuids,
93
                int $initialStatus,
94
                int $timeout,
95
                int $intervalSeconds,
96
        ): int {
97
                $statusUuid = $file->getUuid();
5✔
98
                $cachedStatus = $this->statusCacheService->getStatus($statusUuid);
5✔
99
                $interval = max(1, $intervalSeconds);
5✔
100
                $initialProgress = $this->getSignRequestProgress($file, $signRequest);
5✔
101
                $initialHash = $this->buildProgressHash($initialProgress);
5✔
102

103
                if ($cachedStatus !== false && $cachedStatus !== null && (int)$cachedStatus !== $initialStatus) {
5✔
104
                        return (int)$cachedStatus;
3✔
105
                }
106

107
                for ($elapsed = 0; $elapsed < $timeout; $elapsed += $interval) {
2✔
108
                        if (!empty($errorUuids) && $this->hasAnySignRequestError($errorUuids)) {
2✔
109
                                return $initialStatus;
1✔
110
                        }
111

112
                        $newCachedStatus = $this->statusCacheService->getStatus($statusUuid);
1✔
113
                        if ($newCachedStatus !== $cachedStatus && $newCachedStatus !== false && $newCachedStatus !== null) {
1✔
NEW
114
                                return (int)$newCachedStatus;
×
115
                        }
116

117
                        $currentProgress = $this->getSignRequestProgress($file, $signRequest);
1✔
118
                        $currentHash = $this->buildProgressHash($currentProgress);
1✔
119

120
                        if ($currentHash !== $initialHash) {
1✔
NEW
121
                                if ($newCachedStatus !== false && $newCachedStatus !== null) {
×
NEW
122
                                        return (int)$newCachedStatus;
×
123
                                }
NEW
124
                                if ($cachedStatus !== false && $cachedStatus !== null) {
×
NEW
125
                                        return (int)$cachedStatus;
×
126
                                }
NEW
127
                                return $initialStatus;
×
128
                        }
129

130
                        if ($intervalSeconds > 0) {
1✔
NEW
131
                                sleep($intervalSeconds);
×
132
                        }
133
                }
134

135
                return $initialStatus;
1✔
136
        }
137

138
        private function pollForStatusChangeInternal(
139
                string $statusUuid,
140
                array $errorUuids,
141
                int $initialStatus,
142
                int $timeout,
143
                int $intervalSeconds,
144
        ): int {
145
                $cachedStatus = $this->statusCacheService->getStatus($statusUuid);
2✔
146
                $interval = max(1, $intervalSeconds);
2✔
147

148
                if ($cachedStatus !== false && $cachedStatus !== null && (int)$cachedStatus !== $initialStatus) {
2✔
NEW
149
                        return (int)$cachedStatus;
×
150
                }
151

152
                for ($elapsed = 0; $elapsed < $timeout; $elapsed += $interval) {
2✔
153
                        if (!empty($errorUuids) && $this->hasAnySignRequestError($errorUuids)) {
2✔
154
                                return $initialStatus;
×
155
                        }
156

157
                        $newCachedStatus = $this->statusCacheService->getStatus($statusUuid);
2✔
158
                        if ($newCachedStatus !== $cachedStatus && $newCachedStatus !== false) {
2✔
159
                                return (int)$newCachedStatus;
1✔
160
                        }
161

162
                        if ($intervalSeconds > 0) {
2✔
163
                                sleep($intervalSeconds);
×
164
                        }
165
                }
166

167
                return $initialStatus;
1✔
168
        }
169

170
        /**
171
         * @param array<string, mixed> $progress
172
         */
173
        private function buildProgressHash(array $progress): string {
174
                if (!empty($progress['files']) && is_array($progress['files'])) {
5✔
175
                        usort($progress['files'], function (array $left, array $right): int {
5✔
176
                                return ($left['id'] ?? 0) <=> ($right['id'] ?? 0);
3✔
177
                        });
5✔
178
                }
179

180
                if (!empty($progress['signers']) && is_array($progress['signers'])) {
5✔
NEW
181
                        usort($progress['signers'], function (array $left, array $right): int {
×
NEW
182
                                return ($left['id'] ?? 0) <=> ($right['id'] ?? 0);
×
NEW
183
                        });
×
184
                }
185

186
                return hash('sha256', json_encode($progress, JSON_UNESCAPED_SLASHES) ?: '');
5✔
187
        }
188

189
        public function setSignRequestError(string $uuid, array $error, int $ttl = self::ERROR_CACHE_TTL): void {
190
                $this->signRequestErrors[$uuid] = $error;
5✔
191
                $this->cache->set(self::ERROR_KEY_PREFIX . $uuid, $error, $ttl);
5✔
192
                $this->storeSignRequestErrorInMetadata($uuid, $error);
5✔
193
        }
194

195
        public function getSignRequestError(string $uuid): ?array {
196
                $error = $this->cache->get(self::ERROR_KEY_PREFIX . $uuid);
9✔
197
                if ($error === false || $error === null) {
9✔
198
                        return $this->signRequestErrors[$uuid]
8✔
199
                                ?? $this->getSignRequestErrorFromMetadata($uuid);
8✔
200
                }
201
                return is_array($error) ? $error : ['message' => (string)$error];
2✔
202
        }
203

204
        public function clearSignRequestError(string $uuid): void {
205
                unset($this->signRequestErrors[$uuid]);
2✔
206
                $this->cache->remove(self::ERROR_KEY_PREFIX . $uuid);
2✔
207
                $this->clearSignRequestErrorInMetadata($uuid);
2✔
208
        }
209

210
        private function hasSignRequestError(string $uuid): bool {
211
                $error = $this->getSignRequestError($uuid);
2✔
212
                return $error !== null;
2✔
213
        }
214

215
        private function hasAnySignRequestError(array $uuids): bool {
216
                foreach ($uuids as $uuid) {
2✔
217
                        if ($uuid !== '' && $this->hasSignRequestError($uuid)) {
2✔
218
                                return true;
1✔
219
                        }
220
                }
221
                return false;
1✔
222
        }
223

224
        public function setFileError(string $uuid, int $fileId, array $error, int $ttl = self::ERROR_CACHE_TTL): void {
225
                $key = $this->buildFileErrorKey($uuid, $fileId);
9✔
226
                $this->fileErrors[$key] = $error;
9✔
227
                $this->cache->set($key, $error, $ttl);
9✔
228
                $this->storeFileErrorInMetadata($uuid, $fileId, $error);
9✔
229
        }
230

231
        public function getFileError(string $uuid, int $fileId): ?array {
232
                $key = $this->buildFileErrorKey($uuid, $fileId);
25✔
233
                $error = $this->cache->get($key);
25✔
234
                if ($error === false || $error === null) {
25✔
235
                        return $this->fileErrors[$key]
25✔
236
                                ?? $this->getFileErrorFromMetadata($uuid, $fileId);
25✔
237
                }
238
                return is_array($error) ? $error : ['message' => (string)$error];
×
239
        }
240

241
        public function clearFileError(string $uuid, int $fileId): void {
242
                $key = $this->buildFileErrorKey($uuid, $fileId);
1✔
243
                unset($this->fileErrors[$key]);
1✔
244
                $this->cache->remove($key);
1✔
245
                $this->clearFileErrorInMetadata($uuid, $fileId);
1✔
246
        }
247

248
        private function buildFileErrorKey(string $uuid, int $fileId): string {
249
                return self::FILE_ERROR_KEY_PREFIX . $uuid . '_' . $fileId;
25✔
250
        }
251

252
        private function storeSignRequestErrorInMetadata(string $uuid, array $error): void {
253
                if ($uuid === '') {
5✔
254
                        return;
×
255
                }
256

257
                try {
258
                        $signRequest = $this->signRequestMapper->getByUuidUncached($uuid);
5✔
259
                } catch (DoesNotExistException) {
×
260
                        return;
×
261
                }
262
                if (!$signRequest instanceof SignRequestEntity) {
5✔
263
                        return;
×
264
                }
265

266
                $metadata = $signRequest->getMetadata() ?? [];
5✔
267
                $metadata['libresign_error'] = $error;
5✔
268
                $signRequest->setMetadata($metadata);
5✔
269
                $this->signRequestMapper->update($signRequest);
5✔
270
        }
271

272
        private function getSignRequestErrorFromMetadata(string $uuid): ?array {
273
                if ($uuid === '') {
6✔
274
                        return null;
×
275
                }
276

277
                try {
278
                        $signRequest = $this->signRequestMapper->getByUuidUncached($uuid);
6✔
279
                } catch (DoesNotExistException) {
1✔
280
                        return null;
1✔
281
                }
282
                if (!$signRequest instanceof SignRequestEntity) {
5✔
283
                        return null;
×
284
                }
285

286
                $metadata = $signRequest->getMetadata() ?? [];
5✔
287
                $error = $metadata['libresign_error'] ?? null;
5✔
288
                return is_array($error) ? $error : null;
5✔
289
        }
290

291
        private function clearSignRequestErrorInMetadata(string $uuid): void {
292
                if ($uuid === '') {
2✔
293
                        return;
×
294
                }
295

296
                try {
297
                        $signRequest = $this->signRequestMapper->getByUuidUncached($uuid);
2✔
298
                } catch (DoesNotExistException) {
×
299
                        return;
×
300
                }
301
                if (!$signRequest instanceof SignRequestEntity) {
2✔
302
                        return;
×
303
                }
304

305
                $metadata = $signRequest->getMetadata() ?? [];
2✔
306
                if (!array_key_exists('libresign_error', $metadata)) {
2✔
307
                        return;
2✔
308
                }
309

310
                unset($metadata['libresign_error']);
×
311
                $signRequest->setMetadata($metadata);
×
312
                $this->signRequestMapper->update($signRequest);
×
313
        }
314

315
        private function storeFileErrorInMetadata(string $uuid, int $fileId, array $error): void {
316
                if ($uuid === '') {
9✔
317
                        return;
×
318
                }
319

320
                try {
321
                        $signRequest = $this->signRequestMapper->getByUuidUncached($uuid);
9✔
322
                } catch (DoesNotExistException) {
×
323
                        return;
×
324
                }
325
                if (!$signRequest instanceof SignRequestEntity) {
9✔
326
                        return;
×
327
                }
328

329
                $metadata = $signRequest->getMetadata() ?? [];
9✔
330
                $fileErrors = $metadata['libresign_file_errors'] ?? [];
9✔
331
                if (!is_array($fileErrors)) {
9✔
332
                        $fileErrors = [];
×
333
                }
334

335
                $fileErrors[$fileId] = $error;
9✔
336
                $metadata['libresign_file_errors'] = $fileErrors;
9✔
337
                $signRequest->setMetadata($metadata);
9✔
338
                $this->signRequestMapper->update($signRequest);
9✔
339
        }
340

341
        private function getFileErrorFromMetadata(string $uuid, int $fileId): ?array {
342
                if ($uuid === '') {
18✔
343
                        return null;
7✔
344
                }
345

346
                try {
347
                        $signRequest = $this->signRequestMapper->getByUuidUncached($uuid);
11✔
348
                } catch (DoesNotExistException) {
1✔
349
                        return null;
1✔
350
                }
351
                if (!$signRequest instanceof SignRequestEntity) {
10✔
352
                        return null;
×
353
                }
354

355
                $metadata = $signRequest->getMetadata() ?? [];
10✔
356
                $fileErrors = $metadata['libresign_file_errors'] ?? null;
10✔
357
                if (!is_array($fileErrors)) {
10✔
358
                        return null;
8✔
359
                }
360

361
                $error = $fileErrors[$fileId] ?? null;
2✔
362
                return is_array($error) ? $error : null;
2✔
363
        }
364

365
        private function clearFileErrorInMetadata(string $uuid, int $fileId): void {
366
                if ($uuid === '') {
1✔
367
                        return;
×
368
                }
369

370
                try {
371
                        $signRequest = $this->signRequestMapper->getByUuidUncached($uuid);
1✔
372
                } catch (DoesNotExistException) {
×
373
                        return;
×
374
                }
375
                if (!$signRequest instanceof SignRequestEntity) {
1✔
376
                        return;
×
377
                }
378

379
                $metadata = $signRequest->getMetadata() ?? [];
1✔
380
                $fileErrors = $metadata['libresign_file_errors'] ?? null;
1✔
381
                if (!is_array($fileErrors) || !array_key_exists($fileId, $fileErrors)) {
1✔
382
                        return;
1✔
383
                }
384

385
                unset($fileErrors[$fileId]);
×
386
                if (empty($fileErrors)) {
×
387
                        unset($metadata['libresign_file_errors']);
×
388
                } else {
389
                        $metadata['libresign_file_errors'] = $fileErrors;
×
390
                }
391
                $signRequest->setMetadata($metadata);
×
392
                $this->signRequestMapper->update($signRequest);
×
393
        }
394

395
        /**
396
         * Get progress for a specific sign request
397
         *
398
         * Returns progress data tailored to the specific sign request,
399
         * not the global file status
400
         *
401
         * @return array<string, mixed> Progress data with structure: {total, signed, pending, files?, signers?}
402
         */
403
        public function getSignRequestProgress(FileEntity $file, SignRequestEntity $signRequest): array {
404
                return match (true) {
405
                        $file->getNodeType() === 'envelope' => $this->getEnvelopeProgressForSignRequest($file, $signRequest),
6✔
406
                        !$file->getParentFileId() => $this->getSingleFileProgressForSignRequest($file, $signRequest),
2✔
407
                        default => $this->getFileProgressForSignRequest($file, $signRequest),
6✔
408
                };
409
        }
410

411
        public function getStatusCodeForSignRequest(FileEntity $file, SignRequestEntity $signRequest): int {
412
                return $this->getSignRequestStatusCode($file, $signRequest);
×
413
        }
414

415
        public function isProgressComplete(array $progress): bool {
416
                $total = (int)($progress['total'] ?? 0);
1✔
417
                if ($total <= 0) {
1✔
418
                        return false;
×
419
                }
420
                $signed = (int)($progress['signed'] ?? 0);
1✔
421
                $pending = (int)($progress['pending'] ?? 0);
1✔
422
                $inProgress = (int)($progress['inProgress'] ?? 0);
1✔
423
                $errors = (int)($progress['errors'] ?? 0);
1✔
424
                return ($signed + $errors) >= $total && $pending <= 0 && $inProgress <= 0;
1✔
425
        }
426

427
        /**
428
         * Get progress for a sign request on a single file
429
         *
430
         * Returns counts relative to the sign request status
431
         *
432
         * @return array<string, mixed>
433
         */
434
        public function getSingleFileProgressForSignRequest(FileEntity $file, SignRequestEntity $signRequest): array {
435
                $statusCode = $this->getSignRequestStatusCode($file, $signRequest);
6✔
436
                $isSigned = $statusCode === FileStatus::SIGNED->value;
6✔
437
                $isInProgress = $statusCode === FileStatus::SIGNING_IN_PROGRESS->value;
6✔
438
                $fileError = $this->getFileError($signRequest->getUuid(), $file->getId());
6✔
439
                $hasError = $fileError !== null;
6✔
440

441
                return [
6✔
442
                        'total' => 1,
6✔
443
                        'signed' => $isSigned ? 1 : 0,
6✔
444
                        'inProgress' => $hasError ? 0 : ($isInProgress ? 1 : 0),
6✔
445
                        'errors' => $hasError ? 1 : 0,
6✔
446
                        'pending' => $hasError || $isSigned || $isInProgress ? 0 : 1,
6✔
447
                        'files' => [
6✔
448
                                array_merge(
6✔
449
                                        [
6✔
450
                                                'id' => $file->getId(),
6✔
451
                                                'name' => $file->getName(),
6✔
452
                                                'status' => $statusCode,
6✔
453
                                                'statusText' => $this->fileMapper->getTextOfStatus($statusCode),
6✔
454
                                        ],
6✔
455
                                        $hasError ? ['error' => $fileError] : []
6✔
456
                                )
6✔
457
                        ],
6✔
458
                ];
6✔
459
        }
460

461
        /**
462
         * Get progress for a sign request on an envelope
463
         *
464
         * Returns progress for all files in the envelope relative to this signer
465
         *
466
         * @return array<string, mixed>
467
         */
468
        public function getEnvelopeProgressForSignRequest(FileEntity $envelope, SignRequestEntity $signRequest): array {
469
                $children = $this->fileMapper->getChildrenFiles($envelope->getId());
7✔
470
                if (empty($children)) {
7✔
471
                        $children = [$envelope];
1✔
472
                }
473

474
                $childSignRequests = $this->signRequestMapper
7✔
475
                        ->getByEnvelopeChildrenAndIdentifyMethod($envelope->getId(), $signRequest->getId());
7✔
476
                $childSignRequestsByFileId = [];
7✔
477
                foreach ($childSignRequests as $childSignRequest) {
7✔
478
                        $childSignRequestsByFileId[$childSignRequest->getFileId()] = $childSignRequest;
1✔
479
                }
480

481
                $files = array_map(function (FileEntity $child) use ($signRequest, $childSignRequestsByFileId): array {
7✔
482
                        $childSignRequest = $childSignRequestsByFileId[$child->getId()] ?? null;
7✔
483
                        return $this->mapSignRequestFileProgressWithContext($child, $signRequest, $childSignRequest);
7✔
484
                }, $children);
7✔
485
                $total = count($files);
7✔
486
                $signed = count(array_filter($files, fn (array $file) => $file['status'] === FileStatus::SIGNED->value));
7✔
487
                $inProgress = count(array_filter($files, fn (array $file) => $file['status'] === FileStatus::SIGNING_IN_PROGRESS->value));
7✔
488
                $errors = count(array_filter($files, fn (array $file) => !empty($file['error'])));
7✔
489
                $pending = max(0, $total - $signed - $inProgress - $errors);
7✔
490

491
                return [
7✔
492
                        'total' => $total,
7✔
493
                        'signed' => $signed,
7✔
494
                        'inProgress' => $inProgress,
7✔
495
                        'errors' => $errors,
7✔
496
                        'pending' => $pending,
7✔
497
                        'files' => $files,
7✔
498
                ];
7✔
499
        }
500

501
        /**
502
         * Get progress for a sign request on a child file in an envelope
503
         *
504
         * @return array<string, mixed>
505
         */
506
        public function getFileProgressForSignRequest(FileEntity $file, SignRequestEntity $signRequest): array {
507
                $statusCode = $this->getSignRequestStatusCode($file, $signRequest);
1✔
508
                $isSigned = $statusCode === FileStatus::SIGNED->value;
1✔
509
                $isInProgress = $statusCode === FileStatus::SIGNING_IN_PROGRESS->value;
1✔
510
                $fileError = $this->getFileError($signRequest->getUuid(), $file->getId());
1✔
511
                $hasError = $fileError !== null;
1✔
512

513
                return [
1✔
514
                        'total' => 1,
1✔
515
                        'signed' => $isSigned ? 1 : 0,
1✔
516
                        'inProgress' => $hasError ? 0 : ($isInProgress ? 1 : 0),
1✔
517
                        'errors' => $hasError ? 1 : 0,
1✔
518
                        'pending' => $hasError || $isSigned || $isInProgress ? 0 : 1,
1✔
519
                        'signers' => [
1✔
520
                                [
1✔
521
                                        'id' => $signRequest->getId(),
1✔
522
                                        'displayName' => $signRequest->getDisplayName(),
1✔
523
                                        'signed' => $signRequest->getSigned()?->format('c'),
1✔
524
                                        'status' => $statusCode,
1✔
525
                                ]
1✔
526
                        ],
1✔
527
                ];
1✔
528
        }
529

530
        /**
531
         * Map file progress data
532
         *
533
         * @return array<string, mixed>
534
         */
535
        private function mapFileProgress(FileEntity $file): array {
536
                return [
×
537
                        'id' => $file->getId(),
×
538
                        'name' => $file->getName(),
×
539
                        'status' => $file->getStatus(),
×
540
                        'statusText' => $this->fileMapper->getTextOfStatus($file->getStatus()),
×
541
                ];
×
542
        }
543

544
        private function mapSignRequestFileProgress(FileEntity $file, SignRequestEntity $signRequest): array {
545
                $statusCode = $this->getSignRequestStatusCode($file, $signRequest);
2✔
546
                $error = $this->getFileError($signRequest->getUuid(), $file->getId());
2✔
547

548
                $mapped = [
2✔
549
                        'id' => $file->getId(),
2✔
550
                        'name' => $file->getName(),
2✔
551
                        'status' => $statusCode,
2✔
552
                        'statusText' => $this->fileMapper->getTextOfStatus($statusCode),
2✔
553
                ];
2✔
554

555
                if ($error !== null) {
2✔
556
                        $mapped['error'] = $error;
1✔
557
                }
558

559
                return $mapped;
2✔
560
        }
561

562
        private function mapSignRequestFileProgressWithContext(FileEntity $file, SignRequestEntity $defaultSignRequest, ?SignRequestEntity $childSignRequest): array {
563
                $effectiveSignRequest = $childSignRequest ?? $defaultSignRequest;
9✔
564
                $statusCode = $this->getSignRequestStatusCode($file, $effectiveSignRequest);
9✔
565
                $errorUuid = $childSignRequest?->getUuid() ?? $defaultSignRequest->getUuid();
9✔
566
                $error = $this->getFileError($errorUuid, $file->getId());
9✔
567
                if ($error === null) {
9✔
568
                        $error = $this->findFileErrorAcrossSignRequests($file->getId());
7✔
569
                }
570

571
                $mapped = [
9✔
572
                        'id' => $file->getId(),
9✔
573
                        'name' => $file->getName(),
9✔
574
                        'status' => $statusCode,
9✔
575
                        'statusText' => $this->fileMapper->getTextOfStatus($statusCode),
9✔
576
                ];
9✔
577

578
                if ($error !== null) {
9✔
579
                        $mapped['error'] = $error;
3✔
580
                }
581

582
                return $mapped;
9✔
583
        }
584

585
        private function findFileErrorAcrossSignRequests(int $fileId): ?array {
586
                $signRequests = $this->signRequestMapper->getByFileId($fileId);
7✔
587
                foreach ($signRequests as $signRequest) {
7✔
588
                        $error = $this->getFileError($signRequest->getUuid(), $fileId);
×
589
                        if ($error !== null) {
×
590
                                return $error;
×
591
                        }
592
                }
593
                return null;
7✔
594
        }
595

596
        private function getSignRequestStatusCode(FileEntity $file, SignRequestEntity $signRequest): int {
597
                if ($signRequest->getSigned() !== null) {
18✔
598
                        return FileStatus::SIGNED->value;
2✔
599
                }
600

601
                if ($file->getStatus() === FileStatus::SIGNING_IN_PROGRESS->value) {
16✔
602
                        return FileStatus::SIGNING_IN_PROGRESS->value;
3✔
603
                }
604

605
                return match ($signRequest->getStatusEnum()) {
14✔
606
                        SignRequestStatus::DRAFT => FileStatus::DRAFT->value,
14✔
607
                        SignRequestStatus::ABLE_TO_SIGN => FileStatus::ABLE_TO_SIGN->value,
×
608
                        default => $file->getStatus(),
14✔
609
                };
14✔
610
        }
611
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc