Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
DavDeviceApiController
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
5 / 5
10
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
 listDevices
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 createDevice
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 deleteDevice
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 resolveAuthUserId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Modules\Dav\Presentation\Api;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Modules\Dav\Domain\Repository\DavDeviceRepositoryInterface;
12use App\Shared\Infrastructure\Http\ApiResponseTrait;
13use Nyholm\Psr7\Factory\Psr17Factory;
14use Psr\Http\Message\ResponseInterface;
15use Psr\Http\Message\ServerRequestInterface;
16use Yiisoft\Session\SessionInterface;
17use Yiisoft\User\CurrentUser;
18
19/**
20 * REST API Controller for DAV Mobile and Desktop Synchronization Devices.
21 *
22 * Exposes endpoints for managing enrolled devices, pairing tokens, and configuration links.
23 *
24 * @package App\Modules\Dav\Presentation\Api
25 */
26final readonly class DavDeviceApiController
27{
28    use ApiResponseTrait;
29
30    /**
31     * DavDeviceApiController constructor.
32     *
33     * @param DavDeviceRepositoryInterface $deviceRepository Device repository.
34     * @param SessionInterface $session User session service.
35     * @param CurrentUser $currentUser Current user identity.
36     * @param Psr17Factory $psr17Factory PSR-17 factory.
37     */
38    public function __construct(
39        private DavDeviceRepositoryInterface $deviceRepository,
40        private SessionInterface $session,
41        private CurrentUser $currentUser,
42        private Psr17Factory $psr17Factory
43    ) {
44    }
45
46    /**
47     * Lists all enrolled devices for the currently authenticated user.
48     *
49     * @param ServerRequestInterface $request HTTP server request.
50     * @return ResponseInterface JSON API response.
51     */
52    public function listDevices(ServerRequestInterface $request): ResponseInterface
53    {
54        $userId = $this->resolveAuthUserId($request);
55        if ($userId <= 0) {
56            return $this->jsonResponse($this->psr17Factory, ['error' => 'Unauthorized'], 401);
57        }
58
59        $baseUrl = $this->resolveBaseUrl($request);
60        $devices = $this->deviceRepository->listByUser($userId);
61
62        $items = [];
63        foreach ($devices as $device) {
64            $items[] = $device->toArray($baseUrl);
65        }
66
67        return $this->jsonResponse($this->psr17Factory, ['data' => $items], 200);
68    }
69
70    /**
71     * Creates a new device pairing enrollment.
72     *
73     * @param ServerRequestInterface $request HTTP server request.
74     * @return ResponseInterface JSON API response.
75     */
76    public function createDevice(ServerRequestInterface $request): ResponseInterface
77    {
78        $userId = $this->resolveAuthUserId($request);
79        if ($userId <= 0) {
80            return $this->jsonResponse($this->psr17Factory, ['error' => 'Unauthorized'], 401);
81        }
82
83        $payload = $this->parseJsonBody($request);
84        $deviceName = (string)($payload['device_name'] ?? '');
85        $deviceType = (string)($payload['device_type'] ?? 'ios');
86
87        $device = $this->deviceRepository->create($userId, $deviceName, $deviceType);
88        $baseUrl = $this->resolveBaseUrl($request);
89
90        return $this->jsonResponse($this->psr17Factory, [
91            'message' => 'Device enrolled successfully.',
92            'data'    => $device->toArray($baseUrl),
93        ], 201);
94    }
95
96    /**
97     * Deletes and revokes an enrolled device.
98     *
99     * @param ServerRequestInterface $request HTTP server request.
100     * @param int $deviceId Device database ID.
101     * @return ResponseInterface JSON API response.
102     */
103    public function deleteDevice(ServerRequestInterface $request, int $deviceId): ResponseInterface
104    {
105        $userId = $this->resolveAuthUserId($request);
106        if ($userId <= 0) {
107            return $this->jsonResponse($this->psr17Factory, ['error' => 'Unauthorized'], 401);
108        }
109
110        $deleted = $this->deviceRepository->delete($deviceId, $userId);
111        if (!$deleted) {
112            return $this->jsonResponse(
113                $this->psr17Factory,
114                ['error' => 'Device not found or not owned by current user.'],
115                404
116            );
117        }
118
119        return $this->jsonResponse($this->psr17Factory, ['message' => 'Device removed successfully.'], 200);
120    }
121
122    /**
123     * Resolves authenticated user ID.
124     */
125    private function resolveAuthUserId(ServerRequestInterface $request): int
126    {
127        return $this->resolveCurrentUserId($request, $this->session, $this->currentUser);
128    }
129}