wazuh-kibana-app/public/kibana-integrations/kibana-discover.js

1087 lines
35 KiB
JavaScript
Raw Normal View History

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// WAZUH //////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
import { uiModules } from 'ui/modules';
import discoverTemplate from '../templates/kibana-template/kibana-discover-template.html';
2018-09-10 08:32:49 +00:00
uiModules.get('app/wazuh', ['kibana/courier']).directive('kbnDis', [
function() {
return {
2018-09-10 08:32:49 +00:00
restrict: 'E',
scope: {},
template: discoverTemplate
};
2018-09-10 08:32:49 +00:00
}
]);
// Added dependencies (from Kibana module)
import 'ui/pager';
//import 'ui/typeahead';
import 'ui/doc_viewer';
import 'ui/render_directive';
// Added from its index.js
import 'plugins/kibana/discover/saved_searches/saved_searches';
import 'plugins/kibana/discover/directives/no_results';
import 'plugins/kibana/discover/directives/timechart';
import 'ui/collapsible_sidebar';
import 'plugins/kibana/discover/components/field_chooser/field_chooser';
import 'plugins/kibana/discover/controllers/discover';
//import 'plugins/kibana/discover/styles/main.less';
import 'ui/doc_table/components/table_row';
// Research added (further checks needed)
import 'ui/doc_table/doc_table';
import 'ui/styles/sidebar.less';
import 'ui/styles/table.less';
import 'ui/doc_viewer/doc_viewer';
import 'ui/doc_title/doc_title';
import 'ui/style_compile/style_compile';
import 'ui/registry/doc_views';
import 'plugins/kbn_doc_views/kbn_doc_views';
import 'ui/tooltip/tooltip';
import 'ui/pager_control';
import 'ui/pager';
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
import _ from 'lodash';
import angular from 'angular';
import { getSort } from 'ui/doc_table/lib/get_sort';
import * as columnActions from 'ui/doc_table/actions/columns';
2017-12-14 12:28:31 +00:00
import * as filterActions from 'ui/doc_table/actions/filter';
2018-06-14 12:53:46 +00:00
import dateMath from '@kbn/datemath';
import 'ui/doc_table';
import 'ui/visualize';
import 'ui/fixed_scroll';
import 'ui/directives/validate_json';
import 'ui/filters/moment';
import 'ui/index_patterns';
import 'ui/state_management/app_state';
import { timefilter } from 'ui/timefilter';
import 'ui/share';
import 'ui/query_bar';
2018-09-10 08:32:49 +00:00
import {
hasSearchStategyForIndexPattern,
isDefaultTypeIndexPattern
} from 'ui/courier';
import { toastNotifications, getPainlessError } from 'ui/notify';
import { VisProvider } from 'ui/vis';
import { VislibSeriesResponseHandlerProvider } from 'ui/vis/response_handlers/vislib';
import { DocTitleProvider } from 'ui/doc_title';
import PluginsKibanaDiscoverHitSortFnProvider from 'plugins/kibana/discover/_hit_sort_fn';
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
import { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
import { migrateLegacyQuery } from 'ui/utils/migrateLegacyQuery';
2017-12-14 12:28:31 +00:00
import { FilterManagerProvider } from 'ui/filter_manager';
import { visualizationLoader } from 'ui/visualize/loader/visualization_loader';
import { getDocLink } from 'ui/documentation_links';
import { ShareContextMenuExtensionsRegistryProvider } from 'ui/share';
import { getUnhashableStatesProvider } from 'ui/state_management/state_hashing';
import { RequestAdapter } from 'ui/inspector/adapters';
2018-12-13 10:02:53 +00:00
import {
getRequestInspectorStats,
getResponseInspectorStats
} from 'ui/courier/utils/courier_inspector_utils';
import { tabifyAggResponse } from 'ui/agg_response/tabify';
2018-09-03 09:46:55 +00:00
import 'ui/courier/search_strategy/default_search_strategy';
const app = uiModules.get('apps/discover', [
'kibana/notify',
'kibana/courier',
'kibana/url',
'kibana/index_patterns',
'app/wazuh'
]);
2018-09-10 08:32:49 +00:00
app.directive('discoverAppW', function() {
return {
restrict: 'E',
controllerAs: 'discoverApp',
controller: discoverController
};
});
function discoverController(
$element,
$route,
$scope,
$timeout,
$window,
AppState,
Notifier,
Private,
Promise,
config,
courier,
$rootScope,
2018-05-14 15:12:10 +00:00
$location,
kbnUrl,
localStorage,
breadcrumbState,
2018-05-14 15:12:10 +00:00
getAppState,
2018-05-15 12:01:26 +00:00
globalState,
2018-05-15 14:11:35 +00:00
loadedVisualizations,
discoverPendingUpdates
) {
const Vis = Private(VisProvider);
const docTitle = Private(DocTitleProvider);
const HitSortFn = Private(PluginsKibanaDiscoverHitSortFnProvider);
const queryFilter = Private(FilterBarQueryFilterProvider);
const responseHandler = Private(VislibSeriesResponseHandlerProvider).handler;
2017-12-14 12:28:31 +00:00
const filterManager = Private(FilterManagerProvider);
const notify = new Notifier({
location: 'Discover'
});
const getUnhashableStates = Private(getUnhashableStatesProvider);
2018-12-13 10:02:53 +00:00
const shareContextMenuExtensions = Private(
ShareContextMenuExtensionsRegistryProvider
);
const inspectorAdapters = {
requests: new RequestAdapter()
};
//////////////////////////////////////////////////////////
//////////////////// WAZUH ///////////////////////////////
//////////////////////////////////////////////////////////
const calcWzInterval = () => {
let wzInterval = false;
try {
const from = dateMath.parse(timefilter.getTime().from);
2018-09-10 08:32:49 +00:00
const to = dateMath.parse(timefilter.getTime().to);
2018-09-10 08:32:49 +00:00
const totalSeconds = (to - from) / 1000;
if (totalSeconds <= 3600) wzInterval = 'm';
else if (totalSeconds > 3600 && totalSeconds <= 86400) wzInterval = 'h';
else if (totalSeconds > 86400 && totalSeconds <= 604800) wzInterval = 'd';
else if (totalSeconds > 604800 && totalSeconds <= 2419200)
wzInterval = 'w';
else wzInterval = 'M';
2018-07-31 14:47:20 +00:00
} catch (error) {} // eslint-disable-line
return wzInterval;
};
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
$scope.getDocLink = getDocLink;
///////////////////////////////////////////////////////////////////////////////
//////////// WAZUH ////////////////////////////////////////////////////////////
// Old code: //
// $scope.intervalOptions = intervalOptions; //
///////////////////////////////////////////////////////////////////////////////
$scope.intervalOptions = [
{
display: 'Minute',
val: 'm'
},
{
display: 'Hourly',
val: 'h'
},
{
display: 'Daily',
val: 'd'
},
{
display: 'Weekly',
val: 'w'
},
{
display: 'Monthly',
val: 'M'
},
{
display: 'Yearly',
val: 'y'
},
{
display: 'Custom',
val: 'custom'
}
];
//////////////////////////////////////
//////////////////////////////////////
//////////////////////////////////////
$scope.showInterval = false;
$scope.minimumVisibleRows = 50;
2018-09-10 08:32:49 +00:00
$scope.intervalEnabled = function(interval) {
return interval.val !== 'custom';
};
// the saved savedSearch
const savedSearch = $route.current.locals.savedSearch;
$scope.$on('$destroy', savedSearch.destroy);
const $appStatus = ($scope.appStatus = this.appStatus = {
dirty: !savedSearch.id
});
// WAZUH MODIFIED
$scope.topNavMenu = [];
2018-09-10 08:32:49 +00:00
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// WAZUH //////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2018-09-10 08:32:49 +00:00
$scope.toggleRefresh = () => {
$scope.timefilter.refreshInterval.pause = !$scope.timefilter.refreshInterval
.pause;
};
2018-09-10 08:32:49 +00:00
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2018-02-06 23:32:17 +00:00
// the actual courier.SearchSource
$scope.searchSource = savedSearch.searchSource;
$scope.indexPattern = resolveIndexPatternLoading();
$scope.searchSource
.setField('index', $scope.indexPattern)
.setField('highlightAll', true)
.setField('version', true);
// Even when searching rollups, we want to use the default strategy so that we get back a
// document-like response.
$scope.searchSource.setPreferredSearchStrategyId('default');
// searchSource which applies time range
const timeRangeSearchSource = savedSearch.searchSource.create();
2018-12-13 10:02:53 +00:00
if (isDefaultTypeIndexPattern($scope.indexPattern)) {
timeRangeSearchSource.setField('filter', () => {
return timefilter.createFilter($scope.indexPattern);
});
}
$scope.searchSource.setParent(timeRangeSearchSource);
2018-12-13 10:02:53 +00:00
const pageTitleSuffix =
savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : '';
2017-12-14 12:28:31 +00:00
docTitle.change(`Discover${pageTitleSuffix}`);
if (savedSearch.id && savedSearch.title) {
2018-12-13 10:02:53 +00:00
breadcrumbState.set([
{ text: 'Discover', href: '#/discover' },
{ text: savedSearch.title }
]);
} else {
breadcrumbState.set([{ text: 'Discover' }]);
}
let stateMonitor;
2018-12-13 10:02:53 +00:00
const $state = ($scope.state = new AppState(getStateDefaults()));
const getFieldCounts = async () => {
// the field counts aren't set until we have the data back,
// so we wait for the fetch to be done before proceeding
if (!$scope.fetchStatus) {
return $scope.fieldCounts;
}
return await new Promise(resolve => {
2018-09-10 08:32:49 +00:00
const unwatch = $scope.$watch('fetchStatus', newValue => {
if (newValue) {
return;
}
unwatch();
resolve($scope.fieldCounts);
});
});
};
const getSharingDataFields = async () => {
const selectedFields = $state.columns;
2018-12-13 10:02:53 +00:00
if (selectedFields.length === 1 && selectedFields[0] === '_source') {
const fieldCounts = await getFieldCounts();
return {
searchFields: null,
selectFields: _.keys(fieldCounts).sort()
};
}
const timeFieldName = $scope.indexPattern.timeFieldName;
2018-12-13 10:02:53 +00:00
const fields = timeFieldName
? [timeFieldName, ...selectedFields]
: selectedFields;
return {
searchFields: fields,
selectFields: fields
};
};
this.getSharingData = async () => {
const searchSource = $scope.searchSource.createCopy();
const { searchFields, selectFields } = await getSharingDataFields();
searchSource.setField('fields', searchFields);
searchSource.setField('sort', getSort($state.sort, $scope.indexPattern));
searchSource.setField('highlight', null);
searchSource.setField('highlightAll', null);
searchSource.setField('aggs', null);
searchSource.setField('size', null);
const body = await searchSource.getSearchRequestBody();
return {
searchRequest: {
index: searchSource.getField('index').title,
body
},
fields: selectFields,
metaFields: $scope.indexPattern.metaFields,
2018-12-13 10:02:53 +00:00
conflictedTypesFields: $scope.indexPattern.fields
.filter(f => f.type === 'conflict')
.map(f => f.name),
indexPatternId: searchSource.getField('index').id
};
};
$scope.uiState = $state.makeStateful('uiState');
function getStateDefaults() {
//////////////////////////////////////////////////////////
//////////////////// WAZUH ///////////////////////////////
/////////////////////////////////////////////////////////
let wzInterval = calcWzInterval();
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
return {
2018-09-10 08:32:49 +00:00
query: $scope.searchSource.getField('query') || {
query: '',
2018-12-13 10:02:53 +00:00
language:
localStorage.get('kibana.userQueryLanguage') ||
config.get('search:queryLanguage')
2018-09-10 08:32:49 +00:00
},
2018-12-13 10:02:53 +00:00
sort: getSort.array(
savedSearch.sort,
$scope.indexPattern,
config.get('discover:sort:defaultOrder')
),
columns:
savedSearch.columns.length > 0
? savedSearch.columns
: config.get('defaultColumns').slice(),
index: $scope.indexPattern.id,
2018-09-10 08:32:49 +00:00
interval: wzInterval || 'h', //// WAZUH /////
filters: _.cloneDeep($scope.searchSource.getOwnField('filter'))
};
}
$state.index = $scope.indexPattern.id;
$state.sort = getSort.array($state.sort, $scope.indexPattern);
$scope.getBucketIntervalToolTipText = () => {
2018-12-13 10:02:53 +00:00
return `This interval creates ${
$scope.bucketInterval.scale > 1
? 'buckets that are too large'
: 'too many buckets'
}
to show in the selected time range, so it has been scaled to ${
$scope.bucketInterval.description
}`;
};
2018-09-10 08:32:49 +00:00
$scope.$watchCollection('state.columns', function() {
$state.save();
});
$scope.opts = {
// number of records to fetch, then paginate through
sampleSize: config.get('discover:sampleSize'),
2018-12-13 10:02:53 +00:00
timefield:
isDefaultTypeIndexPattern($scope.indexPattern) &&
$scope.indexPattern.timeFieldName,
savedSearch: savedSearch,
2018-12-13 10:02:53 +00:00
indexPatternList: $route.current.locals.ip.list
};
2018-09-10 08:32:49 +00:00
const init = _.once(function() {
stateMonitor = stateMonitorFactory.create($state, getStateDefaults());
2018-09-10 08:32:49 +00:00
stateMonitor.onChange(status => {
$appStatus.dirty = status.dirty || !savedSearch.id;
});
$scope.$on('$destroy', () => stateMonitor.destroy());
2018-09-10 08:32:49 +00:00
$scope.updateDataSource().then(function() {
$scope.$listen(timefilter, 'fetch', function() {
////////////////////////////////////////////////
// WAZUH //
////////////////////////////////////////////////
$state.interval = calcWzInterval() || 'd';
////////////////////////////////////////////////
////////////////////////////////////////////////
////////////////////////////////////////////////
$scope.fetch();
});
2018-12-13 10:02:53 +00:00
$scope.$watchCollection('state.sort', function(sort) {
2018-09-10 08:32:49 +00:00
if (!sort) return;
2018-09-10 08:32:49 +00:00
// get the current sort from {key: val} to ["key", "val"];
2018-12-13 10:02:53 +00:00
const currentSort = Object.entries(
$scope.searchSource.getField('sort')
).pop();
2018-09-10 08:32:49 +00:00
// if the searchSource doesn't know, tell it so
if (!angular.equals(sort, currentSort)) $scope.fetch();
});
2018-09-10 08:32:49 +00:00
// update data source when filters update
$scope.$listen(queryFilter, 'update', function() {
return $scope
.updateDataSource()
.then(function() {
2017-12-14 12:28:31 +00:00
////////////////////////////////////////////////////////////////////////////
/////////////////////////////// WAZUH ///////////////////////////////////
2018-09-10 08:32:49 +00:00
////////////////////////////////////////////////////////////////////////////
discoverPendingUpdates.removeAll();
2018-09-10 08:32:49 +00:00
discoverPendingUpdates.addItem(
$state.query,
queryFilter.getFilters()
);
2018-05-15 07:40:20 +00:00
$rootScope.$broadcast('updateVis');
2017-12-14 12:28:31 +00:00
$rootScope.$broadcast('fetch');
2018-09-10 08:32:49 +00:00
if ($location.search().tab != 'configuration') {
2018-05-15 12:01:26 +00:00
loadedVisualizations.removeAll();
$rootScope.rendered = false;
2018-09-10 08:32:49 +00:00
$rootScope.loadingStatus = 'Fetching data...';
}
2017-12-14 12:28:31 +00:00
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
$state.save();
2018-09-10 08:32:49 +00:00
})
.catch(console.error); // eslint-disable-line
});
2018-09-10 08:32:49 +00:00
// update data source when hitting forward/back and the query changes
$scope.$listen($state, 'fetch_with_changes', function(diff) {
if (diff.indexOf('query') >= 0) $scope.fetch();
});
2018-09-10 08:32:49 +00:00
// fetch data when filters fire fetch event
$scope.$listen(queryFilter, 'fetch', $scope.fetch);
2018-09-10 08:32:49 +00:00
timefilter.enableAutoRefreshSelector();
$scope.$watch('opts.timefield', function(timefield) {
if (!!timefield) {
timefilter.enableTimeRangeSelector();
} else {
timefilter.disableTimeRangeSelector();
}
});
2018-09-10 08:32:49 +00:00
$scope.$watch('state.interval', function() {
$scope.fetch();
});
2018-09-10 08:32:49 +00:00
$scope.$watch('vis.aggs', function() {
// no timefield, no vis, nothing to update
2018-09-10 08:32:49 +00:00
if (!$scope.opts.timefield) return;
2018-09-10 08:32:49 +00:00
const buckets = $scope.vis.getAggConfig().bySchemaGroup.buckets;
2018-09-10 08:32:49 +00:00
if (buckets && buckets.length === 1) {
$scope.bucketInterval = buckets[0].buckets.getInterval();
}
});
2017-12-14 12:28:31 +00:00
2018-09-10 08:32:49 +00:00
$scope.$watch('state.query', $scope.updateQueryAndFetch);
$scope.$watchMulti(
['rows', 'fetchStatus'],
(function updateResultState() {
let prev = {};
const status = {
LOADING: 'loading', // initial data load
READY: 'ready', // results came back
NO_RESULTS: 'none' // no results came back
};
function pick(rows, oldRows, fetchStatus) {
// initial state, pretend we are loading
if (rows == null && oldRows == null) return status.LOADING;
const rowsEmpty = _.isEmpty(rows);
// An undefined fetchStatus means the requests are still being
// prepared to be sent. When all requests are completed,
// fetchStatus is set to null, so it's important that we
// specifically check for undefined to determine a loading status.
const preparingForFetch = _.isUndefined(fetchStatus);
if (preparingForFetch) return status.LOADING;
else if (rowsEmpty && fetchStatus) return status.LOADING;
else if (!rowsEmpty) return status.READY;
else return status.NO_RESULTS;
}
2018-09-10 08:32:49 +00:00
return function() {
const current = {
rows: $scope.rows,
fetchStatus: $scope.fetchStatus
2017-12-14 12:28:31 +00:00
};
2018-09-10 08:32:49 +00:00
$scope.resultState = pick(
current.rows,
prev.rows,
current.fetchStatus,
prev.fetchStatus
);
/////////////////////////////////////////////////////////////////
// Copying it to the rootScope to access it from the Wazuh App //
/////////////////////////////////////////////////////////////////
$rootScope.resultState = $scope.resultState;
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
prev = current;
};
})()
);
2018-09-10 08:32:49 +00:00
if ($scope.opts.timefield) {
setupVisualization();
$scope.updateTime();
}
init.complete = true;
$state.replace();
});
});
async function saveDataSource(saveOptions) {
await $scope.updateDataSource();
savedSearch.columns = $scope.state.columns;
savedSearch.sort = $scope.state.sort;
try {
const id = await savedSearch.save(saveOptions);
$scope.$evalAsync(() => {
stateMonitor.setInitialState($state.toJSON());
if (id) {
toastNotifications.addSuccess({
title: `Search '${savedSearch.title}' was saved`,
2018-12-13 10:02:53 +00:00
'data-test-subj': 'saveSearchSuccess'
});
if (savedSearch.id !== $route.current.params.id) {
kbnUrl.change('/discover/{{id}}', { id: savedSearch.id });
} else {
// Update defaults so that "reload saved query" functions correctly
$state.setDefaults(getStateDefaults());
docTitle.change(savedSearch.lastSavedTitle);
2018-09-10 08:32:49 +00:00
}
}
});
return { id };
2018-12-13 10:02:53 +00:00
} catch (saveError) {
toastNotifications.addDanger({
title: `Search '${savedSearch.title}' was not saved.`,
text: saveError.message
});
return { error: saveError };
}
}
const filtersAreReady = () => {
const currentUrlPath = $location.path();
if (currentUrlPath && !currentUrlPath.includes('wazuh-discover')) {
let filters = queryFilter.getFilters();
filters = Array.isArray(filters)
? filters.filter(
item =>
item &&
item.$state &&
item.$state.store &&
item.$state.store === 'appState'
)
: [];
if (!filters || !filters.length) return false;
}
return true;
2018-11-02 17:23:00 +00:00
};
2018-09-10 08:32:49 +00:00
$scope.opts.fetch = $scope.fetch = function() {
// Wazuh filters are not ready yet
2018-11-02 17:23:00 +00:00
if (!filtersAreReady()) return;
// ignore requests to fetch before the app inits
if (!init.complete) return;
$scope.fetchError = undefined;
$scope.updateTime();
2018-09-10 08:32:49 +00:00
$scope
.updateDataSource()
2017-12-14 12:28:31 +00:00
.then(setupVisualization)
2018-09-10 08:32:49 +00:00
.then(function() {
2017-12-14 12:28:31 +00:00
$state.save();
return courier.fetch();
})
.catch(notify.error);
};
2018-09-10 08:32:49 +00:00
$scope.updateQueryAndFetch = function(query) {
////////////////////////////////////////////////////////////////////////////
/////////////////////////////// WAZUH ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// We don't need this cause the auto-complete feature breaks using this //
/*if ($state.query.language && $state.query.language !== query.language) {
$state.filters = [];
}*/
// Wazuh filters are not ready yet
2018-11-02 17:23:00 +00:00
if (!filtersAreReady()) return;
discoverPendingUpdates.removeAll();
2018-09-10 08:32:49 +00:00
discoverPendingUpdates.addItem($state.query, queryFilter.getFilters());
2018-05-15 07:40:20 +00:00
$rootScope.$broadcast('updateVis');
$rootScope.$broadcast('fetch');
2017-11-28 16:37:31 +00:00
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
$state.query = migrateLegacyQuery(query);
$scope.fetch();
};
function handleSegmentedFetch(segmented) {
function flushResponseData() {
$scope.fetchError = undefined;
$scope.hits = 0;
$scope.failures = [];
$scope.rows = [];
$scope.fieldCounts = {};
}
if (!$scope.rows) flushResponseData();
const sort = $state.sort;
const timeField = $scope.indexPattern.timeFieldName;
/**
* Basically an emum.
*
* opts:
* "time" - sorted by the timefield
* "non-time" - explicitly sorted by a non-time field, NOT THE SAME AS `sortBy !== "time"`
* "implicit" - no sorting set, NOT THE SAME AS "non-time"
*
* @type {String}
*/
2018-09-10 08:32:49 +00:00
const sortBy = (function() {
if (!Array.isArray(sort)) return 'implicit';
else if (sort[0] === '_score') return 'implicit';
else if (sort[0] === timeField) return 'time';
else return 'non-time';
2018-09-10 08:32:49 +00:00
})();
let sortFn = null;
if (sortBy !== 'implicit') {
sortFn = new HitSortFn(sort[1]);
}
if (sort[0] === '_score') {
segmented.setMaxSegments(1);
}
2018-12-13 10:02:53 +00:00
segmented.setDirection(sortBy === 'time' ? sort[1] || 'desc' : 'desc');
segmented.setSortFn(sortFn);
segmented.setSize($scope.opts.sampleSize);
let inspectorRequests = [];
function logResponseInInspector(resp) {
if (inspectorRequests.length > 0) {
const inspectorRequest = inspectorRequests.shift();
inspectorRequest
.stats(getResponseInspectorStats($scope.searchSource, resp))
.ok({ json: resp });
}
}
// triggered when the status updated
2018-12-13 10:02:53 +00:00
segmented.on('status', function(status) {
$scope.fetchStatus = status;
if (status.complete === 0) {
// starting new segmented search request
inspectorAdapters.requests.reset();
inspectorRequests = [];
}
if (status.remaining > 0) {
const inspectorRequest = inspectorAdapters.requests.start(
`Segment ${$scope.fetchStatus.complete}`,
{
2018-12-13 10:02:53 +00:00
description: `This request queries Elasticsearch to fetch the data for the search.`
}
);
inspectorRequest.stats(getRequestInspectorStats($scope.searchSource));
$scope.searchSource.getSearchRequestBody().then(body => {
inspectorRequest.json(body);
});
inspectorRequests.push(inspectorRequest);
}
});
2018-12-13 10:02:53 +00:00
segmented.on('first', function() {
flushResponseData();
});
2018-12-13 10:02:53 +00:00
segmented.on('segment', resp => {
logResponseInInspector(resp);
if (resp._shards.failed > 0) {
$scope.failures = _.union($scope.failures, resp._shards.failures);
2018-12-13 10:02:53 +00:00
$scope.failures = _.uniq($scope.failures, false, function(failure) {
return failure.index + failure.shard + failure.reason;
});
}
});
2018-12-13 10:02:53 +00:00
segmented.on('emptySegment', function(resp) {
logResponseInInspector(resp);
});
2018-09-10 08:32:49 +00:00
segmented.on('mergedSegment', function(merged) {
$scope.mergedEsResp = merged;
if ($scope.opts.timefield) {
const tabifiedData = tabifyAggResponse($scope.vis.aggs, merged);
$scope.searchSource.rawResponse = merged;
2018-12-13 10:02:53 +00:00
Promise.resolve(responseHandler(tabifiedData)).then(resp => {
$scope.visData = resp;
if (
($scope.tabView !== 'panels' ||
$location.path().includes('wazuh-discover')) &&
$scope.tabView !== 'cluster-monitoring'
) {
const visEl = $element.find('#discoverHistogram')[0];
visualizationLoader.render(
visEl,
$scope.vis,
$scope.visData,
$scope.uiState,
{ listenOnChange: true }
);
}
});
}
$scope.hits = merged.hits.total;
const indexPattern = $scope.searchSource.getField('index');
// the merge rows, use a new array to help watchers
$scope.rows = merged.hits.hits.slice();
let counts = $scope.fieldCounts;
// if we haven't counted yet, or need a fresh count because we are sorting, reset the counts
if (!counts || sortFn) counts = $scope.fieldCounts = {};
2018-09-10 08:32:49 +00:00
$scope.rows.forEach(function(hit) {
// skip this work if we have already done it
if (hit.$$_counted) return;
// when we are sorting results, we need to redo the counts each time because the
// "top 500" may change with each response, so don't mark this as counted
if (!sortFn) hit.$$_counted = true;
const fields = _.keys(indexPattern.flattenHit(hit));
let n = fields.length;
let field;
2018-12-13 10:02:53 +00:00
while ((field = fields[--n])) {
2018-09-10 08:32:49 +00:00
// eslint-disable-line
if (counts[field]) counts[field] += 1;
else counts[field] = 1;
}
});
});
2018-09-10 08:32:49 +00:00
segmented.on('complete', function() {
if ($scope.fetchStatus.hitCount === 0) {
flushResponseData();
}
$scope.fetchStatus = null;
});
2017-12-14 12:28:31 +00:00
}
function beginSegmentedFetch() {
2018-12-13 10:02:53 +00:00
$scope.searchSource
.onBeginSegmentedFetch(handleSegmentedFetch)
2018-09-10 08:32:49 +00:00
.catch(error => {
const fetchError = getPainlessError(error);
if (fetchError) {
$scope.fetchError = fetchError;
} else {
notify.error(error);
}
// Restart. This enables auto-refresh functionality.
2017-12-14 12:28:31 +00:00
beginSegmentedFetch();
});
}
beginSegmentedFetch();
2018-09-10 08:32:49 +00:00
$scope.updateTime = function() {
////////////////////////////////////////////////////////////////////////////
/////////////////////////////// WAZUH ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////
2018-09-10 08:32:49 +00:00
if ($location.search().tab != 'configuration') {
2018-05-15 12:01:26 +00:00
loadedVisualizations.removeAll();
$rootScope.rendered = false;
2018-09-10 08:32:49 +00:00
$rootScope.loadingStatus = 'Fetching data...';
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
2018-05-15 11:32:57 +00:00
$scope.timeRange = {
from: dateMath.parse(timefilter.getTime().from),
to: dateMath.parse(timefilter.getTime().to, { roundUp: true })
};
};
2018-09-10 08:32:49 +00:00
$scope.resetQuery = function() {
kbnUrl.change('/discover/{{id}}', { id: $route.current.params.id });
};
2018-09-10 08:32:49 +00:00
$scope.newQuery = function() {
kbnUrl.change('/discover');
};
$scope.updateDataSource = Promise.method(function updateDataSource() {
$scope.searchSource
.setField('size', $scope.opts.sampleSize)
.setField('sort', getSort($state.sort, $scope.indexPattern))
.setField('query', !$state.query ? null : $state.query)
.setField('filter', queryFilter.getFilters());
});
$scope.setSortOrder = function setSortOrder(columnName, direction) {
$scope.state.sort = [columnName, direction];
};
// TODO: On array fields, negating does not negate the combination, rather all terms
2018-09-10 08:32:49 +00:00
$scope.filterQuery = function(field, values, operation) {
// Commented due to https://github.com/elastic/kibana/issues/22426
//$scope.indexPattern.popularizeField(field, 1);
2018-09-10 08:32:49 +00:00
filterActions.addFilter(
field,
values,
operation,
$scope.indexPattern.id,
$scope.state,
filterManager
);
};
$scope.addColumn = function addColumn(columnName) {
// Commented due to https://github.com/elastic/kibana/issues/22426
//$scope.indexPattern.popularizeField(columnName, 1);
columnActions.addColumn($scope.state.columns, columnName);
};
$scope.removeColumn = function removeColumn(columnName) {
// Commented due to https://github.com/elastic/kibana/issues/22426
//$scope.indexPattern.popularizeField(columnName, 1);
columnActions.removeColumn($scope.state.columns, columnName);
};
$scope.moveColumn = function moveColumn(columnName, newIndex) {
columnActions.moveColumn($scope.state.columns, columnName, newIndex);
};
2018-09-10 08:32:49 +00:00
$scope.scrollToTop = function() {
$window.scrollTo(0, 0);
};
2018-09-10 08:32:49 +00:00
$scope.scrollToBottom = function() {
// delay scrolling to after the rows have been rendered
$timeout(() => {
$element.find('#discoverBottomMarker').focus();
}, 0);
};
2018-09-10 08:32:49 +00:00
$scope.showAllRows = function() {
$scope.minimumVisibleRows = $scope.hits;
};
function setupVisualization() {
2017-12-14 12:28:31 +00:00
// If no timefield has been specified we don't create a histogram of messages
if (!$scope.opts.timefield) return;
const visStateAggs = [
{
type: 'count',
schema: 'metric'
},
{
type: 'date_histogram',
schema: 'segment',
params: {
field: $scope.opts.timefield,
interval: $state.interval,
timeRange: timefilter.getTime()
}
}
];
// we have a vis, just modify the aggs
if ($scope.vis) {
const visState = $scope.vis.getEnabledState();
visState.aggs = visStateAggs;
$scope.vis.setState(visState);
2017-12-14 12:28:31 +00:00
} else {
$scope.vis = new Vis($scope.indexPattern, {
title: savedSearch.title,
type: 'histogram',
params: {
addLegend: false,
addTimeMarker: true
},
aggs: visStateAggs
});
2017-12-14 12:28:31 +00:00
$scope.searchSource.onRequestStart((searchSource, searchRequest) => {
2018-09-10 08:32:49 +00:00
return $scope.vis
.getAggConfig()
.onSearchRequestStart(searchSource, searchRequest);
});
2018-09-10 08:32:49 +00:00
$scope.searchSource.setField('aggs', function() {
2018-06-15 08:30:46 +00:00
//////////////////// WAZUH ////////////////////////////////
// Old code: //
// return $scope.vis.getAggConfig().toDsl(); //
///////////////////////////////////////////////////////////
const result = $scope.vis.getAggConfig().toDsl();
2018-09-10 08:32:49 +00:00
if (
result[2] &&
result[2].date_histogram &&
result[2].date_histogram.interval === '0ms'
) {
result[2].date_histogram.interval = '1d';
2018-06-15 08:30:46 +00:00
}
return result;
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
2017-12-14 12:28:31 +00:00
});
}
$scope.vis.filters = {
timeRange: timefilter.getTime()
};
}
function resolveIndexPatternLoading() {
const {
loaded: loadedIndexPattern,
stateVal,
2018-09-10 08:32:49 +00:00
stateValFound
} = $route.current.locals.ip;
2018-09-10 08:32:49 +00:00
const ownIndexPattern = $scope.searchSource.getOwnField('index');
if (ownIndexPattern && !stateVal) {
return ownIndexPattern;
}
if (stateVal && !stateValFound) {
const warningTitle = `"${stateVal}" is not a configured index pattern ID`;
if (ownIndexPattern) {
toastNotifications.addWarning({
title: warningTitle,
2018-09-10 08:32:49 +00:00
text: `Showing the saved index pattern: "${ownIndexPattern.title}" (${
ownIndexPattern.id
})`
});
return ownIndexPattern;
}
toastNotifications.addWarning({
title: warningTitle,
2018-09-10 08:32:49 +00:00
text: `Showing the default index pattern: "${
loadedIndexPattern.title
}" (${loadedIndexPattern.id})`
});
}
return loadedIndexPattern;
}
// Block the UI from loading if the user has loaded a rollup index pattern but it isn't
// supported.
2018-09-10 08:32:49 +00:00
$scope.isUnsupportedIndexPattern =
!isDefaultTypeIndexPattern($route.current.locals.ip.loaded) &&
!hasSearchStategyForIndexPattern($route.current.locals.ip.loaded);
if ($scope.isUnsupportedIndexPattern) {
$scope.unsupportedIndexPatternType = $route.current.locals.ip.loaded.type;
return;
}
2018-09-10 08:32:49 +00:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////// WAZUH //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const loadFilters = (wzCurrentFilters, localChange) => {
2018-05-14 15:12:10 +00:00
const appState = getAppState();
2018-09-10 08:32:49 +00:00
if (!appState || !globalState) {
$timeout(100).then(() => {
return loadFilters(wzCurrentFilters);
});
2018-05-14 15:12:10 +00:00
} else {
$state.filters = localChange ? $state.filters : [];
2018-04-12 10:41:13 +00:00
2018-09-10 08:32:49 +00:00
queryFilter
.addFilters(wzCurrentFilters)
.then(() => {})
.catch(error => console.log(error.message || error)); // eslint-disable-line
2017-11-28 16:37:31 +00:00
}
};
2018-09-10 08:32:49 +00:00
const wzEventFiltersListener = $rootScope.$on(
'wzEventFilters',
(evt, parameters) => {
loadFilters(parameters.filters, parameters.localChange);
}
);
2018-05-15 15:32:29 +00:00
$scope.tabView = $location.search().tabView || 'panels';
2018-09-10 08:32:49 +00:00
const changeTabViewListener = $rootScope.$on(
'changeTabView',
(evt, parameters) => {
$scope.tabView = parameters.tabView || 'panels';
$scope.updateQueryAndFetch($state.query);
}
);
$scope.$on('$destroy', () => {
wzEventFiltersListener();
changeTabViewListener();
});
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
init();
2017-12-14 12:28:31 +00:00
}