From 83a20e26ee5016295e82105595f951f54272b124 Mon Sep 17 00:00:00 2001 From: Jillian Inapurapu Date: Thu, 30 May 2024 15:42:09 -0700 Subject: [PATCH 1/5] Add button, modal for deleting tier Add removeTier API Add DeleteTierConfirmModal Fix modal close, add permission check to delete tier button --- api/admin_tiers.go | 31 ++ api/client-admin.go | 7 + api/embedded_spec.go | 56 ++++ api/operations/console_api.go | 12 + api/operations/tiering/remove_tier.go | 88 +++++ .../tiering/remove_tier_parameters.go | 88 +++++ .../tiering/remove_tier_responses.go | 115 +++++++ .../tiering/remove_tier_urlbuilder.go | 116 +++++++ swagger.yml | 18 ++ web-app/src/api/consoleApi.ts | 19 +- .../DeleteTierConfirmModal.tsx | 83 +++++ .../ListTiersConfiguration.tsx | 31 ++ web-app/src/screens/Console/HelpTipItem.tsx | 83 +++++ web-app/src/screens/Console/HelpTipTabs.tsx | 303 ++++++++++++++++++ 14 files changed, 1049 insertions(+), 1 deletion(-) create mode 100644 api/operations/tiering/remove_tier.go create mode 100644 api/operations/tiering/remove_tier_parameters.go create mode 100644 api/operations/tiering/remove_tier_responses.go create mode 100644 api/operations/tiering/remove_tier_urlbuilder.go create mode 100644 web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx create mode 100644 web-app/src/screens/Console/HelpTipItem.tsx create mode 100644 web-app/src/screens/Console/HelpTipTabs.tsx diff --git a/api/admin_tiers.go b/api/admin_tiers.go index 122bd3bc12..6bddc106f9 100644 --- a/api/admin_tiers.go +++ b/api/admin_tiers.go @@ -62,6 +62,14 @@ func registerAdminTiersHandlers(api *operations.ConsoleAPI) { } return tieringApi.NewEditTierCredentialsOK() }) + // remove an empty tier + api.TieringRemoveTierHandler = tieringApi.RemoveTierHandlerFunc(func(params tieringApi.RemoveTierParams, session *models.Principal) middleware.Responder { + err := getRemoveTierResponse(session, params) + if err != nil { + return tieringApi.NewRemoveTierDefault(err.Code).WithPayload(err.APIError) + } + return tieringApi.NewRemoveTierNoContent() + }) } // getNotificationEndpoints invokes admin info and returns a list of notification endpoints @@ -409,3 +417,26 @@ func getEditTierCredentialsResponse(session *models.Principal, params tieringApi } return nil } + +func removeTier(ctx context.Context, client MinioAdmin, params *tieringApi.RemoveTierParams) error { + + return client.removeTier(ctx, params.Name) +} + +func getRemoveTierResponse(session *models.Principal, params tieringApi.RemoveTierParams) *CodedAPIError { + ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) + defer cancel() + mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) + if err != nil { + return ErrorWithContext(ctx, err) + } + // create a minioClient interface implementation + // defining the client to be used + adminClient := AdminClient{Client: mAdmin} + // serialize output + err = removeTier(ctx, adminClient, ¶ms) + if err != nil { + return ErrorWithContext(ctx, err) + } + return nil +} diff --git a/api/client-admin.go b/api/client-admin.go index ba11a81c6a..4c8016e716 100644 --- a/api/client-admin.go +++ b/api/client-admin.go @@ -96,6 +96,8 @@ type MinioAdmin interface { editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error // verify Tier status verifyTierStatus(ctx context.Context, tierName string) error + // remove empty Tier + removeTier(ctx context.Context, tierName string) error // Speedtest speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) // Site Relication @@ -448,6 +450,11 @@ func (ac AdminClient) verifyTierStatus(ctx context.Context, tierName string) err return ac.Client.VerifyTier(ctx, tierName) } +// implements madmin.RemoveTier() +func (ac AdminClient) removeTier(ctx context.Context, tierName string) error { + return ac.Client.RemoveTier(ctx, tierName) +} + func NewMinioAdminClient(ctx context.Context, sessionClaims *models.Principal) (*madmin.AdminClient, error) { clientIP := utils.ClientIPFromContext(ctx) adminClient, err := newAdminFromClaims(sessionClaims, clientIP) diff --git a/api/embedded_spec.go b/api/embedded_spec.go index 0f74a1ecf2..2a9083d84b 100644 --- a/api/embedded_spec.go +++ b/api/embedded_spec.go @@ -545,6 +545,34 @@ func init() { } } }, + "/admin/tiers/{name}/remove": { + "delete": { + "tags": [ + "Tiering" + ], + "summary": "Remove Tier", + "operationId": "RemoveTier", + "parameters": [ + { + "type": "string", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/ApiError" + } + } + } + } + }, "/admin/tiers/{type}/{name}": { "get": { "tags": [ @@ -9729,6 +9757,34 @@ func init() { } } }, + "/admin/tiers/{name}/remove": { + "delete": { + "tags": [ + "Tiering" + ], + "summary": "Remove Tier", + "operationId": "RemoveTier", + "parameters": [ + { + "type": "string", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/ApiError" + } + } + } + } + }, "/admin/tiers/{type}/{name}": { "get": { "tags": [ diff --git a/api/operations/console_api.go b/api/operations/console_api.go index 62ec5eba1d..b5ed8f7b52 100644 --- a/api/operations/console_api.go +++ b/api/operations/console_api.go @@ -467,6 +467,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI { PolicyRemovePolicyHandler: policy.RemovePolicyHandlerFunc(func(params policy.RemovePolicyParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation policy.RemovePolicy has not yet been implemented") }), + TieringRemoveTierHandler: tiering.RemoveTierHandlerFunc(func(params tiering.RemoveTierParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation tiering.RemoveTier has not yet been implemented") + }), UserRemoveUserHandler: user.RemoveUserHandlerFunc(func(params user.RemoveUserParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation user.RemoveUser has not yet been implemented") }), @@ -878,6 +881,8 @@ type ConsoleAPI struct { GroupRemoveGroupHandler group.RemoveGroupHandler // PolicyRemovePolicyHandler sets the operation handler for the remove policy operation PolicyRemovePolicyHandler policy.RemovePolicyHandler + // TieringRemoveTierHandler sets the operation handler for the remove tier operation + TieringRemoveTierHandler tiering.RemoveTierHandler // UserRemoveUserHandler sets the operation handler for the remove user operation UserRemoveUserHandler user.RemoveUserHandler // ConfigurationResetConfigHandler sets the operation handler for the reset config operation @@ -1411,6 +1416,9 @@ func (o *ConsoleAPI) Validate() error { if o.PolicyRemovePolicyHandler == nil { unregistered = append(unregistered, "policy.RemovePolicyHandler") } + if o.TieringRemoveTierHandler == nil { + unregistered = append(unregistered, "tiering.RemoveTierHandler") + } if o.UserRemoveUserHandler == nil { unregistered = append(unregistered, "user.RemoveUserHandler") } @@ -2121,6 +2129,10 @@ func (o *ConsoleAPI) initHandlerCache() { if o.handlers["DELETE"] == nil { o.handlers["DELETE"] = make(map[string]http.Handler) } + o.handlers["DELETE"]["/admin/tiers/{name}/remove"] = tiering.NewRemoveTier(o.context, o.TieringRemoveTierHandler) + if o.handlers["DELETE"] == nil { + o.handlers["DELETE"] = make(map[string]http.Handler) + } o.handlers["DELETE"]["/user/{name}"] = user.NewRemoveUser(o.context, o.UserRemoveUserHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) diff --git a/api/operations/tiering/remove_tier.go b/api/operations/tiering/remove_tier.go new file mode 100644 index 0000000000..7dbbe12d06 --- /dev/null +++ b/api/operations/tiering/remove_tier.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2023 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 tiering + +// 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" +) + +// RemoveTierHandlerFunc turns a function with the right signature into a remove tier handler +type RemoveTierHandlerFunc func(RemoveTierParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn RemoveTierHandlerFunc) Handle(params RemoveTierParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// RemoveTierHandler interface for that can handle valid remove tier params +type RemoveTierHandler interface { + Handle(RemoveTierParams, *models.Principal) middleware.Responder +} + +// NewRemoveTier creates a new http.Handler for the remove tier operation +func NewRemoveTier(ctx *middleware.Context, handler RemoveTierHandler) *RemoveTier { + return &RemoveTier{Context: ctx, Handler: handler} +} + +/* + RemoveTier swagger:route DELETE /admin/tiers/{name}/remove Tiering removeTier + +Remove Tier +*/ +type RemoveTier struct { + Context *middleware.Context + Handler RemoveTierHandler +} + +func (o *RemoveTier) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewRemoveTierParams() + 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/api/operations/tiering/remove_tier_parameters.go b/api/operations/tiering/remove_tier_parameters.go new file mode 100644 index 0000000000..6eeb31770a --- /dev/null +++ b/api/operations/tiering/remove_tier_parameters.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2023 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 tiering + +// 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/middleware" + "github.com/go-openapi/strfmt" +) + +// NewRemoveTierParams creates a new RemoveTierParams object +// +// There are no default values defined in the spec. +func NewRemoveTierParams() RemoveTierParams { + + return RemoveTierParams{} +} + +// RemoveTierParams contains all the bound params for the remove tier operation +// typically these are obtained from a http.Request +// +// swagger:parameters RemoveTier +type RemoveTierParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + Name 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 NewRemoveTierParams() beforehand. +func (o *RemoveTierParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *RemoveTierParams) bindName(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.Name = raw + + return nil +} diff --git a/api/operations/tiering/remove_tier_responses.go b/api/operations/tiering/remove_tier_responses.go new file mode 100644 index 0000000000..a594274f4d --- /dev/null +++ b/api/operations/tiering/remove_tier_responses.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2023 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 tiering + +// 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" +) + +// RemoveTierNoContentCode is the HTTP code returned for type RemoveTierNoContent +const RemoveTierNoContentCode int = 204 + +/* +RemoveTierNoContent A successful response. + +swagger:response removeTierNoContent +*/ +type RemoveTierNoContent struct { +} + +// NewRemoveTierNoContent creates RemoveTierNoContent with default headers values +func NewRemoveTierNoContent() *RemoveTierNoContent { + + return &RemoveTierNoContent{} +} + +// WriteResponse to the client +func (o *RemoveTierNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(204) +} + +/* +RemoveTierDefault Generic error response. + +swagger:response removeTierDefault +*/ +type RemoveTierDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.APIError `json:"body,omitempty"` +} + +// NewRemoveTierDefault creates RemoveTierDefault with default headers values +func NewRemoveTierDefault(code int) *RemoveTierDefault { + if code <= 0 { + code = 500 + } + + return &RemoveTierDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the remove tier default response +func (o *RemoveTierDefault) WithStatusCode(code int) *RemoveTierDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the remove tier default response +func (o *RemoveTierDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the remove tier default response +func (o *RemoveTierDefault) WithPayload(payload *models.APIError) *RemoveTierDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the remove tier default response +func (o *RemoveTierDefault) SetPayload(payload *models.APIError) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *RemoveTierDefault) 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/api/operations/tiering/remove_tier_urlbuilder.go b/api/operations/tiering/remove_tier_urlbuilder.go new file mode 100644 index 0000000000..cabe36758e --- /dev/null +++ b/api/operations/tiering/remove_tier_urlbuilder.go @@ -0,0 +1,116 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2023 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 tiering + +// 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" +) + +// RemoveTierURL generates an URL for the remove tier operation +type RemoveTierURL struct { + Name 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 *RemoveTierURL) WithBasePath(bp string) *RemoveTierURL { + 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 *RemoveTierURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *RemoveTierURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/admin/tiers/{name}/remove" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on RemoveTierURL") + } + + _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 *RemoveTierURL) 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 *RemoveTierURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *RemoveTierURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on RemoveTierURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on RemoveTierURL") + } + + 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 *RemoveTierURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/swagger.yml b/swagger.yml index 11182ef53c..457944ce61 100644 --- a/swagger.yml +++ b/swagger.yml @@ -2767,6 +2767,24 @@ paths: $ref: "#/definitions/ApiError" tags: - Tiering + /admin/tiers/{name}/remove: + delete: + summary: Remove Tier + operationId: RemoveTier + parameters: + - name: name + in: path + required: true + type: string + responses: + 204: + description: A successful response. + default: + description: Generic error response. + schema: + $ref: "#/definitions/ApiError" + tags: + - Tiering /nodes: get: diff --git a/web-app/src/api/consoleApi.ts b/web-app/src/api/consoleApi.ts index 5c9c5d63ba..9ab2f2b998 100644 --- a/web-app/src/api/consoleApi.ts +++ b/web-app/src/api/consoleApi.ts @@ -1761,7 +1761,7 @@ export class HttpClient { : payloadFormatter(body), }, ).then(async (response) => { - const r = response as HttpResponse; + const r = response.clone() as HttpResponse; r.data = null as unknown as T; r.error = null as unknown as E; @@ -4542,6 +4542,23 @@ export class Api< ...params, }), + /** + * No description + * + * @tags Tiering + * @name RemoveTier + * @summary Remove Tier + * @request DELETE:/admin/tiers/{name}/remove + * @secure + */ + removeTier: (name: string, params: RequestParams = {}) => + this.request({ + path: `/admin/tiers/${name}/remove`, + method: "DELETE", + secure: true, + ...params, + }), + /** * No description * diff --git a/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx new file mode 100644 index 0000000000..7eac9864aa --- /dev/null +++ b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx @@ -0,0 +1,83 @@ +// 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 . + +import React, { Fragment, useEffect, useState } from "react"; +import get from "lodash/get"; +import { ConfirmModalIcon } from "mds"; +import { Tier } from "api/consoleApi"; +import { api } from "api"; +import { errorToHandler } from "api/errors"; +import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary"; +import { setModalErrorSnackMessage } from "../../../../systemSlice"; +import { useAppDispatch } from "../../../../store"; +import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; +import ConfirmDialog from "screens/Console/Common/ModalWrapper/ConfirmDialog"; + +interface ITierDeleteModal { + open: boolean; + closeModalAndRefresh: (refresh: boolean) => any; + tierName: string; +} + +const DeleteTierConfirmModal = ({ + open, + closeModalAndRefresh, + tierName, +}: ITierDeleteModal) => { + const dispatch = useAppDispatch(); + + const deleteTier = () => { + if (tierName !== "") { + api.admin + .removeTier(tierName) + .then(() => { + closeModalAndRefresh(true); + }) + .catch((err) => { + dispatch(setModalErrorSnackMessage(errorToHandler(err.error))); + }); + } else { + setModalErrorSnackMessage({ + errorMessage: "There was an error deleting the tier", + detailedError: "", + }); + } + }; + + return ( + } + isLoading={false} + onConfirm={() => deleteTier()} + onClose={() => closeModalAndRefresh(true)} + confirmationContent={ + + Are you sure you want to delete the tier {tierName}? +
+
+ Please note +
Only empty tiers can be deleted. If the tier has had objects + transitioned into it, it cannot be removed. +
+ } + /> + ); +}; + +export default DeleteTierConfirmModal; diff --git a/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx b/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx index 3620d994fa..53a30d4808 100644 --- a/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx +++ b/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx @@ -41,7 +41,10 @@ import { actionsTray } from "../../Common/FormComponents/common/styleLibrary"; import { CONSOLE_UI_RESOURCE, IAM_PAGES, + IAM_PERMISSIONS, + IAM_ROLES, IAM_SCOPES, + S3_ALL_RESOURCES, } from "../../../../common/SecureComponent/permissions"; import { hasPermission, @@ -61,6 +64,7 @@ import DistributedOnly from "../../Common/DistributedOnly/DistributedOnly"; import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper"; import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper"; import HelpMenu from "../../HelpMenu"; +import DeleteTierConfirmModal from "./DeleteTierConfirmModal"; const UpdateTierCredentialsModal = withSuspense( React.lazy(() => import("./UpdateTierCredentialsModal")), @@ -76,6 +80,9 @@ const ListTiersConfiguration = () => { const [isLoading, setIsLoading] = useState(true); const [updateCredentialsOpen, setUpdateCredentialsOpen] = useState(false); + + const [deleteTierModalOpen, setDeleteTierModalOpen] = + useState(false); const [selectedTier, setSelectedTier] = useState({ type: "unsupported", status: false, @@ -261,6 +268,10 @@ const ListTiersConfiguration = () => { const closeTierCredentials = () => { setUpdateCredentialsOpen(false); }; + const closeDeleteTier = () => { + setDeleteTierModalOpen(false); + setIsLoading(true); + }; useEffect(() => { dispatch(setHelpName("list-tiers-configuration")); @@ -276,6 +287,13 @@ const ListTiersConfiguration = () => { closeModalAndRefresh={closeTierCredentials} /> )} + {deleteTierModalOpen && ( + + )} } /> @@ -357,6 +375,19 @@ const ListTiersConfiguration = () => { setUpdateCredentialsOpen(true); }, }, + { + type: "delete", + isDisabled: !hasPermission( + "*", + IAM_PERMISSIONS[IAM_ROLES.BUCKET_LIFECYCLE], + true, + ), + + onClick: (tierData: Tier) => { + setSelectedTier(tierData); + setDeleteTierModalOpen(true); + }, + }, ]} columns={[ { diff --git a/web-app/src/screens/Console/HelpTipItem.tsx b/web-app/src/screens/Console/HelpTipItem.tsx new file mode 100644 index 0000000000..e65efad9e5 --- /dev/null +++ b/web-app/src/screens/Console/HelpTipItem.tsx @@ -0,0 +1,83 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2023 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, { Fragment } from "react"; +import { DocItem } from "./HelpMenu.types"; +import placeholderImg from "../../placeholderimage.png"; + +interface IHelpItemProps { + item: DocItem; + displayImage?: boolean; +} + +const HelpTipItem = ({ item, displayImage = true }: IHelpItemProps) => { + return ( + +
+ {displayImage && ( +
+ + {item.title} + +
+ )} +
+ +
+ {item.title} +
+
+
+ {item.body} +
+
+
+
+ ); +}; + +export default HelpTipItem; diff --git a/web-app/src/screens/Console/HelpTipTabs.tsx b/web-app/src/screens/Console/HelpTipTabs.tsx new file mode 100644 index 0000000000..2b0853b70f --- /dev/null +++ b/web-app/src/screens/Console/HelpTipTabs.tsx @@ -0,0 +1,303 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2023 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, { Fragment, useEffect, useRef, useState } from "react"; +import ReactMarkdown from "react-markdown"; +import styled from "styled-components"; +import get from "lodash/get"; +import { + AlertCloseIcon, + Box, + Button, + HelpIcon, + HelpIconFilled, + IconButton, + MinIOTierIcon, + TabItemProps, + Tabs, +} from "mds"; +import { useSelector } from "react-redux"; +import { AppState, useAppDispatch } from "../../store"; +import { setHelpTabName } from "../../systemSlice"; +import { DocItem } from "./HelpMenu.types"; +import HelpTipItem from "./HelpTipItem"; +import MoreLink from "../../common/MoreLink"; + +const HelpMenuContainer = styled.div(({ theme }) => ({ + backgroundColor: get(theme, "bgColor", "#FFF"), + position: "absolute", + zIndex: "10", + border: `${get(theme, "borderColor", "#E2E2E2")} 1px solid`, + borderRadius: 4, + boxShadow: "rgba(0, 0, 0, 0.1) 0px 0px 10px", + width: 754, + "& .tabsPanels": { + padding: "15px 0 0", + }, + "& .helpContainer": { + maxHeight: 400, + overflowY: "auto", + "& .helpItemBlock": { + padding: 5, + "&:hover": { + backgroundColor: get( + theme, + "buttons.regular.hover.background", + "#E6EAEB", + ), + }, + }, + }, +})); + +const HelpTipTabs = () => { + const helpTopics = require("../Console/helpTipTags.json"); + + const [helpItems, setHelpItems] = useState([]); + const [headerDocs, setHeaderDocs] = useState(null); + const [helpItemsVideo, setHelpItemsVideo] = useState([]); + const [headerVideo, setHeaderVideo] = useState(null); + const [helpItemsBlog, setHelpItemsBlog] = useState([]); + const [headerBlog, setHeaderBlog] = useState(null); + + const systemHelpName = useSelector( + (state: AppState) => state.system.helpName, + ); + + const helpTabName = useSelector( + (state: AppState) => state.system.helpTabName, + ); + + const dispatch = useAppDispatch(); + + useEffect(() => { + let docsTotal = 0; + let blogTotal = 0; + let videoTotal = 0; + if (helpTopics[systemHelpName]) { + if (helpTopics[systemHelpName]["docs"]) { + setHeaderDocs(helpTopics[systemHelpName]["docs"]["header"]); + setHelpItems(helpTopics[systemHelpName]["docs"]["links"]); + docsTotal = helpTopics[systemHelpName]["docs"]["links"].length; + } + + if (helpTopics[systemHelpName]["blog"]) { + setHeaderBlog(helpTopics[systemHelpName]["blog"]["header"]); + setHelpItemsBlog(helpTopics[systemHelpName]["blog"]["links"]); + blogTotal = helpTopics[systemHelpName]["blog"]["links"].length; + } + + if (helpTopics[systemHelpName]["video"]) { + setHeaderVideo(helpTopics[systemHelpName]["video"]["header"]); + setHelpItemsVideo(helpTopics[systemHelpName]["video"]["links"]); + videoTotal = helpTopics[systemHelpName]["video"]["links"].length; + } + + let autoSelect = "docs"; + let hadToFlip = false; + // if no docs, eval video o blog + if (docsTotal === 0 && headerDocs === null && helpTabName === "docs") { + // if no blog, default video? + if (videoTotal !== 0 || headerVideo !== null) { + autoSelect = "video"; + } else { + autoSelect = "blog"; + } + hadToFlip = true; + } + if (videoTotal === 0 && headerVideo === null && helpTabName === "video") { + // if no blog, default video? + if (docsTotal !== 0 || headerDocs !== null) { + autoSelect = "docs"; + } else { + autoSelect = "blog"; + } + hadToFlip = true; + } + if (blogTotal === 0 && headerBlog === null && helpTabName === "blog") { + // if no blog, default video? + if (docsTotal !== 0 || headerDocs !== null) { + autoSelect = "docs"; + } else { + autoSelect = "video"; + } + hadToFlip = true; + } + if (hadToFlip) { + dispatch(setHelpTabName(autoSelect)); + } + } else { + setHelpItems(helpTopics["help"]["docs"]["links"]); + setHelpItemsBlog([]); + setHelpItemsVideo([]); + } + }, [ + systemHelpName, + helpTabName, + dispatch, + helpTopics, + headerBlog, + headerDocs, + headerVideo, + ]); + + const helpContent = ( + + {headerDocs && ( +
+
+ {`${headerDocs}`} +
+
+
+ )} + {helpItems && + helpItems.map((aHelpItem, idx) => ( + + + + ))} + + ); + const helpContentVideo = ( + + {headerVideo && ( + +
+ {`${headerVideo}`} +
+
+ + )} + {helpItemsVideo && + helpItemsVideo.map((aHelpItem, idx) => ( + + + + ))} + + ); + const helpContentBlog = ( + + {headerBlog && ( + +
+ {`${headerBlog}`} +
+
+ + )} + {helpItemsBlog && + helpItemsBlog.map((aHelpItem, idx) => ( + + + + ))} + + ); + + const constructHTTabs = () => { + const helpMenuElements: TabItemProps[] = []; + + if (helpItems.length !== 0) { + helpMenuElements.push({ + tabConfig: { label: "Documentation", id: "docs" }, + content: helpContent, + }); + } + + if (helpItemsVideo.length !== 0) { + helpMenuElements.push({ + tabConfig: { label: "Video", id: "video" }, + content: helpContentVideo, + }); + } + + if (helpItemsBlog.length !== 0) { + helpMenuElements.push({ + tabConfig: { label: "Blog", id: "blog" }, + content: helpContentBlog, + }); + } + + return helpMenuElements; + }; + + /* + + + helpMenuElements.push({ + tabConfig: { label: "💡", id: "icon" }, + content: + You can choose to{" "} + + exclude folders and prefixes + {" "} + from versioning if Object Locking is not enabled. +
+ MinIO requires versioning to support replication. +
+ Objects in excluded prefixes do not replicate to any + peer site or remote site. +
, + }); + dispatch(setHelpTabName(item))} + horizontalBarBackground + horizontal + /> + + { + thisStuff[0].map((aHelpItem, idx) => ( + + {aHelpItem.content} + )) + } +

Blog

+

Video

+ */ + + return ( + +

💡

+
+ You can choose to exclude folders and prefixes from versioning if Object + Locking is not enabled. +
+ MinIO requires versioning to support replication. +
+ Objects in excluded prefixes do not replicate to any peer site or remote + site. +
+ + {" "} +

Docs

+
+

Blog

+
+ {helpItemsBlog.map((aHelpItem, idx) => ( + + + + ))} +
+
+ ); +}; + +export default HelpTipTabs; From 5467fb8108d1387c9c9daba928d4e13b24591520 Mon Sep 17 00:00:00 2001 From: Jillian Inapurapu Date: Thu, 6 Jun 2024 14:47:26 -0700 Subject: [PATCH 2/5] Cleanup --- .../DeleteTierConfirmModal.tsx | 6 +- web-app/src/screens/Console/HelpTipItem.tsx | 83 ----- web-app/src/screens/Console/HelpTipTabs.tsx | 303 ------------------ 3 files changed, 1 insertion(+), 391 deletions(-) delete mode 100644 web-app/src/screens/Console/HelpTipItem.tsx delete mode 100644 web-app/src/screens/Console/HelpTipTabs.tsx diff --git a/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx index 7eac9864aa..4a5ed3e539 100644 --- a/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx +++ b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx @@ -1,5 +1,5 @@ // This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. +// Copyright (c) 2024 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 @@ -15,15 +15,11 @@ // along with this program. If not, see . import React, { Fragment, useEffect, useState } from "react"; -import get from "lodash/get"; import { ConfirmModalIcon } from "mds"; -import { Tier } from "api/consoleApi"; import { api } from "api"; import { errorToHandler } from "api/errors"; -import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary"; import { setModalErrorSnackMessage } from "../../../../systemSlice"; import { useAppDispatch } from "../../../../store"; -import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; import ConfirmDialog from "screens/Console/Common/ModalWrapper/ConfirmDialog"; interface ITierDeleteModal { diff --git a/web-app/src/screens/Console/HelpTipItem.tsx b/web-app/src/screens/Console/HelpTipItem.tsx deleted file mode 100644 index e65efad9e5..0000000000 --- a/web-app/src/screens/Console/HelpTipItem.tsx +++ /dev/null @@ -1,83 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2023 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, { Fragment } from "react"; -import { DocItem } from "./HelpMenu.types"; -import placeholderImg from "../../placeholderimage.png"; - -interface IHelpItemProps { - item: DocItem; - displayImage?: boolean; -} - -const HelpTipItem = ({ item, displayImage = true }: IHelpItemProps) => { - return ( - -
- {displayImage && ( -
- - {item.title} - -
- )} -
- -
- {item.title} -
-
-
- {item.body} -
-
-
-
- ); -}; - -export default HelpTipItem; diff --git a/web-app/src/screens/Console/HelpTipTabs.tsx b/web-app/src/screens/Console/HelpTipTabs.tsx deleted file mode 100644 index 2b0853b70f..0000000000 --- a/web-app/src/screens/Console/HelpTipTabs.tsx +++ /dev/null @@ -1,303 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2023 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, { Fragment, useEffect, useRef, useState } from "react"; -import ReactMarkdown from "react-markdown"; -import styled from "styled-components"; -import get from "lodash/get"; -import { - AlertCloseIcon, - Box, - Button, - HelpIcon, - HelpIconFilled, - IconButton, - MinIOTierIcon, - TabItemProps, - Tabs, -} from "mds"; -import { useSelector } from "react-redux"; -import { AppState, useAppDispatch } from "../../store"; -import { setHelpTabName } from "../../systemSlice"; -import { DocItem } from "./HelpMenu.types"; -import HelpTipItem from "./HelpTipItem"; -import MoreLink from "../../common/MoreLink"; - -const HelpMenuContainer = styled.div(({ theme }) => ({ - backgroundColor: get(theme, "bgColor", "#FFF"), - position: "absolute", - zIndex: "10", - border: `${get(theme, "borderColor", "#E2E2E2")} 1px solid`, - borderRadius: 4, - boxShadow: "rgba(0, 0, 0, 0.1) 0px 0px 10px", - width: 754, - "& .tabsPanels": { - padding: "15px 0 0", - }, - "& .helpContainer": { - maxHeight: 400, - overflowY: "auto", - "& .helpItemBlock": { - padding: 5, - "&:hover": { - backgroundColor: get( - theme, - "buttons.regular.hover.background", - "#E6EAEB", - ), - }, - }, - }, -})); - -const HelpTipTabs = () => { - const helpTopics = require("../Console/helpTipTags.json"); - - const [helpItems, setHelpItems] = useState([]); - const [headerDocs, setHeaderDocs] = useState(null); - const [helpItemsVideo, setHelpItemsVideo] = useState([]); - const [headerVideo, setHeaderVideo] = useState(null); - const [helpItemsBlog, setHelpItemsBlog] = useState([]); - const [headerBlog, setHeaderBlog] = useState(null); - - const systemHelpName = useSelector( - (state: AppState) => state.system.helpName, - ); - - const helpTabName = useSelector( - (state: AppState) => state.system.helpTabName, - ); - - const dispatch = useAppDispatch(); - - useEffect(() => { - let docsTotal = 0; - let blogTotal = 0; - let videoTotal = 0; - if (helpTopics[systemHelpName]) { - if (helpTopics[systemHelpName]["docs"]) { - setHeaderDocs(helpTopics[systemHelpName]["docs"]["header"]); - setHelpItems(helpTopics[systemHelpName]["docs"]["links"]); - docsTotal = helpTopics[systemHelpName]["docs"]["links"].length; - } - - if (helpTopics[systemHelpName]["blog"]) { - setHeaderBlog(helpTopics[systemHelpName]["blog"]["header"]); - setHelpItemsBlog(helpTopics[systemHelpName]["blog"]["links"]); - blogTotal = helpTopics[systemHelpName]["blog"]["links"].length; - } - - if (helpTopics[systemHelpName]["video"]) { - setHeaderVideo(helpTopics[systemHelpName]["video"]["header"]); - setHelpItemsVideo(helpTopics[systemHelpName]["video"]["links"]); - videoTotal = helpTopics[systemHelpName]["video"]["links"].length; - } - - let autoSelect = "docs"; - let hadToFlip = false; - // if no docs, eval video o blog - if (docsTotal === 0 && headerDocs === null && helpTabName === "docs") { - // if no blog, default video? - if (videoTotal !== 0 || headerVideo !== null) { - autoSelect = "video"; - } else { - autoSelect = "blog"; - } - hadToFlip = true; - } - if (videoTotal === 0 && headerVideo === null && helpTabName === "video") { - // if no blog, default video? - if (docsTotal !== 0 || headerDocs !== null) { - autoSelect = "docs"; - } else { - autoSelect = "blog"; - } - hadToFlip = true; - } - if (blogTotal === 0 && headerBlog === null && helpTabName === "blog") { - // if no blog, default video? - if (docsTotal !== 0 || headerDocs !== null) { - autoSelect = "docs"; - } else { - autoSelect = "video"; - } - hadToFlip = true; - } - if (hadToFlip) { - dispatch(setHelpTabName(autoSelect)); - } - } else { - setHelpItems(helpTopics["help"]["docs"]["links"]); - setHelpItemsBlog([]); - setHelpItemsVideo([]); - } - }, [ - systemHelpName, - helpTabName, - dispatch, - helpTopics, - headerBlog, - headerDocs, - headerVideo, - ]); - - const helpContent = ( - - {headerDocs && ( -
-
- {`${headerDocs}`} -
-
-
- )} - {helpItems && - helpItems.map((aHelpItem, idx) => ( - - - - ))} - - ); - const helpContentVideo = ( - - {headerVideo && ( - -
- {`${headerVideo}`} -
-
- - )} - {helpItemsVideo && - helpItemsVideo.map((aHelpItem, idx) => ( - - - - ))} - - ); - const helpContentBlog = ( - - {headerBlog && ( - -
- {`${headerBlog}`} -
-
- - )} - {helpItemsBlog && - helpItemsBlog.map((aHelpItem, idx) => ( - - - - ))} - - ); - - const constructHTTabs = () => { - const helpMenuElements: TabItemProps[] = []; - - if (helpItems.length !== 0) { - helpMenuElements.push({ - tabConfig: { label: "Documentation", id: "docs" }, - content: helpContent, - }); - } - - if (helpItemsVideo.length !== 0) { - helpMenuElements.push({ - tabConfig: { label: "Video", id: "video" }, - content: helpContentVideo, - }); - } - - if (helpItemsBlog.length !== 0) { - helpMenuElements.push({ - tabConfig: { label: "Blog", id: "blog" }, - content: helpContentBlog, - }); - } - - return helpMenuElements; - }; - - /* - - - helpMenuElements.push({ - tabConfig: { label: "💡", id: "icon" }, - content: - You can choose to{" "} - - exclude folders and prefixes - {" "} - from versioning if Object Locking is not enabled. -
- MinIO requires versioning to support replication. -
- Objects in excluded prefixes do not replicate to any - peer site or remote site. -
, - }); - dispatch(setHelpTabName(item))} - horizontalBarBackground - horizontal - /> - - { - thisStuff[0].map((aHelpItem, idx) => ( - - {aHelpItem.content} - )) - } -

Blog

-

Video

- */ - - return ( - -

💡

-
- You can choose to exclude folders and prefixes from versioning if Object - Locking is not enabled. -
- MinIO requires versioning to support replication. -
- Objects in excluded prefixes do not replicate to any peer site or remote - site. -
- - {" "} -

Docs

-
-

Blog

-
- {helpItemsBlog.map((aHelpItem, idx) => ( - - - - ))} -
-
- ); -}; - -export default HelpTipTabs; From 0d16bd989afb103b2d4453c0d795a80bd0d19741 Mon Sep 17 00:00:00 2001 From: Jillian Inapurapu Date: Thu, 6 Jun 2024 15:08:35 -0700 Subject: [PATCH 3/5] Add removeTier to AdminClientMock, cleanup --- api/admin_client_mock.go | 13 +++++++++---- .../TiersConfiguration/DeleteTierConfirmModal.tsx | 2 +- .../TiersConfiguration/ListTiersConfiguration.tsx | 2 -- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/api/admin_client_mock.go b/api/admin_client_mock.go index 2165a1fc16..91e1b98dc2 100644 --- a/api/admin_client_mock.go +++ b/api/admin_client_mock.go @@ -66,10 +66,11 @@ var ( deleteSiteReplicationInfoMock func(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error) getSiteReplicationStatus func(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error) - minioListTiersMock func(ctx context.Context) ([]*madmin.TierConfig, error) - minioTierStatsMock func(ctx context.Context) ([]madmin.TierInfo, error) - minioAddTiersMock func(ctx context.Context, tier *madmin.TierConfig) error - minioEditTiersMock func(ctx context.Context, tierName string, creds madmin.TierCreds) error + minioListTiersMock func(ctx context.Context) ([]*madmin.TierConfig, error) + minioTierStatsMock func(ctx context.Context) ([]madmin.TierInfo, error) + minioAddTiersMock func(ctx context.Context, tier *madmin.TierConfig) error + minioRemoveTierMock func(ctx context.Context, tierName string) error + minioEditTiersMock func(ctx context.Context, tierName string, creds madmin.TierCreds) error minioServiceTraceMock func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo @@ -345,6 +346,10 @@ func (ac AdminClientMock) addTier(ctx context.Context, tier *madmin.TierConfig) return minioAddTiersMock(ctx, tier) } +func (ac AdminClientMock) removeTier(ctx context.Context, tierName string) error { + return minioRemoveTierMock(ctx, tierName) +} + func (ac AdminClientMock) editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error { return minioEditTiersMock(ctx, tierName, creds) } diff --git a/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx index 4a5ed3e539..b46993a854 100644 --- a/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx +++ b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React, { Fragment, useEffect, useState } from "react"; +import React from "react"; import { ConfirmModalIcon } from "mds"; import { api } from "api"; import { errorToHandler } from "api/errors"; diff --git a/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx b/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx index 53a30d4808..9ba9b12aae 100644 --- a/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx +++ b/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx @@ -44,7 +44,6 @@ import { IAM_PERMISSIONS, IAM_ROLES, IAM_SCOPES, - S3_ALL_RESOURCES, } from "../../../../common/SecureComponent/permissions"; import { hasPermission, @@ -382,7 +381,6 @@ const ListTiersConfiguration = () => { IAM_PERMISSIONS[IAM_ROLES.BUCKET_LIFECYCLE], true, ), - onClick: (tierData: Tier) => { setSelectedTier(tierData); setDeleteTierModalOpen(true); From fb247b1d00a185a620b951e018bb2fac20fb5b92 Mon Sep 17 00:00:00 2001 From: Jillian Inapurapu Date: Thu, 6 Jun 2024 15:16:26 -0700 Subject: [PATCH 4/5] gofumpt --- api/admin_tiers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/api/admin_tiers.go b/api/admin_tiers.go index 6bddc106f9..5c13cb36cf 100644 --- a/api/admin_tiers.go +++ b/api/admin_tiers.go @@ -419,7 +419,6 @@ func getEditTierCredentialsResponse(session *models.Principal, params tieringApi } func removeTier(ctx context.Context, client MinioAdmin, params *tieringApi.RemoveTierParams) error { - return client.removeTier(ctx, params.Name) } From 40319850453d07aa46215fefe9bca6566e63a6e4 Mon Sep 17 00:00:00 2001 From: Jillian Inapurapu Date: Thu, 6 Jun 2024 15:41:28 -0700 Subject: [PATCH 5/5] Remove refresh on modal close withoit submit --- .../TiersConfiguration/DeleteTierConfirmModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx index b46993a854..0e534e7667 100644 --- a/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx +++ b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx @@ -61,7 +61,7 @@ const DeleteTierConfirmModal = ({ titleIcon={} isLoading={false} onConfirm={() => deleteTier()} - onClose={() => closeModalAndRefresh(true)} + onClose={() => closeModalAndRefresh(false)} confirmationContent={ Are you sure you want to delete the tier {tierName}?