Skip to content

Commit

Permalink
Add object actions - download and preview
Browse files Browse the repository at this point in the history
  • Loading branch information
SanjalKatiyar committed Oct 3, 2024
1 parent 2500ce6 commit e409855
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 12 deletions.
1 change: 0 additions & 1 deletion locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,6 @@
"Create via Object Bucket Claim": "Create via Object Bucket Claim",
"Ideal for Kubernetes environments providing a more abstracted approach to managing storage resources and leveraging dynamic provisioning.": "Ideal for Kubernetes environments providing a more abstracted approach to managing storage resources and leveraging dynamic provisioning.",
"OBC references a StorageClass that uses a provisioner to interact with the S3 API and create the bucket. Kubernetes binds the OBC to the bucket, making it accessible to applications.": "OBC references a StorageClass that uses a provisioner to interact with the S3 API and create the bucket. Kubernetes binds the OBC to the bucket, making it accessible to applications.",
"Download objects": "Download objects",
"Delete objects": "Delete objects",
"Actions": "Actions",
"Create folder": "Create folder",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { GetObjectCommandOutput } from '@aws-sdk/client-s3';
import { S3Commands } from '@odf/shared/s3';
import { getName } from '@odf/shared/selectors';
import { ObjectCrFormat } from '../../../types';

type DownloadAndPreviewFunction = (
bucketName: string,
object: ObjectCrFormat,
noobaaS3: S3Commands,
setDownloadAndPreview: React.Dispatch<
React.SetStateAction<DownloadAndPreviewState>
>
) => void;

type GetObjectURL = (
bucketName: string,
object: ObjectCrFormat,
noobaaS3: S3Commands
) => Promise<string>;

export type DownloadAndPreviewState = {
isDownloading: boolean;
isPreviewing: boolean;
};

const getObjectURL: GetObjectURL = async (bucketName, object, noobaaS3) => {
const responseStream: GetObjectCommandOutput = await noobaaS3.getObject({
Bucket: bucketName,
Key: getName(object),
});
const blob = await new Response(responseStream.Body as ReadableStream).blob();

return window.URL.createObjectURL(blob);
};

export const onDownload: DownloadAndPreviewFunction = async (
bucketName,
object,
noobaaS3,
setDownloadAndPreview
) => {
try {
setDownloadAndPreview((downloadAndPreview) => ({
...downloadAndPreview,
isDownloading: true,
}));

const objectURL = await getObjectURL(bucketName, object, noobaaS3);

// create a download element and trigger download
const downloadLink = document.createElement('a');
downloadLink.href = objectURL;
downloadLink.download = getName(object);
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);

window.URL.revokeObjectURL(objectURL);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error fetching S3 object:', err);
} finally {
setDownloadAndPreview((downloadAndPreview) => ({
...downloadAndPreview,
isDownloading: false,
}));
}
};

export const onPreview: DownloadAndPreviewFunction = async (
bucketName,
object,
noobaaS3,
setDownloadAndPreview
) => {
try {
setDownloadAndPreview((downloadAndPreview) => ({
...downloadAndPreview,
isPreviewing: true,
}));

const objectURL = await getObjectURL(bucketName, object, noobaaS3);

// open the object URL in a new browser tab
window.open(objectURL, '_blank');
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error fetching S3 object:', err);
} finally {
setDownloadAndPreview((downloadAndPreview) => ({
...downloadAndPreview,
isPreviewing: false,
}));
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,7 @@ const getBulkActionsItems = (
_launcher: LaunchModal,
_selectedRows: unknown[]
): IAction[] => [
// ToDo: add bulk download & delete options
{
title: t('Download objects'),
onClick: () => undefined,
},
// ToDo: add bulk delete option
{
title: t('Delete objects'),
onClick: () => undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import { CubesIcon } from '@patternfly/react-icons';
import { ActionsColumn, Td, IAction } from '@patternfly/react-table';
import { BUCKETS_BASE_ROUTE, PREFIX } from '../../../constants';
import { ObjectCrFormat } from '../../../types';
import {
DownloadAndPreviewState,
onDownload,
onPreview,
} from '../download-and-preview/download-and-preview';

const LazyPresignedURLModal = React.lazy(
() => import('../../../modals/s3-browser/presigned-url/PresignedURLModal')
Expand All @@ -37,16 +42,24 @@ const getInlineActionsItems = (
launcher: LaunchModal,
bucketName: string,
object: ObjectCrFormat,
noobaaS3: S3Commands
noobaaS3: S3Commands,
downloadAndPreview: DownloadAndPreviewState,
setDownloadAndPreview: React.Dispatch<
React.SetStateAction<DownloadAndPreviewState>
>
): IAction[] => [
// ToDo: add inline download, preview & delete options
// ToDo: add inline delete option
{
title: t('Download'),
onClick: () => undefined,
onClick: () =>
onDownload(bucketName, object, noobaaS3, setDownloadAndPreview),
isDisabled: downloadAndPreview.isDownloading,
},
{
title: t('Preview'),
onClick: () => undefined,
onClick: () =>
onPreview(bucketName, object, noobaaS3, setDownloadAndPreview),
isDisabled: downloadAndPreview.isPreviewing,
},
{
title: t('Share with presigned URL'),
Expand Down Expand Up @@ -94,6 +107,12 @@ export const TableRow: React.FC<RowComponentType<ObjectCrFormat>> = ({
}) => {
const { t } = useCustomTranslation();

const [downloadAndPreview, setDownloadAndPreview] =
React.useState<DownloadAndPreviewState>({
isDownloading: false,
isPreviewing: false,
});

const { launcher, bucketName, foldersPath, noobaaS3 } = extraProps;
const isFolder = object.isFolder;
const name = getName(object).replace(foldersPath, '');
Expand Down Expand Up @@ -132,7 +151,9 @@ export const TableRow: React.FC<RowComponentType<ObjectCrFormat>> = ({
launcher,
bucketName,
object,
noobaaS3
noobaaS3,
downloadAndPreview,
setDownloadAndPreview
)}
/>
)}
Expand Down
5 changes: 4 additions & 1 deletion packages/shared/src/s3/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
GetObjectCommand,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { ListBuckets, ListObjectsV2, GetSignedUrl } from './types';
import { ListBuckets, ListObjectsV2, GetSignedUrl, GetObject } from './types';

export class S3Commands {
private s3Client: S3Client;
Expand Down Expand Up @@ -33,4 +33,7 @@ export class S3Commands {

getSignedUrl: GetSignedUrl = (input, expiresIn) =>
getSignedUrl(this.s3Client, new GetObjectCommand(input), { expiresIn });

getObject: GetObject = (input) =>
this.s3Client.send(new GetObjectCommand(input));
}
5 changes: 5 additions & 0 deletions packages/shared/src/s3/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ListObjectsV2CommandInput,
ListObjectsV2CommandOutput,
GetObjectCommandInput,
GetObjectCommandOutput,
} from '@aws-sdk/client-s3';

// Bucket command types
Expand All @@ -20,3 +21,7 @@ export type GetSignedUrl = (
input: GetObjectCommandInput,
expiresIn: number
) => Promise<string>;

export type GetObject = (
input: GetObjectCommandInput
) => Promise<GetObjectCommandOutput>;

0 comments on commit e409855

Please sign in to comment.