diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md index d0f53936eb56a..bd85d67772cd9 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md @@ -25,6 +25,7 @@ export interface SearchSourceFields | [highlightAll](./kibana-plugin-plugins-data-public.searchsourcefields.highlightall.md) | boolean | | | [index](./kibana-plugin-plugins-data-public.searchsourcefields.index.md) | IndexPattern | | | [parent](./kibana-plugin-plugins-data-public.searchsourcefields.parent.md) | SearchSourceFields | | +| [pit](./kibana-plugin-plugins-data-public.searchsourcefields.pit.md) | {
id: string;
keep_alive?: string;
} | | | [query](./kibana-plugin-plugins-data-public.searchsourcefields.query.md) | Query | [Query](./kibana-plugin-plugins-data-public.query.md) | | [searchAfter](./kibana-plugin-plugins-data-public.searchsourcefields.searchafter.md) | EsQuerySearchAfter | | | [size](./kibana-plugin-plugins-data-public.searchsourcefields.size.md) | number | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.pit.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.pit.md new file mode 100644 index 0000000000000..c7e426d3f47ca --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.pit.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) > [pit](./kibana-plugin-plugins-data-public.searchsourcefields.pit.md) + +## SearchSourceFields.pit property + +Signature: + +```typescript +pit?: { + id: string; + keep_alive?: string; + }; +``` diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index f11e7f06b6ab9..4f0b88f840d60 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -439,6 +439,8 @@ export class SearchSource { getConfig(UI_SETTINGS.SORT_OPTIONS) ); return addToBody(key, sort); + case 'pit': + return addToRoot(key, val); default: return addToBody(key, val); } diff --git a/src/plugins/data/common/search/search_source/types.ts b/src/plugins/data/common/search/search_source/types.ts index a178b38693d92..0fbbaec5c4a6a 100644 --- a/src/plugins/data/common/search/search_source/types.ts +++ b/src/plugins/data/common/search/search_source/types.ts @@ -100,7 +100,10 @@ export interface SearchSourceFields { searchAfter?: EsQuerySearchAfter; timeout?: string; terminate_after?: number; - + pit?: { + id: string; + keep_alive?: string; + }; parent?: SearchSourceFields; } diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 9720395881ea3..9f6c7e424cdbf 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -2438,6 +2438,11 @@ export interface SearchSourceFields { // (undocumented) parent?: SearchSourceFields; // (undocumented) + pit?: { + id: string; + keep_alive?: string; + }; + // (undocumented) query?: Query; // Warning: (ae-forgotten-export) The symbol "EsQuerySearchAfter" needs to be exported by the entry point index.d.ts // 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..d3a23fa6a939a 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 @@ -16,54 +16,26 @@ import { SORT_DEFAULT_ORDER_SETTING } from '../../../common'; describe('getSharingData', () => { test('returns valid data for sharing', async () => { const searchSourceMock = createSearchSourceMock({ index: indexPatternMock }); - 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({}) - ); + const result = await getSharingData(searchSourceMock, { columns: [] }, ({ + get: (key: string) => { + if (key === SORT_DEFAULT_ORDER_SETTING) { + return 'desc'; + } + return false; + }, + } as unknown) as IUiSettingsClient); expect(result).toMatchInlineSnapshot(` Object { - "conflictedTypesFields": Array [], - "fields": Array [], - "indexPatternId": "the-index-pattern-id", - "metaFields": Array [ - "_index", - "_score", - ], - "searchRequest": Object { - "body": Object { - "_source": Object {}, - "fields": Array [], - "query": Object { - "bool": Object { - "filter": Array [], - "must": Array [], - "must_not": Array [], - "should": Array [], - }, + "searchSource": Object { + "fields": Array [ + "*", + ], + "index": "the-index-pattern-id", + "sort": Array [ + Object { + "_score": "desc", }, - "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..11b2309df305e 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,24 @@ * 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 { 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 { 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,20 +33,45 @@ export async function getSharingData( searchSource.removeField('aggs'); searchSource.removeField('size'); - const body = await searchSource.getSearchRequestBody(); + // Set the fields of the search source to match the saved search columns + searchSource.removeField('fields'); + searchSource.removeField('fieldsFromSource'); + + let columns = state.columns || []; + + // NOTE: A newly saved search with no columns selected has a bug(?) where the + // column array is a single '_source' value which is invalid for CSV export + if (columns && columns.length === 1 && /^_source$/.test(columns.join())) { + columns = []; + } + + // 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 (columns && columns.length > 0 && timeFieldName) { + columns = [timeFieldName, ...columns]; + } + + if (columns.length === 0) { + searchSource.setField('fields', ['*']); + } else { + searchSource.setField('fields', 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), }; } +/** + * makes getSharingData lazy loadable + */ +export function getSharingDataModule() {} + export interface DiscoverCapabilities { createShortUrl?: boolean; save?: boolean; diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts index de76c65ccdc98..fbe853ec6deb5 100644 --- a/src/plugins/discover/public/index.ts +++ b/src/plugins/discover/public/index.ts @@ -16,4 +16,5 @@ export function plugin(initializerContext: PluginInitializerContext) { export { SavedSearch, SavedSearchLoader, createSavedSearchesLoader } from './saved_searches'; export { ISearchEmbeddable, SEARCH_EMBEDDABLE_TYPE, SearchInput } from './application/embeddable'; +export { loadSharingDataHelpers } from './shared'; export { DISCOVER_APP_URL_GENERATOR, DiscoverUrlGeneratorState } from './url_generator'; diff --git a/src/plugins/discover/public/shared/index.ts b/src/plugins/discover/public/shared/index.ts new file mode 100644 index 0000000000000..b1e4d9d87000e --- /dev/null +++ b/src/plugins/discover/public/shared/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* + * Allows the getSharingData function to be lazy loadable + */ +export async function loadSharingDataHelpers() { + return await import('../application/helpers/get_sharing_data'); +} 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 3af329cbf0303..5e20381e35898 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 6673aded2ecbe..db9ddec90b605 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 = ({ ; @@ -45,6 +47,8 @@ export interface ReportingInternalStart { store: ReportingStore; savedObjects: SavedObjectsServiceStart; uiSettings: UiSettingsServiceStart; + esClient: IClusterClient; + data: DataPluginStart; } export class ReportingCore { @@ -239,4 +243,14 @@ export class ReportingCore { const savedObjectsClient = await this.getSavedObjectsClient(request); return await this.getUiSettingsServiceFactory(savedObjectsClient); } + + public async getSearchService() { + const startDeps = await this.getPluginStartDeps(); + return startDeps.data.search; + } + + 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..c2114bc6bcd1e 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 @@ -5,8 +5,8 @@ * 2.0. */ -import { pick, keys, values, some } from 'lodash'; -import { cellHasFormulas } from './cell_has_formula'; +import { keys, pick, some, values } from 'lodash'; +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..0453af4ef23e3 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts @@ -0,0 +1,50 @@ +/* + * 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 { RunTaskFn, RunTaskFnFactory } from '../../types'; +import { decryptJobHeaders } from '../common'; +import { CsvGenerator } from './generate_csv/generate_csv'; +import { TaskPayloadCSV } from './types'; +import { getFieldFormats } from '../../services'; + +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 uiSettingsClient = await reporting.getUiSettingsClient(fakeRequest, logger); + + const searchService = await reporting.getSearchService(); + const searchSourceService = await searchService.searchSource.asScoped(fakeRequest); + const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory( + uiSettingsClient + ); + + const esClient = (await reporting.getEsClient()).asScoped(fakeRequest); + + const csv = new CsvGenerator( + job, + config, + esClient, + uiSettingsClient, + searchSourceService, + fieldFormatsRegistry, + cancellationToken, + logger + ); + + return await csv.generateData(); + }; +}; 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..430f8cadbe90d --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts @@ -0,0 +1,551 @@ +/* + * 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 { identity, range } from 'lodash'; +import { IScopedClusterClient, IUiSettingsClient } from 'src/core/server'; +import { + elasticsearchServiceMock, + savedObjectsClientMock, + uiSettingsServiceMock, +} from 'src/core/server/mocks'; +import { + EsQuerySearchAfter, + FieldFormatsRegistry, + ISearchStartSearchSource, +} from 'src/plugins/data/common'; +import { searchSourceInstanceMock } from 'src/plugins/data/common/search/search_source/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 mockConfig: ReportingConfig; +let uiSettingsClient: IUiSettingsClient; + +const searchSourceMock = { ...searchSourceInstanceMock }; +const mockSearchSourceService: jest.Mocked = { + create: jest.fn().mockReturnValue(searchSourceMock), + createEmpty: jest.fn().mockReturnValue(searchSourceMock), +}; +const mockSearchSourceFetchDefault = jest.fn().mockResolvedValue({ + hits: { + hits: [], + total: 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(); + + 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; + searchSourceMock.fetch = mockSearchSourceFetchDefault; +}); + +const logger = createMockLevelLogger(); + +it('formats an empty search result to CSV content', async () => { + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.content).toMatchInlineSnapshot(` + "date,ip,message + " + `); + expect(csvResult.csv_contains_formulas).toBe(false); + expect(csvResult.needs_sorting).toBe(false); +}); + +it('formats a search result to CSV content', async () => { + searchSourceMock.fetch = jest.fn().mockResolvedValueOnce({ + hits: { + hits: [ + { + fields: { + date: `["2020-12-31T00:14:28.000Z"]`, + ip: `["110.135.176.89"]`, + message: `["This is a great message!"]`, + }, + sort: [1, 'a'] as EsQuerySearchAfter, + }, + ], + total: 1, + }, + }); + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.content).toMatchInlineSnapshot(` + "date,ip,message + \\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"This is a great message!\\" + " + `); + expect(csvResult.csv_contains_formulas).toBe(false); + expect(csvResult.needs_sorting).toBe(false); +}); + +const TEST_NUM_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); + }); + searchSourceMock.fetch = jest.fn().mockResolvedValueOnce({ + hits: { + hits: range(0, TEST_NUM_TOTAL).map((hit, i) => ({ + fields: { + message: ['this is a great message'], + }, + sort: [i, 1] as EsQuerySearchAfter, + })), + total: TEST_NUM_TOTAL, + }, + }); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.size).toBe(2608); + expect(csvResult.max_size_reached).toBe(false); + expect(csvResult.needs_sorting).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' }, + }, + }) + ); + + searchSourceMock.fetch = jest.fn().mockResolvedValueOnce({ + hits: { + hits: range(0, TEST_NUM_TOTAL).map((hit, i) => ({ + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: ['super cali fragile istic XPLA docious'], + }, + sort: [i, ''] as EsQuerySearchAfter, + })), + total: TEST_NUM_TOTAL, + }, + }); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.max_size_reached).toBe(true); + expect(csvResult.needs_sorting).toBe(false); + expect(csvResult.warnings).toEqual([]); + expect(csvResult.content).toMatchInlineSnapshot(` + "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\\" + " + `); +}); + +it('warns if it detects paging through unsorted search results', async () => { + searchSourceMock.fetch = jest.fn().mockResolvedValue({ + hits: { + hits: range(0, 15).map(() => ({ + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: ['super cali fragile istic XPLA docious'], + }, + })), + total: 50, + }, + }); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.needs_sorting).toBe(true); +}); + +it('warns if it detects paging through poorly sorted data', async () => { + searchSourceMock.fetch = jest.fn().mockResolvedValue({ + hits: { + hits: range(0, 15).map(() => ({ + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: ['super cali fragile istic XPLA docious'], + sort: [0, 0] as EsQuerySearchAfter, + }, + })), + total: 50, + }, + }); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + expect(csvResult.needs_sorting).toBe(true); +}); + +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); + }); + searchSourceMock.fetch = jest.fn().mockResolvedValueOnce({ + 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.`], + }, + sort: [1, 'a'] as EsQuerySearchAfter, + }, + ], + total: 1, + }, + }); + + const generateCsv = new CsvGenerator( + createMockJob({ searchSource: {} }), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchInlineSnapshot(` + "\\"_id\\",sku + \\"my-cool-id\\",\\"This is a cool SKU., This is also a cool SKU.\\" + " + `); + expect(csvResult.needs_sorting).toBe(false); + }); + + 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); + }); + searchSourceMock.fetch = jest.fn().mockResolvedValueOnce({ + 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`], + }, + sort: [1, 'a'] as EsQuerySearchAfter, + }, + ], + 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, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchInlineSnapshot(` + "\\"_id\\",\\"_index\\",date,message + \\"my-cool-id\\",\\"my-cool-index\\",\\"2020-12-31T00:14:28.000Z\\",\\"it's nice to see you\\" + " + `); + expect(csvResult.csv_contains_formulas).toBe(false); + expect(csvResult.needs_sorting).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 () => { + searchSourceMock.fetch = jest.fn().mockResolvedValueOnce({ + hits: { + hits: [ + { + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: [TEST_FORMULA], + }, + sort: [1, 'a'] as EsQuerySearchAfter, + }, + ], + total: 1, + }, + }); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchInlineSnapshot(` + "date,ip,message + \\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"'=SUM(A1:A2)\\" + " + `); + expect(csvResult.csv_contains_formulas).toBe(false); + expect(csvResult.needs_sorting).toBe(false); + }); + + it(`escapes formula values in a header, doesn't warn the csv contains formulas`, async () => { + searchSourceMock.fetch = jest.fn().mockResolvedValueOnce({ + hits: { + hits: [ + { + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + [TEST_FORMULA]: 'This is great data', + }, + sort: [1, 'a'] as EsQuerySearchAfter, + }, + ], + 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, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchInlineSnapshot(` + "date,ip,\\"'=SUM(A1:A2)\\" + \\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"This is great data\\" + " + `); + expect(csvResult.csv_contains_formulas).toBe(false); + expect(csvResult.needs_sorting).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' }, + }, + }) + ); + searchSourceMock.fetch = jest.fn().mockResolvedValueOnce({ + hits: { + hits: [ + { + fields: { + date: ['2020-12-31T00:14:28.000Z'], + ip: ['110.135.176.89'], + message: [TEST_FORMULA], + }, + sort: [1, 'a'] as EsQuerySearchAfter, + }, + ], + total: 1, + }, + }); + + const generateCsv = new CsvGenerator( + createMockJob({}), + mockConfig, + mockEsClient, + uiSettingsClient, + mockSearchSourceService, + mockFieldFormatsRegistry, + new CancellationToken(), + logger + ); + + const csvResult = await generateCsv.generateData(); + + expect(csvResult.content).toMatchInlineSnapshot(` + "date,ip,message + \\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"=SUM(A1:A2)\\" + " + `); + expect(csvResult.csv_contains_formulas).toBe(true); + expect(csvResult.needs_sorting).toBe(false); + }); +}); 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..9c0486a40fee9 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts @@ -0,0 +1,394 @@ +/* + * 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 { Datatable } from 'src/plugins/expressions/server'; +import { ReportingConfig } from '../../..'; +import { + EsQuerySearchAfter, + FieldFormat, + FieldFormatConfig, + IFieldFormatsRegistry, + ISearchStartSearchSource, + SearchFieldValue, + tabifyDocs, +} from '../../../../../../../src/plugins/data/common'; +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'; + +export class CsvGenerator { + private _columnMap: number[] | null = null; + private _formatters: Record | null = null; + private csvContainsFormulas = false; + private maxSizeReached = false; + private needsSorting = false; + private csvRowCount = 0; + + constructor( + private job: JobParamsCSV, + private config: ReportingConfig, + private esClient: IScopedClusterClient, + private uiSettingsClient: IUiSettingsClient, + private searchSourceService: ISearchStartSearchSource, + private fieldFormatsRegistry: IFieldFormatsRegistry, + private cancellationToken: CancellationToken, + private logger: LevelLogger + ) {} + + /* + * Build a map for ordering the fields of search results into CSV columns + */ + private getColumnMap(fields: SearchFieldValue[] | undefined, table: Datatable) { + if (this._columnMap) { + return this._columnMap; + } + + // if there are selected fields, re-initialize columnMap with field order is set in searchSource fields + if (fields && fields[0] !== '*') { + this._columnMap = fields.map((field) => + table.columns.findIndex((column) => column.id === field) + ); + } + + // initialize default columnMap, works if fields are asterisk and order doesn't matter + if (!this._columnMap) { + this._columnMap = table.columns.map((c, columnIndex) => columnIndex); + } + + return this._columnMap; + } + + /* + * 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.fieldFormatsRegistry.deserialize(c.meta.params); + formatters[c.id] = fieldFormat; + }); + + this._formatters = formatters; + return this._formatters; + } + + /* + * Use the list of fields to generate the header row + */ + private generateHeader( + fields: SearchFieldValue[] | undefined, + table: Datatable, + builder: MaxSizeStringBuilder, + settings: CsvExportSettings + ) { + const { checkForFormulas, escapeValue, separator } = settings; + const columnMap = this.getColumnMap(fields, table); + + this.logger.debug(`Building CSV header row...`); + const header = + columnMap + .map((columnIndex, position) => { + let value: string; + if (columnIndex > -1) { + value = table.columns[columnIndex].name; + } else { + value = fields && fields[position] ? (fields[position] as string) : 'unknown'; + } + + if (checkForFormulas && cellHasFormulas(value)) { + this.csvContainsFormulas = true; // set warning if heading value has a formula + } + + return escapeValue(value); + }) + .join(separator) + `\n`; + + if (!builder.tryAppend(header)) { + return { + size: 0, + content: '', + maxSizeReached: true, + warnings: [], + }; + } + } + + /* + * Format a Datatable into rows of CSV content + */ + private generateRows( + fields: SearchFieldValue[] | undefined, + table: Datatable, + builder: MaxSizeStringBuilder, + formatters: Record, + settings: CsvExportSettings + ) { + // write the rows + this.logger.debug(`Building ${table.rows.length} CSV data rows...`); + const { checkForFormulas, escapeValue, separator } = settings; + + for (const dataTableRow of table.rows) { + if (this.cancellationToken.isCancelled()) { + break; + } + + const columnMap = this.getColumnMap(fields, table); + const row = + columnMap + .map((columnIndex, position) => { + const tableColumn = table.columns[columnIndex]; + let cell: string[] | string = '-'; + + if (tableColumn != null) { + cell = formatters[tableColumn.id].convert(dataTableRow[tableColumn.id]); + + 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.join(', '); // mimic Discover behavior + } + } else { + this.logger.warn(`Unrecognized field: ${(fields && fields[position]) || 'unknown'}`); + } + + return cell; + }) + .map((value) => { + if (checkForFormulas && cellHasFormulas(value)) { + this.csvContainsFormulas = true; // set warning if cell value has a formula + } + // Escape the values in Data + return escapeValue(value); + }) + .join(separator) + '\n'; + + if (!builder.tryAppend(row)) { + this.logger.warn('max Size Reached'); + this.maxSizeReached = true; + if (this.cancellationToken) { + this.cancellationToken.cancel(); + } + break; + } + + this.csvRowCount++; + } + } + + /* + * Open a Point In Time on the Elasticsearch index and collect all the data from the index + */ + public async generateData(): Promise { + const [settings, searchSource] = await Promise.all([ + getExportSettings(this.uiSettingsClient, this.config, this.job.browserTimezone, this.logger), + this.searchSourceService.create(this.job.searchSource), + ]); + + const index = searchSource.getField('index'); + const fields = searchSource.getField('fields'); + + const { maxSizeBytes, bom, escapeFormulaValues } = settings; + searchSource.setField('size', settings.scroll.size); + + const builder = new MaxSizeStringBuilder(byteSizeValueToNumber(maxSizeBytes), bom); + + let currentRecord = -1; + let totalRecords = 0; + let first = true; + let lastSortId: EsQuerySearchAfter | undefined; + let pitId: string | undefined; + const warnings: string[] = []; + + // open PIT and set field format config + if (index) { + // use _pit API + const { title: indexPatternTitle } = index; + const { duration } = settings.scroll; + this.logger.debug(`Open PIT: index: '${indexPatternTitle}' keep_alive: '${duration}'`); + const { body, statusCode } = await this.esClient.asCurrentUser.openPointInTime({ + index: indexPatternTitle, + keep_alive: duration, + }); + if (statusCode === 404) { + this.logger.error('Unable to open point in time!'); + this.logger.error(new Error(JSON.stringify(body))); + } + pitId = body.id as string; + + // 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); + } + } + + this.logger.debug(`Received PIT ID: ${pitId?.substring(0, 9)}`); + if (pitId) { + searchSource.setField('pit', { id: pitId, keep_alive: '2m' }); + } else { + this.logger.error(`Unable to open a point in time with ${searchSource.getId()}!`); + } + + do { + if (this.cancellationToken.isCancelled()) { + break; + } + + if (lastSortId) { + searchSource.setField('searchAfter', lastSortId); + } + let results: SearchResponse | undefined; + try { + results = await searchSource.fetch(); + } catch (err) { + this.logger.error(err); + } + + if (!results) { + break; + } + + if (results.hits?.total == null) { + this.logger.error('Expected total number of records in the search response'); + totalRecords = -1; + } else { + totalRecords = results.hits.total; + } + this.logger.debug(`Search results: ${totalRecords}`); + + let table: Datatable | undefined; + try { + table = tabifyDocs(results, index, { shallow: true, meta: true }); + } catch (err) { + this.logger.error(err); + } + + if (!table) { + break; + } + + // write the header and initialize formatters / column orderings + // depends on the table to know what order to place the columns + 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); + + const hits = results.hits?.hits || []; + + let tempSortId: EsQuerySearchAfter | undefined; + try { + tempSortId = hits[hits.length - 1]?.sort as EsQuerySearchAfter; + if (lastSortId && tempSortId?.join() === lastSortId?.join()) { + // repeated sort ID: results are not thoroughly sorted + this.needsSorting = true; + } + + const uniqueSortIds = new Set(); + hits.forEach((hit) => { + uniqueSortIds.add(hit.sort?.join()); + }); + if (lastSortId && (!uniqueSortIds.size || uniqueSortIds.size < hits.length)) { + // not enough unique sort IDs for the number of hits: results are not thoroughly sorted + this.needsSorting = true; + } + } catch (err) { + this.logger.error(err); + } + + // update last sort ID for next query + lastSortId = tempSortId; + + // update iterator + currentRecord += table.rows.length; + } while (currentRecord < totalRecords - 1); + + const size = builder.getSizeInBytes(); + this.logger.debug( + `Finished generating. Total size in bytes: ${size}. Row count: ${this.csvRowCount}.` + ); + + // the row count is wrong and it's not because of max size limit + // results are not thoroughly sorted + if (this.csvRowCount !== totalRecords && !this.maxSizeReached) { + this.needsSorting = true; + } + + // 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', + }) + ); + } + + if (this.needsSorting) { + warnings.push( + i18n.translate('xpack.reporting.exportTypes.csv.generateCsv.unsortedResults', { + defaultMessage: `The export may have duplicated or missing data: sort values in the search results must be unique`, + }) + ); + } + + // clean PIT + if (pitId) { + this.logger.debug(`Closing PIT`); + await this.esClient.asCurrentUser.closePointInTime({ body: { id: pitId } }); + } + + return { + content: builder.getString(), + content_type: CONTENT_TYPE_CSV, + csv_contains_formulas: this.csvContainsFormulas && !escapeFormulaValues, + max_size_reached: this.maxSizeReached, + needs_sorting: this.needsSorting, + 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..eab8a06b10121 --- /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 esClient = (await reporting.getEsClient()).asScoped(req); + const savedObjectsClient = context.core.savedObjects.client; + const uiSettingsClient = await reporting.getUiSettingsServiceFactory(savedObjectsClient); + const searchService = await reporting.getSearchService(); + const searchSourceService = await searchService.searchSource.asScoped(req); + + const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory( + uiSettingsClient + ); + + const job = { + objectType: 'immediate-search', + ...immediateJobParams, + }; + + const csv = new CsvGenerator( + job, + config, + esClient, + uiSettingsClient, + searchSourceService, + fieldFormatsRegistry, + new 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..5eb6bcb0b42a9 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!`); } + 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), reporting.getPluginStartDeps(), ]); - 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, 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..6605ec37e57f8 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -118,11 +118,13 @@ export class ReportingPlugin const esqueue = await createQueueFactory(reportingCore, store, logger); // starts polling for pending jobs reportingCore.pluginStart({ + data: plugins.data, browserDriverFactory, savedObjects: core.savedObjects, uiSettings: core.uiSettings, esqueue, store, + esClient: core.elasticsearch.client, }); 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 c7ebd969fc01a..c4016e3f81edd 100644 --- a/x-pack/plugins/reporting/server/routes/generation.ts +++ b/x-pack/plugins/reporting/server/routes/generation.ts @@ -13,7 +13,7 @@ import { API_BASE_URL } from '../../common/constants'; import { LevelLogger as Logger } from '../lib'; import { enqueueJobFactory } from '../lib/enqueue_job'; import { registerGenerateFromJobParams } from './generate_from_jobparams'; -import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; +import { registerGenerateCsvFromSavedObjectImmediate } from './csv_searchsource_immediate'; import { HandlerFunction } from './types'; const esErrors = elasticsearchErrors as Record; 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..7ae6159863970 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,11 +34,13 @@ 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 csvRowCount = get(output, 'csv_row_count', false); const maxSizedReach = get(output, 'max_size_reached', false); metaDataHeaders['kbn-csv-contains-formulas'] = csvContainsFormulas; + metaDataHeaders['kbn-csv-num-rows'] = csvRowCount; metaDataHeaders['kbn-max-size-reached'] = maxSizedReach; } 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..5341db12f6548 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, + getSearchService: () => startDepsMock?.data?.search, + } 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/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..5df05de3b9fd9 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`dashboard Reporting Download CSV E-Commerce Data Downloads a CSV export of a saved search panel 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\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" +" +`; + +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\\",\\"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\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" +" +`; + +exports[`dashboard Reporting Download CSV Field Formatters and Scripted Fields Downloads a 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..126e0a7006403 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('Downloads a 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('Downloads a 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..3c2fd087cfd66 100644 --- a/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap +++ b/x-pack/test/functional/apps/discover/__snapshots__/reporting.snap @@ -2,39 +2,48 @@ 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\\"\\"]\\" +\\"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\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" " `; 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\\"\\"]\\" +\\"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\\" +\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\" " `; exports[`discover Discover Generate CSV: new search generates a report with data 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 +"\\"products.manufacturer\\",\\"products.discount_amount\\",\\"products.base_unit_price\\",type,\\"products.discount_percentage\\",\\"products._id.keyword\\",\\"day_of_week_i\\",\\"total_quantity\\",\\"taxless_total_price\\",\\"total_unique_products\\",\\"geoip.continent_name\\",sku,\\"customer_full_name.keyword\\",\\"category.keyword\\",\\"products.taxless_price\\",\\"products.quantity\\",\\"products.price\\",\\"customer_first_name\\",\\"customer_phone\\",\\"geoip.region_name\\",\\"customer_full_name\\",\\"geoip.country_iso_code\\",\\"order_id\\",\\"products._id\\",\\"products.product_name.keyword\\",\\"products.product_id\\",\\"products.category\\",\\"products.manufacturer.keyword\\",manufacturer,\\"products.unit_discount_amount\\",\\"customer_last_name\\",\\"geoip.location\\",\\"products.tax_amount\\",\\"products.product_name\\",\\"products.min_price\\",\\"manufacturer.keyword\\",\\"products.taxful_price\\",currency,\\"products.base_price\\",email,\\"day_of_week\\",\\"products.sku\\",\\"customer_last_name.keyword\\",\\"products.category.keyword\\",\\"geoip.city_name\\",\\"customer_first_name.keyword\\",\\"order_date\\",\\"products.created_on\\",category,\\"customer_id\\",user,\\"customer_gender\\",\\"taxful_total_price\\",\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\" +\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",order,\\"0, 0, 0, 0\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",5,4,\\"173.96\\",4,Asia,\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"Sultan Al Boone\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"1, 1, 1, 1\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Sultan Al\\",,\\"Abu Dhabi\\",\\"Sultan Al Boone\\",AE,716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"0, 0, 0, 0\\",Boone,\\"{ + \\"\\"coordinates\\"\\": [ + 54.4, + 24.5 + ], + \\"\\"type\\"\\": \\"\\"Point\\"\\" +}\\",\\"0, 0, 0, 0\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"79.99, 59.99, 21.99, 11.99\\",EUR,\\"79.99, 59.99, 21.99, 11.99\\",\\"sultan al@boone-family.zzz\\",Saturday,\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",Boone,\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Abu Dhabi\\",\\"Sultan Al\\",\\"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\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",19,sultan,MALE,\\"173.96\\",3AMtOW0BH63Xcmy432DJ,ecommerce,,\\" - \\" " `; 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 83da642be8762..e5fb8a73234e4 100644 Binary files a/x-pack/test/functional/es_archives/reporting/hugedata/data.json.gz and b/x-pack/test/functional/es_archives/reporting/hugedata/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json b/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json index d36bbc72f4ffa..8580f216a06f6 100644 --- a/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json @@ -7,86 +7,16 @@ }, "index": ".kibana_1", "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "apm-telemetry": "0383a570af33654a51c8a1352417bc6b", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "config": "87aca8fdb053154f11383fce3dbf3edf", - "dashboard": "eb3789e1af878e73f85304333240f65f", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "infrastructure-ui-source": "10acdf67d9a06d462e198282fd6d4b81", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "a4229f8b16a6820c6d724b7e0c1f729d", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "server": "ec97f1c5da1a19609a60874e5af1100c", - "space": "0d5011d73a0ef2f0f615bb42f26f187e", - "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "type": "2f4316de49999235636386fe51dc06c1", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "user-action": "0d409297dc5ebe1e3a1da691c6ee32e3", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, "dynamic": "strict", "properties": { - "apm-telemetry": { - "properties": { - "has_any_services": { - "type": "boolean" - }, - "services_per_agent": { - "properties": { - "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-workpad": { - "dynamic": "false", + "action": { "properties": { - "@created": { - "type": "date" + "actionTypeId": { + "type": "keyword" }, - "@timestamp": { - "type": "date" + "config": { + "enabled": false, + "type": "object" }, "name": { "fields": { @@ -95,391 +25,2208 @@ } }, "type": "text" + }, + "secrets": { + "type": "binary" } } }, - "config": { - "dynamic": "true", + "action_task_params": { "properties": { - "buildNum": { + "actionId": { "type": "keyword" }, - "dateFormat:tz": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "defaultIndex": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "apiKey": { + "type": "binary" }, - "search:queryLanguage": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "params": { + "enabled": false, + "type": "object" } } }, - "dashboard": { + "alert": { "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { + "actions": { "properties": { - "display": { + "actionRef": { "type": "keyword" }, - "pause": { - "type": "boolean" + "actionTypeId": { + "type": "keyword" }, - "section": { - "type": "integer" + "group": { + "type": "keyword" }, - "value": { - "type": "integer" + "params": { + "enabled": false, + "type": "object" } - } + }, + "type": "nested" }, - "timeFrom": { + "alertTypeId": { "type": "keyword" }, - "timeRestore": { - "type": "boolean" + "apiKey": { + "type": "binary" }, - "timeTo": { + "apiKeyOwner": { "type": "keyword" }, - "title": { - "type": "text" + "consumer": { + "type": "keyword" }, - "uiStateJSON": { - "type": "text" + "createdAt": { + "type": "date" }, - "version": { - "type": "integer" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" + "createdBy": { + "type": "keyword" }, - "kibanaSavedObjectMeta": { + "enabled": { + "type": "boolean" + }, + "executionStatus": { "properties": { - "searchSourceJSON": { - "type": "text" + "error": { + "properties": { + "message": { + "type": "keyword" + }, + "reason": { + "type": "keyword" + } + } + }, + "lastExecutionDate": { + "type": "date" + }, + "status": { + "type": "keyword" } } }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" + "meta": { + "properties": { + "versionApiKeyLastmodified": { + "type": "keyword" + } + } }, - "version": { - "type": "integer" + "muteAll": { + "type": "boolean" }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" + "mutedInstanceIds": { + "type": "keyword" }, - "fields": { + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, "type": "text" }, - "intervalName": { + "notifyWhen": { "type": "keyword" }, - "notExpandable": { - "type": "boolean" + "params": { + "enabled": false, + "type": "object" }, - "sourceFilters": { - "type": "text" + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } }, - "timeFieldName": { + "scheduledTaskId": { "type": "keyword" }, - "title": { - "type": "text" + "tags": { + "type": "keyword" }, - "type": { + "throttle": { "type": "keyword" }, - "typeMeta": { + "updatedAt": { + "type": "date" + }, + "updatedBy": { "type": "keyword" } } }, - "infrastructure-ui-source": { + "api_key_pending_invalidation": { "properties": { - "description": { - "type": "text" + "apiKeyId": { + "type": "keyword" }, - "fields": { + "createdAt": { + "type": "date" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { "properties": { - "container": { + "errorIndices": { "type": "keyword" }, - "host": { + "metricsIndices": { "type": "keyword" }, - "pod": { + "onboardingIndices": { "type": "keyword" }, - "tiebreaker": { + "sourcemapIndices": { "type": "keyword" }, - "timestamp": { + "spanIndices": { + "type": "keyword" + }, + "transactionIndices": { "type": "keyword" } } - }, - "logAlias": { - "type": "keyword" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" } } }, - "kql-telemetry": { + "apm-telemetry": { + "dynamic": "false", + "type": "object" + }, + "app_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "application_usage_daily": { + "dynamic": "false", "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" + "timestamp": { + "type": "date" } } }, - "map": { + "application_usage_totals": { + "dynamic": "false", + "type": "object" + }, + "application_usage_transactional": { + "dynamic": "false", + "type": "object" + }, + "canvas-element": { + "dynamic": "false", "properties": { - "bounds": { - "type": "geo_shape" + "@created": { + "type": "date" }, - "description": { - "type": "text" + "@timestamp": { + "type": "date" }, - "layerListJSON": { + "content": { "type": "text" }, - "mapStateJSON": { + "help": { "type": "text" }, - "title": { + "image": { "type": "text" }, - "uiStateJSON": { + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" }, - "version": { - "type": "integer" + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" } } }, - "maps-telemetry": { + "canvas-workpad-template": { + "dynamic": "false", "properties": { - "attributesPerMap": { - "properties": { - "dataSourcesCount": { - "properties": { - "avg": { - "type": "long" + "help": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "tags": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "template_key": { + "type": "keyword" + } + } + }, + "cases": { + "properties": { + "closed_at": { + "type": "date" + }, + "closed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "connector": { + "properties": { + "fields": { + "properties": { + "key": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "id": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "description": { + "type": "text" + }, + "external_service": { + "properties": { + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "external_id": { + "type": "keyword" + }, + "external_title": { + "type": "text" + }, + "external_url": { + "type": "text" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "settings": { + "properties": { + "syncAlerts": { + "type": "boolean" + } + } + }, + "status": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-comments": { + "properties": { + "alertId": { + "type": "keyword" + }, + "comment": { + "type": "text" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "index": { + "type": "keyword" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-configure": { + "properties": { + "closure_type": { + "type": "keyword" + }, + "connector": { + "properties": { + "fields": { + "properties": { + "key": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "id": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-connector-mappings": { + "properties": { + "mappings": { + "properties": { + "action_type": { + "type": "keyword" + }, + "source": { + "type": "keyword" + }, + "target": { + "type": "keyword" + } + } + } + } + }, + "cases-user-actions": { + "properties": { + "action": { + "type": "keyword" + }, + "action_at": { + "type": "date" + }, + "action_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "action_field": { + "type": "keyword" + }, + "new_value": { + "type": "text" + }, + "old_value": { + "type": "text" + } + } + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "core-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "optionsJSON": { + "index": false, + "type": "text" + }, + "panelsJSON": { + "index": false, + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "pause": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "section": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "value": { + "doc_values": false, + "index": false, + "type": "integer" + } + } + }, + "timeFrom": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "timeRestore": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "timeTo": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "endpoint:user-artifact": { + "properties": { + "body": { + "type": "binary" + }, + "compressionAlgorithm": { + "index": false, + "type": "keyword" + }, + "created": { + "index": false, + "type": "date" + }, + "decodedSha256": { + "index": false, + "type": "keyword" + }, + "decodedSize": { + "index": false, + "type": "long" + }, + "encodedSha256": { + "type": "keyword" + }, + "encodedSize": { + "index": false, + "type": "long" + }, + "encryptionAlgorithm": { + "index": false, + "type": "keyword" + }, + "identifier": { + "type": "keyword" + } + } + }, + "endpoint:user-artifact-manifest": { + "properties": { + "created": { + "index": false, + "type": "date" + }, + "ids": { + "index": false, + "type": "keyword" + }, + "schemaVersion": { + "type": "keyword" + }, + "semanticVersion": { + "index": false, + "type": "keyword" + } + } + }, + "enterprise_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "epm-packages": { + "properties": { + "es_index_patterns": { + "enabled": false, + "type": "object" + }, + "install_source": { + "type": "keyword" + }, + "install_started_at": { + "type": "date" + }, + "install_status": { + "type": "keyword" + }, + "install_version": { + "type": "keyword" + }, + "installed_es": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "installed_kibana": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "internal": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "package_assets": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "removable": { + "type": "boolean" + }, + "version": { + "type": "keyword" + } + } + }, + "epm-packages-assets": { + "properties": { + "asset_path": { + "type": "keyword" + }, + "data_base64": { + "type": "binary" + }, + "data_utf8": { + "index": false, + "type": "text" + }, + "install_source": { + "type": "keyword" + }, + "media_type": { + "type": "keyword" + }, + "package_name": { + "type": "keyword" + }, + "package_version": { + "type": "keyword" + } + } + }, + "exception-list": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "os_types": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list-agnostic": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "os_types": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "file-upload-usage-collection-telemetry": { + "properties": { + "file_upload": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "fleet-agent-actions": { + "properties": { + "ack_data": { + "type": "text" + }, + "agent_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "data": { + "type": "binary" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "sent_at": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agent-events": { + "properties": { + "action_id": { + "type": "keyword" + }, + "agent_id": { + "type": "keyword" + }, + "data": { + "type": "text" + }, + "message": { + "type": "text" + }, + "payload": { + "type": "text" + }, + "policy_id": { + "type": "keyword" + }, + "stream_id": { + "type": "keyword" + }, + "subtype": { + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agents": { + "properties": { + "access_api_key_id": { + "type": "keyword" + }, + "active": { + "type": "boolean" + }, + "current_error_events": { + "index": false, + "type": "text" + }, + "default_api_key": { + "type": "binary" + }, + "default_api_key_id": { + "type": "keyword" + }, + "enrolled_at": { + "type": "date" + }, + "last_checkin": { + "type": "date" + }, + "last_checkin_status": { + "type": "keyword" + }, + "last_updated": { + "type": "date" + }, + "local_metadata": { + "type": "flattened" + }, + "packages": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "type": { + "type": "keyword" + }, + "unenrolled_at": { + "type": "date" + }, + "unenrollment_started_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, + "upgrade_started_at": { + "type": "date" + }, + "upgraded_at": { + "type": "date" + }, + "user_provided_metadata": { + "type": "flattened" + }, + "version": { + "type": "keyword" + } + } + }, + "fleet-enrollment-api-keys": { + "properties": { + "active": { + "type": "boolean" + }, + "api_key": { + "type": "binary" + }, + "api_key_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "expire_at": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "legacyIndexPatternRef": { + "index": false, + "type": "text" + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "dynamic": "false", + "type": "object" + }, + "ingest-agent-policies": { + "properties": { + "description": { + "type": "text" + }, + "is_default": { + "type": "boolean" + }, + "is_managed": { + "type": "boolean" + }, + "monitoring_enabled": { + "index": false, + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "package_policies": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest-outputs": { + "properties": { + "ca_sha256": { + "index": false, + "type": "keyword" + }, + "config": { + "type": "flattened" + }, + "config_yaml": { + "type": "text" + }, + "fleet_enroll_password": { + "type": "binary" + }, + "fleet_enroll_username": { + "type": "binary" + }, + "hosts": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "ingest-package-policies": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "inputs": { + "enabled": false, + "properties": { + "compiled_input": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "enabled": { + "type": "boolean" + }, + "streams": { + "properties": { + "compiled_stream": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "type": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "output_id": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "policy_id": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest_manager_settings": { + "properties": { + "agent_auto_upgrade": { + "type": "keyword" + }, + "has_seen_add_data_notice": { + "index": false, + "type": "boolean" + }, + "kibana_ca_sha256": { + "type": "keyword" + }, + "kibana_urls": { + "type": "keyword" + }, + "package_auto_upgrade": { + "type": "keyword" + } + } + }, + "inventory-view": { + "dynamic": "false", + "type": "object" + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "legacy-url-alias": { + "dynamic": "false", + "type": "object" + }, + "lens": { + "properties": { + "description": { + "type": "text" + }, + "expression": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "state": { + "type": "flattened" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "bounds": { + "dynamic": "false", + "type": "object" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "enabled": false, + "type": "object" + }, + "metrics-explorer-view": { + "dynamic": "false", + "type": "object" + }, + "ml-job": { + "properties": { + "datafeed_id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "job_id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "ml-telemetry": { + "dynamic": "false", + "type": "object" + }, + "monitoring-telemetry": { + "properties": { + "reportedClusterUuids": { + "type": "keyword" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "originId": { + "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": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "description": { + "type": "text" + }, + "grid": { + "enabled": false, + "type": "object" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "pre712": { + "type": "boolean" + }, + "sort": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "search-session": { + "properties": { + "appId": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "expires": { + "type": "date" + }, + "idMapping": { + "enabled": false, + "type": "object" + }, + "initialState": { + "enabled": false, + "type": "object" + }, + "name": { + "type": "keyword" + }, + "persisted": { + "type": "boolean" + }, + "restoreState": { + "enabled": false, + "type": "object" + }, + "sessionId": { + "type": "keyword" + }, + "status": { + "type": "keyword" + }, + "touched": { + "type": "date" + }, + "urlGeneratorId": { + "type": "keyword" + } + } + }, + "search-telemetry": { + "dynamic": "false", + "type": "object" + }, + "security-solution-signals-migration": { + "properties": { + "created": { + "index": false, + "type": "date" + }, + "createdBy": { + "index": false, + "type": "text" + }, + "destinationIndex": { + "index": false, + "type": "keyword" + }, + "error": { + "index": false, + "type": "text" + }, + "sourceIndex": { + "type": "keyword" + }, + "status": { + "index": false, + "type": "keyword" + }, + "taskId": { + "index": false, + "type": "keyword" + }, + "updated": { + "index": false, + "type": "date" + }, + "updatedBy": { + "index": false, + "type": "text" + }, + "version": { + "type": "long" + } + } + }, + "server": { + "dynamic": "false", + "type": "object" + }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" + } + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "bulkCreateTimeDurations": { + "type": "float" + }, + "gap": { + "type": "text" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastLookBackDate": { + "type": "date" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "searchAfterTimeDurations": { + "type": "float" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, + "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" + } + } + }, + "type": { + "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" + } + } + }, + "type": { + "type": "text" + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "eventType": { + "type": "keyword" + }, + "excludedRowRendererIds": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" }, - "max": { - "type": "long" + "type": { + "type": "keyword" }, - "min": { - "type": "long" + "value": { + "type": "text" } } }, - "emsVectorLayersCount": { - "dynamic": "true", - "type": "object" + "missing": { + "type": "text" }, - "layerTypesCount": { - "dynamic": "true", - "type": "object" + "query": { + "type": "text" + }, + "range": { + "type": "text" }, - "layersCount": { + "script": { + "type": "text" + } + } + }, + "indexNames": { + "type": "text" + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } }, - "min": { - "type": "long" + "serializedQuery": { + "type": "text" } } } } }, - "mapsTotalCount": { - "type": "long" + "savedQueryId": { + "type": "keyword" }, - "timeCaptured": { - "type": "date" - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "dashboard": { - "fields": { - "keyword": { - "ignore_above": 256, + "sort": { + "dynamic": "false", + "properties": { + "columnId": { "type": "keyword" - } - }, - "type": "text" - }, - "index-pattern": { - "fields": { - "keyword": { - "ignore_above": 256, + }, + "columnType": { + "type": "keyword" + }, + "sortDirection": { "type": "keyword" } - }, + } + }, + "status": { + "type": "keyword" + }, + "templateTimelineId": { "type": "text" }, - "search": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, + "templateTimelineVersion": { + "type": "integer" + }, + "timelineType": { + "type": "keyword" + }, + "title": { "type": "text" }, - "visualization": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, + "updated": { + "type": "date" + }, + "updatedBy": { "type": "text" } } }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "references": { + "siem-ui-timeline-note": { "properties": { - "id": { - "type": "keyword" + "created": { + "type": "date" }, - "name": { + "createdBy": { + "type": "text" + }, + "eventId": { "type": "keyword" }, - "type": { + "note": { + "type": "text" + }, + "timelineId": { "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" }, - "unInstallCount": { - "type": "long" + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" } } }, - "search": { + "siem-ui-timeline-pinned-event": { "properties": { - "columns": { - "type": "keyword" + "created": { + "type": "date" }, - "description": { + "createdBy": { "type": "text" }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } + "eventId": { + "type": "keyword" }, - "sort": { + "timelineId": { "type": "keyword" }, - "title": { - "type": "text" + "updated": { + "type": "date" }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" + "updatedBy": { + "type": "text" } } }, @@ -488,9 +2235,6 @@ "_reserved": { "type": "boolean" }, - "disabledFeatures": { - "type": "keyword" - }, "color": { "type": "keyword" }, @@ -500,6 +2244,10 @@ "disabledFeatures": { "type": "keyword" }, + "imageUrl": { + "index": false, + "type": "text" + }, "initials": { "type": "keyword" }, @@ -514,10 +2262,48 @@ } } }, + "spaces-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "tag": { + "properties": { + "color": { + "type": "text" + }, + "description": { + "type": "text" + }, + "name": { + "type": "text" + } + } + }, "telemetry": { "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, "enabled": { "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" } } }, @@ -565,15 +2351,84 @@ "type": { "type": "keyword" }, + "ui-counter": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, "updated_at": { "type": "date" }, "upgrade-assistant-reindex-operation": { - "dynamic": "true", "properties": { + "errorMessage": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, "indexName": { "type": "keyword" }, + "lastCompletedStep": { + "type": "long" + }, + "locked": { + "type": "date" + }, + "newIndexName": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "reindexOptions": { + "properties": { + "openAndClose": { + "type": "boolean" + }, + "queueSettings": { + "properties": { + "queuedAt": { + "type": "long" + }, + "startedAt": { + "type": "long" + } + } + } + } + }, + "reindexTaskId": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "reindexTaskPercComplete": { + "type": "float" + }, + "runningReindexCount": { + "type": "integer" + }, "status": { "type": "integer" } @@ -631,6 +2486,10 @@ } } }, + "uptime-dynamic-settings": { + "dynamic": "false", + "type": "object" + }, "url": { "properties": { "accessCount": { @@ -654,11 +2513,8 @@ } }, "user-action": { - "properties": { - "count": { - "type": "integer" - } - } + "dynamic": "false", + "type": "object" }, "visualization": { "properties": { @@ -668,26 +2524,35 @@ "kibanaSavedObjectMeta": { "properties": { "searchSourceJSON": { + "index": false, "type": "text" } } }, "savedSearchRefName": { + "doc_values": false, + "index": false, "type": "keyword" }, "title": { "type": "text" }, "uiStateJSON": { + "index": false, "type": "text" }, "version": { "type": "integer" }, "visState": { + "index": false, "type": "text" } } + }, + "workplace_search_telemetry": { + "dynamic": "false", + "type": "object" } } }, @@ -701,42 +2566,3 @@ } } -{ - "type": "index", - "value": { - "aliases": { - }, - "index": "babynames", - "mappings": { - "properties": { - "date": { - "type": "date" - }, - "gender": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "percent": { - "type": "float" - }, - "value": { - "type": "integer" - }, - "year": { - "type": "integer" - } - } - }, - "settings": { - "index": { - "mapping": { - "coerce": "false" - }, - "number_of_replicas": "0", - "number_of_shards": "2" - } - } - } -} diff --git a/x-pack/test/functional/es_archives/reporting/scripted_small2/data.json.gz b/x-pack/test/functional/es_archives/reporting/scripted_small2/data.json.gz deleted file mode 100644 index c86627ddb0732..0000000000000 Binary files a/x-pack/test/functional/es_archives/reporting/scripted_small2/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/reporting/scripted_small2/mappings.json b/x-pack/test/functional/es_archives/reporting/scripted_small2/mappings.json deleted file mode 100644 index e1683e54804a3..0000000000000 --- a/x-pack/test/functional/es_archives/reporting/scripted_small2/mappings.json +++ /dev/null @@ -1,2217 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "action": "6e96ac5e648f57523879661ea72525b7", - "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", - "alert": "7b44fba6773e37c806ce290ea9b7024e", - "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-telemetry": "3525d7c22c42bc80f5e6e9cb3f2b26a2", - "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", - "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "cases": "32aa96a6d3855ddda53010ae2048ac22", - "cases-comments": "c2061fb929f585df57425102fa928b4b", - "cases-configure": "42711cbb311976c0687853f4c1354572", - "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "config": "ae24d22d5986d04124cc6568f771066f", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "lens": "d33c68a69ff1e78c9888dedd2164ac22", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "bfd39d88aadadb4be597ea984d433dbe", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "namespaces": "2f4316de49999235636386fe51dc06c1", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "296a89039fc4260292be36b1b005d8f2", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "uptime-dynamic-settings": "fcdb453a30092f022f2642db29523d80", - "url": "b675c3be8d76ecf029294d51dc7ec65d", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, - "dynamic": "strict", - "properties": { - "action": { - "properties": { - "actionTypeId": { - "type": "keyword" - }, - "config": { - "enabled": false, - "type": "object" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "properties": { - "actionId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "alert": { - "properties": { - "actions": { - "properties": { - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "alertTypeId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "createdBy": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "params": { - "enabled": false, - "type": "object" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "updatedBy": { - "type": "keyword" - } - } - }, - "apm-indices": { - "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } - } - } - }, - "apm-telemetry": { - "properties": { - "agents": { - "properties": { - "dotnet": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "go": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "java": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "js-base": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "nodejs": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "python": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "ruby": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "rum-js": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "cardinality": { - "properties": { - "transaction": { - "properties": { - "name": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - }, - "user_agent": { - "properties": { - "original": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "counts": { - "properties": { - "agent_configuration": { - "properties": { - "all": { - "type": "long" - } - } - }, - "error": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "max_error_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "max_transaction_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "services": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "sourcemap": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "span": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "traces": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - } - } - }, - "has_any_services": { - "type": "boolean" - }, - "indices": { - "properties": { - "all": { - "properties": { - "total": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - } - } - }, - "shards": { - "properties": { - "total": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "ml": { - "properties": { - "all_jobs_count": { - "type": "long" - } - } - } - } - }, - "retainment": { - "properties": { - "error": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "span": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "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" - } - } - }, - "tasks": { - "properties": { - "agent_configuration": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "agents": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "cardinality": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "groupings": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "indices_stats": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "processor_events": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "versions": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "properties": { - "apm_server": { - "properties": { - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "patch": { - "type": "long" - } - } - } - } - } - } - }, - "application_usage_totals": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - } - } - }, - "application_usage_transactional": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - }, - "timestamp": { - "type": "date" - } - } - }, - "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" - } - } - }, - "cases": { - "properties": { - "closed_at": { - "type": "date" - }, - "closed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "connector_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "description": { - "type": "text" - }, - "external_service": { - "properties": { - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "external_id": { - "type": "keyword" - }, - "external_title": { - "type": "text" - }, - "external_url": { - "type": "text" - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "status": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-comments": { - "properties": { - "comment": { - "type": "text" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-configure": { - "properties": { - "closure_type": { - "type": "keyword" - }, - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-user-actions": { - "properties": { - "action": { - "type": "keyword" - }, - "action_at": { - "type": "date" - }, - "action_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "action_field": { - "type": "keyword" - }, - "new_value": { - "type": "text" - }, - "old_value": { - "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" - }, - "search:queryLanguage": { - "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" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "lens": { - "properties": { - "description": { - "type": "text" - }, - "expression": { - "index": false, - "type": "keyword" - }, - "state": { - "type": "flattened" - }, - "title": { - "type": "text" - }, - "visualizationType": { - "type": "keyword" - } - } - }, - "lens-ui-telemetry": { - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "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" - } - } - } - } - }, - "indexPatternsWithGeoFieldCount": { - "type": "long" - }, - "indexPatternsWithGeoPointFieldCount": { - "type": "long" - }, - "indexPatternsWithGeoShapeFieldCount": { - "type": "long" - }, - "mapsTotalCount": { - "type": "long" - }, - "settings": { - "properties": { - "showMapVisualizationTypes": { - "type": "boolean" - } - } - }, - "timeCaptured": { - "type": "date" - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "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" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "namespaces": { - "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" - } - } - }, - "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": { - "allowChangingOptInStatus": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "type": "keyword" - }, - "reportFailureCount": { - "type": "integer" - }, - "reportFailureVersion": { - "type": "keyword" - }, - "sendUsageFrom": { - "type": "keyword" - }, - "userHasSeenNotice": { - "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" - } - } - }, - "tsvb-validation-telemetry": { - "properties": { - "failedRequests": { - "type": "long" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "properties": { - "errorMessage": { - "type": "keyword" - }, - "indexName": { - "type": "keyword" - }, - "lastCompletedStep": { - "type": "integer" - }, - "locked": { - "type": "date" - }, - "newIndexName": { - "type": "keyword" - }, - "reindexOptions": { - "properties": { - "openAndClose": { - "type": "boolean" - }, - "queueSettings": { - "properties": { - "queuedAt": { - "type": "long" - }, - "startedAt": { - "type": "long" - } - } - } - } - }, - "reindexTaskId": { - "type": "keyword" - }, - "reindexTaskPercComplete": { - "type": "float" - }, - "runningReindexCount": { - "type": "integer" - }, - "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" - } - } - } - } - }, - "uptime-dynamic-settings": { - "properties": { - "certAgeThreshold": { - "type": "long" - }, - "certExpirationThreshold": { - "type": "long" - }, - "heartbeatIndices": { - "type": "keyword" - } - } - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "user-action": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "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" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - }, - "index": "babynames", - "mappings": { - "properties": { - "date": { - "type": "date" - }, - "gender": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "percent": { - "type": "float" - }, - "value": { - "type": "integer" - }, - "year": { - "type": "integer" - } - } - }, - "settings": { - "index": { - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} \ No newline at end of file diff --git a/x-pack/test/reporting_api_integration/fixtures.ts b/x-pack/test/reporting_api_integration/fixtures.ts index 00309f6959f51..af87c84df97d0 100644 --- a/x-pack/test/reporting_api_integration/fixtures.ts +++ b/x-pack/test/reporting_api_integration/fixtures.ts @@ -5,274 +5,13 @@ * 2.0. */ -export const CSV_RESULT_TIMEBASED_UTC = `"@timestamp",clientip,extension -"Sep 20, 2015 @ 10:26:48.725","74.214.76.90",jpg -"Sep 20, 2015 @ 10:26:48.540","146.86.123.109",jpg -"Sep 20, 2015 @ 10:26:48.353","233.126.159.144",jpg -"Sep 20, 2015 @ 10:26:45.468","153.139.156.196",png -"Sep 20, 2015 @ 10:26:34.063","25.140.171.133",css -"Sep 20, 2015 @ 10:26:11.181","239.249.202.59",jpg -"Sep 20, 2015 @ 10:26:00.639","95.59.225.31",css -"Sep 20, 2015 @ 10:26:00.094","247.174.57.245",jpg -"Sep 20, 2015 @ 10:25:55.744","116.126.47.226",css -"Sep 20, 2015 @ 10:25:54.701","169.228.188.120",jpg -"Sep 20, 2015 @ 10:25:52.360","74.224.77.232",css -"Sep 20, 2015 @ 10:25:49.913","97.83.96.39",css -"Sep 20, 2015 @ 10:25:44.979","175.188.44.145",css -"Sep 20, 2015 @ 10:25:40.968","89.143.125.181",jpg -"Sep 20, 2015 @ 10:25:36.331","231.169.195.137",css -"Sep 20, 2015 @ 10:25:34.064","137.205.146.206",jpg -"Sep 20, 2015 @ 10:25:32.312","53.0.188.251",jpg -"Sep 20, 2015 @ 10:25:27.254","111.214.104.239",jpg -"Sep 20, 2015 @ 10:25:22.561","111.46.85.146",jpg -"Sep 20, 2015 @ 10:25:06.674","55.100.60.111",jpg -"Sep 20, 2015 @ 10:25:05.114","34.197.178.155",jpg -"Sep 20, 2015 @ 10:24:55.114","163.123.136.118",jpg -"Sep 20, 2015 @ 10:24:54.818","11.195.163.57",jpg -"Sep 20, 2015 @ 10:24:53.742","96.222.137.213",png -"Sep 20, 2015 @ 10:24:48.798","227.228.214.218",jpg -"Sep 20, 2015 @ 10:24:20.223","228.53.110.116",jpg -"Sep 20, 2015 @ 10:24:01.794","196.131.253.111",png -"Sep 20, 2015 @ 10:23:49.521","125.163.133.47",jpg -"Sep 20, 2015 @ 10:23:45.816","148.47.216.255",jpg -"Sep 20, 2015 @ 10:23:36.052","51.105.100.214",jpg -"Sep 20, 2015 @ 10:23:34.323","41.210.252.157",gif -"Sep 20, 2015 @ 10:23:27.213","248.163.75.193",png -"Sep 20, 2015 @ 10:23:14.866","48.43.210.167",png -"Sep 20, 2015 @ 10:23:10.578","33.95.78.209",css -"Sep 20, 2015 @ 10:23:07.001","96.40.73.208",css -"Sep 20, 2015 @ 10:23:02.876","174.32.230.63",jpg -"Sep 20, 2015 @ 10:23:00.019","140.233.207.177",jpg -"Sep 20, 2015 @ 10:22:47.447","37.127.124.65",jpg -"Sep 20, 2015 @ 10:22:45.803","130.171.208.139",png -"Sep 20, 2015 @ 10:22:45.590","39.250.210.253",jpg -"Sep 20, 2015 @ 10:22:43.997","248.239.221.43",css -"Sep 20, 2015 @ 10:22:36.107","232.64.207.109",gif -"Sep 20, 2015 @ 10:22:30.527","24.186.122.118",jpg -"Sep 20, 2015 @ 10:22:25.697","23.3.174.206",jpg -"Sep 20, 2015 @ 10:22:08.272","185.170.80.142",php -"Sep 20, 2015 @ 10:21:40.822","202.22.74.232",png -"Sep 20, 2015 @ 10:21:36.210","39.227.27.167",jpg -"Sep 20, 2015 @ 10:21:19.154","140.233.207.177",jpg -"Sep 20, 2015 @ 10:21:09.852","22.151.97.227",jpg -"Sep 20, 2015 @ 10:21:06.079","157.39.25.197",css -"Sep 20, 2015 @ 10:21:01.357","37.127.124.65",jpg -"Sep 20, 2015 @ 10:20:56.519","23.184.94.58",jpg -"Sep 20, 2015 @ 10:20:40.189","80.83.92.252",jpg -"Sep 20, 2015 @ 10:20:27.012","66.194.157.171",png -"Sep 20, 2015 @ 10:20:24.450","15.191.218.38",jpg -`; - -export const CSV_RESULT_TIMEBASED_CUSTOM = `"@timestamp",clientip,extension -"Sep 20, 2015 @ 03:26:48.725","74.214.76.90",jpg -"Sep 20, 2015 @ 03:26:48.540","146.86.123.109",jpg -"Sep 20, 2015 @ 03:26:48.353","233.126.159.144",jpg -"Sep 20, 2015 @ 03:26:45.468","153.139.156.196",png -"Sep 20, 2015 @ 03:26:34.063","25.140.171.133",css -"Sep 20, 2015 @ 03:26:11.181","239.249.202.59",jpg -"Sep 20, 2015 @ 03:26:00.639","95.59.225.31",css -"Sep 20, 2015 @ 03:26:00.094","247.174.57.245",jpg -"Sep 20, 2015 @ 03:25:55.744","116.126.47.226",css -"Sep 20, 2015 @ 03:25:54.701","169.228.188.120",jpg -"Sep 20, 2015 @ 03:25:52.360","74.224.77.232",css -"Sep 20, 2015 @ 03:25:49.913","97.83.96.39",css -"Sep 20, 2015 @ 03:25:44.979","175.188.44.145",css -"Sep 20, 2015 @ 03:25:40.968","89.143.125.181",jpg -"Sep 20, 2015 @ 03:25:36.331","231.169.195.137",css -"Sep 20, 2015 @ 03:25:34.064","137.205.146.206",jpg -"Sep 20, 2015 @ 03:25:32.312","53.0.188.251",jpg -"Sep 20, 2015 @ 03:25:27.254","111.214.104.239",jpg -"Sep 20, 2015 @ 03:25:22.561","111.46.85.146",jpg -"Sep 20, 2015 @ 03:25:06.674","55.100.60.111",jpg -"Sep 20, 2015 @ 03:25:05.114","34.197.178.155",jpg -"Sep 20, 2015 @ 03:24:55.114","163.123.136.118",jpg -"Sep 20, 2015 @ 03:24:54.818","11.195.163.57",jpg -"Sep 20, 2015 @ 03:24:53.742","96.222.137.213",png -"Sep 20, 2015 @ 03:24:48.798","227.228.214.218",jpg -"Sep 20, 2015 @ 03:24:20.223","228.53.110.116",jpg -"Sep 20, 2015 @ 03:24:01.794","196.131.253.111",png -"Sep 20, 2015 @ 03:23:49.521","125.163.133.47",jpg -"Sep 20, 2015 @ 03:23:45.816","148.47.216.255",jpg -"Sep 20, 2015 @ 03:23:36.052","51.105.100.214",jpg -"Sep 20, 2015 @ 03:23:34.323","41.210.252.157",gif -"Sep 20, 2015 @ 03:23:27.213","248.163.75.193",png -"Sep 20, 2015 @ 03:23:14.866","48.43.210.167",png -"Sep 20, 2015 @ 03:23:10.578","33.95.78.209",css -"Sep 20, 2015 @ 03:23:07.001","96.40.73.208",css -"Sep 20, 2015 @ 03:23:02.876","174.32.230.63",jpg -"Sep 20, 2015 @ 03:23:00.019","140.233.207.177",jpg -"Sep 20, 2015 @ 03:22:47.447","37.127.124.65",jpg -"Sep 20, 2015 @ 03:22:45.803","130.171.208.139",png -"Sep 20, 2015 @ 03:22:45.590","39.250.210.253",jpg -"Sep 20, 2015 @ 03:22:43.997","248.239.221.43",css -"Sep 20, 2015 @ 03:22:36.107","232.64.207.109",gif -"Sep 20, 2015 @ 03:22:30.527","24.186.122.118",jpg -"Sep 20, 2015 @ 03:22:25.697","23.3.174.206",jpg -"Sep 20, 2015 @ 03:22:08.272","185.170.80.142",php -"Sep 20, 2015 @ 03:21:40.822","202.22.74.232",png -"Sep 20, 2015 @ 03:21:36.210","39.227.27.167",jpg -"Sep 20, 2015 @ 03:21:19.154","140.233.207.177",jpg -"Sep 20, 2015 @ 03:21:09.852","22.151.97.227",jpg -"Sep 20, 2015 @ 03:21:06.079","157.39.25.197",css -"Sep 20, 2015 @ 03:21:01.357","37.127.124.65",jpg -"Sep 20, 2015 @ 03:20:56.519","23.184.94.58",jpg -"Sep 20, 2015 @ 03:20:40.189","80.83.92.252",jpg -"Sep 20, 2015 @ 03:20:27.012","66.194.157.171",png -"Sep 20, 2015 @ 03:20:24.450","15.191.218.38",jpg -`; - -export const CSV_RESULT_TIMELESS = `name,power -"Jonelle-Jane Marth","1.177" -"Suzie-May Rishel","1.824" -"Suzie-May Rishel","2.077" -"Rosana Casto","2.808" -"Stephen Cortez","4.986" -"Jonelle-Jane Marth","6.156" -"Jonelle-Jane Marth","7.097" -"Florinda Alejandro","10.373" -"Jonelle-Jane Marth","14.807" -"Suzie-May Rishel","19.738" -"Suzie-May Rishel","20.92" -"Florinda Alejandro","22.209" -`; - -export const CSV_RESULT_SCRIPTED = `date,name,percent,value,year,"years_ago",gender -"Jan 1, 1980 @ 00:00:00.000",Fecki,0,92,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Fecki,0,78,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Fecky,"0.001","2,071","1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Fekki,0,6,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felen,0,40,"1,980","39.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felia,0,21,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felina,0,6,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felinda,"0.001","1,620","1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felinda,"0.001","1,886","1,981","38.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felisa,0,5,"1,981","38.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felita,0,8,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felkys,0,7,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felkys,0,8,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Fell,0,6,"1,980","39.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felle,0,22,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felma,0,8,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felynda,0,31,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Fenita,0,219,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Fenjamin,0,22,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Fenjamin,0,27,"1,981","38.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Fenji,0,5,"1,981","38.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Fennie,0,16,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Fenny,0,5,"1,980","39.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Ferenice,0,9,"1,980","39.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Frijida,0,5,"1,980","39.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Frita,0,14,"1,980","39.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Fritney,0,10,"1,980","39.00000000000000000000",F -`; - -export const CSV_RESULT_SCRIPTED_REQUERY = `date,name,percent,value,year,"years_ago",gender -"Jan 1, 1980 @ 00:00:00.000",Felen,0,40,"1,980","39.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felia,0,21,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felina,0,6,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felinda,"0.001","1,620","1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felinda,"0.001","1,886","1,981","38.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felisa,0,5,"1,981","38.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felita,0,8,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felkys,0,7,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felkys,0,8,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Fell,0,6,"1,980","39.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felle,0,22,"1,980","39.00000000000000000000",F -"Jan 1, 1981 @ 00:00:00.000",Felma,0,8,"1,981","38.00000000000000000000",F -"Jan 1, 1980 @ 00:00:00.000",Felynda,0,31,"1,980","39.00000000000000000000",F -`; - -export const CSV_RESULT_SCRIPTED_RESORTED = `date,year,name,value,"years_ago" -"Jan 1, 1981 @ 00:00:00.000","1,981",Farbara,"6,456","38.00000000000000000000" -"Jan 1, 1980 @ 00:00:00.000","1,980",Farbara,"8,026","39.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Fecky,"1,930","38.00000000000000000000" -"Jan 1, 1980 @ 00:00:00.000","1,980",Fecky,"2,071","39.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Felinda,"1,886","38.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Feth,"3,685","38.00000000000000000000" -"Jan 1, 1980 @ 00:00:00.000","1,980",Feth,"4,246","39.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Fetty,"1,763","38.00000000000000000000" -"Jan 1, 1980 @ 00:00:00.000","1,980",Fetty,"1,967","39.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Feverly,"1,987","38.00000000000000000000" -"Jan 1, 1980 @ 00:00:00.000","1,980",Feverly,"2,249","39.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Fonnie,"2,330","38.00000000000000000000" -"Jan 1, 1980 @ 00:00:00.000","1,980",Fonnie,"2,748","39.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Frenda,"7,162","38.00000000000000000000" -"Jan 1, 1980 @ 00:00:00.000","1,980",Frenda,"8,335","39.00000000000000000000" -`; - -export const CSV_RESULT_HUGE = `date,year,name,value,"years_ago" -"Jan 1, 1984 @ 00:00:00.000","1,984",Fobby,"2,791","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Frent,"3,416","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Frett,"2,679","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Filly,"3,366","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Frian,"34,468","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Fenjamin,"7,191","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Frandon,"5,863","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Fruce,"1,855","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Fryan,"7,236","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Frad,"2,482","35.00000000000000000000" -"Jan 1, 1984 @ 00:00:00.000","1,984",Fradley,"5,175","35.00000000000000000000" -"Jan 1, 1983 @ 00:00:00.000","1,983",Fryan,"7,114","36.00000000000000000000" -"Jan 1, 1983 @ 00:00:00.000","1,983",Fradley,"4,752","36.00000000000000000000" -"Jan 1, 1983 @ 00:00:00.000","1,983",Frian,"35,717","36.00000000000000000000" -"Jan 1, 1983 @ 00:00:00.000","1,983",Farbara,"4,434","36.00000000000000000000" -"Jan 1, 1983 @ 00:00:00.000","1,983",Fenjamin,"5,235","36.00000000000000000000" -"Jan 1, 1983 @ 00:00:00.000","1,983",Fruce,"1,914","36.00000000000000000000" -"Jan 1, 1983 @ 00:00:00.000","1,983",Fobby,"2,888","36.00000000000000000000" -"Jan 1, 1983 @ 00:00:00.000","1,983",Frett,"3,031","36.00000000000000000000" -"Jan 1, 1982 @ 00:00:00.000","1,982",Fonnie,"1,853","37.00000000000000000000" -"Jan 1, 1982 @ 00:00:00.000","1,982",Frandy,"2,082","37.00000000000000000000" -"Jan 1, 1982 @ 00:00:00.000","1,982",Fecky,"1,786","37.00000000000000000000" -"Jan 1, 1982 @ 00:00:00.000","1,982",Frandi,"2,056","37.00000000000000000000" -"Jan 1, 1982 @ 00:00:00.000","1,982",Fridget,"1,864","37.00000000000000000000" -"Jan 1, 1982 @ 00:00:00.000","1,982",Farbara,"5,081","37.00000000000000000000" -"Jan 1, 1982 @ 00:00:00.000","1,982",Feth,"2,818","37.00000000000000000000" -"Jan 1, 1982 @ 00:00:00.000","1,982",Frenda,"6,270","37.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Fetty,"1,763","38.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Fonnie,"2,330","38.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Farbara,"6,456","38.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Felinda,"1,886","38.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Frenda,"7,162","38.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Feth,"3,685","38.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Feverly,"1,987","38.00000000000000000000" -"Jan 1, 1981 @ 00:00:00.000","1,981",Fecky,"1,930","38.00000000000000000000" -"Jan 1, 1980 @ 00:00:00.000","1,980",Fonnie,"2,748","39.00000000000000000000" -`; - -// 'UTC' -export const CSV_RESULT_NANOS = `date,message,"_id" -"Jan 1, 2015 @ 12:10:30.123456789","Hello 2", -"Jan 1, 2015 @ 12:10:30.000000000","Hello 1", -`; - -// 'America/New_York' -export const CSV_RESULT_NANOS_CUSTOM = `date,message,"_id" -"Jan 1, 2015 @ 07:10:30.123456789","Hello 2", -"Jan 1, 2015 @ 07:10:30.000000000","Hello 1", -`; - -export const CSV_RESULT_DOCVALUE = `"order_date",category,currency,"customer_id","order_id","day_of_week_i","order_date","products.created_on",sku -"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes"",""Women's Clothing""]",EUR,26,569309,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0364103641"",""ZO0708807088""]" -"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes"",""Women's Clothing""]",EUR,24,569311,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0024600246"",""ZO0660706607""]" -"Jun 26, 2019 @ 00:00:00.000","[""Men's Clothing"",""Men's Shoes""]",EUR,31,569312,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0425104251"",""ZO0107901079""]" -"Jun 26, 2019 @ 00:00:00.000","[""Men's Shoes""]",EUR,14,569336,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0512505125"",""ZO0384103841""]" -"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing""]",EUR,28,569337,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0634106341"",""ZO0066900669""]" -"Jun 26, 2019 @ 00:00:00.000","[""Men's Accessories"",""Men's Clothing""]",EUR,31,569338,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0702507025"",""ZO0528105281""]" -"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes"",""Women's Clothing""]",EUR,27,569356,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0010500105"",""ZO0172201722""]" -"Jun 26, 2019 @ 00:00:00.000","[""Men's Clothing"",""Men's Shoes""]",EUR,19,569362,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0292402924"",""ZO0681006810""]" -"Jun 26, 2019 @ 00:00:00.000","[""Women's Accessories"",""Women's Clothing""]",EUR,42,569370,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0358603586"",""ZO0641106411""]" -"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing"",""Women's Accessories""]",EUR,20,569371,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0225702257"",""ZO0186601866""]" -"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing"",""Women's Shoes""]",EUR,43,569375,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0347603476"",""ZO0668806688""]" -"Jun 26, 2019 @ 00:00:00.000","[""Men's Clothing""]",EUR,48,569387,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0593805938"",""ZO0125201252""]" -`; - // This concatenates lines of multi-line string into a single line. // It is so long strings can be entered at short widths, making syntax highlighting easier on editors function singleLine(literals: TemplateStringsArray): string { return literals[0].split('\n').join(''); } -export const JOB_PARAMS_RISON = singleLine`(conflictedTypesFields:!(),fields:!('@ti +export const JOB_PARAMS_RISON_CSV_DEPRECATED = singleLine`(conflictedTypesFields:!(),fields:!('@ti mestamp',clientip,extension),indexPatternId:'logstash-*',metaFields:!(_source,_id,_type,_ index,_score),searchRequest:(body:(_source:(excludes:!(),includes:!('@timestamp',clientip ,extension)),docvalue_fields:!(),query:(bool:(filter:!((match_all:()),(range:('@timestamp diff --git a/x-pack/test/reporting_api_integration/reporting_and_security.config.ts b/x-pack/test/reporting_api_integration/reporting_and_security.config.ts index a1b0e8145391a..14e7d9ff67a2b 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security.config.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security.config.ts @@ -43,6 +43,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--server.port=${kbnTestConfig.getPort()}`, `--xpack.reporting.capture.maxAttempts=1`, `--xpack.reporting.csv.maxSizeBytes=2850`, + `--xpack.reporting.csv.escapeFormulaValues=true`, `--xpack.reporting.queue.pollInterval=3000`, `--xpack.security.session.idleTimeout=3600000`, `--xpack.reporting.capture.networkPolicy.rules=${JSON.stringify(testPolicyRules)}`, diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/csv_searchsource_immediate.snap b/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/csv_searchsource_immediate.snap new file mode 100644 index 0000000000000..ff7ce7f5fedfb --- /dev/null +++ b/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/csv_searchsource_immediate.snap @@ -0,0 +1,251 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Reporting APIs CSV generation from SearchSource date formatting Formatted date_nanos data, UTC timezone 1`] = ` +"date,message +\\"Jan 1, 2015 @ 12:10:30.123456789\\",\\"Hello 2\\" +\\"Jan 1, 2015 @ 12:10:30.000000000\\",\\"Hello 1\\" +" +`; + +exports[`Reporting APIs CSV generation from SearchSource date formatting Formatted date_nanos data, custom timezone (New York) 1`] = ` +"date,message +\\"Jan 1, 2015 @ 07:10:30.123456789\\",\\"Hello 2\\" +\\"Jan 1, 2015 @ 07:10:30.000000000\\",\\"Hello 1\\" +" +`; + +exports[`Reporting APIs CSV generation from SearchSource date formatting With filters and timebased data, custom timezone (Phoenix) 1`] = ` +"\\"'@timestamp\\",clientip,extension +\\"Sep 20, 2015 @ 03:26:48.725\\",\\"74.214.76.90\\",jpg +\\"Sep 20, 2015 @ 03:26:48.540\\",\\"146.86.123.109\\",jpg +\\"Sep 20, 2015 @ 03:26:48.353\\",\\"233.126.159.144\\",jpg +\\"Sep 20, 2015 @ 03:26:45.468\\",\\"153.139.156.196\\",png +\\"Sep 20, 2015 @ 03:26:34.063\\",\\"25.140.171.133\\",css +\\"Sep 20, 2015 @ 03:26:11.181\\",\\"239.249.202.59\\",jpg +\\"Sep 20, 2015 @ 03:26:00.639\\",\\"95.59.225.31\\",css +\\"Sep 20, 2015 @ 03:26:00.094\\",\\"247.174.57.245\\",jpg +\\"Sep 20, 2015 @ 03:25:55.744\\",\\"116.126.47.226\\",css +\\"Sep 20, 2015 @ 03:25:54.701\\",\\"169.228.188.120\\",jpg +\\"Sep 20, 2015 @ 03:25:52.360\\",\\"74.224.77.232\\",css +\\"Sep 20, 2015 @ 03:25:49.913\\",\\"97.83.96.39\\",css +\\"Sep 20, 2015 @ 03:25:44.979\\",\\"175.188.44.145\\",css +\\"Sep 20, 2015 @ 03:25:40.968\\",\\"89.143.125.181\\",jpg +\\"Sep 20, 2015 @ 03:25:36.331\\",\\"231.169.195.137\\",css +\\"Sep 20, 2015 @ 03:25:34.064\\",\\"137.205.146.206\\",jpg +\\"Sep 20, 2015 @ 03:25:32.312\\",\\"53.0.188.251\\",jpg +\\"Sep 20, 2015 @ 03:25:27.254\\",\\"111.214.104.239\\",jpg +\\"Sep 20, 2015 @ 03:25:22.561\\",\\"111.46.85.146\\",jpg +\\"Sep 20, 2015 @ 03:25:06.674\\",\\"55.100.60.111\\",jpg +\\"Sep 20, 2015 @ 03:25:05.114\\",\\"34.197.178.155\\",jpg +\\"Sep 20, 2015 @ 03:24:55.114\\",\\"163.123.136.118\\",jpg +\\"Sep 20, 2015 @ 03:24:54.818\\",\\"11.195.163.57\\",jpg +\\"Sep 20, 2015 @ 03:24:53.742\\",\\"96.222.137.213\\",png +\\"Sep 20, 2015 @ 03:24:48.798\\",\\"227.228.214.218\\",jpg +\\"Sep 20, 2015 @ 03:24:20.223\\",\\"228.53.110.116\\",jpg +\\"Sep 20, 2015 @ 03:24:01.794\\",\\"196.131.253.111\\",png +\\"Sep 20, 2015 @ 03:23:49.521\\",\\"125.163.133.47\\",jpg +\\"Sep 20, 2015 @ 03:23:45.816\\",\\"148.47.216.255\\",jpg +\\"Sep 20, 2015 @ 03:23:36.052\\",\\"51.105.100.214\\",jpg +\\"Sep 20, 2015 @ 03:23:34.323\\",\\"41.210.252.157\\",gif +\\"Sep 20, 2015 @ 03:23:27.213\\",\\"248.163.75.193\\",png +\\"Sep 20, 2015 @ 03:23:14.866\\",\\"48.43.210.167\\",png +\\"Sep 20, 2015 @ 03:23:10.578\\",\\"33.95.78.209\\",css +\\"Sep 20, 2015 @ 03:23:07.001\\",\\"96.40.73.208\\",css +\\"Sep 20, 2015 @ 03:23:02.876\\",\\"174.32.230.63\\",jpg +\\"Sep 20, 2015 @ 03:23:00.019\\",\\"140.233.207.177\\",jpg +\\"Sep 20, 2015 @ 03:22:47.447\\",\\"37.127.124.65\\",jpg +\\"Sep 20, 2015 @ 03:22:45.803\\",\\"130.171.208.139\\",png +\\"Sep 20, 2015 @ 03:22:45.590\\",\\"39.250.210.253\\",jpg +\\"Sep 20, 2015 @ 03:22:43.997\\",\\"248.239.221.43\\",css +\\"Sep 20, 2015 @ 03:22:36.107\\",\\"232.64.207.109\\",gif +\\"Sep 20, 2015 @ 03:22:30.527\\",\\"24.186.122.118\\",jpg +\\"Sep 20, 2015 @ 03:22:25.697\\",\\"23.3.174.206\\",jpg +\\"Sep 20, 2015 @ 03:22:08.272\\",\\"185.170.80.142\\",php +\\"Sep 20, 2015 @ 03:21:40.822\\",\\"202.22.74.232\\",png +\\"Sep 20, 2015 @ 03:21:36.210\\",\\"39.227.27.167\\",jpg +\\"Sep 20, 2015 @ 03:21:19.154\\",\\"140.233.207.177\\",jpg +\\"Sep 20, 2015 @ 03:21:09.852\\",\\"22.151.97.227\\",jpg +\\"Sep 20, 2015 @ 03:21:06.079\\",\\"157.39.25.197\\",css +\\"Sep 20, 2015 @ 03:21:01.357\\",\\"37.127.124.65\\",jpg +\\"Sep 20, 2015 @ 03:20:56.519\\",\\"23.184.94.58\\",jpg +\\"Sep 20, 2015 @ 03:20:40.189\\",\\"80.83.92.252\\",jpg +\\"Sep 20, 2015 @ 03:20:27.012\\",\\"66.194.157.171\\",png +\\"Sep 20, 2015 @ 03:20:24.450\\",\\"15.191.218.38\\",jpg +" +`; + +exports[`Reporting APIs CSV generation from SearchSource date formatting With filters and timebased data, default to UTC 1`] = ` +"\\"'@timestamp\\",clientip,extension +\\"Sep 20, 2015 @ 10:26:48.725\\",\\"74.214.76.90\\",jpg +\\"Sep 20, 2015 @ 10:26:48.540\\",\\"146.86.123.109\\",jpg +\\"Sep 20, 2015 @ 10:26:48.353\\",\\"233.126.159.144\\",jpg +\\"Sep 20, 2015 @ 10:26:45.468\\",\\"153.139.156.196\\",png +\\"Sep 20, 2015 @ 10:26:34.063\\",\\"25.140.171.133\\",css +\\"Sep 20, 2015 @ 10:26:11.181\\",\\"239.249.202.59\\",jpg +\\"Sep 20, 2015 @ 10:26:00.639\\",\\"95.59.225.31\\",css +\\"Sep 20, 2015 @ 10:26:00.094\\",\\"247.174.57.245\\",jpg +\\"Sep 20, 2015 @ 10:25:55.744\\",\\"116.126.47.226\\",css +\\"Sep 20, 2015 @ 10:25:54.701\\",\\"169.228.188.120\\",jpg +\\"Sep 20, 2015 @ 10:25:52.360\\",\\"74.224.77.232\\",css +\\"Sep 20, 2015 @ 10:25:49.913\\",\\"97.83.96.39\\",css +\\"Sep 20, 2015 @ 10:25:44.979\\",\\"175.188.44.145\\",css +\\"Sep 20, 2015 @ 10:25:40.968\\",\\"89.143.125.181\\",jpg +\\"Sep 20, 2015 @ 10:25:36.331\\",\\"231.169.195.137\\",css +\\"Sep 20, 2015 @ 10:25:34.064\\",\\"137.205.146.206\\",jpg +\\"Sep 20, 2015 @ 10:25:32.312\\",\\"53.0.188.251\\",jpg +\\"Sep 20, 2015 @ 10:25:27.254\\",\\"111.214.104.239\\",jpg +\\"Sep 20, 2015 @ 10:25:22.561\\",\\"111.46.85.146\\",jpg +\\"Sep 20, 2015 @ 10:25:06.674\\",\\"55.100.60.111\\",jpg +\\"Sep 20, 2015 @ 10:25:05.114\\",\\"34.197.178.155\\",jpg +\\"Sep 20, 2015 @ 10:24:55.114\\",\\"163.123.136.118\\",jpg +\\"Sep 20, 2015 @ 10:24:54.818\\",\\"11.195.163.57\\",jpg +\\"Sep 20, 2015 @ 10:24:53.742\\",\\"96.222.137.213\\",png +\\"Sep 20, 2015 @ 10:24:48.798\\",\\"227.228.214.218\\",jpg +\\"Sep 20, 2015 @ 10:24:20.223\\",\\"228.53.110.116\\",jpg +\\"Sep 20, 2015 @ 10:24:01.794\\",\\"196.131.253.111\\",png +\\"Sep 20, 2015 @ 10:23:49.521\\",\\"125.163.133.47\\",jpg +\\"Sep 20, 2015 @ 10:23:45.816\\",\\"148.47.216.255\\",jpg +\\"Sep 20, 2015 @ 10:23:36.052\\",\\"51.105.100.214\\",jpg +\\"Sep 20, 2015 @ 10:23:34.323\\",\\"41.210.252.157\\",gif +\\"Sep 20, 2015 @ 10:23:27.213\\",\\"248.163.75.193\\",png +\\"Sep 20, 2015 @ 10:23:14.866\\",\\"48.43.210.167\\",png +\\"Sep 20, 2015 @ 10:23:10.578\\",\\"33.95.78.209\\",css +\\"Sep 20, 2015 @ 10:23:07.001\\",\\"96.40.73.208\\",css +\\"Sep 20, 2015 @ 10:23:02.876\\",\\"174.32.230.63\\",jpg +\\"Sep 20, 2015 @ 10:23:00.019\\",\\"140.233.207.177\\",jpg +\\"Sep 20, 2015 @ 10:22:47.447\\",\\"37.127.124.65\\",jpg +\\"Sep 20, 2015 @ 10:22:45.803\\",\\"130.171.208.139\\",png +\\"Sep 20, 2015 @ 10:22:45.590\\",\\"39.250.210.253\\",jpg +\\"Sep 20, 2015 @ 10:22:43.997\\",\\"248.239.221.43\\",css +\\"Sep 20, 2015 @ 10:22:36.107\\",\\"232.64.207.109\\",gif +\\"Sep 20, 2015 @ 10:22:30.527\\",\\"24.186.122.118\\",jpg +\\"Sep 20, 2015 @ 10:22:25.697\\",\\"23.3.174.206\\",jpg +\\"Sep 20, 2015 @ 10:22:08.272\\",\\"185.170.80.142\\",php +\\"Sep 20, 2015 @ 10:21:40.822\\",\\"202.22.74.232\\",png +\\"Sep 20, 2015 @ 10:21:36.210\\",\\"39.227.27.167\\",jpg +\\"Sep 20, 2015 @ 10:21:19.154\\",\\"140.233.207.177\\",jpg +\\"Sep 20, 2015 @ 10:21:09.852\\",\\"22.151.97.227\\",jpg +\\"Sep 20, 2015 @ 10:21:06.079\\",\\"157.39.25.197\\",css +\\"Sep 20, 2015 @ 10:21:01.357\\",\\"37.127.124.65\\",jpg +\\"Sep 20, 2015 @ 10:20:56.519\\",\\"23.184.94.58\\",jpg +\\"Sep 20, 2015 @ 10:20:40.189\\",\\"80.83.92.252\\",jpg +\\"Sep 20, 2015 @ 10:20:27.012\\",\\"66.194.157.171\\",png +\\"Sep 20, 2015 @ 10:20:24.450\\",\\"15.191.218.38\\",jpg +" +`; + +exports[`Reporting APIs CSV generation from SearchSource date formatting With filters and timebased data, explicit UTC format 1`] = ` +"\\"'@timestamp\\",clientip,extension +\\"Sep 20, 2015 @ 10:26:48.725\\",\\"74.214.76.90\\",jpg +\\"Sep 20, 2015 @ 10:26:48.540\\",\\"146.86.123.109\\",jpg +\\"Sep 20, 2015 @ 10:26:48.353\\",\\"233.126.159.144\\",jpg +\\"Sep 20, 2015 @ 10:26:45.468\\",\\"153.139.156.196\\",png +\\"Sep 20, 2015 @ 10:26:34.063\\",\\"25.140.171.133\\",css +\\"Sep 20, 2015 @ 10:26:11.181\\",\\"239.249.202.59\\",jpg +\\"Sep 20, 2015 @ 10:26:00.639\\",\\"95.59.225.31\\",css +\\"Sep 20, 2015 @ 10:26:00.094\\",\\"247.174.57.245\\",jpg +\\"Sep 20, 2015 @ 10:25:55.744\\",\\"116.126.47.226\\",css +\\"Sep 20, 2015 @ 10:25:54.701\\",\\"169.228.188.120\\",jpg +\\"Sep 20, 2015 @ 10:25:52.360\\",\\"74.224.77.232\\",css +\\"Sep 20, 2015 @ 10:25:49.913\\",\\"97.83.96.39\\",css +\\"Sep 20, 2015 @ 10:25:44.979\\",\\"175.188.44.145\\",css +\\"Sep 20, 2015 @ 10:25:40.968\\",\\"89.143.125.181\\",jpg +\\"Sep 20, 2015 @ 10:25:36.331\\",\\"231.169.195.137\\",css +\\"Sep 20, 2015 @ 10:25:34.064\\",\\"137.205.146.206\\",jpg +\\"Sep 20, 2015 @ 10:25:32.312\\",\\"53.0.188.251\\",jpg +\\"Sep 20, 2015 @ 10:25:27.254\\",\\"111.214.104.239\\",jpg +\\"Sep 20, 2015 @ 10:25:22.561\\",\\"111.46.85.146\\",jpg +\\"Sep 20, 2015 @ 10:25:06.674\\",\\"55.100.60.111\\",jpg +\\"Sep 20, 2015 @ 10:25:05.114\\",\\"34.197.178.155\\",jpg +\\"Sep 20, 2015 @ 10:24:55.114\\",\\"163.123.136.118\\",jpg +\\"Sep 20, 2015 @ 10:24:54.818\\",\\"11.195.163.57\\",jpg +\\"Sep 20, 2015 @ 10:24:53.742\\",\\"96.222.137.213\\",png +\\"Sep 20, 2015 @ 10:24:48.798\\",\\"227.228.214.218\\",jpg +\\"Sep 20, 2015 @ 10:24:20.223\\",\\"228.53.110.116\\",jpg +\\"Sep 20, 2015 @ 10:24:01.794\\",\\"196.131.253.111\\",png +\\"Sep 20, 2015 @ 10:23:49.521\\",\\"125.163.133.47\\",jpg +\\"Sep 20, 2015 @ 10:23:45.816\\",\\"148.47.216.255\\",jpg +\\"Sep 20, 2015 @ 10:23:36.052\\",\\"51.105.100.214\\",jpg +\\"Sep 20, 2015 @ 10:23:34.323\\",\\"41.210.252.157\\",gif +\\"Sep 20, 2015 @ 10:23:27.213\\",\\"248.163.75.193\\",png +\\"Sep 20, 2015 @ 10:23:14.866\\",\\"48.43.210.167\\",png +\\"Sep 20, 2015 @ 10:23:10.578\\",\\"33.95.78.209\\",css +\\"Sep 20, 2015 @ 10:23:07.001\\",\\"96.40.73.208\\",css +\\"Sep 20, 2015 @ 10:23:02.876\\",\\"174.32.230.63\\",jpg +\\"Sep 20, 2015 @ 10:23:00.019\\",\\"140.233.207.177\\",jpg +\\"Sep 20, 2015 @ 10:22:47.447\\",\\"37.127.124.65\\",jpg +\\"Sep 20, 2015 @ 10:22:45.803\\",\\"130.171.208.139\\",png +\\"Sep 20, 2015 @ 10:22:45.590\\",\\"39.250.210.253\\",jpg +\\"Sep 20, 2015 @ 10:22:43.997\\",\\"248.239.221.43\\",css +\\"Sep 20, 2015 @ 10:22:36.107\\",\\"232.64.207.109\\",gif +\\"Sep 20, 2015 @ 10:22:30.527\\",\\"24.186.122.118\\",jpg +\\"Sep 20, 2015 @ 10:22:25.697\\",\\"23.3.174.206\\",jpg +\\"Sep 20, 2015 @ 10:22:08.272\\",\\"185.170.80.142\\",php +\\"Sep 20, 2015 @ 10:21:40.822\\",\\"202.22.74.232\\",png +\\"Sep 20, 2015 @ 10:21:36.210\\",\\"39.227.27.167\\",jpg +\\"Sep 20, 2015 @ 10:21:19.154\\",\\"140.233.207.177\\",jpg +\\"Sep 20, 2015 @ 10:21:09.852\\",\\"22.151.97.227\\",jpg +\\"Sep 20, 2015 @ 10:21:06.079\\",\\"157.39.25.197\\",css +\\"Sep 20, 2015 @ 10:21:01.357\\",\\"37.127.124.65\\",jpg +\\"Sep 20, 2015 @ 10:20:56.519\\",\\"23.184.94.58\\",jpg +\\"Sep 20, 2015 @ 10:20:40.189\\",\\"80.83.92.252\\",jpg +\\"Sep 20, 2015 @ 10:20:27.012\\",\\"66.194.157.171\\",png +\\"Sep 20, 2015 @ 10:20:24.450\\",\\"15.191.218.38\\",jpg +" +`; + +exports[`Reporting APIs CSV generation from SearchSource non-timebased Handle _id and _index columns 1`] = ` +"date,message,\\"_id\\",\\"_index\\" +\\"Jan 1, 2015 @ 12:10:30.123456789\\",\\"Hello 2\\",2,nanos +\\"Jan 1, 2015 @ 12:10:30.000000000\\",\\"Hello 1\\",1,nanos +" +`; + +exports[`Reporting APIs CSV generation from SearchSource non-timebased With filters and non-timebased data 1`] = ` +"name,power +\\"Jonelle-Jane Marth\\",1 +\\"Suzie-May Rishel\\",1 +\\"Suzie-May Rishel\\",2 +\\"Rosana Casto\\",2 +\\"Stephen Cortez\\",4 +\\"Jonelle-Jane Marth\\",6 +\\"Jonelle-Jane Marth\\",7 +\\"Florinda Alejandro\\",10 +\\"Jonelle-Jane Marth\\",14 +\\"Suzie-May Rishel\\",19 +\\"Suzie-May Rishel\\",20 +\\"Florinda Alejandro\\",22 +" +`; + +exports[`Reporting APIs CSV generation from SearchSource validation Searches "huge" data, stops at Max Size Reached 1`] = ` +"\\"_id\\",date,name,gender,value,year,\\"years_ago\\",\\"date_informal\\" +\\"1984-Fiff-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fiff,F,5,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Filal-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Filal,F,15,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fillie-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fillie,F,135,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fillyjack-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fillyjack,F,6,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fing-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fing,F,6,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Firan-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Firan,F,9,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fishara-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fishara,F,5,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fishop-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fishop,F,9,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fj-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fj,F,12,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fjorn-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fjorn,F,22,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Flaine-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Flaine,F,234,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Flake-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Flake,F,420,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Flas-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Flas,F,38,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fo-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fo,F,45,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Foaz-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Foaz,F,13,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fob-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fob,F,170,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fobby-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fobby,F,\\"2,791\\",1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fodie-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fodie,F,12,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Foe-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Foe,F,15,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fomani-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fomani,F,7,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Ferley-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Ferley,F,6,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fernardino-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fernardino,F,30,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fernardo-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fernardo,F,115,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fernerd-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fernerd,F,6,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fernice-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fernice,F,7,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Ferry-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Ferry,F,47,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +\\"1984-Fert-F\\",\\"Jan 1, 1984 @ 00:00:00.000\\",Fert,F,140,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\" +" +`; diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/csv_job_params.ts b/x-pack/test/reporting_api_integration/reporting_and_security/csv_job_params.ts index 732e640f29a4d..b3fa9ebe46f8c 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/csv_job_params.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/csv_job_params.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import supertest from 'supertest'; -import { JOB_PARAMS_RISON } from '../fixtures'; +import { JOB_PARAMS_RISON_CSV_DEPRECATED } from '../fixtures'; import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -63,7 +63,7 @@ export default function ({ getService }: FtrProviderContext) { it('Accepts jobParams in POST payload', async () => { const { status: resStatus, text: resText } = (await generateAPI.getCsvFromParamsInPayload({ - jobParams: JOB_PARAMS_RISON, + jobParams: JOB_PARAMS_RISON_CSV_DEPRECATED, })) as supertest.Response; expect(resText).to.match(/"jobtype":"csv"/); expect(resStatus).to.eql(200); @@ -71,7 +71,7 @@ export default function ({ getService }: FtrProviderContext) { it('Accepts jobParams in query string', async () => { const { status: resStatus, text: resText } = (await generateAPI.getCsvFromParamsInQueryString( - JOB_PARAMS_RISON + JOB_PARAMS_RISON_CSV_DEPRECATED )) as supertest.Response; expect(resText).to.match(/"jobtype":"csv"/); expect(resStatus).to.eql(200); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/csv_saved_search.ts b/x-pack/test/reporting_api_integration/reporting_and_security/csv_saved_search.ts deleted file mode 100644 index bb70924f67b75..0000000000000 --- a/x-pack/test/reporting_api_integration/reporting_and_security/csv_saved_search.ts +++ /dev/null @@ -1,411 +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 expect from '@kbn/expect'; -import supertest from 'supertest'; -import * as fixtures from '../fixtures'; -import { FtrProviderContext } from '../ftr_provider_context'; - -interface GenerateOpts { - timerange?: { - timezone: string; - min?: number | string | Date; - max?: number | string | Date; - }; - state: any; -} - -// eslint-disable-next-line import/no-default-export -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const supertestSvc = getService('supertest'); - const reportingAPI = getService('reportingAPI'); - - const generateAPI = { - getCsvFromSavedSearch: async ( - id: string, - { timerange, state }: GenerateOpts, - isImmediate = true - ) => { - 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..7f2a13b7cfe45 --- /dev/null +++ b/x-pack/test/reporting_api_integration/reporting_and_security/csv_searchsource_immediate.ts @@ -0,0 +1,344 @@ +/* + * 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(); + }); + + 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, explicit UTC format', async () => { + const res = (await generateAPI.getCSVFromSearchSource( + getMockJobParams({ + browserTimezone: 'UTC', + 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, 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, custom timezone (Phoenix)', 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 "huge" data, 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.getCSVFromSearchSource( + getMockJobParams({ + searchSource: { + query: { query: '', language: 'lucene' }, + version: true, + sort: [{ date: 'desc' }], + index: '89655130-5013-11e9-bce7-4dabcb8bef24', + fields: [ + '_id', + 'date', + 'name', + 'gender', + 'value', + 'year', + 'years_ago', + 'date_informal', + ], + filter: [ + { + meta: { index: '89655130-5013-11e9-bce7-4dabcb8bef24', params: {} }, + range: { + date: { + gte: '1960-01-01T10:00:00Z', + lte: '1999-01-01T10:00:00Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }) + )) as supertest.Response; + + expect(resStatus).to.eql(200); + expect(resType).to.eql('text/csv'); + expectSnapshot(resText).toMatch(); + + await esArchiver.unload('reporting/hugedata'); + }); + }); + }); +} 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 2981ff81d66eb..b4e05e37d3fda 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 @@ -12,7 +12,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('Reporting APIs', function () { this.tags('ciGroup2'); 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')); diff --git a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis.ts b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis.ts index 99832e3f4b1c2..b8ef01f316381 100644 --- a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis.ts +++ b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { forOwn } from 'lodash'; -import { JOB_PARAMS_RISON } from '../fixtures'; +import { JOB_PARAMS_RISON_CSV_DEPRECATED } from '../fixtures'; import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -35,7 +35,7 @@ export default function ({ getService }: FtrProviderContext) { const { status: resStatus, text: resText } = await supertestNoAuth .post(`/api/reporting/generate/csv`) .set('kbn-xsrf', 'xxx') - .send({ jobParams: JOB_PARAMS_RISON }); + .send({ jobParams: JOB_PARAMS_RISON_CSV_DEPRECATED }); expect(resStatus).to.be(200); @@ -67,7 +67,7 @@ export default function ({ getService }: FtrProviderContext) { const { status: resStatus, text: resText } = await supertestNoAuth .post(`/api/reporting/generate/csv`) .set('kbn-xsrf', 'xxx') - .send({ jobParams: JOB_PARAMS_RISON }); + .send({ jobParams: JOB_PARAMS_RISON_CSV_DEPRECATED }); expect(resStatus).to.be(200); @@ -97,7 +97,7 @@ export default function ({ getService }: FtrProviderContext) { const { status: resStatus, text: resText } = await supertestNoAuth .post(`/api/reporting/generate/csv`) .set('kbn-xsrf', 'xxx') - .send({ jobParams: JOB_PARAMS_RISON }); + .send({ jobParams: JOB_PARAMS_RISON_CSV_DEPRECATED }); expect(resStatus).to.be(200);