Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
FileElementController
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
4 / 4
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createVisibleElement
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
1 / 1
3
 updateVisibleElement
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deleteVisibleElement
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Controller;
10
11use OCA\Libresign\AppInfo\Application;
12use OCA\Libresign\Exception\LibresignException;
13use OCA\Libresign\ResponseDefinitions;
14use OCA\Libresign\Service\FileElementService;
15use OCA\Libresign\Service\Validation\FileInputValidator;
16use OCA\Libresign\Service\Validation\SigningRequestValidator;
17use OCA\Libresign\Service\Validation\VisibleElementValidator;
18use OCP\AppFramework\Db\DoesNotExistException;
19use OCP\AppFramework\Http;
20use OCP\AppFramework\Http\Attribute\ApiRoute;
21use OCP\AppFramework\Http\Attribute\NoAdminRequired;
22use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
23use OCP\AppFramework\Http\DataResponse;
24use OCP\IRequest;
25use OCP\IUserSession;
26use Psr\Log\LoggerInterface;
27
28/**
29 * @psalm-import-type LibresignCoordinate from ResponseDefinitions
30 * @psalm-import-type LibresignErrorsResponse from ResponseDefinitions
31 * @psalm-import-type LibresignFileElementIdResponse from ResponseDefinitions
32 */
33class FileElementController extends AEnvironmentAwareController {
34    public function __construct(
35        IRequest $request,
36        private FileElementService $fileElementService,
37        private IUserSession $userSession,
38        private VisibleElementValidator $visibleElementValidator,
39        private SigningRequestValidator $signingRequestValidator,
40        private LoggerInterface $logger,
41    ) {
42        parent::__construct(Application::APP_ID, $request);
43    }
44
45    /**
46     * Create visible element
47     *
48     * Create visible element of a specific file
49     *
50     * @param string $uuid UUID of sign request. The signer UUID is what the person receives via email when asked to sign. This is not the file UUID.
51     * @param integer $signRequestId Id of sign request
52     * @param integer|null $elementId ID of visible element. Each element has an ID that is returned on validation endpoints.
53     * @param integer|null $fileId LibreSign file ID that owns the visible element
54     * @param string $type The type of element to create, sginature, sinitial, date, datetime, text
55     * @param array{} $metadata Metadata of visible elements to associate with the document
56     * @param LibresignCoordinate $coordinates Coortinates of a visible element on PDF
57     * @return DataResponse<Http::STATUS_OK, LibresignFileElementIdResponse, array{}>|DataResponse<Http::STATUS_NOT_FOUND, LibresignErrorsResponse, array{}>
58     *
59     * 200: OK
60     * 404: Failure when create visible element
61     */
62    #[NoAdminRequired]
63    #[NoCSRFRequired]
64    #[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/file-element/{uuid}', requirements: ['apiVersion' => '(v1)'])]
65    public function createVisibleElement(string $uuid, int $signRequestId, ?int $elementId = null, ?int $fileId = null, string $type = '', array $metadata = [], array $coordinates = []): DataResponse {
66        $visibleElement = [
67            'elementId' => $elementId,
68            'type' => $type,
69            'signRequestId' => $signRequestId,
70            'coordinates' => $coordinates,
71            'metadata' => $metadata,
72            'uuid' => $uuid,
73            'fileId' => $fileId,
74        ];
75        try {
76            $this->visibleElementValidator->validateVisibleElement($visibleElement, FileInputValidator::TYPE_VISIBLE_ELEMENT_PDF);
77            $this->signingRequestValidator->validateExistingFile([
78                'uuid' => $uuid,
79                'userManager' => $this->userSession->getUser()
80            ]);
81            $this->visibleElementValidator->validateSignRequestBelongsToFile($signRequestId, $uuid);
82            if ($elementId !== null) {
83                $this->visibleElementValidator->validateAuthenticatedUserIsOwnerOfPdfVisibleElement($elementId, $this->userSession->getUser()->getUID());
84                $this->visibleElementValidator->validateVisibleElementBelongsToFile($elementId, $uuid);
85            }
86            $fileElement = $this->fileElementService->saveVisibleElement($visibleElement);
87            return new DataResponse([
88                'fileElementId' => $fileElement->getId(),
89            ]);
90        } catch (LibresignException|DoesNotExistException $th) {
91            $this->logger->error($th->getMessage());
92            return new DataResponse(
93                [
94                    'errors' => [['message' => $th->getMessage()]],
95                ],
96                Http::STATUS_NOT_FOUND
97            );
98        }
99    }
100
101    /**
102     * Update visible element
103     *
104     * Update visible element of a specific file
105     *
106     * @param string $uuid UUID of sign request. The signer UUID is what the person receives via email when asked to sign. This is not the file UUID.
107     * @param integer $signRequestId Id of sign request
108     * @param integer|null $elementId ID of visible element. Each element has an ID that is returned on validation endpoints.
109     * @param integer|null $fileId LibreSign file ID that owns the visible element
110     * @param string $type The type of element to create, sginature, sinitial, date, datetime, text
111     * @param array{} $metadata Metadata of visible elements to associate with the document
112     * @param LibresignCoordinate $coordinates Coortinates of a visible element on PDF
113     * @return DataResponse<Http::STATUS_OK, LibresignFileElementIdResponse, array{}>|DataResponse<Http::STATUS_NOT_FOUND, LibresignErrorsResponse, array{}>
114     *
115     * 200: OK
116     * 404: Failure when patch visible element
117     */
118    #[NoAdminRequired]
119    #[NoCSRFRequired]
120    #[ApiRoute(verb: 'PATCH', url: '/api/{apiVersion}/file-element/{uuid}/{elementId}', requirements: ['apiVersion' => '(v1)'])]
121    public function updateVisibleElement(string $uuid, int $signRequestId, ?int $elementId = null, ?int $fileId = null, string $type = '', array $metadata = [], array $coordinates = []): DataResponse {
122        return $this->createVisibleElement($uuid, $signRequestId, $elementId, $fileId, $type, $metadata, $coordinates);
123    }
124
125    /**
126     * Delete visible element
127     *
128     * Delete visible element of a specific file
129     *
130     * @param string $uuid UUID of sign request. The signer UUID is what the person receives via email when asked to sign. This is not the file UUID.
131     * @param integer $elementId ID of visible element. Each element has an ID that is returned on validation endpoints.
132     * @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_NOT_FOUND, LibresignErrorsResponse, array{}>
133     *
134     * 200: OK
135     * 404: Failure when delete visible element or file not found
136     */
137    #[NoAdminRequired]
138    #[NoCSRFRequired]
139    #[ApiRoute(verb: 'DELETE', url: '/api/{apiVersion}/file-element/{uuid}/{elementId}', requirements: ['apiVersion' => '(v1)'])]
140    public function deleteVisibleElement(string $uuid, int $elementId): DataResponse {
141        try {
142            $this->signingRequestValidator->validateExistingFile([
143                'uuid' => $uuid,
144                'userManager' => $this->userSession->getUser()
145            ]);
146            $this->visibleElementValidator->validateAuthenticatedUserIsOwnerOfPdfVisibleElement($elementId, $this->userSession->getUser()->getUID());
147            $this->visibleElementValidator->validateVisibleElementBelongsToFile($elementId, $uuid, true);
148            $this->fileElementService->deleteVisibleElement($elementId);
149            return new DataResponse();
150        } catch (LibresignException|DoesNotExistException $th) {
151            $this->logger->error($th->getMessage());
152            return new DataResponse(
153                [
154                    'errors' => [['message' => $th->getMessage()]],
155                ],
156                Http::STATUS_NOT_FOUND
157            );
158        }
159    }
160}