Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
13.95% covered (danger)
13.95%
18 / 129
5.88% covered (danger)
5.88%
1 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
KeyValueService
13.95% covered (danger)
13.95%
18 / 129
5.88% covered (danger)
5.88%
1 / 17
1454.33
0.00% covered (danger)
0.00%
0 / 1
 getPersistence
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 getUserExecutions
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 spawnDeliveryExecution
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
 initDeliveryExecution
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 addDeliveryToUserExecutionList
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
2.02
 getDeliveryExecutionsByStatus
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 getDeliveryExecutionKeyValue
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDeliveryExecution
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 updateDeliveryExecutionStatus
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 update
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getData
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 deleteDeliveryExecutionData
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
42
 setDeliveryExecutions
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 fixStatus
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
90
 exists
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addMetadata
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 keyValuePrefixDecoration
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2016 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 */
21
22namespace oat\taoDelivery\model\execution\implementation;
23
24use common_Exception;
25use common_Logger;
26use common_persistence_KeyValuePersistence;
27use core_kernel_classes_Resource;
28use oat\generis\model\OntologyRdfs;
29use oat\oatbox\service\ConfigurableService;
30use oat\taoDelivery\model\execution\DeliveryExecutionInterface;
31use oat\taoDelivery\model\execution\DeliveryExecutionServiceInterface;
32use oat\taoDelivery\model\execution\implementation\exception\PersistenceException;
33use oat\taoDelivery\model\execution\KVDeliveryExecution;
34use oat\taoDelivery\model\execution\metadata\DeliveryExecutionMetadataAwareService;
35use oat\taoDelivery\model\execution\metadata\Metadata;
36use oat\taoDelivery\model\execution\metadata\MetadataCollection;
37use oat\taoDelivery\model\execution\DeliveryExecution as DeliveryExecutionWrapper;
38use oat\taoDelivery\model\execution\Delete\DeliveryExecutionDeleteRequest;
39
40/**
41 * Service to manage the execution of deliveries
42 *
43 * @access public
44 * @package taoDelivery
45 */
46class KeyValueService extends ConfigurableService implements
47    DeliveryExecutionMetadataAwareService,
48    DeliveryExecutionServiceInterface
49{
50    public const OPTION_PERSISTENCE = 'persistence';
51
52    public const DELIVERY_EXECUTION_PREFIX = 'kve_de_';
53
54    public const USER_EXECUTIONS_PREFIX = 'kve_ue_';
55
56    public const USER_DELIVERY_PREFIX = 'kve_ud_';
57
58    /**
59     * @var common_persistence_KeyValuePersistence
60     */
61    private $persistence;
62
63    /**
64     * @return common_persistence_KeyValuePersistence
65     */
66    protected function getPersistence()
67    {
68        if (is_null($this->persistence)) {
69            $persistenceOption = $this->getOption(self::OPTION_PERSISTENCE);
70            $this->persistence = (is_object($persistenceOption))
71                ? $persistenceOption
72                : $this
73                    ->getServiceLocator()
74                    ->get(\common_persistence_Manager::SERVICE_ID)
75                    ->getPersistenceById($persistenceOption);
76        }
77        return $this->persistence;
78    }
79
80    /**
81     * @param core_kernel_classes_Resource $compiled
82     * @param string $userUri
83     * @return array
84     */
85    public function getUserExecutions(core_kernel_classes_Resource $compiled, $userUri)
86    {
87        $returnValue = [];
88        $data = $this->getPersistence()->get(self::USER_DELIVERY_PREFIX . $userUri . $compiled->getUri());
89        $keys = $data !== false ? json_decode($data, true) : [];
90        if (is_array($keys)) {
91            foreach ($keys as $key) {
92                $returnValue[$key] = $this->getDeliveryExecution($key);
93            }
94        } else {
95            common_Logger::w(
96                'Non array "' . gettype($keys) . '" received as active Execution Keys for user '
97                    . $userUri . ' with delivery' . $compiled->getUri()
98            );
99        }
100
101        return $returnValue;
102    }
103
104    /**
105     * Spawn a new Delivery Execution
106     *
107     * @param string $label
108     * @param string $deliveryId
109     * @param string $userId
110     * @param string $status
111     * @param string| null $deliveryExecutionId
112     * @return \oat\taoDelivery\model\execution\DeliveryExecution
113     */
114    public function spawnDeliveryExecution($label, $deliveryId, $userId, $status, $deliveryExecutionId = null)
115    {
116        $deliveryExecutionId = self::DELIVERY_EXECUTION_PREFIX . ($deliveryExecutionId ?: \common_Utils::getNewUri());
117        $data = [
118            OntologyRdfs::RDFS_LABEL => $label,
119            DeliveryExecutionInterface::PROPERTY_DELIVERY => $deliveryId,
120            DeliveryExecutionInterface::PROPERTY_SUBJECT => $userId,
121            DeliveryExecutionInterface::PROPERTY_TIME_START => microtime(),
122            DeliveryExecutionInterface::PROPERTY_STATUS => $status,
123            DeliveryExecutionInterface::PROPERTY_METADATA => new MetadataCollection(),
124        ];
125        $kvDe = new KVDeliveryExecution($this, $deliveryExecutionId, $data);
126        $this->updateDeliveryExecutionStatus($kvDe, null, $status);
127        $this->addDeliveryToUserExecutionList($userId, $deliveryId, $kvDe->getIdentifier());
128        return $this->propagate(new DeliveryExecutionWrapper($kvDe));
129    }
130
131    /**
132     * Generate a new delivery execution
133     * @deprecated
134     * @param core_kernel_classes_Resource $assembly
135     * @param string $userId
136     * @return core_kernel_classes_Resource the delivery execution
137     */
138    public function initDeliveryExecution(core_kernel_classes_Resource $assembly, $userId)
139    {
140        common_Logger::w('Call to deprecated function ' . __FUNCTION__);
141        return $this->spawnDeliveryExecution(
142            $assembly->getLabel(),
143            $assembly->getUri(),
144            $userId,
145            KvDeliveryExecution::STATE_ACTIVE
146        );
147    }
148
149    /**
150     * @param $userId
151     * @param $assemblyId
152     * @param $executionId
153     */
154    private function addDeliveryToUserExecutionList($userId, $assemblyId, $executionId)
155    {
156        $uid = self::USER_DELIVERY_PREFIX . $userId . $assemblyId;
157        $data = json_decode($this->getPersistence()->get($uid), true);
158        if (!$data) {
159            $data = [];
160        }
161        $data [] = $executionId;
162        $this->getPersistence()->set($uid, json_encode($data));
163    }
164
165    /**
166     * @param string $userUri
167     * @param string $status
168     * @return array
169     */
170    public function getDeliveryExecutionsByStatus($userUri, $status)
171    {
172        $returnValue = [];
173        $data = $this->getPersistence()->get(self::USER_EXECUTIONS_PREFIX . $userUri . $status);
174        $keys = $data !== false ? json_decode($data, true) : [];
175        if (is_array($keys)) {
176            foreach ($keys as $key) {
177                $de = $this->getDeliveryExecution($key);
178                if ($de->getState()->getUri() !== $status) {
179                    $this->fixStatus($de->getImplementation(), $status, $de->getState()->getUri());
180                } else {
181                    $returnValue[$key] = $de;
182                }
183            }
184        } else {
185            common_Logger::w(
186                'Non array "' . gettype($keys) . '" received as active Delivery Keys for user ' . $userUri
187            );
188        }
189
190        return $returnValue;
191    }
192
193    private function getDeliveryExecutionKeyValue(string $deliveryExecutionUri): KVDeliveryExecution
194    {
195        return new KVDeliveryExecution($this, $deliveryExecutionUri, $this->getData($deliveryExecutionUri));
196    }
197
198    /**
199     * @param string $identifier
200     * @return DeliveryExecutionWrapper
201     */
202    public function getDeliveryExecution($identifier)
203    {
204        $identifier = ($identifier instanceof core_kernel_classes_Resource)
205            ? $identifier->getUri()
206            : $this->keyValuePrefixDecoration($identifier);
207
208        $deImplementation = new KVDeliveryExecution($this, $identifier);
209
210        return $this->propagate(new DeliveryExecutionWrapper($deImplementation));
211    }
212
213    /**
214     * Update the collection of deliveries
215     *
216     * @param KVDeliveryExecution $deliveryExecution
217     * @param string $old
218     * @param string $new
219     * @return mixed
220     */
221    public function updateDeliveryExecutionStatus(KVDeliveryExecution $deliveryExecution, $old, $new)
222    {
223        $this->update($deliveryExecution);
224        $userId = $deliveryExecution->getUserIdentifier();
225        if ($old != null) {
226            $oldReferences = $this->getDeliveryExecutionsByStatus($userId, $old);
227            foreach (array_keys($oldReferences) as $key) {
228                if ($oldReferences[$key]->getIdentifier() == $deliveryExecution->getIdentifier()) {
229                    unset($oldReferences[$key]);
230                }
231            }
232            $this->setDeliveryExecutions($userId, $old, $oldReferences);
233        }
234
235        $newReferences = $this->getDeliveryExecutionsByStatus($userId, $new);
236        $newReferences[$deliveryExecution->getIdentifier()] = $deliveryExecution;
237        return $this->setDeliveryExecutions($userId, $new, $newReferences);
238    }
239
240    /**
241     * @throws common_Exception
242     */
243    public function update(KVDeliveryExecution $de): void
244    {
245        $this->getPersistence()->set($de->getIdentifier(), json_encode($de));
246    }
247
248    public function getData(string $deliveryExecutionId): ?array
249    {
250        $dataString = $this->getPersistence()->get($deliveryExecutionId);
251        $data = json_decode($dataString, true);
252        return $data;
253    }
254
255    /**
256     * @inheritdoc
257     */
258    public function deleteDeliveryExecutionData(DeliveryExecutionDeleteRequest $request)
259    {
260        $deUri = $request->getDeliveryExecution()->getIdentifier();
261        $deliveryUri = $request->getDeliveryResource()->getUri();
262        $userUri = $request->getDeliveryExecution()->getUserIdentifier();
263
264        /** @var \common_ext_ExtensionsManager $extManager */
265        $extManager = $this->getServiceLocator()->get(\common_ext_ExtensionsManager::SERVICE_ID);
266        if ($extManager->isInstalled('taoProctoring')) {
267            $reflect = new \ReflectionClass(\oat\taoProctoring\model\execution\DeliveryExecution::class);
268        } else {
269            $reflect = new \ReflectionClass(\oat\taoDelivery\model\execution\DeliveryExecutionInterface::class);
270        }
271
272        $constants = $reflect->getConstants();
273        $statuses = [];
274        foreach ($constants as $constantName => $constantValue) {
275            if (strpos($constantName, 'STATE_') !== false) {
276                $statuses[] = $constantValue;
277            }
278        }
279
280        foreach ($statuses as $status) {
281            $this->getPersistence()->del(self::USER_EXECUTIONS_PREFIX . $userUri . $status);
282        }
283
284        $deletedDe = $this->getPersistence()->del($deUri);
285        $deletedUE = $this->getPersistence()->del(self::USER_DELIVERY_PREFIX . $userUri . $deliveryUri);
286
287        return $deletedDe && $deletedUE;
288    }
289
290    /**
291     * @param $userUri
292     * @param $status
293     * @param $executions
294     * @return mixed
295     */
296    private function setDeliveryExecutions($userUri, $status, $executions)
297    {
298        $keys = [];
299        foreach ($executions as $execution) {
300            $keys[] = $execution->getIdentifier();
301        }
302        return $this->getPersistence()->set(self::USER_EXECUTIONS_PREFIX . $userUri . $status, json_encode($keys));
303    }
304
305    /**
306     * @param KVDeliveryExecution $deliveryExecution
307     * @param $old
308     * @param $new
309     */
310    private function fixStatus(KVDeliveryExecution $deliveryExecution, $old, $new)
311    {
312        $userId = $deliveryExecution->getUserIdentifier();
313        $oldData = $this->getPersistence()->get(self::USER_EXECUTIONS_PREFIX . $userId . $old);
314        $oldStateKeys = $oldData !== false ? json_decode($oldData, true) : [];
315        $oldStateExecutions = [];
316        if (is_array($oldStateKeys)) {
317            foreach ($oldStateKeys as $key) {
318                $de = $this->getDeliveryExecution($key);
319                if ($de->getIdentifier() !== $deliveryExecution->getIdentifier()) {
320                    $oldStateExecutions[$de->getIdentifier()] = $de;
321                }
322            }
323            $this->setDeliveryExecutions($userId, $old, $oldStateExecutions);
324        }
325
326        $newData = $this->getPersistence()->get(self::USER_EXECUTIONS_PREFIX . $userId . $new);
327        $newStateKeys = $newData !== false ? json_decode($newData, true) : [];
328        $newStateExecutions = [];
329        if (is_array($newStateKeys) && !isset($newStateKeys[$deliveryExecution->getIdentifier()])) {
330            foreach ($newStateKeys as $key) {
331                $de = $this->getDeliveryExecution($key);
332                $newStateExecutions[$de->getIdentifier()] = $de;
333            }
334            $newStateExecutions[$deliveryExecution->getIdentifier()] = $deliveryExecution;
335        }
336        $this->setDeliveryExecutions($userId, $new, $newStateExecutions);
337    }
338
339    /**
340     * @param $deliveryExecutionId
341     * @return bool
342     */
343    public function exists($deliveryExecutionId)
344    {
345        return $this->getPersistence()->exists($deliveryExecutionId);
346    }
347
348    /**
349     * @throws common_Exception|PersistenceException
350     */
351    public function addMetadata(Metadata $metadata, string $deliveryExecutionUri): void
352    {
353        $de = $this->getDeliveryExecutionKeyValue(
354            $this->keyValuePrefixDecoration($deliveryExecutionUri)
355        );
356
357        $de->addMetadata($metadata);
358        $this->update($de);
359    }
360
361    private function keyValuePrefixDecoration(string $identifier): string
362    {
363        if (strpos($identifier, self::DELIVERY_EXECUTION_PREFIX) === false) {
364            return self::DELIVERY_EXECUTION_PREFIX . $identifier;
365        }
366
367        return $identifier;
368    }
369}