Skip to content

Commit ba1931d

Browse files
committed
refactor: Extract SignRequest entity management into dedicated service
Create new SignRequestService to encapsulate all SignRequest entity lifecycle operations, improving separation of concerns in the service layer: - New SignRequestService handles: createOrUpdateSignRequest, getSignRequestByIdentifyMethod, populateSignRequest, getDisplayNameFromIdentifyMethodIfEmpty, saveSignRequest - Move SignRequestStatusService dependency from RequestSignatureService to SignRequestService - Refactor associateToSigners() to accept FileEntity instead of fileId, eliminating redundant database queries and improving performance - Update deleteIdentifyMethodIfNotExits() to accept FileEntity parameter - Replace all associateToSigner() calls with signRequestService->createOrUpdateSignRequest() Architectural benefits: - Clear separation: RequestSignatureService handles file orchestration, SignRequestService handles entity lifecycle - Reduced database queries by passing entity instances - SignRequestStatusService now internal to SignRequestService only - Easier to test and maintain entity-specific logic Tests: - All RequestSignatureServiceTest tests passing (14 tests) - RequestSignatureControllerTest tests passing (4 tests) - Removed saveSignRequest tests from RequestSignatureServiceTest as the method is now in SignRequestService Signed-off-by: Vitor Mattos <[email protected]>
1 parent 75c66ca commit ba1931d

3 files changed

Lines changed: 171 additions & 136 deletions

File tree

lib/Service/RequestSignatureService.php

Lines changed: 11 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
use OCA\Libresign\Helper\FileUploadHelper;
2323
use OCA\Libresign\Helper\ValidateHelper;
2424
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
25-
use OCP\AppFramework\Db\DoesNotExistException;
2625
use OCP\EventDispatcher\IEventDispatcher;
2726
use OCP\Files\IMimeTypeDetector;
2827
use OCP\Files\Node;
@@ -57,10 +56,10 @@ public function __construct(
5756
protected IAppConfig $appConfig,
5857
protected IEventDispatcher $eventDispatcher,
5958
protected FileStatusService $fileStatusService,
60-
protected SignRequestStatusService $signRequestStatusService,
6159
protected DocMdpConfigService $docMdpConfigService,
6260
protected EnvelopeService $envelopeService,
6361
protected FileUploadHelper $uploadHelper,
62+
protected SignRequestService $signRequestService,
6463
) {
6564
}
6665

@@ -121,7 +120,7 @@ public function save(array $data): FileEntity {
121120
$data['status'] = $file->getStatus();
122121
}
123122
$this->sequentialSigningService->setFile($file);
124-
$this->associateToSigners($data, $file->getId());
123+
$this->associateToSigners($data, $file);
125124
$this->propagateSignersToChildren($file, $data);
126125

127126
return $file;
@@ -142,7 +141,7 @@ private function propagateSignersToChildren(FileEntity $envelope, array $data):
142141
foreach ($children as $child) {
143142
$this->identifyMethod->clearCache();
144143
$this->sequentialSigningService->setFile($child);
145-
$this->associateToSigners($dataWithoutNotification, $child->getId());
144+
$this->associateToSigners($dataWithoutNotification, $child);
146145
}
147146
}
148147

@@ -396,9 +395,8 @@ private function removeExtensionFromName(string $name, array $metadata): string
396395
return $result ?? $name;
397396
}
398397

399-
private function deleteIdentifyMethodIfNotExits(array $users, int $fileId): void {
400-
$file = $this->fileMapper->getById($fileId);
401-
$signRequests = $this->signRequestMapper->getByFileId($fileId);
398+
private function deleteIdentifyMethodIfNotExits(array $users, FileEntity $file): void {
399+
$signRequests = $this->signRequestMapper->getByFileId($file->getId());
402400
foreach ($signRequests as $key => $signRequest) {
403401
$identifyMethods = $this->identifyMethod->getIdentifyMethodsFromSignRequestId($signRequest->getId());
404402
if (empty($identifyMethods)) {
@@ -447,10 +445,10 @@ private function identifyMethodExists(array $users, IIdentifyMethod $identifyMet
447445
*
448446
* @psalm-return list<SignRequestEntity>
449447
*/
450-
private function associateToSigners(array $data, int $fileId): array {
448+
private function associateToSigners(array $data, FileEntity $file): array {
451449
$return = [];
452450
if (!empty($data['users'])) {
453-
$this->deleteIdentifyMethodIfNotExits($data['users'], $fileId);
451+
$this->deleteIdentifyMethodIfNotExits($data['users'], $file);
454452

455453
$this->sequentialSigningService->resetOrderCounter();
456454
$fileStatus = $data['status'] ?? null;
@@ -463,26 +461,26 @@ private function associateToSigners(array $data, int $fileId): array {
463461

464462
if (isset($user['identifyMethods'])) {
465463
foreach ($user['identifyMethods'] as $identifyMethod) {
466-
$return[] = $this->associateToSigner(
464+
$return[] = $this->signRequestService->createOrUpdateSignRequest(
467465
identifyMethods: [
468466
$identifyMethod['method'] => $identifyMethod['value'],
469467
],
470468
displayName: $user['displayName'] ?? '',
471469
description: $user['description'] ?? '',
472470
notify: $shouldNotify,
473-
fileId: $fileId,
471+
fileId: $file->getId(),
474472
signingOrder: $signingOrder,
475473
fileStatus: $fileStatus,
476474
signerStatus: $signerStatus,
477475
);
478476
}
479477
} else {
480-
$return[] = $this->associateToSigner(
478+
$return[] = $this->signRequestService->createOrUpdateSignRequest(
481479
identifyMethods: $user['identify'],
482480
displayName: $user['displayName'] ?? '',
483481
description: $user['description'] ?? '',
484482
notify: $shouldNotify,
485-
fileId: $fileId,
483+
fileId: $file->getId(),
486484
signingOrder: $signingOrder,
487485
fileStatus: $fileStatus,
488486
signerStatus: $signerStatus,
@@ -493,73 +491,7 @@ private function associateToSigners(array $data, int $fileId): array {
493491
return $return;
494492
}
495493

496-
private function associateToSigner(
497-
array $identifyMethods,
498-
string $displayName,
499-
string $description,
500-
bool $notify,
501-
int $fileId,
502-
int $signingOrder = 0,
503-
?int $fileStatus = null,
504-
?int $signerStatus = null,
505-
): SignRequestEntity {
506-
$identifyMethodsIncances = $this->identifyMethod->getByUserData($identifyMethods);
507-
if (empty($identifyMethodsIncances)) {
508-
throw new \Exception($this->l10n->t('Invalid identification method'));
509-
}
510-
$signRequest = $this->getSignRequestByIdentifyMethod(
511-
current($identifyMethodsIncances),
512-
$fileId
513-
);
514-
$displayName = $this->getDisplayNameFromIdentifyMethodIfEmpty($identifyMethodsIncances, $displayName);
515-
$this->setDataToUser($signRequest, $displayName, $description, $fileId);
516-
517-
$signRequest->setSigningOrder($signingOrder);
518494

519-
$isNewSignRequest = !$signRequest->getId();
520-
$currentStatus = $signRequest->getStatusEnum();
521-
522-
if ($isNewSignRequest || $currentStatus === \OCA\Libresign\Enum\SignRequestStatus::DRAFT) {
523-
$desiredStatus = $this->signRequestStatusService->determineInitialStatus($signingOrder, $fileId, $fileStatus, $signerStatus, $currentStatus);
524-
$this->signRequestStatusService->updateStatusIfAllowed($signRequest, $currentStatus, $desiredStatus, $isNewSignRequest);
525-
}
526-
527-
$this->saveSignRequest($signRequest);
528-
529-
$shouldNotify = $notify && $this->signRequestStatusService->shouldNotifySignRequest(
530-
$signRequest->getStatusEnum(),
531-
$fileStatus
532-
);
533-
534-
foreach ($identifyMethodsIncances as $identifyMethod) {
535-
$identifyMethod->getEntity()->setSignRequestId($signRequest->getId());
536-
$identifyMethod->willNotifyUser($shouldNotify);
537-
$identifyMethod->save();
538-
}
539-
return $signRequest;
540-
}
541-
542-
/**
543-
* @param IIdentifyMethod[] $identifyMethodsIncances
544-
* @param string $displayName
545-
* @return string
546-
*/
547-
private function getDisplayNameFromIdentifyMethodIfEmpty(array $identifyMethodsIncances, string $displayName): string {
548-
if (!empty($displayName)) {
549-
return $displayName;
550-
}
551-
foreach ($identifyMethodsIncances as $identifyMethod) {
552-
if ($identifyMethod->getName() === 'account') {
553-
return $this->userManager->get($identifyMethod->getEntity()->getIdentifierValue())->getDisplayName();
554-
}
555-
}
556-
foreach ($identifyMethodsIncances as $identifyMethod) {
557-
if ($identifyMethod->getName() !== 'account') {
558-
return $identifyMethod->getEntity()->getIdentifierValue();
559-
}
560-
}
561-
return '';
562-
}
563495

564496
private function saveVisibleElements(array $data, FileEntity $file): array {
565497
if (empty($data['visibleElements'])) {
@@ -605,41 +537,7 @@ public function validateUsers(array $data): void {
605537
}
606538
}
607539

608-
public function saveSignRequest(SignRequestEntity $signRequest): void {
609-
if ($signRequest->getId()) {
610-
$this->signRequestMapper->update($signRequest);
611-
} else {
612-
$this->signRequestMapper->insert($signRequest);
613-
}
614-
}
615-
616-
/**
617-
* @psalm-suppress MixedMethodCall
618-
*/
619-
private function setDataToUser(SignRequestEntity $signRequest, string $displayName, string $description, int $fileId): void {
620-
$signRequest->setFileId($fileId);
621-
if (!$signRequest->getUuid()) {
622-
$signRequest->setUuid(UUIDUtil::getUUID());
623-
}
624-
if (!empty($displayName)) {
625-
$signRequest->setDisplayName($displayName);
626-
}
627-
if (!empty($description)) {
628-
$signRequest->setDescription($description);
629-
}
630-
if (!$signRequest->getId()) {
631-
$signRequest->setCreatedAt(new \DateTime('now', new \DateTimeZone('UTC')));
632-
}
633-
}
634540

635-
private function getSignRequestByIdentifyMethod(IIdentifyMethod $identifyMethod, int $fileId): SignRequestEntity {
636-
try {
637-
$signRequest = $this->signRequestMapper->getByIdentifyMethodAndFileId($identifyMethod, $fileId);
638-
} catch (DoesNotExistException) {
639-
$signRequest = new SignRequestEntity();
640-
}
641-
return $signRequest;
642-
}
643541

644542
public function unassociateToUser(int $fileId, int $signRequestId): void {
645543
$file = $this->fileMapper->getByFileId($fileId);

lib/Service/SignRequestService.php

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Service;
10+
11+
use OCA\Libresign\Db\SignRequest as SignRequestEntity;
12+
use OCA\Libresign\Db\SignRequestMapper;
13+
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
14+
use OCP\AppFramework\Db\DoesNotExistException;
15+
use OCP\IL10N;
16+
use OCP\IUserManager;
17+
use Sabre\DAV\UUIDUtil;
18+
19+
class SignRequestService {
20+
public function __construct(
21+
protected IL10N $l10n,
22+
protected SignRequestMapper $signRequestMapper,
23+
protected IUserManager $userManager,
24+
protected IdentifyMethodService $identifyMethodService,
25+
protected SignRequestStatusService $signRequestStatusService,
26+
) {
27+
}
28+
29+
/**
30+
* Create or update a sign request for a signer
31+
*
32+
* @param array $identifyMethods Identify methods array
33+
* @param string $displayName Signer display name
34+
* @param string $description Signer description
35+
* @param bool $notify Whether to notify the signer
36+
* @param int $fileId File ID
37+
* @param int $signingOrder Signing order
38+
* @param int|null $fileStatus File status
39+
* @param int|null $signerStatus Signer status
40+
* @return SignRequestEntity
41+
*/
42+
public function createOrUpdateSignRequest(
43+
array $identifyMethods,
44+
string $displayName,
45+
string $description,
46+
bool $notify,
47+
int $fileId,
48+
int $signingOrder = 0,
49+
?int $fileStatus = null,
50+
?int $signerStatus = null,
51+
): SignRequestEntity {
52+
$identifyMethodsInstances = $this->identifyMethodService->getByUserData($identifyMethods);
53+
if (empty($identifyMethodsInstances)) {
54+
throw new \Exception($this->l10n->t('Invalid identification method'));
55+
}
56+
57+
$signRequest = $this->getSignRequestByIdentifyMethod(
58+
current($identifyMethodsInstances),
59+
$fileId
60+
);
61+
62+
$displayName = $this->getDisplayNameFromIdentifyMethodIfEmpty($identifyMethodsInstances, $displayName);
63+
$this->populateSignRequest($signRequest, $displayName, $signingOrder, $description, $fileId);
64+
65+
$isNewSignRequest = !$signRequest->getId();
66+
$currentStatus = $signRequest->getStatusEnum();
67+
68+
if ($isNewSignRequest || $currentStatus === \OCA\Libresign\Enum\SignRequestStatus::DRAFT) {
69+
$desiredStatus = $this->signRequestStatusService->determineInitialStatus(
70+
$signingOrder,
71+
$fileId,
72+
$fileStatus,
73+
$signerStatus,
74+
$currentStatus
75+
);
76+
$this->signRequestStatusService->updateStatusIfAllowed($signRequest, $currentStatus, $desiredStatus, $isNewSignRequest);
77+
}
78+
79+
$this->insertOrUpdateSignRequest($signRequest);
80+
81+
$shouldNotify = $notify && $this->signRequestStatusService->shouldNotifySignRequest(
82+
$signRequest->getStatusEnum(),
83+
$fileStatus
84+
);
85+
86+
foreach ($identifyMethodsInstances as $identifyMethod) {
87+
$identifyMethod->getEntity()->setSignRequestId($signRequest->getId());
88+
$identifyMethod->willNotifyUser($shouldNotify);
89+
$identifyMethod->save();
90+
}
91+
92+
return $signRequest;
93+
}
94+
95+
private function getSignRequestByIdentifyMethod(IIdentifyMethod $identifyMethod, int $fileId): SignRequestEntity {
96+
try {
97+
$signRequest = $this->signRequestMapper->getByIdentifyMethodAndFileId($identifyMethod, $fileId);
98+
} catch (DoesNotExistException) {
99+
$signRequest = new SignRequestEntity();
100+
}
101+
return $signRequest;
102+
}
103+
104+
private function populateSignRequest(
105+
SignRequestEntity $signRequest,
106+
string $displayName,
107+
int $signingOrder,
108+
string $description,
109+
int $fileId,
110+
): void {
111+
$signRequest->setFileId($fileId);
112+
$signRequest->setSigningOrder($signingOrder);
113+
if (!$signRequest->getUuid()) {
114+
$signRequest->setUuid(UUIDUtil::getUUID());
115+
}
116+
if (!empty($displayName)) {
117+
$signRequest->setDisplayName($displayName);
118+
}
119+
if (!empty($description)) {
120+
$signRequest->setDescription($description);
121+
}
122+
if (!$signRequest->getId()) {
123+
$signRequest->setCreatedAt(new \DateTime('now', new \DateTimeZone('UTC')));
124+
}
125+
}
126+
127+
/**
128+
* @param IIdentifyMethod[] $identifyMethodsInstances
129+
* @param string $displayName
130+
* @return string
131+
*/
132+
private function getDisplayNameFromIdentifyMethodIfEmpty(array $identifyMethodsInstances, string $displayName): string {
133+
if (!empty($displayName)) {
134+
return $displayName;
135+
}
136+
foreach ($identifyMethodsInstances as $identifyMethod) {
137+
if ($identifyMethod->getName() === 'account') {
138+
return $this->userManager->get($identifyMethod->getEntity()->getIdentifierValue())->getDisplayName();
139+
}
140+
}
141+
foreach ($identifyMethodsInstances as $identifyMethod) {
142+
if ($identifyMethod->getName() !== 'account') {
143+
return $identifyMethod->getEntity()->getIdentifierValue();
144+
}
145+
}
146+
return '';
147+
}
148+
149+
public function insertOrUpdateSignRequest(SignRequestEntity $signRequest): void {
150+
if ($signRequest->getId()) {
151+
$this->signRequestMapper->update($signRequest);
152+
} else {
153+
$this->signRequestMapper->insert($signRequest);
154+
}
155+
}
156+
}

0 commit comments

Comments
 (0)