Skip to content

Commit

Permalink
[8.x] [Reporting] fix dashboard "Copy Post URL" action (ela…
Browse files Browse the repository at this point in the history
…stic#192530) (elastic#195334)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Reporting] fix dashboard "Copy Post URL" action
(elastic#192530)](elastic#192530)

<!--- Backport version: 9.4.3 -->

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

<!--BACKPORT [{"author":{"name":"Tim
Sullivan","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-07T23:54:21Z","message":"[Reporting]
fix dashboard \"Copy Post URL\" action (elastic#192530)\n\n##
Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/191673\r\nCloses
https://github.com/elastic/kibana/issues/183566\r\n\r\nFixes the ability
for the POST URL used to automate generation of\r\nreports by adding a
`generateExportUrl` function to the ShareMenuItemV2\r\ninterface. This
function returns a dynamic export URL for PDF generation\r\nby using the
selected layout option.\r\n\r\nOther changes: provides more strictness
in type definitions by:\r\n * splitting the types that define
`ShareMenuProvider`:\r\n * `ShareMenuProviderV2` provides the
`getShareMenuItems` function\r\n* `ShareMenuProviderLegacy` provides the
`getShareMenuItemsLegacy`\r\nfunction\r\n\r\n### Release note\r\nFixed
an issue with the export options for PNG/PDF reports in
a\r\ndashboard.\r\n\r\n### Checklist\r\n\r\nDelete any items that are
not applicable to this PR.\r\n\r\n- [x] Use the `generateExportUrl`
function inputs to return a POST URL\r\nthat is aware of the layout mode
(`print` or `preserve_layout`) and\r\nscreen dimensions\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] Flaky test
runner:\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6986","sha":"38407ae6b01e071757083fe358116b6dd75c4422","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","v8.16.0","backport:version","v8.15.2"],"title":"[Reporting]
fix dashboard \"Copy Post URL\"
action","number":192530,"url":"https://github.com/elastic/kibana/pull/192530","mergeCommit":{"message":"[Reporting]
fix dashboard \"Copy Post URL\" action (elastic#192530)\n\n##
Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/191673\r\nCloses
https://github.com/elastic/kibana/issues/183566\r\n\r\nFixes the ability
for the POST URL used to automate generation of\r\nreports by adding a
`generateExportUrl` function to the ShareMenuItemV2\r\ninterface. This
function returns a dynamic export URL for PDF generation\r\nby using the
selected layout option.\r\n\r\nOther changes: provides more strictness
in type definitions by:\r\n * splitting the types that define
`ShareMenuProvider`:\r\n * `ShareMenuProviderV2` provides the
`getShareMenuItems` function\r\n* `ShareMenuProviderLegacy` provides the
`getShareMenuItemsLegacy`\r\nfunction\r\n\r\n### Release note\r\nFixed
an issue with the export options for PNG/PDF reports in
a\r\ndashboard.\r\n\r\n### Checklist\r\n\r\nDelete any items that are
not applicable to this PR.\r\n\r\n- [x] Use the `generateExportUrl`
function inputs to return a POST URL\r\nthat is aware of the layout mode
(`print` or `preserve_layout`) and\r\nscreen dimensions\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] Flaky test
runner:\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6986","sha":"38407ae6b01e071757083fe358116b6dd75c4422"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","8.15"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/192530","number":192530,"mergeCommit":{"message":"[Reporting]
fix dashboard \"Copy Post URL\" action (elastic#192530)\n\n##
Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/191673\r\nCloses
https://github.com/elastic/kibana/issues/183566\r\n\r\nFixes the ability
for the POST URL used to automate generation of\r\nreports by adding a
`generateExportUrl` function to the ShareMenuItemV2\r\ninterface. This
function returns a dynamic export URL for PDF generation\r\nby using the
selected layout option.\r\n\r\nOther changes: provides more strictness
in type definitions by:\r\n * splitting the types that define
`ShareMenuProvider`:\r\n * `ShareMenuProviderV2` provides the
`getShareMenuItems` function\r\n* `ShareMenuProviderLegacy` provides the
`getShareMenuItemsLegacy`\r\nfunction\r\n\r\n### Release note\r\nFixed
an issue with the export options for PNG/PDF reports in
a\r\ndashboard.\r\n\r\n### Checklist\r\n\r\nDelete any items that are
not applicable to this PR.\r\n\r\n- [x] Use the `generateExportUrl`
function inputs to return a POST URL\r\nthat is aware of the layout mode
(`print` or `preserve_layout`) and\r\nscreen dimensions\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] Flaky test
runner:\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6986","sha":"38407ae6b01e071757083fe358116b6dd75c4422"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.15","label":"v8.15.2","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Tim Sullivan <[email protected]>
  • Loading branch information
kibanamachine and tsullivan authored Oct 8, 2024
1 parent e9573f3 commit c6cb2f7
Show file tree
Hide file tree
Showing 22 changed files with 220 additions and 257 deletions.
2 changes: 1 addition & 1 deletion examples/share_examples/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class ShareDemoPlugin implements Plugin<void, void, SetupDeps, StartDeps>
public setup(core: CoreSetup<StartDeps>, { share }: SetupDeps) {
share.register({
id: 'demo',
getShareMenuItems: (context) => [
getShareMenuItemsLegacy: (context) => [
{
panel: {
id: 'demo',
Expand Down
3 changes: 3 additions & 0 deletions packages/kbn-reporting/public/reporting_api_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ export class ReportingAPIClient implements IReportingAPI {
});
}

/**
* Adds the browserTimezone and kibana version to report job params
*/
public getDecoratedJobParams<T extends AppParams>(baseParams: T): BaseParams {
// If the TZ is set to the default "Browser", it will not be useful for
// server-side export. We need to derive the timezone and pass it as a param
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import * as Rx from 'rxjs';

import type { ApplicationStart, CoreStart } from '@kbn/core/public';
import { ILicense } from '@kbn/licensing-plugin/public';
import type { LayoutParams } from '@kbn/screenshotting-plugin/common';

import type { ReportingAPIClient } from '../../reporting_api_client';

Expand Down Expand Up @@ -47,13 +46,16 @@ export interface ExportPanelShareOpts {

export interface ReportingSharingData {
title: string;
layout: LayoutParams;
reportingDisabled?: boolean;
[key: string]: unknown;
locatorParams: {
id: string;
params: unknown;
};
}

export interface JobParamsProviderOptions {
sharingData: ReportingSharingData;
shareableUrl?: string;
objectType: string;
optimizedForPrinting?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { CSV_JOB_TYPE, CSV_JOB_TYPE_V2 } from '@kbn/reporting-export-types-csv-c

import type { SearchSourceFields } from '@kbn/data-plugin/common';
import { FormattedMessage, InjectedIntl } from '@kbn/i18n-react';
import { ShareContext, ShareMenuItem } from '@kbn/share-plugin/public';
import { ShareContext, ShareMenuItemV2 } from '@kbn/share-plugin/public';
import type { ExportModalShareOpts } from '.';
import { checkLicense } from '../..';

Expand Down Expand Up @@ -69,7 +69,7 @@ export const reportingCsvShareProvider = ({
};
};

const shareActions: ShareMenuItem[] = [];
const shareActions: ShareMenuItemV2[] = [];

const licenseCheck = checkLicense(license.check('reporting', 'basic'));
const licenseToolTipContent = licenseCheck.message;
Expand Down Expand Up @@ -177,8 +177,8 @@ export const reportingCsvShareProvider = ({
/>
),
generateExport: generateReportingJobCSV,
generateExportUrl: () => absoluteUrl,
generateCopyUrl: reportingUrl,
absoluteUrl,
renderCopyURLButton: true,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,28 @@
*/

import { i18n } from '@kbn/i18n';
import { FormattedMessage, InjectedIntl } from '@kbn/i18n-react';
import { FormattedMessage } from '@kbn/i18n-react';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { ShareContext, ShareMenuItem, ShareMenuProvider } from '@kbn/share-plugin/public';
import { ShareContext, ShareMenuItemV2, ShareMenuProvider } from '@kbn/share-plugin/public';
import React from 'react';
import { firstValueFrom } from 'rxjs';
import {
ExportModalShareOpts,
ExportPanelShareOpts,
JobParamsProviderOptions,
ReportingSharingData,
} from '.';
import { ScreenshotExportOpts } from '@kbn/share-plugin/public/types';
import { ExportModalShareOpts, JobParamsProviderOptions, ReportingSharingData } from '.';
import { checkLicense } from '../../license_check';
import { ScreenCapturePanelContent } from './screen_capture_panel_content_lazy';

const getJobParams = (opts: JobParamsProviderOptions, type: 'pngV2' | 'printablePdfV2') => () => {
const {
objectType,
sharingData: { title, layout, locatorParams },
sharingData: { title, locatorParams },
optimizedForPrinting,
} = opts;

const baseParams = {
objectType,
layout,
title,
};
const el = document.querySelector('[data-shared-items-container]');
const { height, width } = el ? el.getBoundingClientRect() : { height: 768, width: 1024 };
const dimensions = { height, width };
const layoutId = optimizedForPrinting ? ('print' as const) : ('preserve_layout' as const);
const layout = { id: layoutId, dimensions };
const baseParams = { objectType, layout, title };

if (type === 'printablePdfV2') {
// multi locator for PDF V2
Expand All @@ -43,154 +40,8 @@ const getJobParams = (opts: JobParamsProviderOptions, type: 'pngV2' | 'printable
};

/**
* This is used by Canvas
* This is used by Dashboard and Visualize apps (sharing modal)
*/
export const reportingScreenshotShareProvider = ({
apiClient,
license,
application,
usesUiCapabilities,
startServices$,
}: ExportPanelShareOpts): ShareMenuProvider => {
const getShareMenuItems = ({
objectType,
objectId,
isDirty,
onClose,
shareableUrl,
shareableUrlForSavedObject,
...shareOpts
}: ShareContext) => {
const { enableLinks, showLinks, message } = checkLicense(license.check('reporting', 'gold'));
const licenseToolTipContent = message;
const licenseHasScreenshotReporting = showLinks;
const licenseDisabled = !enableLinks;

let capabilityHasDashboardScreenshotReporting = false;
let capabilityHasVisualizeScreenshotReporting = false;
if (usesUiCapabilities) {
capabilityHasDashboardScreenshotReporting =
application.capabilities.dashboard?.generateScreenshot === true;
capabilityHasVisualizeScreenshotReporting =
application.capabilities.visualize?.generateScreenshot === true;
} else {
// deprecated
capabilityHasDashboardScreenshotReporting = true;
capabilityHasVisualizeScreenshotReporting = true;
}

if (!licenseHasScreenshotReporting) {
return [];
}
const isSupportedType = ['dashboard', 'visualization', 'lens'].includes(objectType);

if (!isSupportedType) {
return [];
}

if (objectType === 'dashboard' && !capabilityHasDashboardScreenshotReporting) {
return [];
}

if (
isSupportedType &&
!capabilityHasVisualizeScreenshotReporting &&
!capabilityHasDashboardScreenshotReporting
) {
return [];
}

const { sharingData } = shareOpts as unknown as { sharingData: ReportingSharingData };
const shareActions: ShareMenuItem[] = [];

const pngPanelTitle = i18n.translate('reporting.share.contextMenu.pngReportsButtonLabel', {
defaultMessage: 'PNG Reports',
});

const jobProviderOptions: JobParamsProviderOptions = {
shareableUrl: isDirty ? shareableUrl : shareableUrlForSavedObject ?? shareableUrl,
objectType,
sharingData,
};
const isJobV2Params = ({
sharingData: _sharingData,
}: {
sharingData: Record<string, unknown>;
}) => _sharingData.locatorParams != null;

const isV2Job = isJobV2Params(jobProviderOptions);
const requiresSavedState = !isV2Job;

const panelPng = {
shareMenuItem: {
name: pngPanelTitle,
icon: 'document',
toolTipContent: licenseToolTipContent,
disabled: licenseDisabled || sharingData.reportingDisabled,
['data-test-subj']: 'PNGReports',
sortOrder: 10,
},
panel: {
id: 'reportingPngPanel',
title: pngPanelTitle,
content: (
<ScreenCapturePanelContent
apiClient={apiClient}
startServices$={startServices$}
reportType={'pngV2'}
objectId={objectId}
requiresSavedState={requiresSavedState}
getJobParams={getJobParams(jobProviderOptions, 'pngV2')}
isDirty={isDirty}
onClose={onClose}
/>
),
},
};

const pdfPanelTitle = i18n.translate('reporting.share.contextMenu.pdfReportsButtonLabel', {
defaultMessage: 'PDF Reports',
});

const panelPdf = {
shareMenuItem: {
name: pdfPanelTitle,
icon: 'document',
toolTipContent: licenseToolTipContent,
disabled: licenseDisabled || sharingData.reportingDisabled,
['data-test-subj']: 'PDFReports',
sortOrder: 10,
},
panel: {
id: 'reportingPdfPanel',
title: pdfPanelTitle,
content: (
<ScreenCapturePanelContent
apiClient={apiClient}
startServices$={startServices$}
reportType={'printablePdfV2'}
objectId={objectId}
requiresSavedState={requiresSavedState}
layoutOption={objectType === 'dashboard' ? 'print' : undefined}
getJobParams={getJobParams(jobProviderOptions, 'printablePdfV2')}
isDirty={isDirty}
onClose={onClose}
/>
),
},
};

shareActions.push(panelPng);
shareActions.push(panelPdf);
return shareActions;
};

return {
id: 'screenCaptureReports',
getShareMenuItems,
};
};

export const reportingExportModalProvider = ({
apiClient,
license,
Expand Down Expand Up @@ -249,7 +100,7 @@ export const reportingExportModalProvider = ({
}

const { sharingData } = shareOpts as unknown as { sharingData: ReportingSharingData };
const shareActions: ShareMenuItem[] = [];
const shareActions: ShareMenuItemV2[] = [];

const jobProviderOptions: JobParamsProviderOptions = {
shareableUrl: isDirty ? shareableUrl : shareableUrlForSavedObject ?? shareableUrl,
Expand All @@ -259,32 +110,9 @@ export const reportingExportModalProvider = ({

const requiresSavedState = sharingData.locatorParams === null;

const relativePathPDF = apiClient.getReportingPublicJobPath(
'printablePdfV2',
apiClient.getDecoratedJobParams(getJobParams(jobProviderOptions, 'printablePdfV2')())
);

const relativePathPNG = apiClient.getReportingPublicJobPath(
'pngV2',
apiClient.getDecoratedJobParams(getJobParams(jobProviderOptions, 'pngV2')())
);

const generateReportPDF = ({
intl,
optimizedForPrinting = false,
}: {
intl: InjectedIntl;
optimizedForPrinting?: boolean;
}) => {
const el = document.querySelector('[data-shared-items-container]');
const { height, width } = el ? el.getBoundingClientRect() : { height: 768, width: 1024 };
const dimensions = { height, width };

const generateReportPDF = ({ intl, optimizedForPrinting = false }: ScreenshotExportOpts) => {
const decoratedJobParams = apiClient.getDecoratedJobParams({
...getJobParams(jobProviderOptions, 'printablePdfV2')(),
layout: { id: optimizedForPrinting ? 'print' : 'preserve_layout', dimensions },
objectType,
title: sharingData.title,
...getJobParams({ ...jobProviderOptions, optimizedForPrinting }, 'printablePdfV2')(),
});

return apiClient
Expand Down Expand Up @@ -330,19 +158,27 @@ export const reportingExportModalProvider = ({
});
};

const generateReportPNG = ({ intl }: { intl: InjectedIntl }) => {
const { layout: outerLayout } = getJobParams(jobProviderOptions, 'pngV2')();
let dimensions = outerLayout?.dimensions;
if (!dimensions) {
const el = document.querySelector('[data-shared-items-container]');
const { height, width } = el ? el.getBoundingClientRect() : { height: 768, width: 1024 };
dimensions = { height, width };
}
const generateExportUrlPDF = ({ optimizedForPrinting }: ScreenshotExportOpts) => {
const jobParams = apiClient.getDecoratedJobParams(
getJobParams({ ...jobProviderOptions, optimizedForPrinting }, 'printablePdfV2')()
);
const relativePathPDF = apiClient.getReportingPublicJobPath('printablePdfV2', jobParams);

return new URL(relativePathPDF, window.location.href).toString();
};

const generateExportUrlPNG = () => {
const jobParams = apiClient.getDecoratedJobParams(
getJobParams(jobProviderOptions, 'pngV2')()
);
const relativePathPNG = apiClient.getReportingPublicJobPath('pngV2', jobParams);

return new URL(relativePathPNG, window.location.href).toString();
};

const generateReportPNG = ({ intl }: ScreenshotExportOpts) => {
const decoratedJobParams = apiClient.getDecoratedJobParams({
...getJobParams(jobProviderOptions, 'pngV2')(),
layout: { id: 'preserve_layout', dimensions },
objectType,
title: sharingData.title,
});
return apiClient
.createReportingJob('pngV2', decoratedJobParams)
Expand Down Expand Up @@ -398,6 +234,7 @@ export const reportingExportModalProvider = ({
},
label: 'PDF' as const,
generateExport: generateReportPDF,
generateExportUrl: generateExportUrlPDF,
reportType: 'printablePdfV2',
requiresSavedState,
helpText: (
Expand All @@ -415,7 +252,6 @@ export const reportingExportModalProvider = ({
layoutOption: objectType === 'dashboard' ? ('print' as const) : undefined,
renderLayoutOptionSwitch: objectType === 'dashboard',
renderCopyURLButton: true,
absoluteUrl: new URL(relativePathPDF, window.location.href).toString(),
});

shareActions.push({
Expand All @@ -429,6 +265,7 @@ export const reportingExportModalProvider = ({
},
label: 'PNG' as const,
generateExport: generateReportPNG,
generateExportUrl: generateExportUrlPNG,
reportType: 'pngV2',
requiresSavedState,
helpText: (
Expand All @@ -442,7 +279,6 @@ export const reportingExportModalProvider = ({
),
layoutOption: objectType === 'dashboard' ? ('print' as const) : undefined,
renderCopyURLButton: true,
absoluteUrl: new URL(relativePathPNG, window.location.href).toString(),
});

return shareActions;
Expand Down
Loading

0 comments on commit c6cb2f7

Please sign in to comment.