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

LibreSign / libresign / 27454170133

13 Jun 2026 02:40AM UTC coverage: 56.884%. First build
27454170133

Pull #7740

github

web-flow
Merge 26d3ba702 into 55aab64af
Pull Request #7740: chore: migrate to PHP 8.3

23 of 35 new or added lines in 20 files covered. (65.71%)

10751 of 18900 relevant lines covered (56.88%)

7.01 hits per line

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

82.9
/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
                private ProgressPollDecisionPolicy $pollDecisionPolicy,
37
        ) {
38
                $this->cache = $cacheFactory->createDistributed('libresign_progress');
34✔
39
        }
40

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

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

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

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

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

104
                $initialCachedStatus = $this->pollDecisionPolicy->normalizeCachedStatus($cachedStatus);
5✔
105
                $initialDecision = $this->pollDecisionPolicy->initialStatusFromCache($initialCachedStatus, $initialStatus);
5✔
106
                if ($initialDecision !== null) {
5✔
107
                        return $initialDecision;
3✔
108
                }
109

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

115
                        $newCachedStatus = $this->statusCacheService->getStatus($statusUuid);
1✔
116
                        $newCachedStatusValue = $this->pollDecisionPolicy->normalizeCachedStatus($newCachedStatus);
1✔
117
                        $cacheDecision = $this->pollDecisionPolicy->statusFromCacheChange($initialCachedStatus, $newCachedStatusValue);
1✔
118
                        if ($cacheDecision !== null) {
1✔
119
                                return $cacheDecision;
×
120
                        }
121

122
                        $currentProgress = $this->getSignRequestProgress($file, $signRequest);
1✔
123
                        $currentHash = $this->buildProgressHash($currentProgress);
1✔
124

125
                        if ($currentHash !== $initialHash) {
1✔
126
                                return $this->pollDecisionPolicy->statusFromProgressChange(
×
127
                                        $newCachedStatusValue,
×
128
                                        $initialCachedStatus,
×
129
                                        $initialStatus
×
130
                                );
×
131
                        }
132

133
                        if ($intervalSeconds > 0) {
1✔
134
                                sleep($intervalSeconds);
×
135
                        }
136
                }
137

138
                return $initialStatus;
1✔
139
        }
140

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

151
                if ($cachedStatus !== false && $cachedStatus !== null && (int)$cachedStatus !== $initialStatus) {
2✔
152
                        return (int)$cachedStatus;
×
153
                }
154

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

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

165
                        if ($intervalSeconds > 0) {
2✔
166
                                sleep($intervalSeconds);
×
167
                        }
168
                }
169

170
                return $initialStatus;
1✔
171
        }
172

173
        private function buildProgressHash(array $progress): string {
174
                if (!empty($progress['files']) && is_array($progress['files'])) {
5✔
175
                        usort($progress['files'], fn (array $left, array $right): int => ($left['id'] ?? 0) <=> ($right['id'] ?? 0));
5✔
176
                }
177

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

555
                return $mapped;
2✔
556
        }
557

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

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

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

578
                return $mapped;
9✔
579
        }
580

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

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

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

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