Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 131 |
|
0.00% |
0 / 14 |
CRAP | |
0.00% |
0 / 1 |
tao_actions_TaskQueueWebApi | |
0.00% |
0 / 131 |
|
0.00% |
0 / 14 |
1482 | |
0.00% |
0 / 1 |
getAll | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
12 | |||
get | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
12 | |||
stats | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
archive | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
56 | |||
cancel | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
download | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
56 | |||
checkIfTaskIdExists | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
checkIfIsXmlHttpRequest | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
detectTaskIds | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
getSessionUserUri | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFileReferenceSerializer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTaskLogEntityDecorateProcessor | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFileSystemService | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTaskLogService | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
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) 2019-2021 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); |
19 | * |
20 | */ |
21 | |
22 | use oat\generis\model\fileReference\FileReferenceSerializer; |
23 | use oat\oatbox\filesystem\FileSystemService; |
24 | use oat\tao\model\http\HttpJsonResponseTrait; |
25 | use oat\tao\model\taskQueue\Task\FileReferenceSerializerAwareTrait; |
26 | use oat\tao\model\taskQueue\Task\FilesystemAwareTrait; |
27 | use oat\tao\model\taskQueue\TaskLog\Broker\TaskLogBrokerInterface; |
28 | use oat\tao\model\taskQueue\TaskLog\Decorator\CategoryEntityDecorator; |
29 | use oat\tao\model\taskQueue\TaskLog\Decorator\HasFileEntityDecorator; |
30 | use oat\tao\model\taskQueue\TaskLog\Decorator\RedirectUrlEntityDecorator; |
31 | use oat\tao\model\taskQueue\TaskLog\Decorator\SimpleManagementCollectionDecorator; |
32 | use oat\tao\model\taskQueue\TaskLog\Decorator\TaskLogEntityDecorateProcessor; |
33 | use oat\tao\model\taskQueue\TaskLog\TaskLogFilter; |
34 | use oat\tao\model\taskQueue\TaskLogInterface; |
35 | |
36 | /** |
37 | * API controller to get task queue data by our WEB front-end. |
38 | * |
39 | * @author Gyula Szucs <gyula@taotesting.com> |
40 | */ |
41 | class tao_actions_TaskQueueWebApi extends tao_actions_CommonModule |
42 | { |
43 | use FilesystemAwareTrait; |
44 | use FileReferenceSerializerAwareTrait; |
45 | use HttpJsonResponseTrait; |
46 | |
47 | private const PARAMETER_TASK_ID = 'taskId'; |
48 | private const PARAMETER_LIMIT = 'limit'; |
49 | private const PARAMETER_OFFSET = 'offset'; |
50 | private const ALL = 'all'; |
51 | |
52 | /** |
53 | * @throws common_exception_NotImplemented |
54 | * @throws Exception |
55 | */ |
56 | public function getAll(): void |
57 | { |
58 | $this->checkIfIsXmlHttpRequest(); |
59 | |
60 | $taskLogService = $this->getTaskLogService(); |
61 | $limit = $offset = null; |
62 | |
63 | if ($this->hasRequestParameter(self::PARAMETER_LIMIT)) { |
64 | $limit = (int) $this->getRequestParameter(self::PARAMETER_LIMIT); |
65 | } |
66 | |
67 | if ($this->hasRequestParameter(self::PARAMETER_OFFSET)) { |
68 | $offset = (int) $this->getRequestParameter(self::PARAMETER_OFFSET); |
69 | } |
70 | |
71 | $collection = new SimpleManagementCollectionDecorator( |
72 | $taskLogService->findAvailableByUser($this->getSessionUserUri(), $limit, $offset), |
73 | $taskLogService, |
74 | $this->getFileSystemService(), |
75 | $this->getFileReferenceSerializer(), |
76 | $this->getTaskLogEntityDecorateProcessor(), |
77 | false |
78 | ); |
79 | |
80 | $this->setSuccessJsonResponse($collection->toArray()); |
81 | } |
82 | |
83 | /** |
84 | * @throws common_exception_NotImplemented |
85 | * @throws Exception |
86 | */ |
87 | public function get(): void |
88 | { |
89 | $this->checkIfIsXmlHttpRequest(); |
90 | |
91 | try { |
92 | $this->checkIfTaskIdExists(); |
93 | |
94 | $taskLogService = $this->getTaskLogService(); |
95 | |
96 | $entity = $taskLogService->getByIdAndUser( |
97 | $this->getRequestParameter(self::PARAMETER_TASK_ID), |
98 | $this->getSessionUserUri() |
99 | ); |
100 | |
101 | $entity = new RedirectUrlEntityDecorator( |
102 | new HasFileEntityDecorator( |
103 | new CategoryEntityDecorator($entity, $taskLogService), |
104 | $this->getFileSystemService(), |
105 | $this->getFileReferenceSerializer() |
106 | ), |
107 | $taskLogService, |
108 | common_session_SessionManager::getSession()->getUser() |
109 | ); |
110 | |
111 | $taskLogEntityDecorator = $this->getTaskLogEntityDecorateProcessor(); |
112 | $taskLogEntityDecorator->setEntity($entity); |
113 | |
114 | $this->setSuccessJsonResponse($taskLogEntityDecorator->toArray()); |
115 | } catch (Exception $e) { |
116 | $this->setErrorJsonResponse( |
117 | $e instanceof common_exception_UserReadableException ? $e->getUserMessage() : $e->getMessage(), |
118 | $e->getCode() |
119 | ); |
120 | } |
121 | } |
122 | |
123 | /** |
124 | * @throws common_exception_NotImplemented |
125 | * @throws Exception |
126 | */ |
127 | public function stats(): void |
128 | { |
129 | $this->checkIfIsXmlHttpRequest(); |
130 | |
131 | $this->setSuccessJsonResponse( |
132 | $this->getTaskLogService()->getStats($this->getSessionUserUri())->toArray() |
133 | ); |
134 | } |
135 | |
136 | /** |
137 | * @throws common_exception_NotImplemented |
138 | * @throws Exception |
139 | */ |
140 | public function archive() |
141 | { |
142 | $this->checkIfIsXmlHttpRequest(); |
143 | |
144 | try { |
145 | $this->checkIfTaskIdExists(); |
146 | $taskIds = $this->detectTaskIds(); |
147 | |
148 | // Get task log service |
149 | $taskLogService = $this->getTaskLogService(); |
150 | |
151 | // Define batch size for the chunk of tasks to be processed in each iteration |
152 | $batchSize = $taskLogService->getOption(TaskLogInterface::OPTION_DEFAULT_BATCH_SIZE); |
153 | $success = false; |
154 | |
155 | // If taskIds is ALL, we'll fetch all tasks using pagination |
156 | if ($taskIds === static::ALL) { |
157 | do { |
158 | // Set the filter with limit and offset for pagination |
159 | $filter = (new TaskLogFilter()) |
160 | ->availableForArchived($this->getSessionUserUri()) |
161 | ->setLimit($batchSize) |
162 | ->setSortBy(TaskLogBrokerInterface::COLUMN_CREATED_AT); |
163 | // Fetch the tasks for the current batch |
164 | $taskLogCollection = $taskLogService->search($filter); |
165 | |
166 | // If no tasks are returned, break the loop |
167 | if (count($taskLogCollection) === 0) { |
168 | break; |
169 | } |
170 | |
171 | // Process the current batch |
172 | $success = $taskLogService->archiveCollection($taskLogCollection) || $success; |
173 | } while (count($taskLogCollection) === $batchSize); |
174 | } else { |
175 | // Handle specific task IDs (no need for pagination) |
176 | $filter = (new TaskLogFilter()) |
177 | ->addAvailableFilters($this->getSessionUserUri()) |
178 | ->in(TaskLogBrokerInterface::COLUMN_ID, $taskIds); |
179 | |
180 | // Fetch all tasks based on provided task IDs |
181 | $taskLogCollection = $taskLogService->search($filter); |
182 | |
183 | // Process the tasks without pagination |
184 | $success = $taskLogService->archiveCollection($taskLogCollection); |
185 | } |
186 | |
187 | // Return JSON response |
188 | return $this->returnJson([ |
189 | 'success' => (bool) $success |
190 | ]); |
191 | } catch (Exception $e) { |
192 | $this->setErrorJsonResponse( |
193 | $e instanceof common_exception_UserReadableException ? $e->getUserMessage() : $e->getMessage(), |
194 | $e instanceof \common_exception_NotFound ? 404 : $e->getCode() |
195 | ); |
196 | } |
197 | } |
198 | |
199 | /** |
200 | * @throws Exception |
201 | */ |
202 | public function cancel() |
203 | { |
204 | $this->checkIfIsXmlHttpRequest(); |
205 | |
206 | try { |
207 | $this->checkIfTaskIdExists(); |
208 | $taskIds = $this->detectTaskIds(); |
209 | |
210 | $taskLogService = $this->getTaskLogService(); |
211 | |
212 | $filter = $taskIds === static::ALL |
213 | ? (new TaskLogFilter())->availableForCancelled($this->getSessionUserUri()) |
214 | : (new TaskLogFilter()) |
215 | ->addAvailableFilters($this->getSessionUserUri()) |
216 | ->in(TaskLogBrokerInterface::COLUMN_ID, $taskIds); |
217 | |
218 | return $this->returnJson([ |
219 | 'success' => (bool) $taskLogService->cancelCollection($taskLogService->search($filter)) |
220 | ]); |
221 | } catch (Exception $e) { |
222 | $this->setErrorJsonResponse( |
223 | $e instanceof common_exception_UserReadableException ? $e->getUserMessage() : $e->getMessage(), |
224 | $e instanceof \common_exception_NotFound ? 404 : $e->getCode() |
225 | ); |
226 | } |
227 | } |
228 | |
229 | /** |
230 | * Download the file created by task. |
231 | */ |
232 | public function download() |
233 | { |
234 | try { |
235 | $this->checkIfTaskIdExists(); |
236 | |
237 | $taskLogEntity = $this->getTaskLogService()->getByIdAndUser( |
238 | $this->getRequestParameter(self::PARAMETER_TASK_ID), |
239 | $this->getSessionUserUri() |
240 | ); |
241 | |
242 | if (!$taskLogEntity->getStatus()->isCompleted()) { |
243 | throw new \common_Exception('Task "' . $taskLogEntity->getId() . '" is not downloadable.'); |
244 | } |
245 | |
246 | $fileNameOrSerial = $taskLogEntity->getFileNameFromReport(); |
247 | |
248 | if (empty($fileNameOrSerial)) { |
249 | throw new \common_Exception('Filename not found in report.'); |
250 | } |
251 | |
252 | // first try to get a referenced file is it is a serial |
253 | $file = $this->getReferencedFile($fileNameOrSerial); |
254 | |
255 | // if no file let's try the task queue storage |
256 | if (is_null($file)) { |
257 | $file = $this->getQueueStorageFile($fileNameOrSerial); |
258 | } |
259 | |
260 | if (!$file) { |
261 | throw new \common_exception_NotFound('File not found.'); |
262 | } |
263 | |
264 | header('Set-Cookie: fileDownload=true'); |
265 | setcookie('fileDownload', 'true', 0, '/'); |
266 | header('Content-Disposition: attachment; filename="' . $file->getBasename() . '"'); |
267 | header('Content-Type: ' . $file->getMimeType()); |
268 | |
269 | \tao_helpers_Http::returnStream($file->readPsrStream()); |
270 | exit(); |
271 | } catch (Exception $e) { |
272 | $this->setErrorJsonResponse( |
273 | $e instanceof common_exception_UserReadableException ? $e->getUserMessage() : $e->getMessage(), |
274 | $e->getCode() |
275 | ); |
276 | } |
277 | } |
278 | |
279 | /** |
280 | * @throws common_exception_MissingParameter |
281 | */ |
282 | protected function checkIfTaskIdExists(): void |
283 | { |
284 | if (!$this->hasRequestParameter(self::PARAMETER_TASK_ID)) { |
285 | throw new common_exception_MissingParameter(self::PARAMETER_TASK_ID, $this->getRequestURI()); |
286 | } |
287 | } |
288 | |
289 | /** |
290 | * @throws Exception |
291 | */ |
292 | protected function checkIfIsXmlHttpRequest(): void |
293 | { |
294 | if (!$this->isXmlHttpRequest()) { |
295 | throw new Exception('Only ajax call allowed.'); |
296 | } |
297 | } |
298 | |
299 | /** |
300 | * @return array|string |
301 | */ |
302 | protected function detectTaskIds() |
303 | { |
304 | $taskIdsParams = $this->getRequestParameter(self::PARAMETER_TASK_ID); |
305 | |
306 | if (is_array($taskIdsParams)) { |
307 | return $taskIdsParams; |
308 | } elseif ($taskIdsParams === static::ALL) { |
309 | return static::ALL; |
310 | } else { |
311 | return [$taskIdsParams]; |
312 | } |
313 | } |
314 | |
315 | /** |
316 | * @throws common_exception_Error |
317 | */ |
318 | protected function getSessionUserUri(): string |
319 | { |
320 | return $this->getSession()->getUserUri(); |
321 | } |
322 | |
323 | protected function getFileReferenceSerializer(): FileReferenceSerializer |
324 | { |
325 | return $this->getServiceLocator()->get(FileReferenceSerializer::SERVICE_ID); |
326 | } |
327 | |
328 | protected function getTaskLogEntityDecorateProcessor(): TaskLogEntityDecorateProcessor |
329 | { |
330 | return $this->getServiceManager()->getContainer()->get(TaskLogEntityDecorateProcessor::class); |
331 | } |
332 | |
333 | protected function getFileSystemService(): FileSystemService |
334 | { |
335 | return $this->getServiceLocator()->get(FileSystemService::SERVICE_ID); |
336 | } |
337 | |
338 | protected function getTaskLogService(): TaskLogInterface |
339 | { |
340 | return $this->getServiceLocator()->get(TaskLogInterface::SERVICE_ID); |
341 | } |
342 | } |