Implement inspector for Saved Searches (#22376)

* Implement inspector for Saved Searches

* add inspect top nav to discover app

* add functional test, add support for empty results

* clean up functional test names

* create inspector request before processing response
This commit is contained in:
Nathan Reese 2018-08-29 12:31:36 -06:00 committed by GitHub
parent 57940f1648
commit 5fd01d913e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 152 additions and 1 deletions

View file

@ -55,6 +55,9 @@ import '../components/fetch_error';
import { getPainlessError } from './get_painless_error';
import { showShareContextMenu } from 'ui/share';
import { getUnhashableStatesProvider } from 'ui/state_management/state_hashing';
import { Inspector } from 'ui/inspector';
import { RequestAdapter } from 'ui/inspector/adapters';
import { getRequestInspectorStats, getResponseInspectorStats } from 'ui/courier/utils/courier_inspector_utils';
const app = uiModules.get('apps/discover', [
'kibana/notify',
@ -159,6 +162,9 @@ function discoverController(
location: 'Discover'
});
const getUnhashableStates = Private(getUnhashableStatesProvider);
const inspectorAdapters = {
requests: new RequestAdapter()
};
$scope.getDocLink = getDocLink;
$scope.intervalOptions = intervalOptions;
@ -201,6 +207,15 @@ function discoverController(
objectType: 'search',
});
}
}, {
key: 'inspect',
description: 'Open Inspector for search',
testId: 'openInspectorButton',
run() {
Inspector.open(inspectorAdapters, {
title: savedSearch.title
});
}
}];
// the actual courier.SearchSource
@ -554,9 +569,38 @@ function discoverController(
segmented.setSortFn(sortFn);
segmented.setSize($scope.opts.sampleSize);
let inspectorRequests = [];
function logResponseInInspector(resp) {
if (inspectorRequests.length > 0) {
const inspectorRequest = inspectorRequests.shift();
inspectorRequest
.stats(getResponseInspectorStats($scope.searchSource, resp))
.ok({ json: resp });
}
}
// triggered when the status updated
segmented.on('status', function (status) {
$scope.fetchStatus = status;
if (status.complete === 0) {
// starting new segmented search request
inspectorAdapters.requests.reset();
inspectorRequests = [];
}
if (status.remaining > 0) {
const inspectorRequest = inspectorAdapters.requests.start(
`Segment ${$scope.fetchStatus.complete}`,
{
description: `This request queries Elasticsearch to fetch the data for the search.`,
});
inspectorRequest.stats(getRequestInspectorStats($scope.searchSource));
$scope.searchSource.getSearchRequestBody().then(body => {
inspectorRequest.json(body);
});
inspectorRequests.push(inspectorRequest);
}
});
segmented.on('first', function () {
@ -564,6 +608,7 @@ function discoverController(
});
segmented.on('segment', (resp) => {
logResponseInInspector(resp);
if (resp._shards.failed > 0) {
$scope.failures = _.union($scope.failures, resp._shards.failures);
$scope.failures = _.uniq($scope.failures, false, function (failure) {
@ -572,6 +617,10 @@ function discoverController(
}
});
segmented.on('emptySegment', function (resp) {
logResponseInInspector(resp);
});
segmented.on('mergedSegment', function (merged) {
$scope.mergedEsResp = merged;

View file

@ -22,6 +22,7 @@ import { Embeddable } from 'ui/embeddable';
import searchTemplate from './search_template.html';
import * as columnActions from 'ui/doc_table/actions/columns';
import { getTime } from 'ui/timefilter/get_time';
import { RequestAdapter } from 'ui/inspector/adapters';
export class SearchEmbeddable extends Embeddable {
constructor({ onEmbeddableStateChanged, savedSearch, editUrl, loader, $rootScope, $compile }) {
@ -38,6 +39,13 @@ export class SearchEmbeddable extends Embeddable {
this.$rootScope = $rootScope;
this.$compile = $compile;
this.customization = {};
this.inspectorAdaptors = {
requests: new RequestAdapter()
};
}
getInspectorAdapters() {
return this.inspectorAdaptors;
}
emitEmbeddableStateChange(embeddableState) {
@ -84,6 +92,7 @@ export class SearchEmbeddable extends Embeddable {
this.searchScope.description = this.savedSearch.description;
this.searchScope.searchSource = this.savedSearch.searchSource;
this.searchScope.inspectorAdapters = this.inspectorAdaptors;
const timeRangeSearchSource = this.searchScope.searchSource.create();
timeRangeSearchSource.setField('filter', () => {

View file

@ -13,5 +13,6 @@
on-move-column="moveColumn"
on-remove-column="removeColumn"
data-test-subj="embeddedSavedSearchDocTable"
inspector-adapters="inspectorAdapters"
>
</doc-table>

View file

@ -233,7 +233,7 @@ export function SegmentedSearchRequestProvider(Private, config) {
this.resp = _.omit(this._mergedResp, '_bucketIndex');
if (firstHits) this._handle.emit('first', seg);
if (gotHits) this._handle.emit('segment', seg);
gotHits ? this._handle.emit('segment', seg) : this._handle.emit('emptySegment', seg);
if (haveHits) this._handle.emit('mergedSegment', this.resp);
}

View file

@ -27,6 +27,7 @@ import './components/table_header';
import './components/table_row';
import { dispatchRenderComplete } from '../render_complete';
import { uiModules } from '../modules';
import { getRequestInspectorStats, getResponseInspectorStats } from '../courier/utils/courier_inspector_utils';
import { getLimitedSearchResultsMessage } from './doc_table_strings';
@ -49,6 +50,7 @@ uiModules.get('kibana')
onChangeSortOrder: '=?',
onMoveColumn: '=?',
onRemoveColumn: '=?',
inspectorAdapters: '=?',
},
link: function ($scope, $el) {
const notify = new Notifier();
@ -132,7 +134,26 @@ uiModules.get('kibana')
}
function startSearching() {
let inspectorRequest = undefined;
if (_.has($scope, 'inspectorAdapters.requests')) {
$scope.inspectorAdapters.requests.reset();
inspectorRequest = $scope.inspectorAdapters.requests.start('Data', {
description: `This request queries Elasticsearch to fetch the data for the search.`,
});
inspectorRequest.stats(getRequestInspectorStats($scope.searchSource));
$scope.searchSource.getSearchRequestBody().then(body => {
inspectorRequest.json(body);
});
}
$scope.searchSource.onResults()
.then(resp => {
if (inspectorRequest) {
inspectorRequest
.stats(getResponseInspectorStats($scope.searchSource, resp))
.ok({ json: resp });
}
return resp;
})
.then(onResults)
.catch(error => {
notify.error(error);

View file

@ -0,0 +1,70 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['common', 'header', 'visualize']);
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const STATS_ROW_NAME_INDEX = 0;
const STATS_ROW_VALUE_INDEX = 1;
function getHitCount(requestStats) {
const hitsCountStatsRow = requestStats.find((statsRow) => {
return statsRow[STATS_ROW_NAME_INDEX] === 'Hits';
});
return hitsCountStatsRow[STATS_ROW_VALUE_INDEX];
}
describe('inspect', () => {
before(async () => {
await esArchiver.loadIfNeeded('logstash_functional');
await esArchiver.load('discover');
// delete .kibana index and update configDoc
await kibanaServer.uiSettings.replace({
'dateFormat:tz': 'UTC',
'defaultIndex': 'logstash-*'
});
await PageObjects.common.navigateToApp('discover');
});
afterEach(async () => {
await PageObjects.visualize.closeInspector();
});
it('should display request stats with no results', async () => {
await PageObjects.visualize.openInspector();
const requestStats = await PageObjects.visualize.getInspectorTableData();
expect(getHitCount(requestStats)).to.be('0');
});
it('should display request stats with results', async () => {
await PageObjects.header.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000');
await PageObjects.visualize.openInspector();
const requestStats = await PageObjects.visualize.getInspectorTableData();
expect(getHitCount(requestStats)).to.be('14004');
});
});
}

View file

@ -37,5 +37,6 @@ export default function ({ getService, loadTestFile }) {
loadTestFile(require.resolve('./_sidebar'));
loadTestFile(require.resolve('./_source_filters'));
loadTestFile(require.resolve('./_large_string'));
loadTestFile(require.resolve('./_inspector'));
});
}