[ML] Display info when no datafeed preview results are found (#155650)

When an empty list is returned from the datafeed preview endpoint, it
now displays a callout informing the user that there were no results.
Closes https://github.com/elastic/kibana/issues/153306


![image](https://user-images.githubusercontent.com/22172091/234065738-2c4f3d52-978b-4dec-9129-d755397eaa6f.png)

Also rewrites the datafeed preview component in typescript.
This commit is contained in:
James Gowdy 2023-04-25 13:56:19 +01:00 committed by GitHub
parent 8de71325a3
commit ab039e63f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 104 additions and 112 deletions

View file

@ -1,106 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { EuiSpacer, EuiCallOut, EuiLoadingSpinner } from '@elastic/eui';
import { ml } from '../../../../services/ml_api_service';
import { checkPermission } from '../../../../capabilities/check_capabilities';
import { ML_DATA_PREVIEW_COUNT } from '../../../../../../common/util/job_utils';
import { MLJobEditor } from '../ml_job_editor';
import { FormattedMessage } from '@kbn/i18n-react';
export class DatafeedPreviewPane extends Component {
constructor(props) {
super(props);
this.state = {
previewJson: '',
loading: true,
canPreviewDatafeed: true,
};
}
renderContent() {
const { previewJson, loading, canPreviewDatafeed } = this.state;
if (canPreviewDatafeed === false) {
return (
<EuiCallOut
title={
<FormattedMessage
id="xpack.ml.jobsList.jobDetails.noPermissionToViewDatafeedPreviewTitle"
defaultMessage="You do not have permission to view the datafeed preview"
/>
}
color="warning"
iconType="warning"
>
<p>
<FormattedMessage
id="xpack.ml.jobsList.jobDetails.pleaseContactYourAdministratorLabel"
defaultMessage="Please contact your administrator"
/>
</p>
</EuiCallOut>
);
} else if (loading === true) {
return <EuiLoadingSpinner size="xl" />;
} else {
return <MLJobEditor value={previewJson} readOnly={true} />;
}
}
componentDidMount() {
const canPreviewDatafeed =
checkPermission('canPreviewDatafeed') && this.props.job.datafeed_config !== undefined;
this.setState({ canPreviewDatafeed });
updateDatafeedPreview(this.props.job, canPreviewDatafeed)
.then((previewJson) => {
this.setState({ previewJson, loading: false });
})
.catch((error) => {
console.log('Datafeed preview could not be loaded', error);
this.setState({ loading: false });
});
}
render() {
return (
<React.Fragment>
<EuiSpacer size="s" />
{this.renderContent()}
</React.Fragment>
);
}
}
DatafeedPreviewPane.propTypes = {
job: PropTypes.object.isRequired,
};
function updateDatafeedPreview(job, canPreviewDatafeed) {
return new Promise((resolve, reject) => {
if (canPreviewDatafeed) {
ml.jobs
.datafeedPreview(job.datafeed_config.datafeed_id)
.then((resp) => {
if (Array.isArray(resp)) {
resolve(JSON.stringify(resp.slice(0, ML_DATA_PREVIEW_COUNT), null, 2));
} else {
resolve('');
console.log('Datafeed preview could not be loaded', resp);
}
})
.catch((error) => {
reject(error);
});
}
});
}

View file

@ -0,0 +1,102 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { FC, useEffect, useState } from 'react';
import { EuiCallOut, EuiLoadingSpinner } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { ML_DATA_PREVIEW_COUNT } from '../../../../../../common/util/job_utils';
import { useMlApiContext } from '../../../../contexts/kibana';
import { usePermissionCheck } from '../../../../capabilities/check_capabilities';
import { CombinedJob } from '../../../../../shared';
import { MLJobEditor } from '../ml_job_editor';
interface Props {
job: CombinedJob;
}
export const DatafeedPreviewPane: FC<Props> = ({ job }) => {
const {
jobs: { datafeedPreview },
} = useMlApiContext();
const canPreviewDatafeed = usePermissionCheck('canPreviewDatafeed');
const [loading, setLoading] = useState(false);
const [previewJson, setPreviewJson] = useState<string | null>('');
useEffect(() => {
setLoading(true);
datafeedPreview(job.datafeed_config.datafeed_id).then((resp) => {
if (Array.isArray(resp)) {
if (resp.length === 0) {
setPreviewJson(null);
} else {
setPreviewJson(JSON.stringify(resp.slice(0, ML_DATA_PREVIEW_COUNT), null, 2));
}
} else {
setPreviewJson('');
}
setLoading(false);
});
}, [datafeedPreview, job]);
if (canPreviewDatafeed === false) {
return <InsufficientPermissions />;
}
return loading ? (
<EuiLoadingSpinner size="xl" />
) : (
<>
{previewJson === null ? (
<EmptyResults />
) : (
<MLJobEditor value={previewJson} readOnly={true} />
)}
</>
);
};
const InsufficientPermissions: FC = () => (
<EuiCallOut
title={
<FormattedMessage
id="xpack.ml.jobsList.jobDetails.noPermissionToViewDatafeedPreviewTitle"
defaultMessage="You do not have permission to view the datafeed preview"
/>
}
color="warning"
iconType="warning"
>
<p>
<FormattedMessage
id="xpack.ml.jobsList.jobDetails.pleaseContactYourAdministratorLabel"
defaultMessage="Please contact your administrator"
/>
</p>
</EuiCallOut>
);
const EmptyResults: FC = () => (
<EuiCallOut
title={
<FormattedMessage
id="xpack.ml.jobsList.jobDetails.noResults.title"
defaultMessage="No results"
/>
}
color="warning"
iconType="warning"
>
<p>
<FormattedMessage
id="xpack.ml.jobsList.jobDetails.noResults.text"
defaultMessage="Note: Datafeed preview does not return results from frozen tiers."
/>
</p>
</EuiCallOut>
);

View file

@ -368,8 +368,7 @@ export const jobsApiProvider = (httpService: HttpService) => ({
) {
const body = JSON.stringify({ jobId, snapshotId, replay, end, calendarEvents });
return httpService.http<{
total: number;
categories: Array<{ count?: number; category: Category }>;
success: boolean;
}>({
path: `${ML_BASE_PATH}/jobs/revert_model_snapshot`,
method: 'POST',
@ -379,10 +378,7 @@ export const jobsApiProvider = (httpService: HttpService) => ({
datafeedPreview(datafeedId?: string, job?: Job, datafeed?: Datafeed) {
const body = JSON.stringify({ datafeedId, job, datafeed });
return httpService.http<{
total: number;
categories: Array<{ count?: number; category: Category }>;
}>({
return httpService.http<unknown[]>({
path: `${ML_BASE_PATH}/jobs/datafeed_preview`,
method: 'POST',
body,