[Reporting] Convert main Reporting index to TS (#49129)

* [Reporting] Convert main Reporting index to TS

* fix typo that broke build

* fix type file import

* fix the ts imports for build
This commit is contained in:
Tim Sullivan 2019-11-14 07:29:49 -07:00 committed by GitHub
parent 6219cfdc16
commit 0e9b576b6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 339 additions and 241 deletions

View file

@ -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();
}

View file

@ -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'),
];
},
});
};

View file

@ -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);
};

View file

@ -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}"`);
}
}

View file

@ -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<RangeStats>,

View file

@ -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];

View file

@ -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,

View file

@ -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;