[ML] Moving file data vizualizer to its own plugin (#96408)

* [ML] Moving file data vizualizer to file upload plugin

* removing maps plug dependency

* fixing imports

* small refactor

* adding missing endpoints

* fixing translations

* fxing table controls

* fixing types and disabling geo point test

* actually disabling geo point test

* making endpoints internal

* moving UI code to separate plugin

* enabling maps integration

* cleaning up dependencies

* fixing translation ids

* moving analyze file endpoint out of file upload plugin

* fixing transtations issues

* refactor for lazy loading of component

* updating limits

* updating plugin asciidoc

* code clean up

* further clean up

* adding comment

* fixing really obvious CI error

* removing commented out include

* reenabling geo point test

* fixing incorrectly changed import

* removing ml from labels and identifiers

* renaming function

* moving analyse file endpoint to file upload plugin

* reverting import path changes

* adding esUiShared back in

* fixing navigation tabs alignment in basic license

* adding key to tab wrapper

* reverting test label

* further removal of ml references

* removing ml label from more identifiers

* fixing tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
James Gowdy 2021-04-20 22:17:22 +01:00 committed by GitHub
parent f1d167de86
commit 088a618f92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
219 changed files with 5249 additions and 969 deletions

View file

@ -392,6 +392,10 @@ actitivies.
|The features plugin enhance Kibana with a per-feature privilege system.
|{kib-repo}blob/{branch}/x-pack/plugins/file_data_visualizer[fileDataVisualizer]
|WARNING: Missing README.
|{kib-repo}blob/{branch}/x-pack/plugins/file_upload[fileUpload]
|WARNING: Missing README.

View file

@ -106,6 +106,7 @@ pageLoadAssetSize:
indexPatternFieldEditor: 90489
osquery: 107090
fileUpload: 25664
fileDataVisualizer: 27530
banners: 17946
mapsEms: 26072
timelines: 28613

View file

@ -20,6 +20,7 @@
"xpack.endpoint": "plugins/endpoint",
"xpack.enterpriseSearch": "plugins/enterprise_search",
"xpack.features": "plugins/features",
"xpack.fileDataVisualizer": "plugins/file_data_visualizer",
"xpack.fileUpload": "plugins/file_upload",
"xpack.globalSearch": ["plugins/global_search"],
"xpack.globalSearchBar": ["plugins/global_search_bar"],

View file

@ -0,0 +1,31 @@
/*
* 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.
*/
export const UI_SETTING_MAX_FILE_SIZE = 'fileUpload:maxFileSize';
export const MB = Math.pow(2, 20);
export const MAX_FILE_SIZE = '100MB';
export const MAX_FILE_SIZE_BYTES = 104857600; // 100MB
export const ABSOLUTE_MAX_FILE_SIZE_BYTES = 1073741274; // 1GB
export const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b';
// Value to use in the Elasticsearch index mapping meta data to identify the
// index as having been created by the File Data Visualizer.
export const INDEX_META_DATA_CREATED_BY = 'file-data-visualizer';
export const JOB_FIELD_TYPES = {
BOOLEAN: 'boolean',
DATE: 'date',
GEO_POINT: 'geo_point',
GEO_SHAPE: 'geo_shape',
IP: 'ip',
KEYWORD: 'keyword',
NUMBER: 'number',
TEXT: 'text',
UNKNOWN: 'unknown',
} as const;

View file

@ -5,4 +5,5 @@
* 2.0.
*/
export { createUrlOverrides, processResults, readFile, DEFAULT_LINES_TO_SAMPLE } from './utils';
export * from './constants';
export * from './types';

View file

@ -0,0 +1,22 @@
/*
* 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 { JOB_FIELD_TYPES } from './constants';
export type InputData = any[];
export type JobFieldType = typeof JOB_FIELD_TYPES[keyof typeof JOB_FIELD_TYPES];
export interface DataVisualizerTableState {
pageSize: number;
pageIndex: number;
sortField: string;
sortDirection: string;
visibleFieldTypes: string[];
visibleFieldNames: string[];
showDistributions: boolean;
}

View file

@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/x-pack/plugins/file_data_visualizer'],
};

View file

@ -0,0 +1,27 @@
{
"id": "fileDataVisualizer",
"version": "8.0.0",
"kibanaVersion": "kibana",
"server": true,
"ui": true,
"requiredPlugins": [
"data",
"usageCollection",
"embeddable",
"share",
"discover",
"fileUpload"
],
"optionalPlugins": [
"security",
"maps"
],
"requiredBundles": [
"kibanaReact",
"maps",
"esUiShared"
],
"extraPublicDirs": [
"common"
]
}

View file

@ -0,0 +1,14 @@
/*
* 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 { lazyLoadModules } from '../lazy_load_bundle';
import { FileDataVisualizer } from '../application';
export async function getFileDataVisualizerComponent(): Promise<typeof FileDataVisualizer> {
const modules = await lazyLoadModules();
return modules.FileDataVisualizer;
}

View file

@ -43,7 +43,7 @@ export const AboutPanel: FC<Props> = ({ onFilePickerChange }) => {
<EuiFilePicker
id="filePicker"
initialPromptText={i18n.translate(
'xpack.ml.fileDatavisualizer.aboutPanel.selectOrDragAndDropFileDescription',
'xpack.fileDataVisualizer.aboutPanel.selectOrDragAndDropFileDescription',
{
defaultMessage: 'Select or drag and drop a file',
}
@ -70,7 +70,7 @@ export const LoadingPanel: FC = () => {
<EuiTitle size="s">
<h1 role="alert">
<FormattedMessage
id="xpack.ml.fileDatavisualizer.aboutPanel.analyzingDataTitle"
id="xpack.fileDataVisualizer.aboutPanel.analyzingDataTitle"
defaultMessage="Analyzing data"
/>
</h1>

View file

@ -21,26 +21,22 @@ import {
import { ExperimentalBadge } from '../experimental_badge';
import { useMlKibana } from '../../../../contexts/kibana';
import { useFileDataVisualizerKibana } from '../../kibana_context';
export const WelcomeContent: FC = () => {
const toolTipContent = i18n.translate(
'xpack.ml.fileDatavisualizer.welcomeContent.experimentalFeatureTooltip',
'xpack.fileDataVisualizer.welcomeContent.experimentalFeatureTooltip',
{
defaultMessage: "Experimental feature. We'd love to hear your feedback.",
}
);
const {
services: { fileUpload },
} = useMlKibana();
if (fileUpload === undefined) {
// eslint-disable-next-line no-console
console.error('File upload plugin not available');
return null;
}
const maxFileSize = fileUpload.getMaxBytesFormatted();
services: {
fileUpload: { getMaxBytesFormatted },
},
} = useFileDataVisualizerKibana();
const maxFileSize = getMaxBytesFormatted();
return (
<EuiFlexGroup gutterSize="xl" alignItems="center">
@ -51,7 +47,7 @@ export const WelcomeContent: FC = () => {
<EuiTitle size="m">
<h1>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.welcomeContent.visualizeDataFromLogFileTitle"
id="xpack.fileDataVisualizer.welcomeContent.visualizeDataFromLogFileTitle"
defaultMessage="Visualize data from a log file&nbsp;{experimentalBadge}"
values={{
experimentalBadge: <ExperimentalBadge tooltipContent={toolTipContent} />,
@ -63,7 +59,7 @@ export const WelcomeContent: FC = () => {
<EuiText>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.welcomeContent.visualizeDataFromLogFileDescription"
id="xpack.fileDataVisualizer.welcomeContent.visualizeDataFromLogFileDescription"
defaultMessage="The File Data Visualizer helps you understand the fields and metrics in a log file.
Upload your file, analyze its data, and then choose whether to import the data into an Elasticsearch index."
/>
@ -73,7 +69,7 @@ export const WelcomeContent: FC = () => {
<EuiText>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.welcomeContent.supportedFileFormatDescription"
id="xpack.fileDataVisualizer.welcomeContent.supportedFileFormatDescription"
defaultMessage="The File Data Visualizer supports these file formats:"
/>
</p>
@ -87,7 +83,7 @@ export const WelcomeContent: FC = () => {
<EuiText>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.welcomeContent.delimitedTextFilesDescription"
id="xpack.fileDataVisualizer.welcomeContent.delimitedTextFilesDescription"
defaultMessage="Delimited text files, such as CSV and TSV"
/>
</p>
@ -103,7 +99,7 @@ export const WelcomeContent: FC = () => {
<EuiText>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.welcomeContent.newlineDelimitedJsonDescription"
id="xpack.fileDataVisualizer.welcomeContent.newlineDelimitedJsonDescription"
defaultMessage="Newline-delimited JSON"
/>
</p>
@ -119,7 +115,7 @@ export const WelcomeContent: FC = () => {
<EuiText>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.welcomeContent.logFilesWithCommonFormatDescription"
id="xpack.fileDataVisualizer.welcomeContent.logFilesWithCommonFormatDescription"
defaultMessage="Log files with a common format for the timestamp"
/>
</p>
@ -130,7 +126,7 @@ export const WelcomeContent: FC = () => {
<EuiText>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.welcomeContent.uploadedFilesAllowedSizeDescription"
id="xpack.fileDataVisualizer.welcomeContent.uploadedFilesAllowedSizeDescription"
defaultMessage="You can upload files up to {maxFileSize}."
values={{ maxFileSize }}
/>
@ -140,7 +136,7 @@ export const WelcomeContent: FC = () => {
<EuiText>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.welcomeContent.experimentalFeatureDescription"
id="xpack.fileDataVisualizer.welcomeContent.experimentalFeatureDescription"
defaultMessage="This feature is experimental. Got feedback? Please create an issue in&nbsp;{githubLink}."
values={{
githubLink: (

View file

@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import React, { FC } from 'react';
import { EuiTitle, EuiSpacer, EuiDescriptionList } from '@elastic/eui';
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { FindFileStructureResponse } from '../../../../../file_upload/common';
export const AnalysisSummary: FC<{ results: FindFileStructureResponse }> = ({ results }) => {
const items = createDisplayItems(results);
@ -19,7 +19,7 @@ export const AnalysisSummary: FC<{ results: FindFileStructureResponse }> = ({ re
<EuiTitle size="s">
<h2>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.analysisSummary.summaryTitle"
id="xpack.fileDataVisualizer.analysisSummary.summaryTitle"
defaultMessage="Summary"
/>
</h2>
@ -37,7 +37,7 @@ function createDisplayItems(results: FindFileStructureResponse) {
{
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.analysisSummary.analyzedLinesNumberTitle"
id="xpack.fileDataVisualizer.analysisSummary.analyzedLinesNumberTitle"
defaultMessage="Number of lines analyzed"
/>
),
@ -53,7 +53,7 @@ function createDisplayItems(results: FindFileStructureResponse) {
items.push({
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.analysisSummary.formatTitle"
id="xpack.fileDataVisualizer.analysisSummary.formatTitle"
defaultMessage="Format"
/>
),
@ -64,7 +64,7 @@ function createDisplayItems(results: FindFileStructureResponse) {
items.push({
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.analysisSummary.delimiterTitle"
id="xpack.fileDataVisualizer.analysisSummary.delimiterTitle"
defaultMessage="Delimiter"
/>
),
@ -74,7 +74,7 @@ function createDisplayItems(results: FindFileStructureResponse) {
items.push({
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.analysisSummary.hasHeaderRowTitle"
id="xpack.fileDataVisualizer.analysisSummary.hasHeaderRowTitle"
defaultMessage="Has header row"
/>
),
@ -87,7 +87,7 @@ function createDisplayItems(results: FindFileStructureResponse) {
items.push({
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.analysisSummary.grokPatternTitle"
id="xpack.fileDataVisualizer.analysisSummary.grokPatternTitle"
defaultMessage="Grok pattern"
/>
),
@ -99,7 +99,7 @@ function createDisplayItems(results: FindFileStructureResponse) {
items.push({
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.analysisSummary.timeFieldTitle"
id="xpack.fileDataVisualizer.analysisSummary.timeFieldTitle"
defaultMessage="Time field"
/>
),
@ -111,7 +111,7 @@ function createDisplayItems(results: FindFileStructureResponse) {
items.push({
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.analysisSummary.timeFormatTitle"
id="xpack.fileDataVisualizer.analysisSummary.timeFormatTitle"
defaultMessage="Time {timestampFormats, plural, zero {format} one {format} other {formats}}"
values={{
timestampFormats: results.java_timestamp_formats.length,

View file

@ -39,7 +39,7 @@ export const BottomBar: FC<BottomBarProps> = ({ mode, onChangeMode, onCancel, di
content={
disableImport ? (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.bottomBar.missingImportPrivilegesMessage"
id="xpack.fileDataVisualizer.bottomBar.missingImportPrivilegesMessage"
defaultMessage="You require the ingest_admin role to enable data importing"
/>
) : null
@ -52,7 +52,7 @@ export const BottomBar: FC<BottomBarProps> = ({ mode, onChangeMode, onCancel, di
data-test-subj="mlFileDataVisOpenImportPageButton"
>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.bottomBar.readMode.importButtonLabel"
id="xpack.fileDataVisualizer.bottomBar.readMode.importButtonLabel"
defaultMessage="Import"
/>
</EuiButton>
@ -61,7 +61,7 @@ export const BottomBar: FC<BottomBarProps> = ({ mode, onChangeMode, onCancel, di
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="ghost" onClick={() => onCancel()}>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.bottomBar.readMode.cancelButtonLabel"
id="xpack.fileDataVisualizer.bottomBar.readMode.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
@ -76,7 +76,7 @@ export const BottomBar: FC<BottomBarProps> = ({ mode, onChangeMode, onCancel, di
<EuiFlexItem grow={false}>
<EuiButton color="ghost" onClick={() => onChangeMode(DATAVISUALIZER_MODE.READ)}>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.bottomBar.backButtonLabel"
id="xpack.fileDataVisualizer.bottomBar.backButtonLabel"
defaultMessage="Back"
/>
</EuiButton>
@ -84,7 +84,7 @@ export const BottomBar: FC<BottomBarProps> = ({ mode, onChangeMode, onCancel, di
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="ghost" onClick={() => onCancel()}>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.bottomBar.cancelButtonLabel"
id="xpack.fileDataVisualizer.bottomBar.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>

View file

@ -29,7 +29,7 @@ import {
removeCombinedFieldsFromMappings,
removeCombinedFieldsFromPipeline,
} from './utils';
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { FindFileStructureResponse } from '../../../../../file_upload/common';
interface Props {
mappingsString: string;
@ -110,7 +110,7 @@ export class CombinedFieldsForm extends Component<Props, State> {
return JSON.parse(this.props.mappingsString);
} catch (error) {
throw new Error(
i18n.translate('xpack.ml.fileDatavisualizer.combinedFieldsForm.mappingsParseError', {
i18n.translate('xpack.fileDataVisualizer.combinedFieldsForm.mappingsParseError', {
defaultMessage: 'Error parsing mappings: {error}',
values: { error: error.message },
})
@ -123,7 +123,7 @@ export class CombinedFieldsForm extends Component<Props, State> {
return JSON.parse(this.props.pipelineString);
} catch (error) {
throw new Error(
i18n.translate('xpack.ml.fileDatavisualizer.combinedFieldsForm.pipelineParseError', {
i18n.translate('xpack.fileDataVisualizer.combinedFieldsForm.pipelineParseError', {
defaultMessage: 'Error parsing pipeline: {error}',
values: { error: error.message },
})
@ -149,7 +149,7 @@ export class CombinedFieldsForm extends Component<Props, State> {
};
render() {
const geoPointLabel = i18n.translate('xpack.ml.fileDatavisualizer.geoPointCombinedFieldLabel', {
const geoPointLabel = i18n.translate('xpack.fileDataVisualizer.geoPointCombinedFieldLabel', {
defaultMessage: 'Add geo point field',
});
const panels = [
@ -176,7 +176,7 @@ export class CombinedFieldsForm extends Component<Props, State> {
];
return (
<EuiFormRow
label={i18n.translate('xpack.ml.fileDatavisualizer.combinedFieldsLabel', {
label={i18n.translate('xpack.fileDataVisualizer.combinedFieldsLabel', {
defaultMessage: 'Combined fields',
})}
>
@ -192,11 +192,11 @@ export class CombinedFieldsForm extends Component<Props, State> {
iconType="trash"
color="danger"
onClick={this.removeCombinedField.bind(null, idx)}
title={i18n.translate('xpack.ml.fileDatavisualizer.removeCombinedFieldsLabel', {
title={i18n.translate('xpack.fileDataVisualizer.removeCombinedFieldsLabel', {
defaultMessage: 'Remove combined field',
})}
aria-label={i18n.translate(
'xpack.ml.fileDatavisualizer.removeCombinedFieldsLabel',
'xpack.fileDataVisualizer.removeCombinedFieldsLabel',
{
defaultMessage: 'Remove combined field',
}
@ -216,7 +216,7 @@ export class CombinedFieldsForm extends Component<Props, State> {
isDisabled={this.props.isDisabled}
>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.addCombinedFieldsLabel"
id="xpack.fileDataVisualizer.addCombinedFieldsLabel"
defaultMessage="Add combined field"
/>
</EuiButtonEmpty>

View file

@ -20,10 +20,10 @@ export function CombinedFieldsReadOnlyForm({
}) {
return combinedFields.length ? (
<EuiFormRow
label={i18n.translate('xpack.ml.fileDatavisualizer.combinedFieldsReadOnlyLabel', {
label={i18n.translate('xpack.fileDataVisualizer.combinedFieldsReadOnlyLabel', {
defaultMessage: 'Combined fields',
})}
helpText={i18n.translate('xpack.ml.fileDatavisualizer.combinedFieldsReadOnlyHelpTextLabel', {
helpText={i18n.translate('xpack.fileDataVisualizer.combinedFieldsReadOnlyHelpTextLabel', {
defaultMessage: 'Edit combined fields in advanced tab',
})}
>

View file

@ -29,7 +29,7 @@ import {
getFieldNames,
getNameCollisionMsg,
} from './utils';
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { FindFileStructureResponse } from '../../../../../file_upload/common';
interface Props {
addCombinedField: (combinedField: CombinedField) => void;
@ -119,7 +119,7 @@ export class GeoPointForm extends Component<Props, State> {
return (
<Fragment>
<EuiFormRow
label={i18n.translate('xpack.ml.fileDatavisualizer.geoPointForm.latFieldLabel', {
label={i18n.translate('xpack.fileDataVisualizer.geoPointForm.latFieldLabel', {
defaultMessage: 'Latitude field',
})}
>
@ -131,7 +131,7 @@ export class GeoPointForm extends Component<Props, State> {
</EuiFormRow>
<EuiFormRow
label={i18n.translate('xpack.ml.fileDatavisualizer.geoPointForm.lonFieldLabel', {
label={i18n.translate('xpack.fileDataVisualizer.geoPointForm.lonFieldLabel', {
defaultMessage: 'Longitude field',
})}
>
@ -143,7 +143,7 @@ export class GeoPointForm extends Component<Props, State> {
</EuiFormRow>
<EuiFormRow
label={i18n.translate('xpack.ml.fileDatavisualizer.geoPointForm.geoPointFieldLabel', {
label={i18n.translate('xpack.fileDataVisualizer.geoPointForm.geoPointFieldLabel', {
defaultMessage: 'Geo point field',
})}
isInvalid={this.state.geoPointFieldError !== ''}
@ -154,7 +154,7 @@ export class GeoPointForm extends Component<Props, State> {
onChange={this.onGeoPointFieldChange}
isInvalid={this.state.geoPointFieldError !== ''}
aria-label={i18n.translate(
'xpack.ml.fileDatavisualizer.geoPointForm.geoPointFieldAriaLabel',
'xpack.fileDataVisualizer.geoPointForm.geoPointFieldAriaLabel',
{
defaultMessage: 'Geo point field, required field',
}
@ -179,7 +179,7 @@ export class GeoPointForm extends Component<Props, State> {
onClick={this.onSubmit}
>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.geoPointForm.submitButtonLabel"
id="xpack.fileDataVisualizer.geoPointForm.submitButtonLabel"
defaultMessage="Add"
/>
</EuiButton>

View file

@ -13,7 +13,7 @@ import {
FindFileStructureResponse,
IngestPipeline,
Mappings,
} from '../../../../../../../file_upload/common';
} from '../../../../../file_upload/common';
const COMMON_LAT_NAMES = ['latitude', 'lat'];
const COMMON_LON_NAMES = ['longitude', 'long', 'lon'];
@ -127,7 +127,7 @@ export function createGeoPointCombinedField(
}
export function getNameCollisionMsg(name: string) {
return i18n.translate('xpack.ml.fileDatavisualizer.nameCollisionMsg', {
return i18n.translate('xpack.fileDataVisualizer.nameCollisionMsg', {
defaultMessage: '"{name}" already exists, please provide a unique name',
values: { name },
});

View file

@ -13,7 +13,7 @@ exports[`Overrides render overrides 1`] = `
label={
<FormattedMessage
defaultMessage="Number of lines to sample"
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.linesToSampleFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.linesToSampleFormRowLabel"
values={Object {}}
/>
}
@ -33,7 +33,7 @@ exports[`Overrides render overrides 1`] = `
label={
<FormattedMessage
defaultMessage="Data format"
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.dataFormatFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.dataFormatFormRowLabel"
values={Object {}}
/>
}
@ -94,7 +94,7 @@ exports[`Overrides render overrides 1`] = `
label={
<FormattedMessage
defaultMessage="Timestamp format"
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.timestampFormatFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.timestampFormatFormRowLabel"
values={Object {}}
/>
}
@ -335,7 +335,7 @@ exports[`Overrides render overrides 1`] = `
label={
<FormattedMessage
defaultMessage="Time field"
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.timeFieldFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.timeFieldFormRowLabel"
values={Object {}}
/>
}

View file

@ -69,7 +69,7 @@ export class EditFlyout extends Component {
<EuiTitle>
<h2>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrideSettingsTitle"
id="xpack.fileDataVisualizer.editFlyout.overrideSettingsTitle"
defaultMessage="Override settings"
/>
</h2>
@ -96,7 +96,7 @@ export class EditFlyout extends Component {
<EuiFlexItem grow={false}>
<EuiButtonEmpty iconType="cross" onClick={closeEditFlyout} flush="left">
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.closeOverrideSettingsButtonLabel"
id="xpack.fileDataVisualizer.editFlyout.closeOverrideSettingsButtonLabel"
defaultMessage="Close"
/>
</EuiButtonEmpty>
@ -108,7 +108,7 @@ export class EditFlyout extends Component {
fill
>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.applyOverrideSettingsButtonLabel"
id="xpack.fileDataVisualizer.editFlyout.applyOverrideSettingsButtonLabel"
defaultMessage="Apply"
/>
</EuiButton>

View file

@ -31,7 +31,7 @@ import {
// getCharsetOptions,
} from './options';
import { isTimestampFormatValid } from './overrides_validation';
import { withKibana } from '../../../../../../../../../src/plugins/kibana_react/public';
import { withKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { TIMESTAMP_OPTIONS, CUSTOM_DROPDOWN_OPTION } from './options/option_lists';
@ -52,7 +52,7 @@ class OverridesUI extends Component {
}
linesToSampleErrors = i18n.translate(
'xpack.ml.fileDatavisualizer.editFlyout.overrides.linesToSampleErrorMessage',
'xpack.fileDataVisualizer.editFlyout.overrides.linesToSampleErrorMessage',
{
defaultMessage: 'Value must be greater than {min} and less than or equal to {max}',
values: {
@ -63,7 +63,7 @@ class OverridesUI extends Component {
);
customTimestampFormatErrors = i18n.translate(
'xpack.ml.fileDatavisualizer.editFlyout.overrides.customTimestampFormatErrorMessage',
'xpack.fileDataVisualizer.editFlyout.overrides.customTimestampFormatErrorMessage',
{
defaultMessage: `Timestamp format must be a combination of these Java date/time formats:
yy, yyyy, M, MM, MMM, MMMM, d, dd, EEE, EEEE, H, HH, h, mm, ss, S through SSSSSSSSS, a, XX, XXX, zzz`,
@ -274,12 +274,9 @@ class OverridesUI extends Component {
const timestampFormatHelp = (
<EuiText size="xs">
<EuiLink href={docsUrl} target="_blank">
{i18n.translate(
'xpack.ml.fileDatavisualizer.editFlyout.overrides.timestampFormatHelpText',
{
defaultMessage: 'See more on accepted formats',
}
)}
{i18n.translate('xpack.fileDataVisualizer.editFlyout.overrides.timestampFormatHelpText', {
defaultMessage: 'See more on accepted formats',
})}
</EuiLink>
</EuiText>
);
@ -291,7 +288,7 @@ class OverridesUI extends Component {
isInvalid={linesToSampleValid === false}
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.linesToSampleFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.linesToSampleFormRowLabel"
defaultMessage="Number of lines to sample"
/>
}
@ -306,7 +303,7 @@ class OverridesUI extends Component {
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.dataFormatFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.dataFormatFormRowLabel"
defaultMessage="Data format"
/>
}
@ -324,7 +321,7 @@ class OverridesUI extends Component {
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.delimiterFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.delimiterFormRowLabel"
defaultMessage="Delimiter"
/>
}
@ -341,7 +338,7 @@ class OverridesUI extends Component {
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.customDelimiterFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.customDelimiterFormRowLabel"
defaultMessage="Custom delimiter"
/>
}
@ -353,7 +350,7 @@ class OverridesUI extends Component {
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.quoteCharacterFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.quoteCharacterFormRowLabel"
defaultMessage="Quote character"
/>
}
@ -372,7 +369,7 @@ class OverridesUI extends Component {
id={'hasHeaderRow'}
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.hasHeaderRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.hasHeaderRowLabel"
defaultMessage="Has header row"
/>
}
@ -386,7 +383,7 @@ class OverridesUI extends Component {
id={'shouldTrimFields'}
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.trimFieldsLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.trimFieldsLabel"
defaultMessage="Should trim fields"
/>
}
@ -401,7 +398,7 @@ class OverridesUI extends Component {
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.grokPatternFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.grokPatternFormRowLabel"
defaultMessage="Grok pattern"
/>
}
@ -418,7 +415,7 @@ class OverridesUI extends Component {
helpText={timestampFormatHelp}
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.timestampFormatFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.timestampFormatFormRowLabel"
defaultMessage="Timestamp format"
/>
}
@ -437,7 +434,7 @@ class OverridesUI extends Component {
isInvalid={timestampFormatValid === false}
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.customTimestampFormatFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.customTimestampFormatFormRowLabel"
defaultMessage="Custom timestamp format"
/>
}
@ -453,7 +450,7 @@ class OverridesUI extends Component {
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.timeFieldFormRowLabel"
id="xpack.fileDataVisualizer.editFlyout.overrides.timeFieldFormRowLabel"
defaultMessage="Time field"
/>
}
@ -483,7 +480,7 @@ class OverridesUI extends Component {
<EuiTitle size="s">
<h3>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.editFieldNamesTitle"
id="xpack.fileDataVisualizer.editFlyout.overrides.editFieldNamesTitle"
defaultMessage="Edit field names"
/>
</h3>

View file

@ -10,7 +10,7 @@ import React from 'react';
import { Overrides } from './overrides';
jest.mock('../../../../../../../../../src/plugins/kibana_react/public', () => ({
jest.mock('../../../../../../../src/plugins/kibana_react/public', () => ({
withKibana: (comp) => {
return comp;
},

View file

@ -41,7 +41,7 @@ export function isTimestampFormatValid(timestampFormat) {
if (timestampFormat.indexOf('?') >= 0) {
result.isValid = false;
result.errorMessage = i18n.translate(
'xpack.ml.fileDatavisualizer.editFlyout.overrides.timestampQuestionMarkValidationErrorMessage',
'xpack.fileDataVisualizer.editFlyout.overrides.timestampQuestionMarkValidationErrorMessage',
{
defaultMessage:
'Timestamp format {timestampFormat} not supported because it contains a question mark character ({fieldPlaceholder})',
@ -86,7 +86,7 @@ export function isTimestampFormatValid(timestampFormat) {
result.isValid = false;
result.errorMessage = i18n.translate(
'xpack.ml.fileDatavisualizer.editFlyout.overrides.timestampLetterValidationErrorMessage',
'xpack.fileDataVisualizer.editFlyout.overrides.timestampLetterValidationErrorMessage',
{
defaultMessage:
'Letter { length, plural, one { {lg} } other { group {lg} } } in {format} is not supported',
@ -101,9 +101,10 @@ export function isTimestampFormatValid(timestampFormat) {
if (curChar === 'S') {
// disable exceeds maximum line length error so i18n check passes
result.errorMessage = i18n.translate(
'xpack.ml.fileDatavisualizer.editFlyout.overrides.timestampLetterSValidationErrorMessage',
'xpack.fileDataVisualizer.editFlyout.overrides.timestampLetterSValidationErrorMessage',
{
defaultMessage: 'Letter { length, plural, one { {lg} } other { group {lg} } } in {format} is not supported because it is not preceded by ss and a separator from {sep}', // eslint-disable-line
defaultMessage:
'Letter { length, plural, one { {lg} } other { group {lg} } } in {format} is not supported because it is not preceded by ss and a separator from {sep}', // eslint-disable-line
values: {
length,
lg: letterGroup,
@ -127,7 +128,7 @@ export function isTimestampFormatValid(timestampFormat) {
if (prevLetterGroup == null) {
result.isValid = false;
result.errorMessage = i18n.translate(
'xpack.ml.fileDatavisualizer.editFlyout.overrides.timestampEmptyValidationErrorMessage',
'xpack.fileDataVisualizer.editFlyout.overrides.timestampEmptyValidationErrorMessage',
{
defaultMessage: 'No time format letter groups in timestamp format {timestampFormat}',
values: {

View file

@ -0,0 +1,8 @@
.embeddedMapContent {
width: 100%;
height: 100%;
display: flex;
flex: 1 1 100%;
z-index: 1;
min-height: 0; // Absolute must for Firefox to scroll contents
}

View file

@ -0,0 +1 @@
@import 'embedded_map';

View file

@ -0,0 +1,155 @@
/*
* 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, { useEffect, useRef, useState } from 'react';
import { htmlIdGenerator } from '@elastic/eui';
import { LayerDescriptor } from '../../../../../maps/common/descriptor_types';
import { INITIAL_LOCATION } from '../../../../../maps/common/constants';
import {
MapEmbeddable,
MapEmbeddableInput,
MapEmbeddableOutput,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../maps/public/embeddable';
import { MAP_SAVED_OBJECT_TYPE, RenderTooltipContentParams } from '../../../../../maps/public';
import {
EmbeddableFactory,
ErrorEmbeddable,
isErrorEmbeddable,
ViewMode,
} from '../../../../../../../src/plugins/embeddable/public';
import { useFileDataVisualizerKibana } from '../../kibana_context';
export function EmbeddedMapComponent({
layerList,
mapEmbeddableInput,
renderTooltipContent,
}: {
layerList: LayerDescriptor[];
mapEmbeddableInput?: MapEmbeddableInput;
renderTooltipContent?: (params: RenderTooltipContentParams) => JSX.Element;
}) {
const [embeddable, setEmbeddable] = useState<ErrorEmbeddable | MapEmbeddable | undefined>();
const embeddableRoot: React.RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);
const baseLayers = useRef<LayerDescriptor[]>();
const {
services: { embeddable: embeddablePlugin, maps: mapsPlugin },
} = useFileDataVisualizerKibana();
const factory:
| EmbeddableFactory<MapEmbeddableInput, MapEmbeddableOutput, MapEmbeddable>
| undefined = embeddablePlugin
? embeddablePlugin.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE)
: undefined;
// Update the layer list with updated geo points upon refresh
useEffect(() => {
async function updateIndexPatternSearchLayer() {
if (
embeddable &&
!isErrorEmbeddable(embeddable) &&
Array.isArray(layerList) &&
Array.isArray(baseLayers.current)
) {
embeddable.setLayerList([...baseLayers.current, ...layerList]);
}
}
updateIndexPatternSearchLayer();
}, [embeddable, layerList]);
useEffect(() => {
async function setupEmbeddable() {
if (!factory) {
// eslint-disable-next-line no-console
console.error('Map embeddable not found.');
return;
}
const input: MapEmbeddableInput = {
id: htmlIdGenerator()(),
attributes: { title: '' },
filters: [],
hidePanelTitles: true,
refreshConfig: {
value: 0,
pause: false,
},
viewMode: ViewMode.VIEW,
isLayerTOCOpen: false,
hideFilterActions: true,
// can use mapSettings to center map on anomalies
mapSettings: {
disableInteractive: false,
hideToolbarOverlay: false,
hideLayerControl: false,
hideViewControl: false,
initialLocation: INITIAL_LOCATION.AUTO_FIT_TO_BOUNDS, // this will startup based on data-extent
autoFitToDataBounds: true, // this will auto-fit when there are changes to the filter and/or query
},
};
const embeddableObject = await factory.create(input);
if (embeddableObject && !isErrorEmbeddable(embeddableObject)) {
const basemapLayerDescriptor = mapsPlugin
? await mapsPlugin.createLayerDescriptors.createBasemapLayerDescriptor()
: null;
if (basemapLayerDescriptor) {
baseLayers.current = [basemapLayerDescriptor];
await embeddableObject.setLayerList(baseLayers.current);
}
}
setEmbeddable(embeddableObject);
}
setupEmbeddable();
// we want this effect to execute exactly once after the component mounts
// eslint-disable-next-line
}, []);
useEffect(() => {
if (embeddable && !isErrorEmbeddable(embeddable) && mapEmbeddableInput !== undefined) {
embeddable.updateInput(mapEmbeddableInput);
}
}, [embeddable, mapEmbeddableInput]);
useEffect(() => {
if (embeddable && !isErrorEmbeddable(embeddable) && renderTooltipContent !== undefined) {
embeddable.setRenderTooltipContent(renderTooltipContent);
}
}, [embeddable, renderTooltipContent]);
// We can only render after embeddable has already initialized
useEffect(() => {
if (embeddableRoot.current && embeddable) {
embeddable.render(embeddableRoot.current);
}
}, [embeddable, embeddableRoot]);
if (!embeddablePlugin) {
// eslint-disable-next-line no-console
console.error('Embeddable start plugin not found');
return null;
}
if (!mapsPlugin) {
// eslint-disable-next-line no-console
console.error('Maps start plugin not found');
return null;
}
return (
<div
data-test-subj="mlEmbeddedMapContent"
className="embeddedMapContent"
ref={embeddableRoot}
/>
);
}

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { EmbeddedMapComponent } from './embedded_map';

View file

@ -0,0 +1,59 @@
/*
* 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 { EuiListGroup, EuiListGroupItem } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { ExpandedRowFieldHeader } from '../stats_table/components/expanded_row_field_header';
interface Props {
examples: Array<string | object>;
}
export const ExamplesList: FC<Props> = ({ examples }) => {
if (examples === undefined || examples === null || !Array.isArray(examples)) {
return null;
}
let examplesContent;
if (examples.length === 0) {
examplesContent = (
<FormattedMessage
id="xpack.fileDataVisualizer.fieldDataCard.examplesList.noExamplesMessage"
defaultMessage="No examples were obtained for this field"
/>
);
} else {
examplesContent = examples.map((example, i) => {
return (
<EuiListGroupItem
className="fieldDataCard__codeContent"
size="s"
key={`example_${i}`}
label={typeof example === 'string' ? example : JSON.stringify(example)}
/>
);
});
}
return (
<div data-test-subj="mlFieldDataExamplesList">
<ExpandedRowFieldHeader>
<FormattedMessage
id="xpack.fileDataVisualizer.fieldDataCard.examplesList.title"
defaultMessage="{numExamples, plural, one {Value} other {Examples}}"
values={{
numExamples: examples.length,
}}
/>
</ExpandedRowFieldHeader>
<EuiListGroup showToolTips={true} maxWidth={'s'} gutterSize={'none'} flush={true}>
{examplesContent}
</EuiListGroup>
</div>
);
};

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { ExamplesList } from './examples_list';

View file

@ -14,10 +14,10 @@ import {
OtherContent,
TextContent,
NumberContent,
} from '../../../stats_table/components/field_data_expanded_row';
} from '../stats_table/components/field_data_expanded_row';
import { GeoPointContent } from './geo_point_content/geo_point_content';
import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types';
import type { FileBasedFieldVisConfig } from '../../../stats_table/types/field_vis_config';
import { JOB_FIELD_TYPES } from '../../../../common';
import type { FileBasedFieldVisConfig } from '../stats_table/types/field_vis_config';
export const FileBasedDataVisualizerExpandedRow = ({ item }: { item: FileBasedFieldVisConfig }) => {
const config = item;
@ -25,25 +25,25 @@ export const FileBasedDataVisualizerExpandedRow = ({ item }: { item: FileBasedFi
function getCardContent() {
switch (type) {
case ML_JOB_FIELD_TYPES.NUMBER:
case JOB_FIELD_TYPES.NUMBER:
return <NumberContent config={config} />;
case ML_JOB_FIELD_TYPES.BOOLEAN:
case JOB_FIELD_TYPES.BOOLEAN:
return <BooleanContent config={config} />;
case ML_JOB_FIELD_TYPES.DATE:
case JOB_FIELD_TYPES.DATE:
return <DateContent config={config} />;
case ML_JOB_FIELD_TYPES.GEO_POINT:
case JOB_FIELD_TYPES.GEO_POINT:
return <GeoPointContent config={config} />;
case ML_JOB_FIELD_TYPES.IP:
case JOB_FIELD_TYPES.IP:
return <IpContent config={config} />;
case ML_JOB_FIELD_TYPES.KEYWORD:
case JOB_FIELD_TYPES.KEYWORD:
return <KeywordContent config={config} />;
case ML_JOB_FIELD_TYPES.TEXT:
case JOB_FIELD_TYPES.TEXT:
return <TextContent config={config} />;
default:
@ -53,7 +53,7 @@ export const FileBasedDataVisualizerExpandedRow = ({ item }: { item: FileBasedFi
return (
<div
className="mlDataVisualizerFieldExpandedRow"
className="dataVisualizerFieldExpandedRow"
data-test-subj={`mlDataVisualizerFieldExpandedRow-${fieldName}`}
>
{getCardContent()}

View file

@ -8,7 +8,7 @@
import { Feature, Point } from 'geojson';
import { euiPaletteColorBlind } from '@elastic/eui';
import { DEFAULT_GEO_REGEX } from './geo_point_content';
import { SOURCE_TYPES } from '../../../../../../../../maps/common/constants';
import { SOURCE_TYPES } from '../../../../../../maps/common/constants';
export const convertWKTGeoToLonLat = (
value: string | number

View file

@ -9,12 +9,12 @@ import React, { FC, useMemo } from 'react';
import { EuiFlexItem } from '@elastic/eui';
import { Feature, Point } from 'geojson';
import type { FieldDataRowProps } from '../../../../stats_table/types/field_data_row';
import { DocumentStatsTable } from '../../../../stats_table/components/field_data_expanded_row/document_stats';
import { MlEmbeddedMapComponent } from '../../../../../components/ml_embedded_map';
import type { FieldDataRowProps } from '../../stats_table/types/field_data_row';
import { DocumentStatsTable } from '../../stats_table/components/field_data_expanded_row/document_stats';
import { EmbeddedMapComponent } from '../../embedded_map';
import { convertWKTGeoToLonLat, getGeoPointsLayer } from './format_utils';
import { ExpandedRowContent } from '../../../../stats_table/components/field_data_expanded_row/expanded_row_content';
import { ExamplesList } from '../../../../index_based/components/field_data_row/examples_list';
import { ExpandedRowContent } from '../../stats_table/components/field_data_expanded_row/expanded_row_content';
import { ExamplesList } from '../../examples_list';
export const DEFAULT_GEO_REGEX = RegExp('(?<lat>.+) (?<lon>.+)');
@ -38,7 +38,7 @@ export const GeoPointContent: FC<FieldDataRowProps> = ({ config }) => {
geoPointsFeatures.push({
type: 'Feature',
id: `ml-${config.fieldName}-${i}`,
id: `fileDataVisualizer-${config.fieldName}-${i}`,
geometry: {
type: 'Point',
coordinates: [coordinates.lat, coordinates.lon],
@ -69,10 +69,10 @@ export const GeoPointContent: FC<FieldDataRowProps> = ({ config }) => {
)}
{formattedResults && Array.isArray(formattedResults.layerList) && (
<EuiFlexItem
className={'mlDataVisualizerMapWrapper'}
className={'dataVisualizerMapWrapper'}
data-test-subj={'mlDataVisualizerEmbeddedMap'}
>
<MlEmbeddedMapComponent layerList={formattedResults.layerList} />
<EmbeddedMapComponent layerList={formattedResults.layerList} />
</EuiFlexItem>
)}
</ExpandedRowContent>

View file

@ -1,4 +1,4 @@
.ml-experimental-badge.euiBetaBadge {
.experimental-badge.euiBetaBadge {
font-size: 10px;
vertical-align: middle;
margin-bottom: 5px;

View file

@ -14,10 +14,10 @@ export const ExperimentalBadge: FC<{ tooltipContent: string }> = ({ tooltipConte
return (
<span>
<EuiBetaBadge
className="ml-experimental-badge"
className="experimental-badge"
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.experimentalBadge.experimentalLabel"
id="xpack.fileDataVisualizer.experimentalBadge.experimentalLabel"
defaultMessage="Experimental"
/>
}

View file

@ -20,7 +20,7 @@ import {
EuiText,
EuiSubSteps,
} from '@elastic/eui';
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { FindFileStructureResponse } from '../../../../../file_upload/common';
interface Props {
results: FindFileStructureResponse;
@ -34,7 +34,7 @@ export const ExplanationFlyout: FC<Props> = ({ results, closeFlyout }) => {
<EuiTitle size="m">
<h2>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.explanationFlyout.title"
id="xpack.fileDataVisualizer.explanationFlyout.title"
defaultMessage="Analysis explanation"
/>
</h2>
@ -48,7 +48,7 @@ export const ExplanationFlyout: FC<Props> = ({ results, closeFlyout }) => {
<EuiFlexItem grow={false}>
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
<FormattedMessage
id="xpack.ml.fileDatavisualizer.explanationFlyout.closeButton"
id="xpack.fileDataVisualizer.explanationFlyout.closeButton"
defaultMessage="Close"
/>
</EuiButtonEmpty>
@ -63,7 +63,7 @@ const Content: FC<{ explanation: string[] }> = ({ explanation }) => (
<>
<EuiText size={'s'}>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.explanationFlyout.content"
id="xpack.fileDataVisualizer.explanationFlyout.content"
defaultMessage="The logical steps that have produced the analysis results."
/>

View file

@ -8,7 +8,7 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FileBasedFieldVisConfig } from '../../../stats_table/types';
import { FileBasedFieldVisConfig } from '../stats_table/types';
export const FileBasedNumberContentPreview = ({ config }: { config: FileBasedFieldVisConfig }) => {
const stats = config.stats;
@ -25,7 +25,7 @@ export const FileBasedNumberContentPreview = ({ config }: { config: FileBasedFie
<EuiFlexItem>
<b>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fieldStatsCard.minTitle"
id="xpack.fileDataVisualizer.fieldStatsCard.minTitle"
defaultMessage="min"
/>
</b>
@ -33,7 +33,7 @@ export const FileBasedNumberContentPreview = ({ config }: { config: FileBasedFie
<EuiFlexItem>
<b>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fieldStatsCard.medianTitle"
id="xpack.fileDataVisualizer.fieldStatsCard.medianTitle"
defaultMessage="median"
/>
</b>
@ -41,7 +41,7 @@ export const FileBasedNumberContentPreview = ({ config }: { config: FileBasedFie
<EuiFlexItem>
<b>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fieldStatsCard.maxTitle"
id="xpack.fileDataVisualizer.fieldStatsCard.maxTitle"
defaultMessage="max"
/>
</b>

View file

@ -7,11 +7,11 @@
import React, { FC, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { MultiSelectPicker } from '../../../../components/multi_select_picker';
import { MultiSelectPicker } from '../multi_select_picker';
import type {
FileBasedFieldVisConfig,
FileBasedUnknownFieldVisConfig,
} from '../../../stats_table/types/field_vis_config';
} from '../stats_table/types/field_vis_config';
interface Props {
fields: Array<FileBasedFieldVisConfig | FileBasedUnknownFieldVisConfig>;
@ -26,7 +26,7 @@ export const DataVisualizerFieldNamesFilter: FC<Props> = ({
}) => {
const fieldNameTitle = useMemo(
() =>
i18n.translate('xpack.ml.dataVisualizer.fileBased.fieldNameSelect', {
i18n.translate('xpack.fileDataVisualizer.fieldNameSelect', {
defaultMessage: 'Field name',
}),
[]

View file

@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FieldTypeIcon render component when type matches a field type 1`] = `
<EuiToolTip
content="keyword type"
delay="regular"
position="left"
>
<FieldTypeIconContainer
ariaLabel="keyword type"
color="euiColorVis0"
iconType="tokenText"
needsAria={false}
/>
</EuiToolTip>
`;

View file

@ -0,0 +1,52 @@
/*
* 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 from 'react';
import { mount, shallow } from 'enzyme';
import { FieldTypeIcon } from './field_type_icon';
import { JOB_FIELD_TYPES } from '../../../../common';
describe('FieldTypeIcon', () => {
test(`render component when type matches a field type`, () => {
const typeIconComponent = shallow(
<FieldTypeIcon type={JOB_FIELD_TYPES.KEYWORD} tooltipEnabled={true} needsAria={false} />
);
expect(typeIconComponent).toMatchSnapshot();
});
test(`render with tooltip and test hovering`, () => {
// Use fake timers so we don't have to wait for the EuiToolTip timeout
jest.useFakeTimers();
const typeIconComponent = mount(
<FieldTypeIcon type={JOB_FIELD_TYPES.KEYWORD} tooltipEnabled={true} needsAria={false} />
);
const container = typeIconComponent.find({ 'data-test-subj': 'fieldTypeIcon' });
expect(typeIconComponent.find('EuiToolTip').children()).toHaveLength(1);
container.simulate('mouseover');
// Run the timers so the EuiTooltip will be visible
jest.runAllTimers();
typeIconComponent.update();
expect(typeIconComponent.find('EuiToolTip').children()).toHaveLength(2);
container.simulate('mouseout');
// Run the timers so the EuiTooltip will be hidden again
jest.runAllTimers();
typeIconComponent.update();
expect(typeIconComponent.find('EuiToolTip').children()).toHaveLength(1);
// Clearing all mocks will also reset fake timers.
jest.clearAllMocks();
});
});

View file

@ -0,0 +1,130 @@
/*
* 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 { EuiToken, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { getJobTypeAriaLabel } from '../../util/field_types_utils';
import { JOB_FIELD_TYPES } from '../../../../common';
import type { JobFieldType } from '../../../../common';
interface FieldTypeIconProps {
tooltipEnabled: boolean;
type: JobFieldType;
fieldName?: string;
needsAria: boolean;
}
interface FieldTypeIconContainerProps {
ariaLabel: string | null;
iconType: string;
color: string;
needsAria: boolean;
[key: string]: any;
}
export const FieldTypeIcon: FC<FieldTypeIconProps> = ({
tooltipEnabled = false,
type,
fieldName,
needsAria = true,
}) => {
const ariaLabel = getJobTypeAriaLabel(type);
let iconType = 'questionInCircle';
let color = 'euiColorVis6';
switch (type) {
// Set icon types and colors
case JOB_FIELD_TYPES.BOOLEAN:
iconType = 'tokenBoolean';
color = 'euiColorVis5';
break;
case JOB_FIELD_TYPES.DATE:
iconType = 'tokenDate';
color = 'euiColorVis7';
break;
case JOB_FIELD_TYPES.GEO_POINT:
case JOB_FIELD_TYPES.GEO_SHAPE:
iconType = 'tokenGeo';
color = 'euiColorVis8';
break;
case JOB_FIELD_TYPES.TEXT:
iconType = 'document';
color = 'euiColorVis9';
break;
case JOB_FIELD_TYPES.IP:
iconType = 'tokenIP';
color = 'euiColorVis3';
break;
case JOB_FIELD_TYPES.KEYWORD:
iconType = 'tokenText';
color = 'euiColorVis0';
break;
case JOB_FIELD_TYPES.NUMBER:
iconType = 'tokenNumber';
color = fieldName !== undefined ? 'euiColorVis1' : 'euiColorVis2';
break;
case JOB_FIELD_TYPES.UNKNOWN:
// Use defaults
break;
}
const containerProps = {
ariaLabel,
iconType,
color,
needsAria,
};
if (tooltipEnabled === true) {
// wrap the inner component inside <span> because EuiToolTip doesn't seem
// to support having another component directly inside the tooltip anchor
// see https://github.com/elastic/eui/issues/839
return (
<EuiToolTip
position="left"
content={i18n.translate('xpack.fileDataVisualizer.fieldTypeIcon.fieldTypeTooltip', {
defaultMessage: '{type} type',
values: { type },
})}
>
<FieldTypeIconContainer {...containerProps} />
</EuiToolTip>
);
}
return <FieldTypeIconContainer {...containerProps} />;
};
// If the tooltip is used, it will apply its events to its first inner child.
// To pass on its properties we apply `rest` to the outer `span` element.
const FieldTypeIconContainer: FC<FieldTypeIconContainerProps> = ({
ariaLabel,
iconType,
color,
needsAria,
...rest
}) => {
const wrapperProps: { className: string; 'aria-label'?: string } = {
className: 'field-type-icon',
};
if (needsAria && ariaLabel) {
wrapperProps['aria-label'] = ariaLabel;
}
return (
<span data-test-subj="fieldTypeIcon" {...rest}>
<span {...wrapperProps}>
<EuiToken iconType={iconType} shape="square" size="s" color={color} />
</span>
</span>
);
};

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { FieldTypeIcon } from './field_type_icon';

View file

@ -8,13 +8,25 @@
import React, { FC, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { MultiSelectPicker, Option } from '../../../../components/multi_select_picker';
import { MultiSelectPicker, Option } from '../multi_select_picker';
import type {
FileBasedFieldVisConfig,
FileBasedUnknownFieldVisConfig,
} from '../../../stats_table/types/field_vis_config';
import { FieldTypeIcon } from '../../../../components/field_type_icon';
import { ML_JOB_FIELD_TYPES_OPTIONS } from '../../../index_based/components/search_panel/field_type_filter';
} from '../stats_table/types/field_vis_config';
import { FieldTypeIcon } from '../field_type_icon';
import { JOB_FIELD_TYPES } from '../../../../common';
const JOB_FIELD_TYPES_OPTIONS = {
[JOB_FIELD_TYPES.BOOLEAN]: { name: 'Boolean', icon: 'tokenBoolean' },
[JOB_FIELD_TYPES.DATE]: { name: 'Date', icon: 'tokenDate' },
[JOB_FIELD_TYPES.GEO_POINT]: { name: 'Geo point', icon: 'tokenGeo' },
[JOB_FIELD_TYPES.GEO_SHAPE]: { name: 'Geo shape', icon: 'tokenGeo' },
[JOB_FIELD_TYPES.IP]: { name: 'IP address', icon: 'tokenIP' },
[JOB_FIELD_TYPES.KEYWORD]: { name: 'Keyword', icon: 'tokenKeyword' },
[JOB_FIELD_TYPES.NUMBER]: { name: 'Number', icon: 'tokenNumber' },
[JOB_FIELD_TYPES.TEXT]: { name: 'Text', icon: 'tokenString' },
[JOB_FIELD_TYPES.UNKNOWN]: { name: 'Unknown' },
};
interface Props {
fields: Array<FileBasedFieldVisConfig | FileBasedUnknownFieldVisConfig>;
@ -29,7 +41,7 @@ export const DataVisualizerFieldTypesFilter: FC<Props> = ({
}) => {
const fieldNameTitle = useMemo(
() =>
i18n.translate('xpack.ml.dataVisualizer.fileBased.fieldTypeSelect', {
i18n.translate('xpack.fileDataVisualizer.fieldTypeSelect', {
defaultMessage: 'Field type',
}),
[]
@ -42,9 +54,9 @@ export const DataVisualizerFieldTypesFilter: FC<Props> = ({
if (
type !== undefined &&
!fieldTypesTracker.has(type) &&
ML_JOB_FIELD_TYPES_OPTIONS[type] !== undefined
JOB_FIELD_TYPES_OPTIONS[type] !== undefined
) {
const item = ML_JOB_FIELD_TYPES_OPTIONS[type];
const item = JOB_FIELD_TYPES_OPTIONS[type];
fieldTypesTracker.add(type);
fieldTypes.push({

View file

@ -5,11 +5,11 @@
* 2.0.
*/
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { FindFileStructureResponse } from '../../../../../file_upload/common';
import { getFieldNames, getSupportedFieldType } from './get_field_names';
import { FileBasedFieldVisConfig } from '../../../stats_table/types';
import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types';
import { roundToDecimalPlace } from '../../../../formatters/round_to_decimal_place';
import { FileBasedFieldVisConfig } from '../stats_table/types';
import { JOB_FIELD_TYPES } from '../../../../common';
import { roundToDecimalPlace } from '../utils';
export function createFields(results: FindFileStructureResponse) {
const {
@ -28,20 +28,20 @@ export function createFields(results: FindFileStructureResponse) {
if (fieldStats[name] !== undefined) {
const field: FileBasedFieldVisConfig = {
fieldName: name,
type: ML_JOB_FIELD_TYPES.UNKNOWN,
type: JOB_FIELD_TYPES.UNKNOWN,
};
const f = fieldStats[name];
const m = mappings.properties[name];
// sometimes the timestamp field is not in the mappings, and so our
// collection of fields will be missing a time field with a type of date
if (name === timestampField && field.type === ML_JOB_FIELD_TYPES.UNKNOWN) {
field.type = ML_JOB_FIELD_TYPES.DATE;
if (name === timestampField && field.type === JOB_FIELD_TYPES.UNKNOWN) {
field.type = JOB_FIELD_TYPES.DATE;
}
if (m !== undefined) {
field.type = getSupportedFieldType(m.type);
if (field.type === ML_JOB_FIELD_TYPES.NUMBER) {
if (field.type === JOB_FIELD_TYPES.NUMBER) {
numericFieldsCount += 1;
}
if (m.format !== undefined) {
@ -71,7 +71,7 @@ export function createFields(results: FindFileStructureResponse) {
}
if (f.top_hits !== undefined) {
if (field.type === ML_JOB_FIELD_TYPES.TEXT) {
if (field.type === JOB_FIELD_TYPES.TEXT) {
_stats = {
..._stats,
examples: f.top_hits.map((hit) => hit.value),
@ -84,7 +84,7 @@ export function createFields(results: FindFileStructureResponse) {
}
}
if (field.type === ML_JOB_FIELD_TYPES.DATE) {
if (field.type === JOB_FIELD_TYPES.DATE) {
_stats = {
..._stats,
earliest: f.earliest,
@ -99,9 +99,9 @@ export function createFields(results: FindFileStructureResponse) {
// this could be the message field for a semi-structured log file or a
// field which the endpoint has not been able to work out any information for
const type =
mappings.properties[name] && mappings.properties[name].type === ML_JOB_FIELD_TYPES.TEXT
? ML_JOB_FIELD_TYPES.TEXT
: ML_JOB_FIELD_TYPES.UNKNOWN;
mappings.properties[name] && mappings.properties[name].type === JOB_FIELD_TYPES.TEXT
? JOB_FIELD_TYPES.TEXT
: JOB_FIELD_TYPES.UNKNOWN;
return {
fieldName: name,

View file

@ -5,29 +5,25 @@
* 2.0.
*/
import React, { useMemo, FC } from 'react';
import React, { useMemo, FC, useState } from 'react';
import { EuiFlexGroup, EuiSpacer } from '@elastic/eui';
import type { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { DataVisualizerTable, ItemIdToExpandedRowMap } from '../../../stats_table';
import type { FileBasedFieldVisConfig } from '../../../stats_table/types/field_vis_config';
import type { FindFileStructureResponse } from '../../../../../file_upload/common';
import type { DataVisualizerTableState } from '../../../../common';
import { DataVisualizerTable, ItemIdToExpandedRowMap } from '../stats_table';
import type { FileBasedFieldVisConfig } from '../stats_table/types/field_vis_config';
import { FileBasedDataVisualizerExpandedRow } from '../expanded_row';
import { DataVisualizerFieldNamesFilter } from '../field_names_filter';
import { DataVisualizerFieldTypesFilter } from '../field_types_filter';
import { createFields } from './create_fields';
import { filterFields } from './filter_fields';
import { usePageUrlState } from '../../../../util/url_state';
import { ML_PAGES } from '../../../../../../common/constants/ml_url_generator';
import {
MetricFieldsCount,
TotalFieldsCount,
} from '../../../stats_table/components/field_count_stats';
import type { DataVisualizerFileBasedAppState } from '../../../../../../common/types/ml_url_generator';
import { MetricFieldsCount, TotalFieldsCount } from '../stats_table/components/field_count_stats';
interface Props {
results: FindFileStructureResponse;
}
export const getDefaultDataVisualizerListState = (): Required<DataVisualizerFileBasedAppState> => ({
export const getDefaultDataVisualizerListState = (): DataVisualizerTableState => ({
pageIndex: 0,
pageSize: 10,
sortField: 'fieldName',
@ -52,13 +48,11 @@ function getItemIdToExpandedRowMap(
export const FieldsStatsGrid: FC<Props> = ({ results }) => {
const restorableDefaults = getDefaultDataVisualizerListState();
const [
dataVisualizerListState,
setDataVisualizerListState,
] = usePageUrlState<DataVisualizerFileBasedAppState>(
ML_PAGES.DATA_VISUALIZER_FILE,
const [dataVisualizerListState, setDataVisualizerListState] = useState<DataVisualizerTableState>(
restorableDefaults
);
const visibleFieldTypes =
dataVisualizerListState.visibleFieldTypes ?? restorableDefaults.visibleFieldTypes;
const setVisibleFieldTypes = (values: string[]) => {
@ -73,11 +67,11 @@ export const FieldsStatsGrid: FC<Props> = ({ results }) => {
const { fields, totalFieldsCount, totalMetricFieldsCount } = useMemo(
() => createFields(results),
[results, visibleFieldNames, visibleFieldTypes]
[results]
);
const { filteredFields, visibleFieldsCount, visibleMetricsCount } = useMemo(
() => filterFields(fields, visibleFieldNames, visibleFieldTypes),
[results, visibleFieldNames, visibleFieldTypes]
[fields, visibleFieldNames, visibleFieldTypes]
);
const fieldsCountStats = { visibleFieldsCount, totalFieldsCount };

View file

@ -5,11 +5,11 @@
* 2.0.
*/
import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types';
import { JOB_FIELD_TYPES } from '../../../../common';
import type {
FileBasedFieldVisConfig,
FileBasedUnknownFieldVisConfig,
} from '../../../stats_table/types/field_vis_config';
} from '../stats_table/types/field_vis_config';
export function filterFields(
fields: Array<FileBasedFieldVisConfig | FileBasedUnknownFieldVisConfig>,
@ -32,6 +32,6 @@ export function filterFields(
return {
filteredFields: items,
visibleFieldsCount: items.length,
visibleMetricsCount: items.filter((d) => d.type === ML_JOB_FIELD_TYPES.NUMBER).length,
visibleMetricsCount: items.filter((d) => d.type === JOB_FIELD_TYPES.NUMBER).length,
};
}

View file

@ -6,10 +6,10 @@
*/
import { difference } from 'lodash';
import type { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { MlJobFieldType } from '../../../../../../common/types/field_types';
import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types';
import { ES_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common';
import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/common';
import type { FindFileStructureResponse } from '../../../../../file_upload/common';
import type { JobFieldType } from '../../../../common';
import { JOB_FIELD_TYPES } from '../../../../common';
export function getFieldNames(results: FindFileStructureResponse) {
const { mappings, field_stats: fieldStats, column_names: columnNames } = results;
@ -34,7 +34,7 @@ export function getFieldNames(results: FindFileStructureResponse) {
return tempFields;
}
export function getSupportedFieldType(type: string): MlJobFieldType {
export function getSupportedFieldType(type: string): JobFieldType {
switch (type) {
case ES_FIELD_TYPES.FLOAT:
case ES_FIELD_TYPES.HALF_FLOAT:
@ -44,13 +44,13 @@ export function getSupportedFieldType(type: string): MlJobFieldType {
case ES_FIELD_TYPES.LONG:
case ES_FIELD_TYPES.SHORT:
case ES_FIELD_TYPES.UNSIGNED_LONG:
return ML_JOB_FIELD_TYPES.NUMBER;
return JOB_FIELD_TYPES.NUMBER;
case ES_FIELD_TYPES.DATE:
case ES_FIELD_TYPES.DATE_NANOS:
return ML_JOB_FIELD_TYPES.DATE;
return JOB_FIELD_TYPES.DATE;
default:
return type as MlJobFieldType;
return type as JobFieldType;
}
}

View file

@ -10,7 +10,7 @@ import React, { FC } from 'react';
import { EuiTitle, EuiSpacer } from '@elastic/eui';
import { MLJobEditor, ML_EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor';
import { JsonEditor, EDITOR_MODE } from '../json_editor';
interface Props {
data: string;
@ -19,9 +19,9 @@ interface Props {
}
export const FileContents: FC<Props> = ({ data, format, numberOfLines }) => {
let mode = ML_EDITOR_MODE.TEXT;
if (format === ML_EDITOR_MODE.JSON) {
mode = ML_EDITOR_MODE.JSON;
let mode = EDITOR_MODE.TEXT;
if (format === EDITOR_MODE.JSON) {
mode = EDITOR_MODE.JSON;
}
const formattedData = limitByNumberOfLines(data, numberOfLines);
@ -31,7 +31,7 @@ export const FileContents: FC<Props> = ({ data, format, numberOfLines }) => {
<EuiTitle size="s">
<h2>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileContents.fileContentsTitle"
id="xpack.fileDataVisualizer.fileContents.fileContentsTitle"
defaultMessage="File contents"
/>
</h2>
@ -39,7 +39,7 @@ export const FileContents: FC<Props> = ({ data, format, numberOfLines }) => {
<div>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileContents.firstLinesDescription"
id="xpack.fileDataVisualizer.fileContents.firstLinesDescription"
defaultMessage="First {numberOfLines, plural, zero {# line} one {# line} other {# lines}}"
values={{
numberOfLines,
@ -49,7 +49,7 @@ export const FileContents: FC<Props> = ({ data, format, numberOfLines }) => {
<EuiSpacer size="s" />
<MLJobEditor
<JsonEditor
mode={mode}
readOnly={true}
value={formattedData}

View file

@ -12,7 +12,6 @@ import { EuiSpacer } from '@elastic/eui';
import { isEqual } from 'lodash';
import { ml } from '../../../../services/ml_api_service';
import { AboutPanel, LoadingPanel } from '../about_panel';
import { BottomBar } from '../bottom_bar';
import { ResultsView } from '../results_view';
@ -21,7 +20,6 @@ import { EditFlyout } from '../edit_flyout';
import { ExplanationFlyout } from '../explanation_flyout';
import { ImportView } from '../import_view';
import { DEFAULT_LINES_TO_SAMPLE, readFile, createUrlOverrides, processResults } from '../utils';
import { getFileUpload } from '../../../../util/dependency_cache';
import { MODE } from './constants';
@ -54,14 +52,16 @@ export class FileDataVisualizerView extends Component {
this.originalSettings = {
linesToSample: DEFAULT_LINES_TO_SAMPLE,
};
this.maxFileUploadBytes = getFileUpload().getMaxBytes();
this.savedObjectsClient = props.savedObjectsClient;
this.maxFileUploadBytes = props.fileUpload.getMaxBytes();
}
async componentDidMount() {
// check the user has the correct permission to import data.
// note, calling hasImportPermission with no arguments just checks the
// cluster privileges, the user will still need index privileges to create and ingest
const hasPermissionToImport = await getFileUpload().hasImportPermission({
const hasPermissionToImport = await this.props.fileUpload.hasImportPermission({
checkCreateIndexPattern: false,
checkHasManagePipeline: true,
});
@ -126,7 +126,7 @@ export class FileDataVisualizerView extends Component {
async analyzeFile(fileContents, overrides, isRetry = false) {
try {
const resp = await ml.fileDatavisualizer.analyzeFile(fileContents, overrides);
const resp = await this.props.fileUpload.analyzeFile(fileContents, overrides);
const serverSettings = processResults(resp);
const serverOverrides = resp.overrides;
@ -137,7 +137,7 @@ export class FileDataVisualizerView extends Component {
throw {
message: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileDatavisualizerView.xmlNotCurrentlySupportedErrorMessage"
id="xpack.fileDataVisualizer.fileDatavisualizerView.xmlNotCurrentlySupportedErrorMessage"
defaultMessage="XML not currently supported"
/>
),
@ -345,9 +345,10 @@ export class FileDataVisualizerView extends Component {
fileContents={fileContents}
data={data}
indexPatterns={this.props.indexPatterns}
kibanaConfig={this.props.kibanaConfig}
showBottomBar={this.showBottomBar}
hideBottomBar={this.hideBottomBar}
savedObjectsClient={this.savedObjectsClient}
fileUpload={this.props.fileUpload}
/>
{bottomBarVisible && (

View file

@ -11,8 +11,8 @@ import React, { FC } from 'react';
import { EuiCallOut, EuiSpacer, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/eui';
import numeral from '@elastic/numeral';
import { ErrorResponse } from '../../../../../../common/types/errors';
import { FILE_SIZE_DISPLAY_FORMAT } from '../../../../../../../file_upload/public';
import { FILE_SIZE_DISPLAY_FORMAT } from '../../../../common';
import { FindFileStructureErrorResponse } from '../../../../../file_upload/common';
interface FileTooLargeProps {
fileSize: number;
@ -31,7 +31,7 @@ export const FileTooLarge: FC<FileTooLargeProps> = ({ fileSize, maxFileSize }) =
errorText = (
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileErrorCallouts.fileSizeExceedsAllowedSizeErrorMessage"
id="xpack.fileDataVisualizer.fileErrorCallouts.fileSizeExceedsAllowedSizeErrorMessage"
defaultMessage="The size of the file you selected for upload is {fileSizeFormatted} which
exceeds the maximum permitted size of {maxFileSizeFormatted}"
values={{
@ -46,7 +46,7 @@ export const FileTooLarge: FC<FileTooLargeProps> = ({ fileSize, maxFileSize }) =
errorText = (
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileErrorCallouts.fileSizeExceedsAllowedSizeByDiffFormatErrorMessage"
id="xpack.fileDataVisualizer.fileErrorCallouts.fileSizeExceedsAllowedSizeByDiffFormatErrorMessage"
defaultMessage="The size of the file you selected for upload exceeds the maximum
permitted size of {maxFileSizeFormatted} by {diffFormatted}"
values={{
@ -62,7 +62,7 @@ export const FileTooLarge: FC<FileTooLargeProps> = ({ fileSize, maxFileSize }) =
<EuiCallOut
title={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileErrorCallouts.fileSizeTooLargeTitle"
id="xpack.fileDataVisualizer.fileErrorCallouts.fileSizeTooLargeTitle"
defaultMessage="File size is too large"
/>
}
@ -76,7 +76,7 @@ export const FileTooLarge: FC<FileTooLargeProps> = ({ fileSize, maxFileSize }) =
};
interface FileCouldNotBeReadProps {
error: ErrorResponse;
error: FindFileStructureErrorResponse;
loaded: boolean;
showEditFlyout(): void;
}
@ -92,7 +92,7 @@ export const FileCouldNotBeRead: FC<FileCouldNotBeReadProps> = ({
<EuiCallOut
title={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileErrorCallouts.fileCouldNotBeReadTitle"
id="xpack.fileDataVisualizer.fileErrorCallouts.fileCouldNotBeReadTitle"
defaultMessage="File structure cannot be determined"
/>
}
@ -103,13 +103,13 @@ export const FileCouldNotBeRead: FC<FileCouldNotBeReadProps> = ({
{loaded === false && (
<>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileErrorCallouts.applyOverridesDescription"
id="xpack.fileDataVisualizer.fileErrorCallouts.applyOverridesDescription"
defaultMessage="If you know something about this data, such as the file format or timestamp format, adding initial overrides may help us to infer the rest of the structure."
/>
<br />
<EuiButtonEmpty onClick={showEditFlyout} flush="left" size="xs">
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileErrorCallouts.overrideButton"
id="xpack.fileDataVisualizer.fileErrorCallouts.overrideButton"
defaultMessage="Apply override settings"
/>
</EuiButtonEmpty>
@ -122,7 +122,7 @@ export const FileCouldNotBeRead: FC<FileCouldNotBeReadProps> = ({
<>
<EuiSpacer size="s" />
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileErrorCallouts.revertingToPreviousSettingsDescription"
id="xpack.fileDataVisualizer.fileErrorCallouts.revertingToPreviousSettingsDescription"
defaultMessage="Reverting to previous settings"
/>
</>
@ -132,11 +132,11 @@ export const FileCouldNotBeRead: FC<FileCouldNotBeReadProps> = ({
);
};
export const Explanation: FC<{ error: ErrorResponse }> = ({ error }) => {
export const Explanation: FC<{ error: FindFileStructureErrorResponse }> = ({ error }) => {
if (!error?.body?.attributes?.body?.error?.suppressed?.length) {
return null;
}
const reason: string = error.body.attributes.body.error.suppressed[0].reason;
const reason = error.body.attributes.body.error.suppressed[0].reason;
return (
<>
<EuiSpacer size="s" />

View file

@ -6,7 +6,7 @@
*/
import { i18n } from '@kbn/i18n';
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { FindFileStructureResponse } from '../../../../../file_upload/common';
export function createFilebeatConfig(
index: string,
@ -36,7 +36,7 @@ export function createFilebeatConfig(
}
function getPaths() {
const txt = i18n.translate('xpack.ml.fileDatavisualizer.fileBeatConfig.paths', {
const txt = i18n.translate('xpack.fileDataVisualizer.fileBeatConfig.paths', {
defaultMessage: 'add path to your files here',
});
return [' paths:', ` - '<${txt}>'`];

View file

@ -22,8 +22,8 @@ import {
EuiCopy,
} from '@elastic/eui';
import { createFilebeatConfig } from './filebeat_config';
import { useMlKibana } from '../../../../contexts/kibana';
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { useFileDataVisualizerKibana } from '../../kibana_context'; // copy context?
import { FindFileStructureResponse } from '../../../../../file_upload/common';
export enum EDITOR_MODE {
HIDDEN,
@ -48,7 +48,7 @@ export const FilebeatConfigFlyout: FC<Props> = ({
const [username, setUsername] = useState<string | null>(null);
const {
services: { security },
} = useMlKibana();
} = useFileDataVisualizerKibana();
useEffect(() => {
if (security !== undefined) {
@ -56,12 +56,12 @@ export const FilebeatConfigFlyout: FC<Props> = ({
setUsername(user.username === undefined ? null : user.username);
});
}
}, []);
}, [security]);
useEffect(() => {
const config = createFilebeatConfig(index, results, ingestPipelineId, username);
setFileBeatConfig(config);
}, [username]);
}, [username, index, ingestPipelineId, results]);
return (
<EuiFlyout onClose={closeFlyout} hideCloseButton size={'m'}>
@ -75,7 +75,7 @@ export const FilebeatConfigFlyout: FC<Props> = ({
<EuiFlexItem grow={false}>
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileBeatConfigFlyout.closeButton"
id="xpack.fileDataVisualizer.fileBeatConfigFlyout.closeButton"
defaultMessage="Close"
/>
</EuiButtonEmpty>
@ -85,7 +85,7 @@ export const FilebeatConfigFlyout: FC<Props> = ({
{(copy) => (
<EuiButton onClick={copy}>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.fileBeatConfigFlyout.copyButton"
id="xpack.fileDataVisualizer.fileBeatConfigFlyout.copyButton"
defaultMessage="Copy to clipboard"
/>
</EuiButton>
@ -108,7 +108,7 @@ const Contents: FC<{
<EuiTitle size="s">
<h5>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigTitle"
id="xpack.fileDataVisualizer.resultsLinks.fileBeatConfigTitle"
defaultMessage="Filebeat configuration"
/>
</h5>
@ -116,14 +116,14 @@ const Contents: FC<{
<EuiSpacer size="s" />
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigTopText1"
id="xpack.fileDataVisualizer.resultsLinks.fileBeatConfigTopText1"
defaultMessage="Additional data can be uploaded to the {index} index using Filebeat."
values={{ index: <EuiCode>{index}</EuiCode> }}
/>
</p>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigTopText2"
id="xpack.fileDataVisualizer.resultsLinks.fileBeatConfigTopText2"
defaultMessage="Modify {filebeatYml} to set the connection information:"
values={{ filebeatYml: <EuiCode>filebeat.yml</EuiCode> }}
/>
@ -137,7 +137,7 @@ const Contents: FC<{
<p>
{username === null ? (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigBottomTextNoUsername"
id="xpack.fileDataVisualizer.resultsLinks.fileBeatConfigBottomTextNoUsername"
defaultMessage="Where {esUrl} is the URL of Elasticsearch."
values={{
esUrl: <EuiCode>{'<es_url>'}</EuiCode>,
@ -145,7 +145,7 @@ const Contents: FC<{
/>
) : (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigBottomText"
id="xpack.fileDataVisualizer.resultsLinks.fileBeatConfigBottomText"
defaultMessage="Where {password} is the password of the {user} user, {esUrl} is the URL of Elasticsearch."
values={{
user: <EuiCode>{username}</EuiCode>,

View file

@ -38,56 +38,56 @@ function title(statuses: Statuses) {
case statuses.readStatus:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.readingFileErrorMessage"
id="xpack.fileDataVisualizer.importErrors.readingFileErrorMessage"
defaultMessage="Error reading file"
/>
);
case statuses.parseJSONStatus:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.parsingJSONErrorMessage"
id="xpack.fileDataVisualizer.importErrors.parsingJSONErrorMessage"
defaultMessage="Error parsing JSON"
/>
);
case statuses.indexCreatedStatus:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.creatingIndexErrorMessage"
id="xpack.fileDataVisualizer.importErrors.creatingIndexErrorMessage"
defaultMessage="Error creating index"
/>
);
case statuses.ingestPipelineCreatedStatus:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.creatingIngestPipelineErrorMessage"
id="xpack.fileDataVisualizer.importErrors.creatingIngestPipelineErrorMessage"
defaultMessage="Error creating ingest pipeline"
/>
);
case statuses.uploadStatus:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.uploadingDataErrorMessage"
id="xpack.fileDataVisualizer.importErrors.uploadingDataErrorMessage"
defaultMessage="Error uploading data"
/>
);
case statuses.indexPatternCreatedStatus:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.creatingIndexPatternErrorMessage"
id="xpack.fileDataVisualizer.importErrors.creatingIndexPatternErrorMessage"
defaultMessage="Error creating index pattern"
/>
);
case statuses.permissionCheckStatus:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.checkingPermissionErrorMessage"
id="xpack.fileDataVisualizer.importErrors.checkingPermissionErrorMessage"
defaultMessage="Import permissions error"
/>
);
default:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.defaultErrorMessage"
id="xpack.fileDataVisualizer.importErrors.defaultErrorMessage"
defaultMessage="Error"
/>
);
@ -105,7 +105,7 @@ const ImportError: FC<{ error: any }> = ({ error }) => {
id="more"
buttonContent={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.moreButtonLabel"
id="xpack.fileDataVisualizer.importErrors.moreButtonLabel"
defaultMessage="More"
/>
}
@ -151,7 +151,7 @@ function toString(error: any): ImportError {
}
return {
msg: i18n.translate('xpack.ml.fileDatavisualizer.importErrors.unknownErrorMessage', {
msg: i18n.translate('xpack.fileDataVisualizer.importErrors.unknownErrorMessage', {
defaultMessage: 'Unknown error',
}),
};

View file

@ -80,31 +80,31 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
}
let processFileTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.processFileTitle',
'xpack.fileDataVisualizer.importProgress.processFileTitle',
{
defaultMessage: 'Process file',
}
);
let createIndexTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.createIndexTitle',
'xpack.fileDataVisualizer.importProgress.createIndexTitle',
{
defaultMessage: 'Create index',
}
);
let createIngestPipelineTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.createIngestPipelineTitle',
'xpack.fileDataVisualizer.importProgress.createIngestPipelineTitle',
{
defaultMessage: 'Create ingest pipeline',
}
);
let uploadingDataTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.uploadDataTitle',
'xpack.fileDataVisualizer.importProgress.uploadDataTitle',
{
defaultMessage: 'Upload data',
}
);
let createIndexPatternTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.createIndexPatternTitle',
'xpack.fileDataVisualizer.importProgress.createIndexPatternTitle',
{
defaultMessage: 'Create index pattern',
}
@ -113,7 +113,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
const creatingIndexStatus = (
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importProgress.stepTwoCreatingIndexDescription"
id="xpack.fileDataVisualizer.importProgress.stepTwoCreatingIndexDescription"
defaultMessage="Creating index"
/>
</p>
@ -122,7 +122,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
const creatingIndexAndIngestPipelineStatus = (
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importProgress.stepTwoCreatingIndexIngestPipelineDescription"
id="xpack.fileDataVisualizer.importProgress.stepTwoCreatingIndexIngestPipelineDescription"
defaultMessage="Creating index and ingest pipeline"
/>
</p>
@ -130,7 +130,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
if (completedStep >= 0) {
processFileTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.processingFileTitle',
'xpack.fileDataVisualizer.importProgress.processingFileTitle',
{
defaultMessage: 'Processing file',
}
@ -138,7 +138,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
statusInfo = (
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importProgress.processingImportedFileDescription"
id="xpack.fileDataVisualizer.importProgress.processingImportedFileDescription"
defaultMessage="Processing file for import"
/>
</p>
@ -146,13 +146,13 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
}
if (completedStep >= 1) {
processFileTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.fileProcessedTitle',
'xpack.fileDataVisualizer.importProgress.fileProcessedTitle',
{
defaultMessage: 'File processed',
}
);
createIndexTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.creatingIndexTitle',
'xpack.fileDataVisualizer.importProgress.creatingIndexTitle',
{
defaultMessage: 'Creating index',
}
@ -161,14 +161,11 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
createPipeline === true ? creatingIndexAndIngestPipelineStatus : creatingIndexStatus;
}
if (completedStep >= 2) {
createIndexTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.indexCreatedTitle',
{
defaultMessage: 'Index created',
}
);
createIndexTitle = i18n.translate('xpack.fileDataVisualizer.importProgress.indexCreatedTitle', {
defaultMessage: 'Index created',
});
createIngestPipelineTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.creatingIngestPipelineTitle',
'xpack.fileDataVisualizer.importProgress.creatingIngestPipelineTitle',
{
defaultMessage: 'Creating ingest pipeline',
}
@ -178,13 +175,13 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
}
if (completedStep >= 3) {
createIngestPipelineTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.ingestPipelineCreatedTitle',
'xpack.fileDataVisualizer.importProgress.ingestPipelineCreatedTitle',
{
defaultMessage: 'Ingest pipeline created',
}
);
uploadingDataTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.uploadingDataTitle',
'xpack.fileDataVisualizer.importProgress.uploadingDataTitle',
{
defaultMessage: 'Uploading data',
}
@ -193,14 +190,14 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
}
if (completedStep >= 4) {
uploadingDataTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.dataUploadedTitle',
'xpack.fileDataVisualizer.importProgress.dataUploadedTitle',
{
defaultMessage: 'Data uploaded',
}
);
if (createIndexPattern === true) {
createIndexPatternTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.creatingIndexPatternTitle',
'xpack.fileDataVisualizer.importProgress.creatingIndexPatternTitle',
{
defaultMessage: 'Creating index pattern',
}
@ -208,7 +205,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
statusInfo = (
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importProgress.creatingIndexPatternDescription"
id="xpack.fileDataVisualizer.importProgress.creatingIndexPatternDescription"
defaultMessage="Creating index pattern"
/>
</p>
@ -219,7 +216,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {
}
if (completedStep >= 5) {
createIndexPatternTitle = i18n.translate(
'xpack.ml.fileDatavisualizer.importProgress.indexPatternCreatedTitle',
'xpack.fileDataVisualizer.importProgress.indexPatternCreatedTitle',
{
defaultMessage: 'Index pattern created',
}
@ -293,7 +290,7 @@ const UploadFunctionProgress: FC<{ progress: number }> = ({ progress }) => {
<React.Fragment>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importProgress.uploadingDataDescription"
id="xpack.fileDataVisualizer.importProgress.uploadingDataDescription"
defaultMessage="Uploading data"
/>
</p>

View file

@ -19,8 +19,8 @@ import {
} from '@elastic/eui';
import { CombinedField, CombinedFieldsForm } from '../combined_fields';
import { MLJobEditor, ML_EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor';
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { JsonEditor, EDITOR_MODE } from '../json_editor';
import { FindFileStructureResponse } from '../../../../../file_upload/common';
const EDITOR_HEIGHT = '300px';
interface Props {
@ -69,7 +69,7 @@ export const AdvancedSettings: FC<Props> = ({
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.advancedImportSettings.indexNameLabel"
id="xpack.fileDataVisualizer.advancedImportSettings.indexNameLabel"
defaultMessage="Index name"
/>
}
@ -78,7 +78,7 @@ export const AdvancedSettings: FC<Props> = ({
>
<EuiFieldText
placeholder={i18n.translate(
'xpack.ml.fileDatavisualizer.advancedImportSettings.indexNamePlaceholder',
'xpack.fileDataVisualizer.advancedImportSettings.indexNamePlaceholder',
{
defaultMessage: 'index name',
}
@ -88,7 +88,7 @@ export const AdvancedSettings: FC<Props> = ({
onChange={onIndexChange}
isInvalid={indexNameError !== ''}
aria-label={i18n.translate(
'xpack.ml.fileDatavisualizer.advancedImportSettings.indexNameAriaLabel',
'xpack.fileDataVisualizer.advancedImportSettings.indexNameAriaLabel',
{
defaultMessage: 'Index name, required field',
}
@ -102,7 +102,7 @@ export const AdvancedSettings: FC<Props> = ({
id="createIndexPattern"
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.advancedImportSettings.createIndexPatternLabel"
id="xpack.fileDataVisualizer.advancedImportSettings.createIndexPatternLabel"
defaultMessage="Create index pattern"
/>
}
@ -116,7 +116,7 @@ export const AdvancedSettings: FC<Props> = ({
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.advancedImportSettings.indexPatternNameLabel"
id="xpack.fileDataVisualizer.advancedImportSettings.indexPatternNameLabel"
defaultMessage="Index pattern name"
/>
}
@ -184,14 +184,14 @@ const IndexSettings: FC<JsonEditorProps> = ({ initialized, data, onChange }) =>
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.advancedImportSettings.indexSettingsLabel"
id="xpack.fileDataVisualizer.advancedImportSettings.indexSettingsLabel"
defaultMessage="Index settings"
/>
}
fullWidth
>
<MLJobEditor
mode={ML_EDITOR_MODE.JSON}
<JsonEditor
mode={EDITOR_MODE.JSON}
readOnly={initialized === true}
value={data}
height={EDITOR_HEIGHT}
@ -209,14 +209,14 @@ const Mappings: FC<JsonEditorProps> = ({ initialized, data, onChange }) => {
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.advancedImportSettings.mappingsLabel"
id="xpack.fileDataVisualizer.advancedImportSettings.mappingsLabel"
defaultMessage="Mappings"
/>
}
fullWidth
>
<MLJobEditor
mode={ML_EDITOR_MODE.JSON}
<JsonEditor
mode={EDITOR_MODE.JSON}
readOnly={initialized === true}
value={data}
height={EDITOR_HEIGHT}
@ -234,14 +234,14 @@ const IngestPipeline: FC<JsonEditorProps> = ({ initialized, data, onChange }) =>
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.advancedImportSettings.ingestPipelineLabel"
id="xpack.fileDataVisualizer.advancedImportSettings.ingestPipelineLabel"
defaultMessage="Ingest pipeline"
/>
}
fullWidth
>
<MLJobEditor
mode={ML_EDITOR_MODE.JSON}
<JsonEditor
mode={EDITOR_MODE.JSON}
readOnly={initialized === true}
value={data}
height={EDITOR_HEIGHT}

View file

@ -13,7 +13,7 @@ import { EuiTabbedContent, EuiSpacer } from '@elastic/eui';
import { SimpleSettings } from './simple';
import { AdvancedSettings } from './advanced';
import { CombinedField } from '../combined_fields';
import { FindFileStructureResponse } from '../../../../../../../file_upload/common';
import { FindFileStructureResponse } from '../../../../../file_upload/common';
interface Props {
index: string;
@ -59,7 +59,7 @@ export const ImportSettings: FC<Props> = ({
const tabs = [
{
id: 'simple-settings',
name: i18n.translate('xpack.ml.fileDatavisualizer.importSettings.simpleTabName', {
name: i18n.translate('xpack.fileDataVisualizer.importSettings.simpleTabName', {
defaultMessage: 'Simple',
}),
content: (
@ -80,7 +80,7 @@ export const ImportSettings: FC<Props> = ({
},
{
id: 'advanced-settings',
name: i18n.translate('xpack.ml.fileDatavisualizer.importSettings.advancedTabName', {
name: i18n.translate('xpack.fileDataVisualizer.importSettings.advancedTabName', {
defaultMessage: 'Advanced',
}),
content: (

View file

@ -36,7 +36,7 @@ export const SimpleSettings: FC<Props> = ({
<EuiFormRow
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.simpleImportSettings.indexNameFormRowLabel"
id="xpack.fileDataVisualizer.simpleImportSettings.indexNameFormRowLabel"
defaultMessage="Index name"
/>
}
@ -45,7 +45,7 @@ export const SimpleSettings: FC<Props> = ({
>
<EuiFieldText
placeholder={i18n.translate(
'xpack.ml.fileDatavisualizer.simpleImportSettings.indexNamePlaceholder',
'xpack.fileDataVisualizer.simpleImportSettings.indexNamePlaceholder',
{
defaultMessage: 'index name',
}
@ -55,7 +55,7 @@ export const SimpleSettings: FC<Props> = ({
onChange={onIndexChange}
isInvalid={indexNameError !== ''}
aria-label={i18n.translate(
'xpack.ml.fileDatavisualizer.simpleImportSettings.indexNameAriaLabel',
'xpack.fileDataVisualizer.simpleImportSettings.indexNameAriaLabel',
{
defaultMessage: 'Index name, required field',
}
@ -70,7 +70,7 @@ export const SimpleSettings: FC<Props> = ({
id="createIndexPattern"
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.simpleImportSettings.createIndexPatternLabel"
id="xpack.fileDataVisualizer.simpleImportSettings.createIndexPatternLabel"
defaultMessage="Create index pattern"
/>
}

View file

@ -51,7 +51,7 @@ export class Failures extends Component<Props, State> {
id="failureList"
buttonContent={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.failedDocumentsButtonLabel"
id="xpack.fileDataVisualizer.importSummary.failedDocumentsButtonLabel"
defaultMessage="Failed documents"
/>
}

View file

@ -45,7 +45,7 @@ export const ImportSummary: FC<Props> = ({
<EuiCallOut
title={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.importCompleteTitle"
id="xpack.fileDataVisualizer.importSummary.importCompleteTitle"
defaultMessage="Import complete"
/>
}
@ -62,7 +62,7 @@ export const ImportSummary: FC<Props> = ({
<EuiCallOut
title={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.documentsCouldNotBeImportedTitle"
id="xpack.fileDataVisualizer.importSummary.documentsCouldNotBeImportedTitle"
defaultMessage="Some documents could not be imported"
/>
}
@ -71,7 +71,7 @@ export const ImportSummary: FC<Props> = ({
>
<p>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.documentsCouldNotBeImportedDescription"
id="xpack.fileDataVisualizer.importSummary.documentsCouldNotBeImportedDescription"
defaultMessage="{importFailuresLength} out of {docCount} documents could not be imported.
This could be due to lines not matching the Grok pattern."
values={{
@ -102,7 +102,7 @@ function createDisplayItems(
{
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.indexTitle"
id="xpack.fileDataVisualizer.importSummary.indexTitle"
defaultMessage="Index"
/>
),
@ -111,7 +111,7 @@ function createDisplayItems(
{
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.documentsIngestedTitle"
id="xpack.fileDataVisualizer.importSummary.documentsIngestedTitle"
defaultMessage="Documents ingested"
/>
),
@ -123,7 +123,7 @@ function createDisplayItems(
items.splice(1, 0, {
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.ingestPipelineTitle"
id="xpack.fileDataVisualizer.importSummary.ingestPipelineTitle"
defaultMessage="Ingest pipeline"
/>
),
@ -135,7 +135,7 @@ function createDisplayItems(
items.splice(1, 0, {
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.indexPatternTitle"
id="xpack.fileDataVisualizer.importSummary.indexPatternTitle"
defaultMessage="Index pattern"
/>
),
@ -147,7 +147,7 @@ function createDisplayItems(
items.push({
title: (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importSummary.failedDocumentsTitle"
id="xpack.fileDataVisualizer.importSummary.failedDocumentsTitle"
defaultMessage="Failed documents"
/>
),

View file

@ -20,7 +20,6 @@ import {
import { i18n } from '@kbn/i18n';
import { debounce } from 'lodash';
import { getFileUpload } from '../../../../util/dependency_cache';
import { ResultsLinks } from '../results_links';
import { FilebeatConfigFlyout } from '../filebeat_config_flyout';
import { ImportProgress, IMPORT_STATUS } from '../import_progress';
@ -33,8 +32,6 @@ import {
getDefaultCombinedFields,
} from '../combined_fields';
import { ExperimentalBadge } from '../experimental_badge';
import { getIndexPatternNames, loadIndexPatterns } from '../../../../util/index_utils';
import { ml } from '../../../../services/ml_api_service';
const DEFAULT_TIME_FIELD = '@timestamp';
const DEFAULT_INDEX_SETTINGS = { number_of_shards: 1 };
@ -81,6 +78,7 @@ export class ImportView extends Component {
super(props);
this.state = getDefaultState(DEFAULT_STATE, this.props.results);
this.savedObjectsClient = props.savedObjectsClient;
}
componentDidMount() {
@ -100,7 +98,7 @@ export class ImportView extends Component {
// TODO - sort this function out. it's a mess
async import() {
const { data, results, indexPatterns, kibanaConfig, showBottomBar } = this.props;
const { data, results, indexPatterns, showBottomBar, fileUpload } = this.props;
const { format } = results;
let { timeFieldName } = this.state;
@ -124,14 +122,14 @@ export class ImportView extends Component {
async () => {
// check to see if the user has permission to create and ingest data into the specified index
if (
(await getFileUpload().hasImportPermission({
(await fileUpload.hasImportPermission({
checkCreateIndexPattern: createIndexPattern,
checkHasManagePipeline: true,
indexName: index,
})) === false
) {
errors.push(
i18n.translate('xpack.ml.fileDatavisualizer.importView.importPermissionError', {
i18n.translate('xpack.fileDataVisualizer.importView.importPermissionError', {
defaultMessage:
'You do not have permission to create or import data into index {index}.',
values: {
@ -171,7 +169,7 @@ export class ImportView extends Component {
} catch (error) {
success = false;
const parseError = i18n.translate(
'xpack.ml.fileDatavisualizer.importView.parseSettingsError',
'xpack.fileDataVisualizer.importView.parseSettingsError',
{
defaultMessage: 'Error parsing settings:',
}
@ -184,7 +182,7 @@ export class ImportView extends Component {
} catch (error) {
success = false;
const parseError = i18n.translate(
'xpack.ml.fileDatavisualizer.importView.parseMappingsError',
'xpack.fileDataVisualizer.importView.parseMappingsError',
{
defaultMessage: 'Error parsing mappings:',
}
@ -199,7 +197,7 @@ export class ImportView extends Component {
} catch (error) {
success = false;
const parseError = i18n.translate(
'xpack.ml.fileDatavisualizer.importView.parsePipelineError',
'xpack.fileDataVisualizer.importView.parsePipelineError',
{
defaultMessage: 'Error parsing ingest pipeline:',
}
@ -221,7 +219,7 @@ export class ImportView extends Component {
}
if (success) {
const importer = await getFileUpload().importerFactory(format, {
const importer = await fileUpload.importerFactory(format, {
excludeLinesPattern: results.exclude_lines_pattern,
multilineStartPattern: results.multiline_start_pattern,
});
@ -294,8 +292,7 @@ export class ImportView extends Component {
const indexPatternResp = await createKibanaIndexPattern(
indexPatternName,
indexPatterns,
timeFieldName,
kibanaConfig
timeFieldName
);
success = indexPatternResp.success;
this.setState({
@ -354,16 +351,15 @@ export class ImportView extends Component {
return;
}
const { exists } = await ml.checkIndexExists({ index });
const exists = await this.props.fileUpload.checkIndexExists(index);
const indexNameError = exists ? (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importView.indexNameAlreadyExistsErrorMessage"
id="xpack.fileDataVisualizer.importView.indexNameAlreadyExistsErrorMessage"
defaultMessage="Index name already exists"
/>
) : (
isIndexNameValid(index)
);
this.setState({ checkingValidIndex: false, indexNameError });
}, 500);
@ -427,9 +423,19 @@ export class ImportView extends Component {
};
async loadIndexPatternNames() {
await loadIndexPatterns(this.props.indexPatterns);
const indexPatternNames = getIndexPatternNames();
this.setState({ indexPatternNames });
try {
const indexPatternNames = (
await this.savedObjectsClient.find({
type: 'index-pattern',
fields: ['title'],
perPage: 10000,
})
).savedObjects.map(({ attributes }) => attributes && attributes.title);
this.setState({ indexPatternNames });
} catch (error) {
console.error('failed to load index patterns', error);
}
}
render() {
@ -501,14 +507,14 @@ export class ImportView extends Component {
<EuiTitle size="s">
<h2>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importView.importDataTitle"
id="xpack.fileDataVisualizer.importView.importDataTitle"
defaultMessage="Import data"
/>
&nbsp;
<ExperimentalBadge
tooltipContent={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importView.experimentalFeatureTooltip"
id="xpack.fileDataVisualizer.importView.experimentalFeatureTooltip"
defaultMessage="Experimental feature. We'd love to hear your feedback."
/>
}
@ -549,7 +555,7 @@ export class ImportView extends Component {
data-test-subj="mlFileDataVisImportButton"
>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importView.importButtonLabel"
id="xpack.fileDataVisualizer.importView.importButtonLabel"
defaultMessage="Import"
/>
</EuiButton>
@ -558,7 +564,7 @@ export class ImportView extends Component {
{initialized === true && importing === false && (
<EuiButton onClick={this.clickReset}>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importView.resetButtonLabel"
id="xpack.fileDataVisualizer.importView.resetButtonLabel"
defaultMessage="Reset"
/>
</EuiButton>
@ -690,7 +696,7 @@ function isIndexNameValid(name) {
) {
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importView.indexNameContainsIllegalCharactersErrorMessage"
id="xpack.fileDataVisualizer.importView.indexNameContainsIllegalCharactersErrorMessage"
defaultMessage="Index name contains illegal characters"
/>
);
@ -707,7 +713,7 @@ function isIndexPatternNameValid(name, indexPatternNames, index) {
if (indexPatternNames.find((i) => i === name)) {
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importView.indexPatternNameAlreadyExistsErrorMessage"
id="xpack.fileDataVisualizer.importView.indexPatternNameAlreadyExistsErrorMessage"
defaultMessage="Index pattern name already exists"
/>
);
@ -723,7 +729,7 @@ function isIndexPatternNameValid(name, indexPatternNames, index) {
// name should match index
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importView.indexPatternDoesNotMatchIndexNameErrorMessage"
id="xpack.fileDataVisualizer.importView.indexPatternDoesNotMatchIndexNameErrorMessage"
defaultMessage="Index pattern does not match index name"
/>
);

Some files were not shown because too many files have changed in this diff Show more