diff --git a/apps/studio/components/interfaces/Storage/StorageExplorer/PreviewPane.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/PreviewPane.tsx index b75d7aae782..38c192e436d 100644 --- a/apps/studio/components/interfaces/Storage/StorageExplorer/PreviewPane.tsx +++ b/apps/studio/components/interfaces/Storage/StorageExplorer/PreviewPane.tsx @@ -24,6 +24,7 @@ import { } from 'ui' import { ConfirmationModal } from 'ui-patterns/Dialogs/ConfirmationModal' +import { fromLifecycleRules } from '../BucketVersioningFields.lifecycle' import { URL_EXPIRY_DURATION } from '../Storage.constants' import { StorageItem } from '../Storage.types' import { getBucketVersioningState } from '../StorageVersioning.constants' @@ -36,6 +37,7 @@ import { VersionCompareWidget } from './VersionCompareWidget' import { VersionHistory } from './VersionHistory' import { useIsStorageVersioningEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' +import { bucketLifecycleQueryOptions } from '@/data/storage/bucket-lifecycle-query' import { useObjectPurgeMutation } from '@/data/storage/versioning/object-purge-mutation' import { useObjectVersionRestoreMutation } from '@/data/storage/versioning/object-version-restore-mutation' import { @@ -52,12 +54,6 @@ const PREVIEW_SIZE_LIMIT = 10 * 1024 * 1024 // 10MB const PANEL_WIDTH = 450 -/** - * TODO(storage-versioning): read the bucket's stored policy once the Storage API - * returns it. No condition is set until then, so no row shows an expiry. - */ -const EMPTY_LIFECYCLE_POLICY: LifecyclePolicy = { expiryDays: null, maxVersions: null } - const PreviewFile = ({ item }: { item: StorageItem }) => { const { projectRef, selectedBucket, openedFolders } = useStorageExplorerStateSnapshot() const folderPath = getPathAlongOpenedFolders({ openedFolders, selectedBucket }, false) @@ -338,6 +334,7 @@ export const PreviewPane = () => { projectRef, selectedBucket, selectedFilePreview: file, + openedFolders, setSelectedItemsToDelete, setSelectedFileCustomExpiry, downloadFile, @@ -359,14 +356,29 @@ export const PreviewPane = () => { const versioningState = getBucketVersioningState(selectedBucket) + // The version endpoints address an object by its full path in the bucket, not + // by the leaf name the explorer renders. + const folderPath = getPathAlongOpenedFolders({ openedFolders, selectedBucket }, false) + const filePath = file ? [folderPath, file.name].filter(Boolean).join('/') : undefined + + const { data: lifecycle } = useQuery({ + ...bucketLifecycleQueryOptions({ projectRef, bucketId: selectedBucket?.id }), + enabled: isStorageVersioningEnabled && !!projectRef && !!selectedBucket?.id, + }) + const storedPolicy = fromLifecycleRules(lifecycle) + const lifecyclePolicy: LifecyclePolicy = { + expiryDays: storedPolicy.versionExpiryDays, + maxVersions: storedPolicy.maxNoncurrentVersions, + } + const { data: versions } = useQuery({ ...objectVersionsQueryOptions({ projectRef, bucketId: selectedBucket?.id, - objectName: file?.name, - lifecyclePolicy: EMPTY_LIFECYCLE_POLICY, + path: filePath, + lifecyclePolicy, }), - enabled: isStorageVersioningEnabled && !!projectRef && !!selectedBucket?.id && !!file?.name, + enabled: isStorageVersioningEnabled && !!projectRef && !!selectedBucket?.id && !!filePath, }) const { mutate: restoreVersion, isPending: isRestoring } = useObjectVersionRestoreMutation({ @@ -395,18 +407,18 @@ export const PreviewPane = () => { const isComparing = previewedVersion !== undefined && !previewedVersion.isCurrent const handleRestore = () => { - if (!projectRef || !selectedBucket?.id || !previewedVersion) return + if (!projectRef || !selectedBucket?.id || !previewedVersion || !filePath) return restoreVersion({ projectRef, bucketId: selectedBucket.id, - objectName: file.name, + path: filePath, versionId: previewedVersion.versionId, }) } const handlePurge = () => { - if (!projectRef || !selectedBucket?.id) return - purgeObject({ projectRef, bucketId: selectedBucket.id, objectName: file.name }) + if (!projectRef || !selectedBucket?.id || !filePath) return + purgeObject({ projectRef, bucketId: selectedBucket.id, path: filePath }) } // The compare widget replaces the top of the panel, so scroll up to show it. @@ -469,9 +481,10 @@ export const PreviewPane = () => { projectRef={projectRef} bucketId={selectedBucket?.id} objectName={file.name} + path={filePath ?? file.name} versioningState={versioningState} - lifecyclePolicy={EMPTY_LIFECYCLE_POLICY} - expirationMode="and" + lifecyclePolicy={lifecyclePolicy} + expirationMode={storedPolicy.expirationMode} mimeType={mimeType} previewedVersionId={previewedVersion?.versionId} onPreview={handlePreviewVersion} diff --git a/apps/studio/components/interfaces/Storage/StorageExplorer/VersionHistory.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/VersionHistory.tsx index a740877a7df..b814803789e 100644 --- a/apps/studio/components/interfaces/Storage/StorageExplorer/VersionHistory.tsx +++ b/apps/studio/components/interfaces/Storage/StorageExplorer/VersionHistory.tsx @@ -127,7 +127,10 @@ const VersionActionsMenu = ({ interface VersionHistoryProps { projectRef?: string bucketId?: string + /** Leaf name, for copy. */ objectName: string + /** Full path within the bucket, which is what the version endpoints address. */ + path: string versioningState: BucketVersioningState lifecyclePolicy: LifecyclePolicy expirationMode: ExpirationMode @@ -142,6 +145,7 @@ export const VersionHistory = ({ projectRef, bucketId, objectName, + path, versioningState, lifecyclePolicy, expirationMode, @@ -157,7 +161,7 @@ export const VersionHistory = ({ isError, error, isSuccess, - } = useQuery(objectVersionsQueryOptions({ projectRef, bucketId, objectName, lifecyclePolicy })) + } = useQuery(objectVersionsQueryOptions({ projectRef, bucketId, path, lifecyclePolicy })) const [versionToDelete, setVersionToDelete] = useState() @@ -197,7 +201,7 @@ export const VersionHistory = ({ const handleRestore = (version: ObjectVersion) => { if (!projectRef || !bucketId) return - restoreVersion({ projectRef, bucketId, objectName, versionId: version.versionId }) + restoreVersion({ projectRef, bucketId, path, versionId: version.versionId }) } if (isPending) return @@ -325,7 +329,7 @@ export const VersionHistory = ({ deleteVersion({ projectRef, bucketId, - objectName, + path, versionId: versionToDelete.versionId, }) }}