[Reporting] Clean ups for deprecated export types (#188270)

Epic link: https://github.com/elastic/kibana-team/issues/720 

## Summary

This PR solves a problem in Reporting code, in that most functional
tests and API integration tests that cover PDF functionality were
referencing the deprecated `printable_pdf` export type. As of this PR,
the tests now use the `printable_pdf_v2` export type.

List of clean up changes:
* Fully remove PNGV1 (leftovers from #162517)
* Update tests and documentation to use PDFV2
* Allow users to see removed types in the listing and download reports
for removed types. This makes it possible to see `csv` and `png` job
types in the current version, even though it's not possible to create
new jobs of those types.

Context: the reporting plugin has two deprecated export types which are
still active in the code:
* `printable_pdf` - this is actively replaced with `printable_pdf_v2'
* `csv_searchsource` - this is replaced with `csv_v2' for certain
applications such as ES|QL export. For general CSV export, `csv_v2' is
currently only offered as an API`.

### Checklist

Delete any items that are not applicable to this PR.

- [x]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

### For maintainers

- [x] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Tim Sullivan 2024-08-30 12:17:37 -07:00 committed by GitHub
parent b886fe850c
commit 88626a77ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 234 additions and 356 deletions

View file

@ -19,7 +19,7 @@ PUT _watcher/watch/error_report
"attachments" : {
"error_report.pdf" : {
"reporting" : {
"url": "http://0.0.0.0:5601/api/reporting/generate/printablePdf?jobParams=...", <2>
"url": "http://0.0.0.0:5601/api/reporting/generate/printablePdfV2?jobParams=...", <2>
"retries":40, <3>
"interval":"15s", <4>
"auth":{ <5>

View file

@ -60,9 +60,6 @@ export interface ReportOutput extends TaskRunResult {
*/
export type CsvPagingStrategy = 'pit' | 'scroll';
/**
* @deprecated
*/
export interface BaseParams {
browserTimezone: string; // to format dates in the user's time zone
objectType: string;
@ -80,9 +77,6 @@ export type BaseParamsV2 = BaseParams & {
locatorParams: LocatorParams[];
};
/**
* @deprecated
*/
export interface BasePayload extends BaseParams {
headers: string;
spaceId?: string;

View file

@ -19,8 +19,8 @@
"@kbn/reporting-common",
"@kbn/screenshotting-plugin",
"@kbn/reporting-server",
"@kbn/licensing-plugin",
"@kbn/reporting-export-types-pdf-common",
"@kbn/reporting-mocks-server",
"@kbn/licensing-plugin",
]
}

View file

@ -68,6 +68,7 @@ export class PngExportType extends ExportType<JobParamsPNGV2, TaskPayloadPNGV2>
* @returns jobParams
*/
public createJob = async ({ locatorParams, ...jobParams }: JobParamsPNGV2) => {
// FIXME: validate that locatorParams exists, and contains an ID field and params object
return {
...jobParams,
locatorParams: [locatorParams],

View file

@ -6,8 +6,5 @@
* Side Public License, v 1.
*/
export const PNG_REPORT_TYPE = 'PNG';
export const PNG_REPORT_TYPE_V2 = 'pngV2';
export const PNG_JOB_TYPE = 'PNG';
export const PNG_JOB_TYPE_V2 = 'PNGV2';

View file

@ -22,17 +22,10 @@ import type {
TaskRunResult,
} from '@kbn/reporting-common/types';
import { CSV_JOB_TYPE, CSV_JOB_TYPE_V2 } from '@kbn/reporting-export-types-csv-common';
import { PDF_JOB_TYPE, PDF_JOB_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_JOB_TYPE, PNG_JOB_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
import { PDF_JOB_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_JOB_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
const jobTypes = [
CSV_JOB_TYPE,
CSV_JOB_TYPE_V2,
PDF_JOB_TYPE,
PDF_JOB_TYPE_V2,
PNG_JOB_TYPE,
PNG_JOB_TYPE_V2,
];
const jobTypes = [CSV_JOB_TYPE, CSV_JOB_TYPE_V2, PDF_JOB_TYPE_V2, PNG_JOB_TYPE_V2];
type JobTypeDeclaration = typeof jobTypes;
type JobTypes = JobTypeDeclaration[keyof JobTypeDeclaration];

View file

@ -10,8 +10,8 @@ import React, { Component, ReactElement } from 'react';
import * as Rx from 'rxjs';
import { CSV_REPORT_TYPE, CSV_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-csv-common';
import { PDF_REPORT_TYPE, PDF_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_REPORT_TYPE, PNG_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
import { PDF_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
import {
EuiAccordion,
@ -248,15 +248,16 @@ class ReportingPanelContentUi extends Component<Props, State> {
private prettyPrintReportingType = () => {
switch (this.props.reportType) {
case PDF_REPORT_TYPE:
case 'pdf':
case PDF_REPORT_TYPE_V2:
return 'PDF';
case 'csv':
case CSV_REPORT_TYPE:
case CSV_REPORT_TYPE_V2:
return 'CSV';
case 'png':
case PNG_REPORT_TYPE_V2:
return PNG_REPORT_TYPE;
return 'PNG';
default:
return this.props.reportType;
}

View file

@ -9,8 +9,8 @@
import React from 'react';
import { Observable } from 'rxjs';
import { PDF_REPORT_TYPE, PDF_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_REPORT_TYPE, PNG_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
import { PDF_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
import { StartServices } from '..';
import { ReportingAPIClient } from '../..';
@ -47,8 +47,6 @@ export interface ReportingPublicComponents {
/** Needed for Canvas PDF reports */
ReportingPanelPDFV2(props: ApplicationProps): JSX.Element | null;
ReportingPanelPNGV2(props: ApplicationProps): JSX.Element | undefined;
ReportingModalPDF(props: ApplicationProps): JSX.Element | undefined;
ReportingModalPNG(props: ApplicationProps): JSX.Element | undefined;
}
/**
@ -91,33 +89,5 @@ export function getSharedComponents(
);
}
},
ReportingModalPDF(props: ApplicationProps) {
if (props.layoutOption === 'canvas') {
return (
<ScreenCapturePanelContent
requiresSavedState={false}
reportType={PDF_REPORT_TYPE}
apiClient={apiClient}
layoutOption={'canvas' as const}
startServices$={startServices$}
{...props}
/>
);
}
},
ReportingModalPNG(props: ApplicationProps) {
if (props.layoutOption === 'canvas') {
return (
<ScreenCapturePanelContent
requiresSavedState={false}
reportType={PNG_REPORT_TYPE}
apiClient={apiClient}
layoutOption={'canvas' as const}
startServices$={startServices$}
{...props}
/>
);
}
},
};
}

View file

@ -7,48 +7,23 @@
import {
CSV_JOB_TYPE,
CSV_JOB_TYPE_DEPRECATED,
CSV_JOB_TYPE_V2,
CSV_REPORT_TYPE,
CSV_REPORT_TYPE_V2,
} from '@kbn/reporting-export-types-csv-common';
import {
PDF_JOB_TYPE,
PDF_JOB_TYPE_V2,
PDF_REPORT_TYPE,
PDF_REPORT_TYPE_V2,
} from '@kbn/reporting-export-types-pdf-common';
import {
PNG_JOB_TYPE,
PNG_JOB_TYPE_V2,
PNG_REPORT_TYPE,
PNG_REPORT_TYPE_V2,
} from '@kbn/reporting-export-types-png-common';
import { PDF_JOB_TYPE_V2, PDF_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_JOB_TYPE_V2, PNG_REPORT_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
// Export Type Sets
export const reportTypes = [
CSV_REPORT_TYPE,
CSV_REPORT_TYPE_V2,
PDF_REPORT_TYPE,
PDF_REPORT_TYPE_V2,
PNG_REPORT_TYPE,
PNG_REPORT_TYPE_V2,
];
export const jobTypes = [
CSV_JOB_TYPE,
CSV_JOB_TYPE_V2,
PDF_JOB_TYPE,
PDF_JOB_TYPE_V2,
PNG_JOB_TYPE,
PNG_JOB_TYPE_V2,
];
export const jobTypes = [CSV_JOB_TYPE, CSV_JOB_TYPE_V2, PDF_JOB_TYPE_V2, PNG_JOB_TYPE_V2];
export const USES_HEADLESS_JOB_TYPES = [
PDF_JOB_TYPE,
PNG_JOB_TYPE,
PDF_JOB_TYPE_V2,
PNG_JOB_TYPE_V2,
];
export const USES_HEADLESS_JOB_TYPES = [PDF_JOB_TYPE_V2, PNG_JOB_TYPE_V2];
export const DEPRECATED_JOB_TYPES = [CSV_JOB_TYPE_DEPRECATED];
export const DEPRECATED_JOB_TYPES = ['csv']; // Replaced with csv_searchsource and csv_v2

View file

@ -5,24 +5,25 @@
* 2.0.
*/
import { CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED } from '@kbn/reporting-export-types-csv-common';
import { PDF_JOB_TYPE, PDF_JOB_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_JOB_TYPE, PNG_JOB_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
import { CSV_JOB_TYPE, CSV_JOB_TYPE_V2 } from '@kbn/reporting-export-types-csv-common';
import { PDF_JOB_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PNG_JOB_TYPE_V2 } from '@kbn/reporting-export-types-png-common';
// TODO: Remove this code once everyone is using the new PDF format, then we can also remove the legacy
// export type entirely
// TODO: Remove this code once the enabled CSV export type is using the new format
export const isJobV2Params = ({ sharingData }: { sharingData: Record<string, unknown> }): boolean =>
sharingData.locatorParams != null;
export const prettyPrintJobType = (type: string) => {
switch (type) {
case PDF_JOB_TYPE:
case 'pdf':
case 'printable_pdf':
case PDF_JOB_TYPE_V2:
return 'PDF';
case 'csv':
case CSV_JOB_TYPE:
case CSV_JOB_TYPE_DEPRECATED:
case CSV_JOB_TYPE_V2:
return 'CSV';
case PNG_JOB_TYPE:
case 'png':
case PNG_JOB_TYPE_V2:
return 'PNG';
default:

View file

@ -40,7 +40,7 @@ describe('ReportListing', () => {
it('renders a listing with some items', () => {
const { find } = testBed;
expect(find('reportDownloadLink').length).toBe(mockJobs.length);
expect(find('reportJobRow').length).toBe(mockJobs.length);
});
it('subscribes to license changes, and unsubscribes on dismount', async () => {

View file

@ -327,7 +327,7 @@ export class ReportListingTable extends Component<ListingPropsInternal, State> {
actions: [
{
isPrimary: true,
'data-test-subj': 'reportDownloadLink',
'data-test-subj': (job) => `reportDownloadLink-${job.id}`,
type: 'icon',
icon: 'download',
name: i18n.translate('xpack.reporting.listing.table.downloadReportButtonLabel', {

View file

@ -180,8 +180,14 @@ export class ReportingPublicPlugin
core.application.register({
id: 'reportingRedirect',
mount: async (params) => {
const { mountRedirectApp } = await import('./redirect');
return mountRedirectApp({
const [startServices, importParams] = await Promise.all([
core.getStartServices(),
import('./redirect'),
]);
const [coreStart] = startServices;
const { mountRedirectApp } = importParams;
return mountRedirectApp(coreStart, {
...params,
apiClient,
screenshotMode: screenshotModeSetup,

View file

@ -5,11 +5,11 @@
* 2.0.
*/
import { EuiErrorBoundary } from '@elastic/eui';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import type { AppMountParameters } from '@kbn/core/public';
import type { AppMountParameters, CoreStart } from '@kbn/core/public';
import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public';
import type { SharePluginSetup } from '@kbn/share-plugin/public';
@ -22,22 +22,19 @@ interface MountParams extends AppMountParameters {
share: SharePluginSetup;
}
export const mountRedirectApp = ({
element,
apiClient,
history,
screenshotMode,
share,
}: MountParams) => {
export const mountRedirectApp = (
coreStart: CoreStart,
{ element, apiClient, history, screenshotMode, share }: MountParams
) => {
render(
<EuiErrorBoundary>
<KibanaRenderContextProvider {...coreStart}>
<RedirectApp
apiClient={apiClient}
history={history}
screenshotMode={screenshotMode}
share={share}
/>
</EuiErrorBoundary>,
</KibanaRenderContextProvider>,
element
);

View file

@ -11,7 +11,7 @@ import rison from '@kbn/rison';
import { KibanaRequest, KibanaResponseFactory } from '@kbn/core/server';
import { coreMock, httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks';
import { JobParamsPDFDeprecated, TaskPayloadPDFV2 } from '@kbn/reporting-export-types-pdf-common';
import { JobParamsPDFV2, TaskPayloadPDFV2 } from '@kbn/reporting-export-types-pdf-common';
import { createMockConfigSchema } from '@kbn/reporting-mocks-server';
import { ReportingCore } from '../../..';
@ -54,13 +54,13 @@ const getMockResponseFactory = () =>
} as unknown as KibanaResponseFactory);
const mockLogger = loggingSystemMock.createLogger();
const mockJobParams: JobParamsPDFDeprecated = {
const mockJobParams: JobParamsPDFV2 = {
browserTimezone: 'UTC',
objectType: 'cool_object_type',
title: 'cool_title',
version: 'unknown',
layout: { id: 'preserve_layout' },
relativeUrls: [],
locatorParams: [],
};
describe('Handle request to generate', () => {
@ -145,9 +145,8 @@ describe('Handle request to generate', () => {
"layout": Object {
"id": "preserve_layout",
},
"locatorParams": undefined,
"locatorParams": Array [],
"objectType": "cool_object_type",
"relativeUrls": Array [],
"spaceId": undefined,
"title": "cool_title",
"version": "unknown",
@ -158,7 +157,7 @@ describe('Handle request to generate', () => {
test('provides a default kibana version field for older POST URLs', async () => {
// how do we handle the printable_pdf endpoint that isn't migrating to the class instance of export types?
(mockJobParams as unknown as { version?: string }).version = undefined;
const report = await requestHandler.enqueueJob('printablePdf', mockJobParams);
const report = await requestHandler.enqueueJob('printablePdfV2', mockJobParams);
const { _id, created_at: _created_at, ...snapObj } = report;
expect(snapObj.payload.version).toBe('7.14.0');

View file

@ -10,7 +10,7 @@ import { Readable } from 'stream';
import { JOB_STATUS } from '@kbn/reporting-common';
import { ReportApiJSON } from '@kbn/reporting-common/types';
import { CSV_JOB_TYPE } from '@kbn/reporting-export-types-csv-common';
import { PDF_JOB_TYPE, PDF_JOB_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { PDF_JOB_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common';
import { createMockConfigSchema } from '@kbn/reporting-mocks-server';
import { ReportingCore } from '../../..';
@ -54,7 +54,7 @@ describe('getDocumentPayload', () => {
id: 'id1',
index: '.reporting-12345',
status: JOB_STATUS.COMPLETED,
jobtype: PDF_JOB_TYPE,
jobtype: PDF_JOB_TYPE_V2,
output: {
content_type: 'application/pdf',
size: 1024,

View file

@ -79,34 +79,6 @@ it(`should return 404 if the docId isn't resolve`, async function () {
expect(handlerCalled).toBe(false);
});
it(`should return forbidden if job type is unrecognized`, async function () {
mockJobsQueryFactory.mockReturnValue({
get: jest.fn(() => ({ jobtype: 'notARealJobType' })),
});
let handlerCalled = false;
const handler = async () => {
handlerCalled = true;
return {
status: 200,
options: {},
};
};
await jobManagementPreRouting(
mockCore,
mockResponseFactory,
'doc123',
mockUser,
mockCounters,
options,
handler
);
expect(mockResponseFactory.forbidden).toBeCalled();
expect(handlerCalled).toBe(false);
});
it(`should call callback when document is available`, async function () {
mockJobsQueryFactory.mockReturnValue({
get: jest.fn(() => ({ jobtype: 'csv_searchsource' })),

View file

@ -32,11 +32,6 @@ export const jobManagementPreRouting = async (
{ isInternal }: { isInternal: boolean },
cb: JobManagementResponseHandler
) => {
const licenseInfo = await reporting.getLicenseInfo();
const {
management: { jobTypes = [] },
} = licenseInfo;
const jobsQuery = jobsQueryFactory(reporting, { isInternal });
const doc = await jobsQuery.get(user, jobId);
@ -45,15 +40,6 @@ export const jobManagementPreRouting = async (
}
const { jobtype } = doc;
if (!jobTypes.includes(jobtype)) {
return res.forbidden({
body: i18n.translate('xpack.reporting.jobResponse.errorHandler.notAuthorized', {
defaultMessage: `Sorry, you are not authorized to view or delete {jobtype} reports`,
values: { jobtype },
}),
});
}
counters.usageCounter(jobtype);
try {

View file

@ -37,8 +37,8 @@ describe('jobsQuery', () => {
});
it('should pass parameters in the request body', async () => {
await jobsQuery.list(['pdf'], { username: 'somebody' }, 1, 10, ['id1', 'id2']);
await jobsQuery.list(['pdf'], { username: 'somebody' }, 1, 10, null);
await jobsQuery.list({ username: 'somebody' }, 1, 10, ['id1', 'id2']);
await jobsQuery.list({ username: 'somebody' }, 1, 10, null);
expect(client.search).toHaveBeenCalledTimes(2);
expect(client.search).toHaveBeenNthCalledWith(
@ -51,7 +51,6 @@ describe('jobsQuery', () => {
{},
'constant_score.filter.bool.must',
expect.arrayContaining([
{ terms: { jobtype: ['pdf'] } },
{ term: { created_by: 'somebody' } },
{ ids: { values: ['id1', 'id2'] } },
])
@ -75,7 +74,7 @@ describe('jobsQuery', () => {
});
it('should return reports list', async () => {
await expect(jobsQuery.list(['pdf'], { username: 'somebody' }, 0, 10, [])).resolves.toEqual(
await expect(jobsQuery.list({ username: 'somebody' }, 0, 10, [])).resolves.toEqual(
expect.arrayContaining([
expect.objectContaining({ id: 'id1', jobtype: 'pdf' }),
expect.objectContaining({ id: 'id2', jobtype: 'csv' }),
@ -86,9 +85,7 @@ describe('jobsQuery', () => {
it('should return an empty array when there are no hits', async () => {
client.search.mockResponse({} as Awaited<ReturnType<ElasticsearchClient['search']>>);
await expect(
jobsQuery.list(['pdf'], { username: 'somebody' }, 0, 10, [])
).resolves.toHaveLength(0);
await expect(jobsQuery.list({ username: 'somebody' }, 0, 10, [])).resolves.toHaveLength(0);
});
it('should reject if the report source is missing', async () => {
@ -96,9 +93,9 @@ describe('jobsQuery', () => {
set<Awaited<ReturnType<ElasticsearchClient['search']>>>({}, 'hits.hits', [{}])
);
await expect(
jobsQuery.list(['pdf'], { username: 'somebody' }, 0, 10, [])
).rejects.toBeInstanceOf(Error);
await expect(jobsQuery.list({ username: 'somebody' }, 0, 10, [])).rejects.toBeInstanceOf(
Error
);
});
});
@ -108,7 +105,7 @@ describe('jobsQuery', () => {
});
it('should pass parameters in the request body', async () => {
await jobsQuery.count(['pdf'], { username: 'somebody' });
await jobsQuery.count({ username: 'somebody' });
expect(client.count).toHaveBeenCalledWith(
expect.objectContaining({
@ -116,10 +113,7 @@ describe('jobsQuery', () => {
query: set(
{},
'constant_score.filter.bool.must',
expect.arrayContaining([
{ terms: { jobtype: ['pdf'] } },
{ term: { created_by: 'somebody' } },
])
expect.arrayContaining([{ term: { created_by: 'somebody' } }])
),
}),
})
@ -127,7 +121,7 @@ describe('jobsQuery', () => {
});
it('should return reports number', async () => {
await expect(jobsQuery.count(['pdf'], { username: 'somebody' })).resolves.toBe(10);
await expect(jobsQuery.count({ username: 'somebody' })).resolves.toBe(10);
});
});

View file

@ -40,13 +40,12 @@ export type ReportContent = Pick<ReportSource, 'status' | 'jobtype' | 'output'>
export interface JobsQueryFactory {
list(
jobTypes: string[],
user: ReportingUser,
page: number,
size: number,
jobIds: string[] | null
): Promise<ReportApiJSON[]>;
count(jobTypes: string[], user: ReportingUser): Promise<number>;
count(user: ReportingUser): Promise<number>;
get(user: ReportingUser, id: string): Promise<ReportApiJSON | void>;
getError(id: string): Promise<string>;
getDocumentPayload(doc: ReportApiJSON): Promise<Payload>;
@ -74,7 +73,7 @@ export function jobsQueryFactory(
}
return {
async list(jobTypes, user, page = 0, size = defaultSize, jobIds) {
async list(user, page = 0, size = defaultSize, jobIds) {
const username = getUsername(user);
const body = getSearchBody({
size,
@ -84,7 +83,6 @@ export function jobsQueryFactory(
filter: {
bool: {
must: [
{ terms: { jobtype: jobTypes } },
{ term: { created_by: username } },
...(jobIds ? [{ ids: { values: jobIds } }] : []),
],
@ -111,14 +109,14 @@ export function jobsQueryFactory(
);
},
async count(jobTypes, user) {
async count(user) {
const username = getUsername(user);
const body = {
query: {
constant_score: {
filter: {
bool: {
must: [{ terms: { jobtype: jobTypes } }, { term: { created_by: username } }],
must: [{ term: { created_by: username } }],
},
},
},

View file

@ -120,7 +120,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.expect(400)
.then(({ body }) =>
expect(body.message).toMatchInlineSnapshot(
@ -135,7 +135,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf?jobParams=foo:`)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdfV2?jobParams=foo:`)
.expect(400)
.then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"'));
});
@ -146,7 +146,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({ jobParams: `foo:` })
.expect(400)
.then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"'));
@ -172,7 +172,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({ jobParams: rison.encode({ browserTimezone: 'America/Amsterdam', title: `abc` }) })
.expect(400)
.then(({ body }) =>
@ -188,7 +188,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({ jobParams: rison.encode({ title: `abc` }) })
.expect(500);
});
@ -199,11 +199,10 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({
jobParams: rison.encode({
title: `abc`,
relativeUrls: ['test'],
layout: { id: 'test' },
objectType: 'canvas workpad',
}),
@ -216,19 +215,14 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
created_by: 'Tom Riddle',
id: 'foo',
index: 'foo-index',
jobtype: 'printable_pdf',
jobtype: 'printable_pdf_v2',
payload: {
forceNow: expect.any(String),
isDeprecated: true,
isDeprecated: false,
layout: {
id: 'test',
},
objectType: 'canvas workpad',
objects: [
{
relativeUrl: 'test',
},
],
title: 'abc',
version: '7.14.0',
},
@ -246,11 +240,10 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({
jobParams: rison.encode({
title: `abc`,
relativeUrls: ['test'],
layout: { id: 'test' },
objectType: 'canvas workpad',
}),
@ -259,7 +252,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1);
expect(usageCounter.incrementCounter).toHaveBeenCalledWith({
counterName: `post /internal/reporting/generate/printablePdf`,
counterName: `post /internal/reporting/generate/printablePdfV2`,
counterType: 'reportingApi',
});
});
@ -271,11 +264,10 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({
jobParams: rison.encode({
title: `abc`,
relativeUrls: ['test'],
layout: { id: 'test' },
objectType: 'canvas workpad',
}),

View file

@ -205,22 +205,6 @@ describe(`Reporting Job Management Routes: Internal`, () => {
.expect(404);
});
it('returns a 403 if not a valid job type', async () => {
mockEsClient.search.mockResponseOnce(
getHits({
jobtype: 'invalidJobType',
payload: { title: 'invalid!' },
})
);
registerJobInfoRoutes(reportingCore);
await server.start();
await supertest(httpSetup.server.listener)
.get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/poo`)
.expect(403);
});
it(`returns job's info`, async () => {
mockEsClient.search.mockResponseOnce(
getHits({
@ -238,23 +222,6 @@ describe(`Reporting Job Management Routes: Internal`, () => {
.expect(200);
});
it(`returns 403 if a user cannot view a job's info`, async () => {
mockEsClient.search.mockResponseOnce(
getHits({
jobtype: 'customForbiddenJobType',
payload: {}, // payload is irrelevant
})
);
registerJobInfoRoutes(reportingCore);
await server.start();
await supertest(httpSetup.server.listener)
.get(`${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/test`)
.expect(403);
});
it('when a job is incomplete, "internal" API endpoint should return appropriate response', async () => {
mockEsClient.search.mockResponseOnce(
getHits({

View file

@ -47,14 +47,11 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) {
return handleUnavailable(res);
}
const {
management: { jobTypes = [] },
} = await reporting.getLicenseInfo();
const { page: queryPage = '0', size: querySize = '10', ids: queryIds = null } = req.query;
const page = parseInt(queryPage, 10) || 0;
const size = Math.min(100, parseInt(querySize, 10) || 10);
const jobIds = queryIds ? queryIds.split(',') : null;
const results = await jobsQuery.list(jobTypes, user, page, size, jobIds);
const results = await jobsQuery.list(user, page, size, jobIds);
counters.usageCounter();
@ -86,11 +83,7 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) {
return handleUnavailable(res);
}
const {
management: { jobTypes = [] },
} = await reporting.getLicenseInfo();
const count = await jobsQuery.count(jobTypes, user);
const count = await jobsQuery.count(user);
counters.usageCounter();

View file

@ -119,7 +119,7 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.expect(400)
.then(({ body }) =>
expect(body.message).toMatchInlineSnapshot(
@ -134,7 +134,7 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf?jobParams=foo:`)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdfV2?jobParams=foo:`)
.expect(400)
.then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"'));
});
@ -145,7 +145,7 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({ jobParams: `foo:` })
.expect(400)
.then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"'));
@ -171,7 +171,7 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({ jobParams: rison.encode({ browserTimezone: 'America/Amsterdam', title: `abc` }) })
.expect(400)
.then(({ body }) =>
@ -187,7 +187,7 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({ jobParams: rison.encode({ title: `abc` }) })
.expect(500);
});
@ -197,13 +197,53 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({
jobParams: rison.encode({
title: `abc`,
layout: { id: 'test' },
objectType: 'canvas workpad',
}),
})
.expect(200)
.then(({ body }) => {
expect(body).toMatchObject({
job: {
attempts: 0,
created_by: 'Tom Riddle',
id: 'foo',
index: 'foo-index',
jobtype: 'printable_pdf_v2',
payload: {
forceNow: expect.any(String),
isDeprecated: false,
layout: {
id: 'test',
},
objectType: 'canvas workpad',
title: 'abc',
version: '7.14.0',
},
status: 'pending',
},
path: '/mock-server-basepath/api/reporting/jobs/download/foo',
});
});
});
it('allows deprecated printablePdf request', async () => {
registerGenerationRoutesPublic(reportingCore, mockLogger);
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`)
.send({
jobParams: rison.encode({
title: `abc`,
relativeUrls: ['test'],
layout: { id: 'test' },
relativeUrls: ['test'],
objectType: 'canvas workpad',
}),
})
@ -223,11 +263,6 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
id: 'test',
},
objectType: 'canvas workpad',
objects: [
{
relativeUrl: 'test',
},
],
title: 'abc',
version: '7.14.0',
},
@ -245,11 +280,10 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({
jobParams: rison.encode({
title: `abc`,
relativeUrls: ['test'],
layout: { id: 'test' },
objectType: 'canvas workpad',
}),
@ -258,7 +292,7 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1);
expect(usageCounter.incrementCounter).toHaveBeenCalledWith({
counterName: `post /api/reporting/generate/printablePdf`,
counterName: `post /api/reporting/generate/printablePdfV2`,
counterType: 'reportingApi',
});
});
@ -269,11 +303,10 @@ describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => {
await server.start();
await supertest(httpSetup.server.listener)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`)
.post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdfV2`)
.send({
jobParams: rison.encode({
title: `abc`,
relativeUrls: ['test'],
layout: { id: 'test' },
objectType: 'canvas workpad',
}),

View file

@ -194,22 +194,6 @@ describe(`Reporting Job Management Routes: Public`, () => {
.expect(404);
});
it('returns a 403 if not a valid job type', async () => {
mockEsClient.search.mockResponseOnce(
getHits({
jobtype: 'invalidJobType',
payload: { title: 'invalid!' },
})
);
registerJobInfoRoutesPublic(reportingCore);
await server.start();
await supertest(httpSetup.server.listener)
.get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/poo`)
.expect(403);
});
it('when a job is incomplete', async () => {
mockEsClient.search.mockResponseOnce(
getHits({

View file

@ -33971,7 +33971,6 @@
"xpack.reporting.diagnostic.noUsableSandbox": "Impossible d'utiliser la sandbox Chromium. Vous pouvez la désactiver à vos risques et périls avec \"xpack.screenshotting.browser.chromium.disableSandbox\". Veuillez consulter {url}",
"xpack.reporting.errorHandler.unknownError": "Erreur inconnue",
"xpack.reporting.features.reportingFeatureName": "Reporting",
"xpack.reporting.jobResponse.errorHandler.notAuthorized": "Désolé, vous n'êtes pas autorisé à afficher ou supprimer les rapports {jobtype}",
"xpack.reporting.jobResponse.errorHandler.unknownError": "Erreur inconnue",
"xpack.reporting.jobsQuery.deleteError": "Impossible de supprimer le rapport : {error}",
"xpack.reporting.listing.diagnosticApiCallFailure": "Un problème est survenu lors de l'exécution du diagnostic : {error}",

View file

@ -33956,7 +33956,6 @@
"xpack.reporting.diagnostic.noUsableSandbox": "Chromiumサンドボックスを使用できません。これは「xpack.screenshotting.browser.chromium.disableSandbox」で無効にすることができます。この作業はご自身の責任で行ってください。{url}を参照してください",
"xpack.reporting.errorHandler.unknownError": "不明なエラー",
"xpack.reporting.features.reportingFeatureName": "レポート",
"xpack.reporting.jobResponse.errorHandler.notAuthorized": "{jobtype}レポートを表示または削除する権限がありません",
"xpack.reporting.jobResponse.errorHandler.unknownError": "不明なエラー",
"xpack.reporting.jobsQuery.deleteError": "レポートを削除できません:{error}",
"xpack.reporting.listing.diagnosticApiCallFailure": "診断の実行中に問題が発生しました:{error}",

View file

@ -33997,7 +33997,6 @@
"xpack.reporting.diagnostic.noUsableSandbox": "无法使用 Chromium 沙盒。您自行承担使用“xpack.screenshotting.browser.chromium.disableSandbox”禁用此项的风险。请参见 {url}",
"xpack.reporting.errorHandler.unknownError": "未知错误",
"xpack.reporting.features.reportingFeatureName": "Reporting",
"xpack.reporting.jobResponse.errorHandler.notAuthorized": "抱歉,您无权查看或删除 {jobtype} 报告",
"xpack.reporting.jobResponse.errorHandler.unknownError": "未知错误",
"xpack.reporting.jobsQuery.deleteError": "无法删除报告:{error}",
"xpack.reporting.listing.diagnosticApiCallFailure": "运行诊断时出现问题:{error}",

View file

@ -0,0 +1,66 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`reporting management app Listing of Reports Displays types of report jobs 1`] = `
Array [
Object {
"actions": "",
"createdAt": "2021-07-19 @ 10:29 PM",
"report": "Automated report",
"status": "Done, warnings detected",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:47 PM",
"report": "Discover search [2021-07-19T11:47:35.995-07:00]",
"status": "Done",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:46 PM",
"report": "Discover search [2021-07-19T11:46:00.132-07:00]",
"status": "Done, warnings detected",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:44 PM",
"report": "Discover search [2021-07-19T11:44:48.670-07:00]",
"status": "Done, warnings detected",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:41 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Pending",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:41 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Failed",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:41 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Done, warnings detected",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:38 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Done, warnings detected",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:38 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Done",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:38 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Done",
},
]
`;

View file

@ -83,70 +83,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
it('Displays types of report jobs', async () => {
const list = await pageObjects.reporting.getManagementList();
expectSnapshot(list).toMatchInline(`
Array [
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:47 PM",
"report": "Discover search [2021-07-19T11:47:35.995-07:00]",
"status": "Done",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:46 PM",
"report": "Discover search [2021-07-19T11:46:00.132-07:00]",
"status": "Done, warnings detected",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:44 PM",
"report": "Discover search [2021-07-19T11:44:48.670-07:00]",
"status": "Done, warnings detected",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:41 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Pending",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:41 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Failed",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:41 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Done, warnings detected",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:38 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Done",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 06:38 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Done",
},
Object {
"actions": "",
"createdAt": "2021-07-19 @ 02:41 PM",
"report": "[Flights] Global Flight Dashboard",
"status": "Failed",
},
Object {
"actions": "",
"createdAt": "2020-04-21 @ 07:01 PM",
"report": "[Logs] File Type Scatter Plot",
"status": "Done",
},
]
`);
expectSnapshot(list).toMatch();
});
it('Exposes an action to see the ES query in console', async () => {

View file

@ -28,7 +28,7 @@ export default function ({ getService }: FtrProviderContext) {
it('should fail job when page violates the network policy', async () => {
const downloadPath = await reportingAPI.postJob(
`/api/reporting/generate/printablePdf?jobParams=(layout:(dimensions:(height:720,width:1080),id:preserve_layout),objectType:'canvas%20workpad',relativeUrls:!(%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fworkpad-e7464259-0b75-4b8c-81c8-8422b15ff201%2Fpage%2F1),title:'My%20Canvas%20Workpad')`
`/api/reporting/generate/printablePdfV2?jobParams=(layout:(dimensions:(height:720,width:1080),id:preserve_layout),locatorParams:!((id:CANVAS_APP_LOCATOR,params:(id:workpad-e7464259-0b75-4b8c-81c8-8422b15ff201,page:1,view:workpadPDF),version:'8.16.0')),objectType:'canvas workpad',title:'Workpad of Death',version:'8.16.0')`
);
// Retry the download URL until a "failed" response status is returned

View file

@ -32,7 +32,7 @@ export default function ({ getService }: FtrProviderContext) {
browserTimezone: 'UTC',
title: 'test PDF disallowed',
layout: { id: 'preserve_layout' },
relativeUrls: ['/fooyou'],
locatorParams: [{ id: 'canvas', version: '7.14.0', params: {} }],
objectType: 'dashboard',
version: '7.14.0',
}
@ -48,7 +48,7 @@ export default function ({ getService }: FtrProviderContext) {
browserTimezone: 'UTC',
title: 'test PDF allowed',
layout: { id: 'preserve_layout' },
relativeUrls: ['/fooyou'],
locatorParams: [{ id: 'canvas', version: '7.14.0', params: {} }],
objectType: 'dashboard',
version: '7.14.0',
}
@ -66,7 +66,7 @@ export default function ({ getService }: FtrProviderContext) {
browserTimezone: 'UTC',
title: 'test PDF disallowed',
layout: { id: 'preserve_layout' },
relativeUrls: ['/fooyou'],
locatorParams: [{ id: 'canvas', version: '7.14.0', params: {} }],
objectType: 'visualization',
version: '7.14.0',
}
@ -82,7 +82,7 @@ export default function ({ getService }: FtrProviderContext) {
browserTimezone: 'UTC',
title: 'test PDF allowed',
layout: { id: 'preserve_layout' },
relativeUrls: ['/fooyou'],
locatorParams: [{ id: 'canvas', version: '7.14.0', params: {} }],
objectType: 'visualization',
version: '7.14.0',
}
@ -100,7 +100,7 @@ export default function ({ getService }: FtrProviderContext) {
browserTimezone: 'UTC',
title: 'test PDF disallowed',
layout: { id: 'preserve_layout' },
relativeUrls: ['/fooyou'],
locatorParams: [{ id: 'canvas', version: '7.14.0', params: {} }],
objectType: 'canvas',
version: '7.14.0',
}
@ -116,7 +116,7 @@ export default function ({ getService }: FtrProviderContext) {
browserTimezone: 'UTC',
title: 'test PDF allowed',
layout: { id: 'preserve_layout' },
relativeUrls: ['/fooyou'],
locatorParams: [{ id: 'canvas', version: '7.14.0', params: {} }],
objectType: 'canvas',
version: '7.14.0',
}

View file

@ -157,7 +157,7 @@ export default function ({ getService }: FtrProviderContext) {
const downloadPath = await reportingAPI.postJobJSON(
`/s/non_default_space/api/reporting/generate/pngV2`,
{
jobParams: `(browserTimezone:UTC,layout:(dimensions:(height:512,width:2402),id:png),objectType:dashboard,relativeUrl:'/s/non_default_space/app/dashboards#/view/3c9ee360-e7ee-11ea-a730-d58e9ea7581b?_g=(filters:!!(),refreshInterval:(pause:!!t,value:0),time:(from:!'2019-06-10T03:17:28.800Z!',to:!'2019-07-14T19:25:06.385Z!'))&_a=(description:!'!',filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),query:(language:kuery,query:!'!'),timeRestore:!!t,title:!'Ecom%20Dashboard%20Non%20Default%20Space!',viewMode:view)',title:'Ecom Dashboard Non Default Space')`,
jobParams: `(browserTimezone:UTC,layout:(dimensions:(height:512,width:2402),id:png),locatorParams:(id:DASHBOARD_APP_LOCATOR,params:(dashboardId:e35742df-db88-5bd6-9d35-732b4b04a079,preserveSavedFilters:!t,timeRange:(from:'2019-06-10T03:17:28.800Z',to:'2019-07-14T19:25:06.385Z'),useHash:!f,viewMode:view)),objectType:dashboard,title:'Ecom Dashboard Non Default Space',version:'8.16.0')`,
}
);
@ -168,9 +168,9 @@ export default function ({ getService }: FtrProviderContext) {
it('should complete a job of PDF export of a dashboard in non-default space', async () => {
const downloadPath = await reportingAPI.postJobJSON(
`/s/non_default_space/api/reporting/generate/printablePdf`,
`/s/non_default_space/api/reporting/generate/printablePdfV2`,
{
jobParams: `(browserTimezone:UTC,layout:(dimensions:(height:512,width:2402),id:preserve_layout),objectType:dashboard,relativeUrls:!('/s/non_default_space/app/dashboards#/view/3c9ee360-e7ee-11ea-a730-d58e9ea7581b?_g=(filters:!!(),refreshInterval:(pause:!!t,value:0),time:(from:!'2019-06-10T03:17:28.800Z!',to:!'2019-07-14T19:25:06.385Z!'))&_a=(description:!'!',filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),query:(language:kuery,query:!'!'),timeRestore:!!t,title:!'Ecom%20Dashboard%20Non%20Default%20Space!',viewMode:view)'),title:'Ecom Dashboard Non Default Space')`,
jobParams: `(browserTimezone:UTC,layout:(dimensions:(height:512,width:2402),id:preserve_layout),locatorParams:!((id:DASHBOARD_APP_LOCATOR,params:(dashboardId:e35742df-db88-5bd6-9d35-732b4b04a079,preserveSavedFilters:!t,timeRange:(from:'2019-06-10T03:17:28.800Z',to:'2019-07-14T19:25:06.385Z'),useHash:!f,viewMode:view))),objectType:dashboard,title:'Ecom Dashboard Non Default Space',version:'8.16.0')`,
}
);

View file

@ -8,7 +8,7 @@
import type { LoadActionPerfOptions } from '@kbn/es-archiver';
import { INTERNAL_ROUTES } from '@kbn/reporting-common';
import type { JobParamsCSV } from '@kbn/reporting-export-types-csv-common';
import type { JobParamsPDFDeprecated } from '@kbn/reporting-export-types-pdf-common';
import type { JobParamsPDFV2 } from '@kbn/reporting-export-types-pdf-common';
import type { JobParamsPNGV2 } from '@kbn/reporting-export-types-png-common';
import {
REPORTING_DATA_STREAM_WILDCARD,
@ -142,10 +142,10 @@ export function createScenarios({ getService }: Pick<FtrProviderContext, 'getSer
});
};
const generatePdf = async (username: string, password: string, job: JobParamsPDFDeprecated) => {
const generatePdf = async (username: string, password: string, job: JobParamsPDFV2) => {
const jobParams = rison.encode(job);
return await supertestWithoutAuth
.post(`/api/reporting/generate/printablePdf`)
.post(`/api/reporting/generate/printablePdfV2`)
.auth(username, password)
.set('kbn-xsrf', 'xxx')
.send({ jobParams });
@ -183,7 +183,7 @@ export function createScenarios({ getService }: Pick<FtrProviderContext, 'getSer
const postJobJSON = async (apiPath: string, jobJSON: object = {}): Promise<string> => {
log.debug(`ReportingAPI.postJobJSON((${apiPath}): ${JSON.stringify(jobJSON)})`);
const { body } = await supertest.post(apiPath).set('kbn-xsrf', 'xxx').send(jobJSON);
const { body } = await supertest.post(apiPath).set('kbn-xsrf', 'xxx').send(jobJSON).expect(200);
return body.path;
};

View file

@ -13,6 +13,7 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
const testSubjects = getService('testSubjects');
const browser = getService('browser');
const reportingFunctional = getService('reportingFunctional');
const esArchiver = getService('esArchiver');
describe('Access to Management > Reporting', () => {
before(async () => {
@ -54,5 +55,29 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
await PageObjects.dashboard.expectOnDashboard(dashboardTitle);
});
describe('Download report', () => {
// use archived reports to allow reporting_user to view report jobs they've created
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/reporting/archived_reports');
await reportingFunctional.loginReportingUser();
await PageObjects.common.navigateToApp('reporting');
await testSubjects.existOrFail('reportJobListing');
});
after(async () => {
await esArchiver.unload('x-pack/test/functional/es_archives/reporting/archived_reports');
});
it('user can access download link', async () => {
await testSubjects.existOrFail('reportDownloadLink-kraz9db6154g0763b5141viu');
});
it('user can access download link for export type that is no longer supported', async () => {
// The "csv" export type, aka CSV V1, was removed and can no longer be created.
// Downloading a report of this export type does still work
await testSubjects.existOrFail('reportDownloadLink-krb7arhe164k0763b50bjm31');
});
});
});
};