Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
5.24% covered (danger)
5.24%
10 / 191
16.67% covered (danger)
16.67%
3 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
RdsTaskLogBroker
5.24% covered (danger)
5.24%
10 / 191
16.67% covered (danger)
16.67%
3 / 18
1333.39
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 __toPhpCode
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getPersistence
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getTableName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createContainer
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
12
 add
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 getStatus
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 updateStatus
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 addReport
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 getReport
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 search
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 count
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 getStats
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
2
 archive
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 archiveCollection
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 deleteById
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 getQueryBuilder
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildCounterStatusSql
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
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) 2017 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 */
21
22namespace oat\taoTaskQueue\model\TaskLogBroker;
23
24use oat\oatbox\PhpSerializable;
25use common_report_Report as Report;
26use Doctrine\DBAL\Query\QueryBuilder;
27use oat\taoTaskQueue\model\Entity\TaskLogEntity;
28use oat\taoTaskQueue\model\Entity\TasksLogsStats;
29use oat\taoTaskQueue\model\QueueDispatcherInterface;
30use oat\taoTaskQueue\model\Task\CallbackTaskInterface;
31use oat\taoTaskQueue\model\Task\TaskInterface;
32use oat\taoTaskQueue\model\TaskLog\TaskLogCollection;
33use oat\taoTaskQueue\model\TaskLog\TaskLogCollectionInterface;
34use oat\taoTaskQueue\model\TaskLog\TaskLogFilter;
35use oat\taoTaskQueue\model\TaskLogInterface;
36use oat\taoTaskQueue\model\ValueObjects\TaskLogCategorizedStatus;
37use Psr\Log\LoggerAwareInterface;
38use Zend\ServiceManager\ServiceLocatorAwareTrait;
39use oat\oatbox\log\LoggerAwareTrait;
40
41/**
42 * Storing message logs in RDS.
43 *
44 * @deprecated Use \oat\tao\model\taskQueue\TaskLog\Broker\RdsTaskLogBroker
45 *
46 * @author Gyula Szucs <gyula@taotesting.com>
47 */
48class RdsTaskLogBroker implements TaskLogBrokerInterface, PhpSerializable, LoggerAwareInterface
49{
50    use ServiceLocatorAwareTrait;
51    use LoggerAwareTrait;
52
53    private $persistenceId;
54
55    /**
56     * @var \common_persistence_SqlPersistence
57     */
58    protected $persistence;
59
60    private $containerName;
61
62    /**
63     * RdsTaskLogBroker constructor.
64     *
65     * @param string $persistenceId
66     * @param null $containerName
67     */
68    public function __construct($persistenceId, $containerName = null)
69    {
70        if (empty($persistenceId)) {
71            throw new \InvalidArgumentException("Persistence id needs to be set for " . __CLASS__);
72        }
73
74        $this->persistenceId = $persistenceId;
75        $this->containerName = empty($containerName) ? self::DEFAULT_CONTAINER_NAME : $containerName;
76    }
77
78    public function __toPhpCode()
79    {
80        return 'new ' . get_called_class() . '('
81            . \common_Utils::toHumanReadablePhpString($this->persistenceId)
82            . ', '
83            . \common_Utils::toHumanReadablePhpString($this->containerName)
84            . ')';
85    }
86
87    /**
88     * @return \common_persistence_SqlPersistence
89     */
90    protected function getPersistence()
91    {
92        if (is_null($this->persistence)) {
93            $this->persistence = $this->getServiceLocator()
94                ->get(\common_persistence_Manager::SERVICE_ID)
95                ->getPersistenceById($this->persistenceId);
96        }
97
98        return $this->persistence;
99    }
100
101    /**
102     * @return string
103     */
104    public function getTableName()
105    {
106        return strtolower(QueueDispatcherInterface::QUEUE_PREFIX . '_' . $this->containerName);
107    }
108
109    /**
110     * @inheritdoc
111     */
112    public function createContainer()
113    {
114        /** @var \common_persistence_sql_pdo_mysql_SchemaManager $schemaManager */
115        $schemaManager = $this->getPersistence()->getSchemaManager();
116
117        $fromSchema = $schemaManager->createSchema();
118        $toSchema = clone $fromSchema;
119
120        // if our table does not exist, let's create it
121        if (false === $fromSchema->hasTable($this->getTableName())) {
122            $table = $toSchema->createTable($this->getTableName());
123            $table->addOption('engine', 'InnoDB');
124            $table->addColumn(self::COLUMN_ID, 'string', ["notnull" => true, "length" => 255]);
125            $table->addColumn(
126                self::COLUMN_PARENT_ID,
127                'string',
128                ["notnull" => false, "length" => 255, "default" => null]
129            );
130            $table->addColumn(self::COLUMN_TASK_NAME, 'string', ["notnull" => true, "length" => 255]);
131            $table->addColumn(self::COLUMN_PARAMETERS, 'text', ["notnull" => false, "default" => null]);
132            $table->addColumn(self::COLUMN_LABEL, 'string', ["notnull" => false, "length" => 255]);
133            $table->addColumn(self::COLUMN_STATUS, 'string', ["notnull" => true, "length" => 50]);
134            $table->addColumn(self::COLUMN_MASTER_STATUS, 'boolean', ["default" => 0]);
135            $table->addColumn(self::COLUMN_OWNER, 'string', ["notnull" => false, "length" => 255, "default" => null]);
136            $table->addColumn(self::COLUMN_REPORT, 'text', ["notnull" => false, "default" => null]);
137            $table->addColumn(self::COLUMN_CREATED_AT, 'datetime', ['notnull' => true]);
138            $table->addColumn(self::COLUMN_UPDATED_AT, 'datetime', ['notnull' => false]);
139            $table->setPrimaryKey(['id']);
140            $table->addIndex(
141                [self::COLUMN_TASK_NAME, self::COLUMN_OWNER],
142                $this->getTableName() . 'IDX_task_name_owner'
143            );
144            $table->addIndex([self::COLUMN_STATUS], $this->getTableName() . 'IDX_status');
145            $table->addIndex([self::COLUMN_CREATED_AT], $this->getTableName() . 'IDX_created_at');
146
147            $queries = $this->getPersistence()->getPlatForm()->getMigrateSchemaSql($fromSchema, $toSchema);
148            foreach ($queries as $query) {
149                $this->getPersistence()->exec($query);
150            }
151        }
152    }
153
154    /**
155     * @inheritdoc
156     */
157    public function add(TaskInterface $task, $status, $label = null)
158    {
159        $this->getPersistence()->insert($this->getTableName(), [
160            self::COLUMN_ID   => (string) $task->getId(),
161            self::COLUMN_PARENT_ID  => $task->getParentId() ? (string) $task->getParentId() : null,
162            self::COLUMN_TASK_NAME => $task instanceof CallbackTaskInterface && is_object($task->getCallable())
163                ? get_class($task->getCallable())
164                : get_class($task),
165            self::COLUMN_PARAMETERS => json_encode($task->getParameters()),
166            self::COLUMN_LABEL => (string) $label,
167            self::COLUMN_STATUS => (string) $status,
168            self::COLUMN_OWNER => (string) $task->getOwner(),
169            self::COLUMN_CREATED_AT => $task->getCreatedAt()->format('Y-m-d H:i:s'),
170            self::COLUMN_UPDATED_AT => $this->getPersistence()->getPlatForm()->getNowExpression(),
171            self::COLUMN_MASTER_STATUS => (int) $task->isMasterStatus(),
172        ]);
173    }
174
175    /**
176     * @inheritdoc
177     */
178    public function getStatus($taskId)
179    {
180        $qb = $this->getQueryBuilder()
181            ->select(self::COLUMN_STATUS)
182            ->from($this->getTableName())
183            ->andWhere(self::COLUMN_ID . ' = :id')
184            ->setParameter('id', $taskId);
185
186        return $qb->execute()->fetchColumn();
187    }
188
189    /**
190     * @inheritdoc
191     */
192    public function updateStatus($taskId, $newStatus, $prevStatus = null)
193    {
194        $qb = $this->getQueryBuilder()
195            ->update($this->getTableName())
196            ->set(self::COLUMN_STATUS, ':status_new')
197            ->set(self::COLUMN_UPDATED_AT, ':updated_at')
198            ->where(self::COLUMN_ID . ' = :id')
199            ->setParameter('id', (string) $taskId)
200            ->setParameter('status_new', (string) $newStatus)
201            ->setParameter('updated_at', $this->getPersistence()->getPlatForm()->getNowExpression());
202
203        if ($prevStatus) {
204            $qb->andWhere(self::COLUMN_STATUS . ' = :status_prev')
205                ->setParameter('status_prev', (string) $prevStatus);
206        }
207
208        return $qb->execute();
209    }
210
211    /**
212     * @inheritdoc
213     */
214    public function addReport($taskId, Report $report, $newStatus = null)
215    {
216        $qb = $this->getQueryBuilder()
217            ->update($this->getTableName())
218            ->set(self::COLUMN_REPORT, ':report')
219            ->set(self::COLUMN_STATUS, ':status_new')
220            ->set(self::COLUMN_UPDATED_AT, ':updated_at')
221            ->andWhere(self::COLUMN_ID . ' = :id')
222            ->setParameter('id', (string) $taskId)
223            ->setParameter('report', json_encode($report))
224            ->setParameter('status_new', (string) $newStatus)
225            ->setParameter('updated_at', $this->getPersistence()->getPlatForm()->getNowExpression());
226
227        return $qb->execute();
228    }
229
230    /**
231     * @inheritdoc
232     */
233    public function getReport($taskId)
234    {
235        $qb = $this->getQueryBuilder()
236            ->select(self::COLUMN_REPORT)
237            ->from($this->getTableName())
238            ->andWhere(self::COLUMN_ID . ' = :id')
239            ->setParameter('id', (string) $taskId);
240
241        if (
242            ($reportJson = $qb->execute()->fetchColumn())
243            && ($reportData = json_decode($reportJson, true)) !== null
244            && json_last_error() === JSON_ERROR_NONE
245        ) {
246            // if we have a valid JSON string and no JSON error, let's restore the report object
247            return Report::jsonUnserialize($reportData);
248        }
249
250        return null;
251    }
252
253    /**
254     * @inheritdoc
255     */
256    public function search(TaskLogFilter $filter)
257    {
258        try {
259            $qb = $this->getQueryBuilder()
260                ->select($filter->getColumns())
261                ->from($this->getTableName());
262
263            $qb->setMaxResults($filter->getLimit());
264            $qb->setFirstResult($filter->getOffset());
265
266            if ($filter->getSortBy()) {
267                $qb->orderBy($filter->getSortBy(), $filter->getSortOrder());
268            }
269
270            $filter->applyFilters($qb);
271
272            $collection = TaskLogCollection::createFromArray($qb->execute()->fetchAll());
273        } catch (\Exception $exception) {
274            $this->logError('Searching for task logs failed with MSG: ' . $exception->getMessage());
275
276            $collection = TaskLogCollection::createEmptyCollection();
277        }
278
279        return $collection;
280    }
281
282    /**
283     * @inheritdoc
284     */
285    public function count(TaskLogFilter $filter)
286    {
287        try {
288            $qb = $this->getQueryBuilder()
289                ->select('COUNT(*)')
290                ->from($this->getTableName());
291
292            $filter->applyFilters($qb);
293
294            return (int) $qb->execute()->fetchColumn();
295        } catch (\Exception $e) {
296            $this->logError('Counting task logs failed with MSG: ' . $e->getMessage());
297        }
298
299        return 0;
300    }
301
302    /**
303     * @inheritdoc
304     */
305    public function getStats(TaskLogFilter $filter)
306    {
307        $qb = $this->getQueryBuilder()
308            ->from($this->getTableName());
309
310        $qb->select(
311            $this->buildCounterStatusSql(
312                TasksLogsStats::IN_PROGRESS_TASKS,
313                TaskLogCategorizedStatus::getMappedStatuses(TaskLogCategorizedStatus::STATUS_IN_PROGRESS)
314            )
315            . ', ' . $this->buildCounterStatusSql(
316                TasksLogsStats::COMPLETED_TASKS,
317                TaskLogCategorizedStatus::getMappedStatuses(TaskLogCategorizedStatus::STATUS_COMPLETED)
318            )
319            . ', ' . $this->buildCounterStatusSql(
320                TasksLogsStats::FAILED_TASKS,
321                TaskLogCategorizedStatus::getMappedStatuses(TaskLogCategorizedStatus::STATUS_FAILED)
322            )
323        );
324
325        $filter->applyFilters($qb);
326
327        $row = $qb->execute()->fetch();
328
329        return TasksLogsStats::buildFromArray($row);
330    }
331
332    /**
333     * @inheritdoc
334     */
335    public function archive(TaskLogEntity $entity)
336    {
337        $this->getPersistence()->getPlatform()->beginTransaction();
338
339        try {
340            $qb = $this->getQueryBuilder()
341                ->update($this->getTableName())
342                ->set(self::COLUMN_STATUS, ':status_new')
343                ->set(self::COLUMN_UPDATED_AT, ':updated_at')
344                ->where(self::COLUMN_ID . ' = :id')
345                ->setParameter('id', (string) $entity->getId())
346                ->setParameter('status_new', (string) TaskLogInterface::STATUS_ARCHIVED)
347                ->setParameter('updated_at', $this->getPersistence()->getPlatForm()->getNowExpression());
348
349            $qb->execute();
350            $this->getPersistence()->getPlatform()->commit();
351        } catch (\Exception $e) {
352            $this->getPersistence()->getPlatform()->rollBack();
353
354            return false;
355        }
356
357        return true;
358    }
359
360    /**
361     * @inheritdoc
362     */
363    public function archiveCollection(TaskLogCollectionInterface $collection)
364    {
365        $this->getPersistence()->getPlatform()->beginTransaction();
366
367        try {
368            $qb = $this->getQueryBuilder()
369                ->update($this->getTableName())
370                ->set(self::COLUMN_STATUS, ':status_new')
371                ->set(self::COLUMN_UPDATED_AT, ':updated_at')
372                ->where(self::COLUMN_ID . ' IN(:id)')
373                ->setParameter('id', $collection->getIds(), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
374                ->setParameter('status_new', (string) TaskLogInterface::STATUS_ARCHIVED)
375                ->setParameter('updated_at', $this->getPersistence()->getPlatForm()->getNowExpression());
376
377            $exec = $qb->execute();
378            $this->getPersistence()->getPlatform()->commit();
379        } catch (\Exception $e) {
380            $this->getPersistence()->getPlatform()->rollBack();
381            $this->logDebug($e->getMessage());
382
383            return false;
384        }
385
386        return $exec;
387    }
388
389    /**
390     * @inheritdoc
391     */
392    public function deleteById($taskId)
393    {
394        $this->getPersistence()->getPlatform()->beginTransaction();
395
396        try {
397            $qb = $this->getQueryBuilder()
398                ->delete($this->getTableName())
399                ->where(self::COLUMN_ID . ' = :id')
400                ->setParameter('id', (string) $taskId);
401
402            $qb->execute();
403            $this->getPersistence()->getPlatform()->commit();
404        } catch (\Exception $e) {
405            $this->getPersistence()->getPlatform()->rollBack();
406
407            return false;
408        }
409
410        return true;
411    }
412
413    /**
414     * @return QueryBuilder
415     */
416    private function getQueryBuilder()
417    {
418        /**@var \common_persistence_sql_pdo_mysql_Driver $driver */
419        return $this->getPersistence()->getPlatform()->getQueryBuilder();
420    }
421
422    /**
423     * @param string $statusColumn
424     * @param array $inStatuses
425     * @return string
426     */
427    private function buildCounterStatusSql($statusColumn, array $inStatuses)
428    {
429        if (empty($inStatuses)) {
430            return '';
431        }
432
433        $sql =  "COUNT( CASE WHEN ";
434        foreach ($inStatuses as $status) {
435            if ($status !== reset($inStatuses)) {
436                $sql .= " OR " . self::COLUMN_STATUS . " = '" . $status . "'";
437            } else {
438                $sql .= " " . self::COLUMN_STATUS . " = '" . $status . "'";
439            }
440        }
441
442        $sql .= " THEN 0 END ) AS $statusColumn";
443
444        return $sql;
445    }
446}