Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.16] [Screenshotting/PDF Worker Thread] Add type to GeneratePdfResponse (#196860) #197128

Merged
merged 1 commit into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ import {
} from './constants';
import { REPORTING_TABLE_LAYOUT } from './get_doc_options';
import { getFont } from './get_font';
import type { GeneratePdfRequest, GeneratePdfResponse, WorkerData } from './worker';
import {
GeneratePdfResponseType,
type GeneratePdfRequest,
type WorkerData,
GeneratePdfResponse,
} from './worker';

// Ensure that all dependencies are included in the release bundle.
import './worker_dependencies';
Expand All @@ -32,6 +37,8 @@ export class PdfMaker {
private content: Content[];

private worker?: Worker;
private workerLogger: Logger;

private pageCount: number = 0;
private transferList: ArrayBuffer[] = [];

Expand Down Expand Up @@ -71,6 +78,7 @@ export class PdfMaker {
) {
this.title = '';
this.content = [];
this.workerLogger = logger.get('pdf-worker');

// 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.
Expand Down Expand Up @@ -209,26 +217,37 @@ export class PdfMaker {
const { port1: myPort, port2: theirPort } = new MessageChannel();
this.worker = this.createWorker(theirPort);
this.worker.on('error', (workerError: NodeJS.ErrnoException) => {
this.workerLogger.error(`Worker error: ${workerError}`);
if (workerError.code === 'ERR_WORKER_OUT_OF_MEMORY') {
reject(new errors.PdfWorkerOutOfMemoryError(workerError.message));
} else {
reject(workerError);
}
});
this.worker.on('exit', () => {});
this.worker.on('exit', () => {
this.workerLogger.debug('Worker exited');
});

// We expect one message from the worker generating the PDF buffer.
myPort.on('message', ({ error, data }: GeneratePdfResponse) => {
if (error) {
myPort.on('message', ({ type, error, data, message }: GeneratePdfResponse) => {
if (type === GeneratePdfResponseType.Log && message) {
this.workerLogger.debug(message);
return;
}

if (type === GeneratePdfResponseType.Error) {
reject(new Error(`PDF worker returned the following error: ${error}`));
return;
}
if (!data) {

if (type === GeneratePdfResponseType.Data && !data) {
reject(new Error(`Worker did not generate a PDF!`));
return;
}
this.pageCount = data.metrics.pages;
resolve(data.buffer);

if (data) {
this.pageCount = data.metrics.pages;
resolve(data.buffer);
}
});

// Send the request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,44 @@ export interface GeneratePdfRequest {
data: GenerateReportRequestData;
}

export type GeneratePdfResponse = SuccessResponse | ErrorResponse;

export interface SuccessResponse {
error?: undefined;
data: {
buffer: Uint8Array;
metrics: {
pages: number;
};
export interface GeneratePdfData {
buffer: Uint8Array;
metrics: {
pages: number;
};
}

export interface ErrorResponse {
error: string;
data: null;
export enum GeneratePdfResponseType {
Log,
Data,
Error,
}

interface GeneratePdfLogResponse {
type: GeneratePdfResponseType.Log;
data?: GeneratePdfData;
error?: string;
message?: string;
}

interface GeneratePdfDataResponse {
type: GeneratePdfResponseType.Data;
data?: GeneratePdfData;
error?: string;
message?: string;
}

interface GeneratePdfErrorResponse {
type: GeneratePdfResponseType.Error;
data?: GeneratePdfData;
error?: string;
message?: string;
}
export type GeneratePdfResponse =
| GeneratePdfLogResponse
| GeneratePdfDataResponse
| GeneratePdfErrorResponse;

if (!isMainThread) {
const { port } = workerData as WorkerData;
port.on('message', execute);
Expand All @@ -68,6 +89,11 @@ const getPageCount = (pdfDoc: PDFKit.PDFDocument): number => {

async function execute({ data: { layout, logo, title, content } }: GeneratePdfRequest) {
const { port } = workerData as WorkerData;
port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'Starting execution',
});

try {
const tableBorderWidth = 1;

Expand All @@ -89,6 +115,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
},
};

port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'Initializing PDF printer',
});

const printer = new Printer(fonts);

const docDefinition = _.assign(getTemplate(layout, logo, title, tableBorderWidth, assetPath), {
Expand All @@ -103,6 +134,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
),
});

port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'Generating document stream',
});

const pdfDoc = printer.createPdfKitDocument(docDefinition, {
tableLayouts: {
noBorder: {
Expand All @@ -121,6 +157,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
throw new Error('Document stream has not been generated');
}

port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'Document stream has been generated',
});

const buffer = await new Promise<Buffer>((resolve, reject) => {
const buffers: Buffer[] = [];
pdfDoc.on('error', reject);
Expand All @@ -133,7 +174,13 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
pdfDoc.end();
});

const successResponse: SuccessResponse = {
port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'PDF buffer has been generated',
});

const successResponse: GeneratePdfResponse = {
type: GeneratePdfResponseType.Data,
data: {
buffer,
metrics: {
Expand All @@ -143,7 +190,10 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
};
port.postMessage(successResponse, [buffer.buffer /* Transfer buffer instead of copying */]);
} catch (error) {
const errorResponse: ErrorResponse = { error: error.message, data: null };
const errorResponse: GeneratePdfResponse = {
type: GeneratePdfResponseType.Error,
error: error.message,
};
port.postMessage(errorResponse);
} finally {
process.nextTick(() => {
Expand Down