From 09c4fd342c4b51ba0c69fd964e6f23a3f0eccad0 Mon Sep 17 00:00:00 2001 From: lukmzig Date: Mon, 16 Dec 2024 10:58:44 +0100 Subject: [PATCH] add endpoint to return original image stream --- .../Document/PreviewStreamController.php | 3 +- .../Controller/Image/StreamController.php | 98 +++++++++++++++++++ src/Asset/Encoder/TextEncoder.php | 9 +- src/Asset/Service/BinaryService.php | 24 ++++- src/Asset/Service/BinaryServiceInterface.php | 7 ++ .../Element/CollectionController.php | 2 +- ...ElementProcessingNotCompletedException.php | 3 +- ...ElementStreamResourceNotFoundException.php | 3 +- .../Api/InvalidElementTypeException.php | 8 +- .../Element/CollectionController.php | 3 +- .../Controller/Element/CreateController.php | 3 +- .../Element/CollectionController.php | 2 +- .../Element/CollectionController.php | 2 +- .../Controller/Element/CreateController.php | 2 +- .../Controller/Element/UpdateController.php | 2 +- .../Controller/Element/AssignController.php | 3 +- .../Element/CollectionController.php | 3 +- .../Controller/Element/UnassignController.php | 3 +- src/Util/Constant/ElementTypes.php | 2 + .../Element/CollectionController.php | 2 +- translations/studio_api_docs.en.yaml | 7 +- 21 files changed, 167 insertions(+), 24 deletions(-) create mode 100644 src/Asset/Controller/Image/StreamController.php diff --git a/src/Asset/Controller/Document/PreviewStreamController.php b/src/Asset/Controller/Document/PreviewStreamController.php index 84dcec946..e694fcb85 100644 --- a/src/Asset/Controller/Document/PreviewStreamController.php +++ b/src/Asset/Controller/Document/PreviewStreamController.php @@ -33,6 +33,7 @@ use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags; use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions; use Pimcore\Model\Asset\Document; @@ -97,7 +98,7 @@ public function streamDocumentPreview(int $id): StreamedResponse ); if (!$asset instanceof Document) { - throw new InvalidElementTypeException($asset->getType()); + throw new InvalidElementTypeException($asset->getType(), ElementTypes::TYPE_ASSET); } return $this->documentService->getPreviewStream($asset); diff --git a/src/Asset/Controller/Image/StreamController.php b/src/Asset/Controller/Image/StreamController.php new file mode 100644 index 000000000..8d5433056 --- /dev/null +++ b/src/Asset/Controller/Image/StreamController.php @@ -0,0 +1,98 @@ +value)] + #[Get( + path: self::PREFIX . '/assets/{id}/image/stream', + operationId: 'asset_image_stream', + description: 'asset_image_stream_description', + summary: 'asset_image_stream_summary', + tags: [Tags::Assets->name] + )] + #[IdParameter(type: 'image')] + #[SuccessResponse( + description: 'asset_image_stream_success_response', + content: [new MediaType('image/*')], + headers: [new ContentDisposition(HttpResponseHeaders::INLINE_TYPE->value)] + )] + #[DefaultResponses([ + HttpResponseCodes::BAD_REQUEST, + HttpResponseCodes::UNAUTHORIZED, + HttpResponseCodes::NOT_FOUND, + ])] + public function streamOriginalImage( + int $id, + ): StreamedResponse { + $asset = $this->assetService->getAssetElement( + $this->securityService->getCurrentUser(), + $id + ); + + return $this->binaryService->streamImage($asset); + } +} diff --git a/src/Asset/Encoder/TextEncoder.php b/src/Asset/Encoder/TextEncoder.php index dfb8289dc..f9df414ec 100644 --- a/src/Asset/Encoder/TextEncoder.php +++ b/src/Asset/Encoder/TextEncoder.php @@ -32,7 +32,14 @@ final class TextEncoder implements TextEncoderInterface public function encodeUTF8(ElementInterface $element): string { if (!$element instanceof Text) { - throw new InvalidElementTypeException('Element must be an instance of Text'); + throw new InvalidElementTypeException( + sprintf( + 'should have been (%s) but was (%s)', + Text::class, + $element->getType(), + ), + 'Asset' + ); } if ($element->getFileSize() > self::MAX_FILE_SIZE) { diff --git a/src/Asset/Service/BinaryService.php b/src/Asset/Service/BinaryService.php index 333bc55d2..a6502847d 100644 --- a/src/Asset/Service/BinaryService.php +++ b/src/Asset/Service/BinaryService.php @@ -25,6 +25,7 @@ use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidElementTypeException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidThumbnailConfigurationException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidThumbnailException; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseHeaders; use Pimcore\Bundle\StudioBackendBundle\Util\Trait\StreamedResponseTrait; use Pimcore\Messenger\AssetPreviewImageMessage; @@ -59,19 +60,32 @@ public function downloadVideoByThumbnail( string $thumbnailName ): StreamedResponse { if (!$video instanceof Video) { - throw new InvalidElementTypeException($video->getType()); + throw new InvalidElementTypeException($video->getType(), ElementTypes::TYPE_ASSET); } return $this->getVideoByThumbnail($video, $thumbnailName, HttpResponseHeaders::ATTACHMENT_TYPE->value); } + /** + * @throws ElementStreamResourceNotFoundException|InvalidElementTypeException + */ + public function streamImage( + Asset $image + ): StreamedResponse { + if (!$image instanceof Image) { + throw new InvalidElementTypeException($image->getType(), ElementTypes::TYPE_ASSET); + } + + return $this->getStreamedResponse($image, HttpResponseHeaders::INLINE_TYPE->value); + } + /** * @throws InvalidElementTypeException|InvalidThumbnailException */ public function streamPreviewImageThumbnail(Asset $image): StreamedResponse { if (!$image instanceof Image) { - throw new InvalidElementTypeException($image->getType()); + throw new InvalidElementTypeException($image->getType(), ElementTypes::TYPE_ASSET); } return $this->getStreamedResponse( @@ -85,7 +99,7 @@ public function streamImageThumbnailFromConfig( ImageDownloadConfigParameter $configParameter ): StreamedResponse { if (!$image instanceof Image) { - throw new InvalidElementTypeException($image->getType()); + throw new InvalidElementTypeException($image->getType(), ElementTypes::TYPE_ASSET); } return $this->getStreamedResponse( @@ -105,7 +119,7 @@ public function streamVideoByThumbnail( string $thumbnailName ): StreamedResponse { if (!$video instanceof Video) { - throw new InvalidElementTypeException($video->getType()); + throw new InvalidElementTypeException($video->getType(), ElementTypes::TYPE_ASSET); } return $this->getVideoByThumbnail($video, $thumbnailName, HttpResponseHeaders::INLINE_TYPE->value); @@ -122,7 +136,7 @@ public function streamVideoImageThumbnail( VideoImageStreamConfigParameter $imageConfig ): StreamedResponse { if (!$video instanceof Video) { - throw new InvalidElementTypeException($video->getType()); + throw new InvalidElementTypeException($video->getType(), ElementTypes::TYPE_ASSET); } $this->thumbnailService->validateCustomVideoThumbnailConfig($imageConfig); diff --git a/src/Asset/Service/BinaryServiceInterface.php b/src/Asset/Service/BinaryServiceInterface.php index c3143f88b..0e613a0e0 100644 --- a/src/Asset/Service/BinaryServiceInterface.php +++ b/src/Asset/Service/BinaryServiceInterface.php @@ -43,6 +43,13 @@ public function downloadVideoByThumbnail( string $thumbnailName ): StreamedResponse; + /** + * @throws ElementStreamResourceNotFoundException|InvalidElementTypeException + */ + public function streamImage( + Asset $image + ): StreamedResponse; + /** * @throws InvalidElementTypeException|InvalidThumbnailException */ diff --git a/src/Dependency/Controller/Element/CollectionController.php b/src/Dependency/Controller/Element/CollectionController.php index 3dc663377..923bce800 100644 --- a/src/Dependency/Controller/Element/CollectionController.php +++ b/src/Dependency/Controller/Element/CollectionController.php @@ -68,7 +68,7 @@ public function __construct( tags: [Tags::Dependencies->name] )] #[ElementTypeParameter] - #[IdParameter('element')] + #[IdParameter] #[PageParameter] #[PageSizeParameter] #[DependencyModeParameter] diff --git a/src/Exception/Api/ElementProcessingNotCompletedException.php b/src/Exception/Api/ElementProcessingNotCompletedException.php index 661db7551..d558a0a96 100644 --- a/src/Exception/Api/ElementProcessingNotCompletedException.php +++ b/src/Exception/Api/ElementProcessingNotCompletedException.php @@ -16,6 +16,7 @@ namespace Pimcore\Bundle\StudioBackendBundle\Exception\Api; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use function sprintf; @@ -24,7 +25,7 @@ */ final class ElementProcessingNotCompletedException extends AbstractApiException { - public function __construct(int $id, string $type = 'Element') + public function __construct(int $id, string $type = ElementTypes::TYPE_ELEMENT) { parent::__construct( HttpResponseCodes::NOT_COMPLETED->value, diff --git a/src/Exception/Api/ElementStreamResourceNotFoundException.php b/src/Exception/Api/ElementStreamResourceNotFoundException.php index 7597939bb..df20976ab 100644 --- a/src/Exception/Api/ElementStreamResourceNotFoundException.php +++ b/src/Exception/Api/ElementStreamResourceNotFoundException.php @@ -16,6 +16,7 @@ namespace Pimcore\Bundle\StudioBackendBundle\Exception\Api; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use function sprintf; @@ -24,7 +25,7 @@ */ final class ElementStreamResourceNotFoundException extends AbstractApiException { - public function __construct(int $id, string $type = 'Element') + public function __construct(int $id, string $type = ElementTypes::TYPE_ELEMENT) { parent::__construct( HttpResponseCodes::NOT_FOUND->value, diff --git a/src/Exception/Api/InvalidElementTypeException.php b/src/Exception/Api/InvalidElementTypeException.php index 0e400b34d..3b5d4caeb 100644 --- a/src/Exception/Api/InvalidElementTypeException.php +++ b/src/Exception/Api/InvalidElementTypeException.php @@ -16,6 +16,7 @@ namespace Pimcore\Bundle\StudioBackendBundle\Exception\Api; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use function sprintf; @@ -24,13 +25,14 @@ */ final class InvalidElementTypeException extends AbstractApiException { - public function __construct(string $type) + public function __construct(string $subType, string $elementType = ElementTypes::TYPE_ELEMENT) { parent::__construct( HttpResponseCodes::BAD_REQUEST->value, sprintf( - 'Invalid element type: %s', - $type + 'Invalid %s type: %s', + $elementType, + $subType ) ); } diff --git a/src/Note/Controller/Element/CollectionController.php b/src/Note/Controller/Element/CollectionController.php index e8d23bc37..d6fe2f534 100644 --- a/src/Note/Controller/Element/CollectionController.php +++ b/src/Note/Controller/Element/CollectionController.php @@ -36,6 +36,7 @@ use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions; use Pimcore\Bundle\StudioBackendBundle\Util\Trait\PaginatedResponseTrait; @@ -78,7 +79,7 @@ public function __construct( tags: [Tags::Notes->name] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter(type: ElementTypes::TYPE_ELEMENT)] #[PageParameter] #[PageSizeParameter(50)] #[NoteSortByParameter] diff --git a/src/Note/Controller/Element/CreateController.php b/src/Note/Controller/Element/CreateController.php index 6bf8fdd77..08abc5dda 100644 --- a/src/Note/Controller/Element/CreateController.php +++ b/src/Note/Controller/Element/CreateController.php @@ -32,6 +32,7 @@ use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions; use Symfony\Component\HttpFoundation\JsonResponse; @@ -66,7 +67,7 @@ public function __construct( tags: [Tags::Notes->name] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter(type: ElementTypes::TYPE_ELEMENT)] #[CreateNoteRequestBody] #[SuccessResponse( description: 'note_element_create_success_response', diff --git a/src/Property/Controller/Element/CollectionController.php b/src/Property/Controller/Element/CollectionController.php index fe6348950..a3646bbe7 100644 --- a/src/Property/Controller/Element/CollectionController.php +++ b/src/Property/Controller/Element/CollectionController.php @@ -61,7 +61,7 @@ public function __construct( tags: [Tags::Properties->value] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter] #[SuccessResponse( description: 'property_get_collection_for_element_by_type_and_id_success_response', content: new ItemsJson(ElementProperty::class) diff --git a/src/Schedule/Controller/Element/CollectionController.php b/src/Schedule/Controller/Element/CollectionController.php index 245ccbd80..3b6787446 100644 --- a/src/Schedule/Controller/Element/CollectionController.php +++ b/src/Schedule/Controller/Element/CollectionController.php @@ -59,7 +59,7 @@ public function __construct( tags: [Tags::Schedule->name] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter] #[SuccessResponse( description: 'schedule_get_collection_for_element_by_type_and_id_success_response', content: new ItemsJson(Schedule::class) diff --git a/src/Schedule/Controller/Element/CreateController.php b/src/Schedule/Controller/Element/CreateController.php index 683966860..e1c79c1fd 100644 --- a/src/Schedule/Controller/Element/CreateController.php +++ b/src/Schedule/Controller/Element/CreateController.php @@ -61,7 +61,7 @@ public function __construct( tags: [Tags::Schedule->name] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter] #[SuccessResponse( description: 'schedule_create_for_element_by_type_and_id_success_response', content: new JsonContent(ref: Schedule::class) diff --git a/src/Schedule/Controller/Element/UpdateController.php b/src/Schedule/Controller/Element/UpdateController.php index 0747ae215..b3a1e660c 100644 --- a/src/Schedule/Controller/Element/UpdateController.php +++ b/src/Schedule/Controller/Element/UpdateController.php @@ -66,7 +66,7 @@ public function __construct( tags: [Tags::Schedule->name] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter] #[ElementScheduleRequestBody] #[SuccessResponse( description: 'schedule_update_for_element_by_type_and_id_success_response', diff --git a/src/Tag/Controller/Element/AssignController.php b/src/Tag/Controller/Element/AssignController.php index 53b3d9d2f..bdc5e0846 100644 --- a/src/Tag/Controller/Element/AssignController.php +++ b/src/Tag/Controller/Element/AssignController.php @@ -26,6 +26,7 @@ use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags; use Pimcore\Bundle\StudioBackendBundle\Tag\MappedParameter\ElementParameters; use Pimcore\Bundle\StudioBackendBundle\Tag\Service\TagServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions; use Symfony\Component\HttpFoundation\JsonResponse; @@ -62,7 +63,7 @@ public function __construct( tags: [Tags::TagsForElement->value] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter] #[IdParameter(type: 'tag', name: 'tagId')] #[DefaultResponses([ HttpResponseCodes::UNAUTHORIZED, diff --git a/src/Tag/Controller/Element/CollectionController.php b/src/Tag/Controller/Element/CollectionController.php index a2bd6d665..2e2c750e0 100644 --- a/src/Tag/Controller/Element/CollectionController.php +++ b/src/Tag/Controller/Element/CollectionController.php @@ -27,6 +27,7 @@ use Pimcore\Bundle\StudioBackendBundle\Tag\Attribute\Response\Property\TagCollection; use Pimcore\Bundle\StudioBackendBundle\Tag\MappedParameter\ElementParameters; use Pimcore\Bundle\StudioBackendBundle\Tag\Service\TagServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions; use Pimcore\Bundle\StudioBackendBundle\Util\Trait\PaginatedResponseTrait; @@ -60,7 +61,7 @@ public function __construct( tags: [Tags::TagsForElement->value] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter] #[SuccessResponse( description: 'tag_get_collection_for_element_by_type_and_id_success_response', content: new CollectionJson(new TagCollection()) diff --git a/src/Tag/Controller/Element/UnassignController.php b/src/Tag/Controller/Element/UnassignController.php index 71a235fd6..1c44ebc41 100644 --- a/src/Tag/Controller/Element/UnassignController.php +++ b/src/Tag/Controller/Element/UnassignController.php @@ -26,6 +26,7 @@ use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags; use Pimcore\Bundle\StudioBackendBundle\Tag\MappedParameter\ElementParameters; use Pimcore\Bundle\StudioBackendBundle\Tag\Service\TagServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions; use Symfony\Component\HttpFoundation\JsonResponse; @@ -58,7 +59,7 @@ public function __construct( tags: [Tags::TagsForElement->value] )] #[ElementTypeParameter] - #[IdParameter(type: 'element')] + #[IdParameter] #[IdParameter(type: 'tag', name: 'tagId')] #[DefaultResponses([ HttpResponseCodes::UNAUTHORIZED, diff --git a/src/Util/Constant/ElementTypes.php b/src/Util/Constant/ElementTypes.php index 037cec8b9..39824abd3 100644 --- a/src/Util/Constant/ElementTypes.php +++ b/src/Util/Constant/ElementTypes.php @@ -27,6 +27,8 @@ public const TYPE_DATA_OBJECT = 'data-object'; + public const TYPE_ELEMENT = 'element'; + public const TYPE_OBJECT = 'object'; public const TYPE_VARIANT = 'variant'; diff --git a/src/Version/Controller/Element/CollectionController.php b/src/Version/Controller/Element/CollectionController.php index e7b610b55..9fa37ec37 100644 --- a/src/Version/Controller/Element/CollectionController.php +++ b/src/Version/Controller/Element/CollectionController.php @@ -71,7 +71,7 @@ public function __construct( tags: [Tags::Versions->name] )] #[ElementTypeParameter] - #[IdParameter('element')] + #[IdParameter] #[PageParameter] #[PageSizeParameter] #[SuccessResponse( diff --git a/translations/studio_api_docs.en.yaml b/translations/studio_api_docs.en.yaml index d5b3379f7..c51202cfe 100644 --- a/translations/studio_api_docs.en.yaml +++ b/translations/studio_api_docs.en.yaml @@ -120,11 +120,16 @@ asset_image_download_custom_description: | The {id} must be an ID of existing asset image asset_image_download_custom_success_response: Custom image binary file asset_image_download_custom_summary: Download custom image by ID and configuration -asset_image_stream_custom_summary: Stream custom image thumbnail by ID and configuration +asset_image_stream_description: | + Stream original image asset based on the provided {id}.
+ The {id} must be an ID of existing asset image +asset_image_stream_success_response: Stream of an original image asset +asset_image_stream_summary: Stream original image asset by ID asset_image_stream_custom_description: | Stream image asset thumbnail based on the provided {id} and configuration parameters.
The {id} must be an ID of existing asset image asset_image_stream_custom_success_response: Image asset stream based on custom thumbnail configuration +asset_image_stream_custom_summary: Stream custom image thumbnail by ID and configuration asset_image_stream_preview_description: | Stream image asset preview based on the provided {id}.
The {id} must be an ID of existing asset image