Added bucket details inside objects listing (#1502)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -45,6 +45,7 @@ import {
|
||||
decodeFileName,
|
||||
encodeFileName,
|
||||
niceBytes,
|
||||
niceBytesInt,
|
||||
} from "../../../../../../common/utils";
|
||||
|
||||
import {
|
||||
@@ -1094,27 +1095,6 @@ const ListObjects = ({
|
||||
uploadPath = uploadPath.concat(currentPath);
|
||||
}
|
||||
|
||||
// TODO: Add bucket information panel
|
||||
/*
|
||||
*
|
||||
* subTitle={
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.bucketDetails}>
|
||||
<span className={classes.detailsSpacer}>
|
||||
Created: <strong></strong>
|
||||
</span>
|
||||
<span className={classes.detailsSpacer}>
|
||||
Access: <strong></strong>
|
||||
</span>
|
||||
<span className={classes.detailsSpacer}>
|
||||
SIZE / TOTAL OBJECTS
|
||||
</span>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
}
|
||||
*
|
||||
* */
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{shareFileModalOpen && selectedPreview && (
|
||||
@@ -1178,6 +1158,38 @@ const ListObjects = ({
|
||||
fullSizeBreadcrumbs
|
||||
/>
|
||||
}
|
||||
subTitle={
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.bucketDetails}>
|
||||
<span className={classes.detailsSpacer}>
|
||||
Created:
|
||||
<strong>{bucketInfo?.creation_date || ""}</strong>
|
||||
</span>
|
||||
<span className={classes.detailsSpacer}>
|
||||
Access:
|
||||
<strong>{bucketInfo?.access || ""}</strong>
|
||||
</span>
|
||||
{bucketInfo && (
|
||||
<Fragment>
|
||||
<span className={classes.detailsSpacer}>
|
||||
{bucketInfo.size && (
|
||||
<Fragment>{niceBytesInt(bucketInfo.size)}</Fragment>
|
||||
)}
|
||||
{bucketInfo.size && bucketInfo.objects ? " / " : ""}
|
||||
{bucketInfo.objects && (
|
||||
<Fragment>
|
||||
{bucketInfo.objects} Object
|
||||
{bucketInfo.objects && bucketInfo.objects !== 1
|
||||
? "s"
|
||||
: ""}
|
||||
</Fragment>
|
||||
)}
|
||||
</span>
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
}
|
||||
actions={
|
||||
<Fragment>
|
||||
<input
|
||||
|
||||
@@ -45,6 +45,9 @@ export interface BucketInfo {
|
||||
name: string;
|
||||
access: string;
|
||||
definition: string;
|
||||
creation_date?: string;
|
||||
objects?: number;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface BucketList {
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/mc/cmd"
|
||||
"github.com/minio/mc/pkg/probe"
|
||||
"github.com/minio/minio-go/v7"
|
||||
@@ -561,11 +562,19 @@ func getBucketSetPolicyResponse(session *models.Principal, bucketName string, re
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := setBucketAccessPolicy(ctx, minioClient, bucketName, *req.Access, req.Definition); err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// set bucket access policy
|
||||
bucket, err := getBucketInfo(ctx, minioClient, bucketName)
|
||||
bucket, err := getBucketInfo(ctx, minioClient, adminClient, bucketName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
@@ -624,7 +633,7 @@ func getDeleteBucketResponse(session *models.Principal, params user_api.DeleteBu
|
||||
}
|
||||
|
||||
// getBucketInfo return bucket information including name, policy access, size and creation date
|
||||
func getBucketInfo(ctx context.Context, client MinioClient, bucketName string) (*models.Bucket, error) {
|
||||
func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdmin, bucketName string) (*models.Bucket, error) {
|
||||
var bucketAccess models.BucketAccess
|
||||
policyStr, err := client.getBucketPolicy(context.Background(), bucketName)
|
||||
if err != nil {
|
||||
@@ -655,13 +664,28 @@ func getBucketInfo(ctx context.Context, client MinioClient, bucketName string) (
|
||||
if bucketTags != nil {
|
||||
bucketDetails.Tags = bucketTags.ToMap()
|
||||
}
|
||||
|
||||
info, err := adminClient.AccountInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var bucketInfo madmin.BucketAccessInfo
|
||||
|
||||
for _, bucket := range info.Buckets {
|
||||
if bucket.Name == bucketName {
|
||||
bucketInfo = bucket
|
||||
}
|
||||
}
|
||||
|
||||
return &models.Bucket{
|
||||
Name: &bucketName,
|
||||
Access: &bucketAccess,
|
||||
Definition: policyStr,
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
CreationDate: bucketInfo.Created.Format(time.RFC3339),
|
||||
Size: int64(bucketInfo.Size),
|
||||
Details: bucketDetails,
|
||||
Objects: int64(bucketInfo.Objects),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -676,7 +700,16 @@ func getBucketInfoResponse(session *models.Principal, params user_api.BucketInfo
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
bucket, err := getBucketInfo(ctx, minioClient, params.Name)
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
bucket, err := getBucketInfo(ctx, minioClient, adminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
@@ -256,6 +256,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
minClient := minioClientMock{}
|
||||
adminClient := adminClientMock{}
|
||||
ctx := context.Background()
|
||||
function := "getBucketInfo()"
|
||||
|
||||
@@ -269,8 +270,9 @@ func TestBucketInfo(t *testing.T) {
|
||||
outputExpected := &models.Bucket{
|
||||
Name: swag.String(bucketToSet),
|
||||
Access: models.NewBucketAccess(models.BucketAccessPRIVATE),
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
CreationDate: "0001-01-01T00:00:00Z",
|
||||
Size: 0,
|
||||
Objects: 0,
|
||||
}
|
||||
infoPolicy := `
|
||||
{
|
||||
@@ -307,7 +309,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
return mockBucketList, nil
|
||||
}
|
||||
|
||||
bucketInfo, err := getBucketInfo(ctx, minClient, bucketToSet)
|
||||
bucketInfo, err := getBucketInfo(ctx, minClient, adminClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
@@ -315,6 +317,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
assert.Equal(outputExpected.Access, bucketInfo.Access)
|
||||
assert.Equal(outputExpected.CreationDate, bucketInfo.CreationDate)
|
||||
assert.Equal(outputExpected.Size, bucketInfo.Size)
|
||||
assert.Equal(outputExpected.Objects, bucketInfo.Objects)
|
||||
|
||||
// Test-2: getBucketInfo() get a bucket with PUBLIC access
|
||||
// mock policy for bucket csbucket with readWrite access (should return PUBLIC)
|
||||
@@ -326,10 +329,11 @@ func TestBucketInfo(t *testing.T) {
|
||||
outputExpected = &models.Bucket{
|
||||
Name: swag.String(bucketToSet),
|
||||
Access: models.NewBucketAccess(models.BucketAccessPUBLIC),
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
CreationDate: "0001-01-01T00:00:00Z",
|
||||
Size: 0,
|
||||
Objects: 0,
|
||||
}
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, bucketToSet)
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
@@ -337,6 +341,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
assert.Equal(outputExpected.Access, bucketInfo.Access)
|
||||
assert.Equal(outputExpected.CreationDate, bucketInfo.CreationDate)
|
||||
assert.Equal(outputExpected.Size, bucketInfo.Size)
|
||||
assert.Equal(outputExpected.Objects, bucketInfo.Objects)
|
||||
|
||||
// Test-3: getBucketInfo() get a bucket with PRIVATE access
|
||||
// if bucket has a null statement, the the bucket is PRIVATE
|
||||
@@ -348,10 +353,11 @@ func TestBucketInfo(t *testing.T) {
|
||||
outputExpected = &models.Bucket{
|
||||
Name: swag.String(bucketToSet),
|
||||
Access: models.NewBucketAccess(models.BucketAccessPRIVATE),
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
CreationDate: "0001-01-01T00:00:00Z",
|
||||
Size: 0,
|
||||
Objects: 0,
|
||||
}
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, bucketToSet)
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
@@ -359,6 +365,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
assert.Equal(outputExpected.Access, bucketInfo.Access)
|
||||
assert.Equal(outputExpected.CreationDate, bucketInfo.CreationDate)
|
||||
assert.Equal(outputExpected.Size, bucketInfo.Size)
|
||||
assert.Equal(outputExpected.Objects, bucketInfo.Objects)
|
||||
|
||||
// Test-4: getBucketInfo() returns an error while parsing invalid policy
|
||||
mockPolicy = "policyinvalid"
|
||||
@@ -369,10 +376,10 @@ func TestBucketInfo(t *testing.T) {
|
||||
outputExpected = &models.Bucket{
|
||||
Name: swag.String(bucketToSet),
|
||||
Access: models.NewBucketAccess(models.BucketAccessCUSTOM),
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
CreationDate: "",
|
||||
Size: 0,
|
||||
}
|
||||
_, err = getBucketInfo(ctx, minClient, bucketToSet)
|
||||
_, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("invalid character 'p' looking for beginning of value", err.Error())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user