From 24742325b7f75b6cf30ef4a169aadf36785f1cd2 Mon Sep 17 00:00:00 2001 From: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:37:28 -0800 Subject: [PATCH] Remove useAPI from DeleteUser and DeleteIDPConfigurationModal (#3191) --- api/embedded_spec.go | 22 ++++++--- .../check_user_service_accounts_parameters.go | 22 +++++++-- models/selected_users.go | 44 +++++++++++++++++ swagger.yml | 44 +++++++++-------- web-app/src/api/consoleApi.ts | 13 +++-- .../IDP/DeleteIDPConfigurationModal.tsx | 24 +++++---- .../src/screens/Console/Users/DeleteUser.tsx | 49 +++++++++---------- 7 files changed, 145 insertions(+), 73 deletions(-) create mode 100644 models/selected_users.go diff --git a/api/embedded_spec.go b/api/embedded_spec.go index 7df225638..b663b4653 100644 --- a/api/embedded_spec.go +++ b/api/embedded_spec.go @@ -5268,10 +5268,7 @@ func init() { "in": "body", "required": true, "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/selectedUsers" } } ], @@ -7988,6 +7985,12 @@ func init() { } } }, + "selectedUsers": { + "type": "array", + "items": { + "type": "string" + } + }, "serverDrives": { "type": "object", "properties": { @@ -14458,10 +14461,7 @@ func init() { "in": "body", "required": true, "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/selectedUsers" } } ], @@ -17327,6 +17327,12 @@ func init() { } } }, + "selectedUsers": { + "type": "array", + "items": { + "type": "string" + } + }, "serverDrives": { "type": "object", "properties": { diff --git a/api/operations/user/check_user_service_accounts_parameters.go b/api/operations/user/check_user_service_accounts_parameters.go index b7868d400..0a6877424 100644 --- a/api/operations/user/check_user_service_accounts_parameters.go +++ b/api/operations/user/check_user_service_accounts_parameters.go @@ -29,6 +29,9 @@ import ( "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/validate" + + "github.com/minio/console/models" ) // NewCheckUserServiceAccountsParams creates a new CheckUserServiceAccountsParams object @@ -52,7 +55,7 @@ type CheckUserServiceAccountsParams struct { Required: true In: body */ - SelectedUsers []string + SelectedUsers models.SelectedUsers } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -66,7 +69,7 @@ func (o *CheckUserServiceAccountsParams) BindRequest(r *http.Request, route *mid if runtime.HasBody(r) { defer r.Body.Close() - var body []string + var body models.SelectedUsers if err := route.Consumer.Consume(r.Body, &body); err != nil { if err == io.EOF { res = append(res, errors.Required("selectedUsers", "body", "")) @@ -74,8 +77,19 @@ func (o *CheckUserServiceAccountsParams) BindRequest(r *http.Request, route *mid res = append(res, errors.NewParseError("selectedUsers", "body", "", err)) } } else { - // no validation required on inline body - o.SelectedUsers = body + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.SelectedUsers = body + } } } else { res = append(res, errors.Required("selectedUsers", "body", "")) diff --git a/models/selected_users.go b/models/selected_users.go new file mode 100644 index 000000000..3fb95ac7a --- /dev/null +++ b/models/selected_users.go @@ -0,0 +1,44 @@ +// 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 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" +) + +// SelectedUsers selected users +// +// swagger:model selectedUsers +type SelectedUsers []string + +// Validate validates this selected users +func (m SelectedUsers) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this selected users based on context it is used +func (m SelectedUsers) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} diff --git a/swagger.yml b/swagger.yml index ad2bfa183..8b62b2965 100644 --- a/swagger.yml +++ b/swagger.yml @@ -25,7 +25,7 @@ securityDefinitions: type: apiKey # Apply the key security definition to all APIs security: - - key: [] + - key: [ ] parameters: limit: name: limit @@ -54,7 +54,7 @@ paths: schema: $ref: "#/definitions/ApiError" # Exclude this API from the authentication requirement - security: [] + security: [ ] tags: - Auth post: @@ -74,7 +74,7 @@ paths: schema: $ref: "#/definitions/ApiError" # Exclude this API from the authentication requirement - security: [] + security: [ ] tags: - Auth /login/oauth2/auth: @@ -94,7 +94,7 @@ paths: description: Generic error response. schema: $ref: "#/definitions/ApiError" - security: [] + security: [ ] tags: - Auth @@ -295,8 +295,8 @@ paths: get: summary: List Objects security: - - key: [] - - anonymous: [] + - key: [ ] + - anonymous: [ ] operationId: ListObjects parameters: - name: bucket_name @@ -411,8 +411,8 @@ paths: post: summary: Uploads an Object. security: - - key: [] - - anonymous: [] + - key: [ ] + - anonymous: [ ] consumes: - multipart/form-data parameters: @@ -438,8 +438,8 @@ paths: summary: Download Multiple Objects operationId: DownloadMultipleObjects security: - - key: [] - - anonymous: [] + - key: [ ] + - anonymous: [ ] produces: - application/octet-stream parameters: @@ -471,8 +471,8 @@ paths: summary: Download Object operationId: Download Object security: - - key: [] - - anonymous: [] + - key: [ ] + - anonymous: [ ] produces: - application/octet-stream parameters: @@ -1546,9 +1546,7 @@ paths: in: body required: true schema: - type: array - items: - type: string + $ref: "#/definitions/selectedUsers" responses: 200: description: A successful response. @@ -2889,7 +2887,7 @@ paths: - name: order in: query type: string - enum: [timeDesc, timeAsc] + enum: [ timeDesc, timeAsc ] default: timeDesc - name: timeStart in: query @@ -3766,6 +3764,10 @@ definitions: items: $ref: "#/definitions/user" title: list of resulting users + selectedUsers: + type: array + items: + type: string addUserRequest: type: object required: @@ -4325,7 +4327,7 @@ definitions: properties: loginStrategy: type: string - enum: [form, redirect, service-account, redirect-service-account] + enum: [ form, redirect, service-account, redirect-service-account ] redirectRules: type: array items: @@ -4424,7 +4426,7 @@ definitions: type: string status: type: string - enum: [ok] + enum: [ ok ] operator: type: boolean distributedMode: @@ -4455,7 +4457,7 @@ definitions: type: string values: type: array - items: {} + items: { } resultTarget: type: object properties: @@ -4911,7 +4913,7 @@ definitions: type: string service: type: string - enum: [replication] + enum: [ replication ] syncMode: type: string bandwidth: @@ -5110,7 +5112,7 @@ definitions: format: int64 newer_noncurrent_expiration_versions: type: integer - format: int64 + format: int64 transitionResponse: type: object diff --git a/web-app/src/api/consoleApi.ts b/web-app/src/api/consoleApi.ts index cfad43275..0bd52bacc 100644 --- a/web-app/src/api/consoleApi.ts +++ b/web-app/src/api/consoleApi.ts @@ -149,6 +149,8 @@ export interface ListUsersResponse { users?: User[]; } +export type SelectedUsers = string[]; + export interface AddUserRequest { accessKey: string; secretKey: string; @@ -3242,7 +3244,7 @@ export class Api< * @secure */ checkUserServiceAccounts: ( - selectedUsers: string[], + selectedUsers: SelectedUsers, params: RequestParams = {}, ) => this.request({ @@ -3250,6 +3252,7 @@ export class Api< method: "POST", body: selectedUsers, secure: true, + type: ContentType.Json, format: "json", ...params, }), @@ -3635,7 +3638,7 @@ export class Api< * @secure */ listUsersForPolicy: (policy: string, params: RequestParams = {}) => - this.request({ + this.request({ path: `/policies/${policy}/users`, method: "GET", secure: true, @@ -3653,7 +3656,7 @@ export class Api< * @secure */ listGroupsForPolicy: (policy: string, params: RequestParams = {}) => - this.request({ + this.request({ path: `/policies/${policy}/groups`, method: "GET", secure: true, @@ -3805,7 +3808,7 @@ export class Api< }, params: RequestParams = {}, ) => - this.request({ + this.request({ path: `/bucket-users/${bucket}`, method: "GET", query: query, @@ -4566,7 +4569,7 @@ export class Api< * @secure */ listNodes: (params: RequestParams = {}) => - this.request({ + this.request({ path: `/nodes`, method: "GET", secure: true, diff --git a/web-app/src/screens/Console/IDP/DeleteIDPConfigurationModal.tsx b/web-app/src/screens/Console/IDP/DeleteIDPConfigurationModal.tsx index 380cfdfe6..ae06f31fd 100644 --- a/web-app/src/screens/Console/IDP/DeleteIDPConfigurationModal.tsx +++ b/web-app/src/screens/Console/IDP/DeleteIDPConfigurationModal.tsx @@ -14,16 +14,17 @@ // 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 React, { Fragment, useState } from "react"; import { ConfirmDeleteIcon } from "mds"; import { setErrorSnackMessage, setServerNeedsRestart, } from "../../../systemSlice"; import { useAppDispatch } from "../../../store"; -import { ErrorResponseHandler } from "../../../common/types"; -import useApi from "../Common/Hooks/useApi"; import ConfirmDialog from "../Common/ModalWrapper/ConfirmDialog"; +import { api } from "api"; +import { SetIDPResponse } from "../../../api/consoleApi"; +import { errorToHandler } from "../../../api/errors"; interface IDeleteIDPConfigurationModalProps { closeDeleteModalAndRefresh: (refresh: boolean) => void; @@ -39,22 +40,27 @@ const DeleteIDPConfigurationModal = ({ idpType, }: IDeleteIDPConfigurationModalProps) => { const dispatch = useAppDispatch(); - const onDelSuccess = (res: any) => { + const onDelSuccess = (res: SetIDPResponse) => { closeDeleteModalAndRefresh(true); dispatch(setServerNeedsRestart(res.restart === true)); }; - const onDelError = (err: ErrorResponseHandler) => - dispatch(setErrorSnackMessage(err)); - const onClose = () => closeDeleteModalAndRefresh(false); - const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError); + const onClose = () => closeDeleteModalAndRefresh(false); + const [deleteLoading, setDeleteLoading] = useState(false); if (!idp) { return null; } const onConfirmDelete = () => { - invokeDeleteApi("DELETE", `/api/v1/idp/${idpType}/${idp}`); + setDeleteLoading(true); + api.idp + .deleteConfiguration(idp, idpType) + .then((res) => { + onDelSuccess(res.data); + }) + .catch((err) => dispatch(setErrorSnackMessage(errorToHandler(err.error)))) + .finally(() => setDeleteLoading(false)); }; const displayName = idp === "_" ? "Default" : idp; diff --git a/web-app/src/screens/Console/Users/DeleteUser.tsx b/web-app/src/screens/Console/Users/DeleteUser.tsx index 1ea74e0cd..4ffc92628 100644 --- a/web-app/src/screens/Console/Users/DeleteUser.tsx +++ b/web-app/src/screens/Console/Users/DeleteUser.tsx @@ -17,14 +17,14 @@ import React, { Fragment, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { setErrorSnackMessage } from "../../../systemSlice"; -import { ErrorResponseHandler } from "../../../common/types"; import { ConfirmDeleteIcon, DataTable, InformativeMessage, Loader } from "mds"; import { encodeURLString } from "../../../common/utils"; import { IAM_PAGES } from "../../../common/SecureComponent/permissions"; -import useApi from "../Common/Hooks/useApi"; import ConfirmDialog from "../Common/ModalWrapper/ConfirmDialog"; -import api from "../../../common/api"; import { useAppDispatch } from "../../../store"; +import { api } from "api"; +import { UserServiceAccountItem } from "../../../api/consoleApi"; +import { errorToHandler } from "../../../api/errors"; interface IDeleteUserProps { closeDeleteModalAndRefresh: (refresh: boolean) => void; @@ -40,33 +40,31 @@ const DeleteUser = ({ const navigate = useNavigate(); const dispatch = useAppDispatch(); - const onDelSuccess = () => closeDeleteModalAndRefresh(true); - const onDelError = (err: ErrorResponseHandler) => - dispatch(setErrorSnackMessage(err)); const onClose = () => closeDeleteModalAndRefresh(false); - const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError); const [loadingSA, setLoadingSA] = useState(true); const [hasSA, setHasSA] = useState(false); - const [userSAList, setUserSAList] = useState([]); + const [userSAList, setUserSAList] = useState([]); + const [deleteLoading, setDeleteLoading] = useState(false); const userLoggedIn = localStorage.getItem("userLoggedIn") || ""; useEffect(() => { if (selectedUsers) { - api - .invoke("POST", `/api/v1/users/service-accounts`, selectedUsers) + api.users + .checkUserServiceAccounts(selectedUsers) .then((res) => { - setUserSAList(res.userServiceAccountList); - if (res.hasSA) { - setHasSA(true); + if (res.data) { + setUserSAList(res.data.userServiceAccountList ?? []); + if (res.data.hasSA) { + setHasSA(true); + } } - setLoadingSA(false); }) - .catch((err: ErrorResponseHandler) => { - dispatch(setErrorSnackMessage(err)); - setLoadingSA(false); - }); + .catch((err) => + dispatch(setErrorSnackMessage(errorToHandler(err.error))), + ) + .finally(() => setLoadingSA(false)); } }, [selectedUsers, dispatch]); @@ -102,18 +100,17 @@ const DeleteUser = ({ ); closeDeleteModalAndRefresh(true); } else { - invokeDeleteApi("DELETE", `/api/v1/user/${encodeURLString(user)}`); - closeDeleteModalAndRefresh(true); - navigate(`${IAM_PAGES.USERS}`); + api.user + .removeUser(encodeURLString(user)) + .then((res) => { + closeDeleteModalAndRefresh(true); + navigate(`${IAM_PAGES.USERS}`); + }) + .finally(() => setDeleteLoading(false)); } } }; - interface userSACount { - userName: string; - numSAs: number; - } - const noSAtext = "Are you sure you want to delete the following " + selectedUsers.length +