[Screenshotting] Ensure worker.js content contents are compiled in build (#129796) (#129961)

* [Screenshotting] Expose packageInfo to pdf maker for dist data

* somehow use `dist` in the pdfmaker?

* use worker_src_harness exclusively when running from source

* Update pdfmaker.ts

* Update x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts

Co-authored-by: Michael Dokolin <dokmic@gmail.com>

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

Co-authored-by: Michael Dokolin <dokmic@gmail.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit 82e7356f02)

# Conflicts:
#	x-pack/plugins/screenshotting/server/formats/pdf/index.ts
#	x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/index.ts
#	x-pack/plugins/screenshotting/server/plugin.ts
#	x-pack/plugins/screenshotting/server/screenshots/index.test.ts
#	x-pack/plugins/screenshotting/server/screenshots/index.ts
This commit is contained in:
Tim Sullivan 2022-04-11 17:02:16 -07:00 committed by GitHub
parent a680eb80c8
commit 6a8d17c253
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 56 additions and 12 deletions

View file

@ -6,7 +6,7 @@
*/ */
import { groupBy } from 'lodash'; import { groupBy } from 'lodash';
import type { Logger } from 'src/core/server'; import type { Logger, PackageInfo } from 'src/core/server';
import { LayoutParams, LayoutTypes } from '../../../common'; import { LayoutParams, LayoutTypes } from '../../../common';
import { ScreenshotResult, ScreenshotOptions } from '../../screenshots'; import { ScreenshotResult, ScreenshotOptions } from '../../screenshots';
import { FormattedScreenshotResult } from '../types'; import { FormattedScreenshotResult } from '../types';
@ -54,6 +54,7 @@ export interface PdfScreenshotResult extends Omit<FormattedScreenshotResult, 're
interface ScreenshotsResultsToPdfArgs { interface ScreenshotsResultsToPdfArgs {
logger: Logger; logger: Logger;
packageInfo: PackageInfo;
title?: string; title?: string;
logo?: string; logo?: string;
} }
@ -68,13 +69,14 @@ const getTimeRange = (urlScreenshots: ScreenshotResult['results']) => {
return null; return null;
}; };
export function toPdf({ title, logo, logger }: ScreenshotsResultsToPdfArgs) { export function toPdf({ title, logo, logger, packageInfo }: ScreenshotsResultsToPdfArgs) {
return async (screenshotResult: ScreenshotResult): Promise<PdfScreenshotResult> => { return async (screenshotResult: ScreenshotResult): Promise<PdfScreenshotResult> => {
const timeRange = getTimeRange(screenshotResult.results); const timeRange = getTimeRange(screenshotResult.results);
try { try {
const { buffer, pageCount } = await pngsToPdf({ const { buffer, pageCount } = await pngsToPdf({
results: screenshotResult.results, results: screenshotResult.results,
title: title ? title + (timeRange ? ` - ${timeRange}` : '') : undefined, title: title ? title + (timeRange ? ` - ${timeRange}` : '') : undefined,
packageInfo,
logo, logo,
layout: screenshotResult.layout, layout: screenshotResult.layout,
logger, logger,

View file

@ -5,7 +5,7 @@
* 2.0. * 2.0.
*/ */
import type { Logger } from 'src/core/server'; import type { Logger, PackageInfo } from 'src/core/server';
import { PdfMaker } from './pdfmaker'; import { PdfMaker } from './pdfmaker';
import type { Layout } from '../../../layouts'; import type { Layout } from '../../../layouts';
import { getTracker } from './tracker'; import { getTracker } from './tracker';
@ -17,6 +17,7 @@ interface PngsToPdfArgs {
logger: Logger; logger: Logger;
logo?: string; logo?: string;
title?: string; title?: string;
packageInfo: PackageInfo;
} }
export async function pngsToPdf({ export async function pngsToPdf({
@ -24,9 +25,10 @@ export async function pngsToPdf({
layout, layout,
logo, logo,
title, title,
packageInfo,
logger, logger,
}: PngsToPdfArgs): Promise<{ buffer: Buffer; pageCount: number }> { }: PngsToPdfArgs): Promise<{ buffer: Buffer; pageCount: number }> {
const pdfMaker = new PdfMaker(layout, logo, logger); const pdfMaker = new PdfMaker(layout, logo, packageInfo, logger);
const tracker = getTracker(); const tracker = getTracker();
if (title) { if (title) {
pdfMaker.setTitle(title); pdfMaker.setTitle(title);

View file

@ -7,11 +7,12 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { PackageInfo } from 'kibana/server';
import path from 'path'; import path from 'path';
import { loggingSystemMock } from 'src/core/server/mocks'; import { loggingSystemMock } from 'src/core/server/mocks';
import { isUint8Array } from 'util/types'; import { isUint8Array } from 'util/types';
import { createMockLayout } from '../../../../layouts/mock';
import { errors } from '../../../../../common'; import { errors } from '../../../../../common';
import { createMockLayout } from '../../../../layouts/mock';
import { PdfMaker } from '../pdfmaker'; import { PdfMaker } from '../pdfmaker';
const imageBase64 = Buffer.from( const imageBase64 = Buffer.from(
@ -23,11 +24,19 @@ describe.skip('PdfMaker', () => {
let layout: ReturnType<typeof createMockLayout>; let layout: ReturnType<typeof createMockLayout>;
let pdf: PdfMaker; let pdf: PdfMaker;
let logger: ReturnType<typeof loggingSystemMock.createLogger>; let logger: ReturnType<typeof loggingSystemMock.createLogger>;
let packageInfo: Readonly<PackageInfo>;
beforeEach(() => { beforeEach(() => {
layout = createMockLayout(); layout = createMockLayout();
logger = loggingSystemMock.createLogger(); logger = loggingSystemMock.createLogger();
pdf = new PdfMaker(layout, undefined, logger); packageInfo = {
branch: 'screenshot-test',
buildNum: 567891011,
buildSha: 'screenshot-dfdfed0a',
dist: false,
version: '1000.0.0',
};
pdf = new PdfMaker(layout, undefined, packageInfo, logger);
}); });
describe('generate', () => { describe('generate', () => {
@ -56,14 +65,14 @@ describe.skip('PdfMaker', () => {
protected workerMaxOldHeapSizeMb = 2; protected workerMaxOldHeapSizeMb = 2;
protected workerMaxYoungHeapSizeMb = 2; protected workerMaxYoungHeapSizeMb = 2;
protected workerModulePath = path.resolve(__dirname, './memory_leak_worker.js'); protected workerModulePath = path.resolve(__dirname, './memory_leak_worker.js');
})(layout, undefined, logger); })(layout, undefined, packageInfo, logger);
await expect(leakyMaker.generate()).rejects.toBeInstanceOf(errors.PdfWorkerOutOfMemoryError); await expect(leakyMaker.generate()).rejects.toBeInstanceOf(errors.PdfWorkerOutOfMemoryError);
}); });
it.skip('restarts the PDF worker if it crashes', async () => { it.skip('restarts the PDF worker if it crashes', async () => {
const buggyMaker = new (class BuggyPdfMaker extends PdfMaker { const buggyMaker = new (class BuggyPdfMaker extends PdfMaker {
protected workerModulePath = path.resolve(__dirname, './buggy_worker.js'); protected workerModulePath = path.resolve(__dirname, './buggy_worker.js');
})(layout, undefined, logger); })(layout, undefined, packageInfo, logger);
await expect(buggyMaker.generate()).rejects.toEqual(new Error('This is a bug')); await expect(buggyMaker.generate()).rejects.toEqual(new Error('This is a bug'));
await expect(buggyMaker.generate()).rejects.toEqual(new Error('This is a bug')); await expect(buggyMaker.generate()).rejects.toEqual(new Error('This is a bug'));

View file

@ -5,7 +5,7 @@
* 2.0. * 2.0.
*/ */
import type { Logger } from 'src/core/server'; import type { Logger, PackageInfo } from 'src/core/server';
import { SerializableRecord } from '@kbn/utility-types'; import { SerializableRecord } from '@kbn/utility-types';
import path from 'path'; import path from 'path';
import { Content, ContentImage, ContentText } from 'pdfmake/interfaces'; import { Content, ContentImage, ContentText } from 'pdfmake/interfaces';
@ -34,7 +34,7 @@ export class PdfMaker {
private worker?: Worker; private worker?: Worker;
private pageCount: number = 0; private pageCount: number = 0;
protected workerModulePath = path.resolve(__dirname, './worker.js'); protected workerModulePath: string;
/** /**
* The maximum heap size for old memory region of the worker thread. * The maximum heap size for old memory region of the worker thread.
@ -65,10 +65,18 @@ export class PdfMaker {
constructor( constructor(
private readonly layout: Layout, private readonly layout: Layout,
private readonly logo: string | undefined, private readonly logo: string | undefined,
{ dist }: PackageInfo,
private readonly logger: Logger private readonly logger: Logger
) { ) {
this.title = ''; this.title = '';
this.content = []; this.content = [];
// running in dist: `worker.ts` becomes `worker.js`
// running in source: `worker_src_harness.ts` needs to be wrapped in JS and have a ts-node environment initialized.
this.workerModulePath = path.resolve(
__dirname,
dist ? './worker.js' : './worker_src_harness.js'
);
} }
_addContents(contents: Content[]) { _addContents(contents: Content[]) {

View file

@ -5,5 +5,10 @@
* 2.0. * 2.0.
*/ */
/**
* This file is the harness for importing worker.ts with Kibana running in dev mode.
* The TS file needs to be compiled on the fly, unlike when Kibana is running as a dist.
*/
require('../../../../../../../src/setup_node_env'); require('../../../../../../../src/setup_node_env');
require('./worker.ts'); require('./worker.ts');

View file

@ -17,6 +17,13 @@ export function createMockScreenshottingStart(): jest.Mocked<ScreenshottingStart
const driver = createMockBrowserDriverFactory(); const driver = createMockBrowserDriverFactory();
const { getScreenshots } = createMockScreenshots(); const { getScreenshots } = createMockScreenshots();
const { diagnose } = driver; const { diagnose } = driver;
const packageInfo = {
branch: 'screenshot-test',
buildNum: 567891011,
buildSha: 'screenshot-dfdfed0a',
dist: false,
version: '5000.0.0',
};
return { return {
diagnose, diagnose,
@ -24,7 +31,9 @@ export function createMockScreenshottingStart(): jest.Mocked<ScreenshottingStart
(options): ReturnType<ScreenshottingStart['getScreenshots']> => (options): ReturnType<ScreenshottingStart['getScreenshots']> =>
getScreenshots(options).pipe( getScreenshots(options).pipe(
mergeMap<ScreenshotResult, Promise<PngScreenshotResult | PdfScreenshotResult>>( mergeMap<ScreenshotResult, Promise<PngScreenshotResult | PdfScreenshotResult>>(
options.format === 'pdf' ? toPdf({ logger: loggingSystemMock.createLogger() }) : toPng options.format === 'pdf'
? toPdf({ logger: loggingSystemMock.createLogger(), packageInfo })
: toPng
) )
) )
), ),

View file

@ -11,6 +11,7 @@ import type {
CoreSetup, CoreSetup,
CoreStart, CoreStart,
Logger, Logger,
PackageInfo,
Plugin, Plugin,
PluginInitializerContext, PluginInitializerContext,
} from 'src/core/server'; } from 'src/core/server';
@ -41,6 +42,7 @@ export interface ScreenshottingStart {
* @returns Observable with output messages. * @returns Observable with output messages.
*/ */
diagnose: HeadlessChromiumDriverFactory['diagnose']; diagnose: HeadlessChromiumDriverFactory['diagnose'];
/** /**
* Takes screenshots of multiple pages. * Takes screenshots of multiple pages.
* @param options Screenshots session options. * @param options Screenshots session options.
@ -54,6 +56,7 @@ export interface ScreenshottingStart {
export class ScreenshottingPlugin implements Plugin<void, ScreenshottingStart, SetupDeps> { export class ScreenshottingPlugin implements Plugin<void, ScreenshottingStart, SetupDeps> {
private config: ConfigType; private config: ConfigType;
private logger: Logger; private logger: Logger;
private packageInfo: PackageInfo;
private screenshotMode!: ScreenshotModePluginSetup; private screenshotMode!: ScreenshotModePluginSetup;
private browserDriverFactory!: Promise<HeadlessChromiumDriverFactory>; private browserDriverFactory!: Promise<HeadlessChromiumDriverFactory>;
private screenshots!: Promise<Screenshots>; private screenshots!: Promise<Screenshots>;
@ -61,6 +64,7 @@ export class ScreenshottingPlugin implements Plugin<void, ScreenshottingStart, S
constructor(context: PluginInitializerContext<ConfigType>) { constructor(context: PluginInitializerContext<ConfigType>) {
this.logger = context.logger.get(); this.logger = context.logger.get();
this.config = context.config.get(); this.config = context.config.get();
this.packageInfo = context.env.packageInfo;
} }
setup({ http }: CoreSetup, { screenshotMode }: SetupDeps) { setup({ http }: CoreSetup, { screenshotMode }: SetupDeps) {
@ -108,7 +112,12 @@ export class ScreenshottingPlugin implements Plugin<void, ScreenshottingStart, S
switchMap((screenshots) => screenshots.getScreenshots(options)), switchMap((screenshots) => screenshots.getScreenshots(options)),
mergeMap<ScreenshotResult, Promise<PngScreenshotResult | PdfScreenshotResult>>( mergeMap<ScreenshotResult, Promise<PngScreenshotResult | PdfScreenshotResult>>(
options.format === 'pdf' options.format === 'pdf'
? toPdf({ logger: this.logger, logo: options.logo, title: options.title }) ? toPdf({
logger: this.logger,
packageInfo: this.packageInfo,
logo: options.logo,
title: options.title,
})
: toPng : toPng
) )
), ),