diff --git a/x-pack/plugins/reporting/common/constants.ts b/x-pack/plugins/reporting/common/constants.ts index 79449e5d229b8..07a239494da23 100644 --- a/x-pack/plugins/reporting/common/constants.ts +++ b/x-pack/plugins/reporting/common/constants.ts @@ -54,6 +54,8 @@ export const KBN_SCREENSHOT_HEADER_BLOCK_LIST = [ 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 PDF_JOB_TYPE = 'printable_pdf'; export const PNG_JOB_TYPE = 'PNG'; diff --git a/x-pack/plugins/reporting/kibana.json b/x-pack/plugins/reporting/kibana.json index 33141eec46299..93f914a78fe10 100644 --- a/x-pack/plugins/reporting/kibana.json +++ b/x-pack/plugins/reporting/kibana.json @@ -4,6 +4,7 @@ "kibanaVersion": "kibana", "optionalPlugins": [ "security", + "spaces", "usageCollection" ], "configPath": ["xpack", "reporting"], diff --git a/x-pack/plugins/reporting/server/core.ts b/x-pack/plugins/reporting/server/core.ts index 3657d323b3edf..c7a1c79748b5b 100644 --- a/x-pack/plugins/reporting/server/core.ts +++ b/x-pack/plugins/reporting/server/core.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import Hapi from 'hapi'; import * as Rx from 'rxjs'; import { first, map, take } from 'rxjs/operators'; import { @@ -14,24 +15,27 @@ import { SavedObjectsClientContract, SavedObjectsServiceStart, UiSettingsServiceStart, -} from 'src/core/server'; +} from '../../../../src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { SecurityPluginSetup } from '../../security/server'; +import { DEFAULT_SPACE_ID } from '../../spaces/common/constants'; +import { SpacesPluginSetup } from '../../spaces/server'; import { ReportingConfig } from './'; import { HeadlessChromiumDriverFactory } from './browsers/chromium/driver_factory'; -import { checkLicense, getExportTypesRegistry } from './lib'; +import { checkLicense, getExportTypesRegistry, LevelLogger } from './lib'; import { ESQueueInstance } from './lib/create_queue'; import { screenshotsObservableFactory, ScreenshotsObservableFn } from './lib/screenshots'; import { ReportingStore } from './lib/store'; export interface ReportingInternalSetup { + basePath: Pick; + router: IRouter; features: FeaturesPluginSetup; elasticsearch: ElasticsearchServiceSetup; licensing: LicensingPluginSetup; - basePath: BasePath['get']; - router: IRouter; security?: SecurityPluginSetup; + spaces?: SpacesPluginSetup; } export interface ReportingInternalStart { @@ -50,7 +54,7 @@ export class ReportingCore { private exportTypesRegistry = getExportTypesRegistry(); private config?: ReportingConfig; - constructor() {} + constructor(private logger: LevelLogger) {} /* * Register setupDeps @@ -180,9 +184,9 @@ export class ReportingCore { return this.getPluginSetupDeps().elasticsearch; } - public async getSavedObjectsClient(fakeRequest: KibanaRequest) { + private async getSavedObjectsClient(request: KibanaRequest) { const { savedObjects } = await this.getPluginStartDeps(); - return savedObjects.getScopedClient(fakeRequest) as SavedObjectsClientContract; + return savedObjects.getScopedClient(request) as SavedObjectsClientContract; } public async getUiSettingsServiceFactory(savedObjectsClient: SavedObjectsClientContract) { @@ -190,4 +194,48 @@ export class ReportingCore { const scopedUiSettingsService = uiSettingsService.asScopedToClient(savedObjectsClient); return scopedUiSettingsService; } + + public getSpaceId(request: KibanaRequest): string | undefined { + const spacesService = this.getPluginSetupDeps().spaces?.spacesService; + if (spacesService) { + const spaceId = spacesService?.getSpaceId(request); + + if (spaceId !== DEFAULT_SPACE_ID) { + this.logger.info(`Request uses Space ID: ` + spaceId); + return spaceId; + } else { + this.logger.info(`Request uses default Space`); + } + } + } + + public getFakeRequest(baseRequest: object, spaceId?: string) { + const fakeRequest = KibanaRequest.from({ + path: '/', + route: { settings: {} }, + url: { href: '/' }, + raw: { req: { url: '/' } }, + ...baseRequest, + } as Hapi.Request); + + const spacesService = this.getPluginSetupDeps().spaces?.spacesService; + if (spacesService) { + if (spaceId && spaceId !== DEFAULT_SPACE_ID) { + this.logger.info(`Generating request for space: ` + spaceId); + this.getPluginSetupDeps().basePath.set(fakeRequest, `/s/${spaceId}`); + } + } + + return fakeRequest; + } + + public async getUiSettingsClient(request: KibanaRequest) { + const spacesService = this.getPluginSetupDeps().spaces?.spacesService; + const spaceId = this.getSpaceId(request); + if (spacesService && spaceId) { + this.logger.info(`Creating UI Settings Client for space: ${spaceId}`); + } + const savedObjectsClient = await this.getSavedObjectsClient(request); + return await this.getUiSettingsServiceFactory(savedObjectsClient); + } } diff --git a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts index 5ab029bfd9f29..4f0088467dd68 100644 --- a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts +++ b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts @@ -11,7 +11,6 @@ interface HasEncryptedHeaders { headers?: string; } -// TODO merge functionality with CSV execute job export const decryptJobHeaders = async < JobParamsType, TaskPayloadType extends HasEncryptedHeaders diff --git a/x-pack/plugins/reporting/server/export_types/common/get_absolute_url.test.ts b/x-pack/plugins/reporting/server/export_types/common/get_absolute_url.test.ts index cb792fbd6ae03..0b06beabfd24d 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_absolute_url.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_absolute_url.test.ts @@ -7,7 +7,7 @@ import { getAbsoluteUrlFactory } from './get_absolute_url'; const defaultOptions = { - defaultBasePath: 'sbp', + basePath: 'sbp', protocol: 'http:', hostname: 'localhost', port: 5601, @@ -64,8 +64,8 @@ test(`uses the provided hash with queryString`, () => { }); test(`uses the provided basePath`, () => { - const getAbsoluteUrl = getAbsoluteUrlFactory(defaultOptions); - const absoluteUrl = getAbsoluteUrl({ basePath: '/s/marketing' }); + const getAbsoluteUrl = getAbsoluteUrlFactory({ ...defaultOptions, basePath: '/s/marketing' }); + const absoluteUrl = getAbsoluteUrl(); expect(absoluteUrl).toBe(`http://localhost:5601/s/marketing/app/kibana`); }); diff --git a/x-pack/plugins/reporting/server/export_types/common/get_absolute_url.ts b/x-pack/plugins/reporting/server/export_types/common/get_absolute_url.ts index f996a49e5eadc..72305f47e7189 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_absolute_url.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_absolute_url.ts @@ -7,7 +7,7 @@ import url from 'url'; interface AbsoluteURLFactoryOptions { - defaultBasePath: string; + basePath: string; protocol: string; hostname: string; port: string | number; @@ -17,14 +17,9 @@ export const getAbsoluteUrlFactory = ({ protocol, hostname, port, - defaultBasePath, + basePath, }: AbsoluteURLFactoryOptions) => { - return function getAbsoluteUrl({ - basePath = defaultBasePath, - hash = '', - path = '/app/kibana', - search = '', - } = {}) { + return function getAbsoluteUrl({ hash = '', path = '/app/kibana', search = '' } = {}) { return url.format({ protocol, hostname, diff --git a/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.test.ts b/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.test.ts index a0d8ff0852544..794ea9febb5c0 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.test.ts @@ -5,24 +5,16 @@ */ import { ReportingConfig } from '../../'; -import { ReportingCore } from '../../core'; -import { - createMockConfig, - createMockConfigSchema, - createMockReportingCore, -} from '../../test_helpers'; +import { createMockConfig, createMockConfigSchema } from '../../test_helpers'; import { BasePayload } from '../../types'; -import { TaskPayloadPDF } from '../printable_pdf/types'; -import { getConditionalHeaders, getCustomLogo } from './'; +import { getConditionalHeaders } from './'; let mockConfig: ReportingConfig; -let mockReportingPlugin: ReportingCore; beforeEach(async () => { const reportingConfig = { kibanaServer: { hostname: 'custom-hostname' } }; const mockSchema = createMockConfigSchema(reportingConfig); mockConfig = createMockConfig(mockSchema); - mockReportingPlugin = await createMockReportingCore(mockConfig); }); describe('conditions', () => { @@ -32,7 +24,7 @@ describe('conditions', () => { baz: 'quix', }; - const conditionalHeaders = await getConditionalHeaders({ + const conditionalHeaders = getConditionalHeaders({ job: {} as BasePayload, filteredHeaders: permittedHeaders, config: mockConfig, @@ -51,83 +43,6 @@ describe('conditions', () => { }); }); -test('uses basePath from job when creating saved object service', async () => { - const mockGetSavedObjectsClient = jest.fn(); - mockReportingPlugin.getSavedObjectsClient = mockGetSavedObjectsClient; - - const permittedHeaders = { - foo: 'bar', - baz: 'quix', - }; - const conditionalHeaders = await getConditionalHeaders({ - job: {} as BasePayload, - filteredHeaders: permittedHeaders, - config: mockConfig, - }); - const jobBasePath = '/sbp/s/marketing'; - await getCustomLogo({ - reporting: mockReportingPlugin, - job: { basePath: jobBasePath } as TaskPayloadPDF, - conditionalHeaders, - config: mockConfig, - }); - - const getBasePath = mockGetSavedObjectsClient.mock.calls[0][0].getBasePath; - expect(getBasePath()).toBe(jobBasePath); -}); - -test(`uses basePath from server if job doesn't have a basePath when creating saved object service`, async () => { - const mockGetSavedObjectsClient = jest.fn(); - mockReportingPlugin.getSavedObjectsClient = mockGetSavedObjectsClient; - - const reportingConfig = { kibanaServer: { hostname: 'localhost' }, server: { basePath: '/sbp' } }; - const mockSchema = createMockConfigSchema(reportingConfig); - mockConfig = createMockConfig(mockSchema); - - const permittedHeaders = { - foo: 'bar', - baz: 'quix', - }; - const conditionalHeaders = await getConditionalHeaders({ - job: {} as BasePayload, - filteredHeaders: permittedHeaders, - config: mockConfig, - }); - - await getCustomLogo({ - reporting: mockReportingPlugin, - job: {} as TaskPayloadPDF, - conditionalHeaders, - config: mockConfig, - }); - - const getBasePath = mockGetSavedObjectsClient.mock.calls[0][0].getBasePath; - expect(getBasePath()).toBe(`/sbp`); - expect(mockGetSavedObjectsClient.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "getBasePath": [Function], - "headers": Object { - "baz": "quix", - "foo": "bar", - }, - "path": "/", - "raw": Object { - "req": Object { - "url": "/", - }, - }, - "route": Object { - "settings": Object {}, - }, - "url": Object { - "href": "/", - }, - }, - ] - `); -}); - describe('config formatting', () => { test(`lowercases kibanaServer.hostname`, async () => { const reportingConfig = { kibanaServer: { hostname: 'GREAT-HOSTNAME' } }; diff --git a/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.ts b/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.ts deleted file mode 100644 index ee61d76c8a933..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.ts +++ /dev/null @@ -1,40 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ReportingConfig, ReportingCore } from '../../'; -import { UI_SETTINGS_CUSTOM_PDF_LOGO } from '../../../common/constants'; -import { ConditionalHeaders } from '../../types'; -import { TaskPayloadPDF } from '../printable_pdf/types'; // Logo is PDF only - -export const getCustomLogo = async ({ - reporting, - config, - job, - conditionalHeaders, -}: { - reporting: ReportingCore; - config: ReportingConfig; - job: TaskPayloadPDF; - conditionalHeaders: ConditionalHeaders; -}) => { - const serverBasePath: string = config.kbnConfig.get('server', 'basePath'); - const fakeRequest: any = { - headers: conditionalHeaders.headers, - // This is used by the spaces SavedObjectClientWrapper to determine the existing space. - // We use the basePath from the saved job, which we'll have post spaces being implemented; - // or we use the server base path, which uses the default space - getBasePath: () => job.basePath || serverBasePath, - path: '/', - route: { settings: {} }, - url: { href: '/' }, - raw: { req: { url: '/' } }, - }; - - const savedObjectsClient = await reporting.getSavedObjectsClient(fakeRequest); - const uiSettings = await reporting.getUiSettingsServiceFactory(savedObjectsClient); - const logo: string = await uiSettings.get(UI_SETTINGS_CUSTOM_PDF_LOGO); - return { conditionalHeaders, logo }; -}; diff --git a/x-pack/plugins/reporting/server/export_types/common/get_full_urls.ts b/x-pack/plugins/reporting/server/export_types/common/get_full_urls.ts index d6f472e18bc7b..f4e3a7b723c08 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_full_urls.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_full_urls.ts @@ -36,12 +36,7 @@ export function getFullUrls({ config.get('kibanaServer', 'hostname'), config.get('kibanaServer', 'port'), ] as string[]; - const getAbsoluteUrl = getAbsoluteUrlFactory({ - defaultBasePath: basePath, - protocol, - hostname, - port, - }); + const getAbsoluteUrl = getAbsoluteUrlFactory({ basePath, protocol, hostname, port }); // PDF and PNG job params put in the url differently let relativeUrls: string[] = []; @@ -61,7 +56,6 @@ export function getFullUrls({ const urls = relativeUrls.map((relativeUrl) => { const parsedRelative: UrlWithStringQuery = urlParse(relativeUrl); const jobUrl = getAbsoluteUrl({ - basePath: job.basePath, path: parsedRelative.pathname, hash: parsedRelative.hash, search: parsedRelative.search, 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 e0d03eb4864ca..80eaa52d0951b 100644 --- a/x-pack/plugins/reporting/server/export_types/common/index.ts +++ b/x-pack/plugins/reporting/server/export_types/common/index.ts @@ -6,7 +6,6 @@ export { decryptJobHeaders } from './decrypt_job_headers'; export { getConditionalHeaders } from './get_conditional_headers'; -export { getCustomLogo } from './get_custom_logo'; export { getFullUrls } from './get_full_urls'; export { omitBlockedHeaders } from './omit_blocked_headers'; export { validateUrls } from './validate_urls'; diff --git a/x-pack/plugins/reporting/server/export_types/csv/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv/create_job.ts index be18bd7fff361..d768dc6f8e084 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/create_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/create_job.ts @@ -25,6 +25,7 @@ export const createJobFnFactory: CreateJobFnFactory { - const decryptHeaders = async () => { - try { - if (typeof headers !== 'string') { - throw new Error( - i18n.translate( - 'xpack.reporting.exportTypes.csv.executeJob.missingJobHeadersErrorMessage', - { - defaultMessage: 'Job headers are missing', - } - ) - ); - } - return await crypto.decrypt(headers); - } catch (err) { - logger.error(err); - throw new Error( - i18n.translate( - 'xpack.reporting.exportTypes.csv.executeJob.failedToDecryptReportJobDataErrorMessage', - { - defaultMessage: 'Failed to decrypt report job data. Please ensure that {encryptionKey} is set and re-generate this report. {err}', - values: { encryptionKey: 'xpack.reporting.encryptionKey', err: err.toString() }, - } - ) - ); // prettier-ignore - } - }; - - return KibanaRequest.from({ - headers: await decryptHeaders(), - // This is used by the spaces SavedObjectClientWrapper to determine the existing space. - // We use the basePath from the saved job, which we'll have post spaces being implemented; - // or we use the server base path, which uses the default space - path: '/', - route: { settings: {} }, - url: { href: '/' }, - app: {}, - raw: { req: { url: '/' } }, - } as Hapi.Request); -}; - export const runTaskFnFactory: RunTaskFnFactory> = function executeJobFactoryFn(reporting, parentLogger) { const config = reporting.getConfig(); - const crypto = cryptoFactory(config.get('encryptionKey')); const logger = parentLogger.clone([CSV_JOB_TYPE, 'execute-job']); return async function runTask(jobId, job, cancellationToken) { @@ -67,16 +21,15 @@ export const runTaskFnFactory: RunTaskFnFactory callAsCurrentUser(endpoint, clientParams, options); - const savedObjectsClient = await reporting.getSavedObjectsClient(fakeRequest); - const uiSettingsClient = await reporting.getUiSettingsServiceFactory(savedObjectsClient); - const { content, maxSizeReached, size, csvContainsFormulas, warnings } = await generateCsv( job, config, diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/get_ui_settings.ts b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/get_ui_settings.ts index 915d5010a4885..1f3354debc305 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/get_ui_settings.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/get_ui_settings.ts @@ -6,6 +6,10 @@ import { i18n } from '@kbn/i18n'; import { IUiSettingsClient } from 'kibana/server'; +import { + UI_SETTINGS_CSV_QUOTE_VALUES, + UI_SETTINGS_CSV_SEPARATOR, +} from '../../../../common/constants'; import { ReportingConfig } from '../../../'; import { LevelLogger } from '../../../lib'; @@ -38,8 +42,8 @@ export const getUiSettings = async ( // Separator, QuoteValues const [separator, quoteValues] = await Promise.all([ - client.get('csv:separator'), - client.get('csv:quoteValues'), + client.get(UI_SETTINGS_CSV_SEPARATOR), + client.get(UI_SETTINGS_CSV_QUOTE_VALUES), ]); return { 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 e383f21143149..6ecddae12a988 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 @@ -37,9 +37,7 @@ interface SearchRequest { } export interface GenerateCsvParams { - jobParams: { - browserTimezone: string; - }; + browserTimezone: string; searchRequest: SearchRequest; indexPatternSavedObject: IndexPatternSavedObject; fields: string[]; @@ -57,12 +55,7 @@ export function createGenerateCsv(logger: LevelLogger) { callEndpoint: EndpointCaller, cancellationToken: CancellationToken ): Promise { - const settings = await getUiSettings( - job.jobParams?.browserTimezone, - uiSettingsClient, - config, - logger - ); + 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 : ''; const builder = new MaxSizeStringBuilder(byteSizeValueToNumber(settings.maxSizeBytes), bom); diff --git a/x-pack/plugins/reporting/server/export_types/csv/lib/get_request.ts b/x-pack/plugins/reporting/server/export_types/csv/lib/get_request.ts deleted file mode 100644 index 09e6becc2baec..0000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv/lib/get_request.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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Crypto } from '@elastic/node-crypto'; -import { i18n } from '@kbn/i18n'; -import Hapi from 'hapi'; -import { KibanaRequest } from '../../../../../../../src/core/server'; -import { LevelLogger } from '../../../lib'; - -export const getRequest = async ( - headers: string | undefined, - crypto: Crypto, - logger: LevelLogger -) => { - const decryptHeaders = async () => { - try { - if (typeof headers !== 'string') { - throw new Error( - i18n.translate( - 'xpack.reporting.exportTypes.csv.executeJob.missingJobHeadersErrorMessage', - { - defaultMessage: 'Job headers are missing', - } - ) - ); - } - return await crypto.decrypt(headers); - } catch (err) { - logger.error(err); - throw new Error( - i18n.translate( - 'xpack.reporting.exportTypes.csv.executeJob.failedToDecryptReportJobDataErrorMessage', - { - defaultMessage: 'Failed to decrypt report job data. Please ensure that {encryptionKey} is set and re-generate this report. {err}', - values: { encryptionKey: 'xpack.reporting.encryptionKey', err: err.toString() }, - } - ) - ); // prettier-ignore - } - }; - - return KibanaRequest.from({ - headers: await decryptHeaders(), - // This is used by the spaces SavedObjectClientWrapper to determine the existing space. - // We use the basePath from the saved job, which we'll have post spaces being implemented; - // or we use the server base path, which uses the default space - path: '/', - route: { settings: {} }, - url: { href: '/' }, - raw: { req: { url: '/' } }, - } as Hapi.Request); -}; 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 f420d8b033170..214157db51cb7 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 @@ -29,6 +29,7 @@ export interface IndexPatternSavedObject { } export interface JobParamsDiscoverCsv extends BaseParams { + browserTimezone: string; indexPatternId: string; title: string; searchRequest: SearchRequest; @@ -38,6 +39,7 @@ export interface JobParamsDiscoverCsv extends BaseParams { } export interface TaskPayloadCSV extends BasePayload { + browserTimezone: string; basePath: string; searchRequest: any; fields: any; 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 index 3a5deda176b8c..0ca80581fcc83 100644 --- 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 @@ -48,9 +48,8 @@ export const runTaskFnFactory: RunTaskFnFactory = function e jobLogger.debug(`Execute job generating [${visType}] csv`); const savedObjectsClient = context.core.savedObjects.client; - - const uiConfig = await reporting.getUiSettingsServiceFactory(savedObjectsClient); - const job = await getGenerateCsvParams(jobParams, panel, savedObjectsClient, uiConfig); + const uiSettingsClient = await reporting.getUiSettingsServiceFactory(savedObjectsClient); + const job = await getGenerateCsvParams(jobParams, panel, savedObjectsClient, uiSettingsClient); const elasticsearch = reporting.getElasticsearchService(); const { callAsCurrentUser } = elasticsearch.legacy.client.asScoped(req); @@ -58,7 +57,7 @@ export const runTaskFnFactory: RunTaskFnFactory = function e const { content, maxSizeReached, size, csvContainsFormulas, warnings } = await generateCsv( job, config, - uiConfig, + uiSettingsClient, callAsCurrentUser, new CancellationToken() // can not be cancelled ); 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 index 9646d7eecd5b5..b387245406fbb 100644 --- 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 @@ -45,6 +45,7 @@ describe('Get CSV Job', () => { ); expect(result).toMatchInlineSnapshot(` Object { + "browserTimezone": "PST", "conflictedTypesFields": Array [], "fields": Array [], "indexPatternSavedObject": Object { @@ -57,9 +58,6 @@ describe('Get CSV Job', () => { "timeFieldName": null, "title": null, }, - "jobParams": Object { - "browserTimezone": "PST", - }, "metaFields": Array [], "searchRequest": Object { "body": Object { @@ -99,6 +97,7 @@ describe('Get CSV Job', () => { ); expect(result).toMatchInlineSnapshot(` Object { + "browserTimezone": "PST", "conflictedTypesFields": Array [], "fields": Array [], "indexPatternSavedObject": Object { @@ -111,9 +110,6 @@ describe('Get CSV Job', () => { "timeFieldName": null, "title": null, }, - "jobParams": Object { - "browserTimezone": "PST", - }, "metaFields": Array [], "searchRequest": Object { "body": Object { @@ -156,6 +152,7 @@ describe('Get CSV Job', () => { ); expect(result).toMatchInlineSnapshot(` Object { + "browserTimezone": "Africa/Timbuktu", "conflictedTypesFields": Array [], "fields": Array [], "indexPatternSavedObject": Object { @@ -168,9 +165,6 @@ describe('Get CSV Job', () => { "timeFieldName": null, "title": null, }, - "jobParams": Object { - "browserTimezone": "Africa/Timbuktu", - }, "metaFields": Array [], "searchRequest": Object { "body": Object { @@ -212,6 +206,7 @@ describe('Get CSV Job', () => { ); expect(result).toMatchInlineSnapshot(` Object { + "browserTimezone": "Africa/Timbuktu", "conflictedTypesFields": Array [], "fields": Array [ "@test_time", @@ -226,9 +221,6 @@ describe('Get CSV Job', () => { "timeFieldName": "@test_time", "title": "test search", }, - "jobParams": Object { - "browserTimezone": "Africa/Timbuktu", - }, "metaFields": Array [], "searchRequest": Object { "body": Object { @@ -286,6 +278,7 @@ describe('Get CSV Job', () => { ); expect(result).toMatchInlineSnapshot(` Object { + "browserTimezone": "Africa/Timbuktu", "conflictedTypesFields": Array [], "fields": Array [ "@test_time", @@ -300,9 +293,6 @@ describe('Get CSV Job', () => { "timeFieldName": "@test_time", "title": "test search", }, - "jobParams": Object { - "browserTimezone": "Africa/Timbuktu", - }, "metaFields": Array [], "searchRequest": Object { "body": Object { 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 index 0fc29c5b208d9..26a4b17aaf71f 100644 --- 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 @@ -12,6 +12,8 @@ import { IIndexPattern, Query, } from '../../../../../../../src/plugins/data/server'; +import { TimeRangeParams } from '../../../types'; +import { GenerateCsvParams } from '../../csv/generate_csv'; import { DocValueFields, IndexPatternField, @@ -23,7 +25,6 @@ import { } from '../types'; import { getDataSource } from './get_data_source'; import { getFilters } from './get_filters'; -import { GenerateCsvParams } from '../../csv/generate_csv'; export const getEsQueryConfig = async (config: IUiSettingsClient) => { const configs = await Promise.all([ @@ -49,7 +50,7 @@ export const getGenerateCsvParams = async ( savedObjectsClient: SavedObjectsClientContract, uiConfig: IUiSettingsClient ): Promise => { - let timerange; + let timerange: TimeRangeParams; if (jobParams.post?.timerange) { timerange = jobParams.post?.timerange; } else { @@ -136,7 +137,7 @@ export const getGenerateCsvParams = async ( }; return { - jobParams: { browserTimezone: timerange.timezone }, + browserTimezone: timerange.timezone, indexPatternSavedObject, searchRequest, fields: includes, diff --git a/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts index 173a67ad18edf..3727b2ec7b432 100644 --- a/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts @@ -25,13 +25,13 @@ export const createJobFnFactory: CreateJobFnFactory { - basePath?: string; browserTimezone: string; forceNow?: string; layout: LayoutParams; diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts index 96e634337e6a9..cae706a479b7f 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts @@ -25,10 +25,10 @@ export const createJobFnFactory: CreateJobFnFactory>; @@ -42,9 +42,7 @@ export const runTaskFnFactory: QueuedPdfExecutorFactory = function executeJobFac mergeMap(() => decryptJobHeaders({ encryptionKey, job, logger })), map((decryptedHeaders) => omitBlockedHeaders({ job, decryptedHeaders })), map((filteredHeaders) => getConditionalHeaders({ config, job, filteredHeaders })), - mergeMap((conditionalHeaders) => - getCustomLogo({ reporting, config, job, conditionalHeaders }) - ), + mergeMap((conditionalHeaders) => getCustomLogo(reporting, conditionalHeaders, job.spaceId)), mergeMap(({ logo, conditionalHeaders }) => { const urls = getFullUrls({ config, job }); diff --git a/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.test.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.test.ts similarity index 74% rename from x-pack/plugins/reporting/server/export_types/common/get_custom_logo.test.ts rename to x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.test.ts index ec4e54632eef5..8fa8fa5cbe3cb 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.test.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.test.ts @@ -4,14 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ReportingConfig, ReportingCore } from '../../'; +import { ReportingConfig, ReportingCore } from '../../../'; import { createMockConfig, createMockConfigSchema, createMockReportingCore, -} from '../../test_helpers'; -import { TaskPayloadPDF } from '../printable_pdf/types'; -import { getConditionalHeaders, getCustomLogo } from './'; +} from '../../../test_helpers'; +import { getConditionalHeaders } from '../../common'; +import { TaskPayloadPDF } from '../types'; +import { getCustomLogo } from './get_custom_logo'; let mockConfig: ReportingConfig; let mockReportingPlugin: ReportingCore; @@ -38,18 +39,13 @@ test(`gets logo from uiSettings`, async () => { get: mockGet, }); - const conditionalHeaders = await getConditionalHeaders({ + const conditionalHeaders = getConditionalHeaders({ job: {} as TaskPayloadPDF, filteredHeaders: permittedHeaders, config: mockConfig, }); - const { logo } = await getCustomLogo({ - reporting: mockReportingPlugin, - config: mockConfig, - job: {} as TaskPayloadPDF, - conditionalHeaders, - }); + const { logo } = await getCustomLogo(mockReportingPlugin, conditionalHeaders); expect(mockGet).toBeCalledWith('xpackReporting:customPdfLogo'); expect(logo).toBe('purple pony'); diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.ts new file mode 100644 index 0000000000000..35ab7001ecbe4 --- /dev/null +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ReportingCore } from '../../../'; +import { UI_SETTINGS_CUSTOM_PDF_LOGO } from '../../../../common/constants'; +import { ConditionalHeaders } from '../../../types'; + +export const getCustomLogo = async ( + reporting: ReportingCore, + conditionalHeaders: ConditionalHeaders, + spaceId?: string +) => { + const fakeRequest = reporting.getFakeRequest({ headers: conditionalHeaders.headers }, spaceId); + const uiSettingsClient = await reporting.getUiSettingsClient(fakeRequest); + + const logo: string = await uiSettingsClient.get(UI_SETTINGS_CUSTOM_PDF_LOGO); + + // continue the pipeline + return { conditionalHeaders, logo }; +}; diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts index 3020cbb5f28b0..7fd176e71f2d5 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts @@ -16,7 +16,6 @@ export interface JobParamsPDF extends BaseParams { // Job payload: structure of stored job data provided by create_job export interface TaskPayloadPDF extends BasePayload { - basePath?: string; browserTimezone: string; forceNow?: string; layout: LayoutParams; diff --git a/x-pack/plugins/reporting/server/lib/store/store.ts b/x-pack/plugins/reporting/server/lib/store/store.ts index 0aae8b567bcdb..03d88ca60e2c0 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.ts @@ -12,6 +12,7 @@ import { BaseParams, BaseParamsEncryptedFields, ReportingUser } from '../../type import { indexTimestamp } from './index_timestamp'; import { mapping } from './mapping'; import { Report } from './report'; + interface JobSettings { timeout: number; browser_type: string; diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index adb89abe20280..6a93a35bfcc84 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -34,7 +34,7 @@ export class ReportingPlugin constructor(context: PluginInitializerContext) { this.logger = new LevelLogger(context.logger.get()); this.initializerContext = context; - this.reportingCore = new ReportingCore(); + this.reportingCore = new ReportingCore(this.logger); } public setup(core: CoreSetup, plugins: ReportingSetupDeps) { @@ -70,11 +70,11 @@ export class ReportingPlugin }); const { elasticsearch, http } = core; - const { features, licensing, security } = plugins; + const { features, licensing, security, spaces } = plugins; const { initializerContext: initContext, reportingCore } = this; const router = http.createRouter(); - const basePath = http.basePath.get; + const basePath = http.basePath; reportingCore.pluginSetup({ features, @@ -83,6 +83,7 @@ export class ReportingPlugin basePath, router, security, + spaces, }); registerReportingUsageCollector(reportingCore, plugins); diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts index 979283f9f037c..0acf384869ded 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts @@ -35,19 +35,8 @@ export const registerDiagnoseScreenshot = (reporting: ReportingCore, logger: Log config.get('kibanaServer', 'port'), ] as string[]; - const getAbsoluteUrl = getAbsoluteUrlFactory({ - defaultBasePath: basePath, - protocol, - hostname, - port, - }); - - const hashUrl = getAbsoluteUrl({ - basePath, - path: '/', - hash: '', - search: '', - }); + const getAbsoluteUrl = getAbsoluteUrlFactory({ basePath, protocol, hostname, port }); + const hashUrl = getAbsoluteUrl({ path: '/', hash: '', search: '' }); // Hack the layout to make the base/login page work const layout = { 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 6ec35db5caec6..72772f9f7b755 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 @@ -37,18 +37,19 @@ const createMockPluginSetup = ( return { features: featuresPluginMock.createSetup(), elasticsearch: setupMock.elasticsearch || { legacy: { client: {} } }, - basePath: setupMock.basePath || '/all-about-that-basepath', + basePath: { set: jest.fn() }, router: setupMock.router, security: setupMock.security, licensing: { license$: Rx.of({ isAvailable: true, isActive: true, type: 'basic' }) } as any, }; }; +const logger = createMockLevelLogger(); + const createMockPluginStart = ( mockReportingCore: ReportingCore, startMock?: any ): ReportingInternalStart => { - const logger = createMockLevelLogger(); const store = new ReportingStore(mockReportingCore, logger); return { browserDriverFactory: startMock.browserDriverFactory, @@ -134,7 +135,7 @@ export const createMockReportingCore = async ( } config = config || {}; - const core = new ReportingCore(); + const core = new ReportingCore(logger); core.pluginSetup(setupDepsMock); core.setConfig(config); diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts index c67a95c2de754..a3c63a0fb539d 100644 --- a/x-pack/plugins/reporting/server/types.ts +++ b/x-pack/plugins/reporting/server/types.ts @@ -8,6 +8,7 @@ import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { DataPluginStart } from 'src/plugins/data/server/plugin'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { SpacesPluginSetup } from '../../spaces/server'; import { CancellationToken } from '../../../plugins/reporting/common'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; @@ -53,6 +54,7 @@ export interface BasePayload { jobParams: JobParamsType; title: string; type: string; + spaceId?: string; } export interface JobSource { @@ -95,6 +97,7 @@ export interface ReportingSetupDeps { licensing: LicensingPluginSetup; features: FeaturesPluginSetup; security?: SecurityPluginSetup; + spaces?: SpacesPluginSetup; usageCollection?: UsageCollectionSetup; } @@ -121,7 +124,6 @@ export interface BaseParams { } export interface BaseParamsEncryptedFields extends BaseParams { - basePath?: string; // for screenshot type reports headers: string; // encrypted headers } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 868fa8a7e6177..a4478087bc513 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13937,8 +13937,6 @@ "xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage": "レポートジョブデータの解読に失敗しました。{encryptionKey}が設定されていることを確認してこのレポートを再生成してください。{err}", "xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage": "ジョブヘッダーがありません", "xpack.reporting.exportTypes.csv.executeJob.dateFormateSetting": "Kibana の高度な設定「{dateFormatTimezone}」が「ブラウザー」に設定されていますあいまいさを避けるために日付は UTC 形式に変換されます。", - "xpack.reporting.exportTypes.csv.executeJob.failedToDecryptReportJobDataErrorMessage": "レポートジョブデータの解読に失敗しました{encryptionKey} が設定されていることを確認してこのレポートを再生成してください。{err}", - "xpack.reporting.exportTypes.csv.executeJob.missingJobHeadersErrorMessage": "ジョブヘッダーがありません", "xpack.reporting.exportTypes.csv.generateCsv.escapedFormulaValues": "CSVには、値がエスケープされた式が含まれる場合があります", "xpack.reporting.exportTypes.csv.hitIterator.expectedHitsErrorMessage": "次の Elasticsearch からの応答で期待される {hits}: {response}", "xpack.reporting.exportTypes.csv.hitIterator.expectedScrollIdErrorMessage": "次の Elasticsearch からの応答で期待される {scrollId}: {response}", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8bd3fcb7c3a3f..03b2f14e221f7 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13946,8 +13946,6 @@ "xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage": "无法解密报告作业数据。请确保已设置 {encryptionKey},然后重新生成此报告。{err}", "xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage": "作业标头缺失", "xpack.reporting.exportTypes.csv.executeJob.dateFormateSetting": "Kibana 高级设置“{dateFormatTimezone}”已设置为“浏览器”。日期将格式化为 UTC 以避免混淆。", - "xpack.reporting.exportTypes.csv.executeJob.failedToDecryptReportJobDataErrorMessage": "无法解密报告作业数据。请确保已设置 {encryptionKey},然后重新生成此报告。{err}", - "xpack.reporting.exportTypes.csv.executeJob.missingJobHeadersErrorMessage": "作业标头缺失", "xpack.reporting.exportTypes.csv.generateCsv.escapedFormulaValues": "CSV 可能包含值已转义的公式", "xpack.reporting.exportTypes.csv.hitIterator.expectedHitsErrorMessage": "在以下 Elasticsearch 响应中预期 {hits}:{response}", "xpack.reporting.exportTypes.csv.hitIterator.expectedScrollIdErrorMessage": "在以下 Elasticsearch 响应中预期 {scrollId}:{response}", diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce/data.json.gz b/x-pack/test/functional/es_archives/reporting/ecommerce/data.json.gz index 58ac5616651d4..7736287bc9a37 100644 Binary files a/x-pack/test/functional/es_archives/reporting/ecommerce/data.json.gz and b/x-pack/test/functional/es_archives/reporting/ecommerce/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/data.json.gz b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/data.json.gz index 06e83f8c267d6..0cec8a44dea8d 100644 Binary files a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/data.json.gz and b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/data.json.gz differ diff --git a/x-pack/test/reporting_api_integration/fixtures.ts b/x-pack/test/reporting_api_integration/fixtures.ts index f78edf9c6c04f..72d3ab9092a1d 100644 --- a/x-pack/test/reporting_api_integration/fixtures.ts +++ b/x-pack/test/reporting_api_integration/fixtures.ts @@ -262,12 +262,15 @@ format:strict_date_optional_time,gte:'2004-09-17T21:19:34.213Z',lte:'2019-09-17T t),index:'logstash-*'),title:'A Saved Search With a DATE FILTER',type:search)`; 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 @ 07:26:24.000","[""Men's Shoes"",""Men's Clothing""]",EUR,49,569743,3,"Jun 26, 2019 @ 07:26:24.000","[""Dec 15, 2016 @ 07:26:24.000"",""Dec 15, 2016 @ 07:26:24.000""]","[""ZO0403504035"",""ZO0610306103""]" -"Jun 26, 2019 @ 07:20:38.000","[""Men's Shoes"",""Women's Accessories""]",EUR,29,569736,3,"Jun 26, 2019 @ 07:20:38.000","[""Dec 15, 2016 @ 07:20:38.000"",""Dec 15, 2016 @ 07:20:38.000""]","[""ZO0517305173"",""ZO0319703197""]" -"Jun 26, 2019 @ 07:19:12.000","[""Women's Clothing"",""Women's Shoes""]",EUR,20,569734,3,"Jun 26, 2019 @ 07:19:12.000","[""Dec 15, 2016 @ 07:19:12.000"",""Dec 15, 2016 @ 07:19:12.000""]","[""ZO0348703487"",""ZO0141401414""]" -"Jun 26, 2019 @ 07:00:29.000","[""Women's Clothing""]",EUR,17,569716,3,"Jun 26, 2019 @ 07:00:29.000","[""Dec 15, 2016 @ 07:00:29.000"",""Dec 15, 2016 @ 07:00:29.000""]","[""ZO0146701467"",""ZO0212902129""]" -"Jun 26, 2019 @ 06:56:10.000","[""Women's Clothing"",""Women's Shoes""]",EUR,6,569710,3,"Jun 26, 2019 @ 06:56:10.000","[""Dec 15, 2016 @ 06:56:10.000"",""Dec 15, 2016 @ 06:56:10.000""]","[""ZO0053600536"",""ZO0239702397""]" -"Jun 26, 2019 @ 06:47:31.000","[""Men's Shoes""]",EUR,52,569699,3,"Jun 26, 2019 @ 06:47:31.000","[""Dec 15, 2016 @ 06:47:31.000"",""Dec 15, 2016 @ 06:47:31.000""]","[""ZO0398603986"",""ZO0521305213""]" -"Jun 26, 2019 @ 06:37:26.000","[""Men's Shoes""]",EUR,50,569694,3,"Jun 26, 2019 @ 06:37:26.000","[""Dec 15, 2016 @ 06:37:26.000"",""Dec 15, 2016 @ 06:37:26.000""]","[""ZO0398703987"",""ZO0687806878""]" -"Jun 26, 2019 @ 06:21:36.000","[""Men's Clothing""]",EUR,52,569679,3,"Jun 26, 2019 @ 06:21:36.000","[""Dec 15, 2016 @ 06:21:36.000"",""Dec 15, 2016 @ 06:21:36.000""]","[""ZO0433604336"",""ZO0275702757""]" +"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes""]",EUR,12,570552,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0216402164"",""ZO0666306663""]" +"Jun 26, 2019 @ 00:00:00.000","[""Men's Clothing""]",EUR,34,570520,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0618906189"",""ZO0289502895""]" +"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing""]",EUR,42,570569,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0643506435"",""ZO0646406464""]" +"Jun 26, 2019 @ 00:00:00.000","[""Women's Accessories"",""Women's Clothing""]",EUR,45,570133,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0320503205"",""ZO0049500495""]" +"Jun 26, 2019 @ 00:00:00.000","[""Men's Accessories""]",EUR,4,570161,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0606606066"",""ZO0596305963""]" +"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes"",""Women's Clothing""]",EUR,17,570200,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0025100251"",""ZO0101901019""]" +"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing"",""Women's Shoes""]",EUR,27,732050,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0101201012"",""ZO0230902309"",""ZO0325603256"",""ZO0056400564""]" +"Jun 26, 2019 @ 00:00:00.000","[""Men's Clothing"",""Men's Shoes""]",EUR,52,719675,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0448604486"",""ZO0686206862"",""ZO0395403954"",""ZO0528505285""]" +"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing"",""Women's Accessories""]",EUR,26,570396,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0495604956"",""ZO0208802088""]" +"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes"",""Women's Accessories""]",EUR,17,570037,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0321503215"",""ZO0200102001""]" +"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""]" `; 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 index 9a7d17c971dfe..ca3172807139c 100644 --- 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 @@ -38,11 +38,11 @@ export default function ({ getService }: FtrProviderContext) { }; describe('Generation from Saved Search ID', () => { - describe('Saved Search Features', () => { - after(async () => { - await reportingAPI.deleteAllReports(); - }); + 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'); @@ -350,8 +350,8 @@ export default function ({ getService }: FtrProviderContext) { searchId: 'search:6091ead0-1c6d-11ea-a100-8589bb9d7c6b', postPayload: { timerange: { - min: '2019-06-26T06:20:28Z', - max: '2019-06-26T07:27:58Z', + min: '2019-05-28T00:00:00Z', + max: '2019-06-26T00:00:00Z', timezone: 'UTC', }, state: { @@ -370,8 +370,8 @@ export default function ({ getService }: FtrProviderContext) { { range: { order_date: { - gte: '2019-06-26T06:20:28.066Z', - lte: '2019-06-26T07:27:58.573Z', + gte: '2019-05-28T00:00:00.000Z', + lte: '2019-06-26T00:00:00.000Z', format: 'strict_date_optional_time', }, }, diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts b/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts index 9eafd0c318383..3f2b2e7116206 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts @@ -12,51 +12,106 @@ import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const reportingAPI = getService('reportingAPI'); const supertest = getService('supertest'); const log = getService('log'); + const setSpaceConfig = async (spaceId: string, settings: object) => { + return await kibanaServer.request({ + path: `/s/${spaceId}/api/kibana/settings`, + method: 'POST', + body: { changes: settings }, + }); + }; + const getCompleted$ = (downloadPath: string) => { return Rx.interval(2000).pipe( tap(() => log.debug(`checking report status at ${downloadPath}...`)), switchMap(() => supertest.get(downloadPath)), filter(({ status: statusCode }) => statusCode === 200), + tap(() => log.debug(`report at ${downloadPath} is done`)), map((response) => response.text), first(), timeout(15000) ); }; - describe('Exports from Non-default Space', () => { + describe('Exports and Spaces', () => { before(async () => { await esArchiver.load('reporting/ecommerce'); - await esArchiver.load('reporting/ecommerce_kibana_spaces'); // dashboard in non default space + await esArchiver.load('reporting/ecommerce_kibana_spaces'); // multiple spaces with different config settings }); after(async () => { await esArchiver.unload('reporting/ecommerce'); await esArchiver.unload('reporting/ecommerce_kibana_spaces'); - }); - - afterEach(async () => { await reportingAPI.deleteAllReports(); }); - it('should complete a job of CSV saved search export in non-default space', async () => { - const downloadPath = await reportingAPI.postJob( - `/s/non_default_space/api/reporting/generate/csv?jobParams=%28browserTimezone%3AUTC%2CconflictedTypesFields%3A%21%28%29%2Cfields%3A%21%28order_date%2Ccategory%2Ccustomer_first_name%2Ccustomer_full_name%2Ctotal_quantity%2Ctotal_unique_products%2Ctaxless_total_price%2Ctaxful_total_price%2Ccurrency%29%2CindexPatternId%3A%27067dec90-e7ee-11ea-a730-d58e9ea7581b%27%2CmetaFields%3A%21%28_source%2C_id%2C_type%2C_index%2C_score%29%2CobjectType%3Asearch%2CsearchRequest%3A%28body%3A%28_source%3A%28includes%3A%21%28order_date%2Ccategory%2Ccustomer_first_name%2Ccustomer_full_name%2Ctotal_quantity%2Ctotal_unique_products%2Ctaxless_total_price%2Ctaxful_total_price%2Ccurrency%29%29%2Cdocvalue_fields%3A%21%28%28field%3Aorder_date%2Cformat%3Adate_time%29%29%2Cquery%3A%28bool%3A%28filter%3A%21%28%28match_all%3A%28%29%29%2C%28range%3A%28order_date%3A%28format%3Astrict_date_optional_time%2Cgte%3A%272019-06-11T08%3A24%3A16.425Z%27%2Clte%3A%272019-07-13T09%3A31%3A07.520Z%27%29%29%29%29%2Cmust%3A%21%28%29%2Cmust_not%3A%21%28%29%2Cshould%3A%21%28%29%29%29%2Cscript_fields%3A%28%29%2Csort%3A%21%28%28order_date%3A%28order%3Adesc%2Cunmapped_type%3Aboolean%29%29%29%2Cstored_fields%3A%21%28order_date%2Ccategory%2Ccustomer_first_name%2Ccustomer_full_name%2Ctotal_quantity%2Ctotal_unique_products%2Ctaxless_total_price%2Ctaxful_total_price%2Ccurrency%29%2Cversion%3A%21t%29%2Cindex%3A%27ecommerce%2A%27%29%2Ctitle%3A%27Ecom%20Search%27%29` - ); + describe('CSV saved search export', () => { + it('should use formats from the default space', async () => { + kibanaServer.uiSettings.update({ 'csv:separator': ',', 'dateFormat:tz': 'UTC' }); + const path = await reportingAPI.postJobJSON(`/api/reporting/generate/csv`, { + jobParams: `(conflictedTypesFields:!(),fields:!(order_date,order_date,customer_full_name,taxful_total_price),indexPatternId:aac3e500-f2c7-11ea-8250-fb138aa491e7,metaFields:!(_source,_id,_type,_index,_score),objectType:search,searchRequest:(body:(_source:(includes:!(order_date,customer_full_name,taxful_total_price)),docvalue_fields:!((field:order_date,format:date_time)),query:(bool:(filter:!((match_all:()),(range:(order_date:(format:strict_date_optional_time,gte:'2019-06-11T04:49:43.495Z',lte:'2019-07-14T10:25:34.149Z')))),must:!(),must_not:!(),should:!())),script_fields:(),sort:!((order_date:(order:desc,unmapped_type:boolean))),stored_fields:!(order_date,customer_full_name,taxful_total_price),version:!t),index:'ec*'),title:'EC SEARCH')`, + }); + const csv = await getCompleted$(path).toPromise(); + expect(csv).to.match( + /^"order_date","order_date","customer_full_name","taxful_total_price"\n"Jul 12, 2019 @ 00:00:00.000","Jul 12, 2019 @ 00:00:00.000","Sultan Al Boone","173.96"/ + ); + }); - // Retry the download URL until a "completed" response status is returned - const completed$ = getCompleted$(downloadPath); - const reportCompleted = await completed$.toPromise(); - expect(reportCompleted).to.match(/^"order_date",/); + it('should use formats from non-default spaces', async () => { + setSpaceConfig('non_default_space', { + 'csv:separator': ';', + 'csv:quoteValues': false, + 'dateFormat:tz': 'US/Alaska', + }); + const path = await reportingAPI.postJobJSON( + `/s/non_default_space/api/reporting/generate/csv`, + { + jobParams: `(conflictedTypesFields:!(),fields:!(order_date,category,customer_first_name,customer_full_name,total_quantity,total_unique_products,taxless_total_price,taxful_total_price,currency),indexPatternId:'067dec90-e7ee-11ea-a730-d58e9ea7581b',metaFields:!(_source,_id,_type,_index,_score),objectType:search,searchRequest:(body:(_source:(includes:!(order_date,category,customer_first_name,customer_full_name,total_quantity,total_unique_products,taxless_total_price,taxful_total_price,currency)),docvalue_fields:!((field:order_date,format:date_time)),query:(bool:(filter:!((match_all:()),(range:(order_date:(format:strict_date_optional_time,gte:'2019-06-11T08:24:16.425Z',lte:'2019-07-13T09:31:07.520Z')))),must:!(),must_not:!(),should:!())),script_fields:(),sort:!((order_date:(order:desc,unmapped_type:boolean))),stored_fields:!(order_date,category,customer_first_name,customer_full_name,total_quantity,total_unique_products,taxless_total_price,taxful_total_price,currency),version:!t),index:'ecommerce*'),title:'Ecom Search')`, + } + ); + const csv = await getCompleted$(path).toPromise(); + expect(csv).to.match( + /^order_date;category;customer_first_name;customer_full_name;total_quantity;total_unique_products;taxless_total_price;taxful_total_price;currency\nJul 11, 2019 @ 16:00:00.000;/ + ); + }); + + it(`should use browserTimezone in jobParams for date formatting`, async () => { + const tzParam = 'America/Phoenix'; + const tzSettings = 'Browser'; + setSpaceConfig('non_default_space', { 'csv:separator': ';', 'dateFormat:tz': tzSettings }); + const path = await reportingAPI.postJobJSON(`/api/reporting/generate/csv`, { + jobParams: `(browserTimezone:${tzParam},conflictedTypesFields:!(),fields:!(order_date,category,customer_full_name,taxful_total_price,currency),indexPatternId:aac3e500-f2c7-11ea-8250-fb138aa491e7,metaFields:!(_source,_id,_type,_index,_score),objectType:search,searchRequest:(body:(_source:(includes:!(order_date,category,customer_full_name,taxful_total_price,currency)),docvalue_fields:!((field:order_date,format:date_time)),query:(bool:(filter:!((match_all:()),(range:(order_date:(format:strict_date_optional_time,gte:'2019-05-30T05:09:59.743Z',lte:'2019-07-26T08:47:09.682Z')))),must:!(),must_not:!(),should:!())),script_fields:(),sort:!((order_date:(order:desc,unmapped_type:boolean))),stored_fields:!(order_date,category,customer_full_name,taxful_total_price,currency),version:!t),index:'ec*'),title:'EC SEARCH from DEFAULT')`, + }); + + const csv = await getCompleted$(path).toPromise(); + expect(csv).to.match( + /^"order_date",category,"customer_full_name","taxful_total_price",currency\n"Jul 11, 2019 @ 17:00:00.000"/ + ); + }); + + it(`should default to UTC for date formatting when timezone is not known`, async () => { + kibanaServer.uiSettings.update({ 'csv:separator': ',', 'dateFormat:tz': 'Browser' }); + const path = await reportingAPI.postJobJSON(`/api/reporting/generate/csv`, { + jobParams: `(conflictedTypesFields:!(),fields:!(order_date,order_date,customer_full_name,taxful_total_price),indexPatternId:aac3e500-f2c7-11ea-8250-fb138aa491e7,metaFields:!(_source,_id,_type,_index,_score),objectType:search,searchRequest:(body:(_source:(includes:!(order_date,customer_full_name,taxful_total_price)),docvalue_fields:!((field:order_date,format:date_time)),query:(bool:(filter:!((match_all:()),(range:(order_date:(format:strict_date_optional_time,gte:'2019-06-11T04:49:43.495Z',lte:'2019-07-14T10:25:34.149Z')))),must:!(),must_not:!(),should:!())),script_fields:(),sort:!((order_date:(order:desc,unmapped_type:boolean))),stored_fields:!(order_date,customer_full_name,taxful_total_price),version:!t),index:'ec*'),title:'EC SEARCH')`, + }); + const csv = await getCompleted$(path).toPromise(); + expect(csv).to.match( + /^"order_date","order_date","customer_full_name","taxful_total_price"\n"Jul 12, 2019 @ 00:00:00.000","Jul 12, 2019 @ 00:00:00.000","Sultan Al Boone","173.96"/ + ); + }); }); // FLAKY: https://github.com/elastic/kibana/issues/76551 it.skip('should complete a job of PNG export of a dashboard in non-default space', async () => { - const downloadPath = await reportingAPI.postJob( - `/s/non_default_space/api/reporting/generate/png?jobParams=%28browserTimezone%3AUTC%2Clayout%3A%28dimensions%3A%28height%3A512%2Cwidth%3A2402%29%2Cid%3Apng%29%2CobjectType%3Adashboard%2CrelativeUrl%3A%27%2Fs%2Fnon_default_space%2Fapp%2Fdashboards%23%2Fview%2F3c9ee360-e7ee-11ea-a730-d58e9ea7581b%3F_g%3D%28filters%3A%21%21%28%29%2CrefreshInterval%3A%28pause%3A%21%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3A%21%272019-06-10T03%3A17%3A28.800Z%21%27%2Cto%3A%21%272019-07-14T19%3A25%3A06.385Z%21%27%29%29%26_a%3D%28description%3A%21%27%21%27%2Cfilters%3A%21%21%28%29%2CfullScreenMode%3A%21%21f%2Coptions%3A%28hidePanelTitles%3A%21%21f%2CuseMargins%3A%21%21t%29%2Cquery%3A%28language%3Akuery%2Cquery%3A%21%27%21%27%29%2CtimeRestore%3A%21%21t%2Ctitle%3A%21%27Ecom%2520Dashboard%2520Non%2520Default%2520Space%21%27%2CviewMode%3Aview%29%27%2Ctitle%3A%27Ecom%20Dashboard%20Non%20Default%20Space%27%29` + const downloadPath = await reportingAPI.postJobJSON( + `/s/non_default_space/api/reporting/generate/png`, + { + jobParams: `(browserTimezone:UTC,layout:(dimensions:(height:512,width:2402),id:png),objectType:dashboard,relativeUrl:'/s/non_default_space/app/dashboards#/view/3c9ee360-e7ee-11ea-a730-d58e9ea7581b?_g=(filters:!!(),refreshInterval:(pause:!!t,value:0),time:(from:!'2019-06-10T03:17:28.800Z!',to:!'2019-07-14T19:25:06.385Z!'))&_a=(description:!'!',filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),query:(language:kuery,query:!'!'),timeRestore:!!t,title:!'Ecom%20Dashboard%20Non%20Default%20Space!',viewMode:view)',title:'Ecom Dashboard Non Default Space')`, + } ); const completed$: Rx.Observable = getCompleted$(downloadPath); @@ -66,8 +121,11 @@ export default function ({ getService }: FtrProviderContext) { // FLAKY: https://github.com/elastic/kibana/issues/76551 it.skip('should complete a job of PDF export of a dashboard in non-default space', async () => { - const downloadPath = await reportingAPI.postJob( - `/s/non_default_space/api/reporting/generate/printablePdf?jobParams=%28browserTimezone%3AUTC%2Clayout%3A%28dimensions%3A%28height%3A512%2Cwidth%3A2402%29%2Cid%3Apreserve_layout%29%2CobjectType%3Adashboard%2CrelativeUrls%3A%21%28%27%2Fs%2Fnon_default_space%2Fapp%2Fdashboards%23%2Fview%2F3c9ee360-e7ee-11ea-a730-d58e9ea7581b%3F_g%3D%28filters%3A%21%21%28%29%2CrefreshInterval%3A%28pause%3A%21%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3A%21%272019-06-10T03%3A17%3A28.800Z%21%27%2Cto%3A%21%272019-07-14T19%3A25%3A06.385Z%21%27%29%29%26_a%3D%28description%3A%21%27%21%27%2Cfilters%3A%21%21%28%29%2CfullScreenMode%3A%21%21f%2Coptions%3A%28hidePanelTitles%3A%21%21f%2CuseMargins%3A%21%21t%29%2Cquery%3A%28language%3Akuery%2Cquery%3A%21%27%21%27%29%2CtimeRestore%3A%21%21t%2Ctitle%3A%21%27Ecom%2520Dashboard%2520Non%2520Default%2520Space%21%27%2CviewMode%3Aview%29%27%29%2Ctitle%3A%27Ecom%20Dashboard%20Non%20Default%20Space%27%29` + const downloadPath = await reportingAPI.postJobJSON( + `/s/non_default_space/api/reporting/generate/printablePdf`, + { + jobParams: `(browserTimezone:UTC,layout:(dimensions:(height:512,width:2402),id:preserve_layout),objectType:dashboard,relativeUrls:!('/s/non_default_space/app/dashboards#/view/3c9ee360-e7ee-11ea-a730-d58e9ea7581b?_g=(filters:!!(),refreshInterval:(pause:!!t,value:0),time:(from:!'2019-06-10T03:17:28.800Z!',to:!'2019-07-14T19:25:06.385Z!'))&_a=(description:!'!',filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),query:(language:kuery,query:!'!'),timeRestore:!!t,title:!'Ecom%20Dashboard%20Non%20Default%20Space!',viewMode:view)'),title:'Ecom Dashboard Non Default Space')`, + } ); const completed$ = getCompleted$(downloadPath); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage.ts index aaf4dd3926411..99a46684d8a67 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage.ts @@ -115,8 +115,7 @@ export default function ({ getService }: FtrProviderContext) { }); }); - // FAILING: https://github.com/elastic/kibana/issues/76581 - describe.skip('from new jobs posted', () => { + describe('from new jobs posted', () => { it('should handle csv', async () => { await reportingAPI.expectAllJobsToFinishSuccessfully( await Promise.all([ @@ -133,7 +132,8 @@ export default function ({ getService }: FtrProviderContext) { reportingAPI.expectRecentJobTypeTotalStats(usage, 'printable_pdf', 0); }); - it('should handle preserve_layout pdf', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/76581 + it.skip('should handle preserve_layout pdf', async () => { await reportingAPI.expectAllJobsToFinishSuccessfully( await Promise.all([ reportingAPI.postJob(GenerationUrls.PDF_PRESERVE_DASHBOARD_FILTER_6_3), @@ -150,7 +150,8 @@ export default function ({ getService }: FtrProviderContext) { reportingAPI.expectRecentJobTypeTotalStats(usage, 'printable_pdf', 2); }); - it('should handle print_layout pdf', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/76581 + it.skip('should handle print_layout pdf', async () => { await reportingAPI.expectAllJobsToFinishSuccessfully( await Promise.all([ reportingAPI.postJob(GenerationUrls.PDF_PRINT_DASHBOARD_6_3), diff --git a/x-pack/test/reporting_api_integration/services.ts b/x-pack/test/reporting_api_integration/services.ts index e61e6483855af..2c0252fde7693 100644 --- a/x-pack/test/reporting_api_integration/services.ts +++ b/x-pack/test/reporting_api_integration/services.ts @@ -84,7 +84,7 @@ export function ReportingAPIProvider({ getService }: FtrProviderContext) { ); }, - async postJob(apiPath: string) { + async postJob(apiPath: string): Promise { log.debug(`ReportingAPI.postJob(${apiPath})`); const { body } = await supertest .post(removeWhitespace(apiPath)) @@ -93,6 +93,12 @@ export function ReportingAPIProvider({ getService }: FtrProviderContext) { return body.path; }, + async postJobJSON(apiPath: string, jobJSON: object = {}): Promise { + log.debug(`ReportingAPI.postJobJSON((${apiPath}): ${JSON.stringify(jobJSON)})`); + const { body } = await supertest.post(apiPath).set('kbn-xsrf', 'xxx').send(jobJSON); + return body.path; + }, + /** * * @return {Promise} A function to call to clean up the index alias that was added.