Added Exclude Folders & Exclude Prefixes support (#2973)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2023-08-02 13:35:00 -06:00
committed by GitHub
parent 0d628f589a
commit 49f856bdd5
17 changed files with 433 additions and 194 deletions

View File

@@ -31,7 +31,7 @@ import (
func Test_AddAccessRuleAPI(t *testing.T) { func Test_AddAccessRuleAPI(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
AddBucket("testaccessruleadd", false, false, nil, nil) AddBucket("testaccessruleadd", false, nil, nil, nil)
type args struct { type args struct {
bucket string bucket string
@@ -111,7 +111,7 @@ func Test_AddAccessRuleAPI(t *testing.T) {
func Test_GetAccessRulesAPI(t *testing.T) { func Test_GetAccessRulesAPI(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
AddBucket("testaccessruleget", false, false, nil, nil) AddBucket("testaccessruleget", false, nil, nil, nil)
type args struct { type args struct {
bucket string bucket string
@@ -161,7 +161,7 @@ func Test_GetAccessRulesAPI(t *testing.T) {
func Test_DeleteAccessRuleAPI(t *testing.T) { func Test_DeleteAccessRuleAPI(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
AddBucket("testaccessruledelete", false, false, nil, nil) AddBucket("testaccessruledelete", false, nil, nil, nil)
type args struct { type args struct {
prefix string prefix string

View File

@@ -42,14 +42,14 @@ import (
type AddBucketOps struct { type AddBucketOps struct {
Name string Name string
Locking bool Locking bool
Versioning bool Versioning map[string]interface{}
Quota map[string]interface{} Quota map[string]interface{}
Retention map[string]interface{} Retention map[string]interface{}
Endpoint *string Endpoint *string
UseToken *string UseToken *string
} }
func AddBucket(name string, locking, versioning bool, quota, retention map[string]interface{}) (*http.Response, error) { func AddBucket(name string, locking bool, versioning, quota, retention map[string]interface{}) (*http.Response, error) {
return AddBucketWithOpts(&AddBucketOps{ return AddBucketWithOpts(&AddBucketOps{
Name: name, Name: name,
Locking: locking, Locking: locking,
@@ -142,11 +142,11 @@ func getTokenForEndpoint(endpoint string) string {
return loginToken return loginToken
} }
func setupBucket(name string, locking, versioning bool, quota, retention map[string]interface{}, assert *assert.Assertions, expected int) bool { func setupBucket(name string, locking bool, versioning, quota, retention map[string]interface{}, assert *assert.Assertions, expected int) bool {
return setupBucketForEndpoint(name, locking, versioning, quota, retention, assert, expected, nil, nil) return setupBucketForEndpoint(name, locking, versioning, quota, retention, assert, expected, nil, nil)
} }
func setupBucketForEndpoint(name string, locking, versioning bool, quota, retention map[string]interface{}, assert *assert.Assertions, expected int, endpoint, endpointToken *string) bool { func setupBucketForEndpoint(name string, locking bool, versioning, quota, retention map[string]interface{}, assert *assert.Assertions, expected int, endpoint, endpointToken *string) bool {
/* /*
The intention of this function is to return either true or false to The intention of this function is to return either true or false to
reduce the code by performing the verification in one place only. reduce the code by performing the verification in one place only.
@@ -751,7 +751,7 @@ func TestPutObjectsLegalholdStatus(t *testing.T) {
status := "enabled" status := "enabled"
// 1. Create bucket // 1. Create bucket
if !setupBucket(bucketName, true, true, nil, nil, assert, 200) { if !setupBucket(bucketName, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -838,7 +838,7 @@ func TestGetBucketQuota(t *testing.T) {
validBucket := "testgetbucketquota" validBucket := "testgetbucketquota"
// 1. Create bucket // 1. Create bucket
if !setupBucket(validBucket, true, true, nil, nil, assert, 200) { if !setupBucket(validBucket, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -915,7 +915,7 @@ func TestPutBucketQuota(t *testing.T) {
validBucket := "testputbucketquota" validBucket := "testputbucketquota"
// 1. Create bucket // 1. Create bucket
if !setupBucket(validBucket, true, true, nil, nil, assert, 200) { if !setupBucket(validBucket, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -974,7 +974,7 @@ func TestListBucketEvents(t *testing.T) {
validBucket := "testlistbucketevents" validBucket := "testlistbucketevents"
// 1. Create bucket // 1. Create bucket
if !setupBucket(validBucket, true, true, nil, nil, assert, 200) { if !setupBucket(validBucket, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -1032,7 +1032,7 @@ func TestDeleteObjectsRetentionStatus(t *testing.T) {
validPrefix := encodeBase64(fileName) validPrefix := encodeBase64(fileName)
// 1. Create bucket // 1. Create bucket
if !setupBucket(bucketName, true, true, nil, nil, assert, 200) { if !setupBucket(bucketName, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -1139,7 +1139,7 @@ func TestBucketSetPolicy(t *testing.T) {
validBucketName := "testbucketsetpolicy" validBucketName := "testbucketsetpolicy"
// 1. Create bucket // 1. Create bucket
if !setupBucket(validBucketName, true, true, nil, nil, assert, 200) { if !setupBucket(validBucketName, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -1200,7 +1200,7 @@ func TestRestoreObjectToASelectedVersion(t *testing.T) {
validPrefix := encodeBase64(fileName) validPrefix := encodeBase64(fileName)
// 1. Create bucket // 1. Create bucket
if !setupBucket(bucketName, true, true, nil, nil, assert, 200) { if !setupBucket(bucketName, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -1288,7 +1288,7 @@ func TestPutBucketsTags(t *testing.T) {
// 1. Create the bucket // 1. Create the bucket
assert := assert.New(t) assert := assert.New(t)
validBucketName := "testputbuckettags1" validBucketName := "testputbuckettags1"
if !setupBucket(validBucketName, false, false, nil, nil, assert, 200) { if !setupBucket(validBucketName, false, nil, nil, nil, assert, 200) {
return return
} }
@@ -1346,7 +1346,7 @@ func TestGetsTheMetadataOfAnObject(t *testing.T) {
tags["tag"] = "testputobjecttagbucketonetagone" tags["tag"] = "testputobjecttagbucketonetagone"
// 1. Create the bucket // 1. Create the bucket
if !setupBucket(bucketName, false, false, nil, nil, assert, 200) { if !setupBucket(bucketName, false, nil, nil, nil, assert, 200) {
return return
} }
@@ -1417,7 +1417,7 @@ func TestPutObjectsRetentionStatus(t *testing.T) {
prefix := encodeBase64(fileName) prefix := encodeBase64(fileName)
// 1. Create bucket // 1. Create bucket
if !setupBucket(bucketName, true, true, nil, nil, assert, 200) { if !setupBucket(bucketName, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -1515,7 +1515,7 @@ func TestShareObjectOnURL(t *testing.T) {
versionID := "null" versionID := "null"
// 1. Create the bucket // 1. Create the bucket
if !setupBucket(bucketName, false, false, nil, nil, assert, 200) { if !setupBucket(bucketName, false, nil, nil, nil, assert, 200) {
return return
} }
@@ -1589,7 +1589,7 @@ func TestListObjects(t *testing.T) {
fileName := "testlistobjecttobucket1.txt" fileName := "testlistobjecttobucket1.txt"
// 1. Create the bucket // 1. Create the bucket
if !setupBucket(bucketName, false, false, nil, nil, assert, 200) { if !setupBucket(bucketName, false, nil, nil, nil, assert, 200) {
return return
} }
@@ -1637,7 +1637,7 @@ func TestDeleteObject(t *testing.T) {
numberOfFiles := 2 numberOfFiles := 2
// 1. Create bucket // 1. Create bucket
if !setupBucket(bucketName, true, true, nil, nil, assert, 200) { if !setupBucket(bucketName, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -1703,7 +1703,7 @@ func TestUploadObjectToBucket(t *testing.T) {
fileName := "sample.txt" fileName := "sample.txt"
// 1. Create the bucket // 1. Create the bucket
if !setupBucket(bucketName, false, false, nil, nil, assert, 200) { if !setupBucket(bucketName, false, nil, nil, nil, assert, 200) {
return return
} }
@@ -1738,7 +1738,7 @@ func TestDownloadObject(t *testing.T) {
} }
// 1. Create the bucket // 1. Create the bucket
if !setupBucket(bucketName, true, true, nil, nil, assert, 200) { if !setupBucket(bucketName, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
@@ -1800,7 +1800,7 @@ func TestDeleteMultipleObjects(t *testing.T) {
fileName := "testdeletemultipleobjs" fileName := "testdeletemultipleobjs"
// 1. Create a bucket for this particular test // 1. Create a bucket for this particular test
if !setupBucket(bucketName, false, false, nil, nil, assert, 200) { if !setupBucket(bucketName, false, nil, nil, nil, assert, 200) {
return return
} }
@@ -1877,7 +1877,7 @@ func TestPutObjectTag(t *testing.T) {
versionID := "null" versionID := "null"
// 1. Create the bucket // 1. Create the bucket
if !setupBucket(bucketName, false, false, nil, nil, assert, 200) { if !setupBucket(bucketName, false, nil, nil, nil, assert, 200) {
return return
} }
@@ -1951,7 +1951,7 @@ func TestBucketRetention(t *testing.T) {
retention["mode"] = "compliance" retention["mode"] = "compliance"
retention["unit"] = "years" retention["unit"] = "years"
retention["validity"] = 2 retention["validity"] = 2
if !setupBucket("setbucketretention1", true, true, nil, retention, assert, 200) { if !setupBucket("setbucketretention1", true, map[string]interface{}{"enabled": true}, nil, retention, assert, 200) {
return return
} }
@@ -2002,7 +2002,7 @@ func TestBucketInformationGenericErrorResponse(t *testing.T) {
// 1. Create the bucket // 1. Create the bucket
assert := assert.New(t) assert := assert.New(t)
if !setupBucket("bucketinformation2", false, false, nil, nil, assert, 200) { if !setupBucket("bucketinformation2", false, nil, nil, nil, assert, 200) {
return return
} }
@@ -2047,7 +2047,7 @@ func TestBucketInformationSuccessfulResponse(t *testing.T) {
// 1. Create the bucket // 1. Create the bucket
assert := assert.New(t) assert := assert.New(t)
if !setupBucket("bucketinformation1", false, false, nil, nil, assert, 200) { if !setupBucket("bucketinformation1", false, nil, nil, nil, assert, 200) {
return return
} }
@@ -2160,7 +2160,7 @@ func TestListBuckets(t *testing.T) {
// 1. Create buckets // 1. Create buckets
numberOfBuckets := 3 numberOfBuckets := 3
for i := 1; i <= numberOfBuckets; i++ { for i := 1; i <= numberOfBuckets; i++ {
if !setupBucket("testlistbuckets"+strconv.Itoa(i), false, false, nil, nil, assert, 200) { if !setupBucket("testlistbuckets"+strconv.Itoa(i), false, nil, nil, nil, assert, 200) {
return return
} }
} }
@@ -2267,7 +2267,7 @@ func TestBucketVersioning(t *testing.T) {
requestDataBody := bytes.NewReader(requestDataJSON) requestDataBody := bytes.NewReader(requestDataJSON)
if !setupBucket("test2", true, false, nil, nil, assert, 200) { if !setupBucket("test2", true, nil, nil, nil, assert, 200) {
return return
} }
@@ -2335,7 +2335,7 @@ func TestSetBucketTags(t *testing.T) {
} }
// put bucket // put bucket
if !setupBucket("test4", false, false, nil, nil, assert, 200) { if !setupBucket("test4", false, nil, nil, nil, assert, 200) {
return return
} }
@@ -2402,7 +2402,7 @@ func TestGetBucket(t *testing.T) {
Timeout: 2 * time.Second, Timeout: 2 * time.Second,
} }
if !setupBucket("test3", false, false, nil, nil, assert, 200) { if !setupBucket("test3", false, nil, nil, nil, assert, 200) {
return return
} }
@@ -2455,7 +2455,7 @@ func TestAddBucket(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if !setupBucket(tt.args.bucketName, false, false, nil, nil, assert, tt.expectedStatus) { if !setupBucket(tt.args.bucketName, false, nil, nil, nil, assert, tt.expectedStatus) {
return return
} }
}) })
@@ -3000,7 +3000,7 @@ func TestReturnsTheStatusOfObjectLockingSupportOnTheBucket(t *testing.T) {
) )
} }
func SetBucketVersioning(bucketName string, versioning bool, endpoint, useToken *string) (*http.Response, error) { func SetBucketVersioning(bucketName string, versioning map[string]interface{}, endpoint, useToken *string) (*http.Response, error) {
/* /*
Helper function to set Bucket Versioning Helper function to set Bucket Versioning
*/ */
@@ -3037,7 +3037,7 @@ func TestSetBucketVersioning(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
bucket := "test-set-bucket-versioning" bucket := "test-set-bucket-versioning"
locking := false locking := false
versioning := true versioning := map[string]interface{}{"enabled": true}
// 1. Create bucket with versioning as true and locking as false // 1. Create bucket with versioning as true and locking as false
if !setupBucket(bucket, locking, versioning, nil, nil, assert, 200) { if !setupBucket(bucket, locking, versioning, nil, nil, assert, 200) {
@@ -3045,7 +3045,7 @@ func TestSetBucketVersioning(t *testing.T) {
} }
// 2. Set versioning as False i.e Suspend versioning // 2. Set versioning as False i.e Suspend versioning
response, err := SetBucketVersioning(bucket, false, nil, nil) response, err := SetBucketVersioning(bucket, map[string]interface{}{"enabled": false}, nil, nil)
assert.Nil(err) assert.Nil(err)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@@ -3118,12 +3118,11 @@ func TestEnableBucketEncryption(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
bucketName := "test-enable-bucket-encryption" bucketName := "test-enable-bucket-encryption"
locking := false locking := false
versioning := false
encType := "sse-s3" encType := "sse-s3"
kmsKeyID := "" kmsKeyID := ""
// 1. Add bucket // 1. Add bucket
if !setupBucket(bucketName, locking, versioning, nil, nil, assert, 200) { if !setupBucket(bucketName, locking, nil, nil, nil, assert, 200) {
return return
} }
@@ -3381,7 +3380,6 @@ func TestBucketLifeCycle(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
bucketName := "test-bucket-life-cycle" bucketName := "test-bucket-life-cycle"
locking := false locking := false
versioning := false
ltype := "expiry" ltype := "expiry"
prefix := "" prefix := ""
tags := "" tags := ""
@@ -3392,7 +3390,7 @@ func TestBucketLifeCycle(t *testing.T) {
var noncurrentversionExpirationDays int64 var noncurrentversionExpirationDays int64
// 1. Add bucket // 1. Add bucket
if !setupBucket(bucketName, locking, versioning, nil, nil, assert, 200) { if !setupBucket(bucketName, locking, nil, nil, nil, assert, 200) {
return return
} }
@@ -3597,12 +3595,11 @@ func TestAccessRule(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
bucketName := "test-access-rule-bucket" bucketName := "test-access-rule-bucket"
locking := false locking := false
versioning := false
prefix := "prefix" prefix := "prefix"
access := "readonly" access := "readonly"
// 1. Add bucket // 1. Add bucket
if !setupBucket(bucketName, locking, versioning, nil, nil, assert, 200) { if !setupBucket(bucketName, locking, nil, nil, nil, assert, 200) {
return return
} }
@@ -3846,16 +3843,16 @@ func TestAddRemoteBucket(t *testing.T) {
fmt.Println("targetBucket: ", targetBucket) fmt.Println("targetBucket: ", targetBucket)
// 1. Create bucket // 1. Create bucket
if !setupBucket("source", true, true, nil, nil, assert, 200) { if !setupBucket("source", true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
// 1.1. Create target bucket // 1.1. Create target bucket
targetEndpoint := "http://localhost:9092" targetEndpoint := "http://localhost:9092"
targetToken := getTokenForEndpoint(targetEndpoint) targetToken := getTokenForEndpoint(targetEndpoint)
if !setupBucketForEndpoint(targetBucket, true, true, nil, nil, assert, 200, &targetEndpoint, &targetToken) { if !setupBucketForEndpoint(targetBucket, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200, &targetEndpoint, &targetToken) {
log.Println("bucket already exists") log.Println("bucket already exists")
} }
_, err := SetBucketVersioning(targetBucket, false, &targetURL, &targetToken) _, err := SetBucketVersioning(targetBucket, map[string]interface{}{"enabled": false}, &targetURL, &targetToken)
if err != nil { if err != nil {
log.Println("bucket already has versioning") log.Println("bucket already has versioning")
} }
@@ -3905,16 +3902,16 @@ func TestDeleteRemoteBucket(t *testing.T) {
fmt.Println("targetBucket: ", targetBucket) fmt.Println("targetBucket: ", targetBucket)
// 1. Create bucket // 1. Create bucket
if !setupBucket("deletesource", true, true, nil, nil, assert, 200) { if !setupBucket("deletesource", true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200) {
return return
} }
// 1.1. Create target bucket // 1.1. Create target bucket
targetEndpoint := "http://localhost:9092" targetEndpoint := "http://localhost:9092"
targetToken := getTokenForEndpoint(targetEndpoint) targetToken := getTokenForEndpoint(targetEndpoint)
if !setupBucketForEndpoint(targetBucket, true, true, nil, nil, assert, 200, &targetEndpoint, &targetToken) { if !setupBucketForEndpoint(targetBucket, true, map[string]interface{}{"enabled": true}, nil, nil, assert, 200, &targetEndpoint, &targetToken) {
log.Println("bucket already exists") log.Println("bucket already exists")
} }
_, err := SetBucketVersioning(targetBucket, false, &targetURL, &targetToken) _, err := SetBucketVersioning(targetBucket, map[string]interface{}{"enabled": false}, &targetURL, &targetToken)
if err != nil { if err != nil {
log.Println("bucket already has versioning") log.Println("bucket already has versioning")
} }

View File

@@ -50,7 +50,7 @@ type MakeBucketRequest struct {
Retention *PutBucketRetentionRequest `json:"retention,omitempty"` Retention *PutBucketRetentionRequest `json:"retention,omitempty"`
// versioning // versioning
Versioning bool `json:"versioning,omitempty"` Versioning *SetBucketVersioning `json:"versioning,omitempty"`
} }
// Validate validates this make bucket request // Validate validates this make bucket request
@@ -69,6 +69,10 @@ func (m *MakeBucketRequest) Validate(formats strfmt.Registry) error {
res = append(res, err) res = append(res, err)
} }
if err := m.validateVersioning(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 { if len(res) > 0 {
return errors.CompositeValidationError(res...) return errors.CompositeValidationError(res...)
} }
@@ -122,6 +126,25 @@ func (m *MakeBucketRequest) validateRetention(formats strfmt.Registry) error {
return nil return nil
} }
func (m *MakeBucketRequest) validateVersioning(formats strfmt.Registry) error {
if swag.IsZero(m.Versioning) { // not required
return nil
}
if m.Versioning != nil {
if err := m.Versioning.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("versioning")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("versioning")
}
return err
}
}
return nil
}
// ContextValidate validate this make bucket request based on the context it is used // ContextValidate validate this make bucket request based on the context it is used
func (m *MakeBucketRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { func (m *MakeBucketRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error var res []error
@@ -134,6 +157,10 @@ func (m *MakeBucketRequest) ContextValidate(ctx context.Context, formats strfmt.
res = append(res, err) res = append(res, err)
} }
if err := m.contextValidateVersioning(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 { if len(res) > 0 {
return errors.CompositeValidationError(res...) return errors.CompositeValidationError(res...)
} }
@@ -172,6 +199,22 @@ func (m *MakeBucketRequest) contextValidateRetention(ctx context.Context, format
return nil return nil
} }
func (m *MakeBucketRequest) contextValidateVersioning(ctx context.Context, formats strfmt.Registry) error {
if m.Versioning != nil {
if err := m.Versioning.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("versioning")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("versioning")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation // MarshalBinary interface implementation
func (m *MakeBucketRequest) MarshalBinary() ([]byte, error) { func (m *MakeBucketRequest) MarshalBinary() ([]byte, error) {
if m == nil { if m == nil {

View File

@@ -34,8 +34,14 @@ import (
// swagger:model setBucketVersioning // swagger:model setBucketVersioning
type SetBucketVersioning struct { type SetBucketVersioning struct {
// versioning // enabled
Versioning bool `json:"versioning,omitempty"` Enabled bool `json:"enabled,omitempty"`
// exclude folders
ExcludeFolders bool `json:"excludeFolders,omitempty"`
// exclude prefixes
ExcludePrefixes []string `json:"excludePrefixes"`
} }
// Validate validates this set bucket versioning // Validate validates this set bucket versioning

View File

@@ -126,7 +126,7 @@ export interface BucketObject {
export interface MakeBucketRequest { export interface MakeBucketRequest {
name: string; name: string;
locking?: boolean; locking?: boolean;
versioning?: boolean; versioning?: SetBucketVersioning;
quota?: SetBucketQuota; quota?: SetBucketQuota;
retention?: PutBucketRetentionRequest; retention?: PutBucketRetentionRequest;
} }
@@ -802,7 +802,10 @@ export interface BucketVersioningResponse {
} }
export interface SetBucketVersioning { export interface SetBucketVersioning {
versioning?: boolean; enabled?: boolean;
/** @maxLength 10 */
excludePrefixes?: string[];
excludeFolders?: boolean;
} }
export interface BucketObLockingResponse { export interface BucketObLockingResponse {

View File

@@ -407,6 +407,7 @@ const BucketSummary = () => {
modalOpen={enableVersioningOpen} modalOpen={enableVersioningOpen}
selectedBucket={bucketName} selectedBucket={bucketName}
versioningInfo={versioningInfo} versioningInfo={versioningInfo}
objectLockingEnabled={!!hasObjectLocking}
/> />
)} )}
@@ -581,6 +582,7 @@ const BucketSummary = () => {
} }
onEdit={setBucketVersioning} onEdit={setBucketVersioning}
isLoading={loadingVersioning} isLoading={loadingVersioning}
disabled={hasObjectLocking}
/> />
{versioningInfo?.status === "Enabled" ? ( {versioningInfo?.status === "Enabled" ? (

View File

@@ -15,32 +15,54 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useState } from "react"; import React, { Fragment, useState } from "react";
import { Box, ConfirmModalIcon } from "mds"; import { Box, Button, FormLayout, ModalBox, Switch } from "mds";
import { BucketVersioningResponse } from "api/consoleApi"; import { BucketVersioningResponse } from "api/consoleApi";
import { api } from "api"; import { api } from "api";
import { errorToHandler } from "api/errors"; import { errorToHandler } from "api/errors";
import { setErrorSnackMessage } from "../../../../systemSlice"; import { setErrorSnackMessage } from "../../../../systemSlice";
import { useAppDispatch } from "../../../../store"; import { useAppDispatch } from "../../../../store";
import VersioningInfo from "../VersioningInfo"; import CSVMultiSelector from "../../Common/FormComponents/CSVMultiSelector/CSVMultiSelector";
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog"; import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary";
interface IVersioningEventProps { interface IVersioningEventProps {
closeVersioningModalAndRefresh: (refresh: boolean) => void; closeVersioningModalAndRefresh: (refresh: boolean) => void;
modalOpen: boolean; modalOpen: boolean;
selectedBucket: string; selectedBucket: string;
versioningInfo: BucketVersioningResponse | undefined; versioningInfo: BucketVersioningResponse | undefined;
objectLockingEnabled: boolean;
} }
const parseExcludedPrefixes = (
bucketVersioning: BucketVersioningResponse | undefined,
) => {
const excludedPrefixes = bucketVersioning?.excludedPrefixes;
if (excludedPrefixes) {
return excludedPrefixes.map((item) => item.prefix).join(",");
}
return "";
};
const EnableVersioningModal = ({ const EnableVersioningModal = ({
closeVersioningModalAndRefresh, closeVersioningModalAndRefresh,
modalOpen, modalOpen,
selectedBucket, selectedBucket,
versioningInfo = {}, versioningInfo = {},
objectLockingEnabled,
}: 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);
const [versionState, setVersionState] = useState<boolean>(
versioningInfo?.status === "Enabled",
);
const [excludeFolders, setExcludeFolders] = useState<boolean>(
!!versioningInfo?.excludeFolders,
);
const [excludedPrefixes, setExcludedPrefixes] = useState<string>(
parseExcludedPrefixes(versioningInfo),
);
const enableVersioning = () => { const enableVersioning = () => {
if (versioningLoading) { if (versioningLoading) {
@@ -50,7 +72,11 @@ const EnableVersioningModal = ({
api.buckets api.buckets
.setBucketVersioning(selectedBucket, { .setBucketVersioning(selectedBucket, {
versioning: !isVersioningEnabled, enabled: versionState,
excludeFolders: versionState ? excludeFolders : false,
excludePrefixes: versionState
? excludedPrefixes.split(",").filter((item) => item.trim() !== "")
: [],
}) })
.then(() => { .then(() => {
setVersioningLoading(false); setVersioningLoading(false);
@@ -62,44 +88,76 @@ const EnableVersioningModal = ({
}); });
}; };
const resetForm = () => {
setExcludedPrefixes("");
setExcludeFolders(false);
setVersionState(false);
};
return ( return (
<ConfirmDialog <ModalBox
onClose={() => closeVersioningModalAndRefresh(false)}
open={modalOpen}
title={`Versioning on Bucket`} title={`Versioning on Bucket`}
confirmText={isVersioningEnabled ? "Suspend" : "Enable"} >
isOpen={modalOpen} <FormLayout withBorders={false} containerPadding={false}>
isLoading={versioningLoading} <Switch
titleIcon={<ConfirmModalIcon />} id={"activateVersioning"}
onConfirm={enableVersioning} label={"Versioning Status"}
confirmButtonProps={{ checked={versionState}
variant: "callAction", onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVersionState(e.target.checked);
}} }}
onClose={() => { indicatorLabels={["Enabled", "Disabled"]}
closeVersioningModalAndRefresh(false); />
}} {versionState && !objectLockingEnabled && (
confirmationContent={
<Box id="alert-dialog-description">
Are you sure you want to{" "}
<strong>{isVersioningEnabled ? "suspend" : "enable"}</strong>{" "}
versioning for this bucket?
{isVersioningEnabled && (
<Fragment> <Fragment>
<br /> <Switch
<br /> id={"excludeFolders"}
<strong>File versions won't be automatically deleted.</strong> label={"Exclude Folders"}
checked={excludeFolders}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setExcludeFolders(e.target.checked);
}}
indicatorLabels={["Enabled", "Disabled"]}
/>
<CSVMultiSelector
elements={excludedPrefixes}
label={"Excluded Prefixes"}
name={"excludedPrefixes"}
onChange={(value: string | string[]) => {
let valCh = "";
if (Array.isArray(value)) {
valCh = value.join(",");
} else {
valCh = value;
}
setExcludedPrefixes(valCh);
}}
withBorder={true}
/>
</Fragment> </Fragment>
)} )}
<Box <Box sx={modalStyleUtils.modalButtonBar}>
sx={{ <Button
paddingTop: "20px", id={"clear"}
}} type="button"
> variant="regular"
{isVersioningEnabled ? ( color="primary"
<VersioningInfo versioningState={versioningInfo} /> onClick={resetForm}
) : null} label={"Clear"}
</Box>
</Box>
}
/> />
<Button
type="submit"
variant="callAction"
onClick={enableVersioning}
id="saveTag"
label={"Save"}
/>
</Box>
</FormLayout>
</ModalBox>
); );
}; };

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react"; import React from "react";
import { Box, ValuePair, ActionLink } from "mds"; import { ActionLink, Box, ValuePair } from "mds";
import { SecureComponent } from "../../../../../common/SecureComponent"; import { SecureComponent } from "../../../../../common/SecureComponent";
import EditActionButton from "./EditActionButton"; import EditActionButton from "./EditActionButton";
@@ -28,6 +28,7 @@ type EditablePropertyItemProps = {
value: any; value: any;
onEdit: () => void; onEdit: () => void;
secureCmpProps?: Record<any, any>; secureCmpProps?: Record<any, any>;
disabled?: boolean;
}; };
const SecureAction = ({ const SecureAction = ({
@@ -61,6 +62,7 @@ const EditablePropertyItem = ({
property = null, property = null,
value = null, value = null,
onEdit, onEdit,
disabled = false,
}: EditablePropertyItemProps) => { }: EditablePropertyItemProps) => {
return ( return (
<Box <Box
@@ -84,6 +86,7 @@ const EditablePropertyItem = ({
onClick={onEdit} onClick={onEdit}
label={value} label={value}
sx={{ fontWeight: "bold" }} sx={{ fontWeight: "bold" }}
disabled={disabled}
/> />
</SecureAction> </SecureAction>
} }
@@ -104,6 +107,7 @@ const EditablePropertyItem = ({
height: "16px", height: "16px",
}, },
}} }}
disabled={disabled}
/> />
</SecureAction> </SecureAction>
</Box> </Box>

View File

@@ -48,6 +48,8 @@ import SectionTitle from "../../../Common/SectionTitle";
import { import {
resetForm, resetForm,
setEnableObjectLocking, setEnableObjectLocking,
setExcludedPrefixes,
setExcludeFolders,
setIsDirty, setIsDirty,
setName, setName,
setQuota, setQuota,
@@ -77,6 +79,7 @@ import {
} from "../../../../../api/consoleApi"; } from "../../../../../api/consoleApi";
import { errorToHandler } from "../../../../../api/errors"; import { errorToHandler } from "../../../../../api/errors";
import HelpMenu from "../../../HelpMenu"; import HelpMenu from "../../../HelpMenu";
import CSVMultiSelector from "../../../Common/FormComponents/CSVMultiSelector/CSVMultiSelector";
const ErrorBox = styled.div(({ theme }) => ({ const ErrorBox = styled.div(({ theme }) => ({
color: get(theme, "signalColors.danger", "#C51B3F"), color: get(theme, "signalColors.danger", "#C51B3F"),
@@ -102,6 +105,12 @@ const AddBucket = () => {
const versioningEnabled = useSelector( const versioningEnabled = useSelector(
(state: AppState) => state.addBucket.versioningEnabled, (state: AppState) => state.addBucket.versioningEnabled,
); );
const excludeFolders = useSelector(
(state: AppState) => state.addBucket.excludeFolders,
);
const excludedPrefixes = useSelector(
(state: AppState) => state.addBucket.excludedPrefixes,
);
const lockingEnabled = useSelector( const lockingEnabled = useSelector(
(state: AppState) => state.addBucket.lockingEnabled, (state: AppState) => state.addBucket.lockingEnabled,
); );
@@ -346,6 +355,35 @@ const AddBucket = () => {
) )
} }
/> />
{versioningEnabled && distributedSetup && !lockingEnabled && (
<Fragment>
<Switch
id={"excludeFolders"}
label={"Exclude Folders"}
checked={excludeFolders}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(setExcludeFolders(e.target.checked));
}}
indicatorLabels={["Enabled", "Disabled"]}
/>
<CSVMultiSelector
elements={excludedPrefixes}
label={"Excluded Prefixes"}
name={"excludedPrefixes"}
onChange={(value: string | string[]) => {
let valCh = "";
if (Array.isArray(value)) {
valCh = value.join(",");
} else {
valCh = value;
}
dispatch(setExcludedPrefixes(valCh));
}}
withBorder={true}
/>
</Fragment>
)}
<Switch <Switch
value="locking" value="locking"
id="locking" id="locking"
@@ -363,7 +401,11 @@ const AddBucket = () => {
label={"Object Locking"} label={"Object Locking"}
tooltip={ tooltip={
lockingAllowed lockingAllowed
? "" ? `${
versioningEnabled
? "Exclude Folders & Exclude Prefixes options will not be available if this option is enabled."
: ""
}`
: permissionTooltipHelper( : permissionTooltipHelper(
[ [
IAM_SCOPES.S3_PUT_BUCKET_VERSIONING, IAM_SCOPES.S3_PUT_BUCKET_VERSIONING,

View File

@@ -41,13 +41,25 @@ export const addBucketAsync = createAsyncThunk(
const retentionValidity = state.addBucket.retentionValidity; const retentionValidity = state.addBucket.retentionValidity;
const distributedSetup = state.system.distributedSetup; const distributedSetup = state.system.distributedSetup;
const siteReplicationInfo = state.system.siteReplicationInfo; const siteReplicationInfo = state.system.siteReplicationInfo;
const excludeFolders = state.addBucket.excludeFolders;
const excludedPrefixes = state.addBucket.excludedPrefixes;
let request: MakeBucketRequest = { let request: MakeBucketRequest = {
name: bucketName, name: bucketName,
versioning: versioning: {
enabled:
distributedSetup && !siteReplicationInfo.enabled distributedSetup && !siteReplicationInfo.enabled
? versioningEnabled ? versioningEnabled
: false, : false,
excludePrefixes:
distributedSetup && !siteReplicationInfo.enabled && !lockingEnabled
? excludedPrefixes.split(",").filter((item) => item.trim() !== "")
: [],
excludeFolders:
distributedSetup && !siteReplicationInfo.enabled && !lockingEnabled
? excludeFolders
: false,
},
locking: distributedSetup ? lockingEnabled : false, locking: distributedSetup ? lockingEnabled : false,
}; };

View File

@@ -34,6 +34,8 @@ export interface AddBucketState {
retentionUnit: string; retentionUnit: string;
retentionValidity: number; retentionValidity: number;
navigateTo: string; navigateTo: string;
excludeFolders: boolean;
excludedPrefixes: string;
} }
const initialState: AddBucketState = { const initialState: AddBucketState = {
@@ -52,6 +54,8 @@ const initialState: AddBucketState = {
retentionUnit: "days", retentionUnit: "days",
retentionValidity: 180, retentionValidity: 180,
navigateTo: "", navigateTo: "",
excludeFolders: false,
excludedPrefixes: "",
}; };
export const addBucketsSlice = createSlice({ export const addBucketsSlice = createSlice({
@@ -81,6 +85,12 @@ export const addBucketsSlice = createSlice({
state.retentionValidity = 180; state.retentionValidity = 180;
} }
}, },
setExcludeFolders: (state, action: PayloadAction<boolean>) => {
state.excludeFolders = action.payload;
},
setExcludedPrefixes: (state, action: PayloadAction<string>) => {
state.excludedPrefixes = action.payload;
},
setEnableObjectLocking: (state, action: PayloadAction<boolean>) => { setEnableObjectLocking: (state, action: PayloadAction<boolean>) => {
state.lockingEnabled = action.payload; state.lockingEnabled = action.payload;
}, },
@@ -196,6 +206,8 @@ export const {
setRetentionMode, setRetentionMode,
setRetentionUnit, setRetentionUnit,
setRetentionValidity, setRetentionValidity,
setExcludedPrefixes,
setExcludeFolders,
} = addBucketsSlice.actions; } = addBucketsSlice.actions;
export default addBucketsSlice.reducer; export default addBucketsSlice.reducer;

View File

@@ -20,16 +20,10 @@ import React, {
useEffect, useEffect,
useRef, useRef,
useState, useState,
Fragment,
} from "react"; } from "react";
import get from "lodash/get"; import get from "lodash/get";
import { Theme } from "@mui/material/styles"; import { AddIcon, Box, HelpIcon, InputBox, InputLabel, Tooltip } from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import Grid from "@mui/material/Grid";
import { InputLabel, Tooltip } from "@mui/material";
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
import { AddIcon, HelpIcon } from "mds";
import InputBoxWrapper from "../InputBoxWrapper/InputBoxWrapper";
interface ICSVMultiSelector { interface ICSVMultiSelector {
elements: string; elements: string;
@@ -37,37 +31,10 @@ interface ICSVMultiSelector {
label: string; label: string;
tooltip?: string; tooltip?: string;
commonPlaceholder?: string; commonPlaceholder?: string;
classes: any;
withBorder?: boolean; withBorder?: boolean;
onChange: (elements: string) => void; onChange: (elements: string) => void;
} }
const styles = (theme: Theme) => {
return createStyles({
...fieldBasic,
...tooltipHelper,
inputWithBorder: {
border: "1px solid #EAEAEA",
padding: 15,
height: 150,
overflowY: "auto",
position: "relative",
marginTop: 15,
flex: 1,
},
inputBoxSpacer: {
marginBottom: 7,
},
inputLabel: {
...fieldBasic.inputLabel,
margin: 0,
alignItems: "flex-start",
paddingTop: "20px",
minWidth: 162,
},
});
};
const CSVMultiSelector = ({ const CSVMultiSelector = ({
elements, elements,
name, name,
@@ -76,7 +43,6 @@ const CSVMultiSelector = ({
commonPlaceholder = "", commonPlaceholder = "",
onChange, onChange,
withBorder = false, withBorder = false,
classes,
}: ICSVMultiSelector) => { }: ICSVMultiSelector) => {
const [currentElements, setCurrentElements] = useState<string[]>([""]); const [currentElements, setCurrentElements] = useState<string[]>([""]);
const bottomList = createRef<HTMLDivElement>(); const bottomList = createRef<HTMLDivElement>();
@@ -154,55 +120,65 @@ const CSVMultiSelector = ({
const inputs = currentElements.map((element, index) => { const inputs = currentElements.map((element, index) => {
return ( return (
<div <InputBox
className={classes.inputBoxSpacer}
key={`csv-multi-${name}-${index.toString()}`} key={`csv-multi-${name}-${index.toString()}`}
>
<InputBoxWrapper
id={`${name}-${index.toString()}`} id={`${name}-${index.toString()}`}
label={""} label={""}
name={`${name}-${index.toString()}`} name={`${name}-${index.toString()}`}
value={currentElements[index]} value={currentElements[index]}
onChange={onChangeElement} onChange={onChangeElement}
index={index} index={index}
key={`csv-${name}-${index.toString()}`}
placeholder={commonPlaceholder} placeholder={commonPlaceholder}
overlayIcon={ overlayIcon={index === currentElements.length - 1 ? <AddIcon /> : null}
index === currentElements.length - 1 ? <AddIcon /> : null
}
overlayAction={() => { overlayAction={() => {
addEmptyLine(currentElements); addEmptyLine(currentElements);
}} }}
/> />
</div>
); );
}); });
return ( return (
<React.Fragment> <Fragment>
<Grid item xs={12} className={classes.fieldContainer}> <Box sx={{ display: "flex" }} className={"inputItem"}>
<InputLabel className={classes.inputLabel}> <InputLabel
sx={{
alignItems: "flex-start",
}}
>
<span>{label}</span> <span>{label}</span>
{tooltip !== "" && ( {tooltip !== "" && (
<div className={classes.tooltipContainer}> <Box
<Tooltip title={tooltip} placement="top-start"> sx={{
<div className={classes.tooltip}> marginLeft: 5,
display: "flex",
alignItems: "center",
"& .min-icon": {
width: 13,
},
}}
>
<Tooltip tooltip={tooltip} placement="top">
<Box className={tooltip}>
<HelpIcon /> <HelpIcon />
</div> </Box>
</Tooltip> </Tooltip>
</div> </Box>
)} )}
</InputLabel> </InputLabel>
<Grid <Box
item withBorders={withBorder}
xs={12} sx={{
className={`${withBorder ? classes.inputWithBorder : ""}`} width: "100%",
overflowY: "auto",
height: 150,
position: "relative",
}}
> >
{inputs} {inputs}
<div ref={bottomList} /> <div ref={bottomList} />
</Grid> </Box>
</Grid> </Box>
</React.Fragment> </Fragment>
); );
}; };
export default withStyles(styles)(CSVMultiSelector); export default CSVMultiSelector;

View File

@@ -232,7 +232,7 @@ type MCClient interface {
list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent
get(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *probe.Error) get(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *probe.Error)
shareDownload(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) shareDownload(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error)
setVersioning(ctx context.Context, status string) *probe.Error setVersioning(ctx context.Context, status string, excludePrefix []string, excludeFolders bool) *probe.Error
} }
// Interface implementation // Interface implementation
@@ -265,8 +265,8 @@ func (c mcClient) deleteAllReplicationRules(ctx context.Context) *probe.Error {
return c.client.RemoveReplication(ctx) return c.client.RemoveReplication(ctx)
} }
func (c mcClient) setVersioning(ctx context.Context, status string) *probe.Error { func (c mcClient) setVersioning(ctx context.Context, status string, excludePrefix []string, excludeFolders bool) *probe.Error {
return c.client.SetVersion(ctx, status, []string{}, false) return c.client.SetVersion(ctx, status, excludePrefix, excludeFolders)
} }
func (c mcClient) remove(ctx context.Context, isIncomplete, isRemoveBucket, isBypass, forceDelete bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult { func (c mcClient) remove(ctx context.Context, isIncomplete, isRemoveBucket, isBypass, forceDelete bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {

View File

@@ -7124,7 +7124,7 @@ func init() {
"$ref": "#/definitions/putBucketRetentionRequest" "$ref": "#/definitions/putBucketRetentionRequest"
}, },
"versioning": { "versioning": {
"type": "boolean" "$ref": "#/definitions/setBucketVersioning"
} }
} }
}, },
@@ -8195,8 +8195,18 @@ func init() {
"setBucketVersioning": { "setBucketVersioning": {
"type": "object", "type": "object",
"properties": { "properties": {
"versioning": { "enabled": {
"type": "boolean" "type": "boolean"
},
"excludeFolders": {
"type": "boolean"
},
"excludePrefixes": {
"type": "array",
"maxLength": 10,
"items": {
"type": "string"
}
} }
} }
}, },
@@ -16257,7 +16267,7 @@ func init() {
"$ref": "#/definitions/putBucketRetentionRequest" "$ref": "#/definitions/putBucketRetentionRequest"
}, },
"versioning": { "versioning": {
"type": "boolean" "$ref": "#/definitions/setBucketVersioning"
} }
} }
}, },
@@ -17328,8 +17338,18 @@ func init() {
"setBucketVersioning": { "setBucketVersioning": {
"type": "object", "type": "object",
"properties": { "properties": {
"versioning": { "enabled": {
"type": "boolean" "type": "boolean"
},
"excludeFolders": {
"type": "boolean"
},
"excludePrefixes": {
"type": "array",
"maxLength": 10,
"items": {
"type": "string"
}
} }
} }
}, },

View File

@@ -188,8 +188,8 @@ const (
) )
// removeBucket deletes a bucket // removeBucket deletes a bucket
func doSetVersioning(ctx context.Context, client MCClient, state VersionState) error { func doSetVersioning(ctx context.Context, client MCClient, state VersionState, excludePrefix []string, excludeFolders bool) error {
err := client.setVersioning(ctx, string(state)) err := client.setVersioning(ctx, string(state), excludePrefix, excludeFolders)
if err != nil { if err != nil {
return err.Cause return err.Cause
} }
@@ -212,11 +212,19 @@ func setBucketVersioningResponse(session *models.Principal, params bucketApi.Set
versioningState := VersionSuspend versioningState := VersionSuspend
if params.Body.Versioning { if params.Body.Enabled {
versioningState = VersionEnable versioningState = VersionEnable
} }
if err := doSetVersioning(ctx, amcClient, versioningState); err != nil { var excludePrefixes []string
if params.Body.ExcludePrefixes != nil {
excludePrefixes = params.Body.ExcludePrefixes
}
excludeFolders := params.Body.ExcludeFolders
if err := doSetVersioning(ctx, amcClient, versioningState, excludePrefixes, excludeFolders); err != nil {
return ErrorWithContext(ctx, fmt.Errorf("error setting versioning for bucket: %s", err)) return ErrorWithContext(ctx, fmt.Errorf("error setting versioning for bucket: %s", err))
} }
return nil return nil
@@ -486,8 +494,14 @@ func getMakeBucketResponse(session *models.Principal, params bucketApi.MakeBucke
} }
}() }()
versioningEnabled := false
if br.Versioning != nil && br.Versioning.Enabled {
versioningEnabled = true
}
// enable versioning if indicated or retention enabled // enable versioning if indicated or retention enabled
if br.Versioning || br.Retention != nil { if versioningEnabled || br.Retention != nil {
s3Client, err := newS3BucketClient(session, *br.Name, "", getClientIP(params.HTTPRequest)) s3Client, err := newS3BucketClient(session, *br.Name, "", getClientIP(params.HTTPRequest))
if err != nil { if err != nil {
return nil, ErrorWithContext(ctx, err) return nil, ErrorWithContext(ctx, err)
@@ -496,7 +510,18 @@ func getMakeBucketResponse(session *models.Principal, params bucketApi.MakeBucke
// defining the client to be used // defining the client to be used
amcClient := mcClient{client: s3Client} amcClient := mcClient{client: s3Client}
if err = doSetVersioning(ctx, amcClient, VersionEnable); err != nil { excludePrefixes := []string{}
excludeFolders := false
if br.Versioning.ExcludeFolders && !br.Locking {
excludeFolders = true
}
if br.Versioning.ExcludePrefixes != nil && !br.Locking {
excludePrefixes = br.Versioning.ExcludePrefixes
}
if err = doSetVersioning(ctx, amcClient, VersionEnable, excludePrefixes, excludeFolders); err != nil {
return nil, ErrorWithContext(ctx, fmt.Errorf("error setting versioning for bucket: %s", err)) return nil, ErrorWithContext(ctx, fmt.Errorf("error setting versioning for bucket: %s", err))
} }
} }

View File

@@ -50,7 +50,7 @@ var (
minioSetObjectLockConfigMock func(ctx context.Context, bucketName string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) error minioSetObjectLockConfigMock func(ctx context.Context, bucketName string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) error
minioGetBucketObjectLockConfigMock func(ctx context.Context, bucketName string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) minioGetBucketObjectLockConfigMock func(ctx context.Context, bucketName string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
minioGetObjectLockConfigMock func(ctx context.Context, bucketName string) (lock string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) minioGetObjectLockConfigMock func(ctx context.Context, bucketName string) (lock string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
minioSetVersioningMock func(ctx context.Context, state string) *probe.Error minioSetVersioningMock func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error
minioCopyObjectMock func(ctx context.Context, dst minio.CopyDestOptions, src minio.CopySrcOptions) (minio.UploadInfo, error) minioCopyObjectMock func(ctx context.Context, dst minio.CopyDestOptions, src minio.CopySrcOptions) (minio.UploadInfo, error)
minioSetBucketTaggingMock func(ctx context.Context, bucketName string, tags *tags.Tags) error minioSetBucketTaggingMock func(ctx context.Context, bucketName string, tags *tags.Tags) error
minioRemoveBucketTaggingMock func(ctx context.Context, bucketName string) error minioRemoveBucketTaggingMock func(ctx context.Context, bucketName string) error
@@ -112,8 +112,8 @@ func (mc minioClientMock) copyObject(ctx context.Context, dst minio.CopyDestOpti
return minioCopyObjectMock(ctx, dst, src) return minioCopyObjectMock(ctx, dst, src)
} }
func (c s3ClientMock) setVersioning(ctx context.Context, state string) *probe.Error { func (c s3ClientMock) setVersioning(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error {
return minioSetVersioningMock(ctx, state) return minioSetVersioningMock(ctx, state, excludePrefix, excludeFolders)
} }
func (mc minioClientMock) GetBucketTagging(ctx context.Context, bucketName string) (*tags.Tags, error) { func (mc minioClientMock) GetBucketTagging(ctx context.Context, bucketName string) (*tags.Tags, error) {
@@ -813,9 +813,11 @@ func Test_SetBucketVersioning(t *testing.T) {
type args struct { type args struct {
ctx context.Context ctx context.Context
state VersionState state VersionState
excludePrefix []string
excludeFolders bool
bucketName string bucketName string
client s3ClientMock client s3ClientMock
setVersioningFunc func(ctx context.Context, state string) *probe.Error setVersioningFunc func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error
} }
tests := []struct { tests := []struct {
name string name string
@@ -829,7 +831,36 @@ func Test_SetBucketVersioning(t *testing.T) {
state: VersionEnable, state: VersionEnable,
bucketName: "test", bucketName: "test",
client: minClient, client: minClient,
setVersioningFunc: func(ctx context.Context, state string) *probe.Error { setVersioningFunc: func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error {
return nil
},
},
expectedError: nil,
},
{
name: "Set Bucket Version with Prefixes Success",
args: args{
ctx: ctx,
state: VersionEnable,
excludePrefix: []string{"prefix1", "prefix2"},
bucketName: "test",
client: minClient,
setVersioningFunc: func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error {
return nil
},
},
expectedError: nil,
},
{
name: "Set Bucket Version with Excluded Folders Success",
args: args{
ctx: ctx,
state: VersionEnable,
excludePrefix: []string{"prefix1", "prefix2"},
excludeFolders: true,
bucketName: "test",
client: minClient,
setVersioningFunc: func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error {
return nil return nil
}, },
}, },
@@ -842,7 +873,7 @@ func Test_SetBucketVersioning(t *testing.T) {
state: VersionEnable, state: VersionEnable,
bucketName: "test", bucketName: "test",
client: minClient, client: minClient,
setVersioningFunc: func(ctx context.Context, state string) *probe.Error { setVersioningFunc: func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error {
return probe.NewError(errors.New(errorMsg)) return probe.NewError(errors.New(errorMsg))
}, },
}, },
@@ -854,7 +885,7 @@ func Test_SetBucketVersioning(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
minioSetVersioningMock = tt.args.setVersioningFunc minioSetVersioningMock = tt.args.setVersioningFunc
err := doSetVersioning(tt.args.ctx, tt.args.client, tt.args.state) err := doSetVersioning(tt.args.ctx, tt.args.client, tt.args.state, tt.args.excludePrefix, tt.args.excludeFolders)
fmt.Println(t.Name()) fmt.Println(t.Name())
fmt.Println("Expected:", tt.expectedError, "Error:", err) fmt.Println("Expected:", tt.expectedError, "Error:", err)

View File

@@ -3756,7 +3756,7 @@ definitions:
locking: locking:
type: boolean type: boolean
versioning: versioning:
type: boolean $ref: "#/definitions/setBucketVersioning"
quota: quota:
$ref: "#/definitions/setBucketQuota" $ref: "#/definitions/setBucketQuota"
retention: retention:
@@ -4963,8 +4963,16 @@ definitions:
setBucketVersioning: setBucketVersioning:
type: object type: object
properties: properties:
versioning: enabled:
type: boolean type: boolean
excludePrefixes:
type: array
maxLength: 10
items:
type: string
excludeFolders:
type: boolean
bucketObLockingResponse: bucketObLockingResponse:
type: object type: object
properties: properties: