Delete multiple objects (#856)

* delete multiple objects

* fixing capitalization

* make assets

Co-authored-by: Adam Stafford <adam@minio.io>
Co-authored-by: Adam Stafford <adamstafford@Adams-MacBook-Pro.local>
Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
adfost
2021-07-21 15:49:20 -07:00
committed by GitHub
parent 6f7c026e4c
commit 2e1e4e4e80
18 changed files with 1177 additions and 0 deletions

73
models/delete_file.go Normal file
View File

@@ -0,0 +1,73 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// DeleteFile delete file
//
// swagger:model deleteFile
type DeleteFile struct {
// path
Path string `json:"path,omitempty"`
// recursive
Recursive bool `json:"recursive,omitempty"`
// version ID
VersionID string `json:"versionID,omitempty"`
}
// Validate validates this delete file
func (m *DeleteFile) Validate(formats strfmt.Registry) error {
return nil
}
// ContextValidate validates this delete file based on context it is used
func (m *DeleteFile) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *DeleteFile) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *DeleteFile) UnmarshalBinary(b []byte) error {
var res DeleteFile
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,280 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*!
* Chart.js v2.9.4
* https://www.chartjs.org
* (c) 2020 Chart.js Contributors
* Released under the MIT License
*/
/*!
* cookie
* Copyright(c) 2012-2014 Roman Shtylman
* Copyright(c) 2015 Douglas Christopher Wilson
* MIT Licensed
*/
/*! Conditions:: INITIAL */
/*! Moment Duration Format v2.2.2
* https://github.com/jsmreese/moment-duration-format
* Date: 2018-02-16
*
* Duration format plugin function for the Moment.js library
* http://momentjs.com/
*
* Copyright 2018 John Madhavan-Reese
* Released under the MIT license
*/
/*! Production:: $accept : expression $end */
/*! Production:: css_value : ANGLE */
/*! Production:: css_value : CHS */
/*! Production:: css_value : EMS */
/*! Production:: css_value : EXS */
/*! Production:: css_value : FREQ */
/*! Production:: css_value : LENGTH */
/*! Production:: css_value : PERCENTAGE */
/*! Production:: css_value : REMS */
/*! Production:: css_value : RES */
/*! Production:: css_value : SUB css_value */
/*! Production:: css_value : TIME */
/*! Production:: css_value : VHS */
/*! Production:: css_value : VMAXS */
/*! Production:: css_value : VMINS */
/*! Production:: css_value : VWS */
/*! Production:: css_variable : CSS_VAR LPAREN CSS_CPROP COMMA math_expression RPAREN */
/*! Production:: css_variable : CSS_VAR LPAREN CSS_CPROP RPAREN */
/*! Production:: expression : math_expression EOF */
/*! Production:: math_expression : LPAREN math_expression RPAREN */
/*! Production:: math_expression : NESTED_CALC LPAREN math_expression RPAREN */
/*! Production:: math_expression : SUB PREFIX SUB NESTED_CALC LPAREN math_expression RPAREN */
/*! Production:: math_expression : css_value */
/*! Production:: math_expression : css_variable */
/*! Production:: math_expression : math_expression ADD math_expression */
/*! Production:: math_expression : math_expression DIV math_expression */
/*! Production:: math_expression : math_expression MUL math_expression */
/*! Production:: math_expression : math_expression SUB math_expression */
/*! Production:: math_expression : value */
/*! Production:: value : NUMBER */
/*! Production:: value : SUB NUMBER */
/*! Rule:: $ */
/*! Rule:: (--[0-9a-z-A-Z-]*) */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)% */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)Hz\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)ch\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)cm\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)deg\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)dpcm\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)dpi\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)dppx\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)em\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)ex\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)grad\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)in\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)kHz\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)mm\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)ms\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)pc\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)pt\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)px\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)rad\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)rem\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)s\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)turn\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)vh\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)vmax\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)vmin\b */
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)vw\b */
/*! Rule:: ([a-z]+) */
/*! Rule:: (calc) */
/*! Rule:: (var) */
/*! Rule:: , */
/*! Rule:: - */
/*! Rule:: \( */
/*! Rule:: \) */
/*! Rule:: \* */
/*! Rule:: \+ */
/*! Rule:: \/ */
/*! Rule:: \s+ */
/*! decimal.js-light v2.5.1 https://github.com/MikeMcl/decimal.js-light/LICENCE */
/**
* A better abstraction over CSS.
*
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
* @website https://github.com/cssinjs/jss
* @license MIT
*/
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.10.2
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.1
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**!
* @fileOverview Kickass library to create and place poppers near their reference elements.
* @version 1.16.1-lts
* @license
* Copyright (c) 2016 Federico Zivolo and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
//! moment.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,131 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useState } from "react";
import { connect } from "react-redux";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
LinearProgress,
} from "@material-ui/core";
import { setErrorSnackMessage } from "../../../../../../actions";
import api from "../../../../../../common/api";
interface IDeleteObjectProps {
closeDeleteModalAndRefresh: (refresh: boolean) => void;
deleteOpen: boolean;
selectedObjects: string[];
selectedBucket: string;
setErrorSnackMessage: typeof setErrorSnackMessage;
}
const DeleteObject = ({
closeDeleteModalAndRefresh,
deleteOpen,
selectedBucket,
selectedObjects,
setErrorSnackMessage,
}: IDeleteObjectProps) => {
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
const removeRecord = () => {
if (deleteLoading) {
return;
}
let toSend = [];
for (let i = 0; i < selectedObjects.length; i++) {
if (selectedObjects[i].endsWith("/")) {
toSend.push({
path: selectedObjects[i],
versionID: "",
recursive: true,
});
} else {
toSend.push({
path: selectedObjects[i],
versionID: "",
recursive: false,
});
}
}
setDeleteLoading(true);
api
.invoke(
"POST",
`/api/v1/buckets/${selectedBucket}/delete-objects`,
toSend
)
.then(() => {
setDeleteLoading(false);
closeDeleteModalAndRefresh(true);
})
.catch((err) => {
setDeleteLoading(false);
setErrorSnackMessage(err);
});
};
return (
<Dialog
open={deleteOpen}
onClose={() => {
closeDeleteModalAndRefresh(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete the selected objects?{" "}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
closeDeleteModalAndRefresh(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
removeRecord();
}}
color="secondary"
disabled={deleteLoading}
>
Delete
</Button>
</DialogActions>
</Dialog>
);
};
const mapDispatchToProps = {
setErrorSnackMessage,
};
const connector = connect(null, mapDispatchToProps);
export default connector(DeleteObject);

View File

@@ -75,6 +75,9 @@ import {
} from "../../../../../../actions";
import { BucketVersioning } from "../../../types";
import RewindEnable from "./RewindEnable";
import FileCopyIcon from "@material-ui/icons/FileCopy";
import DeleteIcon from "@material-ui/icons/Delete";
import DeleteMultipleObjects from "./DeleteMultipleObjects";
const commonIcon = {
backgroundRepeat: "no-repeat",
@@ -226,6 +229,7 @@ const ListObjects = ({
const [rewind, setRewind] = useState<RewindObject[]>([]);
const [loadingRewind, setLoadingRewind] = useState<boolean>(true);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [deleteMultipleOpen, setDeleteMultipleOpen] = useState<boolean>(false);
const [createFolderOpen, setCreateFolderOpen] = useState<boolean>(false);
const [selectedObject, setSelectedObject] = useState<string>("");
const [selectedBucket, setSelectedBucket] = useState<string>("");
@@ -236,6 +240,7 @@ const ListObjects = ({
const [loadingVersioning, setLoadingVersioning] = useState<boolean>(true);
const [isVersioned, setIsVersioned] = useState<boolean>(false);
const [rewindSelect, setRewindSelect] = useState<boolean>(false);
const [selectedObjects, setSelectedObjects] = useState<string[]>([]);
const bucketName = match.params["bucket"];
@@ -451,6 +456,16 @@ const ListObjects = ({
}
};
const closeDeleteMultipleModalAndRefresh = (refresh: boolean) => {
setDeleteMultipleOpen(false);
if (refresh) {
setSnackBarMessage(`Objects deleted successfully.`);
setSelectedObjects([]);
setLoading(true);
}
};
const closeAddFolderModal = () => {
setCreateFolderOpen(false);
};
@@ -676,6 +691,25 @@ const ListObjects = ({
}
};
const selectListObjects = (e: React.ChangeEvent<HTMLInputElement>) => {
const targetD = e.target;
const value = targetD.value;
const checked = targetD.checked;
let elements: string[] = [...selectedObjects]; // We clone the selectedBuckets array
if (checked) {
// If the user has checked this field we need to push this to selectedBucketsList
elements.push(value);
} else {
// User has unchecked this field, we need to remove it from the list
elements = elements.filter((element) => element !== value);
}
setSelectedObjects(elements);
return elements;
};
const listModeColumns = [
{
label: "Name",
@@ -737,6 +771,14 @@ const ListObjects = ({
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
/>
)}
{deleteMultipleOpen && (
<DeleteMultipleObjects
deleteOpen={deleteMultipleOpen}
selectedBucket={selectedBucket}
selectedObjects={selectedObjects}
closeDeleteModalAndRefresh={closeDeleteMultipleModalAndRefresh}
/>
)}
{createFolderOpen && (
<CreateFolderModal
modalOpen={createFolderOpen}
@@ -807,6 +849,17 @@ const ListObjects = ({
<RestoreIcon />
</IconButton>
</Badge>
<Button
variant="contained"
color="primary"
startIcon={<DeleteIcon />}
onClick={() => {
setDeleteMultipleOpen(true);
}}
disabled={selectedObjects.length === 0}
>
Delete Selected
</Button>
<Button
variant="contained"
color="primary"
@@ -851,6 +904,8 @@ const ListObjects = ({
idField="name"
records={rewindEnabled ? rewind : filteredRecords}
customPaperHeight={classes.browsePaper}
selectedItems={selectedObjects}
onSelect={selectListObjects}
/>
</Grid>
</Grid>

View File

@@ -595,6 +595,45 @@ func init() {
}
}
},
"/buckets/{bucket_name}/delete-objects": {
"post": {
"tags": [
"UserAPI"
],
"summary": "Delete Multiple Objects",
"operationId": "DeleteMultipleObjects",
"parameters": [
{
"type": "string",
"name": "bucket_name",
"in": "path",
"required": true
},
{
"name": "files",
"in": "body",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/deleteFile"
}
}
}
],
"responses": {
"200": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/buckets/{bucket_name}/encryption/disable": {
"post": {
"tags": [
@@ -3683,6 +3722,20 @@ func init() {
}
}
},
"deleteFile": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"recursive": {
"type": "boolean"
},
"versionID": {
"type": "string"
}
}
},
"directCSIDriveInfo": {
"type": "object",
"properties": {
@@ -5926,6 +5979,45 @@ func init() {
}
}
},
"/buckets/{bucket_name}/delete-objects": {
"post": {
"tags": [
"UserAPI"
],
"summary": "Delete Multiple Objects",
"operationId": "DeleteMultipleObjects",
"parameters": [
{
"type": "string",
"name": "bucket_name",
"in": "path",
"required": true
},
{
"name": "files",
"in": "body",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/deleteFile"
}
}
}
],
"responses": {
"200": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/buckets/{bucket_name}/encryption/disable": {
"post": {
"tags": [
@@ -9136,6 +9228,20 @@ func init() {
}
}
},
"deleteFile": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"recursive": {
"type": "boolean"
},
"versionID": {
"type": "string"
}
}
},
"directCSIDriveInfo": {
"type": "object",
"properties": {

View File

@@ -128,6 +128,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
UserAPIDeleteBucketReplicationRuleHandler: user_api.DeleteBucketReplicationRuleHandlerFunc(func(params user_api.DeleteBucketReplicationRuleParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.DeleteBucketReplicationRule has not yet been implemented")
}),
UserAPIDeleteMultipleObjectsHandler: user_api.DeleteMultipleObjectsHandlerFunc(func(params user_api.DeleteMultipleObjectsParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.DeleteMultipleObjects has not yet been implemented")
}),
UserAPIDeleteObjectHandler: user_api.DeleteObjectHandlerFunc(func(params user_api.DeleteObjectParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.DeleteObject has not yet been implemented")
}),
@@ -435,6 +438,8 @@ type ConsoleAPI struct {
UserAPIDeleteBucketEventHandler user_api.DeleteBucketEventHandler
// UserAPIDeleteBucketReplicationRuleHandler sets the operation handler for the delete bucket replication rule operation
UserAPIDeleteBucketReplicationRuleHandler user_api.DeleteBucketReplicationRuleHandler
// UserAPIDeleteMultipleObjectsHandler sets the operation handler for the delete multiple objects operation
UserAPIDeleteMultipleObjectsHandler user_api.DeleteMultipleObjectsHandler
// UserAPIDeleteObjectHandler sets the operation handler for the delete object operation
UserAPIDeleteObjectHandler user_api.DeleteObjectHandler
// UserAPIDeleteObjectRetentionHandler sets the operation handler for the delete object retention operation
@@ -725,6 +730,9 @@ func (o *ConsoleAPI) Validate() error {
if o.UserAPIDeleteBucketReplicationRuleHandler == nil {
unregistered = append(unregistered, "user_api.DeleteBucketReplicationRuleHandler")
}
if o.UserAPIDeleteMultipleObjectsHandler == nil {
unregistered = append(unregistered, "user_api.DeleteMultipleObjectsHandler")
}
if o.UserAPIDeleteObjectHandler == nil {
unregistered = append(unregistered, "user_api.DeleteObjectHandler")
}
@@ -1121,6 +1129,10 @@ func (o *ConsoleAPI) initHandlerCache() {
o.handlers["DELETE"] = make(map[string]http.Handler)
}
o.handlers["DELETE"]["/buckets/{bucket_name}/replication/{rule_id}"] = user_api.NewDeleteBucketReplicationRule(o.context, o.UserAPIDeleteBucketReplicationRuleHandler)
if o.handlers["POST"] == nil {
o.handlers["POST"] = make(map[string]http.Handler)
}
o.handlers["POST"]["/buckets/{bucket_name}/delete-objects"] = user_api.NewDeleteMultipleObjects(o.context, o.UserAPIDeleteMultipleObjectsHandler)
if o.handlers["DELETE"] == nil {
o.handlers["DELETE"] = make(map[string]http.Handler)
}

View File

@@ -0,0 +1,88 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/console/models"
)
// DeleteMultipleObjectsHandlerFunc turns a function with the right signature into a delete multiple objects handler
type DeleteMultipleObjectsHandlerFunc func(DeleteMultipleObjectsParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn DeleteMultipleObjectsHandlerFunc) Handle(params DeleteMultipleObjectsParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// DeleteMultipleObjectsHandler interface for that can handle valid delete multiple objects params
type DeleteMultipleObjectsHandler interface {
Handle(DeleteMultipleObjectsParams, *models.Principal) middleware.Responder
}
// NewDeleteMultipleObjects creates a new http.Handler for the delete multiple objects operation
func NewDeleteMultipleObjects(ctx *middleware.Context, handler DeleteMultipleObjectsHandler) *DeleteMultipleObjects {
return &DeleteMultipleObjects{Context: ctx, Handler: handler}
}
/* DeleteMultipleObjects swagger:route POST /buckets/{bucket_name}/delete-objects UserAPI deleteMultipleObjects
Delete Multiple Objects
*/
type DeleteMultipleObjects struct {
Context *middleware.Context
Handler DeleteMultipleObjectsHandler
}
func (o *DeleteMultipleObjects) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = NewDeleteMultipleObjectsParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
*r = *aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,127 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/minio/console/models"
)
// NewDeleteMultipleObjectsParams creates a new DeleteMultipleObjectsParams object
//
// There are no default values defined in the spec.
func NewDeleteMultipleObjectsParams() DeleteMultipleObjectsParams {
return DeleteMultipleObjectsParams{}
}
// DeleteMultipleObjectsParams contains all the bound params for the delete multiple objects operation
// typically these are obtained from a http.Request
//
// swagger:parameters DeleteMultipleObjects
type DeleteMultipleObjectsParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: path
*/
BucketName string
/*
Required: true
In: body
*/
Files []*models.DeleteFile
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewDeleteMultipleObjectsParams() beforehand.
func (o *DeleteMultipleObjectsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
res = append(res, err)
}
if runtime.HasBody(r) {
defer r.Body.Close()
var body []*models.DeleteFile
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("files", "body", ""))
} else {
res = append(res, errors.NewParseError("files", "body", "", err))
}
} else {
// validate array of body objects
for i := range body {
if body[i] == nil {
continue
}
if err := body[i].Validate(route.Formats); err != nil {
res = append(res, err)
break
}
}
if len(res) == 0 {
o.Files = body
}
}
} else {
res = append(res, errors.Required("files", "body", ""))
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindBucketName binds and validates parameter BucketName from path.
func (o *DeleteMultipleObjectsParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error {
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// Parameter is provided by construction from the route
o.BucketName = raw
return nil
}

View File

@@ -0,0 +1,113 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/console/models"
)
// DeleteMultipleObjectsOKCode is the HTTP code returned for type DeleteMultipleObjectsOK
const DeleteMultipleObjectsOKCode int = 200
/*DeleteMultipleObjectsOK A successful response.
swagger:response deleteMultipleObjectsOK
*/
type DeleteMultipleObjectsOK struct {
}
// NewDeleteMultipleObjectsOK creates DeleteMultipleObjectsOK with default headers values
func NewDeleteMultipleObjectsOK() *DeleteMultipleObjectsOK {
return &DeleteMultipleObjectsOK{}
}
// WriteResponse to the client
func (o *DeleteMultipleObjectsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(200)
}
/*DeleteMultipleObjectsDefault Generic error response.
swagger:response deleteMultipleObjectsDefault
*/
type DeleteMultipleObjectsDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewDeleteMultipleObjectsDefault creates DeleteMultipleObjectsDefault with default headers values
func NewDeleteMultipleObjectsDefault(code int) *DeleteMultipleObjectsDefault {
if code <= 0 {
code = 500
}
return &DeleteMultipleObjectsDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the delete multiple objects default response
func (o *DeleteMultipleObjectsDefault) WithStatusCode(code int) *DeleteMultipleObjectsDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the delete multiple objects default response
func (o *DeleteMultipleObjectsDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the delete multiple objects default response
func (o *DeleteMultipleObjectsDefault) WithPayload(payload *models.Error) *DeleteMultipleObjectsDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the delete multiple objects default response
func (o *DeleteMultipleObjectsDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *DeleteMultipleObjectsDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,116 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
"strings"
)
// DeleteMultipleObjectsURL generates an URL for the delete multiple objects operation
type DeleteMultipleObjectsURL struct {
BucketName string
_basePath string
// avoid unkeyed usage
_ struct{}
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *DeleteMultipleObjectsURL) WithBasePath(bp string) *DeleteMultipleObjectsURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *DeleteMultipleObjectsURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *DeleteMultipleObjectsURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/buckets/{bucket_name}/delete-objects"
bucketName := o.BucketName
if bucketName != "" {
_path = strings.Replace(_path, "{bucket_name}", bucketName, -1)
} else {
return nil, errors.New("bucketName is required on DeleteMultipleObjectsURL")
}
_basePath := o._basePath
if _basePath == "" {
_basePath = "/api/v1"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *DeleteMultipleObjectsURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *DeleteMultipleObjectsURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *DeleteMultipleObjectsURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on DeleteMultipleObjectsURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on DeleteMultipleObjectsURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *DeleteMultipleObjectsURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -62,6 +62,13 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) {
}
return user_api.NewDeleteObjectOK()
})
// delete multiple objects
api.UserAPIDeleteMultipleObjectsHandler = user_api.DeleteMultipleObjectsHandlerFunc(func(params user_api.DeleteMultipleObjectsParams, session *models.Principal) middleware.Responder {
if err := getDeleteMultiplePathsResponse(session, params); err != nil {
return user_api.NewDeleteMultipleObjectsDefault(int(err.Code)).WithPayload(err)
}
return user_api.NewDeleteMultipleObjectsOK()
})
// download object
api.UserAPIDownloadObjectHandler = user_api.DownloadObjectHandlerFunc(func(params user_api.DownloadObjectParams, session *models.Principal) middleware.Responder {
resp, err := getDownloadObjectResponse(session, params)
@@ -283,6 +290,29 @@ func getDeleteObjectResponse(session *models.Principal, params user_api.DeleteOb
return nil
}
// getDeleteMultiplePathsResponse returns whether there was an error on deletion of any object
func getDeleteMultiplePathsResponse(session *models.Principal, params user_api.DeleteMultipleObjectsParams) *models.Error {
ctx := context.Background()
var version string
s3Client, err := newS3BucketClient(session, params.BucketName, "")
if err != nil {
return prepareError(err)
}
for i := 0; i < len(params.Files); i++ {
if params.Files[i].VersionID != "" {
version = params.Files[i].VersionID
}
// create a mc S3Client interface implementation
// defining the client to be used
mcClient := mcClient{client: s3Client}
err = deleteObjects(ctx, mcClient, params.BucketName, params.Files[i].Path, version, params.Files[i].Recursive)
if err != nil {
return prepareError(err)
}
}
return nil
}
// deleteObjects deletes either a single object or multiple objects based on recursive flag
func deleteObjects(ctx context.Context, client MCClient, bucket, path string, versionID string, recursive bool) error {
if recursive {

View File

@@ -370,6 +370,32 @@ paths:
tags:
- UserAPI
/buckets/{bucket_name}/delete-objects:
post:
summary: Delete Multiple Objects
operationId: DeleteMultipleObjects
parameters:
- name: bucket_name
in: path
required: true
type: string
- name: files
in: body
required: true
schema:
type: array
items:
$ref: "#/definitions/deleteFile"
responses:
200:
description: A successful response.
default:
description: Generic error response.
schema:
$ref: "#/definitions/error"
tags:
- UserAPI
/buckets/{bucket_name}/objects/upload:
post:
summary: Uploads an Object.
@@ -3590,6 +3616,16 @@ definitions:
region:
type: string
deleteFile:
type: object
properties:
path:
type: string
versionID:
type: string
recursive:
type: boolean
tier:
type: object
properties: