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";
|
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),
|
encodeURLString(object.name),
|
||||||
object.version_id,
|
object.version_id,
|
||||||
object.size,
|
object.size,
|
||||||
|
null,
|
||||||
(progress) => {
|
(progress) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateProgress({
|
updateProgress({
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { ErrorResponseHandler } from "../../../../../../common/types";
|
|||||||
import {
|
import {
|
||||||
decodeURLString,
|
decodeURLString,
|
||||||
encodeURLString,
|
encodeURLString,
|
||||||
|
getClientOS,
|
||||||
niceBytes,
|
niceBytes,
|
||||||
niceBytesInt,
|
niceBytesInt,
|
||||||
niceDaysInt,
|
niceDaysInt,
|
||||||
@@ -87,6 +88,7 @@ import {
|
|||||||
setVersionsModeEnabled,
|
setVersionsModeEnabled,
|
||||||
updateProgress,
|
updateProgress,
|
||||||
} from "../../../../ObjectBrowser/objectBrowserSlice";
|
} from "../../../../ObjectBrowser/objectBrowserSlice";
|
||||||
|
import RenameLongFileName from "../../../../ObjectBrowser/RenameLongFilename";
|
||||||
|
|
||||||
const styles = () =>
|
const styles = () =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -155,7 +157,6 @@ const ObjectDetailPanel = ({
|
|||||||
bucketName,
|
bucketName,
|
||||||
versioning,
|
versioning,
|
||||||
locking,
|
locking,
|
||||||
|
|
||||||
onClosePanel,
|
onClosePanel,
|
||||||
}: IObjectDetailPanelProps) => {
|
}: IObjectDetailPanelProps) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -183,6 +184,7 @@ const ObjectDetailPanel = ({
|
|||||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||||
const [previewOpen, setPreviewOpen] = useState<boolean>(false);
|
const [previewOpen, setPreviewOpen] = useState<boolean>(false);
|
||||||
const [totalVersionsSize, setTotalVersionsSize] = useState<number>(0);
|
const [totalVersionsSize, setTotalVersionsSize] = useState<number>(0);
|
||||||
|
const [longFileOpen, setLongFileOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const internalPathsDecoded = decodeURLString(internalPaths) || "";
|
const internalPathsDecoded = decodeURLString(internalPaths) || "";
|
||||||
const allPathData = internalPathsDecoded.split("/");
|
const allPathData = internalPathsDecoded.split("/");
|
||||||
@@ -282,16 +284,29 @@ const ObjectDetailPanel = ({
|
|||||||
setShareFileModalOpen(false);
|
setShareFileModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeFileOpen = () => {
|
||||||
|
setLongFileOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
const downloadObject = (object: IFileInfo) => {
|
const downloadObject = (object: IFileInfo) => {
|
||||||
const identityDownload = encodeURLString(
|
const identityDownload = encodeURLString(
|
||||||
`${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}`
|
`${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
object.name.length > 200 &&
|
||||||
|
getClientOS().toLowerCase().includes("win")
|
||||||
|
) {
|
||||||
|
setLongFileOpen(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const downloadCall = download(
|
const downloadCall = download(
|
||||||
bucketName,
|
bucketName,
|
||||||
internalPaths,
|
internalPaths,
|
||||||
object.version_id,
|
object.version_id,
|
||||||
parseInt(object.size || "0"),
|
parseInt(object.size || "0"),
|
||||||
|
null,
|
||||||
(progress) => {
|
(progress) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateProgress({
|
updateProgress({
|
||||||
@@ -577,6 +592,16 @@ const ObjectDetailPanel = ({
|
|||||||
closeInspectModalAndRefresh={closeInspectModal}
|
closeInspectModalAndRefresh={closeInspectModal}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{longFileOpen && actualInfo && (
|
||||||
|
<RenameLongFileName
|
||||||
|
open={longFileOpen}
|
||||||
|
closeModal={closeFileOpen}
|
||||||
|
currentItem={currentItem}
|
||||||
|
bucketName={bucketName}
|
||||||
|
internalPaths={internalPaths}
|
||||||
|
actualInfo={actualInfo}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{loadingObjectInfo ? (
|
{loadingObjectInfo ? (
|
||||||
<Fragment>{loaderForContainer}</Fragment>
|
<Fragment>{loaderForContainer}</Fragment>
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ const VersionsNavigator = ({
|
|||||||
internalPaths,
|
internalPaths,
|
||||||
object.version_id,
|
object.version_id,
|
||||||
parseInt(object.size || "0"),
|
parseInt(object.size || "0"),
|
||||||
|
null,
|
||||||
(progress) => {
|
(progress) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateProgress({
|
updateProgress({
|
||||||
|
|||||||
@@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
import { BucketObjectItem } from "./ListObjects/types";
|
import { BucketObjectItem } from "./ListObjects/types";
|
||||||
import { IAllowResources } from "../../../types";
|
import { IAllowResources } from "../../../types";
|
||||||
|
import { encodeURLString } from "../../../../../common/utils";
|
||||||
|
|
||||||
export const download = (
|
export const download = (
|
||||||
bucketName: string,
|
bucketName: string,
|
||||||
objectPath: string,
|
objectPath: string,
|
||||||
versionID: any,
|
versionID: any,
|
||||||
fileSize: number,
|
fileSize: number,
|
||||||
|
overrideFileName: string | null = null,
|
||||||
progressCallback: (progress: number) => void,
|
progressCallback: (progress: number) => void,
|
||||||
completeCallback: () => void,
|
completeCallback: () => void,
|
||||||
errorCallback: () => void,
|
errorCallback: () => void,
|
||||||
@@ -29,7 +31,11 @@ export const download = (
|
|||||||
) => {
|
) => {
|
||||||
const anchor = document.createElement("a");
|
const anchor = document.createElement("a");
|
||||||
document.body.appendChild(anchor);
|
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) {
|
if (versionID) {
|
||||||
path = path.concat(`&version_id=${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 InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import { Button, DialogContentText } from "@mui/material";
|
import { Button, DialogContentText } from "@mui/material";
|
||||||
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
|
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
|
||||||
import { setErrorSnackMessage, setSnackBarMessage } from "../../../../systemSlice";
|
import {
|
||||||
|
setErrorSnackMessage,
|
||||||
|
setSnackBarMessage,
|
||||||
|
} from "../../../../systemSlice";
|
||||||
import { IKeyValue } from "../ListTenants/types";
|
import { IKeyValue } from "../ListTenants/types";
|
||||||
import KeyPairEdit from "./KeyPairEdit";
|
import KeyPairEdit from "./KeyPairEdit";
|
||||||
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
|
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
|
||||||
import { ITenantMonitoringStruct } from "../ListTenants/types";
|
import { ITenantMonitoringStruct } from "../ListTenants/types";
|
||||||
import {setPrometheusEnabled,
|
import {
|
||||||
setImage,
|
setPrometheusEnabled,
|
||||||
setSidecarImage,
|
setImage,
|
||||||
setInitImage,
|
setSidecarImage,
|
||||||
setStorageClassName,
|
setInitImage,
|
||||||
setDiskCapacityGB,
|
setStorageClassName,
|
||||||
setServiceAccountName,
|
setDiskCapacityGB,
|
||||||
setCPURequest,
|
setServiceAccountName,
|
||||||
setMemRequest,
|
setCPURequest,
|
||||||
} from "../TenantDetails/tenantMonitoringSlice"
|
setMemRequest,
|
||||||
|
} from "../TenantDetails/tenantMonitoringSlice";
|
||||||
|
|
||||||
import { clearValidationError } from "../utils";
|
import { clearValidationError } from "../utils";
|
||||||
|
|
||||||
interface ITenantMonitoring {
|
interface ITenantMonitoring {
|
||||||
classes: any;
|
classes: any;
|
||||||
@@ -87,105 +91,136 @@ const styles = (theme: Theme) =>
|
|||||||
const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { tenantName, tenantNamespace } = useParams();
|
const { tenantName, tenantNamespace } = useParams();
|
||||||
const prometheusEnabled = useSelector((state: AppState) => state.editTenantMonitoring.prometheusEnabled)
|
const prometheusEnabled = useSelector(
|
||||||
const image = useSelector((state: AppState) => state.editTenantMonitoring.image)
|
(state: AppState) => state.editTenantMonitoring.prometheusEnabled
|
||||||
const sidecarImage = useSelector((state: AppState) => state.editTenantMonitoring.sidecarImage)
|
);
|
||||||
const initImage = useSelector((state: AppState) => state.editTenantMonitoring.initImage)
|
const image = useSelector(
|
||||||
const diskCapacityGB = useSelector((state: AppState) => state.editTenantMonitoring.diskCapacityGB)
|
(state: AppState) => state.editTenantMonitoring.image
|
||||||
const cpuRequest = useSelector((state: AppState) => state.editTenantMonitoring.monitoringCPURequest)
|
);
|
||||||
const memRequest = useSelector((state: AppState) => state.editTenantMonitoring.monitoringMemRequest)
|
const sidecarImage = useSelector(
|
||||||
const serviceAccountName = useSelector((state: AppState) => state.editTenantMonitoring.serviceAccountName)
|
(state: AppState) => state.editTenantMonitoring.sidecarImage
|
||||||
const storageClassName = useSelector((state: AppState) => state.editTenantMonitoring.storageClassName)
|
);
|
||||||
|
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 [validationErrors, setValidationErrors] = useState<any>({});
|
||||||
const [toggleConfirmOpen, setToggleConfirmOpen] = useState<boolean>(false);
|
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);
|
useState<boolean>(true);
|
||||||
const [labelsError, setLabelsError] = useState<any>({});
|
const [labelsError, setLabelsError] = useState<any>({});
|
||||||
const [annotationsError, setAnnotationsError] = useState<any>({});
|
const [annotationsError, setAnnotationsError] = useState<any>({});
|
||||||
const [nodeSelectorError, setNodeSelectorError] = useState<any>({});
|
const [nodeSelectorError, setNodeSelectorError] = useState<any>({});
|
||||||
|
|
||||||
const cleanValidation = (fieldName: string) => {
|
const cleanValidation = (fieldName: string) => {
|
||||||
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
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 trim = (x: IKeyValue[]): IKeyValue[] => {
|
const setMonitoringInfo = (res: ITenantMonitoringStruct) => {
|
||||||
let retval: IKeyValue[] = [];
|
dispatch(setImage(res.image));
|
||||||
for (let i = 0; i < x.length; i++) {
|
dispatch(setSidecarImage(res.sidecarImage));
|
||||||
if (x[i].key !== "") {
|
dispatch(setInitImage(res.initImage));
|
||||||
retval.push(x[i]);
|
dispatch(setStorageClassName(res.storageClassName));
|
||||||
}
|
dispatch(setDiskCapacityGB(res.diskCapacityGB));
|
||||||
}
|
dispatch(setServiceAccountName(res.serviceAccountName));
|
||||||
return retval;
|
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 => {
|
const trim = (x: IKeyValue[]): IKeyValue[] => {
|
||||||
if (
|
let retval: IKeyValue[] = [];
|
||||||
Object.keys(validationErrors).length !== 0 ||
|
for (let i = 0; i < x.length; i++) {
|
||||||
Object.keys(labelsError).length !== 0 ||
|
if (x[i].key !== "") {
|
||||||
Object.keys(annotationsError).length !== 0 ||
|
retval.push(x[i]);
|
||||||
Object.keys(nodeSelectorError).length !== 0
|
|
||||||
) {
|
|
||||||
let err: ErrorResponseHandler = {
|
|
||||||
errorMessage: "Invalid entry",
|
|
||||||
detailedError: "",
|
|
||||||
};
|
|
||||||
dispatch(setErrorSnackMessage(err));
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
return retval;
|
||||||
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 checkValid = (): boolean => {
|
||||||
const submitMonitoringInfo = () => {
|
if (
|
||||||
if(checkValid()){
|
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
|
api
|
||||||
.invoke(
|
.invoke(
|
||||||
"PUT",
|
"PUT",
|
||||||
@@ -209,36 +244,35 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
|||||||
dispatch(setSnackBarMessage(`Prometheus configuration updated.`));
|
dispatch(setSnackBarMessage(`Prometheus configuration updated.`));
|
||||||
})
|
})
|
||||||
.catch((err: ErrorResponseHandler) => {
|
.catch((err: ErrorResponseHandler) => {
|
||||||
setErrorSnackMessage(err)
|
setErrorSnackMessage(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const togglePrometheus = () => {
|
const togglePrometheus = () => {
|
||||||
const configInfo = {
|
const configInfo = {
|
||||||
prometheusEnabled: prometheusEnabled ,
|
prometheusEnabled: prometheusEnabled,
|
||||||
toggle: true,
|
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));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
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 (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
|
||||||
{toggleConfirmOpen && (
|
{toggleConfirmOpen && (
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={toggleConfirmOpen}
|
isOpen={toggleConfirmOpen}
|
||||||
@@ -264,7 +298,7 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
|||||||
<Grid item xs>
|
<Grid item xs>
|
||||||
<h1 className={classes.sectionTitle}>Prometheus Monitoring </h1>
|
<h1 className={classes.sectionTitle}>Prometheus Monitoring </h1>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={7} justifyContent={"end"} textAlign={"right"}>
|
<Grid item xs={7} justifyContent={"end"} textAlign={"right"}>
|
||||||
<FormSwitchWrapper
|
<FormSwitchWrapper
|
||||||
label={""}
|
label={""}
|
||||||
indicatorLabels={["Enabled", "Disabled"]}
|
indicatorLabels={["Enabled", "Disabled"]}
|
||||||
@@ -281,176 +315,174 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
|||||||
<Grid xs={12}>
|
<Grid xs={12}>
|
||||||
<hr className={classes.hrClass} />
|
<hr className={classes.hrClass} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{prometheusEnabled && (
|
{prometheusEnabled && (
|
||||||
|
<Fragment>
|
||||||
<Fragment>
|
<Grid item xs={12} paddingBottom={2}>
|
||||||
<Grid item xs={12} paddingBottom={2}>
|
<InputBoxWrapper
|
||||||
<InputBoxWrapper
|
id={`image`}
|
||||||
id={`image`}
|
label={"Image"}
|
||||||
label={"Image"}
|
placeholder={"quay.io/prometheus/prometheus:latest"}
|
||||||
placeholder={"quay.io/prometheus/prometheus:latest"}
|
name={`image`}
|
||||||
name={`image`}
|
value={image}
|
||||||
value={image}
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
if (event.target.validity.valid) {
|
if (event.target.validity.valid) {
|
||||||
dispatch(setImage(event.target.value));
|
dispatch(setImage(event.target.value));
|
||||||
}
|
}
|
||||||
cleanValidation(`image`)
|
cleanValidation(`image`);
|
||||||
}}
|
}}
|
||||||
key={`image`}
|
key={`image`}
|
||||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||||
error={validationErrors[`image`] || ""}
|
error={validationErrors[`image`] || ""}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} paddingBottom={2}>
|
<Grid item xs={12} paddingBottom={2}>
|
||||||
<InputBoxWrapper
|
<InputBoxWrapper
|
||||||
id={`sidecarImage`}
|
id={`sidecarImage`}
|
||||||
label={"Sidecar Image"}
|
label={"Sidecar Image"}
|
||||||
placeholder={"library/alpine:latest"}
|
placeholder={"library/alpine:latest"}
|
||||||
name={`sidecarImage`}
|
name={`sidecarImage`}
|
||||||
value={sidecarImage}
|
value={sidecarImage}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (event.target.validity.valid) {
|
if (event.target.validity.valid) {
|
||||||
dispatch(setSidecarImage(event.target.value));
|
dispatch(setSidecarImage(event.target.value));
|
||||||
}
|
}
|
||||||
cleanValidation(`sidecarImage`)
|
cleanValidation(`sidecarImage`);
|
||||||
}}
|
}}
|
||||||
key={`sidecarImage`}
|
key={`sidecarImage`}
|
||||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||||
error={validationErrors[`sidecarImage`] || ""}
|
error={validationErrors[`sidecarImage`] || ""}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} paddingBottom={2}>
|
<Grid item xs={12} paddingBottom={2}>
|
||||||
<InputBoxWrapper
|
<InputBoxWrapper
|
||||||
id={`initImage`}
|
id={`initImage`}
|
||||||
label={"Init Image"}
|
label={"Init Image"}
|
||||||
placeholder={"library/busybox:1.33.1"}
|
placeholder={"library/busybox:1.33.1"}
|
||||||
name={`initImage`}
|
name={`initImage`}
|
||||||
value={initImage}
|
value={initImage}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (event.target.validity.valid) {
|
if (event.target.validity.valid) {
|
||||||
dispatch(setInitImage(event.target.value));
|
dispatch(setInitImage(event.target.value));
|
||||||
}
|
}
|
||||||
cleanValidation(`initImage`)
|
cleanValidation(`initImage`);
|
||||||
}}
|
}}
|
||||||
key={`initImage`}
|
key={`initImage`}
|
||||||
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
|
||||||
error={validationErrors[`initImage`] || ""}
|
error={validationErrors[`initImage`] || ""}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} paddingBottom={2}>
|
<Grid item xs={12} paddingBottom={2}>
|
||||||
<InputBoxWrapper
|
<InputBoxWrapper
|
||||||
id={`diskCapacityGB`}
|
id={`diskCapacityGB`}
|
||||||
label={"Disk Capacity"}
|
label={"Disk Capacity"}
|
||||||
placeholder={"Disk Capacity"}
|
placeholder={"Disk Capacity"}
|
||||||
name={`diskCapacityGB`}
|
name={`diskCapacityGB`}
|
||||||
value={diskCapacityGB}
|
value={diskCapacityGB}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (event.target.validity.valid) {
|
if (event.target.validity.valid) {
|
||||||
dispatch(setDiskCapacityGB(event.target.value));
|
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}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
/>
|
cleanValidation(`diskCapacityGB`);
|
||||||
</Grid>
|
}}
|
||||||
<Grid item xs={12} paddingBottom={2}>
|
key={`diskCapacityGB`}
|
||||||
<InputBoxWrapper
|
pattern={"[0-9]*"}
|
||||||
id={`cpuRequest`}
|
error={validationErrors[`diskCapacityGB`] || ""}
|
||||||
label={"CPU Request"}
|
overlayObject={
|
||||||
placeholder={"CPU Request"}
|
<InputUnitMenu
|
||||||
name={`cpuRequest`}
|
id={"size-unit"}
|
||||||
value={cpuRequest}
|
onUnitChange={() => {}}
|
||||||
pattern={"[0-9]*"}
|
unitSelected={"Gi"}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
unitsList={[{ label: "Gi", value: "Gi" }]}
|
||||||
if (event.target.validity.valid) {
|
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));
|
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}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
/>
|
cleanValidation(`cpuRequest`);
|
||||||
</Grid>
|
}}
|
||||||
<Grid item xs={12} paddingBottom={2}>
|
key={`cpuRequest`}
|
||||||
<InputBoxWrapper
|
error={validationErrors[`cpuRequest`] || ""}
|
||||||
id={`serviceAccountName`}
|
/>
|
||||||
label={"Service Account"}
|
</Grid>
|
||||||
placeholder={"Service Account Name"}
|
<Grid item xs={12} paddingBottom={2}>
|
||||||
name={`serviceAccountName`}
|
<InputBoxWrapper
|
||||||
value={serviceAccountName}
|
id={`memRequest`}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
label={"Memory Request"}
|
||||||
if (event.target.validity.valid) {
|
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));
|
dispatch(setServiceAccountName(event.target.value));
|
||||||
}
|
}
|
||||||
cleanValidation(`serviceAccountName`)
|
cleanValidation(`serviceAccountName`);
|
||||||
}
|
}}
|
||||||
}
|
key={`serviceAccountName`}
|
||||||
key={`serviceAccountName`}
|
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
|
||||||
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
|
error={validationErrors[`serviceAccountName`] || ""}
|
||||||
error={validationErrors[`serviceAccountName`] || ""}
|
/>
|
||||||
/>
|
</Grid>
|
||||||
</Grid>
|
<Grid item xs={12} paddingBottom={2}>
|
||||||
<Grid item xs={12} paddingBottom={2}>
|
<InputBoxWrapper
|
||||||
<InputBoxWrapper
|
id={`storageClassName`}
|
||||||
id={`storageClassName`}
|
label={"Storage Class"}
|
||||||
label={"Storage Class"}
|
placeholder={"Storage Class Name"}
|
||||||
placeholder={"Storage Class Name"}
|
name={`storageClassName`}
|
||||||
name={`storageClassName`}
|
value={storageClassName}
|
||||||
value={storageClassName}
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
if (event.target.validity.valid) {
|
||||||
if (event.target.validity.valid) {
|
|
||||||
dispatch(setStorageClassName(event.target.value));
|
dispatch(setStorageClassName(event.target.value));
|
||||||
}
|
}
|
||||||
cleanValidation(`storageClassName`)
|
cleanValidation(`storageClassName`);
|
||||||
}}
|
}}
|
||||||
key={`storageClassName`}
|
key={`storageClassName`}
|
||||||
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
|
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
|
||||||
error={validationErrors[`storageClassName`] || ""}
|
error={validationErrors[`storageClassName`] || ""}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
{labels !== null &&
|
{labels !== null && (
|
||||||
<Grid item xs={12} className={classes.formFieldRow}>
|
<Grid item xs={12} className={classes.formFieldRow}>
|
||||||
<span className={classes.inputLabel}>Labels</span>
|
<span className={classes.inputLabel}>Labels</span>
|
||||||
<KeyPairEdit
|
<KeyPairEdit
|
||||||
newValues={labels}
|
newValues={labels}
|
||||||
@@ -459,10 +491,11 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
|||||||
error={labelsError}
|
error={labelsError}
|
||||||
setError={setLabelsError}
|
setError={setLabelsError}
|
||||||
/>
|
/>
|
||||||
</Grid>}
|
</Grid>
|
||||||
|
)}
|
||||||
{annotations !== null &&
|
|
||||||
<Grid item xs={12} className={classes.formFieldRow}>
|
{annotations !== null && (
|
||||||
|
<Grid item xs={12} className={classes.formFieldRow}>
|
||||||
<span className={classes.inputLabel}>Annotations</span>
|
<span className={classes.inputLabel}>Annotations</span>
|
||||||
<KeyPairEdit
|
<KeyPairEdit
|
||||||
newValues={annotations}
|
newValues={annotations}
|
||||||
@@ -471,9 +504,9 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
|||||||
error={annotationsError}
|
error={annotationsError}
|
||||||
setError={setAnnotationsError}
|
setError={setAnnotationsError}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
}
|
)}
|
||||||
{nodeSelector !== null &&
|
{nodeSelector !== null && (
|
||||||
<Grid item xs={12} className={classes.formFieldRow}>
|
<Grid item xs={12} className={classes.formFieldRow}>
|
||||||
<span className={classes.inputLabel}>Node Selector</span>
|
<span className={classes.inputLabel}>Node Selector</span>
|
||||||
<KeyPairEdit
|
<KeyPairEdit
|
||||||
@@ -484,22 +517,20 @@ const TenantMonitoring = ({ classes }: ITenantMonitoring) => {
|
|||||||
setError={setNodeSelectorError}
|
setError={setNodeSelectorError}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
}
|
)}
|
||||||
<Grid item xs={12} textAlign={"right"}>
|
<Grid item xs={12} textAlign={"right"}>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
disabled={!checkValid()}
|
disabled={!checkValid()}
|
||||||
onClick={() =>
|
onClick={() => submitMonitoringInfo()}
|
||||||
submitMonitoringInfo()
|
>
|
||||||
}
|
Save
|
||||||
>
|
</Button>
|
||||||
Save
|
</Grid>
|
||||||
</Button>
|
</Fragment>
|
||||||
</Grid>
|
)}
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,73 +82,72 @@ const KeyPairEdit = ({
|
|||||||
|
|
||||||
let keyValueInputs = newValues.map((_, index) => {
|
let keyValueInputs = newValues.map((_, index) => {
|
||||||
return (
|
return (
|
||||||
|
<Fragment key={`keyvalue-${index.toString()}`}>
|
||||||
<Fragment key={`keyvalue-${index.toString()}`} >
|
<Grid paddingBottom={1}>
|
||||||
<Grid paddingBottom={1}>
|
<div className={classes.shortened}>
|
||||||
<div className={classes.shortened} >
|
<InputBoxWrapper
|
||||||
<InputBoxWrapper
|
id={`key-${index.toString()}`}
|
||||||
id={`key-${index.toString()}`}
|
label={""}
|
||||||
label={""}
|
placeholder={"Key"}
|
||||||
placeholder={"Key"}
|
name={`key-${index.toString()}`}
|
||||||
name={`key-${index.toString()}`}
|
value={newValues[index].key}
|
||||||
value={newValues[index].key}
|
onChange={(e) => {
|
||||||
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={() => {
|
|
||||||
let tempLabels = [...newValues];
|
let tempLabels = [...newValues];
|
||||||
tempLabels.push({ key: "", value: "" });
|
tempLabels[index].key = e.target.value;
|
||||||
setNewValues(tempLabels);
|
setNewValues(tempLabels);
|
||||||
|
cleanValidation(`key-${index.toString()}`);
|
||||||
}}
|
}}
|
||||||
>
|
index={index}
|
||||||
<AddIcon />
|
key={`csv-key-${index.toString()}`}
|
||||||
</IconButton>
|
error={error[`key-${index.toString()}`] || ""}
|
||||||
</Tooltip>
|
/>
|
||||||
<Tooltip title="Remove" aria-label="removeLabel">
|
<InputBoxWrapper
|
||||||
<IconButton
|
id={`val-${index.toString()}`}
|
||||||
size={"small"}
|
label={""}
|
||||||
style={{ marginLeft: 16 }}
|
placeholder={"Value"}
|
||||||
onClick={() => {
|
name={`val-${index.toString()}`}
|
||||||
if (newValues.length === 1) {
|
value={newValues[index].value}
|
||||||
setNewValues([{ key: "", value: "" }]);
|
onChange={(e) => {
|
||||||
}
|
let tempLabels = [...newValues];
|
||||||
if (newValues.length > 1) {
|
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];
|
let tempLabels = [...newValues];
|
||||||
tempLabels.splice(index, 1);
|
tempLabels.push({ key: "", value: "" });
|
||||||
setNewValues(tempLabels);
|
setNewValues(tempLabels);
|
||||||
}
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<AddIcon />
|
||||||
<DeleteIcon />
|
</IconButton>
|
||||||
</IconButton>
|
</Tooltip>
|
||||||
</Tooltip>
|
<Tooltip title="Remove" aria-label="removeLabel">
|
||||||
</div>
|
<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>
|
</Grid>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -391,7 +391,10 @@ const TenantDetails = ({ classes }: ITenantDetailsProps) => {
|
|||||||
<Route path={"pvcs/:PVCName"} element={<TenantVolumes />} />
|
<Route path={"pvcs/:PVCName"} element={<TenantVolumes />} />
|
||||||
<Route path={"volumes"} element={<VolumesSummary />} />
|
<Route path={"volumes"} element={<VolumesSummary />} />
|
||||||
<Route path={"license"} element={<TenantLicense />} />
|
<Route path={"license"} element={<TenantLicense />} />
|
||||||
<Route path={"monitoring"} element={<EditTenantMonitoringScreen />} />
|
<Route
|
||||||
|
path={"monitoring"}
|
||||||
|
element={<EditTenantMonitoringScreen />}
|
||||||
|
/>
|
||||||
<Route path={"logging"} element={<TenantLogging />} />
|
<Route path={"logging"} element={<TenantLogging />} />
|
||||||
<Route path={"events"} element={<TenantEvents />} />
|
<Route path={"events"} element={<TenantEvents />} />
|
||||||
<Route path={"csr"} element={<TenantCSR />} />
|
<Route path={"csr"} element={<TenantCSR />} />
|
||||||
|
|||||||
@@ -17,91 +17,91 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|||||||
import { IKeyValue } from "../ListTenants/types";
|
import { IKeyValue } from "../ListTenants/types";
|
||||||
|
|
||||||
export interface IEditTenantMonitoring {
|
export interface IEditTenantMonitoring {
|
||||||
prometheusEnabled: boolean;
|
prometheusEnabled: boolean;
|
||||||
image: string;
|
image: string;
|
||||||
sidecarImage: string;
|
sidecarImage: string;
|
||||||
initImage: string;
|
initImage: string;
|
||||||
storageClassName: string;
|
storageClassName: string;
|
||||||
labels: IKeyValue[];
|
labels: IKeyValue[];
|
||||||
annotations: IKeyValue[];
|
annotations: IKeyValue[];
|
||||||
nodeSelector: IKeyValue[];
|
nodeSelector: IKeyValue[];
|
||||||
diskCapacityGB: string;
|
diskCapacityGB: string;
|
||||||
serviceAccountName: string;
|
serviceAccountName: string;
|
||||||
monitoringCPURequest: string;
|
monitoringCPURequest: string;
|
||||||
monitoringMemRequest: string;
|
monitoringMemRequest: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: IEditTenantMonitoring = {
|
const initialState: IEditTenantMonitoring = {
|
||||||
prometheusEnabled: false,
|
prometheusEnabled: false,
|
||||||
image: "",
|
image: "",
|
||||||
sidecarImage: "",
|
sidecarImage: "",
|
||||||
initImage: "",
|
initImage: "",
|
||||||
storageClassName: "",
|
storageClassName: "",
|
||||||
labels: [{key:" ",value:" "}],
|
labels: [{ key: " ", value: " " }],
|
||||||
annotations: [{key:" ",value:" "}],
|
annotations: [{ key: " ", value: " " }],
|
||||||
nodeSelector: [{key:" ",value:" "}],
|
nodeSelector: [{ key: " ", value: " " }],
|
||||||
diskCapacityGB: "0",
|
diskCapacityGB: "0",
|
||||||
serviceAccountName: "",
|
serviceAccountName: "",
|
||||||
monitoringCPURequest: "",
|
monitoringCPURequest: "",
|
||||||
monitoringMemRequest: "",
|
monitoringMemRequest: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const editTenantMonitoringSlice = createSlice({
|
export const editTenantMonitoringSlice = createSlice({
|
||||||
name: "editTenantMonitoring",
|
name: "editTenantMonitoring",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setPrometheusEnabled: (state, action: PayloadAction<boolean>) => {
|
setPrometheusEnabled: (state, action: PayloadAction<boolean>) => {
|
||||||
state.prometheusEnabled = action.payload;
|
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;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
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 {
|
export const {
|
||||||
setPrometheusEnabled,
|
setPrometheusEnabled,
|
||||||
setImage,
|
setImage,
|
||||||
setSidecarImage,
|
setSidecarImage,
|
||||||
setInitImage,
|
setInitImage,
|
||||||
setStorageClassName,
|
setStorageClassName,
|
||||||
setLabels,
|
setLabels,
|
||||||
setAnnotations,
|
setAnnotations,
|
||||||
setNodeSelector,
|
setNodeSelector,
|
||||||
setDiskCapacityGB,
|
setDiskCapacityGB,
|
||||||
setServiceAccountName,
|
setServiceAccountName,
|
||||||
setCPURequest,
|
setCPURequest,
|
||||||
setMemRequest,
|
setMemRequest,
|
||||||
} = editTenantMonitoringSlice.actions;
|
} = 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 createUserReducer from "./screens/Console/Users/AddUsersSlice";
|
||||||
import addPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice";
|
import addPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice";
|
||||||
import editPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/EditPool/editPoolSlice";
|
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({
|
const rootReducer = combineReducers({
|
||||||
system: systemReducer,
|
system: systemReducer,
|
||||||
|
|||||||
@@ -1585,6 +1585,12 @@ func init() {
|
|||||||
"default": false,
|
"default": false,
|
||||||
"name": "preview",
|
"name": "preview",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"name": "override_file_name",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -8672,6 +8678,12 @@ func init() {
|
|||||||
"default": false,
|
"default": false,
|
||||||
"name": "preview",
|
"name": "preview",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"name": "override_file_name",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@@ -40,10 +40,14 @@ func NewDownloadObjectParams() DownloadObjectParams {
|
|||||||
var (
|
var (
|
||||||
// initialize parameters with default values
|
// initialize parameters with default values
|
||||||
|
|
||||||
|
overrideFileNameDefault = string("")
|
||||||
|
|
||||||
previewDefault = bool(false)
|
previewDefault = bool(false)
|
||||||
)
|
)
|
||||||
|
|
||||||
return DownloadObjectParams{
|
return DownloadObjectParams{
|
||||||
|
OverrideFileName: &overrideFileNameDefault,
|
||||||
|
|
||||||
Preview: &previewDefault,
|
Preview: &previewDefault,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,6 +66,11 @@ type DownloadObjectParams struct {
|
|||||||
In: path
|
In: path
|
||||||
*/
|
*/
|
||||||
BucketName string
|
BucketName string
|
||||||
|
/*
|
||||||
|
In: query
|
||||||
|
Default: ""
|
||||||
|
*/
|
||||||
|
OverrideFileName *string
|
||||||
/*
|
/*
|
||||||
Required: true
|
Required: true
|
||||||
In: query
|
In: query
|
||||||
@@ -94,6 +103,11 @@ func (o *DownloadObjectParams) BindRequest(r *http.Request, route *middleware.Ma
|
|||||||
res = append(res, err)
|
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")
|
qPrefix, qhkPrefix, _ := qs.GetOK("prefix")
|
||||||
if err := o.bindPrefix(qPrefix, qhkPrefix, route.Formats); err != nil {
|
if err := o.bindPrefix(qPrefix, qhkPrefix, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
@@ -128,6 +142,25 @@ func (o *DownloadObjectParams) bindBucketName(rawData []string, hasKey bool, for
|
|||||||
return nil
|
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.
|
// bindPrefix binds and validates parameter Prefix from query.
|
||||||
func (o *DownloadObjectParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
func (o *DownloadObjectParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
if !hasKey {
|
if !hasKey {
|
||||||
|
|||||||
@@ -35,9 +35,10 @@ import (
|
|||||||
type DownloadObjectURL struct {
|
type DownloadObjectURL struct {
|
||||||
BucketName string
|
BucketName string
|
||||||
|
|
||||||
Prefix string
|
OverrideFileName *string
|
||||||
Preview *bool
|
Prefix string
|
||||||
VersionID *string
|
Preview *bool
|
||||||
|
VersionID *string
|
||||||
|
|
||||||
_basePath string
|
_basePath string
|
||||||
// avoid unkeyed usage
|
// avoid unkeyed usage
|
||||||
@@ -80,6 +81,14 @@ func (o *DownloadObjectURL) Build() (*url.URL, error) {
|
|||||||
|
|
||||||
qs := make(url.Values)
|
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
|
prefixQ := o.Prefix
|
||||||
if prefixQ != "" {
|
if prefixQ != "" {
|
||||||
qs.Set("prefix", prefixQ)
|
qs.Set("prefix", prefixQ)
|
||||||
|
|||||||
@@ -392,16 +392,27 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
|
|||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
|
|
||||||
isPreview := params.Preview != nil && *params.Preview
|
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
|
// indicate it's a download / inline content to the browser, and the size of the object
|
||||||
var filename string
|
var filename string
|
||||||
prefixElements := strings.Split(prefix, "/")
|
prefixElements := strings.Split(prefix, "/")
|
||||||
if len(prefixElements) > 0 {
|
if len(prefixElements) > 0 && overrideName == "" {
|
||||||
if prefixElements[len(prefixElements)-1] == "" {
|
if prefixElements[len(prefixElements)-1] == "" {
|
||||||
filename = prefixElements[len(prefixElements)-2]
|
filename = prefixElements[len(prefixElements)-2]
|
||||||
} else {
|
} else {
|
||||||
filename = prefixElements[len(prefixElements)-1]
|
filename = prefixElements[len(prefixElements)-1]
|
||||||
}
|
}
|
||||||
|
} else if overrideName != "" {
|
||||||
|
filename = overrideName
|
||||||
}
|
}
|
||||||
|
|
||||||
escapedName := url.PathEscape(filename)
|
escapedName := url.PathEscape(filename)
|
||||||
|
|
||||||
// indicate object size & content type
|
// indicate object size & content type
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
info:
|
info:
|
||||||
title: MinIO Console Server
|
title: MinIO Console Server
|
||||||
@@ -432,6 +433,11 @@ paths:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
- name: override_file_name
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: A successful response.
|
description: A successful response.
|
||||||
|
|||||||
Reference in New Issue
Block a user