UX Trace Screen (#1781)
This commit is contained in:
committed by
GitHub
parent
aaa55a1f4a
commit
461bc94a0b
141
portal-ui/src/icons/FilterIcon.tsx
Normal file
141
portal-ui/src/icons/FilterIcon.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { SVGProps } from "react";
|
||||
|
||||
const FilterIcon = (props: SVGProps<SVGSVGElement>) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
width="14"
|
||||
height="13.088"
|
||||
viewBox="0 0 14 13.088"
|
||||
{...props}
|
||||
>
|
||||
<g id="filter-icon.a949c200" transform="translate(-231.827 -340.123)">
|
||||
<line
|
||||
id="Línea_659"
|
||||
data-name="Línea 659"
|
||||
x2="14"
|
||||
transform="translate(231.827 346.667)"
|
||||
fill="none"
|
||||
stroke="#434343"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
<g
|
||||
id="Grupo_2472"
|
||||
data-name="Grupo 2472"
|
||||
transform="translate(240.693 344.614)"
|
||||
>
|
||||
<circle
|
||||
id="Elipse_611"
|
||||
data-name="Elipse 611"
|
||||
cx="2.053"
|
||||
cy="2.053"
|
||||
r="2.053"
|
||||
transform="translate(0 0)"
|
||||
fill="#fff"
|
||||
/>
|
||||
<circle
|
||||
id="Elipse_612"
|
||||
data-name="Elipse 612"
|
||||
cx="1.597"
|
||||
cy="1.597"
|
||||
r="1.597"
|
||||
transform="translate(0.456 0.456)"
|
||||
fill="none"
|
||||
stroke="#414141"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
</g>
|
||||
<line
|
||||
id="Línea_660"
|
||||
data-name="Línea 660"
|
||||
x2="14"
|
||||
transform="translate(231.827 342.22)"
|
||||
fill="none"
|
||||
stroke="#434343"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
<g
|
||||
id="Grupo_2473"
|
||||
data-name="Grupo 2473"
|
||||
transform="translate(232.394 340.167)"
|
||||
>
|
||||
<circle
|
||||
id="Elipse_613"
|
||||
data-name="Elipse 613"
|
||||
cx="2.053"
|
||||
cy="2.053"
|
||||
r="2.053"
|
||||
transform="translate(0 0)"
|
||||
fill="#fff"
|
||||
/>
|
||||
<circle
|
||||
id="Elipse_614"
|
||||
data-name="Elipse 614"
|
||||
cx="1.597"
|
||||
cy="1.597"
|
||||
r="1.597"
|
||||
transform="translate(0.456 0.456)"
|
||||
fill="none"
|
||||
stroke="#414141"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
</g>
|
||||
<line
|
||||
id="Línea_661"
|
||||
data-name="Línea 661"
|
||||
x2="14"
|
||||
transform="translate(231.827 351.114)"
|
||||
fill="none"
|
||||
stroke="#434343"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
<g
|
||||
id="Grupo_2474"
|
||||
data-name="Grupo 2474"
|
||||
transform="translate(235.161 349.061)"
|
||||
>
|
||||
<circle
|
||||
id="Elipse_615"
|
||||
data-name="Elipse 615"
|
||||
cx="2.053"
|
||||
cy="2.053"
|
||||
r="2.053"
|
||||
transform="translate(0 0)"
|
||||
fill="#fff"
|
||||
/>
|
||||
<circle
|
||||
id="Elipse_616"
|
||||
data-name="Elipse 616"
|
||||
cx="1.597"
|
||||
cy="1.597"
|
||||
r="1.597"
|
||||
transform="translate(0.456 0.456)"
|
||||
fill="none"
|
||||
stroke="#414141"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default FilterIcon;
|
||||
@@ -181,3 +181,4 @@ export { default as LicenseDocIcon } from "./LicenseDocIcon";
|
||||
export { default as SelectAllIcon } from "./SelectAllIcon";
|
||||
export { default as BackIcon } from "./BackIcon";
|
||||
export { default as DeleteNonCurrentIcon } from "./DeleteNonCurrentIcon";
|
||||
export { default as FilterIcon } from "./FilterIcon";
|
||||
|
||||
@@ -79,11 +79,13 @@ const RBIconButton = (props: RBIconProps) => {
|
||||
disabled = false,
|
||||
tooltip,
|
||||
icon = null,
|
||||
className = "",
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<BoxIconButton
|
||||
className={className}
|
||||
classes={classes}
|
||||
tooltip={tooltip || text}
|
||||
variant="outlined"
|
||||
|
||||
@@ -1093,6 +1093,11 @@ const IconsScreen = ({ classes }: IIconsScreenSimple) => {
|
||||
<br />
|
||||
BackIcon
|
||||
</Grid>
|
||||
<Grid item xs={3} sm={2} md={1}>
|
||||
<cicons.FilterIcon />
|
||||
<br />
|
||||
FilterIcon
|
||||
</Grid>
|
||||
</Grid>
|
||||
<h1>Menu Icons</h1>
|
||||
<Grid
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useState } from "react";
|
||||
import { Button, Grid, TextField } from "@mui/material";
|
||||
import { Box, Grid } from "@mui/material";
|
||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||
import { AppState } from "../../../store";
|
||||
import { connect } from "react-redux";
|
||||
@@ -43,6 +43,9 @@ import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import CheckboxWrapper from "../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
|
||||
import moment from "moment/moment";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import { FilterIcon } from "../../../icons";
|
||||
import RBIconButton from "../Buckets/BucketDetails/SummaryItems/RBIconButton";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -89,9 +92,18 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
formBox: {
|
||||
border: "1px solid #EAEAEA",
|
||||
padding: 15,
|
||||
padding: 25,
|
||||
marginBottom: 15,
|
||||
},
|
||||
traceCheckedIcon: {
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
marginLeft: "0px",
|
||||
},
|
||||
unCheckedIcon: {
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
},
|
||||
midColumnCheckboxes: {
|
||||
display: "flex",
|
||||
},
|
||||
@@ -139,6 +151,8 @@ const Trace = ({
|
||||
const [os, setOS] = useState<boolean>(false);
|
||||
const [errors, setErrors] = useState<boolean>(false);
|
||||
|
||||
const [toggleFilter, setToggleFilter] = useState<boolean>(false);
|
||||
|
||||
const startTrace = () => {
|
||||
traceResetMessages();
|
||||
const url = new URL(window.location.toString());
|
||||
@@ -201,240 +215,417 @@ const Trace = ({
|
||||
<Fragment>
|
||||
<PageHeader label={"Trace"} />
|
||||
<PageLayout>
|
||||
<Grid xs={12} className={classes.formBox}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
className={classes.searchField}
|
||||
id="status-code"
|
||||
label="Status Code"
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
}}
|
||||
value={statusCode}
|
||||
onChange={(e) => {
|
||||
setStatusCode(e.target.value);
|
||||
}}
|
||||
disabled={traceStarted}
|
||||
variant="standard"
|
||||
/>
|
||||
<TextField
|
||||
className={classes.searchField}
|
||||
id="method"
|
||||
label="Method"
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
}}
|
||||
value={method}
|
||||
onChange={(e) => {
|
||||
setMethod(e.target.value);
|
||||
}}
|
||||
disabled={traceStarted}
|
||||
variant="standard"
|
||||
/>
|
||||
<TextField
|
||||
label="Function Name"
|
||||
className={classes.searchField}
|
||||
id="func-name"
|
||||
disabled={traceStarted}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
}}
|
||||
value={func}
|
||||
onChange={(e) => {
|
||||
setFunc(e.target.value);
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
<TextField
|
||||
className={classes.searchField}
|
||||
id="path"
|
||||
label="Path"
|
||||
disabled={traceStarted}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
}}
|
||||
value={path}
|
||||
onChange={(e) => {
|
||||
setPath(e.target.value);
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
<TextField
|
||||
type="number"
|
||||
className={classes.searchField}
|
||||
id="fthreshold"
|
||||
label="Response Threshold"
|
||||
disabled={traceStarted}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
}}
|
||||
inputProps={{
|
||||
min: 0,
|
||||
}}
|
||||
value={threshold}
|
||||
onChange={(e) => {
|
||||
setThreshold(parseInt(e.target.value));
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.inlineCheckboxes}>
|
||||
<div className={classes.checkBoxLabel}>Calls to trace:</div>
|
||||
<div className={classes.midColumnCheckboxes}>
|
||||
<CheckboxWrapper
|
||||
checked={all}
|
||||
id={"all_calls"}
|
||||
name={"all_calls"}
|
||||
label={"All"}
|
||||
onChange={(item) => {
|
||||
setAll(item.target.checked);
|
||||
}}
|
||||
value={"all"}
|
||||
disabled={traceStarted}
|
||||
/>
|
||||
<CheckboxWrapper
|
||||
checked={s3 || all}
|
||||
id={"s3_calls"}
|
||||
name={"s3_calls"}
|
||||
label={"S3"}
|
||||
onChange={(item) => {
|
||||
setS3(item.target.checked);
|
||||
}}
|
||||
value={"s3"}
|
||||
disabled={all || traceStarted}
|
||||
/>
|
||||
<CheckboxWrapper
|
||||
checked={internal || all}
|
||||
id={"internal_calls"}
|
||||
name={"internal_calls"}
|
||||
label={"Internal"}
|
||||
onChange={(item) => {
|
||||
setInternal(item.target.checked);
|
||||
}}
|
||||
value={"internal"}
|
||||
disabled={all || traceStarted}
|
||||
/>
|
||||
<CheckboxWrapper
|
||||
checked={storage || all}
|
||||
id={"storage_calls"}
|
||||
name={"storage_calls"}
|
||||
label={"Storage"}
|
||||
onChange={(item) => {
|
||||
setStorage(item.target.checked);
|
||||
}}
|
||||
value={"storage"}
|
||||
disabled={all || traceStarted}
|
||||
/>
|
||||
<CheckboxWrapper
|
||||
checked={os || all}
|
||||
id={"os_calls"}
|
||||
name={"os_calls"}
|
||||
label={"OS"}
|
||||
onChange={(item) => {
|
||||
setOS(item.target.checked);
|
||||
}}
|
||||
value={"os"}
|
||||
disabled={all || traceStarted}
|
||||
/>
|
||||
<span className={classes.separatorBar}>
|
||||
|
|
||||
</span>
|
||||
</div>
|
||||
<CheckboxWrapper
|
||||
checked={errors}
|
||||
id={"only_errors"}
|
||||
name={"only_errors"}
|
||||
label={"Display only Errors"}
|
||||
onChange={(item) => {
|
||||
setErrors(item.target.checked);
|
||||
}}
|
||||
value={"only_errors"}
|
||||
disabled={traceStarted}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.startButton}>
|
||||
{!traceStarted && (
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={traceStarted}
|
||||
onClick={startTrace}
|
||||
>
|
||||
Start
|
||||
</Button>
|
||||
)}
|
||||
{traceStarted && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={stopTrace}
|
||||
>
|
||||
Stop
|
||||
</Button>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid className={classes.formBox}>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
|
||||
<Grid item xs={12} className={classes.tableBlock}>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={[
|
||||
{
|
||||
label: "Time",
|
||||
elementKey: "ptime",
|
||||
renderFunction: (time: Date) => {
|
||||
const timeParse = new Date(time);
|
||||
return timeFromDate(timeParse);
|
||||
"& .trace-checkbox-label": {
|
||||
fontSize: "14px",
|
||||
fontWeight: "normal",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
fontWeight: 600,
|
||||
padding: "20px 0px 20px 0",
|
||||
}}
|
||||
>
|
||||
Calls to Trace
|
||||
</Box>
|
||||
<Box
|
||||
className={`${traceStarted ? "inactive-state" : ""}`}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
|
||||
"&.inactive-state .trace-checkbox-label": {
|
||||
color: "#a6a5a5",
|
||||
},
|
||||
globalClass: classes.timeItem,
|
||||
},
|
||||
{ label: "Name", elementKey: "api" },
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "",
|
||||
renderFunction: (fullElement: TraceMessage) =>
|
||||
`${fullElement.statusCode} ${fullElement.statusMsg}`,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Location",
|
||||
elementKey: "configuration_id",
|
||||
renderFunction: (fullElement: TraceMessage) =>
|
||||
`${fullElement.host} ${fullElement.client}`,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Load Time",
|
||||
elementKey: "callStats.duration",
|
||||
globalClass: classes.timeItem,
|
||||
},
|
||||
{
|
||||
label: "Upload",
|
||||
elementKey: "callStats.rx",
|
||||
renderFunction: niceBytes,
|
||||
globalClass: classes.sizeItem,
|
||||
},
|
||||
{
|
||||
label: "Download",
|
||||
elementKey: "callStats.tx",
|
||||
renderFunction: niceBytes,
|
||||
globalClass: classes.sizeItem,
|
||||
},
|
||||
]}
|
||||
isLoading={false}
|
||||
records={messages}
|
||||
entityName="Traces"
|
||||
idField="api"
|
||||
customEmptyMessage={
|
||||
traceStarted
|
||||
? "No Traced elements received yet"
|
||||
: "Trace is not started yet"
|
||||
}
|
||||
customPaperHeight={classes.tableWrapper}
|
||||
autoScrollToBottom
|
||||
/>
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexFlow: "row",
|
||||
gap: {
|
||||
md: "30px",
|
||||
},
|
||||
|
||||
"& .trace-checked-icon": {
|
||||
border: "1px solid red",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CheckboxWrapper
|
||||
checked={all}
|
||||
id={"all_calls"}
|
||||
name={"all_calls"}
|
||||
label={"All"}
|
||||
onChange={(item) => {
|
||||
setAll(item.target.checked);
|
||||
}}
|
||||
value={"all"}
|
||||
disabled={traceStarted}
|
||||
overrideLabelClasses="trace-checkbox-label"
|
||||
classes={{
|
||||
checkedIcon: classes.traceCheckedIcon,
|
||||
unCheckedIcon: classes.unCheckedIcon,
|
||||
}}
|
||||
/>
|
||||
<CheckboxWrapper
|
||||
checked={s3 || all}
|
||||
id={"s3_calls"}
|
||||
name={"s3_calls"}
|
||||
label={"S3"}
|
||||
onChange={(item) => {
|
||||
setS3(item.target.checked);
|
||||
}}
|
||||
value={"s3"}
|
||||
disabled={traceStarted}
|
||||
overrideLabelClasses="trace-checkbox-label"
|
||||
classes={{
|
||||
checkedIcon: classes.traceCheckedIcon,
|
||||
unCheckedIcon: classes.unCheckedIcon,
|
||||
}}
|
||||
/>
|
||||
<CheckboxWrapper
|
||||
checked={internal || all}
|
||||
id={"internal_calls"}
|
||||
name={"internal_calls"}
|
||||
label={"Internal"}
|
||||
onChange={(item) => {
|
||||
setInternal(item.target.checked);
|
||||
}}
|
||||
value={"internal"}
|
||||
disabled={all || traceStarted}
|
||||
overrideLabelClasses="trace-checkbox-label"
|
||||
classes={{
|
||||
checkedIcon: classes.traceCheckedIcon,
|
||||
unCheckedIcon: classes.unCheckedIcon,
|
||||
}}
|
||||
/>
|
||||
<CheckboxWrapper
|
||||
checked={storage || all}
|
||||
id={"storage_calls"}
|
||||
name={"storage_calls"}
|
||||
label={"Storage"}
|
||||
onChange={(item) => {
|
||||
setStorage(item.target.checked);
|
||||
}}
|
||||
value={"storage"}
|
||||
disabled={all || traceStarted}
|
||||
overrideLabelClasses="trace-checkbox-label"
|
||||
classes={{
|
||||
checkedIcon: classes.traceCheckedIcon,
|
||||
unCheckedIcon: classes.unCheckedIcon,
|
||||
}}
|
||||
/>
|
||||
<CheckboxWrapper
|
||||
checked={os || all}
|
||||
id={"os_calls"}
|
||||
name={"os_calls"}
|
||||
label={"OS"}
|
||||
onChange={(item) => {
|
||||
setOS(item.target.checked);
|
||||
}}
|
||||
value={"os"}
|
||||
disabled={all || traceStarted}
|
||||
overrideLabelClasses="trace-checkbox-label"
|
||||
classes={{
|
||||
checkedIcon: classes.traceCheckedIcon,
|
||||
unCheckedIcon: classes.unCheckedIcon,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<RBIconButton
|
||||
tooltip={"More filter options"}
|
||||
onClick={() => {
|
||||
setToggleFilter(!toggleFilter);
|
||||
}}
|
||||
text={"Filters"}
|
||||
icon={<FilterIcon />}
|
||||
color={"primary"}
|
||||
variant={"outlined"}
|
||||
className={"filters-toggle-button"}
|
||||
style={{
|
||||
width: "118px",
|
||||
background: toggleFilter ? "rgba(8, 28, 66, 0.04)" : "",
|
||||
}}
|
||||
/>
|
||||
|
||||
{!traceStarted && (
|
||||
<RBIconButton
|
||||
text={"Start New Trace"}
|
||||
data-test-id={"trace-start-button"}
|
||||
icon={null}
|
||||
color={"primary"}
|
||||
variant="contained"
|
||||
onClick={startTrace}
|
||||
style={{
|
||||
width: "118px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{traceStarted && (
|
||||
<RBIconButton
|
||||
text={"Stop Trace"}
|
||||
data-test-id={"trace-stop-button"}
|
||||
icon={null}
|
||||
color={"primary"}
|
||||
variant="contained"
|
||||
onClick={stopTrace}
|
||||
style={{
|
||||
width: "118px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
{toggleFilter ? (
|
||||
<Grid
|
||||
item
|
||||
className={`${traceStarted ? "inactive-state" : ""}`}
|
||||
xs={12}
|
||||
sx={{
|
||||
marginTop: "25px",
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
background: "#FBFAFA",
|
||||
padding: "30px",
|
||||
|
||||
"&.inactive-state label": {
|
||||
color: "#a6a5a5",
|
||||
},
|
||||
|
||||
"& .orient-vertical": {
|
||||
flexFlow: "column",
|
||||
"& label": {
|
||||
marginBottom: "10px",
|
||||
fontWeight: 600,
|
||||
},
|
||||
},
|
||||
|
||||
"& .trace-checkbox-label": {
|
||||
fontSize: "14px",
|
||||
fontWeight: "normal",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
gap: "30px",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr 1fr",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<InputBoxWrapper
|
||||
className="orient-vertical"
|
||||
id="trace-status-code"
|
||||
name="trace-status-code"
|
||||
label="Status Code"
|
||||
classes={{}}
|
||||
placeholder="e.g. 503"
|
||||
value={statusCode}
|
||||
onChange={(e) => {
|
||||
setStatusCode(e.target.value);
|
||||
}}
|
||||
disabled={traceStarted}
|
||||
/>
|
||||
|
||||
<InputBoxWrapper
|
||||
className="orient-vertical"
|
||||
id="trace-function-name"
|
||||
name="trace-function-name"
|
||||
label="Function Name"
|
||||
classes={{}}
|
||||
placeholder="e.g. FunctionName2055"
|
||||
value={func}
|
||||
onChange={(e) => {
|
||||
setFunc(e.target.value);
|
||||
}}
|
||||
disabled={traceStarted}
|
||||
/>
|
||||
|
||||
<InputBoxWrapper
|
||||
className="orient-vertical"
|
||||
id="trace-method"
|
||||
name="trace-method"
|
||||
label="Method"
|
||||
classes={{}}
|
||||
placeholder="e.g. Method 2056"
|
||||
value={method}
|
||||
onChange={(e) => {
|
||||
setMethod(e.target.value);
|
||||
}}
|
||||
disabled={traceStarted}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
gap: "30px",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "2fr 1fr",
|
||||
width: "100%",
|
||||
marginTop: "33px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
flex="2"
|
||||
style={{
|
||||
width: "calc( 100% + 10px)",
|
||||
}}
|
||||
>
|
||||
<InputBoxWrapper
|
||||
className="orient-vertical"
|
||||
id="trace-path"
|
||||
name="trace-path"
|
||||
label="Path"
|
||||
classes={{}}
|
||||
placeholder="e.g. my-bucket/my-prefix/*"
|
||||
value={path}
|
||||
onChange={(e) => {
|
||||
setPath(e.target.value);
|
||||
}}
|
||||
disabled={traceStarted}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
marginLeft: "15px",
|
||||
}}
|
||||
>
|
||||
<InputBoxWrapper
|
||||
className="orient-vertical"
|
||||
id="trace-fthreshold"
|
||||
name="trace-fthreshold"
|
||||
label="Response Threshold"
|
||||
type="number"
|
||||
classes={{}}
|
||||
placeholder="e.g. website.io.3249.114.12"
|
||||
value={`${threshold}`}
|
||||
onChange={(e) => {
|
||||
setThreshold(parseInt(e.target.value));
|
||||
}}
|
||||
disabled={traceStarted}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
marginTop: "40px",
|
||||
}}
|
||||
>
|
||||
<CheckboxWrapper
|
||||
checked={errors}
|
||||
id={"only_errors"}
|
||||
name={"only_errors"}
|
||||
label={"Display only Errors"}
|
||||
onChange={(item) => {
|
||||
setErrors(item.target.checked);
|
||||
}}
|
||||
value={"only_errors"}
|
||||
disabled={traceStarted}
|
||||
overrideLabelClasses="trace-checkbox-label"
|
||||
classes={{
|
||||
checkedIcon: classes.traceCheckedIcon,
|
||||
unCheckedIcon: classes.unCheckedIcon,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
) : null}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
fontWeight: 600,
|
||||
marginBottom: "30px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
Trace Results
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.tableBlock}>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={[
|
||||
{
|
||||
label: "Time",
|
||||
elementKey: "ptime",
|
||||
renderFunction: (time: Date) => {
|
||||
const timeParse = new Date(time);
|
||||
return timeFromDate(timeParse);
|
||||
},
|
||||
globalClass: classes.timeItem,
|
||||
},
|
||||
{ label: "Name", elementKey: "api" },
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "",
|
||||
renderFunction: (fullElement: TraceMessage) =>
|
||||
`${fullElement.statusCode} ${fullElement.statusMsg}`,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Location",
|
||||
elementKey: "configuration_id",
|
||||
renderFunction: (fullElement: TraceMessage) =>
|
||||
`${fullElement.host} ${fullElement.client}`,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Load Time",
|
||||
elementKey: "callStats.duration",
|
||||
globalClass: classes.timeItem,
|
||||
},
|
||||
{
|
||||
label: "Upload",
|
||||
elementKey: "callStats.rx",
|
||||
renderFunction: niceBytes,
|
||||
globalClass: classes.sizeItem,
|
||||
},
|
||||
{
|
||||
label: "Download",
|
||||
elementKey: "callStats.tx",
|
||||
renderFunction: niceBytes,
|
||||
globalClass: classes.sizeItem,
|
||||
},
|
||||
]}
|
||||
isLoading={false}
|
||||
records={messages}
|
||||
entityName="Traces"
|
||||
idField="api"
|
||||
customEmptyMessage={
|
||||
traceStarted
|
||||
? "No Traced elements received yet"
|
||||
: "Trace is not started yet"
|
||||
}
|
||||
customPaperHeight={classes.tableWrapper}
|
||||
autoScrollToBottom
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
import * as roles from "../utils/roles";
|
||||
import * as elements from "../utils/elements";
|
||||
import {
|
||||
monitoringElement,
|
||||
supportElement,
|
||||
traceElement,
|
||||
} from "../utils/elements-menu";
|
||||
import { monitoringElement, traceElement } from "../utils/elements-menu";
|
||||
import { Selector } from "testcafe";
|
||||
|
||||
export const traceStartButton = Selector('[data-test-id="trace-start-button"]');
|
||||
export const traceStopButton = Selector('[data-test-id="trace-stop-button"]');
|
||||
|
||||
fixture("For user with Trace permissions")
|
||||
.page("http://localhost:9090")
|
||||
@@ -48,14 +48,14 @@ test("Trace page can be opened", async (t) => {
|
||||
test("Start button can be clicked", async (t) => {
|
||||
await t
|
||||
.navigateTo("http://localhost:9090/tools/trace")
|
||||
.click(elements.startButton);
|
||||
.click(traceStartButton);
|
||||
});
|
||||
|
||||
test("Stop button appears after Start button has been clicked", async (t) => {
|
||||
const stopButtonExists = elements.stopButton.exists;
|
||||
const stopButtonExists = traceStopButton.exists;
|
||||
await t
|
||||
.navigateTo("http://localhost:9090/tools/trace")
|
||||
.click(elements.startButton)
|
||||
.click(traceStartButton)
|
||||
.expect(stopButtonExists)
|
||||
.ok();
|
||||
});
|
||||
@@ -63,6 +63,6 @@ test("Stop button appears after Start button has been clicked", async (t) => {
|
||||
test("Stop button can be clicked after Start button has been clicked", async (t) => {
|
||||
await t
|
||||
.navigateTo("http://localhost:9090/tools/trace")
|
||||
.click(elements.startButton)
|
||||
.click(elements.stopButton);
|
||||
.click(traceStartButton)
|
||||
.click(traceStopButton);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user