From 723ab0009d4181d54d43bda2dd034695d46192ef Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 3 Dec 2019 14:28:13 -0700 Subject: [PATCH 01/17] [Reporting/NPMigration] typescriptify ExportTypeRegistry, remove from server.expose --- .../reporting/common/export_types_registry.js | 64 --------- .../common/lib/screenshots/index.ts | 14 +- .../reporting/export_types/csv/index.ts | 26 ++++ .../export_types/csv/server/index.ts | 22 --- .../csv_from_savedobject/index.ts | 21 +++ .../csv_from_savedobject/server/index.ts | 22 --- .../reporting/export_types/png/index.ts | 27 ++++ .../png/server/execute_job/index.ts | 18 ++- .../export_types/png/server/index.ts | 23 ---- .../png/server/lib/generate_png.ts | 9 +- .../export_types/printable_pdf/index.ts | 27 ++++ .../printable_pdf/server/execute_job/index.ts | 18 ++- .../printable_pdf/server/index.ts | 23 ---- .../printable_pdf/server/lib/generate_pdf.ts | 9 +- x-pack/legacy/plugins/reporting/index.ts | 28 ++-- .../reporting/server/lib/create_queue.ts | 19 ++- .../server/lib/create_worker.test.ts | 13 +- .../reporting/server/lib/create_worker.ts | 20 ++- .../reporting/server/lib/enqueue_job.ts | 19 ++- .../server/lib/export_types_registry.ts | 126 ++++++++++++++---- .../plugins/reporting/server/lib/index.ts | 5 +- .../plugins/reporting/server/routes/index.ts | 19 ++- .../plugins/reporting/server/routes/jobs.ts | 5 +- .../server/routes/lib/get_document_payload.ts | 8 +- .../server/routes/lib/job_response_handler.js | 4 +- ..._handler.js => get_export_type_handler.ts} | 25 ++-- .../server/usage/get_reporting_usage.ts | 13 +- .../usage/reporting_usage_collector.test.js | 47 +++++-- .../server/usage/reporting_usage_collector.ts | 18 ++- x-pack/legacy/plugins/reporting/types.d.ts | 43 +++--- 30 files changed, 438 insertions(+), 297 deletions(-) delete mode 100644 x-pack/legacy/plugins/reporting/common/export_types_registry.js create mode 100644 x-pack/legacy/plugins/reporting/export_types/csv/index.ts delete mode 100644 x-pack/legacy/plugins/reporting/export_types/csv/server/index.ts delete mode 100644 x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/index.ts create mode 100644 x-pack/legacy/plugins/reporting/export_types/png/index.ts delete mode 100644 x-pack/legacy/plugins/reporting/export_types/png/server/index.ts create mode 100644 x-pack/legacy/plugins/reporting/export_types/printable_pdf/index.ts delete mode 100644 x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/index.ts rename x-pack/legacy/plugins/reporting/server/usage/{get_export_type_handler.js => get_export_type_handler.ts} (61%) diff --git a/x-pack/legacy/plugins/reporting/common/export_types_registry.js b/x-pack/legacy/plugins/reporting/common/export_types_registry.js deleted file mode 100644 index 39abd8911e751..0000000000000 --- a/x-pack/legacy/plugins/reporting/common/export_types_registry.js +++ /dev/null @@ -1,64 +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 { isString } from 'lodash'; - -export class ExportTypesRegistry { - - constructor() { - this._map = new Map(); - } - - register(item) { - if (!isString(item.id)) { - throw new Error(`'item' must have a String 'id' property `); - } - - if (this._map.has(item.id)) { - throw new Error(`'item' with id ${item.id} has already been registered`); - } - - this._map.set(item.id, item); - } - - getAll() { - return this._map.values(); - } - - getSize() { - return this._map.size; - } - - getById(id) { - if (!this._map.has(id)) { - throw new Error(`Unknown id ${id}`); - } - - return this._map.get(id); - } - - get(callback) { - let result; - for (const value of this._map.values()) { - if (!callback(value)) { - continue; - } - - if (result) { - throw new Error('Found multiple items matching predicate.'); - } - - result = value; - } - - if (!result) { - throw new Error('Found no items matching predicate'); - } - - return result; - } - -} diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts index 2b4411584d752..152ef32e331b9 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts @@ -6,8 +6,12 @@ import * as Rx from 'rxjs'; import { first, mergeMap } from 'rxjs/operators'; -import { ServerFacade, CaptureConfig } from '../../../../types'; -import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver'; +import { + ServerFacade, + CaptureConfig, + HeadlessChromiumDriverFactory, + HeadlessChromiumDriver as HeadlessBrowser, +} from '../../../../types'; import { ElementsPositionAndAttribute, ScreenshotResults, @@ -26,10 +30,12 @@ import { getElementPositionAndAttributes } from './get_element_position_data'; import { getScreenshots } from './get_screenshots'; import { skipTelemetry } from './skip_telemetry'; -export function screenshotsObservableFactory(server: ServerFacade) { +export function screenshotsObservableFactory( + server: ServerFacade, + browserDriverFactory: HeadlessChromiumDriverFactory +) { const config = server.config(); const captureConfig: CaptureConfig = config.get('xpack.reporting.capture'); - const { browserDriverFactory } = server.plugins.reporting!; return function screenshotsObservable({ logger, diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/index.ts b/x-pack/legacy/plugins/reporting/export_types/csv/index.ts new file mode 100644 index 0000000000000..81876f07062ae --- /dev/null +++ b/x-pack/legacy/plugins/reporting/export_types/csv/index.ts @@ -0,0 +1,26 @@ +/* + * 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 { CSV_JOB_TYPE as jobType } from '../../common/constants'; +import { ExportTypeDefinition, ESQueueCreateJobFn, ESQueueWorkerExecuteFn } from '../../types'; +import { metadata } from './metadata'; +import { createJobFactory } from './server/create_job'; +import { executeJobFactory } from './server/execute_job'; +import { JobParamsDiscoverCsv, JobDocPayloadDiscoverCsv } from './types'; + +export const getExportType = (): ExportTypeDefinition< + JobParamsDiscoverCsv, + ESQueueCreateJobFn, + JobDocPayloadDiscoverCsv, + ESQueueWorkerExecuteFn +> => ({ + ...metadata, + jobType, + jobContentExtension: 'csv', + createJobFactory, + executeJobFactory, + validLicenses: ['trial', 'basic', 'standard', 'gold', 'platinum'], +}); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/index.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/index.ts deleted file mode 100644 index d752cdcd9779d..0000000000000 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ExportTypesRegistry } from '../../../types'; -import { createJobFactory } from './create_job'; -import { executeJobFactory } from './execute_job'; -import { metadata } from '../metadata'; -import { CSV_JOB_TYPE as jobType } from '../../../common/constants'; - -export function register(registry: ExportTypesRegistry) { - registry.register({ - ...metadata, - jobType, - jobContentExtension: 'csv', - createJobFactory, - executeJobFactory, - validLicenses: ['trial', 'basic', 'standard', 'gold', 'platinum'], - }); -} diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/index.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/index.ts index 68ad4a4b49155..e38e2ba02dbaa 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/index.ts @@ -4,9 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ +import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants'; +import { ExportTypeDefinition, ImmediateCreateJobFn, ImmediateExecuteFn } from '../../types'; +import { createJobFactory } from './server/create_job'; +import { executeJobFactory } from './server/execute_job'; +import { metadata } from './metadata'; +import { JobParamsPanelCsv } from './types'; + /* * 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'; + +export const getExportType = (): ExportTypeDefinition< + JobParamsPanelCsv, + ImmediateCreateJobFn, + JobParamsPanelCsv, + ImmediateExecuteFn +> => ({ + ...metadata, + jobType: CSV_FROM_SAVEDOBJECT_JOB_TYPE, + jobContentExtension: 'csv', + createJobFactory, + executeJobFactory, + validLicenses: ['trial', 'basic', 'standard', 'gold', 'platinum'], +}); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/index.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/index.ts deleted file mode 100644 index b614fd3c681b3..0000000000000 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; -import { ExportTypesRegistry } from '../../../types'; -import { metadata } from '../metadata'; -import { createJobFactory } from './create_job'; -import { executeJobFactory } from './execute_job'; - -export function register(registry: ExportTypesRegistry) { - registry.register({ - ...metadata, - jobType: CSV_FROM_SAVEDOBJECT_JOB_TYPE, - jobContentExtension: 'csv', - createJobFactory, - executeJobFactory, - validLicenses: ['trial', 'basic', 'standard', 'gold', 'platinum'], - }); -} diff --git a/x-pack/legacy/plugins/reporting/export_types/png/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/index.ts new file mode 100644 index 0000000000000..6e22416b5aa9c --- /dev/null +++ b/x-pack/legacy/plugins/reporting/export_types/png/index.ts @@ -0,0 +1,27 @@ +/* + * 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 { PNG_JOB_TYPE as jobType } from '../../common/constants'; +import { ExportTypeDefinition, ESQueueCreateJobFn, ESQueueWorkerExecuteFn } from '../../types'; +import { createJobFactory } from './server/create_job'; +import { executeJobFactory } from './server/execute_job'; +import { metadata } from './metadata'; +import { JobParamsPNG, JobDocPayloadPNG } from './types'; + +export const getExportType = (): ExportTypeDefinition< + JobParamsPNG, + ESQueueCreateJobFn, + JobDocPayloadPNG, + ESQueueWorkerExecuteFn +> => ({ + ...metadata, + jobType, + jobContentEncoding: 'base64', + jobContentExtension: 'PNG', + createJobFactory, + executeJobFactory, + validLicenses: ['trial', 'standard', 'gold', 'platinum'], +}); diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts index 6678a83079d31..613cd4d3e13e2 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts @@ -7,7 +7,12 @@ import * as Rx from 'rxjs'; import { mergeMap, catchError, map, takeUntil } from 'rxjs/operators'; import { PLUGIN_ID, PNG_JOB_TYPE } from '../../../../common/constants'; -import { ServerFacade, ExecuteJobFactory, ESQueueWorkerExecuteFn } from '../../../../types'; +import { + ServerFacade, + ExecuteJobFactory, + ESQueueWorkerExecuteFn, + HeadlessChromiumDriverFactory, +} from '../../../../types'; import { LevelLogger } from '../../../../server/lib'; import { decryptJobHeaders, @@ -20,8 +25,15 @@ import { generatePngObservableFactory } from '../lib/generate_png'; export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn(server: ServerFacade) { - const generatePngObservable = generatePngObservableFactory(server); +>> = function executeJobFactoryFn( + server: ServerFacade, + { browserDriverFactory }: { browserDriverFactory?: HeadlessChromiumDriverFactory } = {} +) { + if (!browserDriverFactory) { + throw new Error('Reporting browser driver factory must be passed for PNG job execution'); + } + + const generatePngObservable = generatePngObservableFactory(server, browserDriverFactory); const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PNG_JOB_TYPE, 'execute']); return function executeJob( diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/index.ts deleted file mode 100644 index a569719f02324..0000000000000 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/index.ts +++ /dev/null @@ -1,23 +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 { ExportTypesRegistry } from '../../../types'; -import { createJobFactory } from './create_job'; -import { executeJobFactory } from './execute_job'; -import { metadata } from '../metadata'; -import { PNG_JOB_TYPE as jobType } from '../../../common/constants'; - -export function register(registry: ExportTypesRegistry) { - registry.register({ - ...metadata, - jobType, - jobContentEncoding: 'base64', - jobContentExtension: 'PNG', - createJobFactory, - executeJobFactory, - validLicenses: ['trial', 'standard', 'gold', 'platinum'], - }); -} diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts index 90aeea25db858..e2b1474515786 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts @@ -7,13 +7,16 @@ import * as Rx from 'rxjs'; import { map } from 'rxjs/operators'; import { LevelLogger } from '../../../../server/lib'; -import { ServerFacade, ConditionalHeaders } from '../../../../types'; +import { ServerFacade, HeadlessChromiumDriverFactory, ConditionalHeaders } from '../../../../types'; import { screenshotsObservableFactory } from '../../../common/lib/screenshots'; import { PreserveLayout } from '../../../common/layouts/preserve_layout'; import { LayoutParams } from '../../../common/layouts/layout'; -export function generatePngObservableFactory(server: ServerFacade) { - const screenshotsObservable = screenshotsObservableFactory(server); +export function generatePngObservableFactory( + server: ServerFacade, + browserDriverFactory: HeadlessChromiumDriverFactory +) { + const screenshotsObservable = screenshotsObservableFactory(server, browserDriverFactory); return function generatePngObservable( logger: LevelLogger, diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/index.ts new file mode 100644 index 0000000000000..f2e9c9950ee9f --- /dev/null +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/index.ts @@ -0,0 +1,27 @@ +/* + * 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 { PDF_JOB_TYPE as jobType } from '../../common/constants'; +import { ExportTypeDefinition, ESQueueCreateJobFn, ESQueueWorkerExecuteFn } from '../../types'; +import { createJobFactory } from './server/create_job'; +import { executeJobFactory } from './server/execute_job'; +import { metadata } from './metadata'; +import { JobParamsPDF, JobDocPayloadPDF } from './types'; + +export const getExportType = (): ExportTypeDefinition< + JobParamsPDF, + ESQueueCreateJobFn, + JobDocPayloadPDF, + ESQueueWorkerExecuteFn +> => ({ + ...metadata, + jobType, + jobContentEncoding: 'base64', + jobContentExtension: 'pdf', + createJobFactory, + executeJobFactory, + validLicenses: ['trial', 'standard', 'gold', 'platinum'], +}); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts index 543d5b587906d..68048b2456a7f 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts @@ -6,7 +6,12 @@ import * as Rx from 'rxjs'; import { mergeMap, catchError, map, takeUntil } from 'rxjs/operators'; -import { ExecuteJobFactory, ESQueueWorkerExecuteFn, ServerFacade } from '../../../../types'; +import { + ServerFacade, + ExecuteJobFactory, + ESQueueWorkerExecuteFn, + HeadlessChromiumDriverFactory, +} from '../../../../types'; import { JobDocPayloadPDF } from '../../types'; import { PLUGIN_ID, PDF_JOB_TYPE } from '../../../../common/constants'; import { LevelLogger } from '../../../../server/lib'; @@ -21,8 +26,15 @@ import { export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn(server: ServerFacade) { - const generatePdfObservable = generatePdfObservableFactory(server); +>> = function executeJobFactoryFn( + server: ServerFacade, + { browserDriverFactory }: { browserDriverFactory?: HeadlessChromiumDriverFactory } = {} +) { + if (!browserDriverFactory) { + throw new Error('Reporting browser driver factory must be passed for PDF job execution'); + } + + const generatePdfObservable = generatePdfObservableFactory(server, browserDriverFactory); const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PDF_JOB_TYPE, 'execute']); return function executeJob( diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/index.ts deleted file mode 100644 index df798a7af23ec..0000000000000 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/index.ts +++ /dev/null @@ -1,23 +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 { ExportTypesRegistry } from '../../../types'; -import { createJobFactory } from './create_job'; -import { executeJobFactory } from './execute_job'; -import { metadata } from '../metadata'; -import { PDF_JOB_TYPE as jobType } from '../../../common/constants'; - -export function register(registry: ExportTypesRegistry) { - registry.register({ - ...metadata, - jobType, - jobContentEncoding: 'base64', - jobContentExtension: 'pdf', - createJobFactory, - executeJobFactory, - validLicenses: ['trial', 'standard', 'gold', 'platinum'], - }); -} diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts index 1e0245ebd513f..898a13a2dfe80 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts @@ -8,7 +8,7 @@ import * as Rx from 'rxjs'; import { toArray, mergeMap } from 'rxjs/operators'; import { groupBy } from 'lodash'; import { LevelLogger } from '../../../../server/lib'; -import { ServerFacade, ConditionalHeaders } from '../../../../types'; +import { ServerFacade, HeadlessChromiumDriverFactory, ConditionalHeaders } from '../../../../types'; // @ts-ignore untyped module import { pdf } from './pdf'; import { screenshotsObservableFactory } from '../../../common/lib/screenshots'; @@ -26,8 +26,11 @@ const getTimeRange = (urlScreenshots: ScreenshotResults[]) => { return null; }; -export function generatePdfObservableFactory(server: ServerFacade) { - const screenshotsObservable = screenshotsObservableFactory(server); +export function generatePdfObservableFactory( + server: ServerFacade, + browserDriverFactory: HeadlessChromiumDriverFactory +) { + const screenshotsObservable = screenshotsObservableFactory(server, browserDriverFactory); const captureConcurrency = 1; return function generatePdfObservable( diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index 9add3accd262f..276a5cca8fcf5 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -11,10 +11,11 @@ import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; import { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status'; import { registerRoutes } from './server/routes'; import { + createQueueFactory, + enqueueJobFactory, LevelLogger, checkLicenseFactory, - createQueueFactory, - exportTypesRegistryFactory, + getExportTypesRegistry, runValidations, } from './server/lib'; import { config as reportingConfig } from './config'; @@ -74,20 +75,23 @@ export const reporting = (kibana: any) => { // TODO: Decouple Hapi: Build a server facade object based on the server to // pass through to the libs. Do not pass server directly async init(server: ServerFacade) { + const exportTypesRegistry = getExportTypesRegistry(); + let isCollectorReady = false; // Register a function with server to manage the collection of usage stats const { usageCollection } = server.newPlatform.setup.plugins; - registerReportingUsageCollector(usageCollection, server, () => isCollectorReady); + registerReportingUsageCollector( + usageCollection, + server, + () => isCollectorReady, + exportTypesRegistry + ); const logger = LevelLogger.createForServer(server, [PLUGIN_ID]); - const [exportTypesRegistry, browserFactory] = await Promise.all([ - exportTypesRegistryFactory(server), - createBrowserDriverFactory(server), - ]); - server.expose('exportTypesRegistry', exportTypesRegistry); + const browserDriverFactory = await createBrowserDriverFactory(server); logConfiguration(server, logger); - runValidations(server, logger, browserFactory); + runValidations(server, logger, browserDriverFactory); const { xpack_main: xpackMainPlugin } = server.plugins; mirrorPluginStatus(xpackMainPlugin, this); @@ -101,11 +105,11 @@ export const reporting = (kibana: any) => { // Post initialization of the above code, the collector is now ready to fetch its data isCollectorReady = true; - server.expose('browserDriverFactory', browserFactory); - server.expose('queue', createQueueFactory(server)); + const esqueue = createQueueFactory(server, { exportTypesRegistry, browserDriverFactory }); + const enqueueJob = enqueueJobFactory(server, { exportTypesRegistry, esqueue }); // Reporting routes - registerRoutes(server, logger); + registerRoutes(server, exportTypesRegistry, enqueueJob, logger); }, deprecations({ unused }: any) { diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts b/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts index 174c6d587e523..5cf760250ec0e 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts @@ -5,7 +5,12 @@ */ import { PLUGIN_ID } from '../../common/constants'; -import { ServerFacade, QueueConfig } from '../../types'; +import { + ServerFacade, + ExportTypesRegistry, + HeadlessChromiumDriverFactory, + QueueConfig, +} from '../../types'; // @ts-ignore import { Esqueue } from './esqueue'; import { createWorkerFactory } from './create_worker'; @@ -13,7 +18,15 @@ import { LevelLogger } from './level_logger'; // @ts-ignore import { createTaggedLogger } from './create_tagged_logger'; // TODO remove createTaggedLogger once esqueue is removed -export function createQueueFactory(server: ServerFacade): Esqueue { +interface CreateQueueFactoryOpts { + exportTypesRegistry: ExportTypesRegistry; + browserDriverFactory: HeadlessChromiumDriverFactory; +} + +export function createQueueFactory( + server: ServerFacade, + { exportTypesRegistry, browserDriverFactory }: CreateQueueFactoryOpts +): Esqueue { const queueConfig: QueueConfig = server.config().get('xpack.reporting.queue'); const index = server.config().get('xpack.reporting.index'); @@ -29,7 +42,7 @@ export function createQueueFactory(server: ServerFacade): Esqueue { if (queueConfig.pollEnabled) { // create workers to poll the index for idle jobs waiting to be claimed and executed - const createWorker = createWorkerFactory(server); + const createWorker = createWorkerFactory(server, { exportTypesRegistry, browserDriverFactory }); createWorker(queue); } else { const logger = LevelLogger.createForServer(server, [PLUGIN_ID, 'create_queue']); diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts index afad8f096a8bb..8bf3d299cdb4e 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts @@ -5,7 +5,7 @@ */ import * as sinon from 'sinon'; -import { ServerFacade } from '../../types'; +import { ServerFacade, ExportTypesRegistry, HeadlessChromiumDriverFactory } from '../../types'; import { createWorkerFactory } from './create_worker'; // @ts-ignore import { Esqueue } from './esqueue'; @@ -44,7 +44,10 @@ describe('Create Worker', () => { }); test('Creates a single Esqueue worker for Reporting', async () => { - const createWorker = createWorkerFactory(getMockServer()); + const createWorker = createWorkerFactory(getMockServer(), { + exportTypesRegistry: {} as ExportTypesRegistry, + browserDriverFactory: {} as HeadlessChromiumDriverFactory, + }); const registerWorkerSpy = sinon.spy(queue, 'registerWorker'); createWorker(queue); @@ -75,7 +78,11 @@ Object { { executeJobFactory: executeJobFactoryStub }, { executeJobFactory: executeJobFactoryStub }, { executeJobFactory: executeJobFactoryStub }, - ]) + ]), + { + exportTypesRegistry: {} as ExportTypesRegistry, + browserDriverFactory: {} as HeadlessChromiumDriverFactory, + } ); const registerWorkerSpy = sinon.spy(queue, 'registerWorker'); diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts index 01f59099a1d99..f0abddd6e6ce2 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts @@ -5,6 +5,7 @@ */ import { PLUGIN_ID } from '../../common/constants'; +import { ExportTypesRegistry, HeadlessChromiumDriverFactory } from '../../types'; import { CancellationToken } from '../../common/cancellation_token'; import { ESQueueInstance, @@ -21,14 +22,25 @@ import { import { events as esqueueEvents } from './esqueue'; import { LevelLogger } from './level_logger'; -export function createWorkerFactory(server: ServerFacade) { +interface CreateWorkerFactoryOpts { + exportTypesRegistry: ExportTypesRegistry; + browserDriverFactory: HeadlessChromiumDriverFactory; +} + +export function createWorkerFactory( + server: ServerFacade, + { exportTypesRegistry, browserDriverFactory }: CreateWorkerFactoryOpts +) { + if (!browserDriverFactory) { + throw new Error('need that browser drver factory'); + } + type JobDocPayloadType = JobDocPayload; const config = server.config(); const logger = LevelLogger.createForServer(server, [PLUGIN_ID, 'queue-worker']); const queueConfig: QueueConfig = config.get('xpack.reporting.queue'); const kibanaName: string = config.get('server.name'); const kibanaId: string = config.get('server.uuid'); - const { exportTypesRegistry } = server.plugins.reporting!; // Once more document types are added, this will need to be passed in return function createWorker(queue: ESQueueInstance) { @@ -41,8 +53,8 @@ export function createWorkerFactory(server: ServerFacade) { for (const exportType of exportTypesRegistry.getAll() as Array< ExportTypeDefinition >) { - const executeJobFactory = exportType.executeJobFactory(server); - jobExecutors.set(exportType.jobType, executeJobFactory); + const jobExecutor = exportType.executeJobFactory(server, { browserDriverFactory }); + jobExecutors.set(exportType.jobType, jobExecutor); } const workerFn = (jobSource: JobSource, ...workerRestArgs: any[]) => { diff --git a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts index a8cefa3fdc49b..731ea071bda27 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts @@ -14,6 +14,7 @@ import { ServerFacade, RequestFacade, Logger, + ExportTypesRegistry, CaptureConfig, QueueConfig, ConditionalHeaders, @@ -26,13 +27,20 @@ interface ConfirmedJob { _primary_term: number; } -export function enqueueJobFactory(server: ServerFacade) { +interface EnqueueJobFactoryOpts { + exportTypesRegistry: ExportTypesRegistry; + esqueue: any; +} + +export function enqueueJobFactory( + server: ServerFacade, + { exportTypesRegistry, esqueue }: EnqueueJobFactoryOpts +) { const config = server.config(); const captureConfig: CaptureConfig = config.get('xpack.reporting.capture'); const browserType = captureConfig.browser.type; const maxAttempts = captureConfig.maxAttempts; const queueConfig: QueueConfig = config.get('xpack.reporting.queue'); - const { exportTypesRegistry, queue: jobQueue } = server.plugins.reporting!; return async function enqueueJob( parentLogger: Logger, @@ -46,6 +54,11 @@ export function enqueueJobFactory(server: ServerFacade) { const logger = parentLogger.clone(['queue-job']); const exportType = exportTypesRegistry.getById(exportTypeId); + + if (exportType == null) { + throw new Error(`Export type ${exportTypeId} does not exist in the registry!`); + } + const createJob = exportType.createJobFactory(server) as CreateJobFn; const payload = await createJob(jobParams, headers, request); @@ -57,7 +70,7 @@ export function enqueueJobFactory(server: ServerFacade) { }; return new Promise((resolve, reject) => { - const job = jobQueue.addJob(exportType.jobType, payload, options); + const job = esqueue.addJob(exportType.jobType, payload, options); job.on(esqueueEvents.EVENT_JOB_CREATED, (createdJob: ConfirmedJob) => { if (createdJob.id === job.id) { diff --git a/x-pack/legacy/plugins/reporting/server/lib/export_types_registry.ts b/x-pack/legacy/plugins/reporting/server/lib/export_types_registry.ts index af1457ab52fe2..d3801ae6d1ba4 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/export_types_registry.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/export_types_registry.ts @@ -4,40 +4,108 @@ * you may not use this file except in compliance with the Elastic License. */ -import { resolve as pathResolve } from 'path'; -import glob from 'glob'; -import { ServerFacade } from '../../types'; -import { PLUGIN_ID } from '../../common/constants'; -import { oncePerServer } from './once_per_server'; -import { LevelLogger } from './level_logger'; -// @ts-ignore untype module TODO -import { ExportTypesRegistry } from '../../common/export_types_registry'; - -function scan(pattern: string) { - return new Promise((resolve, reject) => { - glob(pattern, {}, (err, files) => { - if (err) { - return reject(err); +import memoizeOne from 'memoize-one'; +import { isString } from 'lodash'; +import { getExportType as getTypeCsv } from '../../export_types/csv'; +import { getExportType as getTypeCsvFromSavedObject } from '../../export_types/csv_from_savedobject'; +import { getExportType as getTypePng } from '../../export_types/png'; +import { getExportType as getTypePrintablePdf } from '../../export_types/printable_pdf'; +import { ExportTypeDefinition } from '../../types'; + +type GetCallbackFn = ( + item: ExportTypeDefinition +) => boolean; +// => ExportTypeDefinition + +export class ExportTypesRegistry { + private _map: Map> = new Map(); + + constructor() {} + + register( + item: ExportTypeDefinition + ): void { + if (!isString(item.id)) { + throw new Error(`'item' must have a String 'id' property `); + } + + if (this._map.has(item.id)) { + throw new Error(`'item' with id ${item.id} has already been registered`); + } + + this._map.set(item.id, item); + } + + getAll() { + return Array.from(this._map.values()); + } + + getSize() { + return this._map.size; + } + + getById( + id: string + ): ExportTypeDefinition { + if (!this._map.has(id)) { + throw new Error(`Unknown id ${id}`); + } + + return this._map.get(id) as ExportTypeDefinition< + JobParamsType, + CreateJobFnType, + JobPayloadType, + ExecuteJobFnType + >; + } + + get( + findType: GetCallbackFn + ): ExportTypeDefinition { + let result; + for (const value of this._map.values()) { + if (!findType(value)) { + continue; // try next value } + const foundResult: ExportTypeDefinition< + JobParamsType, + CreateJobFnType, + JobPayloadType, + ExecuteJobFnType + > = value; - resolve(files); - }); - }); -} + if (result) { + throw new Error('Found multiple items matching predicate.'); + } + + result = foundResult; + } -const pattern = pathResolve(__dirname, '../../export_types/*/server/index.[jt]s'); -async function exportTypesRegistryFn(server: ServerFacade) { - const logger = LevelLogger.createForServer(server, [PLUGIN_ID, 'exportTypes']); - const exportTypesRegistry = new ExportTypesRegistry(); - const files: string[] = (await scan(pattern)) as string[]; + if (!result) { + throw new Error('Found no items matching predicate'); + } + + return result; + } +} - files.forEach(file => { - logger.debug(`Found exportType at ${file}`); +function getExportTypesRegistryFn(): ExportTypesRegistry { + const registry = new ExportTypesRegistry(); - const { register } = require(file); // eslint-disable-line @typescript-eslint/no-var-requires - register(exportTypesRegistry); + /* this replaces the previously async method of registering export types, + * where this would run a directory scan and types would be registered via + * discovery */ + const getTypeFns: Array<() => ExportTypeDefinition> = [ + getTypeCsv, + getTypeCsvFromSavedObject, + getTypePng, + getTypePrintablePdf, + ]; + getTypeFns.forEach(getType => { + registry.register(getType()); }); - return exportTypesRegistry; + return registry; } -export const exportTypesRegistryFactory = oncePerServer(exportTypesRegistryFn); +// FIXME: is this the best way to return a singleton? +export const getExportTypesRegistry = memoizeOne(getExportTypesRegistryFn); diff --git a/x-pack/legacy/plugins/reporting/server/lib/index.ts b/x-pack/legacy/plugins/reporting/server/lib/index.ts index b11f7bd95d9ef..50d1a276b6b5d 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/index.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/index.ts @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -export { exportTypesRegistryFactory } from './export_types_registry'; +export { getExportTypesRegistry } from './export_types_registry'; // @ts-ignore untyped module export { checkLicenseFactory } from './check_license'; export { LevelLogger } from './level_logger'; -export { createQueueFactory } from './create_queue'; export { cryptoFactory } from './crypto'; export { oncePerServer } from './once_per_server'; export { runValidations } from './validate'; +export { createQueueFactory } from './create_queue'; +export { enqueueJobFactory } from './enqueue_job'; diff --git a/x-pack/legacy/plugins/reporting/server/routes/index.ts b/x-pack/legacy/plugins/reporting/server/routes/index.ts index c48a37a36812e..281654a738895 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/index.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/index.ts @@ -6,20 +6,29 @@ import boom from 'boom'; import { API_BASE_URL } from '../../common/constants'; -import { ServerFacade, RequestFacade, ReportingResponseToolkit, Logger } from '../../types'; -import { enqueueJobFactory } from '../lib/enqueue_job'; +import { + ServerFacade, + RequestFacade, + ExportTypesRegistry, + ReportingResponseToolkit, + Logger, +} from '../../types'; import { registerGenerateFromJobParams } from './generate_from_jobparams'; import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject'; import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; import { registerJobs } from './jobs'; import { registerLegacy } from './legacy'; -export function registerRoutes(server: ServerFacade, logger: Logger) { +export function registerRoutes( + server: ServerFacade, + exportTypesRegistry: ExportTypesRegistry, + enqueueJob: any, + logger: Logger +) { const config = server.config(); const DOWNLOAD_BASE_URL = config.get('server.basePath') + `${API_BASE_URL}/jobs/download`; // @ts-ignore TODO const { errors: esErrors } = server.plugins.elasticsearch.getCluster('admin'); - const enqueueJob = enqueueJobFactory(server); /* * Generates enqueued job details to use in responses @@ -68,5 +77,5 @@ export function registerRoutes(server: ServerFacade, logger: Logger) { registerGenerateCsvFromSavedObjectImmediate(server, logger); } - registerJobs(server); + registerJobs(server, exportTypesRegistry); } diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts index 71d9f0d3ae13b..76d4d4d9e9978 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts @@ -8,6 +8,7 @@ import boom from 'boom'; import { API_BASE_URL } from '../../common/constants'; import { ServerFacade, + ExportTypesRegistry, RequestFacade, ReportingResponseToolkit, JobDocOutput, @@ -24,7 +25,7 @@ import { const MAIN_ENTRY = `${API_BASE_URL}/jobs`; -export function registerJobs(server: ServerFacade) { +export function registerJobs(server: ServerFacade, exportTypesRegistry: ExportTypesRegistry) { const jobsQuery = jobsQueryFactory(server); const getRouteConfig = getRouteConfigFactoryManagementPre(server); const getRouteConfigDownload = getRouteConfigFactoryDownloadPre(server); @@ -119,7 +120,7 @@ export function registerJobs(server: ServerFacade) { }); // trigger a download of the output from a job - const jobResponseHandler = jobResponseHandlerFactory(server); + const jobResponseHandler = jobResponseHandlerFactory(server, exportTypesRegistry); server.route({ path: `${MAIN_ENTRY}/download/{docId}`, method: 'GET', diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/get_document_payload.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/get_document_payload.ts index a69c19c006b61..c3a30f9dda454 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/get_document_payload.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/get_document_payload.ts @@ -9,6 +9,7 @@ import * as _ from 'lodash'; import contentDisposition from 'content-disposition'; import { ServerFacade, + ExportTypesRegistry, ExportTypeDefinition, JobDocExecuted, JobDocOutputExecuted, @@ -40,9 +41,10 @@ const getReportingHeaders = (output: JobDocOutputExecuted, exportType: ExportTyp return metaDataHeaders; }; -export function getDocumentPayloadFactory(server: ServerFacade) { - const exportTypesRegistry = server.plugins.reporting!.exportTypesRegistry; - +export function getDocumentPayloadFactory( + server: ServerFacade, + exportTypesRegistry: ExportTypesRegistry +) { function encodeContent(content: string | null, exportType: ExportTypeType) { switch (exportType.jobContentEncoding) { case 'base64': diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.js b/x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.js index 758c50816c381..6bc370506a255 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.js +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.js @@ -9,9 +9,9 @@ import { jobsQueryFactory } from '../../lib/jobs_query'; import { WHITELISTED_JOB_CONTENT_TYPES } from '../../../common/constants'; import { getDocumentPayloadFactory } from './get_document_payload'; -export function jobResponseHandlerFactory(server) { +export function jobResponseHandlerFactory(server, exportTypesRegistry) { const jobsQuery = jobsQueryFactory(server); - const getDocumentPayload = getDocumentPayloadFactory(server); + const getDocumentPayload = getDocumentPayloadFactory(server, exportTypesRegistry); return function jobResponseHandler(validJobTypes, user, h, params, opts = {}) { const { docId } = params; diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_export_type_handler.js b/x-pack/legacy/plugins/reporting/server/usage/get_export_type_handler.ts similarity index 61% rename from x-pack/legacy/plugins/reporting/server/usage/get_export_type_handler.js rename to x-pack/legacy/plugins/reporting/server/usage/get_export_type_handler.ts index a1949b21aa086..f8913a0dcea6b 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_export_type_handler.js +++ b/x-pack/legacy/plugins/reporting/server/usage/get_export_type_handler.ts @@ -4,17 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { exportTypesRegistryFactory } from '../lib/export_types_registry'; +import { XPackMainPlugin } from '../../../xpack_main/xpack_main'; +import { ExportTypesRegistry } from '../lib/export_types_registry'; /* * Gets a handle to the Reporting export types registry and returns a few * functions for examining them - * @param {Object} server: Kibana server * @return {Object} export type handler */ -export async function getExportTypesHandler(server) { - const exportTypesRegistry = await exportTypesRegistryFactory(server); - +export function getExportTypesHandler(exportTypesRegistry: ExportTypesRegistry) { return { /* * Based on the X-Pack license and which export types are available, @@ -23,12 +21,17 @@ export async function getExportTypesHandler(server) { * @param {Object} xpackInfo: xpack_main plugin info object * @return {Object} availability of each export type */ - getAvailability(xpackInfo) { - const exportTypesAvailability = {}; + getAvailability(xpackInfo: XPackMainPlugin['info']) { + const exportTypesAvailability: { [exportType: string]: boolean } = {}; const xpackInfoAvailable = xpackInfo && xpackInfo.isAvailable(); - const licenseType = xpackInfo.license.getType(); - for(const exportType of exportTypesRegistry.getAll()) { - exportTypesAvailability[exportType.jobType] = xpackInfoAvailable ? exportType.validLicenses.includes(licenseType) : false; + const licenseType: string | undefined = xpackInfo.license.getType(); + if (!licenseType) { + throw new Error('No license type returned from XPackMainPlugin#info!'); + } + for (const exportType of exportTypesRegistry.getAll()) { + exportTypesAvailability[exportType.jobType] = xpackInfoAvailable + ? exportType.validLicenses.includes(licenseType) + : false; } return exportTypesAvailability; @@ -39,6 +42,6 @@ export async function getExportTypesHandler(server) { */ getNumExportTypes() { return exportTypesRegistry.getSize(); - } + }, }; } diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts index 0c85d39ae55d3..bd2d0cb835a79 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { ServerFacade, ESCallCluster } from '../../types'; +import { ServerFacade, ExportTypesRegistry, ESCallCluster } from '../../types'; import { AggregationBuckets, AggregationResults, @@ -16,7 +16,6 @@ import { RangeStats, } from './types'; import { decorateRangeStats } from './decorate_range_stats'; -// @ts-ignore untyped module import { getExportTypesHandler } from './get_export_type_handler'; const JOB_TYPES_KEY = 'jobTypes'; @@ -101,7 +100,11 @@ async function handleResponse( }; } -export async function getReportingUsage(server: ServerFacade, callCluster: ESCallCluster) { +export async function getReportingUsage( + server: ServerFacade, + callCluster: ESCallCluster, + exportTypesRegistry: ExportTypesRegistry +) { const config = server.config(); const reportingIndex = config.get('xpack.reporting.index'); @@ -138,13 +141,13 @@ export async function getReportingUsage(server: ServerFacade, callCluster: ESCal return callCluster('search', params) .then((response: AggregationResults) => handleResponse(server, response)) - .then(async (usage: RangeStatSets) => { + .then((usage: RangeStatSets) => { // Allow this to explicitly throw an exception if/when this config is deprecated, // because we shouldn't collect browserType in that case! const browserType = config.get('xpack.reporting.capture.browser.type'); const xpackInfo = server.plugins.xpack_main.info; - const exportTypesHandler = await getExportTypesHandler(server); + const exportTypesHandler = getExportTypesHandler(exportTypesRegistry); const availability = exportTypesHandler.getAvailability(xpackInfo) as FeatureAvailabilityMap; const { lastDay, last7Days, ...all } = usage; diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js index f23f679865146..25b932c0ac9a2 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js @@ -40,7 +40,6 @@ function getServerMock(customization) { }, }, }, - expose: () => {}, log: () => {}, config: () => ({ get: key => { @@ -56,6 +55,11 @@ function getServerMock(customization) { } const getResponseMock = (customization = {}) => customization; +const getExportTypesRegistryMock = () => ({ + getAll() { + return []; + }, +}); describe('license checks', () => { describe('with a basic license', () => { @@ -67,8 +71,13 @@ describe('license checks', () => { .returns('basic'); const callClusterMock = jest.fn(() => Promise.resolve(getResponseMock())); const usageCollection = getMockUsageCollection(); - const { fetch: getReportingUsage } = getReportingUsageCollector(usageCollection, serverWithBasicLicenseMock); - usageStats = await getReportingUsage(callClusterMock); + const { fetch: getReportingUsage } = getReportingUsageCollector( + usageCollection, + serverWithBasicLicenseMock, + () => {}, + getExportTypesRegistryMock() + ); + usageStats = await getReportingUsage(callClusterMock, getExportTypesRegistryMock()); }); test('sets enables to true', async () => { @@ -93,8 +102,13 @@ describe('license checks', () => { .returns('none'); const callClusterMock = jest.fn(() => Promise.resolve(getResponseMock())); const usageCollection = getMockUsageCollection(); - const { fetch: getReportingUsage } = getReportingUsageCollector(usageCollection, serverWithNoLicenseMock); - usageStats = await getReportingUsage(callClusterMock); + const { fetch: getReportingUsage } = getReportingUsageCollector( + usageCollection, + serverWithNoLicenseMock, + () => {}, + getExportTypesRegistryMock() + ); + usageStats = await getReportingUsage(callClusterMock, getExportTypesRegistryMock()); }); test('sets enables to true', async () => { @@ -121,9 +135,11 @@ describe('license checks', () => { const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( usageCollection, - serverWithPlatinumLicenseMock + serverWithPlatinumLicenseMock, + () => {}, + getExportTypesRegistryMock() ); - usageStats = await getReportingUsage(callClusterMock); + usageStats = await getReportingUsage(callClusterMock, getExportTypesRegistryMock()); }); test('sets enables to true', async () => { @@ -148,8 +164,13 @@ describe('license checks', () => { .returns('basic'); const callClusterMock = jest.fn(() => Promise.resolve({})); const usageCollection = getMockUsageCollection(); - const { fetch: getReportingUsage } = getReportingUsageCollector(usageCollection, serverWithBasicLicenseMock); - usageStats = await getReportingUsage(callClusterMock); + const { fetch: getReportingUsage } = getReportingUsageCollector( + usageCollection, + serverWithBasicLicenseMock, + () => {}, + getExportTypesRegistryMock() + ); + usageStats = await getReportingUsage(callClusterMock, getExportTypesRegistryMock()); }); test('sets enables to true', async () => { @@ -170,7 +191,12 @@ describe('data modeling', () => { serverWithPlatinumLicenseMock.plugins.xpack_main.info.license.getType = sinon .stub() .returns('platinum'); - ({ fetch: getReportingUsage } = getReportingUsageCollector(usageCollection, serverWithPlatinumLicenseMock)); + ({ fetch: getReportingUsage } = getReportingUsageCollector( + usageCollection, + serverWithPlatinumLicenseMock, + () => {}, + getExportTypesRegistryMock() + )); }); test('with normal looking usage data', async () => { @@ -295,6 +321,7 @@ describe('data modeling', () => { }) ) ); + const usageStats = await getReportingUsage(callClusterMock); expect(usageStats).toMatchInlineSnapshot(` Object { diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts index 0a7ef0a194434..40cf315a78cbb 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts @@ -7,7 +7,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; // @ts-ignore untyped module import { KIBANA_STATS_TYPE_MONITORING } from '../../../monitoring/common/constants'; -import { ServerFacade, ESCallCluster } from '../../types'; +import { ServerFacade, ExportTypesRegistry, ESCallCluster } from '../../types'; import { KIBANA_REPORTING_TYPE } from '../../common/constants'; import { getReportingUsage } from './get_reporting_usage'; import { RangeStats } from './types'; @@ -19,12 +19,14 @@ import { RangeStats } from './types'; export function getReportingUsageCollector( usageCollection: UsageCollectionSetup, server: ServerFacade, - isReady: () => boolean + isReady: () => boolean, + exportTypesRegistry: ExportTypesRegistry ) { return usageCollection.makeUsageCollector({ type: KIBANA_REPORTING_TYPE, isReady, - fetch: (callCluster: ESCallCluster) => getReportingUsage(server, callCluster), + fetch: (callCluster: ESCallCluster) => + getReportingUsage(server, callCluster, exportTypesRegistry), /* * Format the response data into a model for internal upload @@ -49,8 +51,14 @@ export function getReportingUsageCollector( export function registerReportingUsageCollector( usageCollection: UsageCollectionSetup, server: ServerFacade, - isReady: () => boolean + isReady: () => boolean, + exportTypesRegistry: ExportTypesRegistry ) { - const collector = getReportingUsageCollector(usageCollection, server, isReady); + const collector = getReportingUsageCollector( + usageCollection, + server, + isReady, + exportTypesRegistry + ); usageCollection.registerCollector(collector); } diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index afba9f3e17838..cc12c483ea3c9 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -16,6 +16,8 @@ import { CancellationToken } from './common/cancellation_token'; import { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver_factory'; import { BrowserType } from './server/browsers/types'; +export type ReportingPlugin = object; // For Plugin contract + export type Job = EventEmitter & { id: string; toJSON: () => { @@ -23,21 +25,6 @@ export type Job = EventEmitter & { }; }; -export interface ReportingPlugin { - queue: { - addJob: (type: string, payload: PayloadType, options: object) => Job; - }; - // TODO: convert exportTypesRegistry to TS - exportTypesRegistry: { - getById: (id: string) => ExportTypeDefinition; - getAll: () => Array>; - get: ( - callback: (item: ExportTypeDefinition) => boolean - ) => ExportTypeDefinition; - }; - browserDriverFactory: HeadlessChromiumDriverFactory; -} - export interface ReportingConfigOptions { browser: BrowserConfig; poll: { @@ -88,7 +75,6 @@ export type ReportingPluginSpecOptions = Legacy.PluginSpecOptions; export type ServerFacade = Legacy.Server & { plugins: { - reporting?: ReportingPlugin; xpack_main?: XPackMainPlugin & { status?: any; }; @@ -246,6 +232,10 @@ export interface JobDocOutputExecuted { size: number; } +export interface ESQueue { + addJob: (type: string, payload: object, options: object) => Job; +} + export interface ESQueueWorker { on: (event: string, handler: any) => void; } @@ -304,7 +294,12 @@ export interface ESQueueInstance { } export type CreateJobFactory = (server: ServerFacade) => CreateJobFnType; -export type ExecuteJobFactory = (server: ServerFacade) => ExecuteJobFnType; +export type ExecuteJobFactory = ( + server: ServerFacade, + opts?: { + browserDriverFactory?: HeadlessChromiumDriverFactory; + } +) => ExecuteJobFnType; export interface ExportTypeDefinition< JobParamsType, @@ -322,17 +317,9 @@ export interface ExportTypeDefinition< validLicenses: string[]; } -export interface ExportTypesRegistry { - register: ( - exportTypeDefinition: ExportTypeDefinition< - JobParamsType, - CreateJobFnType, - JobPayloadType, - ExecuteJobFnType - > - ) => void; -} - +export { ExportTypesRegistry } from './server/lib/export_types_registry'; +export { HeadlessChromiumDriver } from './server/browsers/chromium/driver'; +export { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver_factory'; export { CancellationToken } from './common/cancellation_token'; // Prefer to import this type using: `import { LevelLogger } from 'relative/path/server/lib';` From ed80fea51e2a42e080b243ffe424f182eee9079f Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 3 Dec 2019 14:48:45 -0700 Subject: [PATCH 02/17] Minor routes registration cleanup --- x-pack/legacy/plugins/reporting/index.ts | 5 +- .../reporting/server/routes/generation.ts | 67 ++++++++++++++++ .../plugins/reporting/server/routes/index.ts | 77 +------------------ .../plugins/reporting/server/routes/jobs.ts | 5 +- 4 files changed, 76 insertions(+), 78 deletions(-) create mode 100644 x-pack/legacy/plugins/reporting/server/routes/generation.ts diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index 276a5cca8fcf5..c67ff65460698 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; // @ts-ignore untyped module defintition import { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status'; -import { registerRoutes } from './server/routes'; +import { registerJobGenerationRoutes, registerJobInfoRoutes } from './server/routes'; import { createQueueFactory, enqueueJobFactory, @@ -109,7 +109,8 @@ export const reporting = (kibana: any) => { const enqueueJob = enqueueJobFactory(server, { exportTypesRegistry, esqueue }); // Reporting routes - registerRoutes(server, exportTypesRegistry, enqueueJob, logger); + registerJobGenerationRoutes(server, enqueueJob, logger); + registerJobInfoRoutes(server, exportTypesRegistry); }, deprecations({ unused }: any) { diff --git a/x-pack/legacy/plugins/reporting/server/routes/generation.ts b/x-pack/legacy/plugins/reporting/server/routes/generation.ts new file mode 100644 index 0000000000000..8bbc47c7b3392 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/routes/generation.ts @@ -0,0 +1,67 @@ +/* + * 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 boom from 'boom'; +import { API_BASE_URL } from '../../common/constants'; +import { ServerFacade, RequestFacade, ReportingResponseToolkit, Logger } from '../../types'; +import { registerGenerateFromJobParams } from './generate_from_jobparams'; +import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject'; +import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; +import { registerLegacy } from './legacy'; + +export function registerJobGenerationRoutes(server: ServerFacade, enqueueJob: any, logger: Logger) { + const config = server.config(); + const DOWNLOAD_BASE_URL = config.get('server.basePath') + `${API_BASE_URL}/jobs/download`; + // @ts-ignore TODO + const { errors: esErrors } = server.plugins.elasticsearch.getCluster('admin'); + + /* + * Generates enqueued job details to use in responses + */ + async function handler( + exportTypeId: string, + jobParams: object, + request: RequestFacade, + h: ReportingResponseToolkit + ) { + const user = request.pre.user; + const headers = request.headers; + + const job = await enqueueJob(logger, exportTypeId, jobParams, user, headers, request); + + // return the queue's job information + const jobJson = job.toJSON(); + + return h + .response({ + path: `${DOWNLOAD_BASE_URL}/${jobJson.id}`, + job: jobJson, + }) + .type('application/json'); + } + + function handleError(exportTypeId: string, err: Error) { + if (err instanceof esErrors['401']) { + return boom.unauthorized(`Sorry, you aren't authenticated`); + } + if (err instanceof esErrors['403']) { + return boom.forbidden(`Sorry, you are not authorized to create ${exportTypeId} reports`); + } + if (err instanceof esErrors['404']) { + return boom.boomify(err, { statusCode: 404 }); + } + return err; + } + + registerGenerateFromJobParams(server, handler, handleError); + registerLegacy(server, handler, handleError); + + // Register beta panel-action download-related API's + if (config.get('xpack.reporting.csv.enablePanelActionDownload')) { + registerGenerateCsvFromSavedObject(server, handler, handleError); + registerGenerateCsvFromSavedObjectImmediate(server, logger); + } +} diff --git a/x-pack/legacy/plugins/reporting/server/routes/index.ts b/x-pack/legacy/plugins/reporting/server/routes/index.ts index 281654a738895..13417cfc77505 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/index.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/index.ts @@ -4,78 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -import boom from 'boom'; -import { API_BASE_URL } from '../../common/constants'; -import { - ServerFacade, - RequestFacade, - ExportTypesRegistry, - ReportingResponseToolkit, - Logger, -} from '../../types'; -import { registerGenerateFromJobParams } from './generate_from_jobparams'; -import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject'; -import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; -import { registerJobs } from './jobs'; -import { registerLegacy } from './legacy'; - -export function registerRoutes( - server: ServerFacade, - exportTypesRegistry: ExportTypesRegistry, - enqueueJob: any, - logger: Logger -) { - const config = server.config(); - const DOWNLOAD_BASE_URL = config.get('server.basePath') + `${API_BASE_URL}/jobs/download`; - // @ts-ignore TODO - const { errors: esErrors } = server.plugins.elasticsearch.getCluster('admin'); - - /* - * Generates enqueued job details to use in responses - */ - async function handler( - exportTypeId: string, - jobParams: object, - request: RequestFacade, - h: ReportingResponseToolkit - ) { - const user = request.pre.user; - const headers = request.headers; - - const job = await enqueueJob(logger, exportTypeId, jobParams, user, headers, request); - - // return the queue's job information - const jobJson = job.toJSON(); - - return h - .response({ - path: `${DOWNLOAD_BASE_URL}/${jobJson.id}`, - job: jobJson, - }) - .type('application/json'); - } - - function handleError(exportTypeId: string, err: Error) { - if (err instanceof esErrors['401']) { - return boom.unauthorized(`Sorry, you aren't authenticated`); - } - if (err instanceof esErrors['403']) { - return boom.forbidden(`Sorry, you are not authorized to create ${exportTypeId} reports`); - } - if (err instanceof esErrors['404']) { - return boom.boomify(err, { statusCode: 404 }); - } - return err; - } - - registerGenerateFromJobParams(server, handler, handleError); - registerLegacy(server, handler, handleError); - - // Register beta panel-action download-related API's - if (config.get('xpack.reporting.csv.enablePanelActionDownload')) { - registerGenerateCsvFromSavedObject(server, handler, handleError); - registerGenerateCsvFromSavedObjectImmediate(server, logger); - } - - registerJobs(server, exportTypesRegistry); -} +export { registerJobGenerationRoutes } from './generation'; +export { registerJobInfoRoutes } from './jobs'; diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts index 76d4d4d9e9978..522cc6b8f25a1 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts @@ -25,7 +25,10 @@ import { const MAIN_ENTRY = `${API_BASE_URL}/jobs`; -export function registerJobs(server: ServerFacade, exportTypesRegistry: ExportTypesRegistry) { +export function registerJobInfoRoutes( + server: ServerFacade, + exportTypesRegistry: ExportTypesRegistry +) { const jobsQuery = jobsQueryFactory(server); const getRouteConfig = getRouteConfigFactoryManagementPre(server); const getRouteConfigDownload = getRouteConfigFactoryDownloadPre(server); From a2e86d73689ac7f08fe10b5ef1f80eb7ed22acef Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 10:12:06 -0700 Subject: [PATCH 03/17] move the ETR test file --- .../{common => server/lib}/__tests__/export_types_registry.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename x-pack/legacy/plugins/reporting/{common => server/lib}/__tests__/export_types_registry.js (100%) diff --git a/x-pack/legacy/plugins/reporting/common/__tests__/export_types_registry.js b/x-pack/legacy/plugins/reporting/server/lib/__tests__/export_types_registry.js similarity index 100% rename from x-pack/legacy/plugins/reporting/common/__tests__/export_types_registry.js rename to x-pack/legacy/plugins/reporting/server/lib/__tests__/export_types_registry.js From fca303fbb41ea8ec4677090df9a71a3011138355 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 10:13:26 -0700 Subject: [PATCH 04/17] Re-pack the route registration, reduce LOC changes --- x-pack/legacy/plugins/reporting/index.ts | 10 ++-------- .../reporting/server/routes/generation.ts | 20 +++++++++++++++++-- .../plugins/reporting/server/routes/index.ts | 20 +++++++++++++++++-- .../plugins/reporting/server/routes/jobs.ts | 20 +++++++++++-------- x-pack/legacy/plugins/reporting/types.d.ts | 3 ++- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index c67ff65460698..c0c9e458132f0 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -9,10 +9,8 @@ import { i18n } from '@kbn/i18n'; import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; // @ts-ignore untyped module defintition import { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status'; -import { registerJobGenerationRoutes, registerJobInfoRoutes } from './server/routes'; +import { registerRoutes } from './server/routes'; import { - createQueueFactory, - enqueueJobFactory, LevelLogger, checkLicenseFactory, getExportTypesRegistry, @@ -105,12 +103,8 @@ export const reporting = (kibana: any) => { // Post initialization of the above code, the collector is now ready to fetch its data isCollectorReady = true; - const esqueue = createQueueFactory(server, { exportTypesRegistry, browserDriverFactory }); - const enqueueJob = enqueueJobFactory(server, { exportTypesRegistry, esqueue }); - // Reporting routes - registerJobGenerationRoutes(server, enqueueJob, logger); - registerJobInfoRoutes(server, exportTypesRegistry); + registerRoutes(server, exportTypesRegistry, browserDriverFactory, logger); }, deprecations({ unused }: any) { diff --git a/x-pack/legacy/plugins/reporting/server/routes/generation.ts b/x-pack/legacy/plugins/reporting/server/routes/generation.ts index 8bbc47c7b3392..7bed7bc5773e4 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generation.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generation.ts @@ -6,18 +6,34 @@ import boom from 'boom'; import { API_BASE_URL } from '../../common/constants'; -import { ServerFacade, RequestFacade, ReportingResponseToolkit, Logger } from '../../types'; +import { + ServerFacade, + ExportTypesRegistry, + HeadlessChromiumDriverFactory, + RequestFacade, + ReportingResponseToolkit, + Logger, +} from '../../types'; import { registerGenerateFromJobParams } from './generate_from_jobparams'; import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject'; import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; import { registerLegacy } from './legacy'; +import { createQueueFactory, enqueueJobFactory } from '../lib'; -export function registerJobGenerationRoutes(server: ServerFacade, enqueueJob: any, logger: Logger) { +export function registerJobGenerationRoutes( + server: ServerFacade, + exportTypesRegistry: ExportTypesRegistry, + browserDriverFactory: HeadlessChromiumDriverFactory, + logger: Logger +) { const config = server.config(); const DOWNLOAD_BASE_URL = config.get('server.basePath') + `${API_BASE_URL}/jobs/download`; // @ts-ignore TODO const { errors: esErrors } = server.plugins.elasticsearch.getCluster('admin'); + const esqueue = createQueueFactory(server, { exportTypesRegistry, browserDriverFactory }); + const enqueueJob = enqueueJobFactory(server, { exportTypesRegistry, esqueue }); + /* * Generates enqueued job details to use in responses */ diff --git a/x-pack/legacy/plugins/reporting/server/routes/index.ts b/x-pack/legacy/plugins/reporting/server/routes/index.ts index 13417cfc77505..da664dcb91ae4 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/index.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/index.ts @@ -4,5 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -export { registerJobGenerationRoutes } from './generation'; -export { registerJobInfoRoutes } from './jobs'; +import { + ServerFacade, + ExportTypesRegistry, + HeadlessChromiumDriverFactory, + Logger, +} from '../../types'; +import { registerJobGenerationRoutes } from './generation'; +import { registerJobInfoRoutes } from './jobs'; + +export function registerRoutes( + server: ServerFacade, + exportTypesRegistry: ExportTypesRegistry, + browserDriverFactory: HeadlessChromiumDriverFactory, + logger: Logger +) { + registerJobGenerationRoutes(server, exportTypesRegistry, browserDriverFactory, logger); + registerJobInfoRoutes(server, exportTypesRegistry, logger); +} diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts index 522cc6b8f25a1..30ee3efecc4d7 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts @@ -9,6 +9,7 @@ import { API_BASE_URL } from '../../common/constants'; import { ServerFacade, ExportTypesRegistry, + Logger, RequestFacade, ReportingResponseToolkit, JobDocOutput, @@ -27,7 +28,8 @@ const MAIN_ENTRY = `${API_BASE_URL}/jobs`; export function registerJobInfoRoutes( server: ServerFacade, - exportTypesRegistry: ExportTypesRegistry + exportTypesRegistry: ExportTypesRegistry, + logger: Logger ) { const jobsQuery = jobsQueryFactory(server); const getRouteConfig = getRouteConfigFactoryManagementPre(server); @@ -140,13 +142,15 @@ export function registerJobInfoRoutes( const { statusCode } = response; if (statusCode !== 200) { - const logLevel = statusCode === 500 ? 'error' : 'debug'; - server.log( - [logLevel, 'reporting', 'download'], - `Report ${docId} has non-OK status: [${statusCode}] Reason: [${JSON.stringify( - response.source - )}]` - ); + if (statusCode === 500) { + logger.error(`Report ${docId} has failed: ${JSON.stringify(response.source)}`); + } else { + logger.debug( + `Report ${docId} is not ready: [${statusCode}] Reason: [${JSON.stringify( + response.source + )}]` + ); + } } if (!response.isBoom) { diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index cc12c483ea3c9..07bd9dee004f7 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -13,6 +13,7 @@ import { CallCluster, } from '../../../../src/legacy/core_plugins/elasticsearch'; import { CancellationToken } from './common/cancellation_token'; +import { LevelLogger } from './server/lib/level_logger'; import { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver_factory'; import { BrowserType } from './server/browsers/types'; @@ -323,7 +324,7 @@ export { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver export { CancellationToken } from './common/cancellation_token'; // Prefer to import this type using: `import { LevelLogger } from 'relative/path/server/lib';` -export { LevelLogger as Logger } from './server/lib/level_logger'; +export { LevelLogger as Logger }; export interface AbsoluteURLFactoryOptions { defaultBasePath: string; From 87ff2bc8d8db87e365f61633bde1d1f36bfc2772 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 10:14:18 -0700 Subject: [PATCH 05/17] add EnqueueJobFn type --- .../plugins/reporting/server/lib/enqueue_job.ts | 3 ++- x-pack/legacy/plugins/reporting/types.d.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts index 731ea071bda27..984cf585ee3c7 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts @@ -8,6 +8,7 @@ import { get } from 'lodash'; // @ts-ignore import { events as esqueueEvents } from './esqueue'; import { + EnqueueJobFn, ESQueueCreateJobFn, ImmediateCreateJobFn, Job, @@ -35,7 +36,7 @@ interface EnqueueJobFactoryOpts { export function enqueueJobFactory( server: ServerFacade, { exportTypesRegistry, esqueue }: EnqueueJobFactoryOpts -) { +): EnqueueJobFn { const config = server.config(); const captureConfig: CaptureConfig = config.get('xpack.reporting.capture'); const browserType = captureConfig.browser.type; diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index 07bd9dee004f7..abd2d43298386 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -90,10 +90,19 @@ interface ReportingRequest { management: { jobTypes: any; }; - user: any; + user: string; }; } +export type EnqueueJobFn = ( + parentLogger: LevelLogger, + exportTypeId: string, + jobParams: JobParamsType, + user: string, + headers: Record, + request: RequestFacade +) => Promise; + export type RequestFacade = ReportingRequest & Legacy.Request; export type ResponseFacade = ResponseObject & { From d00f45c2e10b77bea0b9bbaf50e253f299249216 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 10:50:42 -0700 Subject: [PATCH 06/17] Fix usage collector test --- .../usage/reporting_usage_collector.test.js | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js index 25b932c0ac9a2..f761f0d2d270b 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import sinon from 'sinon'; +import { getExportTypesRegistry } from '../lib/export_types_registry'; import { getReportingUsageCollector } from './reporting_usage_collector'; +const exportTypesRegistry = getExportTypesRegistry(); + function getMockUsageCollection() { class MockUsageCollector { constructor(_server, { fetch }) { @@ -55,11 +58,6 @@ function getServerMock(customization) { } const getResponseMock = (customization = {}) => customization; -const getExportTypesRegistryMock = () => ({ - getAll() { - return []; - }, -}); describe('license checks', () => { describe('with a basic license', () => { @@ -75,9 +73,9 @@ describe('license checks', () => { usageCollection, serverWithBasicLicenseMock, () => {}, - getExportTypesRegistryMock() + exportTypesRegistry ); - usageStats = await getReportingUsage(callClusterMock, getExportTypesRegistryMock()); + usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); }); test('sets enables to true', async () => { @@ -106,9 +104,9 @@ describe('license checks', () => { usageCollection, serverWithNoLicenseMock, () => {}, - getExportTypesRegistryMock() + exportTypesRegistry ); - usageStats = await getReportingUsage(callClusterMock, getExportTypesRegistryMock()); + usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); }); test('sets enables to true', async () => { @@ -137,9 +135,9 @@ describe('license checks', () => { usageCollection, serverWithPlatinumLicenseMock, () => {}, - getExportTypesRegistryMock() + exportTypesRegistry ); - usageStats = await getReportingUsage(callClusterMock, getExportTypesRegistryMock()); + usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); }); test('sets enables to true', async () => { @@ -168,9 +166,9 @@ describe('license checks', () => { usageCollection, serverWithBasicLicenseMock, () => {}, - getExportTypesRegistryMock() + exportTypesRegistry ); - usageStats = await getReportingUsage(callClusterMock, getExportTypesRegistryMock()); + usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); }); test('sets enables to true', async () => { @@ -195,7 +193,7 @@ describe('data modeling', () => { usageCollection, serverWithPlatinumLicenseMock, () => {}, - getExportTypesRegistryMock() + exportTypesRegistry )); }); From c76eb67749b6016de8fe05040aabcbdb229d3b67 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 11:06:15 -0700 Subject: [PATCH 07/17] remove a throw error used for development/debugging --- x-pack/legacy/plugins/reporting/server/lib/create_worker.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts index f0abddd6e6ce2..4a2034b7c6575 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts @@ -31,10 +31,6 @@ export function createWorkerFactory( server: ServerFacade, { exportTypesRegistry, browserDriverFactory }: CreateWorkerFactoryOpts ) { - if (!browserDriverFactory) { - throw new Error('need that browser drver factory'); - } - type JobDocPayloadType = JobDocPayload; const config = server.config(); const logger = LevelLogger.createForServer(server, [PLUGIN_ID, 'queue-worker']); From aab588b12b7e461e5b640d1704088c65bcfc7d5c Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 11:46:51 -0700 Subject: [PATCH 08/17] fix imports error --- x-pack/legacy/plugins/reporting/server/routes/jobs.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js index 2d1f48dd790a0..289262a968748 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js @@ -7,7 +7,7 @@ import Hapi from 'hapi'; import { difference, memoize } from 'lodash'; import { registerJobs } from './jobs'; -import { ExportTypesRegistry } from '../../common/export_types_registry'; +import { getExportTypesRegistry } from '../lib/export_types_registry'; jest.mock('./lib/authorized_user_pre_routing', () => { return { authorizedUserPreRoutingFactory: () => () => ({}) @@ -25,7 +25,7 @@ let mockServer; beforeEach(() => { mockServer = new Hapi.Server({ debug: false, port: 8080, routes: { log: { collect: true } } }); mockServer.config = memoize(() => ({ get: jest.fn() })); - const exportTypesRegistry = new ExportTypesRegistry(); + const exportTypesRegistry = getExportTypesRegistry(); exportTypesRegistry.register({ id: 'unencoded', jobType: 'unencodedJobType', From b37de638c0e8587dd1acc58e71527c8c7b118ca8 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 11:47:14 -0700 Subject: [PATCH 09/17] Fix execute job tests --- .../reporting/export_types/png/server/execute_job/index.ts | 4 ---- .../reporting/export_types/png/server/lib/generate_png.ts | 6 +++++- .../export_types/printable_pdf/server/execute_job/index.ts | 4 ---- .../export_types/printable_pdf/server/lib/generate_pdf.ts | 6 +++++- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts index 613cd4d3e13e2..56afad31721f8 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts @@ -29,10 +29,6 @@ export const executeJobFactory: ExecuteJobFactory { export function generatePdfObservableFactory( server: ServerFacade, - browserDriverFactory: HeadlessChromiumDriverFactory + browserDriverFactory?: HeadlessChromiumDriverFactory ) { + if (!browserDriverFactory) { + throw new Error('Reporting browser driver factory must be passed for PDF job execution'); + } + const screenshotsObservable = screenshotsObservableFactory(server, browserDriverFactory); const captureConcurrency = 1; From 152eca2982628503b75b3e6d52eee2074d958f63 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 12:10:25 -0700 Subject: [PATCH 10/17] wip test fixes --- .../reporting/server/routes/jobs.test.js | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js index 289262a968748..8ba0477da873c 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js @@ -6,7 +6,7 @@ import Hapi from 'hapi'; import { difference, memoize } from 'lodash'; -import { registerJobs } from './jobs'; +import { registerJobInfoRoutes } from './jobs'; import { getExportTypesRegistry } from '../lib/export_types_registry'; jest.mock('./lib/authorized_user_pre_routing', () => { return { @@ -21,22 +21,13 @@ jest.mock('./lib/reporting_feature_pre_routing', () => { let mockServer; +let exportTypesRegistry; +const mockLogger = jest.fn(); beforeEach(() => { mockServer = new Hapi.Server({ debug: false, port: 8080, routes: { log: { collect: true } } }); mockServer.config = memoize(() => ({ get: jest.fn() })); - const exportTypesRegistry = getExportTypesRegistry(); - exportTypesRegistry.register({ - id: 'unencoded', - jobType: 'unencodedJobType', - jobContentExtension: 'csv' - }); - exportTypesRegistry.register({ - id: 'base64Encoded', - jobType: 'base64EncodedJobType', - jobContentEncoding: 'base64', - jobContentExtension: 'pdf' - }); + exportTypesRegistry = getExportTypesRegistry(); mockServer.plugins = { elasticsearch: { getCluster: memoize(() => ({ callWithInternalUser: jest.fn() })), @@ -44,9 +35,6 @@ beforeEach(() => { callWithRequest: jest.fn(), callWithInternalUser: jest.fn(), })) - }, - reporting: { - exportTypesRegistry } }; }); @@ -63,7 +51,7 @@ test(`returns 404 if job not found`, async () => { mockServer.plugins.elasticsearch.getCluster('admin') .callWithInternalUser.mockReturnValue(Promise.resolve(getHits())); - registerJobs(mockServer); + registerJobInfoRoutes(mockServer, exportTypesRegistry, mockLogger); const request = { method: 'GET', @@ -79,7 +67,7 @@ test(`returns 401 if not valid job type`, async () => { mockServer.plugins.elasticsearch.getCluster('admin') .callWithInternalUser.mockReturnValue(Promise.resolve(getHits({ jobtype: 'invalidJobType' }))); - registerJobs(mockServer); + registerJobInfoRoutes(mockServer, exportTypesRegistry, mockLogger); const request = { method: 'GET', @@ -96,7 +84,7 @@ describe(`when job is incomplete`, () => { mockServer.plugins.elasticsearch.getCluster('admin') .callWithInternalUser.mockReturnValue(Promise.resolve(getHits({ jobtype: 'unencodedJobType', status: 'pending' }))); - registerJobs(mockServer); + registerJobInfoRoutes(mockServer, exportTypesRegistry, mockLogger); const request = { method: 'GET', @@ -133,7 +121,7 @@ describe(`when job is failed`, () => { mockServer.plugins.elasticsearch.getCluster('admin') .callWithInternalUser.mockReturnValue(Promise.resolve(hits)); - registerJobs(mockServer); + registerJobInfoRoutes(mockServer, exportTypesRegistry, mockLogger); const request = { method: 'GET', @@ -178,7 +166,7 @@ describe(`when job is completed`, () => { }); mockServer.plugins.elasticsearch.getCluster('admin').callWithInternalUser.mockReturnValue(Promise.resolve(hits)); - registerJobs(mockServer); + registerJobInfoRoutes(mockServer, exportTypesRegistry, mockLogger); const request = { method: 'GET', From cf6f5b46e9e06c42201d47f7d1429fcdc2b796f6 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 12:55:41 -0700 Subject: [PATCH 11/17] test fixes for real --- .../reporting/server/routes/jobs.test.js | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js index 8ba0477da873c..c4d4f6e42c9cb 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js @@ -7,7 +7,7 @@ import Hapi from 'hapi'; import { difference, memoize } from 'lodash'; import { registerJobInfoRoutes } from './jobs'; -import { getExportTypesRegistry } from '../lib/export_types_registry'; +import { ExportTypesRegistry } from '../lib/export_types_registry'; jest.mock('./lib/authorized_user_pre_routing', () => { return { authorizedUserPreRoutingFactory: () => () => ({}) @@ -19,15 +19,28 @@ jest.mock('./lib/reporting_feature_pre_routing', () => { }; }); - let mockServer; let exportTypesRegistry; -const mockLogger = jest.fn(); +const mockLogger = { + error: jest.fn(), + debug: jest.fn(), +}; beforeEach(() => { mockServer = new Hapi.Server({ debug: false, port: 8080, routes: { log: { collect: true } } }); mockServer.config = memoize(() => ({ get: jest.fn() })); - exportTypesRegistry = getExportTypesRegistry(); + exportTypesRegistry = new ExportTypesRegistry(); + exportTypesRegistry.register({ + id: 'unencoded', + jobType: 'unencodedJobType', + jobContentExtension: 'csv' + }); + exportTypesRegistry.register({ + id: 'base64Encoded', + jobType: 'base64EncodedJobType', + jobContentEncoding: 'base64', + jobContentExtension: 'pdf' + }); mockServer.plugins = { elasticsearch: { getCluster: memoize(() => ({ callWithInternalUser: jest.fn() })), @@ -79,7 +92,6 @@ test(`returns 401 if not valid job type`, async () => { }); describe(`when job is incomplete`, () => { - const getIncompleteResponse = async () => { mockServer.plugins.elasticsearch.getCluster('admin') .callWithInternalUser.mockReturnValue(Promise.resolve(getHits({ jobtype: 'unencodedJobType', status: 'pending' }))); From 0c4dc740ca3db719ae0872c598ea76f4e0245ae9 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 13:43:10 -0700 Subject: [PATCH 12/17] fix more tests --- .../server/lib/create_worker.test.ts | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts index 8bf3d299cdb4e..eb6df5035b5fa 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts @@ -5,8 +5,9 @@ */ import * as sinon from 'sinon'; -import { ServerFacade, ExportTypesRegistry, HeadlessChromiumDriverFactory } from '../../types'; +import { ServerFacade, HeadlessChromiumDriverFactory } from '../../types'; import { createWorkerFactory } from './create_worker'; +import { ExportTypesRegistry } from './export_types_registry'; // @ts-ignore import { Esqueue } from './esqueue'; // @ts-ignore @@ -22,16 +23,17 @@ configGetStub.withArgs('server.uuid').returns('g9ymiujthvy6v8yrh7567g6fwzgzftzfr const executeJobFactoryStub = sinon.stub(); -const getMockServer = ( - exportTypes: any[] = [{ executeJobFactory: executeJobFactoryStub }] -): ServerFacade => { +const getMockServer = (): ServerFacade => { return ({ log: sinon.stub(), - expose: sinon.stub(), config: () => ({ get: configGetStub }), - plugins: { reporting: { exportTypesRegistry: { getAll: () => exportTypes } } }, } as unknown) as ServerFacade; }; +const getMockExportTypesRegistry = ( + exportTypes: any[] = [{ executeJobFactory: executeJobFactoryStub }] +) => ({ + getAll: () => exportTypes, +}); describe('Create Worker', () => { let queue: Esqueue; @@ -44,8 +46,9 @@ describe('Create Worker', () => { }); test('Creates a single Esqueue worker for Reporting', async () => { + const exportTypesRegistry = getMockExportTypesRegistry(); const createWorker = createWorkerFactory(getMockServer(), { - exportTypesRegistry: {} as ExportTypesRegistry, + exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry, browserDriverFactory: {} as HeadlessChromiumDriverFactory, }); const registerWorkerSpy = sinon.spy(queue, 'registerWorker'); @@ -71,19 +74,17 @@ Object { }); test('Creates a single Esqueue worker for Reporting, even if there are multiple export types', async () => { - const createWorker = createWorkerFactory( - getMockServer([ - { executeJobFactory: executeJobFactoryStub }, - { executeJobFactory: executeJobFactoryStub }, - { executeJobFactory: executeJobFactoryStub }, - { executeJobFactory: executeJobFactoryStub }, - { executeJobFactory: executeJobFactoryStub }, - ]), - { - exportTypesRegistry: {} as ExportTypesRegistry, - browserDriverFactory: {} as HeadlessChromiumDriverFactory, - } - ); + const exportTypesRegistry = getMockExportTypesRegistry([ + { executeJobFactory: executeJobFactoryStub }, + { executeJobFactory: executeJobFactoryStub }, + { executeJobFactory: executeJobFactoryStub }, + { executeJobFactory: executeJobFactoryStub }, + { executeJobFactory: executeJobFactoryStub }, + ]); + const createWorker = createWorkerFactory(getMockServer(), { + exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry, + browserDriverFactory: {} as HeadlessChromiumDriverFactory, + }); const registerWorkerSpy = sinon.spy(queue, 'registerWorker'); createWorker(queue); From bde7728215e6d0dd00711580350d9ed653e1b033 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 13:50:46 -0700 Subject: [PATCH 13/17] fix diffs --- .../legacy/plugins/reporting/server/lib/create_worker.test.ts | 2 +- x-pack/legacy/plugins/reporting/server/routes/jobs.ts | 2 +- x-pack/legacy/plugins/reporting/types.d.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts index eb6df5035b5fa..8f843752491ec 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts @@ -6,8 +6,8 @@ import * as sinon from 'sinon'; import { ServerFacade, HeadlessChromiumDriverFactory } from '../../types'; -import { createWorkerFactory } from './create_worker'; import { ExportTypesRegistry } from './export_types_registry'; +import { createWorkerFactory } from './create_worker'; // @ts-ignore import { Esqueue } from './esqueue'; // @ts-ignore diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts index 30ee3efecc4d7..fd5014911d262 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts @@ -146,7 +146,7 @@ export function registerJobInfoRoutes( logger.error(`Report ${docId} has failed: ${JSON.stringify(response.source)}`); } else { logger.debug( - `Report ${docId} is not ready: [${statusCode}] Reason: [${JSON.stringify( + `Report ${docId} has non-OK status: [${statusCode}] Reason: [${JSON.stringify( response.source )}]` ); diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index abd2d43298386..63357cd85fcae 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -90,7 +90,7 @@ interface ReportingRequest { management: { jobTypes: any; }; - user: string; + user: any; }; } From 226a34b5a914bc3927bc26e1dcf3f03eebd4ba38 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 4 Dec 2019 16:31:20 -0700 Subject: [PATCH 14/17] Add TODOs about the ExportTypesRegistry.register unwrap the factory functions. --- x-pack/legacy/plugins/reporting/server/lib/create_worker.ts | 1 + x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts | 1 + .../plugins/reporting/server/lib/export_types_registry.ts | 2 ++ .../server/routes/generate_from_savedobject_immediate.ts | 5 +++++ 4 files changed, 9 insertions(+) diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts index 4a2034b7c6575..1326e411b6c5c 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts @@ -49,6 +49,7 @@ export function createWorkerFactory( for (const exportType of exportTypesRegistry.getAll() as Array< ExportTypeDefinition >) { + // TODO: the executeJobFn should be unwrapped in the register method of the export types registry const jobExecutor = exportType.executeJobFactory(server, { browserDriverFactory }); jobExecutors.set(exportType.jobType, jobExecutor); } diff --git a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts index 984cf585ee3c7..2d044ab31a160 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts @@ -60,6 +60,7 @@ export function enqueueJobFactory( throw new Error(`Export type ${exportTypeId} does not exist in the registry!`); } + // TODO: the createJobFn should be unwrapped in the register method of the export types registry const createJob = exportType.createJobFactory(server) as CreateJobFn; const payload = await createJob(jobParams, headers, request); diff --git a/x-pack/legacy/plugins/reporting/server/lib/export_types_registry.ts b/x-pack/legacy/plugins/reporting/server/lib/export_types_registry.ts index d3801ae6d1ba4..d553cc07ae3ef 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/export_types_registry.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/export_types_registry.ts @@ -33,6 +33,8 @@ export class ExportTypesRegistry { throw new Error(`'item' with id ${item.id} has already been registered`); } + // TODO: Unwrap the execute function from the item's executeJobFactory + // Move that work out of server/lib/create_worker to reduce dependence on ESQueue this._map.set(item.id, item); } diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts index 8b0dd1a6e7c4f..14cd9fa6b6e54 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts @@ -45,6 +45,11 @@ export function registerGenerateCsvFromSavedObjectImmediate( handler: async (request: RequestFacade, h: ReportingResponseToolkit) => { const logger = parentLogger.clone(['savedobject-csv']); const jobParams = getJobParamsFromRequest(request, { isImmediate: true }); + + /* TODO these functions should be made available in the export types registery after doing + * exportTypesRegistry.getById(CSV_FROM_SAVEDOBJECT_JOB_TYPE) + * The execute job factory methods should always be called with HeadlessChromiumDriverFactory + */ const createJobFn = createJobFactory(server); const executeJobFn = executeJobFactory(server); const jobDocPayload: JobDocPayloadPanelCsv = await createJobFn( From c4110213545b48227b1a30faf0ae88e202f083d1 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 5 Dec 2019 10:42:02 -0700 Subject: [PATCH 15/17] really make headlessbrowserdriver required as an execute job factory option --- .../export_types/png/server/execute_job/index.ts | 8 ++++---- .../export_types/png/server/lib/generate_png.ts | 6 +----- .../printable_pdf/server/execute_job/index.ts | 8 ++++---- .../printable_pdf/server/lib/generate_pdf.ts | 6 +----- .../routes/generate_from_savedobject_immediate.ts | 13 +++++++++---- x-pack/legacy/plugins/reporting/types.d.ts | 4 ++-- 6 files changed, 21 insertions(+), 24 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts index 56afad31721f8..b289ae45dde67 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts @@ -23,11 +23,11 @@ import { import { JobDocPayloadPNG } from '../../types'; import { generatePngObservableFactory } from '../lib/generate_png'; -export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn( +type QueuedPngExecutorFactory = ExecuteJobFactory>; + +export const executeJobFactory: QueuedPngExecutorFactory = function executeJobFactoryFn( server: ServerFacade, - { browserDriverFactory }: { browserDriverFactory?: HeadlessChromiumDriverFactory } = {} + { browserDriverFactory }: { browserDriverFactory: HeadlessChromiumDriverFactory } ) { const generatePngObservable = generatePngObservableFactory(server, browserDriverFactory); const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PNG_JOB_TYPE, 'execute']); diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts index 05face42b111e..e2b1474515786 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts @@ -14,12 +14,8 @@ import { LayoutParams } from '../../../common/layouts/layout'; export function generatePngObservableFactory( server: ServerFacade, - browserDriverFactory?: HeadlessChromiumDriverFactory + browserDriverFactory: HeadlessChromiumDriverFactory ) { - if (!browserDriverFactory) { - throw new Error('Reporting browser driver factory must be passed for PNG job execution'); - } - const screenshotsObservable = screenshotsObservableFactory(server, browserDriverFactory); return function generatePngObservable( diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts index 770f74513fbea..e2b3183464cf2 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts @@ -24,11 +24,11 @@ import { getCustomLogo, } from '../../../common/execute_job/'; -export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn( +type QueuedPdfExecutorFactory = ExecuteJobFactory>; + +export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFactoryFn( server: ServerFacade, - { browserDriverFactory }: { browserDriverFactory?: HeadlessChromiumDriverFactory } = {} + { browserDriverFactory }: { browserDriverFactory: HeadlessChromiumDriverFactory } ) { const generatePdfObservable = generatePdfObservableFactory(server, browserDriverFactory); const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PDF_JOB_TYPE, 'execute']); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts index 4bbf976d830c4..898a13a2dfe80 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts @@ -28,12 +28,8 @@ const getTimeRange = (urlScreenshots: ScreenshotResults[]) => { export function generatePdfObservableFactory( server: ServerFacade, - browserDriverFactory?: HeadlessChromiumDriverFactory + browserDriverFactory: HeadlessChromiumDriverFactory ) { - if (!browserDriverFactory) { - throw new Error('Reporting browser driver factory must be passed for PDF job execution'); - } - const screenshotsObservable = screenshotsObservableFactory(server, browserDriverFactory); const captureConcurrency = 1; diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts index 14cd9fa6b6e54..bc96c27f64c10 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts @@ -10,6 +10,7 @@ import { ServerFacade, RequestFacade, ResponseFacade, + HeadlessChromiumDriverFactory, ReportingResponseToolkit, Logger, JobDocOutputExecuted, @@ -46,12 +47,16 @@ export function registerGenerateCsvFromSavedObjectImmediate( const logger = parentLogger.clone(['savedobject-csv']); const jobParams = getJobParamsFromRequest(request, { isImmediate: true }); - /* TODO these functions should be made available in the export types registery after doing - * exportTypesRegistry.getById(CSV_FROM_SAVEDOBJECT_JOB_TYPE) - * The execute job factory methods should always be called with HeadlessChromiumDriverFactory + /* TODO these functions should be made available in the export types registry: + * + * const { createJobFn, executeJobFn } = exportTypesRegistry.getById(CSV_FROM_SAVEDOBJECT_JOB_TYPE) + * + * Calling an execute job factory requires passing a browserDriverFactory option, so we should not call the factory from here */ const createJobFn = createJobFactory(server); - const executeJobFn = executeJobFactory(server); + const executeJobFn = executeJobFactory(server, { + browserDriverFactory: {} as HeadlessChromiumDriverFactory, + }); const jobDocPayload: JobDocPayloadPanelCsv = await createJobFn( jobParams, request.headers, diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index 63357cd85fcae..597e9cafdc2a2 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -306,8 +306,8 @@ export interface ESQueueInstance { export type CreateJobFactory = (server: ServerFacade) => CreateJobFnType; export type ExecuteJobFactory = ( server: ServerFacade, - opts?: { - browserDriverFactory?: HeadlessChromiumDriverFactory; + opts: { + browserDriverFactory: HeadlessChromiumDriverFactory; } ) => ExecuteJobFnType; From a50757d320b94fccde6c7644e7bec402172b756f Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 5 Dec 2019 12:49:33 -0700 Subject: [PATCH 16/17] fix tests --- .../export_types/png/server/execute_job/index.test.js | 6 +++--- .../printable_pdf/server/execute_job/index.test.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js index 867d537017f41..267c606449c3a 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js @@ -68,7 +68,7 @@ test(`passes browserTimezone to generatePng`, async () => { const generatePngObservable = generatePngObservableFactory(); generatePngObservable.mockReturnValue(Rx.of(Buffer.from(''))); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); const browserTimezone = 'UTC'; await executeJob('pngJobId', { relativeUrl: '/app/kibana#/something', browserTimezone, headers: encryptedHeaders }, cancellationToken); @@ -76,7 +76,7 @@ test(`passes browserTimezone to generatePng`, async () => { }); test(`returns content_type of application/png`, async () => { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); const encryptedHeaders = await encryptHeaders({}); const generatePngObservable = generatePngObservableFactory(); @@ -93,7 +93,7 @@ test(`returns content of generatePng getBuffer base64 encoded`, async () => { const generatePngObservable = generatePngObservableFactory(); generatePngObservable.mockReturnValue(Rx.of(Buffer.from(testContent))); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); const encryptedHeaders = await encryptHeaders({}); const { content } = await executeJob('pngJobId', { relativeUrl: '/app/kibana#/something', timeRange: {}, headers: encryptedHeaders }, cancellationToken); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js index 8084c077ed23f..6a5c47829fd19 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js @@ -67,7 +67,7 @@ test(`passes browserTimezone to generatePdf`, async () => { const generatePdfObservable = generatePdfObservableFactory(); generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(''))); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); const browserTimezone = 'UTC'; await executeJob('pdfJobId', { objects: [], browserTimezone, headers: encryptedHeaders }, cancellationToken); @@ -84,7 +84,7 @@ test(`passes browserTimezone to generatePdf`, async () => { }); test(`returns content_type of application/pdf`, async () => { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); const encryptedHeaders = await encryptHeaders({}); const generatePdfObservable = generatePdfObservableFactory(); @@ -104,7 +104,7 @@ test(`returns content of generatePdf getBuffer base64 encoded`, async () => { const generatePdfObservable = generatePdfObservableFactory(); generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(testContent))); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); const encryptedHeaders = await encryptHeaders({}); const { content } = await executeJob('pdfJobId', { objects: [], timeRange: {}, headers: encryptedHeaders }, cancellationToken); From 7870e26f11646bbf9aa861d16c02f7f95a2cbb33 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 5 Dec 2019 15:03:44 -0700 Subject: [PATCH 17/17] Use constants for license type keywords --- .../plugins/reporting/common/constants.ts | 6 ++++++ .../plugins/reporting/export_types/csv/index.ts | 17 +++++++++++++++-- .../export_types/csv_from_savedobject/index.ts | 17 +++++++++++++++-- .../plugins/reporting/export_types/png/index.ts | 15 +++++++++++++-- .../export_types/printable_pdf/index.ts | 15 +++++++++++++-- 5 files changed, 62 insertions(+), 8 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/common/constants.ts b/x-pack/legacy/plugins/reporting/common/constants.ts index 723320e74bfbd..03b2d51c7b396 100644 --- a/x-pack/legacy/plugins/reporting/common/constants.ts +++ b/x-pack/legacy/plugins/reporting/common/constants.ts @@ -50,3 +50,9 @@ export const PNG_JOB_TYPE = 'PNG'; export const CSV_JOB_TYPE = 'csv'; export const CSV_FROM_SAVEDOBJECT_JOB_TYPE = 'csv_from_savedobject'; export const USES_HEADLESS_JOB_TYPES = [PDF_JOB_TYPE, PNG_JOB_TYPE]; + +export const LICENSE_TYPE_TRIAL = 'trial'; +export const LICENSE_TYPE_BASIC = 'basic'; +export const LICENSE_TYPE_STANDARD = 'standard'; +export const LICENSE_TYPE_GOLD = 'gold'; +export const LICENSE_TYPE_PLATINUM = 'platinum'; diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/index.ts b/x-pack/legacy/plugins/reporting/export_types/csv/index.ts index 81876f07062ae..4f8aeb2be0c99 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/index.ts @@ -4,7 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CSV_JOB_TYPE as jobType } from '../../common/constants'; +import { + CSV_JOB_TYPE as jobType, + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_BASIC, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, +} from '../../common/constants'; import { ExportTypeDefinition, ESQueueCreateJobFn, ESQueueWorkerExecuteFn } from '../../types'; import { metadata } from './metadata'; import { createJobFactory } from './server/create_job'; @@ -22,5 +29,11 @@ export const getExportType = (): ExportTypeDefinition< jobContentExtension: 'csv', createJobFactory, executeJobFactory, - validLicenses: ['trial', 'basic', 'standard', 'gold', 'platinum'], + validLicenses: [ + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_BASIC, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, + ], }); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/index.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/index.ts index e38e2ba02dbaa..4876cea0b1b28 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/index.ts @@ -4,7 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants'; +import { + CSV_FROM_SAVEDOBJECT_JOB_TYPE, + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_BASIC, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, +} from '../../common/constants'; import { ExportTypeDefinition, ImmediateCreateJobFn, ImmediateExecuteFn } from '../../types'; import { createJobFactory } from './server/create_job'; import { executeJobFactory } from './server/execute_job'; @@ -29,5 +36,11 @@ export const getExportType = (): ExportTypeDefinition< jobContentExtension: 'csv', createJobFactory, executeJobFactory, - validLicenses: ['trial', 'basic', 'standard', 'gold', 'platinum'], + validLicenses: [ + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_BASIC, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, + ], }); diff --git a/x-pack/legacy/plugins/reporting/export_types/png/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/index.ts index 6e22416b5aa9c..bc00bc428f306 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/index.ts @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PNG_JOB_TYPE as jobType } from '../../common/constants'; +import { + PNG_JOB_TYPE as jobType, + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, +} from '../../common/constants'; import { ExportTypeDefinition, ESQueueCreateJobFn, ESQueueWorkerExecuteFn } from '../../types'; import { createJobFactory } from './server/create_job'; import { executeJobFactory } from './server/execute_job'; @@ -23,5 +29,10 @@ export const getExportType = (): ExportTypeDefinition< jobContentExtension: 'PNG', createJobFactory, executeJobFactory, - validLicenses: ['trial', 'standard', 'gold', 'platinum'], + validLicenses: [ + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, + ], }); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/index.ts index f2e9c9950ee9f..99880c1237a7a 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/index.ts @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PDF_JOB_TYPE as jobType } from '../../common/constants'; +import { + PDF_JOB_TYPE as jobType, + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, +} from '../../common/constants'; import { ExportTypeDefinition, ESQueueCreateJobFn, ESQueueWorkerExecuteFn } from '../../types'; import { createJobFactory } from './server/create_job'; import { executeJobFactory } from './server/execute_job'; @@ -23,5 +29,10 @@ export const getExportType = (): ExportTypeDefinition< jobContentExtension: 'pdf', createJobFactory, executeJobFactory, - validLicenses: ['trial', 'standard', 'gold', 'platinum'], + validLicenses: [ + LICENSE_TYPE_TRIAL, + LICENSE_TYPE_STANDARD, + LICENSE_TYPE_GOLD, + LICENSE_TYPE_PLATINUM, + ], });