[Reporting] Remove boilerplate needed to get thegetScreenshots function (#64269)

* expose reportingCore in ReportingSetup for apps

* improve tests

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Tim Sullivan 2020-04-27 14:25:26 -07:00 committed by GitHub
parent 4c460b8c42
commit 54dc148226
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 148 additions and 93 deletions

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { screenshotsObservableFactory } from './observable';
export { screenshotsObservableFactory, ScreenshotsObservableFn } from './observable';

View file

@ -21,10 +21,18 @@ import { waitForVisualizations } from './wait_for_visualizations';
const DEFAULT_SCREENSHOT_CLIP_HEIGHT = 1200;
const DEFAULT_SCREENSHOT_CLIP_WIDTH = 1800;
export type ScreenshotsObservableFn = ({
logger,
urls,
conditionalHeaders,
layout,
browserTimezone,
}: ScreenshotObservableOpts) => Rx.Observable<ScreenshotResults[]>;
export function screenshotsObservableFactory(
captureConfig: CaptureConfig,
browserDriverFactory: HeadlessChromiumDriverFactory
) {
): ScreenshotsObservableFn {
return function screenshotsObservable({
logger,
urls,

View file

@ -5,14 +5,14 @@
*/
import * as Rx from 'rxjs';
import { createMockReportingCore, createMockBrowserDriverFactory } from '../../../../test_helpers';
import { cryptoFactory } from '../../../../server/lib/crypto';
import { executeJobFactory } from './index';
import { generatePngObservableFactory } from '../lib/generate_png';
import { CancellationToken } from '../../../../common/cancellation_token';
import { ReportingCore } from '../../../../server';
import { LevelLogger } from '../../../../server/lib';
import { ReportingCore, CaptureConfig } from '../../../../server/types';
import { cryptoFactory } from '../../../../server/lib/crypto';
import { createMockReportingCore } from '../../../../test_helpers';
import { JobDocPayloadPNG } from '../../types';
import { generatePngObservableFactory } from '../lib/generate_png';
import { executeJobFactory } from './index';
jest.mock('../lib/generate_png', () => ({ generatePngObservableFactory: jest.fn() }));
@ -31,8 +31,6 @@ const mockLoggerFactory = {
};
const getMockLogger = () => new LevelLogger(mockLoggerFactory);
const captureConfig = {} as CaptureConfig;
const mockEncryptionKey = 'abcabcsecuresecret';
const encryptHeaders = async (headers: Record<string, string>) => {
const crypto = cryptoFactory(mockEncryptionKey);
@ -46,10 +44,13 @@ beforeEach(async () => {
'server.basePath': '/sbp',
};
const reportingConfig = {
index: '.reporting-2018.10.10',
encryptionKey: mockEncryptionKey,
'kibanaServer.hostname': 'localhost',
'kibanaServer.port': 5601,
'kibanaServer.protocol': 'http',
'queue.indexInterval': 'daily',
'queue.timeout': Infinity,
};
const mockReportingConfig = {
get: (...keys: string[]) => (reportingConfig as any)[keys.join('.')],
@ -74,13 +75,8 @@ afterEach(() => (generatePngObservableFactory as jest.Mock).mockReset());
test(`passes browserTimezone to generatePng`, async () => {
const encryptedHeaders = await encryptHeaders({});
const mockBrowserDriverFactory = await createMockBrowserDriverFactory(getMockLogger());
const generatePngObservable = generatePngObservableFactory(
captureConfig,
mockBrowserDriverFactory
);
(generatePngObservable as jest.Mock).mockReturnValue(Rx.of(Buffer.from('')));
const generatePngObservable = (await generatePngObservableFactory(mockReporting)) as jest.Mock;
generatePngObservable.mockReturnValue(Rx.of(Buffer.from('')));
const executeJob = await executeJobFactory(mockReporting, getMockLogger());
const browserTimezone = 'UTC';
@ -94,26 +90,43 @@ test(`passes browserTimezone to generatePng`, async () => {
cancellationToken
);
expect(generatePngObservable).toBeCalledWith(
expect.any(LevelLogger),
'http://localhost:5601/sbp/app/kibana#/something',
browserTimezone,
expect.anything(),
undefined
);
expect(generatePngObservable.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
LevelLogger {
"_logger": Object {
"get": [MockFunction],
},
"_tags": Array [
"PNG",
"execute",
"pngJobId",
],
"warning": [Function],
},
"http://localhost:5601/sbp/app/kibana#/something",
"UTC",
Object {
"conditions": Object {
"basePath": "/sbp",
"hostname": "localhost",
"port": 5601,
"protocol": "http",
},
"headers": Object {},
},
undefined,
],
]
`);
});
test(`returns content_type of application/png`, async () => {
const executeJob = await executeJobFactory(mockReporting, getMockLogger());
const encryptedHeaders = await encryptHeaders({});
const mockBrowserDriverFactory = await createMockBrowserDriverFactory(getMockLogger());
const generatePngObservable = generatePngObservableFactory(
captureConfig,
mockBrowserDriverFactory
);
(generatePngObservable as jest.Mock).mockReturnValue(Rx.of(Buffer.from('')));
const generatePngObservable = (await generatePngObservableFactory(mockReporting)) as jest.Mock;
generatePngObservable.mockReturnValue(Rx.of(Buffer.from('')));
const { content_type: contentType } = await executeJob(
'pngJobId',
@ -126,13 +139,8 @@ test(`returns content_type of application/png`, async () => {
test(`returns content of generatePng getBuffer base64 encoded`, async () => {
const testContent = 'test content';
const mockBrowserDriverFactory = await createMockBrowserDriverFactory(getMockLogger());
const generatePngObservable = generatePngObservableFactory(
captureConfig,
mockBrowserDriverFactory
);
(generatePngObservable as jest.Mock).mockReturnValue(Rx.of({ buffer: Buffer.from(testContent) }));
const generatePngObservable = (await generatePngObservableFactory(mockReporting)) as jest.Mock;
generatePngObservable.mockReturnValue(Rx.of({ buffer: Buffer.from(testContent) }));
const executeJob = await executeJobFactory(mockReporting, getMockLogger());
const encryptedHeaders = await encryptHeaders({});

View file

@ -25,13 +25,11 @@ export const executeJobFactory: QueuedPngExecutorFactory = async function execut
parentLogger: Logger
) {
const config = reporting.getConfig();
const captureConfig = config.get('capture');
const encryptionKey = config.get('encryptionKey');
const logger = parentLogger.clone([PNG_JOB_TYPE, 'execute']);
return async function executeJob(jobId: string, job: JobDocPayloadPNG, cancellationToken: any) {
const browserDriverFactory = await reporting.getBrowserDriverFactory();
const generatePngObservable = generatePngObservableFactory(captureConfig, browserDriverFactory);
const generatePngObservable = await generatePngObservableFactory(reporting);
const jobLogger = logger.clone([jobId]);
const process$: Rx.Observable<JobDocOutput> = Rx.of(1).pipe(
mergeMap(() => decryptJobHeaders({ encryptionKey, job, logger })),

View file

@ -6,19 +6,15 @@
import * as Rx from 'rxjs';
import { map } from 'rxjs/operators';
import { ReportingCore } from '../../../../server';
import { LevelLogger } from '../../../../server/lib';
import { CaptureConfig } from '../../../../server/types';
import { ConditionalHeaders, HeadlessChromiumDriverFactory } from '../../../../types';
import { ConditionalHeaders } from '../../../../types';
import { LayoutParams } from '../../../common/layouts/layout';
import { PreserveLayout } from '../../../common/layouts/preserve_layout';
import { screenshotsObservableFactory } from '../../../common/lib/screenshots';
import { ScreenshotResults } from '../../../common/lib/screenshots/types';
export function generatePngObservableFactory(
captureConfig: CaptureConfig,
browserDriverFactory: HeadlessChromiumDriverFactory
) {
const screenshotsObservable = screenshotsObservableFactory(captureConfig, browserDriverFactory);
export async function generatePngObservableFactory(reporting: ReportingCore) {
const getScreenshots = await reporting.getScreenshotsObservable();
return function generatePngObservable(
logger: LevelLogger,
@ -32,7 +28,7 @@ export function generatePngObservableFactory(
}
const layout = new PreserveLayout(layoutParams.dimensions);
const screenshots$ = screenshotsObservable({
const screenshots$ = getScreenshots({
logger,
urls: [url],
conditionalHeaders,

View file

@ -5,11 +5,11 @@
*/
import * as Rx from 'rxjs';
import { createMockReportingCore, createMockBrowserDriverFactory } from '../../../../test_helpers';
import { createMockReportingCore } from '../../../../test_helpers';
import { cryptoFactory } from '../../../../server/lib/crypto';
import { LevelLogger } from '../../../../server/lib';
import { CancellationToken } from '../../../../types';
import { ReportingCore, CaptureConfig } from '../../../../server/types';
import { ReportingCore } from '../../../../server';
import { generatePdfObservableFactory } from '../lib/generate_pdf';
import { JobDocPayloadPDF } from '../../types';
import { executeJobFactory } from './index';
@ -22,8 +22,6 @@ const cancellationToken = ({
on: jest.fn(),
} as unknown) as CancellationToken;
const captureConfig = {} as CaptureConfig;
const mockLoggerFactory = {
get: jest.fn().mockImplementation(() => ({
error: jest.fn(),
@ -72,16 +70,64 @@ beforeEach(async () => {
afterEach(() => (generatePdfObservableFactory as jest.Mock).mockReset());
test(`passes browserTimezone to generatePdf`, async () => {
const encryptedHeaders = await encryptHeaders({});
const generatePdfObservable = (await generatePdfObservableFactory(mockReporting)) as jest.Mock;
generatePdfObservable.mockReturnValue(Rx.of(Buffer.from('')));
const executeJob = await executeJobFactory(mockReporting, getMockLogger());
const browserTimezone = 'UTC';
await executeJob(
'pdfJobId',
getJobDocPayload({
relativeUrl: '/app/kibana#/something',
browserTimezone,
headers: encryptedHeaders,
}),
cancellationToken
);
expect(generatePdfObservable.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
LevelLogger {
"_logger": Object {
"get": [MockFunction],
},
"_tags": Array [
"printable_pdf",
"execute",
"pdfJobId",
],
"warning": [Function],
},
undefined,
Array [
"http://localhost:5601/sbp/app/kibana#/something",
],
"UTC",
Object {
"conditions": Object {
"basePath": "/sbp",
"hostname": "localhost",
"port": 5601,
"protocol": "http",
},
"headers": Object {},
},
undefined,
false,
],
]
`);
});
test(`returns content_type of application/pdf`, async () => {
const logger = getMockLogger();
const executeJob = await executeJobFactory(mockReporting, logger);
const mockBrowserDriverFactory = await createMockBrowserDriverFactory(logger);
const encryptedHeaders = await encryptHeaders({});
const generatePdfObservable = generatePdfObservableFactory(
captureConfig,
mockBrowserDriverFactory
);
const generatePdfObservable = await generatePdfObservableFactory(mockReporting);
(generatePdfObservable as jest.Mock).mockReturnValue(Rx.of(Buffer.from('')));
const { content_type: contentType } = await executeJob(
@ -94,12 +140,7 @@ test(`returns content_type of application/pdf`, async () => {
test(`returns content of generatePdf getBuffer base64 encoded`, async () => {
const testContent = 'test content';
const mockBrowserDriverFactory = await createMockBrowserDriverFactory(getMockLogger());
const generatePdfObservable = generatePdfObservableFactory(
captureConfig,
mockBrowserDriverFactory
);
const generatePdfObservable = await generatePdfObservableFactory(mockReporting);
(generatePdfObservable as jest.Mock).mockReturnValue(Rx.of({ buffer: Buffer.from(testContent) }));
const executeJob = await executeJobFactory(mockReporting, getMockLogger());

View file

@ -26,14 +26,12 @@ export const executeJobFactory: QueuedPdfExecutorFactory = async function execut
parentLogger: Logger
) {
const config = reporting.getConfig();
const captureConfig = config.get('capture');
const encryptionKey = config.get('encryptionKey');
const logger = parentLogger.clone([PDF_JOB_TYPE, 'execute']);
return async function executeJob(jobId: string, job: JobDocPayloadPDF, cancellationToken: any) {
const browserDriverFactory = await reporting.getBrowserDriverFactory();
const generatePdfObservable = generatePdfObservableFactory(captureConfig, browserDriverFactory);
const generatePdfObservable = await generatePdfObservableFactory(reporting);
const jobLogger = logger.clone([jobId]);
const process$: Rx.Observable<JobDocOutput> = Rx.of(1).pipe(

View file

@ -7,12 +7,11 @@
import { groupBy } from 'lodash';
import * as Rx from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { ReportingCore } from '../../../../server';
import { LevelLogger } from '../../../../server/lib';
import { CaptureConfig } from '../../../../server/types';
import { ConditionalHeaders, HeadlessChromiumDriverFactory } from '../../../../types';
import { ConditionalHeaders } from '../../../../types';
import { createLayout } from '../../../common/layouts';
import { LayoutInstance, LayoutParams } from '../../../common/layouts/layout';
import { screenshotsObservableFactory } from '../../../common/lib/screenshots';
import { ScreenshotResults } from '../../../common/lib/screenshots/types';
// @ts-ignore untyped module
import { pdf } from './pdf';
@ -27,11 +26,10 @@ const getTimeRange = (urlScreenshots: ScreenshotResults[]) => {
return null;
};
export function generatePdfObservableFactory(
captureConfig: CaptureConfig,
browserDriverFactory: HeadlessChromiumDriverFactory
) {
const screenshotsObservable = screenshotsObservableFactory(captureConfig, browserDriverFactory);
export async function generatePdfObservableFactory(reporting: ReportingCore) {
const config = reporting.getConfig();
const captureConfig = config.get('capture');
const getScreenshots = await reporting.getScreenshotsObservable();
return function generatePdfObservable(
logger: LevelLogger,
@ -43,7 +41,7 @@ export function generatePdfObservableFactory(
logo?: string
): Rx.Observable<{ buffer: Buffer; warnings: string[] }> {
const layout = createLayout(captureConfig, layoutParams) as LayoutInstance;
const screenshots$ = screenshotsObservable({
const screenshots$ = getScreenshots({
logger,
urls,
conditionalHeaders,

View file

@ -24,6 +24,10 @@ import { ReportingConfig, ReportingConfigType } from './config';
import { checkLicenseFactory, getExportTypesRegistry, LevelLogger } from './lib';
import { registerRoutes } from './routes';
import { ReportingSetupDeps } from './types';
import {
screenshotsObservableFactory,
ScreenshotsObservableFn,
} from '../export_types/common/lib/screenshots';
interface ReportingInternalSetup {
browserDriverFactory: HeadlessChromiumDriverFactory;
@ -95,13 +99,13 @@ export class ReportingCore {
return (await this.getPluginStartDeps()).enqueueJob;
}
public async getBrowserDriverFactory(): Promise<HeadlessChromiumDriverFactory> {
return (await this.getPluginSetupDeps()).browserDriverFactory;
}
public getConfig(): ReportingConfig {
return this.config;
}
public async getScreenshotsObservable(): Promise<ScreenshotsObservableFn> {
const { browserDriverFactory } = await this.getPluginSetupDeps();
return screenshotsObservableFactory(this.config.get('capture'), browserDriverFactory);
}
/*
* Outside dependencies

View file

@ -14,3 +14,5 @@ export const plugin = (context: PluginInitializerContext, config: ReportingConfi
export { ReportingPlugin } from './plugin';
export { ReportingConfig, ReportingCore };
export { PreserveLayout, PrintLayout } from '../export_types/common/layouts';

View file

@ -16,21 +16,23 @@ export async function createQueueFactory<JobParamsType, JobPayloadType>(
logger: Logger
): Promise<ESQueueInstance> {
const config = reporting.getConfig();
const queueConfig = config.get('queue');
const index = config.get('index');
const elasticsearch = await reporting.getElasticsearchService();
const queueIndexInterval = config.get('queue', 'indexInterval');
const queueTimeout = config.get('queue', 'timeout');
const queueIndex = config.get('index');
const isPollingEnabled = config.get('queue', 'pollEnabled');
const elasticsearch = await reporting.getElasticsearchService();
const queueOptions = {
interval: queueConfig.indexInterval,
timeout: queueConfig.timeout,
interval: queueIndexInterval,
timeout: queueTimeout,
dateSeparator: '.',
client: elasticsearch.dataClient,
logger: createTaggedLogger(logger, ['esqueue', 'queue-worker']),
};
const queue: ESQueueInstance = new Esqueue(index, queueOptions);
const queue: ESQueueInstance = new Esqueue(queueIndex, queueOptions);
if (queueConfig.pollEnabled) {
if (isPollingEnabled) {
// create workers to poll the index for idle jobs waiting to be claimed and executed
const createWorker = createWorkerFactory(reporting, logger);
await createWorker(queue);

View file

@ -26,12 +26,12 @@ interface ConfirmedJob {
}
export function enqueueJobFactory(reporting: ReportingCore, parentLogger: Logger): EnqueueJobFn {
const logger = parentLogger.clone(['queue-job']);
const config = reporting.getConfig();
const captureConfig = config.get('capture');
const queueConfig = config.get('queue');
const browserType = captureConfig.browser.type;
const maxAttempts = captureConfig.maxAttempts;
const queueTimeout = config.get('queue', 'timeout');
const browserType = config.get('capture', 'browser', 'type');
const maxAttempts = config.get('capture', 'maxAttempts');
const logger = parentLogger.clone(['queue-job']);
return async function enqueueJob<JobParamsType>(
exportTypeId: string,
@ -53,7 +53,7 @@ export function enqueueJobFactory(reporting: ReportingCore, parentLogger: Logger
const payload = await createJob(jobParams, headers, request);
const options = {
timeout: queueConfig.timeout,
timeout: queueTimeout,
created_by: get(user, 'username', false),
browser_type: browserType,
max_attempts: maxAttempts,