Added Rename modal for filenames longer than 200 characters in Windows (#2137)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
73
models/user_s_as.go
Normal file
73
models/user_s_as.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// UserSAs user s as
|
||||
//
|
||||
// swagger:model userSAs
|
||||
type UserSAs struct {
|
||||
|
||||
// path
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// recursive
|
||||
Recursive bool `json:"recursive,omitempty"`
|
||||
|
||||
// version ID
|
||||
VersionID string `json:"versionID,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this user s as
|
||||
func (m *UserSAs) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this user s as based on context it is used
|
||||
func (m *UserSAs) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *UserSAs) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *UserSAs) UnmarshalBinary(b []byte) error {
|
||||
var res UserSAs
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -709,3 +709,13 @@ export const capacityColors = (usedSpace: number, maxSpace: number) => {
|
||||
|
||||
return "#07193E";
|
||||
};
|
||||
|
||||
export const getClientOS = (): string => {
|
||||
const getPlatform = get(window.navigator, "platform", "undefined");
|
||||
|
||||
if (!getPlatform) {
|
||||
return "undefined";
|
||||
}
|
||||
|
||||
return getPlatform;
|
||||
};
|
||||
|
||||
@@ -767,6 +767,7 @@ const ListObjects = () => {
|
||||
encodeURLString(object.name),
|
||||
object.version_id,
|
||||
object.size,
|
||||
null,
|
||||
(progress) => {
|
||||
dispatch(
|
||||
updateProgress({
|
||||
|
||||
@@ -35,6 +35,7 @@ import { ErrorResponseHandler } from "../../../../../../common/types";
|
||||
import {
|
||||
decodeURLString,
|
||||
encodeURLString,
|
||||
getClientOS,
|
||||
niceBytes,
|
||||
niceBytesInt,
|
||||
niceDaysInt,
|
||||
@@ -87,6 +88,7 @@ import {
|
||||
setVersionsModeEnabled,
|
||||
updateProgress,
|
||||
} from "../../../../ObjectBrowser/objectBrowserSlice";
|
||||
import RenameLongFileName from "../../../../ObjectBrowser/RenameLongFilename";
|
||||
|
||||
const styles = () =>
|
||||
createStyles({
|
||||
@@ -155,7 +157,6 @@ const ObjectDetailPanel = ({
|
||||
bucketName,
|
||||
versioning,
|
||||
locking,
|
||||
|
||||
onClosePanel,
|
||||
}: IObjectDetailPanelProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -183,6 +184,7 @@ const ObjectDetailPanel = ({
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [previewOpen, setPreviewOpen] = useState<boolean>(false);
|
||||
const [totalVersionsSize, setTotalVersionsSize] = useState<number>(0);
|
||||
const [longFileOpen, setLongFileOpen] = useState<boolean>(false);
|
||||
|
||||
const internalPathsDecoded = decodeURLString(internalPaths) || "";
|
||||
const allPathData = internalPathsDecoded.split("/");
|
||||
@@ -282,16 +284,29 @@ const ObjectDetailPanel = ({
|
||||
setShareFileModalOpen(false);
|
||||
};
|
||||
|
||||
const closeFileOpen = () => {
|
||||
setLongFileOpen(false);
|
||||
};
|
||||
|
||||
const downloadObject = (object: IFileInfo) => {
|
||||
const identityDownload = encodeURLString(
|
||||
`${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}`
|
||||
);
|
||||
|
||||
if (
|
||||
object.name.length > 200 &&
|
||||
getClientOS().toLowerCase().includes("win")
|
||||
) {
|
||||
setLongFileOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const downloadCall = download(
|
||||
bucketName,
|
||||
internalPaths,
|
||||
object.version_id,
|
||||
parseInt(object.size || "0"),
|
||||
null,
|
||||
(progress) => {
|
||||
dispatch(
|
||||
updateProgress({
|
||||
@@ -577,6 +592,16 @@ const ObjectDetailPanel = ({
|
||||
closeInspectModalAndRefresh={closeInspectModal}
|
||||
/>
|
||||
)}
|
||||
{longFileOpen && actualInfo && (
|
||||
<RenameLongFileName
|
||||
open={longFileOpen}
|
||||
closeModal={closeFileOpen}
|
||||
currentItem={currentItem}
|
||||
bucketName={bucketName}
|
||||
internalPaths={internalPaths}
|
||||
actualInfo={actualInfo}
|
||||
/>
|
||||
)}
|
||||
|
||||
{loadingObjectInfo ? (
|
||||
<Fragment>{loaderForContainer}</Fragment>
|
||||
|
||||
@@ -251,6 +251,7 @@ const VersionsNavigator = ({
|
||||
internalPaths,
|
||||
object.version_id,
|
||||
parseInt(object.size || "0"),
|
||||
null,
|
||||
(progress) => {
|
||||
dispatch(
|
||||
updateProgress({
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
import { BucketObjectItem } from "./ListObjects/types";
|
||||
import { IAllowResources } from "../../../types";
|
||||
import { encodeURLString } from "../../../../../common/utils";
|
||||
|
||||
export const download = (
|
||||
bucketName: string,
|
||||
objectPath: string,
|
||||
versionID: any,
|
||||
fileSize: number,
|
||||
overrideFileName: string | null = null,
|
||||
progressCallback: (progress: number) => void,
|
||||
completeCallback: () => void,
|
||||
errorCallback: () => void,
|
||||
@@ -29,7 +31,11 @@ export const download = (
|
||||
) => {
|
||||
const anchor = document.createElement("a");
|
||||
document.body.appendChild(anchor);
|
||||
let path = `/api/v1/buckets/${bucketName}/objects/download?prefix=${objectPath}`;
|
||||
let path = `/api/v1/buckets/${bucketName}/objects/download?prefix=${objectPath}${
|
||||
overrideFileName !== null && overrideFileName.trim() !== ""
|
||||
? `&override_file_name=${encodeURLString(overrideFileName || "")}`
|
||||
: ""
|
||||
}`;
|
||||
if (versionID) {
|
||||
path = path.concat(`&version_id=${versionID}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { Button } from "@mui/material";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import { EditIcon } from "../../../icons";
|
||||
import {
|
||||
containerForHeader,
|
||||
formFieldStyles,
|
||||
modalStyleUtils,
|
||||
spacingUtils,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { IFileInfo } from "../Buckets/ListBuckets/Objects/ObjectDetails/types";
|
||||
import { encodeURLString } from "../../../common/utils";
|
||||
import { download } from "../Buckets/ListBuckets/Objects/utils";
|
||||
import {
|
||||
cancelObjectInList,
|
||||
completeObject,
|
||||
failObject,
|
||||
setNewObject,
|
||||
updateProgress,
|
||||
} from "./objectBrowserSlice";
|
||||
import { makeid, storeCallForObjectWithID } from "./transferManager";
|
||||
import { useAppDispatch } from "../../../store";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
|
||||
interface IRenameLongFilename {
|
||||
open: boolean;
|
||||
bucketName: string;
|
||||
internalPaths: string;
|
||||
currentItem: string;
|
||||
actualInfo: IFileInfo;
|
||||
closeModal: () => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
...modalStyleUtils,
|
||||
...formFieldStyles,
|
||||
...spacingUtils,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
})
|
||||
);
|
||||
|
||||
const RenameLongFileName = ({
|
||||
open,
|
||||
closeModal,
|
||||
currentItem,
|
||||
internalPaths,
|
||||
actualInfo,
|
||||
bucketName,
|
||||
}: IRenameLongFilename) => {
|
||||
const classes = useStyles();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [newFileName, setNewFileName] = useState(currentItem);
|
||||
|
||||
const doDownload = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
const identityDownload = encodeURLString(
|
||||
`${bucketName}-${
|
||||
actualInfo.name
|
||||
}-${new Date().getTime()}-${Math.random()}`
|
||||
);
|
||||
|
||||
const downloadCall = download(
|
||||
bucketName,
|
||||
internalPaths,
|
||||
actualInfo.version_id,
|
||||
parseInt(actualInfo.size || "0"),
|
||||
newFileName,
|
||||
(progress) => {
|
||||
dispatch(
|
||||
updateProgress({
|
||||
instanceID: identityDownload,
|
||||
progress: progress,
|
||||
})
|
||||
);
|
||||
},
|
||||
() => {
|
||||
dispatch(completeObject(identityDownload));
|
||||
},
|
||||
() => {
|
||||
dispatch(failObject(identityDownload));
|
||||
},
|
||||
() => {
|
||||
dispatch(cancelObjectInList(identityDownload));
|
||||
}
|
||||
);
|
||||
const ID = makeid(8);
|
||||
storeCallForObjectWithID(ID, downloadCall);
|
||||
dispatch(
|
||||
setNewObject({
|
||||
ID,
|
||||
bucketName,
|
||||
done: false,
|
||||
instanceID: identityDownload,
|
||||
percentage: 0,
|
||||
prefix: newFileName,
|
||||
type: "download",
|
||||
waitingForFile: true,
|
||||
failed: false,
|
||||
cancelled: false,
|
||||
})
|
||||
);
|
||||
|
||||
downloadCall.send();
|
||||
closeModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
title={`Rename Download`}
|
||||
modalOpen={open}
|
||||
onClose={closeModal}
|
||||
titleIcon={<EditIcon />}
|
||||
>
|
||||
<div>
|
||||
The file you are trying to download has a long name.
|
||||
<br />
|
||||
This can cause issues on Windows Systems by trimming the file name after
|
||||
download.
|
||||
<br />
|
||||
<br /> Would you like to rename the file for this download?
|
||||
</div>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
doDownload(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.modalFormScrollable}>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
id="download-filename"
|
||||
name="download-filename"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setNewFileName(event.target.value);
|
||||
}}
|
||||
label="Filename for download"
|
||||
type={"text"}
|
||||
value={newFileName}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{newFileName.length > 200 && (
|
||||
<Grid item xs={12}>
|
||||
We suggest filename to be less than 200 characters long. <br />
|
||||
Are you sure you want to continue?
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
<Grid item xs={12} className={classes.modalButtonBar}>
|
||||
<Button type="submit" variant="contained" color="primary">
|
||||
Download File
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default RenameLongFileName;
|
||||
@@ -38,23 +38,27 @@ import Grid from "@mui/material/Grid";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import { Button, DialogContentText } from "@mui/material";
|
||||
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
|
||||
import { setErrorSnackMessage, setSnackBarMessage } from "../../../../systemSlice";
|
||||
import {
|
||||
setErrorSnackMessage,
|
||||
setSnackBarMessage,
|
||||
} from "../../../../systemSlice";
|
||||
import { IKeyValue } from "../ListTenants/types";
|
||||
import KeyPairEdit from "./KeyPairEdit";
|
||||
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
|
||||
import { ITenantMonitoringStruct } from "../ListTenants/types";
|
||||
import {setPrometheusEnabled,
|
||||
setImage,
|
||||
setSidecarImage,
|
||||
setInitImage,
|
||||
setStorageClassName,
|
||||
setDiskCapacityGB,
|
||||
setServiceAccountName,
|
||||
setCPURequest,
|
||||
setMemRequest,
|
||||
} from "../TenantDetails/tenantMonitoringSlice"
|
||||
import {
|
||||
setPrometheusEnabled,
|
||||
setImage,
|
||||
setSidecarImage,
|
||||
setInitImage,
|
||||
setStorageClassName,
|
||||
setDiskCapacityGB,
|
||||
setServiceAccountName,
|
||||
setCPURequest,
|
||||
setMemRequest,
|
||||
} from "../TenantDetails/tenantMonitoringSlice";
|
||||
|
||||
import { clearValidationError } from "../utils";
|
||||
import { clearValidationError } from "../utils";
|
||||
|
||||
interface ITenantMonitoring {
|
||||
classes: any;
|
||||
@@ -87,105 +91,136 @@ const styles = (theme: Theme) =>
|
||||
const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tenantName, tenantNamespace } = useParams();
|
||||
const prometheusEnabled = useSelector((state: AppState) => state.editTenantMonitoring.prometheusEnabled)
|
||||
const image = useSelector((state: AppState) => state.editTenantMonitoring.image)
|
||||
const sidecarImage = useSelector((state: AppState) => state.editTenantMonitoring.sidecarImage)
|
||||
const initImage = useSelector((state: AppState) => state.editTenantMonitoring.initImage)
|
||||
const diskCapacityGB = useSelector((state: AppState) => state.editTenantMonitoring.diskCapacityGB)
|
||||
const cpuRequest = useSelector((state: AppState) => state.editTenantMonitoring.monitoringCPURequest)
|
||||
const memRequest = useSelector((state: AppState) => state.editTenantMonitoring.monitoringMemRequest)
|
||||
const serviceAccountName = useSelector((state: AppState) => state.editTenantMonitoring.serviceAccountName)
|
||||
const storageClassName = useSelector((state: AppState) => state.editTenantMonitoring.storageClassName)
|
||||
const prometheusEnabled = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.prometheusEnabled
|
||||
);
|
||||
const image = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.image
|
||||
);
|
||||
const sidecarImage = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.sidecarImage
|
||||
);
|
||||
const initImage = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.initImage
|
||||
);
|
||||
const diskCapacityGB = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.diskCapacityGB
|
||||
);
|
||||
const cpuRequest = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.monitoringCPURequest
|
||||
);
|
||||
const memRequest = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.monitoringMemRequest
|
||||
);
|
||||
const serviceAccountName = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.serviceAccountName
|
||||
);
|
||||
const storageClassName = useSelector(
|
||||
(state: AppState) => state.editTenantMonitoring.storageClassName
|
||||
);
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
const [toggleConfirmOpen, setToggleConfirmOpen] = useState<boolean>(false);
|
||||
|
||||
const [labels, setLabels] = useState<IKeyValue[]>([{ key: "", value: "" }] );
|
||||
const [annotations, setAnnotations] = useState<IKeyValue[]>( [{ key: "", value: "" }] );
|
||||
const [nodeSelector, setNodeSelector] = useState<IKeyValue[]>([{ key: "", value: "" }] );
|
||||
|
||||
const [labels, setLabels] = useState<IKeyValue[]>([{ key: "", value: "" }]);
|
||||
const [annotations, setAnnotations] = useState<IKeyValue[]>([
|
||||
{ key: "", value: "" },
|
||||
]);
|
||||
const [nodeSelector, setNodeSelector] = useState<IKeyValue[]>([
|
||||
{ key: "", value: "" },
|
||||
]);
|
||||
|
||||
|
||||
const [refreshMonitoringInfo, setRefreshMonitoringInfo] =
|
||||
const [refreshMonitoringInfo, setRefreshMonitoringInfo] =
|
||||
useState<boolean>(true);
|
||||
const [labelsError, setLabelsError] = useState<any>({});
|
||||
const [annotationsError, setAnnotationsError] = useState<any>({});
|
||||
const [nodeSelectorError, setNodeSelectorError] = useState<any>({});
|
||||
const [labelsError, setLabelsError] = useState<any>({});
|
||||
const [annotationsError, setAnnotationsError] = useState<any>({});
|
||||
const [nodeSelectorError, setNodeSelectorError] = useState<any>({});
|
||||
|
||||
const cleanValidation = (fieldName: string) => {
|
||||
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
||||
};
|
||||
|
||||
const setMonitoringInfo = (res : ITenantMonitoringStruct) => {
|
||||
dispatch(setImage(res.image));
|
||||
dispatch(setSidecarImage(res.sidecarImage));
|
||||
dispatch(setInitImage(res.initImage));
|
||||
dispatch(setStorageClassName(res.storageClassName));
|
||||
dispatch(setDiskCapacityGB(res.diskCapacityGB));
|
||||
dispatch(setServiceAccountName(res.serviceAccountName));
|
||||
dispatch(setCPURequest(res.monitoringCPURequest));
|
||||
if (res.monitoringMemRequest) {
|
||||
dispatch(setMemRequest(Math.floor(parseInt(res.monitoringMemRequest, 10) / 1000000000).toString()));
|
||||
} else {
|
||||
dispatch(setMemRequest("0"));
|
||||
}
|
||||
res.labels != null ? setLabels(res.labels) : setLabels([{key:"", value:""}]);
|
||||
res.annotations != null ? setAnnotations(res.annotations) :setAnnotations([{key:"", value:""}]);
|
||||
res.nodeSelector != null ? setNodeSelector(res.nodeSelector) :setNodeSelector([{key:"", value:""}]);
|
||||
}
|
||||
const cleanValidation = (fieldName: string) => {
|
||||
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
||||
};
|
||||
|
||||
const trim = (x: IKeyValue[]): IKeyValue[] => {
|
||||
let retval: IKeyValue[] = [];
|
||||
for (let i = 0; i < x.length; i++) {
|
||||
if (x[i].key !== "") {
|
||||
retval.push(x[i]);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
};
|
||||
const setMonitoringInfo = (res: ITenantMonitoringStruct) => {
|
||||
dispatch(setImage(res.image));
|
||||
dispatch(setSidecarImage(res.sidecarImage));
|
||||
dispatch(setInitImage(res.initImage));
|
||||
dispatch(setStorageClassName(res.storageClassName));
|
||||
dispatch(setDiskCapacityGB(res.diskCapacityGB));
|
||||
dispatch(setServiceAccountName(res.serviceAccountName));
|
||||
dispatch(setCPURequest(res.monitoringCPURequest));
|
||||
if (res.monitoringMemRequest) {
|
||||
dispatch(
|
||||
setMemRequest(
|
||||
Math.floor(
|
||||
parseInt(res.monitoringMemRequest, 10) / 1000000000
|
||||
).toString()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
dispatch(setMemRequest("0"));
|
||||
}
|
||||
res.labels != null
|
||||
? setLabels(res.labels)
|
||||
: setLabels([{ key: "", value: "" }]);
|
||||
res.annotations != null
|
||||
? setAnnotations(res.annotations)
|
||||
: setAnnotations([{ key: "", value: "" }]);
|
||||
res.nodeSelector != null
|
||||
? setNodeSelector(res.nodeSelector)
|
||||
: setNodeSelector([{ key: "", value: "" }]);
|
||||
};
|
||||
|
||||
const checkValid = (): boolean => {
|
||||
if (
|
||||
Object.keys(validationErrors).length !== 0 ||
|
||||
Object.keys(labelsError).length !== 0 ||
|
||||
Object.keys(annotationsError).length !== 0 ||
|
||||
Object.keys(nodeSelectorError).length !== 0
|
||||
) {
|
||||
let err: ErrorResponseHandler = {
|
||||
errorMessage: "Invalid entry",
|
||||
detailedError: "",
|
||||
};
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
const trim = (x: IKeyValue[]): IKeyValue[] => {
|
||||
let retval: IKeyValue[] = [];
|
||||
for (let i = 0; i < x.length; i++) {
|
||||
if (x[i].key !== "") {
|
||||
retval.push(x[i]);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (refreshMonitoringInfo) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/namespaces/${tenantNamespace || ""}/tenants/${
|
||||
tenantName || ""
|
||||
}/monitoring`
|
||||
)
|
||||
.then((res: ITenantMonitoringStruct) => {
|
||||
dispatch(setPrometheusEnabled(res.prometheusEnabled));
|
||||
setMonitoringInfo(res);
|
||||
setRefreshMonitoringInfo(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
setRefreshMonitoringInfo(false);
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [refreshMonitoringInfo]);
|
||||
}
|
||||
return retval;
|
||||
};
|
||||
|
||||
|
||||
const submitMonitoringInfo = () => {
|
||||
if(checkValid()){
|
||||
const checkValid = (): boolean => {
|
||||
if (
|
||||
Object.keys(validationErrors).length !== 0 ||
|
||||
Object.keys(labelsError).length !== 0 ||
|
||||
Object.keys(annotationsError).length !== 0 ||
|
||||
Object.keys(nodeSelectorError).length !== 0
|
||||
) {
|
||||
let err: ErrorResponseHandler = {
|
||||
errorMessage: "Invalid entry",
|
||||
detailedError: "",
|
||||
};
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (refreshMonitoringInfo) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/namespaces/${tenantNamespace || ""}/tenants/${
|
||||
tenantName || ""
|
||||
}/monitoring`
|
||||
)
|
||||
.then((res: ITenantMonitoringStruct) => {
|
||||
dispatch(setPrometheusEnabled(res.prometheusEnabled));
|
||||
setMonitoringInfo(res);
|
||||
setRefreshMonitoringInfo(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
setRefreshMonitoringInfo(false);
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [refreshMonitoringInfo]);
|
||||
|
||||
const submitMonitoringInfo = () => {
|
||||
if (checkValid()) {
|
||||
api
|
||||
.invoke(
|
||||
"PUT",
|
||||
@@ -209,36 +244,35 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
||||
dispatch(setSnackBarMessage(`Prometheus configuration updated.`));
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setErrorSnackMessage(err)
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const togglePrometheus = () => {
|
||||
const configInfo = {
|
||||
prometheusEnabled: prometheusEnabled ,
|
||||
toggle: true,
|
||||
};
|
||||
api
|
||||
.invoke(
|
||||
"PUT",
|
||||
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/monitoring`,
|
||||
configInfo
|
||||
)
|
||||
.then(() => {
|
||||
dispatch(setPrometheusEnabled(!prometheusEnabled));
|
||||
setRefreshMonitoringInfo(true);
|
||||
setToggleConfirmOpen(false);
|
||||
setRefreshMonitoringInfo(true);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
});
|
||||
const togglePrometheus = () => {
|
||||
const configInfo = {
|
||||
prometheusEnabled: prometheusEnabled,
|
||||
toggle: true,
|
||||
};
|
||||
api
|
||||
.invoke(
|
||||
"PUT",
|
||||
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/monitoring`,
|
||||
configInfo
|
||||
)
|
||||
.then(() => {
|
||||
dispatch(setPrometheusEnabled(!prometheusEnabled));
|
||||
setRefreshMonitoringInfo(true);
|
||||
setToggleConfirmOpen(false);
|
||||
setRefreshMonitoringInfo(true);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
||||
{toggleConfirmOpen && (
|
||||
<ConfirmDialog
|
||||
isOpen={toggleConfirmOpen}
|
||||
@@ -264,7 +298,7 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
||||
<Grid item xs>
|
||||
<h1 className={classes.sectionTitle}>Prometheus Monitoring </h1>
|
||||
</Grid>
|
||||
<Grid item xs={7} justifyContent={"end"} textAlign={"right"}>
|
||||
<Grid item xs={7} justifyContent={"end"} textAlign={"right"}>
|
||||
<FormSwitchWrapper
|
||||
label={""}
|
||||
indicatorLabels={["Enabled", "Disabled"]}
|
||||
@@ -281,176 +315,174 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
||||
<Grid xs={12}>
|
||||
<hr className={classes.hrClass} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{prometheusEnabled && (
|
||||
|
||||
<Fragment>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`image`}
|
||||
label={"Image"}
|
||||
placeholder={"quay.io/prometheus/prometheus:latest"}
|
||||
name={`image`}
|
||||
value={image}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
</Grid>
|
||||
|
||||
{prometheusEnabled && (
|
||||
<Fragment>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`image`}
|
||||
label={"Image"}
|
||||
placeholder={"quay.io/prometheus/prometheus:latest"}
|
||||
name={`image`}
|
||||
value={image}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setImage(event.target.value));
|
||||
}
|
||||
cleanValidation(`image`)
|
||||
}}
|
||||
key={`image`}
|
||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||
error={validationErrors[`image`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`sidecarImage`}
|
||||
label={"Sidecar Image"}
|
||||
placeholder={"library/alpine:latest"}
|
||||
name={`sidecarImage`}
|
||||
value={sidecarImage}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
cleanValidation(`image`);
|
||||
}}
|
||||
key={`image`}
|
||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||
error={validationErrors[`image`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`sidecarImage`}
|
||||
label={"Sidecar Image"}
|
||||
placeholder={"library/alpine:latest"}
|
||||
name={`sidecarImage`}
|
||||
value={sidecarImage}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setSidecarImage(event.target.value));
|
||||
}
|
||||
cleanValidation(`sidecarImage`)
|
||||
}}
|
||||
key={`sidecarImage`}
|
||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||
error={validationErrors[`sidecarImage`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`initImage`}
|
||||
label={"Init Image"}
|
||||
placeholder={"library/busybox:1.33.1"}
|
||||
name={`initImage`}
|
||||
value={initImage}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
}
|
||||
cleanValidation(`sidecarImage`);
|
||||
}}
|
||||
key={`sidecarImage`}
|
||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||
error={validationErrors[`sidecarImage`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`initImage`}
|
||||
label={"Init Image"}
|
||||
placeholder={"library/busybox:1.33.1"}
|
||||
name={`initImage`}
|
||||
value={initImage}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setInitImage(event.target.value));
|
||||
}
|
||||
cleanValidation(`initImage`)
|
||||
}}
|
||||
key={`initImage`}
|
||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||
error={validationErrors[`initImage`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`diskCapacityGB`}
|
||||
label={"Disk Capacity"}
|
||||
placeholder={"Disk Capacity"}
|
||||
name={`diskCapacityGB`}
|
||||
value={diskCapacityGB}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
cleanValidation(`initImage`);
|
||||
}}
|
||||
key={`initImage`}
|
||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||
error={validationErrors[`initImage`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`diskCapacityGB`}
|
||||
label={"Disk Capacity"}
|
||||
placeholder={"Disk Capacity"}
|
||||
name={`diskCapacityGB`}
|
||||
value={diskCapacityGB}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setDiskCapacityGB(event.target.value));
|
||||
}
|
||||
cleanValidation(`diskCapacityGB`)
|
||||
}}
|
||||
key={`diskCapacityGB`}
|
||||
pattern={"[0-9]*"}
|
||||
error={validationErrors[`diskCapacityGB`] || ""}
|
||||
overlayObject={
|
||||
<InputUnitMenu
|
||||
id={"size-unit"}
|
||||
onUnitChange={() => {}}
|
||||
unitSelected={"Gi"}
|
||||
unitsList={[{ label: "Gi", value: "Gi" }]}
|
||||
disabled={true}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`cpuRequest`}
|
||||
label={"CPU Request"}
|
||||
placeholder={"CPU Request"}
|
||||
name={`cpuRequest`}
|
||||
value={cpuRequest}
|
||||
pattern={"[0-9]*"}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
cleanValidation(`diskCapacityGB`);
|
||||
}}
|
||||
key={`diskCapacityGB`}
|
||||
pattern={"[0-9]*"}
|
||||
error={validationErrors[`diskCapacityGB`] || ""}
|
||||
overlayObject={
|
||||
<InputUnitMenu
|
||||
id={"size-unit"}
|
||||
onUnitChange={() => {}}
|
||||
unitSelected={"Gi"}
|
||||
unitsList={[{ label: "Gi", value: "Gi" }]}
|
||||
disabled={true}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`cpuRequest`}
|
||||
label={"CPU Request"}
|
||||
placeholder={"CPU Request"}
|
||||
name={`cpuRequest`}
|
||||
value={cpuRequest}
|
||||
pattern={"[0-9]*"}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setCPURequest(event.target.value));
|
||||
}
|
||||
cleanValidation(`cpuRequest`)
|
||||
}}
|
||||
key={`cpuRequest`}
|
||||
error={validationErrors[`cpuRequest`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`memRequest`}
|
||||
label={"Memory Request"}
|
||||
placeholder={"Memory request"}
|
||||
name={`memRequest`}
|
||||
value={memRequest}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setMemRequest(event.target.value));
|
||||
}
|
||||
cleanValidation(`memRequest`)
|
||||
}}
|
||||
pattern={"[0-9]*"}
|
||||
key={`memRequest`}
|
||||
error={validationErrors[`memRequest`] || ""}
|
||||
overlayObject={
|
||||
<InputUnitMenu
|
||||
id={"size-unit"}
|
||||
onUnitChange={() => {}}
|
||||
unitSelected={"Gi"}
|
||||
unitsList={[{ label: "Gi", value: "Gi" }]}
|
||||
disabled={true}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`serviceAccountName`}
|
||||
label={"Service Account"}
|
||||
placeholder={"Service Account Name"}
|
||||
name={`serviceAccountName`}
|
||||
value={serviceAccountName}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
cleanValidation(`cpuRequest`);
|
||||
}}
|
||||
key={`cpuRequest`}
|
||||
error={validationErrors[`cpuRequest`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`memRequest`}
|
||||
label={"Memory Request"}
|
||||
placeholder={"Memory request"}
|
||||
name={`memRequest`}
|
||||
value={memRequest}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setMemRequest(event.target.value));
|
||||
}
|
||||
cleanValidation(`memRequest`);
|
||||
}}
|
||||
pattern={"[0-9]*"}
|
||||
key={`memRequest`}
|
||||
error={validationErrors[`memRequest`] || ""}
|
||||
overlayObject={
|
||||
<InputUnitMenu
|
||||
id={"size-unit"}
|
||||
onUnitChange={() => {}}
|
||||
unitSelected={"Gi"}
|
||||
unitsList={[{ label: "Gi", value: "Gi" }]}
|
||||
disabled={true}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`serviceAccountName`}
|
||||
label={"Service Account"}
|
||||
placeholder={"Service Account Name"}
|
||||
name={`serviceAccountName`}
|
||||
value={serviceAccountName}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setServiceAccountName(event.target.value));
|
||||
}
|
||||
cleanValidation(`serviceAccountName`)
|
||||
}
|
||||
}
|
||||
key={`serviceAccountName`}
|
||||
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
|
||||
error={validationErrors[`serviceAccountName`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`storageClassName`}
|
||||
label={"Storage Class"}
|
||||
placeholder={"Storage Class Name"}
|
||||
name={`storageClassName`}
|
||||
value={storageClassName}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
}
|
||||
cleanValidation(`serviceAccountName`);
|
||||
}}
|
||||
key={`serviceAccountName`}
|
||||
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
|
||||
error={validationErrors[`serviceAccountName`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} paddingBottom={2}>
|
||||
<InputBoxWrapper
|
||||
id={`storageClassName`}
|
||||
label={"Storage Class"}
|
||||
placeholder={"Storage Class Name"}
|
||||
name={`storageClassName`}
|
||||
value={storageClassName}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.validity.valid) {
|
||||
dispatch(setStorageClassName(event.target.value));
|
||||
}
|
||||
cleanValidation(`storageClassName`)
|
||||
}}
|
||||
key={`storageClassName`}
|
||||
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
|
||||
error={validationErrors[`storageClassName`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
{labels !== null &&
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
}
|
||||
cleanValidation(`storageClassName`);
|
||||
}}
|
||||
key={`storageClassName`}
|
||||
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
|
||||
error={validationErrors[`storageClassName`] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
{labels !== null && (
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<span className={classes.inputLabel}>Labels</span>
|
||||
<KeyPairEdit
|
||||
newValues={labels}
|
||||
@@ -459,10 +491,11 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
||||
error={labelsError}
|
||||
setError={setLabelsError}
|
||||
/>
|
||||
</Grid>}
|
||||
|
||||
{annotations !== null &&
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{annotations !== null && (
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<span className={classes.inputLabel}>Annotations</span>
|
||||
<KeyPairEdit
|
||||
newValues={annotations}
|
||||
@@ -471,9 +504,9 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
||||
error={annotationsError}
|
||||
setError={setAnnotationsError}
|
||||
/>
|
||||
</Grid>
|
||||
}
|
||||
{nodeSelector !== null &&
|
||||
</Grid>
|
||||
)}
|
||||
{nodeSelector !== null && (
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<span className={classes.inputLabel}>Node Selector</span>
|
||||
<KeyPairEdit
|
||||
@@ -484,22 +517,20 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
||||
setError={setNodeSelectorError}
|
||||
/>
|
||||
</Grid>
|
||||
}
|
||||
<Grid item xs={12} textAlign={"right"}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={!checkValid()}
|
||||
onClick={() =>
|
||||
submitMonitoringInfo()
|
||||
}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
)}
|
||||
<Grid item xs={12} textAlign={"right"}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={!checkValid()}
|
||||
onClick={() => submitMonitoringInfo()}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -82,73 +82,72 @@ const KeyPairEdit = ({
|
||||
|
||||
let keyValueInputs = newValues.map((_, index) => {
|
||||
return (
|
||||
|
||||
<Fragment key={`keyvalue-${index.toString()}`} >
|
||||
<Grid paddingBottom={1}>
|
||||
<div className={classes.shortened} >
|
||||
<InputBoxWrapper
|
||||
id={`key-${index.toString()}`}
|
||||
label={""}
|
||||
placeholder={"Key"}
|
||||
name={`key-${index.toString()}`}
|
||||
value={newValues[index].key}
|
||||
onChange={(e) => {
|
||||
let tempLabels = [...newValues];
|
||||
tempLabels[index].key = e.target.value;
|
||||
setNewValues(tempLabels);
|
||||
cleanValidation(`key-${index.toString()}`);
|
||||
}}
|
||||
index={index}
|
||||
key={`csv-key-${index.toString()}`}
|
||||
error={error[`key-${index.toString()}`] || ""}
|
||||
/>
|
||||
<InputBoxWrapper
|
||||
id={`val-${index.toString()}`}
|
||||
label={""}
|
||||
placeholder={"Value"}
|
||||
name={`val-${index.toString()}`}
|
||||
value={newValues[index].value}
|
||||
onChange={(e) => {
|
||||
let tempLabels = [...newValues];
|
||||
tempLabels[index].value = e.target.value;
|
||||
setNewValues(tempLabels);
|
||||
cleanValidation(`val-${index.toString()}`);
|
||||
}}
|
||||
index={index}
|
||||
key={`csv-val-${index.toString()}`}
|
||||
error={error[`val-${index.toString()}`] || ""}
|
||||
/>
|
||||
<Tooltip title={`Add ${paramName}`} aria-label="addlabel">
|
||||
<IconButton
|
||||
size={"small"}
|
||||
onClick={() => {
|
||||
<Fragment key={`keyvalue-${index.toString()}`}>
|
||||
<Grid paddingBottom={1}>
|
||||
<div className={classes.shortened}>
|
||||
<InputBoxWrapper
|
||||
id={`key-${index.toString()}`}
|
||||
label={""}
|
||||
placeholder={"Key"}
|
||||
name={`key-${index.toString()}`}
|
||||
value={newValues[index].key}
|
||||
onChange={(e) => {
|
||||
let tempLabels = [...newValues];
|
||||
tempLabels.push({ key: "", value: "" });
|
||||
tempLabels[index].key = e.target.value;
|
||||
setNewValues(tempLabels);
|
||||
cleanValidation(`key-${index.toString()}`);
|
||||
}}
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Remove" aria-label="removeLabel">
|
||||
<IconButton
|
||||
size={"small"}
|
||||
style={{ marginLeft: 16 }}
|
||||
onClick={() => {
|
||||
if (newValues.length === 1) {
|
||||
setNewValues([{ key: "", value: "" }]);
|
||||
}
|
||||
if (newValues.length > 1) {
|
||||
index={index}
|
||||
key={`csv-key-${index.toString()}`}
|
||||
error={error[`key-${index.toString()}`] || ""}
|
||||
/>
|
||||
<InputBoxWrapper
|
||||
id={`val-${index.toString()}`}
|
||||
label={""}
|
||||
placeholder={"Value"}
|
||||
name={`val-${index.toString()}`}
|
||||
value={newValues[index].value}
|
||||
onChange={(e) => {
|
||||
let tempLabels = [...newValues];
|
||||
tempLabels[index].value = e.target.value;
|
||||
setNewValues(tempLabels);
|
||||
cleanValidation(`val-${index.toString()}`);
|
||||
}}
|
||||
index={index}
|
||||
key={`csv-val-${index.toString()}`}
|
||||
error={error[`val-${index.toString()}`] || ""}
|
||||
/>
|
||||
<Tooltip title={`Add ${paramName}`} aria-label="addlabel">
|
||||
<IconButton
|
||||
size={"small"}
|
||||
onClick={() => {
|
||||
let tempLabels = [...newValues];
|
||||
tempLabels.splice(index, 1);
|
||||
tempLabels.push({ key: "", value: "" });
|
||||
setNewValues(tempLabels);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
}}
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Remove" aria-label="removeLabel">
|
||||
<IconButton
|
||||
size={"small"}
|
||||
style={{ marginLeft: 16 }}
|
||||
onClick={() => {
|
||||
if (newValues.length === 1) {
|
||||
setNewValues([{ key: "", value: "" }]);
|
||||
}
|
||||
if (newValues.length > 1) {
|
||||
let tempLabels = [...newValues];
|
||||
tempLabels.splice(index, 1);
|
||||
setNewValues(tempLabels);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -391,7 +391,10 @@ const TenantDetails = ({ classes }: ITenantDetailsProps) => {
|
||||
<Route path={"pvcs/:PVCName"} element={<TenantVolumes />} />
|
||||
<Route path={"volumes"} element={<VolumesSummary />} />
|
||||
<Route path={"license"} element={<TenantLicense />} />
|
||||
<Route path={"monitoring"} element={<EditTenantMonitoringScreen />} />
|
||||
<Route
|
||||
path={"monitoring"}
|
||||
element={<EditTenantMonitoringScreen />}
|
||||
/>
|
||||
<Route path={"logging"} element={<TenantLogging />} />
|
||||
<Route path={"events"} element={<TenantEvents />} />
|
||||
<Route path={"csr"} element={<TenantCSR />} />
|
||||
|
||||
@@ -17,91 +17,91 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { IKeyValue } from "../ListTenants/types";
|
||||
|
||||
export interface IEditTenantMonitoring {
|
||||
prometheusEnabled: boolean;
|
||||
image: string;
|
||||
sidecarImage: string;
|
||||
initImage: string;
|
||||
storageClassName: string;
|
||||
labels: IKeyValue[];
|
||||
annotations: IKeyValue[];
|
||||
nodeSelector: IKeyValue[];
|
||||
diskCapacityGB: string;
|
||||
serviceAccountName: string;
|
||||
monitoringCPURequest: string;
|
||||
monitoringMemRequest: string;
|
||||
prometheusEnabled: boolean;
|
||||
image: string;
|
||||
sidecarImage: string;
|
||||
initImage: string;
|
||||
storageClassName: string;
|
||||
labels: IKeyValue[];
|
||||
annotations: IKeyValue[];
|
||||
nodeSelector: IKeyValue[];
|
||||
diskCapacityGB: string;
|
||||
serviceAccountName: string;
|
||||
monitoringCPURequest: string;
|
||||
monitoringMemRequest: string;
|
||||
}
|
||||
|
||||
const initialState: IEditTenantMonitoring = {
|
||||
prometheusEnabled: false,
|
||||
image: "",
|
||||
sidecarImage: "",
|
||||
initImage: "",
|
||||
storageClassName: "",
|
||||
labels: [{key:" ",value:" "}],
|
||||
annotations: [{key:" ",value:" "}],
|
||||
nodeSelector: [{key:" ",value:" "}],
|
||||
diskCapacityGB: "0",
|
||||
serviceAccountName: "",
|
||||
monitoringCPURequest: "",
|
||||
monitoringMemRequest: "",
|
||||
prometheusEnabled: false,
|
||||
image: "",
|
||||
sidecarImage: "",
|
||||
initImage: "",
|
||||
storageClassName: "",
|
||||
labels: [{ key: " ", value: " " }],
|
||||
annotations: [{ key: " ", value: " " }],
|
||||
nodeSelector: [{ key: " ", value: " " }],
|
||||
diskCapacityGB: "0",
|
||||
serviceAccountName: "",
|
||||
monitoringCPURequest: "",
|
||||
monitoringMemRequest: "",
|
||||
};
|
||||
|
||||
export const editTenantMonitoringSlice = createSlice({
|
||||
name: "editTenantMonitoring",
|
||||
initialState,
|
||||
reducers: {
|
||||
setPrometheusEnabled: (state, action: PayloadAction<boolean>) => {
|
||||
state.prometheusEnabled = action.payload;
|
||||
},
|
||||
setImage: (state, action: PayloadAction<string>) => {
|
||||
state.image = action.payload;
|
||||
},
|
||||
setSidecarImage:(state, action: PayloadAction<string>) => {
|
||||
state.sidecarImage = action.payload;
|
||||
},
|
||||
setInitImage: (state, action: PayloadAction<string>) => {
|
||||
state.initImage = action.payload;
|
||||
},
|
||||
setStorageClassName: (state, action: PayloadAction<string>) => {
|
||||
state.storageClassName = action.payload;
|
||||
},
|
||||
setLabels: (state, action: PayloadAction<IKeyValue[]>) => {
|
||||
state.labels = action.payload;
|
||||
},
|
||||
setAnnotations: (state, action: PayloadAction<IKeyValue[]>) => {
|
||||
state.annotations = action.payload;
|
||||
},
|
||||
setNodeSelector: (state, action: PayloadAction<IKeyValue[]>) => {
|
||||
state.nodeSelector = action.payload;
|
||||
},
|
||||
setDiskCapacityGB: (state, action: PayloadAction<string>) => {
|
||||
state.diskCapacityGB = action.payload;
|
||||
},
|
||||
setServiceAccountName: (state, action: PayloadAction<string>) => {
|
||||
state.serviceAccountName = action.payload;
|
||||
},
|
||||
setCPURequest: (state, action: PayloadAction<string>) => {
|
||||
state.monitoringCPURequest = action.payload;
|
||||
},
|
||||
setMemRequest: (state, action: PayloadAction<string>) => {
|
||||
state.monitoringMemRequest = action.payload;
|
||||
},
|
||||
name: "editTenantMonitoring",
|
||||
initialState,
|
||||
reducers: {
|
||||
setPrometheusEnabled: (state, action: PayloadAction<boolean>) => {
|
||||
state.prometheusEnabled = action.payload;
|
||||
},
|
||||
setImage: (state, action: PayloadAction<string>) => {
|
||||
state.image = action.payload;
|
||||
},
|
||||
setSidecarImage: (state, action: PayloadAction<string>) => {
|
||||
state.sidecarImage = action.payload;
|
||||
},
|
||||
setInitImage: (state, action: PayloadAction<string>) => {
|
||||
state.initImage = action.payload;
|
||||
},
|
||||
setStorageClassName: (state, action: PayloadAction<string>) => {
|
||||
state.storageClassName = action.payload;
|
||||
},
|
||||
setLabels: (state, action: PayloadAction<IKeyValue[]>) => {
|
||||
state.labels = action.payload;
|
||||
},
|
||||
setAnnotations: (state, action: PayloadAction<IKeyValue[]>) => {
|
||||
state.annotations = action.payload;
|
||||
},
|
||||
setNodeSelector: (state, action: PayloadAction<IKeyValue[]>) => {
|
||||
state.nodeSelector = action.payload;
|
||||
},
|
||||
setDiskCapacityGB: (state, action: PayloadAction<string>) => {
|
||||
state.diskCapacityGB = action.payload;
|
||||
},
|
||||
setServiceAccountName: (state, action: PayloadAction<string>) => {
|
||||
state.serviceAccountName = action.payload;
|
||||
},
|
||||
setCPURequest: (state, action: PayloadAction<string>) => {
|
||||
state.monitoringCPURequest = action.payload;
|
||||
},
|
||||
setMemRequest: (state, action: PayloadAction<string>) => {
|
||||
state.monitoringMemRequest = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
setPrometheusEnabled,
|
||||
setImage,
|
||||
setSidecarImage,
|
||||
setInitImage,
|
||||
setStorageClassName,
|
||||
setLabels,
|
||||
setAnnotations,
|
||||
setNodeSelector,
|
||||
setDiskCapacityGB,
|
||||
setServiceAccountName,
|
||||
setCPURequest,
|
||||
setMemRequest,
|
||||
setPrometheusEnabled,
|
||||
setImage,
|
||||
setSidecarImage,
|
||||
setInitImage,
|
||||
setStorageClassName,
|
||||
setLabels,
|
||||
setAnnotations,
|
||||
setNodeSelector,
|
||||
setDiskCapacityGB,
|
||||
setServiceAccountName,
|
||||
setCPURequest,
|
||||
setMemRequest,
|
||||
} = editTenantMonitoringSlice.actions;
|
||||
|
||||
export default editTenantMonitoringSlice.reducer;
|
||||
export default editTenantMonitoringSlice.reducer;
|
||||
|
||||
@@ -31,7 +31,7 @@ import createTenantReducer from "./screens/Console/Tenants/AddTenant/createTenan
|
||||
import createUserReducer from "./screens/Console/Users/AddUsersSlice";
|
||||
import addPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice";
|
||||
import editPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/EditPool/editPoolSlice";
|
||||
import editTenantMonitoringReducer from "./screens/Console/Tenants/TenantDetails/tenantMonitoringSlice"
|
||||
import editTenantMonitoringReducer from "./screens/Console/Tenants/TenantDetails/tenantMonitoringSlice";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
system: systemReducer,
|
||||
|
||||
@@ -1585,6 +1585,12 @@ func init() {
|
||||
"default": false,
|
||||
"name": "preview",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"name": "override_file_name",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -8672,6 +8678,12 @@ func init() {
|
||||
"default": false,
|
||||
"name": "preview",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"name": "override_file_name",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
@@ -40,10 +40,14 @@ func NewDownloadObjectParams() DownloadObjectParams {
|
||||
var (
|
||||
// initialize parameters with default values
|
||||
|
||||
overrideFileNameDefault = string("")
|
||||
|
||||
previewDefault = bool(false)
|
||||
)
|
||||
|
||||
return DownloadObjectParams{
|
||||
OverrideFileName: &overrideFileNameDefault,
|
||||
|
||||
Preview: &previewDefault,
|
||||
}
|
||||
}
|
||||
@@ -62,6 +66,11 @@ type DownloadObjectParams struct {
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
/*
|
||||
In: query
|
||||
Default: ""
|
||||
*/
|
||||
OverrideFileName *string
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
@@ -94,6 +103,11 @@ func (o *DownloadObjectParams) BindRequest(r *http.Request, route *middleware.Ma
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qOverrideFileName, qhkOverrideFileName, _ := qs.GetOK("override_file_name")
|
||||
if err := o.bindOverrideFileName(qOverrideFileName, qhkOverrideFileName, 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)
|
||||
@@ -128,6 +142,25 @@ func (o *DownloadObjectParams) bindBucketName(rawData []string, hasKey bool, for
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindOverrideFileName binds and validates parameter OverrideFileName from query.
|
||||
func (o *DownloadObjectParams) bindOverrideFileName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
|
||||
if raw == "" { // empty values pass all other validations
|
||||
// Default values have been previously initialized by NewDownloadObjectParams()
|
||||
return nil
|
||||
}
|
||||
o.OverrideFileName = &raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPrefix binds and validates parameter Prefix from query.
|
||||
func (o *DownloadObjectParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
|
||||
@@ -35,9 +35,10 @@ import (
|
||||
type DownloadObjectURL struct {
|
||||
BucketName string
|
||||
|
||||
Prefix string
|
||||
Preview *bool
|
||||
VersionID *string
|
||||
OverrideFileName *string
|
||||
Prefix string
|
||||
Preview *bool
|
||||
VersionID *string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
@@ -80,6 +81,14 @@ func (o *DownloadObjectURL) Build() (*url.URL, error) {
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
var overrideFileNameQ string
|
||||
if o.OverrideFileName != nil {
|
||||
overrideFileNameQ = *o.OverrideFileName
|
||||
}
|
||||
if overrideFileNameQ != "" {
|
||||
qs.Set("override_file_name", overrideFileNameQ)
|
||||
}
|
||||
|
||||
prefixQ := o.Prefix
|
||||
if prefixQ != "" {
|
||||
qs.Set("prefix", prefixQ)
|
||||
|
||||
@@ -392,16 +392,27 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
|
||||
defer resp.Close()
|
||||
|
||||
isPreview := params.Preview != nil && *params.Preview
|
||||
// override filename is set
|
||||
decodeOverride, err := base64.StdEncoding.DecodeString(*params.OverrideFileName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
overrideName := string(decodeOverride)
|
||||
|
||||
// indicate it's a download / inline content to the browser, and the size of the object
|
||||
var filename string
|
||||
prefixElements := strings.Split(prefix, "/")
|
||||
if len(prefixElements) > 0 {
|
||||
if len(prefixElements) > 0 && overrideName == "" {
|
||||
if prefixElements[len(prefixElements)-1] == "" {
|
||||
filename = prefixElements[len(prefixElements)-2]
|
||||
} else {
|
||||
filename = prefixElements[len(prefixElements)-1]
|
||||
}
|
||||
} else if overrideName != "" {
|
||||
filename = overrideName
|
||||
}
|
||||
|
||||
escapedName := url.PathEscape(filename)
|
||||
|
||||
// indicate object size & content type
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: MinIO Console Server
|
||||
@@ -432,6 +433,11 @@ paths:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
- name: override_file_name
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
|
||||
Reference in New Issue
Block a user