Lines 75.80% 47 / 62
Methods 20.00% 1 / 5
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 readResponse 90.32% 28 / 31 0.00% 0 / 1 7.04
 assertTechAdminAccess 83.33% 5 / 6 0.00% 0 / 1 4.07
 resolveDateRange 73.33% 11 / 15 0.00% 0 / 1 5.47
 parseDateParam 0.00% 0 / 7 0.00% 0 / 1 30
 [BO\Zmsbackend\Api\BaseController] __invoke 100.00% 3 / 3 100.00% 1 / 1 1
21class AvailabilityHistoryByScope extends \BO\Zmsbackend\Api\BaseController
22{
23    /**
24     * @SuppressWarnings(Param)
25     */
26    #[\Override]
27    public function readResponse(
28        RequestInterface $request,
29        ResponseInterface $response,
30        array $args
31    ): ResponseInterface {
32        $this->assertTechAdminAccess($request);
33
34        $scope = (new \BO\Zmsbackend\Scope\Service\Scope())->readEntity($args['id'], 0);
35        if (!$scope || !$scope->hasId()) {
36            throw new \BO\Zmsbackend\Scope\Exception\ScopeNotFound();
37        }
38
39        [$from, $to] = $this->resolveDateRange();
40        $availabilityId = Validator::param('availabilityId')->isNumber()->getValue();
41        $action = Validator::param('action')->isString()->getValue();
42        if ($action !== null && $action !== '') {
43            if (
44                !in_array(
45                    $action,
46                    \BO\Zmsentities\AvailabilityHistory::ACTIONS,
47                    true
48                )
49            ) {
50                throw new \BO\Zmsbackend\Exception\BadRequest(
51                    'Parameter action must be one of: created, updated, deleted, dldb_slot_update'
52                );
53            }
54        } else {
55            $action = null;
56        }
57
58        $message = \BO\Zmsbackend\Api\Response\Message::create($request);
59        $message->data = (new AvailabilityHistoryService())->readListByScopeId(
60            (int) $scope->getId(),
61            $from,
62            $to,
63            $availabilityId ? (int) $availabilityId : null,
64            $action
65        );
66        $message->setUpdatedMetaData();
67        // Empty history is a valid list. setUpdatedMetaData() otherwise maps it to
68        // meta.error = "Not found", which zmsadmin/zmsclient turns into HTTP 500.
69        $message->statuscode = 200;
70        $message->meta['error'] = false;
71        $message->meta['message'] = '';
72
73        $response = Render::withLastModified($response, time(), '0');
74        return Render::withJson($response, $message, 200);
75    }
76
77    protected function assertTechAdminAccess(RequestInterface $request): void
78    {
79        new User($request, 1);
80        $useraccount = User::readWorkstation()->getUseraccount();
81        if (!$useraccount->hasId()) {
82            throw new UserAccountMissingLogin();
83        }
84        if (!$useraccount->isSuperUser() && !$useraccount->hasRole('system_admin')) {
85            throw new UserAccountMissingRights();
86        }
87    }
88
89    protected function resolveDateRange(): array
90    {
91        $fromParam = Validator::param('from')->isString()->getValue();
92        $toParam = Validator::param('to')->isString()->getValue();
93
94        // History rows use MySQL CURRENT_TIMESTAMP (wall clock). Do not use App::$now:
95        // ZMS_TIMEADJUST on remote freezes that clock, so GET misses real inserts.
96        $timezone = App::$now instanceof \DateTimeInterface
97            ? App::$now->getTimezone()
98            : new \DateTimeZone('Europe/Berlin');
99        $now = new \DateTimeImmutable('now', $timezone);
100        $to = $toParam
101            ? $this->parseDateParam($toParam, true)
102            : $now->setTime(23, 59, 59);
103        $from = $fromParam
104            ? $this->parseDateParam($fromParam, false)
105            : $to->modify('-' . AvailabilityHistoryService::DEFAULT_RETENTION_DAYS . ' days')->setTime(0, 0, 0);
106
107        if ($from > $to) {
108            throw new \BO\Zmsbackend\Exception\BadRequest('Parameter from must be before or equal to to');
109        }
110
111        return [$from, $to];
112    }
113
114    protected function parseDateParam(string $value, bool $endOfDay): \DateTimeImmutable
115    {
116        $date = \DateTimeImmutable::createFromFormat('Y-m-d', $value);
117        $errors = \DateTimeImmutable::getLastErrors();
118        if (
119            !$date
120            || ($errors['warning_count'] ?? 0) > 0
121            || ($errors['error_count'] ?? 0) > 0
122        ) {
123            throw new \BO\Zmsbackend\Exception\BadRequest('Date parameters must use Y-m-d format');
124        }
125
126        return $endOfDay ? $date->setTime(23, 59, 59) : $date->setTime(0, 0, 0);
127    }
128}

Inherited from BO\Zmsbackend\Api\BaseController

20    public function __invoke(RequestInterface $request, ResponseInterface $response, array $args)
21    {
22        $request = $this->initRequest($request);
23        $noCacheResponse = \BO\Slim\Render::withLastModified($response, time(), '0');
24        return $this->readResponse($request, $noCacheResponse, $args);
25    }