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

LibreSign / libresign / 21500489755

30 Jan 2026 01:00AM UTC coverage: 46.602%. First build
21500489755

Pull #6641

github

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

75 of 104 new or added lines in 13 files covered. (72.12%)

7906 of 16965 relevant lines covered (46.6%)

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
        private function buildProgressHash(array $progress): string {
171
                if (!empty($progress['files']) && is_array($progress['files'])) {
5✔
172
                        usort($progress['files'], function (array $left, array $right): int {
5✔
173
                                return ($left['id'] ?? 0) <=> ($right['id'] ?? 0);
3✔
174
                        });
5✔
175
                }
176

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

183
                return hash('sha256', json_encode($progress, JSON_UNESCAPED_SLASHES) ?: '');
5✔
184
        }
185

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

288
        private function clearSignRequestErrorInMetadata(string $uuid): void {
289
                if ($uuid === '') {
2✔
290
                        return;
×
291
                }
292

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

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

307
                unset($metadata['libresign_error']);
×
308
                $signRequest->setMetadata($metadata);
×
309
                $this->signRequestMapper->update($signRequest);
×
310
        }
311

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

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

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

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

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

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

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

358
                $error = $fileErrors[$fileId] ?? null;
2✔
359
                return is_array($error) ? $error : null;
2✔
360
        }
361

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

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

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

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

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

408
        public function getStatusCodeForSignRequest(FileEntity $file, SignRequestEntity $signRequest): int {
409
                return $this->getSignRequestStatusCode($file, $signRequest);
×
410
        }
411

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

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

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

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

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

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

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

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

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

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

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

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

552
                if ($error !== null) {
2✔
553
                        $mapped['error'] = $error;
1✔
554
                }
555

556
                return $mapped;
2✔
557
        }
558

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

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

575
                if ($error !== null) {
9✔
576
                        $mapped['error'] = $error;
3✔
577
                }
578

579
                return $mapped;
9✔
580
        }
581

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

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

598
                if ($file->getStatus() === FileStatus::SIGNING_IN_PROGRESS->value) {
16✔
599
                        return FileStatus::SIGNING_IN_PROGRESS->value;
3✔
600
                }
601

602
                return match ($signRequest->getStatusEnum()) {
14✔
603
                        SignRequestStatus::DRAFT => FileStatus::DRAFT->value,
14✔
604
                        SignRequestStatus::ABLE_TO_SIGN => FileStatus::ABLE_TO_SIGN->value,
×
605
                        default => $file->getStatus(),
14✔
606
                };
14✔
607
        }
608
}
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