improve versioning status display and delete with versions when versioning is suspended (#2689)

This commit is contained in:
Prakash Senthil Vel
2023-03-03 00:11:44 +05:30
committed by GitHub
parent c700ee491e
commit b8e14ee269
18 changed files with 375 additions and 61 deletions

View File

@@ -2289,16 +2289,21 @@ func TestBucketVersioning(t *testing.T) {
200, getVersioningResult.StatusCode, "Status Code is incorrect") 200, getVersioningResult.StatusCode, "Status Code is incorrect")
} }
bodyBytes, _ := ioutil.ReadAll(getVersioningResult.Body) bodyBytes, _ := ioutil.ReadAll(getVersioningResult.Body)
structBucketRepl := models.BucketVersioningResponse{} structBucketRepl := models.BucketVersioningResponse{
ExcludeFolders: false,
ExcludedPrefixes: nil,
MFADelete: "",
Status: "",
}
err = json.Unmarshal(bodyBytes, &structBucketRepl) err = json.Unmarshal(bodyBytes, &structBucketRepl)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
assert.Nil(err) assert.Nil(err)
} }
assert.Equal( assert.Equal(
structBucketRepl.IsVersioned, structBucketRepl.Status,
true, "Enabled",
structBucketRepl.IsVersioned, structBucketRepl.Status,
) )
fmt.Println("Versioned bucket creation test status:", response.Status) fmt.Println("Versioned bucket creation test status:", response.Status)
@@ -3045,7 +3050,7 @@ func TestSetBucketVersioning(t *testing.T) {
return return
} }
// 2. Set versioning as False // 2. Set versioning as False i.e Suspend versioning
response, err := SetBucketVersioning(bucket, false, nil, nil) response, err := SetBucketVersioning(bucket, false, nil, nil)
assert.Nil(err) assert.Nil(err)
if err != nil { if err != nil {
@@ -3069,13 +3074,18 @@ func TestSetBucketVersioning(t *testing.T) {
200, getVersioningResult.StatusCode, "Status Code is incorrect") 200, getVersioningResult.StatusCode, "Status Code is incorrect")
} }
bodyBytes, _ := ioutil.ReadAll(getVersioningResult.Body) bodyBytes, _ := ioutil.ReadAll(getVersioningResult.Body)
result := models.BucketVersioningResponse{} result := models.BucketVersioningResponse{
ExcludeFolders: false,
ExcludedPrefixes: nil,
MFADelete: "",
Status: "",
}
err = json.Unmarshal(bodyBytes, &result) err = json.Unmarshal(bodyBytes, &result)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
assert.Nil(err) assert.Nil(err)
} }
assert.Equal(false, result.IsVersioned, result) assert.Equal("Suspended", result.Status, result)
} }
func EnableBucketEncryption(bucketName, encType, kmsKeyID string) (*http.Response, error) { func EnableBucketEncryption(bucketName, encType, kmsKeyID string) (*http.Response, error) {

View File

@@ -24,7 +24,9 @@ package models
import ( import (
"context" "context"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt" "github.com/go-openapi/strfmt"
"github.com/go-openapi/swag" "github.com/go-openapi/swag"
) )
@@ -34,17 +36,90 @@ import (
// swagger:model bucketVersioningResponse // swagger:model bucketVersioningResponse
type BucketVersioningResponse struct { type BucketVersioningResponse struct {
// is versioned // exclude folders
IsVersioned bool `json:"is_versioned,omitempty"` ExcludeFolders bool `json:"ExcludeFolders,omitempty"`
// excluded prefixes
ExcludedPrefixes []*BucketVersioningResponseExcludedPrefixesItems0 `json:"ExcludedPrefixes"`
// m f a delete
MFADelete string `json:"MFADelete,omitempty"`
// status
Status string `json:"Status,omitempty"`
} }
// Validate validates this bucket versioning response // Validate validates this bucket versioning response
func (m *BucketVersioningResponse) Validate(formats strfmt.Registry) error { func (m *BucketVersioningResponse) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateExcludedPrefixes(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil return nil
} }
// ContextValidate validates this bucket versioning response based on context it is used func (m *BucketVersioningResponse) validateExcludedPrefixes(formats strfmt.Registry) error {
if swag.IsZero(m.ExcludedPrefixes) { // not required
return nil
}
for i := 0; i < len(m.ExcludedPrefixes); i++ {
if swag.IsZero(m.ExcludedPrefixes[i]) { // not required
continue
}
if m.ExcludedPrefixes[i] != nil {
if err := m.ExcludedPrefixes[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("ExcludedPrefixes" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("ExcludedPrefixes" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// ContextValidate validate this bucket versioning response based on the context it is used
func (m *BucketVersioningResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { func (m *BucketVersioningResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateExcludedPrefixes(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *BucketVersioningResponse) contextValidateExcludedPrefixes(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.ExcludedPrefixes); i++ {
if m.ExcludedPrefixes[i] != nil {
if err := m.ExcludedPrefixes[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("ExcludedPrefixes" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("ExcludedPrefixes" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil return nil
} }
@@ -65,3 +140,40 @@ func (m *BucketVersioningResponse) UnmarshalBinary(b []byte) error {
*m = res *m = res
return nil return nil
} }
// BucketVersioningResponseExcludedPrefixesItems0 bucket versioning response excluded prefixes items0
//
// swagger:model BucketVersioningResponseExcludedPrefixesItems0
type BucketVersioningResponseExcludedPrefixesItems0 struct {
// prefix
Prefix string `json:"Prefix,omitempty"`
}
// Validate validates this bucket versioning response excluded prefixes items0
func (m *BucketVersioningResponseExcludedPrefixesItems0) Validate(formats strfmt.Registry) error {
return nil
}
// ContextValidate validates this bucket versioning response excluded prefixes items0 based on context it is used
func (m *BucketVersioningResponseExcludedPrefixesItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *BucketVersioningResponseExcludedPrefixesItems0) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *BucketVersioningResponseExcludedPrefixesItems0) UnmarshalBinary(b []byte) error {
var res BucketVersioningResponseExcludedPrefixesItems0
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -54,7 +54,7 @@ import { decodeURLString, encodeURLString } from "../../../../common/utils";
import { permissionItems } from "../ListBuckets/Objects/utils"; import { permissionItems } from "../ListBuckets/Objects/utils";
import { setErrorSnackMessage } from "../../../../systemSlice"; import { setErrorSnackMessage } from "../../../../systemSlice";
import api from "../../../../common/api"; import api from "../../../../common/api";
import { BucketObjectLocking, BucketVersioning } from "../types"; import { BucketObjectLocking, BucketVersioningInfo } from "../types";
import { ErrorResponseHandler } from "../../../../common/types"; import { ErrorResponseHandler } from "../../../../common/types";
import OBHeader from "../../ObjectBrowser/OBHeader"; import OBHeader from "../../ObjectBrowser/OBHeader";
@@ -401,8 +401,8 @@ const BrowserHandler = () => {
if (displayListObjects) { if (displayListObjects) {
api api
.invoke("GET", `/api/v1/buckets/${bucketName}/versioning`) .invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
.then((res: BucketVersioning) => { .then((res: BucketVersioningInfo) => {
dispatch(setIsVersioned(res.is_versioned)); dispatch(setIsVersioned(res));
dispatch(setLoadingVersioning(false)); dispatch(setLoadingVersioning(false));
}) })
.catch((err: ErrorResponseHandler) => { .catch((err: ErrorResponseHandler) => {

View File

@@ -27,7 +27,7 @@ import {
BucketObjectLocking, BucketObjectLocking,
BucketQuota, BucketQuota,
BucketReplication, BucketReplication,
BucketVersioning, BucketVersioningInfo,
} from "../types"; } from "../types";
import { BucketList } from "../../Watch/types"; import { BucketList } from "../../Watch/types";
import { import {
@@ -61,6 +61,7 @@ import {
setBucketDetailsLoad, setBucketDetailsLoad,
} from "./bucketDetailsSlice"; } from "./bucketDetailsSlice";
import { useAppDispatch } from "../../../../store"; import { useAppDispatch } from "../../../../store";
import VersioningInfo from "../VersioningInfo";
const SetAccessPolicy = withSuspense( const SetAccessPolicy = withSuspense(
React.lazy(() => import("./SetAccessPolicy")) React.lazy(() => import("./SetAccessPolicy"))
@@ -121,7 +122,7 @@ const BucketSummary = ({ classes }: IBucketSummaryProps) => {
const [loadingQuota, setLoadingQuota] = useState<boolean>(true); const [loadingQuota, setLoadingQuota] = useState<boolean>(true);
const [loadingReplication, setLoadingReplication] = useState<boolean>(true); const [loadingReplication, setLoadingReplication] = useState<boolean>(true);
const [loadingRetention, setLoadingRetention] = useState<boolean>(true); const [loadingRetention, setLoadingRetention] = useState<boolean>(true);
const [isVersioned, setIsVersioned] = useState<boolean>(false); const [versioningInfo, setVersioningInfo] = useState<BucketVersioningInfo>();
const [quotaEnabled, setQuotaEnabled] = useState<boolean>(false); const [quotaEnabled, setQuotaEnabled] = useState<boolean>(false);
const [quota, setQuota] = useState<BucketQuota | null>(null); const [quota, setQuota] = useState<BucketQuota | null>(null);
const [encryptionEnabled, setEncryptionEnabled] = useState<boolean>(false); const [encryptionEnabled, setEncryptionEnabled] = useState<boolean>(false);
@@ -203,8 +204,8 @@ const BucketSummary = ({ classes }: IBucketSummaryProps) => {
if (loadingVersioning && distributedSetup) { if (loadingVersioning && distributedSetup) {
api api
.invoke("GET", `/api/v1/buckets/${bucketName}/versioning`) .invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
.then((res: BucketVersioning) => { .then((res: BucketVersioningInfo) => {
setIsVersioned(res.is_versioned); setVersioningInfo(res);
setLoadingVersioning(false); setLoadingVersioning(false);
}) })
.catch((err: ErrorResponseHandler) => { .catch((err: ErrorResponseHandler) => {
@@ -370,6 +371,15 @@ const BucketSummary = ({ classes }: IBucketSummaryProps) => {
loadAllBucketData(); loadAllBucketData();
} }
}; };
let versioningStatus = versioningInfo?.Status;
let versioningText = "Unversioned (Default)";
if (versioningStatus === "Enabled") {
versioningText = "Versioned";
} else if (versioningStatus === "Suspended") {
versioningText = "Suspended";
}
// @ts-ignore // @ts-ignore
return ( return (
<Fragment> <Fragment>
@@ -412,7 +422,7 @@ const BucketSummary = ({ classes }: IBucketSummaryProps) => {
closeVersioningModalAndRefresh={closeEnableVersioning} closeVersioningModalAndRefresh={closeEnableVersioning}
modalOpen={enableVersioningOpen} modalOpen={enableVersioningOpen}
selectedBucket={bucketName} selectedBucket={bucketName}
versioningCurrentState={isVersioned} versioningInfo={versioningInfo}
/> />
)} )}
@@ -576,10 +586,27 @@ const BucketSummary = ({ classes }: IBucketSummaryProps) => {
]} ]}
resourceName={bucketName} resourceName={bucketName}
property={"Current Status:"} property={"Current Status:"}
value={isVersioned ? "Versioned" : "Unversioned (Default)"} value={
<Box
sx={{
display: "flex",
flexDirection: "column",
textDecorationStyle: "normal",
placeItems: "flex-start",
justifyItems: "flex-start",
gap: 3,
}}
>
<div> {versioningText}</div>
</Box>
}
onEdit={setBucketVersioning} onEdit={setBucketVersioning}
isLoading={loadingVersioning} isLoading={loadingVersioning}
/> />
{versioningInfo?.Status === "Enabled" ? (
<VersioningInfo versioningState={versioningInfo} />
) : null}
</Box> </Box>
</Box> </Box>
</Grid> </Grid>

View File

@@ -24,20 +24,24 @@ import { ConfirmModalIcon } from "mds";
import { setErrorSnackMessage } from "../../../../systemSlice"; import { setErrorSnackMessage } from "../../../../systemSlice";
import { useAppDispatch } from "../../../../store"; import { useAppDispatch } from "../../../../store";
import { BucketVersioningInfo } from "../types";
import VersioningInfo from "../VersioningInfo";
interface IVersioningEventProps { interface IVersioningEventProps {
closeVersioningModalAndRefresh: (refresh: boolean) => void; closeVersioningModalAndRefresh: (refresh: boolean) => void;
modalOpen: boolean; modalOpen: boolean;
selectedBucket: string; selectedBucket: string;
versioningCurrentState: boolean; versioningInfo: BucketVersioningInfo | undefined;
} }
const EnableVersioningModal = ({ const EnableVersioningModal = ({
closeVersioningModalAndRefresh, closeVersioningModalAndRefresh,
modalOpen, modalOpen,
selectedBucket, selectedBucket,
versioningCurrentState, versioningInfo = {},
}: IVersioningEventProps) => { }: IVersioningEventProps) => {
const isVersioningEnabled = versioningInfo.Status === "Enabled";
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [versioningLoading, setVersioningLoading] = useState<boolean>(false); const [versioningLoading, setVersioningLoading] = useState<boolean>(false);
@@ -49,7 +53,7 @@ const EnableVersioningModal = ({
api api
.invoke("PUT", `/api/v1/buckets/${selectedBucket}/versioning`, { .invoke("PUT", `/api/v1/buckets/${selectedBucket}/versioning`, {
versioning: !versioningCurrentState, versioning: !isVersioningEnabled,
}) })
.then(() => { .then(() => {
setVersioningLoading(false); setVersioningLoading(false);
@@ -64,7 +68,7 @@ const EnableVersioningModal = ({
return ( return (
<ConfirmDialog <ConfirmDialog
title={`Versioning on Bucket`} title={`Versioning on Bucket`}
confirmText={versioningCurrentState ? "Disable" : "Enable"} confirmText={isVersioningEnabled ? "Suspend" : "Enable"}
isOpen={modalOpen} isOpen={modalOpen}
isLoading={versioningLoading} isLoading={versioningLoading}
titleIcon={<ConfirmModalIcon />} titleIcon={<ConfirmModalIcon />}
@@ -78,15 +82,24 @@ const EnableVersioningModal = ({
confirmationContent={ confirmationContent={
<DialogContentText id="alert-dialog-description"> <DialogContentText id="alert-dialog-description">
Are you sure you want to{" "} Are you sure you want to{" "}
<strong>{versioningCurrentState ? "disable" : "enable"}</strong>{" "} <strong>{isVersioningEnabled ? "suspend" : "enable"}</strong>{" "}
versioning for this bucket? versioning for this bucket?
{versioningCurrentState && ( {isVersioningEnabled && (
<Fragment> <Fragment>
<br /> <br />
<br /> <br />
<strong>File versions won't be automatically deleted.</strong> <strong>File versions won't be automatically deleted.</strong>
</Fragment> </Fragment>
)} )}
<div
style={{
paddingTop: "20px",
}}
>
{isVersioningEnabled ? (
<VersioningInfo versioningState={versioningInfo} />
) : null}
</div>
</DialogContentText> </DialogContentText>
} }
/> />

View File

@@ -28,6 +28,7 @@ import { AppState, useAppDispatch } from "../../../../../../store";
import { hasPermission } from "../../../../../../common/SecureComponent"; import { hasPermission } from "../../../../../../common/SecureComponent";
import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions"; import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { BucketVersioningInfo } from "../../../types";
interface IDeleteObjectProps { interface IDeleteObjectProps {
closeDeleteModalAndRefresh: (refresh: boolean) => void; closeDeleteModalAndRefresh: (refresh: boolean) => void;
@@ -35,7 +36,7 @@ interface IDeleteObjectProps {
selectedObjects: string[]; selectedObjects: string[];
selectedBucket: string; selectedBucket: string;
versioning: boolean; versioning: BucketVersioningInfo;
} }
const DeleteObject = ({ const DeleteObject = ({
@@ -99,6 +100,9 @@ const DeleteObject = ({
} }
}; };
const isVersionedDelete =
versioning?.Status === "Enabled" || versioning?.Status === "Suspended";
return ( return (
<ConfirmDialog <ConfirmDialog
title={`Delete Objects`} title={`Delete Objects`}
@@ -112,7 +116,7 @@ const DeleteObject = ({
<DialogContentText> <DialogContentText>
Are you sure you want to delete the selected {selectedObjects.length}{" "} Are you sure you want to delete the selected {selectedObjects.length}{" "}
objects?{" "} objects?{" "}
{versioning && ( {isVersionedDelete && (
<Fragment> <Fragment>
<br /> <br />
<br /> <br />

View File

@@ -29,6 +29,8 @@ import { AppState, useAppDispatch } from "../../../../../../store";
import { hasPermission } from "../../../../../../common/SecureComponent"; import { hasPermission } from "../../../../../../common/SecureComponent";
import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions"; import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { BucketVersioningInfo } from "../../../types";
import { isVersionedMode } from "../../../../../../utils/validationFunctions";
interface IDeleteObjectProps { interface IDeleteObjectProps {
closeDeleteModalAndRefresh: (refresh: boolean) => void; closeDeleteModalAndRefresh: (refresh: boolean) => void;
@@ -36,7 +38,7 @@ interface IDeleteObjectProps {
selectedObject: string; selectedObject: string;
selectedBucket: string; selectedBucket: string;
versioning: boolean; versioningInfo: BucketVersioningInfo | undefined;
selectedVersion?: string; selectedVersion?: string;
} }
@@ -45,7 +47,7 @@ const DeleteObject = ({
deleteOpen, deleteOpen,
selectedBucket, selectedBucket,
selectedObject, selectedObject,
versioning, versioningInfo,
selectedVersion = "", selectedVersion = "",
}: IDeleteObjectProps) => { }: IDeleteObjectProps) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@@ -120,22 +122,23 @@ const DeleteObject = ({
)} )}
? <br /> ? <br />
<br /> <br />
{versioning && selectedVersion === "" && ( {isVersionedMode(versioningInfo?.Status) &&
<Fragment> selectedVersion === "" && (
<FormSwitchWrapper <Fragment>
label={"Delete All Versions"} <FormSwitchWrapper
indicatorLabels={["Yes", "No"]} label={"Delete All Versions"}
checked={deleteVersions} indicatorLabels={["Yes", "No"]}
value={"delete_versions"} checked={deleteVersions}
id="delete-versions" value={"delete_versions"}
name="delete-versions" id="delete-versions"
onChange={(e) => { name="delete-versions"
setDeleteVersions(!deleteVersions); onChange={(e) => {
}} setDeleteVersions(!deleteVersions);
description="" }}
/> description=""
</Fragment> />
)} </Fragment>
)}
{canBypass && (deleteVersions || selectedVersion !== "") && ( {canBypass && (deleteVersions || selectedVersion !== "") && (
<Fragment> <Fragment>
<div <div

View File

@@ -253,7 +253,7 @@ const ListObjects = () => {
); );
const isVersioned = useSelector( const isVersioned = useSelector(
(state: AppState) => state.objectBrowser.isVersioned (state: AppState) => state.objectBrowser.versionInfo
); );
const lockingEnabled = useSelector( const lockingEnabled = useSelector(
(state: AppState) => state.objectBrowser.lockingEnabled (state: AppState) => state.objectBrowser.lockingEnabled
@@ -1132,7 +1132,7 @@ const ListObjects = () => {
internalPaths={selectedInternalPaths} internalPaths={selectedInternalPaths}
bucketName={bucketName} bucketName={bucketName}
onClosePanel={onClosePanel} onClosePanel={onClosePanel}
versioning={isVersioned} versioningInfo={isVersioned}
locking={lockingEnabled} locking={lockingEnabled}
/> />
)} )}

View File

@@ -82,6 +82,7 @@ import {
import RenameLongFileName from "../../../../ObjectBrowser/RenameLongFilename"; import RenameLongFileName from "../../../../ObjectBrowser/RenameLongFilename";
import TooltipWrapper from "../../../../Common/TooltipWrapper/TooltipWrapper"; import TooltipWrapper from "../../../../Common/TooltipWrapper/TooltipWrapper";
import { downloadObject } from "../../../../ObjectBrowser/utils"; import { downloadObject } from "../../../../ObjectBrowser/utils";
import { BucketVersioningInfo } from "../../../types";
const styles = () => const styles = () =>
createStyles({ createStyles({
@@ -139,7 +140,7 @@ interface IObjectDetailPanelProps {
classes: any; classes: any;
internalPaths: string; internalPaths: string;
bucketName: string; bucketName: string;
versioning: boolean; versioningInfo: BucketVersioningInfo;
locking: boolean; locking: boolean;
onClosePanel: (hardRefresh: boolean) => void; onClosePanel: (hardRefresh: boolean) => void;
} }
@@ -148,7 +149,7 @@ const ObjectDetailPanel = ({
classes, classes,
internalPaths, internalPaths,
bucketName, bucketName,
versioning, versioningInfo,
locking, locking,
onClosePanel, onClosePanel,
}: IObjectDetailPanelProps) => { }: IObjectDetailPanelProps) => {
@@ -606,7 +607,7 @@ const ObjectDetailPanel = ({
selectedBucket={bucketName} selectedBucket={bucketName}
selectedObject={internalPaths} selectedObject={internalPaths}
closeDeleteModalAndRefresh={closeDeleteModal} closeDeleteModalAndRefresh={closeDeleteModal}
versioning={distributedSetup && versioning} versioningInfo={distributedSetup ? versioningInfo : undefined}
selectedVersion={selectedVersion} selectedVersion={selectedVersion}
/> />
)} )}

View File

@@ -0,0 +1,72 @@
import React from "react";
import { Box } from "@mui/material";
import { BucketVersioningInfo } from "./types";
import LabelWithIcon from "./BucketDetails/SummaryItems/LabelWithIcon";
import { DisabledIcon, EnabledIcon } from "mds";
const VersioningInfo = ({
versioningState = {},
}: {
versioningState?: BucketVersioningInfo;
}) => {
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: 2,
}}
>
<Box sx={{ fontWeight: "medium", display: "flex", gap: 2 }}>
{versioningState.ExcludeFolders ? (
<LabelWithIcon
icon={
versioningState.ExcludeFolders ? (
<EnabledIcon style={{ color: "green" }} />
) : (
<DisabledIcon />
)
}
label={
<label style={{ textDecoration: "normal" }}>
Exclude Folders
</label>
}
/>
) : null}
</Box>
{versioningState.ExcludedPrefixes?.length ? (
<Box
sx={{
fontWeight: "medium",
display: "flex",
justifyItems: "end",
placeItems: "flex-start",
flexDirection: "column",
gap: 1,
}}
>
<Box>Excluded Prefixes :</Box>
<div
style={{
maxHeight: "200px",
overflowY: "auto",
placeItems: "flex-start",
justifyItems: "end",
flexDirection: "column",
display: "flex",
}}
>
{versioningState.ExcludedPrefixes?.map((it) => (
<div>
<strong>{it.Prefix}</strong>
</div>
))}
</div>
</Box>
) : null}
</Box>
);
};
export default VersioningInfo;

View File

@@ -81,6 +81,13 @@ export interface BucketVersioning {
is_versioned: boolean; is_versioned: boolean;
} }
export interface BucketVersioningInfo {
ExcludeFolders?: boolean;
ExcludedPrefixes?: Record<"Prefix", string>[];
MFADelete?: string;
Status?: "Enabled" | "Suspended" | "";
}
export interface BucketObjectLocking { export interface BucketObjectLocking {
object_locking_enabled: boolean; object_locking_enabled: boolean;
} }

View File

@@ -21,6 +21,7 @@ import {
IRestoreLocalObjectList, IRestoreLocalObjectList,
} from "../Buckets/ListBuckets/Objects/ListObjects/types"; } from "../Buckets/ListBuckets/Objects/ListObjects/types";
import { IRetentionConfig } from "../../../common/types"; import { IRetentionConfig } from "../../../common/types";
import { BucketVersioningInfo } from "../Buckets/types";
const defaultRewind = { const defaultRewind = {
rewindEnabled: false, rewindEnabled: false,
@@ -57,7 +58,7 @@ const initialState: ObjectBrowserState = {
records: [], records: [],
loadRecords: true, loadRecords: true,
loadingVersioning: true, loadingVersioning: true,
isVersioned: false, versionInfo: {},
lockingEnabled: false, lockingEnabled: false,
loadingLocking: false, loadingLocking: false,
selectedObjects: [], selectedObjects: [],
@@ -290,8 +291,8 @@ export const objectBrowserSlice = createSlice({
setLoadingVersioning: (state, action: PayloadAction<boolean>) => { setLoadingVersioning: (state, action: PayloadAction<boolean>) => {
state.loadingVersioning = action.payload; state.loadingVersioning = action.payload;
}, },
setIsVersioned: (state, action: PayloadAction<boolean>) => { setIsVersioned: (state, action: PayloadAction<BucketVersioningInfo>) => {
state.isVersioned = action.payload; state.versionInfo = action.payload;
}, },
setLockingEnabled: (state, action: PayloadAction<boolean>) => { setLockingEnabled: (state, action: PayloadAction<boolean>) => {
state.lockingEnabled = action.payload; state.lockingEnabled = action.payload;

View File

@@ -16,6 +16,7 @@
import { BucketObjectItem } from "../Buckets/ListBuckets/Objects/ListObjects/types"; import { BucketObjectItem } from "../Buckets/ListBuckets/Objects/ListObjects/types";
import { IRetentionConfig } from "../../../common/types"; import { IRetentionConfig } from "../../../common/types";
import { BucketVersioningInfo } from "../Buckets/types";
export const REWIND_SET_ENABLE = "REWIND/SET_ENABLE"; export const REWIND_SET_ENABLE = "REWIND/SET_ENABLE";
export const REWIND_RESET_REWIND = "REWIND/RESET_REWIND"; export const REWIND_RESET_REWIND = "REWIND/RESET_REWIND";
@@ -83,7 +84,7 @@ export interface ObjectBrowserState {
records: BucketObjectItem[]; records: BucketObjectItem[];
loadRecords: boolean; loadRecords: boolean;
loadingVersioning: boolean; loadingVersioning: boolean;
isVersioned: boolean; versionInfo: BucketVersioningInfo;
lockingEnabled: boolean; lockingEnabled: boolean;
loadingLocking: boolean; loadingLocking: boolean;
selectedObjects: string[]; selectedObjects: string[];

View File

@@ -69,3 +69,7 @@ export const commonFormValidation = (fieldsValidate: IValidation[]) => {
return returnErrors; return returnErrors;
}; };
export const isVersionedMode = (status: string | undefined) => {
return status === "Enabled" || status === "Suspended";
};

View File

@@ -2597,7 +2597,7 @@ func init() {
"tags": [ "tags": [
"Configuration" "Configuration"
], ],
"summary": "Uploads an Object.", "summary": "Uploads a file to import MinIO server config.",
"parameters": [ "parameters": [
{ {
"type": "file", "type": "file",
@@ -5906,8 +5906,25 @@ func init() {
"bucketVersioningResponse": { "bucketVersioningResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"is_versioned": { "ExcludeFolders": {
"type": "boolean" "type": "boolean"
},
"ExcludedPrefixes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"Prefix": {
"type": "string"
}
}
}
},
"MFADelete": {
"type": "string"
},
"Status": {
"type": "string"
} }
} }
}, },
@@ -11432,7 +11449,7 @@ func init() {
"tags": [ "tags": [
"Configuration" "Configuration"
], ],
"summary": "Uploads an Object.", "summary": "Uploads a file to import MinIO server config.",
"parameters": [ "parameters": [
{ {
"type": "file", "type": "file",
@@ -14172,6 +14189,14 @@ func init() {
} }
} }
}, },
"BucketVersioningResponseExcludedPrefixesItems0": {
"type": "object",
"properties": {
"Prefix": {
"type": "string"
}
}
},
"LoginRequestFeatures": { "LoginRequestFeatures": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -14867,8 +14892,20 @@ func init() {
"bucketVersioningResponse": { "bucketVersioningResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"is_versioned": { "ExcludeFolders": {
"type": "boolean" "type": "boolean"
},
"ExcludedPrefixes": {
"type": "array",
"items": {
"$ref": "#/definitions/BucketVersioningResponseExcludedPrefixesItems0"
}
},
"MFADelete": {
"type": "string"
},
"Status": {
"type": "string"
} }
} }
}, },

View File

@@ -51,7 +51,7 @@ func NewPostConfigsImport(ctx *middleware.Context, handler PostConfigsImportHand
/* /*
PostConfigsImport swagger:route POST /configs/import Configuration postConfigsImport PostConfigsImport swagger:route POST /configs/import Configuration postConfigsImport
Uploads an Object. Uploads a file to import MinIO server config.
*/ */
type PostConfigsImport struct { type PostConfigsImport struct {
Context *middleware.Context Context *middleware.Context

View File

@@ -357,9 +357,19 @@ func getBucketVersionedResponse(session *models.Principal, params bucketApi.GetB
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err)) ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
} }
excludedPrefixes := make([]*models.BucketVersioningResponseExcludedPrefixesItems0, len(res.ExcludedPrefixes))
for i, v := range res.ExcludedPrefixes {
excludedPrefixes[i] = &models.BucketVersioningResponseExcludedPrefixesItems0{
Prefix: v.Prefix,
}
}
// serialize output // serialize output
bucketVResponse := &models.BucketVersioningResponse{ bucketVResponse := &models.BucketVersioningResponse{
IsVersioned: res.Status == "Enabled", ExcludeFolders: res.ExcludeFolders,
ExcludedPrefixes: excludedPrefixes,
MFADelete: res.MFADelete,
Status: res.Status,
} }
return bucketVResponse, nil return bucketVResponse, nil
} }

View File

@@ -4908,8 +4908,20 @@ definitions:
bucketVersioningResponse: bucketVersioningResponse:
type: object type: object
properties: properties:
is_versioned: Status:
type: string
MFADelete:
type: string
ExcludedPrefixes:
type: array
items:
type: object
properties:
Prefix:
type: string
ExcludeFolders:
type: boolean type: boolean
setBucketVersioning: setBucketVersioning:
type: object type: object
properties: properties: