mirror of https://github.com/linkerd/linkerd2.git
Replacing native sort with lodash stable sortBy in Web UI (#2501)
Replaces native JS `sort()` with lodash stable `_sortBy` for table rows so that rows are always sorted consistently.
This commit is contained in:
parent
63996e8b8a
commit
384acf2731
|
@ -16,6 +16,7 @@ import Typography from '@material-ui/core/Typography';
|
|||
import _find from 'lodash/find';
|
||||
import _get from 'lodash/get';
|
||||
import _isNil from 'lodash/isNil';
|
||||
import _sortBy from 'lodash/sortBy';
|
||||
import classNames from 'classnames';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
|
||||
|
@ -85,12 +86,12 @@ class BaseTable extends React.Component {
|
|||
generateRows = (tableRows, tableColumns, order, orderBy, filterBy) => {
|
||||
let rows = tableRows;
|
||||
let col = _find(tableColumns, d => d.dataIndex === orderBy);
|
||||
if (orderBy) {
|
||||
rows = tableRows.sort(col.sorter);
|
||||
if (orderBy && col.sorter) {
|
||||
rows = _sortBy(rows, row => col.sorter(row));
|
||||
}
|
||||
if (filterBy) {
|
||||
let columnsToFilter = tableColumns.filter(col => col.filter);
|
||||
let filteredRows = tableRows.filter(row => {
|
||||
let filteredRows = rows.filter(row => {
|
||||
return columnsToFilter.some(col => {
|
||||
let rowText = col.filter(row);
|
||||
return rowText.match(filterBy);
|
||||
|
|
|
@ -9,7 +9,6 @@ import _each from 'lodash/each';
|
|||
import _get from 'lodash/get';
|
||||
import _reduce from 'lodash/reduce';
|
||||
import { apiErrorPropType } from './util/ApiHelpers.jsx';
|
||||
import { numericSort } from './util/Utils.js';
|
||||
import { withContext } from './util/AppContext.jsx';
|
||||
import withREST from './util/withREST.jsx';
|
||||
|
||||
|
@ -18,33 +17,33 @@ const endpointColumns = [
|
|||
title: "Namespace",
|
||||
dataIndex: "namespace",
|
||||
render: d => <Link to={"/namespaces/" + d.namespace}>{d.namespace}</Link>,
|
||||
sorter: (a, b) => (a.namespace).localeCompare(b.namespace)
|
||||
sorter: d => d.namespace
|
||||
},
|
||||
{
|
||||
title: "IP",
|
||||
dataIndex: "ip",
|
||||
sorter: (a, b) => (a.ip).localeCompare(b.ip)
|
||||
sorter: d => d.ip
|
||||
},
|
||||
{
|
||||
title: "Port",
|
||||
dataIndex: "port",
|
||||
sorter: (a, b) => numericSort(a.port, b.port)
|
||||
sorter: d => d.port
|
||||
},
|
||||
{
|
||||
title: "Pod",
|
||||
dataIndex: "name",
|
||||
sorter: (a, b) => (a.name).localeCompare(b.name),
|
||||
sorter: d => d.name,
|
||||
render: d => <Link to={`/namespaces/${d.namespace}/pods/${d.name}`}>{d.name}</Link>
|
||||
},
|
||||
{
|
||||
title: "Resource Version",
|
||||
dataIndex: "resourceVersion",
|
||||
sorter: (a, b) => numericSort(a.resourceVersion, b.resourceVersion)
|
||||
sorter: d => d.resourceVersion
|
||||
},
|
||||
{
|
||||
title: "Service",
|
||||
dataIndex: "service",
|
||||
sorter: (a, b) => (a.service).localeCompare(b.service)
|
||||
sorter: d => d.service
|
||||
}
|
||||
];
|
||||
class Debug extends React.Component {
|
||||
|
|
|
@ -22,7 +22,7 @@ const namespacesColumns = PrefixedLink => [
|
|||
{
|
||||
title: "Namespace",
|
||||
dataIndex: "namespace",
|
||||
sorter: (a, b) => a.namespace.localeCompare(b.namespace),
|
||||
sorter: d => d.namespace,
|
||||
render: d => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { displayName, friendlyTitle, metricToFormatter, numericSort } from './util/Utils.js';
|
||||
import { displayName, friendlyTitle, metricToFormatter } from './util/Utils.js';
|
||||
import BaseTable from './BaseTable.jsx';
|
||||
import ErrorModal from './ErrorModal.jsx';
|
||||
import GrafanaLink from './GrafanaLink.jsx';
|
||||
|
@ -20,21 +20,21 @@ const tcpStatColumns = [
|
|||
dataIndex: "tcp.openConnections",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["NO_UNIT"](d.tcp.openConnections),
|
||||
sorter: (a, b) => numericSort(a.tcp.openConnections, b.tcp.openConnections)
|
||||
sorter: d => d.tcp.openConnections
|
||||
},
|
||||
{
|
||||
title: "Read Bytes / sec",
|
||||
dataIndex: "tcp.readRate",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["BYTES"](d.tcp.readRate),
|
||||
sorter: (a, b) => numericSort(a.tcp.readRate, b.tcp.readRate)
|
||||
sorter: d => d.tcp.readRate
|
||||
},
|
||||
{
|
||||
title: "Write Bytes / sec",
|
||||
dataIndex: "tcp.writeRate",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["BYTES"](d.tcp.writeRate),
|
||||
sorter: (a, b) => numericSort(a.tcp.writeRate, b.tcp.writeRate)
|
||||
sorter: d => d.tcp.writeRate
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -44,44 +44,42 @@ const httpStatColumns = [
|
|||
dataIndex: "successRate",
|
||||
isNumeric: true,
|
||||
render: d => <SuccessRateMiniChart sr={d.successRate} />,
|
||||
sorter: (a, b) => numericSort(a.successRate, b.successRate)
|
||||
sorter: d => d.successRate
|
||||
},
|
||||
{
|
||||
title: "RPS",
|
||||
dataIndex: "requestRate",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["NO_UNIT"](d.requestRate),
|
||||
sorter: (a, b) => numericSort(a.requestRate, b.requestRate)
|
||||
sorter: d => d.requestRate
|
||||
},
|
||||
{
|
||||
title: "P50 Latency",
|
||||
dataIndex: "P50",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["LATENCY"](d.P50),
|
||||
sorter: (a, b) => numericSort(a.P50, b.P50)
|
||||
sorter: d => d.P50
|
||||
},
|
||||
{
|
||||
title: "P95 Latency",
|
||||
dataIndex: "P95",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["LATENCY"](d.P95),
|
||||
sorter: (a, b) => numericSort(a.P95, b.P95)
|
||||
sorter: d => d.P95
|
||||
},
|
||||
{
|
||||
title: "P99 Latency",
|
||||
dataIndex: "P99",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["LATENCY"](d.P99),
|
||||
sorter: (a, b) => numericSort(a.P99, b.P99)
|
||||
sorter: d => d.P99
|
||||
},
|
||||
{
|
||||
title: "TLS",
|
||||
dataIndex: "tlsRequestPercent",
|
||||
isNumeric: true,
|
||||
render: d => _isNil(d.tlsRequestPercent) || d.tlsRequestPercent.get() === -1 ? "---" : d.tlsRequestPercent.prettyRate(),
|
||||
sorter: (a, b) => numericSort(
|
||||
a.tlsRequestPercent ? a.tlsRequestPercent.get() : -1,
|
||||
b.tlsRequestPercent ? b.tlsRequestPercent.get() : -1)
|
||||
sorter: d => d.tlsRequestPercent ? d.tlsRequestPercent.get() : -1
|
||||
},
|
||||
|
||||
];
|
||||
|
@ -98,7 +96,7 @@ const columnDefinitions = (resource, showNamespaceColumn, PrefixedLink, isTcpTab
|
|||
filter: d => d.namespace,
|
||||
isNumeric: false,
|
||||
render: d => !d.namespace ? "---" : <PrefixedLink to={"/namespaces/" + d.namespace}>{d.namespace}</PrefixedLink>,
|
||||
sorter: (a, b) => (a.namespace || "").localeCompare(b.namespace)
|
||||
sorter: d => !d.namespace ? "---" : d.namespace
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -107,7 +105,7 @@ const columnDefinitions = (resource, showNamespaceColumn, PrefixedLink, isTcpTab
|
|||
dataIndex: "pods.totalPods",
|
||||
isNumeric: true,
|
||||
render: d => !d.pods ? null : d.pods.meshedPods + "/" + d.pods.totalPods,
|
||||
sorter: (a, b) => numericSort(a.pods.totalPods, b.pods.totalPods)
|
||||
sorter: d => !d.pods ? -1 : d.pods.totalPods
|
||||
};
|
||||
|
||||
let grafanaColumn = {
|
||||
|
@ -155,7 +153,7 @@ const columnDefinitions = (resource, showNamespaceColumn, PrefixedLink, isTcpTab
|
|||
</Grid>
|
||||
);
|
||||
},
|
||||
sorter: (a, b) => (getResourceDisplayName(a) || "").localeCompare(getResourceDisplayName(b))
|
||||
sorter: d => getResourceDisplayName(d) || -1
|
||||
};
|
||||
|
||||
let columns = [nameColumn];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { directionColumn, extractDisplayName, srcDstColumn, tapLink } from './util/TapUtils.jsx';
|
||||
import { formatLatencySec, numericSort, toShortResourceName } from './util/Utils.js';
|
||||
import { formatLatencySec, toShortResourceName } from './util/Utils.js';
|
||||
|
||||
import BaseTable from './BaseTable.jsx';
|
||||
import PropTypes from 'prop-types';
|
||||
|
@ -30,27 +30,27 @@ const topColumns = (resourceType, ResourceLink, PrefixedLink) => [
|
|||
title: "Method",
|
||||
dataIndex: "httpMethod",
|
||||
filter: d => d.httpMethod,
|
||||
sorter: (a, b) => a.httpMethod.localeCompare(b.httpMethod)
|
||||
sorter: d => d.httpMethod
|
||||
},
|
||||
{
|
||||
title: "Path",
|
||||
dataIndex: "path",
|
||||
filter: d => d.path,
|
||||
sorter: (a, b) => a.path.localeCompare(b.path)
|
||||
sorter: d => d.path
|
||||
},
|
||||
{
|
||||
title: "Count",
|
||||
dataIndex: "count",
|
||||
isNumeric: true,
|
||||
defaultSortOrder: "desc",
|
||||
sorter: (a, b) => numericSort(a.count, b.count)
|
||||
sorter: d => d.count
|
||||
},
|
||||
{
|
||||
title: "Best",
|
||||
dataIndex: "best",
|
||||
isNumeric: true,
|
||||
render: d => formatLatencySec(d.best),
|
||||
sorter: (a, b) => numericSort(a.best, b.best)
|
||||
sorter: d => d.best
|
||||
},
|
||||
{
|
||||
title: "Worst",
|
||||
|
@ -58,14 +58,14 @@ const topColumns = (resourceType, ResourceLink, PrefixedLink) => [
|
|||
isNumeric: true,
|
||||
defaultSortOrder: "desc",
|
||||
render: d => formatLatencySec(d.worst),
|
||||
sorter: (a, b) => numericSort(a.worst, b.worst)
|
||||
sorter: d => d.worst
|
||||
},
|
||||
{
|
||||
title: "Last",
|
||||
dataIndex: "last",
|
||||
isNumeric: true,
|
||||
render: d => formatLatencySec(d.last),
|
||||
sorter: (a, b) => numericSort(a.last, b.last)
|
||||
sorter: d => d.last
|
||||
},
|
||||
{
|
||||
title: "Success Rate",
|
||||
|
@ -73,7 +73,7 @@ const topColumns = (resourceType, ResourceLink, PrefixedLink) => [
|
|||
isNumeric: true,
|
||||
render: d => _isNil(d) || _isNil(d.successRate) ? "---" :
|
||||
<SuccessRateMiniChart sr={d.successRate.get()} />,
|
||||
sorter: (a, b) => numericSort(a.successRate.get(), b.successRate.get()),
|
||||
sorter: d => d.successRate.get()
|
||||
},
|
||||
{
|
||||
title: "Tap",
|
||||
|
|
|
@ -1,67 +1,57 @@
|
|||
import { metricToFormatter, numericSort } from './util/Utils.js';
|
||||
import BaseTable from './BaseTable.jsx';
|
||||
import { DefaultRoute } from './util/MetricUtils.jsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import SuccessRateMiniChart from './util/SuccessRateMiniChart.jsx';
|
||||
import { metricToFormatter } from './util/Utils.js';
|
||||
|
||||
const routesColumns = [
|
||||
{
|
||||
title: "Route",
|
||||
dataIndex: "route",
|
||||
filter: d => d.route,
|
||||
sorter: (a, b) => {
|
||||
if (a.route === DefaultRoute) {
|
||||
return 1;
|
||||
} else {
|
||||
if (b.route === DefaultRoute) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return (a.route).localeCompare(b.route);
|
||||
}
|
||||
sorter: d => d.route
|
||||
},
|
||||
{
|
||||
title: "Service",
|
||||
tooltip: "hostname:port used when communicating with this target",
|
||||
dataIndex: "authority",
|
||||
filter: d => d.authority,
|
||||
sorter: (a, b) => (a.authority).localeCompare(b.authority)
|
||||
sorter: d => d.authority
|
||||
},
|
||||
{
|
||||
title: "Success Rate",
|
||||
dataIndex: "successRate",
|
||||
isNumeric: true,
|
||||
render: d => <SuccessRateMiniChart sr={d.successRate} />,
|
||||
sorter: (a, b) => numericSort(a.successRate, b.successRate)
|
||||
sorter: d => d.successRate
|
||||
},
|
||||
{
|
||||
title: "RPS",
|
||||
dataIndex: "requestRate",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["NO_UNIT"](d.requestRate),
|
||||
sorter: (a, b) => numericSort(a.requestRate, b.requestRate)
|
||||
sorter: d => d.requestRate
|
||||
},
|
||||
{
|
||||
title: "P50 Latency",
|
||||
dataIndex: "latency.P50",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["LATENCY"](d.latency.P50),
|
||||
sorter: (a, b) => numericSort(a.P50, b.P50)
|
||||
sorter: d => d.latency.P50
|
||||
},
|
||||
{
|
||||
title: "P95 Latency",
|
||||
dataIndex: "latency.P95",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["LATENCY"](d.latency.P95),
|
||||
sorter: (a, b) => numericSort(a.P95, b.P95)
|
||||
sorter: d => d.latency.P95
|
||||
},
|
||||
{
|
||||
title: "P99 Latency",
|
||||
dataIndex: "latency.P99",
|
||||
isNumeric: true,
|
||||
render: d => metricToFormatter["LATENCY"](d.latency.P99),
|
||||
sorter: (a, b) => numericSort(a.latency.P99, b.latency.P99)
|
||||
sorter: d => d.latency.P99
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -115,11 +115,6 @@ export const toClassName = name => {
|
|||
return _lowerCase(name).replace(/[^a-zA-Z0-9]/g, "_");
|
||||
};
|
||||
|
||||
/*
|
||||
Definition of sort, for numeric column sorting
|
||||
*/
|
||||
export const numericSort = (a, b) => (_isNil(a) ? -1 : a) - (_isNil(b) ? -1 : b);
|
||||
|
||||
/*
|
||||
Nicely readable names for the stat resources
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue