1414use OCP \AppFramework \Http ;
1515use OCP \AppFramework \Http \Attribute \AnonRateLimit ;
1616use OCP \AppFramework \Http \Attribute \ApiRoute ;
17+ use OCP \AppFramework \Http \Attribute \ExAppRequired ;
1718use OCP \AppFramework \Http \Attribute \NoAdminRequired ;
1819use OCP \AppFramework \Http \Attribute \PublicPage ;
1920use OCP \AppFramework \Http \Attribute \UserRateLimit ;
2021use OCP \AppFramework \Http \DataDownloadResponse ;
2122use OCP \AppFramework \Http \DataResponse ;
2223use OCP \Files \File ;
24+ use OCP \Files \GenericFileException ;
2325use OCP \Files \IRootFolder ;
26+ use OCP \Files \NotPermittedException ;
2427use OCP \IL10N ;
2528use OCP \IRequest ;
29+ use OCP \Lock \LockedException ;
2630use OCP \TaskProcessing \EShapeType ;
2731use OCP \TaskProcessing \Exception \Exception ;
32+ use OCP \TaskProcessing \Exception \NotFoundException ;
33+ use OCP \TaskProcessing \Exception \PreConditionNotMetException ;
2834use OCP \TaskProcessing \Exception \UnauthorizedException ;
2935use OCP \TaskProcessing \Exception \ValidationException ;
36+ use OCP \TaskProcessing \IManager ;
3037use OCP \TaskProcessing \ShapeDescriptor ;
3138use OCP \TaskProcessing \Task ;
39+ use RuntimeException ;
3240
3341/**
3442 * @psalm-import-type CoreTaskProcessingTask from ResponseDefinitions
3543 * @psalm-import-type CoreTaskProcessingTaskType from ResponseDefinitions
3644 */
3745class TaskProcessingApiController extends \OCP \AppFramework \OCSController {
3846 public function __construct (
39- string $ appName ,
40- IRequest $ request ,
41- private \ OCP \ TaskProcessing \ IManager $ taskProcessingManager ,
42- private IL10N $ l ,
43- private ?string $ userId ,
47+ string $ appName ,
48+ IRequest $ request ,
49+ private IManager $ taskProcessingManager ,
50+ private IL10N $ l ,
51+ private ?string $ userId ,
4452 private IRootFolder $ rootFolder ,
4553 ) {
4654 parent ::__construct ($ appName , $ request );
@@ -109,13 +117,13 @@ public function schedule(array $input, string $type, string $appId, string $cust
109117 return new DataResponse ([
110118 'task ' => $ json ,
111119 ]);
112- } catch (\ OCP \ TaskProcessing \ Exception \ PreConditionNotMetException ) {
120+ } catch (PreConditionNotMetException ) {
113121 return new DataResponse (['message ' => $ this ->l ->t ('The given provider is not available ' )], Http::STATUS_PRECONDITION_FAILED );
114122 } catch (ValidationException $ e ) {
115123 return new DataResponse (['message ' => $ e ->getMessage ()], Http::STATUS_BAD_REQUEST );
116- } catch (UnauthorizedException $ e ) {
124+ } catch (UnauthorizedException ) {
117125 return new DataResponse (['message ' => 'User does not have access to the files mentioned in the task input ' ], Http::STATUS_UNAUTHORIZED );
118- } catch (\ OCP \ TaskProcessing \ Exception \ Exception $ e ) {
126+ } catch (Exception ) {
119127 return new DataResponse (['message ' => 'Internal server error ' ], Http::STATUS_INTERNAL_SERVER_ERROR );
120128 }
121129 }
@@ -144,9 +152,9 @@ public function getTask(int $id): DataResponse {
144152 return new DataResponse ([
145153 'task ' => $ json ,
146154 ]);
147- } catch (\ OCP \ TaskProcessing \ Exception \ NotFoundException $ e ) {
155+ } catch (NotFoundException ) {
148156 return new DataResponse (['message ' => $ this ->l ->t ('Task not found ' )], Http::STATUS_NOT_FOUND );
149- } catch (\ RuntimeException $ e ) {
157+ } catch (RuntimeException ) {
150158 return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
151159 }
152160 }
@@ -169,9 +177,9 @@ public function deleteTask(int $id): DataResponse {
169177 $ this ->taskProcessingManager ->deleteTask ($ task );
170178
171179 return new DataResponse (null );
172- } catch (\ OCP \ TaskProcessing \ Exception \ NotFoundException $ e ) {
180+ } catch (NotFoundException ) {
173181 return new DataResponse (null );
174- } catch (\ OCP \ TaskProcessing \ Exception \ Exception $ e ) {
182+ } catch (Exception ) {
175183 return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
176184 }
177185 }
@@ -199,7 +207,7 @@ public function listTasksByApp(string $appId, ?string $customId = null): DataRes
199207 return new DataResponse ([
200208 'tasks ' => $ json ,
201209 ]);
202- } catch (Exception $ e ) {
210+ } catch (Exception ) {
203211 return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
204212 }
205213 }
@@ -226,7 +234,7 @@ public function listTasks(?string $taskType, ?string $customId = null): DataResp
226234 return new DataResponse ([
227235 'tasks ' => $ json ,
228236 ]);
229- } catch (Exception $ e ) {
237+ } catch (Exception ) {
230238 return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
231239 }
232240 }
@@ -247,37 +255,72 @@ public function listTasks(?string $taskType, ?string $customId = null): DataResp
247255 public function getFileContents (int $ taskId , int $ fileId ): Http \DataDownloadResponse |DataResponse {
248256 try {
249257 $ task = $ this ->taskProcessingManager ->getUserTask ($ taskId , $ this ->userId );
250- $ ids = $ this ->extractFileIdsFromTask ($ task );
251- if (!in_array ($ fileId , $ ids )) {
252- return new DataResponse (['message ' => $ this ->l ->t ('Not found ' )], Http::STATUS_NOT_FOUND );
253- }
254- $ node = $ this ->rootFolder ->getFirstNodeById ($ fileId );
255- if ($ node === null ) {
256- $ node = $ this ->rootFolder ->getFirstNodeByIdInPath ($ fileId , '/ ' . $ this ->rootFolder ->getAppDataDirectoryName () . '/ ' );
257- if (!$ node instanceof File) {
258- throw new \OCP \TaskProcessing \Exception \NotFoundException ('Node is not a file ' );
259- }
260- } elseif (!$ node instanceof File) {
261- throw new \OCP \TaskProcessing \Exception \NotFoundException ('Node is not a file ' );
262- }
263- return new Http \DataDownloadResponse ($ node ->getContent (), $ node ->getName (), $ node ->getMimeType ());
264- } catch (\OCP \TaskProcessing \Exception \NotFoundException $ e ) {
258+ return $ this ->getFileContentsInternal ($ task , $ fileId );
259+ } catch (NotFoundException ) {
260+ return new DataResponse (['message ' => $ this ->l ->t ('Not found ' )], Http::STATUS_NOT_FOUND );
261+ } catch (Exception ) {
262+ return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
263+ }
264+ }
265+
266+ /**
267+ * Returns the contents of a file referenced in a task(ExApp route version)
268+ *
269+ * @param int $taskId The id of the task
270+ * @param int $fileId The file id of the file to retrieve
271+ * @return DataDownloadResponse<Http::STATUS_OK, string, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
272+ *
273+ * 200: File content returned
274+ * 404: Task or file not found
275+ */
276+ #[ExAppRequired]
277+ #[ApiRoute(verb: 'GET ' , url: '/tasks_provider/{taskId}/file/{fileId} ' , root: '/taskprocessing ' )]
278+ public function getFileContentsExApp (int $ taskId , int $ fileId ): Http \DataDownloadResponse |DataResponse {
279+ try {
280+ $ task = $ this ->taskProcessingManager ->getTask ($ taskId );
281+ return $ this ->getFileContentsInternal ($ task , $ fileId );
282+ } catch (NotFoundException ) {
265283 return new DataResponse (['message ' => $ this ->l ->t ('Not found ' )], Http::STATUS_NOT_FOUND );
266- } catch (Exception $ e ) {
284+ } catch (Exception ) {
267285 return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
268286 }
269287 }
270288
289+ /**
290+ * @throws NotPermittedException
291+ * @throws NotFoundException
292+ * @throws GenericFileException
293+ * @throws LockedException
294+ *
295+ * @return DataDownloadResponse<Http::STATUS_OK, string, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
296+ */
297+ private function getFileContentsInternal (Task $ task , int $ fileId ): Http \DataDownloadResponse |DataResponse {
298+ $ ids = $ this ->extractFileIdsFromTask ($ task );
299+ if (!in_array ($ fileId , $ ids )) {
300+ return new DataResponse (['message ' => $ this ->l ->t ('Not found ' )], Http::STATUS_NOT_FOUND );
301+ }
302+ $ node = $ this ->rootFolder ->getFirstNodeById ($ fileId );
303+ if ($ node === null ) {
304+ $ node = $ this ->rootFolder ->getFirstNodeByIdInPath ($ fileId , '/ ' . $ this ->rootFolder ->getAppDataDirectoryName () . '/ ' );
305+ if (!$ node instanceof File) {
306+ throw new NotFoundException ('Node is not a file ' );
307+ }
308+ } elseif (!$ node instanceof File) {
309+ throw new NotFoundException ('Node is not a file ' );
310+ }
311+ return new Http \DataDownloadResponse ($ node ->getContent (), $ node ->getName (), $ node ->getMimeType ());
312+ }
313+
271314 /**
272315 * @param Task $task
273316 * @return list<int>
274- * @throws \OCP\TaskProcessing\Exception\ NotFoundException
317+ * @throws NotFoundException
275318 */
276319 private function extractFileIdsFromTask (Task $ task ): array {
277320 $ ids = [];
278321 $ taskTypes = $ this ->taskProcessingManager ->getAvailableTaskTypes ();
279322 if (!isset ($ taskTypes [$ task ->getTaskTypeId ()])) {
280- throw new \ OCP \ TaskProcessing \ Exception \ NotFoundException ('Could not find task type ' );
323+ throw new NotFoundException ('Could not find task type ' );
281324 }
282325 $ taskType = $ taskTypes [$ task ->getTaskTypeId ()];
283326 foreach ($ taskType ['inputShape ' ] + $ taskType ['optionalInputShape ' ] as $ key => $ descriptor ) {
@@ -317,22 +360,22 @@ private function extractFileIdsFromTask(Task $task): array {
317360 * 200: Progress updated successfully
318361 * 404: Task not found
319362 */
320- #[NoAdminRequired ]
321- #[ApiRoute(verb: 'POST ' , url: '/tasks /{taskId}/progress ' , root: '/taskprocessing ' )]
363+ #[ExAppRequired ]
364+ #[ApiRoute(verb: 'POST ' , url: '/tasks_provider /{taskId}/progress ' , root: '/taskprocessing ' )]
322365 public function setProgress (int $ taskId , float $ progress ): DataResponse {
323366 try {
324367 $ this ->taskProcessingManager ->setTaskProgress ($ taskId , $ progress );
325- $ task = $ this ->taskProcessingManager ->getUserTask ($ taskId, $ this -> userId );
368+ $ task = $ this ->taskProcessingManager ->getTask ($ taskId );
326369
327370 /** @var CoreTaskProcessingTask $json */
328371 $ json = $ task ->jsonSerialize ();
329372
330373 return new DataResponse ([
331374 'task ' => $ json ,
332375 ]);
333- } catch (\ OCP \ TaskProcessing \ Exception \ NotFoundException $ e ) {
376+ } catch (NotFoundException ) {
334377 return new DataResponse (['message ' => $ this ->l ->t ('Not found ' )], Http::STATUS_NOT_FOUND );
335- } catch (Exception $ e ) {
378+ } catch (Exception ) {
336379 return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
337380 }
338381 }
@@ -348,25 +391,23 @@ public function setProgress(int $taskId, float $progress): DataResponse {
348391 * 200: Result updated successfully
349392 * 404: Task not found
350393 */
351- #[NoAdminRequired ]
352- #[ApiRoute(verb: 'POST ' , url: '/tasks /{taskId}/result ' , root: '/taskprocessing ' )]
394+ #[ExAppRequired ]
395+ #[ApiRoute(verb: 'POST ' , url: '/tasks_provider /{taskId}/result ' , root: '/taskprocessing ' )]
353396 public function setResult (int $ taskId , ?array $ output = null , ?string $ errorMessage = null ): DataResponse {
354397 try {
355- // Check if the current user can access the task
356- $ this ->taskProcessingManager ->getUserTask ($ taskId , $ this ->userId );
357398 // set result
358399 $ this ->taskProcessingManager ->setTaskResult ($ taskId , $ errorMessage , $ output );
359- $ task = $ this ->taskProcessingManager ->getUserTask ($ taskId, $ this -> userId );
400+ $ task = $ this ->taskProcessingManager ->getTask ($ taskId );
360401
361402 /** @var CoreTaskProcessingTask $json */
362403 $ json = $ task ->jsonSerialize ();
363404
364405 return new DataResponse ([
365406 'task ' => $ json ,
366407 ]);
367- } catch (\ OCP \ TaskProcessing \ Exception \ NotFoundException $ e ) {
408+ } catch (NotFoundException ) {
368409 return new DataResponse (['message ' => $ this ->l ->t ('Not found ' )], Http::STATUS_NOT_FOUND );
369- } catch (Exception $ e ) {
410+ } catch (Exception ) {
370411 return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
371412 }
372413 }
@@ -396,9 +437,59 @@ public function cancelTask(int $taskId): DataResponse {
396437 return new DataResponse ([
397438 'task ' => $ json ,
398439 ]);
399- } catch (\ OCP \ TaskProcessing \ Exception \ NotFoundException $ e ) {
440+ } catch (NotFoundException ) {
400441 return new DataResponse (['message ' => $ this ->l ->t ('Not found ' )], Http::STATUS_NOT_FOUND );
401- } catch (Exception $ e ) {
442+ } catch (Exception ) {
443+ return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
444+ }
445+ }
446+
447+ /**
448+ * Returns the next scheduled task for the taskTypeId
449+ *
450+ * @param list<string> $providerIds The ids of the providers
451+ * @param list<string> $taskTypeIds The ids of the task types
452+ * @return DataResponse<Http::STATUS_OK, array{task: CoreTaskProcessingTask, provider: array{name: string}}, array{}>|DataResponse<Http::STATUS_NO_CONTENT, null, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
453+ *
454+ * 200: Task returned
455+ * 204: No task found
456+ */
457+ #[ExAppRequired]
458+ #[ApiRoute(verb: 'GET ' , url: '/tasks_provider/next ' , root: '/taskprocessing ' )]
459+ public function getNextScheduledTask (array $ providerIds , array $ taskTypeIds ): DataResponse {
460+ try {
461+ // restrict $providerIds to providers that are configured as preferred for the passed task types
462+ $ providerIds = array_values (array_intersect (array_unique (array_map (fn ($ taskTypeId ) => $ this ->taskProcessingManager ->getPreferredProvider ($ taskTypeId )->getId (), $ taskTypeIds )), $ providerIds ));
463+ // restrict $taskTypeIds to task types that can actually be run by one of the now restricted providers
464+ $ taskTypeIds = array_values (array_filter ($ taskTypeIds , fn ($ taskTypeId ) => in_array ($ this ->taskProcessingManager ->getPreferredProvider ($ taskTypeId )->getId (), $ providerIds , true )));
465+ if (count ($ providerIds ) === 0 || count ($ taskTypeIds ) === 0 ) {
466+ throw new NotFoundException ();
467+ }
468+
469+ $ taskIdsToIgnore = [];
470+ while (true ) {
471+ $ task = $ this ->taskProcessingManager ->getNextScheduledTask ($ taskTypeIds , $ taskIdsToIgnore );
472+ $ provider = $ this ->taskProcessingManager ->getPreferredProvider ($ task ->getTaskTypeId ());
473+ if (in_array ($ provider ->getId (), $ providerIds , true )) {
474+ if ($ this ->taskProcessingManager ->lockTask ($ task )) {
475+ break ;
476+ }
477+ }
478+ $ taskIdsToIgnore [] = (int )$ task ->getId ();
479+ }
480+
481+ /** @var CoreTaskProcessingTask $json */
482+ $ json = $ task ->jsonSerialize ();
483+
484+ return new DataResponse ([
485+ 'task ' => $ json ,
486+ 'provider ' => [
487+ 'name ' => $ provider ->getId (),
488+ ],
489+ ]);
490+ } catch (NotFoundException ) {
491+ return new DataResponse (null , Http::STATUS_NO_CONTENT );
492+ } catch (Exception ) {
402493 return new DataResponse (['message ' => $ this ->l ->t ('Internal error ' )], Http::STATUS_INTERNAL_SERVER_ERROR );
403494 }
404495 }
0 commit comments