add encoded filename as part of delete api url (#3141)

This commit is contained in:
Prakash Senthil Vel
2023-12-07 03:58:46 +05:30
committed by GitHub
parent 607d94fef4
commit 6767bfa2d2
11 changed files with 65 additions and 39 deletions

View File

@@ -423,8 +423,9 @@ func DeleteObject(bucketName, path string, recursive, allVersions bool) (*http.R
DELETE: DELETE:
{{baseUrl}}/buckets/bucketName/objects?path=Y2VzYXJpby50eHQ=&recursive=false&all_versions=false {{baseUrl}}/buckets/bucketName/objects?path=Y2VzYXJpby50eHQ=&recursive=false&all_versions=false
*/ */
url := "http://localhost:9090/api/v1/buckets/" + bucketName + "/objects?path=" + prefixEncoded := base64.StdEncoding.EncodeToString([]byte(path))
path + "&recursive=" + strconv.FormatBool(recursive) + "&all_versions=" + url := "http://localhost:9090/api/v1/buckets/" + bucketName + "/objects?prefix=" +
prefixEncoded + "&recursive=" + strconv.FormatBool(recursive) + "&all_versions=" +
strconv.FormatBool(allVersions) strconv.FormatBool(allVersions)
request, err := http.NewRequest( request, err := http.NewRequest(
"DELETE", "DELETE",
@@ -1639,7 +1640,6 @@ func TestDeleteObject(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
bucketName := "testdeleteobjectbucket1" bucketName := "testdeleteobjectbucket1"
fileName := "testdeleteobjectfile" fileName := "testdeleteobjectfile"
path := "dGVzdGRlbGV0ZW9iamVjdGZpbGUxLnR4dA==" // fileName encoded base64
numberOfFiles := 2 numberOfFiles := 2
// 1. Create bucket // 1. Create bucket
@@ -1662,8 +1662,9 @@ func TestDeleteObject(t *testing.T) {
} }
} }
objPathFull := fileName + "1.txt" // would be encoded in DeleteObject util method.
// 3. Delete only one object from the bucket. // 3. Delete only one object from the bucket.
deleteResponse, deleteError := DeleteObject(bucketName, path, false, false) deleteResponse, deleteError := DeleteObject(bucketName, objPathFull, false, false)
assert.Nil(deleteError) assert.Nil(deleteError)
if deleteError != nil { if deleteError != nil {
log.Println(deleteError) log.Println(deleteError)

View File

@@ -2092,7 +2092,7 @@ export class Api<
deleteObject: ( deleteObject: (
bucketName: string, bucketName: string,
query: { query: {
path: string; prefix: string;
version_id?: string; version_id?: string;
recursive?: boolean; recursive?: boolean;
all_versions?: boolean; all_versions?: boolean;

View File

@@ -25,6 +25,8 @@ 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 { BucketVersioningResponse } from "api/consoleApi"; import { BucketVersioningResponse } from "api/consoleApi";
import { api } from "../../../../../../api";
import { encodeURLString } from "../../../../../../common/utils";
interface IDeleteObjectProps { interface IDeleteObjectProps {
closeDeleteModalAndRefresh: (refresh: boolean) => void; closeDeleteModalAndRefresh: (refresh: boolean) => void;
@@ -86,13 +88,37 @@ const DeleteObject = ({
} }
if (toSend) { if (toSend) {
invokeDeleteApi( if (selectedObjects.length === 1) {
"POST", const firstObject = selectedObjects[0];
`/api/v1/buckets/${selectedBucket}/delete-objects?all_versions=${deleteVersions}${ api.buckets
bypassGovernance ? "&bypass=true" : "" .deleteObject(selectedBucket, {
}`, prefix: encodeURLString(firstObject),
toSend, all_versions: deleteVersions,
); bypass: bypassGovernance,
recursive: firstObject.endsWith("/"), //if it is just a prefix
})
.then(onDelSuccess)
.catch((err) => {
dispatch(
setErrorSnackMessage({
errorMessage: `Could not delete object. ${err.statusText}. ${
retentionConfig
? "Please check retention mode and if object is WORM protected."
: ""
}`,
detailedError: "",
}),
);
});
} else {
invokeDeleteApi(
"POST",
`/api/v1/buckets/${selectedBucket}/delete-objects?all_versions=${deleteVersions}${
bypassGovernance ? "&bypass=true" : ""
}`,
toSend,
);
}
} }
}; };

View File

@@ -59,7 +59,7 @@ const DeleteNonCurrentVersions = ({
if (deleteLoading) { if (deleteLoading) {
api.buckets api.buckets
.deleteObject(selectedBucket, { .deleteObject(selectedBucket, {
path: selectedObject, prefix: selectedObject,
non_current_versions: true, non_current_versions: true,
bypass: bypassGovernance, bypass: bypassGovernance,
}) })

View File

@@ -80,7 +80,7 @@ const DeleteObject = ({
const recursive = decodedSelectedObject.endsWith("/"); const recursive = decodedSelectedObject.endsWith("/");
invokeDeleteApi( invokeDeleteApi(
"DELETE", "DELETE",
`/api/v1/buckets/${selectedBucket}/objects?path=${selectedObject}${ `/api/v1/buckets/${selectedBucket}/objects?prefix=${selectedObject}${
selectedVersion !== "" selectedVersion !== ""
? `&version_id=${selectedVersion}` ? `&version_id=${selectedVersion}`
: `&recursive=${recursive}&all_versions=${deleteVersions}` : `&recursive=${recursive}&all_versions=${deleteVersions}`

View File

@@ -17,11 +17,8 @@
import * as roles from "../utils/roles"; import * as roles from "../utils/roles";
import * as elements from "../utils/elements"; import * as elements from "../utils/elements";
import * as functions from "../utils/functions"; import * as functions from "../utils/functions";
import { bucketsElement } from "../utils/elements-menu";
import { testBucketBrowseButtonFor } from "../utils/functions"; import { testBucketBrowseButtonFor } from "../utils/functions";
import { Selector } from "testcafe"; import { Selector } from "testcafe";
import * as constants from "../utils/constants";
import { deleteAllVersions } from "../utils/elements";
fixture("For user with Bucket Read & Write permissions").page( fixture("For user with Bucket Read & Write permissions").page(
"http://localhost:9090", "http://localhost:9090",
@@ -45,6 +42,9 @@ test
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt") .setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
.wait(1000); .wait(1000);
})("All versions of an object can be deleted from a bucket", async (t) => { })("All versions of an object can be deleted from a bucket", async (t) => {
const versionRows = Selector(
"div.ReactVirtualized__Grid.ReactVirtualized__Table__Grid > div > div:nth-child(1)",
);
await t await t
.useRole(roles.bucketReadWrite) .useRole(roles.bucketReadWrite)
.navigateTo("http://localhost:9090/browser") .navigateTo("http://localhost:9090/browser")
@@ -55,11 +55,7 @@ test
.click(elements.deleteButton) .click(elements.deleteButton)
.click(elements.deleteAllVersions) .click(elements.deleteAllVersions)
.click(Selector("button:enabled").withExactText("Delete").nth(1)) .click(Selector("button:enabled").withExactText("Delete").nth(1))
.expect( .expect(versionRows.exists)
Selector(
"div.ReactVirtualized__Grid.ReactVirtualized__Table__Grid > div > div:nth-child(1)",
).exists,
)
.notOk(); .notOk();
}) })
.after(async (t) => { .after(async (t) => {

View File

@@ -1534,7 +1534,7 @@ func init() {
}, },
{ {
"type": "string", "type": "string",
"name": "path", "name": "prefix",
"in": "query", "in": "query",
"required": true "required": true
}, },
@@ -10684,7 +10684,7 @@ func init() {
}, },
{ {
"type": "string", "type": "string",
"name": "path", "name": "prefix",
"in": "query", "in": "query",
"required": true "required": true
}, },

View File

@@ -71,7 +71,7 @@ type DeleteObjectParams struct {
Required: true Required: true
In: query In: query
*/ */
Path string Prefix string
/* /*
In: query In: query
*/ */
@@ -113,8 +113,8 @@ func (o *DeleteObjectParams) BindRequest(r *http.Request, route *middleware.Matc
res = append(res, err) res = append(res, err)
} }
qPath, qhkPath, _ := qs.GetOK("path") qPrefix, qhkPrefix, _ := qs.GetOK("prefix")
if err := o.bindPath(qPath, qhkPath, route.Formats); err != nil { if err := o.bindPrefix(qPrefix, qhkPrefix, route.Formats); err != nil {
res = append(res, err) res = append(res, err)
} }
@@ -216,10 +216,10 @@ func (o *DeleteObjectParams) bindNonCurrentVersions(rawData []string, hasKey boo
return nil return nil
} }
// bindPath binds and validates parameter Path from query. // bindPrefix binds and validates parameter Prefix from query.
func (o *DeleteObjectParams) bindPath(rawData []string, hasKey bool, formats strfmt.Registry) error { func (o *DeleteObjectParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey { if !hasKey {
return errors.Required("path", "query", rawData) return errors.Required("prefix", "query", rawData)
} }
var raw string var raw string
if len(rawData) > 0 { if len(rawData) > 0 {
@@ -229,10 +229,10 @@ func (o *DeleteObjectParams) bindPath(rawData []string, hasKey bool, formats str
// Required: true // Required: true
// AllowEmptyValue: false // AllowEmptyValue: false
if err := validate.RequiredString("path", "query", raw); err != nil { if err := validate.RequiredString("prefix", "query", raw); err != nil {
return err return err
} }
o.Path = raw o.Prefix = raw
return nil return nil
} }

View File

@@ -38,7 +38,7 @@ type DeleteObjectURL struct {
AllVersions *bool AllVersions *bool
Bypass *bool Bypass *bool
NonCurrentVersions *bool NonCurrentVersions *bool
Path string Prefix string
Recursive *bool Recursive *bool
VersionID *string VersionID *string
@@ -107,9 +107,9 @@ func (o *DeleteObjectURL) Build() (*url.URL, error) {
qs.Set("non_current_versions", nonCurrentVersionsQ) qs.Set("non_current_versions", nonCurrentVersionsQ)
} }
pathQ := o.Path prefixQ := o.Prefix
if pathQ != "" { if prefixQ != "" {
qs.Set("path", pathQ) qs.Set("prefix", prefixQ)
} }
var recursiveQ string var recursiveQ string

View File

@@ -63,6 +63,7 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) {
}) })
// delete object // delete object
api.ObjectDeleteObjectHandler = objectApi.DeleteObjectHandlerFunc(func(params objectApi.DeleteObjectParams, session *models.Principal) middleware.Responder { api.ObjectDeleteObjectHandler = objectApi.DeleteObjectHandlerFunc(func(params objectApi.DeleteObjectParams, session *models.Principal) middleware.Responder {
fmt.Println("ObjectDeleteObjectHandler", params.Prefix)
if err := getDeleteObjectResponse(session, params); err != nil { if err := getDeleteObjectResponse(session, params); err != nil {
return objectApi.NewDeleteObjectDefault(err.Code).WithPayload(err.APIError) return objectApi.NewDeleteObjectDefault(err.Code).WithPayload(err.APIError)
} }
@@ -70,6 +71,8 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) {
}) })
// delete multiple objects // delete multiple objects
api.ObjectDeleteMultipleObjectsHandler = objectApi.DeleteMultipleObjectsHandlerFunc(func(params objectApi.DeleteMultipleObjectsParams, session *models.Principal) middleware.Responder { api.ObjectDeleteMultipleObjectsHandler = objectApi.DeleteMultipleObjectsHandlerFunc(func(params objectApi.DeleteMultipleObjectsParams, session *models.Principal) middleware.Responder {
fmt.Println("ObjectDeleteMultipleObjectsHandler", params)
if err := getDeleteMultiplePathsResponse(session, params); err != nil { if err := getDeleteMultiplePathsResponse(session, params); err != nil {
return objectApi.NewDeleteMultipleObjectsDefault(err.Code).WithPayload(err.APIError) return objectApi.NewDeleteMultipleObjectsDefault(err.Code).WithPayload(err.APIError)
} }
@@ -764,8 +767,8 @@ func getDeleteObjectResponse(session *models.Principal, params objectApi.DeleteO
ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel() defer cancel()
var prefix string var prefix string
if params.Path != "" { if params.Prefix != "" {
encodedPrefix := SanitizeEncodedPrefix(params.Path) encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix) decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
if err != nil { if err != nil {
return ErrorWithContext(ctx, err) return ErrorWithContext(ctx, err)

View File

@@ -339,7 +339,7 @@ paths:
in: path in: path
required: true required: true
type: string type: string
- name: path - name: prefix
in: query in: query
required: true required: true
type: string type: string