import { friendlyTitle, metricToFormatter, numericSort } from './util/Utils.js'; import BaseTable from './BaseTable.jsx'; import ErrorModal from './ErrorModal.jsx'; import GrafanaLink from './GrafanaLink.jsx'; import Grid from '@material-ui/core/Grid'; import PropTypes from 'prop-types'; import React from 'react'; import SuccessRateMiniChart from './util/SuccessRateMiniChart.jsx'; import _cloneDeep from 'lodash/cloneDeep'; import _each from 'lodash/each'; import _get from 'lodash/get'; import _isEmpty from 'lodash/isEmpty'; import _isNil from 'lodash/isNil'; import { processedMetricsPropType } from './util/MetricUtils.jsx'; import { withContext } from './util/AppContext.jsx'; const columnDefinitions = (resource, showNamespaceColumn, PrefixedLink) => { let isAuthorityTable = resource === "authority"; let nsColumn = [ { title: "Namespace", dataIndex: "namespace", isNumeric: false, render: d => !d.namespace ? "---" : {d.namespace}, sorter: (a, b) => (a.namespace || "").localeCompare(b.namespace) } ]; let meshedColumn = { title: "Meshed", 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) }; let columns = [ { title: friendlyTitle(resource).singular, dataIndex: "name", isNumeric: false, render: d => { let nameContents; if (resource === "namespace") { nameContents = {d.name}; } else if (!d.added || isAuthorityTable) { nameContents = d.name; } else { nameContents = ( {d.name} ); } return ( {nameContents} { _isEmpty(d.errors) ? null : } ); }, sorter: (a, b) => (a.name || "").localeCompare(b.name) }, { title: "Success Rate", dataIndex: "successRate", isNumeric: true, render: d => , sorter: (a, b) => numericSort(a.successRate, b.successRate) }, { title: "RPS", dataIndex: "requestRate", isNumeric: true, render: d => metricToFormatter["NO_UNIT"](d.requestRate), sorter: (a, b) => numericSort(a.requestRate, b.requestRate) }, { title: "P50 Latency", dataIndex: "P50", isNumeric: true, render: d => metricToFormatter["LATENCY"](d.P50), sorter: (a, b) => numericSort(a.P50, b.P50) }, { title: "P95 Latency", dataIndex: "P95", isNumeric: true, render: d => metricToFormatter["LATENCY"](d.P95), sorter: (a, b) => numericSort(a.P95, b.P95) }, { title: "P99 Latency", dataIndex: "P99", isNumeric: true, render: d => metricToFormatter["LATENCY"](d.P99), sorter: (a, b) => numericSort(a.P99, b.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) }, { title: "Grafana", key: "grafanaDashboard", isNumeric: true, render: row => { if (!isAuthorityTable && (!row.added || _get(row, "pods.totalPods") === "0") ) { return null; } return ( ); } } ]; // don't add the meshed column on a Authority MetricsTable if (!isAuthorityTable) { columns.splice(1, 0, meshedColumn); } if (!showNamespaceColumn) { return columns; } else { return nsColumn.concat(columns); } }; const preprocessMetrics = metrics => { let tableData = _cloneDeep(metrics); _each(tableData, datum => { _each(datum.latency, (value, quantile) => { datum[quantile] = value; }); }); return tableData; }; class MetricsTable extends React.Component { static propTypes = { api: PropTypes.shape({ PrefixedLink: PropTypes.func.isRequired, }).isRequired, metrics: PropTypes.arrayOf(processedMetricsPropType), resource: PropTypes.string.isRequired, showNamespaceColumn: PropTypes.bool }; static defaultProps = { showNamespaceColumn: true, metrics: [] }; render() { const { metrics, resource, showNamespaceColumn, api } = this.props; let showNsColumn = resource === "namespace" ? false : showNamespaceColumn; let columns = columnDefinitions(resource, showNsColumn, api.PrefixedLink); let rows = preprocessMetrics(metrics); return ( ); } } export default withContext(MetricsTable);