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

View File

@@ -24,7 +24,9 @@ package models
import (
"context"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
@@ -34,17 +36,90 @@ import (
// swagger:model bucketVersioningResponse
type BucketVersioningResponse struct {
// is versioned
IsVersioned bool `json:"is_versioned,omitempty"`
// exclude folders
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
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
}
// 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 {
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
}
@@ -65,3 +140,40 @@ func (m *BucketVersioningResponse) UnmarshalBinary(b []byte) error {
*m = res
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 { setErrorSnackMessage } from "../../../../systemSlice";
import api from "../../../../common/api";
import { BucketObjectLocking, BucketVersioning } from "../types";
import { BucketObjectLocking, BucketVersioningInfo } from "../types";
import { ErrorResponseHandler } from "../../../../common/types";
import OBHeader from "../../ObjectBrowser/OBHeader";
@@ -401,8 +401,8 @@ const BrowserHandler = () => {
if (displayListObjects) {
api
.invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
.then((res: BucketVersioning) => {
dispatch(setIsVersioned(res.is_versioned));
.then((res: BucketVersioningInfo) => {
dispatch(setIsVersioned(res));
dispatch(setLoadingVersioning(false));
})
.catch((err: ErrorResponseHandler) => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -82,6 +82,7 @@ import {
import RenameLongFileName from "../../../../ObjectBrowser/RenameLongFilename";
import TooltipWrapper from "../../../../Common/TooltipWrapper/TooltipWrapper";
import { downloadObject } from "../../../../ObjectBrowser/utils";
import { BucketVersioningInfo } from "../../../types";
const styles = () =>
createStyles({
@@ -139,7 +140,7 @@ interface IObjectDetailPanelProps {
classes: any;
internalPaths: string;
bucketName: string;
versioning: boolean;
versioningInfo: BucketVersioningInfo;
locking: boolean;
onClosePanel: (hardRefresh: boolean) => void;
}
@@ -148,7 +149,7 @@ const ObjectDetailPanel = ({
classes,
internalPaths,
bucketName,
versioning,
versioningInfo,
locking,
onClosePanel,
}: IObjectDetailPanelProps) => {
@@ -606,7 +607,7 @@ const ObjectDetailPanel = ({
selectedBucket={bucketName}
selectedObject={internalPaths}
closeDeleteModalAndRefresh={closeDeleteModal}
versioning={distributedSetup && versioning}
versioningInfo={distributedSetup ? versioningInfo : undefined}
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;
}
export interface BucketVersioningInfo {
ExcludeFolders?: boolean;
ExcludedPrefixes?: Record<"Prefix", string>[];
MFADelete?: string;
Status?: "Enabled" | "Suspended" | "";
}
export interface BucketObjectLocking {
object_locking_enabled: boolean;
}

View File

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

View File

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

View File

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

View File

@@ -2597,7 +2597,7 @@ func init() {
"tags": [
"Configuration"
],
"summary": "Uploads an Object.",
"summary": "Uploads a file to import MinIO server config.",
"parameters": [
{
"type": "file",
@@ -5906,8 +5906,25 @@ func init() {
"bucketVersioningResponse": {
"type": "object",
"properties": {
"is_versioned": {
"ExcludeFolders": {
"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": [
"Configuration"
],
"summary": "Uploads an Object.",
"summary": "Uploads a file to import MinIO server config.",
"parameters": [
{
"type": "file",
@@ -14172,6 +14189,14 @@ func init() {
}
}
},
"BucketVersioningResponseExcludedPrefixesItems0": {
"type": "object",
"properties": {
"Prefix": {
"type": "string"
}
}
},
"LoginRequestFeatures": {
"type": "object",
"properties": {
@@ -14867,8 +14892,20 @@ func init() {
"bucketVersioningResponse": {
"type": "object",
"properties": {
"is_versioned": {
"ExcludeFolders": {
"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
Uploads an Object.
Uploads a file to import MinIO server config.
*/
type PostConfigsImport struct {
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))
}
excludedPrefixes := make([]*models.BucketVersioningResponseExcludedPrefixesItems0, len(res.ExcludedPrefixes))
for i, v := range res.ExcludedPrefixes {
excludedPrefixes[i] = &models.BucketVersioningResponseExcludedPrefixesItems0{
Prefix: v.Prefix,
}
}
// serialize output
bucketVResponse := &models.BucketVersioningResponse{
IsVersioned: res.Status == "Enabled",
ExcludeFolders: res.ExcludeFolders,
ExcludedPrefixes: excludedPrefixes,
MFADelete: res.MFADelete,
Status: res.Status,
}
return bucketVResponse, nil
}

View File

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