diff --git a/kibana-reports/server/executor/createScheduledReport.ts b/kibana-reports/server/executor/createScheduledReport.ts index 1cc04649..af60bfa5 100644 --- a/kibana-reports/server/executor/createScheduledReport.ts +++ b/kibana-reports/server/executor/createScheduledReport.ts @@ -23,7 +23,7 @@ import { ILegacyClusterClient, Logger } from '../../../../src/core/server'; import { createSavedSearchReport } from '../routes/utils/savedSearchReportHelper'; import { ReportSchemaType } from '../model'; import { CreateReportResultType } from '../routes/utils/types'; -import { createVisualReport } from '../routes/utils/visualReportHelper'; +import { createVisualReport } from '../routes/utils/visual_report/visualReportHelper'; import { deliverReport } from '../routes/lib/deliverReport'; import { updateReportState } from '../routes/lib/updateReportState'; diff --git a/kibana-reports/server/routes/lib/createReport.ts b/kibana-reports/server/routes/lib/createReport.ts index bf051e24..c33c51b8 100644 --- a/kibana-reports/server/routes/lib/createReport.ts +++ b/kibana-reports/server/routes/lib/createReport.ts @@ -30,7 +30,7 @@ import { import { createSavedSearchReport } from '../utils/savedSearchReportHelper'; import { ReportSchemaType } from '../../model'; import { CreateReportResultType } from '../utils/types'; -import { createVisualReport } from '../utils/visualReportHelper'; +import { createVisualReport } from '../utils/visual_report/visualReportHelper'; import { SetCookie } from 'puppeteer'; import { deliverReport } from './deliverReport'; import { updateReportState } from './updateReportState'; diff --git a/kibana-reports/server/routes/utils/__tests__/visualReportHelper.test.ts b/kibana-reports/server/routes/utils/__tests__/visualReportHelper.test.ts index e5ea93cb..e015bb5a 100644 --- a/kibana-reports/server/routes/utils/__tests__/visualReportHelper.test.ts +++ b/kibana-reports/server/routes/utils/__tests__/visualReportHelper.test.ts @@ -14,7 +14,7 @@ */ import 'regenerator-runtime/runtime'; -import { createVisualReport } from '../visualReportHelper'; +import { createVisualReport } from '../visual_report/visualReportHelper'; import { Logger } from '../../../../../../src/core/server'; import { ReportParamsSchemaType, reportSchema } from '../../../model'; diff --git a/kibana-reports/server/routes/utils/visual_report/report_template.html b/kibana-reports/server/routes/utils/visual_report/report_template.html new file mode 100644 index 00000000..6f198bce --- /dev/null +++ b/kibana-reports/server/routes/utils/visual_report/report_template.html @@ -0,0 +1,244 @@ + + + + + + Kibana Reports + + + + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ + diff --git a/kibana-reports/server/routes/utils/visualReportHelper.ts b/kibana-reports/server/routes/utils/visual_report/visualReportHelper.ts similarity index 70% rename from kibana-reports/server/routes/utils/visualReportHelper.ts rename to kibana-reports/server/routes/utils/visual_report/visualReportHelper.ts index f9d19b46..5078eca9 100644 --- a/kibana-reports/server/routes/utils/visualReportHelper.ts +++ b/kibana-reports/server/routes/utils/visual_report/visualReportHelper.ts @@ -16,16 +16,18 @@ import puppeteer, { ElementHandle, SetCookie } from 'puppeteer'; import createDOMPurify from 'dompurify'; import { JSDOM } from 'jsdom'; -import { Logger } from '../../../../../src/core/server'; +import { Logger } from '../../../../../../src/core/server'; import { DEFAULT_REPORT_HEADER, REPORT_TYPE, FORMAT, SELECTOR, -} from './constants'; -import { getFileName } from './helpers'; -import { CreateReportResultType } from './types'; +} from '../constants'; +import { getFileName } from '../helpers'; +import { CreateReportResultType } from '../types'; import { ReportParamsSchemaType, VisualReportSchemaType } from 'server/model'; +import fs from 'fs'; +import cheerio from 'cheerio'; export const createVisualReport = async ( reportParams: ReportParamsSchemaType, @@ -82,22 +84,33 @@ export const createVisualReport = async ( }); let buffer: Buffer; - let element: ElementHandle; // remove top nav bar await page.evaluate( /* istanbul ignore next */ (selector) => { document.querySelector(selector)?.remove(); + document + .querySelectorAll("[class^='euiButton']") + .forEach((e) => e.remove()); + document.querySelector( + '.coreSystemRootDomElement.euiBody--headerIsFixed' + ).style.paddingTop = '0px'; }, SELECTOR.topNavBar ); + // force wait for any resize to load after the above DOM modification + await page.waitFor(1000); // crop content switch (reportSource) { case REPORT_TYPE.dashboard: - element = await page.waitForSelector(SELECTOR.dashboard); + await page.waitForSelector(SELECTOR.dashboard, { + visible: true, + }); break; case REPORT_TYPE.visualization: - element = await page.waitForSelector(SELECTOR.visualization); + await page.waitForSelector(SELECTOR.visualization, { + visible: true, + }); break; default: throw Error( @@ -105,33 +118,14 @@ export const createVisualReport = async ( ); } - const screenshot = await element.screenshot({ fullPage: false }); - - /** - * Sets the content of the page to have the header be above the trimmed screenshot - * and the footer be below it - */ - // TODO: make all html templates into files, such as reporting context menu button, and embedded html of email body - await page.setContent(` - - - - - - -
- ${reportHeader} - - ${reportFooter} -
- - - `); + const screenshot = await page.screenshot({ fullPage: true }); + + const templateHtml = composeReportHtml( + reportHeader, + reportFooter, + screenshot.toString('base64') + ); + await page.setContent(templateHtml); // create pdf or png accordingly if (reportFormat === FORMAT.pdf) { @@ -160,3 +154,27 @@ export const createVisualReport = async ( return { timeCreated, dataUrl: buffer.toString('base64'), fileName }; }; + +export const composeReportHtml = ( + header: string, + footer: string, + screenshot: string +) => { + const $ = cheerio.load(fs.readFileSync(`${__dirname}/report_template.html`), { + decodeEntities: false, + }); + + $('.reportWrapper img').attr('src', `data:image/png;base64,${screenshot}`); + $('#reportingHeader > div.mde-preview > div.mde-preview-content').html( + header + ); + if (footer === '') { + $('#reportingFooter').attr('hidden', 'true'); + } else { + $('#reportingFooter > div.mde-preview > div.mde-preview-content').html( + footer + ); + } + + return $.root().html() || ''; +};