import 'whatwg-fetch';
import { processMultiResourceRollup, processSingleResourceRollup } from './util/MetricUtils.jsx';
import Accordion from './util/Accordion.jsx';
import Divider from '@material-ui/core/Divider';
import ErrorBanner from './ErrorBanner.jsx';
import Grid from '@material-ui/core/Grid';
import MetricsTable from './MetricsTable.jsx';
import PropTypes from 'prop-types';
import React from 'react';
import SimpleChip from './util/Chip.jsx';
import Spinner from './util/Spinner.jsx';
import Typography from '@material-ui/core/Typography';
import _ from 'lodash';
import { friendlyTitle } from './util/Utils.js';
import { withContext } from './util/AppContext.jsx';
class NamespaceLanding extends React.Component {
static propTypes = {
api: PropTypes.shape({
cancelCurrentRequests: PropTypes.func.isRequired,
fetchMetrics: PropTypes.func.isRequired,
getCurrentPromises: PropTypes.func.isRequired,
setCurrentRequests: PropTypes.func.isRequired,
urlsForResource: PropTypes.func.isRequired,
}).isRequired,
controllerNamespace: PropTypes.string.isRequired
}
constructor(props) {
super(props);
this.api = this.props.api;
this.handleApiError = this.handleApiError.bind(this);
this.loadFromServer = this.loadFromServer.bind(this);
this.state = this.getInitialState();
}
getInitialState() {
return {
selectedNs: null,
metricsByNs: {},
namespaces: [],
pollingInterval: 2000,
pendingRequests: false,
loaded: false,
error: null
};
}
componentDidMount() {
this.loadFromServer();
this.timerId = window.setInterval(this.loadFromServer, this.state.pollingInterval);
}
componentWillUnmount() {
window.clearInterval(this.timerId);
this.api.cancelCurrentRequests();
}
onNamespaceChange = ns => {
this.setState({
selectedNs: ns
});
}
loadFromServer() {
if (this.state.pendingRequests) {
return; // don't make more requests if the ones we sent haven't completed
}
this.setState({ pendingRequests: true });
// TODO: make this one request
let apiRequests = [
this.api.fetchMetrics(this.api.urlsForResource("namespace"))
];
if (!_.isEmpty(this.state.selectedNs)) {
apiRequests = _.concat(apiRequests, [this.api.fetchMetrics(this.api.urlsForResource("all", this.state.selectedNs))]);
}
this.api.setCurrentRequests(apiRequests);
Promise.all(this.api.getCurrentPromises())
.then(([allNs, metricsForNs]) => {
let namespaces = processSingleResourceRollup(allNs);
let metricsByNs = this.state.metricsByNs;
if (!_.isNil(this.state.selectedNs) && !_.isNil(metricsForNs)) {
metricsByNs[this.state.selectedNs] = processMultiResourceRollup(metricsForNs);
}
// by default, show the first non-linkerd meshed namesapce
// if no other meshed namespaces are found, show the linkerd namespace
let defaultOpenNs = _.find(namespaces, ns => ns.added && ns.name !== this.props.controllerNamespace);
defaultOpenNs = defaultOpenNs || _.find(namespaces, ['name', this.props.controllerNamespace]);
this.setState({
namespaces,
metricsByNs,
defaultOpenNs,
selectedNs: this.state.selectedNs || defaultOpenNs.name,
pendingRequests: false,
loaded: true,
error: null
});
})
.catch(this.handleApiError);
}
handleApiError = e => {
if (e.isCanceled) {
return;
}
this.setState({
pendingRequests: false,
error: e
});
}
renderResourceSection(resource, metrics) {
if (_.isEmpty(metrics)) {
return null;
}
return (