Skip to content

Commit dda7fd7

Browse files
committed
feat(TaskProcessing): add endpoints for ExApp access without user context
Signed-off-by: Anupam Kumar <[email protected]>
1 parent c09168e commit dda7fd7

File tree

4 files changed

+3532
-58
lines changed

4 files changed

+3532
-58
lines changed

core/Controller/TaskProcessingApiController.php

Lines changed: 196 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,10 @@ public function __construct(
5959
}
6060

6161
/**
62-
* Returns all available TaskProcessing task types
63-
*
64-
* @return DataResponse<Http::STATUS_OK, array{types: array<string, CoreTaskProcessingTaskType>}, array{}>
65-
*
66-
* 200: Task types returned
62+
* @return array<string, CoreTaskProcessingTaskType>
6763
*/
68-
#[NoAdminRequired]
69-
#[ApiRoute(verb: 'GET', url: '/tasktypes', root: '/taskprocessing')]
70-
public function taskTypes(): DataResponse {
71-
/** @var array<string, CoreTaskProcessingTaskType> $taskTypes */
72-
$taskTypes = array_map(function (array $tt) {
64+
private function getTaskTypesInternal(): array {
65+
return array_map(function (array $tt) {
7366
$tt['inputShape'] = array_map(function ($descriptor) {
7467
return $descriptor->jsonSerialize();
7568
}, $tt['inputShape']);
@@ -134,11 +127,72 @@ public function taskTypes(): DataResponse {
134127
}
135128
return $tt;
136129
}, $this->taskProcessingManager->getAvailableTaskTypes());
130+
}
131+
132+
/**
133+
* Returns all available TaskProcessing task types
134+
*
135+
* @return DataResponse<Http::STATUS_OK, array{types: array<string, CoreTaskProcessingTaskType>}, array{}>
136+
*
137+
* 200: Task types returned
138+
*/
139+
#[NoAdminRequired]
140+
#[ApiRoute(verb: 'GET', url: '/tasktypes', root: '/taskprocessing')]
141+
public function taskTypes(): DataResponse {
137142
return new DataResponse([
138-
'types' => $taskTypes,
143+
'types' => $this->getTaskTypesInternal(),
139144
]);
140145
}
141146

147+
/**
148+
* Returns all available TaskProcessing task types
149+
*
150+
* Endpoint for ExApp usage without user context
151+
*
152+
* @return DataResponse<Http::STATUS_OK, array{types: array<string, CoreTaskProcessingTaskType>}, array{}>
153+
*
154+
* 200: Task types returned
155+
*/
156+
#[ExAppRequired]
157+
#[ApiRoute(verb: 'GET', url: '/tasks_consumer/tasktypes', root: '/taskprocessing')]
158+
public function taskTypesExAppEndpoint(): DataResponse {
159+
return new DataResponse([
160+
'types' => $this->getTaskTypesInternal(),
161+
]);
162+
}
163+
164+
private function handleScheduleTaskInternal(
165+
array $input, string $type, string $appId, string $customId = '',
166+
?string $webhookUri = null, ?string $webhookMethod = null, bool $includeWatermark = true,
167+
): DataResponse {
168+
$task = new Task($type, $input, $appId, $this->userId, $customId);
169+
$task->setWebhookUri($webhookUri);
170+
$task->setWebhookMethod($webhookMethod);
171+
$task->setIncludeWatermark($includeWatermark);
172+
try {
173+
$this->taskProcessingManager->scheduleTask($task);
174+
175+
/** @var CoreTaskProcessingTask $json */
176+
$json = $task->jsonSerialize();
177+
178+
return new DataResponse([
179+
'task' => $json,
180+
]);
181+
} catch (PreConditionNotMetException) {
182+
return new DataResponse(['message' => $this->l->t('The given provider is not available')], Http::STATUS_PRECONDITION_FAILED);
183+
} catch (ValidationException $e) {
184+
return new DataResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
185+
} catch (UnauthorizedException) {
186+
if ($userId === null) {
187+
// validation error happens in the manager if no user context is given
188+
return new DataResponse(['message' => 'Cannot schedule task with files referenced without user context'], Http::STATUS_UNAUTHORIZED);
189+
}
190+
return new DataResponse(['message' => 'User does not have access to the files mentioned in the task input'], Http::STATUS_UNAUTHORIZED);
191+
} catch (Exception) {
192+
return new DataResponse(['message' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
193+
}
194+
}
195+
142196
/**
143197
* Schedules a task
144198
*
@@ -163,27 +217,67 @@ public function schedule(
163217
array $input, string $type, string $appId, string $customId = '',
164218
?string $webhookUri = null, ?string $webhookMethod = null, bool $includeWatermark = true,
165219
): DataResponse {
166-
$task = new Task($type, $input, $appId, $this->userId, $customId);
167-
$task->setWebhookUri($webhookUri);
168-
$task->setWebhookMethod($webhookMethod);
169-
$task->setIncludeWatermark($includeWatermark);
220+
return $this->handleScheduleTaskInternal(
221+
$input,
222+
$type,
223+
$appId,
224+
$customId,
225+
$webhookUri,
226+
$webhookMethod,
227+
$includeWatermark,
228+
);
229+
}
230+
231+
/**
232+
* Schedules a task
233+
*
234+
* Endpoint for ExApp usage without user context. Files cannot be referenced in this case.
235+
*
236+
* @param array<string, mixed> $input Task's input parameters
237+
* @param string $type Type of the task
238+
* @param string $appId ID of the app that will execute the task
239+
* @param string $customId An arbitrary identifier for the task
240+
* @param string|null $webhookUri URI to be requested when the task finishes
241+
* @param string|null $webhookMethod Method used for the webhook request (HTTP:GET, HTTP:POST, HTTP:PUT, HTTP:DELETE or AppAPI:APP_ID:GET, AppAPI:APP_ID:POST...)
242+
* @param bool $includeWatermark Whether to include a watermark in the output file or not
243+
* @return DataResponse<Http::STATUS_OK, array{task: CoreTaskProcessingTask}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_BAD_REQUEST|Http::STATUS_PRECONDITION_FAILED|Http::STATUS_UNAUTHORIZED, array{message: string}, array{}>
244+
*
245+
* 200: Task scheduled successfully
246+
* 400: Scheduling task is not possible
247+
* 412: Scheduling task is not possible
248+
* 401: Cannot schedule task because it references files in its input
249+
*/
250+
#[ExAppRequired]
251+
#[ApiRoute(verb: 'POST', url: '/tasks_consumer/schedule', root: '/taskprocessing')]
252+
public function scheduleExAppEndpoint(
253+
array $input, string $type, string $appId, string $customId = '',
254+
?string $webhookUri = null, ?string $webhookMethod = null, bool $includeWatermark = true,
255+
): DataResponse {
256+
return $this->handleScheduleTaskInternal(
257+
$input,
258+
$type,
259+
$appId,
260+
$customId,
261+
$webhookUri,
262+
$webhookMethod,
263+
$includeWatermark,
264+
);
265+
}
266+
267+
private function handleGetTaskInternal(int $id): DataResponse {
170268
try {
171-
$this->taskProcessingManager->scheduleTask($task);
269+
$task = $this->taskProcessingManager->getUserTask($id, $this->userId);
172270

173271
/** @var CoreTaskProcessingTask $json */
174272
$json = $task->jsonSerialize();
175273

176274
return new DataResponse([
177275
'task' => $json,
178276
]);
179-
} catch (PreConditionNotMetException) {
180-
return new DataResponse(['message' => $this->l->t('The given provider is not available')], Http::STATUS_PRECONDITION_FAILED);
181-
} catch (ValidationException $e) {
182-
return new DataResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
183-
} catch (UnauthorizedException) {
184-
return new DataResponse(['message' => 'User does not have access to the files mentioned in the task input'], Http::STATUS_UNAUTHORIZED);
185-
} catch (Exception) {
186-
return new DataResponse(['message' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
277+
} catch (NotFoundException) {
278+
return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND);
279+
} catch (RuntimeException) {
280+
return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR);
187281
}
188282
}
189283

@@ -202,18 +296,39 @@ public function schedule(
202296
#[NoAdminRequired]
203297
#[ApiRoute(verb: 'GET', url: '/task/{id}', root: '/taskprocessing')]
204298
public function getTask(int $id): DataResponse {
299+
return $this->handleGetTaskInternal($id);
300+
}
301+
302+
/**
303+
* Gets a task including status and result
304+
*
305+
* Endpoint for ExApp usage without user context
306+
*
307+
* Tasks are removed 1 week after receiving their last update
308+
*
309+
* @param int $id The id of the task
310+
*
311+
* @return DataResponse<Http::STATUS_OK, array{task: CoreTaskProcessingTask}, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
312+
*
313+
* 200: Task returned
314+
* 404: Task not found
315+
*/
316+
#[ExAppRequired]
317+
#[ApiRoute(verb: 'GET', url: '/tasks_consumer/task/{id}', root: '/taskprocessing')]
318+
public function getTaskExAppEndpoint(int $id): DataResponse {
319+
return $this->handleGetTaskInternal($id);
320+
}
321+
322+
private function handleDeleteTaskInternal(int $id): DataResponse {
205323
try {
206324
$task = $this->taskProcessingManager->getUserTask($id, $this->userId);
207325

208-
/** @var CoreTaskProcessingTask $json */
209-
$json = $task->jsonSerialize();
326+
$this->taskProcessingManager->deleteTask($task);
210327

211-
return new DataResponse([
212-
'task' => $json,
213-
]);
328+
return new DataResponse(null);
214329
} catch (NotFoundException) {
215-
return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND);
216-
} catch (RuntimeException) {
330+
return new DataResponse(null);
331+
} catch (Exception) {
217332
return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR);
218333
}
219334
}
@@ -230,17 +345,24 @@ public function getTask(int $id): DataResponse {
230345
#[NoAdminRequired]
231346
#[ApiRoute(verb: 'DELETE', url: '/task/{id}', root: '/taskprocessing')]
232347
public function deleteTask(int $id): DataResponse {
233-
try {
234-
$task = $this->taskProcessingManager->getUserTask($id, $this->userId);
235-
236-
$this->taskProcessingManager->deleteTask($task);
348+
return $this->handleDeleteTaskInternal($id);
349+
}
237350

238-
return new DataResponse(null);
239-
} catch (NotFoundException) {
240-
return new DataResponse(null);
241-
} catch (Exception) {
242-
return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR);
243-
}
351+
/**
352+
* Deletes a task
353+
*
354+
* Endpoint for ExApp usage without user context
355+
*
356+
* @param int $id The id of the task
357+
*
358+
* @return DataResponse<Http::STATUS_OK, null, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
359+
*
360+
* 200: Task deleted
361+
*/
362+
#[ExAppRequired]
363+
#[ApiRoute(verb: 'DELETE', url: '/tasks_consumer/task/{id}', root: '/taskprocessing')]
364+
public function deleteTaskExAppEndpoint(int $id): DataResponse {
365+
return $this->handleDeleteTaskInternal($id);
244366
}
245367

246368

@@ -489,18 +611,7 @@ public function setResult(int $taskId, ?array $output = null, ?string $errorMess
489611
}
490612
}
491613

492-
/**
493-
* Cancels a task
494-
*
495-
* @param int $taskId The id of the task
496-
* @return DataResponse<Http::STATUS_OK, array{task: CoreTaskProcessingTask}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
497-
*
498-
* 200: Task canceled successfully
499-
* 404: Task not found
500-
*/
501-
#[NoAdminRequired]
502-
#[ApiRoute(verb: 'POST', url: '/tasks/{taskId}/cancel', root: '/taskprocessing')]
503-
public function cancelTask(int $taskId): DataResponse {
614+
private function handleCancelTaskInternal(int $taskId): DataResponse {
504615
try {
505616
// Check if the current user can access the task
506617
$this->taskProcessingManager->getUserTask($taskId, $this->userId);
@@ -521,6 +632,38 @@ public function cancelTask(int $taskId): DataResponse {
521632
}
522633
}
523634

635+
/**
636+
* Cancels a task
637+
*
638+
* @param int $taskId The id of the task
639+
* @return DataResponse<Http::STATUS_OK, array{task: CoreTaskProcessingTask}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
640+
*
641+
* 200: Task canceled successfully
642+
* 404: Task not found
643+
*/
644+
#[NoAdminRequired]
645+
#[ApiRoute(verb: 'POST', url: '/tasks/{taskId}/cancel', root: '/taskprocessing')]
646+
public function cancelTask(int $taskId): DataResponse {
647+
return $this->handleCancelTaskInternal($taskId);
648+
}
649+
650+
/**
651+
* Cancels a task
652+
*
653+
* Endpoint for ExApp usage without user context
654+
*
655+
* @param int $taskId The id of the task
656+
* @return DataResponse<Http::STATUS_OK, array{task: CoreTaskProcessingTask}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}>
657+
*
658+
* 200: Task canceled successfully
659+
* 404: Task not found
660+
*/
661+
#[ExAppRequired]
662+
#[ApiRoute(verb: 'POST', url: '/tasks_consumer/tasks/{taskId}/cancel', root: '/taskprocessing')]
663+
public function cancelTaskExAppInternal(int $taskId): DataResponse {
664+
return $this->handleCancelTaskInternal($taskId);
665+
}
666+
524667
/**
525668
* Returns the next scheduled task for the taskTypeId
526669
*

0 commit comments

Comments
 (0)