mirror of
https://github.com/valitydev/wazuh-kibana-app.git
synced 2024-11-06 09:55:18 +00:00
Adapt the app for Kibana 7.3.0
This commit is contained in:
parent
de0e46a714
commit
61ee711bd0
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "wazuh",
|
"name": "wazuh",
|
||||||
"version": "3.9.4",
|
"version": "3.9.4",
|
||||||
"revision": "0527",
|
"revision": "0528",
|
||||||
"code": "0527-0",
|
"code": "0528-0",
|
||||||
"kibana": {
|
"kibana": {
|
||||||
"version": "7.2.0"
|
"version": "7.3.0"
|
||||||
},
|
},
|
||||||
"description": "Wazuh app",
|
"description": "Wazuh app",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
@ -15,7 +15,7 @@ import { uiModules } from 'ui/modules';
|
|||||||
import discoverTemplate from '../templates/kibana-template/kibana-discover-template.html';
|
import discoverTemplate from '../templates/kibana-template/kibana-discover-template.html';
|
||||||
|
|
||||||
uiModules.get('app/wazuh', ['kibana/courier']).directive('kbnDis', [
|
uiModules.get('app/wazuh', ['kibana/courier']).directive('kbnDis', [
|
||||||
function() {
|
function () {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {},
|
scope: {},
|
||||||
@ -25,7 +25,7 @@ uiModules.get('app/wazuh', ['kibana/courier']).directive('kbnDis', [
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Added dependencies (from Kibana module)
|
// Added dependencies (from Kibana module)
|
||||||
import 'ui/pager';
|
import 'plugins/kibana/discover/doc_table/pager';
|
||||||
import 'ui/render_directive';
|
import 'ui/render_directive';
|
||||||
|
|
||||||
// Added from its index.js
|
// Added from its index.js
|
||||||
@ -40,10 +40,7 @@ import 'plugins/kibana/discover/controllers/discover';
|
|||||||
import 'ui/style_compile/style_compile';
|
import 'ui/style_compile/style_compile';
|
||||||
import 'ui/registry/doc_views';
|
import 'ui/registry/doc_views';
|
||||||
import 'plugins/kbn_doc_views/kbn_doc_views';
|
import 'plugins/kbn_doc_views/kbn_doc_views';
|
||||||
import 'ui/pager_control';
|
import 'plugins/kibana/discover/doc_table/pager_control';
|
||||||
import 'ui/pager';
|
|
||||||
|
|
||||||
import { data } from 'plugins/data';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -71,15 +68,17 @@ import {
|
|||||||
} from 'ui/courier';
|
} from 'ui/courier';
|
||||||
import { toastNotifications } from 'ui/notify';
|
import { toastNotifications } from 'ui/notify';
|
||||||
import { VisProvider } from 'ui/vis';
|
import { VisProvider } from 'ui/vis';
|
||||||
import { VislibSeriesResponseHandlerProvider } from 'ui/vis/response_handlers/vislib';
|
import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter';
|
||||||
import { DocTitleProvider } from 'ui/doc_title';
|
import { vislibSeriesResponseHandlerProvider } from 'ui/vis/response_handlers/vislib';
|
||||||
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
|
import { docTitle } from 'ui/doc_title';
|
||||||
import { intervalOptions } from 'ui/agg_types/buckets/_interval_options';
|
import { intervalOptions } from 'ui/agg_types/buckets/_interval_options';
|
||||||
import { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
|
import { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
|
||||||
import uiRoutes from 'ui/routes';
|
import uiRoutes from 'ui/routes';
|
||||||
|
|
||||||
import { StateProvider } from 'ui/state_management/state';
|
import { StateProvider } from 'ui/state_management/state';
|
||||||
import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query';
|
import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query';
|
||||||
import { FilterManagerProvider } from 'ui/filter_manager';
|
import { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
|
||||||
|
import { getFilterGenerator } from 'ui/filter_manager';
|
||||||
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
||||||
import { VisualizeLoaderProvider } from 'ui/visualize/loader/visualize_loader';
|
import { VisualizeLoaderProvider } from 'ui/visualize/loader/visualize_loader';
|
||||||
import { recentlyAccessed } from 'ui/persisted_log';
|
import { recentlyAccessed } from 'ui/persisted_log';
|
||||||
@ -104,15 +103,14 @@ import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_s
|
|||||||
const { getRootBreadcrumbs, getSavedSearchBreadcrumbs } = data.search.ui;
|
const { getRootBreadcrumbs, getSavedSearchBreadcrumbs } = data.search.ui;
|
||||||
import { buildVislibDimensions } from 'ui/visualize/loader/pipeline_helpers/build_pipeline';
|
import { buildVislibDimensions } from 'ui/visualize/loader/pipeline_helpers/build_pipeline';
|
||||||
import 'ui/capabilities/route_setup';
|
import 'ui/capabilities/route_setup';
|
||||||
|
|
||||||
import { defaultSearchStrategy } from 'ui/courier/search_strategy/default_search_strategy';
|
import { defaultSearchStrategy } from 'ui/courier/search_strategy/default_search_strategy';
|
||||||
|
import { data } from 'plugins/data/setup';
|
||||||
data.search.loadLegacyDirectives();
|
data.search.loadLegacyDirectives();
|
||||||
|
|
||||||
const fetchStatuses = {
|
const fetchStatuses = {
|
||||||
UNINITIALIZED: 'uninitialized',
|
UNINITIALIZED: 'uninitialized',
|
||||||
LOADING: 'loading',
|
LOADING: 'loading',
|
||||||
COMPLETE: 'complete'
|
COMPLETE: 'complete',
|
||||||
};
|
};
|
||||||
|
|
||||||
const app = uiModules.get('apps/discover', [
|
const app = uiModules.get('apps/discover', [
|
||||||
@ -123,7 +121,7 @@ const app = uiModules.get('apps/discover', [
|
|||||||
'app/wazuh'
|
'app/wazuh'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
app.directive('discoverAppW', function() {
|
app.directive('discoverAppW', function () {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
controllerAs: 'discoverApp',
|
controllerAs: 'discoverApp',
|
||||||
@ -138,7 +136,6 @@ function discoverController(
|
|||||||
$timeout,
|
$timeout,
|
||||||
$window,
|
$window,
|
||||||
AppState,
|
AppState,
|
||||||
Notifier,
|
|
||||||
Private,
|
Private,
|
||||||
Promise,
|
Promise,
|
||||||
config,
|
config,
|
||||||
@ -158,17 +155,13 @@ function discoverController(
|
|||||||
const visualizeLoader = Private(VisualizeLoaderProvider);
|
const visualizeLoader = Private(VisualizeLoaderProvider);
|
||||||
let visualizeHandler;
|
let visualizeHandler;
|
||||||
const Vis = Private(VisProvider);
|
const Vis = Private(VisProvider);
|
||||||
const docTitle = Private(DocTitleProvider);
|
const responseHandler = vislibSeriesResponseHandlerProvider().handler;
|
||||||
const queryFilter = Private(FilterBarQueryFilterProvider);
|
|
||||||
const responseHandler = Private(VislibSeriesResponseHandlerProvider).handler;
|
|
||||||
const filterManager = Private(FilterManagerProvider);
|
|
||||||
const notify = new Notifier({
|
|
||||||
location: 'Discover'
|
|
||||||
});
|
|
||||||
const getUnhashableStates = Private(getUnhashableStatesProvider);
|
const getUnhashableStates = Private(getUnhashableStatesProvider);
|
||||||
const shareContextMenuExtensions = Private(
|
const shareContextMenuExtensions = Private(ShareContextMenuExtensionsRegistryProvider);
|
||||||
ShareContextMenuExtensionsRegistryProvider
|
|
||||||
);
|
const queryFilter = Private(FilterBarQueryFilterProvider);
|
||||||
|
const filterGen = getFilterGenerator(queryFilter);
|
||||||
|
|
||||||
const inspectorAdapters = {
|
const inspectorAdapters = {
|
||||||
requests: new RequestAdapter()
|
requests: new RequestAdapter()
|
||||||
};
|
};
|
||||||
@ -186,7 +179,7 @@ function discoverController(
|
|||||||
$scope.fetchStatus = fetchStatuses.UNINITIALIZED;
|
$scope.fetchStatus = fetchStatuses.UNINITIALIZED;
|
||||||
$scope.refreshInterval = timefilter.getRefreshInterval();
|
$scope.refreshInterval = timefilter.getRefreshInterval();
|
||||||
|
|
||||||
$scope.intervalEnabled = function(interval) {
|
$scope.intervalEnabled = function (interval) {
|
||||||
return interval.val !== 'custom';
|
return interval.val !== 'custom';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -198,9 +191,9 @@ function discoverController(
|
|||||||
if (filterUpdateSubscription) filterUpdateSubscription.unsubscribe();
|
if (filterUpdateSubscription) filterUpdateSubscription.unsubscribe();
|
||||||
});
|
});
|
||||||
|
|
||||||
const $appStatus = ($scope.appStatus = this.appStatus = {
|
const $appStatus = $scope.appStatus = this.appStatus = {
|
||||||
dirty: !savedSearch.id
|
dirty: !savedSearch.id
|
||||||
});
|
};
|
||||||
|
|
||||||
// WAZUH MODIFIED
|
// WAZUH MODIFIED
|
||||||
$scope.topNavMenu = [];
|
$scope.topNavMenu = [];
|
||||||
@ -228,35 +221,26 @@ function discoverController(
|
|||||||
|
|
||||||
$scope.searchSource.setParent(timeRangeSearchSource);
|
$scope.searchSource.setParent(timeRangeSearchSource);
|
||||||
|
|
||||||
const pageTitleSuffix =
|
const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : '';
|
||||||
savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : '';
|
|
||||||
docTitle.change(`Wazuh${pageTitleSuffix}`);
|
docTitle.change(`Wazuh${pageTitleSuffix}`);
|
||||||
const discoverBreadcrumbsTitle = i18n.translate(
|
const discoverBreadcrumbsTitle = i18n.translate('kbn.discover.discoverBreadcrumbTitle', {
|
||||||
'kbn.discover.discoverBreadcrumbTitle',
|
defaultMessage: 'Wazuh',
|
||||||
{
|
});
|
||||||
defaultMessage: 'Wazuh'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (savedSearch.id && savedSearch.title) {
|
if (savedSearch.id && savedSearch.title) {
|
||||||
chrome.breadcrumbs.set([
|
chrome.breadcrumbs.set([{
|
||||||
{
|
text: discoverBreadcrumbsTitle,
|
||||||
text: discoverBreadcrumbsTitle,
|
href: '#/discover'
|
||||||
href: '#/discover'
|
}, { text: savedSearch.title }]);
|
||||||
},
|
|
||||||
{ text: savedSearch.title }
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
chrome.breadcrumbs.set([
|
chrome.breadcrumbs.set([{
|
||||||
{
|
text: discoverBreadcrumbsTitle,
|
||||||
text: discoverBreadcrumbsTitle
|
}]);
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let stateMonitor;
|
let stateMonitor;
|
||||||
|
|
||||||
const $state = ($scope.state = new AppState(getStateDefaults()));
|
const $state = $scope.state = new AppState(getStateDefaults());
|
||||||
|
|
||||||
$scope.filters = queryFilter.getFilters();
|
$scope.filters = queryFilter.getFilters();
|
||||||
$scope.screenTitle = savedSearch.title;
|
$scope.screenTitle = savedSearch.title;
|
||||||
@ -308,7 +292,7 @@ function discoverController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return await new Promise(resolve => {
|
return await new Promise(resolve => {
|
||||||
const unwatch = $scope.$watch('fetchStatus', newValue => {
|
const unwatch = $scope.$watch('fetchStatus', (newValue) => {
|
||||||
if (newValue === fetchStatuses.COMPLETE) {
|
if (newValue === fetchStatuses.COMPLETE) {
|
||||||
unwatch();
|
unwatch();
|
||||||
resolve($scope.fieldCounts);
|
resolve($scope.fieldCounts);
|
||||||
@ -329,10 +313,7 @@ function discoverController(
|
|||||||
|
|
||||||
const timeFieldName = $scope.indexPattern.timeFieldName;
|
const timeFieldName = $scope.indexPattern.timeFieldName;
|
||||||
const hideTimeColumn = config.get('doc_table:hideTimeColumn');
|
const hideTimeColumn = config.get('doc_table:hideTimeColumn');
|
||||||
const fields =
|
const fields = (timeFieldName && !hideTimeColumn) ? [timeFieldName, ...selectedFields] : selectedFields;
|
||||||
timeFieldName && !hideTimeColumn
|
|
||||||
? [timeFieldName, ...selectedFields]
|
|
||||||
: selectedFields;
|
|
||||||
return {
|
return {
|
||||||
searchFields: fields,
|
searchFields: fields,
|
||||||
selectFields: fields
|
selectFields: fields
|
||||||
@ -358,9 +339,7 @@ function discoverController(
|
|||||||
},
|
},
|
||||||
fields: selectFields,
|
fields: selectFields,
|
||||||
metaFields: $scope.indexPattern.metaFields,
|
metaFields: $scope.indexPattern.metaFields,
|
||||||
conflictedTypesFields: $scope.indexPattern.fields
|
conflictedTypesFields: $scope.indexPattern.fields.filter(f => f.type === 'conflict').map(f => f.name),
|
||||||
.filter(f => f.type === 'conflict')
|
|
||||||
.map(f => f.name),
|
|
||||||
indexPatternId: searchSource.getField('index').id
|
indexPatternId: searchSource.getField('index').id
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -371,19 +350,10 @@ function discoverController(
|
|||||||
return {
|
return {
|
||||||
query: $scope.searchSource.getField('query') || {
|
query: $scope.searchSource.getField('query') || {
|
||||||
query: '',
|
query: '',
|
||||||
language:
|
language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage')
|
||||||
localStorage.get('kibana.userQueryLanguage') ||
|
|
||||||
config.get('search:queryLanguage')
|
|
||||||
},
|
},
|
||||||
sort: getSort.array(
|
sort: getSort.array(savedSearch.sort, $scope.indexPattern, config.get('discover:sort:defaultOrder')),
|
||||||
savedSearch.sort,
|
columns: savedSearch.columns.length > 0 ? savedSearch.columns : config.get('defaultColumns').slice(),
|
||||||
$scope.indexPattern,
|
|
||||||
config.get('discover:sort:defaultOrder')
|
|
||||||
),
|
|
||||||
columns:
|
|
||||||
savedSearch.columns.length > 0
|
|
||||||
? savedSearch.columns
|
|
||||||
: config.get('defaultColumns').slice(),
|
|
||||||
index: $scope.indexPattern.id,
|
index: $scope.indexPattern.id,
|
||||||
interval: 'auto',
|
interval: 'auto',
|
||||||
filters: _.cloneDeep($scope.searchSource.getOwnField('filter'))
|
filters: _.cloneDeep($scope.searchSource.getOwnField('filter'))
|
||||||
@ -396,138 +366,123 @@ function discoverController(
|
|||||||
$scope.getBucketIntervalToolTipText = () => {
|
$scope.getBucketIntervalToolTipText = () => {
|
||||||
return i18n.translate('kbn.discover.bucketIntervalTooltip', {
|
return i18n.translate('kbn.discover.bucketIntervalTooltip', {
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
defaultMessage:
|
defaultMessage: 'This interval creates {bucketsDescription} to show in the selected time range, so it has been scaled to {bucketIntervalDescription}',
|
||||||
'This interval creates {bucketsDescription} to show in the selected time range, so it has been scaled to {bucketIntervalDescription}',
|
|
||||||
values: {
|
values: {
|
||||||
bucketsDescription:
|
bucketsDescription: $scope.bucketInterval.scale > 1
|
||||||
$scope.bucketInterval.scale > 1
|
? i18n.translate('kbn.discover.bucketIntervalTooltip.tooLargeBucketsText', {
|
||||||
? i18n.translate(
|
defaultMessage: 'buckets that are too large',
|
||||||
'kbn.discover.bucketIntervalTooltip.tooLargeBucketsText',
|
})
|
||||||
{
|
: i18n.translate('kbn.discover.bucketIntervalTooltip.tooManyBucketsText', {
|
||||||
defaultMessage: 'buckets that are too large'
|
defaultMessage: 'too many buckets',
|
||||||
}
|
}),
|
||||||
)
|
bucketIntervalDescription: $scope.bucketInterval.description,
|
||||||
: i18n.translate(
|
},
|
||||||
'kbn.discover.bucketIntervalTooltip.tooManyBucketsText',
|
|
||||||
{
|
|
||||||
defaultMessage: 'too many buckets'
|
|
||||||
}
|
|
||||||
),
|
|
||||||
bucketIntervalDescription: $scope.bucketInterval.description
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.$watchCollection('state.columns', function() {
|
$scope.$watchCollection('state.columns', function () {
|
||||||
$state.save();
|
$state.save();
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.opts = {
|
$scope.opts = {
|
||||||
// number of records to fetch, then paginate through
|
// number of records to fetch, then paginate through
|
||||||
sampleSize: config.get('discover:sampleSize'),
|
sampleSize: config.get('discover:sampleSize'),
|
||||||
timefield:
|
timefield: isDefaultTypeIndexPattern($scope.indexPattern) && $scope.indexPattern.timeFieldName,
|
||||||
isDefaultTypeIndexPattern($scope.indexPattern) &&
|
|
||||||
$scope.indexPattern.timeFieldName,
|
|
||||||
savedSearch: savedSearch,
|
savedSearch: savedSearch,
|
||||||
indexPatternList: $route.current.locals.ip.list
|
indexPatternList: $route.current.locals.ip.list,
|
||||||
};
|
};
|
||||||
|
|
||||||
const init = _.once(function() {
|
const init = _.once(function () {
|
||||||
stateMonitor = stateMonitorFactory.create($state, getStateDefaults());
|
stateMonitor = stateMonitorFactory.create($state, getStateDefaults());
|
||||||
stateMonitor.onChange(status => {
|
stateMonitor.onChange((status) => {
|
||||||
$appStatus.dirty = status.dirty || !savedSearch.id;
|
$appStatus.dirty = status.dirty || !savedSearch.id;
|
||||||
});
|
});
|
||||||
$scope.$on('$destroy', () => stateMonitor.destroy());
|
$scope.$on('$destroy', () => stateMonitor.destroy());
|
||||||
|
|
||||||
$scope.updateDataSource().then(function() {
|
$scope.updateDataSource()
|
||||||
$scope.$listen(timefilter, 'fetch', function() {
|
.then(function () {
|
||||||
// WAZUH
|
$scope.$listen(timefilter, 'autoRefreshFetch', $scope.fetch);
|
||||||
$rootScope.$broadcast('updateVis');
|
$scope.$listen(timefilter, 'refreshIntervalUpdate', $scope.updateRefreshInterval);
|
||||||
$scope.fetch();
|
$scope.$listen(timefilter, 'timeUpdate', $scope.updateTime);
|
||||||
});
|
|
||||||
|
|
||||||
$scope.$listen(timefilter, 'refreshIntervalUpdate', () => {
|
$scope.$watchCollection('state.sort', function (sort) {
|
||||||
$scope.updateRefreshInterval();
|
if (!sort) return;
|
||||||
});
|
|
||||||
$scope.$listen(timefilter, 'timeUpdate', () => {
|
|
||||||
$scope.updateTime();
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.$watchCollection('state.sort', function(sort) {
|
// get the current sort from {key: val} to ["key", "val"];
|
||||||
if (!sort) return;
|
const currentSort = Object.entries(
|
||||||
|
$scope.searchSource.getField('sort')
|
||||||
|
).pop();
|
||||||
|
|
||||||
// get the current sort from {key: val} to ["key", "val"];
|
// if the searchSource doesn't know, tell it so
|
||||||
const currentSort = Object.entries(
|
if (!angular.equals(sort, currentSort)) $scope.fetch();
|
||||||
$scope.searchSource.getField('sort')
|
});
|
||||||
).pop();
|
|
||||||
|
|
||||||
// if the searchSource doesn't know, tell it so
|
// update data source when filters update
|
||||||
if (!angular.equals(sort, currentSort)) $scope.fetch();
|
filterUpdateSubscription = subscribeWithScope($scope, queryFilter.getUpdates$(), {
|
||||||
});
|
next: () => {
|
||||||
|
$scope.filters = queryFilter.getFilters();
|
||||||
|
$scope
|
||||||
|
.updateDataSource()
|
||||||
|
.then(function () {
|
||||||
|
/////////////////////////////// WAZUH ///////////////////////////////////
|
||||||
|
if (!filtersAreReady()) return;
|
||||||
|
discoverPendingUpdates.removeAll();
|
||||||
|
discoverPendingUpdates.addItem(
|
||||||
|
$state.query,
|
||||||
|
queryFilter.getFilters()
|
||||||
|
);
|
||||||
|
$rootScope.$broadcast('updateVis');
|
||||||
|
$rootScope.$broadcast('fetch');
|
||||||
|
if ($location.search().tab != 'configuration') {
|
||||||
|
loadedVisualizations.removeAll();
|
||||||
|
$rootScope.rendered = false;
|
||||||
|
$rootScope.loadingStatus = 'Fetching data...';
|
||||||
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
$state.save();
|
||||||
|
})
|
||||||
|
.catch(console.error); // eslint-disable-line
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// update data source when filters update
|
// fetch data when filters fire fetch event
|
||||||
filterUpdateSubscription = queryFilter.getUpdates$().subscribe(() => {
|
filterFetchSubscription = subscribeWithScope($scope, queryFilter.getUpdates$(), {
|
||||||
$scope.filters = queryFilter.getFilters();
|
next: $scope.fetch
|
||||||
$scope
|
});
|
||||||
.updateDataSource()
|
|
||||||
.then(function() {
|
|
||||||
/////////////////////////////// WAZUH ///////////////////////////////////
|
|
||||||
if (!filtersAreReady()) return;
|
|
||||||
discoverPendingUpdates.removeAll();
|
|
||||||
discoverPendingUpdates.addItem(
|
|
||||||
$state.query,
|
|
||||||
queryFilter.getFilters()
|
|
||||||
);
|
|
||||||
$rootScope.$broadcast('updateVis');
|
|
||||||
$rootScope.$broadcast('fetch');
|
|
||||||
if ($location.search().tab != 'configuration') {
|
|
||||||
loadedVisualizations.removeAll();
|
|
||||||
$rootScope.rendered = false;
|
|
||||||
$rootScope.loadingStatus = 'Fetching data...';
|
|
||||||
}
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
$state.save();
|
|
||||||
})
|
|
||||||
.catch(console.error); // eslint-disable-line
|
|
||||||
});
|
|
||||||
|
|
||||||
// fetch data when filters fire fetch event
|
// update data source when hitting forward/back and the query changes
|
||||||
filterFetchSubscription = queryFilter
|
$scope.$listen($state, 'fetch_with_changes', function (diff) {
|
||||||
.getFetches$()
|
if (diff.indexOf('query') >= 0) $scope.fetch();
|
||||||
.subscribe($scope.fetch);
|
});
|
||||||
|
|
||||||
// update data source when hitting forward/back and the query changes
|
$scope.$watch('opts.timefield', function (timefield) {
|
||||||
$scope.$listen($state, 'fetch_with_changes', function(diff) {
|
$scope.enableTimeRangeSelector = !!timefield;
|
||||||
if (diff.indexOf('query') >= 0) $scope.fetch();
|
});
|
||||||
});
|
|
||||||
|
|
||||||
$scope.$watch('opts.timefield', function(timefield) {
|
$scope.$watch('state.interval', function () {
|
||||||
$scope.enableTimeRangeSelector = !!timefield;
|
$scope.fetch();
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.$watch('state.interval', function() {
|
$scope.$watch('vis.aggs', function () {
|
||||||
$scope.fetch();
|
// no timefield, no vis, nothing to update
|
||||||
});
|
if (!$scope.opts.timefield) return;
|
||||||
|
|
||||||
$scope.$watch('vis.aggs', function() {
|
const buckets = $scope.vis.getAggConfig().bySchemaGroup.buckets;
|
||||||
// no timefield, no vis, nothing to update
|
|
||||||
if (!$scope.opts.timefield) return;
|
|
||||||
|
|
||||||
const buckets = $scope.vis.getAggConfig().bySchemaGroup.buckets;
|
if (buckets && buckets.length === 1) {
|
||||||
|
$scope.bucketInterval = buckets[0].buckets.getInterval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (buckets && buckets.length === 1) {
|
$scope.$watch('state.query', (newQuery) => {
|
||||||
$scope.bucketInterval = buckets[0].buckets.getInterval();
|
const query = migrateLegacyQuery(newQuery);
|
||||||
}
|
$scope.updateQueryAndFetch({ query });
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.$watch('state.query', newQuery => {
|
$scope.$watchMulti([
|
||||||
const query = migrateLegacyQuery(newQuery);
|
'rows',
|
||||||
$scope.updateQueryAndFetch({ query });
|
'fetchStatus'
|
||||||
});
|
], (function updateResultState() {
|
||||||
|
|
||||||
$scope.$watchMulti(
|
|
||||||
['rows', 'fetchStatus'],
|
|
||||||
(function updateResultState() {
|
|
||||||
let prev = {};
|
let prev = {};
|
||||||
const status = {
|
const status = {
|
||||||
LOADING: 'loading', // initial data load
|
LOADING: 'loading', // initial data load
|
||||||
@ -540,16 +495,14 @@ function discoverController(
|
|||||||
if (rows == null && oldRows == null) return status.LOADING;
|
if (rows == null && oldRows == null) return status.LOADING;
|
||||||
|
|
||||||
const rowsEmpty = _.isEmpty(rows);
|
const rowsEmpty = _.isEmpty(rows);
|
||||||
const preparingForFetch =
|
const preparingForFetch = fetchStatus === fetchStatuses.UNINITIALIZED;
|
||||||
fetchStatus === fetchStatuses.UNINITIALIZED;
|
|
||||||
if (preparingForFetch) return status.LOADING;
|
if (preparingForFetch) return status.LOADING;
|
||||||
else if (rowsEmpty && fetchStatus === fetchStatuses.LOADING)
|
else if (rowsEmpty && fetchStatus === fetchStatuses.LOADING) return status.LOADING;
|
||||||
return status.LOADING;
|
|
||||||
else if (!rowsEmpty) return status.READY;
|
else if (!rowsEmpty) return status.READY;
|
||||||
else return status.NO_RESULTS;
|
else return status.NO_RESULTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return function() {
|
return function () {
|
||||||
const current = {
|
const current = {
|
||||||
rows: $scope.rows,
|
rows: $scope.rows,
|
||||||
fetchStatus: $scope.fetchStatus
|
fetchStatus: $scope.fetchStatus
|
||||||
@ -561,24 +514,21 @@ function discoverController(
|
|||||||
current.fetchStatus,
|
current.fetchStatus,
|
||||||
prev.fetchStatus
|
prev.fetchStatus
|
||||||
);
|
);
|
||||||
|
|
||||||
// Copying it to the rootScope to access it from the Wazuh App //
|
// Copying it to the rootScope to access it from the Wazuh App //
|
||||||
$rootScope.resultState = $scope.resultState;
|
$rootScope.resultState = $scope.resultState;
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
prev = current;
|
prev = current;
|
||||||
};
|
};
|
||||||
})()
|
}()));
|
||||||
);
|
|
||||||
|
|
||||||
if ($scope.opts.timefield) {
|
if ($scope.opts.timefield) {
|
||||||
setupVisualization();
|
setupVisualization();
|
||||||
$scope.updateTime();
|
$scope.updateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
init.complete = true;
|
init.complete = true;
|
||||||
$state.replace();
|
$state.replace();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
@ -586,26 +536,25 @@ function discoverController(
|
|||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wazuh - aux function for checking filters status
|
* Wazuh - aux function for checking filters status
|
||||||
*/
|
*/
|
||||||
const filtersAreReady = () => {
|
const filtersAreReady = () => {
|
||||||
const currentUrlPath = $location.path();
|
const currentUrlPath = $location.path();
|
||||||
if (currentUrlPath && !currentUrlPath.includes('wazuh-discover')) {
|
if (currentUrlPath && !currentUrlPath.includes('wazuh-discover')) {
|
||||||
let filters = queryFilter.getFilters();
|
let filters = queryFilter.getFilters();
|
||||||
filters = Array.isArray(filters)
|
filters = Array.isArray(filters)
|
||||||
? filters.filter(
|
? filters.filter(
|
||||||
item => (((item || {}).$state || {}).store || '') === 'appState'
|
item => (((item || {}).$state || {}).store || '') === 'appState'
|
||||||
)
|
)
|
||||||
: [];
|
: [];
|
||||||
if (!filters || !filters.length) return false;
|
if (!filters || !filters.length) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.opts.fetch = $scope.fetch = function() {
|
$scope.opts.fetch = $scope.fetch = function () {
|
||||||
// Wazuh filters are not ready yet
|
// Wazuh filters are not ready yet
|
||||||
if (!filtersAreReady()) return;
|
if (!filtersAreReady()) return;
|
||||||
|
|
||||||
// ignore requests to fetch before the app inits
|
// ignore requests to fetch before the app inits
|
||||||
if (!init.complete) return;
|
if (!init.complete) return;
|
||||||
|
|
||||||
@ -613,19 +562,34 @@ function discoverController(
|
|||||||
|
|
||||||
$scope.updateTime();
|
$scope.updateTime();
|
||||||
|
|
||||||
$scope
|
// Abort any in-progress requests before fetching again
|
||||||
.updateDataSource()
|
$scope.searchSource.cancelQueued();
|
||||||
|
|
||||||
|
$scope.updateDataSource()
|
||||||
.then(setupVisualization)
|
.then(setupVisualization)
|
||||||
.then(function() {
|
.then(function () {
|
||||||
$state.save();
|
$state.save();
|
||||||
$scope.fetchStatus = fetchStatuses.LOADING;
|
$scope.fetchStatus = fetchStatuses.LOADING;
|
||||||
logInspectorRequest();
|
logInspectorRequest();
|
||||||
return courier.fetch();
|
return $scope.searchSource.fetch();
|
||||||
})
|
})
|
||||||
.catch(notify.error);
|
.then(onResults)
|
||||||
|
.catch((error) => {
|
||||||
|
const fetchError = getPainlessError(error);
|
||||||
|
|
||||||
|
if (fetchError) {
|
||||||
|
$scope.fetchError = fetchError;
|
||||||
|
} else {
|
||||||
|
toastNotifications.addError(error, {
|
||||||
|
title: i18n.translate('kbn.discover.errorLoadingData', {
|
||||||
|
defaultMessage: 'Error loading data',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.updateQueryAndFetch = function({ query, dateRange }) {
|
$scope.updateQueryAndFetch = function ({ query, dateRange }) {
|
||||||
// Wazuh filters are not ready yet
|
// Wazuh filters are not ready yet
|
||||||
if (!filtersAreReady()) return;
|
if (!filtersAreReady()) return;
|
||||||
|
|
||||||
@ -635,7 +599,6 @@ function discoverController(
|
|||||||
$rootScope.$broadcast('updateVis');
|
$rootScope.$broadcast('updateVis');
|
||||||
$rootScope.$broadcast('fetch');
|
$rootScope.$broadcast('fetch');
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
timefilter.setTime(dateRange);
|
timefilter.setTime(dateRange);
|
||||||
$state.query = query;
|
$state.query = query;
|
||||||
$scope.fetch();
|
$scope.fetch();
|
||||||
@ -647,12 +610,8 @@ function discoverController(
|
|||||||
if ($scope.opts.timefield) {
|
if ($scope.opts.timefield) {
|
||||||
const tabifiedData = tabifyAggResponse($scope.vis.aggs, resp);
|
const tabifiedData = tabifyAggResponse($scope.vis.aggs, resp);
|
||||||
$scope.searchSource.rawResponse = resp;
|
$scope.searchSource.rawResponse = resp;
|
||||||
Promise.resolve(
|
Promise
|
||||||
buildVislibDimensions($scope.vis, {
|
.resolve(buildVislibDimensions($scope.vis, { timeRange: $scope.timeRange, searchSource: $scope.searchSource }))
|
||||||
timeRange: $scope.timeRange,
|
|
||||||
searchSource: $scope.searchSource
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(resp => responseHandler(tabifiedData, resp))
|
.then(resp => responseHandler(tabifiedData, resp))
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if (visualizeHandler) {
|
if (visualizeHandler) {
|
||||||
@ -662,7 +621,7 @@ function discoverController(
|
|||||||
visType: $scope.vis.type.name,
|
visType: $scope.vis.type.name,
|
||||||
visData: resp,
|
visData: resp,
|
||||||
visConfig: $scope.vis.params,
|
visConfig: $scope.vis.params,
|
||||||
params: {}
|
params: {},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -673,7 +632,7 @@ function discoverController(
|
|||||||
$scope.rows = resp.hits.hits;
|
$scope.rows = resp.hits.hits;
|
||||||
|
|
||||||
// if we haven't counted yet, reset the counts
|
// if we haven't counted yet, reset the counts
|
||||||
const counts = ($scope.fieldCounts = $scope.fieldCounts || {});
|
const counts = $scope.fieldCounts = $scope.fieldCounts || {};
|
||||||
|
|
||||||
$scope.rows.forEach(hit => {
|
$scope.rows.forEach(hit => {
|
||||||
const fields = Object.keys($scope.indexPattern.flattenHit(hit));
|
const fields = Object.keys($scope.indexPattern.flattenHit(hit));
|
||||||
@ -683,8 +642,6 @@ function discoverController(
|
|||||||
});
|
});
|
||||||
|
|
||||||
$scope.fetchStatus = fetchStatuses.COMPLETE;
|
$scope.fetchStatus = fetchStatuses.COMPLETE;
|
||||||
|
|
||||||
return $scope.searchSource.onResults().then(onResults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let inspectorRequest;
|
let inspectorRequest;
|
||||||
@ -692,15 +649,11 @@ function discoverController(
|
|||||||
function logInspectorRequest() {
|
function logInspectorRequest() {
|
||||||
inspectorAdapters.requests.reset();
|
inspectorAdapters.requests.reset();
|
||||||
const title = i18n.translate('kbn.discover.inspectorRequestDataTitle', {
|
const title = i18n.translate('kbn.discover.inspectorRequestDataTitle', {
|
||||||
defaultMessage: 'Data'
|
defaultMessage: 'Data',
|
||||||
|
});
|
||||||
|
const description = i18n.translate('kbn.discover.inspectorRequestDescription', {
|
||||||
|
defaultMessage: 'This request queries Elasticsearch to fetch the data for the search.',
|
||||||
});
|
});
|
||||||
const description = i18n.translate(
|
|
||||||
'kbn.discover.inspectorRequestDescription',
|
|
||||||
{
|
|
||||||
defaultMessage:
|
|
||||||
'This request queries Elasticsearch to fetch the data for the search.'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
inspectorRequest = inspectorAdapters.requests.start(title, { description });
|
inspectorRequest = inspectorAdapters.requests.start(title, { description });
|
||||||
inspectorRequest.stats(getRequestInspectorStats($scope.searchSource));
|
inspectorRequest.stats(getRequestInspectorStats($scope.searchSource));
|
||||||
$scope.searchSource.getSearchRequestBody().then(body => {
|
$scope.searchSource.getSearchRequestBody().then(body => {
|
||||||
@ -714,27 +667,7 @@ function discoverController(
|
|||||||
.ok({ json: resp });
|
.ok({ json: resp });
|
||||||
}
|
}
|
||||||
|
|
||||||
function startSearching() {
|
$scope.updateTime = function () {
|
||||||
return $scope.searchSource
|
|
||||||
.onResults()
|
|
||||||
.then(onResults)
|
|
||||||
.catch(error => {
|
|
||||||
const fetchError = getPainlessError(error);
|
|
||||||
|
|
||||||
if (fetchError) {
|
|
||||||
$scope.fetchError = fetchError;
|
|
||||||
} else {
|
|
||||||
notify.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restart. This enables auto-refresh functionality.
|
|
||||||
startSearching();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
startSearching();
|
|
||||||
|
|
||||||
$scope.updateTime = function() {
|
|
||||||
/////////////////////////////// WAZUH ///////////////////////////////////
|
/////////////////////////////// WAZUH ///////////////////////////////////
|
||||||
if ($location.search().tab != 'configuration') {
|
if ($location.search().tab != 'configuration') {
|
||||||
loadedVisualizations.removeAll();
|
loadedVisualizations.removeAll();
|
||||||
@ -742,7 +675,6 @@ function discoverController(
|
|||||||
$rootScope.loadingStatus = 'Fetching data...';
|
$rootScope.loadingStatus = 'Fetching data...';
|
||||||
}
|
}
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
$scope.timeRange = {
|
$scope.timeRange = {
|
||||||
from: dateMath.parse(timefilter.getTime().from),
|
from: dateMath.parse(timefilter.getTime().from),
|
||||||
to: dateMath.parse(timefilter.getTime().to, { roundUp: true })
|
to: dateMath.parse(timefilter.getTime().to, { roundUp: true })
|
||||||
@ -750,26 +682,26 @@ function discoverController(
|
|||||||
$scope.time = timefilter.getTime();
|
$scope.time = timefilter.getTime();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.toMoment = function(datetime) {
|
$scope.toMoment = function (datetime) {
|
||||||
return moment(datetime).format(config.get('dateFormat'));
|
return moment(datetime).format(config.get('dateFormat'));
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.updateRefreshInterval = function() {
|
$scope.updateRefreshInterval = function () {
|
||||||
$scope.refreshInterval = timefilter.getRefreshInterval();
|
$scope.refreshInterval = timefilter.getRefreshInterval();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onRefreshChange = function({ isPaused, refreshInterval }) {
|
$scope.onRefreshChange = function ({ isPaused, refreshInterval }) {
|
||||||
timefilter.setRefreshInterval({
|
timefilter.setRefreshInterval({
|
||||||
pause: isPaused,
|
pause: isPaused,
|
||||||
value: refreshInterval ? refreshInterval : $scope.refreshInterval.value
|
value: refreshInterval ? refreshInterval : $scope.refreshInterval.value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.resetQuery = function() {
|
$scope.resetQuery = function () {
|
||||||
kbnUrl.change('/discover/{{id}}', { id: $route.current.params.id });
|
kbnUrl.change('/discover/{{id}}', { id: $route.current.params.id });
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.newQuery = function() {
|
$scope.newQuery = function () {
|
||||||
kbnUrl.change('/discover');
|
kbnUrl.change('/discover');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -786,47 +718,39 @@ function discoverController(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: On array fields, negating does not negate the combination, rather all terms
|
// TODO: On array fields, negating does not negate the combination, rather all terms
|
||||||
$scope.filterQuery = function(field, values, operation) {
|
$scope.filterQuery = function (field, values, operation) {
|
||||||
// Commented due to https://github.com/elastic/kibana/issues/22426
|
// Commented due to https://github.com/elastic/kibana/issues/22426
|
||||||
//$scope.indexPattern.popularizeField(field, 1);
|
//$scope.indexPattern.popularizeField(field, 1);
|
||||||
filterActions.addFilter(
|
filterActions.addFilter(field, values, operation, $scope.indexPattern.id, $scope.state, filterGen);
|
||||||
field,
|
|
||||||
values,
|
|
||||||
operation,
|
|
||||||
$scope.indexPattern.id,
|
|
||||||
$scope.state,
|
|
||||||
filterManager
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.addColumn = function addColumn(columnName) {
|
$scope.addColumn = function addColumn(columnName) {
|
||||||
// Commented due to https://github.com/elastic/kibana/issues/22426
|
// Commented due to https://github.com/elastic/kibana/issues/22426
|
||||||
//$scope.indexPattern.popularizeField(columnName, 1);
|
//$scope.indexPattern.popularizeField(field, 1);
|
||||||
columnActions.addColumn($scope.state.columns, columnName);
|
columnActions.addColumn($scope.state.columns, columnName);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.removeColumn = function removeColumn(columnName) {
|
$scope.removeColumn = function removeColumn(columnName) {
|
||||||
// Commented due to https://github.com/elastic/kibana/issues/22426
|
// Commented due to https://github.com/elastic/kibana/issues/22426
|
||||||
//$scope.indexPattern.popularizeField(columnName, 1);
|
//$scope.indexPattern.popularizeField(field, 1); columnActions.removeColumn($scope.state.columns, columnName);
|
||||||
columnActions.removeColumn($scope.state.columns, columnName);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.moveColumn = function moveColumn(columnName, newIndex) {
|
$scope.moveColumn = function moveColumn(columnName, newIndex) {
|
||||||
columnActions.moveColumn($scope.state.columns, columnName, newIndex);
|
columnActions.moveColumn($scope.state.columns, columnName, newIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.scrollToTop = function() {
|
$scope.scrollToTop = function () {
|
||||||
$window.scrollTo(0, 0);
|
$window.scrollTo(0, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.scrollToBottom = function() {
|
$scope.scrollToBottom = function () {
|
||||||
// delay scrolling to after the rows have been rendered
|
// delay scrolling to after the rows have been rendered
|
||||||
$timeout(() => {
|
$timeout(() => {
|
||||||
$element.find('#discoverBottomMarker').focus();
|
$element.find('#discoverBottomMarker').focus();
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.showAllRows = function() {
|
$scope.showAllRows = function () {
|
||||||
$scope.minimumVisibleRows = $scope.hits;
|
$scope.minimumVisibleRows = $scope.hits;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -845,12 +769,11 @@ function discoverController(
|
|||||||
params: {
|
params: {
|
||||||
field: $scope.opts.timefield,
|
field: $scope.opts.timefield,
|
||||||
interval: $state.interval,
|
interval: $state.interval,
|
||||||
timeRange: timefilter.getTime()
|
timeRange: timefilter.getTime(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// we have a vis, just modify the aggs
|
|
||||||
if ($scope.vis) {
|
if ($scope.vis) {
|
||||||
const visState = $scope.vis.getEnabledState();
|
const visState = $scope.vis.getEnabledState();
|
||||||
visState.aggs = visStateAggs;
|
visState.aggs = visStateAggs;
|
||||||
@ -859,6 +782,7 @@ function discoverController(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const visSavedObject = {
|
const visSavedObject = {
|
||||||
indexPattern: $scope.indexPattern.id,
|
indexPattern: $scope.indexPattern.id,
|
||||||
visState: {
|
visState: {
|
||||||
@ -879,34 +803,24 @@ function discoverController(
|
|||||||
visSavedObject.vis = $scope.vis;
|
visSavedObject.vis = $scope.vis;
|
||||||
|
|
||||||
$scope.searchSource.onRequestStart((searchSource, searchRequest) => {
|
$scope.searchSource.onRequestStart((searchSource, searchRequest) => {
|
||||||
return $scope.vis
|
return $scope.vis.getAggConfig().onSearchRequestStart(searchSource, searchRequest);
|
||||||
.getAggConfig()
|
|
||||||
.onSearchRequestStart(searchSource, searchRequest);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.searchSource.setField('aggs', function() {
|
$scope.searchSource.setField('aggs', function () {
|
||||||
//////////////////// WAZUH ////////////////////////////////
|
//////////////////// WAZUH ////////////////////////////////
|
||||||
// Old code: //
|
|
||||||
// return $scope.vis.getAggConfig().toDsl(); //
|
|
||||||
const result = $scope.vis.getAggConfig().toDsl();
|
const result = $scope.vis.getAggConfig().toDsl();
|
||||||
if (((result[2] || {}).date_histogram || {}).interval === '0ms') {
|
if (((result[2] || {}).date_histogram || {}).interval === '0ms') {
|
||||||
result[2].date_histogram.interval = '1d';
|
result[2].date_histogram.interval = '1d';
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
});
|
});
|
||||||
|
|
||||||
$timeout(async () => {
|
$timeout(async () => {
|
||||||
const visEl = $element.find('#discoverHistogram')[0];
|
const visEl = $element.find('#discoverHistogram')[0];
|
||||||
if (visEl) {
|
visualizeHandler = await visualizeLoader.embedVisualizationWithSavedObject(visEl, visSavedObject, {
|
||||||
visualizeHandler = await visualizeLoader.embedVisualizationWithSavedObject(
|
autoFetch: false,
|
||||||
visEl,
|
});
|
||||||
visSavedObject,
|
|
||||||
{
|
|
||||||
autoFetch: false
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -914,7 +828,7 @@ function discoverController(
|
|||||||
const {
|
const {
|
||||||
loaded: loadedIndexPattern,
|
loaded: loadedIndexPattern,
|
||||||
stateVal,
|
stateVal,
|
||||||
stateValFound
|
stateValFound,
|
||||||
} = $route.current.locals.ip;
|
} = $route.current.locals.ip;
|
||||||
|
|
||||||
const ownIndexPattern = $scope.searchSource.getOwnField('index');
|
const ownIndexPattern = $scope.searchSource.getOwnField('index');
|
||||||
@ -924,47 +838,36 @@ function discoverController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stateVal && !stateValFound) {
|
if (stateVal && !stateValFound) {
|
||||||
const warningTitle = i18n(
|
const warningTitle = i18n.translate('kbn.discover.valueIsNotConfiguredIndexPatternIDWarningTitle', {
|
||||||
'kbn.discover.valueIsNotConfiguredIndexPatternIDWarningTitle',
|
defaultMessage: '{stateVal} is not a configured index pattern ID',
|
||||||
{
|
values: {
|
||||||
defaultMessage: '{stateVal} is not a configured index pattern ID',
|
stateVal: `"${stateVal}"`,
|
||||||
values: {
|
},
|
||||||
stateVal: `"${stateVal}"`
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ownIndexPattern) {
|
if (ownIndexPattern) {
|
||||||
toastNotifications.addWarning({
|
toastNotifications.addWarning({
|
||||||
title: warningTitle,
|
title: warningTitle,
|
||||||
text: i18n(
|
text: i18n.translate('kbn.discover.showingSavedIndexPatternWarningDescription', {
|
||||||
'kbn.discover.showingSavedIndexPatternWarningDescription',
|
defaultMessage: 'Showing the saved index pattern: "{ownIndexPatternTitle}" ({ownIndexPatternId})',
|
||||||
{
|
values: {
|
||||||
defaultMessage:
|
ownIndexPatternTitle: ownIndexPattern.title,
|
||||||
'Showing the saved index pattern: "{ownIndexPatternTitle}" ({ownIndexPatternId})',
|
ownIndexPatternId: ownIndexPattern.id,
|
||||||
values: {
|
},
|
||||||
ownIndexPatternTitle: ownIndexPattern.title,
|
}),
|
||||||
ownIndexPatternId: ownIndexPattern.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
return ownIndexPattern;
|
return ownIndexPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
toastNotifications.addWarning({
|
toastNotifications.addWarning({
|
||||||
title: warningTitle,
|
title: warningTitle,
|
||||||
text: i18n(
|
text: i18n.translate('kbn.discover.showingDefaultIndexPatternWarningDescription', {
|
||||||
'kbn.discover.showingDefaultIndexPatternWarningDescription',
|
defaultMessage: 'Showing the default index pattern: "{loadedIndexPatternTitle}" ({loadedIndexPatternId})',
|
||||||
{
|
values: {
|
||||||
defaultMessage:
|
loadedIndexPatternTitle: loadedIndexPattern.title,
|
||||||
'Showing the default index pattern: "{loadedIndexPatternTitle}" ({loadedIndexPatternId})',
|
loadedIndexPatternId: loadedIndexPattern.id,
|
||||||
values: {
|
},
|
||||||
loadedIndexPatternTitle: loadedIndexPattern.title,
|
}),
|
||||||
loadedIndexPatternId: loadedIndexPattern.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,9 +876,10 @@ function discoverController(
|
|||||||
|
|
||||||
// Block the UI from loading if the user has loaded a rollup index pattern but it isn't
|
// Block the UI from loading if the user has loaded a rollup index pattern but it isn't
|
||||||
// supported.
|
// supported.
|
||||||
$scope.isUnsupportedIndexPattern =
|
$scope.isUnsupportedIndexPattern = (
|
||||||
!isDefaultTypeIndexPattern($route.current.locals.ip.loaded) &&
|
!isDefaultTypeIndexPattern($route.current.locals.ip.loaded)
|
||||||
!hasSearchStategyForIndexPattern($route.current.locals.ip.loaded);
|
&& !hasSearchStategyForIndexPattern($route.current.locals.ip.loaded)
|
||||||
|
);
|
||||||
|
|
||||||
if ($scope.isUnsupportedIndexPattern) {
|
if ($scope.isUnsupportedIndexPattern) {
|
||||||
$scope.unsupportedIndexPatternType = $route.current.locals.ip.loaded.type;
|
$scope.unsupportedIndexPatternType = $route.current.locals.ip.loaded.type;
|
||||||
@ -1017,7 +921,7 @@ function discoverController(
|
|||||||
|
|
||||||
queryFilter
|
queryFilter
|
||||||
.addFilters(wzCurrentFilters)
|
.addFilters(wzCurrentFilters)
|
||||||
.then(() => {})
|
.then(() => { })
|
||||||
.catch(error => console.log(error.message || error)); // eslint-disable-line
|
.catch(error => console.log(error.message || error)); // eslint-disable-line
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1046,6 +950,5 @@ function discoverController(
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
@ -25,7 +25,7 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import chrome from 'ui/chrome';
|
import chrome from 'ui/chrome';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
|
import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { IPrivate } from 'ui/private';
|
import { IPrivate } from 'ui/private';
|
||||||
import { EmbeddedVisualizeHandler } from './embedded_visualize_handler';
|
import { EmbeddedVisualizeHandler } from './embedded_visualize_handler';
|
||||||
|
@ -27,9 +27,9 @@ import classNames from 'classnames';
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import chrome from 'ui/chrome';
|
import chrome from 'ui/chrome';
|
||||||
import { IndexPattern } from 'ui/index_patterns';
|
import { IndexPattern } from 'ui/index_patterns';
|
||||||
import { FilterEditor } from 'ui/filter_bar/filter_editor';
|
import { FilterEditor } from 'plugins/data/filter/filter_bar/filter_editor';
|
||||||
import { FilterItem } from './filter_item';
|
import { FilterItem } from './filter_item';
|
||||||
import { FilterOptions } from 'ui/filter_bar/filter_options';
|
import { FilterOptions } from 'plugins/data/filter/filter_bar/filter_options';
|
||||||
|
|
||||||
const config = chrome.getUiSettingsClient();
|
const config = chrome.getUiSettingsClient();
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { IndexPattern } from 'ui/index_patterns';
|
import { IndexPattern } from 'ui/index_patterns';
|
||||||
import { FilterEditor } from 'ui/filter_bar/filter_editor';
|
import { FilterEditor } from 'plugins/data/filter/filter_bar/filter_editor';
|
||||||
import { FilterView } from './filter_view';
|
import { FilterView } from './filter_view';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -64,7 +64,7 @@ class FilterItemUI extends Component<Props, State> {
|
|||||||
const dataTestSubjValue = filter.meta.value ? `filter-value-${filter.meta.value}` : '';
|
const dataTestSubjValue = filter.meta.value ? `filter-value-${filter.meta.value}` : '';
|
||||||
const dataTestSubjDisabled = `filter-${
|
const dataTestSubjDisabled = `filter-${
|
||||||
this.props.filter.meta.disabled ? 'disabled' : 'enabled'
|
this.props.filter.meta.disabled ? 'disabled' : 'enabled'
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const badge = (
|
const badge = (
|
||||||
<FilterView
|
<FilterView
|
||||||
@ -83,13 +83,13 @@ class FilterItemUI extends Component<Props, State> {
|
|||||||
{
|
{
|
||||||
name: isFilterPinned(filter)
|
name: isFilterPinned(filter)
|
||||||
? this.props.intl.formatMessage({
|
? this.props.intl.formatMessage({
|
||||||
id: 'common.ui.filterBar.unpinFilterButtonLabel',
|
id: 'common.ui.filterBar.unpinFilterButtonLabel',
|
||||||
defaultMessage: 'Unpin',
|
defaultMessage: 'Unpin',
|
||||||
})
|
})
|
||||||
: this.props.intl.formatMessage({
|
: this.props.intl.formatMessage({
|
||||||
id: 'common.ui.filterBar.pinFilterButtonLabel',
|
id: 'common.ui.filterBar.pinFilterButtonLabel',
|
||||||
defaultMessage: 'Pin across all apps',
|
defaultMessage: 'Pin across all apps',
|
||||||
}),
|
}),
|
||||||
icon: 'pin',
|
icon: 'pin',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.closePopover();
|
this.closePopover();
|
||||||
@ -109,13 +109,13 @@ class FilterItemUI extends Component<Props, State> {
|
|||||||
{
|
{
|
||||||
name: negate
|
name: negate
|
||||||
? this.props.intl.formatMessage({
|
? this.props.intl.formatMessage({
|
||||||
id: 'common.ui.filterBar.includeFilterButtonLabel',
|
id: 'common.ui.filterBar.includeFilterButtonLabel',
|
||||||
defaultMessage: 'Include results',
|
defaultMessage: 'Include results',
|
||||||
})
|
})
|
||||||
: this.props.intl.formatMessage({
|
: this.props.intl.formatMessage({
|
||||||
id: 'common.ui.filterBar.excludeFilterButtonLabel',
|
id: 'common.ui.filterBar.excludeFilterButtonLabel',
|
||||||
defaultMessage: 'Exclude results',
|
defaultMessage: 'Exclude results',
|
||||||
}),
|
}),
|
||||||
icon: negate ? 'plusInCircle' : 'minusInCircle',
|
icon: negate ? 'plusInCircle' : 'minusInCircle',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.closePopover();
|
this.closePopover();
|
||||||
@ -126,13 +126,13 @@ class FilterItemUI extends Component<Props, State> {
|
|||||||
{
|
{
|
||||||
name: disabled
|
name: disabled
|
||||||
? this.props.intl.formatMessage({
|
? this.props.intl.formatMessage({
|
||||||
id: 'common.ui.filterBar.enableFilterButtonLabel',
|
id: 'common.ui.filterBar.enableFilterButtonLabel',
|
||||||
defaultMessage: 'Re-enable',
|
defaultMessage: 'Re-enable',
|
||||||
})
|
})
|
||||||
: this.props.intl.formatMessage({
|
: this.props.intl.formatMessage({
|
||||||
id: 'common.ui.filterBar.disableFilterButtonLabel',
|
id: 'common.ui.filterBar.disableFilterButtonLabel',
|
||||||
defaultMessage: 'Temporarily disable',
|
defaultMessage: 'Temporarily disable',
|
||||||
}),
|
}),
|
||||||
icon: `${disabled ? 'eye' : 'eyeClosed'}`,
|
icon: `${disabled ? 'eye' : 'eyeClosed'}`,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.closePopover();
|
this.closePopover();
|
||||||
|
@ -19,7 +19,7 @@ import React, { SFC } from 'react';
|
|||||||
import {
|
import {
|
||||||
existsOperator,
|
existsOperator,
|
||||||
isOneOfOperator
|
isOneOfOperator
|
||||||
} from 'ui/filter_bar/filter_editor/lib/filter_operators';
|
} from 'plugins/data/filter/filter_bar/filter_editor/lib/filter_operators';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
filter: Filter;
|
filter: Filter;
|
||||||
@ -76,12 +76,12 @@ export const FilterView: SFC<Props> = ({ filter, ...rest }: Props) => {
|
|||||||
<span>{getFilterDisplayText(filter)}</span>
|
<span>{getFilterDisplayText(filter)}</span>
|
||||||
</EuiBadge>
|
</EuiBadge>
|
||||||
) : (
|
) : (
|
||||||
<EuiBadge
|
<EuiBadge
|
||||||
className={rest.className}
|
className={rest.className}
|
||||||
>
|
>
|
||||||
<span>{getFilterDisplayText(filter)}</span>
|
<span>{getFilterDisplayText(filter)}</span>
|
||||||
</EuiBadge>
|
</EuiBadge>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getFilterDisplayText(filter: Filter) {
|
export function getFilterDisplayText(filter: Filter) {
|
||||||
@ -91,8 +91,8 @@ export function getFilterDisplayText(filter: Filter) {
|
|||||||
|
|
||||||
const prefix = filter.meta.negate
|
const prefix = filter.meta.negate
|
||||||
? ` ${i18n.translate('common.ui.filterBar.negatedFilterPrefix', {
|
? ` ${i18n.translate('common.ui.filterBar.negatedFilterPrefix', {
|
||||||
defaultMessage: 'NOT '
|
defaultMessage: 'NOT '
|
||||||
})}`
|
})}`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
switch (filter.meta.type) {
|
switch (filter.meta.type) {
|
||||||
@ -107,7 +107,7 @@ export function getFilterDisplayText(filter: Filter) {
|
|||||||
case 'phrases':
|
case 'phrases':
|
||||||
return `${prefix}${filter.meta.key} ${isOneOfOperator.message} ${
|
return `${prefix}${filter.meta.key} ${isOneOfOperator.message} ${
|
||||||
filter.meta.value
|
filter.meta.value
|
||||||
}`;
|
}`;
|
||||||
case 'query_string':
|
case 'query_string':
|
||||||
return `${prefix}${filter.meta.value}`;
|
return `${prefix}${filter.meta.value}`;
|
||||||
case 'range':
|
case 'range':
|
||||||
|
380
public/kibana-integrations/search-bar/query_bar.tsx
Normal file
380
public/kibana-integrations/search-bar/query_bar.tsx
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
* Author: Elasticsearch B.V.
|
||||||
|
* Updated by Wazuh, Inc.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2019 Wazuh, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Find more information about this on the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { doesKueryExpressionHaveLuceneSyntaxError } from '@kbn/es-query';
|
||||||
|
import { IndexPattern } from 'ui/index_patterns';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { get, isEqual } from 'lodash';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Storage } from 'ui/storage';
|
||||||
|
import { timeHistory } from 'ui/timefilter/time_history';
|
||||||
|
|
||||||
|
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink, EuiSuperDatePicker } from '@elastic/eui';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { EuiSuperUpdateButton } from '@elastic/eui';
|
||||||
|
|
||||||
|
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||||
|
import { documentationLinks } from 'ui/documentation_links';
|
||||||
|
import { Toast, toastNotifications } from 'ui/notify';
|
||||||
|
import chrome from 'ui/chrome';
|
||||||
|
import { PersistedLog } from 'ui/persisted_log';
|
||||||
|
import { QueryBarInput } from 'plugins/data/query/query_bar/components/query_bar_input';
|
||||||
|
|
||||||
|
import { getQueryLog } from 'plugins/data/query/query_bar/lib/get_query_log';
|
||||||
|
import { Query } from 'plugins/data/query/query_bar/index';
|
||||||
|
|
||||||
|
const config = chrome.getUiSettingsClient();
|
||||||
|
|
||||||
|
interface DateRange {
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
query: Query;
|
||||||
|
onSubmit: (payload: { dateRange: DateRange; query: Query }) => void;
|
||||||
|
disableAutoFocus?: boolean;
|
||||||
|
appName: string;
|
||||||
|
screenTitle: string;
|
||||||
|
indexPatterns: Array<IndexPattern | string>;
|
||||||
|
store: Storage;
|
||||||
|
intl: InjectedIntl;
|
||||||
|
prepend?: any;
|
||||||
|
showDatePicker?: boolean;
|
||||||
|
dateRangeFrom?: string;
|
||||||
|
dateRangeTo?: string;
|
||||||
|
isRefreshPaused?: boolean;
|
||||||
|
refreshInterval?: number;
|
||||||
|
showAutoRefreshOnly?: boolean;
|
||||||
|
onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void;
|
||||||
|
customSubmitButton?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
query: Query;
|
||||||
|
inputIsPristine: boolean;
|
||||||
|
currentProps?: Props;
|
||||||
|
dateRangeFrom: string;
|
||||||
|
dateRangeTo: string;
|
||||||
|
isDateRangeInvalid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryBarUI extends Component<Props, State> {
|
||||||
|
public static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||||
|
if (isEqual(prevState.currentProps, nextProps)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextQuery = null;
|
||||||
|
if (nextProps.query.query !== prevState.query.query) {
|
||||||
|
nextQuery = {
|
||||||
|
query: nextProps.query.query,
|
||||||
|
language: nextProps.query.language,
|
||||||
|
};
|
||||||
|
} else if (nextProps.query.language !== prevState.query.language) {
|
||||||
|
nextQuery = {
|
||||||
|
query: '',
|
||||||
|
language: nextProps.query.language,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextDateRange = null;
|
||||||
|
if (
|
||||||
|
nextProps.dateRangeFrom !== get(prevState, 'currentProps.dateRangeFrom') ||
|
||||||
|
nextProps.dateRangeTo !== get(prevState, 'currentProps.dateRangeTo')
|
||||||
|
) {
|
||||||
|
nextDateRange = {
|
||||||
|
dateRangeFrom: nextProps.dateRangeFrom,
|
||||||
|
dateRangeTo: nextProps.dateRangeTo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextState: any = {
|
||||||
|
currentProps: nextProps,
|
||||||
|
};
|
||||||
|
if (nextQuery) {
|
||||||
|
nextState.query = nextQuery;
|
||||||
|
}
|
||||||
|
if (nextDateRange) {
|
||||||
|
nextState.dateRangeFrom = nextDateRange.dateRangeFrom;
|
||||||
|
nextState.dateRangeTo = nextDateRange.dateRangeTo;
|
||||||
|
}
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Keep the "draft" value in local state until the user actually submits the query. There are a couple advantages:
|
||||||
|
|
||||||
|
1. Each app doesn't have to maintain its own "draft" value if it wants to put off updating the query in app state
|
||||||
|
until the user manually submits their changes. Most apps have watches on the query value in app state so we don't
|
||||||
|
want to trigger those on every keypress. Also, some apps (e.g. dashboard) already juggle multiple query values,
|
||||||
|
each with slightly different semantics and I'd rather not add yet another variable to the mix.
|
||||||
|
|
||||||
|
2. Changes to the local component state won't trigger an Angular digest cycle. Triggering digest cycles on every
|
||||||
|
keypress has been a major source of performance issues for us in previous implementations of the query bar.
|
||||||
|
See https://github.com/elastic/kibana/issues/14086
|
||||||
|
*/
|
||||||
|
public state = {
|
||||||
|
query: {
|
||||||
|
query: this.props.query.query,
|
||||||
|
language: this.props.query.language,
|
||||||
|
},
|
||||||
|
inputIsPristine: true,
|
||||||
|
currentProps: this.props,
|
||||||
|
dateRangeFrom: _.get(this.props, 'dateRangeFrom', 'now-15m'),
|
||||||
|
dateRangeTo: _.get(this.props, 'dateRangeTo', 'now'),
|
||||||
|
isDateRangeInvalid: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public inputRef: HTMLInputElement | null = null;
|
||||||
|
|
||||||
|
private persistedLog: PersistedLog | undefined;
|
||||||
|
|
||||||
|
public isDirty = () => {
|
||||||
|
if (!this.props.showDatePicker) {
|
||||||
|
return this.state.query.query !== this.props.query.query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.state.query.query !== this.props.query.query ||
|
||||||
|
this.state.dateRangeFrom !== this.props.dateRangeFrom ||
|
||||||
|
this.state.dateRangeTo !== this.props.dateRangeTo
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
public onClickSubmitButton = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
if (this.persistedLog) {
|
||||||
|
this.persistedLog.add(this.state.query.query);
|
||||||
|
}
|
||||||
|
this.onSubmit(() => event.preventDefault());
|
||||||
|
};
|
||||||
|
|
||||||
|
public onChange = (query: Query) => {
|
||||||
|
this.setState({
|
||||||
|
query,
|
||||||
|
inputIsPristine: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
public onTimeChange = ({
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
isInvalid,
|
||||||
|
isQuickSelection,
|
||||||
|
}: {
|
||||||
|
start: string;
|
||||||
|
end: string;
|
||||||
|
isInvalid: boolean;
|
||||||
|
isQuickSelection: boolean;
|
||||||
|
}) => {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
dateRangeFrom: start,
|
||||||
|
dateRangeTo: end,
|
||||||
|
isDateRangeInvalid: isInvalid,
|
||||||
|
},
|
||||||
|
() => isQuickSelection && this.onSubmit()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
public onSubmit = (preventDefault?: () => void) => {
|
||||||
|
if (preventDefault) {
|
||||||
|
preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleLuceneSyntaxWarning();
|
||||||
|
|
||||||
|
timeHistory.add({
|
||||||
|
from: this.state.dateRangeFrom,
|
||||||
|
to: this.state.dateRangeTo,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.props.onSubmit({
|
||||||
|
query: {
|
||||||
|
query: this.state.query.query,
|
||||||
|
language: this.state.query.language,
|
||||||
|
},
|
||||||
|
dateRange: {
|
||||||
|
from: this.state.dateRangeFrom,
|
||||||
|
to: this.state.dateRangeTo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private onInputSubmit = (query: Query) => {
|
||||||
|
this.setState({ query }, () => {
|
||||||
|
this.onSubmit();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
this.persistedLog = getQueryLog(this.props.appName, this.props.query.language);
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate(prevProps: Props) {
|
||||||
|
if (prevProps.query.language !== this.props.query.language) {
|
||||||
|
this.persistedLog = getQueryLog(this.props.appName, this.props.query.language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const classes = classNames('kbnQueryBar', {
|
||||||
|
'kbnQueryBar--withDatePicker': this.props.showDatePicker,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiFlexGroup className={classes} responsive={!!this.props.showDatePicker} gutterSize="s">
|
||||||
|
<EuiFlexItem>
|
||||||
|
<QueryBarInput
|
||||||
|
appName={this.props.appName}
|
||||||
|
disableAutoFocus={this.props.disableAutoFocus}
|
||||||
|
indexPatterns={this.props.indexPatterns}
|
||||||
|
prepend={this.props.prepend}
|
||||||
|
query={this.state.query}
|
||||||
|
screenTitle={this.props.screenTitle}
|
||||||
|
store={this.props.store}
|
||||||
|
onChange={this.onChange}
|
||||||
|
onSubmit={this.onInputSubmit}
|
||||||
|
persistedLog={this.persistedLog}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem grow={false}>{this.renderUpdateButton()}</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderUpdateButton() {
|
||||||
|
const button = this.props.customSubmitButton ? (
|
||||||
|
React.cloneElement(this.props.customSubmitButton, { onClick: this.onClickSubmitButton })
|
||||||
|
) : (
|
||||||
|
<EuiSuperUpdateButton
|
||||||
|
needsUpdate={this.isDirty()}
|
||||||
|
isDisabled={this.state.isDateRangeInvalid}
|
||||||
|
onClick={this.onClickSubmitButton}
|
||||||
|
data-test-subj="querySubmitButton"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.props.showDatePicker) {
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiFlexGroup responsive={false} gutterSize="s">
|
||||||
|
{this.renderDatePicker()}
|
||||||
|
<EuiFlexItem grow={false}>{button}</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderDatePicker() {
|
||||||
|
if (!this.props.showDatePicker) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recentlyUsedRanges = timeHistory
|
||||||
|
.get()
|
||||||
|
.map(({ from, to }: { from: string; to: string }) => {
|
||||||
|
return {
|
||||||
|
start: from,
|
||||||
|
end: to,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const commonlyUsedRanges = config
|
||||||
|
.get('timepicker:quickRanges')
|
||||||
|
.map(({ from, to, display }: { from: string; to: string; display: string }) => {
|
||||||
|
return {
|
||||||
|
start: from,
|
||||||
|
end: to,
|
||||||
|
label: display,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiFlexItem className="kbnQueryBar__datePickerWrapper">
|
||||||
|
<EuiSuperDatePicker
|
||||||
|
start={this.state.dateRangeFrom}
|
||||||
|
end={this.state.dateRangeTo}
|
||||||
|
isPaused={this.props.isRefreshPaused}
|
||||||
|
refreshInterval={this.props.refreshInterval}
|
||||||
|
onTimeChange={this.onTimeChange}
|
||||||
|
onRefreshChange={this.props.onRefreshChange}
|
||||||
|
showUpdateButton={false}
|
||||||
|
recentlyUsedRanges={recentlyUsedRanges}
|
||||||
|
commonlyUsedRanges={commonlyUsedRanges}
|
||||||
|
dateFormat={config.get('dateFormat')}
|
||||||
|
isAutoRefreshOnly={this.props.showAutoRefreshOnly}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleLuceneSyntaxWarning() {
|
||||||
|
const { intl, store } = this.props;
|
||||||
|
const { query, language } = this.state.query;
|
||||||
|
if (
|
||||||
|
language === 'kuery' &&
|
||||||
|
typeof query === 'string' &&
|
||||||
|
!store.get('kibana.luceneSyntaxWarningOptOut') &&
|
||||||
|
doesKueryExpressionHaveLuceneSyntaxError(query)
|
||||||
|
) {
|
||||||
|
const toast = toastNotifications.addWarning({
|
||||||
|
title: intl.formatMessage({
|
||||||
|
id: 'data.query.queryBar.luceneSyntaxWarningTitle',
|
||||||
|
defaultMessage: 'Lucene syntax warning',
|
||||||
|
}),
|
||||||
|
text: (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id="data.query.queryBar.luceneSyntaxWarningMessage"
|
||||||
|
defaultMessage="It looks like you may be trying to use Lucene query syntax, although you
|
||||||
|
have Kibana Query Language (KQL) selected. Please review the KQL docs {link}."
|
||||||
|
values={{
|
||||||
|
link: (
|
||||||
|
<EuiLink href={documentationLinks.query.kueryQuerySyntax} target="_blank">
|
||||||
|
<FormattedMessage
|
||||||
|
id="data.query.queryBar.syntaxOptionsDescription.docsLinkText"
|
||||||
|
defaultMessage="here"
|
||||||
|
/>
|
||||||
|
</EuiLink>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s">
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiButton size="s" onClick={() => this.onLuceneSyntaxWarningOptOut(toast)}>
|
||||||
|
Don't show again
|
||||||
|
</EuiButton>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onLuceneSyntaxWarningOptOut(toast: Toast) {
|
||||||
|
this.props.store.set('kibana.luceneSyntaxWarningOptOut', true);
|
||||||
|
toastNotifications.remove(toast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export const QueryBar = injectI18n(QueryBarUI);
|
@ -13,36 +13,34 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { data } from 'plugins/data';
|
|
||||||
import { EuiFilterButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
import { EuiFilterButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||||
import { Filter } from '@kbn/es-query';
|
import { Filter } from '@kbn/es-query';
|
||||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import ResizeObserver from 'resize-observer-polyfill';
|
import ResizeObserver from 'resize-observer-polyfill';
|
||||||
import { FilterBar } from './filter_bar';
|
|
||||||
import { IndexPattern } from 'ui/index_patterns';
|
import { IndexPattern } from 'ui/index_patterns';
|
||||||
const { QueryBar } = data.query.ui;
|
|
||||||
import { Storage } from 'ui/storage';
|
import { Storage } from 'ui/storage';
|
||||||
|
|
||||||
interface Query {
|
import { QueryBar } from './query_bar';
|
||||||
query: string;
|
import { Query } from 'plugins/data/query/query_bar/index';
|
||||||
language: string;
|
import { FilterBar } from './filter_bar';
|
||||||
}
|
|
||||||
|
|
||||||
interface DateRange {
|
interface DateRange {
|
||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NgReact lib requires that changes to the props need to be made in the directive config as well
|
||||||
|
* See [search_bar\directive\index.js] file
|
||||||
|
*/
|
||||||
interface Props {
|
interface Props {
|
||||||
query: {
|
query: Query;
|
||||||
query: string;
|
|
||||||
language: string;
|
|
||||||
};
|
|
||||||
onQuerySubmit: (payload: { dateRange: DateRange; query: Query }) => void;
|
onQuerySubmit: (payload: { dateRange: DateRange; query: Query }) => void;
|
||||||
disableAutoFocus?: boolean;
|
disableAutoFocus?: boolean;
|
||||||
appName: string;
|
appName: string;
|
||||||
|
screenTitle: string;
|
||||||
indexPatterns: IndexPattern[];
|
indexPatterns: IndexPattern[];
|
||||||
store: Storage;
|
store: Storage;
|
||||||
filters: Filter[];
|
filters: Filter[];
|
||||||
@ -87,9 +85,9 @@ class SearchBarUI extends Component<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// member-ordering rules conflict with use-before-declaration rules
|
// member-ordering rules conflict with use-before-declaration rules
|
||||||
/* tslint:disable */
|
/* eslint-disable */
|
||||||
public ro = new ResizeObserver(this.setFilterBarHeight);
|
public ro = new ResizeObserver(this.setFilterBarHeight);
|
||||||
/* tslint:enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
public toggleFiltersVisible = () => {
|
public toggleFiltersVisible = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -113,25 +111,25 @@ class SearchBarUI extends Component<Props, State> {
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const filtersAppliedText = this.props.intl.formatMessage({
|
const filtersAppliedText = this.props.intl.formatMessage({
|
||||||
id: 'common.ui.searchBar.filtersButtonFiltersAppliedTitle',
|
id: 'data.search.searchBar.filtersButtonFiltersAppliedTitle',
|
||||||
defaultMessage: 'filters applied.',
|
defaultMessage: 'filters applied.',
|
||||||
});
|
});
|
||||||
const clickToShowOrHideText = this.state.isFiltersVisible
|
const clickToShowOrHideText = this.state.isFiltersVisible
|
||||||
? this.props.intl.formatMessage({
|
? this.props.intl.formatMessage({
|
||||||
id: 'common.ui.searchBar.filtersButtonClickToShowTitle',
|
id: 'data.search.searchBar.filtersButtonClickToShowTitle',
|
||||||
defaultMessage: 'Select to hide',
|
defaultMessage: 'Select to hide',
|
||||||
})
|
})
|
||||||
: this.props.intl.formatMessage({
|
: this.props.intl.formatMessage({
|
||||||
id: 'common.ui.searchBar.filtersButtonClickToHideTitle',
|
id: 'data.search.searchBar.filtersButtonClickToHideTitle',
|
||||||
defaultMessage: 'Select to show',
|
defaultMessage: 'Select to show',
|
||||||
});
|
});
|
||||||
|
|
||||||
const filterTriggerButton = (
|
const filterTriggerButton = (
|
||||||
<EuiFilterButton
|
<EuiFilterButton
|
||||||
onClick={this.toggleFiltersVisible}
|
onClick={this.toggleFiltersVisible}
|
||||||
isSelected={this.state.isFiltersVisible}
|
isSelected={this.state.isFiltersVisible}
|
||||||
hasActiveFilters={this.state.isFiltersVisible}
|
hasActiveFilters={this.state.isFiltersVisible}
|
||||||
numFilters={this.props.filters.length > 0 ? this.props.filters.length : null}
|
numFilters={this.props.filters.length > 0 ? this.props.filters.length : undefined}
|
||||||
aria-controls="GlobalFilterGroup"
|
aria-controls="GlobalFilterGroup"
|
||||||
aria-expanded={!!this.state.isFiltersVisible}
|
aria-expanded={!!this.state.isFiltersVisible}
|
||||||
title={`${this.props.filters.length} ${filtersAppliedText} ${clickToShowOrHideText}`}
|
title={`${this.props.filters.length} ${filtersAppliedText} ${clickToShowOrHideText}`}
|
||||||
@ -149,6 +147,7 @@ class SearchBarUI extends Component<Props, State> {
|
|||||||
{this.props.showQueryBar ? (
|
{this.props.showQueryBar ? (
|
||||||
<QueryBar
|
<QueryBar
|
||||||
query={this.props.query}
|
query={this.props.query}
|
||||||
|
screenTitle={this.props.screenTitle}
|
||||||
onSubmit={this.props.onQuerySubmit}
|
onSubmit={this.props.onQuerySubmit}
|
||||||
appName={this.props.appName}
|
appName={this.props.appName}
|
||||||
indexPatterns={this.props.indexPatterns}
|
indexPatterns={this.props.indexPatterns}
|
||||||
@ -163,8 +162,8 @@ class SearchBarUI extends Component<Props, State> {
|
|||||||
onRefreshChange={this.props.onRefreshChange}
|
onRefreshChange={this.props.onRefreshChange}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
''
|
''
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.props.showFilterBar ? (
|
{this.props.showFilterBar ? (
|
||||||
<div
|
<div
|
||||||
@ -188,8 +187,8 @@ class SearchBarUI extends Component<Props, State> {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
''
|
''
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,101 +5,56 @@
|
|||||||
<div data-transclude-slots>
|
<div data-transclude-slots>
|
||||||
<!-- Breadcrumbs. -->
|
<!-- Breadcrumbs. -->
|
||||||
<div data-transclude-slot="topLeftCorner" class="kuiLocalBreadcrumbs">
|
<div data-transclude-slot="topLeftCorner" class="kuiLocalBreadcrumbs">
|
||||||
<h1 tabindex="0" id="kui_local_breadcrumb" class="kuiLocalBreadcrumb" ng-if="opts.savedSearch.id">
|
<h1 id="kui_local_breadcrumb" class="kuiLocalBreadcrumb" ng-if="opts.savedSearch.id">
|
||||||
<span class="kuiLocalBreadcrumb__emphasis">
|
<span class="kuiLocalBreadcrumb__emphasis">
|
||||||
<span
|
<button class="kuiLink" type="button" id="reload_saved_search" aria-label="{{::'kbn.discover.reloadSavedSearchAriaLabel' | i18n: {defaultMessage: 'Reload saved search'} }}"
|
||||||
id="reload_saved_search"
|
|
||||||
aria-label="{{::'kbn.discover.reloadSavedSearchAriaLabel' | i18n: {defaultMessage: 'Reload saved search'} }}"
|
|
||||||
tooltip="{{::'kbn.discover.reloadSavedSearchTooltip' | i18n: {defaultMessage: 'Reload saved search'} }}"
|
tooltip="{{::'kbn.discover.reloadSavedSearchTooltip' | i18n: {defaultMessage: 'Reload saved search'} }}"
|
||||||
tooltip-placement="right"
|
tooltip-placement="right" tooltip-append-to-body="1" ng-click="resetQuery()">
|
||||||
tooltip-append-to-body="1"
|
<span class="kuiIcon fa-undo small"></span>
|
||||||
ng-click="resetQuery()"
|
{{::'kbn.discover.reloadSavedSearchButton' | i18n: {defaultMessage: 'Reload'} }}
|
||||||
kbn-accessible-click
|
</button>
|
||||||
class="kuiIcon fa-undo small"
|
|
||||||
></span>
|
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="kuiLocalBreadcrumb">
|
<div class="kuiLocalBreadcrumb">
|
||||||
<span data-test-subj="discoverQueryHits" class="kuiLocalBreadcrumb__emphasis">{{(hits || 0) | number:0}}</span>
|
<span data-test-subj="discoverQueryHits" class="kuiLocalBreadcrumb__emphasis">{{(hits || 0) | number:0}}</span>
|
||||||
<span
|
<span i18n-id="kbn.discover.hitsPluralTitle" i18n-default-message="{hits, plural, one {hit} other {hits}}"
|
||||||
i18n-id="kbn.discover.hitsPluralTitle"
|
i18n-values="{ hits }"></span>
|
||||||
i18n-default-message="{hits, plural, one {hit} other {hits}}"
|
|
||||||
i18n-values="{ hits }"
|
|
||||||
></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search. -->
|
<!-- Search. -->
|
||||||
<div data-transclude-slot="bottomRow" class="fullWidth">
|
<div data-transclude-slot="bottomRow" class="fullWidth">
|
||||||
<wz-search-bar
|
<wz-search-bar query="state.query" screen-title="screenTitle" on-query-submit="updateQueryAndFetch" app-name="'discover'"
|
||||||
query="state.query"
|
index-patterns="[indexPattern]" filters="filters" on-filters-updated="onFiltersUpdated" show-date-picker="enableTimeRangeSelector"
|
||||||
screen-title="screenTitle"
|
date-range-from="time.from" date-range-to="time.to" is-refresh-paused="refreshInterval.pause"
|
||||||
on-query-submit="updateQueryAndFetch"
|
refresh-interval="refreshInterval.value" on-refresh-change="onRefreshChange">
|
||||||
app-name="'discover'"
|
</wz-search-bar>
|
||||||
index-patterns="[indexPattern]"
|
|
||||||
filters="filters"
|
|
||||||
on-filters-updated="onFiltersUpdated"
|
|
||||||
show-date-picker="enableTimeRangeSelector"
|
|
||||||
date-range-from="time.from"
|
|
||||||
date-range-to="time.to"
|
|
||||||
is-refresh-paused="refreshInterval.pause"
|
|
||||||
refresh-interval="refreshInterval.value"
|
|
||||||
on-refresh-change="onRefreshChange"
|
|
||||||
></wz-search-bar>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</kbn-top-nav>
|
</kbn-top-nav>
|
||||||
|
|
||||||
|
|
||||||
<main class="container-fluid">
|
<main class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-2 sidebar-container collapsible-sidebar" id="discover-sidebar">
|
<div class="col-md-2 sidebar-container collapsible-sidebar" id="discover-sidebar">
|
||||||
<disc-field-chooser
|
<disc-field-chooser class="dscFieldChooser" columns="state.columns" hits="rows" field-counts="fieldCounts"
|
||||||
class="dscFieldChooser"
|
index-pattern="searchSource.getField('index')" index-pattern-list="opts.indexPatternList" state="state"
|
||||||
columns="state.columns"
|
on-add-field="addColumn" on-add-filter="filterQuery" on-remove-field="removeColumn">
|
||||||
hits="rows"
|
|
||||||
field-counts="fieldCounts"
|
|
||||||
index-pattern="searchSource.getField('index')"
|
|
||||||
index-pattern-list="opts.indexPatternList"
|
|
||||||
state="state"
|
|
||||||
on-add-field="addColumn"
|
|
||||||
on-add-filter="filterQuery"
|
|
||||||
on-remove-field="removeColumn"
|
|
||||||
>
|
|
||||||
</disc-field-chooser>
|
</disc-field-chooser>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dscWrapper col-md-10">
|
<div class="dscWrapper col-md-10">
|
||||||
<div class="dscWrapper__content">
|
<div class="dscWrapper__content">
|
||||||
<discover-unsupported-index-pattern
|
<discover-unsupported-index-pattern ng-if="isUnsupportedIndexPattern" unsupported-type="unsupportedIndexPatternType"></discover-unsupported-index-pattern>
|
||||||
ng-if="isUnsupportedIndexPattern"
|
|
||||||
unsupported-type="unsupportedIndexPatternType"
|
|
||||||
></discover-unsupported-index-pattern>
|
|
||||||
|
|
||||||
<discover-no-results
|
<discover-no-results ng-show="resultState === 'none'" shard-failures="failures" time-field-name="opts.timefield"
|
||||||
ng-show="resultState === 'none'"
|
query-language="state.query.language" get-doc-link="getDocLink"></discover-no-results>
|
||||||
shard-failures="failures"
|
|
||||||
time-field-name="opts.timefield"
|
|
||||||
query-language="state.query.language"
|
|
||||||
get-doc-link="getDocLink"
|
|
||||||
></discover-no-results>
|
|
||||||
|
|
||||||
<!-- loading -->
|
<!-- loading -->
|
||||||
<div ng-show="resultState === 'loading'">
|
<div ng-show="resultState === 'loading'">
|
||||||
<discover-fetch-error
|
<discover-fetch-error ng-show="fetchError" fetch-error="fetchError"></discover-fetch-error>
|
||||||
ng-show="fetchError"
|
|
||||||
fetch-error="fetchError"
|
|
||||||
></discover-fetch-error>
|
|
||||||
|
|
||||||
<div
|
<div ng-hide="fetchError" class="dscOverlay">
|
||||||
ng-hide="fetchError"
|
<h2 i18n-id="kbn.discover.searchingTitle" i18n-default-message="Searching" class="euiTitle euiTitle--small"></h2>
|
||||||
class="dscOverlay"
|
|
||||||
>
|
|
||||||
<h2
|
|
||||||
i18n-id="kbn.discover.searchingTitle"
|
|
||||||
i18n-default-message="Searching"
|
|
||||||
class="euiTitle euiTitle--small"
|
|
||||||
></h2>
|
|
||||||
<div class="euiSpacer euiSpacer--m"></div>
|
<div class="euiSpacer euiSpacer--m"></div>
|
||||||
<div class="euiLoadingSpinner euiLoadingSpinner--large" data-test-subj="loadingSpinner"></div>
|
<div class="euiLoadingSpinner euiLoadingSpinner--large" data-test-subj="loadingSpinner"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -107,112 +62,61 @@
|
|||||||
|
|
||||||
<!-- result -->
|
<!-- result -->
|
||||||
<div class="dscResults" ng-show="resultState === 'ready'">
|
<div class="dscResults" ng-show="resultState === 'ready'">
|
||||||
<button
|
<button class="kuiButton kuiButton--basic kuiButton--iconText dscSkipButton" ng-click="showAllRows(); scrollToBottom()">
|
||||||
class="kuiButton kuiButton--basic kuiButton--iconText dscSkipButton"
|
|
||||||
ng-click="showAllRows(); scrollToBottom()"
|
|
||||||
>
|
|
||||||
<span class="kuiButton__inner">
|
<span class="kuiButton__inner">
|
||||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-chevron-down"></span>
|
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-chevron-down"></span>
|
||||||
<span
|
<span i18n-id="kbn.discover.skipToBottomButtonLabel" i18n-default-message="Skip to bottom"></span>
|
||||||
i18n-id="kbn.discover.skipToBottomButtonLabel"
|
|
||||||
i18n-default-message="Skip to bottom"
|
|
||||||
></span>
|
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<section
|
<section aria-label="{{::'kbn.discover.histogramOfFoundDocumentsAriaLabel' | i18n: {defaultMessage: 'Histogram of found documents'} }}"
|
||||||
aria-label="{{::'kbn.discover.histogramOfFoundDocumentsAriaLabel' | i18n: {defaultMessage: 'Histogram of found documents'} }}"
|
class="dscTimechart" ng-if="opts.timefield">
|
||||||
class="dscTimechart"
|
|
||||||
ng-if="opts.timefield"
|
|
||||||
>
|
|
||||||
<header class="dscTimechart__header">
|
<header class="dscTimechart__header">
|
||||||
<div class="small">
|
<div class="small">
|
||||||
<span
|
<label for="dscResultsIntervalSelector" tooltip="{{::'kbn.discover.howToChangeTheTimeTooltip' | i18n: {defaultMessage: 'To change the time, click the clock icon in the navigation bar'} }}">
|
||||||
tooltip="{{::'kbn.discover.howToChangeTheTimeTooltip' | i18n: {defaultMessage: 'To change the time, click the clock icon in the navigation bar'} }}"
|
|
||||||
>
|
|
||||||
{{toMoment(timeRange.from)}} - {{toMoment(timeRange.to)}}
|
{{toMoment(timeRange.from)}} - {{toMoment(timeRange.to)}}
|
||||||
</span>
|
</label>
|
||||||
|
|
||||||
—
|
—
|
||||||
|
|
||||||
<span class="form-inline">
|
<span class="form-inline">
|
||||||
<select
|
<select id="dscResultsIntervalSelector" class="dscResults__interval form-control" ng-model="state.interval"
|
||||||
class="dscResults__interval form-control"
|
|
||||||
ng-model="state.interval"
|
|
||||||
ng-options="interval.val as interval.display for interval in intervalOptions | filter: intervalEnabled"
|
ng-options="interval.val as interval.display for interval in intervalOptions | filter: intervalEnabled"
|
||||||
ng-blur="toggleInterval()"
|
ng-blur="toggleInterval()" data-test-subj="discoverIntervalSelect">
|
||||||
data-test-subj="discoverIntervalSelect"
|
|
||||||
>
|
|
||||||
</select>
|
</select>
|
||||||
<span ng-if="bucketInterval.scaled">
|
<span ng-if="bucketInterval.scaled">
|
||||||
<icon-tip
|
<icon-tip content="getBucketIntervalToolTipText()" position="'top'"></icon-tip>
|
||||||
content="getBucketIntervalToolTipText()"
|
<span i18n-id="kbn.discover.scaledToDescription" i18n-default-message="Scaled to {bucketIntervalDescription}"
|
||||||
position="'top'"
|
|
||||||
></icon-tip>
|
|
||||||
<span
|
|
||||||
i18n-id="kbn.discover.scaledToDescription"
|
|
||||||
i18n-default-message="Scaled to {bucketIntervalDescription}"
|
|
||||||
i18n-values="{
|
i18n-values="{
|
||||||
bucketIntervalDescription: bucketInterval.description
|
bucketIntervalDescription: bucketInterval.description
|
||||||
}"
|
}"></span>
|
||||||
></span>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div id="discoverHistogram"
|
<div id="discoverHistogram" ng-show="vis && rows.length !== 0" style="display: flex; height: 200px">
|
||||||
ng-show="vis && rows.length !== 0"
|
|
||||||
style="display: flex; height: 200px"
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section class="dscTable" fixed-scroll aria-label="{{::'kbn.discover.documentsAriaLabel' | i18n: {defaultMessage: 'Documents'} }}">
|
||||||
class="dscTable"
|
<doc-table hits="rows" index-pattern="indexPattern" sorting="state.sort" columns="state.columns"
|
||||||
fixed-scroll
|
infinite-scroll="true" filter="filterQuery" filters="state.filters" data-shared-item data-title="{{opts.savedSearch.lastSavedTitle}}"
|
||||||
aria-label="{{::'kbn.discover.documentsAriaLabel' | i18n: {defaultMessage: 'Documents'} }}"
|
data-description="{{opts.savedSearch.description}}" minimum-visible-rows="minimumVisibleRows"
|
||||||
>
|
render-complete on-add-column="addColumn" on-change-sort-order="setSortOrder" on-move-column="moveColumn"
|
||||||
<doc-table
|
on-remove-column="removeColumn"></doc-table>
|
||||||
hits="rows"
|
|
||||||
index-pattern="indexPattern"
|
|
||||||
sorting="state.sort"
|
|
||||||
columns="state.columns"
|
|
||||||
infinite-scroll="true"
|
|
||||||
filter="filterQuery"
|
|
||||||
filters="state.filters"
|
|
||||||
data-shared-item
|
|
||||||
data-title="{{opts.savedSearch.lastSavedTitle}}"
|
|
||||||
data-description="{{opts.savedSearch.description}}"
|
|
||||||
minimum-visible-rows="minimumVisibleRows"
|
|
||||||
render-complete
|
|
||||||
on-add-column="addColumn"
|
|
||||||
on-change-sort-order="setSortOrder"
|
|
||||||
on-move-column="moveColumn"
|
|
||||||
on-remove-column="removeColumn"
|
|
||||||
></doc-table>
|
|
||||||
|
|
||||||
<a tabindex="0" id="discoverBottomMarker"></a>
|
<a tabindex="0" id="discoverBottomMarker"></a>
|
||||||
|
|
||||||
<div
|
<div ng-if="rows.length == opts.sampleSize" class="dscTable__footer">
|
||||||
ng-if="rows.length == opts.sampleSize"
|
<span i18n-id="kbn.discover.howToSeeOtherMatchingDocumentsDescription" i18n-default-message="These are the first {sampleSize} documents matching
|
||||||
class="dscTable__footer"
|
your search, refine your search to see others. "
|
||||||
>
|
|
||||||
<span
|
|
||||||
i18n-id="kbn.discover.howToSeeOtherMatchingDocumentsDescription"
|
|
||||||
i18n-default-message="These are the first {sampleSize} documents matching
|
|
||||||
your search, refine your search to see others. "
|
|
||||||
i18n-values="{
|
i18n-values="{
|
||||||
sampleSize: opts.sampleSize,
|
sampleSize: opts.sampleSize,
|
||||||
}"
|
}"></span>
|
||||||
></span>
|
<a kbn-accessible-click ng-click="scrollToTop()" i18n-id="kbn.discover.backToTopLinkText"
|
||||||
<a
|
i18n-default-message="Back to top."></a>
|
||||||
kbn-accessible-click
|
|
||||||
ng-click="scrollToTop()"
|
|
||||||
i18n-id="kbn.discover.backToTopLinkText"
|
|
||||||
i18n-default-message="Back to top."
|
|
||||||
></a>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@ -220,4 +124,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</discover-app-w>
|
</discover-app-w>
|
@ -1,11 +1,15 @@
|
|||||||
<discover-app-w class="app-container">
|
<discover-app-w class="app-container">
|
||||||
<!-- Local nav. -->
|
<!-- Local nav. -->
|
||||||
<div data-transclude-slot="topLeftCorner" class="kuiLocalBreadcrumbs" ng-if="tabView === 'discover'">
|
<div data-transclude-slot="topLeftCorner" class="kuiLocalBreadcrumbs" ng-if="tabView === 'discover'">
|
||||||
<h1 tabindex="0" id="kui_local_breadcrumb" class="kuiLocalBreadcrumb" ng-if="opts.savedSearch.id">
|
<!-- Breadcrumbs. -->
|
||||||
|
<h1 id="kui_local_breadcrumb" class="kuiLocalBreadcrumb" ng-if="opts.savedSearch.id">
|
||||||
<span class="kuiLocalBreadcrumb__emphasis">
|
<span class="kuiLocalBreadcrumb__emphasis">
|
||||||
<span id="reload_saved_search" aria-label="{{::'kbn.discover.reloadSavedSearchAriaLabel' | i18n: {defaultMessage: 'Reload saved search'} }}"
|
<button class="kuiLink" type="button" id="reload_saved_search" aria-label="{{::'kbn.discover.reloadSavedSearchAriaLabel' | i18n: {defaultMessage: 'Reload saved search'} }}"
|
||||||
tooltip="{{::'kbn.discover.reloadSavedSearchTooltip' | i18n: {defaultMessage: 'Reload saved search'} }}"
|
tooltip="{{::'kbn.discover.reloadSavedSearchTooltip' | i18n: {defaultMessage: 'Reload saved search'} }}"
|
||||||
tooltip-placement="right" tooltip-append-to-body="1" ng-click="resetQuery()" kbn-accessible-click class="kuiIcon fa-undo small"></span>
|
tooltip-placement="right" tooltip-append-to-body="1" ng-click="resetQuery()">
|
||||||
|
<span class="kuiIcon fa-undo small"></span>
|
||||||
|
{{::'kbn.discover.reloadSavedSearchButton' | i18n: {defaultMessage: 'Reload'} }}
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="kuiLocalBreadcrumb">
|
<div class="kuiLocalBreadcrumb">
|
||||||
@ -19,8 +23,8 @@
|
|||||||
<div data-transclude-slots>
|
<div data-transclude-slots>
|
||||||
<!-- Search. -->
|
<!-- Search. -->
|
||||||
<div data-transclude-slot="bottomRow" class="fullWidth">
|
<div data-transclude-slot="bottomRow" class="fullWidth">
|
||||||
<wz-search-bar query="state.query" on-query-submit="updateQueryAndFetch" app-name="'discover'" index-patterns="[indexPattern]"
|
<wz-search-bar query="state.query" screen-title="screenTitle" on-query-submit="updateQueryAndFetch" app-name="'discover'"
|
||||||
filters="filters" on-filters-updated="onFiltersUpdated" show-date-picker="enableTimeRangeSelector"
|
index-patterns="[indexPattern]" filters="filters" on-filters-updated="onFiltersUpdated" show-date-picker="enableTimeRangeSelector"
|
||||||
date-range-from="time.from" date-range-to="time.to" is-refresh-paused="refreshInterval.pause"
|
date-range-from="time.from" date-range-to="time.to" is-refresh-paused="refreshInterval.pause"
|
||||||
refresh-interval="refreshInterval.value" on-refresh-change="onRefreshChange" watch-depth="reference"
|
refresh-interval="refreshInterval.value" on-refresh-change="onRefreshChange" watch-depth="reference"
|
||||||
show-filter-bar="tabView !== 'cluster-monitoring'"></wz-search-bar>
|
show-filter-bar="tabView !== 'cluster-monitoring'"></wz-search-bar>
|
||||||
@ -51,7 +55,7 @@
|
|||||||
<div ng-hide="fetchError" class="dscOverlay">
|
<div ng-hide="fetchError" class="dscOverlay">
|
||||||
<h2 i18n-id="kbn.discover.searchingTitle" i18n-default-message="Searching" class="euiTitle euiTitle--small"></h2>
|
<h2 i18n-id="kbn.discover.searchingTitle" i18n-default-message="Searching" class="euiTitle euiTitle--small"></h2>
|
||||||
<div class="euiSpacer euiSpacer--m"></div>
|
<div class="euiSpacer euiSpacer--m"></div>
|
||||||
<div class="euiLoadingSpinner euiLoadingSpinner--large"></div>
|
<div class="euiLoadingSpinner euiLoadingSpinner--large" data-test-subj="loadingSpinner"></div>
|
||||||
<div class="euiSpacer euiSpacer--m"></div>
|
<div class="euiSpacer euiSpacer--m"></div>
|
||||||
<div ng-show="fetchStatus">{{fetchStatus.complete}} / {{fetchStatus.total}}</div>
|
<div ng-show="fetchStatus">{{fetchStatus.complete}} / {{fetchStatus.total}}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -70,14 +74,15 @@
|
|||||||
class="dscTimechart" ng-if="opts.timefield">
|
class="dscTimechart" ng-if="opts.timefield">
|
||||||
<header class="dscTimechart__header">
|
<header class="dscTimechart__header">
|
||||||
<div class="small">
|
<div class="small">
|
||||||
<span tooltip="{{::'kbn.discover.howToChangeTheTimeTooltip' | i18n: {defaultMessage: 'To change the time, click the clock icon in the navigation bar'} }}">
|
<label for="dscResultsIntervalSelector" tooltip="{{::'kbn.discover.howToChangeTheTimeTooltip' | i18n: {defaultMessage: 'To change the time, click the clock icon in the navigation bar'} }}">
|
||||||
{{toMoment(timeRange.from)}} - {{toMoment(timeRange.to)}}
|
{{toMoment(timeRange.from)}} - {{toMoment(timeRange.to)}}
|
||||||
</span>
|
</label>
|
||||||
|
|
||||||
—
|
—
|
||||||
|
|
||||||
<span class="form-inline">
|
<span class="form-inline">
|
||||||
<select class="dscResults__interval form-control" ng-model="state.interval" ng-options="interval.val as interval.display for interval in intervalOptions | filter: intervalEnabled"
|
<select id="dscResultsIntervalSelector" class="dscResults__interval form-control" ng-model="state.interval"
|
||||||
|
ng-options="interval.val as interval.display for interval in intervalOptions | filter: intervalEnabled"
|
||||||
ng-blur="toggleInterval()" data-test-subj="discoverIntervalSelect">
|
ng-blur="toggleInterval()" data-test-subj="discoverIntervalSelect">
|
||||||
</select>
|
</select>
|
||||||
<span ng-if="bucketInterval.scaled">
|
<span ng-if="bucketInterval.scaled">
|
||||||
|
Loading…
Reference in New Issue
Block a user