[Monitoring] Stop a new request when one is inflight (#27253)

* Convert all pages to use the base controlller, then add logic in there to stop a new request when one is inflight

* Reuse the promise

* Undo logstash changes

* Update in catch too

* Add unit test

* Fix cluster name showing up

* Update broken test

* Just use updateDataPromise
This commit is contained in:
Chris Roberson 2018-12-17 14:38:21 -05:00 committed by GitHub
parent 08fd427125
commit faa57fd7a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 355 additions and 446 deletions

View file

@ -18,9 +18,9 @@ import { MonitoringTimeseriesContainer } from '../../chart';
import { ShardAllocation } from '../shard_allocation/shard_allocation'; import { ShardAllocation } from '../shard_allocation/shard_allocation';
export const Index = ({ export const Index = ({
scope,
indexSummary, indexSummary,
metrics, metrics,
scope,
kbnUrl, kbnUrl,
...props ...props
}) => { }) => {
@ -51,7 +51,7 @@ export const Index = ({
))} ))}
</EuiFlexGrid> </EuiFlexGrid>
<EuiSpacer size="m"/> <EuiSpacer size="m"/>
<ShardAllocation scope={scope} kbnUrl={kbnUrl} type="index" /> <ShardAllocation scope={scope} {...props} kbnUrl={kbnUrl} type="index" />
</EuiPageContent> </EuiPageContent>
</EuiPageBody> </EuiPageBody>
</EuiPage> </EuiPage>

View file

@ -21,6 +21,10 @@ function DetailStatusUI({ stats, intl }) {
const metrics = [ const metrics = [
{ {
label: intl.formatMessage({
id: 'xpack.monitoring.kibana.detailStatus.transportAddressLabel',
defaultMessage: 'Transport Address'
}),
value: transportAddress, value: transportAddress,
'data-test-subj': 'transportAddress' 'data-test-subj': 'transportAddress'
}, },

View file

@ -22,6 +22,9 @@ function DetailStatusUi({ stats, intl }) {
const firstMetrics = [ const firstMetrics = [
{ {
label: intl.formatMessage({
id: 'xpack.monitoring.logstash.detailStatus.transportAddressLabel', defaultMessage: 'Transport Address'
}),
value: httpAddress, value: httpAddress,
'data-test-subj': 'httpAddress' 'data-test-subj': 'httpAddress'
}, },

View file

@ -7,7 +7,6 @@
import './main'; import './main';
import './chart'; import './chart';
import './sparkline'; import './sparkline';
import './cluster/overview';
import './cluster/listing'; import './cluster/listing';
import './elasticsearch/cluster_status'; import './elasticsearch/cluster_status';
import './elasticsearch/index_summary'; import './elasticsearch/index_summary';

View file

@ -1,40 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import { Overview } from 'plugins/monitoring/components/cluster/overview';
import { uiModules } from 'ui/modules';
import { I18nProvider } from '@kbn/i18n/react';
const uiModule = uiModules.get('monitoring/directives', []);
uiModule.directive('monitoringClusterOverview', (kbnUrl, showLicenseExpiration) => {
return {
restrict: 'E',
scope: { cluster: '=' },
link(scope, $el) {
const changeUrl = target => {
scope.$evalAsync(() => {
kbnUrl.changePath(target);
});
};
scope.$watch('cluster', cluster => {
ReactDOM.render((
<I18nProvider>
<Overview
cluster={cluster}
changeUrl={changeUrl}
showLicenseExpiration={showLicenseExpiration}
/>
</I18nProvider>
), $el[0]);
});
}
};
});

View file

@ -253,7 +253,7 @@
</div> </div>
<div ng-if="monitoringMain.inOverview" class="kuiLocalTabs" role="navigation"> <div ng-if="monitoringMain.inOverview" class="kuiLocalTabs" role="navigation">
<a class="kuiLocalTab" data-test-subj="clusterName">{{ cluster.cluster_name }}</a> <a class="kuiLocalTab" data-test-subj="clusterName">{{ pageData.cluster_name }}</a>
</div> </div>
<div ng-if="monitoringMain.inAlerts" class="kuiLocalTabs" role="navigation"> <div ng-if="monitoringMain.inAlerts" class="kuiLocalTabs" role="navigation">

View file

@ -101,6 +101,8 @@ uiModule.directive('monitoringMain', (breadcrumbs, license, kbnUrl, config) => {
clusterName: get(scope, 'cluster.cluster_name') clusterName: get(scope, 'cluster.cluster_name')
}); });
attributes.$observe('instance', instance => controller.instance = instance);
attributes.$observe('resolver', resolver => controller.resolver = resolver);
} }
}; };
}); });

View file

@ -69,6 +69,25 @@ describe('MonitoringViewBaseController', function () {
expect(executorService.start.calledOnce).to.be(true); expect(executorService.start.calledOnce).to.be(true);
}); });
it('does not allow for a new request if one is inflight', done => {
let counter = 0;
const opts = {
title: 'testo',
getPageData: () => Promise.resolve(++counter),
$injector,
$scope
};
const ctrl = new MonitoringViewBaseController(opts);
Promise.all([
ctrl.updateData(),
ctrl.updateData(),
]).then(() => {
expect(counter).to.be(1);
done();
});
});
describe('time filter', () => { describe('time filter', () => {
it('enables timepicker and auto refresh #1', () => { it('enables timepicker and auto refresh #1', () => {
expect(timefilter.isTimeRangeSelectorEnabled).to.be(true); expect(timefilter.isTimeRangeSelectorEnabled).to.be(true);

View file

@ -75,7 +75,7 @@ export class MonitoringViewBaseController {
titleService($scope.cluster, title); titleService($scope.cluster, title);
this.data = { ...defaultData }; $scope.pageData = this.data = { ...defaultData };
this._isDataInitialized = false; this._isDataInitialized = false;
this.reactNodeId = reactNodeId; this.reactNodeId = reactNodeId;
@ -96,12 +96,22 @@ export class MonitoringViewBaseController {
timefilter.enableAutoRefreshSelector(); timefilter.enableAutoRefreshSelector();
} }
this.updateDataPromise = null;
this.updateData = () => { this.updateData = () => {
if (this.updateDataPromise) {
// Do not sent another request if one is inflight
// See https://github.com/elastic/kibana/issues/24082
return this.updateDataPromise;
}
const _api = apiUrlFn ? apiUrlFn() : api; const _api = apiUrlFn ? apiUrlFn() : api;
return _getPageData($injector, _api) return this.updateDataPromise = _getPageData($injector, _api)
.then(pageData => { .then(pageData => {
this._isDataInitialized = true; // render will replace loading screen with the react component this._isDataInitialized = true; // render will replace loading screen with the react component
this.data = pageData; // update the view's data with the fetch result $scope.pageData = this.data = pageData; // update the view's data with the fetch result
this.updateDataPromise = null;
})
.catch(() => {
this.updateDataPromise = null;
}); });
}; };
this.updateData(); this.updateData();
@ -126,6 +136,8 @@ export class MonitoringViewBaseController {
mode: 'absolute' mode: 'absolute'
}); });
}; };
this.setTitle = title => titleService($scope.cluster, title);
} }
renderReact(component) { renderReact(component) {

View file

@ -1,3 +1,3 @@
<monitoring-main name="overview" data-test-subj="clusterOverviewContainer"> <monitoring-main name="overview" data-test-subj="clusterOverviewContainer">
<monitoring-cluster-overview cluster="cluster"></monitoring-cluster-overview> <div id="monitoringClusterOverviewApp"></div>
</monitoring-main> </monitoring-main>

View file

@ -3,11 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import React from 'react';
import uiRoutes from 'ui/routes'; import uiRoutes from 'ui/routes';
import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
import template from './index.html'; import template from './index.html';
import { timefilter } from 'ui/timefilter'; import { MonitoringViewBaseController } from '../../';
import { Overview } from 'plugins/monitoring/components/cluster/overview';
import { I18nProvider } from '@kbn/i18n/react';
uiRoutes.when('/overview', { uiRoutes.when('/overview', {
template, template,
@ -21,28 +23,40 @@ uiRoutes.when('/overview', {
return monitoringClusters(globalState.cluster_uuid, globalState.ccs); return monitoringClusters(globalState.cluster_uuid, globalState.ccs);
} }
}, },
controller($injector, $scope, i18n) { controller: class extends MonitoringViewBaseController {
timefilter.enableTimeRangeSelector(); constructor($injector, $scope, i18n) {
timefilter.enableAutoRefreshSelector(); const kbnUrl = $injector.get('kbnUrl');
const monitoringClusters = $injector.get('monitoringClusters');
const globalState = $injector.get('globalState');
const $route = $injector.get('$route'); super({
$scope.cluster = $route.current.locals.cluster; title: i18n('xpack.monitoring.cluster.overviewTitle', {
defaultMessage: 'Overview'
}),
defaultData: {},
getPageData: () => monitoringClusters(globalState.cluster_uuid, globalState.ccs),
reactNodeId: 'monitoringClusterOverviewApp',
$scope,
$injector
});
const title = $injector.get('title'); const changeUrl = target => {
title($scope.cluster, i18n('xpack.monitoring.cluster.overviewTitle', { defaultMessage: 'Overview' })); $scope.$evalAsync(() => {
kbnUrl.changePath(target);
});
};
const $executor = $injector.get('$executor'); $scope.$watch(() => this.data, data => {
const monitoringClusters = $injector.get('monitoringClusters'); this.renderReact(
const globalState = $injector.get('globalState'); <I18nProvider>
$executor.register({ <Overview
execute: () => monitoringClusters(globalState.cluster_uuid, globalState.ccs), cluster={data}
handleResponse(cluster) { changeUrl={changeUrl}
$scope.cluster = cluster; showLicenseExpiration={true}
} />
}); </I18nProvider>
);
$executor.start($scope); });
}
$scope.$on('$destroy', $executor.destroy);
} }
}); });

View file

@ -1,7 +1,7 @@
<monitoring-main <monitoring-main
product="elasticsearch" name="indices" product="elasticsearch" name="indices"
instance="{{ indexName }}" instance="{{ monitoringElasticsearchAdvancedIndexApp.indexName }}"
resolver="{{ indexName }}" resolver="{{ monitoringElasticsearchAdvancedIndexApp.indexName }}"
page="advanced" page="advanced"
> >
<div id="monitoringElasticsearchAdvancedIndexApp"></div> <div id="monitoringElasticsearchAdvancedIndexApp"></div>

View file

@ -8,8 +8,6 @@
* Controller for Advanced Index Detail * Controller for Advanced Index Detail
*/ */
import React from 'react'; import React from 'react';
import { render } from 'react-dom';
import { find } from 'lodash';
import uiRoutes from 'ui/routes'; import uiRoutes from 'ui/routes';
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
@ -17,7 +15,7 @@ import template from './index.html';
import { timefilter } from 'ui/timefilter'; import { timefilter } from 'ui/timefilter';
import { AdvancedIndex } from '../../../../components/elasticsearch/index/advanced'; import { AdvancedIndex } from '../../../../components/elasticsearch/index/advanced';
import { I18nProvider } from '@kbn/i18n/react'; import { I18nProvider } from '@kbn/i18n/react';
import moment from 'moment'; import { MonitoringViewBaseController } from '../../../base_controller';
function getPageData($injector) { function getPageData($injector) {
const globalState = $injector.get('globalState'); const globalState = $injector.get('globalState');
@ -51,57 +49,39 @@ uiRoutes.when('/elasticsearch/indices/:index/advanced', {
}, },
pageData: getPageData pageData: getPageData
}, },
controller($injector, $scope, i18n) { controllerAs: 'monitoringElasticsearchAdvancedIndexApp',
timefilter.enableTimeRangeSelector(); controller: class extends MonitoringViewBaseController {
timefilter.enableAutoRefreshSelector(); constructor($injector, $scope, i18n) {
const $route = $injector.get('$route');
const indexName = $route.current.params.index;
const $route = $injector.get('$route'); super({
const globalState = $injector.get('globalState'); title: i18n('xpack.monitoring.elasticsearch.indices.advanced.routeTitle', {
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid }); defaultMessage: 'Elasticsearch - Indices - {indexName} - Advanced',
$scope.indexName = $route.current.params.index; values: {
$scope.pageData = $route.current.locals.pageData; indexName,
}
}),
defaultData: {},
getPageData,
reactNodeId: 'monitoringElasticsearchAdvancedIndexApp',
$scope,
$injector
});
const title = $injector.get('title'); this.indexName = indexName;
const routeTitle = i18n('xpack.monitoring.elasticsearch.indices.advanced.routeTitle', {
defaultMessage: 'Elasticsearch - Indices - {indexName} - Advanced',
values: {
indexName: $scope.indexName
}
});
title($scope.cluster, routeTitle); $scope.$watch(() => this.data, data => {
this.renderReact(
const $executor = $injector.get('$executor'); <I18nProvider>
$executor.register({ <AdvancedIndex
execute: () => getPageData($injector), indexSummary={data.indexSummary}
handleResponse: (response) => $scope.pageData = response metrics={data.metrics}
}); onBrush={this.onBrush}
/>
$executor.start($scope); </I18nProvider>
);
$scope.$on('$destroy', $executor.destroy);
function onBrush({ xaxis }) {
timefilter.setTime({
from: moment(xaxis.from),
to: moment(xaxis.to),
mode: 'absolute',
}); });
} }
this.renderReact = () => {
render(
<I18nProvider>
<AdvancedIndex
indexSummary={$scope.pageData.indexSummary}
metrics={$scope.pageData.metrics}
onBrush={onBrush}
/>
</I18nProvider>,
document.getElementById('monitoringElasticsearchAdvancedIndexApp')
);
};
$scope.$watch('pageData', this.renderReact);
} }
}); });

View file

@ -1,8 +1,8 @@
<monitoring-main <monitoring-main
product="elasticsearch" product="elasticsearch"
name="indices" name="indices"
instance="{{ indexName }}" instance="{{ monitoringElasticsearchIndexApp.indexName }}"
resolver="{{ indexName }}" resolver="{{ monitoringElasticsearchIndexApp.indexName }}"
page="overview" page="overview"
> >
<div id="monitoringElasticsearchIndexApp"></div> <div id="monitoringElasticsearchIndexApp"></div>

View file

@ -8,9 +8,6 @@
* Controller for single index detail * Controller for single index detail
*/ */
import React from 'react'; import React from 'react';
import { render } from 'react-dom';
import { find } from 'lodash';
import moment from 'moment';
import uiRoutes from 'ui/routes'; import uiRoutes from 'ui/routes';
import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
@ -20,6 +17,7 @@ import { I18nProvider } from '@kbn/i18n/react';
import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels'; import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels';
import { indicesByNodes } from '../../../components/elasticsearch/shard_allocation/transformers/indices_by_nodes'; import { indicesByNodes } from '../../../components/elasticsearch/shard_allocation/transformers/indices_by_nodes';
import { Index } from '../../../components/elasticsearch/index/index'; import { Index } from '../../../components/elasticsearch/index/index';
import { MonitoringViewBaseController } from '../../base_controller';
function getPageData($injector) { function getPageData($injector) {
const $http = $injector.get('$http'); const $http = $injector.get('$http');
@ -53,69 +51,55 @@ uiRoutes.when('/elasticsearch/indices/:index', {
}, },
pageData: getPageData pageData: getPageData
}, },
controller($injector, $scope, i18n) { controllerAs: 'monitoringElasticsearchIndexApp',
timefilter.enableTimeRangeSelector(); controller: class extends MonitoringViewBaseController {
timefilter.enableAutoRefreshSelector(); constructor($injector, $scope, i18n) {
const $route = $injector.get('$route');
const kbnUrl = $injector.get('kbnUrl');
const indexName = $route.current.params.index;
const $route = $injector.get('$route'); super({
const kbnUrl = $injector.get('kbnUrl'); title: i18n('xpack.monitoring.elasticsearch.indices.overview.routeTitle', {
const globalState = $injector.get('globalState'); defaultMessage: 'Elasticsearch - Indices - {indexName} - Overview',
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid }); values: {
$scope.pageData = $route.current.locals.pageData; indexName,
$scope.indexName = $route.current.params.index; }
}),
defaultData: {},
getPageData,
reactNodeId: 'monitoringElasticsearchIndexApp',
$scope,
$injector
});
const title = $injector.get('title'); this.indexName = indexName;
const routeTitle = i18n('xpack.monitoring.elasticsearch.indices.overview.routeTitle', { const transformer = indicesByNodes();
defaultMessage: 'Elasticsearch - Indices - {indexName} - Overview',
values: {
indexName: $scope.indexName
}
});
title($scope.cluster, routeTitle); $scope.$watch(() => this.data, data => {
if (!data || !data.shards) {
return;
}
const $executor = $injector.get('$executor'); const shards = data.shards;
$executor.register({ data.totalCount = shards.length;
execute: () => getPageData($injector), data.showing = transformer(shards, data.nodes);
handleResponse: (response) => $scope.pageData = response if (shards.some((shard) => shard.state === 'UNASSIGNED')) {
}); data.labels = labels.indexWithUnassigned;
} else {
data.labels = labels.index;
}
$executor.start($scope); this.renderReact(
<I18nProvider>
$scope.$on('$destroy', $executor.destroy); <Index
scope={$scope}
function onBrush({ xaxis }) { kbnUrl={kbnUrl}
timefilter.setTime({ onBrush={this.onBrush}
from: moment(xaxis.from), {...data}
to: moment(xaxis.to), />
mode: 'absolute', </I18nProvider>
);
}); });
} }
const transformer = indicesByNodes();
this.renderReact = () => {
const shards = $scope.pageData.shards;
$scope.totalCount = shards.length;
$scope.showing = transformer(shards, $scope.pageData.nodes);
if (shards.some((shard) => shard.state === 'UNASSIGNED')) {
$scope.labels = labels.indexWithUnassigned;
} else {
$scope.labels = labels.index;
}
render(
<I18nProvider>
<Index
scope={$scope}
kbnUrl={kbnUrl}
onBrush={onBrush}
{...$scope.pageData}
/>
</I18nProvider>,
document.getElementById('monitoringElasticsearchIndexApp')
);
};
$scope.$watch('pageData', this.renderReact);
} }
}); });

View file

@ -8,8 +8,6 @@
* Controller for Advanced Node Detail * Controller for Advanced Node Detail
*/ */
import React from 'react'; import React from 'react';
import { render } from 'react-dom';
import { find } from 'lodash';
import uiRoutes from 'ui/routes'; import uiRoutes from 'ui/routes';
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
@ -17,7 +15,7 @@ import template from './index.html';
import { timefilter } from 'ui/timefilter'; import { timefilter } from 'ui/timefilter';
import { I18nProvider } from '@kbn/i18n/react'; import { I18nProvider } from '@kbn/i18n/react';
import { AdvancedNode } from '../../../../components/elasticsearch/node/advanced'; import { AdvancedNode } from '../../../../components/elasticsearch/node/advanced';
import moment from 'moment'; import { MonitoringViewBaseController } from '../../../base_controller';
function getPageData($injector) { function getPageData($injector) {
const $http = $injector.get('$http'); const $http = $injector.get('$http');
@ -51,58 +49,38 @@ uiRoutes.when('/elasticsearch/nodes/:node/advanced', {
}, },
pageData: getPageData pageData: getPageData
}, },
controller($injector, $scope, i18n) { controller: class extends MonitoringViewBaseController {
timefilter.enableTimeRangeSelector(); constructor($injector, $scope, i18n) {
timefilter.enableAutoRefreshSelector(); super({
defaultData: {},
getPageData,
reactNodeId: 'monitoringElasticsearchAdvancedNodeApp',
$scope,
$injector
});
const $route = $injector.get('$route'); $scope.$watch(() => this.data, data => {
const globalState = $injector.get('globalState'); if (!data || !data.nodeSummary) {
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid }); return;
$scope.pageData = $route.current.locals.pageData; }
const title = $injector.get('title'); this.setTitle(i18n('xpack.monitoring.elasticsearch.node.advanced.routeTitle', {
const routeTitle = i18n('xpack.monitoring.elasticsearch.node.advanced.routeTitle', { defaultMessage: 'Elasticsearch - Nodes - {nodeSummaryName} - Advanced',
defaultMessage: 'Elasticsearch - Nodes - {nodeSummaryName} - Advanced', values: {
values: { nodeSummaryName: data.nodeSummary.name
nodeSummaryName: $scope.pageData.nodeSummary.name }
} }));
});
title($scope.cluster, routeTitle); this.renderReact(
<I18nProvider>
const $executor = $injector.get('$executor'); <AdvancedNode
$executor.register({ nodeSummary={data.nodeSummary}
execute: () => getPageData($injector), metrics={data.metrics}
handleResponse: (response) => { onBrush={this.onBrush}
$scope.pageData = response; />
} </I18nProvider>
}); );
$executor.start($scope);
$scope.$on('$destroy', $executor.destroy);
function onBrush({ xaxis }) {
timefilter.setTime({
from: moment(xaxis.from),
to: moment(xaxis.to),
mode: 'absolute',
}); });
} }
this.renderReact = () => {
render(
<I18nProvider>
<AdvancedNode
nodeSummary={$scope.pageData.nodeSummary}
metrics={$scope.pageData.metrics}
onBrush={onBrush}
/>
</I18nProvider>,
document.getElementById('monitoringElasticsearchAdvancedNodeApp')
);
};
$scope.$watch('pageData', this.renderReact);
} }
}); });

View file

@ -1,11 +1,11 @@
<monitoring-main <monitoring-main
product="elasticsearch" product="elasticsearch"
name="nodes" name="nodes"
instance="{{ pageData.nodeSummary.name }}" instance="{{ monitoringElasticsearchNodeApp.nodeName }}"
resolver="{{ pageData.nodeSummary.resolver }}" resolver="{{ monitoringElasticsearchNodeApp.data.nodeSummary.resolver }}"
page="overview" page="overview"
tab-icon-class="{{ pageData.nodeSummary.nodeTypeClass }}" tab-icon-class="{{ monitoringElasticsearchNodeApp.data.nodeSummary.nodeTypeClass }}"
tab-icon-class="{{ pageData.nodeSummary.nodeTypeLabel }}" tab-icon-class="{{ monitoringElasticsearchNodeApp.data.nodeSummary.nodeTypeLabel }}"
> >
<div id="monitoringElasticsearchNodeApp"></div> <div id="monitoringElasticsearchNodeApp"></div>
</monitoring-main> </monitoring-main>

View file

@ -8,18 +8,16 @@
* Controller for Node Detail * Controller for Node Detail
*/ */
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { partial } from 'lodash';
import { find, partial } from 'lodash';
import uiRoutes from 'ui/routes'; import uiRoutes from 'ui/routes';
import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
import { getPageData } from './get_page_data'; import { getPageData } from './get_page_data';
import template from './index.html'; import template from './index.html';
import { timefilter } from 'ui/timefilter';
import { Node } from '../../../components/elasticsearch/node/node'; import { Node } from '../../../components/elasticsearch/node/node';
import { I18nProvider } from '@kbn/i18n/react'; import { I18nProvider } from '@kbn/i18n/react';
import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels'; import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels';
import { nodesByIndices } from '../../../components/elasticsearch/shard_allocation/transformers/nodes_by_indices'; import { nodesByIndices } from '../../../components/elasticsearch/shard_allocation/transformers/nodes_by_indices';
import moment from 'moment'; import { MonitoringViewBaseController } from '../../base_controller';
uiRoutes.when('/elasticsearch/nodes/:node', { uiRoutes.when('/elasticsearch/nodes/:node', {
template, template,
@ -30,78 +28,63 @@ uiRoutes.when('/elasticsearch/nodes/:node', {
}, },
pageData: getPageData pageData: getPageData
}, },
controller($injector, $scope, i18n) { controllerAs: 'monitoringElasticsearchNodeApp',
timefilter.enableTimeRangeSelector(); controller: class extends MonitoringViewBaseController {
timefilter.enableAutoRefreshSelector(); constructor($injector, $scope, i18n) {
const $route = $injector.get('$route');
const kbnUrl = $injector.get('kbnUrl');
const nodeName = $route.current.params.node;
const $route = $injector.get('$route'); super({
const kbnUrl = $injector.get('kbnUrl'); title: i18n('xpack.monitoring.elasticsearch.node.overview.routeTitle', {
const globalState = $injector.get('globalState'); defaultMessage: 'Elasticsearch - Nodes - {nodeName} - Overview',
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid }); values: {
$scope.pageData = $route.current.locals.pageData; nodeName,
}
}),
defaultData: {},
getPageData,
reactNodeId: 'monitoringElasticsearchNodeApp',
$scope,
$injector
});
const title = $injector.get('title'); this.nodeName = nodeName;
const routeTitle = i18n('xpack.monitoring.elasticsearch.node.overview.routeTitle', {
defaultMessage: 'Elasticsearch - Nodes - {nodeSummaryName} - Overview',
values: {
nodeSummaryName: $scope.pageData.nodeSummary.name
}
});
title($scope.cluster, routeTitle); const features = $injector.get('features');
const callPageData = partial(getPageData, $injector);
// show/hide system indices in shard allocation view
$scope.showSystemIndices = features.isEnabled('showSystemIndices', false);
$scope.toggleShowSystemIndices = (isChecked) => {
$scope.showSystemIndices = isChecked;
// preserve setting in localStorage
features.update('showSystemIndices', isChecked);
// update the page
callPageData().then(data => this.data = data);
};
const features = $injector.get('features'); const transformer = nodesByIndices();
const callPageData = partial(getPageData, $injector); $scope.$watch(() => this.data, data => {
// show/hide system indices in shard allocation view if (!data || !data.shards) {
$scope.showSystemIndices = features.isEnabled('showSystemIndices', false); return;
$scope.toggleShowSystemIndices = (isChecked) => { }
$scope.showSystemIndices = isChecked;
// preserve setting in localStorage
features.update('showSystemIndices', isChecked);
// update the page
callPageData().then((pageData) => $scope.pageData = pageData);
};
const $executor = $injector.get('$executor'); const shards = data.shards;
$executor.register({ $scope.totalCount = shards.length;
execute: () => callPageData(), $scope.showing = transformer(shards, data.nodes);
handleResponse: (response) => { $scope.labels = labels.node;
$scope.pageData = response;
}
});
$executor.start($scope); this.renderReact(
<I18nProvider>
$scope.$on('$destroy', $executor.destroy); <Node
scope={$scope}
function onBrush({ xaxis }) { kbnUrl={kbnUrl}
timefilter.setTime({ onBrush={this.onBrush}
from: moment(xaxis.from), {...data}
to: moment(xaxis.to), />
mode: 'absolute', </I18nProvider>
);
}); });
} }
const transformer = nodesByIndices();
this.renderReact = () => {
const shards = $scope.pageData.shards;
$scope.totalCount = shards.length;
$scope.showing = transformer(shards, $scope.pageData.nodes);
$scope.labels = labels.node;
render(
<I18nProvider>
<Node
scope={$scope}
kbnUrl={kbnUrl}
onBrush={onBrush}
{...$scope.pageData}
/>
</I18nProvider>,
document.getElementById('monitoringElasticsearchNodeApp')
);
};
$scope.$watch('pageData', this.renderReact);
} }
}); });

View file

@ -8,8 +8,7 @@
* Kibana Instance * Kibana Instance
*/ */
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { get } from 'lodash';
import { get, find } from 'lodash';
import uiRoutes from'ui/routes'; import uiRoutes from'ui/routes';
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
@ -19,6 +18,7 @@ import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiFlexGroup, EuiFlexI
import { MonitoringTimeseriesContainer } from '../../../components/chart'; import { MonitoringTimeseriesContainer } from '../../../components/chart';
import { DetailStatus } from 'plugins/monitoring/components/kibana/detail_status'; import { DetailStatus } from 'plugins/monitoring/components/kibana/detail_status';
import { I18nProvider } from '@kbn/i18n/react'; import { I18nProvider } from '@kbn/i18n/react';
import { MonitoringViewBaseController } from '../../base_controller';
function getPageData($injector) { function getPageData($injector) {
const $http = $injector.get('$http'); const $http = $injector.get('$http');
@ -51,87 +51,74 @@ uiRoutes.when('/kibana/instances/:uuid', {
}, },
pageData: getPageData pageData: getPageData
}, },
controller($injector, $scope) { controllerAs: 'monitoringKibanaInstanceApp',
timefilter.enableTimeRangeSelector(); controller: class extends MonitoringViewBaseController {
timefilter.enableAutoRefreshSelector(); constructor($injector, $scope) {
super({
title: `Kibana - ${get($scope.pageData, 'kibanaSummary.name')}`,
defaultData: {},
getPageData,
reactNodeId: 'monitoringKibanaInstanceApp',
$scope,
$injector
});
const $route = $injector.get('$route'); $scope.$watch(() => this.data, data => {
const globalState = $injector.get('globalState'); if (!data || !data.metrics) {
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid }); return;
$scope.pageData = $route.current.locals.pageData; }
const title = $injector.get('title'); this.setTitle(`Kibana - ${get(data, 'kibanaSummary.name')}`);
title($scope.cluster, `Kibana - ${get($scope.pageData, 'kibanaSummary.name')}`);
const $executor = $injector.get('$executor'); this.renderReact(
$executor.register({ <I18nProvider>
execute: () => getPageData($injector), <EuiPage>
handleResponse: (response) => $scope.pageData = response <EuiPageBody>
}); <EuiPageContent>
<DetailStatus stats={data.kibanaSummary} />
$executor.start($scope); <EuiSpacer size="m"/>
<EuiFlexGroup>
$scope.$on('$destroy', $executor.destroy); <EuiFlexItem grow={true}>
<MonitoringTimeseriesContainer
$scope.$watch('pageData', renderReact); series={data.metrics.kibana_requests}
renderReact(); />
</EuiFlexItem>
function renderReact() { <EuiFlexItem grow={true}>
const app = document.getElementById('monitoringKibanaInstanceApp'); <MonitoringTimeseriesContainer
if (!app) { series={data.metrics.kibana_response_times}
return; />
} </EuiFlexItem>
</EuiFlexGroup>
const overviewPage = ( <EuiFlexGroup>
<I18nProvider> <EuiFlexItem grow={true}>
<EuiPage> <MonitoringTimeseriesContainer
<EuiPageBody> series={data.metrics.kibana_memory}
<EuiPageContent> />
<DetailStatus stats={$scope.pageData.kibanaSummary} /> </EuiFlexItem>
<EuiSpacer size="m"/> <EuiFlexItem grow={true}>
<EuiFlexGroup> <MonitoringTimeseriesContainer
<EuiFlexItem grow={true}> series={data.metrics.kibana_average_concurrent_connections}
<MonitoringTimeseriesContainer />
series={$scope.pageData.metrics.kibana_requests} </EuiFlexItem>
/> </EuiFlexGroup>
</EuiFlexItem> <EuiFlexGroup>
<EuiFlexItem grow={true}> <EuiFlexItem grow={true}>
<MonitoringTimeseriesContainer <MonitoringTimeseriesContainer
series={$scope.pageData.metrics.kibana_response_times} series={data.metrics.kibana_os_load}
/> />
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> <EuiFlexItem grow={true}>
<EuiFlexGroup> <MonitoringTimeseriesContainer
<EuiFlexItem grow={true}> series={data.metrics.kibana_process_delay}
<MonitoringTimeseriesContainer />
series={$scope.pageData.metrics.kibana_memory} </EuiFlexItem>
/> </EuiFlexGroup>
</EuiFlexItem> </EuiPageContent>
<EuiFlexItem grow={true}> </EuiPageBody>
<MonitoringTimeseriesContainer </EuiPage>
series={$scope.pageData.metrics.kibana_average_concurrent_connections} </I18nProvider>
/> );
</EuiFlexItem> });
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem grow={true}>
<MonitoringTimeseriesContainer
series={$scope.pageData.metrics.kibana_os_load}
/>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<MonitoringTimeseriesContainer
series={$scope.pageData.metrics.kibana_process_delay}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</I18nProvider>
);
render(overviewPage, app);
} }
} }
}); });

View file

@ -8,8 +8,6 @@
* Kibana Overview * Kibana Overview
*/ */
import React from 'react'; import React from 'react';
import { render } from 'react-dom';
import { find } from 'lodash';
import uiRoutes from'ui/routes'; import uiRoutes from'ui/routes';
import { MonitoringTimeseriesContainer } from '../../../components/chart'; import { MonitoringTimeseriesContainer } from '../../../components/chart';
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
@ -19,6 +17,7 @@ import { timefilter } from 'ui/timefilter';
import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { ClusterStatus } from '../../../components/kibana/cluster_status'; import { ClusterStatus } from '../../../components/kibana/cluster_status';
import { I18nProvider } from '@kbn/i18n/react'; import { I18nProvider } from '@kbn/i18n/react';
import { MonitoringViewBaseController } from '../../base_controller';
function getPageData($injector) { function getPageData($injector) {
const $http = $injector.get('$http'); const $http = $injector.get('$http');
@ -50,64 +49,49 @@ uiRoutes.when('/kibana', {
}, },
pageData: getPageData pageData: getPageData
}, },
controller($injector, $scope) { controllerAs: 'monitoringKibanaOverviewApp',
timefilter.enableTimeRangeSelector(); controller: class extends MonitoringViewBaseController {
timefilter.enableAutoRefreshSelector(); constructor($injector, $scope) {
super({
title: `Kibana`,
defaultData: {},
getPageData,
reactNodeId: 'monitoringKibanaOverviewApp',
$scope,
$injector
});
const $route = $injector.get('$route'); $scope.$watch(() => this.data, data => {
const globalState = $injector.get('globalState'); if (!data || !data.clusterStatus) {
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid }); return;
$scope.pageData = $route.current.locals.pageData; }
const title = $injector.get('title'); this.renderReact(
title($scope.cluster, 'Kibana'); <I18nProvider>
<EuiPage>
<EuiPageBody>
<EuiPageContent>
<ClusterStatus stats={data.clusterStatus} />
<EuiSpacer size="m"/>
<EuiFlexGroup>
<EuiFlexItem grow={true}>
<MonitoringTimeseriesContainer
series={data.metrics.kibana_cluster_requests}
/>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<MonitoringTimeseriesContainer
series={data.metrics.kibana_cluster_response_times}
/>
</EuiFlexItem>
</EuiFlexGroup>
const $executor = $injector.get('$executor'); </EuiPageContent>
$executor.register({ </EuiPageBody>
execute: () => getPageData($injector), </EuiPage>
handleResponse: (response) => $scope.pageData = response </I18nProvider>
}); );
});
$executor.start($scope);
$scope.$on('$destroy', $executor.destroy);
$scope.$watch('pageData', renderReact);
renderReact();
function renderReact() {
const app = document.getElementById('monitoringKibanaOverviewApp');
if (!app) {
return;
}
const overviewPage = (
<I18nProvider>
<EuiPage>
<EuiPageBody>
<EuiPageContent>
<ClusterStatus stats={$scope.pageData.clusterStatus} />
<EuiSpacer size="m"/>
<EuiFlexGroup>
<EuiFlexItem grow={true}>
<MonitoringTimeseriesContainer
series={$scope.pageData.metrics.kibana_cluster_requests}
/>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<MonitoringTimeseriesContainer
series={$scope.pageData.metrics.kibana_cluster_response_times}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</I18nProvider>
);
render(overviewPage, app);
} }
} }
}); });

View file

@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }) {
it('should have Instance Summary Status showing correct info', async () => { it('should have Instance Summary Status showing correct info', async () => {
expect(await instance.getSummary()).to.eql({ expect(await instance.getSummary()).to.eql({
transportAddress: 'tsullivan.local:5601', transportAddress: 'Transport Address:\ntsullivan.local:5601',
osFreeMemory: 'OS Free Memory:\n1.5 GB', osFreeMemory: 'OS Free Memory:\n1.5 GB',
version: 'Version:\n7.0.0-alpha1', version: 'Version:\n7.0.0-alpha1',
uptime: 'Uptime:\n3 minutes', uptime: 'Uptime:\n3 minutes',