From 472b2d62a38e00d76bb0a08310cdc1cf21fa3741 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 00:43:05 -0700 Subject: [PATCH 01/20] LevelLogger TS --- .../reporting/server/lib/level_logger.js | 42 --------------- .../reporting/server/lib/level_logger.ts | 51 +++++++++++++++++++ 2 files changed, 51 insertions(+), 42 deletions(-) delete mode 100644 x-pack/plugins/reporting/server/lib/level_logger.js create mode 100644 x-pack/plugins/reporting/server/lib/level_logger.ts diff --git a/x-pack/plugins/reporting/server/lib/level_logger.js b/x-pack/plugins/reporting/server/lib/level_logger.js deleted file mode 100644 index 92d83799b2c22..0000000000000 --- a/x-pack/plugins/reporting/server/lib/level_logger.js +++ /dev/null @@ -1,42 +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. - */ - - -export class LevelLogger { - - static createForServer(server, tags) { - return new LevelLogger( - (tags, msg) => server.log(tags, msg), - tags, - server.config().get('logging.verbose')); - } - - constructor(logger, tags, isVerbose) { - this._logger = logger; - this._tags = tags; - this.isVerbose = isVerbose; - } - - error(msg, tags = []) { - this._logger([...this._tags, ...tags, 'error'], msg); - } - - warning(msg, tags = []) { - this._logger([...this._tags, ...tags, 'warning'], msg); - } - - debug(msg, tags = []) { - this._logger([...this._tags, ...tags, 'debug'], msg); - } - - info(msg, tags = []) { - this._logger([...this._tags, ...tags, 'info'], msg); - } - - clone(tags) { - return new LevelLogger(this._logger, [...this._tags, ...tags], this.isVerbose); - } -} diff --git a/x-pack/plugins/reporting/server/lib/level_logger.ts b/x-pack/plugins/reporting/server/lib/level_logger.ts new file mode 100644 index 0000000000000..6735994d8cfef --- /dev/null +++ b/x-pack/plugins/reporting/server/lib/level_logger.ts @@ -0,0 +1,51 @@ +/* + * 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 { Logger } from '../../types'; + +type ServerLog = (tags: string[], msg: string) => void; + +export class LevelLogger implements Logger { + private _logger: any; + private _tags: string[]; + + public warn: (msg: string, tags?: string[]) => void; + + static createForServer(server: any, tags: string[]) { + return new LevelLogger((tags: string[], msg: string) => server.log(tags, msg), tags); + } + + constructor(logger: ServerLog, tags: string[]) { + this._logger = logger; + this._tags = tags; + + /* + * This shortcut provides maintenance convenience: Reporting code has been + * using both .warn and .warning + */ + this.warn = this.warning.bind(this); + } + + public error(msg: string, tags: string[] = []) { + this._logger([...this._tags, ...tags, 'error'], msg); + } + + public warning(msg: string, tags: string[] = []) { + this._logger([...this._tags, ...tags, 'warning'], msg); + } + + public debug(msg: string, tags: string[] = []) { + this._logger([...this._tags, ...tags, 'debug'], msg); + } + + public info(msg: string, tags: string[] = []) { + this._logger([...this._tags, ...tags, 'info'], msg); + } + + public clone(tags: string[]) { + return new LevelLogger(this._logger, [...this._tags, ...tags]); + } +} From 4b999e93107ba9877a303016b6bf3ce5006dc701 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 4 Apr 2019 15:42:15 -0700 Subject: [PATCH 02/20] Prevent creating a job in the reporting index when the type is immediate --- .../csv_from_savedobject/index.ts | 14 +++ .../server/create_job/create_job.ts | 30 ++----- .../server/execute_job.ts | 72 +++++++++------- .../server/lib/generate_csv.ts | 2 - .../server/lib/index.d.ts | 2 +- x-pack/plugins/reporting/server/lib/index.ts | 8 +- .../routes/generate_from_savedobject.ts | 85 ++++++++++--------- x-pack/plugins/reporting/types.d.ts | 21 ++++- 8 files changed, 130 insertions(+), 104 deletions(-) create mode 100644 x-pack/plugins/reporting/export_types/csv_from_savedobject/index.ts diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.ts new file mode 100644 index 0000000000000..6cc56cb5fa4f1 --- /dev/null +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/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; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './index.d'; + +/* + * These functions are exported to share with the API route handler that + * generates csv from saved object immediately on request. + */ +export { executeJobFactory } from './server/execute_job'; +export { createJobFactory } from './server/create_job'; diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts index d6166f16dab77..737a5d81ac4c6 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts @@ -8,8 +8,8 @@ import { notFound, notImplemented } from 'boom'; import { Request } from 'hapi'; import { get } from 'lodash'; // @ts-ignore -import { createTaggedLogger, cryptoFactory, oncePerServer } from '../../../../server/lib'; -import { JobDocPayload, JobParams, KbnServer, Logger } from '../../../../types'; +import { cryptoFactory, LevelLogger, oncePerServer } from '../../../../server/lib'; +import { JobDocPayload, JobParams, KbnServer } from '../../../../types'; import { SavedObject, SavedObjectServiceError, @@ -18,7 +18,6 @@ import { TimeRangeParams, VisObjectAttributesJSON, } from '../../'; -import { createGenerateCsv } from '../lib/generate_csv'; import { createJobSearch } from './create_job_search'; interface VisData { @@ -27,21 +26,18 @@ interface VisData { panel: SearchPanel; } -function createJobFn(server: KbnServer) { +type CreateJobFn = (jobParams: JobParams, headers: any, req: Request) => Promise; + +function createJobFn(server: KbnServer): CreateJobFn { const crypto = cryptoFactory(server); - const logger: Logger = { - debug: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'debug']), - warning: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'warning']), - error: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'error']), - }; - const generateCsv = createGenerateCsv(logger); + const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']);; return async function createJob( jobParams: JobParams, headers: any, req: Request ): Promise { - const { isImmediate, savedObjectType, savedObjectId } = jobParams; + const { savedObjectType, savedObjectId } = jobParams; const serializedEncryptedHeaders = await crypto.encrypt(headers); const client = req.getSavedObjectsClient(); @@ -89,18 +85,6 @@ function createJobFn(server: KbnServer) { let type: string = ''; let result: any = null; - if (isImmediate) { - try { - ({ type, result } = await generateCsv(req, server, visType, panel)); - } catch (err) { - if (err.stack) { - logger.error(err.stack); - } - logger.error(`Generate CSV Error! ${err}`); - throw err; - } - } - return { jobParams: { ...jobParams, panel, visType }, title, diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts index 4c0168e442ae0..4a214587c7c0b 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts @@ -4,45 +4,45 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Request } from 'hapi'; import { i18n } from '@kbn/i18n'; +import { cryptoFactory, LevelLogger, oncePerServer } from '../../../server/lib'; + +import { JobDocOutputExecuted, JobDocPayload, KbnServer } from '../../../types'; import { CONTENT_TYPE_CSV } from '../../../common/constants'; -// @ts-ignore -import { createTaggedLogger, cryptoFactory, oncePerServer } from '../../../server/lib'; -import { JobDocPayload, KbnServer, Logger } from '../../../types'; import { createGenerateCsv } from './lib/generate_csv'; -interface JobDocOutputPseudo { - content_type: 'text/csv'; - content: string | null | undefined; -} - interface FakeRequest { headers: any; getBasePath: (opts: any) => string; server: KbnServer; } -/* - * @return {Object}: pseudo-JobDocOutput. See interface JobDocOutput - */ -function executeJobFn(server: KbnServer) { +type ExecuteJobFn = (job: JobDocPayload, realRequest?: Request) => Promise; + +function executeJobFn(server: KbnServer): ExecuteJobFn { const crypto = cryptoFactory(server); const config = server.config(); const serverBasePath = config.get('server.basePath'); - const logger: Logger = { - debug: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'debug']), - warning: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'warning']), - error: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'error']), - }; - + const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']); const generateCsv = createGenerateCsv(logger); - return async function executeJob(job: JobDocPayload): Promise { - const { basePath, objects, headers: serializedEncryptedHeaders, jobParams } = job; // FIXME how to remove payload.objects for cleanup? + + return async function executeJob(job: JobDocPayload, realRequest?: Request): Promise { + const { basePath, jobParams } = job; const { isImmediate, panel, visType } = jobParams; - if (!isImmediate) { - logger.debug(`Execute job generating [${visType}] csv`); + + logger.debug(`Execute job generating [${visType}] csv`); + + let requestObject: Request | FakeRequest; + if (isImmediate && realRequest) { + logger.debug(`executing job from immediate API`); + + requestObject = realRequest; + } else { + logger.debug(`executing job async using encrypted headers`); let decryptedHeaders; + const serializedEncryptedHeaders = job.headers; try { decryptedHeaders = await crypto.decrypt(serializedEncryptedHeaders); } catch (err) { @@ -58,25 +58,33 @@ function executeJobFn(server: KbnServer) { ); } - const fakeRequest: FakeRequest = { + requestObject = { headers: decryptedHeaders, getBasePath: () => basePath || serverBasePath, server, }; - - const content = await generateCsv(fakeRequest, server, visType as string, panel); - return { - content_type: CONTENT_TYPE_CSV, - content: content.result ? content.result.content : null, - }; } - logger.debug(`Execute job using previously-generated [${visType}] csv`); + let content: string; + let maxSizeReached = false; + let size = 0; + try { + ({ + result: { content, maxSizeReached, size }, + } = await generateCsv(requestObject, server, visType as string, panel)); + } catch (err) { + if (err.stack) { + logger.error(err.stack); + } + logger.error(`Generate CSV Error! ${err}`); + throw err; + } - // if job was created with "immediate", just return the data in the job doc return { content_type: CONTENT_TYPE_CSV, - content: objects, + content, + max_size_reached: maxSizeReached, + size, }; }; } diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv.ts index a5929e20847cf..378b38596bcfb 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv.ts @@ -6,8 +6,6 @@ import { badRequest } from 'boom'; import { Request } from 'hapi'; -// @ts-ignore -import { createTaggedLogger } from '../../../../server/lib'; import { KbnServer, Logger } from '../../../../types'; import { SearchPanel, VisPanel } from '../../'; import { generateCsvSearch } from './generate_csv_search'; diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/index.d.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/index.d.ts index 7220cbc666083..041e411360f33 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/index.d.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/index.d.ts @@ -20,7 +20,7 @@ export interface SavedSearchGeneratorResult { export interface CsvResultFromSearch { type: string; - result: SavedSearchGeneratorResult | null; + result: SavedSearchGeneratorResult; } type EndpointCaller = (method: string, params: any) => Promise; diff --git a/x-pack/plugins/reporting/server/lib/index.ts b/x-pack/plugins/reporting/server/lib/index.ts index 9021abb5374fd..72c28a43d9fa4 100644 --- a/x-pack/plugins/reporting/server/lib/index.ts +++ b/x-pack/plugins/reporting/server/lib/index.ts @@ -4,9 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore +// @ts-ignore untyped module +export { LevelLogger } from './level_logger'; +// @ts-ignore untyped module export { createTaggedLogger } from './create_tagged_logger'; -// @ts-ignore +// @ts-ignore untyped module export { cryptoFactory } from './crypto'; -// @ts-ignore +// @ts-ignore untyped module export { oncePerServer } from './once_per_server'; diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts index 6c8b907d90d81..2c949f213b167 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -7,12 +7,18 @@ import { Request, ResponseObject, ResponseToolkit } from 'hapi'; import Joi from 'joi'; import { get } from 'lodash'; + +// @ts-ignore no module definition import { API_BASE_URL_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants'; -import { JobDoc, JobDocOutput, JobParams, KbnServer } from '../../types'; -// @ts-ignore +// @ts-ignore no module definition +import { LevelLogger } from '../lib/level_logger'; +// @ts-ignore no module definition import { getDocumentPayloadFactory } from './lib/get_document_payload'; -import { getRouteConfigFactoryReportingPre } from './lib/route_config_factories'; + +import { createJobFactory, executeJobFactory } from '../../export_types/csv_from_savedobject'; +import { JobDocPayload, JobDocOutputExecuted, JobParams, KbnServer } from '../../types'; import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types'; +import { getRouteConfigFactoryReportingPre } from './lib/route_config_factories'; const BASE_GENERATE = `${API_BASE_URL_V1}/generate`; @@ -85,55 +91,50 @@ export function registerGenerateCsvFromSavedObject( }), }, }; - const getDocumentPayload = getDocumentPayloadFactory(server); - // csv: immediate download + /* + * 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 executeJob function to run the scan and scroll queries and capture the entire CSV in a result object. + */ server.route({ path: `${BASE_GENERATE}/immediate/csv/saved-object/{savedObjectType}:{savedObjectId}`, method: 'POST', options: routeOptions, handler: async (request: Request, h: ResponseToolkit) => { - /* - * 1. Queue a job with getJobFromRouteHandler - * - `isImmediate: true` gets us the complete result data in payload.objects - * 2. Copy the completed data stashed in the job as output.content - * - Makes the content available for download - * 3. Return a response with CSV content - */ - const queuedJob: QueuedJobPayload = await getJobFromRouteHandler( - handleRoute, - handleRouteError, - request, - h, - { - isImmediate: true, - } - ); - - // FIXME this is REALLY ugly - const jobSource: JobDoc = get(queuedJob, 'source.job'); - const output: JobDocOutput = getDocumentPayload({ - _source: { - ...jobSource, - status: 'completed', - output: { - content: jobSource.payload.objects, - content_type: 'text/csv', - }, - }, - }); + const logger = LevelLogger.createStaticFromServer(server, ['reporting', 'savedobject-csv']); + + const { savedObjectType, savedObjectId } = request.params; + const jobParams: JobParams = { + savedObjectType, + savedObjectId, + isImmediate: true, + }; + const createJobFn = createJobFactory(server); + const executeJobFn = executeJobFactory(server, request); + const jobDocPayload: JobDocPayload = await createJobFn(jobParams, request.headers, request); + const { + content_type: jobOutputContentType, + content: jobOutputContent, + size: jobOutputSize, + }: JobDocOutputExecuted = await executeJobFn(jobDocPayload, request); - const response: ResponseObject = h - .response(output.content) - .type(output.contentType) - .code(output.statusCode); + logger.info(`job output size: ${jobOutputSize} bytes`); - if (output.headers) { - Object.keys(output.headers).forEach(key => { - response.header(key, output.headers[key]); - }); + /* + * ESQueue worker function defaults `content` to null, even if the + * executeJob returned undefined. + * + * This converts null to undefined so the value can be sent to h.response() + */ + if (jobOutputContent === null) { + logger.warn('CSV Job Execution created empty content result'); } + const response = h + .response(jobOutputContent ? jobOutputContent : undefined) + .type(jobOutputContentType); + // Set header for buffer download, not streaming const { isBoom } = response as KibanaResponse; if (isBoom == null) { response.header('accept-ranges', 'none'); diff --git a/x-pack/plugins/reporting/types.d.ts b/x-pack/plugins/reporting/types.d.ts index 4b54c2bf3748b..561b1097011da 100644 --- a/x-pack/plugins/reporting/types.d.ts +++ b/x-pack/plugins/reporting/types.d.ts @@ -132,7 +132,7 @@ export interface JobDocPayload { export interface JobDocOutput { content: string; // encoded content contentType: string; - headers: any; + headers?: any; size?: number; statusCode: number; } @@ -149,6 +149,25 @@ export interface JobSource { _source: JobDoc; } +/* + * A snake_cased field is the only significant difference in structure of + * JobDocOutputExecuted vs JobDocOutput. + * + * JobDocOutput is the structure of the object returned by getDocumentPayload + * + * data in the _source fields of the + * Reporting index. + * + * The ESQueueWorker internals have executed job objects returned with this + * structure. See `_formatOutput` in reporting/server/lib/esqueue/worker.js + */ +export interface JobDocOutputExecuted { + content_type: string; // vs `contentType` above + content: string | null; // defaultOutput is null + max_size_reached: boolean; + size: number; +} + export interface ESQueueWorker { on: (event: string, handler: any) => void; } From 0e8d0bf75f191742e48e132fa8e87052a94f941c Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 10:57:04 -0700 Subject: [PATCH 03/20] self-review cleanup --- .../export_types/csv_from_savedobject/server/execute_job.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts index 4a214587c7c0b..339b062ab5b3c 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts @@ -36,11 +36,9 @@ function executeJobFn(server: KbnServer): ExecuteJobFn { let requestObject: Request | FakeRequest; if (isImmediate && realRequest) { logger.debug(`executing job from immediate API`); - requestObject = realRequest; } else { logger.debug(`executing job async using encrypted headers`); - let decryptedHeaders; const serializedEncryptedHeaders = job.headers; try { From ad99f1b067f2f3186cf89ed45775d0d8420c9af4 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 10:57:18 -0700 Subject: [PATCH 04/20] a change that depends on another pr --- .../reporting/server/routes/generate_from_savedobject.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts index 2c949f213b167..c68bb69474a4f 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -11,12 +11,11 @@ import { get } from 'lodash'; // @ts-ignore no module definition import { API_BASE_URL_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants'; // @ts-ignore no module definition -import { LevelLogger } from '../lib/level_logger'; -// @ts-ignore no module definition import { getDocumentPayloadFactory } from './lib/get_document_payload'; import { createJobFactory, executeJobFactory } from '../../export_types/csv_from_savedobject'; import { JobDocPayload, JobDocOutputExecuted, JobParams, KbnServer } from '../../types'; +import { LevelLogger } from '../lib/level_logger'; import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types'; import { getRouteConfigFactoryReportingPre } from './lib/route_config_factories'; From c0bb8023a8e242b864bbc3ad6041a582c73ec4e2 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 10:57:37 -0700 Subject: [PATCH 05/20] these shouldn't be needed, not accessed in our code --- x-pack/plugins/reporting/types.d.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/plugins/reporting/types.d.ts b/x-pack/plugins/reporting/types.d.ts index 561b1097011da..2de8f079e0def 100644 --- a/x-pack/plugins/reporting/types.d.ts +++ b/x-pack/plugins/reporting/types.d.ts @@ -132,9 +132,6 @@ export interface JobDocPayload { export interface JobDocOutput { content: string; // encoded content contentType: string; - headers?: any; - size?: number; - statusCode: number; } export interface JobDoc { From 6d53f0ffc32dfce3a06655caff009bdb4e42329e Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 11:01:03 -0700 Subject: [PATCH 06/20] self review cleanup 2 --- .../csv_from_savedobject/server/create_job/create_job.ts | 4 ++-- .../csv_from_savedobject/server/execute_job.ts | 7 +++++-- x-pack/plugins/reporting/server/lib/index.ts | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts index 737a5d81ac4c6..7ef16b5e768b4 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts @@ -7,7 +7,7 @@ import { notFound, notImplemented } from 'boom'; import { Request } from 'hapi'; import { get } from 'lodash'; -// @ts-ignore + import { cryptoFactory, LevelLogger, oncePerServer } from '../../../../server/lib'; import { JobDocPayload, JobParams, KbnServer } from '../../../../types'; import { @@ -30,7 +30,7 @@ type CreateJobFn = (jobParams: JobParams, headers: any, req: Request) => Promise function createJobFn(server: KbnServer): CreateJobFn { const crypto = cryptoFactory(server); - const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']);; + const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']); return async function createJob( jobParams: JobParams, diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts index 339b062ab5b3c..6fc7cf0bfdb72 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts @@ -6,8 +6,8 @@ import { Request } from 'hapi'; import { i18n } from '@kbn/i18n'; -import { cryptoFactory, LevelLogger, oncePerServer } from '../../../server/lib'; +import { cryptoFactory, LevelLogger, oncePerServer } from '../../../server/lib'; import { JobDocOutputExecuted, JobDocPayload, KbnServer } from '../../../types'; import { CONTENT_TYPE_CSV } from '../../../common/constants'; import { createGenerateCsv } from './lib/generate_csv'; @@ -27,7 +27,10 @@ function executeJobFn(server: KbnServer): ExecuteJobFn { const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']); const generateCsv = createGenerateCsv(logger); - return async function executeJob(job: JobDocPayload, realRequest?: Request): Promise { + return async function executeJob( + job: JobDocPayload, + realRequest?: Request + ): Promise { const { basePath, jobParams } = job; const { isImmediate, panel, visType } = jobParams; diff --git a/x-pack/plugins/reporting/server/lib/index.ts b/x-pack/plugins/reporting/server/lib/index.ts index 72c28a43d9fa4..026c444ca35d7 100644 --- a/x-pack/plugins/reporting/server/lib/index.ts +++ b/x-pack/plugins/reporting/server/lib/index.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore untyped module -export { LevelLogger } from './level_logger'; // @ts-ignore untyped module export { createTaggedLogger } from './create_tagged_logger'; // @ts-ignore untyped module export { cryptoFactory } from './crypto'; // @ts-ignore untyped module export { oncePerServer } from './once_per_server'; + +export { LevelLogger } from './level_logger'; From 0bdbf93f88295c4086a42771326cdb14cc1e900f Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 11:07:26 -0700 Subject: [PATCH 07/20] logger bug fix --- .../reporting/server/routes/generate_from_savedobject.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts index c68bb69474a4f..c69aa4f5660f1 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -101,7 +101,7 @@ export function registerGenerateCsvFromSavedObject( method: 'POST', options: routeOptions, handler: async (request: Request, h: ResponseToolkit) => { - const logger = LevelLogger.createStaticFromServer(server, ['reporting', 'savedobject-csv']); + const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']); const { savedObjectType, savedObjectId } = request.params; const jobParams: JobParams = { From 976f0ed5746a4024ac45b041e973d6a4e5408494 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 15:25:07 -0700 Subject: [PATCH 08/20] types fix --- .../server/create_job/create_job.ts | 11 ++++------- x-pack/plugins/reporting/types.d.ts | 10 ++++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts index 7ef16b5e768b4..21fb1f8e27a7b 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts @@ -82,16 +82,13 @@ function createJobFn(server: KbnServer): CreateJobFn { throw new Error(`Unable to create a job from saved object data! Error: ${err}`); }); - let type: string = ''; - let result: any = null; - return { + basePath: req.getBasePath(), + headers: serializedEncryptedHeaders, jobParams: { ...jobParams, panel, visType }, + type: null, // resolved in executeJob + objects: null, // resolved in executeJob title, - type, - objects: result ? result.content : result, - headers: serializedEncryptedHeaders, - basePath: req.getBasePath(), }; }; } diff --git a/x-pack/plugins/reporting/types.d.ts b/x-pack/plugins/reporting/types.d.ts index 2de8f079e0def..089db797abd86 100644 --- a/x-pack/plugins/reporting/types.d.ts +++ b/x-pack/plugins/reporting/types.d.ts @@ -117,16 +117,14 @@ export interface JobParams { } export interface JobDocPayload { - basePath?: string; - forceNow?: string; - headers?: Record; + basePath: string; + headers: Record; jobParams: JobParams; - objects?: string | null; // string if completed job; null if incomplete job; relativeUrl?: string; timeRange?: any; title: string; - type: string; - urls?: string[]; + type?: string | null; // string if completed job; null if incomplete job; + objects?: string | null; // string if completed job; null if incomplete job; } export interface JobDocOutput { From aab2aa708b890144b6fdc96a0d0788f08a122579 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 15:32:52 -0700 Subject: [PATCH 09/20] pretty --- .../csv_from_savedobject/server/create_job/create_job.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts index 21fb1f8e27a7b..3ba54473474ee 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts @@ -86,7 +86,7 @@ function createJobFn(server: KbnServer): CreateJobFn { basePath: req.getBasePath(), headers: serializedEncryptedHeaders, jobParams: { ...jobParams, panel, visType }, - type: null, // resolved in executeJob + type: null, // resolved in executeJob objects: null, // resolved in executeJob title, }; From 7c3ca7f2d26437ea6a12b78d8f9c8a3c3cf44052 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 15:35:08 -0700 Subject: [PATCH 10/20] squash logger lint fix --- x-pack/plugins/reporting/server/lib/level_logger.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/lib/level_logger.ts b/x-pack/plugins/reporting/server/lib/level_logger.ts index 6735994d8cfef..765439c81a7d6 100644 --- a/x-pack/plugins/reporting/server/lib/level_logger.ts +++ b/x-pack/plugins/reporting/server/lib/level_logger.ts @@ -15,7 +15,8 @@ export class LevelLogger implements Logger { public warn: (msg: string, tags?: string[]) => void; static createForServer(server: any, tags: string[]) { - return new LevelLogger((tags: string[], msg: string) => server.log(tags, msg), tags); + const serverLog: ServerLog = (tgs: string[], msg: string) => server.log(tgs, msg); + return new LevelLogger(serverLog, tags); } constructor(logger: ServerLog, tags: string[]) { From 3b966c370cfae34e333e13e4fbbf6869db23cb37 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 15:42:38 -0700 Subject: [PATCH 11/20] cleanup --- .../export_types/csv_from_savedobject/index.d.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.d.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.d.ts index 3f35f7e237f5b..84f22cac54f00 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.d.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.d.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { TimeRangeParams } from '../../types'; + export interface SavedObjectServiceError { statusCode: number; error?: string; @@ -91,12 +93,6 @@ export interface IndexPatternSavedObject { }; } -export interface TimeRangeParams { - timezone: string; - min: Date | string | number; - max: Date | string | number; -} - export interface VisPanel { indexPatternSavedObjectId?: string; savedSearchObjectId?: string; From 527df353b72b037492c8d9b7a8b1208e62626617 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 11:18:52 -0700 Subject: [PATCH 12/20] fix non-immediate --- .../server/execute_job.ts | 2 +- .../server/lib/generate_csv.ts | 13 +- .../server/lib/generate_csv_search.ts | 17 ++- .../server/lib/index.d.ts | 20 +-- .../routes/generate_from_savedobject.ts | 4 +- x-pack/plugins/reporting/types.d.ts | 20 +++ x-pack/test/functional/config.js | 1 + .../api/generate/csv_saved_search.ts | 142 ++++++++++++++---- 8 files changed, 159 insertions(+), 60 deletions(-) diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts index 6fc7cf0bfdb72..1b44154424f6b 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts @@ -72,7 +72,7 @@ function executeJobFn(server: KbnServer): ExecuteJobFn { try { ({ result: { content, maxSizeReached, size }, - } = await generateCsv(requestObject, server, visType as string, panel)); + } = await generateCsv(requestObject, server, visType as string, panel, jobParams)); } catch (err) { if (err.stack) { logger.error(err.stack); diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv.ts index 378b38596bcfb..cf2d621b7d201 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv.ts @@ -6,7 +6,7 @@ import { badRequest } from 'boom'; import { Request } from 'hapi'; -import { KbnServer, Logger } from '../../../../types'; +import { KbnServer, Logger, JobParams } from '../../../../types'; import { SearchPanel, VisPanel } from '../../'; import { generateCsvSearch } from './generate_csv_search'; @@ -21,7 +21,8 @@ export function createGenerateCsv(logger: Logger) { request: Request | FakeRequest, server: KbnServer, visType: string, - panel: VisPanel | SearchPanel + panel: VisPanel | SearchPanel, + jobParams: JobParams ) { // This should support any vis type that is able to fetch // and model data on the server-side @@ -30,7 +31,13 @@ export function createGenerateCsv(logger: Logger) { // expression that we could run through the interpreter to get csv switch (visType) { case 'search': - return await generateCsvSearch(request as Request, server, logger, panel as SearchPanel); + return await generateCsvSearch( + request as Request, + server, + logger, + panel as SearchPanel, + jobParams + ); default: throw badRequest(`Unsupported or unrecognized saved object type: ${visType}`); } diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts index a17aa8d46b65b..f0eae018c9ed3 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts @@ -7,7 +7,7 @@ // @ts-ignore no module definition import { buildEsQuery } from '@kbn/es-query'; import { Request } from 'hapi'; -import { KbnServer, Logger } from '../../../../types'; +import { KbnServer, Logger, JobParams } from '../../../../types'; // @ts-ignore no module definition import { createGenerateCsv } from '../../../csv/server/lib/generate_csv'; import { @@ -23,8 +23,8 @@ import { ESQueryConfig, GenerateCsvParams, Filter, - ReqPayload, IndexPatternField, + QueryFilter, } from './'; import { getDataSource } from './get_data_source'; import { getFilters } from './get_filters'; @@ -49,7 +49,8 @@ export async function generateCsvSearch( req: Request, server: KbnServer, logger: Logger, - searchPanel: SearchPanel + searchPanel: SearchPanel, + jobParams: JobParams ): Promise { const { savedObjects, uiSettingsServiceFactory } = server; const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(req); @@ -77,9 +78,13 @@ export async function generateCsvSearch( fields: indexPatternFields, } = indexPatternSavedObject; - const { - state: { query: payloadQuery, sort: payloadSort = [] }, - } = req.payload as ReqPayload; + let payloadQuery: QueryFilter | undefined; + let payloadSort: any[] = []; + if (jobParams.post.state) { + ({ + post: { state: { query: payloadQuery, sort: payloadSort = [] } }, + } = jobParams); + } const { includes, timezone, combinedFilter } = getFilters( indexPatternSavedObjectId, diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/index.d.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/index.d.ts index 041e411360f33..350ea3fb2ab13 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/index.d.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/index.d.ts @@ -4,13 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - SavedSearchObjectAttributes, - SearchPanel, - SearchRequest, - SearchSource, - TimeRangeParams, -} from '../../'; +import { SavedSearchObjectAttributes, SearchPanel, SearchRequest, SearchSource } from '../../'; export interface SavedSearchGeneratorResult { content: string; @@ -34,18 +28,6 @@ type FormatsMap = Map< } >; -interface ReqPayload { - state: { - sort: Array<{ - [sortKey: string]: { - order: string; - }; - }>; - docvalue_fields: any; - query: any; - }; -} - export interface GenerateCsvParams { searchRequest: SearchRequest; callEndpoint: EndpointCaller; diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts index c69aa4f5660f1..c8eb8fd9fcb74 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -14,7 +14,7 @@ import { API_BASE_URL_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/con import { getDocumentPayloadFactory } from './lib/get_document_payload'; import { createJobFactory, executeJobFactory } from '../../export_types/csv_from_savedobject'; -import { JobDocPayload, JobDocOutputExecuted, JobParams, KbnServer } from '../../types'; +import { JobDocPayload, JobParamPostPayload, JobDocOutputExecuted, JobParams, KbnServer } from '../../types'; import { LevelLogger } from '../lib/level_logger'; import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types'; import { getRouteConfigFactoryReportingPre } from './lib/route_config_factories'; @@ -44,6 +44,7 @@ const getJobFromRouteHandler = async ( savedObjectType, savedObjectId, isImmediate: options.isImmediate, + post: request.payload as JobParamPostPayload, }; result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, request, h); } catch (err) { @@ -108,6 +109,7 @@ export function registerGenerateCsvFromSavedObject( savedObjectType, savedObjectId, isImmediate: true, + post: {}, // FIXME }; const createJobFn = createJobFactory(server); const executeJobFn = executeJobFactory(server, request); diff --git a/x-pack/plugins/reporting/types.d.ts b/x-pack/plugins/reporting/types.d.ts index 089db797abd86..973303d4ff117 100644 --- a/x-pack/plugins/reporting/types.d.ts +++ b/x-pack/plugins/reporting/types.d.ts @@ -107,11 +107,31 @@ export interface CryptoFactory { decrypt: (headers?: Record) => string; } +export interface TimeRangeParams { + timezone: string; + min: Date | string | number; + max: Date | string | number; +} + +type PostPayloadState = Partial<{ + state: { + query: any; + sort: any[]; + columns: string[]; // TODO + }; +}>; + +// retain POST payload data, needed for async +interface JobParamPostPayload extends PostPayloadState { + timeRange: TimeRangeParams; +} + // params that come into a request export interface JobParams { savedObjectType: string; savedObjectId: string; isImmediate: boolean; + post: JobParamPostPayload; panel?: any; // has to be resolved by the request handler visType?: string; // has to be resolved by the request handler } diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 5c542ea3e17a8..f9ad99e059442 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -187,6 +187,7 @@ export default async function ({ readConfigFile }) { '--server.uuid=5b2de169-2785-441b-ae8c-186a1936b17d', '--xpack.xpack_main.telemetry.enabled=false', '--xpack.maps.showMapsInspectorAdapter=true', + '--xpack.reporting.queue.pollInterval=3000', // make it explicitly the default '--xpack.security.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', // server restarts should not invalidate active sessions ], }, diff --git a/x-pack/test/reporting/api/generate/csv_saved_search.ts b/x-pack/test/reporting/api/generate/csv_saved_search.ts index 18cb791537bfa..488c93899f089 100644 --- a/x-pack/test/reporting/api/generate/csv_saved_search.ts +++ b/x-pack/test/reporting/api/generate/csv_saved_search.ts @@ -28,9 +28,13 @@ export default function({ getService }: { getService: any }) { const esArchiver = getService('esArchiver'); const supertestSvc = getService('supertest'); const generateAPI = { - getCsvFromSavedSearch: async (id: string, { timerange, state }: GenerateOpts) => { + getCsvFromSavedSearch: async ( + id: string, + { timerange, state }: GenerateOpts, + isImmediate = true + ) => { return await supertestSvc - .post(`/api/reporting/v1/generate/immediate/csv/saved-object/${id}`) + .post(`/api/reporting/v1/generate/${isImmediate ? 'immediate/' : ''}csv/saved-object/${id}`) .set('kbn-xsrf', 'xxx') .send({ timerange, state }); }, @@ -163,39 +167,22 @@ export default function({ getService }: { getService: any }) { // load test data that contains a saved search and documents await esArchiver.load('reporting/scripted'); + const params = { + searchId: 'search:f34bf440-5014-11e9-bce7-4dabcb8bef24', + 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: 'Fe*' } }] } } ] } } ] } } } // prettier-ignore + }, + isImmediate: true, + }; 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: { - query: { - bool: { - filter: [ - { - bool: { - filter: [ - { - bool: { - minimum_should_match: 1, - should: [{ query_string: { fields: ['name'], query: 'Fe*' } }], - }, - }, - ], - }, - }, - ], - }, - }, - }, - } + params.searchId, + params.postPayload, + params.isImmediate )) as supertest.Response; expect(resStatus).to.eql(200); @@ -232,5 +219,100 @@ export default function({ getService }: { getService: any }) { await esArchiver.unload('reporting/scripted'); }); }); + + describe('Non-Immediate', () => { + it('using queries in job params', async () => { + // load test data that contains a saved search and documents + await esArchiver.load('reporting/scripted'); + + const params = { + searchId: 'search:f34bf440-5014-11e9-bce7-4dabcb8bef24', + 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: 'Fe*' } }] } } ] } } ] } } } // prettier-ignore + }, + isImmediate: false, + }; + 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('application/json'); + const { + path: jobDownloadPath, + job: { index: jobIndex, jobtype: jobType, created_by: jobCreatedBy, payload: jobPayload }, + } = JSON.parse(resText); + + expect(jobDownloadPath.slice(0, 29)).to.equal('/api/reporting/jobs/download/'); + expect(jobIndex.slice(0, 11)).to.equal('.reporting-'); + expect(jobType).to.be('csv_from_savedobject'); + expect(jobCreatedBy).to.be('elastic'); + + const { + title: payloadTitle, + objects: payloadObjects, + jobParams: payloadParams, + } = jobPayload; + expect(payloadTitle).to.be('EVERYBABY2'); + expect(payloadObjects).to.be(null); // value for non-immediate + expect(payloadParams.savedObjectType).to.be('search'); + expect(payloadParams.savedObjectId).to.be('f34bf440-5014-11e9-bce7-4dabcb8bef24'); + expect(payloadParams.isImmediate).to.be(false); + + const { state: postParamState, timerange: postParamTimerange } = payloadParams.post; + expect(postParamState).to.eql({ + query: { bool: { filter: [ { bool: { filter: [ { bool: { minimum_should_match: 1, should: [{ query_string: { fields: ['name'], query: 'Fe*' } }] } } ] } } ] } } // prettier-ignore + }); + expect(postParamTimerange).to.eql({ + max: '1981-01-01T10:00:00.000Z', + min: '1979-01-01T10:00:00.000Z', + timezone: 'UTC', + }); + + const { + indexPatternSavedObjectId: payloadPanelIndexPatternSavedObjectId, + timerange: payloadPanelTimerange, + } = payloadParams.panel; + expect(payloadPanelIndexPatternSavedObjectId).to.be('89655130-5013-11e9-bce7-4dabcb8bef24'); + expect(payloadPanelTimerange).to.eql({ + timezone: 'UTC', + min: '1979-01-01T10:00:00.000Z', + max: '1981-01-01T10:00:00.000Z', + }); + + expect(payloadParams.visType).to.be('search'); + + // check the resource at jobDownloadPath + const downloadFromPath = async (downloadPath: string) => { + const { status, text, type } = await supertestSvc + .get(downloadPath) + .set('kbn-xsrf', 'xxx'); + return { + status, + text, + type, + }; + }; + + await new Promise(resolve => { + setTimeout(async () => { + const { status, text, type } = await downloadFromPath(jobDownloadPath); + expect(status).to.eql(200); + expect(type).to.eql('text/csv'); + expect(text).to.eql(CSV_RESULT_SCRIPTED_REQUERY); + resolve(); + }, 5000); // x-pack/test/functional/config settings are inherited, uses 3 seconds for polling interval. + }); + + await esArchiver.unload('reporting/scripted'); + }); + }); }); } From a22a1720f766122732eb8e1a39441db29bc73a98 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 15:49:29 -0700 Subject: [PATCH 13/20] fix an un-exported config mixin --- x-pack/test/api_integration/config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/config.js b/x-pack/test/api_integration/config.js index a9da26722c823..530028c30d552 100644 --- a/x-pack/test/api_integration/config.js +++ b/x-pack/test/api_integration/config.js @@ -18,7 +18,7 @@ import { SpacesServiceProvider, } from '../common/services'; -export default async function ({ readConfigFile }) { +export async function getApiIntegrationConfig({ readConfigFile }) { const kibanaAPITestsConfig = await readConfigFile(require.resolve('../../../test/api_integration/config.js')); const xPackFunctionalTestsConfig = await readConfigFile(require.resolve('../functional/config.js')); @@ -56,3 +56,5 @@ export default async function ({ readConfigFile }) { esTestCluster: xPackFunctionalTestsConfig.get('esTestCluster'), }; } + +export default getApiIntegrationConfig; From 486d6a639d17268b11a15e708ed499356a043508 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 16:33:18 -0700 Subject: [PATCH 14/20] fix space mapping --- .../functional/es_archives/reporting/scripted/mappings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/es_archives/reporting/scripted/mappings.json b/x-pack/test/functional/es_archives/reporting/scripted/mappings.json index 21df5f2ffb4cb..58e1d6c7d9c7e 100644 --- a/x-pack/test/functional/es_archives/reporting/scripted/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/scripted/mappings.json @@ -488,6 +488,9 @@ "_reserved": { "type": "boolean" }, + "disabledFeatures": { + "type": "keyword" + }, "color": { "type": "keyword" }, @@ -733,4 +736,4 @@ } } } -} \ No newline at end of file +} From 18281c6ac84c307ae725cc344b5896cc5a7dc1e3 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 17:24:07 -0700 Subject: [PATCH 15/20] oops --- .../export_types/csv_from_savedobject/index.d.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.d.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.d.ts index 84f22cac54f00..3f35f7e237f5b 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.d.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/index.d.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimeRangeParams } from '../../types'; - export interface SavedObjectServiceError { statusCode: number; error?: string; @@ -93,6 +91,12 @@ export interface IndexPatternSavedObject { }; } +export interface TimeRangeParams { + timezone: string; + min: Date | string | number; + max: Date | string | number; +} + export interface VisPanel { indexPatternSavedObjectId?: string; savedSearchObjectId?: string; From b0e594c32b7524e085036db8e8f5849b5147f728 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 17:29:43 -0700 Subject: [PATCH 16/20] fix from an experiment --- x-pack/plugins/reporting/types.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/reporting/types.d.ts b/x-pack/plugins/reporting/types.d.ts index 089db797abd86..fbe63687a0b8b 100644 --- a/x-pack/plugins/reporting/types.d.ts +++ b/x-pack/plugins/reporting/types.d.ts @@ -117,8 +117,8 @@ export interface JobParams { } export interface JobDocPayload { - basePath: string; - headers: Record; + basePath?: string; + headers?: Record; jobParams: JobParams; relativeUrl?: string; timeRange?: any; From 61c1c1b305b56653c7e83212c25756af2551e114 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 21:03:31 -0700 Subject: [PATCH 17/20] fix ts --- .../common/execute_job/add_force_now_query_string.ts | 2 +- x-pack/plugins/reporting/types.d.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/export_types/common/execute_job/add_force_now_query_string.ts b/x-pack/plugins/reporting/export_types/common/execute_job/add_force_now_query_string.ts index 10bf569bdc72d..fda91c51ef0c2 100644 --- a/x-pack/plugins/reporting/export_types/common/execute_job/add_force_now_query_string.ts +++ b/x-pack/plugins/reporting/export_types/common/execute_job/add_force_now_query_string.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore + import url from 'url'; import { getAbsoluteUrlFactory } from '../../../common/get_absolute_url'; import { ConditionalHeaders, JobDocPayload, KbnServer } from '../../../types'; diff --git a/x-pack/plugins/reporting/types.d.ts b/x-pack/plugins/reporting/types.d.ts index fbe63687a0b8b..bb3e041e32672 100644 --- a/x-pack/plugins/reporting/types.d.ts +++ b/x-pack/plugins/reporting/types.d.ts @@ -118,11 +118,13 @@ export interface JobParams { export interface JobDocPayload { basePath?: string; + forceNow?: string; headers?: Record; jobParams: JobParams; relativeUrl?: string; timeRange?: any; title: string; + urls?: string[]; type?: string | null; // string if completed job; null if incomplete job; objects?: string | null; // string if completed job; null if incomplete job; } From d67d468bf5fc6a763d041c35eed0a3021d54aa83 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 22:18:46 -0700 Subject: [PATCH 18/20] fix post property for immediate --- .../server/routes/generate_from_savedobject.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts index c8eb8fd9fcb74..9739691ec0413 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -14,7 +14,13 @@ import { API_BASE_URL_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/con import { getDocumentPayloadFactory } from './lib/get_document_payload'; import { createJobFactory, executeJobFactory } from '../../export_types/csv_from_savedobject'; -import { JobDocPayload, JobParamPostPayload, JobDocOutputExecuted, JobParams, KbnServer } from '../../types'; +import { + JobDocPayload, + JobParamPostPayload, + JobDocOutputExecuted, + JobParams, + KbnServer, +} from '../../types'; import { LevelLogger } from '../lib/level_logger'; import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types'; import { getRouteConfigFactoryReportingPre } from './lib/route_config_factories'; @@ -40,11 +46,12 @@ const getJobFromRouteHandler = async ( const { savedObjectType, savedObjectId } = request.params; let result: QueuedJobPayload; try { + const { timeRange, state } = request.payload as JobParamPostPayload; const jobParams: JobParams = { savedObjectType, savedObjectId, isImmediate: options.isImmediate, - post: request.payload as JobParamPostPayload, + post: { timeRange, state }, }; result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, request, h); } catch (err) { @@ -103,13 +110,13 @@ export function registerGenerateCsvFromSavedObject( options: routeOptions, handler: async (request: Request, h: ResponseToolkit) => { const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']); - const { savedObjectType, savedObjectId } = request.params; + const { timeRange, state } = request.payload as JobParamPostPayload; const jobParams: JobParams = { savedObjectType, savedObjectId, isImmediate: true, - post: {}, // FIXME + post: { timeRange, state }, }; const createJobFn = createJobFactory(server); const executeJobFn = executeJobFactory(server, request); From 4be22cb70668b638502b67ead5d8c4cbc3e28073 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 22:34:37 -0700 Subject: [PATCH 19/20] use the same spelling that kibana browser side does --- .../reporting/server/routes/generate_from_savedobject.ts | 8 ++++---- x-pack/plugins/reporting/types.d.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts index 9739691ec0413..70e47c59b7f9b 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -46,12 +46,12 @@ const getJobFromRouteHandler = async ( const { savedObjectType, savedObjectId } = request.params; let result: QueuedJobPayload; try { - const { timeRange, state } = request.payload as JobParamPostPayload; + const { timerange, state } = request.payload as JobParamPostPayload; const jobParams: JobParams = { savedObjectType, savedObjectId, isImmediate: options.isImmediate, - post: { timeRange, state }, + post: { timerange, state }, }; result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, request, h); } catch (err) { @@ -111,12 +111,12 @@ export function registerGenerateCsvFromSavedObject( handler: async (request: Request, h: ResponseToolkit) => { const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']); const { savedObjectType, savedObjectId } = request.params; - const { timeRange, state } = request.payload as JobParamPostPayload; + const { timerange, state } = request.payload as JobParamPostPayload; const jobParams: JobParams = { savedObjectType, savedObjectId, isImmediate: true, - post: { timeRange, state }, + post: { timerange, state }, }; const createJobFn = createJobFactory(server); const executeJobFn = executeJobFactory(server, request); diff --git a/x-pack/plugins/reporting/types.d.ts b/x-pack/plugins/reporting/types.d.ts index 919aaa06fccbd..f41a58bf8cb2c 100644 --- a/x-pack/plugins/reporting/types.d.ts +++ b/x-pack/plugins/reporting/types.d.ts @@ -123,7 +123,7 @@ type PostPayloadState = Partial<{ // retain POST payload data, needed for async interface JobParamPostPayload extends PostPayloadState { - timeRange: TimeRangeParams; + timerange: TimeRangeParams; } // params that come into a request From b55e5e8d70b80fe5737f8dfb1eb978164316413b Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 12 Apr 2019 23:12:01 -0700 Subject: [PATCH 20/20] post payload should pretty much be optional --- .../export_types/common/execute_job/get_custom_logo.ts | 1 + .../server/lib/generate_csv_search.ts | 2 +- .../server/routes/generate_from_savedobject.ts | 10 ++++++---- x-pack/plugins/reporting/types.d.ts | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts b/x-pack/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts index 23fe6e69f41f8..b41e4c8218614 100644 --- a/x-pack/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts +++ b/x-pack/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import { UI_SETTINGS_CUSTOM_PDF_LOGO } from '../../../common/constants'; import { ConditionalHeaders, JobDocPayload, KbnServer } from '../../../types'; diff --git a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts index f0eae018c9ed3..fdc3f0fac6751 100644 --- a/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts +++ b/x-pack/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts @@ -80,7 +80,7 @@ export async function generateCsvSearch( let payloadQuery: QueryFilter | undefined; let payloadSort: any[] = []; - if (jobParams.post.state) { + if (jobParams.post && jobParams.post.state) { ({ post: { state: { query: payloadQuery, sort: payloadSort = [] } }, } = jobParams); diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts index 70e47c59b7f9b..9339e33cac2c0 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -47,11 +47,12 @@ const getJobFromRouteHandler = async ( let result: QueuedJobPayload; try { const { timerange, state } = request.payload as JobParamPostPayload; + const post = timerange || state ? { timerange, state } : undefined; const jobParams: JobParams = { + isImmediate: options.isImmediate, savedObjectType, savedObjectId, - isImmediate: options.isImmediate, - post: { timerange, state }, + post, }; result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, request, h); } catch (err) { @@ -112,11 +113,12 @@ export function registerGenerateCsvFromSavedObject( const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']); const { savedObjectType, savedObjectId } = request.params; const { timerange, state } = request.payload as JobParamPostPayload; + const post = timerange || state ? { timerange, state } : undefined; const jobParams: JobParams = { + isImmediate: true, savedObjectType, savedObjectId, - isImmediate: true, - post: { timerange, state }, + post, }; const createJobFn = createJobFactory(server); const executeJobFn = executeJobFactory(server, request); diff --git a/x-pack/plugins/reporting/types.d.ts b/x-pack/plugins/reporting/types.d.ts index f41a58bf8cb2c..235bf04e58437 100644 --- a/x-pack/plugins/reporting/types.d.ts +++ b/x-pack/plugins/reporting/types.d.ts @@ -131,7 +131,7 @@ export interface JobParams { savedObjectType: string; savedObjectId: string; isImmediate: boolean; - post: JobParamPostPayload; + post?: JobParamPostPayload; panel?: any; // has to be resolved by the request handler visType?: string; // has to be resolved by the request handler }