mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
ability to set human readable title of data view & ability to edit data view (#124191)
* ability to set human readable title of data view & ability to edit data view * add readable data view title to discover * fix for failing API test * fix and add functional tests * add missing lens support * update snapshots after name change * fix for broken test and type checks * fix condition check * remove description, rename readableTitle to name and fixes * revert kibana.yaml change * fixes for failed tests * fixes for broken tests * fix functional tests 2 * add confirm modal for edit * update header to match figma * fix data views with index pattern * fix for badge breaking in test due to index change * remove unused translations * comment fixes and add name to sample data * fix test for adding names to sample data * some ui changes * fix for failing tests for removing Actions * comment fixes, change edit modal, fix breadcrumbs * change copy and fix for failing tests * change text to have unique key * update jest snapshots * fixes for unified_search and some ui changes * add test fixes and disabled editing data view id when in edit mode * fix for translation bug * fix for failing tests * review comment changes * some nit fixes * revert the operation change check * fixed name not showing in dashboard with visualizations from different dataviews * added a comment for the management breadcrumb
This commit is contained in:
parent
7aace2384c
commit
5731d1aa58
74 changed files with 625 additions and 207 deletions
|
@ -222,7 +222,7 @@ export const ControlEditor = ({
|
|||
}}
|
||||
trigger={{
|
||||
label:
|
||||
state.selectedDataView?.title ??
|
||||
state.selectedDataView?.getName() ??
|
||||
ControlGroupStrings.manageControl.getSelectDataViewMessage(),
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Data view editor
|
||||
|
||||
Create data views from within Kibana apps.
|
||||
Create data views from within Kibana apps.
|
||||
|
||||
## How to use
|
||||
|
||||
|
@ -10,11 +10,11 @@ You will then receive in the start contract of the dataViewEditor plugin the fol
|
|||
|
||||
### `userPermissions.editDataView(): boolean`
|
||||
|
||||
Convenience method that uses the `core.application.capabilities` api to determine whether the user can create or edit the data view.
|
||||
Convenience method that uses the `core.application.capabilities` api to determine whether the user can create or edit the data view.
|
||||
|
||||
### `openEditor(options: DataViewEditorProps): CloseEditor`
|
||||
|
||||
Use this method to display the data view editor to create an index pattern.
|
||||
Use this method to display the data view editor to create a data view.
|
||||
|
||||
#### `options`
|
||||
|
||||
|
@ -34,6 +34,10 @@ The default index pattern type can be optionally specified as `rollup`.
|
|||
|
||||
The editor can require a timestamp field on the index pattern.
|
||||
|
||||
`editData: DataView` (optional)
|
||||
|
||||
Data View object passed to edit an existing Data View.
|
||||
|
||||
### IndexPatternEditorComponent
|
||||
|
||||
This the React component interface equivalent to `openEditor`. It takes the same arguments -
|
||||
|
@ -44,5 +48,6 @@ This the React component interface equivalent to `openEditor`. It takes the same
|
|||
onCancel={...}
|
||||
defaultTypeIsRollup={false}
|
||||
requireTimestampField={false}
|
||||
editData={...}
|
||||
/>
|
||||
```
|
||||
|
|
|
@ -28,9 +28,13 @@ const customIndexPatternIdLabel = i18n.translate(
|
|||
|
||||
interface AdvancedParamsContentProps {
|
||||
disableAllowHidden: boolean;
|
||||
disableId: boolean;
|
||||
}
|
||||
|
||||
export const AdvancedParamsContent = ({ disableAllowHidden }: AdvancedParamsContentProps) => (
|
||||
export const AdvancedParamsContent = ({
|
||||
disableAllowHidden,
|
||||
disableId,
|
||||
}: AdvancedParamsContentProps) => (
|
||||
<AdvancedParamsSection>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
|
@ -57,6 +61,7 @@ export const AdvancedParamsContent = ({ disableAllowHidden }: AdvancedParamsCont
|
|||
componentProps={{
|
||||
euiFieldProps: {
|
||||
'aria-label': customIndexPatternIdLabel,
|
||||
disabled: disableId,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { OverlayStart } from '@kbn/core/public';
|
||||
|
||||
interface EditDataViewDeps {
|
||||
dataViewName: string;
|
||||
overlays: OverlayStart | undefined;
|
||||
onEdit: () => void;
|
||||
}
|
||||
|
||||
export const editDataViewModal = ({ dataViewName, overlays, onEdit }: EditDataViewDeps) =>
|
||||
overlays &&
|
||||
overlays
|
||||
.openConfirm(
|
||||
i18n.translate('indexPatternEditor.editDataView.editConfirmationModal.modalDescription', {
|
||||
defaultMessage: 'Changing this data view can break other objects that depend on it.',
|
||||
}),
|
||||
{
|
||||
confirmButtonText: i18n.translate(
|
||||
'indexPatternEditor.editDataView.editConfirmationModal.confirmButton',
|
||||
{
|
||||
defaultMessage: 'Confirm',
|
||||
}
|
||||
),
|
||||
title: i18n.translate('indexPatternEditor.editDataView.editConfirmationModal.editHeader', {
|
||||
defaultMessage: `Edit '{name}'`,
|
||||
values: {
|
||||
name: dataViewName,
|
||||
},
|
||||
}),
|
||||
buttonColor: 'danger',
|
||||
}
|
||||
)
|
||||
.then(async (isConfirmed) => {
|
||||
if (isConfirmed) {
|
||||
onEdit();
|
||||
}
|
||||
});
|
|
@ -23,6 +23,7 @@ export const DataViewEditor = ({
|
|||
services,
|
||||
defaultTypeIsRollup = false,
|
||||
requireTimestampField = false,
|
||||
editData,
|
||||
showEmptyPrompt = true,
|
||||
}: DataViewEditorPropsWithServices) => {
|
||||
const { Provider: KibanaReactContextProvider } =
|
||||
|
@ -36,6 +37,7 @@ export const DataViewEditor = ({
|
|||
onCancel={onCancel}
|
||||
defaultTypeIsRollup={defaultTypeIsRollup}
|
||||
requireTimestampField={requireTimestampField}
|
||||
editData={editData}
|
||||
showEmptyPrompt={showEmptyPrompt}
|
||||
/>
|
||||
</EuiFlyout>
|
||||
|
|
|
@ -13,6 +13,7 @@ import memoizeOne from 'memoize-one';
|
|||
import { DataViewField } from '@kbn/data-views-plugin/public';
|
||||
|
||||
import {
|
||||
DataView,
|
||||
DataViewSpec,
|
||||
Form,
|
||||
useForm,
|
||||
|
@ -39,6 +40,7 @@ import {
|
|||
TimestampField,
|
||||
TypeField,
|
||||
TitleField,
|
||||
NameField,
|
||||
schema,
|
||||
Footer,
|
||||
AdvancedParamsContent,
|
||||
|
@ -46,6 +48,7 @@ import {
|
|||
PreviewPanel,
|
||||
RollupBetaWarning,
|
||||
} from '.';
|
||||
import { editDataViewModal } from './confirm_modals/edit_data_view_changed_modal';
|
||||
|
||||
export interface Props {
|
||||
/**
|
||||
|
@ -58,6 +61,7 @@ export interface Props {
|
|||
onCancel: () => void;
|
||||
defaultTypeIsRollup?: boolean;
|
||||
requireTimestampField?: boolean;
|
||||
editData?: DataView;
|
||||
showEmptyPrompt?: boolean;
|
||||
}
|
||||
|
||||
|
@ -65,20 +69,38 @@ const editorTitle = i18n.translate('indexPatternEditor.title', {
|
|||
defaultMessage: 'Create data view',
|
||||
});
|
||||
|
||||
const editorTitleEditMode = i18n.translate('indexPatternEditor.titleEditMode', {
|
||||
defaultMessage: 'Edit data view',
|
||||
});
|
||||
|
||||
const IndexPatternEditorFlyoutContentComponent = ({
|
||||
onSave,
|
||||
onCancel,
|
||||
defaultTypeIsRollup,
|
||||
requireTimestampField = false,
|
||||
editData,
|
||||
showEmptyPrompt = true,
|
||||
}: Props) => {
|
||||
const {
|
||||
services: { http, dataViews, uiSettings, searchClient },
|
||||
services: { http, dataViews, uiSettings, searchClient, overlays },
|
||||
} = useKibana<DataViewEditorContext>();
|
||||
|
||||
const { form } = useForm<IndexPatternConfig, FormInternal>({
|
||||
// Prefill with data if editData exists
|
||||
defaultValue: {
|
||||
type: defaultTypeIsRollup ? INDEX_PATTERN_TYPE.ROLLUP : INDEX_PATTERN_TYPE.DEFAULT,
|
||||
...(editData
|
||||
? {
|
||||
title: editData.title,
|
||||
id: editData.id,
|
||||
name: editData.name,
|
||||
...(editData.timeFieldName
|
||||
? {
|
||||
timestampField: { label: editData.timeFieldName, value: editData.timeFieldName },
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
schema,
|
||||
onSubmit: async (formData, isValid) => {
|
||||
|
@ -90,6 +112,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
title: formData.title,
|
||||
timeFieldName: formData.timestampField?.value,
|
||||
id: formData.id,
|
||||
name: formData.name,
|
||||
};
|
||||
|
||||
if (type === INDEX_PATTERN_TYPE.ROLLUP && rollupIndex) {
|
||||
|
@ -102,7 +125,17 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
};
|
||||
}
|
||||
|
||||
await onSave(indexPatternStub);
|
||||
if (editData && editData.title !== formData.title) {
|
||||
editDataViewModal({
|
||||
dataViewName: formData.name || formData.title,
|
||||
overlays,
|
||||
onEdit: async () => {
|
||||
await onSave(indexPatternStub);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await onSave(indexPatternStub);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -157,13 +190,15 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
useEffect(() => {
|
||||
loadSources();
|
||||
const getTitles = async () => {
|
||||
const indexPatternTitles = await dataViews.getTitles();
|
||||
const indexPatternTitles = await dataViews.getTitles(editData ? true : false);
|
||||
|
||||
setExistingIndexPatterns(indexPatternTitles);
|
||||
setExistingIndexPatterns(
|
||||
editData ? indexPatternTitles.filter((v) => v !== editData.title) : indexPatternTitles
|
||||
);
|
||||
setIsLoadingIndexPatterns(false);
|
||||
};
|
||||
getTitles();
|
||||
}, [http, dataViews, loadSources]);
|
||||
}, [http, dataViews, editData, loadSources]);
|
||||
|
||||
// loading rollup info
|
||||
useEffect(() => {
|
||||
|
@ -221,11 +256,6 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadTimestampFieldOptions(title);
|
||||
getFields().timestampField?.setValue('');
|
||||
}, [loadTimestampFieldOptions, title, getFields]);
|
||||
|
||||
const reloadMatchedIndices = useCallback(
|
||||
async (newTitle: string) => {
|
||||
const isRollupIndex = (indexName: string) =>
|
||||
|
@ -275,9 +305,27 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
[http, allowHidden, allSources, type, rollupIndicesCapabilities, searchClient, isLoadingSources]
|
||||
);
|
||||
|
||||
// If editData exists, loadSources so that MatchedIndices can be loaded for the Timestampfields
|
||||
useEffect(() => {
|
||||
if (editData) {
|
||||
loadSources();
|
||||
reloadMatchedIndices(editData.title);
|
||||
}
|
||||
// We use the below eslint-disable as adding 'loadSources' and 'reloadMatchedIndices' as a dependency creates an infinite loop
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [editData]);
|
||||
|
||||
useEffect(() => {
|
||||
loadTimestampFieldOptions(editData ? editData.title : title);
|
||||
if (!editData) getFields().timestampField?.setValue('');
|
||||
// We use the below eslint-disable as adding editData as a dependency create an infinite loop
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loadTimestampFieldOptions, title, getFields]);
|
||||
|
||||
const onTypeChange = useCallback(
|
||||
(newType) => {
|
||||
form.setFieldValue('title', '');
|
||||
form.setFieldValue('name', '');
|
||||
form.setFieldValue('timestampField', '');
|
||||
if (newType === INDEX_PATTERN_TYPE.ROLLUP) {
|
||||
form.setFieldValue('allowHidden', false);
|
||||
|
@ -297,7 +345,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
|
||||
const indexPatternTypeSelect = showIndexPatternTypeSelect() ? (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<TypeField onChange={onTypeChange} />
|
||||
|
@ -327,11 +375,17 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
<FlyoutPanels.Group flyoutClassName={'indexPatternEditorFlyout'} maxWidth={1180}>
|
||||
<FlyoutPanels.Item className="fieldEditor__mainFlyoutPanel" border="right">
|
||||
<EuiTitle data-test-subj="flyoutTitle">
|
||||
<h2>{editorTitle}</h2>
|
||||
<h2>{editData ? editorTitleEditMode : editorTitle}</h2>
|
||||
</EuiTitle>
|
||||
<Form form={form} className="indexPatternEditor__form">
|
||||
{indexPatternTypeSelect}
|
||||
<EuiSpacer size="m" />
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<NameField editData={editData} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<TitleField
|
||||
|
@ -343,7 +397,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<TimestampField
|
||||
|
@ -355,12 +409,16 @@ const IndexPatternEditorFlyoutContentComponent = ({
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<AdvancedParamsContent disableAllowHidden={type === INDEX_PATTERN_TYPE.ROLLUP} />
|
||||
<AdvancedParamsContent
|
||||
disableAllowHidden={type === INDEX_PATTERN_TYPE.ROLLUP}
|
||||
disableId={!!editData}
|
||||
/>
|
||||
</Form>
|
||||
<Footer
|
||||
onCancel={onCancel}
|
||||
onSubmit={() => form.submit()}
|
||||
submitDisabled={form.isSubmitted && !form.isValid}
|
||||
isEdit={!!editData}
|
||||
/>
|
||||
</FlyoutPanels.Item>
|
||||
<FlyoutPanels.Item>
|
||||
|
|
|
@ -18,6 +18,7 @@ const IndexPatternFlyoutContentContainer = ({
|
|||
onCancel = () => {},
|
||||
defaultTypeIsRollup,
|
||||
requireTimestampField = false,
|
||||
editData,
|
||||
showEmptyPrompt = true,
|
||||
}: DataViewEditorProps) => {
|
||||
const {
|
||||
|
@ -26,14 +27,25 @@ const IndexPatternFlyoutContentContainer = ({
|
|||
|
||||
const onSaveClick = async (dataViewSpec: DataViewSpec) => {
|
||||
try {
|
||||
const indexPattern = await dataViews.createAndSave(dataViewSpec);
|
||||
let saveResponse;
|
||||
if (editData) {
|
||||
const { name = '', timeFieldName, title = '' } = dataViewSpec;
|
||||
editData.title = title;
|
||||
editData.name = name;
|
||||
editData.timeFieldName = timeFieldName;
|
||||
saveResponse = await dataViews.updateSavedObject(editData);
|
||||
} else {
|
||||
saveResponse = await dataViews.createAndSave(dataViewSpec);
|
||||
}
|
||||
|
||||
const message = i18n.translate('indexPatternEditor.saved', {
|
||||
defaultMessage: "Saved '{indexPatternTitle}'",
|
||||
values: { indexPatternTitle: indexPattern.title },
|
||||
});
|
||||
notifications.toasts.addSuccess(message);
|
||||
await onSave(indexPattern);
|
||||
if (saveResponse && !(saveResponse instanceof Error)) {
|
||||
const message = i18n.translate('indexPatternEditor.saved', {
|
||||
defaultMessage: "Saved '{indexPatternName}'",
|
||||
values: { indexPatternName: saveResponse.getName() },
|
||||
});
|
||||
notifications.toasts.addSuccess(message);
|
||||
await onSave(saveResponse);
|
||||
}
|
||||
} catch (e) {
|
||||
const title = i18n.translate('indexPatternEditor.dataView.unableSaveLabel', {
|
||||
defaultMessage: 'Failed to save data view.',
|
||||
|
@ -49,6 +61,7 @@ const IndexPatternFlyoutContentContainer = ({
|
|||
onCancel={onCancel}
|
||||
defaultTypeIsRollup={defaultTypeIsRollup}
|
||||
requireTimestampField={requireTimestampField}
|
||||
editData={editData}
|
||||
showEmptyPrompt={showEmptyPrompt}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -21,6 +21,7 @@ interface FooterProps {
|
|||
onCancel: () => void;
|
||||
onSubmit: () => void;
|
||||
submitDisabled: boolean;
|
||||
isEdit: boolean;
|
||||
}
|
||||
|
||||
const closeButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutCloseButtonLabel', {
|
||||
|
@ -31,7 +32,11 @@ const saveButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutSaveButt
|
|||
defaultMessage: 'Create data view',
|
||||
});
|
||||
|
||||
export const Footer = ({ onCancel, onSubmit, submitDisabled }: FooterProps) => {
|
||||
const editButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutEditButtonLabel', {
|
||||
defaultMessage: 'Save',
|
||||
});
|
||||
|
||||
export const Footer = ({ onCancel, onSubmit, submitDisabled, isEdit }: FooterProps) => {
|
||||
return (
|
||||
<EuiFlyoutFooter className="indexPatternEditor__footer">
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
|
@ -54,7 +59,7 @@ export const Footer = ({ onCancel, onSubmit, submitDisabled }: FooterProps) => {
|
|||
fill
|
||||
disabled={submitDisabled}
|
||||
>
|
||||
{saveButtonLabel}
|
||||
{isEdit ? editButtonLabel : saveButtonLabel}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { NameField } from './name_field';
|
||||
export { TimestampField } from './timestamp_field';
|
||||
export { TypeField } from './type_field';
|
||||
export { TitleField } from './title_field';
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiFormRow, EuiFieldText } from '@elastic/eui';
|
||||
import { DataView, UseField } from '../../shared_imports';
|
||||
import { IndexPatternConfig } from '../../types';
|
||||
|
||||
interface NameFieldProps {
|
||||
editData?: DataView;
|
||||
}
|
||||
|
||||
export const NameField = ({ editData }: NameFieldProps) => {
|
||||
return (
|
||||
<UseField<string, IndexPatternConfig>
|
||||
path="name"
|
||||
componentProps={{
|
||||
euiFieldProps: {
|
||||
'aria-label': i18n.translate('indexPatternEditor.form.nameAriaLabel', {
|
||||
defaultMessage: 'Name field optional',
|
||||
}),
|
||||
},
|
||||
}}
|
||||
>
|
||||
{(field) => {
|
||||
return (
|
||||
<EuiFormRow label={field.label} fullWidth>
|
||||
<EuiFieldText
|
||||
value={field.value}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
||||
field.setValue(e.target.value);
|
||||
}}
|
||||
fullWidth
|
||||
data-test-subj="createIndexPatternNameInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
}}
|
||||
</UseField>
|
||||
);
|
||||
};
|
|
@ -62,7 +62,7 @@ const createTitlesNoDupesValidator = (
|
|||
if (namesNotAllowed.includes(value)) {
|
||||
return {
|
||||
message: i18n.translate('indexPatternEditor.dataViewExists.ValidationErrorMessage', {
|
||||
defaultMessage: 'A data view with this title already exists.',
|
||||
defaultMessage: 'An index pattern with this name already exists.',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ export const TitleField = ({
|
|||
componentProps={{
|
||||
euiFieldProps: {
|
||||
'aria-label': i18n.translate('indexPatternEditor.form.titleAriaLabel', {
|
||||
defaultMessage: 'Title field',
|
||||
defaultMessage: 'Index pattern field',
|
||||
}),
|
||||
},
|
||||
}}
|
||||
|
@ -222,7 +222,7 @@ export const TitleField = ({
|
|||
}}
|
||||
isLoading={field.isValidating}
|
||||
fullWidth
|
||||
data-test-subj="createIndexPatternNameInput"
|
||||
data-test-subj="createIndexPatternTitleInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
|
|
|
@ -25,7 +25,7 @@ export const singleAstriskValidator = (
|
|||
export const schema = {
|
||||
title: {
|
||||
label: i18n.translate('indexPatternEditor.editor.form.titleLabel', {
|
||||
defaultMessage: 'Name',
|
||||
defaultMessage: 'Index pattern',
|
||||
}),
|
||||
defaultValue: '',
|
||||
helpText: i18n.translate('indexPatternEditor.validations.titleHelpText', {
|
||||
|
@ -36,7 +36,7 @@ export const schema = {
|
|||
{
|
||||
validator: fieldValidators.emptyField(
|
||||
i18n.translate('indexPatternEditor.validations.titleIsRequiredErrorMessage', {
|
||||
defaultMessage: 'A name is required.',
|
||||
defaultMessage: 'An Index pattern is required.',
|
||||
})
|
||||
),
|
||||
},
|
||||
|
@ -45,6 +45,12 @@ export const schema = {
|
|||
},
|
||||
],
|
||||
},
|
||||
name: {
|
||||
label: i18n.translate('indexPatternEditor.editor.form.nameLabel', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
defaultValue: '',
|
||||
},
|
||||
timestampField: {
|
||||
label: i18n.translate('indexPatternEditor.editor.form.timeFieldLabel', {
|
||||
defaultMessage: 'Timestamp field',
|
||||
|
|
|
@ -12,7 +12,7 @@ export { IndexPatternEditorFlyoutContent } from './data_view_editor_flyout_conte
|
|||
export { DataViewEditor } from './data_view_editor';
|
||||
|
||||
export { schema } from './form_schema';
|
||||
export { TimestampField, TypeField, TitleField } from './form_fields';
|
||||
export { NameField, TimestampField, TypeField, TitleField } from './form_fields';
|
||||
export { EmptyPrompts } from './empty_prompts';
|
||||
export { PreviewPanel } from './preview_panel';
|
||||
export { LoadingIndices } from './loading_indices';
|
||||
|
|
|
@ -49,6 +49,10 @@ export interface DataViewEditorProps {
|
|||
* Sets whether a timestamp field is required to create an index pattern. Defaults to false.
|
||||
*/
|
||||
requireTimestampField?: boolean;
|
||||
/**
|
||||
* Pass the data view to be edited.
|
||||
*/
|
||||
editData?: DataView;
|
||||
/**
|
||||
* If set to false, the screen for prompting a user to create a data view will be skipped, and the user will be taken directly
|
||||
* to data view creation.
|
||||
|
@ -157,6 +161,7 @@ export interface IndexPatternConfig {
|
|||
allowHidden: boolean;
|
||||
id?: string;
|
||||
type: INDEX_PATTERN_TYPE;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface FormInternal extends Omit<IndexPatternConfig, 'timestampField'> {
|
||||
|
|
|
@ -117,6 +117,8 @@ export const WithFieldEditorDependencies =
|
|||
const dependencies: Context = {
|
||||
dataView: {
|
||||
title: indexPatternNameForTest,
|
||||
name: indexPatternNameForTest,
|
||||
getName: () => indexPatternNameForTest,
|
||||
fields: { getAll: spyIndexPatternGetAllFields },
|
||||
} as any,
|
||||
uiSettings: uiSettingsServiceMock.createStartContract(),
|
||||
|
|
|
@ -218,7 +218,7 @@ const FieldEditorFlyoutContentComponent = ({
|
|||
id="indexPatternFieldEditor.editor.flyoutEditFieldSubtitle"
|
||||
defaultMessage="Data view: {patternName}"
|
||||
values={{
|
||||
patternName: <i>{dataView.title}</i>,
|
||||
patternName: <i>{dataView.getName()}</i>,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
|
|
|
@ -4,7 +4,9 @@ exports[`getting index patterns 1`] = `
|
|||
Array [
|
||||
Object {
|
||||
"default": true,
|
||||
"getName": [Function],
|
||||
"id": "test",
|
||||
"name": undefined,
|
||||
"namespaces": undefined,
|
||||
"sort": "0test name",
|
||||
"tags": Array [
|
||||
|
@ -17,7 +19,9 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"default": false,
|
||||
"getName": [Function],
|
||||
"id": "test1",
|
||||
"name": "Test Name 1",
|
||||
"namespaces": undefined,
|
||||
"sort": "1test name 1",
|
||||
"tags": Array [],
|
||||
|
|
|
@ -9,42 +9,42 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
|
||||
export function getListBreadcrumbs() {
|
||||
export function getListBreadcrumbs(withLink?: boolean) {
|
||||
return [
|
||||
{
|
||||
text: i18n.translate('indexPatternManagement.dataViews.listBreadcrumb', {
|
||||
defaultMessage: 'Data views',
|
||||
}),
|
||||
href: `/`,
|
||||
href: withLink ? `/` : '',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getCreateBreadcrumbs() {
|
||||
export function getCreateBreadcrumbs(withLink?: boolean) {
|
||||
return [
|
||||
...getListBreadcrumbs(),
|
||||
...getListBreadcrumbs(true),
|
||||
{
|
||||
text: i18n.translate('indexPatternManagement.dataViews.createBreadcrumb', {
|
||||
defaultMessage: 'Create data view',
|
||||
}),
|
||||
href: `/create`,
|
||||
href: withLink ? `/create` : '',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getEditBreadcrumbs(indexPattern: DataView) {
|
||||
export function getEditBreadcrumbs(indexPattern: DataView, withLink?: boolean) {
|
||||
return [
|
||||
...getListBreadcrumbs(),
|
||||
...getListBreadcrumbs(true),
|
||||
{
|
||||
text: indexPattern.title,
|
||||
href: `/patterns/${indexPattern.id}`,
|
||||
text: indexPattern.getName(),
|
||||
href: withLink ? `/patterns/${indexPattern.id}` : '',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getEditFieldBreadcrumbs(indexPattern: DataView, fieldName: string) {
|
||||
return [
|
||||
...getEditBreadcrumbs(indexPattern),
|
||||
...getEditBreadcrumbs(indexPattern, true),
|
||||
{
|
||||
text: fieldName,
|
||||
},
|
||||
|
@ -53,7 +53,7 @@ export function getEditFieldBreadcrumbs(indexPattern: DataView, fieldName: strin
|
|||
|
||||
export function getCreateFieldBreadcrumbs(indexPattern: DataView) {
|
||||
return [
|
||||
...getEditBreadcrumbs(indexPattern),
|
||||
...getEditBreadcrumbs(indexPattern, true),
|
||||
{
|
||||
text: i18n.translate('indexPatternManagement.indexPatterns.createFieldBreadcrumb', {
|
||||
defaultMessage: 'Create field',
|
||||
|
|
|
@ -59,7 +59,7 @@ export const CreateEditField = withRouter(
|
|||
|
||||
const docFieldName = spec?.name || newFieldPlaceholder;
|
||||
|
||||
chrome.docTitle.change([docFieldName, indexPattern.title]);
|
||||
chrome.docTitle.change([docFieldName, indexPattern.getName()]);
|
||||
|
||||
const redirectAway = () => {
|
||||
history.push(
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
*/
|
||||
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import { withRouter, RouteComponentProps, useLocation } from 'react-router-dom';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiHorizontalRule,
|
||||
EuiSpacer,
|
||||
EuiBadge,
|
||||
EuiText,
|
||||
EuiLink,
|
||||
EuiCallOut,
|
||||
EuiCode,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
@ -33,6 +33,10 @@ import { IndexHeader } from './index_header';
|
|||
import { getTags } from '../utils';
|
||||
import { removeDataView, RemoveDataViewProps } from './remove_data_view';
|
||||
|
||||
const codeStyle = {
|
||||
marginLeft: '8px',
|
||||
};
|
||||
|
||||
export interface EditIndexPatternProps extends RouteComponentProps {
|
||||
indexPattern: DataView;
|
||||
}
|
||||
|
@ -41,13 +45,6 @@ export interface SavedObjectRelationWithTitle extends SavedObjectRelation {
|
|||
title: string;
|
||||
}
|
||||
|
||||
const mappingAPILink = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.timeFilterLabel.mappingAPILink',
|
||||
{
|
||||
defaultMessage: 'field mappings',
|
||||
}
|
||||
);
|
||||
|
||||
const mappingConflictHeader = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.mappingConflictHeader',
|
||||
{
|
||||
|
@ -66,7 +63,7 @@ const securitySolution = 'security-solution';
|
|||
|
||||
export const EditIndexPattern = withRouter(
|
||||
({ indexPattern, history, location }: EditIndexPatternProps) => {
|
||||
const { uiSettings, overlays, chrome, dataViews, savedObjectsManagement } =
|
||||
const { uiSettings, overlays, chrome, dataViews, IndexPatternEditor, savedObjectsManagement } =
|
||||
useKibana<IndexPatternManagmentContext>().services;
|
||||
const [fields, setFields] = useState<DataViewField[]>(indexPattern.getNonScriptedFields());
|
||||
const [conflictedFields, setConflictedFields] = useState<DataViewField[]>(
|
||||
|
@ -74,6 +71,7 @@ export const EditIndexPattern = withRouter(
|
|||
);
|
||||
const [defaultIndex, setDefaultIndex] = useState<string>(uiSettings.get('defaultIndex'));
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [showEditDialog, setShowEditDialog] = useState<boolean>(false);
|
||||
const [relationships, setRelationships] = useState<SavedObjectRelationWithTitle[]>([]);
|
||||
const [allowedTypes, setAllowedTypes] = useState<SavedObjectManagementTypeInfo[]>([]);
|
||||
|
||||
|
@ -120,11 +118,32 @@ export const EditIndexPattern = withRouter(
|
|||
},
|
||||
});
|
||||
|
||||
const timeFilterHeader = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.timeFilterHeader',
|
||||
const isRollup = new URLSearchParams(useLocation().search).get('type') === 'rollup';
|
||||
const displayIndexPatternEditor = showEditDialog ? (
|
||||
<IndexPatternEditor
|
||||
onSave={() => setShowEditDialog(false)}
|
||||
onCancel={() => setShowEditDialog(false)}
|
||||
defaultTypeIsRollup={isRollup}
|
||||
editData={indexPattern}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
const editPattern = () => {
|
||||
setShowEditDialog(true);
|
||||
};
|
||||
|
||||
const indexPatternHeading = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.indexPatternHeading',
|
||||
{
|
||||
defaultMessage: "Time field: '{timeFieldName}'",
|
||||
values: { timeFieldName: indexPattern.timeFieldName },
|
||||
defaultMessage: 'Index pattern:',
|
||||
}
|
||||
);
|
||||
|
||||
const timeFilterHeading = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.timeFilterHeading',
|
||||
{
|
||||
defaultMessage: 'Time field:',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -141,11 +160,8 @@ export const EditIndexPattern = withRouter(
|
|||
defaultMessage: 'Data view details',
|
||||
});
|
||||
|
||||
chrome.docTitle.change(indexPattern.title);
|
||||
chrome.docTitle.change(indexPattern.getName());
|
||||
|
||||
const showTagsSection = Boolean(indexPattern.timeFieldName || (tags && tags.length > 0));
|
||||
const kibana = useKibana();
|
||||
const docsUrl = kibana.services.docLinks!.links.elasticsearch.mapping;
|
||||
const userEditPermission = dataViews.getCanSaveSync();
|
||||
|
||||
const warning =
|
||||
|
@ -155,7 +171,7 @@ export const EditIndexPattern = withRouter(
|
|||
id="indexPatternManagement.editDataView.deleteWarningWithNamespaces"
|
||||
defaultMessage="Delete the data view {dataViewName} from every space it is shared in. You can't undo this action."
|
||||
values={{
|
||||
dataViewName: <EuiCode>{indexPattern.title}</EuiCode>,
|
||||
dataViewName: <EuiCode>{indexPattern.getName()}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
|
@ -163,7 +179,7 @@ export const EditIndexPattern = withRouter(
|
|||
id="indexPatternManagement.editDataView.deleteWarning"
|
||||
defaultMessage="The data view {dataViewName} will be deleted. You can't undo this action."
|
||||
values={{
|
||||
dataViewName: <EuiCode>{indexPattern.title}</EuiCode>,
|
||||
dataViewName: <EuiCode>{indexPattern.getName()}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -173,48 +189,49 @@ export const EditIndexPattern = withRouter(
|
|||
<IndexHeader
|
||||
indexPattern={indexPattern}
|
||||
setDefault={setDefaultPattern}
|
||||
editIndexPatternClick={editPattern}
|
||||
deleteIndexPatternClick={() =>
|
||||
removeHandler([indexPattern as RemoveDataViewProps], <div>{warning}</div>)
|
||||
}
|
||||
defaultIndex={defaultIndex}
|
||||
canSave={userEditPermission}
|
||||
>
|
||||
{showTagsSection && (
|
||||
<EuiFlexGroup wrap gutterSize="s">
|
||||
{Boolean(indexPattern.timeFieldName) && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="warning">{timeFilterHeader}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{indexPattern.id && indexPattern.id.indexOf(securitySolution) === 0 && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge>{securityDataView}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{tags.map((tag: any) => (
|
||||
<EuiFlexItem grow={false} key={tag.key}>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup wrap gutterSize="l" alignItems="center">
|
||||
{Boolean(indexPattern.title) && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="none" alignItems="center">
|
||||
<EuiText size="s">{indexPatternHeading}</EuiText>
|
||||
<EuiCode style={codeStyle}>{indexPattern.title}</EuiCode>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{Boolean(indexPattern.timeFieldName) && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="none" alignItems="center">
|
||||
<EuiText size="s">{timeFilterHeading}</EuiText>
|
||||
<EuiCode style={codeStyle}>{indexPattern.timeFieldName}</EuiCode>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{indexPattern.id && indexPattern.id.indexOf(securitySolution) === 0 && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge>{securityDataView}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{tags.map((tag: any) => (
|
||||
<EuiFlexItem grow={false} key={tag.key}>
|
||||
{tag.key === 'default' ? (
|
||||
<EuiBadge iconType="starFilled" color="default">
|
||||
{tag.name}
|
||||
</EuiBadge>
|
||||
) : (
|
||||
<EuiBadge color="hollow">{tag.name}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="indexPatternManagement.editIndexPattern.timeFilterLabel.timeFilterDetail"
|
||||
defaultMessage="View and edit fields in {indexPatternTitle}. Field attributes, such as type and searchability, are based on {mappingAPILink} in Elasticsearch."
|
||||
values={{
|
||||
indexPatternTitle: <strong>{indexPattern.title}</strong>,
|
||||
mappingAPILink: (
|
||||
<EuiLink href={docsUrl} target="_blank" external>
|
||||
{mappingAPILink}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>{' '}
|
||||
</p>
|
||||
</EuiText>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
{conflictedFields.length > 0 && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
|
@ -224,7 +241,7 @@ export const EditIndexPattern = withRouter(
|
|||
</>
|
||||
)}
|
||||
</IndexHeader>
|
||||
<EuiSpacer />
|
||||
<EuiSpacer size="xl" />
|
||||
<Tabs
|
||||
indexPattern={indexPattern}
|
||||
saveIndexPattern={dataViews.updateSavedObject.bind(dataViews)}
|
||||
|
@ -237,6 +254,7 @@ export const EditIndexPattern = withRouter(
|
|||
setFields(indexPattern.getNonScriptedFields());
|
||||
}}
|
||||
/>
|
||||
{displayIndexPatternEditor}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButtonIcon, EuiPageHeader, EuiToolTip } from '@elastic/eui';
|
||||
import { EuiButton, EuiButtonEmpty, EuiPageHeader } from '@elastic/eui';
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
|
||||
interface IndexHeaderProps {
|
||||
indexPattern: DataView;
|
||||
defaultIndex?: string;
|
||||
setDefault?: () => void;
|
||||
editIndexPatternClick?: () => void;
|
||||
deleteIndexPatternClick?: () => void;
|
||||
canSave: boolean;
|
||||
}
|
||||
|
@ -24,7 +25,15 @@ const setDefaultAriaLabel = i18n.translate('indexPatternManagement.editDataView.
|
|||
});
|
||||
|
||||
const setDefaultTooltip = i18n.translate('indexPatternManagement.editDataView.setDefaultTooltip', {
|
||||
defaultMessage: 'Set as default data view.',
|
||||
defaultMessage: 'Set as default',
|
||||
});
|
||||
|
||||
const editAriaLabel = i18n.translate('indexPatternManagement.editDataView.editAria', {
|
||||
defaultMessage: 'Edit data view.',
|
||||
});
|
||||
|
||||
const editTooltip = i18n.translate('indexPatternManagement.editDataView.editTooltip', {
|
||||
defaultMessage: 'Edit',
|
||||
});
|
||||
|
||||
const removeAriaLabel = i18n.translate('indexPatternManagement.editDataView.removeAria', {
|
||||
|
@ -32,42 +41,52 @@ const removeAriaLabel = i18n.translate('indexPatternManagement.editDataView.remo
|
|||
});
|
||||
|
||||
const removeTooltip = i18n.translate('indexPatternManagement.editDataView.removeTooltip', {
|
||||
defaultMessage: 'Delete data view.',
|
||||
defaultMessage: 'Delete',
|
||||
});
|
||||
|
||||
export const IndexHeader: React.FC<IndexHeaderProps> = ({
|
||||
defaultIndex,
|
||||
indexPattern,
|
||||
setDefault,
|
||||
editIndexPatternClick,
|
||||
deleteIndexPatternClick,
|
||||
children,
|
||||
canSave,
|
||||
}) => {
|
||||
return (
|
||||
<EuiPageHeader
|
||||
pageTitle={<span data-test-subj="indexPatternTitle">{indexPattern.title}</span>}
|
||||
pageTitle={<span data-test-subj="indexPatternTitle">{indexPattern.getName()}</span>}
|
||||
rightSideItems={[
|
||||
canSave && (
|
||||
<EuiButton
|
||||
onClick={editIndexPatternClick}
|
||||
iconType="pencil"
|
||||
aria-label={editAriaLabel}
|
||||
data-test-subj="editIndexPatternButton"
|
||||
>
|
||||
{editTooltip}
|
||||
</EuiButton>
|
||||
),
|
||||
defaultIndex !== indexPattern.id && setDefault && canSave && (
|
||||
<EuiToolTip content={setDefaultTooltip}>
|
||||
<EuiButtonIcon
|
||||
color="text"
|
||||
onClick={setDefault}
|
||||
iconType="starFilled"
|
||||
aria-label={setDefaultAriaLabel}
|
||||
data-test-subj="setDefaultIndexPatternButton"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
<EuiButton
|
||||
onClick={setDefault}
|
||||
iconType="starFilled"
|
||||
aria-label={setDefaultAriaLabel}
|
||||
data-test-subj="setDefaultIndexPatternButton"
|
||||
>
|
||||
{setDefaultTooltip}
|
||||
</EuiButton>
|
||||
),
|
||||
canSave && (
|
||||
<EuiToolTip content={removeTooltip}>
|
||||
<EuiButtonIcon
|
||||
color="danger"
|
||||
onClick={deleteIndexPatternClick}
|
||||
iconType="trash"
|
||||
aria-label={removeAriaLabel}
|
||||
data-test-subj="deleteIndexPatternButton"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
<EuiButtonEmpty
|
||||
color="danger"
|
||||
onClick={deleteIndexPatternClick}
|
||||
iconType="trash"
|
||||
aria-label={removeAriaLabel}
|
||||
data-test-subj="deleteIndexPatternButton"
|
||||
>
|
||||
{removeTooltip}
|
||||
</EuiButtonEmpty>
|
||||
),
|
||||
].filter(Boolean)}
|
||||
>
|
||||
|
|
|
@ -27,6 +27,8 @@ export interface RemoveDataViewProps {
|
|||
id: string;
|
||||
title: string;
|
||||
namespaces?: string[] | undefined;
|
||||
name?: string;
|
||||
getName: () => string;
|
||||
}
|
||||
|
||||
interface RemoveDataViewDeps {
|
||||
|
|
|
@ -29,7 +29,7 @@ exports[`delete modal content render 1`] = `
|
|||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"field": "title",
|
||||
"field": "name",
|
||||
"name": "Data view",
|
||||
"sortable": true,
|
||||
},
|
||||
|
@ -46,7 +46,9 @@ exports[`delete modal content render 1`] = `
|
|||
items={
|
||||
Array [
|
||||
Object {
|
||||
"getName": [Function],
|
||||
"id": "1",
|
||||
"name": "logstash-*",
|
||||
"namespaces": Array [
|
||||
"a",
|
||||
"b",
|
||||
|
@ -98,7 +100,7 @@ exports[`delete modal content render 2`] = `
|
|||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"field": "title",
|
||||
"field": "name",
|
||||
"name": "Data view",
|
||||
"sortable": true,
|
||||
},
|
||||
|
@ -107,7 +109,9 @@ exports[`delete modal content render 2`] = `
|
|||
items={
|
||||
Array [
|
||||
Object {
|
||||
"getName": [Function],
|
||||
"id": "1",
|
||||
"name": "logstash-*",
|
||||
"namespaces": Array [
|
||||
"a",
|
||||
"b",
|
||||
|
@ -159,7 +163,7 @@ exports[`delete modal content render 3`] = `
|
|||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"field": "title",
|
||||
"field": "name",
|
||||
"name": "Data view",
|
||||
"sortable": true,
|
||||
},
|
||||
|
@ -176,7 +180,9 @@ exports[`delete modal content render 3`] = `
|
|||
items={
|
||||
Array [
|
||||
Object {
|
||||
"getName": [Function],
|
||||
"id": "1",
|
||||
"name": "logstash-*",
|
||||
"namespaces": Array [
|
||||
"*",
|
||||
],
|
||||
|
@ -226,7 +232,7 @@ exports[`delete modal content render 4`] = `
|
|||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"field": "title",
|
||||
"field": "name",
|
||||
"name": "Data view",
|
||||
"sortable": true,
|
||||
},
|
||||
|
@ -243,14 +249,18 @@ exports[`delete modal content render 4`] = `
|
|||
items={
|
||||
Array [
|
||||
Object {
|
||||
"getName": [Function],
|
||||
"id": "1",
|
||||
"name": "logstash-*",
|
||||
"namespaces": Array [
|
||||
"*",
|
||||
],
|
||||
"title": "logstash-*",
|
||||
},
|
||||
Object {
|
||||
"getName": [Function],
|
||||
"id": "1",
|
||||
"name": "log*",
|
||||
"namespaces": Array [
|
||||
"a",
|
||||
"b",
|
||||
|
|
|
@ -9,20 +9,34 @@
|
|||
import { deleteModalMsg } from './delete_modal_msg';
|
||||
|
||||
describe('delete modal content', () => {
|
||||
const toDVProps = (title: string, namespaces: string[]) => {
|
||||
const toDVProps = (title: string, name: string, namespaces: string[]) => {
|
||||
return {
|
||||
id: '1',
|
||||
title,
|
||||
name,
|
||||
namespaces,
|
||||
getName: () => title,
|
||||
};
|
||||
};
|
||||
|
||||
test('render', () => {
|
||||
expect(deleteModalMsg([toDVProps('logstash-*', ['a', 'b', 'c'])], true)).toMatchSnapshot();
|
||||
expect(deleteModalMsg([toDVProps('logstash-*', ['a', 'b', 'c'])], false)).toMatchSnapshot();
|
||||
expect(deleteModalMsg([toDVProps('logstash-*', ['*'])], true)).toMatchSnapshot();
|
||||
expect(
|
||||
deleteModalMsg([toDVProps('logstash-*', ['*']), toDVProps('log*', ['a', 'b', 'c'])], true)
|
||||
deleteModalMsg([toDVProps('logstash-*', 'Logstash Star', ['a', 'b', 'c'])], true)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
deleteModalMsg([toDVProps('logstash-*', 'Logstash Star', ['a', 'b', 'c'])], false)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
deleteModalMsg([toDVProps('logstash-*', 'Logstash Star', ['*'])], true)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
deleteModalMsg(
|
||||
[
|
||||
toDVProps('logstash-*', 'Logstash Star', ['*']),
|
||||
toDVProps('log*', 'Log Star', ['a', 'b', 'c']),
|
||||
],
|
||||
true
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -34,7 +34,7 @@ const tableTitle = i18n.translate('indexPatternManagement.dataViewTable.tableTit
|
|||
export const deleteModalMsg = (views: RemoveDataViewProps[], hasSpaces: boolean) => {
|
||||
const columns: Array<EuiTableFieldDataColumnType<any>> = [
|
||||
{
|
||||
field: 'title',
|
||||
field: 'name',
|
||||
name: dataViewColumnName,
|
||||
sortable: true,
|
||||
},
|
||||
|
@ -50,6 +50,7 @@ export const deleteModalMsg = (views: RemoveDataViewProps[], hasSpaces: boolean)
|
|||
});
|
||||
}
|
||||
|
||||
views.forEach((view) => (view.name = view.getName()));
|
||||
return (
|
||||
<div>
|
||||
<EuiCallOut
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
EuiInMemoryTable,
|
||||
EuiPageHeader,
|
||||
EuiSpacer,
|
||||
EuiIconTip,
|
||||
EuiBasicTableColumn,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
@ -209,7 +210,20 @@ export const IndexPatternTable = ({
|
|||
width: '70%',
|
||||
render: (name: string, dataView: IndexPatternTableItem) => (
|
||||
<div>
|
||||
<EuiLink {...reactRouterNavigate(history, `patterns/${dataView.id}`)}>{name}</EuiLink>
|
||||
<EuiLink {...reactRouterNavigate(history, `patterns/${dataView.id}`)}>
|
||||
{dataView.getName()}
|
||||
{dataView.name ? (
|
||||
<>
|
||||
|
||||
<EuiIconTip
|
||||
type="iInCircle"
|
||||
color="text"
|
||||
aria-label={dataView.title}
|
||||
content={dataView.title}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</EuiLink>
|
||||
{dataView?.id?.indexOf(securitySolution) === 0 && (
|
||||
<>
|
||||
 <EuiBadge>{securityDataView}</EuiBadge>
|
||||
|
|
|
@ -15,8 +15,11 @@ export interface IndexPatternCreationOption {
|
|||
export interface IndexPatternTableItem {
|
||||
id: string;
|
||||
title: string;
|
||||
name?: string;
|
||||
info?: string;
|
||||
default: boolean;
|
||||
tags?: Array<{ key: string; name: string }>;
|
||||
sort: string;
|
||||
namespaces?: string[];
|
||||
getName: () => string;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ const indexPatternContractMock = {
|
|||
{
|
||||
id: 'test1',
|
||||
title: 'test name 1',
|
||||
name: 'Test Name 1',
|
||||
},
|
||||
])
|
||||
),
|
||||
|
|
|
@ -35,7 +35,7 @@ const isRollup = (indexPatternType: string = '') => {
|
|||
export async function getIndexPatterns(defaultIndex: string, dataViewsService: DataViewsContract) {
|
||||
const existingIndexPatterns = await dataViewsService.getIdsWithTitle(true);
|
||||
const indexPatternsListItems = existingIndexPatterns.map((idxPattern) => {
|
||||
const { id, title, namespaces } = idxPattern;
|
||||
const { id, title, namespaces, name } = idxPattern;
|
||||
const isDefault = defaultIndex === id;
|
||||
const tags = getTags(idxPattern, isDefault);
|
||||
|
||||
|
@ -43,12 +43,14 @@ export async function getIndexPatterns(defaultIndex: string, dataViewsService: D
|
|||
id,
|
||||
namespaces,
|
||||
title,
|
||||
name,
|
||||
default: isDefault,
|
||||
tags,
|
||||
// the prepending of 0 at the default pattern takes care of prioritization
|
||||
// so the sorting will but the default index on top
|
||||
// or on bottom of a the table
|
||||
sort: `${isDefault ? '0' : '1'}${title}`,
|
||||
getName: () => (name ? name : title),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ export function stubbedSavedObjectDataView(
|
|||
timeFieldName: 'time',
|
||||
fields: JSON.stringify(stubLogstashFieldSpecMap),
|
||||
title: 'title',
|
||||
name: 'Name',
|
||||
},
|
||||
version: '2',
|
||||
references: [],
|
||||
|
|
|
@ -801,6 +801,7 @@ Object {
|
|||
},
|
||||
},
|
||||
"id": "test-pattern",
|
||||
"name": "Name",
|
||||
"runtimeFieldMap": Object {
|
||||
"runtime_field": Object {
|
||||
"script": Object {
|
||||
|
|
|
@ -40,6 +40,7 @@ Object {
|
|||
},
|
||||
"fields": Object {},
|
||||
"id": "id",
|
||||
"name": "Kibana *",
|
||||
"namespaces": undefined,
|
||||
"runtimeFieldMap": Object {
|
||||
"aRuntimeField": Object {
|
||||
|
|
|
@ -48,7 +48,7 @@ function create(id: string) {
|
|||
const {
|
||||
type,
|
||||
version,
|
||||
attributes: { timeFieldName, fields, title },
|
||||
attributes: { timeFieldName, fields, title, name },
|
||||
} = stubbedSavedObjectIndexPattern(id);
|
||||
|
||||
return new DataView({
|
||||
|
@ -59,6 +59,7 @@ function create(id: string) {
|
|||
timeFieldName,
|
||||
fields: { ...JSON.parse(fields), runtime_field: runtimeField },
|
||||
title,
|
||||
name,
|
||||
runtimeFieldMap,
|
||||
},
|
||||
fieldFormats: fieldFormatsMock,
|
||||
|
|
|
@ -127,11 +127,14 @@ export class DataView implements DataViewBase {
|
|||
* Map of runtime field definitions by field name
|
||||
*/
|
||||
private runtimeFieldMap: Record<string, RuntimeFieldSpec>;
|
||||
|
||||
/**
|
||||
* Prevents errors when index pattern exists before indices
|
||||
*/
|
||||
public readonly allowNoIndex: boolean = false;
|
||||
/**
|
||||
* Name of the data view. Human readable name used to differentiate data view.
|
||||
*/
|
||||
public name: string = '';
|
||||
|
||||
/**
|
||||
* constructor
|
||||
|
@ -167,8 +170,14 @@ export class DataView implements DataViewBase {
|
|||
this.allowNoIndex = spec.allowNoIndex || false;
|
||||
this.runtimeFieldMap = spec.runtimeFieldMap || {};
|
||||
this.namespaces = spec.namespaces || [];
|
||||
this.name = spec.name || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of Data View
|
||||
*/
|
||||
getName = () => (this.name ? this.name : this.title);
|
||||
|
||||
/**
|
||||
* Get last saved saved object fields
|
||||
*/
|
||||
|
@ -274,6 +283,7 @@ export class DataView implements DataViewBase {
|
|||
runtimeFieldMap: this.runtimeFieldMap,
|
||||
fieldAttrs: this.fieldAttrs,
|
||||
allowNoIndex: this.allowNoIndex,
|
||||
name: this.name,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -379,6 +389,7 @@ export class DataView implements DataViewBase {
|
|||
typeMeta: JSON.stringify(this.typeMeta ?? {}),
|
||||
allowNoIndex: this.allowNoIndex ? this.allowNoIndex : undefined,
|
||||
runtimeFieldMap: runtimeFieldMap ? JSON.stringify(runtimeFieldMap) : undefined,
|
||||
name: this.name,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ const savedObject = {
|
|||
version: 'version',
|
||||
attributes: {
|
||||
title: 'kibana-*',
|
||||
name: 'Kibana *',
|
||||
timeFieldName: '@timestamp',
|
||||
fields: '[]',
|
||||
sourceFilters: '[{"value":"item1"},{"value":"item2"}]',
|
||||
|
@ -159,7 +160,7 @@ describe('IndexPatterns', () => {
|
|||
expect(await indexPatterns.getIds()).toEqual(['id']);
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
type: 'index-pattern',
|
||||
fields: ['title', 'type', 'typeMeta'],
|
||||
fields: ['title', 'type', 'typeMeta', 'name'],
|
||||
perPage: 10000,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,11 +37,14 @@ import { DuplicateDataViewError, DataViewInsufficientAccessError } from '../erro
|
|||
|
||||
const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3;
|
||||
|
||||
export type DataViewSavedObjectAttrs = Pick<DataViewAttributes, 'title' | 'type' | 'typeMeta'>;
|
||||
export type DataViewSavedObjectAttrs = Pick<
|
||||
DataViewAttributes,
|
||||
'title' | 'type' | 'typeMeta' | 'name'
|
||||
>;
|
||||
|
||||
export type IndexPatternListSavedObjectAttrs = Pick<
|
||||
DataViewAttributes,
|
||||
'title' | 'type' | 'typeMeta'
|
||||
'title' | 'type' | 'typeMeta' | 'name'
|
||||
>;
|
||||
|
||||
/**
|
||||
|
@ -68,6 +71,7 @@ export interface DataViewListItem {
|
|||
* Data view type meta
|
||||
*/
|
||||
typeMeta?: TypeMeta;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,7 +248,7 @@ export interface DataViewsServicePublicMethods {
|
|||
indexPattern: DataView,
|
||||
saveAttempts?: number,
|
||||
ignoreErrors?: boolean
|
||||
) => Promise<void | Error>;
|
||||
) => Promise<DataView | void | Error>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -306,7 +310,7 @@ export class DataViewsService {
|
|||
private async refreshSavedObjectsCache() {
|
||||
const so = await this.savedObjectsClient.find<DataViewSavedObjectAttrs>({
|
||||
type: DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
fields: ['title', 'type', 'typeMeta'],
|
||||
fields: ['title', 'type', 'typeMeta', 'name'],
|
||||
perPage: 10000,
|
||||
});
|
||||
this.savedObjectsCache = so;
|
||||
|
@ -377,6 +381,7 @@ export class DataViewsService {
|
|||
title: obj?.attributes?.title,
|
||||
type: obj?.attributes?.type,
|
||||
typeMeta: obj?.attributes?.typeMeta && JSON.parse(obj?.attributes?.typeMeta),
|
||||
name: obj?.attributes?.name,
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -602,6 +607,7 @@ export class DataViewsService {
|
|||
type,
|
||||
fieldAttrs,
|
||||
allowNoIndex,
|
||||
name,
|
||||
},
|
||||
} = savedObject;
|
||||
|
||||
|
@ -628,6 +634,7 @@ export class DataViewsService {
|
|||
fieldAttrs: parsedFieldAttrs,
|
||||
allowNoIndex,
|
||||
runtimeFieldMap: parsedRuntimeFieldMap,
|
||||
name,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -833,7 +840,7 @@ export class DataViewsService {
|
|||
indexPattern: DataView,
|
||||
saveAttempts: number = 0,
|
||||
ignoreErrors: boolean = false
|
||||
): Promise<void | Error> {
|
||||
): Promise<DataView | void | Error> {
|
||||
if (!indexPattern.id) return;
|
||||
if (!(await this.getCanSave())) {
|
||||
throw new DataViewInsufficientAccessError(indexPattern.id);
|
||||
|
@ -858,6 +865,7 @@ export class DataViewsService {
|
|||
.then((resp) => {
|
||||
indexPattern.id = resp.id;
|
||||
indexPattern.version = resp.version;
|
||||
return indexPattern;
|
||||
})
|
||||
.catch(async (err) => {
|
||||
if (err?.res?.status === 409 && saveAttempts++ < MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS) {
|
||||
|
|
|
@ -153,6 +153,10 @@ export interface DataViewAttributes {
|
|||
* Prevents errors when index pattern exists before indices
|
||||
*/
|
||||
allowNoIndex?: boolean;
|
||||
/**
|
||||
* Name of the data view. Human readable name used to differentiate data view.
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -476,6 +480,10 @@ export type DataViewSpec = {
|
|||
* Array of namespace ids
|
||||
*/
|
||||
namespaces?: string[];
|
||||
/**
|
||||
* Name of the data view. Human readable name used to differentiate data view.
|
||||
*/
|
||||
name?: string;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
|
|
|
@ -69,6 +69,7 @@ const dataViewSpecSchema = schema.object({
|
|||
),
|
||||
allowNoIndex: schema.maybe(schema.boolean()),
|
||||
runtimeFieldMap: schema.maybe(schema.recordOf(schema.string(), runtimeFieldSchema)),
|
||||
name: schema.maybe(schema.string()),
|
||||
});
|
||||
|
||||
const registerCreateDataViewRouteFactory =
|
||||
|
|
|
@ -86,8 +86,10 @@ export const buildDataViewMock = ({
|
|||
const dataView = {
|
||||
id: `${name}-id`,
|
||||
title: `${name}-title`,
|
||||
name,
|
||||
metaFields: ['_index', '_score'],
|
||||
fields: dataViewFields,
|
||||
getName: () => name,
|
||||
getComputedFields: () => ({ docvalueFields: [], scriptFields: {}, storedFields: ['*'] }),
|
||||
getSourceFiltering: () => ({}),
|
||||
getFieldByName: jest.fn((fieldName: string) => dataViewFields.getByName(fieldName)),
|
||||
|
|
|
@ -323,7 +323,7 @@ export function DiscoverSidebarComponent({
|
|||
onAddField={editField}
|
||||
onDataViewCreated={createNewDataView}
|
||||
trigger={{
|
||||
label: selectedIndexPattern?.title || '',
|
||||
label: selectedIndexPattern?.getName() || '',
|
||||
'data-test-subj': 'indexPattern-switch-link',
|
||||
title: selectedIndexPattern?.title || '',
|
||||
fullWidth: true,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
export interface IndexPatternRef {
|
||||
id: string;
|
||||
title: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface FieldDetails {
|
||||
|
|
|
@ -174,7 +174,7 @@ export const DiscoverTopNav = ({
|
|||
|
||||
const dataViewPickerProps = {
|
||||
trigger: {
|
||||
label: indexPattern?.title || '',
|
||||
label: indexPattern?.getName() || '',
|
||||
'data-test-subj': 'discover-dataView-switch-link',
|
||||
title: indexPattern?.title || '',
|
||||
},
|
||||
|
|
|
@ -22,6 +22,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
runtimeFieldMap: '{}',
|
||||
timeFieldName: 'order_date',
|
||||
title: 'kibana_sample_data_ecommerce',
|
||||
name: 'Kibana Sample Data eCommerce',
|
||||
typeMeta: '{}',
|
||||
},
|
||||
coreMigrationVersion: '8.0.0',
|
||||
|
|
|
@ -208,6 +208,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
migrationVersion: {},
|
||||
attributes: {
|
||||
title: 'kibana_sample_data_flights',
|
||||
name: 'Kibana Sample Data Flights',
|
||||
timeFieldName: 'timestamp',
|
||||
fieldFormatMap:
|
||||
'{"hour_of_day":{"id":"number","params":{"pattern":"00"}},"AvgTicketPrice":{"id":"number","params":{"pattern":"$0,0.[00]"}}}',
|
||||
|
|
|
@ -370,6 +370,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
migrationVersion: {},
|
||||
attributes: {
|
||||
title: 'kibana_sample_data_logs',
|
||||
name: 'Kibana Sample Data Logs',
|
||||
timeFieldName: 'timestamp',
|
||||
fieldFormatMap: '{"hour_of_day":{}}',
|
||||
runtimeFieldMap:
|
||||
|
|
|
@ -18,7 +18,11 @@ import {
|
|||
reactRouterNavigate,
|
||||
KibanaThemeProvider,
|
||||
} from '@kbn/kibana-react-plugin/public';
|
||||
import { ManagementSection, MANAGEMENT_BREADCRUMB } from '../../utils';
|
||||
import {
|
||||
ManagementSection,
|
||||
MANAGEMENT_BREADCRUMB,
|
||||
MANAGEMENT_BREADCRUMB_NO_HREF,
|
||||
} from '../../utils';
|
||||
import { ManagementRouter } from './management_router';
|
||||
import { managementSidebarNav } from '../management_sidebar_nav/management_sidebar_nav';
|
||||
import { SectionsServiceStart } from '../../types';
|
||||
|
@ -53,8 +57,14 @@ export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppPr
|
|||
...(item.href ? reactRouterNavigate(scopedHistory, item.href) : {}),
|
||||
});
|
||||
|
||||
// Clicking the Management breadcrumb to navigate back to the "root" only
|
||||
// makes sense if there's a management app open. So when one isn't open
|
||||
// this breadcrumb shouldn't be a clickable link.
|
||||
const managementBreadcrumb = crumbs.length
|
||||
? MANAGEMENT_BREADCRUMB
|
||||
: MANAGEMENT_BREADCRUMB_NO_HREF;
|
||||
setBreadcrumbs([
|
||||
wrapBreadcrumb(MANAGEMENT_BREADCRUMB, history),
|
||||
wrapBreadcrumb(managementBreadcrumb, history),
|
||||
...crumbs.map((item) => wrapBreadcrumb(item, appHistory || history)),
|
||||
]);
|
||||
},
|
||||
|
|
|
@ -8,9 +8,15 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const managementBreadcrumbText = i18n.translate('management.breadcrumb', {
|
||||
defaultMessage: 'Stack Management',
|
||||
});
|
||||
|
||||
export const MANAGEMENT_BREADCRUMB_NO_HREF = {
|
||||
text: managementBreadcrumbText,
|
||||
};
|
||||
|
||||
export const MANAGEMENT_BREADCRUMB = {
|
||||
text: i18n.translate('management.breadcrumb', {
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
text: managementBreadcrumbText,
|
||||
href: '/',
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { MANAGEMENT_BREADCRUMB } from './breadcrumbs';
|
||||
export { MANAGEMENT_BREADCRUMB, MANAGEMENT_BREADCRUMB_NO_HREF } from './breadcrumbs';
|
||||
export type { RegisterManagementAppArgs } from './management_app';
|
||||
export { ManagementApp } from './management_app';
|
||||
export type { RegisterManagementSectionArgs } from './management_section';
|
||||
|
|
|
@ -46,7 +46,7 @@ export function Example({}: {} & StorybookParams) {
|
|||
});
|
||||
};
|
||||
|
||||
const triggerLabel = dataView?.title || 'Choose Data View';
|
||||
const triggerLabel = dataView?.getName() || 'Choose Data View';
|
||||
|
||||
return (
|
||||
<DataViewPicker
|
||||
|
|
|
@ -69,7 +69,7 @@ describe('SavedObjectsFinder', () => {
|
|||
|
||||
expect(core.savedObjects.client.find).toHaveBeenCalledWith({
|
||||
type: ['search'],
|
||||
fields: ['title'],
|
||||
fields: ['title', 'name'],
|
||||
search: undefined,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
|
@ -224,7 +224,7 @@ describe('SavedObjectsFinder', () => {
|
|||
|
||||
expect(core.savedObjects.client.find).toHaveBeenCalledWith({
|
||||
type: ['search'],
|
||||
fields: ['title'],
|
||||
fields: ['title', 'name'],
|
||||
search: 'abc*',
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
|
@ -267,7 +267,7 @@ describe('SavedObjectsFinder', () => {
|
|||
|
||||
expect(core.savedObjects.client.find).toHaveBeenCalledWith({
|
||||
type: ['type1', 'type2'],
|
||||
fields: ['title', 'field1', 'field2', 'field3'],
|
||||
fields: ['title', 'name', 'field1', 'field2', 'field3'],
|
||||
search: 'abc*',
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
|
@ -333,7 +333,7 @@ describe('SavedObjectsFinder', () => {
|
|||
|
||||
expect(core.savedObjects.client.find).toHaveBeenCalledWith({
|
||||
type: ['search', 'vis'],
|
||||
fields: ['title'],
|
||||
fields: ['title', 'name'],
|
||||
search: undefined,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
|
|
|
@ -52,12 +52,14 @@ export interface SavedObjectMetaData<T = unknown> {
|
|||
|
||||
interface FinderAttributes {
|
||||
title?: string;
|
||||
name?: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface SavedObjectFinderState {
|
||||
items: Array<{
|
||||
title: string | null;
|
||||
name: string | null;
|
||||
id: SimpleSavedObject['id'];
|
||||
type: SimpleSavedObject['type'];
|
||||
savedObject: SimpleSavedObject<FinderAttributes>;
|
||||
|
@ -121,7 +123,7 @@ class SavedObjectFinderUi extends React.Component<
|
|||
|
||||
const fields = Object.values(metaDataMap)
|
||||
.map((metaData) => metaData.includeFields || [])
|
||||
.reduce((allFields, currentFields) => allFields.concat(currentFields), ['title']);
|
||||
.reduce((allFields, currentFields) => allFields.concat(currentFields), ['title', 'name']);
|
||||
|
||||
const perPage = this.props.uiSettings.get(LISTING_LIMIT_SETTING);
|
||||
const resp = await this.props.savedObjects.client.find<FinderAttributes>({
|
||||
|
@ -155,13 +157,14 @@ class SavedObjectFinderUi extends React.Component<
|
|||
page: 0,
|
||||
items: resp.savedObjects.map((savedObject) => {
|
||||
const {
|
||||
attributes: { title },
|
||||
attributes: { name, title },
|
||||
id,
|
||||
type,
|
||||
} = savedObject;
|
||||
|
||||
const titleToUse = typeof title === 'string' ? title : '';
|
||||
return {
|
||||
title: typeof title === 'string' ? title : '',
|
||||
title: titleToUse,
|
||||
name: typeof name === 'string' ? name : titleToUse,
|
||||
id,
|
||||
type,
|
||||
savedObject,
|
||||
|
@ -458,7 +461,7 @@ class SavedObjectFinderUi extends React.Component<
|
|||
)!;
|
||||
const fullName = currentSavedObjectMetaData.getTooltipForSavedObject
|
||||
? currentSavedObjectMetaData.getTooltipForSavedObject(item.savedObject)
|
||||
: `${item.title} (${currentSavedObjectMetaData!.name})`;
|
||||
: `${item.name} (${currentSavedObjectMetaData!.name})`;
|
||||
const iconType = (
|
||||
currentSavedObjectMetaData ||
|
||||
({
|
||||
|
@ -469,7 +472,7 @@ class SavedObjectFinderUi extends React.Component<
|
|||
<EuiListGroupItem
|
||||
key={item.id}
|
||||
iconType={iconType}
|
||||
label={item.title}
|
||||
label={item.name}
|
||||
onClick={
|
||||
onChoose
|
||||
? () => {
|
||||
|
|
|
@ -38,9 +38,9 @@ export function DataViewsList({
|
|||
data-test-subj="indexPattern-switcher"
|
||||
searchable
|
||||
singleSelection="always"
|
||||
options={dataViewsList?.map(({ title, id }) => ({
|
||||
options={dataViewsList?.map(({ title, id, name }) => ({
|
||||
key: id,
|
||||
label: title,
|
||||
label: name ? name : title,
|
||||
value: id,
|
||||
checked: id === currentDataViewId ? 'on' : undefined,
|
||||
}))}
|
||||
|
|
|
@ -231,7 +231,7 @@ class FilterEditorUI extends Component<Props, State> {
|
|||
})}
|
||||
options={this.props.indexPatterns}
|
||||
selectedOptions={selectedIndexPattern ? [selectedIndexPattern] : []}
|
||||
getLabel={(indexPattern) => indexPattern.title}
|
||||
getLabel={(indexPattern) => indexPattern.getName()}
|
||||
onChange={this.onIndexPatternChange}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
isClearable={false}
|
||||
|
|
|
@ -167,11 +167,11 @@ function SidebarTitle({ savedSearch, vis, isLinkedSearch, eventEmitter }: Sideba
|
|||
title={i18n.translate('visDefaultEditor.sidebar.indexPatternAriaLabel', {
|
||||
defaultMessage: 'Index pattern: {title}',
|
||||
values: {
|
||||
title: vis.data.indexPattern!.title,
|
||||
title: vis.data.indexPattern!.getName(),
|
||||
},
|
||||
})}
|
||||
>
|
||||
{vis.data.indexPattern!.title}
|
||||
{vis.data.indexPattern!.getName()}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
) : (
|
||||
|
|
|
@ -24,7 +24,7 @@ type IdsWithTitle = Awaited<ReturnType<DataViewsService['getIdsWithTitle']>>;
|
|||
type SelectedOptions = EuiComboBoxProps<string>['selectedOptions'];
|
||||
|
||||
const toComboBoxOptions = (options: IdsWithTitle) =>
|
||||
options.map(({ title, id }) => ({ label: title, id }));
|
||||
options.map(({ name, title, id }) => ({ label: name ? name : title, id }));
|
||||
|
||||
export const ComboBoxSelect = ({
|
||||
fetchedIndex,
|
||||
|
@ -56,7 +56,7 @@ export const ComboBoxSelect = ({
|
|||
options = [
|
||||
{
|
||||
id: indexPattern.id,
|
||||
label: indexPattern.title,
|
||||
label: indexPattern.getName(),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ export const IndexPatternSelect = ({
|
|||
allowSwitchMode={allowIndexSwitchingMode}
|
||||
onIndexChange={onIndexChange}
|
||||
onModeChange={onModeChange}
|
||||
placeholder={fetchedIndex.defaultIndex?.title ?? ''}
|
||||
placeholder={fetchedIndex.defaultIndex?.getName() ?? ''}
|
||||
data-test-subj="metricsIndexPatternInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
|
|
@ -324,7 +324,7 @@ const TopNav = ({
|
|||
}),
|
||||
},
|
||||
})
|
||||
: vis.data.indexPattern.title,
|
||||
: vis.data.indexPattern.getName(),
|
||||
},
|
||||
isMissingCurrent: isMissingCurrentDataView,
|
||||
onChangeDataView,
|
||||
|
|
|
@ -18,7 +18,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
describe('Filter panel', () => {
|
||||
before(async () => {
|
||||
await PageObjects.common.navigateToApp('discover');
|
||||
await PageObjects.discover.selectIndexPattern('kibana_sample_data_flights');
|
||||
await PageObjects.discover.selectIndexPattern('Kibana Sample Data Flights');
|
||||
});
|
||||
|
||||
it('a11y test on add filter panel', async () => {
|
||||
|
|
|
@ -19,7 +19,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'timePicker']);
|
||||
|
||||
const createDataView = async (dataViewName: string) => {
|
||||
await testSubjects.setValue('createIndexPatternNameInput', dataViewName, {
|
||||
await testSubjects.setValue('createIndexPatternTitleInput', dataViewName, {
|
||||
clearWithKeyboard: true,
|
||||
typeCharByChar: true,
|
||||
});
|
||||
|
|
|
@ -34,7 +34,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const createDataView = async (dataViewName: string) => {
|
||||
await PageObjects.discover.clickIndexPatternActions();
|
||||
await PageObjects.discover.clickCreateNewDataView();
|
||||
await testSubjects.setValue('createIndexPatternNameInput', dataViewName, {
|
||||
await testSubjects.setValue('createIndexPatternTitleInput', dataViewName, {
|
||||
clearWithKeyboard: true,
|
||||
typeCharByChar: true,
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']);
|
||||
|
||||
const createDataView = async (dataViewName: string) => {
|
||||
await testSubjects.setValue('createIndexPatternNameInput', dataViewName, {
|
||||
await testSubjects.setValue('createIndexPatternTitleInput', dataViewName, {
|
||||
clearWithKeyboard: true,
|
||||
typeCharByChar: true,
|
||||
});
|
||||
|
|
|
@ -118,6 +118,30 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
describe('edit index pattern', () => {
|
||||
it('on edit click', async () => {
|
||||
await PageObjects.settings.editIndexPattern('logstash-*', '@timestamp', 'Logstash Star');
|
||||
|
||||
await retry.try(async () => {
|
||||
expect(await testSubjects.getVisibleText('indexPatternTitle')).to.contain(
|
||||
`Logstash Star`
|
||||
);
|
||||
});
|
||||
});
|
||||
it('shows edit confirm message when editing index-pattern', async () => {
|
||||
await PageObjects.settings.editIndexPattern(
|
||||
'logstash-2*',
|
||||
'@timestamp',
|
||||
'Index Star',
|
||||
true
|
||||
);
|
||||
|
||||
await retry.try(async () => {
|
||||
expect(await testSubjects.getVisibleText('indexPatternTitle')).to.contain(`Index Star`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('index pattern deletion', function indexDelete() {
|
||||
before(function () {
|
||||
const expectedAlertText = 'Delete data view';
|
||||
|
|
|
@ -17,7 +17,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
const createDataView = async (dataViewName: string) => {
|
||||
await testSubjects.setValue('createIndexPatternNameInput', dataViewName, {
|
||||
await testSubjects.setValue('createIndexPatternTitleInput', dataViewName, {
|
||||
clearWithKeyboard: true,
|
||||
typeCharByChar: true,
|
||||
});
|
||||
|
|
|
@ -140,7 +140,7 @@ export class SettingsPageObject extends FtrService {
|
|||
}
|
||||
|
||||
async getIndexPatternField() {
|
||||
return this.testSubjects.find('createIndexPatternNameInput');
|
||||
return this.testSubjects.find('createIndexPatternTitleInput');
|
||||
}
|
||||
|
||||
async getTimeFieldNameField() {
|
||||
|
@ -160,6 +160,16 @@ export class SettingsPageObject extends FtrService {
|
|||
return await this.find.displayedByCssSelector('option[value="' + selection + '"]');
|
||||
}
|
||||
|
||||
async getNameField() {
|
||||
return this.testSubjects.find('createIndexPatternNameInput');
|
||||
}
|
||||
|
||||
async setNameField(dataViewName: string) {
|
||||
const field = await this.getNameField();
|
||||
await field.clearValue();
|
||||
await field.type(dataViewName);
|
||||
}
|
||||
|
||||
async getSaveIndexPatternButton() {
|
||||
return await this.testSubjects.find('saveIndexPatternButton');
|
||||
}
|
||||
|
@ -186,6 +196,10 @@ export class SettingsPageObject extends FtrService {
|
|||
await this.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
async clickEditIndexButton() {
|
||||
await this.testSubjects.click('editIndexPatternButton');
|
||||
}
|
||||
|
||||
async clickDeletePattern() {
|
||||
await this.testSubjects.click('deleteIndexPatternButton');
|
||||
}
|
||||
|
@ -411,7 +425,8 @@ export class SettingsPageObject extends FtrService {
|
|||
indexPatternName: string,
|
||||
// null to bypass default value
|
||||
timefield: string | null = '@timestamp',
|
||||
isStandardIndexPattern = true
|
||||
isStandardIndexPattern = true,
|
||||
dataViewName?: string
|
||||
) {
|
||||
await this.retry.try(async () => {
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
|
@ -442,6 +457,9 @@ export class SettingsPageObject extends FtrService {
|
|||
if (timefield) {
|
||||
await this.selectTimeFieldOption(timefield);
|
||||
}
|
||||
if (dataViewName) {
|
||||
await this.setNameField(dataViewName);
|
||||
}
|
||||
await (await this.getSaveIndexPatternButton()).click();
|
||||
});
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
|
@ -457,13 +475,53 @@ export class SettingsPageObject extends FtrService {
|
|||
|
||||
if (!isStandardIndexPattern) {
|
||||
const badges = await this.find.allByCssSelector('.euiBadge__text');
|
||||
const text = await badges[1].getVisibleText();
|
||||
const text = await badges[0].getVisibleText();
|
||||
expect(text).to.equal('Rollup');
|
||||
}
|
||||
|
||||
return await this.getIndexPatternIdFromUrl();
|
||||
}
|
||||
|
||||
async editIndexPattern(
|
||||
indexPatternName: string,
|
||||
// null to bypass default value
|
||||
timefield: string | null = '@timestamp',
|
||||
dataViewName?: string,
|
||||
errorCheck?: boolean
|
||||
) {
|
||||
if (!indexPatternName) {
|
||||
throw new Error('No Data View name provided for edit');
|
||||
}
|
||||
|
||||
this.clickEditIndexButton();
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
|
||||
await this.retry.try(async () => {
|
||||
await this.setIndexPatternField(indexPatternName);
|
||||
});
|
||||
if (dataViewName) {
|
||||
await this.setNameField(dataViewName);
|
||||
}
|
||||
if (timefield) {
|
||||
await this.selectTimeFieldOption(timefield);
|
||||
}
|
||||
await (await this.getSaveIndexPatternButton()).click();
|
||||
|
||||
if (errorCheck) {
|
||||
await this.retry.try(async () => {
|
||||
this.log.debug('getAlertText');
|
||||
await this.testSubjects.getVisibleText('confirmModalTitleText');
|
||||
});
|
||||
await this.retry.try(async () => {
|
||||
this.log.debug('acceptConfirmation');
|
||||
await this.testSubjects.click('confirmModalConfirmButton');
|
||||
});
|
||||
}
|
||||
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
return await this.getIndexPatternIdFromUrl();
|
||||
}
|
||||
|
||||
async clickAddNewIndexPatternButton() {
|
||||
await this.common.scrollKibanaBodyTop();
|
||||
|
||||
|
|
|
@ -51,12 +51,14 @@ export interface SavedObjectMetaData<T = unknown> {
|
|||
|
||||
interface FinderAttributes {
|
||||
title?: string;
|
||||
name?: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface SavedObjectFinderState {
|
||||
items: Array<{
|
||||
title: string | null;
|
||||
name: string | null;
|
||||
id: SimpleSavedObject['id'];
|
||||
type: SimpleSavedObject['type'];
|
||||
savedObject: SimpleSavedObject<FinderAttributes>;
|
||||
|
@ -126,7 +128,7 @@ export class SavedObjectFinderUi extends React.Component<
|
|||
|
||||
const fields = Object.values(metaDataMap)
|
||||
.map((metaData) => metaData.includeFields || [])
|
||||
.reduce((allFields, currentFields) => allFields.concat(currentFields), ['title']);
|
||||
.reduce((allFields, currentFields) => allFields.concat(currentFields), ['title', 'name']);
|
||||
|
||||
const perPage = this.props.uiSettings.get(LISTING_LIMIT_SETTING);
|
||||
const resp = await this.props.savedObjects.client.find<FinderAttributes>({
|
||||
|
@ -160,13 +162,14 @@ export class SavedObjectFinderUi extends React.Component<
|
|||
page: 0,
|
||||
items: resp.savedObjects.map((savedObject) => {
|
||||
const {
|
||||
attributes: { title },
|
||||
attributes: { name, title },
|
||||
id,
|
||||
type,
|
||||
} = savedObject;
|
||||
|
||||
const titleToUse = typeof title === 'string' ? title : '';
|
||||
return {
|
||||
title: typeof title === 'string' ? title : '',
|
||||
title: titleToUse,
|
||||
name: typeof name === 'string' ? name : titleToUse,
|
||||
id,
|
||||
type,
|
||||
savedObject,
|
||||
|
@ -487,7 +490,7 @@ export class SavedObjectFinderUi extends React.Component<
|
|||
|
||||
const fullName = currentSavedObjectMetaData.getTooltipForSavedObject
|
||||
? currentSavedObjectMetaData.getTooltipForSavedObject(item.savedObject)
|
||||
: `${item.title} (${currentSavedObjectMetaData.name})`;
|
||||
: `${item.name} (${currentSavedObjectMetaData.name})`;
|
||||
|
||||
const iconType = (
|
||||
currentSavedObjectMetaData ||
|
||||
|
@ -500,7 +503,7 @@ export class SavedObjectFinderUi extends React.Component<
|
|||
<EuiListGroupItem
|
||||
key={item.id}
|
||||
iconType={iconType}
|
||||
label={item.title}
|
||||
label={item.name}
|
||||
onClick={
|
||||
onChoose
|
||||
? () => {
|
||||
|
|
|
@ -696,7 +696,7 @@ export const LensTopNavMenu = ({
|
|||
|
||||
const dataViewPickerProps = {
|
||||
trigger: {
|
||||
label: currentIndexPattern?.title || '',
|
||||
label: currentIndexPattern?.getName?.() || '',
|
||||
'data-test-subj': 'lns-dataView-switch-link',
|
||||
title: currentIndexPattern?.title || '',
|
||||
},
|
||||
|
|
|
@ -25,13 +25,12 @@ export function LayerPanel({ state, layerId, onChangeIndexPattern }: IndexPatter
|
|||
const notFoundTitleLabel = i18n.translate('xpack.lens.layerPanel.missingDataView', {
|
||||
defaultMessage: 'Data view not found',
|
||||
});
|
||||
|
||||
return (
|
||||
<I18nProvider>
|
||||
<ChangeIndexPattern
|
||||
data-test-subj="indexPattern-switcher"
|
||||
trigger={{
|
||||
label: indexPattern?.title || notFoundTitleLabel,
|
||||
label: indexPattern?.name || notFoundTitleLabel,
|
||||
title: indexPattern?.title || notFoundTitleLabel,
|
||||
'data-test-subj': 'lns_layerIndexPatternLabel',
|
||||
size: 's',
|
||||
|
|
|
@ -65,7 +65,7 @@ export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPa
|
|||
})
|
||||
.concat(documentField);
|
||||
|
||||
const { typeMeta, title, timeFieldName, fieldFormatMap } = dataView;
|
||||
const { typeMeta, title, name, timeFieldName, fieldFormatMap } = dataView;
|
||||
if (typeMeta?.aggs) {
|
||||
const aggs = Object.keys(typeMeta.aggs);
|
||||
newFields.forEach((field, index) => {
|
||||
|
@ -85,6 +85,7 @@ export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPa
|
|||
return {
|
||||
id: dataView.id!, // id exists for sure because we got index patterns by id
|
||||
title,
|
||||
name: name ? name : title,
|
||||
timeFieldName,
|
||||
fieldFormatMap:
|
||||
fieldFormatMap &&
|
||||
|
|
|
@ -53,6 +53,7 @@ export interface IndexPattern {
|
|||
fields: IndexPatternField[];
|
||||
getFieldByName(name: string): IndexPatternField | undefined;
|
||||
title: string;
|
||||
name?: string;
|
||||
timeFieldName?: string;
|
||||
fieldFormatMap?: Record<
|
||||
string,
|
||||
|
@ -106,4 +107,5 @@ export interface IndexPatternPrivateState {
|
|||
export interface IndexPatternRef {
|
||||
id: string;
|
||||
title: string;
|
||||
name?: string;
|
||||
}
|
||||
|
|
|
@ -4483,7 +4483,7 @@
|
|||
"indexPatternEditor.rollupDataView.warning.textParagraphTwo": "Vous pouvez mettre une vue de données de cumul en correspondance avec un index de cumul et zéro index régulier ou plus. Une vue de données de cumul dispose d'indicateurs, de champs, d'intervalles et d'agrégations limités. Un index de cumul se limite aux index disposant d'une configuration de tâche ou de plusieurs tâches avec des configurations compatibles.",
|
||||
"indexPatternEditor.rollupIndexPattern.warning.title": "Fonctionnalité bêta",
|
||||
"indexPatternEditor.rollupLabel": "Cumul",
|
||||
"indexPatternEditor.saved": "\"{indexPatternTitle}\" enregistré",
|
||||
"indexPatternEditor.saved": "\"{indexPatternName}\" enregistré",
|
||||
"indexPatternEditor.status.matchAnyLabel.matchAnyDetail": "Votre modèle d'indexation peut correspondre à {sourceCount, plural, one {# source} other {# sources} }.",
|
||||
"indexPatternEditor.status.noSystemIndicesLabel": "Aucun flux de données, index ni alias d'index ne correspond à votre modèle d'indexation.",
|
||||
"indexPatternEditor.status.noSystemIndicesWithPromptLabel": "Aucun flux de données, index ni alias d'index ne correspond à votre modèle d'indexation.",
|
||||
|
@ -4778,9 +4778,6 @@
|
|||
"indexPatternManagement.editIndexPattern.tabs.fieldsHeader": "Champs",
|
||||
"indexPatternManagement.editIndexPattern.tabs.scriptedHeader": "Champs scriptés",
|
||||
"indexPatternManagement.editIndexPattern.tabs.sourceHeader": "Filtres de champ",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterHeader": "Champ temporel : \"{timeFieldName}\"",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterLabel.mappingAPILink": "mappings de champ",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterLabel.timeFilterDetail": "Affichez et modifiez les champs dans {indexPatternTitle}. Les attributs de champ tels que le type et le niveau de recherche sont basés sur {mappingAPILink} dans Elasticsearch.",
|
||||
"indexPatternManagement.fieldTypeConflict": "Conflit de type de champ",
|
||||
"indexPatternManagement.formatHeader": "Format",
|
||||
"indexPatternManagement.formatLabel": "Le formatage détermine la façon dont les valeurs sont affichées. La modification de ce paramètre peut également affecter la valeur du champ et la mise en surbrillance dans Discover.",
|
||||
|
|
|
@ -4576,7 +4576,7 @@
|
|||
"indexPatternEditor.rollupDataView.warning.textParagraphTwo": "ロールアップデータビューは、1つのロールアップインデックスとゼロ以上の標準インデックスと一致させることができます。ロールアップデータビューでは、メトリック、フィールド、間隔、アグリゲーションが制限されています。ロールアップインデックスは、1つのジョブ構成があるインデックス、または複数のジョブと互換する構成があるインデックスに制限されています。",
|
||||
"indexPatternEditor.rollupIndexPattern.warning.title": "ベータ機能",
|
||||
"indexPatternEditor.rollupLabel": "ロールアップ",
|
||||
"indexPatternEditor.saved": "Saved '{indexPatternTitle}'",
|
||||
"indexPatternEditor.saved": "Saved '{indexPatternName}'",
|
||||
"indexPatternEditor.status.matchAnyLabel.matchAnyDetail": "インデックスパターンは、{sourceCount, plural, other {# 個のソース} }と一致します。",
|
||||
"indexPatternEditor.status.noSystemIndicesLabel": "データストリーム、インデックス、またはインデックスエイリアスがインデックスパターンと一致しません。",
|
||||
"indexPatternEditor.status.noSystemIndicesWithPromptLabel": "データストリーム、インデックス、またはインデックスエイリアスがインデックスパターンと一致しません。",
|
||||
|
@ -4872,9 +4872,6 @@
|
|||
"indexPatternManagement.editIndexPattern.tabs.fieldsHeader": "フィールド",
|
||||
"indexPatternManagement.editIndexPattern.tabs.scriptedHeader": "スクリプトフィールド",
|
||||
"indexPatternManagement.editIndexPattern.tabs.sourceHeader": "フィールドフィルター",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterHeader": "時刻フィールド:「{timeFieldName}」",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterLabel.mappingAPILink": "フィールドマッピング",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterLabel.timeFilterDetail": "{indexPatternTitle}でフィールドを表示して編集します。型や検索可否などのフィールド属性はElasticsearchで{mappingAPILink}に基づきます。",
|
||||
"indexPatternManagement.fieldTypeConflict": "フィールドタイプの矛盾",
|
||||
"indexPatternManagement.formatHeader": "フォーマット",
|
||||
"indexPatternManagement.formatLabel": "書式設定は値の表示方法を制御します。この設定を変更すると、Discoverでのフィールド値とハイライトにも影響する場合があります。",
|
||||
|
|
|
@ -4587,7 +4587,7 @@
|
|||
"indexPatternEditor.rollupDataView.warning.textParagraphTwo": "可以根据一个汇总/打包索引和零个或更多常规索引匹配汇总/打包数据视图。汇总/打包数据视图的指标、字段、时间间隔和聚合有限。汇总/打包索引仅限于具有一个作业配置或多个作业配置兼容的索引。",
|
||||
"indexPatternEditor.rollupIndexPattern.warning.title": "公测版功能",
|
||||
"indexPatternEditor.rollupLabel": "汇总/打包",
|
||||
"indexPatternEditor.saved": "已保存“{indexPatternTitle}”",
|
||||
"indexPatternEditor.saved": "已保存“{indexPatternName}”",
|
||||
"indexPatternEditor.status.matchAnyLabel.matchAnyDetail": "您的索引模式可以匹配{sourceCount, plural, other {# 个源} }。",
|
||||
"indexPatternEditor.status.noSystemIndicesLabel": "没有数据流、索引或索引别名匹配您的索引模式。",
|
||||
"indexPatternEditor.status.noSystemIndicesWithPromptLabel": "没有数据流、索引或索引别名匹配您的索引模式。",
|
||||
|
@ -4883,9 +4883,6 @@
|
|||
"indexPatternManagement.editIndexPattern.tabs.fieldsHeader": "字段",
|
||||
"indexPatternManagement.editIndexPattern.tabs.scriptedHeader": "脚本字段",
|
||||
"indexPatternManagement.editIndexPattern.tabs.sourceHeader": "字段筛选",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterHeader": "时间字段:“{timeFieldName}”",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterLabel.mappingAPILink": "字段映射",
|
||||
"indexPatternManagement.editIndexPattern.timeFilterLabel.timeFilterDetail": "查看和编辑 {indexPatternTitle} 中的字段。字段属性,如类型和可搜索性,基于 Elasticsearch 中的 {mappingAPILink}。",
|
||||
"indexPatternManagement.fieldTypeConflict": "字段类型冲突",
|
||||
"indexPatternManagement.formatHeader": "格式",
|
||||
"indexPatternManagement.formatLabel": "格式设置控制值的显示方式。更改此设置还可能影响字段值和字段在 Discover 中的突出显示。",
|
||||
|
|
|
@ -109,7 +109,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await browser.refresh();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await appMenu.clickLink('Discover');
|
||||
await PageObjects.discover.selectIndexPattern('kibana_sample_data_flights');
|
||||
await PageObjects.discover.selectIndexPattern('Kibana Sample Data Flights');
|
||||
await PageObjects.timePicker.setCommonlyUsedTime('sample_data range');
|
||||
await retry.try(async function () {
|
||||
const hitCount = parseInt(await PageObjects.discover.getHitCount(), 10);
|
||||
|
|
|
@ -16,7 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const PageObjects = getPageObjects(['common', 'lens', 'header', 'timePicker']);
|
||||
|
||||
const createDataView = async (dataViewName: string) => {
|
||||
await testSubjects.setValue('createIndexPatternNameInput', dataViewName, {
|
||||
await testSubjects.setValue('createIndexPatternTitleInput', dataViewName, {
|
||||
clearWithKeyboard: true,
|
||||
typeCharByChar: true,
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue