[8.15] [Canvas/PDF report] Allow canvas to generate PDF report (#224309) (#224342)

# Backport

This will backport the following commits from `main` to `8.18.5-1`:
- [[Canvas/PDF report] Allow canvas to generate PDF report
(#224309)](https://github.com/elastic/kibana/pull/224309)

<!--- Backport version: 10.0.1 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Tim Sullivan 2025-06-20 11:37:29 -07:00 committed by GitHub
parent 53b64ebbb6
commit a62102f89f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 47 additions and 5 deletions

View file

@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
export const CANVAS_APP_LOCATOR = 'CANVAS_APP_LOCATOR';
export const DISCOVER_APP_ID = 'discover';
export const DASHBOARD_APP_ID = 'dashboards';

View file

@ -7,6 +7,7 @@
*/
export {
CANVAS_APP_LOCATOR,
DASHBOARD_APP_ID,
DISCOVER_APP_ID,
VISUALIZE_APP_ID,

View file

@ -7,6 +7,7 @@
*/
import {
CANVAS_APP_LOCATOR,
DASHBOARD_APP_LOCATOR,
LENS_APP_LOCATOR,
VISUALIZE_APP_LOCATOR,
@ -52,6 +53,7 @@ export const JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY =
// Allowed locator types for reporting: the "reportable" analytical apps we expect to redirect to during screenshotting
export const REPORTING_REDIRECT_ALLOWED_LOCATOR_TYPES = [
CANVAS_APP_LOCATOR,
DASHBOARD_APP_LOCATOR,
LENS_APP_LOCATOR,
VISUALIZE_APP_LOCATOR,

View file

@ -10,4 +10,3 @@ export const UI_SETTINGS = {
};
export type { CanvasAppLocator, CanvasAppLocatorParams } from './locator';
export { CANVAS_APP_LOCATOR } from './locator';

View file

@ -6,6 +6,7 @@
*/
import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/common';
import { CANVAS_APP_LOCATOR } from '@kbn/deeplinks-analytics';
import { CANVAS_APP } from './lib/constants';
@ -18,8 +19,6 @@ export type CanvasAppLocatorParams = {
export type CanvasAppLocator = LocatorPublic<CanvasAppLocatorParams>;
export const CANVAS_APP_LOCATOR = 'CANVAS_APP_LOCATOR';
export class CanvasAppLocatorDefinition implements LocatorDefinition<CanvasAppLocatorParams> {
id = CANVAS_APP_LOCATOR;

View file

@ -7,7 +7,8 @@
import type { RedirectOptions } from '@kbn/share-plugin/public';
import { JobAppParamsPDFV2 } from '@kbn/reporting-export-types-pdf-common';
import { CanvasAppLocatorParams, CANVAS_APP_LOCATOR } from '../../../../common/locator';
import { CANVAS_APP_LOCATOR } from '@kbn/deeplinks-analytics';
import { CanvasAppLocatorParams } from '../../../../common/locator';
import { CanvasWorkpad } from '../../../../types';
export interface CanvasWorkpadSharingData {

View file

@ -90,6 +90,7 @@
"@kbn/presentation-publishing",
"@kbn/react-kibana-context-render",
"@kbn/search-types",
"@kbn/deeplinks-analytics",
],
"exclude": [
"target/**/*",

View file

@ -21,6 +21,7 @@ export const DownloadButton = ({ getUrl, job }: Props) => {
<EuiButton
size="s"
data-test-subj="downloadCompletedReportButton"
data-test-jobId={job.id}
href={getUrl(job.id)}
target="_blank"
>

View file

@ -66,6 +66,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(res.get('content-disposition')).to.equal(
'attachment; filename=The%20Very%20Cool%20Workpad%20for%20PDF%20Tests.pdf'
);
const jobId = await PageObjects.reporting.getReportJobId(60000);
const reportInfo = await PageObjects.reporting.getReportInfo(jobId);
// verify "completed" status (no warnings)
expect(reportInfo).to.have.property('status', 'completed');
});
});
});

View file

@ -12,7 +12,7 @@ import type SuperTest from 'supertest';
import { format as formatUrl } from 'url';
import { promisify } from 'util';
import { REPORT_TABLE_ID, REPORT_TABLE_ROW_ID } from '@kbn/reporting-common';
import { INTERNAL_ROUTES, REPORT_TABLE_ID, REPORT_TABLE_ROW_ID } from '@kbn/reporting-common';
import { FtrService } from '../ftr_provider_context';
const writeFileAsync = promisify(fs.writeFile);
@ -37,6 +37,30 @@ export class ReportingPageObject extends FtrService {
`);
}
async getReportJobId(timeout: number): Promise<string> {
this.log.debug('getReportJobId');
try {
// get the report job id from a data attribute on the download button
const jobIdElement = await this.find.byCssSelector('[data-test-jobId]', timeout);
if (!jobIdElement) {
throw new Error('Failed to find report job id.');
}
const jobId = await jobIdElement.getAttribute('data-test-jobId');
if (!jobId) {
throw new Error('Failed to find report job id.');
}
return jobId;
} catch (err) {
let errorText = 'Unknown error';
if (await this.find.existsByCssSelector('[data-test-errorText]')) {
const errorTextEl = await this.find.byCssSelector('[data-test-errorText]');
errorText = (await errorTextEl.getAttribute('data-test-errorText')) ?? errorText;
}
throw new Error(`Test report failed: ${errorText}: ${err}`, { cause: err });
}
}
async getReportURL(timeout: number) {
this.log.debug('getReportURL');
@ -77,6 +101,12 @@ export class ReportingPageObject extends FtrService {
return res ?? '';
}
async getReportInfo(jobId: string) {
this.log.debug(`getReportInfo for ${jobId}`);
const response = await this.getResponse(INTERNAL_ROUTES.JOBS.INFO_PREFIX + `/${jobId}`);
return response.body;
}
async getRawReportData(url: string): Promise<Buffer> {
this.log.debug(`getRawReportData for ${url}`);
const response = await this.getResponse(url);