File Actions:
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx
index 2ee74ee87..5d2832d2c 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx
@@ -1,12 +1,31 @@
-import React, { useState, useRef } from "react";
+// This file is part of MinIO Console Server
+// Copyright (c) 2020 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 .
+
+import React, { useState, useRef, useEffect } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";
+import { IFileInfo } from "./types";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import RadioGroupSelector from "../../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import DateSelector from "../../../../Common/FormComponents/DateSelector/DateSelector";
+import api from "../../../../../../common/api";
+import Typography from "@material-ui/core/Typography";
const styles = (theme: Theme) =>
createStyles({
@@ -18,14 +37,19 @@ const styles = (theme: Theme) =>
buttonContainer: {
textAlign: "right",
},
+ errorBlock: {
+ color: "red",
+ },
...modalBasic,
});
interface ISetRetentionProps {
classes: any;
open: boolean;
- closeModalAndRefresh: () => void;
+ closeModalAndRefresh: (updateInfo: boolean) => void;
objectName: string;
+ bucketName: string;
+ objectInfo: IFileInfo;
}
interface IRefObject {
@@ -37,9 +61,23 @@ const SetRetention = ({
open,
closeModalAndRefresh,
objectName,
+ objectInfo,
+ bucketName,
}: ISetRetentionProps) => {
- const [statusEnabled, setStatusEnabled] = useState(false);
+ const [statusEnabled, setStatusEnabled] = useState(true);
+ const [error, setError] = useState("");
const [type, setType] = useState("");
+ const [date, setDate] = useState("");
+ const [isDateValid, setIsDateValid] = useState(false);
+ const [isSaving, setIsSaving] = useState(false);
+ const [alreadyConfigured, setAlreadyConfigured] = useState(false);
+
+ useEffect(() => {
+ if (objectInfo.retention_mode) {
+ setType(objectInfo.retention_mode.toLowerCase());
+ setAlreadyConfigured(true);
+ }
+ }, []);
const dateElement = useRef(null);
@@ -59,15 +97,90 @@ const SetRetention = ({
}
};
+ const addRetention = (
+ selectedObject: string,
+ versionId: string | null,
+ expireDate: string
+ ) => {
+ api
+ .invoke(
+ "PUT",
+ `/api/v1/buckets/${bucketName}/objects/retention?prefix=${selectedObject}&version_id=${versionId}`,
+ {
+ expires: expireDate,
+ mode: type,
+ }
+ )
+ .then((res: any) => {
+ setIsSaving(false);
+ closeModalAndRefresh(true);
+ })
+ .catch((error) => {
+ setError(error);
+ setIsSaving(false);
+ });
+ };
+
+ const disableRetention = (
+ selectedObject: string,
+ versionId: string | null
+ ) => {
+ api
+ .invoke(
+ "DELETE",
+ `/api/v1/buckets/${bucketName}/objects/retention?prefix=${selectedObject}&version_id=${versionId}`
+ )
+ .then((res: any) => {
+ setIsSaving(false);
+ closeModalAndRefresh(true);
+ })
+ .catch((error) => {
+ setError(error);
+ setIsSaving(false);
+ });
+ };
+
+ const saveNewRetentionPolicy = () => {
+ setIsSaving(true);
+ const selectedObject = objectInfo.name;
+ const versionId = objectInfo.version_id;
+
+ const expireDate =
+ !statusEnabled && type === "governance" ? "" : `${date}T23:59:59Z`;
+
+ if (!statusEnabled && type === "governance") {
+ disableRetention(selectedObject, versionId);
+
+ return;
+ }
+
+ addRetention(selectedObject, versionId, expireDate);
+ };
+
+ const showSwitcher =
+ alreadyConfigured && (type === "governance" || type === "");
+
return (
{
resetForm();
- closeModalAndRefresh();
+ closeModalAndRefresh(false);
}}
>
+ {error !== "" && (
+
+
+
+ {error}
+
+
+ )}
{objectName}
@@ -78,27 +191,30 @@ const SetRetention = ({
onSubmit(e);
}}
>
-
- ) => {
- setStatusEnabled(!statusEnabled);
- setType("");
- }}
- label={"Status"}
- indicatorLabels={["Enabled", "Disabled"]}
- />
-
+ {showSwitcher && (
+
+ ) => {
+ setStatusEnabled(!statusEnabled);
+ }}
+ label={"Status"}
+ indicatorLabels={["Enabled", "Disabled"]}
+ />
+
+ )}
{
setType(e.target.value);
}}
@@ -115,7 +231,10 @@ const SetRetention = ({
disableOptions={dateFieldDisabled()}
ref={dateElement}
borderBottom={true}
- onDateChange={() => {}}
+ onDateChange={(date: string, isValid: boolean) => {
+ setIsDateValid(isValid);
+ setDate(date);
+ }}
/>
@@ -127,7 +246,17 @@ const SetRetention = ({
>
Reset
-
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/types.ts b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/types.ts
index e4f243f9c..d6ac7b084 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/types.ts
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/types.ts
@@ -23,5 +23,5 @@ export interface IFileInfo {
retention_until_date?: string;
size?: string;
tags?: object;
- version_id: string;
+ version_id: string | null;
}
diff --git a/portal-ui/src/screens/Console/Common/FormComponents/RadioGroupSelector/RadioGroupSelector.tsx b/portal-ui/src/screens/Console/Common/FormComponents/RadioGroupSelector/RadioGroupSelector.tsx
index 3c1b7c71b..529150b79 100644
--- a/portal-ui/src/screens/Console/Common/FormComponents/RadioGroupSelector/RadioGroupSelector.tsx
+++ b/portal-ui/src/screens/Console/Common/FormComponents/RadioGroupSelector/RadioGroupSelector.tsx
@@ -146,7 +146,7 @@ export const RadioGroupSelector = ({
return (
}
label={selectorOption.label}
disabled={disableOptions}
diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go
index bcad60c8f..95c3c2d1f 100644
--- a/restapi/embedded_spec.go
+++ b/restapi/embedded_spec.go
@@ -663,6 +663,44 @@ func init() {
}
}
}
+ },
+ "delete": {
+ "tags": [
+ "UserAPI"
+ ],
+ "summary": "Delete Object retention from an object",
+ "operationId": "DeleteObjectRetention",
+ "parameters": [
+ {
+ "type": "string",
+ "name": "bucket_name",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "prefix",
+ "in": "query",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "version_id",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A successful response."
+ },
+ "default": {
+ "description": "Generic error response.",
+ "schema": {
+ "$ref": "#/definitions/error"
+ }
+ }
+ }
}
},
"/buckets/{bucket_name}/objects/share": {
@@ -5685,6 +5723,44 @@ func init() {
}
}
}
+ },
+ "delete": {
+ "tags": [
+ "UserAPI"
+ ],
+ "summary": "Delete Object retention from an object",
+ "operationId": "DeleteObjectRetention",
+ "parameters": [
+ {
+ "type": "string",
+ "name": "bucket_name",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "prefix",
+ "in": "query",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "version_id",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A successful response."
+ },
+ "default": {
+ "description": "Generic error response.",
+ "schema": {
+ "$ref": "#/definitions/error"
+ }
+ }
+ }
}
},
"/buckets/{bucket_name}/objects/share": {
diff --git a/restapi/operations/console_api.go b/restapi/operations/console_api.go
index 89c24331f..31f23a23d 100644
--- a/restapi/operations/console_api.go
+++ b/restapi/operations/console_api.go
@@ -119,6 +119,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
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")
}),
+ UserAPIDeleteObjectRetentionHandler: user_api.DeleteObjectRetentionHandlerFunc(func(params user_api.DeleteObjectRetentionParams, principal *models.Principal) middleware.Responder {
+ return middleware.NotImplemented("operation user_api.DeleteObjectRetention has not yet been implemented")
+ }),
UserAPIDeleteRemoteBucketHandler: user_api.DeleteRemoteBucketHandlerFunc(func(params user_api.DeleteRemoteBucketParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.DeleteRemoteBucket has not yet been implemented")
}),
@@ -399,6 +402,8 @@ type ConsoleAPI struct {
UserAPIDeleteBucketEventHandler user_api.DeleteBucketEventHandler
// 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
+ UserAPIDeleteObjectRetentionHandler user_api.DeleteObjectRetentionHandler
// UserAPIDeleteRemoteBucketHandler sets the operation handler for the delete remote bucket operation
UserAPIDeleteRemoteBucketHandler user_api.DeleteRemoteBucketHandler
// UserAPIDeleteServiceAccountHandler sets the operation handler for the delete service account operation
@@ -657,6 +662,9 @@ func (o *ConsoleAPI) Validate() error {
if o.UserAPIDeleteObjectHandler == nil {
unregistered = append(unregistered, "user_api.DeleteObjectHandler")
}
+ if o.UserAPIDeleteObjectRetentionHandler == nil {
+ unregistered = append(unregistered, "user_api.DeleteObjectRetentionHandler")
+ }
if o.UserAPIDeleteRemoteBucketHandler == nil {
unregistered = append(unregistered, "user_api.DeleteRemoteBucketHandler")
}
@@ -1026,6 +1034,10 @@ func (o *ConsoleAPI) initHandlerCache() {
if o.handlers["DELETE"] == nil {
o.handlers["DELETE"] = make(map[string]http.Handler)
}
+ o.handlers["DELETE"]["/buckets/{bucket_name}/objects/retention"] = user_api.NewDeleteObjectRetention(o.context, o.UserAPIDeleteObjectRetentionHandler)
+ if o.handlers["DELETE"] == nil {
+ o.handlers["DELETE"] = make(map[string]http.Handler)
+ }
o.handlers["DELETE"]["/remote-buckets/{source-bucket-name}/{arn}"] = user_api.NewDeleteRemoteBucket(o.context, o.UserAPIDeleteRemoteBucketHandler)
if o.handlers["DELETE"] == nil {
o.handlers["DELETE"] = make(map[string]http.Handler)
diff --git a/restapi/operations/user_api/delete_object_retention.go b/restapi/operations/user_api/delete_object_retention.go
new file mode 100644
index 000000000..e758c3391
--- /dev/null
+++ b/restapi/operations/user_api/delete_object_retention.go
@@ -0,0 +1,90 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// This file is part of MinIO Console Server
+// Copyright (c) 2020 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 .
+//
+
+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"
+)
+
+// DeleteObjectRetentionHandlerFunc turns a function with the right signature into a delete object retention handler
+type DeleteObjectRetentionHandlerFunc func(DeleteObjectRetentionParams, *models.Principal) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn DeleteObjectRetentionHandlerFunc) Handle(params DeleteObjectRetentionParams, principal *models.Principal) middleware.Responder {
+ return fn(params, principal)
+}
+
+// DeleteObjectRetentionHandler interface for that can handle valid delete object retention params
+type DeleteObjectRetentionHandler interface {
+ Handle(DeleteObjectRetentionParams, *models.Principal) middleware.Responder
+}
+
+// NewDeleteObjectRetention creates a new http.Handler for the delete object retention operation
+func NewDeleteObjectRetention(ctx *middleware.Context, handler DeleteObjectRetentionHandler) *DeleteObjectRetention {
+ return &DeleteObjectRetention{Context: ctx, Handler: handler}
+}
+
+/*DeleteObjectRetention swagger:route DELETE /buckets/{bucket_name}/objects/retention UserAPI deleteObjectRetention
+
+Delete Object retention from an object
+
+*/
+type DeleteObjectRetention struct {
+ Context *middleware.Context
+ Handler DeleteObjectRetentionHandler
+}
+
+func (o *DeleteObjectRetention) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ r = rCtx
+ }
+ var Params = NewDeleteObjectRetentionParams()
+
+ 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)
+
+}
diff --git a/restapi/operations/user_api/delete_object_retention_parameters.go b/restapi/operations/user_api/delete_object_retention_parameters.go
new file mode 100644
index 000000000..e1c72c05c
--- /dev/null
+++ b/restapi/operations/user_api/delete_object_retention_parameters.go
@@ -0,0 +1,155 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// This file is part of MinIO Console Server
+// Copyright (c) 2020 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 .
+//
+
+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/errors"
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/validate"
+)
+
+// NewDeleteObjectRetentionParams creates a new DeleteObjectRetentionParams object
+// no default values defined in spec.
+func NewDeleteObjectRetentionParams() DeleteObjectRetentionParams {
+
+ return DeleteObjectRetentionParams{}
+}
+
+// DeleteObjectRetentionParams contains all the bound params for the delete object retention operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters DeleteObjectRetention
+type DeleteObjectRetentionParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*
+ Required: true
+ In: path
+ */
+ BucketName string
+ /*
+ Required: true
+ In: query
+ */
+ Prefix string
+ /*
+ Required: true
+ In: query
+ */
+ VersionID string
+}
+
+// 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 NewDeleteObjectRetentionParams() beforehand.
+func (o *DeleteObjectRetentionParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ qs := runtime.Values(r.URL.Query())
+
+ rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
+ if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ qPrefix, qhkPrefix, _ := qs.GetOK("prefix")
+ if err := o.bindPrefix(qPrefix, qhkPrefix, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ qVersionID, qhkVersionID, _ := qs.GetOK("version_id")
+ if err := o.bindVersionID(qVersionID, qhkVersionID, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+// bindBucketName binds and validates parameter BucketName from path.
+func (o *DeleteObjectRetentionParams) 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
+}
+
+// bindPrefix binds and validates parameter Prefix from query.
+func (o *DeleteObjectRetentionParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ if !hasKey {
+ return errors.Required("prefix", "query", rawData)
+ }
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: true
+ // AllowEmptyValue: false
+ if err := validate.RequiredString("prefix", "query", raw); err != nil {
+ return err
+ }
+
+ o.Prefix = raw
+
+ return nil
+}
+
+// bindVersionID binds and validates parameter VersionID from query.
+func (o *DeleteObjectRetentionParams) bindVersionID(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ if !hasKey {
+ return errors.Required("version_id", "query", rawData)
+ }
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: true
+ // AllowEmptyValue: false
+ if err := validate.RequiredString("version_id", "query", raw); err != nil {
+ return err
+ }
+
+ o.VersionID = raw
+
+ return nil
+}
diff --git a/restapi/operations/user_api/delete_object_retention_responses.go b/restapi/operations/user_api/delete_object_retention_responses.go
new file mode 100644
index 000000000..c51a0021d
--- /dev/null
+++ b/restapi/operations/user_api/delete_object_retention_responses.go
@@ -0,0 +1,113 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// This file is part of MinIO Console Server
+// Copyright (c) 2020 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 .
+//
+
+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"
+)
+
+// DeleteObjectRetentionOKCode is the HTTP code returned for type DeleteObjectRetentionOK
+const DeleteObjectRetentionOKCode int = 200
+
+/*DeleteObjectRetentionOK A successful response.
+
+swagger:response deleteObjectRetentionOK
+*/
+type DeleteObjectRetentionOK struct {
+}
+
+// NewDeleteObjectRetentionOK creates DeleteObjectRetentionOK with default headers values
+func NewDeleteObjectRetentionOK() *DeleteObjectRetentionOK {
+
+ return &DeleteObjectRetentionOK{}
+}
+
+// WriteResponse to the client
+func (o *DeleteObjectRetentionOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
+
+ rw.WriteHeader(200)
+}
+
+/*DeleteObjectRetentionDefault Generic error response.
+
+swagger:response deleteObjectRetentionDefault
+*/
+type DeleteObjectRetentionDefault struct {
+ _statusCode int
+
+ /*
+ In: Body
+ */
+ Payload *models.Error `json:"body,omitempty"`
+}
+
+// NewDeleteObjectRetentionDefault creates DeleteObjectRetentionDefault with default headers values
+func NewDeleteObjectRetentionDefault(code int) *DeleteObjectRetentionDefault {
+ if code <= 0 {
+ code = 500
+ }
+
+ return &DeleteObjectRetentionDefault{
+ _statusCode: code,
+ }
+}
+
+// WithStatusCode adds the status to the delete object retention default response
+func (o *DeleteObjectRetentionDefault) WithStatusCode(code int) *DeleteObjectRetentionDefault {
+ o._statusCode = code
+ return o
+}
+
+// SetStatusCode sets the status to the delete object retention default response
+func (o *DeleteObjectRetentionDefault) SetStatusCode(code int) {
+ o._statusCode = code
+}
+
+// WithPayload adds the payload to the delete object retention default response
+func (o *DeleteObjectRetentionDefault) WithPayload(payload *models.Error) *DeleteObjectRetentionDefault {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the delete object retention default response
+func (o *DeleteObjectRetentionDefault) SetPayload(payload *models.Error) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *DeleteObjectRetentionDefault) 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
+ }
+ }
+}
diff --git a/restapi/operations/user_api/delete_object_retention_urlbuilder.go b/restapi/operations/user_api/delete_object_retention_urlbuilder.go
new file mode 100644
index 000000000..fbc442950
--- /dev/null
+++ b/restapi/operations/user_api/delete_object_retention_urlbuilder.go
@@ -0,0 +1,133 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// This file is part of MinIO Console Server
+// Copyright (c) 2020 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 .
+//
+
+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"
+)
+
+// DeleteObjectRetentionURL generates an URL for the delete object retention operation
+type DeleteObjectRetentionURL struct {
+ BucketName string
+
+ Prefix string
+ VersionID 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 *DeleteObjectRetentionURL) WithBasePath(bp string) *DeleteObjectRetentionURL {
+ 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 *DeleteObjectRetentionURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *DeleteObjectRetentionURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/buckets/{bucket_name}/objects/retention"
+
+ bucketName := o.BucketName
+ if bucketName != "" {
+ _path = strings.Replace(_path, "{bucket_name}", bucketName, -1)
+ } else {
+ return nil, errors.New("bucketName is required on DeleteObjectRetentionURL")
+ }
+
+ _basePath := o._basePath
+ if _basePath == "" {
+ _basePath = "/api/v1"
+ }
+ _result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ qs := make(url.Values)
+
+ prefixQ := o.Prefix
+ if prefixQ != "" {
+ qs.Set("prefix", prefixQ)
+ }
+
+ versionIDQ := o.VersionID
+ if versionIDQ != "" {
+ qs.Set("version_id", versionIDQ)
+ }
+
+ _result.RawQuery = qs.Encode()
+
+ return &_result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *DeleteObjectRetentionURL) 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 *DeleteObjectRetentionURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *DeleteObjectRetentionURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on DeleteObjectRetentionURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on DeleteObjectRetentionURL")
+ }
+
+ 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 *DeleteObjectRetentionURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/restapi/user_objects.go b/restapi/user_objects.go
index 03edf58de..7ad6ae02f 100644
--- a/restapi/user_objects.go
+++ b/restapi/user_objects.go
@@ -107,6 +107,14 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) {
}
return user_api.NewPutObjectRetentionOK()
})
+ // delete object retention
+ api.UserAPIDeleteObjectRetentionHandler = user_api.DeleteObjectRetentionHandlerFunc(func(params user_api.DeleteObjectRetentionParams, session *models.Principal) middleware.Responder {
+ if err := deleteObjectRetentionResponse(session, params); err != nil {
+ return user_api.NewDeleteObjectRetentionDefault(int(err.Code)).WithPayload(err)
+ }
+ return user_api.NewDeleteObjectRetentionOK()
+ })
+ // set tags in object
api.UserAPIPutObjectTagsHandler = user_api.PutObjectTagsHandlerFunc(func(params user_api.PutObjectTagsParams, session *models.Principal) middleware.Responder {
if err := getPutObjectTagsResponse(session, params); err != nil {
return user_api.NewPutObjectTagsDefault(int(err.Code)).WithPayload(err)
@@ -512,6 +520,32 @@ func setObjectRetention(ctx context.Context, client MinioClient, bucketName, ver
return client.putObjectRetention(ctx, bucketName, prefix, opts)
}
+func deleteObjectRetentionResponse(session *models.Principal, params user_api.DeleteObjectRetentionParams) *models.Error {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
+ defer cancel()
+ mClient, err := newMinioClient(session)
+ if err != nil {
+ return prepareError(err)
+ }
+ // create a minioClient interface implementation
+ // defining the client to be used
+ minioClient := minioClient{client: mClient}
+ err = deleteObjectRetention(ctx, minioClient, params.BucketName, params.Prefix, params.VersionID)
+ if err != nil {
+ return prepareError(err)
+ }
+ return nil
+}
+
+func deleteObjectRetention(ctx context.Context, client MinioClient, bucketName, prefix, versionID string) error {
+ opts := minio.PutObjectRetentionOptions{
+ GovernanceBypass: true,
+ VersionID: versionID,
+ }
+
+ return client.putObjectRetention(ctx, bucketName, prefix, opts)
+}
+
func getPutObjectTagsResponse(session *models.Principal, params user_api.PutObjectTagsParams) *models.Error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
diff --git a/restapi/user_objects_test.go b/restapi/user_objects_test.go
index 1b7a44c47..8d0bf76c0 100644
--- a/restapi/user_objects_test.go
+++ b/restapi/user_objects_test.go
@@ -40,7 +40,7 @@ var minioGetObjectLegalHoldMock func(ctx context.Context, bucketName, objectName
var minioGetObjectRetentionMock func(ctx context.Context, bucketName, objectName, versionID string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error)
var minioPutObjectMock func(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error)
var minioPutObjectLegalHoldMock func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error
-var miinoPutObjectRetentionMock func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error
+var minioPutObjectRetentionMock func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error
var minioGetObjectTaggingMock func(ctx context.Context, bucketName, objectName string, opts minio.GetObjectTaggingOptions) (*tags.Tags, error)
var minioPutObjectTaggingMock func(ctx context.Context, bucketName, objectName string, otags *tags.Tags, opts minio.PutObjectTaggingOptions) error
@@ -70,7 +70,7 @@ func (ac minioClientMock) putObjectLegalHold(ctx context.Context, bucketName, ob
}
func (ac minioClientMock) putObjectRetention(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error {
- return miinoPutObjectRetentionMock(ctx, bucketName, objectName, opts)
+ return minioPutObjectRetentionMock(ctx, bucketName, objectName, opts)
}
func (ac minioClientMock) getObjectTagging(ctx context.Context, bucketName, objectName string, opts minio.GetObjectTaggingOptions) (*tags.Tags, error) {
@@ -985,7 +985,7 @@ func Test_putObjectRetention(t *testing.T) {
for _, tt := range tests {
t.Run(tt.test, func(t *testing.T) {
- miinoPutObjectRetentionMock = tt.args.retentionFunc
+ minioPutObjectRetentionMock = tt.args.retentionFunc
err := setObjectRetention(ctx, client, tt.args.bucket, tt.args.prefix, tt.args.versionID, tt.args.opts)
if tt.wantError != nil {
fmt.Println(t.Name())
@@ -997,3 +997,44 @@ func Test_putObjectRetention(t *testing.T) {
})
}
}
+
+func Test_deleteObjectRetention(t *testing.T) {
+ assert := assert.New(t)
+ ctx := context.Background()
+ client := minioClientMock{}
+ type args struct {
+ bucket string
+ prefix string
+ versionID string
+ retentionFunc func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error
+ }
+ tests := []struct {
+ test string
+ args args
+ wantError error
+ }{
+ {
+ test: "Delete Object retention governance",
+ args: args{
+ bucket: "buck1",
+ versionID: "someversion",
+ prefix: "folder/file.txt",
+ retentionFunc: func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error {
+ return nil
+ },
+ },
+ wantError: nil,
+ }}
+ for _, tt := range tests {
+ t.Run(tt.test, func(t *testing.T) {
+ minioPutObjectRetentionMock = tt.args.retentionFunc
+ err := deleteObjectRetention(ctx, client, tt.args.bucket, tt.args.prefix, tt.args.versionID)
+ if tt.wantError != nil {
+ fmt.Println(t.Name())
+ assert.Equal(tt.wantError.Error(), err.Error(), fmt.Sprintf("deleteObjectRetention() error: `%s`, wantErr: `%s`", err, tt.wantError))
+ } else {
+ assert.Nil(err, fmt.Sprintf("deleteObjectRetention() error: %v, wantErr: %v", err, tt.wantError))
+ }
+ })
+ }
+}
diff --git a/swagger.yml b/swagger.yml
index 16bebfe86..7d6e361a3 100644
--- a/swagger.yml
+++ b/swagger.yml
@@ -464,6 +464,31 @@ paths:
$ref: "#/definitions/error"
tags:
- UserAPI
+ delete:
+ summary: Delete Object retention from an object
+ operationId: DeleteObjectRetention
+ parameters:
+ - name: bucket_name
+ in: path
+ required: true
+ type: string
+ - name: prefix
+ in: query
+ required: true
+ type: string
+ - name: version_id
+ in: query
+ required: true
+ type: string
+ responses:
+ 200:
+ description: A successful response.
+ default:
+ description: Generic error response.
+ schema:
+ $ref: "#/definitions/error"
+ tags:
+ - UserAPI
/buckets/{bucket_name}/objects/tags:
put: