mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* [ML] multi-line json support for analytics job editor * [ML] advanced editor with xjson * [ML] add jest mock for XJsonMode * [ML] add xJson mode to the json tab * [ML] fix mocks
This commit is contained in:
parent
8fd6f43470
commit
617c874e2a
16 changed files with 97 additions and 39 deletions
|
@ -4,5 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export function MLJobEditor(props: any): any;
|
||||
export const EDITOR_MODE: any;
|
||||
export function XJsonMode() {}
|
|
@ -5,3 +5,4 @@
|
|||
*/
|
||||
|
||||
export { usePartialState } from './use_partial_state';
|
||||
export { useXJsonMode, xJsonMode } from './use_x_json_mode';
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
collapseLiteralStrings,
|
||||
expandLiteralStrings,
|
||||
XJsonMode,
|
||||
} from '../../../../shared_imports';
|
||||
|
||||
export const xJsonMode = new XJsonMode();
|
||||
|
||||
export const useXJsonMode = (json: string) => {
|
||||
const [xJson, setXJson] = useState(expandLiteralStrings(json));
|
||||
|
||||
return {
|
||||
xJson,
|
||||
setXJson,
|
||||
xJsonMode,
|
||||
convertToJson: collapseLiteralStrings,
|
||||
};
|
||||
};
|
|
@ -17,8 +17,10 @@ import {
|
|||
} from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { collapseLiteralStrings } from '../../../../../../../../../../../src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools';
|
||||
|
||||
import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form';
|
||||
import { xJsonMode } from '../../../../../components/custom_hooks';
|
||||
|
||||
export const CreateAnalyticsAdvancedEditor: FC<CreateAnalyticsFormProps> = ({ actions, state }) => {
|
||||
const {
|
||||
|
@ -42,7 +44,8 @@ export const CreateAnalyticsAdvancedEditor: FC<CreateAnalyticsFormProps> = ({ ac
|
|||
const onChange = (str: string) => {
|
||||
setAdvancedEditorRawString(str);
|
||||
try {
|
||||
setJobConfig(JSON.parse(str));
|
||||
const resultJobConfig = JSON.parse(collapseLiteralStrings(str));
|
||||
setJobConfig(resultJobConfig);
|
||||
} catch (e) {
|
||||
resetAdvancedEditorMessages();
|
||||
}
|
||||
|
@ -119,7 +122,7 @@ export const CreateAnalyticsAdvancedEditor: FC<CreateAnalyticsFormProps> = ({ ac
|
|||
style={{ maxWidth: '100%' }}
|
||||
>
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
mode={xJsonMode}
|
||||
width="100%"
|
||||
value={advancedEditorRawString}
|
||||
onChange={onChange}
|
||||
|
|
|
@ -29,6 +29,8 @@ jest.mock('react', () => {
|
|||
return { ...r, memo: (x: any) => x };
|
||||
});
|
||||
|
||||
jest.mock('../../../../../../../shared_imports');
|
||||
|
||||
describe('Data Frame Analytics: <CreateAnalyticsButton />', () => {
|
||||
test('Minimal initialization', () => {
|
||||
const { getLastHookValue } = getMountedHook();
|
||||
|
|
|
@ -29,7 +29,7 @@ export const CreateAnalyticsFlyout: FC<CreateAnalyticsFormProps> = ({
|
|||
const { isJobCreated, isJobStarted, isModalButtonDisabled, isValid } = state;
|
||||
|
||||
return (
|
||||
<EuiFlyout size="s" onClose={closeModal} data-test-subj="mlAnalyticsCreateJobFlyout">
|
||||
<EuiFlyout size="m" onClose={closeModal} data-test-subj="mlAnalyticsCreateJobFlyout">
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle>
|
||||
<h2 data-test-subj="mlDataFrameAnalyticsFlyoutHeaderTitle">
|
||||
|
|
|
@ -9,12 +9,12 @@ import React from 'react';
|
|||
|
||||
import { EuiTitle, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { MLJobEditor, EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor';
|
||||
import { MLJobEditor, ML_EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor';
|
||||
|
||||
export function FileContents({ data, format, numberOfLines }) {
|
||||
let mode = EDITOR_MODE.TEXT;
|
||||
if (format === EDITOR_MODE.JSON) {
|
||||
mode = EDITOR_MODE.JSON;
|
||||
let mode = ML_EDITOR_MODE.TEXT;
|
||||
if (format === ML_EDITOR_MODE.JSON) {
|
||||
mode = ML_EDITOR_MODE.JSON;
|
||||
}
|
||||
|
||||
const formattedData = limitByNumberOfLines(data, numberOfLines);
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
EuiFlexItem,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { MLJobEditor, EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor';
|
||||
import { MLJobEditor, ML_EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor';
|
||||
const EDITOR_HEIGHT = '300px';
|
||||
|
||||
export function AdvancedSettings({
|
||||
|
@ -149,7 +149,7 @@ function IndexSettings({ initialized, data, onChange }) {
|
|||
fullWidth
|
||||
>
|
||||
<MLJobEditor
|
||||
mode={EDITOR_MODE.JSON}
|
||||
mode={ML_EDITOR_MODE.JSON}
|
||||
readOnly={initialized === true}
|
||||
value={data}
|
||||
height={EDITOR_HEIGHT}
|
||||
|
@ -175,7 +175,7 @@ function Mappings({ initialized, data, onChange }) {
|
|||
fullWidth
|
||||
>
|
||||
<MLJobEditor
|
||||
mode={EDITOR_MODE.JSON}
|
||||
mode={ML_EDITOR_MODE.JSON}
|
||||
readOnly={initialized === true}
|
||||
value={data}
|
||||
height={EDITOR_HEIGHT}
|
||||
|
@ -201,7 +201,7 @@ function IngestPipeline({ initialized, data, onChange }) {
|
|||
fullWidth
|
||||
>
|
||||
<MLJobEditor
|
||||
mode={EDITOR_MODE.JSON}
|
||||
mode={ML_EDITOR_MODE.JSON}
|
||||
readOnly={initialized === true}
|
||||
value={data}
|
||||
height={EDITOR_HEIGHT}
|
||||
|
|
|
@ -30,6 +30,7 @@ import { mlMessageBarService } from '../../../../components/messagebar';
|
|||
import { withKibana } from '../../../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { collapseLiteralStrings } from '../../../../../../shared_imports';
|
||||
|
||||
export class EditJobFlyoutUI extends Component {
|
||||
_initialJobFormState = null;
|
||||
|
@ -225,7 +226,7 @@ export class EditJobFlyoutUI extends Component {
|
|||
groups: this.state.jobGroups,
|
||||
mml: this.state.jobModelMemoryLimit,
|
||||
detectorDescriptions: this.state.jobDetectorDescriptions,
|
||||
datafeedQuery: this.state.datafeedQuery,
|
||||
datafeedQuery: collapseLiteralStrings(this.state.datafeedQuery),
|
||||
datafeedQueryDelay: this.state.datafeedQueryDelay,
|
||||
datafeedFrequency: this.state.datafeedFrequency,
|
||||
datafeedScrollSize: this.state.datafeedScrollSize,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { EuiFieldText, EuiForm, EuiFormRow, EuiSpacer, EuiFieldNumber } from '@e
|
|||
import { calculateDatafeedFrequencyDefaultSeconds } from '../../../../../../../common/util/job_utils';
|
||||
import { getNewJobDefaults } from '../../../../../services/ml_server_info';
|
||||
import { parseInterval } from '../../../../../../../common/util/parse_interval';
|
||||
import { MLJobEditor } from '../../ml_job_editor';
|
||||
import { MLJobEditor, ML_EDITOR_MODE } from '../../ml_job_editor';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
function getDefaults(bucketSpan, jobDefaults) {
|
||||
|
@ -85,7 +85,12 @@ export class Datafeed extends Component {
|
|||
}
|
||||
style={{ maxWidth: 'inherit' }}
|
||||
>
|
||||
<MLJobEditor value={query} onChange={this.onQueryChange} height="200px" />
|
||||
<MLJobEditor
|
||||
mode={ML_EDITOR_MODE.XJSON}
|
||||
value={query}
|
||||
onChange={this.onQueryChange}
|
||||
height="200px"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={
|
||||
|
|
|
@ -9,14 +9,14 @@ import React from 'react';
|
|||
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { MLJobEditor } from '../ml_job_editor';
|
||||
import { ML_EDITOR_MODE, MLJobEditor } from '../ml_job_editor';
|
||||
|
||||
export function JsonPane({ job }) {
|
||||
const json = JSON.stringify(job, null, 2);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<EuiSpacer size="s" />
|
||||
<MLJobEditor value={json} readOnly={true} />
|
||||
<MLJobEditor value={json} readOnly={true} mode={ML_EDITOR_MODE.XJSON} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { MLJobEditor, EDITOR_MODE } from './ml_job_editor';
|
||||
export { MLJobEditor, ML_EDITOR_MODE } from './ml_job_editor';
|
||||
|
|
|
@ -4,23 +4,38 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { EuiCodeEditor } from '@elastic/eui';
|
||||
import { expandLiteralStrings } from '../../../../../../shared_imports';
|
||||
import { xJsonMode } from '../../../../components/custom_hooks';
|
||||
|
||||
export const EDITOR_MODE = { TEXT: 'text', JSON: 'json' };
|
||||
export const ML_EDITOR_MODE = { TEXT: 'text', JSON: 'json', XJSON: xJsonMode };
|
||||
|
||||
export function MLJobEditor({
|
||||
interface MlJobEditorProps {
|
||||
value: string;
|
||||
height?: string;
|
||||
width?: string;
|
||||
mode?: typeof ML_EDITOR_MODE[keyof typeof ML_EDITOR_MODE];
|
||||
readOnly?: boolean;
|
||||
syntaxChecking?: boolean;
|
||||
theme?: string;
|
||||
onChange?: Function;
|
||||
}
|
||||
export const MLJobEditor: FC<MlJobEditorProps> = ({
|
||||
value,
|
||||
height = '500px',
|
||||
width = '100%',
|
||||
mode = EDITOR_MODE.JSON,
|
||||
mode = ML_EDITOR_MODE.JSON,
|
||||
readOnly = false,
|
||||
syntaxChecking = true,
|
||||
theme = 'textmate',
|
||||
onChange = () => {},
|
||||
}) {
|
||||
}) => {
|
||||
if (mode === ML_EDITOR_MODE.XJSON) {
|
||||
value = expandLiteralStrings(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiCodeEditor
|
||||
value={value}
|
||||
|
@ -40,14 +55,4 @@ export function MLJobEditor({
|
|||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
MLJobEditor.propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
height: PropTypes.string,
|
||||
width: PropTypes.string,
|
||||
mode: PropTypes.string,
|
||||
readOnly: PropTypes.bool,
|
||||
syntaxChecking: PropTypes.bool,
|
||||
theme: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
};
|
|
@ -18,8 +18,9 @@ import {
|
|||
EuiFlyoutBody,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { collapseLiteralStrings } from '../../../../../../../../shared_imports';
|
||||
import { Datafeed } from '../../../../common/job_creator/configs';
|
||||
import { MLJobEditor } from '../../../../../jobs_list/components/ml_job_editor';
|
||||
import { ML_EDITOR_MODE, MLJobEditor } from '../../../../../jobs_list/components/ml_job_editor';
|
||||
import { isValidJson } from '../../../../../../../../common/util/validation_utils';
|
||||
import { JobCreatorContext } from '../../job_creator_context';
|
||||
|
||||
|
@ -68,10 +69,11 @@ export const JsonEditorFlyout: FC<Props> = ({ isDisabled, jobEditorMode, datafee
|
|||
|
||||
function onDatafeedChange(json: string) {
|
||||
setDatafeedConfigString(json);
|
||||
let valid = isValidJson(json);
|
||||
const jsonValue = collapseLiteralStrings(json);
|
||||
let valid = isValidJson(jsonValue);
|
||||
if (valid) {
|
||||
// ensure that the user hasn't altered the indices list in the json.
|
||||
const { indices }: Datafeed = JSON.parse(json);
|
||||
const { indices }: Datafeed = JSON.parse(jsonValue);
|
||||
const originalIndices = jobCreator.indices.sort();
|
||||
valid =
|
||||
originalIndices.length === indices.length &&
|
||||
|
@ -82,7 +84,7 @@ export const JsonEditorFlyout: FC<Props> = ({ isDisabled, jobEditorMode, datafee
|
|||
|
||||
function onSave() {
|
||||
const jobConfig = JSON.parse(jobConfigString);
|
||||
const datafeedConfig = JSON.parse(datafeedConfigString);
|
||||
const datafeedConfig = JSON.parse(collapseLiteralStrings(datafeedConfigString));
|
||||
jobCreator.cloneFromExistingJob(jobConfig, datafeedConfig);
|
||||
jobCreatorUpdate();
|
||||
setShowJsonFlyout(false);
|
||||
|
@ -191,6 +193,7 @@ const Contents: FC<{
|
|||
<MLJobEditor
|
||||
value={value}
|
||||
height={EDITOR_HEIGHT}
|
||||
mode={ML_EDITOR_MODE.XJSON}
|
||||
readOnly={editJson === false}
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
|
|
@ -52,6 +52,8 @@ jest.mock('../../util/dependency_cache', () => ({
|
|||
getToastNotifications: () => ({ addSuccess: jest.fn(), addDanger: jest.fn() }),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../shared_imports');
|
||||
|
||||
describe('TimeSeriesExplorerUrlStateManager', () => {
|
||||
test('Initial render shows "No single metric jobs found"', () => {
|
||||
const props = {
|
||||
|
|
12
x-pack/legacy/plugins/ml/shared_imports.ts
Normal file
12
x-pack/legacy/plugins/ml/shared_imports.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { XJsonMode } from '../../../plugins/es_ui_shared/console_lang/ace/modes/x_json';
|
||||
|
||||
export {
|
||||
collapseLiteralStrings,
|
||||
expandLiteralStrings,
|
||||
} from '../../../../src/plugins/es_ui_shared/console_lang/lib';
|
Loading…
Add table
Add a link
Reference in a new issue