[Reporting/NP Migration] Remove server.expose of ExportTypeRegistry (#50973)
* [Reporting/NPMigration] typescriptify ExportTypeRegistry, remove from server.expose * Minor routes registration cleanup * move the ETR test file * Re-pack the route registration, reduce LOC changes * add EnqueueJobFn type * Fix usage collector test * remove a throw error used for development/debugging * fix imports error * Fix execute job tests * wip test fixes * test fixes for real * fix more tests * fix diffs * Add TODOs about the ExportTypesRegistry.register unwrap the factory functions. * really make headlessbrowserdriver required as an execute job factory option * fix tests * Use constants for license type keywords
This commit is contained in:
parent
f53e1a9dbd
commit
711b44b7fb
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
39
x-pack/legacy/plugins/reporting/export_types/csv/index.ts
Normal file
39
x-pack/legacy/plugins/reporting/export_types/csv/index.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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,
|
||||
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';
|
||||
import { executeJobFactory } from './server/execute_job';
|
||||
import { JobParamsDiscoverCsv, JobDocPayloadDiscoverCsv } from './types';
|
||||
|
||||
export const getExportType = (): ExportTypeDefinition<
|
||||
JobParamsDiscoverCsv,
|
||||
ESQueueCreateJobFn<JobParamsDiscoverCsv>,
|
||||
JobDocPayloadDiscoverCsv,
|
||||
ESQueueWorkerExecuteFn<JobDocPayloadDiscoverCsv>
|
||||
> => ({
|
||||
...metadata,
|
||||
jobType,
|
||||
jobContentExtension: 'csv',
|
||||
createJobFactory,
|
||||
executeJobFactory,
|
||||
validLicenses: [
|
||||
LICENSE_TYPE_TRIAL,
|
||||
LICENSE_TYPE_BASIC,
|
||||
LICENSE_TYPE_STANDARD,
|
||||
LICENSE_TYPE_GOLD,
|
||||
LICENSE_TYPE_PLATINUM,
|
||||
],
|
||||
});
|
|
@ -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'],
|
||||
});
|
||||
}
|
|
@ -4,9 +4,43 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
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';
|
||||
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>,
|
||||
JobParamsPanelCsv,
|
||||
ImmediateExecuteFn<JobParamsPanelCsv>
|
||||
> => ({
|
||||
...metadata,
|
||||
jobType: CSV_FROM_SAVEDOBJECT_JOB_TYPE,
|
||||
jobContentExtension: 'csv',
|
||||
createJobFactory,
|
||||
executeJobFactory,
|
||||
validLicenses: [
|
||||
LICENSE_TYPE_TRIAL,
|
||||
LICENSE_TYPE_BASIC,
|
||||
LICENSE_TYPE_STANDARD,
|
||||
LICENSE_TYPE_GOLD,
|
||||
LICENSE_TYPE_PLATINUM,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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'],
|
||||
});
|
||||
}
|
38
x-pack/legacy/plugins/reporting/export_types/png/index.ts
Normal file
38
x-pack/legacy/plugins/reporting/export_types/png/index.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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,
|
||||
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';
|
||||
import { metadata } from './metadata';
|
||||
import { JobParamsPNG, JobDocPayloadPNG } from './types';
|
||||
|
||||
export const getExportType = (): ExportTypeDefinition<
|
||||
JobParamsPNG,
|
||||
ESQueueCreateJobFn<JobParamsPNG>,
|
||||
JobDocPayloadPNG,
|
||||
ESQueueWorkerExecuteFn<JobDocPayloadPNG>
|
||||
> => ({
|
||||
...metadata,
|
||||
jobType,
|
||||
jobContentEncoding: 'base64',
|
||||
jobContentExtension: 'PNG',
|
||||
createJobFactory,
|
||||
executeJobFactory,
|
||||
validLicenses: [
|
||||
LICENSE_TYPE_TRIAL,
|
||||
LICENSE_TYPE_STANDARD,
|
||||
LICENSE_TYPE_GOLD,
|
||||
LICENSE_TYPE_PLATINUM,
|
||||
],
|
||||
});
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
@ -18,10 +23,13 @@ import {
|
|||
import { JobDocPayloadPNG } from '../../types';
|
||||
import { generatePngObservableFactory } from '../lib/generate_png';
|
||||
|
||||
export const executeJobFactory: ExecuteJobFactory<ESQueueWorkerExecuteFn<
|
||||
JobDocPayloadPNG
|
||||
>> = function executeJobFactoryFn(server: ServerFacade) {
|
||||
const generatePngObservable = generatePngObservableFactory(server);
|
||||
type QueuedPngExecutorFactory = ExecuteJobFactory<ESQueueWorkerExecuteFn<JobDocPayloadPNG>>;
|
||||
|
||||
export const executeJobFactory: QueuedPngExecutorFactory = function executeJobFactoryFn(
|
||||
server: ServerFacade,
|
||||
{ browserDriverFactory }: { browserDriverFactory: HeadlessChromiumDriverFactory }
|
||||
) {
|
||||
const generatePngObservable = generatePngObservableFactory(server, browserDriverFactory);
|
||||
const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PNG_JOB_TYPE, 'execute']);
|
||||
|
||||
return function executeJob(
|
||||
|
|
|
@ -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'],
|
||||
});
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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,
|
||||
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';
|
||||
import { metadata } from './metadata';
|
||||
import { JobParamsPDF, JobDocPayloadPDF } from './types';
|
||||
|
||||
export const getExportType = (): ExportTypeDefinition<
|
||||
JobParamsPDF,
|
||||
ESQueueCreateJobFn<JobParamsPDF>,
|
||||
JobDocPayloadPDF,
|
||||
ESQueueWorkerExecuteFn<JobDocPayloadPDF>
|
||||
> => ({
|
||||
...metadata,
|
||||
jobType,
|
||||
jobContentEncoding: 'base64',
|
||||
jobContentExtension: 'pdf',
|
||||
createJobFactory,
|
||||
executeJobFactory,
|
||||
validLicenses: [
|
||||
LICENSE_TYPE_TRIAL,
|
||||
LICENSE_TYPE_STANDARD,
|
||||
LICENSE_TYPE_GOLD,
|
||||
LICENSE_TYPE_PLATINUM,
|
||||
],
|
||||
});
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
@ -19,10 +24,13 @@ import {
|
|||
getCustomLogo,
|
||||
} from '../../../common/execute_job/';
|
||||
|
||||
export const executeJobFactory: ExecuteJobFactory<ESQueueWorkerExecuteFn<
|
||||
JobDocPayloadPDF
|
||||
>> = function executeJobFactoryFn(server: ServerFacade) {
|
||||
const generatePdfObservable = generatePdfObservableFactory(server);
|
||||
type QueuedPdfExecutorFactory = ExecuteJobFactory<ESQueueWorkerExecuteFn<JobDocPayloadPDF>>;
|
||||
|
||||
export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFactoryFn(
|
||||
server: ServerFacade,
|
||||
{ browserDriverFactory }: { browserDriverFactory: HeadlessChromiumDriverFactory }
|
||||
) {
|
||||
const generatePdfObservable = generatePdfObservableFactory(server, browserDriverFactory);
|
||||
const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PDF_JOB_TYPE, 'execute']);
|
||||
|
||||
return function executeJob(
|
||||
|
|
|
@ -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'],
|
||||
});
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -13,8 +13,7 @@ import { registerRoutes } from './server/routes';
|
|||
import {
|
||||
LevelLogger,
|
||||
checkLicenseFactory,
|
||||
createQueueFactory,
|
||||
exportTypesRegistryFactory,
|
||||
getExportTypesRegistry,
|
||||
runValidations,
|
||||
} from './server/lib';
|
||||
import { config as reportingConfig } from './config';
|
||||
|
@ -74,20 +73,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 +103,8 @@ 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));
|
||||
|
||||
// Reporting routes
|
||||
registerRoutes(server, logger);
|
||||
registerRoutes(server, exportTypesRegistry, browserDriverFactory, logger);
|
||||
},
|
||||
|
||||
deprecations({ unused }: any) {
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
|
||||
import * as sinon from 'sinon';
|
||||
import { ServerFacade } from '../../types';
|
||||
import { ServerFacade, HeadlessChromiumDriverFactory } from '../../types';
|
||||
import { ExportTypesRegistry } from './export_types_registry';
|
||||
import { createWorkerFactory } from './create_worker';
|
||||
// @ts-ignore
|
||||
import { Esqueue } from './esqueue';
|
||||
|
@ -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,7 +46,11 @@ describe('Create Worker', () => {
|
|||
});
|
||||
|
||||
test('Creates a single Esqueue worker for Reporting', async () => {
|
||||
const createWorker = createWorkerFactory(getMockServer());
|
||||
const exportTypesRegistry = getMockExportTypesRegistry();
|
||||
const createWorker = createWorkerFactory(getMockServer(), {
|
||||
exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry,
|
||||
browserDriverFactory: {} as HeadlessChromiumDriverFactory,
|
||||
});
|
||||
const registerWorkerSpy = sinon.spy(queue, 'registerWorker');
|
||||
|
||||
createWorker(queue);
|
||||
|
@ -68,15 +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 },
|
||||
])
|
||||
);
|
||||
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);
|
||||
|
|
|
@ -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,21 @@ import {
|
|||
import { events as esqueueEvents } from './esqueue';
|
||||
import { LevelLogger } from './level_logger';
|
||||
|
||||
export function createWorkerFactory<JobParamsType>(server: ServerFacade) {
|
||||
interface CreateWorkerFactoryOpts {
|
||||
exportTypesRegistry: ExportTypesRegistry;
|
||||
browserDriverFactory: HeadlessChromiumDriverFactory;
|
||||
}
|
||||
|
||||
export function createWorkerFactory<JobParamsType>(
|
||||
server: ServerFacade,
|
||||
{ exportTypesRegistry, browserDriverFactory }: CreateWorkerFactoryOpts
|
||||
) {
|
||||
type JobDocPayloadType = JobDocPayload<JobParamsType>;
|
||||
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<JobParamsType, JobDocPayloadType>) {
|
||||
|
@ -41,8 +49,9 @@ export function createWorkerFactory<JobParamsType>(server: ServerFacade) {
|
|||
for (const exportType of exportTypesRegistry.getAll() as Array<
|
||||
ExportTypeDefinition<JobParamsType, any, any, any>
|
||||
>) {
|
||||
const executeJobFactory = exportType.executeJobFactory(server);
|
||||
jobExecutors.set(exportType.jobType, executeJobFactory);
|
||||
// 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);
|
||||
}
|
||||
|
||||
const workerFn = (jobSource: JobSource<JobParamsType>, ...workerRestArgs: any[]) => {
|
||||
|
|
|
@ -8,12 +8,14 @@ import { get } from 'lodash';
|
|||
// @ts-ignore
|
||||
import { events as esqueueEvents } from './esqueue';
|
||||
import {
|
||||
EnqueueJobFn,
|
||||
ESQueueCreateJobFn,
|
||||
ImmediateCreateJobFn,
|
||||
Job,
|
||||
ServerFacade,
|
||||
RequestFacade,
|
||||
Logger,
|
||||
ExportTypesRegistry,
|
||||
CaptureConfig,
|
||||
QueueConfig,
|
||||
ConditionalHeaders,
|
||||
|
@ -26,13 +28,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
|
||||
): EnqueueJobFn {
|
||||
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<JobParamsType>(
|
||||
parentLogger: Logger,
|
||||
|
@ -46,6 +55,12 @@ 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!`);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
|
@ -57,7 +72,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) {
|
||||
|
|
|
@ -4,40 +4,110 @@
|
|||
* 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';
|
||||
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';
|
||||
|
||||
function scan(pattern: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
glob(pattern, {}, (err, files) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
type GetCallbackFn<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType> = (
|
||||
item: ExportTypeDefinition<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType>
|
||||
) => boolean;
|
||||
// => ExportTypeDefinition<T, U, V, W>
|
||||
|
||||
export class ExportTypesRegistry {
|
||||
private _map: Map<string, ExportTypeDefinition<any, any, any, any>> = new Map();
|
||||
|
||||
constructor() {}
|
||||
|
||||
register<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType>(
|
||||
item: ExportTypeDefinition<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType>
|
||||
): 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`);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return Array.from(this._map.values());
|
||||
}
|
||||
|
||||
getSize() {
|
||||
return this._map.size;
|
||||
}
|
||||
|
||||
getById<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType>(
|
||||
id: string
|
||||
): ExportTypeDefinition<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType> {
|
||||
if (!this._map.has(id)) {
|
||||
throw new Error(`Unknown id ${id}`);
|
||||
}
|
||||
|
||||
return this._map.get(id) as ExportTypeDefinition<
|
||||
JobParamsType,
|
||||
CreateJobFnType,
|
||||
JobPayloadType,
|
||||
ExecuteJobFnType
|
||||
>;
|
||||
}
|
||||
|
||||
get<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType>(
|
||||
findType: GetCallbackFn<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType>
|
||||
): ExportTypeDefinition<JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType> {
|
||||
let result;
|
||||
for (const value of this._map.values()) {
|
||||
if (!findType(value)) {
|
||||
continue; // try next value
|
||||
}
|
||||
const foundResult: ExportTypeDefinition<
|
||||
JobParamsType,
|
||||
CreateJobFnType,
|
||||
JobPayloadType,
|
||||
ExecuteJobFnType
|
||||
> = value;
|
||||
|
||||
if (result) {
|
||||
throw new Error('Found multiple items matching predicate.');
|
||||
}
|
||||
|
||||
resolve(files);
|
||||
});
|
||||
});
|
||||
result = foundResult;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
throw new Error('Found no items matching predicate');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
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[];
|
||||
function getExportTypesRegistryFn(): ExportTypesRegistry {
|
||||
const registry = new ExportTypesRegistry();
|
||||
|
||||
files.forEach(file => {
|
||||
logger.debug(`Found exportType at ${file}`);
|
||||
|
||||
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<any, any, any, any>> = [
|
||||
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);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
ServerFacade,
|
||||
RequestFacade,
|
||||
ResponseFacade,
|
||||
HeadlessChromiumDriverFactory,
|
||||
ReportingResponseToolkit,
|
||||
Logger,
|
||||
JobDocOutputExecuted,
|
||||
|
@ -45,8 +46,17 @@ 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 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,
|
||||
|
|
83
x-pack/legacy/plugins/reporting/server/routes/generation.ts
Normal file
83
x-pack/legacy/plugins/reporting/server/routes/generation.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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,
|
||||
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,
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -4,69 +4,21 @@
|
|||
* 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 { enqueueJobFactory } from '../lib/enqueue_job';
|
||||
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';
|
||||
import {
|
||||
ServerFacade,
|
||||
ExportTypesRegistry,
|
||||
HeadlessChromiumDriverFactory,
|
||||
Logger,
|
||||
} from '../../types';
|
||||
import { registerJobGenerationRoutes } from './generation';
|
||||
import { registerJobInfoRoutes } from './jobs';
|
||||
|
||||
export function registerRoutes(server: ServerFacade, 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
|
||||
*/
|
||||
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);
|
||||
export function registerRoutes(
|
||||
server: ServerFacade,
|
||||
exportTypesRegistry: ExportTypesRegistry,
|
||||
browserDriverFactory: HeadlessChromiumDriverFactory,
|
||||
logger: Logger
|
||||
) {
|
||||
registerJobGenerationRoutes(server, exportTypesRegistry, browserDriverFactory, logger);
|
||||
registerJobInfoRoutes(server, exportTypesRegistry, logger);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import Hapi from 'hapi';
|
||||
import { difference, memoize } from 'lodash';
|
||||
import { registerJobs } from './jobs';
|
||||
import { ExportTypesRegistry } from '../../common/export_types_registry';
|
||||
import { registerJobInfoRoutes } from './jobs';
|
||||
import { ExportTypesRegistry } from '../lib/export_types_registry';
|
||||
jest.mock('./lib/authorized_user_pre_routing', () => {
|
||||
return {
|
||||
authorizedUserPreRoutingFactory: () => () => ({})
|
||||
|
@ -19,13 +19,17 @@ jest.mock('./lib/reporting_feature_pre_routing', () => {
|
|||
};
|
||||
});
|
||||
|
||||
|
||||
let mockServer;
|
||||
let exportTypesRegistry;
|
||||
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() }));
|
||||
const exportTypesRegistry = new ExportTypesRegistry();
|
||||
exportTypesRegistry = new ExportTypesRegistry();
|
||||
exportTypesRegistry.register({
|
||||
id: 'unencoded',
|
||||
jobType: 'unencodedJobType',
|
||||
|
@ -44,9 +48,6 @@ beforeEach(() => {
|
|||
callWithRequest: jest.fn(),
|
||||
callWithInternalUser: jest.fn(),
|
||||
}))
|
||||
},
|
||||
reporting: {
|
||||
exportTypesRegistry
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -63,7 +64,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 +80,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',
|
||||
|
@ -91,12 +92,11 @@ 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' })));
|
||||
|
||||
registerJobs(mockServer);
|
||||
registerJobInfoRoutes(mockServer, exportTypesRegistry, mockLogger);
|
||||
|
||||
const request = {
|
||||
method: 'GET',
|
||||
|
@ -133,7 +133,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 +178,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',
|
||||
|
|
|
@ -8,6 +8,8 @@ import boom from 'boom';
|
|||
import { API_BASE_URL } from '../../common/constants';
|
||||
import {
|
||||
ServerFacade,
|
||||
ExportTypesRegistry,
|
||||
Logger,
|
||||
RequestFacade,
|
||||
ReportingResponseToolkit,
|
||||
JobDocOutput,
|
||||
|
@ -24,7 +26,11 @@ import {
|
|||
|
||||
const MAIN_ENTRY = `${API_BASE_URL}/jobs`;
|
||||
|
||||
export function registerJobs(server: ServerFacade) {
|
||||
export function registerJobInfoRoutes(
|
||||
server: ServerFacade,
|
||||
exportTypesRegistry: ExportTypesRegistry,
|
||||
logger: Logger
|
||||
) {
|
||||
const jobsQuery = jobsQueryFactory(server);
|
||||
const getRouteConfig = getRouteConfigFactoryManagementPre(server);
|
||||
const getRouteConfigDownload = getRouteConfigFactoryDownloadPre(server);
|
||||
|
@ -119,7 +125,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',
|
||||
|
@ -136,13 +142,15 @@ export function registerJobs(server: ServerFacade) {
|
|||
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} has non-OK status: [${statusCode}] Reason: [${JSON.stringify(
|
||||
response.source
|
||||
)}]`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.isBoom) {
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 }) {
|
||||
|
@ -40,7 +43,6 @@ function getServerMock(customization) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expose: () => {},
|
||||
log: () => {},
|
||||
config: () => ({
|
||||
get: key => {
|
||||
|
@ -67,8 +69,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,
|
||||
() => {},
|
||||
exportTypesRegistry
|
||||
);
|
||||
usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry);
|
||||
});
|
||||
|
||||
test('sets enables to true', async () => {
|
||||
|
@ -93,8 +100,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,
|
||||
() => {},
|
||||
exportTypesRegistry
|
||||
);
|
||||
usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry);
|
||||
});
|
||||
|
||||
test('sets enables to true', async () => {
|
||||
|
@ -121,9 +133,11 @@ describe('license checks', () => {
|
|||
const usageCollection = getMockUsageCollection();
|
||||
const { fetch: getReportingUsage } = getReportingUsageCollector(
|
||||
usageCollection,
|
||||
serverWithPlatinumLicenseMock
|
||||
serverWithPlatinumLicenseMock,
|
||||
() => {},
|
||||
exportTypesRegistry
|
||||
);
|
||||
usageStats = await getReportingUsage(callClusterMock);
|
||||
usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry);
|
||||
});
|
||||
|
||||
test('sets enables to true', async () => {
|
||||
|
@ -148,8 +162,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,
|
||||
() => {},
|
||||
exportTypesRegistry
|
||||
);
|
||||
usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry);
|
||||
});
|
||||
|
||||
test('sets enables to true', async () => {
|
||||
|
@ -170,7 +189,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,
|
||||
() => {},
|
||||
exportTypesRegistry
|
||||
));
|
||||
});
|
||||
|
||||
test('with normal looking usage data', async () => {
|
||||
|
@ -295,6 +319,7 @@ describe('data modeling', () => {
|
|||
})
|
||||
)
|
||||
);
|
||||
|
||||
const usageStats = await getReportingUsage(callClusterMock);
|
||||
expect(usageStats).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
55
x-pack/legacy/plugins/reporting/types.d.ts
vendored
55
x-pack/legacy/plugins/reporting/types.d.ts
vendored
|
@ -13,9 +13,12 @@ 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';
|
||||
|
||||
export type ReportingPlugin = object; // For Plugin contract
|
||||
|
||||
export type Job = EventEmitter & {
|
||||
id: string;
|
||||
toJSON: () => {
|
||||
|
@ -23,21 +26,6 @@ export type Job = EventEmitter & {
|
|||
};
|
||||
};
|
||||
|
||||
export interface ReportingPlugin {
|
||||
queue: {
|
||||
addJob: <PayloadType>(type: string, payload: PayloadType, options: object) => Job;
|
||||
};
|
||||
// TODO: convert exportTypesRegistry to TS
|
||||
exportTypesRegistry: {
|
||||
getById: <T, U, V, W>(id: string) => ExportTypeDefinition<T, U, V, W>;
|
||||
getAll: <T, U, V, W>() => Array<ExportTypeDefinition<T, U, V, W>>;
|
||||
get: <T, U, V, W>(
|
||||
callback: (item: ExportTypeDefinition<T, U, V, W>) => boolean
|
||||
) => ExportTypeDefinition<T, U, V, W>;
|
||||
};
|
||||
browserDriverFactory: HeadlessChromiumDriverFactory;
|
||||
}
|
||||
|
||||
export interface ReportingConfigOptions {
|
||||
browser: BrowserConfig;
|
||||
poll: {
|
||||
|
@ -88,7 +76,6 @@ export type ReportingPluginSpecOptions = Legacy.PluginSpecOptions;
|
|||
|
||||
export type ServerFacade = Legacy.Server & {
|
||||
plugins: {
|
||||
reporting?: ReportingPlugin;
|
||||
xpack_main?: XPackMainPlugin & {
|
||||
status?: any;
|
||||
};
|
||||
|
@ -107,6 +94,15 @@ interface ReportingRequest {
|
|||
};
|
||||
}
|
||||
|
||||
export type EnqueueJobFn = <JobParamsType>(
|
||||
parentLogger: LevelLogger,
|
||||
exportTypeId: string,
|
||||
jobParams: JobParamsType,
|
||||
user: string,
|
||||
headers: Record<string, string>,
|
||||
request: RequestFacade
|
||||
) => Promise<Job>;
|
||||
|
||||
export type RequestFacade = ReportingRequest & Legacy.Request;
|
||||
|
||||
export type ResponseFacade = ResponseObject & {
|
||||
|
@ -246,6 +242,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 +304,12 @@ export interface ESQueueInstance<JobParamsType, JobDocPayloadType> {
|
|||
}
|
||||
|
||||
export type CreateJobFactory<CreateJobFnType> = (server: ServerFacade) => CreateJobFnType;
|
||||
export type ExecuteJobFactory<ExecuteJobFnType> = (server: ServerFacade) => ExecuteJobFnType;
|
||||
export type ExecuteJobFactory<ExecuteJobFnType> = (
|
||||
server: ServerFacade,
|
||||
opts: {
|
||||
browserDriverFactory: HeadlessChromiumDriverFactory;
|
||||
}
|
||||
) => ExecuteJobFnType;
|
||||
|
||||
export interface ExportTypeDefinition<
|
||||
JobParamsType,
|
||||
|
@ -322,21 +327,13 @@ export interface ExportTypeDefinition<
|
|||
validLicenses: string[];
|
||||
}
|
||||
|
||||
export interface ExportTypesRegistry {
|
||||
register: <JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType>(
|
||||
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';`
|
||||
export { LevelLogger as Logger } from './server/lib/level_logger';
|
||||
export { LevelLogger as Logger };
|
||||
|
||||
export interface AbsoluteURLFactoryOptions {
|
||||
defaultBasePath: string;
|
||||
|
|
Loading…
Reference in a new issue