mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Fix runtime mapping texts to runtime fields, add transform switch modal (#97008)
This commit is contained in:
parent
64f30a224e
commit
71e8118984
25 changed files with 280 additions and 121 deletions
|
@ -77,15 +77,15 @@ describe('ML runtime field utils', () => {
|
|||
).toBe(false);
|
||||
});
|
||||
|
||||
it('allows object with most basic runtime mapping', () => {
|
||||
it('allows object with most basic runtime field', () => {
|
||||
expect(isRuntimeMappings({ fieldName: { type: 'keyword' } })).toBe(true);
|
||||
});
|
||||
it('allows object with multiple most basic runtime mappings', () => {
|
||||
it('allows object with multiple most basic runtime fields', () => {
|
||||
expect(
|
||||
isRuntimeMappings({ fieldName1: { type: 'keyword' }, fieldName2: { type: 'keyword' } })
|
||||
).toBe(true);
|
||||
});
|
||||
it('allows object with runtime mappings including scripts', () => {
|
||||
it('allows object with runtime fields including scripts', () => {
|
||||
expect(
|
||||
isRuntimeMappings({
|
||||
fieldName1: { type: 'keyword' },
|
||||
|
|
|
@ -364,7 +364,7 @@ export const ConfigurationStepForm: FC<ConfigurationStepProps> = ({
|
|||
}
|
||||
return !option.key?.includes(runtimeMappingKey);
|
||||
});
|
||||
// Runtime mappings have been removed
|
||||
// Runtime fields have been removed
|
||||
if (runtimeMappings === undefined && runtimeMappingsUpdated === true) {
|
||||
setDependentVariableOptions(filteredOptions);
|
||||
} else if (runtimeMappings) {
|
||||
|
@ -374,7 +374,7 @@ export const ConfigurationStepForm: FC<ConfigurationStepProps> = ({
|
|||
}
|
||||
}
|
||||
|
||||
// Update includes - remove previous runtime mappings then add supported runtime fields to includes
|
||||
// Update includes - remove previous runtime fields then add supported runtime fields to includes
|
||||
const updatedIncludes = includes.filter((field) => {
|
||||
const isRemovedRuntimeField = previousRuntimeMapping && previousRuntimeMapping[field];
|
||||
return !isRemovedRuntimeField;
|
||||
|
|
|
@ -20,19 +20,48 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { XJsonMode } from '@kbn/ace';
|
||||
import { RuntimeField } from '../../../../../../../../../../src/plugins/data/common/index_patterns';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form';
|
||||
import { XJson } from '../../../../../../../../../../src/plugins/es_ui_shared/public';
|
||||
import { getCombinedRuntimeMappings } from '../../../../../components/data_grid/common';
|
||||
import { isPopulatedObject } from '../../../../../../../common/util/object_utils';
|
||||
import { RuntimeMappingsEditor } from './runtime_mappings_editor';
|
||||
import { isRuntimeMappings } from '../../../../../../../common';
|
||||
import { SwitchModal } from './switch_modal';
|
||||
|
||||
const advancedEditorsSidebarWidth = '220px';
|
||||
const COPY_TO_CLIPBOARD_RUNTIME_MAPPINGS = i18n.translate(
|
||||
const COPY_RUNTIME_FIELDS_TO_CLIPBOARD_TEXT = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.indexPreview.copyRuntimeMappingsClipboardTooltip',
|
||||
{
|
||||
defaultMessage: 'Copy Dev Console statement of the runtime mappings to the clipboard.',
|
||||
defaultMessage: 'Copy Dev Console statement of the runtime fields to the clipboard.',
|
||||
}
|
||||
);
|
||||
|
||||
const APPLY_CHANGES_TEXT = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.advancedSourceEditorApplyButtonText',
|
||||
{
|
||||
defaultMessage: 'Apply changes',
|
||||
}
|
||||
);
|
||||
|
||||
const RUNTIME_FIELDS_EDITOR_HELP_TEXT = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.advancedRuntimeFieldsEditorHelpText',
|
||||
{
|
||||
defaultMessage: 'The advanced editor allows you to edit the runtime fields of the source.',
|
||||
}
|
||||
);
|
||||
|
||||
const EDIT_SWITCH_LABEL_TEXT = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.advancedEditorRuntimeFieldsSwitchLabel',
|
||||
{
|
||||
defaultMessage: 'Edit runtime fields',
|
||||
}
|
||||
);
|
||||
|
||||
const RUNTIME_FIELDS_LABEL_TEXT = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.runtimeFieldsLabel',
|
||||
{
|
||||
defaultMessage: 'Runtime fields',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -45,12 +74,15 @@ interface Props {
|
|||
state: CreateAnalyticsFormProps['state'];
|
||||
}
|
||||
|
||||
type RuntimeMappings = Record<string, RuntimeField>;
|
||||
|
||||
export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
||||
const [isRuntimeMappingsEditorEnabled, setIsRuntimeMappingsEditorEnabled] = useState<boolean>(
|
||||
false
|
||||
);
|
||||
const [
|
||||
isRuntimeMappingsEditorSwitchModalVisible,
|
||||
setRuntimeMappingsEditorSwitchModalVisible,
|
||||
] = useState<boolean>(false);
|
||||
|
||||
const [
|
||||
isRuntimeMappingsEditorApplyButtonEnabled,
|
||||
setIsRuntimeMappingsEditorApplyButtonEnabled,
|
||||
|
@ -59,7 +91,6 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
advancedEditorRuntimeMappingsLastApplied,
|
||||
setAdvancedEditorRuntimeMappingsLastApplied,
|
||||
] = useState<string>();
|
||||
const [advancedEditorRuntimeMappings, setAdvancedEditorRuntimeMappings] = useState<string>();
|
||||
|
||||
const { setFormState } = actions;
|
||||
const { jobType, previousRuntimeMapping, runtimeMappings } = state.form;
|
||||
|
@ -90,22 +121,22 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
runtimeMappingsUpdated: true,
|
||||
previousRuntimeMapping: previous,
|
||||
});
|
||||
setAdvancedEditorRuntimeMappings(prettySourceConfig);
|
||||
setAdvancedRuntimeMappingsConfig(prettySourceConfig);
|
||||
setAdvancedEditorRuntimeMappingsLastApplied(prettySourceConfig);
|
||||
setIsRuntimeMappingsEditorApplyButtonEnabled(false);
|
||||
};
|
||||
|
||||
// If switching to KQL after updating via editor - reset search
|
||||
const toggleEditorHandler = (reset = false) => {
|
||||
if (reset === true) {
|
||||
setFormState({ runtimeMappingsUpdated: false });
|
||||
}
|
||||
if (isRuntimeMappingsEditorEnabled === false) {
|
||||
setAdvancedEditorRuntimeMappingsLastApplied(advancedEditorRuntimeMappings);
|
||||
setFormState({
|
||||
runtimeMappingsUpdated: false,
|
||||
});
|
||||
|
||||
setAdvancedRuntimeMappingsConfig(advancedEditorRuntimeMappingsLastApplied ?? '');
|
||||
}
|
||||
|
||||
setIsRuntimeMappingsEditorEnabled(!isRuntimeMappingsEditorEnabled);
|
||||
setIsRuntimeMappingsEditorApplyButtonEnabled(false);
|
||||
setIsRuntimeMappingsEditorApplyButtonEnabled(isRuntimeMappings(runtimeMappings));
|
||||
};
|
||||
|
||||
useEffect(function getInitialRuntimeMappings() {
|
||||
|
@ -114,8 +145,11 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
runtimeMappings
|
||||
);
|
||||
|
||||
const prettySourceConfig = JSON.stringify(combinedRuntimeMappings, null, 2);
|
||||
|
||||
if (combinedRuntimeMappings) {
|
||||
setAdvancedRuntimeMappingsConfig(JSON.stringify(combinedRuntimeMappings, null, 2));
|
||||
setAdvancedRuntimeMappingsConfig(prettySourceConfig);
|
||||
setAdvancedEditorRuntimeMappingsLastApplied(prettySourceConfig);
|
||||
setFormState({
|
||||
runtimeMappings: combinedRuntimeMappings,
|
||||
});
|
||||
|
@ -125,12 +159,7 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
return (
|
||||
<>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFormRow
|
||||
fullWidth={true}
|
||||
label={i18n.translate('xpack.ml.dataframe.analytics.createWizard.runtimeMappingsLabel', {
|
||||
defaultMessage: 'Runtime mappings',
|
||||
})}
|
||||
>
|
||||
<EuiFormRow fullWidth={true} label={RUNTIME_FIELDS_LABEL_TEXT}>
|
||||
<EuiFlexGroup alignItems="baseline" justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={true}>
|
||||
{isPopulatedObject(runtimeMappings) ? (
|
||||
|
@ -139,8 +168,8 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
</EuiText>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.dataframe.analytics.createWizard.noRuntimeMappingsLabel"
|
||||
defaultMessage="No runtime mapping"
|
||||
id="xpack.ml.dataframe.analytics.createWizard.noRuntimeFieldLabel"
|
||||
defaultMessage="No runtime field"
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -170,27 +199,41 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiSwitch
|
||||
disabled={jobType === undefined}
|
||||
label={i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.advancedEditorRuntimeMappingsSwitchLabel',
|
||||
{
|
||||
defaultMessage: 'Edit runtime mappings',
|
||||
}
|
||||
)}
|
||||
label={EDIT_SWITCH_LABEL_TEXT}
|
||||
checked={isRuntimeMappingsEditorEnabled}
|
||||
onChange={() => toggleEditorHandler()}
|
||||
onChange={() => {
|
||||
if (
|
||||
isRuntimeMappingsEditorEnabled &&
|
||||
advancedRuntimeMappingsConfig !== advancedEditorRuntimeMappingsLastApplied
|
||||
) {
|
||||
setRuntimeMappingsEditorSwitchModalVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
toggleEditorHandler();
|
||||
}}
|
||||
data-test-subj="mlDataFrameAnalyticsRuntimeMappingsEditorSwitch"
|
||||
/>
|
||||
{isRuntimeMappingsEditorSwitchModalVisible && (
|
||||
<SwitchModal
|
||||
onCancel={() => setRuntimeMappingsEditorSwitchModalVisible(false)}
|
||||
onConfirm={() => {
|
||||
setRuntimeMappingsEditorSwitchModalVisible(false);
|
||||
toggleEditorHandler(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiCopy
|
||||
beforeMessage={COPY_TO_CLIPBOARD_RUNTIME_MAPPINGS}
|
||||
beforeMessage={COPY_RUNTIME_FIELDS_TO_CLIPBOARD_TEXT}
|
||||
textToCopy={advancedRuntimeMappingsConfig ?? ''}
|
||||
>
|
||||
{(copy: () => void) => (
|
||||
<EuiButtonIcon
|
||||
onClick={copy}
|
||||
iconType="copyClipboard"
|
||||
aria-label={COPY_TO_CLIPBOARD_RUNTIME_MAPPINGS}
|
||||
aria-label={COPY_RUNTIME_FIELDS_TO_CLIPBOARD_TEXT}
|
||||
/>
|
||||
)}
|
||||
</EuiCopy>
|
||||
|
@ -201,15 +244,7 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
{isRuntimeMappingsEditorEnabled && (
|
||||
<EuiFlexItem style={{ width: advancedEditorsSidebarWidth }}>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="xs">
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.advancedRuntimeMappingsEditorHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'The advanced editor allows you to edit the runtime mappings of the source.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
<EuiText size="xs">{RUNTIME_FIELDS_EDITOR_HELP_TEXT}</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiButton
|
||||
style={{ width: 'fit-content' }}
|
||||
|
@ -219,12 +254,7 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
disabled={!isRuntimeMappingsEditorApplyButtonEnabled}
|
||||
data-test-subj="mlDataFrameAnalyticsRuntimeMappingsApplyButton"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.advancedSourceEditorApplyButtonText',
|
||||
{
|
||||
defaultMessage: 'Apply changes',
|
||||
}
|
||||
)}
|
||||
{APPLY_CHANGES_TEXT}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 } from 'react';
|
||||
import { EuiConfirmModal } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
interface Props {
|
||||
onCancel: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
const modalTitle = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.runtimeEditorSwitchModalTitle',
|
||||
{
|
||||
defaultMessage: 'Edits will be lost',
|
||||
}
|
||||
);
|
||||
|
||||
const cancelButtonText = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.runtimeEditorSwitchModalCancelButtonText',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
);
|
||||
|
||||
const applyChangesText = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.runtimeEditorSwitchModalConfirmButtonText',
|
||||
{
|
||||
defaultMessage: 'Close editor',
|
||||
}
|
||||
);
|
||||
const modalMessage = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.createWizard.runtimeEditorSwitchModalBodyText',
|
||||
{
|
||||
defaultMessage: `The changes in the editor haven't been applied yet. By closing the editor you will lose your edits.`,
|
||||
}
|
||||
);
|
||||
|
||||
export const SwitchModal: FC<Props> = ({ onCancel, onConfirm }) => (
|
||||
<EuiConfirmModal
|
||||
title={modalTitle}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
cancelButtonText={cancelButtonText}
|
||||
confirmButtonText={applyChangesText}
|
||||
buttonColor="danger"
|
||||
defaultFocusedButton="confirm"
|
||||
>
|
||||
<p>{modalMessage}</p>
|
||||
</EuiConfirmModal>
|
||||
);
|
|
@ -102,7 +102,7 @@ describe('filter_runtime_mappings', () => {
|
|||
datafeed = getDatafeed();
|
||||
});
|
||||
|
||||
test('returns no runtime mappings, no mappings in aggs', () => {
|
||||
test('returns no runtime fields, no mappings in aggs', () => {
|
||||
const resp = filterRuntimeMappings(job, datafeed);
|
||||
expect(Object.keys(resp.runtime_mappings).length).toEqual(0);
|
||||
|
||||
|
@ -111,7 +111,7 @@ describe('filter_runtime_mappings', () => {
|
|||
expect(resp.discarded_mappings.airline_lower).not.toEqual(undefined);
|
||||
});
|
||||
|
||||
test('returns no runtime mappings, no runtime mappings in datafeed', () => {
|
||||
test('returns no runtime fields, no runtime fields in datafeed', () => {
|
||||
datafeed.runtime_mappings = undefined;
|
||||
const resp = filterRuntimeMappings(job, datafeed);
|
||||
expect(Object.keys(resp.runtime_mappings).length).toEqual(0);
|
||||
|
@ -131,7 +131,7 @@ describe('filter_runtime_mappings', () => {
|
|||
expect(resp.discarded_mappings.airline_lower).not.toEqual(undefined);
|
||||
});
|
||||
|
||||
test('return no runtime mappings, no mappings in aggs', () => {
|
||||
test('return no runtime fields, no mappings in aggs', () => {
|
||||
datafeed.aggregations = getAggs();
|
||||
datafeed.aggregations!.buckets!.aggregations!.responsetime!.avg!.field! = 'responsetime';
|
||||
|
||||
|
@ -154,7 +154,7 @@ describe('filter_runtime_mappings', () => {
|
|||
expect(resp.discarded_mappings.airline_lower).not.toEqual(undefined);
|
||||
});
|
||||
|
||||
test('return two runtime mappings, no mappings in aggs', () => {
|
||||
test('return two runtime fields, no mappings in aggs', () => {
|
||||
// set the detector field to be a runtime mapping
|
||||
job.analysis_config.detectors[0].field_name = 'responsetime_big';
|
||||
// set the detector by field to be a runtime mapping
|
||||
|
@ -167,7 +167,7 @@ describe('filter_runtime_mappings', () => {
|
|||
expect(Object.keys(resp.discarded_mappings).length).toEqual(0);
|
||||
});
|
||||
|
||||
test('return two runtime mappings, no mappings in aggs, categorization job', () => {
|
||||
test('return two runtime fields, no mappings in aggs, categorization job', () => {
|
||||
job.analysis_config.detectors[0].function = 'count';
|
||||
// set the detector field to be a runtime mapping
|
||||
job.analysis_config.detectors[0].field_name = undefined;
|
||||
|
|
|
@ -970,7 +970,7 @@ export function resultsServiceProvider(mlApiServices) {
|
|||
},
|
||||
},
|
||||
},
|
||||
// Runtime mappings only needed to support when query includes a runtime field
|
||||
// Runtime fields only needed to support when query includes a runtime field
|
||||
// even though the default timeField can be a search time runtime field
|
||||
// because currently Kibana doesn't support that
|
||||
...(isPopulatedObject(runtimeMappings) && query
|
||||
|
|
|
@ -627,7 +627,7 @@ export class DataVisualizer {
|
|||
// filter aggregation with exists query.
|
||||
const aggs: Aggs = datafeedAggregations !== undefined ? { ...datafeedAggregations } : {};
|
||||
|
||||
// Combine runtime mappings from the index pattern as well as the datafeed
|
||||
// Combine runtime fields from the index pattern as well as the datafeed
|
||||
const combinedRuntimeMappings: RuntimeMappings = {
|
||||
...(isPopulatedObject(runtimeMappings) ? runtimeMappings : {}),
|
||||
...(isPopulatedObject(datafeedConfig) && isPopulatedObject(datafeedConfig.runtime_mappings)
|
||||
|
|
|
@ -166,7 +166,7 @@ const validateFactory = (client: IScopedClusterClient, job: CombinedJob): Valida
|
|||
}
|
||||
} else {
|
||||
// only report uniqueFieldName as not aggregatable if it's not part
|
||||
// of a valid categorization configuration and if it's not a scripted field or runtime mapping.
|
||||
// of a valid categorization configuration and if it's not a scripted field or runtime field.
|
||||
if (
|
||||
!isValidCategorizationConfig(job, uniqueFieldName) &&
|
||||
!isScriptField(job, uniqueFieldName) &&
|
||||
|
|
|
@ -20,7 +20,7 @@ export const dataVisualizerFieldHistogramsSchema = schema.object({
|
|||
fields: schema.arrayOf(schema.any()),
|
||||
/** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */
|
||||
samplerShardSize: schema.number(),
|
||||
/** Optional search time runtime mappings */
|
||||
/** Optional search time runtime fields */
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
});
|
||||
|
||||
|
@ -40,7 +40,7 @@ export const dataVisualizerFieldStatsSchema = schema.object({
|
|||
interval: schema.maybe(schema.number()),
|
||||
/** Maximum number of examples to return for text type fields. */
|
||||
maxExamples: schema.number(),
|
||||
/** Optional search time runtime mappings */
|
||||
/** Optional search time runtime fields */
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
});
|
||||
|
||||
|
@ -59,6 +59,6 @@ export const dataVisualizerOverallStatsSchema = schema.object({
|
|||
earliest: schema.maybe(schema.number()),
|
||||
/** Latest timestamp for search, as epoch ms (optional). */
|
||||
latest: schema.maybe(schema.number()),
|
||||
/** Optional search time runtime mappings */
|
||||
/** Optional search time runtime fields */
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ export const fieldHistogramsRequestSchema = schema.object({
|
|||
query: schema.any(),
|
||||
/** The fields to return histogram data. */
|
||||
fields: schema.arrayOf(schema.any()),
|
||||
/** Optional runtime mappings */
|
||||
/** Optional runtime fields */
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
/** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */
|
||||
samplerShardSize: schema.number(),
|
||||
|
|
|
@ -266,7 +266,7 @@ describe('Transform: Common', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('getCreateTransformRequestBody() with runtime mappings', () => {
|
||||
test('getCreateTransformRequestBody() with runtime fields', () => {
|
||||
const runtimeMappings = {
|
||||
rt_bytes_bigger: {
|
||||
type: 'double',
|
||||
|
|
|
@ -26,9 +26,6 @@ export const AdvancedPivotEditorSwitch: FC<StepDefineFormHook> = ({
|
|||
isAdvancedPivotEditorApplyButtonEnabled,
|
||||
},
|
||||
},
|
||||
pivotConfig: {
|
||||
actions: { setAggList, setGroupByList },
|
||||
},
|
||||
}) => {
|
||||
return (
|
||||
<EuiFormRow>
|
||||
|
|
|
@ -27,6 +27,8 @@ export const AdvancedQueryEditorSwitch: FC<StepDefineFormHook> = ({
|
|||
isAdvancedSourceEditorEnabled,
|
||||
isAdvancedSourceEditorSwitchModalVisible,
|
||||
sourceConfigUpdated,
|
||||
advancedEditorSourceConfigLastApplied,
|
||||
advancedEditorSourceConfig,
|
||||
},
|
||||
},
|
||||
searchBar: {
|
||||
|
@ -53,7 +55,11 @@ export const AdvancedQueryEditorSwitch: FC<StepDefineFormHook> = ({
|
|||
)}
|
||||
checked={isAdvancedSourceEditorEnabled}
|
||||
onChange={() => {
|
||||
if (isAdvancedSourceEditorEnabled && sourceConfigUpdated) {
|
||||
if (
|
||||
isAdvancedSourceEditorEnabled &&
|
||||
(sourceConfigUpdated ||
|
||||
advancedEditorSourceConfig !== advancedEditorSourceConfigLastApplied)
|
||||
) {
|
||||
setAdvancedSourceEditorSwitchModalVisible(true);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -8,35 +8,58 @@
|
|||
import React, { FC } from 'react';
|
||||
import { EuiSwitch } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { StepDefineFormHook } from '../step_define';
|
||||
import { SwitchModal } from './switch_modal';
|
||||
import { useAdvancedRuntimeMappingsEditor } from '../step_define/hooks/use_advanced_runtime_mappings_editor';
|
||||
|
||||
export const AdvancedRuntimeMappingsEditorSwitch: FC<
|
||||
StepDefineFormHook['runtimeMappingsEditor']
|
||||
> = (props) => {
|
||||
type Props = ReturnType<typeof useAdvancedRuntimeMappingsEditor>;
|
||||
export const AdvancedRuntimeMappingsEditorSwitch: FC<Props> = (props) => {
|
||||
const {
|
||||
actions: { setRuntimeMappingsUpdated, toggleRuntimeMappingsEditor },
|
||||
state: { isRuntimeMappingsEditorEnabled },
|
||||
actions: { toggleRuntimeMappingsEditor, setRuntimeMappingsEditorSwitchModalVisible },
|
||||
state: {
|
||||
isRuntimeMappingsEditorEnabled,
|
||||
isRuntimeMappingsEditorSwitchModalVisible,
|
||||
advancedEditorRuntimeMappingsLastApplied,
|
||||
advancedRuntimeMappingsConfig,
|
||||
},
|
||||
} = props;
|
||||
|
||||
// If switching to KQL after updating via editor - reset search
|
||||
const toggleEditorHandler = (reset = false) => {
|
||||
if (reset === true) {
|
||||
setRuntimeMappingsUpdated(false);
|
||||
}
|
||||
toggleRuntimeMappingsEditor(reset);
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiSwitch
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedEditorRuntimeMappingsSwitchLabel',
|
||||
{
|
||||
defaultMessage: 'Edit runtime mappings',
|
||||
}
|
||||
<>
|
||||
<EuiSwitch
|
||||
label={i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedEditorRuntimeFieldsSwitchLabel',
|
||||
{
|
||||
defaultMessage: 'Edit runtime fields',
|
||||
}
|
||||
)}
|
||||
checked={isRuntimeMappingsEditorEnabled}
|
||||
onChange={() => {
|
||||
if (
|
||||
isRuntimeMappingsEditorEnabled &&
|
||||
advancedRuntimeMappingsConfig !== advancedEditorRuntimeMappingsLastApplied
|
||||
) {
|
||||
setRuntimeMappingsEditorSwitchModalVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
toggleEditorHandler();
|
||||
}}
|
||||
data-test-subj="transformAdvancedRuntimeMappingsEditorSwitch"
|
||||
/>
|
||||
{isRuntimeMappingsEditorSwitchModalVisible && (
|
||||
<SwitchModal
|
||||
onCancel={() => setRuntimeMappingsEditorSwitchModalVisible(false)}
|
||||
onConfirm={() => {
|
||||
setRuntimeMappingsEditorSwitchModalVisible(false);
|
||||
toggleEditorHandler(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
checked={isRuntimeMappingsEditorEnabled}
|
||||
onChange={() => toggleEditorHandler()}
|
||||
data-test-subj="transformAdvancedRuntimeMappingsEditorSwitch"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 } from 'react';
|
||||
import { EuiConfirmModal } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
interface Props {
|
||||
onCancel: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
const modalTitle = i18n.translate('xpack.transform.stepDefineForm.runtimeEditorSwitchModalTitle', {
|
||||
defaultMessage: 'Edits will be lost',
|
||||
});
|
||||
|
||||
const cancelButtonText = i18n.translate(
|
||||
'xpack.transform.stepDefineForm.runtimeEditorSwitchModalCancelButtonText',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
);
|
||||
|
||||
const applyChangesText = i18n.translate(
|
||||
'xpack.transform.stepDefineForm.runtimeEditorSwitchModalConfirmButtonText',
|
||||
{
|
||||
defaultMessage: 'Close editor',
|
||||
}
|
||||
);
|
||||
const modalMessage = i18n.translate(
|
||||
'xpack.transform.stepDefineForm.runtimeEditorSwitchModalBodyText',
|
||||
{
|
||||
defaultMessage: `The changes in the advanced editor haven't been applied yet. By closing the editor you will lose your edits.`,
|
||||
}
|
||||
);
|
||||
|
||||
export const SwitchModal: FC<Props> = ({ onCancel, onConfirm }) => (
|
||||
<EuiConfirmModal
|
||||
title={modalTitle}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
cancelButtonText={cancelButtonText}
|
||||
confirmButtonText={applyChangesText}
|
||||
buttonColor="danger"
|
||||
defaultFocusedButton="confirm"
|
||||
>
|
||||
<p>{modalMessage}</p>
|
||||
</EuiConfirmModal>
|
||||
);
|
|
@ -29,9 +29,9 @@ import { isPivotAggConfigWithUiSupport } from '../../../../common/pivot_group_by
|
|||
|
||||
const advancedEditorsSidebarWidth = '220px';
|
||||
const COPY_TO_CLIPBOARD_RUNTIME_MAPPINGS = i18n.translate(
|
||||
'xpack.transform.indexPreview.copyRuntimeMappingsClipboardTooltip',
|
||||
'xpack.transform.indexPreview.copyRuntimeFieldsClipboardTooltip',
|
||||
{
|
||||
defaultMessage: 'Copy Dev Console statement of the runtime mappings to the clipboard.',
|
||||
defaultMessage: 'Copy Dev Console statement of the runtime fields to the clipboard.',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -87,15 +87,15 @@ export const AdvancedRuntimeMappingsSettings: FC<StepDefineFormHook> = (props) =
|
|||
|
||||
<EuiFormRow
|
||||
fullWidth={true}
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.runtimeMappingsLabel', {
|
||||
defaultMessage: 'Runtime mappings',
|
||||
label={i18n.translate('xpack.transform.stepDefineForm.runtimeFieldsLabel', {
|
||||
defaultMessage: 'Runtime fields',
|
||||
})}
|
||||
>
|
||||
<EuiFlexGroup alignItems="baseline" justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={true}>
|
||||
{runtimeMappings !== undefined && Object.keys(runtimeMappings).length > 0 ? (
|
||||
<FormattedMessage
|
||||
id="xpack.transform.stepDefineForm.runtimeMappingsListLabel"
|
||||
id="xpack.transform.stepDefineForm.runtimeFieldsListLabel"
|
||||
defaultMessage="{runtimeFields}"
|
||||
values={{
|
||||
runtimeFields: Object.keys(runtimeMappings).join(','),
|
||||
|
@ -104,7 +104,7 @@ export const AdvancedRuntimeMappingsSettings: FC<StepDefineFormHook> = (props) =
|
|||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.transform.stepDefineForm.noRuntimeMappingsLabel"
|
||||
defaultMessage="No runtime mapping"
|
||||
defaultMessage="No runtime field"
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -145,10 +145,10 @@ export const AdvancedRuntimeMappingsSettings: FC<StepDefineFormHook> = (props) =
|
|||
<EuiSpacer size="s" />
|
||||
<EuiText size="xs">
|
||||
{i18n.translate(
|
||||
'xpack.transform.stepDefineForm.advancedRuntimeMappingsEditorHelpText',
|
||||
'xpack.transform.stepDefineForm.advancedRuntimeFieldsEditorHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'The advanced editor allows you to edit the runtime mappings of the transform configuration.',
|
||||
'The advanced editor allows you to edit the runtime fields of the transform configuration.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
|
|
|
@ -37,7 +37,7 @@ export function applyTransformConfigToDefineState(
|
|||
transformConfig?: TransformBaseConfig,
|
||||
indexPattern?: StepDefineFormProps['searchItems']['indexPattern']
|
||||
): StepDefineExposedState {
|
||||
// apply runtime mappings from both the index pattern and inline configurations
|
||||
// apply runtime fields from both the index pattern and inline configurations
|
||||
state.runtimeMappings = getCombinedRuntimeMappings(
|
||||
indexPattern,
|
||||
transformConfig?.source?.runtime_mappings
|
||||
|
|
|
@ -39,7 +39,7 @@ export function getSupportedFilterAggs(
|
|||
];
|
||||
}
|
||||
|
||||
throw new Error(`The field ${fieldName} does not exist in the index or runtime mappings`);
|
||||
throw new Error(`The field ${fieldName} does not exist in the index or runtime fields`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,11 +62,8 @@ export const useAdvancedRuntimeMappingsEditor = (defaults: StepDefineExposedStat
|
|||
const toggleRuntimeMappingsEditor = (reset = false) => {
|
||||
if (reset === true) {
|
||||
setRuntimeMappingsUpdated(false);
|
||||
setAdvancedRuntimeMappingsConfig(advancedEditorRuntimeMappingsLastApplied);
|
||||
}
|
||||
if (isRuntimeMappingsEditorEnabled === false) {
|
||||
setAdvancedEditorRuntimeMappingsLastApplied(advancedRuntimeMappingsConfig);
|
||||
}
|
||||
|
||||
setRuntimeMappingsEditorEnabled(!isRuntimeMappingsEditorEnabled);
|
||||
setRuntimeMappingsEditorApplyButtonEnabled(false);
|
||||
};
|
||||
|
|
|
@ -51,7 +51,7 @@ export const useStepDefineForm = ({ overrides, onChange, searchItems }: StepDefi
|
|||
// source config hook
|
||||
const advancedSourceEditor = useAdvancedSourceEditor(defaults, previewRequest);
|
||||
|
||||
// runtime mappings config hook
|
||||
// runtime fields config hook
|
||||
const runtimeMappingsEditor = useAdvancedRuntimeMappingsEditor(defaults);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -21758,7 +21758,6 @@
|
|||
"xpack.transform.groupByLabelForm.editIntervalAriaLabel": "間隔を編集",
|
||||
"xpack.transform.home.breadcrumbTitle": "変換",
|
||||
"xpack.transform.indexPreview.copyClipboardTooltip": "インデックスプレビューの開発コンソールステートメントをクリップボードにコピーします。",
|
||||
"xpack.transform.indexPreview.copyRuntimeMappingsClipboardTooltip": "ランタイムマッピングの開発コンソールステートメントをクリップボードにコピーします。",
|
||||
"xpack.transform.latestPreview.latestPreviewIncompleteConfigCalloutBody": "1 つ以上の一意キーと並べ替えフィールドを選択してください。",
|
||||
"xpack.transform.licenseCheckErrorMessage": "ライセンス確認失敗",
|
||||
"xpack.transform.list.emptyPromptButtonText": "初めての変換を作成してみましょう。",
|
||||
|
@ -21818,14 +21817,12 @@
|
|||
"xpack.transform.stepDefineForm.advancedEditorHelpText": "詳細エディターでは、変換のピボット構成を編集できます。",
|
||||
"xpack.transform.stepDefineForm.advancedEditorHelpTextLink": "使用可能なオプションの詳細を確認してください。",
|
||||
"xpack.transform.stepDefineForm.advancedEditorLabel": "ピボット構成オブジェクト",
|
||||
"xpack.transform.stepDefineForm.advancedEditorRuntimeMappingsSwitchLabel": "ランタイムマッピングの編集",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSourceConfigSwitchLabel": "JSONクエリを編集",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchLabel": "JSON構成を編集",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchModalBodyText": "詳細エディターの変更は適用されませんでした。詳細エディターを無効にすると、編集内容が失われます。",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchModalCancelButtonText": "キャンセル",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchModalConfirmButtonText": "詳細エディターを無効にする",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchModalTitle": "適用されていない変更",
|
||||
"xpack.transform.stepDefineForm.advancedRuntimeMappingsEditorHelpText": "高度なエディターでは、変換構成のランタイムマッピングを編集できます。",
|
||||
"xpack.transform.stepDefineForm.advancedSourceEditorApplyButtonText": "変更を適用",
|
||||
"xpack.transform.stepDefineForm.advancedSourceEditorAriaLabel": "クエリの詳細エディター",
|
||||
"xpack.transform.stepDefineForm.advancedSourceEditorHelpText": "高度なエディターでは、変換構成のソースクエリ句を編集できます。",
|
||||
|
@ -21851,8 +21848,6 @@
|
|||
"xpack.transform.stepDefineForm.pivotLabel": "ピボット",
|
||||
"xpack.transform.stepDefineForm.queryPlaceholderKql": "例:{example}",
|
||||
"xpack.transform.stepDefineForm.queryPlaceholderLucene": "例:{example}",
|
||||
"xpack.transform.stepDefineForm.runtimeMappingsLabel": "ランタイムマッピング",
|
||||
"xpack.transform.stepDefineForm.runtimeMappingsListLabel": "{runtimeFields}",
|
||||
"xpack.transform.stepDefineForm.savedSearchLabel": "保存検索",
|
||||
"xpack.transform.stepDefineForm.sortFieldOptionsEmptyError": "並べ替えの条件にする日付フィールドがありません。別のフィールド型を使用するには、構成をクリップボードにコピーして、コンソールで変換を作成し続けます。",
|
||||
"xpack.transform.stepDefineForm.sortHelpText": "最新のドキュメントを特定するために使用する日付フィールドを選択してます。",
|
||||
|
|
|
@ -22106,7 +22106,6 @@
|
|||
"xpack.transform.groupByLabelForm.editIntervalAriaLabel": "编辑时间间隔",
|
||||
"xpack.transform.home.breadcrumbTitle": "转换",
|
||||
"xpack.transform.indexPreview.copyClipboardTooltip": "将索引预览的开发控制台语句复制到剪贴板。",
|
||||
"xpack.transform.indexPreview.copyRuntimeMappingsClipboardTooltip": "将运行时映射的开发控制台语句复制到剪贴板。",
|
||||
"xpack.transform.latestPreview.latestPreviewIncompleteConfigCalloutBody": "请选择至少一个唯一键和排序字段。",
|
||||
"xpack.transform.licenseCheckErrorMessage": "许可证检查失败",
|
||||
"xpack.transform.list.emptyPromptButtonText": "创建您的首个转换",
|
||||
|
@ -22167,14 +22166,12 @@
|
|||
"xpack.transform.stepDefineForm.advancedEditorHelpText": "高级编辑器允许您编辑数据帧转换的数据透视表配置。",
|
||||
"xpack.transform.stepDefineForm.advancedEditorHelpTextLink": "详细了解可用选项。",
|
||||
"xpack.transform.stepDefineForm.advancedEditorLabel": "数据透视表配置对象",
|
||||
"xpack.transform.stepDefineForm.advancedEditorRuntimeMappingsSwitchLabel": "编辑运行时映射",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSourceConfigSwitchLabel": "编辑 JSON 查询",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchLabel": "编辑 JSON 配置",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchModalBodyText": "高级编辑器中的更改尚未应用。禁用高级编辑器将会使您的编辑丢失。",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchModalCancelButtonText": "取消",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchModalConfirmButtonText": "禁用高级编辑器",
|
||||
"xpack.transform.stepDefineForm.advancedEditorSwitchModalTitle": "未应用的更改",
|
||||
"xpack.transform.stepDefineForm.advancedRuntimeMappingsEditorHelpText": "高级编辑器允许您编辑转换配置的运行时映射。",
|
||||
"xpack.transform.stepDefineForm.advancedSourceEditorApplyButtonText": "应用更改",
|
||||
"xpack.transform.stepDefineForm.advancedSourceEditorAriaLabel": "高级查询编辑器",
|
||||
"xpack.transform.stepDefineForm.advancedSourceEditorHelpText": "高级编辑器允许您编辑转换配置的源查询子句。",
|
||||
|
@ -22200,8 +22197,6 @@
|
|||
"xpack.transform.stepDefineForm.pivotLabel": "数据透视表",
|
||||
"xpack.transform.stepDefineForm.queryPlaceholderKql": "例如,{example}",
|
||||
"xpack.transform.stepDefineForm.queryPlaceholderLucene": "例如,{example}",
|
||||
"xpack.transform.stepDefineForm.runtimeMappingsLabel": "运行时映射",
|
||||
"xpack.transform.stepDefineForm.runtimeMappingsListLabel": "{runtimeFields}",
|
||||
"xpack.transform.stepDefineForm.savedSearchLabel": "已保存搜索",
|
||||
"xpack.transform.stepDefineForm.sortFieldOptionsEmptyError": "没有日期字段可用于排序。要使用其他字段类型,请将配置复制到剪贴板,然后继续在控制台中创建转换。",
|
||||
"xpack.transform.stepDefineForm.sortHelpText": "选择要用于标识最新文档的日期字段。",
|
||||
|
|
|
@ -62,6 +62,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
{ color: '#D3DAE6', percentage: 8 },
|
||||
{ color: '#F5F7FA', percentage: 15 },
|
||||
],
|
||||
runtimeFieldsEditorContent: ['{', ' "uppercase_y": {', ' "type": "keyword",'],
|
||||
row: {
|
||||
type: 'classification',
|
||||
status: 'stopped',
|
||||
|
@ -113,9 +114,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
JSON.stringify(testData.runtimeFields)
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.applyRuntimeMappings();
|
||||
await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent([
|
||||
'{"uppercase_y":{"type":"keyword","script":"emit(params._source.y.toUpperCase())"}}',
|
||||
]);
|
||||
await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent(
|
||||
testData.expected.runtimeFieldsEditorContent
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('inputs the dependent variable');
|
||||
await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists();
|
||||
|
|
|
@ -72,6 +72,11 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
// anti-aliasing
|
||||
{ color: '#F5F7FA', percentage: 30 },
|
||||
],
|
||||
runtimeFieldsEditorContent: [
|
||||
'{',
|
||||
' "lowercase_central_air": {',
|
||||
' "type": "keyword",',
|
||||
],
|
||||
row: {
|
||||
type: 'outlier_detection',
|
||||
status: 'stopped',
|
||||
|
@ -124,9 +129,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
JSON.stringify(testData.runtimeFields)
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.applyRuntimeMappings();
|
||||
await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent([
|
||||
'{"lowercase_central_air":{"type":"keyword","script":"emit(params._source.CentralAir.toLowerCase())"}}',
|
||||
]);
|
||||
await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent(
|
||||
testData.expected.runtimeFieldsEditorContent
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('does not display the dependent variable input');
|
||||
await ml.dataFrameAnalyticsCreation.assertDependentVariableInputMissing();
|
||||
|
|
|
@ -55,6 +55,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
{ color: '#F5F7FA', percentage: 10 },
|
||||
{ color: '#D3DAE6', percentage: 3 },
|
||||
],
|
||||
runtimeFieldsEditorContent: ['{', ' "uppercase_stab": {', ' "type": "keyword",'],
|
||||
row: {
|
||||
type: 'regression',
|
||||
status: 'stopped',
|
||||
|
@ -107,9 +108,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
JSON.stringify(testData.runtimeFields)
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.applyRuntimeMappings();
|
||||
await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent([
|
||||
'{"uppercase_stab":{"type":"keyword","script":"emit(params._source.stabf.toUpperCase())"}}',
|
||||
]);
|
||||
await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent(
|
||||
testData.expected.runtimeFieldsEditorContent
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('inputs the dependent variable');
|
||||
await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue