Skip to content

Commit 3a47f42

Browse files
committed
feat(TaskProcessing): Store output files with extension to ease mimetype guessing
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
1 parent 0316285 commit 3a47f42

5 files changed

Lines changed: 96 additions & 12 deletions

File tree

lib/private/TaskProcessing/Manager.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
use OCP\TaskProcessing\Exception\UnauthorizedException;
5858
use OCP\TaskProcessing\Exception\UserFacingProcessingException;
5959
use OCP\TaskProcessing\Exception\ValidationException;
60+
use OCP\TaskProcessing\FileShaped;
6061
use OCP\TaskProcessing\IInternalTaskType;
6162
use OCP\TaskProcessing\IManager;
6263
use OCP\TaskProcessing\IProvider;
@@ -1601,14 +1602,28 @@ public function encapsulateOutputFileData(array $output, ...$specs): array {
16011602
continue;
16021603
}
16031604
if (EShapeType::getScalarType($type) === $type) {
1605+
if ($output[$key] instanceof FileShaped) {
1606+
$data = $output[$key]->getData();
1607+
$ext = $output[$key]->getExtension();
1608+
} else {
1609+
$data = $output[$key];
1610+
$ext = '';
1611+
}
16041612
/** @var SimpleFile $file */
1605-
$file = $folder->newFile(time() . '-' . rand(1, 100000), $output[$key]);
1613+
$file = $folder->newFile(time() . '-' . rand(1, 100000) . $ext, $data);
16061614
$newOutput[$key] = $file->getId(); // polymorphic call to SimpleFile
16071615
} else {
16081616
$newOutput = [];
16091617
foreach ($output[$key] as $item) {
1618+
if ($item instanceof FileShaped) {
1619+
$data = $item->getData();
1620+
$ext = $item->getExtension();
1621+
} else {
1622+
$data = $item;
1623+
$ext = '';
1624+
}
16101625
/** @var SimpleFile $file */
1611-
$file = $folder->newFile(time() . '-' . rand(1, 100000), $item);
1626+
$file = $folder->newFile(time() . '-' . rand(1, 100000) . $ext, $data);
16121627
$newOutput[$key][] = $file->getId();
16131628
}
16141629
}

lib/public/TaskProcessing/EShapeType.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99

1010
namespace OCP\TaskProcessing;
1111

12+
use OCP\AppFramework\Attribute\Consumable;
1213
use OCP\TaskProcessing\Exception\ValidationException;
1314

1415
/**
1516
* The input and output Shape types
1617
*
1718
* @since 30.0.0
1819
*/
20+
#[Consumable(since: '30.0.0')]
1921
enum EShapeType: int {
2022
/**
2123
* @since 30.0.0
@@ -156,28 +158,28 @@ public function validateInput(mixed $value): void {
156158
*/
157159
public function validateOutputWithFileData(mixed $value): void {
158160
$this->validateNonFileType($value);
159-
if ($this === EShapeType::Image && !is_string($value)) {
161+
if ($this === EShapeType::Image && !is_string($value) && !($value instanceof FileShaped && $value->getShapeType() === EShapeType::Image)) {
160162
throw new ValidationException('Non-image item provided for Image slot');
161163
}
162-
if ($this === EShapeType::ListOfImages && (!is_array($value) || count(array_filter($value, fn ($item) => !is_string($item))) > 0)) {
164+
if ($this === EShapeType::ListOfImages && (!is_array($value) || count(array_filter($value, fn ($item) => !is_string($item) && !($value instanceof FileShaped && $value->getShapeType() === EShapeType::Image))) > 0)) {
163165
throw new ValidationException('Non-image list item provided for ListOfImages slot');
164166
}
165-
if ($this === EShapeType::Audio && !is_string($value)) {
167+
if ($this === EShapeType::Audio && !is_string($value) && !($value instanceof FileShaped && $value->getShapeType() === EShapeType::Audio)) {
166168
throw new ValidationException('Non-audio item provided for Audio slot');
167169
}
168-
if ($this === EShapeType::ListOfAudios && (!is_array($value) || count(array_filter($value, fn ($item) => !is_string($item))) > 0)) {
170+
if ($this === EShapeType::ListOfAudios && (!is_array($value) || count(array_filter($value, fn ($item) => !is_string($item) && !($value instanceof FileShaped && $value->getShapeType() === EShapeType::Audio))) > 0)) {
169171
throw new ValidationException('Non-audio list item provided for ListOfAudio slot');
170172
}
171-
if ($this === EShapeType::Video && !is_string($value)) {
173+
if ($this === EShapeType::Video && !is_string($value) && !($value instanceof FileShaped && $value->getShapeType() === EShapeType::Video)) {
172174
throw new ValidationException('Non-video item provided for Video slot');
173175
}
174-
if ($this === EShapeType::ListOfVideos && (!is_array($value) || count(array_filter($value, fn ($item) => !is_string($item))) > 0)) {
175-
throw new ValidationException('Non-video list item provided for ListOfTexts slot');
176+
if ($this === EShapeType::ListOfVideos && (!is_array($value) || count(array_filter($value, fn ($item) => !is_string($item) && !($value instanceof FileShaped && $value->getShapeType() === EShapeType::Video))) > 0)) {
177+
throw new ValidationException('Non-video list item provided for ListOfVideos slot');
176178
}
177179
if ($this === EShapeType::File && !is_string($value)) {
178180
throw new ValidationException('Non-file item provided for File slot');
179181
}
180-
if ($this === EShapeType::ListOfFiles && (!is_array($value) || count(array_filter($value, fn ($item) => !is_string($item))) > 0)) {
182+
if ($this === EShapeType::ListOfFiles && (!is_array($value) || count(array_filter($value, fn ($item) => !is_string($item) && !($value instanceof FileShaped && $value->getShapeType() === EShapeType::File))) > 0)) {
181183
throw new ValidationException('Non-audio list item provided for ListOfFiles slot');
182184
}
183185
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCP\TaskProcessing;
9+
10+
use OCP\AppFramework\Attribute\Consumable;
11+
12+
/**
13+
* Data object for file-shaped output entries
14+
*
15+
* @since 50.0.0
16+
*/
17+
#[Consumable(since: '35.0.0')]
18+
class FileShaped {
19+
/**
20+
* @param EShapeType $shapeType
21+
* @param string $data
22+
* @param string $mimeType (optional)
23+
* @param string $extension (optional)
24+
*
25+
* @since 30.0.0
26+
*/
27+
public function __construct(
28+
private EShapeType $shapeType,
29+
private string $data,
30+
private string $mimeType = 'application/octet-stream',
31+
private string $extension = '.bin',
32+
) {
33+
}
34+
35+
/**
36+
* @return string
37+
* @since 35.0.0
38+
*/
39+
public function getData(): string {
40+
return $this->data;
41+
}
42+
43+
/**
44+
* @since 35.0.0
45+
*/
46+
public function getShapeType(): EShapeType {
47+
return $this->shapeType;
48+
}
49+
50+
/**
51+
* @since 35.0.0
52+
*/
53+
public function getMimeType(): string {
54+
return $this->mimeType;
55+
}
56+
57+
/**
58+
* @since 35.0.0
59+
*/
60+
public function getExtension(): string {
61+
return $this->extension;
62+
}
63+
}

lib/public/TaskProcessing/ISynchronousOptionsAwareProvider.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace OCP\TaskProcessing;
1111

12+
use OCP\AppFramework\Attribute\Implementable;
1213
use OCP\Files\File;
1314
use OCP\TaskProcessing\Exception\ProcessingException;
1415

@@ -17,6 +18,7 @@
1718
* implement a task processing provider
1819
* @since 35.0.0
1920
*/
21+
#[Implementable(since: '35.0.0')]
2022
interface ISynchronousOptionsAwareProvider extends ISynchronousProvider {
2123

2224
/**
@@ -26,7 +28,7 @@ interface ISynchronousOptionsAwareProvider extends ISynchronousProvider {
2628
* @param array<string, list<numeric|string|File>|numeric|string|File> $input The task input
2729
* @param callable(float):bool $reportProgress Report the task progress. If this returns false, that means the task was cancelled and processing should be stopped.
2830
* @param SynchronousProviderOptions $options The task options
29-
* @psalm-return array<string, list<numeric|string>|numeric|string>
31+
* @psalm-return array<string, list<numeric|string|FileShaped>|numeric|string|FileShaped>
3032
* @throws ProcessingException
3133
* @since 35.0.0
3234
*/

lib/public/TaskProcessing/ISynchronousProvider.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace OCP\TaskProcessing;
1111

12+
use OCP\AppFramework\Attribute\Implementable;
1213
use OCP\Files\File;
1314
use OCP\TaskProcessing\Exception\ProcessingException;
1415

@@ -17,6 +18,7 @@
1718
* implement a task processing provider
1819
* @since 30.0.0
1920
*/
21+
#[Implementable(since: '30.0.0')]
2022
interface ISynchronousProvider extends IProvider {
2123

2224
/**
@@ -25,7 +27,7 @@ interface ISynchronousProvider extends IProvider {
2527
* @param null|string $userId The user that created the current task
2628
* @param array<string, list<numeric|string|File>|numeric|string|File> $input The task input
2729
* @param callable(float):bool $reportProgress Report the task progress. If this returns false, that means the task was cancelled and processing should be stopped.
28-
* @psalm-return array<string, list<numeric|string>|numeric|string>
30+
* @psalm-return array<string, list<numeric|string|FileShaped>|numeric|string|FileShaped>
2931
* @throws ProcessingException
3032
* @since 30.0.0
3133
*/

0 commit comments

Comments
 (0)