import { directionColumn, srcDstColumn } from './util/TapUtils.jsx'; import { formatLatencySec, formatWithComma } from './util/Utils.js'; import Card from '@material-ui/core/Card'; import CardContent from '@material-ui/core/CardContent'; import CircularProgress from '@material-ui/core/CircularProgress'; import ExpandableTable from './ExpandableTable.jsx'; import Grid from '@material-ui/core/Grid'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; import ListItemText from '@material-ui/core/ListItemText'; import PropTypes from 'prop-types'; import React from 'react'; import { Trans } from '@lingui/macro'; import Typography from '@material-ui/core/Typography'; import _get from 'lodash/get'; import _isEmpty from 'lodash/isEmpty'; import _isNull from 'lodash/isNull'; import { headersDisplay } from './TapEventHeadersTable.jsx'; import { withContext } from './util/AppContext.jsx'; import { withStyles } from '@material-ui/core/styles'; // https://godoc.org/google.golang.org/grpc/codes#Code const grpcStatusCodes = { 0: 'OK', 1: 'Canceled', 2: 'Unknown', 3: 'InvalidArgument', 4: 'DeadlineExceeded', 5: 'NotFound', 6: 'AlreadyExists', 7: 'PermissionDenied', 8: 'ResourceExhausted', 9: 'FailedPrecondition', 10: 'Aborted', 11: 'OutOfRange', 12: 'Unimplemented', 13: 'Internal', 14: 'Unavailable', 15: 'DataLoss', 16: 'Unauthenticated', }; const spinnerStyles = theme => ({ progress: { margin: theme.spacing(2), }, }); const SpinnerBase = () => ; const Spinner = withStyles(spinnerStyles)(SpinnerBase); const formatTapLatency = str => { return formatLatencySec(str.replace('s', '')); }; const httpStatusCol = { title: columnTitleHTTPStatus, key: 'http-status', render: datum => { const d = _get(datum, 'responseInit.http.responseInit'); return !d ? : d.httpStatus; }, }; const responseInitLatencyCol = { title: columnTitleLatency, key: 'rsp-latency', isNumeric: true, render: datum => { const d = _get(datum, 'responseInit.http.responseInit'); return !d ? : formatTapLatency(d.sinceRequestInit); }, }; const grpcStatusCol = { title: columnTitleGRPCStatus, key: 'grpc-status', render: datum => { const d = _get(datum, 'responseEnd.http.responseEnd'); return !d ? : _isNull(d.eos) ? '---' : grpcStatusCodes[_get(d, 'eos.grpcStatusCode')]; }, }; const pathCol = { title: columnTitlePath, key: 'path', render: datum => { const d = _get(datum, 'requestInit.http.requestInit'); return !d ? : d.path; }, }; const methodCol = { title: columnTitleMethod, key: 'method', render: datum => { const d = _get(datum, 'requestInit.http.requestInit'); return !d ? : _get(d, 'method.registered'); }, }; const topLevelColumns = (resourceType, ResourceLink) => [ { title: columnTitleDirection, key: 'direction', render: d => directionColumn(d.base.proxyDirection), }, { title: columnTitleName, key: 'src-dst', render: d => { const datum = { direction: _get(d, 'base.proxyDirection'), source: _get(d, 'base.source'), destination: _get(d, 'base.destination'), sourceLabels: _get(d, 'base.sourceMeta.labels', {}), destinationLabels: _get(d, 'base.destinationMeta.labels', {}), }; return srcDstColumn(datum, resourceType, ResourceLink); }, }, ]; const tapColumns = (resourceType, ResourceLink) => { return topLevelColumns(resourceType, ResourceLink).concat( [methodCol, pathCol, responseInitLatencyCol, httpStatusCol, grpcStatusCol], ); }; const itemDisplay = (title, value) => { return ( ); }; const requestInitSection = d => ( Request Init
{itemDisplay('Authority', _get(d, 'requestInit.http.requestInit.authority'))} {itemDisplay('Path', _get(d, 'requestInit.http.requestInit.path'))} {itemDisplay('Scheme', _get(d, 'requestInit.http.requestInit.scheme.registered'))} {itemDisplay('Method', _get(d, 'requestInit.http.requestInit.method.registered'))} {headersDisplay('Headers', _get(d, 'requestInit.http.requestInit.headers'))}
); const responseInitSection = d => _isEmpty(d.responseInit) ? null : ( Response Init
{itemDisplay('HTTP Status', _get(d, 'responseInit.http.responseInit.httpStatus'))} {itemDisplay('Latency', formatTapLatency(_get(d, 'responseInit.http.responseInit.sinceRequestInit')))} {headersDisplay('Headers', _get(d, 'responseInit.http.responseInit.headers'))}
); const responseEndSection = d => _isEmpty(d.responseEnd) ? null : ( Response End
{itemDisplay('GRPC Status', _isNull(_get(d, 'responseEnd.http.responseEnd.eos')) ? 'N/A' : grpcStatusCodes[_get(d, 'responseEnd.http.responseEnd.eos.grpcStatusCode')])} {itemDisplay('Latency', formatTapLatency(_get(d, 'responseEnd.http.responseEnd.sinceResponseInit')))} {itemDisplay('Response Length (B)', formatWithComma(_get(d, 'responseEnd.http.responseEnd.responseBytes')))}
); // hide verbose information const expandedRowRender = (d, expandedWrapStyle) => { return ( {requestInitSection(d)} {responseInitSection(d)} {responseEndSection(d)} ); }; const TapEventTable = ({ tableRows, resource, api }) => { const resourceType = resource.split('/')[0]; const columns = tapColumns(resourceType, api.ResourceLink); return ( ); }; TapEventTable.propTypes = { api: PropTypes.shape({ ResourceLink: PropTypes.func.isRequired, }).isRequired, resource: PropTypes.string, tableRows: PropTypes.arrayOf(PropTypes.shape({})), }; TapEventTable.defaultProps = { resource: '', tableRows: [], }; export default withContext(TapEventTable);