[ML] Fixing file import, module creation and results viewing permission checks (#72825)
* [ML] Fixing file import and module creation permission checks * correcting searches on results index * fixing test * removing unnecessary index * updating apidoc * fixing test Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
2d9eaf013b
commit
18df677da7
|
@ -72,6 +72,7 @@ export function getPluginPrivileges() {
|
||||||
const adminMlCapabilitiesKeys = Object.keys(adminMlCapabilities);
|
const adminMlCapabilitiesKeys = Object.keys(adminMlCapabilities);
|
||||||
const allMlCapabilitiesKeys = [...adminMlCapabilitiesKeys, ...userMlCapabilitiesKeys];
|
const allMlCapabilitiesKeys = [...adminMlCapabilitiesKeys, ...userMlCapabilitiesKeys];
|
||||||
// TODO: include ML in base privileges for the `8.0` release: https://github.com/elastic/kibana/issues/71422
|
// TODO: include ML in base privileges for the `8.0` release: https://github.com/elastic/kibana/issues/71422
|
||||||
|
const savedObjects = ['index-pattern', 'dashboard', 'search', 'visualization'];
|
||||||
const privilege = {
|
const privilege = {
|
||||||
app: [PLUGIN_ID, 'kibana'],
|
app: [PLUGIN_ID, 'kibana'],
|
||||||
excludeFromBasePrivileges: true,
|
excludeFromBasePrivileges: true,
|
||||||
|
@ -79,10 +80,6 @@ export function getPluginPrivileges() {
|
||||||
insightsAndAlerting: ['jobsListLink'],
|
insightsAndAlerting: ['jobsListLink'],
|
||||||
},
|
},
|
||||||
catalogue: [PLUGIN_ID],
|
catalogue: [PLUGIN_ID],
|
||||||
savedObject: {
|
|
||||||
all: [],
|
|
||||||
read: ['index-pattern', 'dashboard', 'search', 'visualization'],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -90,11 +87,19 @@ export function getPluginPrivileges() {
|
||||||
...privilege,
|
...privilege,
|
||||||
api: allMlCapabilitiesKeys.map((k) => `ml:${k}`),
|
api: allMlCapabilitiesKeys.map((k) => `ml:${k}`),
|
||||||
ui: allMlCapabilitiesKeys,
|
ui: allMlCapabilitiesKeys,
|
||||||
|
savedObject: {
|
||||||
|
all: savedObjects,
|
||||||
|
read: savedObjects,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
...privilege,
|
...privilege,
|
||||||
api: userMlCapabilitiesKeys.map((k) => `ml:${k}`),
|
api: userMlCapabilitiesKeys.map((k) => `ml:${k}`),
|
||||||
ui: userMlCapabilitiesKeys,
|
ui: userMlCapabilitiesKeys,
|
||||||
|
savedObject: {
|
||||||
|
all: [],
|
||||||
|
read: savedObjects,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ export const BottomBar: FC<BottomBarProps> = ({ mode, onChangeMode, onCancel, di
|
||||||
disableImport ? (
|
disableImport ? (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="xpack.ml.fileDatavisualizer.bottomBar.missingImportPrivilegesMessage"
|
id="xpack.ml.fileDatavisualizer.bottomBar.missingImportPrivilegesMessage"
|
||||||
defaultMessage="You don't have the privileges required to import data"
|
defaultMessage="You require the ingest_admin role to enable data importing"
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { debounce } from 'lodash';
|
||||||
import { importerFactory } from './importer';
|
import { importerFactory } from './importer';
|
||||||
import { ResultsLinks } from '../results_links';
|
import { ResultsLinks } from '../results_links';
|
||||||
import { FilebeatConfigFlyout } from '../filebeat_config_flyout';
|
import { FilebeatConfigFlyout } from '../filebeat_config_flyout';
|
||||||
|
@ -66,6 +67,7 @@ const DEFAULT_STATE = {
|
||||||
indexPatternNameError: '',
|
indexPatternNameError: '',
|
||||||
timeFieldName: undefined,
|
timeFieldName: undefined,
|
||||||
isFilebeatFlyoutVisible: false,
|
isFilebeatFlyoutVisible: false,
|
||||||
|
checkingValidIndex: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ImportView extends Component {
|
export class ImportView extends Component {
|
||||||
|
@ -76,14 +78,12 @@ export class ImportView extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.loadIndexNames();
|
|
||||||
this.loadIndexPatternNames();
|
this.loadIndexPatternNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
clickReset = () => {
|
clickReset = () => {
|
||||||
const state = getDefaultState(this.state, this.props.results);
|
const state = getDefaultState(this.state, this.props.results);
|
||||||
this.setState(state, () => {
|
this.setState(state, () => {
|
||||||
this.loadIndexNames();
|
|
||||||
this.loadIndexPatternNames();
|
this.loadIndexPatternNames();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -326,21 +326,33 @@ export class ImportView extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
onIndexChange = (e) => {
|
onIndexChange = (e) => {
|
||||||
const name = e.target.value;
|
const index = e.target.value;
|
||||||
const { indexNames, indexPattern, indexPatternNames } = this.state;
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
index: name,
|
index,
|
||||||
indexNameError: isIndexNameValid(name, indexNames),
|
checkingValidIndex: true,
|
||||||
// if index pattern has been altered, check that it still matches the inputted index
|
|
||||||
...(indexPattern === ''
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
indexPatternNameError: isIndexPatternNameValid(indexPattern, indexPatternNames, name),
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
this.debounceIndexCheck(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debounceIndexCheck = debounce(async (index) => {
|
||||||
|
if (index === '') {
|
||||||
|
this.setState({ checkingValidIndex: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { exists } = await ml.checkIndexExists({ index });
|
||||||
|
const indexNameError = exists ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.ml.fileDatavisualizer.importView.indexNameAlreadyExistsErrorMessage"
|
||||||
|
defaultMessage="Index name already exists"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
isIndexNameValid(index)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.setState({ checkingValidIndex: false, indexNameError });
|
||||||
|
}, 500);
|
||||||
|
|
||||||
onIndexPatternChange = (e) => {
|
onIndexPatternChange = (e) => {
|
||||||
const name = e.target.value;
|
const name = e.target.value;
|
||||||
const { indexPatternNames, index } = this.state;
|
const { indexPatternNames, index } = this.state;
|
||||||
|
@ -396,12 +408,6 @@ export class ImportView extends Component {
|
||||||
this.props.showBottomBar();
|
this.props.showBottomBar();
|
||||||
};
|
};
|
||||||
|
|
||||||
async loadIndexNames() {
|
|
||||||
const indices = await ml.getIndices();
|
|
||||||
const indexNames = indices.map((i) => i.name);
|
|
||||||
this.setState({ indexNames });
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadIndexPatternNames() {
|
async loadIndexPatternNames() {
|
||||||
await loadIndexPatterns(this.props.indexPatterns);
|
await loadIndexPatterns(this.props.indexPatterns);
|
||||||
const indexPatternNames = getIndexPatternNames();
|
const indexPatternNames = getIndexPatternNames();
|
||||||
|
@ -437,6 +443,7 @@ export class ImportView extends Component {
|
||||||
indexPatternNameError,
|
indexPatternNameError,
|
||||||
timeFieldName,
|
timeFieldName,
|
||||||
isFilebeatFlyoutVisible,
|
isFilebeatFlyoutVisible,
|
||||||
|
checkingValidIndex,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const createPipeline = pipelineString !== '';
|
const createPipeline = pipelineString !== '';
|
||||||
|
@ -459,7 +466,8 @@ export class ImportView extends Component {
|
||||||
index === '' ||
|
index === '' ||
|
||||||
indexNameError !== '' ||
|
indexNameError !== '' ||
|
||||||
(createIndexPattern === true && indexPatternNameError !== '') ||
|
(createIndexPattern === true && indexPatternNameError !== '') ||
|
||||||
initialized === true;
|
initialized === true ||
|
||||||
|
checkingValidIndex === true;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiPage data-test-subj="mlPageFileDataVisImport">
|
<EuiPage data-test-subj="mlPageFileDataVisImport">
|
||||||
|
@ -655,16 +663,7 @@ function getDefaultState(state, results) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIndexNameValid(name, indexNames) {
|
function isIndexNameValid(name) {
|
||||||
if (indexNames.find((i) => i === name)) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.ml.fileDatavisualizer.importView.indexNameAlreadyExistsErrorMessage"
|
|
||||||
defaultMessage="Index name already exists"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const reg = new RegExp('[\\\\/*?"<>|\\s,#]+');
|
const reg = new RegExp('[\\\\/*?"<>|\\s,#]+');
|
||||||
if (
|
if (
|
||||||
name !== name.toLowerCase() || // name should be lowercase
|
name !== name.toLowerCase() || // name should be lowercase
|
||||||
|
|
|
@ -11,7 +11,6 @@ import url from 'url';
|
||||||
|
|
||||||
import { DASHBOARD_APP_URL_GENERATOR } from '../../../../../../../../src/plugins/dashboard/public';
|
import { DASHBOARD_APP_URL_GENERATOR } from '../../../../../../../../src/plugins/dashboard/public';
|
||||||
|
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from '../../../../../common/constants/index_patterns';
|
|
||||||
import { getPartitioningFieldNames } from '../../../../../common/util/job_utils';
|
import { getPartitioningFieldNames } from '../../../../../common/util/job_utils';
|
||||||
import { parseInterval } from '../../../../../common/util/parse_interval';
|
import { parseInterval } from '../../../../../common/util/parse_interval';
|
||||||
import { replaceTokensInUrlValue, isValidLabel } from '../../../util/custom_url_utils';
|
import { replaceTokensInUrlValue, isValidLabel } from '../../../util/custom_url_utils';
|
||||||
|
@ -295,11 +294,11 @@ export function getTestUrl(job, customUrl) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
ml.esSearch({
|
ml.results
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
.anomalySearch({
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
body,
|
body,
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.hits.total > 0) {
|
if (resp.hits.total > 0) {
|
||||||
const record = resp.hits.hits[0]._source;
|
const record = resp.hits.hits[0]._source;
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from '../../../../../../common/constants/index_patterns';
|
|
||||||
import { escapeForElasticsearchQuery } from '../../../../util/string_utils';
|
import { escapeForElasticsearchQuery } from '../../../../util/string_utils';
|
||||||
import { ml } from '../../../../services/ml_api_service';
|
import { ml } from '../../../../services/ml_api_service';
|
||||||
|
|
||||||
|
@ -53,78 +52,78 @@ export function getScoresByRecord(
|
||||||
jobIdFilterStr += `"${String(firstSplitField.value).replace(/\\/g, '\\\\')}"`;
|
jobIdFilterStr += `"${String(firstSplitField.value).replace(/\\/g, '\\\\')}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
ml.esSearch({
|
ml.results
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
.anomalySearch({
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
bool: {
|
bool: {
|
||||||
filter: [
|
filter: [
|
||||||
{
|
{
|
||||||
query_string: {
|
query_string: {
|
||||||
query: 'result_type:record',
|
query: 'result_type:record',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
bool: {
|
||||||
bool: {
|
must: [
|
||||||
must: [
|
{
|
||||||
{
|
range: {
|
||||||
range: {
|
timestamp: {
|
||||||
timestamp: {
|
gte: earliestMs,
|
||||||
gte: earliestMs,
|
lte: latestMs,
|
||||||
lte: latestMs,
|
format: 'epoch_millis',
|
||||||
format: 'epoch_millis',
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
query_string: {
|
||||||
|
query: jobIdFilterStr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aggs: {
|
||||||
|
detector_index: {
|
||||||
|
terms: {
|
||||||
|
field: 'detector_index',
|
||||||
|
order: {
|
||||||
|
recordScore: 'desc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aggs: {
|
||||||
|
recordScore: {
|
||||||
|
max: {
|
||||||
|
field: 'record_score',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
byTime: {
|
||||||
|
date_histogram: {
|
||||||
|
field: 'timestamp',
|
||||||
|
interval,
|
||||||
|
min_doc_count: 1,
|
||||||
|
extended_bounds: {
|
||||||
|
min: earliestMs,
|
||||||
|
max: latestMs,
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
query_string: {
|
aggs: {
|
||||||
query: jobIdFilterStr,
|
recordScore: {
|
||||||
|
max: {
|
||||||
|
field: 'record_score',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
aggs: {
|
|
||||||
detector_index: {
|
|
||||||
terms: {
|
|
||||||
field: 'detector_index',
|
|
||||||
order: {
|
|
||||||
recordScore: 'desc',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
aggs: {
|
|
||||||
recordScore: {
|
|
||||||
max: {
|
|
||||||
field: 'record_score',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
byTime: {
|
|
||||||
date_histogram: {
|
|
||||||
field: 'timestamp',
|
|
||||||
interval,
|
|
||||||
min_doc_count: 1,
|
|
||||||
extended_bounds: {
|
|
||||||
min: earliestMs,
|
|
||||||
max: latestMs,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
aggs: {
|
|
||||||
recordScore: {
|
|
||||||
max: {
|
|
||||||
field: 'record_score',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
.then((resp: any) => {
|
.then((resp: any) => {
|
||||||
const detectorsByIndex = get(resp, ['aggregations', 'detector_index', 'buckets'], []);
|
const detectorsByIndex = get(resp, ['aggregations', 'detector_index', 'buckets'], []);
|
||||||
detectorsByIndex.forEach((dtr: any) => {
|
detectorsByIndex.forEach((dtr: any) => {
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from '../../../common/constants/index_patterns';
|
|
||||||
import { ml } from './ml_api_service';
|
import { ml } from './ml_api_service';
|
||||||
|
|
||||||
// Gets a basic summary of the most recently run forecasts for the specified
|
// Gets a basic summary of the most recently run forecasts for the specified
|
||||||
|
@ -48,19 +47,19 @@ function getForecastsSummary(job, query, earliestMs, maxResults) {
|
||||||
filterCriteria.push(query);
|
filterCriteria.push(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
ml.esSearch({
|
ml.results
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
.anomalySearch({
|
||||||
size: maxResults,
|
size: maxResults,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
bool: {
|
bool: {
|
||||||
filter: filterCriteria,
|
filter: filterCriteria,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
sort: [{ forecast_create_timestamp: { order: 'desc' } }],
|
||||||
},
|
},
|
||||||
sort: [{ forecast_create_timestamp: { order: 'desc' } }],
|
})
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.hits.total !== 0) {
|
if (resp.hits.total !== 0) {
|
||||||
obj.forecasts = resp.hits.hits.map((hit) => hit._source);
|
obj.forecasts = resp.hits.hits.map((hit) => hit._source);
|
||||||
|
@ -106,29 +105,29 @@ function getForecastDateRange(job, forecastId) {
|
||||||
// TODO - add in criteria for detector index and entity fields (by, over, partition)
|
// TODO - add in criteria for detector index and entity fields (by, over, partition)
|
||||||
// once forecasting with these parameters is supported.
|
// once forecasting with these parameters is supported.
|
||||||
|
|
||||||
ml.esSearch({
|
ml.results
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
.anomalySearch({
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
bool: {
|
bool: {
|
||||||
filter: filterCriteria,
|
filter: filterCriteria,
|
||||||
},
|
|
||||||
},
|
|
||||||
aggs: {
|
|
||||||
earliest: {
|
|
||||||
min: {
|
|
||||||
field: 'timestamp',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
latest: {
|
aggs: {
|
||||||
max: {
|
earliest: {
|
||||||
field: 'timestamp',
|
min: {
|
||||||
|
field: 'timestamp',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
latest: {
|
||||||
|
max: {
|
||||||
|
field: 'timestamp',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
obj.earliest = _.get(resp, 'aggregations.earliest.value', null);
|
obj.earliest = _.get(resp, 'aggregations.earliest.value', null);
|
||||||
obj.latest = _.get(resp, 'aggregations.latest.value', null);
|
obj.latest = _.get(resp, 'aggregations.latest.value', null);
|
||||||
|
@ -243,9 +242,8 @@ function getForecastData(
|
||||||
min: aggType.min,
|
min: aggType.min,
|
||||||
};
|
};
|
||||||
|
|
||||||
return ml
|
return ml.results
|
||||||
.esSearch$({
|
.anomalySearch$({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
|
@ -343,18 +341,18 @@ function getForecastRequestStats(job, forecastId) {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
ml.esSearch({
|
ml.results
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
.anomalySearch({
|
||||||
size: 1,
|
size: 1,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
bool: {
|
bool: {
|
||||||
filter: filterCriteria,
|
filter: filterCriteria,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.hits.total !== 0) {
|
if (resp.hits.total !== 0) {
|
||||||
obj.stats = _.first(resp.hits.hits)._source;
|
obj.stats = _.first(resp.hits.hits)._source;
|
||||||
|
|
|
@ -96,4 +96,22 @@ export const resultsApiProvider = (httpService: HttpService) => ({
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
anomalySearch(obj: any) {
|
||||||
|
const body = JSON.stringify(obj);
|
||||||
|
return httpService.http<any>({
|
||||||
|
path: `${basePath()}/results/anomaly_search`,
|
||||||
|
method: 'POST',
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
anomalySearch$(obj: any) {
|
||||||
|
const body = JSON.stringify(obj);
|
||||||
|
return httpService.http$<any>({
|
||||||
|
path: `${basePath()}/results/anomaly_search`,
|
||||||
|
method: 'POST',
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -262,8 +262,8 @@ export function resultsServiceRxProvider(mlApiServices: MlApiServices) {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return mlApiServices
|
return mlApiServices.results
|
||||||
.esSearch$({
|
.anomalySearch$({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
|
@ -399,8 +399,8 @@ export function resultsServiceRxProvider(mlApiServices: MlApiServices) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return mlApiServices
|
return mlApiServices.results
|
||||||
.esSearch$({
|
.anomalySearch$({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
size: maxResults !== undefined ? maxResults : 100,
|
size: maxResults !== undefined ? maxResults : 100,
|
||||||
|
@ -484,8 +484,8 @@ export function resultsServiceRxProvider(mlApiServices: MlApiServices) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return mlApiServices
|
return mlApiServices.results
|
||||||
.esSearch$({
|
.anomalySearch$({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import _ from 'lodash';
|
||||||
|
|
||||||
import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils';
|
import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils';
|
||||||
import { escapeForElasticsearchQuery } from '../../util/string_utils';
|
import { escapeForElasticsearchQuery } from '../../util/string_utils';
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from '../../../../common/constants/index_patterns';
|
|
||||||
import {
|
import {
|
||||||
ANOMALY_SWIM_LANE_HARD_LIMIT,
|
ANOMALY_SWIM_LANE_HARD_LIMIT,
|
||||||
SWIM_LANE_DEFAULT_PAGE_SIZE,
|
SWIM_LANE_DEFAULT_PAGE_SIZE,
|
||||||
|
@ -66,9 +65,8 @@ export function resultsServiceProvider(mlApiServices) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mlApiServices
|
mlApiServices.results
|
||||||
.esSearch({
|
.anomalySearch({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
|
@ -238,9 +236,8 @@ export function resultsServiceProvider(mlApiServices) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mlApiServices
|
mlApiServices.results
|
||||||
.esSearch({
|
.anomalySearch({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
|
@ -378,9 +375,8 @@ export function resultsServiceProvider(mlApiServices) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mlApiServices
|
mlApiServices.results
|
||||||
.esSearch({
|
.anomalySearch({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
|
@ -560,9 +556,8 @@ export function resultsServiceProvider(mlApiServices) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mlApiServices
|
mlApiServices.results
|
||||||
.esSearch({
|
.anomalySearch({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
|
@ -721,9 +716,8 @@ export function resultsServiceProvider(mlApiServices) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mlApiServices
|
mlApiServices.results
|
||||||
.esSearch({
|
.anomalySearch({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: maxResults !== undefined ? maxResults : 100,
|
size: maxResults !== undefined ? maxResults : 100,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
body: {
|
body: {
|
||||||
|
@ -854,9 +848,8 @@ export function resultsServiceProvider(mlApiServices) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mlApiServices
|
mlApiServices.results
|
||||||
.esSearch({
|
.anomalySearch({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: maxResults !== undefined ? maxResults : 100,
|
size: maxResults !== undefined ? maxResults : 100,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
body: {
|
body: {
|
||||||
|
@ -980,9 +973,8 @@ export function resultsServiceProvider(mlApiServices) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mlApiServices
|
mlApiServices.results
|
||||||
.esSearch({
|
.anomalySearch({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: maxResults !== undefined ? maxResults : 100,
|
size: maxResults !== undefined ? maxResults : 100,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
body: {
|
body: {
|
||||||
|
@ -1307,9 +1299,8 @@ export function resultsServiceProvider(mlApiServices) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
mlApiServices
|
mlApiServices.results
|
||||||
.esSearch({
|
.anomalySearch({
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
query: {
|
query: {
|
||||||
|
|
|
@ -18,17 +18,17 @@ import {
|
||||||
// - ML_ANNOTATIONS_INDEX_ALIAS_READ alias is present
|
// - ML_ANNOTATIONS_INDEX_ALIAS_READ alias is present
|
||||||
// - ML_ANNOTATIONS_INDEX_ALIAS_WRITE alias is present
|
// - ML_ANNOTATIONS_INDEX_ALIAS_WRITE alias is present
|
||||||
export async function isAnnotationsFeatureAvailable({
|
export async function isAnnotationsFeatureAvailable({
|
||||||
callAsCurrentUser,
|
callAsInternalUser,
|
||||||
}: ILegacyScopedClusterClient) {
|
}: ILegacyScopedClusterClient) {
|
||||||
try {
|
try {
|
||||||
const indexParams = { index: ML_ANNOTATIONS_INDEX_PATTERN };
|
const indexParams = { index: ML_ANNOTATIONS_INDEX_PATTERN };
|
||||||
|
|
||||||
const annotationsIndexExists = await callAsCurrentUser('indices.exists', indexParams);
|
const annotationsIndexExists = await callAsInternalUser('indices.exists', indexParams);
|
||||||
if (!annotationsIndexExists) {
|
if (!annotationsIndexExists) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotationsReadAliasExists = await callAsCurrentUser('indices.existsAlias', {
|
const annotationsReadAliasExists = await callAsInternalUser('indices.existsAlias', {
|
||||||
index: ML_ANNOTATIONS_INDEX_ALIAS_READ,
|
index: ML_ANNOTATIONS_INDEX_ALIAS_READ,
|
||||||
name: ML_ANNOTATIONS_INDEX_ALIAS_READ,
|
name: ML_ANNOTATIONS_INDEX_ALIAS_READ,
|
||||||
});
|
});
|
||||||
|
@ -37,7 +37,7 @@ export async function isAnnotationsFeatureAvailable({
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotationsWriteAliasExists = await callAsCurrentUser('indices.existsAlias', {
|
const annotationsWriteAliasExists = await callAsInternalUser('indices.existsAlias', {
|
||||||
index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
|
index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
|
||||||
name: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
|
name: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,8 +52,8 @@ describe('annotation_service', () => {
|
||||||
|
|
||||||
const response = await deleteAnnotation(annotationMockId);
|
const response = await deleteAnnotation(annotationMockId);
|
||||||
|
|
||||||
expect(mockFunct.callAsCurrentUser.mock.calls[0][0]).toBe('delete');
|
expect(mockFunct.callAsInternalUser.mock.calls[0][0]).toBe('delete');
|
||||||
expect(mockFunct.callAsCurrentUser.mock.calls[0][1]).toEqual(deleteParamsMock);
|
expect(mockFunct.callAsInternalUser.mock.calls[0][1]).toEqual(deleteParamsMock);
|
||||||
expect(response).toBe(acknowledgedResponseMock);
|
expect(response).toBe(acknowledgedResponseMock);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -73,8 +73,8 @@ describe('annotation_service', () => {
|
||||||
|
|
||||||
const response: GetResponse = await getAnnotations(indexAnnotationArgsMock);
|
const response: GetResponse = await getAnnotations(indexAnnotationArgsMock);
|
||||||
|
|
||||||
expect(mockFunct.callAsCurrentUser.mock.calls[0][0]).toBe('search');
|
expect(mockFunct.callAsInternalUser.mock.calls[0][0]).toBe('search');
|
||||||
expect(mockFunct.callAsCurrentUser.mock.calls[0][1]).toEqual(getAnnotationsRequestMock);
|
expect(mockFunct.callAsInternalUser.mock.calls[0][1]).toEqual(getAnnotationsRequestMock);
|
||||||
expect(Object.keys(response.annotations)).toHaveLength(1);
|
expect(Object.keys(response.annotations)).toHaveLength(1);
|
||||||
expect(response.annotations[jobIdMock]).toHaveLength(2);
|
expect(response.annotations[jobIdMock]).toHaveLength(2);
|
||||||
expect(isAnnotations(response.annotations[jobIdMock])).toBeTruthy();
|
expect(isAnnotations(response.annotations[jobIdMock])).toBeTruthy();
|
||||||
|
@ -89,7 +89,7 @@ describe('annotation_service', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mlClusterClientSpyError: any = {
|
const mlClusterClientSpyError: any = {
|
||||||
callAsCurrentUser: jest.fn(() => {
|
callAsInternalUser: jest.fn(() => {
|
||||||
return Promise.resolve(mockEsError);
|
return Promise.resolve(mockEsError);
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -124,10 +124,10 @@ describe('annotation_service', () => {
|
||||||
|
|
||||||
const response = await indexAnnotation(annotationMock, usernameMock);
|
const response = await indexAnnotation(annotationMock, usernameMock);
|
||||||
|
|
||||||
expect(mockFunct.callAsCurrentUser.mock.calls[0][0]).toBe('index');
|
expect(mockFunct.callAsInternalUser.mock.calls[0][0]).toBe('index');
|
||||||
|
|
||||||
// test if the annotation has been correctly augmented
|
// test if the annotation has been correctly augmented
|
||||||
const indexParamsCheck = mockFunct.callAsCurrentUser.mock.calls[0][1];
|
const indexParamsCheck = mockFunct.callAsInternalUser.mock.calls[0][1];
|
||||||
const annotation = indexParamsCheck.body;
|
const annotation = indexParamsCheck.body;
|
||||||
expect(annotation.create_username).toBe(usernameMock);
|
expect(annotation.create_username).toBe(usernameMock);
|
||||||
expect(annotation.modified_username).toBe(usernameMock);
|
expect(annotation.modified_username).toBe(usernameMock);
|
||||||
|
@ -154,10 +154,10 @@ describe('annotation_service', () => {
|
||||||
|
|
||||||
const response = await indexAnnotation(annotationMock, usernameMock);
|
const response = await indexAnnotation(annotationMock, usernameMock);
|
||||||
|
|
||||||
expect(mockFunct.callAsCurrentUser.mock.calls[0][0]).toBe('index');
|
expect(mockFunct.callAsInternalUser.mock.calls[0][0]).toBe('index');
|
||||||
|
|
||||||
// test if the annotation has been correctly augmented
|
// test if the annotation has been correctly augmented
|
||||||
const indexParamsCheck = mockFunct.callAsCurrentUser.mock.calls[0][1];
|
const indexParamsCheck = mockFunct.callAsInternalUser.mock.calls[0][1];
|
||||||
const annotation = indexParamsCheck.body;
|
const annotation = indexParamsCheck.body;
|
||||||
expect(annotation.create_username).toBe(usernameMock);
|
expect(annotation.create_username).toBe(usernameMock);
|
||||||
expect(annotation.modified_username).toBe(usernameMock);
|
expect(annotation.modified_username).toBe(usernameMock);
|
||||||
|
@ -196,9 +196,9 @@ describe('annotation_service', () => {
|
||||||
|
|
||||||
await indexAnnotation(annotation, modifiedUsernameMock);
|
await indexAnnotation(annotation, modifiedUsernameMock);
|
||||||
|
|
||||||
expect(mockFunct.callAsCurrentUser.mock.calls[1][0]).toBe('index');
|
expect(mockFunct.callAsInternalUser.mock.calls[1][0]).toBe('index');
|
||||||
// test if the annotation has been correctly updated
|
// test if the annotation has been correctly updated
|
||||||
const indexParamsCheck = mockFunct.callAsCurrentUser.mock.calls[1][1];
|
const indexParamsCheck = mockFunct.callAsInternalUser.mock.calls[1][1];
|
||||||
const modifiedAnnotation = indexParamsCheck.body;
|
const modifiedAnnotation = indexParamsCheck.body;
|
||||||
expect(modifiedAnnotation.annotation).toBe(modifiedAnnotationText);
|
expect(modifiedAnnotation.annotation).toBe(modifiedAnnotationText);
|
||||||
expect(modifiedAnnotation.create_username).toBe(originalUsernameMock);
|
expect(modifiedAnnotation.create_username).toBe(originalUsernameMock);
|
||||||
|
|
|
@ -76,7 +76,7 @@ export interface DeleteParams {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function annotationProvider({ callAsCurrentUser }: ILegacyScopedClusterClient) {
|
export function annotationProvider({ callAsInternalUser }: ILegacyScopedClusterClient) {
|
||||||
async function indexAnnotation(annotation: Annotation, username: string) {
|
async function indexAnnotation(annotation: Annotation, username: string) {
|
||||||
if (isAnnotation(annotation) === false) {
|
if (isAnnotation(annotation) === false) {
|
||||||
// No need to translate, this will not be exposed in the UI.
|
// No need to translate, this will not be exposed in the UI.
|
||||||
|
@ -103,7 +103,7 @@ export function annotationProvider({ callAsCurrentUser }: ILegacyScopedClusterCl
|
||||||
delete params.body.key;
|
delete params.body.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await callAsCurrentUser('index', params);
|
return await callAsInternalUser('index', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAnnotations({
|
async function getAnnotations({
|
||||||
|
@ -286,7 +286,7 @@ export function annotationProvider({ callAsCurrentUser }: ILegacyScopedClusterCl
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await callAsCurrentUser('search', params);
|
const resp = await callAsInternalUser('search', params);
|
||||||
|
|
||||||
if (resp.error !== undefined && resp.message !== undefined) {
|
if (resp.error !== undefined && resp.message !== undefined) {
|
||||||
// No need to translate, this will not be exposed in the UI.
|
// No need to translate, this will not be exposed in the UI.
|
||||||
|
@ -335,7 +335,7 @@ export function annotationProvider({ callAsCurrentUser }: ILegacyScopedClusterCl
|
||||||
refresh: 'wait_for',
|
refresh: 'wait_for',
|
||||||
};
|
};
|
||||||
|
|
||||||
return await callAsCurrentUser('delete', param);
|
return await callAsInternalUser('delete', param);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -23,7 +23,7 @@ interface BoolQuery {
|
||||||
bool: { [key: string]: any };
|
bool: { [key: string]: any };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function analyticsAuditMessagesProvider({ callAsCurrentUser }: ILegacyScopedClusterClient) {
|
export function analyticsAuditMessagesProvider({ callAsInternalUser }: ILegacyScopedClusterClient) {
|
||||||
// search for audit messages,
|
// search for audit messages,
|
||||||
// analyticsId is optional. without it, all analytics will be listed.
|
// analyticsId is optional. without it, all analytics will be listed.
|
||||||
async function getAnalyticsAuditMessages(analyticsId: string) {
|
async function getAnalyticsAuditMessages(analyticsId: string) {
|
||||||
|
@ -69,7 +69,7 @@ export function analyticsAuditMessagesProvider({ callAsCurrentUser }: ILegacySco
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await callAsCurrentUser('search', {
|
const resp = await callAsInternalUser('search', {
|
||||||
index: ML_NOTIFICATION_INDEX_PATTERN,
|
index: ML_NOTIFICATION_INDEX_PATTERN,
|
||||||
ignore_unavailable: true,
|
ignore_unavailable: true,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
|
|
|
@ -34,7 +34,7 @@ const anomalyDetectorTypeFilter = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function jobAuditMessagesProvider({ callAsCurrentUser, callAsInternalUser }) {
|
export function jobAuditMessagesProvider({ callAsInternalUser }) {
|
||||||
// search for audit messages,
|
// search for audit messages,
|
||||||
// jobId is optional. without it, all jobs will be listed.
|
// jobId is optional. without it, all jobs will be listed.
|
||||||
// from is optional and should be a string formatted in ES time units. e.g. 12h, 1d, 7d
|
// from is optional and should be a string formatted in ES time units. e.g. 12h, 1d, 7d
|
||||||
|
@ -100,7 +100,7 @@ export function jobAuditMessagesProvider({ callAsCurrentUser, callAsInternalUser
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await callAsCurrentUser('search', {
|
const resp = await callAsInternalUser('search', {
|
||||||
index: ML_NOTIFICATION_INDEX_PATTERN,
|
index: ML_NOTIFICATION_INDEX_PATTERN,
|
||||||
ignore_unavailable: true,
|
ignore_unavailable: true,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
|
@ -155,7 +155,7 @@ export function jobAuditMessagesProvider({ callAsCurrentUser, callAsInternalUser
|
||||||
levelsPerJobAggSize = jobIds.length;
|
levelsPerJobAggSize = jobIds.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = await callAsCurrentUser('search', {
|
const resp = await callAsInternalUser('search', {
|
||||||
index: ML_NOTIFICATION_INDEX_PATTERN,
|
index: ML_NOTIFICATION_INDEX_PATTERN,
|
||||||
ignore_unavailable: true,
|
ignore_unavailable: true,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
|
|
|
@ -48,7 +48,7 @@ interface Results {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobsProvider(mlClusterClient: ILegacyScopedClusterClient) {
|
export function jobsProvider(mlClusterClient: ILegacyScopedClusterClient) {
|
||||||
const { callAsCurrentUser, callAsInternalUser } = mlClusterClient;
|
const { callAsInternalUser } = mlClusterClient;
|
||||||
|
|
||||||
const { forceDeleteDatafeed, getDatafeedIdsByJobId } = datafeedsProvider(mlClusterClient);
|
const { forceDeleteDatafeed, getDatafeedIdsByJobId } = datafeedsProvider(mlClusterClient);
|
||||||
const { getAuditMessagesSummary } = jobAuditMessagesProvider(mlClusterClient);
|
const { getAuditMessagesSummary } = jobAuditMessagesProvider(mlClusterClient);
|
||||||
|
@ -400,7 +400,7 @@ export function jobsProvider(mlClusterClient: ILegacyScopedClusterClient) {
|
||||||
const detailed = true;
|
const detailed = true;
|
||||||
const jobIds = [];
|
const jobIds = [];
|
||||||
try {
|
try {
|
||||||
const tasksList = await callAsCurrentUser('tasks.list', { actions, detailed });
|
const tasksList = await callAsInternalUser('tasks.list', { actions, detailed });
|
||||||
Object.keys(tasksList.nodes).forEach((nodeId) => {
|
Object.keys(tasksList.nodes).forEach((nodeId) => {
|
||||||
const tasks = tasksList.nodes[nodeId].tasks;
|
const tasks = tasksList.nodes[nodeId].tasks;
|
||||||
Object.keys(tasks).forEach((taskId) => {
|
Object.keys(tasks).forEach((taskId) => {
|
||||||
|
|
|
@ -9,9 +9,9 @@ import { ILegacyScopedClusterClient } from 'kibana/server';
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from '../../../../../common/constants/index_patterns';
|
import { ML_RESULTS_INDEX_PATTERN } from '../../../../../common/constants/index_patterns';
|
||||||
import { CategoryId, Category } from '../../../../../common/types/categories';
|
import { CategoryId, Category } from '../../../../../common/types/categories';
|
||||||
|
|
||||||
export function topCategoriesProvider({ callAsCurrentUser }: ILegacyScopedClusterClient) {
|
export function topCategoriesProvider({ callAsInternalUser }: ILegacyScopedClusterClient) {
|
||||||
async function getTotalCategories(jobId: string): Promise<{ total: number }> {
|
async function getTotalCategories(jobId: string): Promise<{ total: number }> {
|
||||||
const totalResp = await callAsCurrentUser('search', {
|
const totalResp = await callAsInternalUser('search', {
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
|
@ -37,7 +37,7 @@ export function topCategoriesProvider({ callAsCurrentUser }: ILegacyScopedCluste
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTopCategoryCounts(jobId: string, numberOfCategories: number) {
|
async function getTopCategoryCounts(jobId: string, numberOfCategories: number) {
|
||||||
const top: SearchResponse<any> = await callAsCurrentUser('search', {
|
const top: SearchResponse<any> = await callAsInternalUser('search', {
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
|
@ -99,7 +99,7 @@ export function topCategoriesProvider({ callAsCurrentUser }: ILegacyScopedCluste
|
||||||
field: 'category_id',
|
field: 'category_id',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const result: SearchResponse<any> = await callAsCurrentUser('search', {
|
const result: SearchResponse<any> = await callAsInternalUser('search', {
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
size,
|
size,
|
||||||
body: {
|
body: {
|
||||||
|
|
|
@ -75,7 +75,6 @@ function getFieldObject(fieldType: PartitionFieldsType, aggs: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPartitionFieldsValuesFactory = ({
|
export const getPartitionFieldsValuesFactory = ({
|
||||||
callAsCurrentUser,
|
|
||||||
callAsInternalUser,
|
callAsInternalUser,
|
||||||
}: ILegacyScopedClusterClient) =>
|
}: ILegacyScopedClusterClient) =>
|
||||||
/**
|
/**
|
||||||
|
@ -102,7 +101,7 @@ export const getPartitionFieldsValuesFactory = ({
|
||||||
|
|
||||||
const isModelPlotEnabled = job?.model_plot_config?.enabled;
|
const isModelPlotEnabled = job?.model_plot_config?.enabled;
|
||||||
|
|
||||||
const resp = await callAsCurrentUser('search', {
|
const resp = await callAsInternalUser('search', {
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
|
|
|
@ -31,7 +31,7 @@ interface Influencer {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resultsServiceProvider(mlClusterClient: ILegacyScopedClusterClient) {
|
export function resultsServiceProvider(mlClusterClient: ILegacyScopedClusterClient) {
|
||||||
const { callAsCurrentUser } = mlClusterClient;
|
const { callAsInternalUser } = mlClusterClient;
|
||||||
// Obtains data for the anomalies table, aggregating anomalies by day or hour as requested.
|
// Obtains data for the anomalies table, aggregating anomalies by day or hour as requested.
|
||||||
// Return an Object with properties 'anomalies' and 'interval' (interval used to aggregate anomalies,
|
// Return an Object with properties 'anomalies' and 'interval' (interval used to aggregate anomalies,
|
||||||
// one of day, hour or second. Note 'auto' can be provided as the aggregationInterval in the request,
|
// one of day, hour or second. Note 'auto' can be provided as the aggregationInterval in the request,
|
||||||
|
@ -134,7 +134,7 @@ export function resultsServiceProvider(mlClusterClient: ILegacyScopedClusterClie
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp: SearchResponse<any> = await callAsCurrentUser('search', {
|
const resp: SearchResponse<any> = await callAsInternalUser('search', {
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
size: maxRecords,
|
size: maxRecords,
|
||||||
|
@ -288,7 +288,7 @@ export function resultsServiceProvider(mlClusterClient: ILegacyScopedClusterClie
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const resp = await callAsCurrentUser('search', query);
|
const resp = await callAsInternalUser('search', query);
|
||||||
const maxScore = _.get(resp, ['aggregations', 'max_score', 'value'], null);
|
const maxScore = _.get(resp, ['aggregations', 'max_score', 'value'], null);
|
||||||
|
|
||||||
return { maxScore };
|
return { maxScore };
|
||||||
|
@ -326,7 +326,7 @@ export function resultsServiceProvider(mlClusterClient: ILegacyScopedClusterClie
|
||||||
// Size of job terms agg, consistent with maximum number of jobs supported by Java endpoints.
|
// Size of job terms agg, consistent with maximum number of jobs supported by Java endpoints.
|
||||||
const maxJobs = 10000;
|
const maxJobs = 10000;
|
||||||
|
|
||||||
const resp = await callAsCurrentUser('search', {
|
const resp = await callAsInternalUser('search', {
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
size: 0,
|
size: 0,
|
||||||
body: {
|
body: {
|
||||||
|
@ -370,7 +370,7 @@ export function resultsServiceProvider(mlClusterClient: ILegacyScopedClusterClie
|
||||||
// from the given index and job ID.
|
// from the given index and job ID.
|
||||||
// Returned response consists of a list of examples against category ID.
|
// Returned response consists of a list of examples against category ID.
|
||||||
async function getCategoryExamples(jobId: string, categoryIds: any, maxExamples: number) {
|
async function getCategoryExamples(jobId: string, categoryIds: any, maxExamples: number) {
|
||||||
const resp = await callAsCurrentUser('search', {
|
const resp = await callAsInternalUser('search', {
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
size: ANOMALIES_TABLE_DEFAULT_QUERY_SIZE, // Matches size of records in anomaly summary table.
|
size: ANOMALIES_TABLE_DEFAULT_QUERY_SIZE, // Matches size of records in anomaly summary table.
|
||||||
|
@ -405,7 +405,7 @@ export function resultsServiceProvider(mlClusterClient: ILegacyScopedClusterClie
|
||||||
// Returned response contains four properties - categoryId, regex, examples
|
// Returned response contains four properties - categoryId, regex, examples
|
||||||
// and terms (space delimited String of the common tokens matched in values of the category).
|
// and terms (space delimited String of the common tokens matched in values of the category).
|
||||||
async function getCategoryDefinition(jobId: string, categoryId: string) {
|
async function getCategoryDefinition(jobId: string, categoryId: string) {
|
||||||
const resp = await callAsCurrentUser('search', {
|
const resp = await callAsInternalUser('search', {
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
rest_total_hits_as_int: true,
|
rest_total_hits_as_int: true,
|
||||||
size: 1,
|
size: 1,
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
"GetMaxAnomalyScore",
|
"GetMaxAnomalyScore",
|
||||||
"GetCategoryExamples",
|
"GetCategoryExamples",
|
||||||
"GetPartitionFieldsValues",
|
"GetPartitionFieldsValues",
|
||||||
|
"AnomalySearch",
|
||||||
|
|
||||||
"Modules",
|
"Modules",
|
||||||
"DataRecognizer",
|
"DataRecognizer",
|
||||||
|
|
|
@ -513,7 +513,7 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat
|
||||||
mlLicense.fullLicenseAPIGuard(async (context, request, response) => {
|
mlLicense.fullLicenseAPIGuard(async (context, request, response) => {
|
||||||
try {
|
try {
|
||||||
const { analyticsId } = request.params;
|
const { analyticsId } = request.params;
|
||||||
const results = await context.ml!.mlClient.callAsCurrentUser(
|
const results = await context.ml!.mlClient.callAsInternalUser(
|
||||||
'ml.updateDataFrameAnalytics',
|
'ml.updateDataFrameAnalytics',
|
||||||
{
|
{
|
||||||
body: request.body,
|
body: request.body,
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RequestHandlerContext } from 'kibana/server';
|
import { RequestHandlerContext } from 'kibana/server';
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
import { wrapError } from '../client/error_wrapper';
|
import { wrapError } from '../client/error_wrapper';
|
||||||
import { RouteInitialization } from '../types';
|
import { RouteInitialization } from '../types';
|
||||||
import {
|
import {
|
||||||
|
@ -15,6 +16,7 @@ import {
|
||||||
partitionFieldValuesSchema,
|
partitionFieldValuesSchema,
|
||||||
} from './schemas/results_service_schema';
|
} from './schemas/results_service_schema';
|
||||||
import { resultsServiceProvider } from '../models/results_service';
|
import { resultsServiceProvider } from '../models/results_service';
|
||||||
|
import { ML_RESULTS_INDEX_PATTERN } from '../../common/constants/index_patterns';
|
||||||
|
|
||||||
function getAnomaliesTableData(context: RequestHandlerContext, payload: any) {
|
function getAnomaliesTableData(context: RequestHandlerContext, payload: any) {
|
||||||
const rs = resultsServiceProvider(context.ml!.mlClient);
|
const rs = resultsServiceProvider(context.ml!.mlClient);
|
||||||
|
@ -232,4 +234,35 @@ export function resultsServiceRoutes({ router, mlLicense }: RouteInitialization)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @apiGroup ResultsService
|
||||||
|
*
|
||||||
|
* @api {post} /api/ml/results/anomaly_search Performs a search on the anomaly results index
|
||||||
|
* @apiName AnomalySearch
|
||||||
|
*/
|
||||||
|
router.post(
|
||||||
|
{
|
||||||
|
path: '/api/ml/results/anomaly_search',
|
||||||
|
validate: {
|
||||||
|
body: schema.maybe(schema.any()),
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
tags: ['access:ml:canGetJobs'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mlLicense.fullLicenseAPIGuard(async (context, request, response) => {
|
||||||
|
const body = {
|
||||||
|
...request.body,
|
||||||
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
return response.ok({
|
||||||
|
body: await context.ml!.mlClient.callAsInternalUser('search', body),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return response.customError(wrapError(error));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ export function getMlSystemProvider(
|
||||||
return {
|
return {
|
||||||
mlSystemProvider(mlClusterClient: ILegacyScopedClusterClient, request: KibanaRequest) {
|
mlSystemProvider(mlClusterClient: ILegacyScopedClusterClient, request: KibanaRequest) {
|
||||||
// const hasMlCapabilities = getHasMlCapabilities(request);
|
// const hasMlCapabilities = getHasMlCapabilities(request);
|
||||||
const { callAsCurrentUser, callAsInternalUser } = mlClusterClient;
|
const { callAsInternalUser } = mlClusterClient;
|
||||||
return {
|
return {
|
||||||
async mlCapabilities() {
|
async mlCapabilities() {
|
||||||
isMinimumLicense();
|
isMinimumLicense();
|
||||||
|
@ -77,7 +77,7 @@ export function getMlSystemProvider(
|
||||||
// integration and currently alerting does not supply a request object.
|
// integration and currently alerting does not supply a request object.
|
||||||
// await hasMlCapabilities(['canAccessML']);
|
// await hasMlCapabilities(['canAccessML']);
|
||||||
|
|
||||||
return callAsCurrentUser('search', {
|
return callAsInternalUser('search', {
|
||||||
...searchParams,
|
...searchParams,
|
||||||
index: ML_RESULTS_INDEX_PATTERN,
|
index: ML_RESULTS_INDEX_PATTERN,
|
||||||
});
|
});
|
||||||
|
|
|
@ -101,7 +101,7 @@ export function MachineLearningDataVisualizerFileBasedProvider(
|
||||||
},
|
},
|
||||||
|
|
||||||
async startImportAndWaitForProcessing() {
|
async startImportAndWaitForProcessing() {
|
||||||
await testSubjects.click('mlFileDataVisImportButton');
|
await testSubjects.clickWhenNotDisabled('mlFileDataVisImportButton');
|
||||||
await retry.tryForTime(60 * 1000, async () => {
|
await retry.tryForTime(60 * 1000, async () => {
|
||||||
await testSubjects.existOrFail('mlFileImportSuccessCallout');
|
await testSubjects.existOrFail('mlFileImportSuccessCallout');
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue