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:
Carol A. Scott 2019-03-21 16:21:30 -07:00 committed by GitHub
parent 63996e8b8a
commit 384acf2731
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 40 additions and 57 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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>

View File

@ -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];

View File

@ -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",

View File

@ -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
}
];

View File

@ -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
*/