From 60c8c0233d77190ddad0343d206d7694c7fe24d4 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Tue, 16 Mar 2021 19:43:18 -0700 Subject: [PATCH] [7.x] [Reporting-CSV Export] Re-write CSV Export using SearchSource (#88303) (#94757) * [Reporting-CSV Export] Re-write CSV Export using SearchSource (#88303) * [Reporting-CSV Export] Re-write CSV Export using SearchSource * replace PIT solution with scan-and-scroll * update tests * cleanup * simplify pr * update docs * update docs * update telemetry schema * use getSearchRequestBody instead of flatten * Revert "update docs" This reverts commit ab9f4d9642136fb0f53213dbe06037efaa89b6b9. * optimize some async calls * cleanup * --wip-- [skip ci] * fix telemetry schema * fix telemetry tests * fix snapshot * api docs * api doc updates * use import type * format the data through chains of maps * add another saved search to reporting/ecommerce_kibana * add a failing test * add error logging to query failures * put clear scroll in a finally so the ES error can be captured * log dat error * set dat fieldsFromSource * --wip-- [skip ci] * Revert "add another saved search to reporting/ecommerce_kibana" This reverts commit 6edf26eff255189c4a2c88a93a327497bd833410. * functional test fixes * clean up ecommerce test archive * add test for new search with fieldsFromSource set * add tests and refactor tests * cleanup redundant conditionals * add GenerateCsv.getFields * fix some tests * fix double-escaping * fix test snapshots and refactoring * fix other tests * fix test * fix default index pattern in functional tests * fix ts and sort fields when they come from API response * --wip-- [skip ci] * fix formatting and increase maxSizeBytes for testing * remove client-side logic for sanitizing fields * do not prepend timefield name if it already is a column * test the logic to prepend timeField * test the logic to sort the fields * fix functional test * preserve the error from data.search * add functional test for ES returning an error * fix snapshot Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> * simplify logging tags * update snapshots * --wip-- [skip ci] * fix types * update jest snapshot * Revert "simplify logging tags" This reverts commit e844dbd6ab02d1147c798ccf91c7726eac47b206. * Revert "update jest snapshot" This reverts commit 88497529de92ba3fc24e771f04cd0e9f0ca6737b. * fix types again Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- api_docs/data.json | 50 +- api_docs/data_search.json | 177 ++ api_docs/discover.json | 19 + api_docs/reporting.json | 283 +- .../components/top_nav/get_top_nav_links.ts | 3 +- .../helpers/get_sharing_data.test.ts | 155 +- .../application/helpers/get_sharing_data.ts | 73 +- x-pack/plugins/reporting/common/constants.ts | 10 +- x-pack/plugins/reporting/common/types.ts | 3 +- .../components/reporting_panel_content.tsx | 10 +- .../get_csv_panel_action.test.ts | 15 +- .../panel_actions/get_csv_panel_action.tsx | 62 +- .../register_csv_reporting.tsx | 20 +- x-pack/plugins/reporting/server/core.ts | 21 +- .../server/export_types/common/index.ts | 1 - .../generate_csv/check_cells_for_formulas.ts | 2 +- .../export_types/csv/generate_csv/index.ts | 11 +- .../server/export_types/csv/types.d.ts | 7 +- .../csv_from_savedobject/create_job.ts | 96 - .../csv_from_savedobject/execute_job.ts | 80 - .../lib/get_csv_job.test.ts | 340 --- .../csv_from_savedobject/lib/get_csv_job.ts | 155 - .../lib/get_data_source.ts | 56 - .../lib/get_filters.test.ts | 208 -- .../csv_from_savedobject/lib/get_filters.ts | 55 - .../csv_from_savedobject/types.d.ts | 153 - .../csv_searchsource/create_job.ts | 30 + .../csv_searchsource/execute_job.test.ts | 75 + .../csv_searchsource/execute_job.ts | 49 + .../__snapshots__/generate_csv.test.ts.snap | 163 ++ .../generate_csv/cell_has_formula.ts | 0 .../generate_csv/escape_value.test.ts | 0 .../generate_csv/escape_value.ts | 0 .../generate_csv/generate_csv.test.ts | 645 ++++ .../generate_csv/generate_csv.ts | 400 +++ .../generate_csv/get_export_settings.test.ts | 83 + .../generate_csv/get_export_settings.ts | 85 + .../csv_searchsource/generate_csv/index.ts | 8 + .../max_size_string_builder.test.ts | 0 .../generate_csv/max_size_string_builder.ts | 0 .../export_types/csv_searchsource/index.ts | 40 + .../metadata.ts | 6 +- .../export_types/csv_searchsource/types.d.ts | 18 + .../csv_searchsource_immediate/execute_job.ts | 81 + .../index.ts | 13 +- .../csv_searchsource_immediate/metadata.ts | 13 + .../csv_searchsource_immediate/types.d.ts | 24 + .../reporting/server/lib/enqueue_job.ts | 14 +- .../server/lib/export_types_registry.ts | 8 +- x-pack/plugins/reporting/server/plugin.ts | 6 +- ...diate.ts => csv_searchsource_immediate.ts} | 44 +- .../reporting/server/routes/generation.ts | 2 +- .../server/routes/lib/get_document_payload.ts | 4 +- .../routes/lib/get_job_params_from_request.ts | 22 - .../create_mock_reportingplugin.ts | 35 +- x-pack/plugins/reporting/server/types.ts | 7 +- .../reporting_usage_collector.test.ts.snap | 24 + .../server/usage/decorate_range_stats.ts | 8 +- .../usage/reporting_usage_collector.test.ts | 154 + .../plugins/reporting/server/usage/schema.ts | 2 + .../plugins/reporting/server/usage/types.ts | 4 +- .../schema/xpack_plugins.json | 176 ++ .../reporting/__snapshots__/download_csv.snap | 53 + .../apps/dashboard/reporting/download_csv.ts | 160 +- .../discover/__snapshots__/reporting.snap | 106 +- .../functional/apps/discover/reporting.ts | 100 +- .../reporting/ecommerce/data.json.gz | Bin 957893 -> 956882 bytes .../reporting/ecommerce/mappings.json | 1015 ------- .../reporting/ecommerce_kibana/data.json | 420 ++- .../reporting/ecommerce_kibana/mappings.json | 51 - .../reporting/hugedata/data.json.gz | Bin 33745 -> 33885 bytes .../reporting/hugedata/mappings.json | 2590 ++++++++++++++--- .../reporting/scripted_small2/data.json.gz | Bin 4436 -> 0 bytes .../reporting/scripted_small2/mappings.json | 2217 -------------- .../reporting_api_integration/fixtures.ts | 261 -- .../reporting_and_security.config.ts | 2 +- .../csv_searchsource_immediate.snap | 250 ++ .../csv_saved_search.ts | 411 --- .../csv_searchsource_immediate.ts | 512 ++++ .../reporting_and_security/index.ts | 2 +- 80 files changed, 6528 insertions(+), 5930 deletions(-) delete mode 100644 x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts delete mode 100644 x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts delete mode 100644 x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.test.ts delete mode 100644 x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.ts delete mode 100644 x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_data_source.ts delete mode 100644 x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.test.ts delete mode 100644 x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.ts delete mode 100644 x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/create_job.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.test.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/__snapshots__/generate_csv.test.ts.snap rename x-pack/plugins/reporting/server/export_types/{csv => csv_searchsource}/generate_csv/cell_has_formula.ts (100%) rename x-pack/plugins/reporting/server/export_types/{csv => csv_searchsource}/generate_csv/escape_value.test.ts (100%) rename x-pack/plugins/reporting/server/export_types/{csv => csv_searchsource}/generate_csv/escape_value.ts (100%) create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.test.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/index.ts rename x-pack/plugins/reporting/server/export_types/{csv => csv_searchsource}/generate_csv/max_size_string_builder.test.ts (100%) rename x-pack/plugins/reporting/server/export_types/{csv => csv_searchsource}/generate_csv/max_size_string_builder.ts (100%) create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/index.ts rename x-pack/plugins/reporting/server/export_types/{csv_from_savedobject => csv_searchsource}/metadata.ts (65%) create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource/types.d.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts rename x-pack/plugins/reporting/server/export_types/{csv_from_savedobject => csv_searchsource_immediate}/index.ts (75%) create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/metadata.ts create mode 100644 x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/types.d.ts rename x-pack/plugins/reporting/server/routes/{generate_from_savedobject_immediate.ts => csv_searchsource_immediate.ts} (55%) delete mode 100644 x-pack/plugins/reporting/server/routes/lib/get_job_params_from_request.ts create mode 100644 x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap delete mode 100644 x-pack/test/functional/es_archives/reporting/scripted_small2/data.json.gz delete mode 100644 x-pack/test/functional/es_archives/reporting/scripted_small2/mappings.json create mode 100644 x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/csv_searchsource_immediate.snap delete mode 100644 x-pack/test/reporting_api_integration/reporting_and_security/csv_saved_search.ts create mode 100644 x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts diff --git a/api_docs/data.json b/api_docs/data.json index 85876404ddb97..30d8a7190d4f7 100644 --- a/api_docs/data.json +++ b/api_docs/data.json @@ -22013,7 +22013,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 245 + "lineNumber": 246 }, "signature": [ "typeof ", @@ -22034,7 +22034,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 246 + "lineNumber": 247 }, "signature": [ "typeof ", @@ -22055,7 +22055,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 247 + "lineNumber": 248 }, "signature": [ "({ display: string; val: string; enabled(agg: ", @@ -22077,7 +22077,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 248 + "lineNumber": 249 }, "signature": [ "typeof ", @@ -22098,7 +22098,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 249 + "lineNumber": 250 }, "signature": [ "typeof ", @@ -22119,7 +22119,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 250 + "lineNumber": 251 }, "signature": [ "typeof ", @@ -22140,7 +22140,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 251 + "lineNumber": 252 }, "signature": [ "(agg: ", @@ -22162,7 +22162,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 252 + "lineNumber": 253 }, "signature": [ "(agg: ", @@ -22184,7 +22184,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 253 + "lineNumber": 254 }, "signature": [ "(...types: string[]) => (agg: ", @@ -22206,7 +22206,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 254 + "lineNumber": 255 }, "signature": [ "typeof ", @@ -22227,7 +22227,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 255 + "lineNumber": 256 }, "signature": [ "typeof ", @@ -22248,7 +22248,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 256 + "lineNumber": 257 } }, { @@ -22259,7 +22259,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 257 + "lineNumber": 258 }, "signature": [ "typeof ", @@ -22280,7 +22280,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 258 + "lineNumber": 259 }, "signature": [ "typeof ", @@ -22301,7 +22301,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 259 + "lineNumber": 260 }, "signature": [ "typeof ", @@ -22322,7 +22322,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 260 + "lineNumber": 261 } }, { @@ -22333,7 +22333,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 261 + "lineNumber": 262 }, "signature": [ "string[]" @@ -22347,7 +22347,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 262 + "lineNumber": 263 }, "signature": [ "typeof ", @@ -22368,7 +22368,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 263 + "lineNumber": 264 }, "signature": [ "typeof ", @@ -22386,7 +22386,7 @@ "label": "aggs", "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 244 + "lineNumber": 245 } }, { @@ -22397,7 +22397,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 265 + "lineNumber": 266 }, "signature": [ "typeof ", @@ -22418,7 +22418,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 266 + "lineNumber": 267 }, "signature": [ "typeof ", @@ -22439,7 +22439,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 267 + "lineNumber": 268 }, "signature": [ "typeof ", @@ -22460,7 +22460,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 268 + "lineNumber": 269 }, "signature": [ "typeof ", @@ -22478,7 +22478,7 @@ "label": "search", "source": { "path": "src/plugins/data/server/index.ts", - "lineNumber": 243 + "lineNumber": 244 }, "initialIsOpen": false }, diff --git a/api_docs/data_search.json b/api_docs/data_search.json index def169091aa75..8c828ff7ce80a 100644 --- a/api_docs/data_search.json +++ b/api_docs/data_search.json @@ -1573,6 +1573,183 @@ } ], "interfaces": [ + { + "id": "def-server.IScopedSearchClient", + "type": "Interface", + "label": "IScopedSearchClient", + "signature": [ + { + "pluginId": "data", + "scope": "server", + "docId": "kibDataSearchPluginApi", + "section": "def-server.IScopedSearchClient", + "text": "IScopedSearchClient" + }, + " extends ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.ISearchClient", + "text": "ISearchClient" + } + ], + "description": [], + "tags": [], + "children": [ + { + "tags": [], + "id": "def-server.IScopedSearchClient.saveSession", + "type": "Function", + "label": "saveSession", + "description": [], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 90 + }, + "signature": [ + "(sessionId: string, attributes: Partial) => Promise<", + { + "pluginId": "core", + "scope": "common", + "docId": "kibCorePluginApi", + "section": "def-common.SavedObject", + "text": "SavedObject" + }, + " | undefined>" + ] + }, + { + "tags": [], + "id": "def-server.IScopedSearchClient.getSession", + "type": "Function", + "label": "getSession", + "description": [], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 91 + }, + "signature": [ + "(sessionId: string) => Promise<", + { + "pluginId": "core", + "scope": "common", + "docId": "kibCorePluginApi", + "section": "def-common.SavedObject", + "text": "SavedObject" + }, + ">" + ] + }, + { + "tags": [], + "id": "def-server.IScopedSearchClient.findSessions", + "type": "Function", + "label": "findSessions", + "description": [], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 92 + }, + "signature": [ + "(options: Pick<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptions", + "text": "SavedObjectsFindOptions" + }, + ", \"filter\" | \"fields\" | \"searchAfter\" | \"search\" | \"page\" | \"perPage\" | \"sortField\" | \"sortOrder\" | \"searchFields\" | \"rootSearchFields\" | \"hasReference\" | \"hasReferenceOperator\" | \"defaultSearchOperator\" | \"namespaces\" | \"typeToNamespacesMap\" | \"preference\" | \"pit\">) => Promise<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindResponse", + "text": "SavedObjectsFindResponse" + }, + ">" + ] + }, + { + "tags": [], + "id": "def-server.IScopedSearchClient.updateSession", + "type": "Function", + "label": "updateSession", + "description": [], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 93 + }, + "signature": [ + "(sessionId: string, attributes: Partial) => Promise<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsUpdateResponse", + "text": "SavedObjectsUpdateResponse" + }, + ">" + ] + }, + { + "tags": [], + "id": "def-server.IScopedSearchClient.cancelSession", + "type": "Function", + "label": "cancelSession", + "description": [], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 94 + }, + "signature": [ + "(sessionId: string) => Promise<{}>" + ] + }, + { + "tags": [], + "id": "def-server.IScopedSearchClient.deleteSession", + "type": "Function", + "label": "deleteSession", + "description": [], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 95 + }, + "signature": [ + "(sessionId: string) => Promise<{}>" + ] + }, + { + "tags": [], + "id": "def-server.IScopedSearchClient.extendSession", + "type": "Function", + "label": "extendSession", + "description": [], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 96 + }, + "signature": [ + "(sessionId: string, expires: Date) => Promise<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsUpdateResponse", + "text": "SavedObjectsUpdateResponse" + }, + ">" + ] + } + ], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 89 + }, + "initialIsOpen": false + }, { "id": "def-server.ISearchSessionService", "type": "Interface", diff --git a/api_docs/discover.json b/api_docs/discover.json index 69e1d0366a712..adcecddfcc444 100644 --- a/api_docs/discover.json +++ b/api_docs/discover.json @@ -40,6 +40,25 @@ "lineNumber": 18 }, "initialIsOpen": false + }, + { + "id": "def-public.loadSharingDataHelpers", + "type": "Function", + "label": "loadSharingDataHelpers", + "signature": [ + "() => Promise" + ], + "description": [], + "children": [], + "tags": [], + "returnComment": [], + "source": { + "path": "src/plugins/discover/public/shared/index.ts", + "lineNumber": 12 + }, + "initialIsOpen": false } ], "interfaces": [ diff --git a/api_docs/reporting.json b/api_docs/reporting.json index 1296fb8926178..da8fc11184836 100644 --- a/api_docs/reporting.json +++ b/api_docs/reporting.json @@ -851,7 +851,29 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 58 + "lineNumber": 69 + } + }, + { + "type": "Object", + "label": "context", + "isRequired": true, + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.PluginInitializerContext", + "text": "PluginInitializerContext" + }, + "; }>; autoDownload: boolean; }>; timeouts: Readonly<{} & { openUrl: number | moment.Duration; waitForElements: number | moment.Duration; renderComplete: number | moment.Duration; }>; networkPolicy: Readonly<{} & { enabled: boolean; rules: Readonly<{ host?: string | undefined; protocol?: string | undefined; } & { allow: boolean; }>[]; }>; zoom: number; viewport: Readonly<{} & { height: number; width: number; }>; loadDelay: number | moment.Duration; maxAttempts: number; }>; kibanaServer: Readonly<{ hostname?: string | undefined; port?: number | undefined; protocol?: string | undefined; } & {}>; queue: Readonly<{} & { timeout: number | moment.Duration; pollInterval: number | moment.Duration; indexInterval: string; pollEnabled: boolean; pollIntervalErrorMultiplier: number; }>; csv: Readonly<{} & { scroll: Readonly<{} & { size: number; duration: string; }>; checkForFormulas: boolean; escapeFormulaValues: boolean; enablePanelActionDownload: boolean; maxSizeBytes: number | ", + "ByteSizeValue", + "; useByteOrderMarkEncoding: boolean; }>; roles: Readonly<{} & { allow: string[]; }>; poll: Readonly<{} & { jobCompletionNotifier: Readonly<{} & { interval: number; intervalErrorMultiplier: number; }>; jobsRefresh: Readonly<{} & { interval: number; intervalErrorMultiplier: number; }>; }>; }>>" + ], + "description": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 69 } } ], @@ -859,7 +881,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 58 + "lineNumber": 69 } }, { @@ -895,7 +917,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 63 + "lineNumber": 79 } } ], @@ -903,7 +925,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 63 + "lineNumber": 79 } }, { @@ -939,7 +961,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 71 + "lineNumber": 93 } } ], @@ -947,7 +969,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 71 + "lineNumber": 93 } }, { @@ -963,7 +985,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 79 + "lineNumber": 106 } }, { @@ -979,7 +1001,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 90 + "lineNumber": 117 } }, { @@ -995,7 +1017,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 97 + "lineNumber": 124 } }, { @@ -1031,7 +1053,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 104 + "lineNumber": 131 } } ], @@ -1039,7 +1061,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 104 + "lineNumber": 131 } }, { @@ -1057,7 +1079,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 112 + "lineNumber": 139 } }, { @@ -1080,7 +1102,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 132 + "lineNumber": 159 } }, { @@ -1104,7 +1126,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 142 + "lineNumber": 169 } }, { @@ -1127,7 +1149,76 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 150 + "lineNumber": 177 + } + }, + { + "id": "def-server.ReportingCore.scheduleTask", + "type": "Function", + "label": "scheduleTask", + "signature": [ + "(report: ", + { + "pluginId": "reporting", + "scope": "server", + "docId": "kibReportingPluginApi", + "section": "def-server.ReportTaskParams", + "text": "ReportTaskParams" + }, + "<", + { + "pluginId": "reporting", + "scope": "server", + "docId": "kibReportingPluginApi", + "section": "def-server.BasePayload", + "text": "BasePayload" + }, + ">) => Promise<", + { + "pluginId": "taskManager", + "scope": "server", + "docId": "kibTaskManagerPluginApi", + "section": "def-server.ConcreteTaskInstance", + "text": "ConcreteTaskInstance" + }, + ">" + ], + "description": [], + "children": [ + { + "type": "Object", + "label": "report", + "isRequired": true, + "signature": [ + { + "pluginId": "reporting", + "scope": "server", + "docId": "kibReportingPluginApi", + "section": "def-server.ReportTaskParams", + "text": "ReportTaskParams" + }, + "<", + { + "pluginId": "reporting", + "scope": "server", + "docId": "kibReportingPluginApi", + "section": "def-server.BasePayload", + "text": "BasePayload" + }, + ">" + ], + "description": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 181 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 181 } }, { @@ -1151,7 +1242,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 154 + "lineNumber": 185 } }, { @@ -1175,7 +1266,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 158 + "lineNumber": 189 } }, { @@ -1199,7 +1290,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 168 + "lineNumber": 199 } }, { @@ -1222,7 +1313,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 177 + "lineNumber": 208 } }, { @@ -1245,7 +1336,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 184 + "lineNumber": 216 } }, { @@ -1291,7 +1382,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 193 + "lineNumber": 225 } } ], @@ -1299,7 +1390,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 193 + "lineNumber": 225 } }, { @@ -1344,7 +1435,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 199 + "lineNumber": 231 } }, { @@ -1363,7 +1454,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 199 + "lineNumber": 231 } } ], @@ -1371,7 +1462,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 199 + "lineNumber": 231 } }, { @@ -1409,7 +1500,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 213 + "lineNumber": 245 } }, { @@ -1422,7 +1513,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 213 + "lineNumber": 245 } }, { @@ -1441,7 +1532,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 213 + "lineNumber": 245 } } ], @@ -1449,7 +1540,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 213 + "lineNumber": 245 } }, { @@ -1502,7 +1593,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 233 + "lineNumber": 265 } }, { @@ -1521,7 +1612,7 @@ "description": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 233 + "lineNumber": 265 } } ], @@ -1529,13 +1620,137 @@ "returnComment": [], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 233 + "lineNumber": 265 + } + }, + { + "id": "def-server.ReportingCore.getDataService", + "type": "Function", + "label": "getDataService", + "signature": [ + "() => Promise<", + { + "pluginId": "data", + "scope": "server", + "docId": "kibDataPluginApi", + "section": "def-server.DataPluginStart", + "text": "DataPluginStart" + }, + ">" + ], + "description": [], + "children": [], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 275 + } + }, + { + "id": "def-server.ReportingCore.getEsClient", + "type": "Function", + "label": "getEsClient", + "signature": [ + "() => Promise<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.IClusterClient", + "text": "IClusterClient" + }, + ">" + ], + "description": [], + "children": [], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 280 + } + }, + { + "id": "def-server.ReportingCore.trackReport", + "type": "Function", + "label": "trackReport", + "signature": [ + "(reportId: string) => void" + ], + "description": [], + "children": [ + { + "type": "string", + "label": "reportId", + "isRequired": true, + "signature": [ + "string" + ], + "description": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 285 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 285 + } + }, + { + "id": "def-server.ReportingCore.untrackReport", + "type": "Function", + "label": "untrackReport", + "signature": [ + "(reportId: string) => void" + ], + "description": [], + "children": [ + { + "type": "string", + "label": "reportId", + "isRequired": true, + "signature": [ + "string" + ], + "description": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 289 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 289 + } + }, + { + "id": "def-server.ReportingCore.countConcurrentReports", + "type": "Function", + "label": "countConcurrentReports", + "signature": [ + "() => number" + ], + "description": [], + "children": [], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/reporting/server/core.ts", + "lineNumber": 293 } } ], "source": { "path": "x-pack/plugins/reporting/server/core.ts", - "lineNumber": 50 + "lineNumber": 58 }, "initialIsOpen": false }, @@ -2230,4 +2445,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts index a1215836f9c5f..65fef2e4d030f 100644 --- a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts +++ b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts @@ -97,8 +97,7 @@ export const getTopNavLinks = ({ const sharingData = await getSharingData( searchSource, state.appStateContainer.getState(), - services.uiSettings, - getFieldCounts + services.uiSettings ); services.share.toggleShareContextMenu({ anchorElement, diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts index 5e0e48e619a27..ebb1946b524cd 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts @@ -11,59 +11,130 @@ import { getSharingData, showPublicUrlSwitch } from './get_sharing_data'; import { IUiSettingsClient } from 'kibana/public'; import { createSearchSourceMock } from '../../../../data/common/search/search_source/mocks'; import { indexPatternMock } from '../../__mocks__/index_pattern'; -import { SORT_DEFAULT_ORDER_SETTING } from '../../../common'; +import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; +import { IndexPattern } from 'src/plugins/data/public'; describe('getSharingData', () => { + let mockConfig: IUiSettingsClient; + + beforeEach(() => { + mockConfig = ({ + get: (key: string) => { + if (key === SORT_DEFAULT_ORDER_SETTING) { + return 'desc'; + } + if (key === DOC_HIDE_TIME_COLUMN_SETTING) { + return false; + } + return false; + }, + } as unknown) as IUiSettingsClient; + }); + test('returns valid data for sharing', async () => { const searchSourceMock = createSearchSourceMock({ index: indexPatternMock }); + const result = await getSharingData(searchSourceMock, { columns: [] }, mockConfig); + expect(result).toMatchInlineSnapshot(` + Object { + "searchSource": Object { + "index": "the-index-pattern-id", + "sort": Array [ + Object { + "_score": "desc", + }, + ], + }, + } + `); + }); + + test('fields have prepended timeField', async () => { + const index = { ...indexPatternMock } as IndexPattern; + index.timeFieldName = 'cool-timefield'; + + const searchSourceMock = createSearchSourceMock({ index }); const result = await getSharingData( searchSourceMock, - { columns: [] }, - ({ - get: (key: string) => { - if (key === SORT_DEFAULT_ORDER_SETTING) { - return 'desc'; - } - return false; - }, - } as unknown) as IUiSettingsClient, - () => Promise.resolve({}) + { + columns: [ + 'cool-field-1', + 'cool-field-2', + 'cool-field-3', + 'cool-field-4', + 'cool-field-5', + 'cool-field-6', + ], + }, + mockConfig ); expect(result).toMatchInlineSnapshot(` Object { - "conflictedTypesFields": Array [], - "fields": Array [], - "indexPatternId": "the-index-pattern-id", - "metaFields": Array [ - "_index", - "_score", + "searchSource": Object { + "fields": Array [ + "cool-timefield", + "cool-field-1", + "cool-field-2", + "cool-field-3", + "cool-field-4", + "cool-field-5", + "cool-field-6", + ], + "index": "the-index-pattern-id", + "sort": Array [ + Object { + "_doc": "desc", + }, + ], + }, + } + `); + }); + + test('fields conditionally do not have prepended timeField', async () => { + mockConfig = ({ + get: (key: string) => { + if (key === DOC_HIDE_TIME_COLUMN_SETTING) { + return true; + } + return false; + }, + } as unknown) as IUiSettingsClient; + + const index = { ...indexPatternMock } as IndexPattern; + index.timeFieldName = 'cool-timefield'; + + const searchSourceMock = createSearchSourceMock({ index }); + const result = await getSharingData( + searchSourceMock, + { + columns: [ + 'cool-field-1', + 'cool-field-2', + 'cool-field-3', + 'cool-field-4', + 'cool-field-5', + 'cool-field-6', ], - "searchRequest": Object { - "body": Object { - "_source": Object {}, - "fields": Array [], - "query": Object { - "bool": Object { - "filter": Array [], - "must": Array [], - "must_not": Array [], - "should": Array [], - }, + }, + mockConfig + ); + expect(result).toMatchInlineSnapshot(` + Object { + "searchSource": Object { + "fields": Array [ + "cool-field-1", + "cool-field-2", + "cool-field-3", + "cool-field-4", + "cool-field-5", + "cool-field-6", + ], + "index": "the-index-pattern-id", + "sort": Array [ + Object { + "_doc": false, }, - "runtime_mappings": Object {}, - "script_fields": Object {}, - "sort": Array [ - Object { - "_score": Object { - "order": "desc", - }, - }, - ], - "stored_fields": Array [ - "*", - ], - }, - "index": "the-index-pattern-title", + ], }, } `); diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.ts index 2455589cf69fc..f0e07ccc38deb 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.ts @@ -6,57 +6,28 @@ * Side Public License, v 1. */ -import { Capabilities, IUiSettingsClient } from 'kibana/public'; +import type { Capabilities, IUiSettingsClient } from 'kibana/public'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; import { getSortForSearchSource } from '../angular/doc_table'; import { ISearchSource } from '../../../../data/common'; import { AppState } from '../angular/discover_state'; -import { SortOrder } from '../../saved_searches/types'; - -const getSharingDataFields = async ( - getFieldCounts: () => Promise>, - selectedFields: string[], - timeFieldName: string, - hideTimeColumn: boolean -) => { - if ( - selectedFields.length === 0 || - (selectedFields.length === 1 && selectedFields[0] === '_source') - ) { - const fieldCounts = await getFieldCounts(); - return { - searchFields: undefined, - selectFields: Object.keys(fieldCounts).sort(), - }; - } - - const fields = - timeFieldName && !hideTimeColumn ? [timeFieldName, ...selectedFields] : selectedFields; - return { - searchFields: fields, - selectFields: fields, - }; -}; +import type { SavedSearch, SortOrder } from '../../saved_searches/types'; /** * Preparing data to share the current state as link or CSV/Report */ export async function getSharingData( currentSearchSource: ISearchSource, - state: AppState, - config: IUiSettingsClient, - getFieldCounts: () => Promise> + state: AppState | SavedSearch, + config: IUiSettingsClient ) { const searchSource = currentSearchSource.createCopy(); const index = searchSource.getField('index')!; + const fields = { + fields: searchSource.getField('fields'), + fieldsFromSource: searchSource.getField('fieldsFromSource'), + }; - const { searchFields, selectFields } = await getSharingDataFields( - getFieldCounts, - state.columns || [], - index.timeFieldName || '', - config.get(DOC_HIDE_TIME_COLUMN_SETTING) - ); - searchSource.setField('fieldsFromSource', searchFields); searchSource.setField( 'sort', getSortForSearchSource(state.sort as SortOrder[], index, config.get(SORT_DEFAULT_ORDER_SETTING)) @@ -66,17 +37,27 @@ export async function getSharingData( searchSource.removeField('aggs'); searchSource.removeField('size'); - const body = await searchSource.getSearchRequestBody(); + // fields get re-set to match the saved search columns + let columns = state.columns || []; + + if (columns && columns.length > 0) { + // conditionally add the time field column: + let timeFieldName: string | undefined; + const hideTimeColumn = config.get(DOC_HIDE_TIME_COLUMN_SETTING); + if (!hideTimeColumn && index && index.timeFieldName) { + timeFieldName = index.timeFieldName; + } + if (timeFieldName && !columns.includes(timeFieldName)) { + columns = [timeFieldName, ...columns]; + } + + // if columns were selected in the saved search, use them for the searchSource's fields + const fieldsKey = fields.fieldsFromSource ? 'fieldsFromSource' : 'fields'; + searchSource.setField(fieldsKey, columns); + } return { - searchRequest: { - index: index.title, - body, - }, - fields: selectFields, - metaFields: index.metaFields, - conflictedTypesFields: index.fields.filter((f) => f.type === 'conflict').map((f) => f.name), - indexPatternId: index.id, + searchSource: searchSource.getSerializedFields(true), }; } diff --git a/x-pack/plugins/reporting/common/constants.ts b/x-pack/plugins/reporting/common/constants.ts index e3f58dd20cecb..2a95557473fc0 100644 --- a/x-pack/plugins/reporting/common/constants.ts +++ b/x-pack/plugins/reporting/common/constants.ts @@ -50,6 +50,7 @@ export const KBN_SCREENSHOT_HEADER_BLOCK_LIST_STARTS_WITH_PATTERN = ['proxy-']; export const UI_SETTINGS_CUSTOM_PDF_LOGO = 'xpackReporting:customPdfLogo'; export const UI_SETTINGS_CSV_SEPARATOR = 'csv:separator'; export const UI_SETTINGS_CSV_QUOTE_VALUES = 'csv:quoteValues'; +export const UI_SETTINGS_DATEFORMAT_TZ = 'dateFormat:tz'; export const LAYOUT_TYPES = { PRESERVE_LAYOUT: 'preserve_layout', @@ -57,13 +58,16 @@ export const LAYOUT_TYPES = { }; // Export Type Definitions +export const CSV_REPORT_TYPE = 'CSV'; +export const CSV_JOB_TYPE = 'csv_searchsource'; + export const PDF_REPORT_TYPE = 'printablePdf'; export const PDF_JOB_TYPE = 'printable_pdf'; export const PNG_REPORT_TYPE = 'PNG'; export const PNG_JOB_TYPE = 'PNG'; -export const CSV_FROM_SAVEDOBJECT_JOB_TYPE = 'csv_from_savedobject'; +export const CSV_SEARCHSOURCE_IMMEDIATE_TYPE = 'csv_searchsource_immediate'; // This is deprecated because it lacks support for runtime fields // but the extension points are still needed for pre-existing scripted automation, until 8.0 @@ -86,9 +90,9 @@ export const API_BASE_GENERATE = `${API_BASE_URL}/generate`; export const API_LIST_URL = `${API_BASE_URL}/jobs`; export const API_DIAGNOSE_URL = `${API_BASE_URL}/diagnose`; -// hacky endpoint +// hacky endpoint: download CSV without queueing a report export const API_BASE_URL_V1 = '/api/reporting/v1'; // -export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv/saved-object`; +export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv_searchsource`; // Management UI route export const REPORTING_MANAGEMENT_HOME = '/app/management/insightsAndAlerting/reporting'; diff --git a/x-pack/plugins/reporting/common/types.ts b/x-pack/plugins/reporting/common/types.ts index 459ca8892bd9e..22ed732d4dd4d 100644 --- a/x-pack/plugins/reporting/common/types.ts +++ b/x-pack/plugins/reporting/common/types.ts @@ -47,9 +47,10 @@ export interface ReportDocumentHead { export interface TaskRunResult { content_type: string | null; content: string | null; - csv_contains_formulas?: boolean; size: number; + csv_contains_formulas?: boolean; max_size_reached?: boolean; + needs_sorting?: boolean; warnings?: string[]; } diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx index 6f6cf2dc9351b..399b503fe48d3 100644 --- a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx @@ -11,11 +11,7 @@ import React, { Component, ReactElement } from 'react'; import { ToastsSetup } from 'src/core/public'; import url from 'url'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; -import { - CSV_REPORT_TYPE_DEPRECATED, - PDF_REPORT_TYPE, - PNG_REPORT_TYPE, -} from '../../common/constants'; +import { CSV_REPORT_TYPE, PDF_REPORT_TYPE, PNG_REPORT_TYPE } from '../../common/constants'; import { BaseParams } from '../../common/types'; import { ReportingAPIClient } from '../lib/reporting_api_client'; @@ -177,8 +173,8 @@ class ReportingPanelContentUi extends Component { switch (this.props.reportType) { case PDF_REPORT_TYPE: return 'PDF'; - case 'csv': - return CSV_REPORT_TYPE_DEPRECATED; + case 'csv_searchsource': + return CSV_REPORT_TYPE; case 'png': return PNG_REPORT_TYPE; default: diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts index f452719e91713..4e1b9ccd2642f 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts @@ -52,7 +52,20 @@ describe('GetCsvReportPanelAction', () => { context = { embeddable: { type: 'search', - getSavedSearch: () => ({ id: 'lebowski' }), + getSavedSearch: () => { + const searchSource = { + createCopy: () => searchSource, + removeField: jest.fn(), + setField: jest.fn(), + getField: jest.fn().mockImplementation((key: string) => { + if (key === 'index') { + return 'my-test-index-*'; + } + }), + getSerializedFields: jest.fn().mockImplementation(() => ({})), + }; + return { searchSource }; + }, getTitle: () => `The Dude`, getInspectorAdapters: () => null, getInput: () => ({ diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index cc1da146eff32..d440edc3f3fe9 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; import { i18n } from '@kbn/i18n'; -import _ from 'lodash'; import moment from 'moment-timezone'; import { CoreSetup } from 'src/core/public'; import { + loadSharingDataHelpers, ISearchEmbeddable, + SavedSearch, SEARCH_EMBEDDABLE_TYPE, } from '../../../../../src/plugins/discover/public'; import { IEmbeddable, ViewMode } from '../../../../../src/plugins/embeddable/public'; @@ -21,6 +21,7 @@ import { } from '../../../../../src/plugins/ui_actions/public'; import { LicensingPluginSetup } from '../../../licensing/public'; import { API_GENERATE_IMMEDIATE, CSV_REPORTING_ACTION } from '../../common/constants'; +import { JobParamsDownloadCSV } from '../../server/export_types/csv_searchsource_immediate/types'; import { checkLicense } from '../lib/license_check'; function isSavedSearchEmbeddable( @@ -61,17 +62,16 @@ export class GetCsvReportPanelAction implements ActionDefinition }); } - public getSearchRequestBody({ searchEmbeddable }: { searchEmbeddable: any }) { - const adapters = searchEmbeddable.getInspectorAdapters(); - if (!adapters) { - return {}; - } - - if (adapters.requests.requests.length === 0) { - return {}; - } + public async getSearchSource(savedSearch: SavedSearch, embeddable: ISearchEmbeddable) { + const { getSharingData } = await loadSharingDataHelpers(); + const searchSource = savedSearch.searchSource.createCopy(); + const { searchSource: serializedSearchSource } = await getSharingData( + searchSource, + savedSearch, // TODO: get unsaved state (using embeddale.searchScope): https://github.com/elastic/kibana/issues/43977 + this.core.uiSettings + ); - return searchEmbeddable.getSavedSearch().searchSource.getSearchRequestBody(); + return serializedSearchSource; } public isCompatible = async (context: ActionContext) => { @@ -95,34 +95,18 @@ export class GetCsvReportPanelAction implements ActionDefinition return; } - const { - timeRange: { to, from }, - } = embeddable.getInput(); + const savedSearch = embeddable.getSavedSearch(); + const searchSource = await this.getSearchSource(savedSearch, embeddable); - const searchEmbeddable = embeddable; - const searchRequestBody = await this.getSearchRequestBody({ searchEmbeddable }); - const state = _.pick(searchRequestBody, ['sort', 'docvalue_fields', 'query']); const kibanaTimezone = this.core.uiSettings.get('dateFormat:tz'); + const browserTimezone = kibanaTimezone === 'Browser' ? moment.tz.guess() : kibanaTimezone; + const immediateJobParams: JobParamsDownloadCSV = { + searchSource, + browserTimezone, + title: savedSearch.title, + }; - const id = `search:${embeddable.getSavedSearch().id}`; - const timezone = kibanaTimezone === 'Browser' ? moment.tz.guess() : kibanaTimezone; - const fromTime = dateMath.parse(from); - const toTime = dateMath.parse(to, { roundUp: true }); - - if (!fromTime || !toTime) { - return this.onGenerationFail( - new Error(`Invalid time range: From: ${fromTime}, To: ${toTime}`) - ); - } - - const body = JSON.stringify({ - timerange: { - min: fromTime.format(), - max: toTime.format(), - timezone, - }, - state, - }); + const body = JSON.stringify(immediateJobParams); this.isDownloading = true; @@ -137,11 +121,11 @@ export class GetCsvReportPanelAction implements ActionDefinition }); await this.core.http - .post(`${API_GENERATE_IMMEDIATE}/${id}`, { body }) + .post(`${API_GENERATE_IMMEDIATE}`, { body }) .then((rawResponse: string) => { this.isDownloading = false; - const download = `${embeddable.getSavedSearch().title}.csv`; + const download = `${savedSearch.title}.csv`; const blob = new Blob([rawResponse], { type: 'text/csv;charset=utf-8;' }); // Hack for IE11 Support diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx index 31c86ae4c5669..97433f7a4f0c1 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx @@ -11,10 +11,8 @@ import React from 'react'; import { IUiSettingsClient, ToastsSetup } from 'src/core/public'; import { ShareContext } from '../../../../../src/plugins/share/public'; import { LicensingPluginSetup } from '../../../licensing/public'; -import { - JobParamsDeprecatedCSV, - SearchRequestDeprecatedCSV, -} from '../../server/export_types/csv/types'; +import { CSV_JOB_TYPE } from '../../common/constants'; +import { JobParamsCSV } from '../../server/export_types/csv_searchsource/types'; import { ReportingPanelContent } from '../components/reporting_panel_content_lazy'; import { checkLicense } from '../lib/license_check'; import { ReportingAPIClient } from '../lib/reporting_api_client'; @@ -56,22 +54,18 @@ export const csvReportingProvider = ({ objectType, objectId, sharingData, - isDirty, onClose, + isDirty, }: ShareContext) => { if ('search' !== objectType) { return []; } - const jobParams: JobParamsDeprecatedCSV = { + const jobParams: JobParamsCSV = { browserTimezone, - objectType, title: sharingData.title as string, - indexPatternId: sharingData.indexPatternId as string, - searchRequest: sharingData.searchRequest as SearchRequestDeprecatedCSV, - fields: sharingData.fields as string[], - metaFields: sharingData.metaFields as string[], - conflictedTypesFields: sharingData.conflictedTypesFields as string[], + objectType, + searchSource: sharingData.searchSource, }; const getJobParams = () => jobParams; @@ -99,7 +93,7 @@ export const csvReportingProvider = ({ ; @@ -41,10 +43,12 @@ export interface ReportingInternalSetup { export interface ReportingInternalStart { browserDriverFactory: HeadlessChromiumDriverFactory; - esqueue: ESQueueInstance; store: ReportingStore; savedObjects: SavedObjectsServiceStart; uiSettings: UiSettingsServiceStart; + esClient: IClusterClient; + data: DataPluginStart; + esqueue: ESQueueInstance; } export class ReportingCore { @@ -155,6 +159,10 @@ export class ReportingCore { return (await this.getPluginStartDeps()).esqueue; } + public async getStore() { + return (await this.getPluginStartDeps()).store; + } + public async getLicenseInfo() { const { licensing } = this.getPluginSetupDeps(); return await licensing.license$ @@ -181,6 +189,7 @@ export class ReportingCore { return this.pluginSetupDeps; } + // NOTE: Uses the Legacy API public getElasticsearchService() { return this.getPluginSetupDeps().elasticsearch; } @@ -239,4 +248,14 @@ export class ReportingCore { const savedObjectsClient = await this.getSavedObjectsClient(request); return await this.getUiSettingsServiceFactory(savedObjectsClient); } + + public async getDataService() { + const startDeps = await this.getPluginStartDeps(); + return startDeps.data; + } + + public async getEsClient() { + const startDeps = await this.getPluginStartDeps(); + return startDeps.esClient; + } } diff --git a/x-pack/plugins/reporting/server/export_types/common/index.ts b/x-pack/plugins/reporting/server/export_types/common/index.ts index 1003ecf83601c..8832577281bb2 100644 --- a/x-pack/plugins/reporting/server/export_types/common/index.ts +++ b/x-pack/plugins/reporting/server/export_types/common/index.ts @@ -12,7 +12,6 @@ export { omitBlockedHeaders } from './omit_blocked_headers'; export { validateUrls } from './validate_urls'; export interface TimeRangeParams { - timezone: string; min?: Date | string | number | null; max?: Date | string | number | null; } diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/check_cells_for_formulas.ts b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/check_cells_for_formulas.ts index 942739f0d9945..f650bbaed1271 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/check_cells_for_formulas.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/check_cells_for_formulas.ts @@ -6,7 +6,7 @@ */ import { pick, keys, values, some } from 'lodash'; -import { cellHasFormulas } from './cell_has_formula'; +import { cellHasFormulas } from '../../csv_searchsource/generate_csv/cell_has_formula'; interface IFlattened { [header: string]: string; diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/index.ts b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/index.ts index ed05180501e32..629a81df350be 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/index.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/index.ts @@ -13,15 +13,18 @@ import { CSV_BOM_CHARS } from '../../../../common/constants'; import { byteSizeValueToNumber } from '../../../../common/schema_utils'; import { LevelLogger } from '../../../lib'; import { getFieldFormats } from '../../../services'; -import { IndexPatternSavedObjectDeprecatedCSV, SavedSearchGeneratorResult } from '../types'; +import { createEscapeValue } from '../../csv_searchsource/generate_csv/escape_value'; +import { MaxSizeStringBuilder } from '../../csv_searchsource/generate_csv/max_size_string_builder'; +import { + IndexPatternSavedObjectDeprecatedCSV, + SavedSearchGeneratorResultDeprecatedCSV, +} from '../types'; import { checkIfRowsHaveFormulas } from './check_cells_for_formulas'; -import { createEscapeValue } from './escape_value'; import { fieldFormatMapFactory } from './field_format_map'; import { createFlattenHit } from './flatten_hit'; import { createFormatCsvValues } from './format_csv_values'; import { getUiSettings } from './get_ui_settings'; import { createHitIterator, EndpointCaller } from './hit_iterator'; -import { MaxSizeStringBuilder } from './max_size_string_builder'; interface SearchRequest { index: string; @@ -55,7 +58,7 @@ export function createGenerateCsv(logger: LevelLogger) { uiSettingsClient: IUiSettingsClient, callEndpoint: EndpointCaller, cancellationToken: CancellationToken - ): Promise { + ): Promise { const settings = await getUiSettings(job.browserTimezone, uiSettingsClient, config, logger); const escapeValue = createEscapeValue(settings.quoteValues, settings.escapeFormulaValues); const bom = config.get('csv', 'useByteOrderMarkEncoding') ? CSV_BOM_CHARS : ''; diff --git a/x-pack/plugins/reporting/server/export_types/csv/types.d.ts b/x-pack/plugins/reporting/server/export_types/csv/types.d.ts index 4c4f33d0ee9f7..604d451d822b6 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/types.d.ts @@ -77,15 +77,10 @@ type FormatsMapDeprecatedCSV = Map< } >; -export interface SavedSearchGeneratorResult { +export interface SavedSearchGeneratorResultDeprecatedCSV { content: string; size: number; maxSizeReached: boolean; csvContainsFormulas?: boolean; warnings: string[]; } - -export interface CsvResultFromSearch { - type: string; - result: SavedSearchGeneratorResult; -} diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts deleted file mode 100644 index b27c244aa11ae..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts +++ /dev/null @@ -1,96 +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 { notFound, notImplemented } from '@hapi/boom'; -import { get } from 'lodash'; -import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; -import { CsvFromSavedObjectRequest } from '../../routes/generate_from_savedobject_immediate'; -import { CreateJobFnFactory } from '../../types'; -import { - JobParamsPanelCsv, - JobPayloadPanelCsv, - SavedObject, - SavedObjectReference, - SavedObjectServiceError, - VisObjectAttributesJSON, -} from './types'; -import type { ReportingRequestHandlerContext } from '../../types'; - -export type ImmediateCreateJobFn = ( - jobParams: JobParamsPanelCsv, - context: ReportingRequestHandlerContext, - req: CsvFromSavedObjectRequest -) => Promise; - -export const createJobFnFactory: CreateJobFnFactory = function createJobFactoryFn( - reporting, - parentLogger -) { - const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'create-job']); - - return async function createJob(jobParams, context, req) { - const { savedObjectType, savedObjectId } = jobParams; - - const panel = await Promise.resolve() - .then(() => context.core.savedObjects.client.get(savedObjectType, savedObjectId)) - .then(async (savedObject: SavedObject) => { - const { attributes, references } = savedObject; - const { kibanaSavedObjectMeta: kibanaSavedObjectMetaJSON } = attributes; - const { timerange } = req.body; - - if (!kibanaSavedObjectMetaJSON) { - throw new Error('Could not parse saved object data!'); - } - - const kibanaSavedObjectMeta = { - ...kibanaSavedObjectMetaJSON, - searchSource: JSON.parse(kibanaSavedObjectMetaJSON.searchSourceJSON), - }; - - const { visState: visStateJSON } = attributes as VisObjectAttributesJSON; - if (visStateJSON) { - throw notImplemented('Visualization types are not yet implemented'); - } - - // saved search type - const { searchSource } = kibanaSavedObjectMeta; - if (!searchSource || !references) { - throw new Error('The saved search object is missing configuration fields!'); - } - - const indexPatternMeta = references.find( - (ref: SavedObjectReference) => ref.type === 'index-pattern' - ); - if (!indexPatternMeta) { - throw new Error('Could not find index pattern for the saved search!'); - } - - return { - attributes: { - ...attributes, - kibanaSavedObjectMeta: { searchSource }, - }, - indexPatternSavedObjectId: indexPatternMeta.id, - timerange, - }; - }) - .catch((err: Error) => { - const boomErr = (err as unknown) as { isBoom: boolean }; - if (boomErr.isBoom) { - throw err; - } - const errPayload: SavedObjectServiceError = get(err, 'output.payload', { statusCode: 0 }); - if (errPayload.statusCode === 404) { - throw notFound(errPayload.message); - } - logger.error(err); - throw new Error(`Unable to create a job from saved object data! Error: ${err}`); - }); - - return { ...jobParams, panel }; - }; -}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts deleted file mode 100644 index b037e72699dd6..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts +++ /dev/null @@ -1,80 +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 { KibanaRequest } from 'src/core/server'; -import { CancellationToken } from '../../../common'; -import { CONTENT_TYPE_CSV, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; -import { TaskRunResult } from '../../lib/tasks'; -import { RunTaskFnFactory } from '../../types'; -import { createGenerateCsv } from '../csv/generate_csv'; -import { getGenerateCsvParams } from './lib/get_csv_job'; -import { JobPayloadPanelCsv } from './types'; -import type { ReportingRequestHandlerContext } from '../../types'; - -/* - * ImmediateExecuteFn receives the job doc payload because the payload was - * generated in the ScheduleFn - */ -export type ImmediateExecuteFn = ( - jobId: null, - job: JobPayloadPanelCsv, - context: ReportingRequestHandlerContext, - req: KibanaRequest -) => Promise; - -export const runTaskFnFactory: RunTaskFnFactory = function executeJobFactoryFn( - reporting, - parentLogger -) { - const config = reporting.getConfig(); - const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'execute-job']); - - return async function runTask(jobId, jobPayload, context, req) { - const generateCsv = createGenerateCsv(logger); - const { panel } = jobPayload; - - logger.debug(`Execute job generating saved search CSV`); - - const savedObjectsClient = context.core.savedObjects.client; - const uiSettingsClient = await reporting.getUiSettingsServiceFactory(savedObjectsClient); - const job = await getGenerateCsvParams( - jobPayload, - panel, - savedObjectsClient, - uiSettingsClient, - logger - ); - - const elasticsearch = reporting.getElasticsearchService(); - const { callAsCurrentUser } = elasticsearch.legacy.client.asScoped(req); - - const { content, maxSizeReached, size, csvContainsFormulas, warnings } = await generateCsv( - job, - config, - uiSettingsClient, - callAsCurrentUser, - new CancellationToken() // can not be cancelled - ); - - if (csvContainsFormulas) { - logger.warn(`CSV may contain formulas whose values have been escaped`); - } - - if (maxSizeReached) { - logger.warn(`Max size reached: CSV output truncated to ${size} bytes`); - } - - return { - content_type: CONTENT_TYPE_CSV, - content, - max_size_reached: maxSizeReached, - size, - csv_contains_formulas: csvContainsFormulas, - warnings, - }; - }; -}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.test.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.test.ts deleted file mode 100644 index fc6e092962d3b..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.test.ts +++ /dev/null @@ -1,340 +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 { createMockLevelLogger } from '../../../test_helpers'; -import { JobParamsPanelCsv, SearchPanel } from '../types'; -import { getGenerateCsvParams } from './get_csv_job'; - -const logger = createMockLevelLogger(); - -describe('Get CSV Job', () => { - let mockJobParams: JobParamsPanelCsv; - let mockSearchPanel: SearchPanel; - let mockSavedObjectsClient: any; - let mockUiSettingsClient: any; - beforeEach(() => { - mockJobParams = { savedObjectType: 'search', savedObjectId: '234-ididid' }; - mockSearchPanel = { - indexPatternSavedObjectId: '123-indexId', - attributes: { - title: 'my search', - sort: [], - kibanaSavedObjectMeta: { - searchSource: { query: { isSearchSourceQuery: true }, filter: [] }, - }, - uiState: 56, - }, - timerange: { timezone: 'PST', min: 0, max: 100 }, - }; - mockSavedObjectsClient = { - get: () => ({ - attributes: { fields: null, title: null, timeFieldName: null }, - }), - }; - mockUiSettingsClient = { - get: () => ({}), - }; - }); - - it('creates a data structure needed by generateCsv', async () => { - const result = await getGenerateCsvParams( - mockJobParams, - mockSearchPanel, - mockSavedObjectsClient, - mockUiSettingsClient, - logger - ); - expect(result).toMatchInlineSnapshot(` - Object { - "browserTimezone": "PST", - "conflictedTypesFields": Array [], - "fields": Array [], - "indexPatternSavedObject": Object { - "attributes": Object { - "fields": null, - "timeFieldName": null, - "title": null, - }, - "fields": Array [], - "timeFieldName": null, - "title": null, - }, - "metaFields": Array [], - "searchRequest": Object { - "body": Object { - "_source": Object { - "includes": Array [], - }, - "docvalue_fields": undefined, - "query": Object { - "bool": Object { - "filter": Array [], - "must": Array [], - "must_not": Array [], - "should": Array [], - }, - }, - "script_fields": Object {}, - "sort": Array [], - }, - "index": null, - }, - } - `); - }); - - it('uses query and sort from the payload', async () => { - mockJobParams.post = { - state: { - query: ['this is the query'], - sort: ['this is the sort'], - }, - }; - const result = await getGenerateCsvParams( - mockJobParams, - mockSearchPanel, - mockSavedObjectsClient, - mockUiSettingsClient, - logger - ); - expect(result).toMatchInlineSnapshot(` - Object { - "browserTimezone": "PST", - "conflictedTypesFields": Array [], - "fields": Array [], - "indexPatternSavedObject": Object { - "attributes": Object { - "fields": null, - "timeFieldName": null, - "title": null, - }, - "fields": Array [], - "timeFieldName": null, - "title": null, - }, - "metaFields": Array [], - "searchRequest": Object { - "body": Object { - "_source": Object { - "includes": Array [], - }, - "docvalue_fields": undefined, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "0": "this is the query", - }, - ], - "must": Array [], - "must_not": Array [], - "should": Array [], - }, - }, - "script_fields": Object {}, - "sort": Array [ - "this is the sort", - ], - }, - "index": null, - }, - } - `); - }); - - it('uses timerange timezone from the payload', async () => { - mockJobParams.post = { - timerange: { timezone: 'Africa/Timbuktu', min: 0, max: 9000 }, - }; - const result = await getGenerateCsvParams( - mockJobParams, - mockSearchPanel, - mockSavedObjectsClient, - mockUiSettingsClient, - logger - ); - expect(result).toMatchInlineSnapshot(` - Object { - "browserTimezone": "Africa/Timbuktu", - "conflictedTypesFields": Array [], - "fields": Array [], - "indexPatternSavedObject": Object { - "attributes": Object { - "fields": null, - "timeFieldName": null, - "title": null, - }, - "fields": Array [], - "timeFieldName": null, - "title": null, - }, - "metaFields": Array [], - "searchRequest": Object { - "body": Object { - "_source": Object { - "includes": Array [], - }, - "docvalue_fields": undefined, - "query": Object { - "bool": Object { - "filter": Array [], - "must": Array [], - "must_not": Array [], - "should": Array [], - }, - }, - "script_fields": Object {}, - "sort": Array [], - }, - "index": null, - }, - } - `); - }); - - it('uses timerange min and max (numeric) when index pattern has timefieldName', async () => { - mockJobParams.post = { - timerange: { timezone: 'Africa/Timbuktu', min: 0, max: 900000000 }, - }; - mockSavedObjectsClient = { - get: () => ({ - attributes: { fields: null, title: 'test search', timeFieldName: '@test_time' }, - }), - }; - const result = await getGenerateCsvParams( - mockJobParams, - mockSearchPanel, - mockSavedObjectsClient, - mockUiSettingsClient, - logger - ); - expect(result).toMatchInlineSnapshot(` - Object { - "browserTimezone": "Africa/Timbuktu", - "conflictedTypesFields": Array [], - "fields": Array [ - "@test_time", - ], - "indexPatternSavedObject": Object { - "attributes": Object { - "fields": null, - "timeFieldName": "@test_time", - "title": "test search", - }, - "fields": Array [], - "timeFieldName": "@test_time", - "title": "test search", - }, - "metaFields": Array [], - "searchRequest": Object { - "body": Object { - "_source": Object { - "includes": Array [ - "@test_time", - ], - }, - "docvalue_fields": undefined, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@test_time": Object { - "format": "strict_date_time", - "gte": "1970-01-01T00:00:00Z", - "lte": "1970-01-11T10:00:00Z", - }, - }, - }, - ], - "must": Array [], - "must_not": Array [], - "should": Array [], - }, - }, - "script_fields": Object {}, - "sort": Array [], - }, - "index": "test search", - }, - } - `); - }); - - it('uses timerange min and max (string) when index pattern has timefieldName', async () => { - mockJobParams.post = { - timerange: { - timezone: 'Africa/Timbuktu', - min: '1980-01-01T00:00:00Z', - max: '1990-01-01T00:00:00Z', - }, - }; - mockSavedObjectsClient = { - get: () => ({ - attributes: { fields: null, title: 'test search', timeFieldName: '@test_time' }, - }), - }; - const result = await getGenerateCsvParams( - mockJobParams, - mockSearchPanel, - mockSavedObjectsClient, - mockUiSettingsClient, - logger - ); - expect(result).toMatchInlineSnapshot(` - Object { - "browserTimezone": "Africa/Timbuktu", - "conflictedTypesFields": Array [], - "fields": Array [ - "@test_time", - ], - "indexPatternSavedObject": Object { - "attributes": Object { - "fields": null, - "timeFieldName": "@test_time", - "title": "test search", - }, - "fields": Array [], - "timeFieldName": "@test_time", - "title": "test search", - }, - "metaFields": Array [], - "searchRequest": Object { - "body": Object { - "_source": Object { - "includes": Array [ - "@test_time", - ], - }, - "docvalue_fields": undefined, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@test_time": Object { - "format": "strict_date_time", - "gte": "1980-01-01T00:00:00Z", - "lte": "1990-01-01T00:00:00Z", - }, - }, - }, - ], - "must": Array [], - "must_not": Array [], - "should": Array [], - }, - }, - "script_fields": Object {}, - "sort": Array [], - }, - "index": "test search", - }, - } - `); - }); -}); diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.ts deleted file mode 100644 index e4570816e26ff..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.ts +++ /dev/null @@ -1,155 +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 { IUiSettingsClient, SavedObjectsClientContract } from 'kibana/server'; -import { EsQueryConfig } from 'src/plugins/data/server'; -import { esQuery, Filter, Query } from '../../../../../../../src/plugins/data/server'; -import { LevelLogger } from '../../../lib'; -import { TimeRangeParams } from '../../common'; -import { GenerateCsvParams } from '../../csv/generate_csv'; -import { - DocValueFields, - IndexPatternField, - JobParamsPanelCsv, - QueryFilter, - SavedSearchObjectAttributes, - SearchPanel, - SearchSource, -} from '../types'; -import { getDataSource } from './get_data_source'; -import { getFilters } from './get_filters'; - -export const getEsQueryConfig = async (config: IUiSettingsClient) => { - const configs = await Promise.all([ - config.get('query:allowLeadingWildcards'), - config.get('query:queryString:options'), - config.get('courier:ignoreFilterIfFieldNotInIndex'), - ]); - const [allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex] = configs; - return { - allowLeadingWildcards, - queryStringOptions, - ignoreFilterIfFieldNotInIndex, - } as EsQueryConfig; -}; - -/* - * Create a CSV Job object for CSV From SavedObject to use as a job parameter - * for generateCsv - */ -export const getGenerateCsvParams = async ( - jobParams: JobParamsPanelCsv, - panel: SearchPanel, - savedObjectsClient: SavedObjectsClientContract, - uiConfig: IUiSettingsClient, - logger: LevelLogger -): Promise => { - let timerange: TimeRangeParams | null; - if (jobParams.post?.timerange) { - timerange = jobParams.post?.timerange; - } else { - timerange = panel.timerange || null; - } - const { indexPatternSavedObjectId } = panel; - const savedSearchObjectAttr = panel.attributes as SavedSearchObjectAttributes; - const { indexPatternSavedObject } = await getDataSource( - savedObjectsClient, - indexPatternSavedObjectId - ); - const esQueryConfig = await getEsQueryConfig(uiConfig); - - const { - kibanaSavedObjectMeta: { - searchSource: { - filter: [searchSourceFilter], - query: searchSourceQuery, - }, - }, - } = savedSearchObjectAttr as { kibanaSavedObjectMeta: { searchSource: SearchSource } }; - - const { - timeFieldName: indexPatternTimeField, - title: esIndex, - fields: indexPatternFields, - } = indexPatternSavedObject; - - if (!indexPatternFields || indexPatternFields.length === 0) { - logger.error( - new Error( - `No fields are selected in the saved search! Please select fields as columns in the saved search and try again.` - ) - ); - } - - let payloadQuery: QueryFilter | undefined; - let payloadSort: any[] = []; - let docValueFields: DocValueFields[] | undefined; - if (jobParams.post && jobParams.post.state) { - ({ - post: { - state: { query: payloadQuery, sort: payloadSort = [], docvalue_fields: docValueFields }, - }, - } = jobParams); - } - const { includes, combinedFilter } = getFilters( - indexPatternSavedObjectId, - indexPatternTimeField, - timerange, - savedSearchObjectAttr, - searchSourceFilter, - payloadQuery - ); - - const savedSortConfigs = savedSearchObjectAttr.sort; - const sortConfig = [...payloadSort]; - savedSortConfigs.forEach(([savedSortField, savedSortOrder]) => { - sortConfig.push({ [savedSortField]: { order: savedSortOrder } }); - }); - - const scriptFieldsConfig = - indexPatternFields && - indexPatternFields - .filter((f: IndexPatternField) => f.scripted) - .reduce((accum: any, curr: IndexPatternField) => { - return { - ...accum, - [curr.name]: { - script: { - source: curr.script, - lang: curr.lang, - }, - }, - }; - }, {}); - - const searchRequest = { - index: esIndex, - body: { - _source: { includes }, - docvalue_fields: docValueFields, - query: esQuery.buildEsQuery( - // compromise made while factoring out IIndexPattern type - // @ts-expect-error - indexPatternSavedObject, - (searchSourceQuery as unknown) as Query, - (combinedFilter as unknown) as Filter, - esQueryConfig - ), - script_fields: scriptFieldsConfig, - sort: sortConfig, - }, - }; - - return { - browserTimezone: timerange?.timezone, - indexPatternSavedObject, - searchRequest, - fields: includes, - metaFields: [], - conflictedTypesFields: [], - }; -}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_data_source.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_data_source.ts deleted file mode 100644 index d903a1d8ba9e8..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_data_source.ts +++ /dev/null @@ -1,56 +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 { IndexPatternSavedObjectDeprecatedCSV } from '../../csv/types'; -import { SavedObjectReference, SavedSearchObjectAttributesJSON, SearchSource } from '../types'; - -export async function getDataSource( - savedObjectsClient: any, - indexPatternId?: string, - savedSearchObjectId?: string -): Promise<{ - indexPatternSavedObject: IndexPatternSavedObjectDeprecatedCSV; - searchSource: SearchSource | null; -}> { - let indexPatternSavedObject: IndexPatternSavedObjectDeprecatedCSV; - let searchSource: SearchSource | null = null; - - if (savedSearchObjectId) { - try { - const { attributes, references } = (await savedObjectsClient.get( - 'search', - savedSearchObjectId - )) as { attributes: SavedSearchObjectAttributesJSON; references: SavedObjectReference[] }; - searchSource = JSON.parse(attributes.kibanaSavedObjectMeta.searchSourceJSON); - const { id: indexPatternFromSearchId } = references.find( - ({ type }) => type === 'index-pattern' - ) as { id: string }; - ({ indexPatternSavedObject } = await getDataSource( - savedObjectsClient, - indexPatternFromSearchId - )); - return { searchSource, indexPatternSavedObject }; - } catch (err) { - throw new Error(`Could not get saved search info! ${err}`); - } - } - try { - const { attributes } = await savedObjectsClient.get('index-pattern', indexPatternId); - const { fields, title, timeFieldName } = attributes; - const parsedFields = fields ? JSON.parse(fields) : []; - - indexPatternSavedObject = { - fields: parsedFields, - title, - timeFieldName, - attributes, - }; - } catch (err) { - throw new Error(`Could not get index pattern saved object! ${err}`); - } - return { indexPatternSavedObject, searchSource }; -} diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.test.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.test.ts deleted file mode 100644 index ca5bf12e1d510..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.test.ts +++ /dev/null @@ -1,208 +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 { TimeRangeParams } from '../../common'; -import { QueryFilter, SavedSearchObjectAttributes, SearchSourceFilter } from '../types'; -import { getFilters } from './get_filters'; - -interface Args { - indexPatternId: string; - indexPatternTimeField: string | null; - timerange: TimeRangeParams | null; - savedSearchObjectAttr: SavedSearchObjectAttributes; - searchSourceFilter: SearchSourceFilter; - queryFilter: QueryFilter; -} - -describe('CSV from Saved Object: get_filters', () => { - let args: Args; - beforeEach(() => { - args = { - indexPatternId: 'logs-test-*', - indexPatternTimeField: 'testtimestamp', - timerange: { - timezone: 'UTC', - min: '1901-01-01T00:00:00.000Z', - max: '1902-01-01T00:00:00.000Z', - }, - savedSearchObjectAttr: { - title: 'test', - sort: [{ sortField: { order: 'asc' } }], - kibanaSavedObjectMeta: { - searchSource: { - query: { isSearchSourceQuery: true }, - filter: ['hello searchSource filter 1'], - }, - }, - columns: ['larry'], - uiState: null, - }, - searchSourceFilter: { isSearchSourceFilter: true, isFilter: true }, - queryFilter: { isQueryFilter: true, isFilter: true }, - }; - }); - - describe('search', () => { - it('for timebased search', () => { - const filters = getFilters( - args.indexPatternId, - args.indexPatternTimeField, - args.timerange, - args.savedSearchObjectAttr, - args.searchSourceFilter, - args.queryFilter - ); - - expect(filters).toEqual({ - combinedFilter: [ - { - range: { - testtimestamp: { - format: 'strict_date_time', - gte: '1901-01-01T00:00:00Z', - lte: '1902-01-01T00:00:00Z', - }, - }, - }, - { isFilter: true, isSearchSourceFilter: true }, - { isFilter: true, isQueryFilter: true }, - ], - includes: ['testtimestamp', 'larry'], - timezone: 'UTC', - }); - }); - - it('for non-timebased search', () => { - args.indexPatternTimeField = null; - args.timerange = null; - - const filters = getFilters( - args.indexPatternId, - args.indexPatternTimeField, - args.timerange, - args.savedSearchObjectAttr, - args.searchSourceFilter, - args.queryFilter - ); - - expect(filters).toEqual({ - combinedFilter: [ - { isFilter: true, isSearchSourceFilter: true }, - { isFilter: true, isQueryFilter: true }, - ], - includes: ['larry'], - timezone: null, - }); - }); - }); - - describe('errors', () => { - it('throw if timebased and timerange is missing', () => { - args.timerange = null; - - const throwFn = () => - getFilters( - args.indexPatternId, - args.indexPatternTimeField, - args.timerange, - args.savedSearchObjectAttr, - args.searchSourceFilter, - args.queryFilter - ); - - expect(throwFn).toThrow( - 'Time range params are required for index pattern [logs-test-*], using time field [testtimestamp]' - ); - }); - }); - - it('composes the defined filters', () => { - expect( - getFilters( - args.indexPatternId, - args.indexPatternTimeField, - args.timerange, - args.savedSearchObjectAttr, - undefined, - undefined - ) - ).toEqual({ - combinedFilter: [ - { - range: { - testtimestamp: { - format: 'strict_date_time', - gte: '1901-01-01T00:00:00Z', - lte: '1902-01-01T00:00:00Z', - }, - }, - }, - ], - includes: ['testtimestamp', 'larry'], - timezone: 'UTC', - }); - - expect( - getFilters( - args.indexPatternId, - args.indexPatternTimeField, - args.timerange, - args.savedSearchObjectAttr, - undefined, - args.queryFilter - ) - ).toEqual({ - combinedFilter: [ - { - range: { - testtimestamp: { - format: 'strict_date_time', - gte: '1901-01-01T00:00:00Z', - lte: '1902-01-01T00:00:00Z', - }, - }, - }, - { isFilter: true, isQueryFilter: true }, - ], - includes: ['testtimestamp', 'larry'], - timezone: 'UTC', - }); - }); - - describe('timefilter', () => { - it('formats the datetime to the provided timezone', () => { - args.timerange = { - timezone: 'MST', - min: '1901-01-01T00:00:00Z', - max: '1902-01-01T00:00:00Z', - }; - - expect( - getFilters( - args.indexPatternId, - args.indexPatternTimeField, - args.timerange, - args.savedSearchObjectAttr - ) - ).toEqual({ - combinedFilter: [ - { - range: { - testtimestamp: { - format: 'strict_date_time', - gte: '1900-12-31T17:00:00-07:00', - lte: '1901-12-31T17:00:00-07:00', - }, - }, - }, - ], - includes: ['testtimestamp', 'larry'], - timezone: 'MST', - }); - }); - }); -}); diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.ts deleted file mode 100644 index c252b66952360..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.ts +++ /dev/null @@ -1,55 +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 { badRequest } from '@hapi/boom'; -import moment from 'moment-timezone'; -import { TimeRangeParams } from '../../common'; -import { Filter, QueryFilter, SavedSearchObjectAttributes, SearchSourceFilter } from '../types'; - -export function getFilters( - indexPatternId: string, - indexPatternTimeField: string | null, - timerange: TimeRangeParams | null, - savedSearchObjectAttr: SavedSearchObjectAttributes, - searchSourceFilter?: SearchSourceFilter, - queryFilter?: QueryFilter -) { - let includes: string[]; - let timeFilter: any | null; - let timezone: string | null; - - if (indexPatternTimeField) { - if (!timerange || timerange.min == null || timerange.max == null) { - throw badRequest( - `Time range params are required for index pattern [${indexPatternId}], using time field [${indexPatternTimeField}]` - ); - } - - timezone = timerange.timezone; - const { min: gte, max: lte } = timerange; - timeFilter = { - range: { - [indexPatternTimeField]: { - format: 'strict_date_time', - gte: moment.tz(moment(gte), timezone).format(), - lte: moment.tz(moment(lte), timezone).format(), - }, - }, - }; - - const savedSearchCols = savedSearchObjectAttr.columns || []; - includes = [indexPatternTimeField, ...savedSearchCols]; - } else { - includes = savedSearchObjectAttr.columns || []; - timeFilter = null; - timezone = null; - } - - const combinedFilter: Filter[] = [timeFilter, searchSourceFilter, queryFilter].filter(Boolean); // builds an array of defined filters - - return { timezone, combinedFilter, includes }; -} diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts deleted file mode 100644 index a4fbdb69bbbba..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts +++ /dev/null @@ -1,153 +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 { TimeRangeParams } from '../common'; - -export interface FakeRequest { - headers: Record; -} - -export interface JobParamsPanelCsvPost { - timerange?: TimeRangeParams; - state?: any; -} - -export interface SearchPanel { - indexPatternSavedObjectId: string; - attributes: SavedSearchObjectAttributes; - timerange?: TimeRangeParams; -} - -export interface JobPayloadPanelCsv extends JobParamsPanelCsv { - panel: SearchPanel; -} - -export interface JobParamsPanelCsv { - savedObjectType: string; - savedObjectId: string; - post?: JobParamsPanelCsvPost; - visType?: string; -} - -export interface SavedObjectServiceError { - statusCode: number; - error?: string; - message?: string; -} - -export interface SavedObjectMetaJSON { - searchSourceJSON: string; -} - -export interface SavedObjectMeta { - searchSource: SearchSource; -} - -export interface SavedSearchObjectAttributesJSON { - title: string; - sort: any[]; - columns: string[]; - kibanaSavedObjectMeta: SavedObjectMetaJSON; - uiState: any; -} - -export interface SavedSearchObjectAttributes { - title: string; - sort: any[]; - columns?: string[]; - kibanaSavedObjectMeta: SavedObjectMeta; - uiState: any; -} - -export interface VisObjectAttributesJSON { - title: string; - visState: string; // JSON string - type: string; - params: any; - uiStateJSON: string; // also JSON string - aggs: any[]; - sort: any[]; - kibanaSavedObjectMeta: SavedObjectMeta; -} - -export interface VisObjectAttributes { - title: string; - visState: string; // JSON string - type: string; - params: any; - uiState: { - vis: { - params: { - sort: { - columnIndex: string; - direction: string; - }; - }; - }; - }; - aggs: any[]; - sort: any[]; - kibanaSavedObjectMeta: SavedObjectMeta; -} - -export interface SavedObjectReference { - name: string; // should be kibanaSavedObjectMeta.searchSourceJSON.index - type: string; // should be index-pattern - id: string; -} - -export interface SavedObject { - attributes: any; - references: SavedObjectReference[]; -} - -export interface VisPanel { - indexPatternSavedObjectId?: string; - savedSearchObjectId?: string; - attributes: VisObjectAttributes; - timerange: TimeRangeParams; -} - -export interface DocValueFields { - field: string; - format: string; -} - -export interface SearchSourceQuery { - isSearchSourceQuery: boolean; -} - -export interface SearchSource { - query: SearchSourceQuery; - filter: any[]; -} - -/* - * These filter types are stub types to help ensure things get passed to - * non-Typescript functions in the right order. An actual structure is not - * needed because the code doesn't look into the properties; just combines them - * and passes them through to other non-TS modules. - */ -export interface Filter { - isFilter: boolean; -} -export interface TimeFilter extends Filter { - isTimeFilter: boolean; -} -export interface QueryFilter extends Filter { - isQueryFilter: boolean; -} -export interface SearchSourceFilter extends Filter { - isSearchSourceFilter: boolean; -} - -export interface IndexPatternField { - scripted: boolean; - lang?: string; - script?: string; - name: string; -} diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/create_job.ts new file mode 100644 index 0000000000000..a389f2a3252ca --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/create_job.ts @@ -0,0 +1,30 @@ +/* + * 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 { CSV_JOB_TYPE } from '../../../common/constants'; +import { cryptoFactory } from '../../lib'; +import { CreateJobFn, CreateJobFnFactory } from '../../types'; +import { JobParamsCSV, TaskPayloadCSV } from './types'; + +export const createJobFnFactory: CreateJobFnFactory< + CreateJobFn +> = function createJobFactoryFn(reporting, parentLogger) { + const logger = parentLogger.clone([CSV_JOB_TYPE, 'create-job']); + + const config = reporting.getConfig(); + const crypto = cryptoFactory(config.get('encryptionKey')); + + return async function createJob(jobParams, context, request) { + const serializedEncryptedHeaders = await crypto.encrypt(request.headers); + + return { + headers: serializedEncryptedHeaders, + spaceId: reporting.getSpaceId(request, logger), + ...jobParams, + }; + }; +}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.test.ts new file mode 100644 index 0000000000000..1c2e15ebc5d9b --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.test.ts @@ -0,0 +1,75 @@ +/* + * 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. + */ + +jest.mock('./generate_csv/generate_csv', () => ({ + CsvGenerator: class CsvGeneratorMock { + generateData() { + return { + content: 'test\n123', + }; + } + }, +})); + +import nodeCrypto from '@elastic/node-crypto'; +import { ReportingCore } from '../../'; +import { CancellationToken } from '../../../common'; +import { + createMockConfig, + createMockConfigSchema, + createMockLevelLogger, + createMockReportingCore, +} from '../../test_helpers'; +import { runTaskFnFactory } from './execute_job'; + +const logger = createMockLevelLogger(); +const encryptionKey = 'tetkey'; +const headers = { sid: 'cooltestheaders' }; +let encryptedHeaders: string; +let reportingCore: ReportingCore; + +beforeAll(async () => { + const crypto = nodeCrypto({ encryptionKey }); + const config = createMockConfig( + createMockConfigSchema({ + encryptionKey, + csv: { + checkForFormulas: true, + escapeFormulaValues: true, + maxSizeBytes: 180000, + scroll: { size: 500, duration: '30s' }, + }, + }) + ); + + encryptedHeaders = await crypto.encrypt(headers); + + reportingCore = await createMockReportingCore(config); +}); + +test('gets the csv content from job parameters', async () => { + const runTask = runTaskFnFactory(reportingCore, logger); + + const payload = await runTask( + 'cool-job-id', + { + headers: encryptedHeaders, + browserTimezone: 'US/Alaska', + searchSource: {}, + objectType: 'search', + title: 'Test Search', + }, + new CancellationToken() + ); + + expect(payload).toMatchInlineSnapshot(` + Object { + "content": "test + 123", + } + `); +}); diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts new file mode 100644 index 0000000000000..ff50377ab13c5 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts @@ -0,0 +1,49 @@ +/* + * 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 { CSV_JOB_TYPE } from '../../../common/constants'; +import { getFieldFormats } from '../../services'; +import { RunTaskFn, RunTaskFnFactory } from '../../types'; +import { decryptJobHeaders } from '../common'; +import { CsvGenerator } from './generate_csv/generate_csv'; +import { TaskPayloadCSV } from './types'; + +export const runTaskFnFactory: RunTaskFnFactory> = ( + reporting, + parentLogger +) => { + const config = reporting.getConfig(); + + return async function runTask(jobId, job, cancellationToken) { + const logger = parentLogger.clone([CSV_JOB_TYPE, 'execute-job', jobId]); + + const encryptionKey = config.get('encryptionKey'); + const headers = await decryptJobHeaders(encryptionKey, job.headers, logger); + const fakeRequest = reporting.getFakeRequest({ headers }, job.spaceId, logger); + const uiSettings = await reporting.getUiSettingsClient(fakeRequest, logger); + const dataPluginStart = await reporting.getDataService(); + const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory(uiSettings); + + const [es, searchSourceStart] = await Promise.all([ + (await reporting.getEsClient()).asScoped(fakeRequest), + await dataPluginStart.search.searchSource.asScoped(fakeRequest), + ]); + + const clients = { + uiSettings, + data: dataPluginStart.search.asScoped(fakeRequest), + es, + }; + const dependencies = { + searchSourceStart, + fieldFormatsRegistry, + }; + + const csv = new CsvGenerator(job, config, clients, dependencies, cancellationToken, logger); + return await csv.generateData(); + }; +}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/__snapshots__/generate_csv.test.ts.snap b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/__snapshots__/generate_csv.test.ts.snap new file mode 100644 index 0000000000000..62c9ecff830ff --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/__snapshots__/generate_csv.test.ts.snap @@ -0,0 +1,163 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`fields cells can be multi-value 1`] = ` +"\\"_id\\",sku +\\"my-cool-id\\",\\"This is a cool SKU., This is also a cool SKU.\\" +" +`; + +exports[`fields provides top-level underscored fields as columns 1`] = ` +"\\"_id\\",\\"_index\\",date,message +\\"my-cool-id\\",\\"my-cool-index\\",\\"2020-12-31T00:14:28.000Z\\",\\"it's nice to see you\\" +" +`; + +exports[`fields sorts the fields when they are to be used as table column names 1`] = ` +"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",date,\\"message_t\\",\\"message_u\\",\\"message_v\\",\\"message_w\\",\\"message_x\\",\\"message_y\\",\\"message_z\\" +\\"my-cool-id\\",\\"my-cool-index\\",\\"'-\\",\\"'-\\",\\"2020-12-31T00:14:28.000Z\\",\\"test field T\\",\\"test field U\\",\\"test field V\\",\\"test field W\\",\\"test field X\\",\\"test field Y\\",\\"test field Z\\" +" +`; + +exports[`formats a search result to CSV content 1`] = ` +"date,ip,message +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"This is a great message!\\" +" +`; + +exports[`formats an empty search result to CSV content 1`] = ` +"date,ip,message +" +`; + +exports[`formulas can check for formulas, without escaping them 1`] = ` +"date,ip,message +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"=SUM(A1:A2)\\" +" +`; + +exports[`formulas escapes formula values in a cell, doesn't warn the csv contains formulas 1`] = ` +"date,ip,message +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"'=SUM(A1:A2)\\" +" +`; + +exports[`formulas escapes formula values in a header, doesn't warn the csv contains formulas 1`] = ` +"date,ip,\\"'=SUM(A1:A2)\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"This is great data\\" +" +`; + +exports[`uses the scrollId to page all the data 1`] = ` +"date,ip,message +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\" +" +`; + +exports[`warns if max size was reached 1`] = ` +"date,ip,message +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\" +\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\" +" +`; diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/cell_has_formula.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/cell_has_formula.ts similarity index 100% rename from x-pack/plugins/reporting/server/export_types/csv/generate_csv/cell_has_formula.ts rename to x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/cell_has_formula.ts diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/escape_value.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/escape_value.test.ts similarity index 100% rename from x-pack/plugins/reporting/server/export_types/csv/generate_csv/escape_value.test.ts rename to x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/escape_value.test.ts diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/escape_value.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/escape_value.ts similarity index 100% rename from x-pack/plugins/reporting/server/export_types/csv/generate_csv/escape_value.ts rename to x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/escape_value.ts diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts new file mode 100644 index 0000000000000..0193eaaff2c8d --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts @@ -0,0 +1,645 @@ +/* + * 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 * as Rx from 'rxjs'; +import { identity, range } from 'lodash'; +import { IScopedClusterClient, IUiSettingsClient, SearchResponse } from 'src/core/server'; +import { + elasticsearchServiceMock, + savedObjectsClientMock, + uiSettingsServiceMock, +} from 'src/core/server/mocks'; +import { FieldFormatsRegistry, ISearchStartSearchSource } from 'src/plugins/data/common'; +import { searchSourceInstanceMock } from 'src/plugins/data/common/search/search_source/mocks'; +import { IScopedSearchClient } from 'src/plugins/data/server'; +import { dataPluginMock } from 'src/plugins/data/server/mocks'; +import { ReportingConfig } from '../../../'; +import { CancellationToken } from '../../../../common'; +import { + UI_SETTINGS_CSV_QUOTE_VALUES, + UI_SETTINGS_CSV_SEPARATOR, + UI_SETTINGS_DATEFORMAT_TZ, +} from '../../../../common/constants'; +import { + createMockConfig, + createMockConfigSchema, + createMockLevelLogger, +} from '../../../test_helpers'; +import { JobParamsCSV } from '../types'; +import { CsvGenerator } from './generate_csv'; + +const createMockJob = (baseObj: any = {}): JobParamsCSV => ({ + ...baseObj, +}); + +let mockEsClient: IScopedClusterClient; +let mockDataClient: IScopedSearchClient; +let mockConfig: ReportingConfig; +let uiSettingsClient: IUiSettingsClient; + +const searchSourceMock = { ...searchSourceInstanceMock }; +const mockSearchSourceService: jest.Mocked = { + create: jest.fn().mockReturnValue(searchSourceMock), + createEmpty: jest.fn().mockReturnValue(searchSourceMock), +}; +const mockDataClientSearchDefault = jest.fn().mockImplementation( + (): Rx.Observable<{ rawResponse: SearchResponse }> => + Rx.of({ + rawResponse: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, failed: 0, skipped: 0 }, + hits: { + hits: [], + total: 0, + max_score: 0, + }, + }, + }) +); +const mockSearchSourceGetFieldDefault = jest.fn().mockImplementation((key: string) => { + switch (key) { + case 'fields': + return ['date', 'ip', 'message']; + case 'index': + return { + fields: { + getByName: jest.fn().mockImplementation(() => []), + getByType: jest.fn().mockImplementation(() => []), + }, + getFormatterForField: jest.fn(), + }; + } +}); + +const mockFieldFormatsRegistry = ({ + deserialize: jest + .fn() + .mockImplementation(() => ({ id: 'string', convert: jest.fn().mockImplementation(identity) })), +} as unknown) as FieldFormatsRegistry; + +beforeEach(async () => { + mockEsClient = elasticsearchServiceMock.createScopedClusterClient(); + mockDataClient = dataPluginMock.createStartContract().search.asScoped({} as any); + mockDataClient.search = mockDataClientSearchDefault; + + uiSettingsClient = uiSettingsServiceMock + .createStartContract() + .asScopedToClient(savedObjectsClientMock.create()); + uiSettingsClient.get = jest.fn().mockImplementation((key): any => { + switch (key) { + case UI_SETTINGS_CSV_QUOTE_VALUES: + return true; + case UI_SETTINGS_CSV_SEPARATOR: + return ','; + case UI_SETTINGS_DATEFORMAT_TZ: + return 'Browser'; + } + }); + + mockConfig = createMockConfig( + createMockConfigSchema({ + csv: { + checkForFormulas: true, + escapeFormulaValues: true, + maxSizeBytes: 180000, + scroll: { size: 500, duration: '30s' }, + }, + }) + ); + + searchSourceMock.getField = mockSearchSourceGetFieldDefault; +}); + +const logger = createMockLevelLogger(); + +it('formats an empty search result to CSV content', async () => { + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.content).toMatchSnapshot(); + expect(csvResult.csv_contains_formulas).toBe(false); +}); + +it('formats a search result to CSV content', async () => { + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: [ + { + fields: { + date: `["2020-12-31T00:14:28.000Z"]`, + ip: `["110.135.176.89"]`, + message: `["This is a great message!"]`, + }, + }, + ], + total: 1, + }, + }, + }) + ); + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.content).toMatchSnapshot(); + expect(csvResult.csv_contains_formulas).toBe(false); +}); + +const HITS_TOTAL = 100; + +it('calculates the bytes of the content', async () => { + searchSourceMock.getField = jest.fn().mockImplementation((key: string) => { + if (key === 'fields') { + return ['message']; + } + return mockSearchSourceGetFieldDefault(key); + }); + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: range(0, HITS_TOTAL).map((hit, i) => ({ + fields: { + message: ['this is a great message'], + }, + })), + total: HITS_TOTAL, + }, + }, + }) + ); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.size).toBe(2608); + expect(csvResult.max_size_reached).toBe(false); + expect(csvResult.warnings).toEqual([]); +}); + +it('warns if max size was reached', async () => { + const TEST_MAX_SIZE = 500; + + mockConfig = createMockConfig( + createMockConfigSchema({ + csv: { + checkForFormulas: true, + escapeFormulaValues: true, + maxSizeBytes: TEST_MAX_SIZE, + scroll: { size: 500, duration: '30s' }, + }, + }) + ); + + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: range(0, HITS_TOTAL).map((hit, i) => ({ + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: ['super cali fragile istic XPLA docious'], + }, + })), + total: HITS_TOTAL, + }, + }, + }) + ); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.max_size_reached).toBe(true); + expect(csvResult.warnings).toEqual([]); + expect(csvResult.content).toMatchSnapshot(); +}); + +it('uses the scrollId to page all the data', async () => { + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + _scroll_id: 'awesome-scroll-hero', + hits: { + hits: range(0, HITS_TOTAL / 10).map((hit, i) => ({ + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: ['hit from the initial search'], + }, + })), + total: HITS_TOTAL, + }, + }, + }) + ); + mockEsClient.asCurrentUser.scroll = jest.fn().mockResolvedValue({ + body: { + hits: { + hits: range(0, HITS_TOTAL / 10).map((hit, i) => ({ + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: ['hit from a subsequent scroll'], + }, + })), + }, + }, + }); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.warnings).toEqual([]); + expect(csvResult.content).toMatchSnapshot(); +}); + +describe('fields', () => { + it('cells can be multi-value', async () => { + searchSourceMock.getField = jest.fn().mockImplementation((key: string) => { + if (key === 'fields') { + return ['_id', 'sku']; + } + return mockSearchSourceGetFieldDefault(key); + }); + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: [ + { + _id: 'my-cool-id', + _index: 'my-cool-index', + _version: 4, + fields: { + sku: [`This is a cool SKU.`, `This is also a cool SKU.`], + }, + }, + ], + total: 1, + }, + }, + }) + ); + + const generateCsv = new CsvGenerator( + createMockJob({ searchSource: {} }), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchSnapshot(); + }); + + it('provides top-level underscored fields as columns', async () => { + searchSourceMock.getField = jest.fn().mockImplementation((key: string) => { + if (key === 'fields') { + return ['_id', '_index', 'date', 'message']; + } + return mockSearchSourceGetFieldDefault(key); + }); + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: [ + { + _id: 'my-cool-id', + _index: 'my-cool-index', + _version: 4, + fields: { + date: ['2020-12-31T00:14:28.000Z'], + message: [`it's nice to see you`], + }, + }, + ], + total: 1, + }, + }, + }) + ); + + const generateCsv = new CsvGenerator( + createMockJob({ + searchSource: { + query: { query: '', language: 'kuery' }, + sort: [{ '@date': 'desc' }], + index: '93f4bc50-6662-11eb-98bc-f550e2308366', + fields: ['_id', '_index', '@date', 'message'], + filter: [], + }, + }), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchSnapshot(); + expect(csvResult.csv_contains_formulas).toBe(false); + }); + + it('sorts the fields when they are to be used as table column names', async () => { + searchSourceMock.getField = jest.fn().mockImplementation((key: string) => { + if (key === 'fields') { + return ['*']; + } + return mockSearchSourceGetFieldDefault(key); + }); + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: [ + { + _id: 'my-cool-id', + _index: 'my-cool-index', + _version: 4, + fields: { + date: ['2020-12-31T00:14:28.000Z'], + message_z: [`test field Z`], + message_y: [`test field Y`], + message_x: [`test field X`], + message_w: [`test field W`], + message_v: [`test field V`], + message_u: [`test field U`], + message_t: [`test field T`], + }, + }, + ], + total: 1, + }, + }, + }) + ); + + const generateCsv = new CsvGenerator( + createMockJob({ + searchSource: { + query: { query: '', language: 'kuery' }, + sort: [{ '@date': 'desc' }], + index: '93f4bc50-6662-11eb-98bc-f550e2308366', + fields: ['*'], + filter: [], + }, + }), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchSnapshot(); + expect(csvResult.csv_contains_formulas).toBe(false); + }); +}); + +describe('formulas', () => { + const TEST_FORMULA = '=SUM(A1:A2)'; + + it(`escapes formula values in a cell, doesn't warn the csv contains formulas`, async () => { + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: [ + { + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: [TEST_FORMULA], + }, + }, + ], + total: 1, + }, + }, + }) + ); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchSnapshot(); + expect(csvResult.csv_contains_formulas).toBe(false); + }); + + it(`escapes formula values in a header, doesn't warn the csv contains formulas`, async () => { + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: [ + { + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + [TEST_FORMULA]: 'This is great data', + }, + }, + ], + total: 1, + }, + }, + }) + ); + + searchSourceMock.getField = jest.fn().mockImplementation((key: string) => { + if (key === 'fields') { + return ['date', 'ip', TEST_FORMULA]; + } + return mockSearchSourceGetFieldDefault(key); + }); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchSnapshot(); + expect(csvResult.csv_contains_formulas).toBe(false); + }); + + it('can check for formulas, without escaping them', async () => { + mockConfig = createMockConfig( + createMockConfigSchema({ + csv: { + checkForFormulas: true, + escapeFormulaValues: false, + maxSizeBytes: 180000, + scroll: { size: 500, duration: '30s' }, + }, + }) + ); + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + hits: { + hits: [ + { + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: [TEST_FORMULA], + }, + }, + ], + total: 1, + }, + }, + }) + ); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchSnapshot(); + expect(csvResult.csv_contains_formulas).toBe(true); + }); +}); diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts new file mode 100644 index 0000000000000..370fc42921acf --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts @@ -0,0 +1,400 @@ +/* + * 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 { SearchResponse } from 'elasticsearch'; +import { IScopedClusterClient, IUiSettingsClient } from 'src/core/server'; +import { IScopedSearchClient } from 'src/plugins/data/server'; +import { Datatable } from 'src/plugins/expressions/server'; +import { ReportingConfig } from '../../..'; +import { + ES_SEARCH_STRATEGY, + FieldFormat, + FieldFormatConfig, + IFieldFormatsRegistry, + IndexPattern, + ISearchSource, + ISearchStartSearchSource, + SearchFieldValue, + tabifyDocs, +} from '../../../../../../../src/plugins/data/common'; +import { KbnServerError } from '../../../../../../../src/plugins/kibana_utils/server'; +import { CancellationToken } from '../../../../common'; +import { CONTENT_TYPE_CSV } from '../../../../common/constants'; +import { byteSizeValueToNumber } from '../../../../common/schema_utils'; +import { LevelLogger } from '../../../lib'; +import { TaskRunResult } from '../../../lib/tasks'; +import { JobParamsCSV } from '../types'; +import { cellHasFormulas } from './cell_has_formula'; +import { CsvExportSettings, getExportSettings } from './get_export_settings'; +import { MaxSizeStringBuilder } from './max_size_string_builder'; + +interface Clients { + es: IScopedClusterClient; + data: IScopedSearchClient; + uiSettings: IUiSettingsClient; +} + +interface Dependencies { + searchSourceStart: ISearchStartSearchSource; + fieldFormatsRegistry: IFieldFormatsRegistry; +} + +// Function to check if the field name values can be used as the header row +function isPlainStringArray( + fields: SearchFieldValue[] | string | boolean | undefined +): fields is string[] { + let result = true; + if (Array.isArray(fields)) { + fields.forEach((field) => { + if (typeof field !== 'string' || field === '*' || field === '_source') { + result = false; + } + }); + } + return result; +} + +export class CsvGenerator { + private _formatters: Record | null = null; + private csvContainsFormulas = false; + private maxSizeReached = false; + private csvRowCount = 0; + + constructor( + private job: JobParamsCSV, + private config: ReportingConfig, + private clients: Clients, + private dependencies: Dependencies, + private cancellationToken: CancellationToken, + private logger: LevelLogger + ) {} + + private async scan( + index: IndexPattern, + searchSource: ISearchSource, + scrollSettings: CsvExportSettings['scroll'] + ) { + const searchBody = await searchSource.getSearchRequestBody(); + this.logger.debug(`executing search request`); + const searchParams = { + params: { + body: searchBody, + index: index.title, + scroll: scrollSettings.duration, + size: scrollSettings.size, + }, + }; + const results = ( + await this.clients.data.search(searchParams, { strategy: ES_SEARCH_STRATEGY }).toPromise() + ).rawResponse; + + return results; + } + + private async scroll(scrollId: string, scrollSettings: CsvExportSettings['scroll']) { + this.logger.debug(`executing scroll request`); + const results = ( + await this.clients.es.asCurrentUser.scroll({ + scroll: scrollSettings.duration, + scroll_id: scrollId, + }) + ).body as SearchResponse; + return results; + } + + /* + * Load field formats for each field in the list + */ + private getFormatters(table: Datatable) { + if (this._formatters) { + return this._formatters; + } + + // initialize field formats + const formatters: Record = {}; + table.columns.forEach((c) => { + const fieldFormat = this.dependencies.fieldFormatsRegistry.deserialize(c.meta.params); + formatters[c.id] = fieldFormat; + }); + + this._formatters = formatters; + return this._formatters; + } + + private escapeValues(settings: CsvExportSettings) { + return (value: string) => { + if (settings.checkForFormulas && cellHasFormulas(value)) { + this.csvContainsFormulas = true; // set warning if cell value has a formula + } + return settings.escapeValue(value); + }; + } + + // use fields/fieldsFromSource from the searchSource to get the ordering of columns + // otherwise use the table columns as they are + private getFields(searchSource: ISearchSource, table: Datatable): string[] { + const fieldValues: Record = { + fields: searchSource.getField('fields'), + fieldsFromSource: searchSource.getField('fieldsFromSource'), + }; + const fieldSource = fieldValues.fieldsFromSource ? 'fieldsFromSource' : 'fields'; + this.logger.debug(`Getting search source fields from: '${fieldSource}'`); + + const fields = fieldValues[fieldSource]; + // Check if field name values are string[] and if the fields are user-defined + if (isPlainStringArray(fields)) { + return fields; + } + + // Default to using the table column IDs as the fields + const columnIds = table.columns.map((c) => c.id); + // Fields in the API response don't come sorted - they need to be sorted client-side + columnIds.sort(); + return columnIds; + } + + private formatCellValues(formatters: Record) { + return ({ + column: tableColumn, + data: dataTableCell, + }: { + column: string; + data: any; + }): string => { + let cell: string[] | string | object; + // check truthiness to guard against _score, _type, etc + if (tableColumn && dataTableCell) { + try { + cell = formatters[tableColumn].convert(dataTableCell); + } catch (err) { + this.logger.error(err); + cell = '-'; + } + + try { + // expected values are a string of JSON where the value(s) is in an array + cell = JSON.parse(cell); + } catch (e) { + // ignore + } + + // We have to strip singular array values out of their array wrapper, + // So that the value appears the visually the same as seen in Discover + if (Array.isArray(cell)) { + cell = cell.map((c) => (typeof c === 'object' ? JSON.stringify(c) : c)).join(', '); + } + + // Check for object-type value (geoip) + if (typeof cell === 'object') { + cell = JSON.stringify(cell); + } + + return cell; + } + + return '-'; // Unknown field: it existed in searchSource but has no value in the result + }; + } + + /* + * Use the list of fields to generate the header row + */ + private generateHeader( + fields: string[], + table: Datatable, + builder: MaxSizeStringBuilder, + settings: CsvExportSettings + ) { + this.logger.debug(`Building CSV header row...`); + const header = fields.map(this.escapeValues(settings)).join(settings.separator) + '\n'; + + if (!builder.tryAppend(header)) { + return { + size: 0, + content: '', + maxSizeReached: true, + warnings: [], + }; + } + } + + /* + * Format a Datatable into rows of CSV content + */ + private generateRows( + fields: string[], + table: Datatable, + builder: MaxSizeStringBuilder, + formatters: Record, + settings: CsvExportSettings + ) { + this.logger.debug(`Building ${table.rows.length} CSV data rows...`); + for (const dataTableRow of table.rows) { + if (this.cancellationToken.isCancelled()) { + break; + } + + const row = + fields + .map((f) => ({ column: f, data: dataTableRow[f] })) + .map(this.formatCellValues(formatters)) + .map(this.escapeValues(settings)) + .join(settings.separator) + '\n'; + + if (!builder.tryAppend(row)) { + this.logger.warn(`Max Size Reached after ${this.csvRowCount} rows.`); + this.maxSizeReached = true; + if (this.cancellationToken) { + this.cancellationToken.cancel(); + } + break; + } + + this.csvRowCount++; + } + } + + public async generateData(): Promise { + const [settings, searchSource] = await Promise.all([ + getExportSettings( + this.clients.uiSettings, + this.config, + this.job.browserTimezone, + this.logger + ), + this.dependencies.searchSourceStart.create(this.job.searchSource), + ]); + + const index = searchSource.getField('index'); + + if (!index) { + throw new Error(`The search must have a revference to an index pattern!`); + } + + const { maxSizeBytes, bom, escapeFormulaValues, scroll: scrollSettings } = settings; + + const builder = new MaxSizeStringBuilder(byteSizeValueToNumber(maxSizeBytes), bom); + const warnings: string[] = []; + let first = true; + let currentRecord = -1; + let totalRecords = 0; + let scrollId: string | undefined; + + // apply timezone from the job to all date field formatters + try { + index.fields.getByType('date').forEach(({ name }) => { + this.logger.debug(`setting timezone on ${name}`); + const format: FieldFormatConfig = { + ...index.fieldFormatMap[name], + id: index.fieldFormatMap[name]?.id || 'date', // allow id: date_nanos + params: { + ...index.fieldFormatMap[name]?.params, + timezone: settings.timezone, + }, + }; + index.setFieldFormat(name, format); + }); + } catch (err) { + this.logger.error(err); + } + + try { + do { + if (this.cancellationToken.isCancelled()) { + break; + } + let results: SearchResponse | undefined; + if (scrollId == null) { + // open a scroll cursor in Elasticsearch + results = await this.scan(index, searchSource, scrollSettings); + scrollId = results?._scroll_id; + if (results.hits?.total != null) { + totalRecords = results.hits.total; + this.logger.debug(`Total search results: ${totalRecords}`); + } + } else { + // use the scroll cursor in Elasticsearch + results = await this.scroll(scrollId, scrollSettings); + } + + if (!results) { + this.logger.warning(`Search results are undefined!`); + break; + } + + let table: Datatable | undefined; + try { + table = tabifyDocs(results, index, { shallow: true, meta: true }); + } catch (err) { + this.logger.error(err); + } + + if (!table) { + break; + } + + const fields = this.getFields(searchSource, table); + + if (first) { + first = false; + this.generateHeader(fields, table, builder, settings); + } + + if (table.rows.length < 1) { + break; // empty report with just the header + } + + const formatters = this.getFormatters(table); + this.generateRows(fields, table, builder, formatters, settings); + + // update iterator + currentRecord += table.rows.length; + } while (currentRecord < totalRecords - 1); + + // Add warnings to be logged + if (this.csvContainsFormulas && escapeFormulaValues) { + warnings.push( + i18n.translate('xpack.reporting.exportTypes.csv.generateCsv.escapedFormulaValues', { + defaultMessage: 'CSV may contain formulas whose values have been escaped', + }) + ); + } + } catch (err) { + this.logger.error(err); + if (err instanceof KbnServerError && err.errBody) { + throw JSON.stringify(err.errBody.error); + } + } finally { + // clear scrollID + if (scrollId) { + this.logger.debug(`executing clearScroll request`); + try { + await this.clients.es.asCurrentUser.clearScroll({ scroll_id: [scrollId] }); + } catch (err) { + this.logger.error(err); + } + } else { + this.logger.warn(`No scrollId to clear!`); + } + } + + const size = builder.getSizeInBytes(); + this.logger.debug( + `Finished generating. Total size in bytes: ${size}. Row count: ${this.csvRowCount}.` + ); + + return { + content: builder.getString(), + content_type: CONTENT_TYPE_CSV, + csv_contains_formulas: this.csvContainsFormulas && !escapeFormulaValues, + max_size_reached: this.maxSizeReached, + size, + warnings, + }; + } +} diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.test.ts new file mode 100644 index 0000000000000..efdb583a89dc8 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.test.ts @@ -0,0 +1,83 @@ +/* + * 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 { + UI_SETTINGS_DATEFORMAT_TZ, + UI_SETTINGS_CSV_QUOTE_VALUES, + UI_SETTINGS_CSV_SEPARATOR, +} from '../../../../common/constants'; +import { IUiSettingsClient } from 'kibana/server'; +import { savedObjectsClientMock, uiSettingsServiceMock } from 'src/core/server/mocks'; +import { + createMockConfig, + createMockConfigSchema, + createMockLevelLogger, +} from '../../../test_helpers'; +import { getExportSettings } from './get_export_settings'; + +describe('getExportSettings', () => { + let uiSettingsClient: IUiSettingsClient; + const config = createMockConfig(createMockConfigSchema({})); + const logger = createMockLevelLogger(); + + beforeEach(() => { + uiSettingsClient = uiSettingsServiceMock + .createStartContract() + .asScopedToClient(savedObjectsClientMock.create()); + uiSettingsClient.get = jest.fn().mockImplementation((key: string) => { + switch (key) { + case UI_SETTINGS_CSV_QUOTE_VALUES: + return true; + case UI_SETTINGS_CSV_SEPARATOR: + return ','; + case UI_SETTINGS_DATEFORMAT_TZ: + return 'Browser'; + } + + return 'helo world'; + }); + }); + + test('getExportSettings: returns the expected result', async () => { + expect(await getExportSettings(uiSettingsClient, config, '', logger)).toMatchInlineSnapshot(` + Object { + "bom": "", + "checkForFormulas": undefined, + "escapeFormulaValues": undefined, + "escapeValue": [Function], + "maxSizeBytes": undefined, + "scroll": Object { + "duration": undefined, + "size": undefined, + }, + "separator": ",", + "timezone": "UTC", + } + `); + }); + + test('escapeValue function', async () => { + const { escapeValue } = await getExportSettings(uiSettingsClient, config, '', logger); + expect(escapeValue(`test`)).toBe(`test`); + expect(escapeValue(`this is, a test`)).toBe(`"this is, a test"`); + expect(escapeValue(`"tet"`)).toBe(`"""tet"""`); + expect(escapeValue(`@foo`)).toBe(`"@foo"`); + }); + + test('non-default timezone', async () => { + uiSettingsClient.get = jest.fn().mockImplementation((key: string) => { + switch (key) { + case UI_SETTINGS_DATEFORMAT_TZ: + return `America/Aruba`; + } + }); + + expect( + await getExportSettings(uiSettingsClient, config, '', logger).then(({ timezone }) => timezone) + ).toBe(`America/Aruba`); + }); +}); diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.ts new file mode 100644 index 0000000000000..17a10f3034242 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.ts @@ -0,0 +1,85 @@ +/* + * 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 { ByteSizeValue } from '@kbn/config-schema'; +import { i18n } from '@kbn/i18n'; +import { IUiSettingsClient } from 'kibana/server'; +import { ReportingConfig } from '../../../'; +import { + CSV_BOM_CHARS, + UI_SETTINGS_DATEFORMAT_TZ, + UI_SETTINGS_CSV_QUOTE_VALUES, + UI_SETTINGS_CSV_SEPARATOR, +} from '../../../../common/constants'; +import { LevelLogger } from '../../../lib'; +import { createEscapeValue } from './escape_value'; + +export interface CsvExportSettings { + timezone: string; + scroll: { + size: number; + duration: string; + }; + bom: string; + separator: string; + maxSizeBytes: number | ByteSizeValue; + checkForFormulas: boolean; + escapeFormulaValues: boolean; + escapeValue: (value: string) => string; +} + +export const getExportSettings = async ( + client: IUiSettingsClient, + config: ReportingConfig, + timezone: string | undefined, + logger: LevelLogger +): Promise => { + // Timezone + let setTimezone: string; + // timezone in job params? + if (timezone) { + setTimezone = timezone; + } else { + // timezone in settings? + setTimezone = await client.get(UI_SETTINGS_DATEFORMAT_TZ); + if (setTimezone === 'Browser') { + // if `Browser`, hardcode it to 'UTC' so the export has data that makes sense + logger.warn( + i18n.translate('xpack.reporting.exportTypes.csv.executeJob.dateFormateSetting', { + defaultMessage: + 'Kibana Advanced Setting "{dateFormatTimezone}" is set to "Browser". Dates will be formatted as UTC to avoid ambiguity.', + values: { dateFormatTimezone: 'dateFormat:tz' }, + }) + ); + setTimezone = 'UTC'; + } + } + + // Separator, QuoteValues + const [separator, quoteValues] = await Promise.all([ + client.get(UI_SETTINGS_CSV_SEPARATOR), + client.get(UI_SETTINGS_CSV_QUOTE_VALUES), + ]); + + const escapeFormulaValues = config.get('csv', 'escapeFormulaValues'); + const escapeValue = createEscapeValue(quoteValues, escapeFormulaValues); + const bom = config.get('csv', 'useByteOrderMarkEncoding') ? CSV_BOM_CHARS : ''; + + return { + timezone: setTimezone, + scroll: { + size: config.get('csv', 'scroll', 'size'), + duration: config.get('csv', 'scroll', 'duration'), + }, + bom, + separator, + maxSizeBytes: config.get('csv', 'maxSizeBytes'), + checkForFormulas: config.get('csv', 'checkForFormulas'), + escapeFormulaValues, + escapeValue, + }; +}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/index.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/index.ts new file mode 100644 index 0000000000000..4e08ff2a222dc --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { CsvGenerator } from './generate_csv'; diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/max_size_string_builder.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/max_size_string_builder.test.ts similarity index 100% rename from x-pack/plugins/reporting/server/export_types/csv/generate_csv/max_size_string_builder.test.ts rename to x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/max_size_string_builder.test.ts diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/max_size_string_builder.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/max_size_string_builder.ts similarity index 100% rename from x-pack/plugins/reporting/server/export_types/csv/generate_csv/max_size_string_builder.ts rename to x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/max_size_string_builder.ts diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/index.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/index.ts new file mode 100644 index 0000000000000..65126a0a62cb8 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/index.ts @@ -0,0 +1,40 @@ +/* + * 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 { + CSV_JOB_TYPE as jobType, + LICENSE_TYPE_BASIC, + LICENSE_TYPE_ENTERPRISE, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_TRIAL, +} from '../../../common/constants'; +import { CreateJobFn, ExportTypeDefinition, RunTaskFn } from '../../types'; +import { createJobFnFactory } from './create_job'; +import { runTaskFnFactory } from './execute_job'; +import { metadata } from './metadata'; +import { JobParamsCSV, TaskPayloadCSV } from './types'; + +export const getExportType = (): ExportTypeDefinition< + CreateJobFn, + RunTaskFn +> => ({ + ...metadata, + jobType, + jobContentExtension: 'csv', + createJobFnFactory, + runTaskFnFactory, + validLicenses: [ + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_BASIC, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, + LICENSE_TYPE_ENTERPRISE, + ], +}); diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/metadata.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/metadata.ts similarity index 65% rename from x-pack/plugins/reporting/server/export_types/csv_from_savedobject/metadata.ts rename to x-pack/plugins/reporting/server/export_types/csv_searchsource/metadata.ts index 76bf106acb5de..187d64d872a9d 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/metadata.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/metadata.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; +import { CSV_JOB_TYPE } from '../../../common/constants'; export const metadata = { - id: CSV_FROM_SAVEDOBJECT_JOB_TYPE, - name: CSV_FROM_SAVEDOBJECT_JOB_TYPE, + id: 'csv_searchsource', + name: CSV_JOB_TYPE, }; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/types.d.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/types.d.ts new file mode 100644 index 0000000000000..f0ad4e00ebd5c --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/types.d.ts @@ -0,0 +1,18 @@ +/* + * 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 { BaseParams, BasePayload } from '../../types'; + +export type RawValue = string | object | null | undefined; + +interface BaseParamsCSV { + browserTimezone: string; + searchSource: any; +} + +export type JobParamsCSV = BaseParamsCSV & BaseParams; +export type TaskPayloadCSV = BaseParamsCSV & BasePayload; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts new file mode 100644 index 0000000000000..c8475e85bd847 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts @@ -0,0 +1,81 @@ +/* + * 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 { KibanaRequest } from 'src/core/server'; +import { CancellationToken } from '../../../common'; +import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE } from '../../../common/constants'; +import { TaskRunResult } from '../../lib/tasks'; +import { getFieldFormats } from '../../services'; +import { ReportingRequestHandlerContext, RunTaskFnFactory } from '../../types'; +import { CsvGenerator } from '../csv_searchsource/generate_csv/generate_csv'; +import { JobParamsDownloadCSV } from './types'; + +/* + * ImmediateExecuteFn receives the job doc payload because the payload was + * generated in the ScheduleFn + */ +export type ImmediateExecuteFn = ( + jobId: null, + job: JobParamsDownloadCSV, + context: ReportingRequestHandlerContext, + req: KibanaRequest +) => Promise; + +export const runTaskFnFactory: RunTaskFnFactory = function executeJobFactoryFn( + reporting, + parentLogger +) { + const config = reporting.getConfig(); + const logger = parentLogger.clone([CSV_SEARCHSOURCE_IMMEDIATE_TYPE, 'execute-job']); + + return async function runTask(jobId, immediateJobParams, context, req) { + const job = { + objectType: 'immediate-search', + ...immediateJobParams, + }; + + const savedObjectsClient = context.core.savedObjects.client; + const uiSettings = await reporting.getUiSettingsServiceFactory(savedObjectsClient); + const dataPluginStart = await reporting.getDataService(); + const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory(uiSettings); + + const [es, searchSourceStart] = await Promise.all([ + (await reporting.getEsClient()).asScoped(req), + await dataPluginStart.search.searchSource.asScoped(req), + ]); + const clients = { + uiSettings, + data: dataPluginStart.search.asScoped(req), + es, + }; + const dependencies = { + fieldFormatsRegistry, + searchSourceStart, + }; + const cancellationToken = new CancellationToken(); + + const csv = new CsvGenerator(job, config, clients, dependencies, cancellationToken, logger); + const result = await csv.generateData(); + + if (result.csv_contains_formulas) { + logger.warn(`CSV may contain formulas whose values have been escaped`); + } + + if (result.max_size_reached) { + logger.warn(`Max size reached: CSV output truncated to ${result.size} bytes`); + } + + const { warnings } = result; + if (warnings) { + warnings.forEach((warning) => { + logger.warning(warning); + }); + } + + return result; + }; +}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/index.ts similarity index 75% rename from x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts rename to x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/index.ts index c3a0df9529a4d..9d915db4797b3 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/index.ts @@ -6,7 +6,7 @@ */ import { - CSV_FROM_SAVEDOBJECT_JOB_TYPE, + CSV_SEARCHSOURCE_IMMEDIATE_TYPE, LICENSE_TYPE_BASIC, LICENSE_TYPE_ENTERPRISE, LICENSE_TYPE_GOLD, @@ -15,7 +15,6 @@ import { LICENSE_TYPE_TRIAL, } from '../../../common/constants'; import { ExportTypeDefinition } from '../../types'; -import { createJobFnFactory, ImmediateCreateJobFn } from './create_job'; import { ImmediateExecuteFn, runTaskFnFactory } from './execute_job'; import { metadata } from './metadata'; @@ -23,17 +22,13 @@ import { metadata } from './metadata'; * These functions are exported to share with the API route handler that * generates csv from saved object immediately on request. */ -export { createJobFnFactory } from './create_job'; export { runTaskFnFactory } from './execute_job'; -export const getExportType = (): ExportTypeDefinition< - ImmediateCreateJobFn, - ImmediateExecuteFn -> => ({ +export const getExportType = (): ExportTypeDefinition => ({ ...metadata, - jobType: CSV_FROM_SAVEDOBJECT_JOB_TYPE, + jobType: CSV_SEARCHSOURCE_IMMEDIATE_TYPE, jobContentExtension: 'csv', - createJobFnFactory, + createJobFnFactory: null, runTaskFnFactory, validLicenses: [ LICENSE_TYPE_TRIAL, diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/metadata.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/metadata.ts new file mode 100644 index 0000000000000..c27b8484697dd --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/metadata.ts @@ -0,0 +1,13 @@ +/* + * 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 { CSV_SEARCHSOURCE_IMMEDIATE_TYPE } from '../../../common/constants'; + +export const metadata = { + id: CSV_SEARCHSOURCE_IMMEDIATE_TYPE, + name: CSV_SEARCHSOURCE_IMMEDIATE_TYPE, +}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/types.d.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/types.d.ts new file mode 100644 index 0000000000000..276016dd61233 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/types.d.ts @@ -0,0 +1,24 @@ +/* + * 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 { TimeRangeParams } from '../common'; + +export interface FakeRequest { + headers: Record; +} + +export interface JobParamsDownloadCSV { + browserTimezone: string; + title: string; + searchSource: any; +} + +export interface SavedObjectServiceError { + statusCode: number; + error?: string; + message?: string; +} diff --git a/x-pack/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/plugins/reporting/server/lib/enqueue_job.ts index 1c0eb8f4f5b77..b409e5f8d9ac8 100644 --- a/x-pack/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/plugins/reporting/server/lib/enqueue_job.ts @@ -47,12 +47,16 @@ export function enqueueJobFactory( throw new Error(`Export type ${exportTypeId} does not exist in the registry!`); } - const [createJob, { store }] = await Promise.all([ - exportType.createJobFnFactory(reporting, logger), - reporting.getPluginStartDeps(), + if (!exportType.createJobFnFactory) { + throw new Error(`Export type ${exportTypeId} is not an async job type!`); + } + + const [createJob, store] = await Promise.all([ + exportType.createJobFnFactory(reporting, logger.clone([exportType.id])), + reporting.getStore(), ]); - const job = await createJob(jobParams, context, request); + const job = await createJob!(jobParams, context, request); const pendingReport = new Report({ jobtype: exportType.jobType, created_by: user ? user.username : false, @@ -67,7 +71,7 @@ export function enqueueJobFactory( // store the pending report, puts it in the Reporting Management UI table const report = await store.addReport(pendingReport); - logger.info(`Scheduled ${exportType.name} report: ${report._id}`); + logger.info(`Queued ${exportType.name} report: ${report._id}`); return report; }; diff --git a/x-pack/plugins/reporting/server/lib/export_types_registry.ts b/x-pack/plugins/reporting/server/lib/export_types_registry.ts index 5502692306319..890af43297751 100644 --- a/x-pack/plugins/reporting/server/lib/export_types_registry.ts +++ b/x-pack/plugins/reporting/server/lib/export_types_registry.ts @@ -6,8 +6,9 @@ */ import { isString } from 'lodash'; -import { getExportType as getTypeCsv } from '../export_types/csv'; -import { getExportType as getTypeCsvFromSavedObject } from '../export_types/csv_from_savedobject'; +import { getExportType as getTypeCsvDeprecated } from '../export_types/csv'; +import { getExportType as getTypeCsvFromSavedObject } from '../export_types/csv_searchsource_immediate'; +import { getExportType as getTypeCsv } from '../export_types/csv_searchsource'; import { getExportType as getTypePng } from '../export_types/png'; import { getExportType as getTypePrintablePdf } from '../export_types/printable_pdf'; import { CreateJobFn, ExportTypeDefinition } from '../types'; @@ -82,8 +83,9 @@ export function getExportTypesRegistry(): ExportTypesRegistry { const registry = new ExportTypesRegistry(); type CreateFnType = CreateJobFn; // can not specify params types because different type of params are not assignable to each other type RunFnType = any; // can not specify because ImmediateExecuteFn is not assignable to RunTaskFn - const getTypeFns: Array<() => ExportTypeDefinition> = [ + const getTypeFns: Array<() => ExportTypeDefinition> = [ getTypeCsv, + getTypeCsvDeprecated, getTypeCsvFromSavedObject, getTypePng, getTypePrintablePdf, diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index 7d30ae78e5c84..32b87340fb315 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -29,8 +29,8 @@ export class ReportingPlugin constructor(context: PluginInitializerContext) { this.logger = new LevelLogger(context.logger.get()); - this.initializerContext = context; this.reportingCore = new ReportingCore(this.logger); + this.initializerContext = context; } public setup(core: CoreSetup, plugins: ReportingSetupDeps) { @@ -121,8 +121,10 @@ export class ReportingPlugin browserDriverFactory, savedObjects: core.savedObjects, uiSettings: core.uiSettings, - esqueue, store, + esClient: core.elasticsearch.client, + data: plugins.data, + esqueue, }); this.logger.debug('Start complete'); diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/plugins/reporting/server/routes/csv_searchsource_immediate.ts similarity index 55% rename from x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts rename to x-pack/plugins/reporting/server/routes/csv_searchsource_immediate.ts index 6d000cffb9195..55092b5236ce6 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/csv_searchsource_immediate.ts @@ -8,26 +8,17 @@ import { schema } from '@kbn/config-schema'; import { KibanaRequest } from 'src/core/server'; import { ReportingCore } from '../'; -import { createJobFnFactory } from '../export_types/csv_from_savedobject/create_job'; -import { runTaskFnFactory } from '../export_types/csv_from_savedobject/execute_job'; -import { - JobParamsPanelCsv, - JobParamsPanelCsvPost, -} from '../export_types/csv_from_savedobject/types'; +import { runTaskFnFactory } from '../export_types/csv_searchsource_immediate/execute_job'; +import { JobParamsDownloadCSV } from '../export_types/csv_searchsource_immediate/types'; import { LevelLogger as Logger } from '../lib'; import { TaskRunResult } from '../lib/tasks'; import { authorizedUserPreRoutingFactory } from './lib/authorized_user_pre_routing'; -import { getJobParamsFromRequest } from './lib/get_job_params_from_request'; import { HandlerErrorFunction } from './types'; const API_BASE_URL_V1 = '/api/reporting/v1'; const API_BASE_GENERATE_V1 = `${API_BASE_URL_V1}/generate`; -export type CsvFromSavedObjectRequest = KibanaRequest< - JobParamsPanelCsv, - unknown, - JobParamsPanelCsvPost ->; +export type CsvFromSavedObjectRequest = KibanaRequest; /* * This function registers API Endpoints for immediate Reporting jobs. The API inputs are: @@ -47,43 +38,28 @@ export function registerGenerateCsvFromSavedObjectImmediate( const userHandler = authorizedUserPreRoutingFactory(reporting); const { router } = setupDeps; - /* - * CSV export with the `immediate` option does not queue a job with Reporting's ESQueue to run the job async. Instead, this does: - * - re-use the createJob function to build up es query config - * - re-use the runTask function to run the scan and scroll queries and capture the entire CSV in a result object. - */ + // This API calls run the SearchSourceImmediate export type's runTaskFn directly router.post( { - path: `${API_BASE_GENERATE_V1}/immediate/csv/saved-object/{savedObjectType}:{savedObjectId}`, + path: `${API_BASE_GENERATE_V1}/immediate/csv_searchsource`, validate: { - params: schema.object({ - savedObjectType: schema.string({ minLength: 5 }), - savedObjectId: schema.string({ minLength: 5 }), - }), body: schema.object({ - state: schema.object({}, { unknowns: 'allow' }), - timerange: schema.object({ - timezone: schema.string({ defaultValue: 'UTC' }), - min: schema.nullable(schema.oneOf([schema.number(), schema.string({ minLength: 5 })])), - max: schema.nullable(schema.oneOf([schema.number(), schema.string({ minLength: 5 })])), - }), + searchSource: schema.object({}, { unknowns: 'allow' }), + browserTimezone: schema.string({ defaultValue: 'UTC' }), + title: schema.string(), }), }, }, userHandler(async (user, context, req: CsvFromSavedObjectRequest, res) => { - const logger = parentLogger.clone(['savedobject-csv']); - const jobParams = getJobParamsFromRequest(req); - const createJob = createJobFnFactory(reporting, logger); + const logger = parentLogger.clone(['csv_searchsource_immediate']); const runTaskFn = runTaskFnFactory(reporting, logger); try { - // FIXME: no create job for immediate download - const payload = await createJob(jobParams, context, req); const { content_type: jobOutputContentType, content: jobOutputContent, size: jobOutputSize, - }: TaskRunResult = await runTaskFn(null, payload, context, req); + }: TaskRunResult = await runTaskFn(null, req.body, context, req); logger.info(`Job output size: ${jobOutputSize} bytes`); diff --git a/x-pack/plugins/reporting/server/routes/generation.ts b/x-pack/plugins/reporting/server/routes/generation.ts index 3a0282a220a35..949836148f9ca 100644 --- a/x-pack/plugins/reporting/server/routes/generation.ts +++ b/x-pack/plugins/reporting/server/routes/generation.ts @@ -12,8 +12,8 @@ import { ReportingCore } from '../'; import { API_BASE_URL } from '../../common/constants'; import { LevelLogger as Logger } from '../lib'; import { enqueueJobFactory } from '../lib/enqueue_job'; +import { registerGenerateCsvFromSavedObjectImmediate } from './csv_searchsource_immediate'; import { registerGenerateFromJobParams } from './generate_from_jobparams'; -import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; import { registerLegacy } from './legacy'; import { HandlerFunction } from './types'; diff --git a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts index 26f1a64a7ef63..4e8e888e4e266 100644 --- a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts +++ b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts @@ -8,7 +8,7 @@ // @ts-ignore import contentDisposition from 'content-disposition'; import { get } from 'lodash'; -import { CSV_JOB_TYPE_DEPRECATED } from '../../../common/constants'; +import { CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED } from '../../../common/constants'; import { ExportTypesRegistry, statuses } from '../../lib'; import { ReportDocument } from '../../lib/store'; import { TaskRunResult } from '../../lib/tasks'; @@ -34,7 +34,7 @@ const getTitle = (exportType: ExportTypeDefinition, title?: string): string => const getReportingHeaders = (output: TaskRunResult, exportType: ExportTypeDefinition) => { const metaDataHeaders: Record = {}; - if (exportType.jobType === CSV_JOB_TYPE_DEPRECATED) { + if (exportType.jobType === CSV_JOB_TYPE || exportType.jobType === CSV_JOB_TYPE_DEPRECATED) { const csvContainsFormulas = get(output, 'csv_contains_formulas', false); const maxSizedReach = get(output, 'max_size_reached', false); diff --git a/x-pack/plugins/reporting/server/routes/lib/get_job_params_from_request.ts b/x-pack/plugins/reporting/server/routes/lib/get_job_params_from_request.ts deleted file mode 100644 index 8dce491e3df09..0000000000000 --- a/x-pack/plugins/reporting/server/routes/lib/get_job_params_from_request.ts +++ /dev/null @@ -1,22 +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 { JobParamsPanelCsv } from '../../export_types/csv_from_savedobject/types'; -import { CsvFromSavedObjectRequest } from '../generate_from_savedobject_immediate'; - -export function getJobParamsFromRequest(request: CsvFromSavedObjectRequest): JobParamsPanelCsv { - const { savedObjectType, savedObjectId } = request.params; - const { timerange, state } = request.body; - - const post = timerange || state ? { timerange, state } : undefined; - - return { - savedObjectType, - savedObjectId, - post, - }; -} diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts index ea8480ef3493d..b4bda5e041764 100644 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts +++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts @@ -12,7 +12,10 @@ jest.mock('../lib/create_queue'); import _ from 'lodash'; import * as Rx from 'rxjs'; -import { coreMock } from 'src/core/server/mocks'; +import { coreMock, elasticsearchServiceMock } from 'src/core/server/mocks'; +import { fieldFormats } from 'src/plugins/data/server'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { dataPluginMock } from 'src/plugins/data/server/mocks'; import { ReportingConfig, ReportingCore } from '../'; import { featuresPluginMock } from '../../../features/server/mocks'; import { @@ -23,6 +26,7 @@ import { import { ReportingConfigType } from '../config'; import { ReportingInternalSetup, ReportingInternalStart } from '../core'; import { ReportingStore } from '../lib'; +import { setFieldFormats } from '../services'; import { createMockLevelLogger } from './create_mock_levellogger'; (initializeBrowserDriverFactory as jest.Mock< @@ -45,16 +49,23 @@ export const createMockPluginSetup = (setupMock?: any): ReportingInternalSetup = const logger = createMockLevelLogger(); -const createMockPluginStart = ( - mockReportingCore: ReportingCore, +const createMockReportingStore = () => ({} as ReportingStore); + +export const createMockPluginStart = ( + mockReportingCore: ReportingCore | undefined, startMock?: any ): ReportingInternalStart => { - const store = new ReportingStore(mockReportingCore, logger); + const store = mockReportingCore + ? new ReportingStore(mockReportingCore, logger) + : createMockReportingStore(); + return { browserDriverFactory: startMock.browserDriverFactory, esqueue: startMock.esqueue, + esClient: elasticsearchServiceMock.createClusterClient(), savedObjects: startMock.savedObjects || { getScopedClient: jest.fn() }, uiSettings: startMock.uiSettings || { asScopedToClient: () => ({ get: jest.fn() }) }, + data: startMock.data || dataPluginMock.createStartContract(), store, ...startMock, }; @@ -121,11 +132,18 @@ export const createMockReportingCore = async ( setupDepsMock: ReportingInternalSetup | undefined = undefined, startDepsMock: ReportingInternalStart | undefined = undefined ) => { - config = config || {}; + const mockReportingCore = ({ + getConfig: () => config, + getElasticsearchService: () => setupDepsMock?.elasticsearch, + getDataService: () => startDepsMock?.data, + } as unknown) as ReportingCore; if (!setupDepsMock) { setupDepsMock = createMockPluginSetup({}); } + if (!startDepsMock) { + startDepsMock = createMockPluginStart(mockReportingCore, {}); + } const context = coreMock.createPluginInitializerContext(createMockConfigSchema()); const core = new ReportingCore(logger); @@ -140,5 +158,12 @@ export const createMockReportingCore = async ( await core.pluginStart(startDepsMock); await core.pluginStartsUp(); + setFieldFormats({ + fieldFormatServiceFactory() { + const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); + return Promise.resolve(fieldFormatsRegistry); + }, + }); + return core; }; diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts index b395738ad4445..f84d2fbdf4712 100644 --- a/x-pack/plugins/reporting/server/types.ts +++ b/x-pack/plugins/reporting/server/types.ts @@ -80,13 +80,16 @@ export type RunTaskFnFactory = ( logger: LevelLogger ) => RunTaskFnType; -export interface ExportTypeDefinition { +export interface ExportTypeDefinition< + CreateJobFnType = CreateJobFn | null, + RunTaskFnType = RunTaskFn +> { id: string; name: string; jobType: string; jobContentEncoding?: string; jobContentExtension: string; - createJobFnFactory: CreateJobFnFactory; + createJobFnFactory: CreateJobFnFactory | null; // immediate job does not have a "create" phase runTaskFnFactory: RunTaskFnFactory; validLicenses: string[]; } diff --git a/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap b/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap index ed2637d7a1bcb..150154fa996c5 100644 --- a/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap +++ b/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap @@ -13,6 +13,10 @@ Object { "available": true, "total": 0, }, + "csv_searchsource": Object { + "available": true, + "total": 0, + }, "enabled": true, "last7Days": Object { "PNG": Object { @@ -24,6 +28,10 @@ Object { "available": true, "total": 0, }, + "csv_searchsource": Object { + "available": true, + "total": 0, + }, "printable_pdf": Object { "app": Object { "dashboard": 0, @@ -75,6 +83,10 @@ Object { "available": true, "total": 0, }, + "csv_searchsource": Object { + "available": true, + "total": 0, + }, "enabled": true, "last7Days": Object { "PNG": Object { @@ -86,6 +98,10 @@ Object { "available": true, "total": 0, }, + "csv_searchsource": Object { + "available": true, + "total": 0, + }, "printable_pdf": Object { "app": Object { "dashboard": 0, @@ -166,6 +182,10 @@ Object { "available": true, "total": 1, }, + "csv_searchsource": Object { + "available": true, + "total": 0, + }, "enabled": true, "last7Days": Object { "PNG": Object { @@ -177,6 +197,10 @@ Object { "available": true, "total": 1, }, + "csv_searchsource": Object { + "available": true, + "total": 0, + }, "printable_pdf": Object { "app": Object { "canvas workpad": 1, diff --git a/x-pack/plugins/reporting/server/usage/decorate_range_stats.ts b/x-pack/plugins/reporting/server/usage/decorate_range_stats.ts index a750e9e196b20..9fc0141ab742e 100644 --- a/x-pack/plugins/reporting/server/usage/decorate_range_stats.ts +++ b/x-pack/plugins/reporting/server/usage/decorate_range_stats.ts @@ -6,7 +6,12 @@ */ import { uniq } from 'lodash'; -import { CSV_JOB_TYPE_DEPRECATED, PDF_JOB_TYPE, PNG_JOB_TYPE } from '../../common/constants'; +import { + CSV_JOB_TYPE, + CSV_JOB_TYPE_DEPRECATED, + PDF_JOB_TYPE, + PNG_JOB_TYPE, +} from '../../common/constants'; import { AvailableTotal, ExportType, FeatureAvailabilityMap, RangeStats } from './types'; function getForFeature( @@ -55,6 +60,7 @@ export const decorateRangeStats = ( // combine the known types with any unknown type found in reporting data const keysBasic = uniq([ + CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED, PNG_JOB_TYPE, ...Object.keys(rangeStatsBasic), diff --git a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts index 9335fee766740..05b80bc8acc75 100644 --- a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -354,11 +354,13 @@ describe('data modeling', () => { available: true, browser_type: 'chromium', csv: { available: true, total: 4 }, + csv_searchsource: { available: true, total: 4 }, enabled: true, last7Days: { PNG: { available: true, total: 0 }, _all: 0, csv: { available: true, total: 0 }, + csv_searchsource: { available: true, total: 0 }, printable_pdf: { app: { dashboard: 0, visualization: 0 }, available: true, @@ -389,11 +391,13 @@ describe('data modeling', () => { available: true, browser_type: 'chromium', csv: { available: true, total: 0 }, + csv_searchsource: { available: true, total: 0 }, enabled: true, last7Days: { PNG: { available: true, total: 3 }, _all: 4, csv: { available: true, total: 0 }, + csv_searchsource: { available: true, total: 0 }, printable_pdf: { app: { 'canvas workpad': 1, dashboard: 0, visualization: 0 }, available: true, @@ -431,6 +435,7 @@ describe('data modeling', () => { layout: { preserve_layout: 0, print: 0 }, }, csv: { available: true, total: 0 }, + csv_searchsource: { available: true, total: 0 }, PNG: { available: true, total: 0 }, }, _all: 0, @@ -443,6 +448,7 @@ describe('data modeling', () => { layout: { preserve_layout: 0, print: 0 }, }, csv: { available: true, total: 0 }, + csv_searchsource: { available: true, total: 0 }, PNG: { available: true, total: 0 }, }); }); @@ -491,6 +497,14 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "available": Object { + "type": "boolean", + }, + "total": Object { + "type": "long", + }, + }, "enabled": Object { "type": "boolean", }, @@ -514,6 +528,14 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "available": Object { + "type": "boolean", + }, + "total": Object { + "type": "long", + }, + }, "printable_pdf": Object { "app": Object { "canvas workpad": Object { @@ -585,6 +607,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -620,6 +653,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -655,6 +699,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -690,6 +745,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -725,6 +791,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -760,6 +837,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -845,6 +933,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -880,6 +979,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -915,6 +1025,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -950,6 +1071,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -985,6 +1117,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", @@ -1020,6 +1163,17 @@ describe('Ready for collection observable', () => { "type": "long", }, }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, "printable_pdf": Object { "canvas workpad": Object { "type": "long", diff --git a/x-pack/plugins/reporting/server/usage/schema.ts b/x-pack/plugins/reporting/server/usage/schema.ts index ec15ae4b1ac47..8528543b09e07 100644 --- a/x-pack/plugins/reporting/server/usage/schema.ts +++ b/x-pack/plugins/reporting/server/usage/schema.ts @@ -16,6 +16,7 @@ const appCountsSchema: MakeSchemaFrom = { const byAppCountsSchema: MakeSchemaFrom = { csv: appCountsSchema, + csv_searchsource: appCountsSchema, PNG: appCountsSchema, printable_pdf: appCountsSchema, }; @@ -27,6 +28,7 @@ const availableTotalSchema: MakeSchemaFrom = { const jobTypesSchema: MakeSchemaFrom = { csv: availableTotalSchema, + csv_searchsource: availableTotalSchema, PNG: availableTotalSchema, printable_pdf: { ...availableTotalSchema, diff --git a/x-pack/plugins/reporting/server/usage/types.ts b/x-pack/plugins/reporting/server/usage/types.ts index 5970df6ccae43..58def60a24ccb 100644 --- a/x-pack/plugins/reporting/server/usage/types.ts +++ b/x-pack/plugins/reporting/server/usage/types.ts @@ -59,7 +59,7 @@ export interface AvailableTotal { total: number; } -type BaseJobTypes = 'csv' | 'PNG' | 'printable_pdf'; +type BaseJobTypes = 'csv' | 'csv_searchsource' | 'PNG' | 'printable_pdf'; export interface LayoutCounts { print: number; preserve_layout: number; @@ -106,7 +106,7 @@ export type ReportingUsageType = RangeStats & { last7Days: RangeStats; }; -export type ExportType = 'csv' | 'printable_pdf' | 'PNG'; +export type ExportType = 'csv' | 'csv_searchsource' | 'printable_pdf' | 'PNG'; export type FeatureAvailabilityMap = { [F in ExportType]: boolean }; export interface KeyCountBucket { diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 4a0ab555f8f40..8367fcb6deef2 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -2436,6 +2436,16 @@ } } }, + "csv_searchsource": { + "properties": { + "available": { + "type": "boolean" + }, + "total": { + "type": "long" + } + } + }, "PNG": { "properties": { "available": { @@ -2521,6 +2531,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -2564,6 +2587,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -2607,6 +2643,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -2650,6 +2699,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -2693,6 +2755,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -2736,6 +2811,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -2787,6 +2875,16 @@ } } }, + "csv_searchsource": { + "properties": { + "available": { + "type": "boolean" + }, + "total": { + "type": "long" + } + } + }, "PNG": { "properties": { "available": { @@ -2872,6 +2970,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -2915,6 +3026,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -2958,6 +3082,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -3001,6 +3138,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -3044,6 +3194,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { @@ -3087,6 +3250,19 @@ } } }, + "csv_searchsource": { + "properties": { + "canvas workpad": { + "type": "long" + }, + "dashboard": { + "type": "long" + }, + "visualization": { + "type": "long" + } + } + }, "PNG": { "properties": { "canvas workpad": { diff --git a/x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap b/x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap new file mode 100644 index 0000000000000..10384b865c82e --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap @@ -0,0 +1,53 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`dashboard Reporting Download CSV E-Commerce Data Download CSV export of a saved search panel 1`] = ` +"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\" +" +`; + +exports[`dashboard Reporting Download CSV E-Commerce Data Downloads a filtered CSV export of a saved search panel 1`] = ` +"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\" +" +`; + +exports[`dashboard Reporting Download CSV Field Formatters and Scripted Fields Download CSV export of a saved search panel 1`] = ` +"date,\\"_id\\",name,gender,value,year,\\"years_ago\\",\\"date_informal\\" +\\"Jan 1, 1984 @ 00:00:00.000\\",\\"1984-Fethany-F\\",Fethany,F,5,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"Jan 1, 1983 @ 00:00:00.000\\",\\"1983-Fethany-F\\",Fethany,F,\\"1,043\\",1983,\\"36.00000000000000000000\\",\\"Jan 1st 83\\" +\\"Jan 1, 1982 @ 00:00:00.000\\",\\"1982-Fethany-F\\",Fethany,F,780,1982,\\"37.00000000000000000000\\",\\"Jan 1st 82\\" +\\"Jan 1, 1981 @ 00:00:00.000\\",\\"1981-Fethany-F\\",Fethany,F,655,1981,\\"38.00000000000000000000\\",\\"Jan 1st 81\\" +\\"Jan 1, 1980 @ 00:00:00.000\\",\\"1980-Fethany-F\\",Fethany,F,702,1980,\\"39.00000000000000000000\\",\\"Jan 1st 80\\" +" +`; diff --git a/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts b/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts index 72f07ef90d703..d4a909f6a0474 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts +++ b/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts @@ -9,91 +9,139 @@ import { REPO_ROOT } from '@kbn/utils'; import expect from '@kbn/expect'; import fs from 'fs'; import path from 'path'; -import * as Rx from 'rxjs'; -import { filter, first, map, timeout } from 'rxjs/operators'; import { FtrProviderContext } from '../../../ftr_provider_context'; -const csvPath = path.resolve(REPO_ROOT, 'target/functional-tests/downloads/Ecommerce Data.csv'); - -// checks every 100ms for the file to exist in the download dir -// just wait up to 5 seconds -const getDownload$ = (filePath: string) => { - return Rx.interval(100).pipe( - map(() => fs.existsSync(filePath)), - filter((value) => value === true), - first(), - timeout(5000) - ); -}; - export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); const dashboardPanelActions = getService('dashboardPanelActions'); const log = getService('log'); const testSubjects = getService('testSubjects'); + const filterBar = getService('filterBar'); const find = getService('find'); - const PageObjects = getPageObjects(['reporting', 'common', 'dashboard']); + const retry = getService('retry'); + const PageObjects = getPageObjects(['reporting', 'common', 'dashboard', 'timePicker']); + + const getCsvPath = (name: string) => + path.resolve(REPO_ROOT, `target/functional-tests/downloads/${name}.csv`); + + // checks every 100ms for the file to exist in the download dir + // just wait up to 5 seconds + const getDownload = (filePath: string) => { + return retry.tryForTime(5000, async () => { + expect(fs.existsSync(filePath)).to.be(true); + return fs.readFileSync(filePath).toString(); + }); + }; + + const clickActionsMenu = async (headingTestSubj: string) => { + const savedSearchPanel = await testSubjects.find('embeddablePanelHeading-' + headingTestSubj); + await dashboardPanelActions.toggleContextMenu(savedSearchPanel); + }; + + const clickDownloadCsv = async () => { + log.debug('click "More"'); + await dashboardPanelActions.clickContextMenuMoreItem(); + + const actionItemTestSubj = 'embeddablePanelAction-downloadCsvReport'; + await testSubjects.existOrFail(actionItemTestSubj); // wait for the full panel to display or else the test runner could click the wrong option! + log.debug('click "Download CSV"'); + await testSubjects.click(actionItemTestSubj); + await testSubjects.existOrFail('csvDownloadStarted'); // validate toast panel + }; describe('Download CSV', () => { before('initialize tests', async () => { log.debug('ReportingPage:initTests'); - await esArchiver.loadIfNeeded('reporting/ecommerce'); - await esArchiver.loadIfNeeded('reporting/ecommerce_kibana'); await browser.setWindowSize(1600, 850); }); - after('clean up archives and previous file download', async () => { - await esArchiver.unload('reporting/ecommerce'); - await esArchiver.unload('reporting/ecommerce_kibana'); - }); - afterEach('remove download', () => { try { - fs.unlinkSync(csvPath); + fs.unlinkSync(getCsvPath('Ecommerce Data')); } catch (e) { // it might not have been there to begin with } }); - it('Downloads a CSV export of a saved search panel', async function () { - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); - const savedSearchPanel = await testSubjects.find('embeddablePanelHeading-EcommerceData'); - await dashboardPanelActions.toggleContextMenu(savedSearchPanel); + describe('E-Commerce Data', () => { + before(async () => { + await esArchiver.load('reporting/ecommerce'); + await esArchiver.load('reporting/ecommerce_kibana'); + }); + after(async () => { + await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); + }); - const actionExists = await testSubjects.exists('embeddablePanelAction-downloadCsvReport'); - if (!actionExists) { - await dashboardPanelActions.clickContextMenuMoreItem(); - } - await testSubjects.existOrFail('embeddablePanelAction-downloadCsvReport'); // wait for the full panel to display or else the test runner could click the wrong option! - await testSubjects.click('embeddablePanelAction-downloadCsvReport'); - await testSubjects.existOrFail('csvDownloadStarted'); // validate toast panel + it('Download CSV export of a saved search panel', async function () { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); + await clickActionsMenu('EcommerceData'); + await clickDownloadCsv(); + + const csvFile = await getDownload(getCsvPath('Ecommerce Data')); + expectSnapshot(csvFile).toMatch(); + }); - const fileExists = await getDownload$(csvPath).toPromise(); - expect(fileExists).to.be(true); + it('Downloads a filtered CSV export of a saved search panel', async function () { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); - // no need to validate download contents, API Integration tests do that some different variations + // add a filter + await filterBar.addFilter('currency', 'is', 'EUR'); + + await clickActionsMenu('EcommerceData'); + await clickDownloadCsv(); + + const csvFile = await getDownload(getCsvPath('Ecommerce Data')); + expectSnapshot(csvFile).toMatch(); + }); + + it('Gets the correct filename if panel titles are hidden', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard Hidden Panel Titles'); + const savedSearchPanel = await find.byCssSelector( + '[data-test-embeddable-id="94eab06f-60ac-4a85-b771-3a8ed475c9bb"]' + ); // panel title is hidden + await dashboardPanelActions.toggleContextMenu(savedSearchPanel); + + await clickDownloadCsv(); + await testSubjects.existOrFail('csvDownloadStarted'); + + const csvFile = await getDownload(getCsvPath('Ecommerce Data')); // file exists with proper name + expect(csvFile).to.not.be(null); + }); }); - it('Gets the correct filename if panel titles are hidden', async () => { - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard Hidden Panel Titles'); - const savedSearchPanel = await find.byCssSelector( - '[data-test-embeddable-id="94eab06f-60ac-4a85-b771-3a8ed475c9bb"]' - ); // panel title is hidden - await dashboardPanelActions.toggleContextMenu(savedSearchPanel); - - const actionExists = await testSubjects.exists('embeddablePanelAction-downloadCsvReport'); - if (!actionExists) { - await dashboardPanelActions.clickContextMenuMoreItem(); - } - await testSubjects.existOrFail('embeddablePanelAction-downloadCsvReport'); - await testSubjects.click('embeddablePanelAction-downloadCsvReport'); - await testSubjects.existOrFail('csvDownloadStarted'); + describe('Field Formatters and Scripted Fields', () => { + before(async () => { + await esArchiver.load('reporting/hugedata'); + }); + after(async () => { + await esArchiver.unload('reporting/hugedata'); + }); + + it('Download CSV export of a saved search panel', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('names dashboard'); + await PageObjects.timePicker.setAbsoluteRange( + 'Jan 01, 1980 @ 00:00:00.000', + 'Dec 31, 1984 @ 23:59:59.000' + ); + + await PageObjects.common.sleep(1000); + + await filterBar.addFilter('name.keyword', 'is', 'Fethany'); + + await PageObjects.common.sleep(1000); + + await clickActionsMenu('namessearch'); + await clickDownloadCsv(); - const fileExists = await getDownload$(csvPath).toPromise(); // file exists with proper name - expect(fileExists).to.be(true); + const csvFile = await getDownload(getCsvPath('namessearch')); + expectSnapshot(csvFile).toMatch(); + }); }); }); } diff --git a/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap b/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap index 43771b00525cc..8b33a1b41788d 100644 --- a/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap +++ b/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap @@ -1,40 +1,88 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`discover Discover Generate CSV: archived search generates a report with data 1`] = ` -"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"order_date\\",\\"products.created_on\\",sku -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Shoes\\"\\",\\"\\"Men's Clothing\\"\\",\\"\\"Women's Accessories\\"\\",\\"\\"Men's Accessories\\"\\"]\\",EUR,19,716724,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0687606876\\"\\",\\"\\"ZO0290502905\\"\\",\\"\\"ZO0126701267\\"\\",\\"\\"ZO0308503085\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Women's Shoes\\"\\",\\"\\"Women's Clothing\\"\\"]\\",EUR,45,591503,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0006400064\\"\\",\\"\\"ZO0150601506\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0638206382\\"\\",\\"\\"ZO0038800388\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0297602976\\"\\",\\"\\"ZO0565605656\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0561405614\\"\\",\\"\\"ZO0281602816\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Shoes\\"\\",\\"\\"Men's Clothing\\"\\"]\\",EUR,41,591636,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0385003850\\"\\",\\"\\"ZO0408604086\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0505605056\\"\\",\\"\\"ZO0513605136\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0276702767\\"\\",\\"\\"ZO0291702917\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0046600466\\"\\",\\"\\"ZO0050800508\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Clothing\\"\\",\\"\\"Men's Shoes\\"\\"]\\",EUR,48,590970,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0455604556\\"\\",\\"\\"ZO0680806808\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Women's Clothing\\"\\",\\"\\"Women's Shoes\\"\\"]\\",EUR,46,591299,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0229002290\\"\\",\\"\\"ZO0674406744\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0529905299\\"\\",\\"\\"ZO0617006170\\"\\"]\\" +exports[`discover Discover CSV Export Generate CSV: archived search generates a report with data 1`] = ` +"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\" " `; -exports[`discover Discover Generate CSV: archived search generates a report with filtered data 1`] = ` -"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"order_date\\",\\"products.created_on\\",sku -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Shoes\\"\\",\\"\\"Men's Clothing\\"\\",\\"\\"Women's Accessories\\"\\",\\"\\"Men's Accessories\\"\\"]\\",EUR,19,716724,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0687606876\\"\\",\\"\\"ZO0290502905\\"\\",\\"\\"ZO0126701267\\"\\",\\"\\"ZO0308503085\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Women's Shoes\\"\\",\\"\\"Women's Clothing\\"\\"]\\",EUR,45,591503,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0006400064\\"\\",\\"\\"ZO0150601506\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0638206382\\"\\",\\"\\"ZO0038800388\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0297602976\\"\\",\\"\\"ZO0565605656\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0561405614\\"\\",\\"\\"ZO0281602816\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Shoes\\"\\",\\"\\"Men's Clothing\\"\\"]\\",EUR,41,591636,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0385003850\\"\\",\\"\\"ZO0408604086\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0505605056\\"\\",\\"\\"ZO0513605136\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0276702767\\"\\",\\"\\"ZO0291702917\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0046600466\\"\\",\\"\\"ZO0050800508\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Clothing\\"\\",\\"\\"Men's Shoes\\"\\"]\\",EUR,48,590970,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0455604556\\"\\",\\"\\"ZO0680806808\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Women's Clothing\\"\\",\\"\\"Women's Shoes\\"\\"]\\",EUR,46,591299,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0229002290\\"\\",\\"\\"ZO0674406744\\"\\"]\\" -\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0529905299\\"\\",\\"\\"ZO0617006170\\"\\"]\\" +exports[`discover Discover CSV Export Generate CSV: archived search generates a report with discover:searchFieldsFromSource = true 1`] = ` +"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\" " `; -exports[`discover Discover Generate CSV: new search generates a report with data 1`] = ` +exports[`discover Discover CSV Export Generate CSV: archived search generates a report with filtered data 1`] = ` +"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\" +" +`; + +exports[`discover Discover CSV Export Generate CSV: new search generates a report from a new search with data: default 1`] = ` +"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",category,\\"category.keyword\\",currency,\\"customer_first_name\\",\\"customer_first_name.keyword\\",\\"customer_full_name\\",\\"customer_full_name.keyword\\",\\"customer_gender\\",\\"customer_id\\",\\"customer_last_name\\",\\"customer_last_name.keyword\\",\\"customer_phone\\",\\"day_of_week\\",\\"day_of_week_i\\",email,\\"geoip.city_name\\",\\"geoip.continent_name\\",\\"geoip.country_iso_code\\",\\"geoip.location\\",\\"geoip.region_name\\",manufacturer,\\"manufacturer.keyword\\",\\"order_date\\",\\"order_id\\",\\"products._id\\",\\"products._id.keyword\\",\\"products.base_price\\",\\"products.base_unit_price\\",\\"products.category\\",\\"products.category.keyword\\",\\"products.created_on\\",\\"products.discount_amount\\",\\"products.discount_percentage\\",\\"products.manufacturer\\",\\"products.manufacturer.keyword\\",\\"products.min_price\\",\\"products.price\\",\\"products.product_id\\",\\"products.product_name\\",\\"products.product_name.keyword\\",\\"products.quantity\\",\\"products.sku\\",\\"products.tax_amount\\",\\"products.taxful_price\\",\\"products.taxless_price\\",\\"products.unit_discount_amount\\",sku,\\"taxful_total_price\\",\\"taxless_total_price\\",\\"total_quantity\\",\\"total_unique_products\\",type,user +3AMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"_doc\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,\\"Sultan Al\\",\\"Sultan Al\\",\\"Sultan Al Boone\\",\\"Sultan Al Boone\\",MALE,19,Boone,Boone,,Saturday,5,\\"sultan al@boone-family.zzz\\",\\"Abu Dhabi\\",Asia,AE,\\"{ + \\"\\"coordinates\\"\\": [ + 54.4, + 24.5 + ], + \\"\\"type\\"\\": \\"\\"Point\\"\\" +}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"173.96\\",\\"173.96\\",4,4,order,sultan +" +`; + +exports[`discover Discover CSV Export Generate CSV: new search generates a report from a new search with data: discover:searchFieldsFromSource 1`] = ` "\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",category,\\"category.keyword\\",currency,\\"customer_first_name\\",\\"customer_first_name.keyword\\",\\"customer_full_name\\",\\"customer_full_name.keyword\\",\\"customer_gender\\",\\"customer_id\\",\\"customer_last_name\\",\\"customer_last_name.keyword\\",\\"customer_phone\\",\\"day_of_week\\",\\"day_of_week_i\\",email,\\"geoip.city_name\\",\\"geoip.continent_name\\",\\"geoip.country_iso_code\\",\\"geoip.location\\",\\"geoip.region_name\\",manufacturer,\\"manufacturer.keyword\\",\\"order_date\\",\\"order_id\\",\\"products._id\\",\\"products._id.keyword\\",\\"products.base_price\\",\\"products.base_unit_price\\",\\"products.category\\",\\"products.category.keyword\\",\\"products.created_on\\",\\"products.discount_amount\\",\\"products.discount_percentage\\",\\"products.manufacturer\\",\\"products.manufacturer.keyword\\",\\"products.min_price\\",\\"products.price\\",\\"products.product_id\\",\\"products.product_name\\",\\"products.product_name.keyword\\",\\"products.quantity\\",\\"products.sku\\",\\"products.tax_amount\\",\\"products.taxful_price\\",\\"products.taxless_price\\",\\"products.unit_discount_amount\\",sku,\\"taxful_total_price\\",\\"taxless_total_price\\",\\"total_quantity\\",\\"total_unique_products\\",type,user +3AMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"_doc\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,\\"Sultan Al\\",\\"Sultan Al\\",\\"Sultan Al Boone\\",\\"Sultan Al Boone\\",MALE,19,Boone,Boone,,Saturday,5,\\"sultan al@boone-family.zzz\\",\\"Abu Dhabi\\",Asia,AE,\\"{ + \\"\\"coordinates\\"\\": [ + 54.4, + 24.5 + ], + \\"\\"type\\"\\": \\"\\"Point\\"\\" +}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"173.96\\",\\"173.96\\",4,4,order,sultan " `; diff --git a/x-pack/test/functional/apps/discover/reporting.ts b/x-pack/test/functional/apps/discover/reporting.ts index dfc44a8e0e12d..d7dd961e2f103 100644 --- a/x-pack/test/functional/apps/discover/reporting.ts +++ b/x-pack/test/functional/apps/discover/reporting.ts @@ -12,18 +12,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const es = getService('es'); const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const browser = getService('browser'); const PageObjects = getPageObjects(['reporting', 'common', 'discover', 'timePicker']); const filterBar = getService('filterBar'); - describe('Discover', () => { + const setFieldsFromSource = async (setValue: boolean) => { + await kibanaServer.uiSettings.update({ 'discover:searchFieldsFromSource': setValue }); + }; + + describe('Discover CSV Export', () => { before('initialize tests', async () => { log.debug('ReportingPage:initTests'); - await esArchiver.loadIfNeeded('reporting/ecommerce'); + await esArchiver.load('reporting/ecommerce'); + await esArchiver.load('reporting/ecommerce_kibana'); await browser.setWindowSize(1600, 850); }); after('clean up archives', async () => { await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); await es.deleteByQuery({ index: '.reporting-*', refresh: true, @@ -31,7 +38,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('Generate CSV: new search', () => { + describe('Check Available', () => { beforeEach(() => PageObjects.common.navigateToApp('discover')); it('is not available if new', async () => { @@ -63,8 +70,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.reporting.openCsvReportingPanel(); expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); }); + }); - it('generates a report with data', async () => { + describe('Generate CSV: new search', () => { + beforeEach(async () => { + await esArchiver.load('reporting/ecommerce_kibana'); // reload the archive to wipe out changes made by each test + await PageObjects.common.navigateToApp('discover'); + }); + + it('generates a report from a new search with data: default', async () => { await PageObjects.discover.clickNewSearchButton(); await PageObjects.reporting.setTimepickerInDataRange(); await PageObjects.discover.saveSearch('my search - with data - expectReportCanBeCreated'); @@ -79,6 +93,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expectSnapshot(res.text).toMatch(); }); + it('generates a report from a new search with data: discover:searchFieldsFromSource', async () => { + await setFieldsFromSource(true); + await PageObjects.discover.clickNewSearchButton(); + await PageObjects.reporting.setTimepickerInDataRange(); + await PageObjects.discover.saveSearch( + 'my search - with fieldsFromSource data - expectReportCanBeCreated' + ); + await PageObjects.reporting.openCsvReportingPanel(); + await PageObjects.reporting.clickGenerateReportButton(); + + const url = await PageObjects.reporting.getReportURL(60000); + const res = await PageObjects.reporting.getResponse(url); + + expect(res.status).to.equal(200); + expect(res.get('content-type')).to.equal('text/csv; charset=utf-8'); + expectSnapshot(res.text).toMatch(); + await setFieldsFromSource(false); + }); + it('generates a report with no data', async () => { await PageObjects.reporting.setTimepickerInNoDataRange(); await PageObjects.discover.saveSearch('my search - no data - expectReportCanBeCreated'); @@ -98,6 +131,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); describe('Generate CSV: archived search', () => { + const setupPage = async () => { + const fromTime = 'Apr 27, 2019 @ 23:56:51.374'; + const toTime = 'Aug 23, 2019 @ 16:18:51.821'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }; + + const getReport = async () => { + await PageObjects.reporting.openCsvReportingPanel(); + await PageObjects.reporting.clickGenerateReportButton(); + + const url = await PageObjects.reporting.getReportURL(60000); + const res = await PageObjects.reporting.getResponse(url); + + expect(res.status).to.equal(200); + expect(res.get('content-type')).to.equal('text/csv; charset=utf-8'); + return res; + }; + before(async () => { await esArchiver.load('reporting/ecommerce'); await esArchiver.load('reporting/ecommerce_kibana'); @@ -111,41 +162,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { beforeEach(() => PageObjects.common.navigateToApp('discover')); it('generates a report with data', async () => { + await setupPage(); await PageObjects.discover.loadSavedSearch('Ecommerce Data'); - const fromTime = 'Apr 27, 2019 @ 23:56:51.374'; - const toTime = 'Aug 23, 2019 @ 16:18:51.821'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.reporting.openCsvReportingPanel(); - await PageObjects.reporting.clickGenerateReportButton(); - - const url = await PageObjects.reporting.getReportURL(60000); - const res = await PageObjects.reporting.getResponse(url); - - expect(res.status).to.equal(200); - expect(res.get('content-type')).to.equal('text/csv; charset=utf-8'); - expectSnapshot(res.text).toMatch(); + const { text } = await getReport(); + expectSnapshot(text).toMatch(); }); it('generates a report with filtered data', async () => { + await setupPage(); await PageObjects.discover.loadSavedSearch('Ecommerce Data'); - const fromTime = 'Apr 27, 2019 @ 23:56:51.374'; - const toTime = 'Aug 23, 2019 @ 16:18:51.821'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); // filter and re-save await filterBar.addFilter('currency', 'is', 'EUR'); - await PageObjects.discover.saveSearch(`Ecommerce Data: EUR Filtered`); + await PageObjects.discover.saveSearch(`Ecommerce Data: EUR Filtered`); // renamed the search - await PageObjects.reporting.openCsvReportingPanel(); - await PageObjects.reporting.clickGenerateReportButton(); + const { text } = await getReport(); + expectSnapshot(text).toMatch(); + await PageObjects.discover.saveSearch(`Ecommerce Data`); // rename the search back for the next test + }); - const url = await PageObjects.reporting.getReportURL(60000); - const res = await PageObjects.reporting.getResponse(url); + it('generates a report with discover:searchFieldsFromSource = true', async () => { + await setupPage(); + await PageObjects.discover.loadSavedSearch('Ecommerce Data'); - expect(res.status).to.equal(200); - expect(res.get('content-type')).to.equal('text/csv; charset=utf-8'); - expectSnapshot(res.text).toMatch(); + await setFieldsFromSource(true); + await browser.refresh(); + + const { text } = await getReport(); + expectSnapshot(text).toMatch(); + await setFieldsFromSource(false); }); }); }); diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce/data.json.gz b/x-pack/test/functional/es_archives/reporting/ecommerce/data.json.gz index 7736287bc9a37e5203c887d89bcfbe7d98fe1dd9..e1710365c4b414dc0c0071e9a2317512580a625b 100644 GIT binary patch delta 58013 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 aYUPK@`ti!#h{gQ_LMGQ5w^2qL!v6rllKrRv diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce/mappings.json b/x-pack/test/functional/es_archives/reporting/ecommerce/mappings.json index 9e3275bd40bfe..6b474059a8e7a 100644 --- a/x-pack/test/functional/es_archives/reporting/ecommerce/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/ecommerce/mappings.json @@ -209,1018 +209,3 @@ } } } - -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "apm-telemetry": "07ee1939fa4302c62ddc052ec03fed90", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "config": "87aca8fdb053154f11383fce3dbf3edf", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "a4229f8b16a6820c6d724b7e0c1f729d", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "server": "ec97f1c5da1a19609a60874e5af1100c", - "siem-ui-timeline": "1f6f0860ad7bc0dba3e42467ca40470d", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", - "space": "25de8c2deec044392922989cfcf24c54", - "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, - "dynamic": "strict", - "properties": { - "apm-telemetry": { - "properties": { - "has_any_services": { - "type": "boolean" - }, - "services_per_agent": { - "properties": { - "dotnet": { - "null_value": 0, - "type": "long" - }, - "go": { - "null_value": 0, - "type": "long" - }, - "java": { - "null_value": 0, - "type": "long" - }, - "js-base": { - "null_value": 0, - "type": "long" - }, - "nodejs": { - "null_value": 0, - "type": "long" - }, - "python": { - "null_value": 0, - "type": "long" - }, - "ruby": { - "null_value": 0, - "type": "long" - }, - "rum-js": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "canvas-element": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "content": { - "type": "text" - }, - "help": { - "type": "text" - }, - "image": { - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - }, - "dateFormat:tz": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "defaultIndex": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "file-upload-telemetry": { - "properties": { - "filesUploadedTotalCount": { - "type": "long" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "infrastructure-ui-source": { - "properties": { - "description": { - "type": "text" - }, - "fields": { - "properties": { - "container": { - "type": "keyword" - }, - "host": { - "type": "keyword" - }, - "pod": { - "type": "keyword" - }, - "tiebreaker": { - "type": "keyword" - }, - "timestamp": { - "type": "keyword" - } - } - }, - "logAlias": { - "type": "keyword" - }, - "logColumns": { - "properties": { - "fieldColumn": { - "properties": { - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - } - } - }, - "messageColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - }, - "timestampColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - } - }, - "type": "nested" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "map": { - "properties": { - "bounds": { - "type": "geo_shape" - }, - "description": { - "type": "text" - }, - "layerListJSON": { - "type": "text" - }, - "mapStateJSON": { - "type": "text" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "maps-telemetry": { - "properties": { - "attributesPerMap": { - "properties": { - "dataSourcesCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "emsVectorLayersCount": { - "dynamic": "true", - "type": "object" - }, - "layerTypesCount": { - "dynamic": "true", - "type": "object" - }, - "layersCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - } - } - }, - "mapsTotalCount": { - "type": "long" - }, - "timeCaptured": { - "type": "date" - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "index-pattern": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "space": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "siem-ui-timeline": { - "properties": { - "columns": { - "properties": { - "aggregatable": { - "type": "boolean" - }, - "category": { - "type": "keyword" - }, - "columnHeaderType": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "example": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "indexes": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "placeholder": { - "type": "text" - }, - "searchable": { - "type": "boolean" - }, - "type": { - "type": "keyword" - } - } - }, - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "dataProviders": { - "properties": { - "and": { - "properties": { - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "dateRange": { - "properties": { - "end": { - "type": "date" - }, - "start": { - "type": "date" - } - } - }, - "description": { - "type": "text" - }, - "favorite": { - "properties": { - "favoriteDate": { - "type": "date" - }, - "fullName": { - "type": "text" - }, - "keySearch": { - "type": "text" - }, - "userName": { - "type": "text" - } - } - }, - "kqlMode": { - "type": "keyword" - }, - "kqlQuery": { - "properties": { - "filterQuery": { - "properties": { - "kuery": { - "properties": { - "expression": { - "type": "text" - }, - "kind": { - "type": "keyword" - } - } - }, - "serializedQuery": { - "type": "text" - } - } - } - } - }, - "sort": { - "properties": { - "columnId": { - "type": "keyword" - }, - "sortDirection": { - "type": "keyword" - } - } - }, - "title": { - "type": "text" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-note": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-pinned-event": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "telemetry": { - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "dynamic": "true", - "properties": { - "indexName": { - "type": "keyword" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "null_value": true, - "type": "boolean" - } - } - } - } - }, - "ui_open": { - "properties": { - "cluster": { - "null_value": 0, - "type": "long" - }, - "indices": { - "null_value": 0, - "type": "long" - }, - "overview": { - "null_value": 0, - "type": "long" - } - } - }, - "ui_reindex": { - "properties": { - "close": { - "null_value": 0, - "type": "long" - }, - "open": { - "null_value": 0, - "type": "long" - }, - "start": { - "null_value": 0, - "type": "long" - }, - "stop": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} \ No newline at end of file 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 index a0f384a96e6b4..e0b2ee3079296 100644 --- a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json +++ b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/data.json @@ -1,31 +1,413 @@ -{ "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:7.0.0", + "index": ".kibana_1", + "source": { + "config": { + "buildNum": 9007199254740991, + "dateFormat:tz": "UTC", + "defaultIndex": "5193f870-d861-11e9-a311-0fa548c5f953" + }, + "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": "config:8.0.0", + "index": ".kibana_1", + "source": { + "config": { + "buildNum": 9007199254740991, + "dateFormat:tz": "UTC", + "defaultIndex": "5193f870-d861-11e9-a311-0fa548c5f953" + }, + "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": "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" + }, + "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": "search:6091ead0-1c6d-11ea-a100-8589bb9d7c6b", + "index": ".kibana_1", + "source": { + "references": [ + { + "id": "5193f870-d861-11e9-a311-0fa548c5f953", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "search": { + "columns": [ + "order_date", + "category", + "currency", + "customer_id", + "order_id", + "day_of_week_i", + "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": "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 + }, + "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:0a464230-79f0-11ea-ae7f-13c5d6e410a0", + "index": ".kibana_1", + "source": { + "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:200609c0-79f0-11ea-ae7f-13c5d6e410a0", + "index": ".kibana_1", + "source": { + "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:ef8757d0-7ac2-11ea-b69c-cf0d7935cd67", + "index": ".kibana_1", + "source": { + "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:132ab9c0-7ac3-11ea-b69c-cf0d7935cd67", + "index": ".kibana_1", + "source": { + "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": "visualization:4a36acd0-7ac3-11ea-b69c-cf0d7935cd67", + "index": ".kibana_1", + "source": { + "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": "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": "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 + }, + "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": "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" } } } +{ + "type": "doc", + "value": { + "id": "visualization:1bba55f0-507e-11eb-9c0d-97106882b997", + "index": ".kibana_1", + "source": { + "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}}" + } + } + } +} diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/mappings.json b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/mappings.json index 8ee08f968d072..9170d8e00a77d 100644 --- a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana/mappings.json @@ -57,7 +57,6 @@ "map": "9134b47593116d7953f6adba096fc463", "maps-telemetry": "5ef305b18111b77789afefbd36b66171", "metrics-explorer-view": "3d1b76c39bfb2cc8296b024d73854724", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "ml-job": "3bb64c31915acf93fc724af137a0891b", "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", "monitoring-telemetry": "2669d5ec15e82391cf58df4294ee9c68", @@ -1745,56 +1744,6 @@ "dynamic": "false", "type": "object" }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "config": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "dashboard": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "index-pattern": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "search": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "visualization": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, "ml-job": { "properties": { "datafeed_id": { diff --git a/x-pack/test/functional/es_archives/reporting/hugedata/data.json.gz b/x-pack/test/functional/es_archives/reporting/hugedata/data.json.gz index 83da642be87622f3b3d7a7b0b92f62e88fed5977..e5fb8a73234e4fca80958028dc282642ece8b96a 100644 GIT binary patch literal 33885 zcmV)tK$pKCiwFo)GcaHP17u-zVJ>QOZ*Bn8U0HJ@HQINMx|sMXGx2320sQgR#l_|N z@jk!2zPOw&Czo&Vz;D_70gwCvf54x`6wgkUi&=4Vt*yemd&?T|vA5U475fSNQ+wtFQhz|JBs;Po`r_%#4hbVFa5!9$BvC zNQ|eNkeQyEsrWN5?y$LA*mkdRE659hhx-=Uooiqv#n0Jq`Si!}r+l*hK3`{7Rl|OE z68-PbKmKr4iGA>d4MgtL=ssJo^Kx;yz2d30p@0~(+E$9s6(&FviiufLhk-(Oy@%ZL2U`^(?*as}VLJFr@oYxwI| zxC6z9kMEt3?=PP|z-eb!aPW`a$M=`-FX7C*Ha2&pvNpH}PRxpYK3%;nmv@y+`1JSq zI^1m^-Mg+nt|rSeuRqq8;%C$f_o)&HK2$HteZ;ruKHTSU!(#0|yO-uZ;hWihaIa|l zr0tt|fBC_QIbY*5uYbkAoK!#Mvme~!p+zrIYH0VK((XGa0QZ?*?tiXT=0J&R9l6{) z^ueo1>ta23#)F8gYQxw&U%zR%f;ODs#_pco)o^x*!lGf zJkE=J-k~=+@D)xszge%#Rc$3sEm6BC%Li1ZYx=aOZ;ca`_B9UI`qwyKX`}aWt(%Q= z&ouS${L}smc5SJ;*6*|Xx;@Vx?OF$7KirLL`?=4`>~2+^ts!!kkT>Ltc>aNIAGYKL zkF4+YG^u{b*1!)RQLggoKg#;jWm()5)dO$W>-+1kzn(8A+5C37T3<`WNPYkMcCj(t zue1C6ucz7ScD&5W>B?O&&Q|%~;iB4qJ-s;;_51I?zxZx>@yjnLaua2zEx+!+3iO=L zk-OWG7F>)jK>2_D3nKNG4_7;vgpSf(b)OZBIhg96AG?0;s4Cxgh;Q{p`E<`|JG;%s z^L%$KySXXzn{0h}w9K<9y5jGalYgQ_i193&ukxoi$D8BYf$!!e?RfT<_uI04&F!@0 z!~Xj!p+&`A{uW=`#w-qA==SUnHGO&Jf_IK}oQ)rybA36BJaj?l`d22eq>>T-b)GL) z*J3QJq0&)FnvhE3!wtvOfJ zV|UkY>fJ`=niOU4%3}3j50lS1Xf<5xO!e56^K6{YPd{)8#4baX+W-EKx5a9`yn#;Q z_>EN`5@?mvFVHM79ei%~uwMEg$A3>pzhwn|Pi^A$qmEzdRNeWSZL_`C9{INOl;+I0 zSoJ4ozDMU*Prt}-vHQ4K&MwLvm^}27=f70f^8}#VpzQ8C8{(g8Is7Plj(U-YS36nW z-OmAxZ+C{)y#4u+!@u|cI{2D8@;K}{+zA(9HJ##oFRk&}UGgko*xYw{dGtGfD^?HL zy!evU&bsjxwO@Vi`aFI%n?Sb*k&W+icfp_YbskSwiS<|aYR~fSo=4m-SH-4-S}*T? zm#p%#$X6T99B1W6=)J%eyw6+RF8}jCWig!>3)g`iF_p{Tzo}uWgg?U_2es)LI?x$o zjNJ0ESmS zxAuy`{0)7Hqt~~^^uRst+P@!BZ=XH&bO?u9zL9jkSpDmlQxvY9%-BAi8SU85Xz~Dn zF4m(1y7QvU*qd$zc7`~5u?@aF!MC}=k0c{uBKCrSif}x2$g*K z;x+>ha|gEWk{eIkuh>sDx&t3ETk_`5G~BiGu;pUg@)^%FN!e_a3qePM5j8SdrbmR3 zF%{J085?_Be%_5qT23+7{5(YeDF^>v=FJ}VS>p3zXIW<^KhJmkFBp`m@7ws%`jz^f zVV!k7)0w{@oc9Vr{X#egdxj%!!h6aaGkYZkiSWkM8a{fPKQ0%av(ejzx80>>0^Zgo zAF3x25RbkitXQPgrAC^nKez*1d+MG&mT zBwn>h99SU2aj>M)IHGQ}AOtWF4X7v)9~g8Y0C8PNoZ-SQ?yIdpL2;C`9I*C@#1(T%KirtPS6u z0H`?69t27QP=<3ul;PCaCSjn=15_)1eGGk2d@mSld?3RsvA+~(#hPFktpQnKzW6O) z6w|)>`$miaXu#fu)j;EomfGqjW|VYckqFvs)-znQS;!Wxlo+>LAg78q<3gV)CWlF+ zfl0In6Ih=jmMJSJ6~MBZRMf|v0_DO>ccP#{4NDyxMt!U7L95E(XYuajH@>eM`& z_pDU&y@B^8R2ChzHpDOpb*xbhq-ZBvg_(vqz|(iC!O0NJVrj{V2H&orOO(1v9DF@t z@?3+1D_*f8u?p!GH;hsU02wouGEY`UcYza6gHxQZ3gE;Uqudz61(kvd-K6c3b;1&u z11isZCZZ_9@Odb8VD+6}011vmB@|uifS#CCPE4<%4uqs?PSoa>I#blaK-k8MF=ejn-ypfC0bG@ zv_3CxZY3v^2CZo23ZiAWlAKyAwIGyHpEY)&TorMD?47wk9*2s94+5AJP^Be%|t`mIhKOTGx#e<3ws*Ev$TOzF|diJK++fu9k zxqA>SsV(JNf__oPEwv4>e7>qB>i+mCn|$t_y*nvF=?0}OgoY4ETdt`U=T%{~MBpDk zEeD`rAVkO>hcV=Z&TBD!=-GFH1R?g$G4a5tB^o}NW`f3-A5HGLDx~^m4j{qnaPo_r zo-J^36LMF>8TCp4RGf1U12r7KzIa^T6@3q;RzeJ4vkvTu$Z$ zSW*)uHCN!Kq(#)uBMhx%Ws;y3N3KqwH4t{?RO_Z#_pMqWLJ%yiaX3}tR`w1R$`C?p z7}G9AGPvRh{7JZmQ<1#7UEcSuTzhNg_!53x3B$q1Q%^!-x$* zuRiswQ=eXgKE-1crAr}Isujd&Ek|B zVj#8wcisex`?6PUr20V8vfoS}N)RwY#kG_5Gg{nqKskuM~P`d#X89?yaK4Sl-g(t5e>vLzEr5U z3v8oC6y@xBOu~r}3jT&M6ON?hJO3SSE6l!-h zb1(^1k?txCm6D7B4V6Z6Ym_It>1tSh;7~E872{xr7TKsLwh>;s&<NWvakc@BbQ!;z^o0|@vA znz{9bf$dQC-k0e+0hSb0Yr?F+vMb)qe{$Q2XRj6Ow&#kEN`~WB$T4KrQW=G7&w0`@ zs*_-lB||Hci-yr++5%YCVu7|+#xvvDJBhcAf%!ymMO$JRE}##B8jh1DsA3-S&z=R}?xvjx|@*5?sQ)Nl0lYiwz(jE$;e`b3lSn|2K>k zqgf)T;?yb()nHbzNP<+HuL>ijfGQY8C6UT*-f1=oOBLmIf^c*6;}D3O90O#wCq*60~d^$rrY!bczaPL4i=f}DYLg***V zasE9Blop&@4K7Pb0zTdo;dEB}@wiTq6OHrkC_*SD3Uq;SELW4n7}{hFvXeSBmI|ym zXLSNBN(^uYMif>yaL-l~-txyb!knJh=nHOmHkjSvh6~cO%(@4nPGUfj0;8yOaR4Kx zr4Kgh!D?Ot%oykh1vQy5eBULR)odtEmwbmx_jd&qtwr@k|?4*~@Xiz1?yg zB2drzy7T6^CGYA)5}4xD={QW3putf|rNyPzRdnQn|5TOXhQVooN zC@Bb+tcjSn&Z-Rxl?t#Z5_J+Vf_wS`kZ^YzTe?)6D@0(^*CV1^txwy>Q`H zjNzwYHf}z_t`hN%KD4a_=oN4XO2IUVz}@GY>4vU$07wN`yt5Aj#&9VHh<<=`Dc004 z@ACGodHcS%EjfvoAd-WMXSWZpHpn5^<7+U-Dh<)|E-xs-ai|PoLK57- zglHTyP3Qz5+N0tT0V{x)sza$te{2;MLi2$vHcao zT2C$~*ybhLt%vfY!>H(^sj!On>*1U-uDWUjlh6q=b!O2=8`Uvsz>0QPL9hl=gIt?o z0xlYDhrt~O2eLqlv&19=Jdvh&Vn7U4#Snq(^l70zWiqU*wY3w$6-l#B42M}xnB<%( z36Y_1m6YzXrb*C>bX}*=f`G>mMX~AuYezlNPG@0s?}LYd&9EtxQV8%@&>M2__|1HS zcL}U^tg!dN!vIjYqcg)D!wnV6aCN3ewPS$24;_Xyic88Fu0c*Mu7c&E@0~1_i9n^; zj3W-I@Ln8-NDf^eF_OcNXY9V4DJCKjRB<8Ui5+nUva&C#Ro_bW3U*`^Nnnaos31Q; z->eM*pALbuIg?JcQZ;&)E#m!o98xGkD8s-klgw&OwE_)mVCBmjw~~xW090HE7=~#O zi5Q*+r#KH4#Az5?-vCZ|+4n9%C_zZ0B$hL991=8=dFCi}vgE%*>T^ENdrq&{LWJ-d z#1;w6;gtfesBor|A(XG8z&x>528cTLfz2U_u2Kq#;EL6**PvVi(MkbU6k~8Agj8!V z8}5RneQ&qxL3F(vQ*zipJERMHLtvH)P3NQQf-SOARq7JKtgqH1SgY0*EQ4UzwW z3@cT=WuAtY#rBr@DOxs+8lD=nVln{4K!^Zd16eJvvd4Tr@7*Fnh5%f{sm?@E69nY3 z?miFZF9uRZP@Chr{}2ge7``#m8m`;k3kjSMA;5;QSo%i42BBaf4WouY2#K3QX(I9bO4cGC1*yXv1-JSp}vk zeRI^hn$8EZ-VR|9={7v*HjFyq${*muN+F43`AxV~CrQ@V0oTC7?G$juhTCDd1~JdCc3cDSTd!t_sP9?sC%_Vt zG0r(dZx6pb9b#8YS~@`!m|-lbMsZDVJ`P3qm?qXJGt(B1wT^lPs+2>BS%ynojfgAP?mlGny=cNQr zFF~^ySR_4j4VZRD8_VA3E0=*8xSW6qa-*3LmI~rocD)lr6p4_EjC(>zF$4V})0F>z zdvBv#$B}CbKhXt>Wc|LteGl2zBs*3lK$bJ3JpCoRx09J{Znp5Tx~dF(IZmR%0AqR` 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˾C_Vojy7lQE09P4q(~ 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

f^VA3z+;f_!$%;Zz)J#6V%kmwclVlopxg2ynoE{ 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 literal 33745 zcmV+9KpVdwiwFn`TCHCI17u-zVJ>QOZ*Bn8y<2l!$&n`b-oK)@cFpERRTl1X=UZP! zX1zAuJ=R^@W;2pZmL!s77QsYKB2gujtp5ADpOfJ2c=~}l;zR^xC5Z)+fa8O^$LIII z{@pj9c?G z=jlU#KJD-Kr=R!y)z7Dg{qH{h)~mbQyAyxe|J#_CuV314Kl#0z{r#)c;ivQA?#O@Q zhqs6OZ|I*#lIWX@^xsTh?f?4pZyk5?E zqNE(W2W!6b2JCkL`~2O@SNr&#yV<>b^)kKOzee}z-@o7QPOsj+_&*Q()8qfx9p60c z-b`rRKD^o=_e-;0GlL75mv4COM;3nIpO=3D;d}b_EC0wY-`Z>IUcPp2Ld5Ba{0%?R z_lGy99sA7x+@J1e*ZJ2kQRPn*Cxv+)jNg3nDL#A`f8!7IQ`bCw3;(gB7vbMt{2wpZ;V1IR6Fy@y&l?cYD~~(=U$?x3}~gH;4P_*`$xW-re4_Jn#0i2tV!V z+c};mNsjwB^VfL#LqF|KyZ86=r+hR04jG>T7W#bo?EH0EJ*H1~qHkqCeX8jvHhpaR z8{eGozL`EgY0$s#U;iupjHllgp?{u^&foOqYqE$Rz86j2-+ud3fgDd?STtU8YbW-{ zfA~zn*-?M4yR4j_sO_hEdp?|RCsX_H|M}nl{eS(#-~Yq^!cTSg=MT>D`Xhg_y|t$z z{$R`t<6j_r4=ko1)>HcR?6E$x()nk}w)@eO?B2XNv7vtc(bIl+^ZV1?`+vH7^`HC`xqtdBzy0oW zS!M+uMV$LrlPmjFoKJote~UOzAN)d|rw>0{ph?d@m*~?Eb`WWnW00TN&GbTiD$IwE zJc)u^h{C`1xg>A)q{^p0z$~Sif3YG9w2|f0H(fI;+eVxV7g!Od5jOk^j3mqDMf%$W zx_p)SBh%mEXZ@S+zM)t75C4rn?SK2z_a9+rhB+(7?r^-_-``Ikt>GH}^zCQd<^BHm z`~?2%CI0w=`FtcdFLy5=8HM|0Ag9NHU%vU@hVT8e!@*@#{rRVRR&ed}3zxo6kSa5# zo8A4}mv_6<&5PF|C1)eEB}_A0+PyaW?{+t@OwN1QA>RD$Z0W=2{~XXS;-vHb{`}3y z|M}(Z?o2wxxyx_PZx6?J8^%#Z0n(qY5c0d}JG=kn%RJog|7CZ2b2#3A)bF>4oBjXX z9rw3Cu!4Tn?oYqlpZNDbd?My|Z%&7sf7(4oT)*(c=lGMcxqAA=UoMZ&A59pHBsen88{=JWTNlaG=c`3G_%->@I~<{$p;@BjUuo*O9XF6L81 zDbF_j4au_0-+M~lTz;BzBIXj~DH)g#UgyKN^Q?f-(LeEf3fC&TxA(iB?mtO|fBvuk zg>2#HfB&-|AbIoC;pGE4u>ZL~&Eel?KN$MvceD4of6ADD`uqGDetz@0W4nCKzepcF z96z&>&#Z(C7Vq!>lN{mQ>3{4VN!_Hc`1xPBQ{mQM1wykX{(~!kh22d(y1`!gDQZ&2@$T?90~9a7;e$_xs1Yij6!XiXxP4t*t2ngxJAAS1H z{D+TB*;sO#B+~|P9d>UHcYV>CM9{#B5ONBTEmx0{w+paB3$P4>b$nH>TBZyv65%>n zK143FrRad-livicmxs<+y*wyD zIUhXwls)}2db6~{)B-20gA;CtlZ^-ltTn+p>y!1*vOnCyw^snF%(JUNMFJ{Wn@mU+ zQeF}UKmn*t{Q66JgUVmQQses=xs7grHP9+GK^?7utZ;w)d4D|I^ycqRVkn>ydv`e! zjZe|X9G94ZX;>g_0>&hso7}$!!M{Szj<2Lv{d; zSk{t?O5DnAp-(^Vj-3^XBv7Hs(WT@v@z6w(;nMb+Pl#F%Y9k{rp?H-%31e}1{?>iGw7GZoI3KLkp^9v-w zb*NlGwCF+#87*P9Bvn$HuX=!ED@4Yzy<_HCi0gm_h|z^; zoOj%*z0|JUr06w2g@+1omA-`s4J`_r18ESL=mA}f&x@Nk$;s3~t5~^Iw2}?pLe4%q z0|=5ewotB_xIcDh?vK}@vgCsZra%-v5_?}pI8j2XI(3IiNR^T-4JjK!A}1ex2wYbc zGBUXZ%fd=fmHI0MD(6#1m!oqo8@DVAMu}u?3^sbwFF>0iFTj#~)kf6)@yFe( zciq|hB!!l42r*M=NP%>=5wd$;7uH4u{_)4V0Thgc(Cl%Hk{3I#zW z0U?`6ze>vljg%j)?zv{9dNT)-;OlVm$2XlV@c2e^SL0N@DgagH+;yPF@$1LOyZ49Q z#nf4d@imJ9J9RHTpVZRxFwG1#fGV>=9VnuqDO3DRN;Of_XVKA3>dvQ|3Yp}xgba(I zU=@Qyn<(`2k)pz|r0&q9!yRgYRhE-k0n0}de6)ewl+TRX^9aKxS(z$mm659zv<6{U zlWM&=oO`PliJ*cNBbQTsu;8uG-40Evs#puGLe0{_@}8bS&bVh!U_~#~b6WBCDqt0| zh83{D#gsA{=e;Au`tXJ)D?ImZ-=2lg7-`Nf7$6nH3|@E+0s#O82Qq5Aa8Y4C&z521QPW|{b=ujK9cm*H`B)wC`K4;44$B(SjBi; zMm1JS#%z)2tAbQf47h?+!a#n?C;DY3Cb~pQ@>W{}fK`wxbyg};24Vn2j{m@W6!PuqsIdL9!n=tU*zIU(oEQ6MX8KK={;_Edw&lM7O zZSx!j%gHiR$3zH7g=U+jFmM~n-hEBq3RvDjj0Usgwkx~Lf7;x{vtJbJZRd`US%&LY zXgOq#KBU0A=Y(`jXc7!!HM9!3sE!uKOkm~A4cajzVd7aF;@zS!UkR>aOVr^a`rsg0 z9-4rF0(oe{9cH-Bcb(fGXBo7(aF|}v+gdWAP?f2b4%ILlEUF+?=Bsq10#St&z#AX3MJFu2YBO0m8DI@>RcL|wlDEa9>WX%ChYwfog()WQ7iiu7ekreMpbsCdu|1NEMo(iqtp@WNz>7@9*AsR;x({ z1+H;MmMXzj=CRh`8i+O2fvb!+tiT1vJ4_J>wm{Y>lvBkb;U;|ap?7lhK?`yQc?xYE zpvwHa3RHB~=149pctbv3h;W+qemtHl-#JK`4^-ElDznQo1eYRv3 zFK4_Z6^kzU6r!-pFf^$;Ukq{td;A*AaZW>YzVgBnT!$(dbl&g*6BEH_VU?k4GG_^u zpeofX1*%BYlzkQv37)eQN`qpj_C`*=^Ro68s5q}|lFc@NjZWzL5VOh~`S;GlgDX%u z?$$QZf_D)GGT&x+*HuEQFtvOgDTn|rV|I|d3m`1I!cRAZZJu1NaOS1ht!sJGF*^F7 z7FNZ6UC$}=*45ItJqzsU=7lPd@;icT(sB@)r*6JEJ&Fwu}Xob z&=gk+Vyr63INsA2T@>16u+?tuN^lj@td(+Dwg$bm7(7LW(w3BVS<@ph24vX3Q&B}8Tn#(0vD1E z&(x@G46u9YP}3;u!6NSNX2ir* zf~qVeT)Bud$Yx(wtKLfW6icP#hkWk|6I zT805Lddx9G45VQTtfaj0CdrrzK$V4nI!r?(Vr(6p$~=^c(-^032&et2_mv=)K+`DC z?F^Png2|&Wqts;O|Af@L{q4SUdi^4VhSv}$5^%z+23$qq%q)YJuL5G8+$%#wo!`J^ zNupIsp%PrB+VwRkmt(YQfK|j8CJ{8%8s^|GN!t6|?j(W+))))a-~v|fWgizIw6K|1 zum*Y+!P;zuJiAEVx2WRO0<2uWRKNz=8*Lb{V>wzy9G)f6$f@&Wih+wcd`97!=TWy9 z(_ukxYBZ4+vWQuVuY^aiu;=ciewJeR5C}dZ*11^tL5!=IIYdmsc}&DCvJ)6ZbgiyD z*8{DBX=A2Jjig>+tqImypM=I|Nzb->HV?-0yyeG?aA^9AaH6`)>?-p$+v$R7%rT`X=N4}ks2{4qF zy~#sV+Z2|L{rRj${s$>5&9CLY4lgTxE%$4*Y>XbB3ucE`10Y61D0mIBEpN8R{q1dc zizFEoxW?(uWKk;wDLen zrfC=?Lm?y|g^IktOQ5ULyuwD&WEH$hW62e~cwi`kcRqM>WX~L~+9;5$f>-hLsN;o* z0c}XZB#0n&U$ojfILq2U|GJh&jE1+K1>zH=VM&OZoCK|4hNYyQh0sVnSf7D!db78e`odaJdRq^#a_v&_ zvJ@{8UYWoGp9Q=cb{>w-kC`g(k7{?9}hdXWdKP z`kglD?lQ$UE?MBRP8LphO1<>kIQGZ#PPanG{;2i24YEZ6DnV74Vo;$PWFJ*|eCBGio7dPUpARU_uL#03wg> zl7}Gdop>}Si5w<##phA7rZ)OK$>Mx&`KuomQR<7H^e9{d1!L& z6`tVg-O4u$p~N`!6eeeaMdA*8)qD9UN)Bw3_Mu9EmD{5R*dT?jCx>5NkSO=}N=4#} zZ3TioisKWThj%7Z3vryw@i$ z@$yafQ$jtwDyWx+7ozvUM!pf4V3~NZP5QR69$e*QLW2v~93!R}FxwFEnUCWpnbJyt zl|POuV2O*VfXI2RE3FMJU&j%mq3hhcl720*6qGOub-qCKaq2RYyelkSA5qna6vV=ik+7V*C10(GhFw(L-(6g z7b(<4VerUfIvhuOvt$!Z5;?Oy{#pgGQj7d`1+hWaHA6+nN|kI4vN0xqu_9unykZqG zpTI-TK6~&s3Qd6$WE;hhbs#GZBG;8{n3`JuY3Z{%&k7!si$q=dvVqHX*+(fx+}qHw zQ~!OilMLD;IYLJXR0FHf-LHT(#^DUChE}1gUqfq*D}xtH?C-Zu`XK%w0nwygbqIc; zdad&N9Ef~@i5}3!xb*Z*a?z7|-S_V^x!0N|C~EZHI-jCPa7I|ekInYLRspM2wKTAh z>@c895z!G`3#i&1V5|aFAvx9c$|WvxfD6`SDf$uBY8z&HU*3vSYJyrL&NxJ?M(2DX z&r&<*M@rmO=$tQAs_W*5yokn=mr90$!ywE-x!kT^RspI~tx{@btIXXr*}6P=_3kT# z)&0TDsVwq92Jv(j5&^LkOYX|6o?_v&ZOt6PK4 zCE}jW8D1VNgf~rkxv&;k#d@XLU{20EQZJ5jGJw!k+3XHM6|hSEmD+)7f!B&((Dt22cT&ya|IC>RRbWzC8h~eQ@eyajp#g0pZ z%S1=K6hipiy5A}CD$ixSupEZ@u&tAJHzi8@&1;3LI{F;h70 zqjX5jHtGJhwZJNS6IZ~BHaapva`HBcn@h3D*f5*n&z)`XXD!?r;?w}E)X%%#u^~yI zAT`LPqDi6lr`vt+&~O$)W2KN1hCJ=$L~>crOFeAj+baQ9=G|8T8|c>UBx~9E%Q^yHSK$JRr$crw5?C&vFg%7RUL2?nm#YR>*(zZb zE_V2lQ}Q-rG@dV7Ed7@h_iVCE_~%3KaVU~NbM>pc@0ziQ3P=^Q2oBpRs!EelU3wK~t1 z3!Ov7U4=65CqYrfJq7GbT#hx{|1}RgU3tWHc93@UNXbztZk-4{`|#Dj`+o+*go7LW;b*je(+{EEF%A?fw?i+JrFFfU1lx=unL@Q)eq7RcL=I zQiHsIvr=_m>cAo>U=4Bz!`1?;RI${x4kp{oJz748n7p(rw^_I3t9QNkI*s)#m&XSoxQR`86;=YO)K_Uhtv-NFb}!GKs{8CO zi*Ow)AgBw6_N&3wX%oo z^VI0UmktHEytunzlg*L0hx`7Tb^mpYezBw#&?-x#de~ZE6`G+AmX8_O!Fvxxx?tH-D|K9L6YacvEXS&lX^{w;Vhyo% z25Mnd=&*FGMsipcuqt&}x?YXsuquF6>abQ6%Q$B!;IWyLTF>jd+Qwm(A4e{9SK4Xh zL57wlrRtw<*YfR<3=mF;Jbwcw33;q`8TZ@X3$jQ81uCSd&=FP zHZNoqA6EE4u6VF9*6&Q}Ri)RxP`Orm-ED~30-(igrxf6`;MzvH-gKY&Vi7bxLD95} z6i~-tZ4`QL+pLFlwZJM=trf7wSX(}6*5S>uJFjOMl&IRFOODT4IdIkrcG%{kJxp+Q zZ=N}7%`?2vDcT%t^wE1^N&v;JGMl8ANzne9rICk3^gKc!hZQnkY+9P3Yj%0{_~Y(< z_v<>d5Q^jSq*X3vB;*o;5CS!6pn1Zp`)tK5ghJ>^w~{jy{y^{<^HRfHv#XCRYxffs zvk;1_AEe|8^#Ch$_G^FSR|BuR`Lb;^myl!8;EaqDNf1FH*ZE z%lqyckxY7A2%t>RzY)zFsU@=&_(;8#saLPR# zdv9SQ2{hjR#f|&tErHr(e4qwYrFwN8DoByLQHC+Dh`L%} z6)M&mSVL@%oWObuOjJGcG3Uf>UNE>h4F|8_a6{^H<}T8bvW!YVB(%r(OfDQh&7q*DzI867^W&W?`wuuywrv+T%=KPI}u?AeF#;C%DfY$Q_a7d90(@X8Ltx}{U zYX2x_Wkb{$>G#=lmDAEvzoqcf;CRq5SxDr(jBxA`#BFw4OrcbPs;J#hhbme#frU&_ zjZlgPah>EAyKk#NRqEaqsGhNQtjP^gR|%?8XQe>p`G#ml1IfDR@-o^<(iGTMuBv}z zsKq;9Cfb+rj&F4lr4Crd-b!20mNR)Q6M5Q~B55b|ueCZ`Q3tG2ex-qBW9C*YOcWGm z3K5rM0=`Mf>*BC>y*nH(LTJ&Bhm=F$2AZi{xB#rl{K=Cg9y<@qU4&5ZBBWxXkO#eq zfI%QylAqs-Jk$WJm_O)%4VA2^0$8~N(*YY|l2|?oQ8LbN;^X?-Q&qB7^iNUfCpkJDD4U#>50;_ukpJhQCKI>2TZ4{HHdOel1~0`I~I!6I*`5!S85 zCdYW5@EQRwwfHl}?V?LwqC&#CI5DBOJYly|KW&-MM?_52LpvFI=+^Hi;-*s23ch{lfZDYhvc}a~8S*n(aWsPkNkiLsU zf4F8T6f*CLJ0#2zLNeJ1-IP-E$R_FfyPMuCm?sggV};hze+_vqk0GVJd=71s9Z0i! z4WV7SaskB1r|U4LKma`SDQkQ%LWR*aDfCZD)tkVp?0t;I%dQSig$ng`OaYUe-G?;q)N6Id*$|10CJYe3 zEd%H#O>VR8b>B}j%Wxg6FYjwEiF*?|iW6GD^uF#l%b>6Y;~lT2G1g6NL0AGPjrMGk zoT&p>sdDLXfltV{fWaUsmvFgG%w=s-=pj)bduK`~85GtKEwL(ZqYuN(;*+Ge6`WSFZry5LYmlkni_G^$4%hGk zigM7TM4StWp9%qwl+fD77V7|3XpU=uk@LzJocB4zH}JXq7qpNd}E@K}s0%bVY?t@_CQrCT(qX;3`Yjtik0eNX)xJ3#CVA6~DfXVyXAwrPUqCIWimg!JNJfmF zf*w!50AC)K(y+OVPLWuc< z!mUGRn)MsiK&nuuG^8BC1Du_)-dGUM0NdPZ_4((BL$lfObDs0yC{@B0E=uz)7vJ}lgrWX)X1{8MMNe+FJ zO3q`2tE{C^eKjtXQik2#u&Q4s8#3dwUY=6Sc9B!$68<&W~+3t z#z`FCRXNC7*gUM3kSa)uBA%R)_EFfK}+=SHK#Bl+LDDzQ(T94wW5t$ZMbiI9{R>g1{S^l)11HP(^%H2Z}fW zI-5<-4mgrthR>~*BUVAG)cmxyh}m%ORR9xnm<^9$E<>DF zP3~2YDz!ijDeH*5NBZV-Fisjc@l7_}I)bXkw0KdK{!X10ALPgYBlURG8K}pb>o{ff z*=O=pfm7~LS|HYJ709mtV!EBam@aT%DA#d(j*zXe_FY_@vdIKf6|~CgUMpw~vIp2s zVD;V{w$s*z89u>EufNX(SrEuVlf4$M5>RE$S2|E*xb?|fmAmyqU!}Np6Adw{oH?=q zo>5&os%A|x6@V)1Wl(_%7$cIW@)R?BE7ZC~kG@GKLj|A;{kaAdxA1x1jGR3I6@_I* z&1O+609EMBuL1=SB3K*3bmznfn`9uRW)1(lo8Bya5%|B;4CRuNLIQ9Bl-7@czpl2P~#0Q(f=?`)?{fc zPMjdz=q2p`5MDEn%qrBXy_(G$WuMxlScmSJzr%H? zz(h;b-BC#BxO-FxqMNPy*uC$qRqqv7HON6BR|lvZ2;LCVWWEOd*G zkJGNZ>8Yz@F!OX}B&QyXaQs~V7=3%i7~8d>7FLDzW)&+) z$wJ8V-;|xt!laT!KDQ}2*TAYUq|vdW=x^EzM!bsiRzg0sU2TFSz3t5uScdWW=aX*L zxHeI)Ui!I-akkyFtla|~mf<>D7|4BPW34AXYMtP`#7l(Ru+b`T74ruTE=V>9o?hZ^ z5OVd)cTrq;zfF`w-$;Mu(#{H*L@a_d3nS0d3#!$m3G%|XD%4er371w^wdA*K$l$E! zn^l72immpfRD!Eqx30qlJ_hf=@C{>^oN!11U6Ysb39j1#m;?ziK9zVf#~M_k+#q!- zF~}MaTM@DfO7;~T`?~Pe?&m@$88ihOW09Dx23NUyjjvp;8d}9%RY8mN3iv1!v3qh{ zD78Fs#puEVjq7MD&XtJ zAtDB%5%h~CR%pcIs+(LkVM)6mb)1FJ>~MtZCRh)ya_!RK0szu2n=@p?{qe%Wizer2 zpY&@4^-|ypq*ys*lZ^KUq@Kw({kTvKuHwpu23L+;ul7h`wl`U-RYxh`waLQiI&c+p z2n8-;@D3p+p8_A#7S=$0I$f}RQsU5kriW!v4Sj`3 zY!ywOE^RN+vQ2KH*lKVUtJih7ERgS-S}KWn#VnBt6Wh)ARDrA5dns_ygJ`+G*#Z!m zkY=er9faM!zpMKc#!+blC(ZKKJKhzQOqA*xZPp$^Ev$;8pzByUgJg!zxg1Hmf>4(= z%}%3K!K#>|Ud0NKca=hb$fJoUlrzvIsfxW7KOKNTTm=h>m1c^40!2a23HfFl+iau$ z3b5iWZP84pM9CD1(hJ%7CQSaxVGWSCc~n35+XJo| zT%`#J9WL&)afw$?CLT0f!j&2}hYz4!`i_w?R%WplzkOi%=&aE^Lc-Hs1jd7=Zc3S5Fyw-We631Z>54VTz^ps^#B1MNh zoe@oRT+VPpq}XmTOBJ{Z-TVq%BaN3mbT`EZy>*^94F|{;y^BfcO=`zYD?nAMS8720 zIWb*UFq&*+o{;)W4G&+4)n8N(4>2Z7j5Ht@48aIBj3yTeI9t;@ZK~3SF-BJ=n0oI# z*y|VRc#92>U|<%v*hq2CCjGhl0oIP}SN4t!vJ)L6JWC%Af-c_tY&*+2;y4LyA+1P|O#x_tX=D+c;-(;X*mRp%gib~gc4VeYUfXEllNH6-{* z(9YY>S%RxbfeDexx{uyNln!HoI8w32jKIU|*PWLD0fHO}#(hts>>=_`^zAr7Qg5Wq*1m}f0p7+jN-%w&h1 zmw1x^if>Ooxok}i0f}XyaA&5;RaRFAs6vfWff}ZZmgMYS;yURSF7G4x^Nfk29bR6y z%rdl7stQ<@s?`cskjVVFDq%bY($B(b8@)Pqzc?_wMuXm z>Xr_diJ7Roqfo~M@>$Xjf+p*WE^wX8r@V^cI%{)YL)`MiC$KtqAYX(~>H9FPL?mnX zH}R;i^&2SGsR358CF(m6hnP*BHLJ$L?o!p#*P9R2*iZ+qQsvU&8YGUsXjk_n4T|F~@vNJPIy=Mm}5w2t9F>+GbanUdY z=Y&42W+~!$2Tj;rBi@tjT44a zrCyLt%<%l`ZD*`V0wta7JaWz6lWzwXgfyvZm3e=xvMi+ttH+gXDPwew0%KLdxffMd zSpqHh7Y)%*8;G&{Y?Bl6Gp$acRKcpqx36NA!A5Vr2ecWMC7o6?CW9m81Nn4PCgN8a zjL4X_HQ<~6WPqF|ZGf~V-uSV5~GNCDtBn?6X;8hZ^3UHdGB z>a~VgU-6_~HLlB*YnXmrZjj}fSP`;PXTJv7Aca+Tfvop-wMmAnaM?r-KayWJ(WPkf zGT#tv6B>WgG)AL1j`K& z3%Ht8UoU9&u2j!5Tt^E4cn4zgF_V%JiPCM*8tAgDaJE{%Yc*cv!>>Ja&4Ed|-a z?RK#SyHWVl`(Bd{6*#cqBH;v(qOidno3zznC8$bysRk7}D;vRcsFWlw zHp)W|E7Q|wf;TY+BJoR0j14QVy@&B95xx%9>ds5A<7BOmAx_lPh@H9KwK^t1V)nN{ z{m|+V=LB3Ws0z(*4XP1n){IqmPj%ff815>JTNcixrkP zG8bE++QmFZ<({+dhYIztvaI_-tA6n;ouOC|lg&w5BneJ>2sg>P*MO=_t#qiETLoUw zW^Fc6DCPKOS@#-HmAQ5eDvAM(ODSfwq+STp$(SbdbQh?4KZ7j7b-nVDVnJ`C4VL0( zVWvksS+Pl<2gLTi)6`=R&MW0pY6kq%QUtbtUiQmIGB$JpmOMbS4EYW9U;^o_p!HfI+M z_ym~23mi*Qt8J1qH*YEwzX};C%WxH~oWUZ|hD>iDul!rOt0XoXZIhs>0#~VSsc^-_ z_i8N zvH^seAx06dZJxv`a1}m^sxc08F6@J=AHvsP2Mhy&6>Ll~2E=7>=u(s8wI5u4_hRcs z3QfbtsPueYk+6+ zpwJ40M8=pLST0{1!G%dl)UBS$Lj_h*?D7w4tLZ=+%4S&yaQO$_fq9?6L(ata$@NEJ zb#9X@=5t(B;ns9%Sft*X_TXaTzBlK+^U_wz7D3UZUfnfIEiMvKC6dpfV~QY@uA$j5 zVt=THZ&Law!@TsDwm zQLq!80R+KuHEFc@V2j;J#9G#w_yYwzoUSpE$h=UMZL(M%>j75595uj33a9G;R>2}w zz=pU6^}!;$S2b1=&WRg#kw-dxV#{0_q&UX1O;gF65h{&u)?P?%k>CaVK1aw_NZN>R zg-jp5?X@DMiTNU%79W6wQftQ)tWO!bE z*(M>bjo5;DRf4O~-Rp3T(y%na)p-LkeI>;TW905Eol{_(ZQHeD+qP|6jXjfz?W9p- z+eTxjvCYO#8l$lrr(xrNp6`EmXEx^Exv%Ry*ILIK!xbjiJ4R%bR)Dn*Y9SK23@n30 z{!3hs!Ybxn3L3tAoIQ4cPsgQ-ngY}%B}qy76gVh3{qn71l!3YFVQc%%_0OiR;8dXo z990pSTO39xr&2i^XM_sNO~&r2V`wT$%+ReMoi&uKFb0&|fN5*js4ik1R|xn*r65&| zTY3GDbWDa>O?I7ic3N0EaB!&vI`Sd@R+LB=^u(=rV2q>dE?YBx5Wniu7X%6^$|2hq z0jz?V!4sBn!I_29)SOW5krKRZPCK?qo~Lpn%$cyj5rnWGNDdqR9hANYA z%jfyU7}NxfsgskN`=&c{FP}=iU51%yFm-l+L8Kna!>M-s)HEnHDRt*t`L*;LAwvTs zQc)f(dy6gasm%~FaEmZhjsSt2qo*UzSrKw8rPDVxTUHP(c>BTW{9C3d9RN#UEQzgf zIxYG4F&i#g-}q>YR`p%~E^FzM*SL6I`1S3E5)cJP^^mW)CiYN6l+B<0;M0X}L9(h0 zJ$_4T;M_hw

Aq+@yi$M0`Eyds0bGbgig>O%${IB~2(Zgjdl@WRz99wF9g$OG`z@ zla3FUqVXw+YQ&lWG3li?^Qw{62Ev_Cjr{v?Xq)-qw#285XjiO>)TxZ9-Q$(XaDFs9 z%6id+|6y_FJ!XBwN2rN^;G!GeT8ozn0PDCYyrm{~lCH}7C9xr>#l!dfK19qcDoiU( zBM_l(5(2vNIA4ecJ|Kw1VtRF4YJ-kY^^DX=yc`{xG#I|~qICM!AAlukjZD-{n}Z+W z_ewQpsdcsMNU!wy#!T~knJAnVJOf%JGL&-*=2>*xr1rr8yKp@_s@oBRREJ~I?s>$M zw&7*!4bWcohT--0gaTZ;S-d)g>+&4av~O*p>~$f(m@ks&TtD|OWG9ycU(c@2^Z4bN zJUn8w=8A&iwe2gzy^_!xvNm^Trh5R9Q{v$aBy?{`soFd{eQ0BJd3KP{<8^d_#heHr zDpNXc!W05QDx`1OFd4jd2r-kg$szPpYZ5r-xeay|0QomTOIjzi7|kd#9vb%!ZZbI` z{OiaH&wSGO+y-0x@6%ir(F>Gx{OryTo`%0U`A;S+X{TXyxpXtJV^v62!$7AR1j;CG}KJRl#qSpY%yEFp@LQ^ z)GAIPSu5;|03)xAJ!XZuB>^(c%;Nc9kro_cS|q5m&k$26J&sKT(BL=?$k& z97A(*K`W~EadLH_YOT{Aq)Rpd|I43Jmt?}p9lik#I6|gm7|0Vb)YTI<#t%-tY^CCJ zj{XvvCIUh>XreCm7pv2UVR-K-4hJcH?B+Pe3gu0v@|2+$cYUK3I3(0LF_*#*W2#Oo zUH>D=5?OUGs$JxoG(M1etvIaUhIbrN7IH6#ZYBr>CnX~QV0NY~YL333)a}b1I83I^*=jd`?o6yW|?R6s378a&#S*cG*|M;7PYTn`yRNuwfn#E7rpN+w<3yf!gFv z1;KBL&tHV%I38q09lw2vL)-&=J$;`7K9!uP{Zgb&QH?ojXb0??z>z1}TJ0&IhTEtt z4cZ>GUf(9&W}nUAvGv+l=A~gp%I@VDGYm@C^QJwVW=xNjsOls>E|r&c4}PT-$tf5s zv9IuAp_~J3vCo&=qfGxvM`$Y)E)OU=pYWQs2RRtZqe-Me&6)Y_6rRM~eqIl4?YJpm zduPJyoa#wg?}Z+Lm3M}3Ob4=B9US+O`nI{1$4P`vRv;MI7mw)?h}??dg5fkm=M~s- z=@3j4oH0w4JAU;~6P<_>^EzjFNPc!`%ip>+JDgyr+wJ4pPv9|M!4#9HVN^Fl8@8qi zXBP={F#Qf3sA7Tbhyb9Oc>Bl0f8!KO>~U%$yz%?=_;mAZp`+yKiO5WD1MfS&SUP`+ z4u=W6pWjdCp8ZIb3MFOjV6)tU8LTBo|AO6-+Z%g%!IrFcx8jpiPHRD_CiQ zPz~fg)yyO{*fNp#bHek%s+=XbSnq(Oy8;S;=?@CF%gp!6xU)P#MF+olsKqLRzB$wy z-nPCyUQSlAZ_>0aNINp5tk`a0he0~#hfz`m?U`&_#|k1>U+tzbaQdQ~aqRwZ_Yd+_ zAb=Wkj3K9wFFFPbAh^AX9IGda+}@%{UKxD4%lzzXNlrUS_Q^5l*ujq}Rs&MC8t z1kWD!AYLNL#zd=^dyoEZe$vg9+~K0c4bbv zpo{Mo;?bxO`nfr|w4@ZM9-65&p%#NThz%di+2WI&eqr~LGZDL{^m#y(1xyorGvL5o zy(!`nQn$?c?Dr_J_b7di$=#SpF9|9cXpb=FPcPDd7+rmZcjB9O14VbH~49 zTD2KgLym(llbm8Cd|5FuAs!jkXm`XB$_)#z0q}~qhWBuR%Km(75gs*)L@Ly#pz~o7 z>xon4&OfSvFyc*Xlz<)AZZffoEF)#m+#b)n0$DRhvI;SULnFtfE;NY zqHU6xgIrk0soJfij^7^!nFK2l9dbT)wsW+fUM7{kGeRNlCRM=vYguBQ@ODf+J5!_9 z=67<78nF0oMzakI6>YQH^SeN*LOq!a_S+kU5c>ruUcJ4;Vv|q6hJQJH{lZzD4l2*324xRHoFkG*%T&e^fMfC)@S+EY?vlQu8D#cCF`yGFG1K_ zhI|vBX3dAL6=@A;7Z~*hmem>RuwW|6g)ImRaURATwd28=~W0e_v(a z5o(CZ8Fw_?zAe+MWs3+RCj{FpkB=AaU@Q+wpiXEPAQoAe_fv%T48m*|NLRLJeAX_UTk#lXS%y#&c;oynbUMuiJ{dqCH^{T<;AuGFt!Rism?h|-UJk{ zCh9o-GYMbWOv=VZXWrGe3S4yw`D2zTM4vn;W&T~zOS#y1$AX~>O5M_8Pe@%0cKY9+ z@ryXM%y9c%71fPtq*h_zTAp5&HcME@cUm3G{@WB(>r+x3Hq};j7B|S6H##p}DCZQ( zU}S15qdFP(B7R21FCICx`(VE3Y(r<4s6=@C|7#ZmyrXh+=xuA=41CB%mdcFNe`T8G zE#tmW{$y#aQOV9^-UD1+0D3w$iK1O$r}^Y<;s4%j(5Xzb*n7{mkg#8dxbY?D%g)-v zN@pUfnWygmp9%P+xl5IRl&c39DJ86umaSafT}MAd6zpMWZ78Ew0&9llcPaFbrp8)% z=Jx^kiRyG$_mD9ABW-6Wo_%b;H)7sR<&-LczaWCJbw=eS_Y3G|H!jqeq{C`%$2LuT3Q8XHpDwReqLipefSlq z$xm&ogW93ksuXD-7w&Xd;`Ot-alP=~Ft>DW0dtnVNrnvX*r?7L&E@Mo&q{EO_Q(na z@o>1x^T5Y@Ff*xv*w`V%pqv^e->CHu>bA;{dnfdz0V3Ntx1eJQQWoUx>@X8^Wx+Xd zuf{7K~DIJv0XZOpMBYmtnHs%ntvNZx4Z{CjDja zN^dh-^UYC(((I`lsf2um|u%usgH6k%2f z306xrq+rm4^eea^>|9Bc(PBP^6{PSUkqA ziyw+kFb7{W)4l$Z zBjFwpHOjG*xHlB~@D;clvraRdgr&~Gpso=_hiuXaw)B1#A?e(Y5?c2{Qv9NwhL+BO zpa1WiovC&n!J*h2ZqdZ*YPIO#bG`3-X}%W815A;BZ>U|0-mpTMVOca!7MTKD&H%i&)#|BEbOX3-4%=0yi|D zgxf|vGwhq>@4g~3lM$jHP;k5bAqV5h27xG33Ggmu1AK`Vr2R_7c>3v{iTU~qgmZR&>GcQ<;|i;8y%|v6GAeDo)yRpOvzTYy+XlX_31f0j zaP(=7A=WKk_Ib9CvxH?3D(4c!VJdVE51TaKf@pgtw+8?{qqchaGM6fiR+M!I;iWo| z+)qQ)Lv1xSEgR?{#0Dg|N=Ur8SURmt`M9Z^78LK{*3J!G3n!~Cq|Cp5lWa7qKjWF{r}qI# zIHF*pGUQbglGyy`K&ffu)tS3`;eI#IEbO72i{)?y$wYawYNG zba`MY<_y?gSH~6d#;qFGCx=Lezv!o-Bwss}<=J@;c>3oh036T9!^LHns(7x$oOLo2 z?TJBgA+5*3x`mY}#-h}%M6a8$q0*`K#jk1Ad!SH?Y(!GF>@(cz<(g$WHw;g$3x6ri z`v&{<{gE?F!Za~n?&d;ebgThOJ6xztl6Nv(Mjx??tFNILAV$fK4jIa(c*^2JWS0sV zxApwZYuR_@E8aeniOqm_1oIDxH^a5SLv^1|KCU6Apme=rBm(E|0;ec!P3{)ds%n@CW>MMUYQdD{?t!Q+SY^*tp zg7wuy6aTeOrF(oF=f#jfug|iuHVtCh6GuuwDM@IoOm)rN&&{yh!7%M>Sx=f^D?h<* z33gMeF4^n7ChSTD$^zZfYERrQnMrv@a0ij>OPEOdAHPFi%7vpDyiy!+1yO~L5D3y- z=87M+2O4Nogj2nc!!!ERXVsS-OTn3x39Z_YA}e2(X>c!7ABPKsez6h`0NaZ41lkSqIR?`ijZI>R`ri$) zzw$Bz1Ao=se-Q3I`-LTS!mEQ?(ns_Ii3K0Gdv(E-l0|BX(uAcoHSC;A7M-JOXwc9A zx#u9g13m&$daDDCRta zL9IQovH}uN1gun#Z=B3Y?z%KI?ghLONAyoD4^&QZG3cfD{IUqz4mld?hf=ZO%hQFfYHC*tNBXD9Ockc-Ar?)7=5 zlE)TSt`I1Vv6;jNWgx9GC_PyBba^HT238ab|d9+g)i;p{RZ-kTSZQcJ@s+; zSJyQXANUHqDP5Z*08Zr|;72i0h3YE2Unwl4HS6UZ@kP7P8jMI@*1st<(SYQk3~o_OrR{siPlFXB{Ku>R`z)b-P^3T zU_5hdSnxP$CC($*@I1$A;l?YOeJ8ZOssKE`a>YlaG=HbI_K zqa6QFgnVO9v*AU5xx?;-fsh8G(k;{fQdU@vxE*jruR?vNSH0T|b@n*Y->|iN*%MP> z^fm>CpJNa!hjPq26WNDjEY&rGB&yPDE%6}DWc9x3=4{%EK3;(b;5BHu zgD?#jmyr>=oC5G0mU*N(upcjy|3SY_P=k$wyy56y1qCef)fs@orrMLe;IX0!HPAD4 z;93**twkDcS+ZrU!SS*Tr9SH)dRRw}mc(Gsa5~rz-0Zy$n6ds&)td)jarM&b8&v7d<5#hF7%H1v?eH0{wOn53peI38SpjgdcB;GJ(p zpddHW#tiR@BPw)$KKJ4v~Xc!p7-t?MK@`%-ZcdeSF3=hVfVVV6#yfG4f~- zi-djCp_7M6uXTU|6l)8aby_(X+=+Q`pMFD2z_}SNLf%Zx(Y#&o3GrAyCuhFUVjNZ6 zC?8Dc62p*lAk)H8mEZ7~s3kHJ(WGosAWj2A31l+Vd1xG# z3*uj=XG?~)?rj-pE%J!RV6B6|Q>YD!@}s)sVlbe!BVZwuL%>6~YY><`4nb@<=<7+) zK){dY@blO^ULF}UGZ{mS;JM`U97MynZReSNSV$MNmIBX-VK`okw>uvWh|Rb)7;r-B>8kMcvMDowjXKp0@t1L zk1QFjDvx_tb9olY2vxv%*N8o@z*W&TO3XB6l?KZ7>0Wt0-C@hBCEg-Xs#%^Ba}6*1 z5=(}pMr|%*0$PS&2Bg`KDeDG9qiQ6*@UF5FUd8lmcWEMdn-k?TMoA(CIX zemcp#vs`|t34K_4vMU`iE8j&iew_<9 zzU>RsTsHZ3aT-uyPMI=^ZSr-7i@_O{Yf7-YX6Sw6N;TGEZkNc)){M48FzQtI&6l^2J(U6e8V}S+Rb=_W^gr$jaKp%KGIm)LBM=j(0CHd;7be(hmU!!$|etE z%@NG1h2{}+P|VV}nKGaHO&lAtWy7Y$uHPfDcWi_XGi|9!iB9I1ZB2hWD;`L&k}`nW zi5Di;xa6-n2fQ1k0|Z$;wl_SRtc=*>d2P$Tp5=sDJ?C^TaH87>SqovMI9lYMiG%g; z%HIm8El@cV%9IR9G9cLjt_5D$CavPz;{JlDI!vfE)q6KO3-RxRivOq6>O!WRs zq$Ah#iQDy7Of!mc{80Uk1m3IfgFgw4lUs1LoQ+}JcQSo+*Tzlo6{46#t1R)im8WE{)l$ucj>Jb zbd?nIQXy5$NTdvlu?Uq9lgv_^!pzuz6)aGI3Mey}lb|<1%FDq3-&7_V8m_<-O+CTlPd;}znTsRie(fq+!f@zb$d zr@{A1ZG`DgcOlHLPFPr>zId3R?e;QH$NUe7@LLc1T<}w7@ctjm=E^mkj&#rK-eRi^ z$*DJ5q?`#`N=_EDvXD@xKikZOfQubKiyzNGs=ea;>vTN2RN>;_$cCL4?kg*9J?E8+ z5!vv@$7z-V!VD302&8?qz2JE&hCIJFF8eXaXoS5@t}F^Y0*47xw3Rb6>6Y75+(EW| zasbRc)E-b`ZEz7l73e2mgb|`JifzU?rH2A8+O-?TQLq!M!z?H*DZeZ_7+HEXr0|w% z5r#~yq<&>>v`Z~@Y3*mSDklPoc^@>>pUj^7XMkEM%dvq!zp*!e+~PWnAC=z}4eAv% zeZP_1IOSHHnETJ|)n`yuK|PURzXbKd?~R_jJ{uIZ>P3d z1{?Dr(He-JsKWLe;dIS8pglS+x7~Pv@%*=a48_C=|5M?%9!}eGTQM9dcvHy#ye9?Q-^=RmlCClWTpnOa^sTg`cU*V- z8G>{k;Rk^?3)mQo$$Wsg>+8Yt7cG5Dm~J-jmhh33vnxK9RRC&D>dgP z>v6kD?b2ly=Bk5wIvZiNYCToHXJ;CQUqt1nF(MWa#4$9hi6>8ho3IorK3UsXLMdo3tfF}RwEZU}u3p)(GiC@oE*IGN^ zM5zYCDXmd}H(E1nPKl^EAagG+-r;^XI@R$&zSHv@O#tqik7q zT8c+`!IyOZfWB@9q;s$AGQ5O$g>)ZVVY;dK%ZOhaa{z=A=lwgQ*40EKTErvG^U!~p|qmU5~GpuJ>>pK1NrEO%h4 za%LsA7&wlCGPye$Tkwh-Hn$WF*Ejl?NaUj@u~gcNZzb@%RnS{lW$M&{ujZmQ7_dwF z+ppUwM4o1jh*JSYg!EN&pVwb(%=(9E8-b0Lr0~mAU1E`pj&HpjY_wpyN;jwX9R1d4=E*&jnSIF|lLoYBl=n3nnV~I&QrTO>1%$NVg ziYacqzRo82+JqOQz5va4(Ce>Nk8*o4n6V_0ouV9sl!HSgzn1}u_lbs7I{q{c0+zYP z0<;sW&{9nJxwZ3<`Ts-E*q}uNcax@*E?fu?dDi)@?M(qzq0Asz>{QymU1+#_$?@S9 zI|?cVU-8f{m(C>4)HXD9ljoO)ZAulxGh_NJF?CP?LTjBOA{&4*__0{kRl;Aw=I_F= zFc)ZS(mO(IVh2$nZ#)8hHw1t2BXb7SCF%~H&I~n5b=AiSOoJO&1Zh5IiA#DP29RtI z>9+hF;>x$}xpYI$)lE5Y_6Y1n$>-=)yCkY+(p>^j)yB4Fz^$7qt@*=q9j65J>_y=6 zI=W|+xlzh{`fbGp!aH_jkH4Rp{MS$4fd7+oPH=O>f2H*D1ROd1&_P$!4QfUzS>9aw|lpVd1+ zoF<)P=uoHFKf?yI^;#VJUBUN@4;-MOV@|P@5?fWUtnyEng-&hTi)zY8$93=Tr%(bt zs&!xN{*oC#S0Dj14)U}9q3n4u5a&V!zJ;?*$_S?xDwbh|%l+hCKm&|CBPsj`@n5KC zwN|?gt>pdIJHV0^NhX^>@niYcNzB}O27Tz8T0R>PE31B68v0G_zZF0u8n?mW_@EHy zMIZ)`_Pb?HT)k@cvTq`$QXys(%&dy^oC0Ff2vg^vi=2dWi z!lXeuT6!#oH**H9b$an!D6Lf*|6f32-j@^kpmPb!!-tvxJ(Dy?EbYlwy#vpu^S-Cw zI|?wY&tl5we{7$?40kLYOGrjsbk;9Ez3bqTT+8WAnMS%AA$wMkyeru{#QBUWi!PyZ{-1wLujPcov%LT=ygvpM{39078WUKEd0b?$gc9 zek>cwW^^+D$^3u`?w`NRP;+tt6~Wr;s-C2kk}t1T^~=6w)>x9z_HjypX4nc%{&WQ` z@+mw?Kf%oGeL7w1eLLuKzhsXtt{6L6i+AEW9fljd8c_K8k>X1y2*1TM*0GdfvHQbO zuTWx;C22Djr}JNc(TPJs|9}qjr=fzN8-cUM3kj9K z^oO{2))z$T5R^`*rxoI(rW3#r-!Q+Dbt3m>TtlvXbbGz?(D~+`ME#wxSL?o;ABr&L zu_q^SV+NyZGIr%8R`{(Q=P~ESvBhE*-ly^T5W}T~ZMr7&oO-l;>yqH1rYU|)ZtE;* zOI2xoc5&3#(Vm@IjdF)X+s_Axsr7g6ftX5HQk5{-w(;{m4&{=s><$riySgh1kn3y? zx^*ISP?v9m4bAKRbGO|quQrlv9e%MF20^6`6O!{{v0ogx9DlG$P)|p1q#v(!^&cH$ z;2E@<*!5R3OIFEh{0Zhc>j=sedN+Jo_xh?5)+y78m?y|Ud7)ochKkA1Z)DPGcy8EG z_*DlsQ-C9xLe*Hf#!TfmV7dfpG<`tNp{f=hH2VdaxF$WHWo-ei79ntjOCOU~(y|Uf zq_bx5!gC~1fEOD{TZfy?&u7LeUo9xj5TD1wSduFJW8*7H>ySl!P6<&7FXxJ*R4Hw7 z@u}n~{di(kpsJ`4|3_4dFu(nDhv}E$H%wdV4D27-!BCc+57pCMsQzmh_V|!97nN*0 z_oFK>--F(RZEGO83nc6egrKYgm2E#KIW3M+yNiyV*okX#^98=z^iIQ{kMvi;4W2Wq zWce?$MWHfAYO*(MqU=D~Cm+c2zdS4)-2I_5VfQ5ag+~N?4U;8Kb}iAi)ZRE`QuexG zqXI;l`qhZ2AK&!a^+n15NTA9|T;Kbd%-|D;VnYj&=$0XDYxg#?1X2g;kz7)ig_}ICR$@ipFu5|uecRO z&ls)@KWH?}4UKjwj>b-&Fg>(i`u_J<_IPo80{Um$2ZZ$^0qa)B0}89lONT?H10=tx ze1Kj_*^BP3-WSXUY=e(sOP}w~<(ks*nPFf3SqRV-@9OWqmkz^0*M-aAoERTBqTg}Q z);7WCP<@E8w|3y@8wg|~oZ{CZJJFbDD`+-So`s44bpa6k%TOIDhhIg=n0Ow%T zkgTeaiFp5JvJG^b_OD~dVAhYBD78b=MST2g2)N4Qg-oCHOB%D^C%yoqzZKtp_J4g5 z?a%h3My7vr+{H?tDWp6Q;P1yPG&X{Xmzs_$%h|aT>K)S{_+nK5>VU zg9!AYuVA%K-rQ-4^aP>WPKTO2yp0_ml;$9g62mD%CozgNS@WtB1=}6mM_HeVnRFK4 zkb#QQEqNgpOF^YIAj6d?xewZ9@10zp-*#qC3Bc7H}`M#du2j? z0Xd@X!+0RJ);R5%Q?7TrQ>#JxQ+va{{I}OgF+CEohD!*>r|Fzmt}C zn7U^vH4H;h8#gMIVcAbKMoXo+cKMEjJkMa#`HczHIptVopgihwb8h7z&(cGv&m{Qn z-2-6yh%gyED+JXrEmbcxI%R6RmL&@Y&_Lerlfz87yW>iz!BZ#2CYu0K4GnRllXVCBh5)?3r2>so6Q81hDD*Ym2YQGw;A@u(|HsBG4gUC=sk zaLO=M_HpT1r(=KqV6vN>uvNR&0f*KN@(zr9Z`7LDC{R5|n$`1IM9mP|(Lu8{mch7$ zMN#RCzV=zz@A+LNOepTdEK#5r$3AQS=B5=aPv<~W-e<}eyP;Q`5f(Eh11u|#=InIZ zSdCXVhtqE#UvcNEzrAVFSLE(U5myB%f8lJ`BW$f|X_TvkD+9LY>Avh*g$4t+faCflDVa95HU3NfS9iv#MK4o+ zxe^YtddQk0%vp+Mz(|~#fK0!#c-Td(@EN#;3#c3RyE+~gmmgAMe&RbqEsuW2Vw?jN z5S1>M_*`<{xrs-i<|vWcyoUX^w2Z_oH1l7q`iwfAf#_BVM2i95xyVaX2i**u9O+S1 zCbL}mZPo~!D7L{N{rCUwANd5Koa8bKe)4V~HF(##V|Nnc2dL1cXW zbms)ou-8)Q0vfb}1QK!hc8dSX;wIV{e??(E}45I3eYJvy}$9f5Xi1(%aeJP{-~39wg&YV3|aL<~ZvVbw1y ziXbV{urr{Z+zU@!UVyi(C3EhCo~OZRv^wp9hakMrO8X1&@KL7SD!bdI!SFbcRIUG! zVvif1wS@E6wbjrEWhZ;vvjJ%siwJtj!XSb<_XkRljj7kL&Ad`#@z^>f&oX=lCfiv^ z8nCkT~lR3ELFQ}6}BCl>;>{_BZ@AI@+fQ!)7#PHt&kc)FPcQWyZkUr!ALr5nli=QHI z^Lw}X><~^sP>m?QAcIaOyM;#U+G4l{QuY?b-O*Eb5~3nR6SWanQ!}HJi8LDCS8Jek zENKiZ*rv1Gry+MU-7g$(9;IwDCDv??lpBF!c4u|@h5%DJQTlF1x)2PN(nx9XScx>L zM47O_Xf1qFP~_j`=I@l8H9xg@S{=x~Oc>}8f*SK?%%4V% zUifTQ`mD%iWi4qTRAmZh1}hqZpSDde*UJ%4z07!&*bJ58&R8Y>%{-{O&RyQChgnxM zVMhw@sMX(Lss4^Q*Pbg()7+Y3s%vGCHG^DYWqxAx31hHQzR8xp)%88xuk2BUuXYYc zJ>=-?(9IVS?v-Hz`S;1ok#OzAt(+*pN0yS7N<(%F^KW9t}HmjNC) z{8K5RsLs_4e-w%!a8?UoF${7P)pxMZ;s)~f2&p6R8{vf`bkZmWF9P>$Xk@jO%P`>m zaoLE|XlBgn>1*EhfUdS_pQ3Mhx1cmi%~dH778jkxazCZ6RS_gmsV^w0}) z!xIpfh-&{W!82GV_>eQec+egr2p+2g23(P(BdF^c?R-iG941+sDzSwnkK+5(oV6d$ z0CzwA5^O?I4 zi;%kWD^yvy%x~$vB!JCDSXy&FXEGB(7R}+{1XD!UPFjgZr1h#Ck{x3wE*2+s-D{d2 zTqskK>vV}sB0q^IkRLvQh{K4}f)PR?8JqZy!R4-cVx#K2Fq3Q^}_fpnP5!~r7I zq?5fw)S4{OejlD&M^RF4i1MA)4M;GM_%c4(^E*l3cMh4w_{brYmPiB#=`)p?DRLwS zZke)L#jw!Tni-l&^^CA84h!&s*-9Bb>ACJ?E$lXUjV_LBb;76b2L-!~<~vqZZ^k2c4zK zK;5R`C`K|&)5mrb64-3!FZP#w+^RZav&IXbYkrjmMptj76jA<|)7qnf+DvqCL83

_p|apNQ@SMWGS0fPhRPPmqUVfT7m0N8LPC689Cug+ zuYra<3j3!DQ72WhHXA{aL1i9F+cGqfs=EAC7)FP>2Hl(`tjIzmDnvq7Bb!9N#|L6% z;3rEzf@Wo_!m`TgPu+|MN0GS!yuwHN*HSGSs>4E@i4q(fURK^ca&G5E#3nRjdF&>2 zEoButX3nuHju;m={ydCx%=BZ-pW%b9j{}D@VGnL%_fS*B@pPa@tkG=yLCtIf=N3fEC1}gS&hIvxm$yaoQ7xeksQ9AM8H^eE)I! zzGymd=zQ@!)WH-<$N|_&19AwBWnP~}CRiRC2cn7?E zTRkDX(lcqBdaN-x>e66r9Q|R$zko2TAs7%Ky!@@E?0023E>ReLw`3;AclEUquES5G z$CiIXl3R6b&d=2IVX-ns(0V0u5+Iv`&IrZ=52$JK1U{28$b+z97+hXjZq)jNeV2gG zbrSWEAax5iDv6_Pjb!@>Ayp5%Xvz*@Y{Oa(b`^>QJZ@W^mdT`Udm{6naA!eQO#d0pQ(-qIiOuWtK zpzJGCy5K>oR8FC=xy!VUoQo;-MGKZ>tNfRc8T{7k`LaP8_h}OO|4e@=j4=SYKv@wsSz=#Foi?o<)-PCncS1ah4Ix0uMzL&3|_o@m-_ zXt5AI#H=B*uD0K>>9Kx?Nw>EhRF9?FDHhZ8{3Q75{EUy13nF}ljde&G5fkWS2#sEp z^8ZODAt)Z4A?@O=rKecte6-mT;Y#AVWDxYi^-2^0GVzCCEFFGJZ2-g^nd_@>C8w+j z=h5G}H}>0c)Nszg5o?o%;rk=LTkISgSzu#=7Z7AgViq!(#DJt9MZ)j3=BB~YXzK&tx?Q=RR z?(le{j?^ATX^3LEp?{0`XuS}^h|tzqg7O6x)vhLQ0eQLjo`GY-uAcOugvj~Z(dn2& z_;U_s!GZ4@B8TyD7cwZ7{!fbHnh~N#rv#sc`|g=3yLdiZs3o z@@5>z%_PRHhgmG6J%b`Y6&fVV1N3y(&nw6_gn{q(4+rzcD9K&4|N@^cI zM=CEM0DqOQOM{g`;E7duvX{cuuO0G$Z1-&Ep*zK(GXOcrB}$5HZG7-grW zGM8k&9=UKic8CzsCX8Pz-L*h1&g0-9CTpBSIao)=%zfD$ff=gC^Da*4?Z2H7+k~`_ zf7o#zvg7mo3DPQyo|2$L;l(QVaO^IIYvpwV5vXnxHq1+*NsNPkLPt=!S?w~Krv;4= z_cSykRRCn;C>WdS8%mNY4|BDvh!{ED_Kr3ZX)PXpjS=rKdYcFTCt5c$vxRdid9jxy z@fX5{`()uvQr^l=VQp~Mr4^%Ds{K%MlBpI^xwPgw`?wEJ=UdX&shbAfhbA9ZG#8Su z6=zMLkGG#*5J+VGoQe4xtdq%%u}d7gTj?r{*qtpsI z?*SnzjmDVLOL9v}jeU0yEl}`O%a*k*CE7W2{u|x4^A(I@Ocr z-{%$C^!~vfR}I)*%6G%A%=SkWb-3w<*H&$BDpX7o)M*qqwNJAHcIoQ`Kw#w{#8wKYcjZpjn9B`UwC1OG^IMon`VLe%yEm%k zqi;J0h2nhjV1orH@2kj6EAp36$?svv_Mw3F`gER*6DJH?dfs$ z=kR8Q#T`}{ki?FF`P}P1U@cQ*zQsIV6kwGejoOMoq~dv-emG?_VflEJ*^svmnaejE z@@8f(QOZ*BnPU2AjOxYho?ze2D3q3ukh;C@j*wb@DE zw42RN&F+{!}Z+{&WEN?g(*#OYwXjjVr^dR4^5ZZvapZ+ z9YT}&gkBsRo0)Q2q^l}j=Jt%s>$Due?;S`q*hoL9pY}h8SLMl~#^Cj)f14v;b^>le zY6RUiuUE6AGP6%fWe*aB^$^h^;g?vCnH*y^q7whBx!spoF_t5tNB}4||IUVcU|QcE4^I2?76Qvv^=@6g zTCQ{35!`{`7_OVnxCHc}M9`R!G1eohbpT4!Wj;@@#%hE{=<%>LSGVz)tkZ1v{dxhd z8X<|bCY(uzw8n=92)%l}EEY*Mu70!6`tkBr+dyj*d$ScqX~8+BXvh(!LyV0cPNqf< z*({k%Cu(BmgdJXAnxvRskN>?k#oc#Fezi`nYBaL-)a0gXxhIa&ro+dEM_k&m;bSaD z(EWhnStP6SK#SX7A3t9Au5XQ7QdLDdSy!g~!4z+kRqJRoV#)jZj^$R_50BeB+*(yY zFWRk(%W@i>(pektqkbM$v-U9yn56X@2``UdGHFW2$#z$$y^6ZQ&nkxRo>Tt z@_P9nP>i3_a-C%9Z;6ebHR5pMJjqJq?0c0i%&TNot&2KLfRPY1L}G}E_4I^~srB^e zUtevLPNQ(xrth&|W1BVV!&L+O_z@235eD0OM%!NJc5E9L+F0ww=MNWm;P%Q-$<^}1 z#TdS(Gx+zzMZR84O!493#fOVkQY4GAISQXkGyAc~ny#jRyhy3DakZSj^Pqs5~PdhkuLka#WzpC4&R*Rt#!RLn98Qb$(DQ#073@lNB#+oI5 zxJa(93UdWMt<_qXWcIpPF1}q(f3jm9p?Ur*#SW-KVNZ@-ms)mLwZ% zB<7RZ3=Na1A(oLPLxuT#NVUOau5_ZN@-JgcivWL+tAr^|3ylI>aqG&N#&QISlbE zDX%BXqyXxAWS|ApoF4pm2c7-*jrtDB@f0UgsYzrL-rc`FlJ{!Dhog9%R^`1Uxp~O@ zaD4gPR3jZho)-sxA*V9-(+cZRjNt4wjXWAw23jcR8_a!G{>*PA`Csb#A?jJYV-(mFqwPj z{{yTY61(oyhCO(>Y;L9a5F=}Sh>j_S-x0HQa~EOw%U@cPAh$bi(5+hg3B;iV0ePS& zlZQbY%Z-;8GmaTO#mYC=Z6{(jBiKH;n}n&2@4#9f^nCm7oaW=C9WWTfGe)+lkcuVA zvmh_Cw45$MS^RMgkGRGRi`|c(%;*#2njBMbIUM&L*EVvfGmiOuDwLSX=P~9dvKc&* zn2}T23_&N3x*j7n;{A{MJvqK+)HgFxk8pC2nb7gN>5T`heKZU6(T|*eJMhs?qBOeC zj7CkLosPVnUwZucuD=HuOqbbuk+=J|m9F))O}VGt*ot<=gLG z46^jb4E}1SH_JZ`UR^_uVO}brtT|JteieUV|yb?n;6edB()q!nS`zGYf<(cAtg zBtVNf(-KK31>c>y?H1lemWF=W{85n*U}!90=ox{*NK;J&<6H}=_`Vjr2Sz%8p&`Ja z8kjT~7Ziv~`YRWuEP=>Jr=Yk}BQn-r>I*(^bP4Av1H4o!L6vdQk@VnfalkBPsW9RWK;T|{C zvpncseoF z89dN14O|XVN{tZ`w5u|%7#*Z9mbl#XPF!w$F&a@usAh~{>vRQ0U43zRmmun5^(9Ms zR+cPzPMTuFb7u0M!!t(jtWt3fIwy zc40Y7bdCW_paJ<{0Zq|ZQbiP|*cpUeQ7GJm(r~vDXhNySuRuf=luIE%UnG^Nt1Kis z!=;g62~-v@ESxbagcKP5rc!lX3yGc&;To1a9G9@zY(;op!MQP`eAd4qfea5qqZFeQ z5=KY@!1;2~!38p==-~)QgCYCG8D&E;mTK4T_Km?z{K+33?joF$L ztwfjOMRzhZ7BGRPM-Z2@Mhr?)1k`Z znU}@{vLpt$NURxEvKyjF^tBx%5-fo(*9!~Af+DJcvml!ZbR7$JX-jzCxad6vSUj%` zfJQK$R zAqOCwxwIsB9Z;U+({$OhZ)t?^1?i_=-%)Ez5@Cf@pu!~H1yTZ2f*~|3MTipIF-78X zxso6h74feF12s#8QA4 zIw@rek5o~7PGF!q;&G@FNCV+Rl-W{M7n$feQ>`{p*761_&5^adR7g!RCX{Gem#a>K zCR}N*df!`OMexON$RwGBWKs%BrDHuJcJaP&orwWVpvoKpMl#M2)sQ%G%*lzsglkNC zmG{nvtqh)wk`bmrX%K`cMybP=h-(XI@FIu2|3wbDZ;=DAJT0jfkY-EH9V?y>c7Mk# zae`3&Ym6^oE1yV_Ec7F*Pv1B#Wf)I%gw<$I)=m7A7?TloY{^G$Hyz zhb=a}uL*C2@DO3p8O0E!2v~4UbXRj&bWeje;G});u4shtxZxhA-*7x|0v+)&a3FR| zNG~-P_RT6?#UZXFA;=Z8c-RCI8!tADAuTxLmAD+Gr|8UPk4^8auaUyXZZKlt zd4TyrmZ@=fM$XA@g5YV)s_%_|Ab}@L^|0udMS>+%V?3}>!jUatK@b-hE5~x*=*rSW z@4h!%g9ILB(O@?~gs~zJrlf1T#aWWd2&jbWiti0EQYs2bwjf*yTlegG78lhEcf7p^ zBp>~K`Z{nuhY+Wn7dVA*w4V3$^OSWc$|DqK0%K9`EzW5B_75zz{m7B)T}x-#g&qZ% zK)>q)1_LHiXetO{TspS@kuxRQQA#tx!1!(H>38PTsbt>wOLRa2AFY#+RuhV_pd2aX zFeMi(!7F}az!Iu255t0$lt?N7hl4Q*N5zVw=!FLs36{`U{V*&LjDWvr4r(HZorLSc z;;hGn)7`T7<%(7WPa0sS+o&Rf5h1watxG)mEJ}?_|I0XY?=nteJ&VQ?;IUKM*r74! z$pm5m6KF_&U3CcISMhTeu;uP5AzkK!pJjd|?`C$OaSxbCOCc=~#(& z7KMocOrXLX1LnzJ)Kvp>_Z%vkcm8T`UvDwXe@pv!z*YtiGzxLbq<#A;g%O8BDamK=T-|8`WKd=BiZ%=LM0l2{Wmq*z~eD zy-0sa``&EO$nd<m^L~R`1g|+g^u7jr?t7Z;2rOFL zy^#cSMC>*%$0nxe7PNsSLajF#81LLbZ_XtKG@<_G2sFLSCV^Oag#QL7)XU`F{~(An zt^gGKE*bmU6h~Hlju$^if+p0UywG4<*hHKX!l|??=R43iUtAl(pkhE1YEWKi2qe { - return await supertestSvc - .post(`/api/reporting/v1/generate/${isImmediate ? 'immediate/' : ''}csv/saved-object/${id}`) - .set('kbn-xsrf', 'xxx') - .send({ timerange, state }); - }, - }; - - describe('Generation from Saved Search ID', () => { - after(async () => { - await reportingAPI.deleteAllReports(); - }); - - describe('Saved Search Features', () => { - it('With filters and timebased data, explicit UTC format', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/logs'); - await esArchiver.load('logstash_functional'); - - const res = (await generateAPI.getCsvFromSavedSearch( - 'search:d7a79750-3edd-11e9-99cc-4d80163ee9e7', - { - timerange: { - timezone: 'UTC', - min: '2015-09-19T10:00:00.000Z', - max: '2015-09-21T10:00:00.000Z', - }, - state: {}, - } - )) as supertest.Response; - const { status: resStatus, text: resText, type: resType } = res; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_TIMEBASED_UTC); - - await esArchiver.unload('reporting/logs'); - await esArchiver.unload('logstash_functional'); - }); - - it('With filters and timebased data, default to UTC', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/logs'); - await esArchiver.load('logstash_functional'); - - const res = (await generateAPI.getCsvFromSavedSearch( - 'search:d7a79750-3edd-11e9-99cc-4d80163ee9e7', - { - // @ts-expect-error: timerange.timezone is missing from post params - timerange: { - min: '2015-09-19T10:00:00.000Z', - max: '2015-09-21T10:00:00.000Z', - }, - state: {}, - } - )) as supertest.Response; - const { status: resStatus, text: resText, type: resType } = res; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_TIMEBASED_UTC); - - await esArchiver.unload('reporting/logs'); - await esArchiver.unload('logstash_functional'); - }); - - it('With filters and timebased data, custom timezone', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/logs'); - await esArchiver.load('logstash_functional'); - - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - 'search:d7a79750-3edd-11e9-99cc-4d80163ee9e7', - { - timerange: { - timezone: 'America/Phoenix', - min: '2015-09-19T10:00:00.000Z', - max: '2015-09-21T10:00:00.000Z', - }, - state: {}, - } - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_TIMEBASED_CUSTOM); - - await esArchiver.unload('reporting/logs'); - await esArchiver.unload('logstash_functional'); - }); - - it('With filters and non-timebased data', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/sales'); - - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - 'search:71e3ee20-3f99-11e9-b8ee-6b9604f2f877', - { - state: {}, - } - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_TIMELESS); - - await esArchiver.unload('reporting/sales'); - }); - - it('With scripted fields and field formatters', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/scripted_small2'); - - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - 'search:a6d51430-ace2-11ea-815f-39e12f89a8c2', - { - timerange: { - timezone: 'UTC', - min: '1979-01-01T10:00:00Z', - max: '1981-01-01T10:00:00Z', - }, - state: {}, - } - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_SCRIPTED); - - await esArchiver.unload('reporting/scripted_small2'); - }); - - it('Formatted date_nanos data, UTC timezone', async () => { - await esArchiver.load('reporting/nanos'); - - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - 'search:e4035040-a295-11e9-a900-ef10e0ac769e', - { - state: {}, - } - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_NANOS); - - await esArchiver.unload('reporting/nanos'); - }); - - it('Formatted date_nanos data, custom time zone', async () => { - await esArchiver.load('reporting/nanos'); - - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - 'search:e4035040-a295-11e9-a900-ef10e0ac769e', - { - state: {}, - timerange: { timezone: 'America/New_York' }, - } - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_NANOS_CUSTOM); - - await esArchiver.unload('reporting/nanos'); - }); - }); - - describe('API Features', () => { - it('Return a 404', async () => { - const { body } = (await generateAPI.getCsvFromSavedSearch('search:gobbledygook', { - timerange: { timezone: 'UTC', min: 63097200000, max: 126255599999 }, - state: {}, - })) as supertest.Response; - const expectedBody = { - error: 'Not Found', - message: 'Saved object [search/gobbledygook] not found', - statusCode: 404, - }; - expect(body).to.eql(expectedBody); - }); - - it('Return 400 if time range param is needed but missing', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/logs'); - await esArchiver.load('logstash_functional'); - - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - 'search:d7a79750-3edd-11e9-99cc-4d80163ee9e7', - { state: {} } - )) as supertest.Response; - - expect(resStatus).to.eql(400); - expect(resType).to.eql('application/json'); - const { message: errorMessage } = JSON.parse(resText); - expect(errorMessage).to.eql( - 'Time range params are required for index pattern [logstash-*], using time field [@timestamp]' - ); - - await esArchiver.unload('reporting/logs'); - await esArchiver.unload('logstash_functional'); - }); - - it('Stops at Max Size Reached', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/hugedata'); - - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - 'search:f34bf440-5014-11e9-bce7-4dabcb8bef24', - { - timerange: { - timezone: 'UTC', - min: '1960-01-01T10:00:00Z', - max: '1999-01-01T10:00:00Z', - }, - state: {}, - } - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_HUGE); - - await esArchiver.unload('reporting/hugedata'); - }); - }); - - describe('Merge user state into the query', () => { - it('for query', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/scripted_small2'); - - const params = { - searchId: 'search:a6d51430-ace2-11ea-815f-39e12f89a8c2', - postPayload: { - timerange: { timezone: 'UTC', min: '1979-01-01T10:00:00Z', max: '1981-01-01T10:00:00Z' }, // prettier-ignore - state: { query: { bool: { filter: [ { bool: { filter: [ { bool: { minimum_should_match: 1, should: [{ query_string: { fields: ['name'], query: 'Fel*' } }] } } ] } } ] } } }, // prettier-ignore - }, - isImmediate: true, - }; - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - params.searchId, - params.postPayload, - params.isImmediate - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_SCRIPTED_REQUERY); - - await esArchiver.unload('reporting/scripted_small2'); - }); - - it('for sort', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/hugedata'); - - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - 'search:f34bf440-5014-11e9-bce7-4dabcb8bef24', - { - timerange: { - timezone: 'UTC', - min: '1979-01-01T10:00:00Z', - max: '1981-01-01T10:00:00Z', - }, - state: { sort: [{ name: { order: 'asc', unmapped_type: 'boolean' } }] }, - } - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_SCRIPTED_RESORTED); - - await esArchiver.unload('reporting/hugedata'); - }); - - it('for docvalue_fields', async () => { - // load test data that contains a saved search and documents - await esArchiver.load('reporting/ecommerce'); - await esArchiver.load('reporting/ecommerce_kibana'); - - const params = { - searchId: 'search:6091ead0-1c6d-11ea-a100-8589bb9d7c6b', - postPayload: { - timerange: { - min: '2019-05-28T00:00:00Z', - max: '2019-06-26T00:00:00Z', - timezone: 'UTC', - }, - state: { - sort: [ - { order_date: { order: 'desc', unmapped_type: 'boolean' } }, - { order_id: { order: 'asc', unmapped_type: 'boolean' } }, - ], - docvalue_fields: [ - { field: 'customer_birth_date', format: 'date_time' }, - { field: 'order_date', format: 'date_time' }, - { field: 'products.created_on', format: 'date_time' }, - ], - query: { - bool: { - must: [], - filter: [ - { match_all: {} }, - { match_all: {} }, - { - range: { - order_date: { - gte: '2019-05-28T00:00:00.000Z', - lte: '2019-06-26T00:00:00.000Z', - format: 'strict_date_optional_time', - }, - }, - }, - ], - should: [], - must_not: [], - }, - }, - }, - }, - isImmediate: true, - }; - const { - status: resStatus, - text: resText, - type: resType, - } = (await generateAPI.getCsvFromSavedSearch( - params.searchId, - params.postPayload, - params.isImmediate - )) as supertest.Response; - - expect(resStatus).to.eql(200); - expect(resType).to.eql('text/csv'); - expect(resText).to.eql(fixtures.CSV_RESULT_DOCVALUE); - - await esArchiver.unload('reporting/ecommerce'); - await esArchiver.unload('reporting/ecommerce_kibana'); - }); - }); - }); -} diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts b/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts new file mode 100644 index 0000000000000..27c6a05f740bf --- /dev/null +++ b/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts @@ -0,0 +1,512 @@ +/* + * 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 expect from '@kbn/expect'; +import supertest from 'supertest'; +import { JobParamsDownloadCSV } from '../../../plugins/reporting/server/export_types/csv_searchsource_immediate/types'; +import { FtrProviderContext } from '../ftr_provider_context'; + +const getMockJobParams = (obj: Partial): JobParamsDownloadCSV => ({ + title: `Mock CSV Title`, + ...(obj as any), +}); + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + const supertestSvc = getService('supertest'); + const reportingAPI = getService('reportingAPI'); + + const generateAPI = { + getCSVFromSearchSource: async (job: JobParamsDownloadCSV) => { + return await supertestSvc + .post(`/api/reporting/v1/generate/immediate/csv_searchsource`) + .set('kbn-xsrf', 'xxx') + .send(job); + }, + }; + + describe('CSV Generation from SearchSource', () => { + before(async () => { + await kibanaServer.uiSettings.update({ + 'csv:quoteValues': false, + 'dateFormat:tz': 'UTC', + defaultIndex: 'logstash-*', + }); + }); + after(async () => { + await reportingAPI.deleteAllReports(); + }); + + it('Exports CSV with almost all fields when using fieldsFromSource', async () => { + await esArchiver.load('reporting/ecommerce'); + await esArchiver.load('reporting/ecommerce_kibana'); + + const { + status: resStatus, + text: resText, + type: resType, + } = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + query: { query: '', language: 'kuery' }, + index: '5193f870-d861-11e9-a311-0fa548c5f953', + sort: [{ order_date: 'desc' }], + fieldsFromSource: [ + '_id', + '_index', + '_score', + '_source', + '_type', + 'category', + 'category.keyword', + 'currency', + 'customer_birth_date', + 'customer_first_name', + 'customer_first_name.keyword', + 'customer_full_name', + 'customer_full_name.keyword', + 'customer_gender', + 'customer_id', + 'customer_last_name', + 'customer_last_name.keyword', + 'customer_phone', + 'day_of_week', + 'day_of_week_i', + 'email', + 'geoip.city_name', + 'geoip.continent_name', + 'geoip.country_iso_code', + 'geoip.location', + 'geoip.region_name', + 'manufacturer', + 'manufacturer.keyword', + 'order_date', + 'order_id', + 'products._id', + 'products._id.keyword', + 'products.base_price', + 'products.base_unit_price', + 'products.category', + 'products.category.keyword', + 'products.created_on', + 'products.discount_amount', + 'products.discount_percentage', + 'products.manufacturer', + 'products.manufacturer.keyword', + 'products.min_price', + 'products.price', + 'products.product_id', + 'products.product_name', + 'products.product_name.keyword', + 'products.quantity', + 'products.sku', + 'products.tax_amount', + 'products.taxful_price', + 'products.taxless_price', + 'products.unit_discount_amount', + 'sku', + 'taxful_total_price', + 'taxless_total_price', + 'total_quantity', + 'total_unique_products', + 'type', + 'user', + ], + filter: [], + parent: { + query: { language: 'kuery', query: '' }, + filter: [], + parent: { + filter: [ + { + meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} }, + range: { + order_date: { + gte: '2019-03-23T03:06:17.785Z', + lte: '2019-10-04T02:33:16.708Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + }, + browserTimezone: 'UTC', + title: 'testfooyu78yt90-', + }) + )) as supertest.Response; + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); + }); + + it('Exports CSV with all fields when using defaults', async () => { + await esArchiver.load('reporting/ecommerce'); + await esArchiver.load('reporting/ecommerce_kibana'); + + const { + status: resStatus, + text: resText, + type: resType, + } = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + query: { query: '', language: 'kuery' }, + index: '5193f870-d861-11e9-a311-0fa548c5f953', + sort: [{ order_date: 'desc' }], + fields: ['*'], + filter: [], + parent: { + query: { language: 'kuery', query: '' }, + filter: [], + parent: { + filter: [ + { + meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} }, + range: { + order_date: { + gte: '2019-03-23T03:06:17.785Z', + lte: '2019-10-04T02:33:16.708Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + }, + browserTimezone: 'UTC', + title: 'testfooyu78yt90-', + }) + )) as supertest.Response; + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); + }); + + it('Logs the error explanation if the search query returns an error', async () => { + await esArchiver.load('reporting/ecommerce'); + await esArchiver.load('reporting/ecommerce_kibana'); + + const { status: resStatus, text: resText } = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + query: { query: '', language: 'kuery' }, + index: '5193f870-d861-11e9-a311-0fa548c5f953', + sort: [{ order_date: 'desc' }], + fields: ['order_date', 'products'], // products is a non-leaf field + filter: [], + parent: { + query: { language: 'kuery', query: '' }, + filter: [], + parent: { + filter: [ + { + meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} }, + range: { + order_date: { + gte: '2019-03-23T03:06:17.785Z', + lte: '2019-10-04T02:33:16.708Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + }, + browserTimezone: 'UTC', + title: 'testfooyu78yt90-', + }) + )) as supertest.Response; + expect(resStatus).to.eql(500); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); + }); + + describe('date formatting', () => { + before(async () => { + // load test data that contains a saved search and documents + await esArchiver.load('reporting/logs'); + await esArchiver.load('logstash_functional'); + }); + after(async () => { + await esArchiver.unload('reporting/logs'); + await esArchiver.unload('logstash_functional'); + }); + + it('With filters and timebased data, default to UTC', async () => { + const res = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + fields: ['@timestamp', 'clientip', 'extension'], + filter: [ + { + range: { + '@timestamp': { + gte: '2015-09-20T10:19:40.307Z', + lt: '2015-09-20T10:26:56.221Z', + }, + }, + }, + { + range: { + '@timestamp': { + format: 'strict_date_optional_time', + gte: '2015-01-12T07:00:55.654Z', + lte: '2016-01-29T21:08:10.881Z', + }, + }, + }, + ], + index: 'logstash-*', + query: { language: 'kuery', query: '' }, + sort: [{ '@timestamp': 'desc' }], + }, + }) + )) as supertest.Response; + const { status: resStatus, text: resText, type: resType } = res; + + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + }); + + it('With filters and timebased data, non-default timezone', async () => { + const res = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + browserTimezone: 'America/Phoenix', + searchSource: { + fields: ['@timestamp', 'clientip', 'extension'], + filter: [ + { + range: { + '@timestamp': { + gte: '2015-09-20T10:19:40.307Z', + lt: '2015-09-20T10:26:56.221Z', + }, + }, + }, + { + range: { + '@timestamp': { + format: 'strict_date_optional_time', + gte: '2015-01-12T07:00:55.654Z', + lte: '2016-01-29T21:08:10.881Z', + }, + }, + }, + ], + index: 'logstash-*', + query: { language: 'kuery', query: '' }, + sort: [{ '@timestamp': 'desc' }], + }, + }) + )) as supertest.Response; + const { status: resStatus, text: resText, type: resType } = res; + + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + }); + + it('Formatted date_nanos data, UTC timezone', async () => { + await esArchiver.load('reporting/nanos'); + + const res = await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + query: { query: '', language: 'kuery' }, + version: true, + index: '907bc200-a294-11e9-a900-ef10e0ac769e', + sort: [{ date: 'desc' }], + fields: ['date', 'message'], + filter: [], + }, + }) + ); + const { status: resStatus, text: resText, type: resType } = res; + + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/nanos'); + }); + + it('Formatted date_nanos data, custom timezone (New York)', async () => { + await esArchiver.load('reporting/nanos'); + + const res = await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + browserTimezone: 'America/New_York', + searchSource: { + query: { query: '', language: 'kuery' }, + version: true, + index: '907bc200-a294-11e9-a900-ef10e0ac769e', + sort: [{ date: 'desc' }], + fields: ['date', 'message'], + filter: [], + }, + }) + ); + const { status: resStatus, text: resText, type: resType } = res; + + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/nanos'); + }); + }); + + describe('non-timebased', () => { + it('Handle _id and _index columns', async () => { + await esArchiver.load('reporting/nanos'); + + const res = await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + query: { query: '', language: 'kuery' }, + version: true, + index: '907bc200-a294-11e9-a900-ef10e0ac769e', + sort: [{ date: 'desc' }], + fields: ['date', 'message', '_id', '_index'], + filter: [], + }, + }) + ); + const { status: resStatus, text: resText, type: resType } = res; + + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/nanos'); + }); + + it('With filters and non-timebased data', async () => { + // load test data that contains a saved search and documents + await esArchiver.load('reporting/sales'); + + const { + status: resStatus, + text: resText, + type: resType, + } = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + query: { query: '', language: 'kuery' }, + version: true, + index: 'timeless-sales', + sort: [{ power: 'asc' }], + fields: ['name', 'power'], + filter: [ + { + range: { power: { gte: 1, lt: null } }, + }, + ], + }, + }) + )) as supertest.Response; + + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/sales'); + }); + }); + + describe('validation', () => { + it('Return a 404', async () => { + const { body } = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + index: 'gobbledygook', + }, + }) + )) as supertest.Response; + const expectedBody = { + error: 'Not Found', + message: 'Saved object [index-pattern/gobbledygook] not found', + statusCode: 404, + }; + expect(body).to.eql(expectedBody); + }); + + it(`Searches large amount of data, stops at Max Size Reached`, async () => { + await esArchiver.load('reporting/ecommerce'); + await esArchiver.load('reporting/ecommerce_kibana'); + + const { + status: resStatus, + text: resText, + type: resType, + } = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + version: true, + query: { query: '', language: 'kuery' }, + index: '5193f870-d861-11e9-a311-0fa548c5f953', + sort: [{ order_date: 'desc' }], + fields: [ + 'order_date', + 'category', + 'currency', + 'customer_id', + 'order_id', + 'day_of_week_i', + 'products.created_on', + 'sku', + ], + filter: [], + parent: { + query: { language: 'kuery', query: '' }, + filter: [], + parent: { + filter: [ + { + meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} }, + range: { + order_date: { + gte: '2019-03-23T03:06:17.785Z', + lte: '2019-10-04T02:33:16.708Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + }, + browserTimezone: 'UTC', + title: 'Ecommerce Data', + }) + )) as supertest.Response; + + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); + }); + }); + }); +} diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts index 99a965b9e4179..b74cc4f172b61 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts @@ -14,7 +14,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./bwc_existing_indexes')); loadTestFile(require.resolve('./bwc_generation_urls')); loadTestFile(require.resolve('./csv_job_params')); - loadTestFile(require.resolve('./csv_saved_search')); + loadTestFile(require.resolve('./csv_searchsource_immediate')); loadTestFile(require.resolve('./network_policy')); loadTestFile(require.resolve('./spaces')); loadTestFile(require.resolve('./usage'));