From cbf5d02d581a9cfd1cff3c250bcd9e597cd44755 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 2 Mar 2021 19:07:03 -0700 Subject: [PATCH] rename advanced setting ml:fileDataVisualizerMaxFileSize to fileUpload:maxFileSize and increase max geojson upload size to 1GB (#92620) * rename ml:fileDataVisualizerMaxFileSize to fileUppload:maxFileSize * add saved object migration * file preview * importing status * remove console statement * import complete view * fix geojson_importer test * tslint * i18n fixes * cleanup * update documenation for advanced setting rename * advanced settings usage_collection * remove ml:fileDataVisualizerMaxFileSize from schemas and types * add copy buttons for import response and fix geojson upload functional tests * tslint * remove clipboard-read check * return early if env does not support reading from clipboard * fix reporting tests * review feedback * update GeoJsonFileSource to support showing results trimmed icon and tooltip * add fileUpload to useMlKibana context and replace dependencyCache with useMlKibana * tslint * review feedback * lower case file name * default to selecting geo_shape when file contains both points and shapes * fix wizard onError callback to not advance to next step Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/management/advanced-options.asciidoc | 10 +- docs/user/ml/index.asciidoc | 28 +- .../saved_objects/migrations.test.ts | 34 ++ .../ui_settings/saved_objects/migrations.ts | 19 + .../server/collectors/management/schema.ts | 2 +- .../server/collectors/management/types.ts | 2 +- src/plugins/telemetry/schema/oss_plugins.json | 2 +- .../plugins/file_upload/common/constants.ts | 2 + x-pack/plugins/file_upload/kibana.json | 3 +- .../plugins/file_upload/public/api/index.ts | 2 + .../public/components/geojson_file_picker.tsx | 155 ++++++++ .../components/import_complete_view.tsx | 159 ++++++++ .../public/components/index_settings.js | 1 + .../public/components/json_import_progress.js | 135 ------- .../components/json_index_file_picker.js | 267 ------------- .../components/json_upload_and_parse.js | 369 ++++++++---------- .../file_upload/public/get_max_bytes.ts | 31 ++ .../geojson_importer/geojson_importer.test.js | 130 +++--- .../geojson_importer/geojson_importer.ts | 354 +++++++++++------ .../public/importer/geojson_importer/index.ts | 2 +- .../file_upload/public/importer/importer.ts | 4 +- .../file_upload/public/importer/index.ts | 1 + .../public/importer/validate_file.ts | 52 +++ .../file_upload/public/kibana_services.ts | 1 + .../public/lazy_load_bundle/index.ts | 2 +- x-pack/plugins/file_upload/public/plugin.ts | 3 + x-pack/plugins/file_upload/server/plugin.ts | 23 ++ .../source_descriptor_types.ts | 2 + .../layers/file_upload_wizard/wizard.tsx | 17 +- .../geojson_file_source.ts | 10 + .../plugins/ml/common/constants/settings.ts | 1 - x-pack/plugins/ml/public/application/app.tsx | 1 + .../contexts/kibana/kibana_context.ts | 2 + .../datavisualizer_selector.tsx | 11 +- .../about_panel/welcome_content.tsx | 14 +- .../file_datavisualizer_view.js | 4 +- .../file_based/components/utils/index.ts | 2 - .../file_based/components/utils/utils.ts | 26 +- .../ml/server/lib/register_settings.ts | 20 - .../translations/translations/ja-JP.json | 23 -- .../translations/translations/zh-CN.json | 23 -- .../import_geojson/file_indexing_panel.js | 7 + .../reporting/ecommerce_kibana/data.json | 31 ++ .../reporting/ecommerce_kibana/data.json.gz | Bin 4526 -> 0 bytes .../ecommerce_kibana_spaces/data.json | 362 +++++++++++++++++ .../ecommerce_kibana_spaces/data.json.gz | Bin 2354 -> 0 bytes .../test/functional/page_objects/gis_page.ts | 19 +- 47 files changed, 1428 insertions(+), 940 deletions(-) create mode 100644 x-pack/plugins/file_upload/public/components/geojson_file_picker.tsx create mode 100644 x-pack/plugins/file_upload/public/components/import_complete_view.tsx delete mode 100644 x-pack/plugins/file_upload/public/components/json_import_progress.js delete mode 100644 x-pack/plugins/file_upload/public/components/json_index_file_picker.js create mode 100644 x-pack/plugins/file_upload/public/get_max_bytes.ts create mode 100644 x-pack/plugins/file_upload/public/importer/validate_file.ts create mode 100644 x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json delete mode 100644 x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json.gz create mode 100644 x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/data.json delete mode 100644 x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/data.json.gz diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index f09ff74085979..ee6e8fc21bfec 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -83,6 +83,10 @@ specific dashboard, application, or saved object as they enter each space. [[fields-popularlimit]]`fields:popularLimit`:: The top N most popular fields to show. +[[fileupload-maxfilesize]]`fileUpload:maxFileSize`:: +Sets the file size limit when importing files. The default +value is `100MB`. The highest supported value for this setting is `1GB`. + [[filtereditor-suggestvalues]]`filterEditor:suggestValues`:: Set this property to `false` to prevent the filter editor from suggesting values for fields. @@ -258,7 +262,7 @@ Hides the "Time" column in *Discover* and in all saved searches on dashboards. Highlights results in *Discover* and saved searches on dashboards. Highlighting slows requests when working on big documents. -[[doctable-legacy]]`doc_table:legacy`:: +[[doctable-legacy]]`doc_table:legacy`:: Controls the way the document table looks and works. Set this property to `true` to revert to the legacy implementation. [[discover-searchFieldsFromSource]]`discover:searchFieldsFromSource`:: @@ -282,10 +286,6 @@ must contain `from` and `to` values (see {ref}/common-options.html#date-math[accepted formats]). It is ignored unless `ml:anomalyDetection:results:enableTimeDefaults` is enabled. -[[ml-filedatavisualizermaxfilesize]]`ml:fileDataVisualizerMaxFileSize`:: -Sets the file size limit when importing data in the {data-viz}. The default -value is `100MB`. The highest supported value for this setting is `1GB`. - [float] [[kibana-notification-settings]] diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index fa15e0652e2ab..3c463da842faa 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -20,10 +20,10 @@ image::user/ml/images/ml-data-visualizer-sample.jpg[{data-viz} for sample flight experimental[] You can also upload a CSV, NDJSON, or log file. The *{data-viz}* identifies the file format and field mappings. You can then optionally import that data into an {es} index. To change the default file size limit, see -<>. +<>. If {stack-security-features} are enabled, users must have the necessary -privileges to use {ml-features}. Refer to +privileges to use {ml-features}. Refer to {ml-docs}/setup.html#setup-privileges[Set up {ml-features}]. NOTE: There are limitations in {ml-features} that affect {kib}. For more information, refer to {ml-docs}/ml-limitations.html[Machine learning]. @@ -40,15 +40,15 @@ false positives. {anomaly-detect-cap} runs in and scales with {es}, and includes an intuitive UI on the {kib} *Machine Learning* page for creating {anomaly-jobs} and understanding results. -If you have a license that includes the {ml-features}, you can +If you have a license that includes the {ml-features}, you can create {anomaly-jobs} and manage jobs and {dfeeds} from the *Job Management* -pane: +pane: [role="screenshot"] image::user/ml/images/ml-job-management.png[Job Management] -You can use the *Settings* pane to create and edit -{ml-docs}/ml-calendars.html[calendars] and the filters that are used in +You can use the *Settings* pane to create and edit +{ml-docs}/ml-calendars.html[calendars] and the filters that are used in {ml-docs}/ml-rules.html[custom rules]: [role="screenshot"] @@ -69,13 +69,13 @@ occurring in your operational environment at that time: image::user/ml/images/ml-annotations-list.png[Single Metric Viewer with annotations] In some circumstances, annotations are also added automatically. For example, if -the {anomaly-job} detects that there is missing data, it annotates the affected -time period. For more information, see -{ml-docs}/ml-delayed-data-detection.html[Handling delayed data]. The +the {anomaly-job} detects that there is missing data, it annotates the affected +time period. For more information, see +{ml-docs}/ml-delayed-data-detection.html[Handling delayed data]. The *Job Management* pane shows the full list of annotations for each job. -NOTE: The {kib} {ml-features} use pop-ups. You must configure your web -browser so that it does not block pop-up windows or create an exception for your +NOTE: The {kib} {ml-features} use pop-ups. You must configure your web +browser so that it does not block pop-up windows or create an exception for your {kib} URL. For more information about the {anomaly-detect} feature, see @@ -89,7 +89,7 @@ experimental[] The Elastic {ml} {dfanalytics} feature enables you to analyze your data using {classification}, {oldetection}, and {regression} algorithms and generate new -indices that contain the results alongside your source data. +indices that contain the results alongside your source data. If you have a license that includes the {ml-features}, you can create {dfanalytics-jobs} and view their results on the *Data Frame Analytics* page in @@ -98,5 +98,5 @@ If you have a license that includes the {ml-features}, you can create [role="screenshot"] image::user/ml/images/outliers.png[{oldetection-cap} results in {kib}] -For more information about the {dfanalytics} feature, see -{ml-docs}/ml-dfanalytics.html[{ml-cap} {dfanalytics}]. \ No newline at end of file +For more information about the {dfanalytics} feature, see +{ml-docs}/ml-dfanalytics.html[{ml-cap} {dfanalytics}]. diff --git a/src/core/server/ui_settings/saved_objects/migrations.test.ts b/src/core/server/ui_settings/saved_objects/migrations.test.ts index ea5b4133b49c3..7b53f4e21dd9b 100644 --- a/src/core/server/ui_settings/saved_objects/migrations.test.ts +++ b/src/core/server/ui_settings/saved_objects/migrations.test.ts @@ -44,3 +44,37 @@ describe('ui_settings 7.9.0 migrations', () => { }); }); }); + +describe('ui_settings 7.13.0 migrations', () => { + const migration = migrations['7.13.0']; + + test('returns doc on empty object', () => { + expect(migration({} as SavedObjectUnsanitizedDoc)).toEqual({ + references: [], + }); + }); + test('properly renames ml:fileDataVisualizerMaxFileSize to fileUpload:maxFileSize', () => { + const doc = { + type: 'config', + id: '8.0.0', + attributes: { + buildNum: 9007199254740991, + 'ml:fileDataVisualizerMaxFileSize': '250MB', + }, + references: [], + updated_at: '2020-06-09T20:18:20.349Z', + migrationVersion: {}, + }; + expect(migration(doc)).toEqual({ + type: 'config', + id: '8.0.0', + attributes: { + buildNum: 9007199254740991, + 'fileUpload:maxFileSize': '250MB', + }, + references: [], + updated_at: '2020-06-09T20:18:20.349Z', + migrationVersion: {}, + }); + }); +}); diff --git a/src/core/server/ui_settings/saved_objects/migrations.ts b/src/core/server/ui_settings/saved_objects/migrations.ts index 7ea5573076ba0..b5dc13fcdae3b 100644 --- a/src/core/server/ui_settings/saved_objects/migrations.ts +++ b/src/core/server/ui_settings/saved_objects/migrations.ts @@ -28,4 +28,23 @@ export const migrations = { }), references: doc.references || [], }), + '7.13.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => ({ + ...doc, + ...(doc.attributes && { + attributes: Object.keys(doc.attributes).reduce( + (acc, key) => + key === 'ml:fileDataVisualizerMaxFileSize' + ? { + ...acc, + ['fileUpload:maxFileSize']: doc.attributes[key], + } + : { + ...acc, + [key]: doc.attributes[key], + }, + {} + ), + }), + references: doc.references || [], + }), }; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index a35c45d2273b6..5effd780a003c 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -220,7 +220,7 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, - 'ml:fileDataVisualizerMaxFileSize': { + 'fileUpload:maxFileSize': { type: 'keyword', _meta: { description: 'Non-default value of setting.' }, }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index f5d1157cbaa77..7ddc090cdbc53 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -74,7 +74,7 @@ export interface UsageStats { 'discover:sort:defaultOrder': string; 'context:step': number; 'accessibility:disableAnimations': boolean; - 'ml:fileDataVisualizerMaxFileSize': string; + 'fileUpload:maxFileSize': string; 'ml:anomalyDetection:results:enableTimeDefaults': boolean; 'ml:anomalyDetection:results:timeDefaults': string; 'truncate:maxHeight': number; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index e4cc9f0299771..0dcae57149707 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7742,7 +7742,7 @@ "description": "Non-default value of setting." } }, - "ml:fileDataVisualizerMaxFileSize": { + "fileUpload:maxFileSize": { "type": "keyword", "_meta": { "description": "Non-default value of setting." diff --git a/x-pack/plugins/file_upload/common/constants.ts b/x-pack/plugins/file_upload/common/constants.ts index 11ad80f5c955e..ea36e51466703 100644 --- a/x-pack/plugins/file_upload/common/constants.ts +++ b/x-pack/plugins/file_upload/common/constants.ts @@ -5,6 +5,8 @@ * 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 diff --git a/x-pack/plugins/file_upload/kibana.json b/x-pack/plugins/file_upload/kibana.json index 7676a01d0b0f9..6d55e4f46d7e8 100644 --- a/x-pack/plugins/file_upload/kibana.json +++ b/x-pack/plugins/file_upload/kibana.json @@ -4,5 +4,6 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data", "usageCollection"] + "requiredPlugins": ["data", "usageCollection"], + "requiredBundles": ["kibanaReact"] } diff --git a/x-pack/plugins/file_upload/public/api/index.ts b/x-pack/plugins/file_upload/public/api/index.ts index 359bc4a1687b5..8884c398fa2e6 100644 --- a/x-pack/plugins/file_upload/public/api/index.ts +++ b/x-pack/plugins/file_upload/public/api/index.ts @@ -12,6 +12,8 @@ import type { IImporter, ImportFactoryOptions } from '../importer'; export interface FileUploadStartApi { getFileUploadComponent(): Promise>; importerFactory(format: string, options: ImportFactoryOptions): Promise; + getMaxBytes(): number; + getMaxBytesFormatted(): string; } export async function getFileUploadComponent(): Promise< diff --git a/x-pack/plugins/file_upload/public/components/geojson_file_picker.tsx b/x-pack/plugins/file_upload/public/components/geojson_file_picker.tsx new file mode 100644 index 0000000000000..724a23a43202f --- /dev/null +++ b/x-pack/plugins/file_upload/public/components/geojson_file_picker.tsx @@ -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, { Component } from 'react'; +import { EuiFilePicker, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { MB } from '../../common'; +import { getMaxBytesFormatted } from '../get_max_bytes'; +import { validateFile } from '../importer'; +import { GeoJsonImporter, GeoJsonPreview, GEOJSON_FILE_TYPES } from '../importer/geojson_importer'; + +interface Props { + onSelect: ({ + features, + hasPoints, + hasShapes, + importer, + indexName, + previewCoverage, + }: GeoJsonPreview & { + indexName: string; + importer: GeoJsonImporter; + }) => void; + onClear: () => void; +} + +interface State { + error: string | null; + isLoadingPreview: boolean; + previewSummary: string | null; +} + +export class GeoJsonFilePicker extends Component { + private _isMounted = false; + + state: State = { + error: null, + isLoadingPreview: false, + previewSummary: null, + }; + + async componentDidMount() { + this._isMounted = true; + } + + componentWillUnmount() { + this._isMounted = false; + } + + _onFileSelect = (files: FileList | null) => { + this.props.onClear(); + + this.setState({ + error: null, + isLoadingPreview: false, + previewSummary: null, + }); + + if (files && files.length) { + this._loadFilePreview(files[0]); + } + }; + + async _loadFilePreview(file: File) { + this.setState({ isLoadingPreview: true }); + + let importer: GeoJsonImporter | null = null; + let previewError: string | null = null; + let preview: GeoJsonPreview | null = null; + try { + validateFile(file, GEOJSON_FILE_TYPES); + importer = new GeoJsonImporter(file); + preview = await importer.previewFile(10000, MB * 3); + if (preview.features.length === 0) { + previewError = i18n.translate('xpack.fileUpload.geojsonFilePicker.noFeaturesDetected', { + defaultMessage: 'No GeoJson features found in selected file.', + }); + } + } catch (error) { + previewError = error.message; + } + + if (!this._isMounted) { + return; + } + + this.setState({ + error: previewError, + isLoadingPreview: false, + previewSummary: + !previewError && preview + ? i18n.translate('xpack.fileUpload.geojsonFilePicker.previewSummary', { + defaultMessage: 'Previewing {numFeatures} features, {previewCoverage}% of file.', + values: { + numFeatures: preview.features.length, + previewCoverage: preview.previewCoverage, + }, + }) + : null, + }); + + if (importer && preview) { + this.props.onSelect({ + ...preview, + importer, + indexName: file.name.split('.')[0].toLowerCase(), + }); + } + } + + _renderHelpText() { + return this.state.previewSummary !== null ? ( + this.state.previewSummary + ) : ( + + {i18n.translate('xpack.fileUpload.geojsonFilePicker.acceptedFormats', { + defaultMessage: 'Formats accepted: {fileTypes}', + values: { fileTypes: GEOJSON_FILE_TYPES.join(', ') }, + })} +
+ {i18n.translate('xpack.fileUpload.geojsonFilePicker.maxSize', { + defaultMessage: 'Max size: {maxFileSize}', + values: { maxFileSize: getMaxBytesFormatted() }, + })} +
+ {i18n.translate('xpack.fileUpload.geojsonFilePicker.acceptedCoordinateSystem', { + defaultMessage: 'Coordinates must be in EPSG:4326 coordinate reference system.', + })} +
+ ); + } + + render() { + return ( + + + + ); + } +} diff --git a/x-pack/plugins/file_upload/public/components/import_complete_view.tsx b/x-pack/plugins/file_upload/public/components/import_complete_view.tsx new file mode 100644 index 0000000000000..c6209ae765c72 --- /dev/null +++ b/x-pack/plugins/file_upload/public/components/import_complete_view.tsx @@ -0,0 +1,159 @@ +/* + * 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, { Component, Fragment } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonIcon, + EuiCallOut, + EuiCopy, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CodeEditor, KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { getHttp, getUiSettings } from '../kibana_services'; +import { ImportResponse } from '../../common'; + +const services = { + uiSettings: getUiSettings(), +}; + +interface Props { + importResp?: ImportResponse; + indexPatternResp?: object; +} + +export class ImportCompleteView extends Component { + _renderCodeEditor(json: object | undefined, title: string, copyButtonDataTestSubj: string) { + if (!json) { + return null; + } + + const jsonAsString = JSON.stringify(json, null, 2); + + return ( + + + + +

{title}

+
+
+ + + + {(copy) => ( + + )} + + +
+
+ {}} + options={{ + readOnly: true, + lineNumbers: 'off', + fontSize: 12, + minimap: { + enabled: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + automaticLayout: true, + }} + /> +
+ +
+ ); + } + + _getStatusMsg() { + if (!this.props.importResp || !this.props.importResp.success) { + return i18n.translate('xpack.fileUpload.uploadFailureMsg', { + defaultMessage: 'File upload failed.', + }); + } + + const successMsg = i18n.translate('xpack.fileUpload.uploadSuccessMsg', { + defaultMessage: 'File upload complete: indexed {numFeatures} features.', + values: { + numFeatures: this.props.importResp.docCount, + }, + }); + + const failedFeaturesMsg = this.props.importResp.failures.length + ? i18n.translate('xpack.fileUpload.failedFeaturesMsg', { + defaultMessage: 'Unable to index {numFailures} features.', + values: { + numFailures: this.props.importResp.failures.length, + }, + }) + : ''; + + return `${successMsg} ${failedFeaturesMsg}`; + } + + render() { + return ( + + +

{this._getStatusMsg()}

+
+ {this._renderCodeEditor( + this.props.importResp, + i18n.translate('xpack.fileUpload.jsonImport.indexingResponse', { + defaultMessage: 'Import response', + }), + 'indexRespCopyButton' + )} + {this._renderCodeEditor( + this.props.indexPatternResp, + i18n.translate('xpack.fileUpload.jsonImport.indexPatternResponse', { + defaultMessage: 'Index pattern response', + }), + 'indexPatternRespCopyButton' + )} + +
+ + + + +
+
+
+ ); + } +} diff --git a/x-pack/plugins/file_upload/public/components/index_settings.js b/x-pack/plugins/file_upload/public/components/index_settings.js index 3cad31cdbe3df..7d7ab3516e651 100644 --- a/x-pack/plugins/file_upload/public/components/index_settings.js +++ b/x-pack/plugins/file_upload/public/components/index_settings.js @@ -118,6 +118,7 @@ export class IndexSettings extends Component { text: indexType, value: indexType, }))} + value={this.props.selectedIndexType} onChange={({ target }) => setSelectedIndexType(target.value)} /> diff --git a/x-pack/plugins/file_upload/public/components/json_import_progress.js b/x-pack/plugins/file_upload/public/components/json_import_progress.js deleted file mode 100644 index 1adf7d9039e56..0000000000000 --- a/x-pack/plugins/file_upload/public/components/json_import_progress.js +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { Fragment, Component } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiCodeBlock, EuiSpacer, EuiText, EuiTitle, EuiProgress, EuiCallOut } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { getHttp } from '../kibana_services'; - -export class JsonImportProgress extends Component { - state = { - indexDataJson: null, - indexPatternJson: null, - indexName: '', - importStage: '', - }; - - componentDidUpdate(prevProps, prevState) { - this._setIndex(this.props); - this._formatIndexDataResponse({ ...this.state, ...this.props }); - this._formatIndexPatternResponse({ ...this.state, ...this.props }); - if (prevState.importStage !== this.props.importStage) { - this.setState({ - importStage: this.props.importStage, - }); - } - } - - // Retain last index for UI purposes - _setIndex = ({ indexName }) => { - if (indexName && !this.state.indexName) { - this.setState({ indexName }); - } - }; - - // Format json responses - _formatIndexDataResponse = ({ indexDataResp, indexDataJson }) => { - if (indexDataResp && !indexDataJson) { - this.setState({ indexDataJson: JSON.stringify(indexDataResp, null, 2) }); - } - }; - - _formatIndexPatternResponse = ({ indexPatternResp, indexPatternJson }) => { - if (indexPatternResp && !indexPatternJson) { - this.setState({ indexPatternJson: JSON.stringify(indexPatternResp, null, 2) }); - } - }; - - render() { - const { complete } = this.props; - const { indexPatternJson, indexDataJson, indexName, importStage } = this.state; - const importMessage = complete ? importStage : `${importStage}: ${indexName}`; - - return ( - - {!complete ? : null} - -

- -

-
- {importMessage &&

{importMessage}

}
- - {complete ? ( - - {indexDataJson ? ( - - -

- -

-
- - {indexDataJson} - - -
- ) : null} - {indexPatternJson ? ( - - -

- -

-
- - {indexPatternJson} - - -
- ) : null} - -
- {i18n.translate('xpack.fileUpload.jsonImport.indexModsMsg', { - defaultMessage: 'Further index modifications can be made using\n', - })} - - {i18n.translate('xpack.fileUpload.jsonImport.indexMgmtLink', { - defaultMessage: 'Index Management', - })} - - . -
-
-
- ) : null} -
- ); - } -} diff --git a/x-pack/plugins/file_upload/public/components/json_index_file_picker.js b/x-pack/plugins/file_upload/public/components/json_index_file_picker.js deleted file mode 100644 index 78bf7378578de..0000000000000 --- a/x-pack/plugins/file_upload/public/components/json_index_file_picker.js +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { Fragment, Component } from 'react'; -import { EuiFilePicker, EuiFormRow, EuiProgress } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; - -const MAX_FILE_SIZE = 52428800; -const ACCEPTABLE_FILETYPES = ['json', 'geojson']; -const acceptedFileTypeString = ACCEPTABLE_FILETYPES.map((type) => `.${type}`).join(','); -const acceptedFileTypeStringMessage = ACCEPTABLE_FILETYPES.map((type) => `.${type}`).join(', '); - -export class JsonIndexFilePicker extends Component { - state = { - fileUploadError: '', - percentageProcessed: 0, - featuresProcessed: 0, - fileParseActive: false, - currentFileTracker: null, - }; - - async componentDidMount() { - this._isMounted = true; - } - - componentWillUnmount() { - this._isMounted = false; - } - - isFileParseActive = () => this._isMounted && this.state.fileParseActive; - - _fileHandler = (fileList) => { - const fileArr = Array.from(fileList); - this.props.resetFileAndIndexSettings(); - this.setState({ - fileUploadError: '', - percentageProcessed: 0, - featuresProcessed: 0, - }); - if (fileArr.length === 0) { - // Remove - this.setState({ - fileParseActive: false, - }); - return; - } - const file = fileArr[0]; - - this.setState( - { - fileParseActive: true, - currentFileTracker: Symbol(), - }, - () => this._parseFile(file) - ); - }; - - _getFileNameAndCheckType({ name }) { - let fileNameOnly; - try { - if (!name) { - throw new Error( - i18n.translate('xpack.fileUpload.jsonIndexFilePicker.noFileNameError', { - defaultMessage: 'No file name provided', - }) - ); - } - - const splitNameArr = name.split('.'); - const fileType = splitNameArr.pop(); - if (!ACCEPTABLE_FILETYPES.includes(fileType)) { - //should only occur if browser does not accept the accept parameter - throw new Error( - i18n.translate('xpack.fileUpload.jsonIndexFilePicker.acceptableTypesError', { - defaultMessage: 'File is not one of acceptable types: {types}', - values: { - types: ACCEPTABLE_FILETYPES.join(', '), - }, - }) - ); - } - - fileNameOnly = splitNameArr[0]; - } catch (error) { - this.setState({ - fileUploadError: i18n.translate( - 'xpack.fileUpload.jsonIndexFilePicker.fileProcessingError', - { - defaultMessage: 'File processing error: {errorMessage}', - values: { - errorMessage: error.message, - }, - } - ), - }); - return; - } - return fileNameOnly.toLowerCase(); - } - - setFileProgress = ({ featuresProcessed, bytesProcessed, totalBytes }) => { - const percentageProcessed = parseInt((100 * bytesProcessed) / totalBytes); - if (this.isFileParseActive()) { - this.setState({ featuresProcessed, percentageProcessed }); - } - }; - - async _parseFile(file) { - const { currentFileTracker } = this.state; - const { setFileRef, setParsedFile, resetFileAndIndexSettings } = this.props; - - if (file.size > MAX_FILE_SIZE) { - this.setState({ - fileUploadError: i18n.translate('xpack.fileUpload.jsonIndexFilePicker.acceptableFileSize', { - defaultMessage: 'File size {fileSize} exceeds maximum file size of {maxFileSize}', - values: { - fileSize: bytesToSize(file.size), - maxFileSize: bytesToSize(MAX_FILE_SIZE), - }, - }), - }); - resetFileAndIndexSettings(); - return; - } - - const defaultIndexName = this._getFileNameAndCheckType(file); - if (!defaultIndexName) { - resetFileAndIndexSettings(); - return; - } - - const fileResult = await this.props.geojsonImporter - .readFile(file, this.setFileProgress, this.isFileParseActive) - .catch((err) => { - if (this._isMounted) { - this.setState({ - fileParseActive: false, - percentageProcessed: 0, - featuresProcessed: 0, - fileUploadError: ( - - ), - }); - resetFileAndIndexSettings(); - return; - } - }); - - if (!this._isMounted) { - return; - } - - // If another file is replacing this one, leave file parse active - this.setState({ - percentageProcessed: 0, - featuresProcessed: 0, - fileParseActive: currentFileTracker !== this.state.currentFileTracker, - }); - if (!fileResult) { - resetFileAndIndexSettings(); - return; - } - - if (fileResult.errors.length) { - this.setState({ - fileUploadError: ( - - ), - }); - } - setFileRef(file); - setParsedFile(fileResult, defaultIndexName); - } - - render() { - const { fileUploadError, percentageProcessed, featuresProcessed } = this.state; - - return ( - - {percentageProcessed ? ( - - ) : null} - - } - isInvalid={fileUploadError !== ''} - error={[fileUploadError]} - helpText={ - percentageProcessed ? ( - i18n.translate('xpack.fileUpload.jsonIndexFilePicker.parsingFile', { - defaultMessage: '{featuresProcessed} features parsed...', - values: { - featuresProcessed, - }, - }) - ) : ( - - {i18n.translate('xpack.fileUpload.jsonIndexFilePicker.formatsAccepted', { - defaultMessage: 'Formats accepted: {acceptedFileTypeStringMessage}', - values: { - acceptedFileTypeStringMessage, - }, - })}{' '} -
- -
- {i18n.translate('xpack.fileUpload.jsonIndexFilePicker.coordinateSystemAccepted', { - defaultMessage: 'Coordinates must be in EPSG:4326 coordinate reference system.', - })}{' '} -
- ) - } - > - - } - onChange={this._fileHandler} - accept={acceptedFileTypeString} - /> -
-
- ); - } -} - -function bytesToSize(bytes) { - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; - if (bytes === 0) return 'n/a'; - const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10); - if (i === 0) return `${bytes} ${sizes[i]})`; - return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}`; -} diff --git a/x-pack/plugins/file_upload/public/components/json_upload_and_parse.js b/x-pack/plugins/file_upload/public/components/json_upload_and_parse.js index d4f6858eb5995..ae9f5e9ac8154 100644 --- a/x-pack/plugins/file_upload/public/components/json_upload_and_parse.js +++ b/x-pack/plugins/file_upload/public/components/json_upload_and_parse.js @@ -7,65 +7,41 @@ import React, { Component, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiForm } from '@elastic/eui'; +import { EuiForm, EuiProgress, EuiText } from '@elastic/eui'; import PropTypes from 'prop-types'; import { IndexSettings } from './index_settings'; -import { JsonIndexFilePicker } from './json_index_file_picker'; -import { JsonImportProgress } from './json_import_progress'; -import _ from 'lodash'; -import { GeoJsonImporter } from '../importer/geojson_importer'; -import { ES_FIELD_TYPES } from '../../../../../src/plugins/data/public'; import { getIndexPatternService } from '../kibana_services'; +import { GeoJsonFilePicker } from './geojson_file_picker'; +import { ImportCompleteView } from './import_complete_view'; +import { ES_FIELD_TYPES } from '../../../../../src/plugins/data/public'; -const INDEXING_STAGE = { - INDEXING_STARTED: i18n.translate('xpack.fileUpload.jsonUploadAndParse.dataIndexingStarted', { - defaultMessage: 'Data indexing started', - }), - WRITING_TO_INDEX: i18n.translate('xpack.fileUpload.jsonUploadAndParse.writingToIndex', { - defaultMessage: 'Writing to index', - }), - INDEXING_COMPLETE: i18n.translate('xpack.fileUpload.jsonUploadAndParse.indexingComplete', { - defaultMessage: 'Indexing complete', - }), - CREATING_INDEX_PATTERN: i18n.translate( - 'xpack.fileUpload.jsonUploadAndParse.creatingIndexPattern', - { defaultMessage: 'Creating index pattern' } - ), - INDEX_PATTERN_COMPLETE: i18n.translate( - 'xpack.fileUpload.jsonUploadAndParse.indexPatternComplete', - { defaultMessage: 'Index pattern complete' } - ), - INDEXING_ERROR: i18n.translate('xpack.fileUpload.jsonUploadAndParse.dataIndexingError', { - defaultMessage: 'Data indexing error', - }), - INDEX_PATTERN_ERROR: i18n.translate('xpack.fileUpload.jsonUploadAndParse.indexPatternError', { - defaultMessage: 'Index pattern error', - }), +const PHASE = { + CONFIGURE: 'CONFIGURE', + IMPORT: 'IMPORT', + COMPLETE: 'COMPLETE', }; -export class JsonUploadAndParse extends Component { - geojsonImporter = new GeoJsonImporter(); +function getWritingToIndexMsg(progress) { + return i18n.translate('xpack.fileUpload.jsonUploadAndParse.writingToIndex', { + defaultMessage: 'Writing to index: {progress}% complete', + values: { progress }, + }); +} +export class JsonUploadAndParse extends Component { state = { - // File state - fileRef: null, - parsedFile: null, - indexedFile: null, - // Index state indexTypes: [], selectedIndexType: '', indexName: '', - indexRequestInFlight: false, - indexPatternRequestInFlight: false, hasIndexErrors: false, isIndexReady: false, // Progress-tracking state - showImportProgress: false, - currentIndexingStage: INDEXING_STAGE.INDEXING_STARTED, - indexDataResp: '', - indexPatternResp: '', + importStatus: '', + phase: PHASE.CONFIGURE, + importResp: undefined, + indexPatternResp: undefined, }; componentDidMount() { @@ -74,102 +50,36 @@ export class JsonUploadAndParse extends Component { componentWillUnmount() { this._isMounted = false; - } - - _resetFileAndIndexSettings = () => { - if (this.props.onFileRemove && this.state.fileRef) { - this.props.onFileRemove(this.state.fileRef); - } - this.setState({ - indexTypes: [], - selectedIndexType: '', - indexName: '', - indexedFile: null, - parsedFile: null, - fileRef: null, - }); - }; - - componentDidUpdate() { - this._updateIndexType(); - this._setIndexReady({ ...this.state, ...this.props }); - this._indexData({ ...this.state, ...this.props }); - if (this.props.isIndexingTriggered && !this.state.showImportProgress && this._isMounted) { - this.setState({ showImportProgress: true }); + if (this._geojsonImporter) { + this._geojsonImporter.destroy(); + this._geojsonImporter = null; } } - _updateIndexType() { - let nextIndexTypes = []; - if (this.state.parsedFile) { - nextIndexTypes = - this.state.parsedFile.geometryTypes.includes('Point') || - this.state.parsedFile.geometryTypes.includes('MultiPoint') - ? [ES_FIELD_TYPES.GEO_POINT, ES_FIELD_TYPES.GEO_SHAPE] - : [ES_FIELD_TYPES.GEO_SHAPE]; - } - if (!_.isEqual(nextIndexTypes, this.state.indexTypes)) { - this.setState({ indexTypes: nextIndexTypes }); - } - - if (!this.state.selectedIndexType && nextIndexTypes.length) { - // auto select index type - this.setState({ selectedIndexType: nextIndexTypes[0] }); - } else if ( - this.state.selectedIndexType && - !nextIndexTypes.includes(this.state.selectedIndexType) - ) { - // unselected indexType if selected type is not longer an option - this.setState({ selectedIndexType: null }); + componentDidUpdate() { + this._setIndexReady(); + if (this.props.isIndexingTriggered && this.state.phase === PHASE.CONFIGURE) { + this._import(); } } - _setIndexReady = ({ - parsedFile, - selectedIndexType, - indexName, - hasIndexErrors, - indexRequestInFlight, - onIndexReady, - }) => { + _setIndexReady = () => { const isIndexReady = - !!parsedFile && - !!selectedIndexType && - !!indexName && - !hasIndexErrors && - !indexRequestInFlight; + this._geojsonImporter !== undefined && + !!this.state.selectedIndexType && + !!this.state.indexName && + !this.state.hasIndexErrors && + this.state.phase === PHASE.CONFIGURE; if (isIndexReady !== this.state.isIndexReady) { this.setState({ isIndexReady }); - if (onIndexReady) { - onIndexReady(isIndexReady); - } + this.props.onIndexReady(isIndexReady); } }; - _indexData = async ({ - indexedFile, - parsedFile, - indexRequestInFlight, - indexName, - selectedIndexType, - isIndexingTriggered, - isIndexReady, - onIndexingComplete, - onIndexingError, - }) => { - // Check index ready - const filesAreEqual = _.isEqual(indexedFile, parsedFile); - if (!isIndexingTriggered || filesAreEqual || !isIndexReady || indexRequestInFlight) { - return; - } - this.setState({ - indexRequestInFlight: true, - currentIndexingStage: INDEXING_STAGE.WRITING_TO_INDEX, - }); - - this.geojsonImporter.setDocs(parsedFile.parsedGeojson, selectedIndexType); - - // initialize import + _import = async () => { + // + // create index + // const settings = { number_of_shards: 1, }; @@ -181,8 +91,16 @@ export class JsonUploadAndParse extends Component { }, }; const ingestPipeline = {}; - const initializeImportResp = await this.geojsonImporter.initializeImport( - indexName, + this.setState({ + importStatus: i18n.translate('xpack.fileUpload.jsonUploadAndParse.dataIndexingStarted', { + defaultMessage: 'Creating index: {indexName}', + values: { indexName: this.state.indexName }, + }), + phase: PHASE.IMPORT, + }); + this._geojsonImporter.setGeoFieldType(this.state.selectedIndexType); + const initializeImportResp = await this._geojsonImporter.initializeImport( + this.state.indexName, settings, mappings, ingestPipeline @@ -192,131 +110,180 @@ export class JsonUploadAndParse extends Component { } if (initializeImportResp.index === undefined || initializeImportResp.id === undefined) { this.setState({ - indexRequestInFlight: false, - currentIndexingStage: INDEXING_STAGE.INDEXING_ERROR, + phase: PHASE.COMPLETE, }); - this._resetFileAndIndexSettings(); - onIndexingError(); + this.props.onIndexingError(); return; } + // // import file - const importResp = await this.geojsonImporter.import( + // + this.setState({ + importStatus: getWritingToIndexMsg(0), + }); + const importResp = await this._geojsonImporter.import( initializeImportResp.id, - indexName, + this.state.indexName, initializeImportResp.pipelineId, - () => {} + (progress) => { + if (this._isMounted) { + this.setState({ + importStatus: getWritingToIndexMsg(progress), + }); + } + } ); if (!this._isMounted) { return; } + if (!importResp.success) { this.setState({ - indexDataResp: importResp, - indexRequestInFlight: false, - currentIndexingStage: INDEXING_STAGE.INDEXING_ERROR, + importResp, + importStatus: i18n.translate('xpack.fileUpload.jsonUploadAndParse.dataIndexingError', { + defaultMessage: 'Data indexing error', + }), + phase: PHASE.COMPLETE, }); - this._resetFileAndIndexSettings(); - onIndexingError(); + this.props.onIndexingError(); return; } - this.setState({ - indexDataResp: importResp, - indexedFile: parsedFile, - currentIndexingStage: INDEXING_STAGE.INDEXING_COMPLETE, - }); + // // create index pattern + // this.setState({ - indexPatternRequestInFlight: true, - currentIndexingStage: INDEXING_STAGE.CREATING_INDEX_PATTERN, + importResp, + importStatus: i18n.translate('xpack.fileUpload.jsonUploadAndParse.creatingIndexPattern', { + defaultMessage: 'Creating index pattern: {indexName}', + values: { indexName: this.state.indexName }, + }), }); let indexPattern; try { indexPattern = await getIndexPatternService().createAndSave( { - title: indexName, + title: this.state.indexName, }, true ); } catch (error) { if (this._isMounted) { this.setState({ - indexPatternRequestInFlight: false, - currentIndexingStage: INDEXING_STAGE.INDEX_PATTERN_ERROR, + importStatus: i18n.translate('xpack.fileUpload.jsonUploadAndParse.indexPatternError', { + defaultMessage: 'Index pattern error', + }), + phase: PHASE.COMPLETE, }); - this._resetFileAndIndexSettings(); - onIndexingError(); + this.props.onIndexingError(); } return; } if (!this._isMounted) { return; } + + // + // Successful import + // this.setState({ indexPatternResp: { success: true, id: indexPattern.id, fields: indexPattern.fields, }, - indexPatternRequestInFlight: false, + phase: PHASE.COMPLETE, + importStatus: '', }); - this.setState({ - currentIndexingStage: INDEXING_STAGE.INDEX_PATTERN_COMPLETE, - }); - this._resetFileAndIndexSettings(); - onIndexingComplete({ + this.props.onIndexingComplete({ indexDataResp: importResp, indexPattern, }); }; - render() { - const { - currentIndexingStage, - indexDataResp, - indexPatternResp, - fileRef, + _onFileSelect = ({ features, hasPoints, hasShapes, importer, indexName, previewCoverage }) => { + this._geojsonImporter = importer; + + const geoFieldTypes = hasPoints + ? [ES_FIELD_TYPES.GEO_POINT, ES_FIELD_TYPES.GEO_SHAPE] + : [ES_FIELD_TYPES.GEO_SHAPE]; + + const newState = { + indexTypes: geoFieldTypes, + indexName, + }; + if (!this.state.selectedIndexType) { + // auto select index type + newState.selectedIndexType = + hasPoints && !hasShapes ? ES_FIELD_TYPES.GEO_POINT : ES_FIELD_TYPES.GEO_SHAPE; + } else if ( + this.state.selectedIndexType && + !geoFieldTypes.includes(this.state.selectedIndexType) + ) { + // unselected indexType if selected type is not longer an option + newState.selectedIndexType = ''; + } + this.setState(newState); + + this.props.onFileUpload( + { + type: 'FeatureCollection', + features, + }, indexName, - indexTypes, - showImportProgress, - } = this.state; + previewCoverage + ); + }; + + _onFileClear = () => { + if (this._geojsonImporter) { + this._geojsonImporter.destroy(); + this._geojsonImporter = undefined; + } + + this.props.onFileRemove(); + + this.setState({ + indexTypes: [], + selectedIndexType: '', + indexName: '', + }); + }; + + render() { + if (this.state.phase === PHASE.IMPORT) { + return ( + + + +

{this.state.importStatus}

+
+
+ ); + } + + if (this.state.phase === PHASE.COMPLETE) { + return ( + + ); + } return ( - {showImportProgress ? ( - - ) : ( - - this.setState({ fileRef })} - setParsedFile={(parsedFile, indexName) => { - this.setState({ parsedFile, indexName }); - this.props.onFileUpload(parsedFile.parsedGeojson, indexName); - }} - resetFileAndIndexSettings={this._resetFileAndIndexSettings} - geojsonImporter={this.geojsonImporter} - /> - this.setState({ indexName })} - indexTypes={indexTypes} - setSelectedIndexType={(selectedIndexType) => this.setState({ selectedIndexType })} - setHasIndexErrors={(hasIndexErrors) => this.setState({ hasIndexErrors })} - /> - - )} + + this.setState({ indexName })} + indexTypes={this.state.indexTypes} + selectedIndexType={this.state.selectedIndexType} + setSelectedIndexType={(selectedIndexType) => this.setState({ selectedIndexType })} + setHasIndexErrors={(hasIndexErrors) => this.setState({ hasIndexErrors })} + /> ); } diff --git a/x-pack/plugins/file_upload/public/get_max_bytes.ts b/x-pack/plugins/file_upload/public/get_max_bytes.ts new file mode 100644 index 0000000000000..2e002e65248c9 --- /dev/null +++ b/x-pack/plugins/file_upload/public/get_max_bytes.ts @@ -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. + */ + +// @ts-ignore +import numeral from '@elastic/numeral'; +import { + MAX_FILE_SIZE, + MAX_FILE_SIZE_BYTES, + ABSOLUTE_MAX_FILE_SIZE_BYTES, + FILE_SIZE_DISPLAY_FORMAT, + UI_SETTING_MAX_FILE_SIZE, +} from '../common'; +import { getUiSettings } from './kibana_services'; + +export function getMaxBytes() { + const maxFileSize = getUiSettings().get(UI_SETTING_MAX_FILE_SIZE, MAX_FILE_SIZE); + // @ts-ignore + const maxBytes = numeral(maxFileSize.toUpperCase()).value(); + if (maxBytes < MAX_FILE_SIZE_BYTES) { + return MAX_FILE_SIZE_BYTES; + } + return maxBytes <= ABSOLUTE_MAX_FILE_SIZE_BYTES ? maxBytes : ABSOLUTE_MAX_FILE_SIZE_BYTES; +} + +export function getMaxBytesFormatted() { + return numeral(getMaxBytes()).format(FILE_SIZE_DISPLAY_FORMAT); +} diff --git a/x-pack/plugins/file_upload/public/importer/geojson_importer/geojson_importer.test.js b/x-pack/plugins/file_upload/public/importer/geojson_importer/geojson_importer.test.js index e348686dc060a..98f14f3e58166 100644 --- a/x-pack/plugins/file_upload/public/importer/geojson_importer/geojson_importer.test.js +++ b/x-pack/plugins/file_upload/public/importer/geojson_importer/geojson_importer.test.js @@ -5,7 +5,7 @@ * 2.0. */ -import { GeoJsonImporter } from './geojson_importer'; +import { GeoJsonImporter, toEsDocs } from './geojson_importer'; import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/public'; import '@loaders.gl/polyfills'; @@ -25,9 +25,7 @@ const FEATURE_COLLECTION = { ], }; -describe('readFile', () => { - const setFileProgress = jest.fn((a) => a); - +describe('previewFile', () => { const FILE_WITH_FEATURE_COLLECTION = new File( [JSON.stringify(FEATURE_COLLECTION)], 'testfile.json', @@ -39,38 +37,26 @@ describe('readFile', () => { jest.restoreAllMocks(); }); - test('should throw error if no file provided', async () => { - const importer = new GeoJsonImporter(); - await importer - .readFile(null, setFileProgress, () => { - return true; - }) - .catch((e) => { - expect(e.message).toMatch('Error, no file provided'); - }); - }); - - test('should abort if file parse is cancelled', async () => { - const importer = new GeoJsonImporter(); - - const results = await importer.readFile(FILE_WITH_FEATURE_COLLECTION, setFileProgress, () => { - return false; + test('should stop reading when importer is destroyed', async () => { + const importer = new GeoJsonImporter(FILE_WITH_FEATURE_COLLECTION); + importer.destroy(); + const results = await importer.previewFile(); + expect(results).toEqual({ + features: [], + previewCoverage: 0, + hasPoints: false, + hasShapes: false, }); - - expect(results).toBeNull(); }); test('should read features from feature collection', async () => { - const importer = new GeoJsonImporter(); - const results = await importer.readFile(FILE_WITH_FEATURE_COLLECTION, setFileProgress, () => { - return true; - }); - - expect(setFileProgress).toHaveBeenCalled(); + const importer = new GeoJsonImporter(FILE_WITH_FEATURE_COLLECTION); + const results = await importer.previewFile(); expect(results).toEqual({ - errors: [], - geometryTypes: ['Point'], - parsedGeojson: FEATURE_COLLECTION, + previewCoverage: 100, + hasPoints: true, + hasShapes: false, + features: FEATURE_COLLECTION.features, }); }); @@ -99,20 +85,14 @@ describe('readFile', () => { { type: 'text/json' } ); - const importer = new GeoJsonImporter(); - const results = await importer.readFile( - fileWithFeaturesWithoutGeometry, - setFileProgress, - () => { - return true; - } - ); + const importer = new GeoJsonImporter(fileWithFeaturesWithoutGeometry); + const results = await importer.previewFile(); - expect(setFileProgress).toHaveBeenCalled(); expect(results).toEqual({ - errors: ['2 features without geometry omitted'], - geometryTypes: ['Point'], - parsedGeojson: FEATURE_COLLECTION, + previewCoverage: 100, + hasPoints: true, + hasShapes: false, + features: FEATURE_COLLECTION.features, }); }); @@ -134,20 +114,18 @@ describe('readFile', () => { { type: 'text/json' } ); - const importer = new GeoJsonImporter(); - const results = await importer.readFile(fileWithUnwrapedFeature, setFileProgress, () => { - return true; - }); + const importer = new GeoJsonImporter(fileWithUnwrapedFeature); + const results = await importer.previewFile(); - expect(setFileProgress).toHaveBeenCalled(); expect(results).toEqual({ - errors: [], - geometryTypes: ['Point'], - parsedGeojson: FEATURE_COLLECTION, + previewCoverage: 100, + hasPoints: true, + hasShapes: false, + features: FEATURE_COLLECTION.features, }); }); - test('should throw if no features', async () => { + test('should return empty feature collection if no features', async () => { const fileWithNoFeatures = new File( [ JSON.stringify({ @@ -159,17 +137,18 @@ describe('readFile', () => { { type: 'text/json' } ); - const importer = new GeoJsonImporter(); - await importer - .readFile(fileWithNoFeatures, setFileProgress, () => { - return true; - }) - .catch((e) => { - expect(e.message).toMatch('Error, no features detected'); - }); + const importer = new GeoJsonImporter(fileWithNoFeatures); + const results = await importer.previewFile(); + + expect(results).toEqual({ + previewCoverage: 100, + hasPoints: false, + hasShapes: false, + features: [], + }); }); - test('should throw if no features with geometry', async () => { + test('should return empty feature collection if no features with geometry', async () => { const fileWithFeaturesWithNoGeometry = new File( [ JSON.stringify({ @@ -186,22 +165,22 @@ describe('readFile', () => { { type: 'text/json' } ); - const importer = new GeoJsonImporter(); - await importer - .readFile(fileWithFeaturesWithNoGeometry, setFileProgress, () => { - return true; - }) - .catch((e) => { - expect(e.message).toMatch('Error, no features detected'); - }); + const importer = new GeoJsonImporter(fileWithFeaturesWithNoGeometry); + const results = await importer.previewFile(); + + expect(results).toEqual({ + previewCoverage: 100, + hasPoints: false, + hasShapes: false, + features: [], + }); }); }); -describe('setDocs', () => { +describe('toEsDocs', () => { test('should convert features to geo_point ES documents', () => { - const importer = new GeoJsonImporter(); - importer.setDocs(FEATURE_COLLECTION, ES_FIELD_TYPES.GEO_POINT); - expect(importer.getDocs()).toEqual([ + const esDocs = toEsDocs(FEATURE_COLLECTION.features, ES_FIELD_TYPES.GEO_POINT); + expect(esDocs).toEqual([ { coordinates: [-112.0372, 46.608058], population: 200, @@ -210,9 +189,8 @@ describe('setDocs', () => { }); test('should convert features to geo_shape ES documents', () => { - const importer = new GeoJsonImporter(); - importer.setDocs(FEATURE_COLLECTION, ES_FIELD_TYPES.GEO_SHAPE); - expect(importer.getDocs()).toEqual([ + const esDocs = toEsDocs(FEATURE_COLLECTION.features, ES_FIELD_TYPES.GEO_SHAPE); + expect(esDocs).toEqual([ { coordinates: { type: 'point', diff --git a/x-pack/plugins/file_upload/public/importer/geojson_importer/geojson_importer.ts b/x-pack/plugins/file_upload/public/importer/geojson_importer/geojson_importer.ts index 189084e9180da..3294655916b5b 100644 --- a/x-pack/plugins/file_upload/public/importer/geojson_importer/geojson_importer.ts +++ b/x-pack/plugins/file_upload/public/importer/geojson_importer/geojson_importer.ts @@ -7,7 +7,6 @@ import { Feature, - FeatureCollection, Point, MultiPoint, LineString, @@ -18,161 +17,274 @@ import { import { i18n } from '@kbn/i18n'; // @ts-expect-error import { JSONLoader, loadInBatches } from './loaders'; -import { CreateDocsResponse } from '../types'; -import { Importer } from '../importer'; +import { CreateDocsResponse, ImportResults } from '../types'; +import { callImportRoute, Importer, IMPORT_RETRIES } from '../importer'; import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/public'; // @ts-expect-error import { geoJsonCleanAndValidate } from './geojson_clean_and_validate'; +import { ImportFailure, ImportResponse, MB } from '../../../common'; + +const IMPORT_CHUNK_SIZE_MB = 10 * MB; +export const GEOJSON_FILE_TYPES = ['.json', '.geojson']; + +export interface GeoJsonPreview { + features: Feature[]; + hasPoints: boolean; + hasShapes: boolean; + previewCoverage: number; +} export class GeoJsonImporter extends Importer { - constructor() { + private _file: File; + private _isActive = true; + private _iterator?: Iterator; + private _hasNext = true; + private _features: Feature[] = []; + private _totalBytesProcessed = 0; + private _unimportedBytesProcessed = 0; + private _totalFeatures = 0; + private _geometryTypesMap = new Map(); + private _invalidFeatures: ImportFailure[] = []; + private _prevBatchLastFeature?: Feature; + private _geoFieldType: ES_FIELD_TYPES.GEO_POINT | ES_FIELD_TYPES.GEO_SHAPE = + ES_FIELD_TYPES.GEO_SHAPE; + + constructor(file: File) { super(); + + this._file = file; } - public read(data: ArrayBuffer): { success: boolean } { - throw new Error('read(data: ArrayBuffer) not supported, use readFile instead.'); + public destroy() { + this._isActive = false; } - protected _createDocs(text: string): CreateDocsResponse { - throw new Error('_createDocs not implemented.'); + public async previewFile(rowLimit?: number, sizeLimit?: number): Promise { + await this._readUntil(rowLimit, sizeLimit); + return { + features: [...this._features], + previewCoverage: this._hasNext + ? Math.round((this._unimportedBytesProcessed / this._file.size) * 100) + : 100, + hasPoints: this._geometryTypesMap.has('Point') || this._geometryTypesMap.has('MultiPoint'), + hasShapes: + this._geometryTypesMap.has('LineString') || + this._geometryTypesMap.has('MultiLineString') || + this._geometryTypesMap.has('Polygon') || + this._geometryTypesMap.has('MultiPolygon'), + }; } - public getDocs() { - return this._docArray; + public setGeoFieldType(geoFieldType: ES_FIELD_TYPES.GEO_POINT | ES_FIELD_TYPES.GEO_SHAPE) { + this._geoFieldType = geoFieldType; } - public setDocs( - featureCollection: FeatureCollection, - geoFieldType: ES_FIELD_TYPES.GEO_POINT | ES_FIELD_TYPES.GEO_SHAPE - ) { - this._docArray = []; - for (let i = 0; i < featureCollection.features.length; i++) { - const feature = featureCollection.features[i]; - const geometry = feature.geometry as - | Point - | MultiPoint - | LineString - | MultiLineString - | Polygon - | MultiPolygon; - const coordinates = - geoFieldType === ES_FIELD_TYPES.GEO_SHAPE - ? { - type: geometry.type.toLowerCase(), - coordinates: geometry.coordinates, - } - : geometry.coordinates; - const properties = feature.properties ? feature.properties : {}; - this._docArray.push({ - coordinates, - ...properties, - }); + public async import( + id: string, + index: string, + pipelineId: string, + setImportProgress: (progress: number) => void + ): Promise { + if (!id || !index) { + return { + success: false, + error: i18n.translate('xpack.fileUpload.import.noIdOrIndexSuppliedErrorMessage', { + defaultMessage: 'no ID or index supplied', + }), + }; } + + let success = true; + const failures: ImportFailure[] = [...this._invalidFeatures]; + let error; + + while ((this._features.length > 0 || this._hasNext) && this._isActive) { + await this._readUntil(undefined, IMPORT_CHUNK_SIZE_MB); + if (!this._isActive) { + return { + success: false, + failures, + docCount: this._totalFeatures, + }; + } + + let retries = IMPORT_RETRIES; + let resp: ImportResponse = { + success: false, + failures: [], + docCount: 0, + id: '', + index: '', + pipelineId: '', + }; + const data = toEsDocs(this._features, this._geoFieldType); + const progress = Math.round((this._totalBytesProcessed / this._file.size) * 100); + this._features = []; + this._unimportedBytesProcessed = 0; + + while (resp.success === false && retries > 0) { + try { + resp = await callImportRoute({ + id, + index, + data, + settings: {}, + mappings: {}, + ingestPipeline: { + id: pipelineId, + }, + }); + + if (retries < IMPORT_RETRIES) { + // eslint-disable-next-line no-console + console.log(`Retrying import ${IMPORT_RETRIES - retries}`); + } + + retries--; + } catch (err) { + resp.success = false; + resp.error = err; + retries = 0; + } + } + + failures.push(...resp.failures); + + if (!resp.success) { + success = false; + error = resp.error; + break; + } + + setImportProgress(progress); + } + + const result: ImportResults = { + success, + failures, + docCount: this._totalFeatures, + }; + + if (success) { + setImportProgress(100); + } else { + result.error = error; + } + + return result; } - public async readFile( - file: File, - setFileProgress: ({ - featuresProcessed, - bytesProcessed, - totalBytes, - }: { - featuresProcessed: number; - bytesProcessed: number; - totalBytes: number; - }) => void, - isFileParseActive: () => boolean - ): Promise<{ - errors: string[]; - geometryTypes: string[]; - parsedGeojson: FeatureCollection; - } | null> { - if (!file) { - throw new Error( - i18n.translate('xpack.fileUpload.fileParser.noFileProvided', { - defaultMessage: 'Error, no file provided', - }) - ); + private async _readUntil(rowLimit?: number, sizeLimit?: number) { + while ( + this._isActive && + this._hasNext && + (rowLimit === undefined || this._features.length < rowLimit) && + (sizeLimit === undefined || this._unimportedBytesProcessed < sizeLimit) + ) { + await this._next(); } + } - return new Promise(async (resolve, reject) => { - const batches = await loadInBatches(file, JSONLoader, { + private async _next() { + if (this._iterator === undefined) { + this._iterator = await loadInBatches(this._file, JSONLoader, { json: { jsonpaths: ['$.features'], _rootObjectBatches: true, }, }); + } - const rawFeatures: unknown[] = []; - for await (const batch of batches) { - if (!isFileParseActive()) { - break; - } + if (!this._isActive || !this._iterator) { + return; + } - if (batch.batchType === 'root-object-batch-complete') { - // Handle single feature geoJson - if (rawFeatures.length === 0) { - rawFeatures.push(batch.container); - } - } else { - rawFeatures.push(...batch.data); - } + const { value: batch, done } = await this._iterator.next(); - setFileProgress({ - featuresProcessed: rawFeatures.length, - bytesProcessed: batch.bytesUsed, - totalBytes: file.size, - }); - } + if (!this._isActive || done) { + this._hasNext = false; + return; + } + + if ('bytesUsed' in batch) { + const bytesRead = batch.bytesUsed - this._totalBytesProcessed; + this._unimportedBytesProcessed += bytesRead; + this._totalBytesProcessed = batch.bytesUsed; + } - if (!isFileParseActive()) { - resolve(null); - return; + const rawFeatures: unknown[] = this._prevBatchLastFeature ? [this._prevBatchLastFeature] : []; + this._prevBatchLastFeature = undefined; + const isLastBatch = batch.batchType === 'root-object-batch-complete'; + if (isLastBatch) { + // Handle single feature geoJson + if (this._totalFeatures === 0) { + rawFeatures.push(batch.container); } + } else { + rawFeatures.push(...batch.data); + } - if (rawFeatures.length === 0) { - reject( - new Error( - i18n.translate('xpack.fileUpload.fileParser.noFeaturesDetected', { - defaultMessage: 'Error, no features detected', - }) - ) - ); - return; + for (let i = 0; i < rawFeatures.length; i++) { + const rawFeature = rawFeatures[i] as Feature; + if (!isLastBatch && i === rawFeatures.length - 1) { + // Do not process last feature until next batch is read, features on batch boundary may be incomplete. + this._prevBatchLastFeature = rawFeature; + continue; } - const features: Feature[] = []; - const geometryTypesMap = new Map(); - let invalidCount = 0; - for (let i = 0; i < rawFeatures.length; i++) { - const rawFeature = rawFeatures[i] as Feature; - if (!rawFeature.geometry || !rawFeature.geometry.type) { - invalidCount++; - } else { - if (!geometryTypesMap.has(rawFeature.geometry.type)) { - geometryTypesMap.set(rawFeature.geometry.type, true); - } - features.push(geoJsonCleanAndValidate(rawFeature)); + this._totalFeatures++; + if (!rawFeature.geometry || !rawFeature.geometry.type) { + this._invalidFeatures.push({ + item: this._totalFeatures, + reason: i18n.translate('xpack.fileUpload.geojsonImporter.noGeometry', { + defaultMessage: 'Feature does not contain required field "geometry"', + }), + doc: rawFeature, + }); + } else { + if (!this._geometryTypesMap.has(rawFeature.geometry.type)) { + this._geometryTypesMap.set(rawFeature.geometry.type, true); } + this._features.push(geoJsonCleanAndValidate(rawFeature)); } + } + } - const errors: string[] = []; - if (invalidCount > 0) { - errors.push( - i18n.translate('xpack.fileUpload.fileParser.featuresOmitted', { - defaultMessage: '{invalidCount} features without geometry omitted', - values: { invalidCount }, - }) - ); - } - resolve({ - errors, - geometryTypes: Array.from(geometryTypesMap.keys()), - parsedGeojson: { - type: 'FeatureCollection', - features, - }, - }); + public read(data: ArrayBuffer): { success: boolean } { + throw new Error('read(data: ArrayBuffer) not supported, use readFile instead.'); + } + + protected _createDocs(text: string): CreateDocsResponse { + throw new Error('_createDocs not implemented.'); + } +} + +export function toEsDocs( + features: Feature[], + geoFieldType: ES_FIELD_TYPES.GEO_POINT | ES_FIELD_TYPES.GEO_SHAPE +) { + const esDocs = []; + for (let i = 0; i < features.length; i++) { + const feature = features[i]; + const geometry = feature.geometry as + | Point + | MultiPoint + | LineString + | MultiLineString + | Polygon + | MultiPolygon; + const coordinates = + geoFieldType === ES_FIELD_TYPES.GEO_SHAPE + ? { + type: geometry.type.toLowerCase(), + coordinates: geometry.coordinates, + } + : geometry.coordinates; + const properties = feature.properties ? feature.properties : {}; + esDocs.push({ + coordinates, + ...properties, }); } + return esDocs; } diff --git a/x-pack/plugins/file_upload/public/importer/geojson_importer/index.ts b/x-pack/plugins/file_upload/public/importer/geojson_importer/index.ts index 9ffb84e603161..b5f6845e28324 100644 --- a/x-pack/plugins/file_upload/public/importer/geojson_importer/index.ts +++ b/x-pack/plugins/file_upload/public/importer/geojson_importer/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { GeoJsonImporter } from './geojson_importer'; +export { GeoJsonImporter, GeoJsonPreview, GEOJSON_FILE_TYPES } from './geojson_importer'; diff --git a/x-pack/plugins/file_upload/public/importer/importer.ts b/x-pack/plugins/file_upload/public/importer/importer.ts index 8bdb465bd69cf..2689f3badc397 100644 --- a/x-pack/plugins/file_upload/public/importer/importer.ts +++ b/x-pack/plugins/file_upload/public/importer/importer.ts @@ -22,7 +22,7 @@ import { CreateDocsResponse, IImporter, ImportResults } from './types'; const CHUNK_SIZE = 5000; const MAX_CHUNK_CHAR_COUNT = 1000000; -const IMPORT_RETRIES = 5; +export const IMPORT_RETRIES = 5; const STRING_CHUNKS_MB = 100; export abstract class Importer implements IImporter { @@ -232,7 +232,7 @@ function createDocumentChunks(docArray: ImportDoc[]) { return chunks; } -function callImportRoute({ +export function callImportRoute({ id, index, data, diff --git a/x-pack/plugins/file_upload/public/importer/index.ts b/x-pack/plugins/file_upload/public/importer/index.ts index face822f91efb..22465ae2df8a7 100644 --- a/x-pack/plugins/file_upload/public/importer/index.ts +++ b/x-pack/plugins/file_upload/public/importer/index.ts @@ -6,4 +6,5 @@ */ export { importerFactory } from './importer_factory'; +export { validateFile } from './validate_file'; export * from './types'; diff --git a/x-pack/plugins/file_upload/public/importer/validate_file.ts b/x-pack/plugins/file_upload/public/importer/validate_file.ts new file mode 100644 index 0000000000000..4c7fe704d8afa --- /dev/null +++ b/x-pack/plugins/file_upload/public/importer/validate_file.ts @@ -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 { i18n } from '@kbn/i18n'; +import { getMaxBytes, getMaxBytesFormatted } from '../get_max_bytes'; + +export function validateFile(file: File, types: string[]) { + if (file.size > getMaxBytes()) { + throw new Error( + i18n.translate('xpack.fileUpload.fileSizeError', { + defaultMessage: 'File size {fileSize} exceeds maximum file size of {maxFileSize}', + values: { + fileSize: bytesToSize(file.size), + maxFileSize: getMaxBytesFormatted(), + }, + }) + ); + } + + if (!file.name) { + throw new Error( + i18n.translate('xpack.fileUpload.noFileNameError', { + defaultMessage: 'File name not provided', + }) + ); + } + + const nameSplit = file.name.split('.'); + const fileType = nameSplit.pop(); + if (!types.includes(`.${fileType}`)) { + throw new Error( + i18n.translate('xpack.fileUpload.fileTypeError', { + defaultMessage: 'File is not one of acceptable types: {types}', + values: { + types: types.join(', '), + }, + }) + ); + } +} + +function bytesToSize(bytes: number) { + if (bytes === 0) return 'n/a'; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + const i = Math.round(Math.floor(Math.log(bytes) / Math.log(1024))); + if (i === 0) return `${bytes} ${sizes[i]})`; + return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}`; +} diff --git a/x-pack/plugins/file_upload/public/kibana_services.ts b/x-pack/plugins/file_upload/public/kibana_services.ts index c007c5c2273a8..a604136ca34e4 100644 --- a/x-pack/plugins/file_upload/public/kibana_services.ts +++ b/x-pack/plugins/file_upload/public/kibana_services.ts @@ -18,3 +18,4 @@ export function setStartServices(core: CoreStart, plugins: FileUploadStartDepend export const getIndexPatternService = () => pluginsStart.data.indexPatterns; export const getHttp = () => coreStart.http; export const getSavedObjectsClient = () => coreStart.savedObjects.client; +export const getUiSettings = () => coreStart.uiSettings; diff --git a/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts b/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts index 9cfc0896f5c2d..a61faa5bef0c4 100644 --- a/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts +++ b/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts @@ -12,7 +12,7 @@ import { IImporter, ImportFactoryOptions, ImportResults } from '../importer'; export interface FileUploadComponentProps { isIndexingTriggered: boolean; - onFileUpload: (geojsonFile: FeatureCollection, name: string) => void; + onFileUpload: (geojsonFile: FeatureCollection, name: string, previewCoverage: number) => void; onFileRemove: () => void; onIndexReady: (indexReady: boolean) => void; onIndexingComplete: (results: { diff --git a/x-pack/plugins/file_upload/public/plugin.ts b/x-pack/plugins/file_upload/public/plugin.ts index 5d3918193d48a..af3dd65b788a7 100644 --- a/x-pack/plugins/file_upload/public/plugin.ts +++ b/x-pack/plugins/file_upload/public/plugin.ts @@ -9,6 +9,7 @@ import { CoreStart, Plugin } from '../../../../src/core/public'; import { FileUploadStartApi, getFileUploadComponent, importerFactory } from './api'; import { setStartServices } from './kibana_services'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; +import { getMaxBytes, getMaxBytesFormatted } from './get_max_bytes'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface FileUploadSetupDependencies {} @@ -34,6 +35,8 @@ export class FileUploadPlugin return { getFileUploadComponent, importerFactory, + getMaxBytes, + getMaxBytesFormatted, }; } } diff --git a/x-pack/plugins/file_upload/server/plugin.ts b/x-pack/plugins/file_upload/server/plugin.ts index 74a18633d8a26..94b4f0368d562 100644 --- a/x-pack/plugins/file_upload/server/plugin.ts +++ b/x-pack/plugins/file_upload/server/plugin.ts @@ -5,10 +5,13 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'src/core/server'; +import { schema } from '@kbn/config-schema'; import { fileUploadRoutes } from './routes'; import { initFileUploadTelemetry } from './telemetry'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; +import { UI_SETTING_MAX_FILE_SIZE, MAX_FILE_SIZE } from '../common'; interface SetupDeps { usageCollection: UsageCollectionSetup; @@ -18,6 +21,26 @@ export class FileUploadPlugin implements Plugin { async setup(coreSetup: CoreSetup, plugins: SetupDeps) { fileUploadRoutes(coreSetup.http.createRouter()); + coreSetup.uiSettings.register({ + [UI_SETTING_MAX_FILE_SIZE]: { + name: i18n.translate('xpack.fileUpload.maxFileSizeUiSetting.name', { + defaultMessage: 'Maximum file upload size', + }), + value: MAX_FILE_SIZE, + description: i18n.translate('xpack.fileUpload.maxFileSizeUiSetting.description', { + defaultMessage: + 'Sets the file size limit when importing files. The highest supported value for this setting is 1GB.', + }), + schema: schema.string(), + validation: { + regexString: '\\d+[mMgG][bB]', + message: i18n.translate('xpack.fileUpload.maxFileSizeUiSetting.error', { + defaultMessage: 'Should be a valid data size. e.g. 200MB, 1GB', + }), + }, + }, + }); + initFileUploadTelemetry(coreSetup, plugins.usageCollection); } diff --git a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts index c825f148cd0ce..7b757aa9cf10b 100644 --- a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts @@ -174,6 +174,8 @@ export type InlineFieldDescriptor = { export type GeojsonFileSourceDescriptor = { __fields?: InlineFieldDescriptor[]; __featureCollection: FeatureCollection; + areResultsTrimmed: boolean; + tooltipContent: string | null; name: string; type: string; }; diff --git a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx index 138ed7a8cd0b1..30e3317a3a3cf 100644 --- a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; import React, { Component } from 'react'; import { FeatureCollection } from 'geojson'; import { EuiPanel } from '@elastic/eui'; @@ -70,7 +71,7 @@ export class ClientFileCreateSourceEditor extends Component { + _onFileUpload = (geojsonFile: FeatureCollection, name: string, previewCoverage: number) => { if (!this._isMounted) { return; } @@ -80,8 +81,19 @@ export class ClientFileCreateSourceEditor extends Component = ({ coreStart, deps, appMountParams }) => { embeddable: deps.embeddable, maps: deps.maps, triggersActionsUi: deps.triggersActionsUi, + fileUpload: deps.fileUpload, ...coreStart, }; diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts b/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts index 99d4b77547d9d..3c86bb520600f 100644 --- a/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts +++ b/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts @@ -18,6 +18,7 @@ import { MlServicesContext } from '../../app'; import { IStorageWrapper } from '../../../../../../../src/plugins/kibana_utils/public'; import type { EmbeddableStart } from '../../../../../../../src/plugins/embeddable/public'; import type { MapsStartApi } from '../../../../../maps/public'; +import type { FileUploadPluginStart } from '../../../../../file_upload/public'; import type { LensPublicStart } from '../../../../../lens/public'; import { TriggersAndActionsUIPublicPluginStart } from '../../../../../triggers_actions_ui/public'; @@ -30,6 +31,7 @@ interface StartPlugins { maps?: MapsStartApi; lens?: LensPublicStart; triggersActionsUi?: TriggersAndActionsUIPublicPluginStart; + fileUpload?: FileUploadPluginStart; } export type StartServices = CoreStart & StartPlugins & { diff --git a/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx b/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx index 79d17a7846b8c..fc03ff475e05a 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx @@ -25,9 +25,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { isFullLicense } from '../license'; import { useTimefilter, useMlKibana, useNavigateToPath } from '../contexts/kibana'; - import { NavigationMenu } from '../components/navigation_menu'; -import { getMaxBytesFormatted } from './file_based/components/utils'; import { HelpMenu } from '../components/help_menu'; function startTrialDescription() { @@ -58,8 +56,10 @@ export const DatavisualizerSelector: FC = () => { licenseManagement, http: { basePath }, docLinks, + fileUpload, }, } = useMlKibana(); + const helpLink = docLinks.links.ml.guide; const navigateToPath = useNavigateToPath(); @@ -68,7 +68,12 @@ export const DatavisualizerSelector: FC = () => { licenseManagement.enabled === true && isFullLicense() === false; - const maxFileSize = getMaxBytesFormatted(); + if (fileUpload === undefined) { + // eslint-disable-next-line no-console + console.error('File upload plugin not available'); + return null; + } + const maxFileSize = fileUpload.getMaxBytesFormatted(); return ( diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx index f20319e606681..2c441e42dea2f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx @@ -20,7 +20,8 @@ import { } from '@elastic/eui'; import { ExperimentalBadge } from '../experimental_badge'; -import { getMaxBytesFormatted } from '../utils'; + +import { useMlKibana } from '../../../../contexts/kibana'; export const WelcomeContent: FC = () => { const toolTipContent = i18n.translate( @@ -30,7 +31,16 @@ export const WelcomeContent: FC = () => { } ); - const maxFileSize = getMaxBytesFormatted(); + 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(); return ( diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js index 8b683a154519e..90c2b2bf18639 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js @@ -22,12 +22,12 @@ import { ExplanationFlyout } from '../explanation_flyout'; import { ImportView } from '../import_view'; import { DEFAULT_LINES_TO_SAMPLE, - getMaxBytes, readFile, createUrlOverrides, processResults, hasImportPermission, } from '../utils'; +import { getFileUpload } from '../../../../util/dependency_cache'; import { MODE } from './constants'; @@ -60,7 +60,7 @@ export class FileDataVisualizerView extends Component { this.originalSettings = { linesToSample: DEFAULT_LINES_TO_SAMPLE, }; - this.maxFileUploadBytes = getMaxBytes(); + this.maxFileUploadBytes = getFileUpload().getMaxBytes(); } async componentDidMount() { diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts index 41ea5582a7de5..209f2ef5ad174 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts @@ -10,7 +10,5 @@ export { hasImportPermission, processResults, readFile, - getMaxBytes, - getMaxBytesFormatted, DEFAULT_LINES_TO_SAMPLE, } from './utils'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts index 2c1b02b53354a..ebde771603fcf 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts @@ -6,19 +6,9 @@ */ import { isEqual } from 'lodash'; -// @ts-ignore -import numeral from '@elastic/numeral'; import { ml } from '../../../../services/ml_api_service'; import { AnalysisResult, InputOverrides } from '../../../../../../common/types/file_datavisualizer'; -import { - MB, - MAX_FILE_SIZE, - MAX_FILE_SIZE_BYTES, - ABSOLUTE_MAX_FILE_SIZE_BYTES, - FILE_SIZE_DISPLAY_FORMAT, -} from '../../../../../../../file_upload/public'; -import { getUiSettings } from '../../../../util/dependency_cache'; -import { FILE_DATA_VISUALIZER_MAX_FILE_SIZE } from '../../../../../../common/constants/settings'; +import { MB } from '../../../../../../../file_upload/public'; export const DEFAULT_LINES_TO_SAMPLE = 1000; const UPLOAD_SIZE_MB = 5; @@ -66,20 +56,6 @@ export function readFile(file: File) { }); } -export function getMaxBytes() { - const maxFileSize = getUiSettings().get(FILE_DATA_VISUALIZER_MAX_FILE_SIZE, MAX_FILE_SIZE); - // @ts-ignore - const maxBytes = numeral(maxFileSize.toUpperCase()).value(); - if (maxBytes < MAX_FILE_SIZE_BYTES) { - return MAX_FILE_SIZE_BYTES; - } - return maxBytes <= ABSOLUTE_MAX_FILE_SIZE_BYTES ? maxBytes : ABSOLUTE_MAX_FILE_SIZE_BYTES; -} - -export function getMaxBytesFormatted() { - return numeral(getMaxBytes()).format(FILE_SIZE_DISPLAY_FORMAT); -} - export function createUrlOverrides(overrides: InputOverrides, originalSettings: InputOverrides) { const formattedOverrides: InputOverrides = {}; for (const o in overrideDefaults) { diff --git a/x-pack/plugins/ml/server/lib/register_settings.ts b/x-pack/plugins/ml/server/lib/register_settings.ts index 5f9107bd29815..8f279114344e4 100644 --- a/x-pack/plugins/ml/server/lib/register_settings.ts +++ b/x-pack/plugins/ml/server/lib/register_settings.ts @@ -9,34 +9,14 @@ import { CoreSetup } from 'kibana/server'; import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { - FILE_DATA_VISUALIZER_MAX_FILE_SIZE, ANOMALY_DETECTION_DEFAULT_TIME_RANGE, ANOMALY_DETECTION_ENABLE_TIME_RANGE, DEFAULT_AD_RESULTS_TIME_FILTER, DEFAULT_ENABLE_AD_RESULTS_TIME_FILTER, } from '../../common/constants/settings'; -import { MAX_FILE_SIZE } from '../../../file_upload/common'; export function registerKibanaSettings(coreSetup: CoreSetup) { coreSetup.uiSettings.register({ - [FILE_DATA_VISUALIZER_MAX_FILE_SIZE]: { - name: i18n.translate('xpack.ml.maxFileSizeSettingsName', { - defaultMessage: 'File Data Visualizer maximum file upload size', - }), - value: MAX_FILE_SIZE, - description: i18n.translate('xpack.ml.maxFileSizeSettingsDescription', { - defaultMessage: - 'Sets the file size limit when importing data in the File Data Visualizer. The highest supported value for this setting is 1GB.', - }), - category: ['machineLearning'], - schema: schema.string(), - validation: { - regexString: '\\d+[mMgG][bB]', - message: i18n.translate('xpack.ml.maxFileSizeSettingsError', { - defaultMessage: 'Should be a valid data size. e.g. 200MB, 1GB', - }), - }, - }, [ANOMALY_DETECTION_ENABLE_TIME_RANGE]: { name: i18n.translate('xpack.ml.advancedSettings.enableAnomalyDetectionDefaultTimeRangeName', { defaultMessage: 'Enable time filter defaults for anomaly detection results', diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a2213b76e7271..74c45a309f197 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7429,8 +7429,6 @@ "xpack.features.savedObjectsManagementFeatureName": "保存されたオブジェクトの管理", "xpack.features.visualizeFeatureName": "可視化", "xpack.fileUpload.enterIndexName": "インデックス名を入力", - "xpack.fileUpload.fileParser.noFeaturesDetected": "エラー、機能が検出されませんでした", - "xpack.fileUpload.fileParser.noFileProvided": "エラー、ファイルが提供されていません", "xpack.fileUpload.httpService.fetchError": "フェッチ実行エラー:{error}", "xpack.fileUpload.httpService.noUrl": "URLが指定されていません", "xpack.fileUpload.indexNameReqField": "インデックス名、必須フィールド", @@ -7446,29 +7444,11 @@ "xpack.fileUpload.indexSettings.indexNameContainsIllegalCharactersErrorMessage": "インデックス名に許可されていない文字が含まれています", "xpack.fileUpload.indexSettings.indexNameGuidelines": "インデックス名ガイドライン", "xpack.fileUpload.jsonImport.indexingResponse": "インデックス応答", - "xpack.fileUpload.jsonImport.indexingStatus": "インデックスステータス", "xpack.fileUpload.jsonImport.indexMgmtLink": "インデックス管理", "xpack.fileUpload.jsonImport.indexModsMsg": "次を使用すると、その他のインデックス修正を行うことができます。\n", "xpack.fileUpload.jsonImport.indexPatternResponse": "インデックスパターン応答", - "xpack.fileUpload.jsonIndexFilePicker.acceptableFileSize": "ファイルサイズ {fileSize} は最大ファイルサイズの{maxFileSize} を超えています", - "xpack.fileUpload.jsonIndexFilePicker.acceptableTypesError": "ファイルは使用可能なタイプのいずれかではありません。{types}", - "xpack.fileUpload.jsonIndexFilePicker.coordinateSystemAccepted": "座標は EPSG:4326 座標参照系でなければなりません。", - "xpack.fileUpload.jsonIndexFilePicker.fileParseError": "ファイル解析エラーが検出されました:{error}", - "xpack.fileUpload.jsonIndexFilePicker.filePicker": "ファイルをアップロード", - "xpack.fileUpload.jsonIndexFilePicker.filePickerLabel": "アップロードするファイルを選択", - "xpack.fileUpload.jsonIndexFilePicker.fileProcessingError": "ファイル処理エラー: {errorMessage}", - "xpack.fileUpload.jsonIndexFilePicker.formatsAccepted": "許可されているフォーマット:{acceptedFileTypeStringMessage}", - "xpack.fileUpload.jsonIndexFilePicker.maxSize": "最大サイズ:{maxFileSize}", - "xpack.fileUpload.jsonIndexFilePicker.noFileNameError": "ファイル名が指定されていません", - "xpack.fileUpload.jsonIndexFilePicker.parsingFile": "{featuresProcessed} 機能が解析されました...", - "xpack.fileUpload.jsonIndexFilePicker.unableParseFile": "ファイルをパースできません。{error}", - "xpack.fileUpload.jsonUploadAndParse.creatingIndexPattern": "インデックスパターンを作成中です", "xpack.fileUpload.jsonUploadAndParse.dataIndexingError": "データインデックスエラー", - "xpack.fileUpload.jsonUploadAndParse.dataIndexingStarted": "データインデックスが開始しました", - "xpack.fileUpload.jsonUploadAndParse.indexingComplete": "インデックス完了", - "xpack.fileUpload.jsonUploadAndParse.indexPatternComplete": "インデックスパターンの完了", "xpack.fileUpload.jsonUploadAndParse.indexPatternError": "インデックスパターンエラー", - "xpack.fileUpload.jsonUploadAndParse.writingToIndex": "インデックスに書き込み中", "xpack.fleet.agentBulkActions.agentsSelected": "{count, plural, other {#個のエージェント}}が選択されました", "xpack.fleet.agentBulkActions.clearSelection": "選択した項目をクリア", "xpack.fleet.agentBulkActions.reassignPolicy": "新しいポリシーに割り当てる", @@ -13427,9 +13407,6 @@ "xpack.ml.management.syncSavedObjectsFlyout.sync.error": "一部のジョブを同期できません。", "xpack.ml.management.syncSavedObjectsFlyout.sync.success": "{successCount} {successCount, plural, other {件のジョブ}}が同期されました", "xpack.ml.management.syncSavedObjectsFlyout.syncButton": "同期", - "xpack.ml.maxFileSizeSettingsDescription": "ファイルデータビジュアライザーでデータをインポートするときのファイルサイズ上限を設定します。この設定でサポートされている最大値は1 GBです。", - "xpack.ml.maxFileSizeSettingsError": "200 MB、1 GBなどの有効なデータサイズにしてください。", - "xpack.ml.maxFileSizeSettingsName": "ファイルデータビジュアライザーの最大ファイルアップロードサイズ", "xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 他のすべてのリクエストはキャンセルされました。", "xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "フィールド値の例のサンプルをトークン化することができませんでした。{message}", "xpack.ml.models.jobService.categorization.messages.insufficientPrivileges": "権限が不十分なため、フィールド値の例のトークン化を実行できませんでした。そのため、フィールド値を確認し、カテゴリー分けジョブでの使用が適当かを確認することができません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index da5ec5d185c40..7233fa023a66d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7448,8 +7448,6 @@ "xpack.features.savedObjectsManagementFeatureName": "已保存对象管理", "xpack.features.visualizeFeatureName": "Visualize", "xpack.fileUpload.enterIndexName": "输入索引名称", - "xpack.fileUpload.fileParser.noFeaturesDetected": "错误,未检测到特征", - "xpack.fileUpload.fileParser.noFileProvided": "错误,未提供任何文件", "xpack.fileUpload.httpService.fetchError": "执行提取时出错:{error}", "xpack.fileUpload.httpService.noUrl": "未提供 URL", "xpack.fileUpload.indexNameReqField": "索引名称,必填字段", @@ -7465,29 +7463,11 @@ "xpack.fileUpload.indexSettings.indexNameContainsIllegalCharactersErrorMessage": "索引名称包含非法字符。", "xpack.fileUpload.indexSettings.indexNameGuidelines": "索引名称指引", "xpack.fileUpload.jsonImport.indexingResponse": "索引响应", - "xpack.fileUpload.jsonImport.indexingStatus": "索引状态", "xpack.fileUpload.jsonImport.indexMgmtLink": "索引管理", "xpack.fileUpload.jsonImport.indexModsMsg": "要进一步做索引修改,可以使用\n", "xpack.fileUpload.jsonImport.indexPatternResponse": "索引模式响应", - "xpack.fileUpload.jsonIndexFilePicker.acceptableFileSize": "文件大小 {fileSize} 超过最大文件大小 {maxFileSize}", - "xpack.fileUpload.jsonIndexFilePicker.acceptableTypesError": "文件不是可接受类型之一:{types}", - "xpack.fileUpload.jsonIndexFilePicker.coordinateSystemAccepted": "坐标必须在 EPSG:4326 坐标参考系中。", - "xpack.fileUpload.jsonIndexFilePicker.fileParseError": "检测到文件解析错误:{error}", - "xpack.fileUpload.jsonIndexFilePicker.filePicker": "上传文件", - "xpack.fileUpload.jsonIndexFilePicker.filePickerLabel": "选择文件进行上传", - "xpack.fileUpload.jsonIndexFilePicker.fileProcessingError": "文件处理错误:{errorMessage}", - "xpack.fileUpload.jsonIndexFilePicker.formatsAccepted": "接受的格式:{acceptedFileTypeStringMessage}", - "xpack.fileUpload.jsonIndexFilePicker.maxSize": "最大大小:{maxFileSize}", - "xpack.fileUpload.jsonIndexFilePicker.noFileNameError": "未提供任何文件名称", - "xpack.fileUpload.jsonIndexFilePicker.parsingFile": "已处理 {featuresProcessed} 个特征......", - "xpack.fileUpload.jsonIndexFilePicker.unableParseFile": "无法解析文件:{error}", - "xpack.fileUpload.jsonUploadAndParse.creatingIndexPattern": "正在创建索引模式", "xpack.fileUpload.jsonUploadAndParse.dataIndexingError": "数据索引错误", - "xpack.fileUpload.jsonUploadAndParse.dataIndexingStarted": "数据索引已启动", - "xpack.fileUpload.jsonUploadAndParse.indexingComplete": "索引完成", - "xpack.fileUpload.jsonUploadAndParse.indexPatternComplete": "索引模式完成", "xpack.fileUpload.jsonUploadAndParse.indexPatternError": "索引模式错误", - "xpack.fileUpload.jsonUploadAndParse.writingToIndex": "正在写入索引", "xpack.fleet.agentBulkActions.agentsSelected": "已选择 {count, plural, other {# 个代理}}", "xpack.fleet.agentBulkActions.clearSelection": "清除所选内容", "xpack.fleet.agentBulkActions.reassignPolicy": "分配到新策略", @@ -13459,9 +13439,6 @@ "xpack.ml.management.syncSavedObjectsFlyout.sync.error": "一些作业无法同步。", "xpack.ml.management.syncSavedObjectsFlyout.sync.success": "{successCount} 个{successCount, plural, other {作业}}已同步", "xpack.ml.management.syncSavedObjectsFlyout.syncButton": "同步", - "xpack.ml.maxFileSizeSettingsDescription": "设置在文件数据可视化工具中导入数据时的文件大小限制。此设置支持的最高值为 1GB。", - "xpack.ml.maxFileSizeSettingsError": "应为有效的数据大小。如 200MB、1GB", - "xpack.ml.maxFileSizeSettingsName": "文件数据可视化工具最大文件上传大小", "xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 所有其他请求已取消。", "xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "无法对示例字段值样本进行分词。{message}", "xpack.ml.models.jobService.categorization.messages.insufficientPrivileges": "由于权限不足,无法对字段值示例执行分词。因此,无法检查字段值是否适合用于归类作业。", diff --git a/x-pack/test/functional/apps/maps/import_geojson/file_indexing_panel.js b/x-pack/test/functional/apps/maps/import_geojson/file_indexing_panel.js index 0ce9b7022b38d..a5b376cbb33a5 100644 --- a/x-pack/test/functional/apps/maps/import_geojson/file_indexing_panel.js +++ b/x-pack/test/functional/apps/maps/import_geojson/file_indexing_panel.js @@ -13,6 +13,7 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['maps', 'common']); const log = getService('log'); const security = getService('security'); + const browser = getService('browser'); const IMPORT_FILE_PREVIEW_NAME = 'Import File'; const FILE_LOAD_DIR = 'test_upload_files'; @@ -102,6 +103,9 @@ export default function ({ getService, getPageObjects }) { const pointGeojsonFiles = ['point.json', 'multi_point.json']; pointGeojsonFiles.forEach(async (pointFile) => { it(`should index with type geo_point for file: ${pointFile}`, async () => { + if (!(await browser.checkBrowserPermission('clipboard-read'))) { + return; + } await loadFileAndIndex(pointFile); const indexPatternResults = await PageObjects.maps.getIndexPatternResults(); const coordinatesField = indexPatternResults.fields.find( @@ -120,6 +124,9 @@ export default function ({ getService, getPageObjects }) { ]; nonPointGeojsonFiles.forEach(async (shapeFile) => { it(`should index with type geo_shape for file: ${shapeFile}`, async () => { + if (!(await browser.checkBrowserPermission('clipboard-read'))) { + return; + } await loadFileAndIndex(shapeFile); const indexPatternResults = await PageObjects.maps.getIndexPatternResults(); const coordinatesField = indexPatternResults.fields.find( diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json new file mode 100644 index 0000000000000..a0f384a96e6b4 --- /dev/null +++ b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json @@ -0,0 +1,31 @@ +{ "type": "doc", "value": { "id": "config:7.0.0", "index": ".kibana_1", "source": { "config": { "buildNum": 9007199254740991, "dateFormat:tz": "UTC", "defaultIndex": "5193f870-d861-11e9-a311-0fa548c5f953" }, "migrationVersion": { "config": "7.13.0" }, "references": [ ], "type": "config", "updated_at": "2019-09-16T09:06:51.201Z" } } } + +{ "type": "doc", "value": { "id": "config:8.0.0", "index": ".kibana_1", "source": { "config": { "buildNum": 9007199254740991, "dateFormat:tz": "UTC", "defaultIndex": "5193f870-d861-11e9-a311-0fa548c5f953" }, "migrationVersion": { "config": "7.13.0" }, "references": [ ], "type": "config", "updated_at": "2019-12-11T23:22:12.698Z" } } } + +{ "type": "doc", "value": { "id": "index-pattern:5193f870-d861-11e9-a311-0fa548c5f953", "index": ".kibana_1", "source": { "index-pattern": { "fields": "[{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"category\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"category.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"category\"}}},{\"name\":\"currency\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"customer_birth_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"customer_first_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"customer_first_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"customer_first_name\"}}},{\"name\":\"customer_full_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"customer_full_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"customer_full_name\"}}},{\"name\":\"customer_gender\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"customer_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"customer_last_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"customer_last_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"customer_last_name\"}}},{\"name\":\"customer_phone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"day_of_week\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"day_of_week_i\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"manufacturer\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"manufacturer.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"manufacturer\"}}},{\"name\":\"order_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"order_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products._id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"products._id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"products._id\"}}},{\"name\":\"products.base_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.base_unit_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.category\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"products.category.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"products.category\"}}},{\"name\":\"products.created_on\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.discount_amount\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.discount_percentage\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.manufacturer\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"products.manufacturer.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"products.manufacturer\"}}},{\"name\":\"products.min_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.product_id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.product_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"products.product_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"products.product_name\"}}},{\"name\":\"products.quantity\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.sku\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.tax_amount\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.taxful_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.taxless_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.unit_discount_amount\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sku\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"taxful_total_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"taxless_total_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"total_quantity\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"total_unique_products\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", "timeFieldName": "order_date", "title": "ecommerce" }, "migrationVersion": { "index-pattern": "7.11.0" }, "references": [ ], "type": "index-pattern", "updated_at": "2019-12-11T23:24:13.381Z" } } } + +{ "type": "doc", "value": { "id": "search:6091ead0-1c6d-11ea-a100-8589bb9d7c6b", "index": ".kibana_1", "source": { "migrationVersion": { "search": "7.9.3" }, "references": [ { "id": "5193f870-d861-11e9-a311-0fa548c5f953", "name": "kibanaSavedObjectMeta.searchSourceJSON.index", "type": "index-pattern" } ], "search": { "columns": [ "category", "currency", "customer_id", "order_id", "day_of_week_i", "order_date", "products.created_on", "sku" ], "description": "", "hits": 0, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" }, "sort": [ [ "order_date", "desc" ] ], "title": "Ecommerce Data", "version": 1 }, "type": "search", "updated_at": "2019-12-11T23:24:28.540Z" } } } + +{ "type": "doc", "value": { "id": "dashboard:constructed-sample-saved-object-id", "index": ".kibana_1", "source": { "dashboard": { "description": "", "hits": 0, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}" }, "optionsJSON": "{\"hidePanelTitles\":true,\"useMargins\":true}", "panelsJSON": "[{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1c12c2f2-80c2-4d5c-b722-55b2415006e1\"},\"panelIndex\":\"1c12c2f2-80c2-4d5c-b722-55b2415006e1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_0\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1c4b99e1-7785-444f-a1c5-f592893b1a96\"},\"panelIndex\":\"1c4b99e1-7785-444f-a1c5-f592893b1a96\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":15,\"w\":48,\"h\":18,\"i\":\"94eab06f-60ac-4a85-b771-3a8ed475c9bb\"},\"panelIndex\":\"94eab06f-60ac-4a85-b771-3a8ed475c9bb\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":33,\"w\":48,\"h\":8,\"i\":\"52c19b6b-7117-42ac-a74e-c507a1c3ffc0\"},\"panelIndex\":\"52c19b6b-7117-42ac-a74e-c507a1c3ffc0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":41,\"w\":11,\"h\":10,\"i\":\"a1e889dc-b80e-4937-a576-979f34d1859b\"},\"panelIndex\":\"a1e889dc-b80e-4937-a576-979f34d1859b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":11,\"y\":41,\"w\":5,\"h\":10,\"i\":\"4930b035-d756-4cc5-9a18-1af9e67d6f31\"},\"panelIndex\":\"4930b035-d756-4cc5-9a18-1af9e67d6f31\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"}]", "refreshInterval": { "pause": true, "value": 0 }, "timeFrom": "2019-06-26T06:20:28.066Z", "timeRestore": true, "timeTo": "2019-06-26T07:27:58.573Z", "title": "Ecom Dashboard Hidden Panel Titles", "version": 1 }, "migrationVersion": { "dashboard": "7.11.0" }, "references": [ { "id": "0a464230-79f0-11ea-ae7f-13c5d6e410a0", "name": "panel_0", "type": "visualization" }, { "id": "200609c0-79f0-11ea-ae7f-13c5d6e410a0", "name": "panel_1", "type": "visualization" }, { "id": "6091ead0-1c6d-11ea-a100-8589bb9d7c6b", "name": "panel_2", "type": "search" }, { "id": "4a36acd0-7ac3-11ea-b69c-cf0d7935cd67", "name": "panel_3", "type": "visualization" }, { "id": "ef8757d0-7ac2-11ea-b69c-cf0d7935cd67", "name": "panel_4", "type": "visualization" }, { "id": "132ab9c0-7ac3-11ea-b69c-cf0d7935cd67", "name": "panel_5", "type": "visualization" } ], "type": "dashboard", "updated_at": "2020-04-10T00:37:48.462Z" } } } + +{ "type": "doc", "value": { "id": "visualization:0a464230-79f0-11ea-ae7f-13c5d6e410a0", "index": ".kibana_1", "source": { "migrationVersion": { "visualization": "7.12.0" }, "references": [ { "id": "5193f870-d861-11e9-a311-0fa548c5f953", "name": "kibanaSavedObjectMeta.searchSourceJSON.index", "type": "index-pattern" } ], "type": "visualization", "updated_at": "2020-04-08T23:24:05.971Z", "visualization": { "description": "", "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" }, "title": "e-commerce area chart", "uiStateJSON": "{}", "version": 1, "visState": "{\"type\":\"area\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"order_date\",\"timeRange\":{\"from\":\"2019-06-26T06:20:28.066Z\",\"to\":\"2019-06-26T07:27:58.573Z\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}}],\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"labels\":{},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"title\":\"e-commerce area chart\"}" } } } } + +{ "type": "doc", "value": { "id": "visualization:200609c0-79f0-11ea-ae7f-13c5d6e410a0", "index": ".kibana_1", "source": { "migrationVersion": { "visualization": "7.12.0" }, "references": [ { "id": "5193f870-d861-11e9-a311-0fa548c5f953", "name": "kibanaSavedObjectMeta.searchSourceJSON.index", "type": "index-pattern" } ], "type": "visualization", "updated_at": "2020-04-08T23:24:42.460Z", "visualization": { "description": "", "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" }, "title": "e-commerce pie chart", "uiStateJSON": "{}", "version": 1, "visState": "{\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"order_date\",\"timeRange\":{\"from\":\"2019-06-26T06:20:28.066Z\",\"to\":\"2019-06-26T07:27:58.573Z\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100}},\"title\":\"e-commerce pie chart\"}" } } } } + +{ "type": "doc", "value": { "id": "visualization:ef8757d0-7ac2-11ea-b69c-cf0d7935cd67", "index": ".kibana_1", "source": { "migrationVersion": { "visualization": "7.12.0" }, "references": [ { "id": "5193f870-d861-11e9-a311-0fa548c5f953", "name": "kibanaSavedObjectMeta.searchSourceJSON.index", "type": "index-pattern" } ], "type": "visualization", "updated_at": "2020-04-10T00:33:44.909Z", "visualization": { "description": "", "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"filter\":[]}" }, "title": "게이지", "uiStateJSON": "{}", "version": 1, "visState": "{\"type\":\"gauge\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}],\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"alignment\":\"automatic\",\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":75},{\"from\":75,\"to\":100}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"rgba(105,112,125,0.2)\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"rgba(105,112,125,0.2)\",\"bgColor\":true,\"subText\":\"\",\"fontSize\":60}}},\"title\":\"게이지\"}" } } } } + +{ "type": "doc", "value": { "id": "visualization:132ab9c0-7ac3-11ea-b69c-cf0d7935cd67", "index": ".kibana_1", "source": { "migrationVersion": { "visualization": "7.12.0" }, "references": [ { "id": "5193f870-d861-11e9-a311-0fa548c5f953", "name": "kibanaSavedObjectMeta.searchSourceJSON.index", "type": "index-pattern" } ], "type": "visualization", "updated_at": "2020-04-10T00:34:44.700Z", "visualization": { "description": "", "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"filter\":[]}" }, "title": "Українська", "uiStateJSON": "{}", "version": 1, "visState": "{\"type\":\"metric\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}],\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"title\":\"Українська\"}" } } } } + +{ "type": "doc", "value": { "id": "visualization:4a36acd0-7ac3-11ea-b69c-cf0d7935cd67", "index": ".kibana_1", "source": { "migrationVersion": { "visualization": "7.12.0" }, "references": [ ], "type": "visualization", "updated_at": "2020-04-10T00:36:17.053Z", "visualization": { "description": "", "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" }, "title": "Tiểu thuyết", "uiStateJSON": "{}", "version": 1, "visState": "{\"type\":\"markdown\",\"aggs\":[],\"params\":{\"fontSize\":12,\"openLinksInNewTab\":false,\"markdown\":\"Tiểu thuyết là một thể loại văn xuôi có hư cấu, thông qua nhân vật, hoàn cảnh, sự việc để phản ánh bức tranh xã hội rộng lớn và những vấn đề của cuộc sống con người, biểu hiện tính chất tường thuật, tính chất kể chuyện bằng ngôn ngữ văn xuôi theo những chủ đề xác định.\\n\\nTrong một cách hiểu khác, nhận định của Belinski: \\\"tiểu thuyết là sử thi của đời tư\\\" chỉ ra khái quát nhất về một dạng thức tự sự, trong đó sự trần thuật tập trung vào số phận của một cá nhân trong quá trình hình thành và phát triển của nó. Sự trần thuật ở đây được khai triển trong không gian và thời gian nghệ thuật đến mức đủ để truyền đạt cơ cấu của nhân cách[1].\\n\\n\\n[1]^ Mục từ Tiểu thuyết trong cuốn 150 thuật ngữ văn học, Lại Nguyên Ân biên soạn, Nhà xuất bản Đại học Quốc gia Hà Nội, in lần thứ 2 có sửa đổi bổ sung. H. 2003. Trang 326.\"},\"title\":\"Tiểu thuyết\"}" } } } } + +{ "type": "doc", "value": { "id": "space:default", "index": ".kibana_1", "source": { "space": { "_reserved": true, "description": "This is the default space", "disabledFeatures": [ ], "name": "Default Space" }, "type": "space", "updated_at": "2021-01-07T00:17:12.785Z" } } } + +{ "type": "doc", "value": { "id": "ui-counter:visualize:06012021:click:tagcloud", "index": ".kibana_1", "source": { "type": "ui-counter", "ui-counter": { "count": 1 }, "updated_at": "2021-01-07T00:18:52.592Z" } } } + +{ "type": "doc", "value": { "id": "ui-counter:data_plugin:06012021:click:discover:query_submitted", "index": ".kibana_1", "source": { "type": "ui-counter", "ui-counter": { "count": 1 }, "updated_at": "2021-01-07T00:18:52.592Z" } } } + +{ "type": "doc", "value": { "id": "dashboard:6c263e00-1c6d-11ea-a100-8589bb9d7c6b", "index": ".kibana_1", "source": { "dashboard": { "description": "", "hits": 0, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}" }, "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":true}", "panelsJSON": "[{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1c12c2f2-80c2-4d5c-b722-55b2415006e1\"},\"panelIndex\":\"1c12c2f2-80c2-4d5c-b722-55b2415006e1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_0\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1c4b99e1-7785-444f-a1c5-f592893b1a96\"},\"panelIndex\":\"1c4b99e1-7785-444f-a1c5-f592893b1a96\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":35,\"w\":48,\"h\":18,\"i\":\"94eab06f-60ac-4a85-b771-3a8ed475c9bb\"},\"panelIndex\":\"94eab06f-60ac-4a85-b771-3a8ed475c9bb\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":15,\"w\":48,\"h\":8,\"i\":\"52c19b6b-7117-42ac-a74e-c507a1c3ffc0\"},\"panelIndex\":\"52c19b6b-7117-42ac-a74e-c507a1c3ffc0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":23,\"w\":16,\"h\":12,\"i\":\"a1e889dc-b80e-4937-a576-979f34d1859b\"},\"panelIndex\":\"a1e889dc-b80e-4937-a576-979f34d1859b\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":null},\"panelRefName\":\"panel_4\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":16,\"y\":23,\"w\":12,\"h\":12,\"i\":\"4930b035-d756-4cc5-9a18-1af9e67d6f31\"},\"panelIndex\":\"4930b035-d756-4cc5-9a18-1af9e67d6f31\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"8.0.0\",\"gridData\":{\"x\":28,\"y\":23,\"w\":20,\"h\":12,\"i\":\"55112375-d6f0-44f7-a8fb-867c8f7d464d\"},\"panelIndex\":\"55112375-d6f0-44f7-a8fb-867c8f7d464d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"}]", "refreshInterval": { "pause": true, "value": 0 }, "timeFrom": "2019-03-23T03:06:17.785Z", "timeRestore": true, "timeTo": "2019-10-04T02:33:16.708Z", "title": "Ecom Dashboard", "version": 1 }, "migrationVersion": { "dashboard": "7.11.0" }, "references": [ { "id": "0a464230-79f0-11ea-ae7f-13c5d6e410a0", "name": "panel_0", "type": "visualization" }, { "id": "200609c0-79f0-11ea-ae7f-13c5d6e410a0", "name": "panel_1", "type": "visualization" }, { "id": "6091ead0-1c6d-11ea-a100-8589bb9d7c6b", "name": "panel_2", "type": "search" }, { "id": "4a36acd0-7ac3-11ea-b69c-cf0d7935cd67", "name": "panel_3", "type": "visualization" }, { "id": "ef8757d0-7ac2-11ea-b69c-cf0d7935cd67", "name": "panel_4", "type": "visualization" }, { "id": "132ab9c0-7ac3-11ea-b69c-cf0d7935cd67", "name": "panel_5", "type": "visualization" }, { "id": "1bba55f0-507e-11eb-9c0d-97106882b997", "name": "panel_6", "type": "visualization" } ], "type": "dashboard", "updated_at": "2021-01-07T00:22:16.102Z" } } } + +{ "type": "doc", "value": { "id": "visualization:1bba55f0-507e-11eb-9c0d-97106882b997", "index": ".kibana_1", "source": { "migrationVersion": { "visualization": "7.12.0" }, "references": [ { "id": "6091ead0-1c6d-11ea-a100-8589bb9d7c6b", "name": "search_0", "type": "search" } ], "type": "visualization", "updated_at": "2021-01-07T00:23:04.624Z", "visualization": { "description": "", "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" }, "savedSearchRefName": "search_0", "title": "Tag Cloud of Names", "uiStateJSON": "{}", "version": 1, "visState": "{\"title\":\"Tag Cloud of Names\",\"type\":\"tagcloud\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"customer_first_name.keyword\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"}],\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true}}" } } } } + +{ "type": "doc", "value": { "id": "ui-counter:DashboardPanelVersionInUrl:06012021:loaded:8.0.0", "index": ".kibana_1", "source": { "type": "ui-counter", "ui-counter": { "count": 85 }, "updated_at": "2021-01-07T00:23:25.741Z" } } } diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json.gz b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json.gz deleted file mode 100644 index 8b2b8f9c114de2968d1ebb6395c1af082486c6a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4526 zcmV;f5mD|RiwFqyRQ6v217u-zVJ>QOZ*BnXUCnRg#+AS4uOK>?U7*=0emDDOk3DNI zMzUjdCV)q?jUrjyUE6H3%@2KeARyixf<+QweFzr8!dOoXW02h)th4b+!XgJ5K9Bzd zT^IQy_EiEGt%lwEiZIiZK<_~{ghol zlUmec?QR$h>8NLQPzSLNH1Nm)yV03Y2M2hdu+}(CBR3l-Mu^W&N_}rTox+ukkfqqR zYUq}ZY+Hd*9!|)^Fq-0|mwd`-{|4V-m&iqlTzub!=dRca6x9L?m!Y3^$%gPup`@Wc{Hf zoiU8w04J7A^04-5uJfZ;Btuln6M^-BX^7)pOH(Vl(pf}C?X@A zR9hp0y@ydaeJ6AuGr`0;m7{hQA!DW~NYVFJaK>&Jkt)t0ojN4qZR^0v8*6y-=Ei`y zMN|=$8>^zrZ#=ynumWKtC5s-np<@@;|0s+On*mM8K~lx3TpH(^;>>||CggBGj2f=- zqVr!}w%M&Xb@~DnfS-bwr{d!bgELNQB0oB+@t8);WglLFH|4LzTODbfgi{g?92zC# z0gED4&_zoHvS_^#U97-6q*0sm-B%vaQI|B!vc}jS z4u*b+Z^zqLai;-IHWB<)&tPwUNxVLXu}hS&a&fpY6?|t?G?R>z1N^jaK*hiQc>$4K^`=Y$Rm2sagfmD@CrwUH*IF|B)$7b76sY_A8cfH0l0^$zlpvckozQ#HxWAD zpKW4NdbKrqLur3z(n*-$O)~7kkd;VY_WS|nlL!1y@KnqwCAj| z(XjN!bTszifAW^^&)318(9^sDoE{zNQ8-}Cm zih+Saf;7-x3k{NHE6W+orG&XWTwJDyAr}4c;(748R(E zb3QL-Tz71nD6(aB4O!RqA^3FHkcWn?c5Tg3ux+kjyve|8VO&|0@m%4YO~$z1%^B|& zjN3ZF4l;+biLfi{7?^M@OOZ9)C7x~>E`+qDj5iy2EsU#|WL(qgjL-QtR9CSb(~&Jj zv1DBZ9!8le(w`+T_KD&s>x~*9< zHY`)NEqkcxp3*h!fDRWE>D92Wf`vS%xX=E-X?TD_vQ^Lz|eE zXAZUHzOC87Yhl~~06e9JbT1;hQ}yP6>Dn6@jsL*!YuX1Jbd zEt1t5f+n!PFf2h`ZA@KnNL|rX>~MjvgWWifdSzcYx348xtcql$%L?iv)YB}86CK@D z*UDlmbN8Cs=4H2rRZhi%Q7>9Bx4K1KuA`NzK;3LZfsBrADctSUnO>gSUn!rz#l747 zbVM$;Wf=Cjk~_w*t7DR)I|+!YKz>wnu0lk-PsUksO2i0pj__#24~XC<07N_U2J0?>s>KREAQJ1&QaD=CMK%0|QFCfd5J{VJoKVaX}I{lc8rX-to zKEbYd6?{B)&w0coE8vt7gg>I$ZtSSKf%bUO7+08QbaZjW18fdnG+^Sn5l9c?w2H# z9P&fNXGgiXYN&mHh9eJF!#Vi!MD4PtIpWrj_qi>;O_F^=f)7~@f9bq~ ziRWk3OIX^jX3~8?Be!xW!WY&o^aZ;WneOH4T~s#b8GBwo4E==8N~hhP_lU4J3rjHg z^@nAJN9-K1a5r)8{~1BJ-onwOs*f>C-9z7F+*Z_?_s9%O>`wMGo|TdfftFL>&wdMn zA4a_C$M0IEsXwS23%jE6V;cL^c}z>P@*w(9@WKmR4{4InVDu2orr_~Y5`}_tZallo zBa#SQm(Gxy=GXTb(nT4Pu0pbk-p~wbM#;4#NI>U4KY9c5qe9Qu5CmxaP8g&~srO>w zu6X)#kYg|S`ecvzEAyF^KDe;?E4tvO`}unpeExZX*3 z&#(XT#kYTX@$)PC-4RYlH{p3#I)oB{W-b9%J&k8RKKvPu0$zGiEb@#NqYox93Z{8E zDf4Voun2)mxUAe8XJqGZA8d&PyF#>|!+k3fGQ=SO~pI(d5-vua1BC z`Kxb!_}5qe`@?5H{QA}Lt8cCtSW0o=W+Tf*VWc=6Sb{YF$lpd-6%Zid=FWD(bDihH z1?8#_-_C!3%*7rYJaXofS2*9J)$Y3D9SeW_UlspeZ(Pr*EiNTp<)9-bZl`KtT;DUK#yxF1v;LwyOCOiN)Wkl~C3 zk7zJ~&FwbX?_;MDvU7q!y@pltPrsC=XD7c)pb73set35LoJxD&{UVSK($jCL9@g1`Xt3tFh2c#AnlzUzf9WFI6VC_V4c1S#%(D+JNcuu2Xp<> zmA?C&5uT0NE$Q@mFqWLNlRvmp5@Gm!aQc-5GyjfC5qt;A{@KaD0ad^iK>L3~3(W9! zz{z|D6P&!jlAA(bm(B7y+;_u33P#_5b9V9tZA*^eaLgtL2Xp!oCUIf%uYpD02Sxz< z0(Ir~gyD3VWY|dO?D&t+H5dWE@ExF(rIL(ESOA3aCoeL92dB>k%fAZ7o!#94zWPxJ zorIKKxIO0J0OMo~S8Yb-_+^j}%2<4x_%w(ov?uNEGQY8?HsJmxw9sq-8~z1lJhA~C z(?3fQ=95s+*Xi?w&jgI_F`xpE2kPbm5h`J7W4Z&$aM0g<4%+9+Oro>n7eOvL=z08d z#%eN{eons(xh}YpUj`YVg5k_i1Vsh}KA!#;a60DS!J@u|A57!3F+&M)0JzP`1gC%M zNIOd)oSpnT!}t3`_|6pjl?%LLS_~KSO@zIUC>BP=I0Z5B>tF;IzbpuX1dsn5Fx-C- zDtwXIF0ctU@flaqF#vSGe=e3o4w3W6Rr^GFnrR)r;Nl;ot+SJ_xn%!SsxLl)kQwvm zfutCyKvFU{K=>Q5kw<)4ZjaK#)Bk`~^iN=)lzocXdJWprHV6?+ldIR^EA6{~;Y0X% z=_hOsmvJq<2OYQBf@@1OfYl~M3Oq|HU+c`Qgf;z^vX%NR1Sk%h%&-JN0=|KwS`eE9z_Bfw$PFX~(;wmeWhl`>h>Ri6MJwxpnwt88<*rU0@ z%uh!&sB^|&k%PF|<57A5Db|$24rZe`omY>Wu4-xoUBl}buSxxQX~S?|Kz?69e)R%! z?M*EpU#y&eGYZJnyl`GIbA_vSuYjCAUgPx+LF)V0Sxs&(h(TQx!={vzujT2wb?$D9DrsZ~rmZzJ#x7_j@4ZIf4&Fd_r*JM@eBaQt9AjJ~7 zX2U{yg`L6lk;)zxDrUz*-SZ0R?~CT|i{@{wXueF{aj;>4CLtmbM%|HNE)N{Fg3NAL zg?wcRb@Q4^@=IF-_9vpuj)JaIlHZh>8+|^tu1O2~t**zyrq?_cF3T5un%*&0{mvf? z^Y?*wIQ`7oFNmc$IL0IC0o%As;ZR~d<4c|$&re_$C|2u)^OJ%rKXocSUt0bQYMn|l zkl-{vr&8>qKmWoj$=*IJvA|nIl^WQEcutlbm$Nh>OvW%Ebtfb#?LtefkBS=M4_Kpk zKtxmG=3-Ezz7m;a@C_TX-&)mjP62h3j<@jHXXAK*7V(bAT>C>pv(pS$T1C zj*4%s*F2Of9n=)zpYP%`-|~L&)5xEn+xQ{&h_}vP5nS(-rfXbyOv8#!jgF3MnCs{}#^y%%Jj$ zV_e!!f~OjtQFwL*y;0m(xoD@Gjv>?g!I^p|#LZPt&*N*hu&sUKX5v?(EGq(dU$+N< z;gDqRuuSBRU8*Gy!@y<+=}D7VnrlM0#n#)lk|sflY@7HekL!AUcsisaim& zn1){ZDFu@;KJvwKS@B6++3^k3<6>YFIWe_6^3~%u?pa|}^fcgd3<#w<0DRuu4Z{^S zUvVhl*u+()2f74IIiCo8i#I!MXuVZ_v604@%h&XQMyGw+2%0LDU%Gif!(w5AiU%1;O&fy;L5K@$2`(AW%F;V2Hip;fc`=QYI|?e~X{XZOoH@4xL_a?@R z%2KDj&+s|R9;k#pYeefXN~Z5}(z@2U^r$yQ@jB zG&_D6Zqy7kgn?AEoW<6(vq$(rK!xX)Krz{CR!E&mHVZ| zdQqi+zn(yK|Cgv^k9B$efAWXbKj{p2hss%?os%=U9^Fy`YmEF6FKs#|)|aaSj11m) zQ4(Vs8}%8;66;SN+FmAPis(fP+FQ}~6@GPjq9EF40tdqKbT+ za}TDQ@&XKi%mI@7!N1JMp6-5lrAhf~>df8ni;Q0g||U-)e|*NPbce27lO?x5-XB*WL}# zm-dQke!1lpv_DtYX;=Kj(1x+S)qxds@qw)HJIXY_K)<8&*(a?tI^?}lb0R)yfhXeY zoN5a?X^#=|&>gX4jq;e^cWvUG}l#O!T4m;tyOSUjN!Y(po$$I`K0U^~+aW#zvVx&4Y9&JlgCe#k94x|yx1c37ajF0iW~@bc#oS!g z%GICk5mc%T9bIdUfI$tb_8X5wHrcQNPG#oeyRYo@7xgyFiXGa{^Sqh)_#>4riJNr@ zHxE$B;);!d^%Ux;E2>7BykwF*DAuUk^i^DxPzi}wsm6VOAIgZ+$bz|r|D5praN5q8 z5g(UC81cirPn}^H^OmYAt7)?xS$B1*aHD%TTMbM^&dv=Q2;H%=s1H$4hdZa(X*T8F z)$#GVY%7y9OIRBN?NrnT$b5MOhzU6bT|SWNMq@i!-_&$|S=MDNd{PcctSPQUXT(V? zCN!7?f)ATfJM%N&56)I88A=(z^10vpIWCqv4+wnNt6}89qxZ}Ud9ta1Xg2pnH}2>i zSsJmEHUJl^oNxuy=xxXI0#`Fr%+Gpy>r~nzPB}5~lyZd7lLviDV#|~>x~s3tIwFOT z>wTsx3U@@UN>_SW`sh!C9%VL1L^i;iPx8DruJ!vvSh7RqGr8d?rDuk4iud}CNi{F$ zyMbcehwRa@G~O0pIRynkjAZ@#Y5Vhk1OLCS&^mDzPP&DB?f+E&#dqV@Y=81U#(!gq z?K`~dnE%-rQY-oY)Jv8rjMjSO+w-`aDV#+ioJJl6OZZ_V4Yc$^^ z_D=u?zBk`M4%Us$W~+xD|3=PahuX=>1~Yxji9@sPSF2J)U3`VUIe|z^Ryy{j { layerAddReady = await this.importFileButtonEnabled(); @@ -454,21 +454,20 @@ export function GisPageProvider({ getService, getPageObjects }: FtrProviderConte ); } - async getCodeBlockParsedJson(dataTestSubjName: string) { - log.debug(`Get parsed code block for ${dataTestSubjName}`); - const indexRespCodeBlock = await testSubjects.find(`${dataTestSubjName}`); - const indexRespJson = await indexRespCodeBlock.getAttribute('innerText'); - return JSON.parse(indexRespJson); + async clickCopyButton(dataTestSubj: string): Promise { + log.debug(`Click ${dataTestSubj} copy button`); + + await testSubjects.click(dataTestSubj); + + return await browser.getClipboardValue(); } async getIndexResults() { - log.debug('Get index results'); - return await this.getCodeBlockParsedJson('indexRespCodeBlock'); + return JSON.parse(await this.clickCopyButton('indexRespCopyButton')); } async getIndexPatternResults() { - log.debug('Get index pattern results'); - return await this.getCodeBlockParsedJson('indexPatternRespCodeBlock'); + return JSON.parse(await this.clickCopyButton('indexPatternRespCopyButton')); } async setLayerQuery(layerName: string, query: string) {