diff --git a/x-pack/legacy/plugins/reporting/config.ts b/x-pack/legacy/plugins/reporting/config.ts new file mode 100644 index 000000000000..8b1d6f6f1980 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/config.ts @@ -0,0 +1,171 @@ +/* + * 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 { BROWSER_TYPE } from './common/constants'; +// @ts-ignore untyped module +import { config as appConfig } from './server/config/config'; +import { getDefaultChromiumSandboxDisabled } from './server/browsers'; + +export async function config(Joi: any) { + return Joi.object({ + enabled: Joi.boolean().default(true), + kibanaServer: Joi.object({ + protocol: Joi.string().valid(['http', 'https']), + hostname: Joi.string(), + port: Joi.number().integer(), + }).default(), + queue: Joi.object({ + indexInterval: Joi.string().default('week'), + pollEnabled: Joi.boolean().default(true), + pollInterval: Joi.number() + .integer() + .default(3000), + pollIntervalErrorMultiplier: Joi.number() + .integer() + .default(10), + timeout: Joi.number() + .integer() + .default(120000), + }).default(), + capture: Joi.object({ + networkPolicy: Joi.object({ + enabled: Joi.boolean().default(true), + rules: Joi.array() + .items( + Joi.object({ + allow: Joi.boolean().required(), + protocol: Joi.string(), + host: Joi.string(), + }) + ) + .default([ + { allow: true, protocol: 'http:' }, + { allow: true, protocol: 'https:' }, + { allow: true, protocol: 'ws:' }, + { allow: true, protocol: 'wss:' }, + { allow: true, protocol: 'data:' }, + { allow: false }, // Default action is to deny! + ]), + }).default(), + zoom: Joi.number() + .integer() + .default(2), + viewport: Joi.object({ + width: Joi.number() + .integer() + .default(1950), + height: Joi.number() + .integer() + .default(1200), + }).default(), + timeout: Joi.number() + .integer() + .default(20000), // deprecated + loadDelay: Joi.number() + .integer() + .default(3000), + settleTime: Joi.number() + .integer() + .default(1000), // deprecated + concurrency: Joi.number() + .integer() + .default(appConfig.concurrency), // deprecated + browser: Joi.object({ + type: Joi.any() + .valid(BROWSER_TYPE) + .default(BROWSER_TYPE), + autoDownload: Joi.boolean().when('$dist', { + is: true, + then: Joi.default(false), + otherwise: Joi.default(true), + }), + chromium: Joi.object({ + inspect: Joi.boolean() + .when('$dev', { + is: false, + then: Joi.valid(false), + else: Joi.default(false), + }) + .default(), + disableSandbox: Joi.boolean().default(await getDefaultChromiumSandboxDisabled()), + proxy: Joi.object({ + enabled: Joi.boolean().default(false), + server: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .when('enabled', { + is: Joi.valid(false), + then: Joi.valid(null), + else: Joi.required(), + }), + bypass: Joi.array() + .items(Joi.string().regex(/^[^\s]+$/)) + .when('enabled', { + is: Joi.valid(false), + then: Joi.valid(null), + else: Joi.default([]), + }), + }).default(), + maxScreenshotDimension: Joi.number() + .integer() + .default(1950), + }).default(), + }).default(), + maxAttempts: Joi.number() + .integer() + .greater(0) + .when('$dist', { + is: true, + then: Joi.default(3), + otherwise: Joi.default(1), + }) + .default(), + }).default(), + csv: Joi.object({ + checkForFormulas: Joi.boolean().default(true), + enablePanelActionDownload: Joi.boolean().default(true), + maxSizeBytes: Joi.number() + .integer() + .default(1024 * 1024 * 10), // bytes in a kB * kB in a mB * 10 + scroll: Joi.object({ + duration: Joi.string() + .regex(/^[0-9]+(d|h|m|s|ms|micros|nanos)$/, { name: 'DurationString' }) + .default('30s'), + size: Joi.number() + .integer() + .default(500), + }).default(), + }).default(), + encryptionKey: Joi.when(Joi.ref('$dist'), { + is: true, + then: Joi.string(), + otherwise: Joi.string().default('a'.repeat(32)), + }), + roles: Joi.object({ + allow: Joi.array() + .items(Joi.string()) + .default(['reporting_user']), + }).default(), + index: Joi.string().default('.reporting'), + poll: Joi.object({ + jobCompletionNotifier: Joi.object({ + interval: Joi.number() + .integer() + .default(10000), + intervalErrorMultiplier: Joi.number() + .integer() + .default(5), + }).default(), + jobsRefresh: Joi.object({ + interval: Joi.number() + .integer() + .default(5000), + intervalErrorMultiplier: Joi.number() + .integer() + .default(5), + }).default(), + }).default(), + }).default(); +} diff --git a/x-pack/legacy/plugins/reporting/index.js b/x-pack/legacy/plugins/reporting/index.js deleted file mode 100644 index 1fe83124e2b4..000000000000 --- a/x-pack/legacy/plugins/reporting/index.js +++ /dev/null @@ -1,226 +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 { resolve } from 'path'; -import { PLUGIN_ID, BROWSER_TYPE, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; -import { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status'; -import { registerRoutes } from './server/routes'; -import { - LevelLogger, - checkLicenseFactory, - createQueueFactory, - exportTypesRegistryFactory, - runValidations, -} from './server/lib'; -import { config as appConfig } from './server/config/config'; -import { createBrowserDriverFactory, getDefaultChromiumSandboxDisabled } from './server/browsers'; -import { logConfiguration } from './log_configuration'; -import { getReportingUsageCollector } from './server/usage'; -import { i18n } from '@kbn/i18n'; - -const kbToBase64Length = (kb) => { - return Math.floor((kb * 1024 * 8) / 6); -}; - -export const reporting = (kibana) => { - return new kibana.Plugin({ - id: PLUGIN_ID, - configPrefix: 'xpack.reporting', - publicDir: resolve(__dirname, 'public'), - require: ['kibana', 'elasticsearch', 'xpack_main'], - - uiExports: { - shareContextMenuExtensions: [ - 'plugins/reporting/share_context_menu/register_csv_reporting', - 'plugins/reporting/share_context_menu/register_reporting', - ], - embeddableActions: [ - 'plugins/reporting/panel_actions/get_csv_panel_action', - ], - home: ['plugins/reporting/register_feature'], - managementSections: ['plugins/reporting/views/management'], - injectDefaultVars(server, options) { - const config = server.config(); - return { - reportingPollConfig: options.poll, - enablePanelActionDownload: config.get('xpack.reporting.csv.enablePanelActionDownload'), - }; - }, - uiSettingDefaults: { - [UI_SETTINGS_CUSTOM_PDF_LOGO]: { - name: i18n.translate('xpack.reporting.pdfFooterImageLabel', { - defaultMessage: 'PDF footer image' - }), - value: null, - description: i18n.translate('xpack.reporting.pdfFooterImageDescription', { - defaultMessage: `Custom image to use in the PDF's footer` - }), - type: 'image', - options: { - maxSize: { - length: kbToBase64Length(200), - description: '200 kB', - } - }, - category: [PLUGIN_ID], - } - } - }, - - config: async function (Joi) { - return Joi.object({ - enabled: Joi.boolean().default(true), - kibanaServer: Joi.object({ - protocol: Joi.string().valid(['http', 'https']), - hostname: Joi.string(), - port: Joi.number().integer() - }).default(), - queue: Joi.object({ - indexInterval: Joi.string().default('week'), - pollEnabled: Joi.boolean().default(true), - pollInterval: Joi.number().integer().default(3000), - pollIntervalErrorMultiplier: Joi.number().integer().default(10), - timeout: Joi.number().integer().default(120000), - }).default(), - capture: Joi.object({ - networkPolicy: Joi.object({ - enabled: Joi.boolean().default(true), - rules: Joi.array().items(Joi.object({ - allow: Joi.boolean().required(), - protocol: Joi.string(), - host: Joi.string(), - })).default([ - { allow: true, protocol: 'http:' }, - { allow: true, protocol: 'https:' }, - { allow: true, protocol: 'ws:' }, - { allow: true, protocol: 'wss:' }, - { allow: true, protocol: 'data:' }, - { allow: false }, // Default action is to deny! - ]), - }).default(), - zoom: Joi.number().integer().default(2), - viewport: Joi.object({ - width: Joi.number().integer().default(1950), - height: Joi.number().integer().default(1200) - }).default(), - timeout: Joi.number().integer().default(20000), //deprecated - loadDelay: Joi.number().integer().default(3000), - settleTime: Joi.number().integer().default(1000), //deprecated - concurrency: Joi.number().integer().default(appConfig.concurrency), //deprecated - browser: Joi.object({ - type: Joi.any().valid(BROWSER_TYPE).default(BROWSER_TYPE), - autoDownload: Joi.boolean().when('$dist', { - is: true, - then: Joi.default(false), - otherwise: Joi.default(true), - }), - chromium: Joi.object({ - inspect: Joi.boolean().when('$dev', { - is: false, - then: Joi.valid(false), - else: Joi.default(false), - }).default(), - disableSandbox: Joi.boolean().default(await getDefaultChromiumSandboxDisabled()), - proxy: Joi.object({ - enabled: Joi.boolean().default(false), - server: Joi.string().uri({ scheme: ['http', 'https'] }).when('enabled', { - is: Joi.valid(false), - then: Joi.valid(null), - else: Joi.required() - }), - bypass: Joi.array().items(Joi.string().regex(/^[^\s]+$/)).when('enabled', { - is: Joi.valid(false), - then: Joi.valid(null), - else: Joi.default([]) - }) - }).default(), - maxScreenshotDimension: Joi.number().integer().default(1950) - }).default() - }).default(), - maxAttempts: Joi.number().integer().greater(0).when('$dist', { - is: true, - then: Joi.default(3), - otherwise: Joi.default(1), - }).default() - }).default(), - csv: Joi.object({ - checkForFormulas: Joi.boolean().default(true), - enablePanelActionDownload: Joi.boolean().default(true), - maxSizeBytes: Joi.number().integer().default(1024 * 1024 * 10), // bytes in a kB * kB in a mB * 10 - scroll: Joi.object({ - duration: Joi.string().regex(/^[0-9]+(d|h|m|s|ms|micros|nanos)$/, { name: 'DurationString' }).default('30s'), - size: Joi.number().integer().default(500) - }).default(), - }).default(), - encryptionKey: Joi.when(Joi.ref('$dist'), { - is: true, - then: Joi.string(), - otherwise: Joi.string().default('a'.repeat(32)), - }), - roles: Joi.object({ - allow: Joi.array().items(Joi.string()).default(['reporting_user']), - }).default(), - index: Joi.string().default('.reporting'), - poll: Joi.object({ - jobCompletionNotifier: Joi.object({ - interval: Joi.number().integer().default(10000), - intervalErrorMultiplier: Joi.number().integer().default(5) - }).default(), - jobsRefresh: Joi.object({ - interval: Joi.number().integer().default(5000), - intervalErrorMultiplier: Joi.number().integer().default(5) - }).default(), - }).default(), - }).default(); - }, - - // TODO: Decouple Hapi: Build a server facade object based on the server to - // pass through to the libs. Do not pass server directly - init: async function (server) { - let isCollectorReady = false; - const isReady = () => isCollectorReady; - // Register a function with server to manage the collection of usage stats - server.usage.collectorSet.register(getReportingUsageCollector(server, isReady)); - - const logger = LevelLogger.createForServer(server, [PLUGIN_ID]); - const [exportTypesRegistry, browserFactory] = await Promise.all([ - exportTypesRegistryFactory(server), - createBrowserDriverFactory(server), - ]); - server.expose('exportTypesRegistry', exportTypesRegistry); - - logConfiguration(server, logger); - runValidations(server, logger, browserFactory); - - const { xpack_main: xpackMainPlugin } = server.plugins; - mirrorPluginStatus(xpackMainPlugin, this); - const checkLicense = checkLicenseFactory(exportTypesRegistry); - xpackMainPlugin.status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(this.id).registerLicenseCheckResultsGenerator(checkLicense); - }); - - // 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)); - - // Reporting routes - registerRoutes(server, logger); - }, - - deprecations: function ({ unused }) { - return [ - unused('capture.concurrency'), - unused('capture.timeout'), - unused('capture.settleTime'), - unused('kibanaApp'), - ]; - }, - }); -}; diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts new file mode 100644 index 000000000000..e2b5970d1efb --- /dev/null +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -0,0 +1,121 @@ +/* + * 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 { resolve } from 'path'; +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 { + LevelLogger, + checkLicenseFactory, + createQueueFactory, + exportTypesRegistryFactory, + runValidations, +} from './server/lib'; +import { config as reportingConfig } from './config'; +import { logConfiguration } from './log_configuration'; +import { createBrowserDriverFactory } from './server/browsers'; +import { getReportingUsageCollector } from './server/usage'; +import { ReportingConfigOptions, ReportingPluginSpecOptions, ServerFacade } from './types.d'; + +const kbToBase64Length = (kb: number) => { + return Math.floor((kb * 1024 * 8) / 6); +}; + +export const reporting = (kibana: any) => { + return new kibana.Plugin({ + id: PLUGIN_ID, + configPrefix: 'xpack.reporting', + publicDir: resolve(__dirname, 'public'), + require: ['kibana', 'elasticsearch', 'xpack_main'], + config: reportingConfig, + + uiExports: { + shareContextMenuExtensions: [ + 'plugins/reporting/share_context_menu/register_csv_reporting', + 'plugins/reporting/share_context_menu/register_reporting', + ], + embeddableActions: ['plugins/reporting/panel_actions/get_csv_panel_action'], + home: ['plugins/reporting/register_feature'], + managementSections: ['plugins/reporting/views/management'], + injectDefaultVars(server: ServerFacade, options?: ReportingConfigOptions) { + const config = server.config(); + return { + reportingPollConfig: options ? options.poll : {}, + enablePanelActionDownload: config.get('xpack.reporting.csv.enablePanelActionDownload'), + }; + }, + uiSettingDefaults: { + [UI_SETTINGS_CUSTOM_PDF_LOGO]: { + name: i18n.translate('xpack.reporting.pdfFooterImageLabel', { + defaultMessage: 'PDF footer image', + }), + value: null, + description: i18n.translate('xpack.reporting.pdfFooterImageDescription', { + defaultMessage: `Custom image to use in the PDF's footer`, + }), + type: 'image', + options: { + maxSize: { + length: kbToBase64Length(200), + description: '200 kB', + }, + }, + category: [PLUGIN_ID], + }, + }, + }, + + // 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) { + let isCollectorReady = false; + // Register a function with server to manage the collection of usage stats + server.usage.collectorSet.register( + getReportingUsageCollector(server, () => isCollectorReady) + ); + + const logger = LevelLogger.createForServer(server, [PLUGIN_ID]); + const [exportTypesRegistry, browserFactory] = await Promise.all([ + exportTypesRegistryFactory(server), + createBrowserDriverFactory(server), + ]); + server.expose('exportTypesRegistry', exportTypesRegistry); + + logConfiguration(server, logger); + runValidations(server, logger, browserFactory); + + const { xpack_main: xpackMainPlugin } = server.plugins; + mirrorPluginStatus(xpackMainPlugin, this); + const checkLicense = checkLicenseFactory(exportTypesRegistry); + xpackMainPlugin.status.once('green', () => { + // Register a function that is called whenever the xpack info changes, + // to re-compute the license check results for this plugin + xpackMainPlugin.info.feature(this.id).registerLicenseCheckResultsGenerator(checkLicense); + }); + + // 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)); + + // Reporting routes + registerRoutes(server, logger); + }, + + deprecations({ unused }: any) { + return [ + unused('capture.concurrency'), + unused('capture.timeout'), + unused('capture.settleTime'), + unused('kibanaApp'), + ]; + }, + } as ReportingPluginSpecOptions); +}; diff --git a/x-pack/legacy/plugins/reporting/log_configuration.js b/x-pack/legacy/plugins/reporting/log_configuration.ts similarity index 50% rename from x-pack/legacy/plugins/reporting/log_configuration.js rename to x-pack/legacy/plugins/reporting/log_configuration.ts index 94ff90f5944d..b07475df6304 100644 --- a/x-pack/legacy/plugins/reporting/log_configuration.js +++ b/x-pack/legacy/plugins/reporting/log_configuration.ts @@ -4,21 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ -import getosSync from 'getos'; +import getosSync, { LinuxOs } from 'getos'; import { promisify } from 'util'; +import { ServerFacade, Logger } from './types'; const getos = promisify(getosSync); -export async function logConfiguration(server, logger) { +export async function logConfiguration(server: ServerFacade, logger: Logger) { const config = server.config(); const browserType = config.get('xpack.reporting.capture.browser.type'); logger.debug(`Browser type: ${browserType}`); if (browserType === 'chromium') { - logger.debug(`Chromium sandbox disabled: ${config.get('xpack.reporting.capture.browser.chromium.disableSandbox')}`); + logger.debug( + `Chromium sandbox disabled: ${config.get( + 'xpack.reporting.capture.browser.chromium.disableSandbox' + )}` + ); } const os = await getos(); - logger.debug(`Running on os "${os.os}", distribution "${os.dist}", release "${os.release}"`); + const { os: osName, dist, release } = os as LinuxOs; + if (dist) { + logger.debug(`Running on os "${osName}", distribution "${dist}", release "${release}"`); + } else { + logger.debug(`Running on os "${osName}"`); + } } diff --git a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts index cec69a74e7e2..0118dea38d98 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts @@ -6,7 +6,7 @@ import { uniq } from 'lodash'; import { CSV_JOB_TYPE, PDF_JOB_TYPE, PNG_JOB_TYPE } from '../../common/constants'; -import { AvailableTotal, FeatureAvailabilityMap, RangeStats, ExportType } from './'; +import { AvailableTotal, FeatureAvailabilityMap, RangeStats, ExportType } from './types.d'; function getForFeature( range: Partial, 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 51d8d9157b9b..69be64580ff5 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 @@ -14,7 +14,7 @@ import { RangeAggregationResults, RangeStats, UsageObject, -} from './'; +} from './types'; import { decorateRangeStats } from './decorate_range_stats'; // @ts-ignore untyped module import { getExportTypesHandler } from './get_export_type_handler'; @@ -37,14 +37,17 @@ const getKeyCount = (buckets: KeyCountBucket[]): { [key: string]: number } => function getAggStats(aggs: AggregationResults) { const { buckets: jobBuckets } = aggs[JOB_TYPES_KEY] as AggregationBuckets; - const jobTypes = jobBuckets.reduce((accum, { key, doc_count: count }) => { - return { - ...accum, - [key]: { - total: count, - }, - }; - }, {}) as JobTypes; + const jobTypes: JobTypes = jobBuckets.reduce( + (accum: JobTypes, { key, doc_count: count }: { key: string; doc_count: number }) => { + return { + ...accum, + [key]: { + total: count, + }, + }; + }, + {} as JobTypes + ); // merge pdf stats into pdf jobtype key const pdfJobs = jobTypes[PRINTABLE_PDF_JOBTYPE]; diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage_collector.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage_collector.ts index 42b439e49b26..0e1e0f36ab94 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage_collector.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage_collector.ts @@ -13,7 +13,7 @@ import { getReportingUsage } from './get_reporting_usage'; * @param {Object} server * @return {Object} kibana usage stats type collection object */ -export function getReportingUsageCollector(server: any, isReady: boolean) { +export function getReportingUsageCollector(server: any, isReady: () => boolean) { const { collectorSet } = server.usage; return collectorSet.makeUsageCollector({ type: KIBANA_REPORTING_TYPE, diff --git a/x-pack/legacy/plugins/reporting/server/usage/index.js b/x-pack/legacy/plugins/reporting/server/usage/index.ts similarity index 100% rename from x-pack/legacy/plugins/reporting/server/usage/index.js rename to x-pack/legacy/plugins/reporting/server/usage/index.ts diff --git a/x-pack/legacy/plugins/reporting/server/usage/index.d.ts b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts similarity index 100% rename from x-pack/legacy/plugins/reporting/server/usage/index.d.ts rename to x-pack/legacy/plugins/reporting/server/usage/types.d.ts diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index 731d0f084a71..a34943786ac5 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -24,6 +24,23 @@ export interface ReportingPlugin { }; browserDriverFactory: HeadlessChromiumDriverFactory; } + +export interface ReportingConfigOptions { + browser: BrowserConfig; + poll: { + jobCompletionNotifier: { + interval: number; + intervalErrorMultiplier: number; + }; + jobsRefresh: { + interval: number; + intervalErrorMultiplier: number; + }; + }; + queue: QueueConfig; + capture: CaptureConfig; +} + export interface NetworkPolicyRule { allow: boolean; protocol: string; @@ -36,6 +53,8 @@ export interface NetworkPolicy { } // Tracks which parts of the legacy plugin system are being used +export type ReportingPluginSpecOptions = Legacy.PluginSpecOptions; + export type ServerFacade = Legacy.Server & { plugins: { reporting?: ReportingPlugin;