mirror of
https://github.com/OpenMaxIO/openmaxio-object-browser
synced 2026-07-01 07:41:18 -07:00
improve versioning status display and delete with versions when versioning is suspended (#2689)
This commit is contained in:
committed by
GitHub
parent
c700ee491e
commit
b8e14ee269
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
72
portal-ui/src/screens/Console/Buckets/VersioningInfo.tsx
Normal file
72
portal-ui/src/screens/Console/Buckets/VersioningInfo.tsx
Normal 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;
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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[];
|
||||||
|
|||||||
@@ -69,3 +69,7 @@ export const commonFormValidation = (fieldsValidate: IValidation[]) => {
|
|||||||
|
|
||||||
return returnErrors;
|
return returnErrors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isVersionedMode = (status: string | undefined) => {
|
||||||
|
return status === "Enabled" || status === "Suspended";
|
||||||
|
};
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user