mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Reporting] Convert Export Type Definitions to Typescript (#51643)
* simplify ts * fix generate_png + get_full_url * fix pdf execute job * fix pdf create job * fix decrypt job headers * fix generate pdf / generate png * remove log * export consts * move export type registration to ts * more export type registration to ts * ts generics * remove console.log * use generics * fix ts * fix ts * fix ts * fix ts * Multi-type handling readability fix * Support createJob's jobParams * i18n fixes * track down mysterious field * revisit ts-ignores * remove an any type in get_conditional_headers * ts fixes * typed export treatment for csv_from_savedobject#executeJob * refactor helper function plain bonkers signature * i18n merge fix * add error handling test * todo * fix .headers type def * Reduce number of loc change * remove unused params from generic signatures * Remove as/any * hoist out GenericWorkerFn for naming * remove unnecessary fields from JobDocPayloadPanelCsv * Introduce user defined type guard
This commit is contained in:
parent
c4143b28f4
commit
45ef370e84
45 changed files with 422 additions and 368 deletions
|
@ -4,10 +4,10 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import { cryptoFactory } from '../../../server/lib/crypto';
|
||||
import { createMockServer } from '../../../test_helpers/create_mock_server';
|
||||
import { decryptJobHeaders } from './index';
|
||||
import { Logger } from '../../../types';
|
||||
import { decryptJobHeaders } from './decrypt_job_headers';
|
||||
|
||||
let mockServer: any;
|
||||
beforeEach(() => {
|
||||
|
@ -24,17 +24,16 @@ describe('headers', () => {
|
|||
await expect(
|
||||
decryptJobHeaders({
|
||||
job: {
|
||||
title: 'cool-job-bro',
|
||||
type: 'csv',
|
||||
jobParams: {
|
||||
savedObjectId: 'abc-123',
|
||||
isImmediate: false,
|
||||
savedObjectType: 'search',
|
||||
},
|
||||
headers: 'Q53+9A+zf+Xe+ceR/uB/aR/Sw/8e+M+qR+WiG+8z+EY+mo+HiU/zQL+Xn',
|
||||
},
|
||||
logger: ({
|
||||
error: jest.fn(),
|
||||
} as unknown) as Logger,
|
||||
server: mockServer,
|
||||
})
|
||||
).rejects.toBeDefined();
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Failed to decrypt report job data. Please ensure that xpack.reporting.encryptionKey is set and re-generate this report. Error: Invalid IV length]`
|
||||
);
|
||||
});
|
||||
|
||||
test(`passes back decrypted headers that were passed in`, async () => {
|
||||
|
@ -48,13 +47,9 @@ describe('headers', () => {
|
|||
job: {
|
||||
title: 'cool-job-bro',
|
||||
type: 'csv',
|
||||
jobParams: {
|
||||
savedObjectId: 'abc-123',
|
||||
isImmediate: false,
|
||||
savedObjectType: 'search',
|
||||
},
|
||||
headers: encryptedHeaders,
|
||||
},
|
||||
logger: {} as Logger,
|
||||
server: mockServer,
|
||||
});
|
||||
expect(decryptedHeaders).toEqual(headers);
|
||||
|
|
|
@ -3,18 +3,48 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
// @ts-ignore
|
||||
import { cryptoFactory } from '../../../server/lib/crypto';
|
||||
import { CryptoFactory, JobDocPayload, ServerFacade } from '../../../types';
|
||||
|
||||
export const decryptJobHeaders = async ({
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { cryptoFactory } from '../../../server/lib/crypto';
|
||||
import { CryptoFactory, ServerFacade, Logger } from '../../../types';
|
||||
|
||||
interface HasEncryptedHeaders {
|
||||
headers?: string;
|
||||
}
|
||||
|
||||
// TODO merge functionality with CSV execute job
|
||||
export const decryptJobHeaders = async <
|
||||
JobParamsType,
|
||||
JobDocPayloadType extends HasEncryptedHeaders
|
||||
>({
|
||||
job,
|
||||
server,
|
||||
logger,
|
||||
}: {
|
||||
job: JobDocPayload;
|
||||
job: JobDocPayloadType;
|
||||
server: ServerFacade;
|
||||
}) => {
|
||||
logger: Logger;
|
||||
}): Promise<{
|
||||
job: JobDocPayloadType;
|
||||
server: ServerFacade;
|
||||
decryptedHeaders: Record<string, string>;
|
||||
}> => {
|
||||
const crypto: CryptoFactory = cryptoFactory(server);
|
||||
const decryptedHeaders: string = await crypto.decrypt(job.headers);
|
||||
return { job, decryptedHeaders, server };
|
||||
try {
|
||||
const decryptedHeaders: Record<string, string> = await crypto.decrypt(job.headers);
|
||||
return { job, decryptedHeaders, server };
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
|
||||
throw new Error(
|
||||
i18n.translate(
|
||||
'xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Failed to decrypt report job data. Please ensure that {encryptionKey} is set and re-generate this report. {err}',
|
||||
values: { encryptionKey: 'xpack.reporting.encryptionKey', err: err.toString() },
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@ describe('conditions', () => {
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -45,7 +45,7 @@ describe('conditions', () => {
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -66,7 +66,7 @@ describe('conditions', () => {
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -83,7 +83,7 @@ describe('conditions', () => {
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -98,7 +98,7 @@ describe('conditions', () => {
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -121,7 +121,7 @@ describe('conditions', () => {
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -138,7 +138,7 @@ describe('conditions', () => {
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -154,7 +154,7 @@ test('uses basePath from job when creating saved object service', async () => {
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -181,7 +181,7 @@ test(`uses basePath from server if job doesn't have a basePath when creating sav
|
|||
};
|
||||
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: permittedHeaders,
|
||||
server: mockServer,
|
||||
});
|
||||
|
@ -204,7 +204,7 @@ describe('config formatting', () => {
|
|||
test(`lowercases server.host`, async () => {
|
||||
mockServer = createMockServer({ settings: { 'server.host': 'COOL-HOSTNAME' } });
|
||||
const { conditionalHeaders } = await getConditionalHeaders({
|
||||
job: {} as JobDocPayload,
|
||||
job: {} as JobDocPayload<any>,
|
||||
filteredHeaders: {},
|
||||
server: mockServer,
|
||||
});
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { ConditionalHeaders, JobDocPayload, ServerFacade } from '../../../types';
|
||||
import { ConditionalHeaders, ServerFacade } from '../../../types';
|
||||
|
||||
export const getConditionalHeaders = ({
|
||||
export const getConditionalHeaders = <JobDocPayloadType>({
|
||||
job,
|
||||
filteredHeaders,
|
||||
server,
|
||||
}: {
|
||||
job: JobDocPayload;
|
||||
job: JobDocPayloadType;
|
||||
filteredHeaders: Record<string, string>;
|
||||
server: ServerFacade;
|
||||
}) => {
|
||||
|
|
|
@ -5,10 +5,17 @@
|
|||
*/
|
||||
|
||||
import { createMockServer } from '../../../test_helpers/create_mock_server';
|
||||
import { ServerFacade } from '../../../types';
|
||||
import { JobDocPayloadPNG } from '../../png/types';
|
||||
import { JobDocPayloadPDF } from '../../printable_pdf/types';
|
||||
import { getFullUrls } from './get_full_urls';
|
||||
|
||||
interface FullUrlsOpts {
|
||||
job: JobDocPayloadPNG & JobDocPayloadPDF;
|
||||
server: ServerFacade;
|
||||
conditionalHeaders: any;
|
||||
}
|
||||
|
||||
let mockServer: any;
|
||||
beforeEach(() => {
|
||||
mockServer = createMockServer('');
|
||||
|
@ -17,9 +24,9 @@ beforeEach(() => {
|
|||
test(`fails if no URL is passed`, async () => {
|
||||
await expect(
|
||||
getFullUrls({
|
||||
job: {} as JobDocPayloadPNG,
|
||||
job: {},
|
||||
server: mockServer,
|
||||
})
|
||||
} as FullUrlsOpts)
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: No valid URL fields found in Job Params! Expected \`job.relativeUrl\` or \`job.objects[{ relativeUrl }]\`]`
|
||||
);
|
||||
|
@ -33,9 +40,9 @@ test(`fails if URLs are file-protocols for PNGs`, async () => {
|
|||
job: {
|
||||
relativeUrl,
|
||||
forceNow,
|
||||
} as JobDocPayloadPNG,
|
||||
},
|
||||
server: mockServer,
|
||||
})
|
||||
} as FullUrlsOpts)
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Found invalid URL(s), all URLs must be relative: ${relativeUrl}]`
|
||||
);
|
||||
|
@ -50,9 +57,9 @@ test(`fails if URLs are absolute for PNGs`, async () => {
|
|||
job: {
|
||||
relativeUrl,
|
||||
forceNow,
|
||||
} as JobDocPayloadPNG,
|
||||
},
|
||||
server: mockServer,
|
||||
})
|
||||
} as FullUrlsOpts)
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Found invalid URL(s), all URLs must be relative: ${relativeUrl}]`
|
||||
);
|
||||
|
@ -70,9 +77,9 @@ test(`fails if URLs are file-protocols for PDF`, async () => {
|
|||
},
|
||||
],
|
||||
forceNow,
|
||||
} as JobDocPayloadPDF,
|
||||
},
|
||||
server: mockServer,
|
||||
})
|
||||
} as FullUrlsOpts)
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Found invalid URL(s), all URLs must be relative: ${relativeUrl}]`
|
||||
);
|
||||
|
@ -91,9 +98,9 @@ test(`fails if URLs are absolute for PDF`, async () => {
|
|||
},
|
||||
],
|
||||
forceNow,
|
||||
} as JobDocPayloadPDF,
|
||||
},
|
||||
server: mockServer,
|
||||
})
|
||||
} as FullUrlsOpts)
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Found invalid URL(s), all URLs must be relative: ${relativeUrl}]`
|
||||
);
|
||||
|
@ -118,9 +125,9 @@ test(`fails if any URLs are absolute or file's for PDF`, async () => {
|
|||
job: {
|
||||
objects,
|
||||
forceNow,
|
||||
} as JobDocPayloadPDF,
|
||||
},
|
||||
server: mockServer,
|
||||
})
|
||||
} as FullUrlsOpts)
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Found invalid URL(s), all URLs must be relative: ${objects[1].relativeUrl} ${objects[2].relativeUrl}]`
|
||||
);
|
||||
|
@ -131,9 +138,9 @@ test(`fails if URL does not route to a visualization`, async () => {
|
|||
getFullUrls({
|
||||
job: {
|
||||
relativeUrl: '/app/phoney',
|
||||
} as JobDocPayloadPNG,
|
||||
},
|
||||
server: mockServer,
|
||||
})
|
||||
} as FullUrlsOpts)
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: No valid hash in the URL! A hash is expected for the application to route to the intended visualization.]`
|
||||
);
|
||||
|
@ -145,9 +152,9 @@ test(`adds forceNow to hash's query, if it exists`, async () => {
|
|||
job: {
|
||||
relativeUrl: '/app/kibana#/something',
|
||||
forceNow,
|
||||
} as JobDocPayloadPNG,
|
||||
},
|
||||
server: mockServer,
|
||||
});
|
||||
} as FullUrlsOpts);
|
||||
|
||||
expect(urls[0]).toEqual(
|
||||
'http://localhost:5601/sbp/app/kibana#/something?forceNow=2000-01-01T00%3A00%3A00.000Z'
|
||||
|
@ -161,9 +168,9 @@ test(`appends forceNow to hash's query, if it exists`, async () => {
|
|||
job: {
|
||||
relativeUrl: '/app/kibana#/something?_g=something',
|
||||
forceNow,
|
||||
} as JobDocPayloadPNG,
|
||||
},
|
||||
server: mockServer,
|
||||
});
|
||||
} as FullUrlsOpts);
|
||||
|
||||
expect(urls[0]).toEqual(
|
||||
'http://localhost:5601/sbp/app/kibana#/something?_g=something&forceNow=2000-01-01T00%3A00%3A00.000Z'
|
||||
|
@ -174,9 +181,9 @@ test(`doesn't append forceNow query to url, if it doesn't exists`, async () => {
|
|||
const { urls } = await getFullUrls({
|
||||
job: {
|
||||
relativeUrl: '/app/kibana#/something',
|
||||
} as JobDocPayloadPNG,
|
||||
},
|
||||
server: mockServer,
|
||||
});
|
||||
} as FullUrlsOpts);
|
||||
|
||||
expect(urls[0]).toEqual('http://localhost:5601/sbp/app/kibana#/something');
|
||||
});
|
||||
|
@ -192,9 +199,9 @@ test(`adds forceNow to each of multiple urls`, async () => {
|
|||
{ relativeUrl: '/app/kibana#/something_ddd' },
|
||||
],
|
||||
forceNow,
|
||||
} as JobDocPayloadPDF,
|
||||
},
|
||||
server: mockServer,
|
||||
});
|
||||
} as FullUrlsOpts);
|
||||
|
||||
expect(urls).toEqual([
|
||||
'http://localhost:5601/sbp/app/kibana#/something_aaa?forceNow=2000-01-01T00%3A00%3A00.000Z',
|
||||
|
|
|
@ -12,21 +12,26 @@ import {
|
|||
} from 'url';
|
||||
import { getAbsoluteUrlFactory } from '../../../common/get_absolute_url';
|
||||
import { validateUrls } from '../../../common/validate_urls';
|
||||
import { ServerFacade } from '../../../types';
|
||||
import { ServerFacade, ConditionalHeaders } from '../../../types';
|
||||
import { JobDocPayloadPNG } from '../../png/types';
|
||||
import { JobDocPayloadPDF } from '../../printable_pdf/types';
|
||||
|
||||
interface KeyedRelativeUrl {
|
||||
relativeUrl: string;
|
||||
function isPngJob(job: JobDocPayloadPNG | JobDocPayloadPDF): job is JobDocPayloadPNG {
|
||||
return (job as JobDocPayloadPNG).relativeUrl !== undefined;
|
||||
}
|
||||
function isPdfJob(job: JobDocPayloadPNG | JobDocPayloadPDF): job is JobDocPayloadPDF {
|
||||
return (job as JobDocPayloadPDF).objects !== undefined;
|
||||
}
|
||||
|
||||
export async function getFullUrls({
|
||||
export async function getFullUrls<JobDocPayloadType>({
|
||||
job,
|
||||
server,
|
||||
...mergeValues // pass-throughs
|
||||
}: {
|
||||
job: JobDocPayloadPNG | JobDocPayloadPDF;
|
||||
job: JobDocPayloadPDF | JobDocPayloadPNG;
|
||||
server: ServerFacade;
|
||||
conditionalHeaders: ConditionalHeaders;
|
||||
logo?: string;
|
||||
}) {
|
||||
const config = server.config();
|
||||
|
||||
|
@ -40,12 +45,10 @@ export async function getFullUrls({
|
|||
// PDF and PNG job params put in the url differently
|
||||
let relativeUrls: string[] = [];
|
||||
|
||||
if (job.relativeUrl) {
|
||||
// single page (png)
|
||||
if (isPngJob(job)) {
|
||||
relativeUrls = [job.relativeUrl];
|
||||
} else if (job.objects) {
|
||||
// multi page (pdf)
|
||||
relativeUrls = job.objects.map((obj: KeyedRelativeUrl) => obj.relativeUrl);
|
||||
} else if (isPdfJob(job)) {
|
||||
relativeUrls = job.objects.map(obj => obj.relativeUrl);
|
||||
} else {
|
||||
throw new Error(
|
||||
`No valid URL fields found in Job Params! Expected \`job.relativeUrl\` or \`job.objects[{ relativeUrl }]\``
|
||||
|
@ -93,5 +96,5 @@ export async function getFullUrls({
|
|||
});
|
||||
});
|
||||
|
||||
return { job, urls, server, ...mergeValues };
|
||||
return { job, server, urls, ...mergeValues };
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
*/
|
||||
import { omit } from 'lodash';
|
||||
import { KBN_SCREENSHOT_HEADER_BLACKLIST } from '../../../common/constants';
|
||||
import { JobDocPayload, ServerFacade } from '../../../types';
|
||||
import { ServerFacade } from '../../../types';
|
||||
|
||||
export const omitBlacklistedHeaders = ({
|
||||
export const omitBlacklistedHeaders = <JobDocPayloadType>({
|
||||
job,
|
||||
decryptedHeaders,
|
||||
server,
|
||||
}: {
|
||||
job: JobDocPayload;
|
||||
job: JobDocPayloadType;
|
||||
decryptedHeaders: Record<string, string>;
|
||||
server: ServerFacade;
|
||||
}) => {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ServerFacade, CaptureConfig } from '../../../../types';
|
|||
import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
|
||||
import {
|
||||
ElementsPositionAndAttribute,
|
||||
Screenshot,
|
||||
ScreenshotResults,
|
||||
ScreenshotObservableOpts,
|
||||
TimeRange,
|
||||
} from './types';
|
||||
|
@ -26,11 +26,6 @@ import { getElementPositionAndAttributes } from './get_element_position_data';
|
|||
import { getScreenshots } from './get_screenshots';
|
||||
import { skipTelemetry } from './skip_telemetry';
|
||||
|
||||
interface ScreenshotResults {
|
||||
timeRange: TimeRange;
|
||||
screenshots: Screenshot[];
|
||||
}
|
||||
|
||||
export function screenshotsObservableFactory(server: ServerFacade) {
|
||||
const config = server.config();
|
||||
const captureConfig: CaptureConfig = config.get('xpack.reporting.capture');
|
||||
|
@ -48,7 +43,7 @@ export function screenshotsObservableFactory(server: ServerFacade) {
|
|||
browserTimezone,
|
||||
});
|
||||
|
||||
// @ts-ignore this needs to be refactored to use less random type declaration and instead rely on structures that work with inference
|
||||
// @ts-ignore this needs to be refactored to use less random type declaration and instead rely on structures that work with inference TODO
|
||||
return create$.pipe(
|
||||
mergeMap(({ driver$, exit$ }) => {
|
||||
const screenshot$ = driver$.pipe(
|
||||
|
|
|
@ -30,7 +30,12 @@ export interface ElementsPositionAndAttribute {
|
|||
}
|
||||
|
||||
export interface Screenshot {
|
||||
base64EncodedData: string;
|
||||
base64EncodedData: Buffer;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ScreenshotResults {
|
||||
timeRange: TimeRange;
|
||||
screenshots: Screenshot[];
|
||||
}
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
export const metadata = {
|
||||
id: 'csv',
|
||||
name: 'CSV'
|
||||
name: 'CSV',
|
||||
};
|
|
@ -5,15 +5,23 @@
|
|||
*/
|
||||
|
||||
import { cryptoFactory } from '../../../server/lib/crypto';
|
||||
import { ConditionalHeaders, ServerFacade, RequestFacade } from '../../../types';
|
||||
import {
|
||||
CreateJobFactory,
|
||||
ConditionalHeaders,
|
||||
ServerFacade,
|
||||
RequestFacade,
|
||||
ESQueueCreateJobFn,
|
||||
} from '../../../types';
|
||||
import { JobParamsDiscoverCsv } from '../types';
|
||||
|
||||
export const createJobFactory = function createJobFn(server: ServerFacade) {
|
||||
export const createJobFactory: CreateJobFactory<ESQueueCreateJobFn<
|
||||
JobParamsDiscoverCsv
|
||||
>> = function createJobFactoryFn(server: ServerFacade) {
|
||||
const crypto = cryptoFactory(server);
|
||||
|
||||
return async function createJob(
|
||||
jobParams: JobParamsDiscoverCsv,
|
||||
headers: ConditionalHeaders,
|
||||
headers: ConditionalHeaders['headers'],
|
||||
request: RequestFacade
|
||||
) {
|
||||
const serializedEncryptedHeaders = await crypto.encrypt(headers);
|
||||
|
|
|
@ -4,20 +4,30 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExecuteJobFactory, ESQueueWorkerExecuteFn, ServerFacade } from '../../../types';
|
||||
import { CSV_JOB_TYPE, PLUGIN_ID } from '../../../common/constants';
|
||||
import { cryptoFactory, LevelLogger } from '../../../server/lib';
|
||||
import { JobDocPayloadDiscoverCsv } from '../types';
|
||||
// @ts-ignore untyped module TODO
|
||||
import { createGenerateCsv } from './lib/generate_csv';
|
||||
// @ts-ignore untyped module TODO
|
||||
import { fieldFormatMapFactory } from './lib/field_format_map';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export function executeJobFactory(server) {
|
||||
export const executeJobFactory: ExecuteJobFactory<ESQueueWorkerExecuteFn<
|
||||
JobDocPayloadDiscoverCsv
|
||||
>> = function executeJobFactoryFn(server: ServerFacade) {
|
||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
|
||||
const crypto = cryptoFactory(server);
|
||||
const config = server.config();
|
||||
const logger = LevelLogger.createForServer(server, [PLUGIN_ID, CSV_JOB_TYPE, 'execute-job']);
|
||||
const serverBasePath = config.get('server.basePath');
|
||||
|
||||
return async function executeJob(jobId, job, cancellationToken) {
|
||||
return async function executeJob(
|
||||
jobId: string,
|
||||
job: JobDocPayloadDiscoverCsv,
|
||||
cancellationToken: any
|
||||
) {
|
||||
const jobLogger = logger.clone([jobId]);
|
||||
|
||||
const {
|
||||
|
@ -65,7 +75,7 @@ export function executeJobFactory(server) {
|
|||
},
|
||||
};
|
||||
|
||||
const callEndpoint = (endpoint, clientParams = {}, options = {}) => {
|
||||
const callEndpoint = (endpoint: string, clientParams = {}, options = {}) => {
|
||||
return callWithRequest(fakeRequest, endpoint, clientParams, options);
|
||||
};
|
||||
const savedObjects = server.savedObjects;
|
||||
|
@ -76,6 +86,7 @@ export function executeJobFactory(server) {
|
|||
|
||||
const [formatsMap, uiSettings] = await Promise.all([
|
||||
(async () => {
|
||||
// @ts-ignore fieldFormatServiceFactory' does not exist on type 'ServerFacade TODO
|
||||
const fieldFormats = await server.fieldFormatServiceFactory(uiConfig);
|
||||
return fieldFormatMapFactory(indexPatternSavedObject, fieldFormats);
|
||||
})(),
|
||||
|
@ -125,4 +136,4 @@ export function executeJobFactory(server) {
|
|||
csv_contains_formulas: csvContainsFormulas,
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
|
@ -4,12 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ExportTypesRegistry } from '../../../types';
|
||||
import { createJobFactory } from './create_job';
|
||||
import { executeJobFactory } from './execute_job';
|
||||
import { metadata } from '../metadata';
|
||||
import { CSV_JOB_TYPE as jobType } from '../../../common/constants';
|
||||
|
||||
export function register(registry) {
|
||||
export function register(registry: ExportTypesRegistry) {
|
||||
registry.register({
|
||||
...metadata,
|
||||
jobType,
|
|
@ -84,8 +84,8 @@ describe(`Check CSV Injected values`, () => {
|
|||
checkIfRowsHaveFormulas(
|
||||
{
|
||||
_doc: 'foo-bar',
|
||||
// @ts-ignore need to assert non-string values still return false
|
||||
value: nonRow,
|
||||
// need to assert non-string values still return false
|
||||
value: (nonRow as unknown) as string,
|
||||
title: 'nice',
|
||||
},
|
||||
['_doc', 'value', 'title']
|
||||
|
|
|
@ -15,19 +15,14 @@ export interface JobParamPostPayloadDiscoverCsv extends JobParamPostPayload {
|
|||
|
||||
export interface JobParamsDiscoverCsv {
|
||||
indexPatternId?: string;
|
||||
post?: JobParamPostPayloadDiscoverCsv; // delete this
|
||||
post?: JobParamPostPayloadDiscoverCsv;
|
||||
}
|
||||
|
||||
export interface JobDocPayloadDiscoverCsv extends JobDocPayload {
|
||||
export interface JobDocPayloadDiscoverCsv extends JobDocPayload<JobParamsDiscoverCsv> {
|
||||
basePath: string;
|
||||
searchRequest: any;
|
||||
fields: any;
|
||||
indexPatternSavedObject: any;
|
||||
metaFields: any;
|
||||
conflictedTypesFields: any;
|
||||
}
|
||||
|
||||
export type ESQueueCreateJobFnDiscoverCsv = (
|
||||
jobParams: JobParamsDiscoverCsv,
|
||||
headers: ConditionalHeaders,
|
||||
request: RequestFacade
|
||||
) => Promise<JobParamsDiscoverCsv>;
|
||||
|
|
|
@ -8,7 +8,12 @@ import { notFound, notImplemented } from 'boom';
|
|||
import { get } from 'lodash';
|
||||
import { PLUGIN_ID, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../../common/constants';
|
||||
import { cryptoFactory, LevelLogger } from '../../../../server/lib';
|
||||
import { ImmediateCreateJobFn, ServerFacade, RequestFacade } from '../../../../types';
|
||||
import {
|
||||
CreateJobFactory,
|
||||
ImmediateCreateJobFn,
|
||||
ServerFacade,
|
||||
RequestFacade,
|
||||
} from '../../../../types';
|
||||
import {
|
||||
SavedObject,
|
||||
SavedObjectServiceError,
|
||||
|
@ -27,7 +32,9 @@ interface VisData {
|
|||
panel: SearchPanel;
|
||||
}
|
||||
|
||||
export function createJobFactory(server: ServerFacade): ImmediateCreateJobFn {
|
||||
export const createJobFactory: CreateJobFactory<ImmediateCreateJobFn<
|
||||
JobParamsPanelCsv
|
||||
>> = function createJobFactoryFn(server: ServerFacade) {
|
||||
const crypto = cryptoFactory(server);
|
||||
const logger = LevelLogger.createForServer(server, [
|
||||
PLUGIN_ID,
|
||||
|
@ -88,9 +95,8 @@ export function createJobFactory(server: ServerFacade): ImmediateCreateJobFn {
|
|||
return {
|
||||
headers: serializedEncryptedHeaders,
|
||||
jobParams: { ...jobParams, panel, visType },
|
||||
type: null, // resolved in executeJob
|
||||
objects: null, // resolved in executeJob
|
||||
type: null,
|
||||
title,
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { cryptoFactory, LevelLogger } from '../../../server/lib';
|
||||
import {
|
||||
ExecuteJobFactory,
|
||||
ImmediateExecuteFn,
|
||||
JobDocOutputExecuted,
|
||||
ServerFacade,
|
||||
|
@ -26,7 +27,9 @@ import {
|
|||
} from '../types';
|
||||
import { createGenerateCsv } from './lib';
|
||||
|
||||
export function executeJobFactory(server: ServerFacade): ImmediateExecuteFn {
|
||||
export const executeJobFactory: ExecuteJobFactory<ImmediateExecuteFn<
|
||||
JobParamsPanelCsv
|
||||
>> = function executeJobFactoryFn(server: ServerFacade) {
|
||||
const crypto = cryptoFactory(server);
|
||||
const logger = LevelLogger.createForServer(server, [
|
||||
PLUGIN_ID,
|
||||
|
@ -118,4 +121,4 @@ export function executeJobFactory(server: ServerFacade): ImmediateExecuteFn {
|
|||
size,
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// @ts-ignore no module definition
|
||||
// @ts-ignore no module definition TODO
|
||||
import { createGenerateCsv } from '../../../csv/server/lib/generate_csv';
|
||||
import { CancellationToken } from '../../../../common/cancellation_token';
|
||||
import { ServerFacade, RequestFacade, Logger } from '../../../../types';
|
||||
|
|
|
@ -25,8 +25,7 @@ export interface JobParamsPanelCsv {
|
|||
visType?: string;
|
||||
}
|
||||
|
||||
export interface JobDocPayloadPanelCsv extends JobDocPayload {
|
||||
type: string | null;
|
||||
export interface JobDocPayloadPanelCsv extends JobDocPayload<JobParamsPanelCsv> {
|
||||
jobParams: JobParamsPanelCsv;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
export const metadata = {
|
||||
id: 'png',
|
||||
name: 'PNG'
|
||||
name: 'PNG',
|
||||
};
|
|
@ -4,17 +4,25 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ServerFacade, RequestFacade, ConditionalHeaders } from '../../../../types';
|
||||
import {
|
||||
CreateJobFactory,
|
||||
ServerFacade,
|
||||
RequestFacade,
|
||||
ESQueueCreateJobFn,
|
||||
ConditionalHeaders,
|
||||
} from '../../../../types';
|
||||
import { validateUrls } from '../../../../common/validate_urls';
|
||||
import { cryptoFactory } from '../../../../server/lib/crypto';
|
||||
import { JobParamsPNG } from '../../types';
|
||||
|
||||
export const createJobFactory = function createJobFn(server: ServerFacade) {
|
||||
export const createJobFactory: CreateJobFactory<ESQueueCreateJobFn<
|
||||
JobParamsPNG
|
||||
>> = function createJobFactoryFn(server: ServerFacade) {
|
||||
const crypto = cryptoFactory(server);
|
||||
|
||||
return async function createJob(
|
||||
{ objectType, title, relativeUrl, browserTimezone, layout }: JobParamsPNG,
|
||||
headers: ConditionalHeaders,
|
||||
headers: ConditionalHeaders['headers'],
|
||||
request: RequestFacade
|
||||
) {
|
||||
const serializedEncryptedHeaders = await crypto.encrypt(headers);
|
||||
|
|
|
@ -45,6 +45,7 @@ beforeEach(() => {
|
|||
getScopedSavedObjectsClient: jest.fn(),
|
||||
},
|
||||
uiSettingsServiceFactory: jest.fn().mockReturnValue({ get: jest.fn() }),
|
||||
log: jest.fn(),
|
||||
};
|
||||
|
||||
mockServer.config().get.mockImplementation((key) => {
|
||||
|
|
|
@ -5,39 +5,33 @@
|
|||
*/
|
||||
|
||||
import * as Rx from 'rxjs';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { mergeMap, catchError, map, takeUntil } from 'rxjs/operators';
|
||||
import { PLUGIN_ID, PNG_JOB_TYPE } from '../../../../common/constants';
|
||||
import { ServerFacade, ExecuteJobFactory, ESQueueWorkerExecuteFn } from '../../../../types';
|
||||
import { LevelLogger } from '../../../../server/lib';
|
||||
import { generatePngObservableFactory } from '../lib/generate_png';
|
||||
import {
|
||||
decryptJobHeaders,
|
||||
omitBlacklistedHeaders,
|
||||
getConditionalHeaders,
|
||||
getFullUrls,
|
||||
} from '../../../common/execute_job/';
|
||||
import { JobDocPayloadPNG } from '../../types';
|
||||
import { generatePngObservableFactory } from '../lib/generate_png';
|
||||
|
||||
export function executeJobFactory(server) {
|
||||
export const executeJobFactory: ExecuteJobFactory<ESQueueWorkerExecuteFn<
|
||||
JobDocPayloadPNG
|
||||
>> = function executeJobFactoryFn(server: ServerFacade) {
|
||||
const generatePngObservable = generatePngObservableFactory(server);
|
||||
const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PNG_JOB_TYPE, 'execute']);
|
||||
|
||||
return function executeJob(jobId, jobToExecute, cancellationToken) {
|
||||
return function executeJob(
|
||||
jobId: string,
|
||||
jobToExecute: JobDocPayloadPNG,
|
||||
cancellationToken: any
|
||||
) {
|
||||
const jobLogger = logger.clone([jobId]);
|
||||
const process$ = Rx.of({ job: jobToExecute, server }).pipe(
|
||||
const process$ = Rx.of({ job: jobToExecute, server, logger }).pipe(
|
||||
mergeMap(decryptJobHeaders),
|
||||
catchError(err => {
|
||||
jobLogger.error(err);
|
||||
return Rx.throwError(
|
||||
i18n.translate(
|
||||
'xpack.reporting.exportTypes.png.compShim.failedToDecryptReportJobDataErrorMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Failed to decrypt report job data. Please ensure that {encryptionKey} is set and re-generate this report. {err}',
|
||||
values: { encryptionKey: 'xpack.reporting.encryptionKey', err: err.toString() },
|
||||
}
|
||||
)
|
||||
);
|
||||
}),
|
||||
map(omitBlacklistedHeaders),
|
||||
map(getConditionalHeaders),
|
||||
mergeMap(getFullUrls),
|
||||
|
@ -51,11 +45,13 @@ export function executeJobFactory(server) {
|
|||
job.layout
|
||||
);
|
||||
}),
|
||||
map(buffer => ({
|
||||
content_type: 'image/png',
|
||||
content: buffer.toString('base64'),
|
||||
size: buffer.byteLength,
|
||||
})),
|
||||
map((buffer: Buffer) => {
|
||||
return {
|
||||
content_type: 'image/png',
|
||||
content: buffer.toString('base64'),
|
||||
size: buffer.byteLength,
|
||||
};
|
||||
}),
|
||||
catchError(err => {
|
||||
jobLogger.error(err);
|
||||
return Rx.throwError(err);
|
||||
|
@ -63,7 +59,6 @@ export function executeJobFactory(server) {
|
|||
);
|
||||
|
||||
const stop$ = Rx.fromEventPattern(cancellationToken.on);
|
||||
|
||||
return process$.pipe(takeUntil(stop$)).toPromise();
|
||||
};
|
||||
}
|
||||
};
|
|
@ -4,12 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ExportTypesRegistry } from '../../../types';
|
||||
import { createJobFactory } from './create_job';
|
||||
import { executeJobFactory } from './execute_job';
|
||||
import { metadata } from '../metadata';
|
||||
import { PNG_JOB_TYPE as jobType } from '../../../common/constants';
|
||||
|
||||
export function register(registry) {
|
||||
export function register(registry: ExportTypesRegistry) {
|
||||
registry.register({
|
||||
...metadata,
|
||||
jobType,
|
|
@ -5,40 +5,15 @@
|
|||
*/
|
||||
|
||||
import * as Rx from 'rxjs';
|
||||
import { toArray, mergeMap } from 'rxjs/operators';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { LevelLogger } from '../../../../server/lib';
|
||||
import { ServerFacade, ConditionalHeaders } from '../../../../types';
|
||||
import { screenshotsObservableFactory } from '../../../common/lib/screenshots';
|
||||
import { PreserveLayout } from '../../../common/layouts/preserve_layout';
|
||||
import { LayoutParams } from '../../../common/layouts/layout';
|
||||
|
||||
interface ScreenshotData {
|
||||
base64EncodedData: string;
|
||||
}
|
||||
|
||||
interface UrlScreenshot {
|
||||
screenshots: ScreenshotData[];
|
||||
}
|
||||
|
||||
export function generatePngObservableFactory(server: ServerFacade) {
|
||||
const screenshotsObservable = screenshotsObservableFactory(server);
|
||||
const captureConcurrency = 1;
|
||||
|
||||
// prettier-ignore
|
||||
const createPngWithScreenshots = async ({ urlScreenshots }: { urlScreenshots: UrlScreenshot[] }): Promise<string> => {
|
||||
if (urlScreenshots.length !== 1) {
|
||||
throw new Error(
|
||||
`Expected there to be 1 URL screenshot, but there are ${urlScreenshots.length}`
|
||||
);
|
||||
}
|
||||
if (urlScreenshots[0].screenshots.length !== 1) {
|
||||
throw new Error(
|
||||
`Expected there to be 1 screenshot, but there are ${urlScreenshots[0].screenshots.length}`
|
||||
);
|
||||
}
|
||||
|
||||
return urlScreenshots[0].screenshots[0].base64EncodedData;
|
||||
};
|
||||
|
||||
return function generatePngObservable(
|
||||
logger: LevelLogger,
|
||||
|
@ -46,24 +21,30 @@ export function generatePngObservableFactory(server: ServerFacade) {
|
|||
browserTimezone: string,
|
||||
conditionalHeaders: ConditionalHeaders,
|
||||
layoutParams: LayoutParams
|
||||
): Rx.Observable<string> {
|
||||
): Rx.Observable<Buffer> {
|
||||
if (!layoutParams || !layoutParams.dimensions) {
|
||||
throw new Error(`LayoutParams.Dimensions is undefined.`);
|
||||
}
|
||||
|
||||
const layout = new PreserveLayout(layoutParams.dimensions);
|
||||
const screenshots$ = Rx.of(url).pipe(
|
||||
mergeMap(
|
||||
iUrl =>
|
||||
screenshotsObservable({ logger, url: iUrl, conditionalHeaders, layout, browserTimezone }),
|
||||
(jUrl: string, screenshot: UrlScreenshot) => screenshot,
|
||||
captureConcurrency
|
||||
)
|
||||
const screenshots$ = screenshotsObservable({
|
||||
logger,
|
||||
url,
|
||||
conditionalHeaders,
|
||||
layout,
|
||||
browserTimezone,
|
||||
}).pipe(
|
||||
map(urlScreenshots => {
|
||||
if (urlScreenshots.screenshots.length !== 1) {
|
||||
throw new Error(
|
||||
`Expected there to be 1 screenshot, but there are ${urlScreenshots.screenshots.length}`
|
||||
);
|
||||
}
|
||||
|
||||
return urlScreenshots.screenshots[0].base64EncodedData;
|
||||
})
|
||||
);
|
||||
|
||||
return screenshots$.pipe(
|
||||
toArray(),
|
||||
mergeMap(urlScreenshots => createPngWithScreenshots({ urlScreenshots }))
|
||||
);
|
||||
return screenshots$;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { LayoutInstance } from '../common/layouts/layout';
|
||||
import { LayoutInstance, LayoutParams } from '../common/layouts/layout';
|
||||
import { ConditionalHeaders, JobDocPayload, RequestFacade } from '../../types';
|
||||
|
||||
// Job params: structure of incoming user request data
|
||||
|
@ -17,17 +17,10 @@ export interface JobParamsPNG {
|
|||
}
|
||||
|
||||
// Job payload: structure of stored job data provided by create_job
|
||||
export interface JobDocPayloadPNG extends JobDocPayload {
|
||||
export interface JobDocPayloadPNG extends JobDocPayload<JobParamsPNG> {
|
||||
basePath?: string;
|
||||
browserTimezone: string;
|
||||
forceNow?: string;
|
||||
layout: LayoutInstance;
|
||||
layout: LayoutParams;
|
||||
relativeUrl: string;
|
||||
objects: undefined;
|
||||
}
|
||||
|
||||
export type ESQueueCreateJobFnPNG = (
|
||||
jobParams: JobParamsPNG,
|
||||
headers: ConditionalHeaders,
|
||||
request: RequestFacade
|
||||
) => Promise<JobParamsPNG>;
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
export const metadata = {
|
||||
id: 'printablePdf',
|
||||
name: 'PDF'
|
||||
name: 'PDF',
|
||||
};
|
|
@ -5,20 +5,39 @@
|
|||
*/
|
||||
|
||||
import { PLUGIN_ID, PDF_JOB_TYPE } from '../../../../common/constants';
|
||||
import {
|
||||
CreateJobFactory,
|
||||
ESQueueCreateJobFn,
|
||||
ServerFacade,
|
||||
RequestFacade,
|
||||
ConditionalHeaders,
|
||||
} from '../../../../types';
|
||||
import { validateUrls } from '../../../../common/validate_urls';
|
||||
import { LevelLogger } from '../../../../server/lib';
|
||||
import { cryptoFactory } from '../../../../server/lib/crypto';
|
||||
import { JobParamsPDF } from '../../types';
|
||||
// @ts-ignore untyped module
|
||||
import { compatibilityShimFactory } from './compatibility_shim';
|
||||
|
||||
export function createJobFactory(server) {
|
||||
interface CreateJobFnOpts {
|
||||
objectType: any;
|
||||
title: string;
|
||||
relativeUrls: string[];
|
||||
browserTimezone: string;
|
||||
layout: any;
|
||||
}
|
||||
|
||||
export const createJobFactory: CreateJobFactory<ESQueueCreateJobFn<
|
||||
JobParamsPDF
|
||||
>> = function createJobFactoryFn(server: ServerFacade) {
|
||||
const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PDF_JOB_TYPE, 'create']);
|
||||
const compatibilityShim = compatibilityShimFactory(server, logger);
|
||||
const crypto = cryptoFactory(server);
|
||||
|
||||
return compatibilityShim(async function createJobFn(
|
||||
{ objectType, title, relativeUrls, browserTimezone, layout },
|
||||
headers,
|
||||
request
|
||||
{ objectType, title, relativeUrls, browserTimezone, layout }: CreateJobFnOpts,
|
||||
headers: ConditionalHeaders['headers'],
|
||||
request: RequestFacade
|
||||
) {
|
||||
const serializedEncryptedHeaders = await crypto.encrypt(headers);
|
||||
|
||||
|
@ -35,4 +54,4 @@ export function createJobFactory(server) {
|
|||
forceNow: new Date().toISOString(),
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
import * as Rx from 'rxjs';
|
||||
import { mergeMap, catchError, map, takeUntil } from 'rxjs/operators';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExecuteJobFactory, ESQueueWorkerExecuteFn, ServerFacade } from '../../../../types';
|
||||
import { JobDocPayloadPDF } from '../../types';
|
||||
import { PLUGIN_ID, PDF_JOB_TYPE } from '../../../../common/constants';
|
||||
import { LevelLogger } from '../../../../server/lib';
|
||||
import { generatePdfObservableFactory } from '../lib/generate_pdf';
|
||||
|
@ -18,44 +19,40 @@ import {
|
|||
getCustomLogo,
|
||||
} from '../../../common/execute_job/';
|
||||
|
||||
export function executeJobFactory(server) {
|
||||
export const executeJobFactory: ExecuteJobFactory<ESQueueWorkerExecuteFn<
|
||||
JobDocPayloadPDF
|
||||
>> = function executeJobFactoryFn(server: ServerFacade) {
|
||||
const generatePdfObservable = generatePdfObservableFactory(server);
|
||||
const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PDF_JOB_TYPE, 'execute']);
|
||||
|
||||
return function executeJob(jobId, jobToExecute, cancellationToken) {
|
||||
return function executeJob(
|
||||
jobId: string,
|
||||
jobToExecute: JobDocPayloadPDF,
|
||||
cancellationToken: any
|
||||
) {
|
||||
const jobLogger = logger.clone([jobId]);
|
||||
|
||||
const process$ = Rx.of({ job: jobToExecute, server }).pipe(
|
||||
const process$ = Rx.of({ job: jobToExecute, server, logger }).pipe(
|
||||
mergeMap(decryptJobHeaders),
|
||||
catchError(err => {
|
||||
jobLogger.error(err);
|
||||
return Rx.throwError(
|
||||
i18n.translate(
|
||||
'xpack.reporting.exportTypes.printablePdf.compShim.failedToDecryptReportJobDataErrorMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Failed to decrypt report job data. Please ensure that {encryptionKey} is set and re-generate this report. {err}',
|
||||
values: { encryptionKey: 'xpack.reporting.encryptionKey', err: err.toString() },
|
||||
}
|
||||
)
|
||||
);
|
||||
}),
|
||||
map(omitBlacklistedHeaders),
|
||||
map(getConditionalHeaders),
|
||||
mergeMap(getCustomLogo),
|
||||
mergeMap(getFullUrls),
|
||||
mergeMap(({ job, conditionalHeaders, logo, urls }) => {
|
||||
return generatePdfObservable(
|
||||
jobLogger,
|
||||
job.title,
|
||||
urls,
|
||||
job.browserTimezone,
|
||||
conditionalHeaders,
|
||||
job.layout,
|
||||
logo
|
||||
);
|
||||
}),
|
||||
map(buffer => ({
|
||||
mergeMap(
|
||||
({ job, conditionalHeaders, logo, urls }): Rx.Observable<Buffer> => {
|
||||
const { browserTimezone, layout } = jobToExecute;
|
||||
return generatePdfObservable(
|
||||
jobLogger,
|
||||
job.title,
|
||||
urls,
|
||||
browserTimezone,
|
||||
conditionalHeaders,
|
||||
layout,
|
||||
logo
|
||||
);
|
||||
}
|
||||
),
|
||||
map((buffer: Buffer) => ({
|
||||
content_type: 'application/pdf',
|
||||
content: buffer.toString('base64'),
|
||||
size: buffer.byteLength,
|
||||
|
@ -70,4 +67,4 @@ export function executeJobFactory(server) {
|
|||
|
||||
return process$.pipe(takeUntil(stop$)).toPromise();
|
||||
};
|
||||
}
|
||||
};
|
|
@ -4,12 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ExportTypesRegistry } from '../../../types';
|
||||
import { createJobFactory } from './create_job';
|
||||
import { executeJobFactory } from './execute_job';
|
||||
import { metadata } from '../metadata';
|
||||
import { PDF_JOB_TYPE as jobType } from '../../../common/constants';
|
||||
|
||||
export function register(registry) {
|
||||
export function register(registry: ExportTypesRegistry) {
|
||||
registry.register({
|
||||
...metadata,
|
||||
jobType,
|
|
@ -13,21 +13,10 @@ import { ServerFacade, ConditionalHeaders } from '../../../../types';
|
|||
import { pdf } from './pdf';
|
||||
import { screenshotsObservableFactory } from '../../../common/lib/screenshots';
|
||||
import { createLayout } from '../../../common/layouts';
|
||||
import { TimeRange } from '../../../common/lib/screenshots/types';
|
||||
import { ScreenshotResults } from '../../../common/lib/screenshots/types';
|
||||
import { LayoutInstance, LayoutParams } from '../../../common/layouts/layout';
|
||||
|
||||
interface ScreenshotData {
|
||||
base64EncodedData: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface UrlScreenshot {
|
||||
screenshots: ScreenshotData[];
|
||||
timeRange: TimeRange;
|
||||
}
|
||||
|
||||
const getTimeRange = (urlScreenshots: UrlScreenshot[]) => {
|
||||
const getTimeRange = (urlScreenshots: ScreenshotResults[]) => {
|
||||
const grouped = groupBy(urlScreenshots.map(u => u.timeRange));
|
||||
const values = Object.values(grouped);
|
||||
if (values.length === 1) {
|
||||
|
@ -48,20 +37,16 @@ export function generatePdfObservableFactory(server: ServerFacade) {
|
|||
browserTimezone: string,
|
||||
conditionalHeaders: ConditionalHeaders,
|
||||
layoutParams: LayoutParams,
|
||||
logo: string
|
||||
logo?: string
|
||||
) {
|
||||
const layout = createLayout(server, layoutParams) as LayoutInstance;
|
||||
|
||||
const screenshots$ = Rx.from(urls).pipe(
|
||||
mergeMap(
|
||||
url => screenshotsObservable({ logger, url, conditionalHeaders, layout, browserTimezone }),
|
||||
captureConcurrency
|
||||
)
|
||||
);
|
||||
|
||||
return screenshots$.pipe(
|
||||
),
|
||||
toArray(),
|
||||
mergeMap(async (urlScreenshots: UrlScreenshot[]) => {
|
||||
mergeMap(async (urlScreenshots: ScreenshotResults[]) => {
|
||||
const pdfOutput = pdf.create(layout, logo);
|
||||
|
||||
if (title) {
|
||||
|
@ -84,5 +69,7 @@ export function generatePdfObservableFactory(server: ServerFacade) {
|
|||
return buffer;
|
||||
})
|
||||
);
|
||||
|
||||
return screenshots$;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { LayoutInstance } from '../common/layouts/layout';
|
||||
import { ConditionalHeaders, JobDocPayload, ServerFacade, RequestFacade } from '../../types';
|
||||
import { LayoutInstance, LayoutParams } from '../common/layouts/layout';
|
||||
import { JobDocPayload, ServerFacade, RequestFacade } from '../../types';
|
||||
|
||||
// Job params: structure of incoming user request data, after being parsed from RISON
|
||||
export interface JobParamsPDF {
|
||||
|
@ -17,21 +17,12 @@ export interface JobParamsPDF {
|
|||
}
|
||||
|
||||
// Job payload: structure of stored job data provided by create_job
|
||||
export interface JobDocPayloadPDF extends JobDocPayload {
|
||||
export interface JobDocPayloadPDF extends JobDocPayload<JobParamsPDF> {
|
||||
basePath?: string;
|
||||
browserTimezone: string;
|
||||
forceNow?: string;
|
||||
layout: any;
|
||||
layout: LayoutParams;
|
||||
objects: Array<{
|
||||
relativeUrl: string;
|
||||
}>;
|
||||
relativeUrl: undefined;
|
||||
}
|
||||
|
||||
export type ESQueueCreateJobFnPDF = (
|
||||
jobParams: JobParamsPDF,
|
||||
headers: ConditionalHeaders,
|
||||
request: RequestFacade
|
||||
) => Promise<JobParamsPDF>;
|
||||
|
||||
export type CreateJobFactoryPDF = (server: ServerFacade) => ESQueueCreateJobFnPDF;
|
||||
|
|
|
@ -11,9 +11,8 @@ import {
|
|||
QueueConfig,
|
||||
ExportTypeDefinition,
|
||||
ESQueueWorkerExecuteFn,
|
||||
ImmediateExecuteFn,
|
||||
JobDoc,
|
||||
JobDocPayload,
|
||||
ImmediateExecuteFn,
|
||||
JobSource,
|
||||
RequestFacade,
|
||||
ServerFacade,
|
||||
|
@ -22,7 +21,8 @@ import {
|
|||
import { events as esqueueEvents } from './esqueue';
|
||||
import { LevelLogger } from './level_logger';
|
||||
|
||||
export function createWorkerFactory(server: ServerFacade) {
|
||||
export function createWorkerFactory<JobParamsType>(server: ServerFacade) {
|
||||
type JobDocPayloadType = JobDocPayload<JobParamsType>;
|
||||
const config = server.config();
|
||||
const logger = LevelLogger.createForServer(server, [PLUGIN_ID, 'queue-worker']);
|
||||
const queueConfig: QueueConfig = config.get('xpack.reporting.queue');
|
||||
|
@ -31,34 +31,47 @@ export function createWorkerFactory(server: ServerFacade) {
|
|||
const { exportTypesRegistry } = server.plugins.reporting!;
|
||||
|
||||
// Once more document types are added, this will need to be passed in
|
||||
return function createWorker(queue: ESQueueInstance) {
|
||||
return function createWorker(queue: ESQueueInstance<JobParamsType, JobDocPayloadType>) {
|
||||
// export type / execute job map
|
||||
const jobExecutors: Map<string, ESQueueWorkerExecuteFn | ImmediateExecuteFn> = new Map();
|
||||
const jobExecutors: Map<
|
||||
string,
|
||||
ImmediateExecuteFn<JobParamsType> | ESQueueWorkerExecuteFn<JobDocPayloadType>
|
||||
> = new Map();
|
||||
|
||||
for (const exportType of exportTypesRegistry.getAll() as ExportTypeDefinition[]) {
|
||||
for (const exportType of exportTypesRegistry.getAll() as Array<
|
||||
ExportTypeDefinition<JobParamsType, any, any, any>
|
||||
>) {
|
||||
const executeJobFactory = exportType.executeJobFactory(server);
|
||||
jobExecutors.set(exportType.jobType, executeJobFactory);
|
||||
}
|
||||
|
||||
const workerFn = (
|
||||
job: JobSource,
|
||||
arg1: JobDocPayload | JobDoc,
|
||||
arg2: CancellationToken | RequestFacade | undefined
|
||||
) => {
|
||||
const workerFn = (jobSource: JobSource<JobParamsType>, ...workerRestArgs: any[]) => {
|
||||
const {
|
||||
_id: jobId,
|
||||
_source: { jobtype: jobType },
|
||||
} = jobSource;
|
||||
|
||||
const jobTypeExecutor = jobExecutors.get(jobType);
|
||||
// pass the work to the jobExecutor
|
||||
if (!jobExecutors.get(job._source.jobtype)) {
|
||||
throw new Error(`Unable to find a job executor for the claimed job: [${job._id}]`);
|
||||
if (!jobTypeExecutor) {
|
||||
throw new Error(`Unable to find a job executor for the claimed job: [${jobId}]`);
|
||||
}
|
||||
// job executor function signature is different depending on whether it
|
||||
// is ESQueueWorkerExecuteFn or ImmediateExecuteFn
|
||||
if (job._id) {
|
||||
const jobExecutor = jobExecutors.get(job._source.jobtype) as ESQueueWorkerExecuteFn;
|
||||
return jobExecutor(job._id, arg1 as JobDoc, arg2 as CancellationToken);
|
||||
|
||||
if (jobId) {
|
||||
const jobExecutorWorker = jobTypeExecutor as ESQueueWorkerExecuteFn<JobDocPayloadType>;
|
||||
return jobExecutorWorker(
|
||||
jobId,
|
||||
...(workerRestArgs as [JobDocPayloadType, CancellationToken])
|
||||
);
|
||||
} else {
|
||||
const jobExecutor = jobExecutors.get(job._source.jobtype) as ImmediateExecuteFn;
|
||||
return jobExecutor(null, arg1 as JobDocPayload, arg2 as RequestFacade);
|
||||
const jobExecutorImmediate = jobExecutors.get(jobType) as ImmediateExecuteFn<JobParamsType>;
|
||||
return jobExecutorImmediate(
|
||||
null,
|
||||
...(workerRestArgs as [JobDocPayload<JobParamsType>, RequestFacade])
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const workerOptions = {
|
||||
kibanaName,
|
||||
kibanaId,
|
||||
|
|
|
@ -8,6 +8,8 @@ import { get } from 'lodash';
|
|||
// @ts-ignore
|
||||
import { events as esqueueEvents } from './esqueue';
|
||||
import {
|
||||
ESQueueCreateJobFn,
|
||||
ImmediateCreateJobFn,
|
||||
Job,
|
||||
ServerFacade,
|
||||
RequestFacade,
|
||||
|
@ -32,17 +34,19 @@ export function enqueueJobFactory(server: ServerFacade) {
|
|||
const queueConfig: QueueConfig = config.get('xpack.reporting.queue');
|
||||
const { exportTypesRegistry, queue: jobQueue } = server.plugins.reporting!;
|
||||
|
||||
return async function enqueueJob(
|
||||
return async function enqueueJob<JobParamsType>(
|
||||
parentLogger: Logger,
|
||||
exportTypeId: string,
|
||||
jobParams: object,
|
||||
jobParams: JobParamsType,
|
||||
user: string,
|
||||
headers: ConditionalHeaders['headers'],
|
||||
request: RequestFacade
|
||||
): Promise<Job> {
|
||||
type CreateJobFn = ESQueueCreateJobFn<JobParamsType> | ImmediateCreateJobFn<JobParamsType>;
|
||||
|
||||
const logger = parentLogger.clone(['queue-job']);
|
||||
const exportType = exportTypesRegistry.getById(exportTypeId);
|
||||
const createJob = exportType.createJobFactory(server);
|
||||
const createJob = exportType.createJobFactory(server) as CreateJobFn;
|
||||
const payload = await createJob(jobParams, headers, request);
|
||||
|
||||
const options = {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ServerFacade } from '../../types';
|
|||
import { PLUGIN_ID } from '../../common/constants';
|
||||
import { oncePerServer } from './once_per_server';
|
||||
import { LevelLogger } from './level_logger';
|
||||
// @ts-ignore untype module
|
||||
// @ts-ignore untype module TODO
|
||||
import { ExportTypesRegistry } from '../../common/export_types_registry';
|
||||
|
||||
function scan(pattern: string) {
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// @ts-ignore untyped module
|
||||
export { exportTypesRegistryFactory } from './export_types_registry';
|
||||
// @ts-ignore untyped module
|
||||
export { checkLicenseFactory } from './check_license';
|
||||
|
||||
export { LevelLogger } from './level_logger';
|
||||
export { createQueueFactory } from './create_queue';
|
||||
export { cryptoFactory } from './crypto';
|
||||
|
|
|
@ -38,7 +38,7 @@ export function registerGenerateCsvFromSavedObject(
|
|||
* 3. Ensure that details for a queued job were returned
|
||||
*/
|
||||
|
||||
let result: QueuedJobPayload;
|
||||
let result: QueuedJobPayload<any>;
|
||||
try {
|
||||
const jobParams = getJobParamsFromRequest(request, { isImmediate: false });
|
||||
result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, request, h);
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
ResponseFacade,
|
||||
ReportingResponseToolkit,
|
||||
Logger,
|
||||
JobIDForImmediate,
|
||||
JobDocOutputExecuted,
|
||||
} from '../../types';
|
||||
import { JobDocPayloadPanelCsv } from '../../export_types/csv_from_savedobject/types';
|
||||
|
@ -57,11 +56,7 @@ export function registerGenerateCsvFromSavedObjectImmediate(
|
|||
content_type: jobOutputContentType,
|
||||
content: jobOutputContent,
|
||||
size: jobOutputSize,
|
||||
}: JobDocOutputExecuted = await executeJobFn(
|
||||
null as JobIDForImmediate,
|
||||
jobDocPayload,
|
||||
request
|
||||
);
|
||||
}: JobDocOutputExecuted = await executeJobFn(null, jobDocPayload, request);
|
||||
|
||||
logger.info(`Job output size: ${jobOutputSize} bytes`);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import { registerLegacy } from './legacy';
|
|||
export function registerRoutes(server: ServerFacade, logger: Logger) {
|
||||
const config = server.config();
|
||||
const DOWNLOAD_BASE_URL = config.get('server.basePath') + `${API_BASE_URL}/jobs/download`;
|
||||
// @ts-ignore
|
||||
// @ts-ignore TODO
|
||||
const { errors: esErrors } = server.plugins.elasticsearch.getCluster('admin');
|
||||
const enqueueJob = enqueueJobFactory(server);
|
||||
|
||||
|
@ -30,7 +30,6 @@ export function registerRoutes(server: ServerFacade, logger: Logger) {
|
|||
request: RequestFacade,
|
||||
h: ReportingResponseToolkit
|
||||
) {
|
||||
// @ts-ignore
|
||||
const user = request.pre.user;
|
||||
const headers = request.headers;
|
||||
|
||||
|
|
|
@ -6,7 +6,13 @@
|
|||
|
||||
import boom from 'boom';
|
||||
import { API_BASE_URL } from '../../common/constants';
|
||||
import { JobDoc, ServerFacade, RequestFacade, ReportingResponseToolkit } from '../../types';
|
||||
import {
|
||||
ServerFacade,
|
||||
RequestFacade,
|
||||
ReportingResponseToolkit,
|
||||
JobDocOutput,
|
||||
JobSource,
|
||||
} from '../../types';
|
||||
// @ts-ignore
|
||||
import { jobsQueryFactory } from '../lib/jobs_query';
|
||||
// @ts-ignore
|
||||
|
@ -65,8 +71,7 @@ export function registerJobs(server: ServerFacade) {
|
|||
const { docId } = request.params;
|
||||
|
||||
return jobsQuery.get(request.pre.user, docId, { includeContent: true }).then(
|
||||
(doc: any): JobDoc => {
|
||||
const job = doc._source;
|
||||
({ _source: job }: JobSource<any>): JobDocOutput => {
|
||||
if (!job) {
|
||||
throw boom.notFound();
|
||||
}
|
||||
|
@ -90,9 +95,9 @@ export function registerJobs(server: ServerFacade) {
|
|||
handler: (request: RequestFacade) => {
|
||||
const { docId } = request.params;
|
||||
|
||||
return jobsQuery.get(request.pre.user, docId).then(
|
||||
(doc: any): JobDoc => {
|
||||
const job: JobDoc = doc._source;
|
||||
return jobsQuery
|
||||
.get(request.pre.user, docId)
|
||||
.then(({ _source: job }: JobSource<any>): JobSource<any>['_source'] => {
|
||||
if (!job) {
|
||||
throw boom.notFound();
|
||||
}
|
||||
|
@ -103,14 +108,13 @@ export function registerJobs(server: ServerFacade) {
|
|||
}
|
||||
|
||||
return {
|
||||
...doc._source,
|
||||
...job,
|
||||
payload: {
|
||||
...payload,
|
||||
headers: undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -21,10 +21,12 @@ interface ICustomHeaders {
|
|||
|
||||
const DEFAULT_TITLE = 'report';
|
||||
|
||||
const getTitle = (exportType: ExportTypeDefinition, title?: string): string =>
|
||||
type ExportTypeType = ExportTypeDefinition<any, any, any, any>;
|
||||
|
||||
const getTitle = (exportType: ExportTypeType, title?: string): string =>
|
||||
`${title || DEFAULT_TITLE}.${exportType.jobContentExtension}`;
|
||||
|
||||
const getReportingHeaders = (output: JobDocOutputExecuted, exportType: ExportTypeDefinition) => {
|
||||
const getReportingHeaders = (output: JobDocOutputExecuted, exportType: ExportTypeType) => {
|
||||
const metaDataHeaders: ICustomHeaders = {};
|
||||
|
||||
if (exportType.jobType === CSV_JOB_TYPE) {
|
||||
|
@ -41,7 +43,7 @@ const getReportingHeaders = (output: JobDocOutputExecuted, exportType: ExportTyp
|
|||
export function getDocumentPayloadFactory(server: ServerFacade) {
|
||||
const exportTypesRegistry = server.plugins.reporting!.exportTypesRegistry;
|
||||
|
||||
function encodeContent(content: string | null, exportType: ExportTypeDefinition) {
|
||||
function encodeContent(content: string | null, exportType: ExportTypeType) {
|
||||
switch (exportType.jobContentEncoding) {
|
||||
case 'base64':
|
||||
return content ? Buffer.from(content, 'base64') : content; // Buffer.from rejects null
|
||||
|
@ -51,9 +53,7 @@ export function getDocumentPayloadFactory(server: ServerFacade) {
|
|||
}
|
||||
|
||||
function getCompleted(output: JobDocOutputExecuted, jobType: string, title: string) {
|
||||
const exportType = exportTypesRegistry.get(
|
||||
(item: ExportTypeDefinition) => item.jobType === jobType
|
||||
);
|
||||
const exportType = exportTypesRegistry.get((item: ExportTypeType) => item.jobType === jobType);
|
||||
const filename = getTitle(exportType, title);
|
||||
const headers = getReportingHeaders(output, exportType);
|
||||
|
||||
|
@ -88,9 +88,11 @@ export function getDocumentPayloadFactory(server: ServerFacade) {
|
|||
};
|
||||
}
|
||||
|
||||
return function getDocumentPayload(doc: { _source: JobDocExecuted }) {
|
||||
return function getDocumentPayload(doc: {
|
||||
_source: JobDocExecuted<{ output: JobDocOutputExecuted }>;
|
||||
}) {
|
||||
const { status, jobtype: jobType, payload: { title } = { title: '' } } = doc._source;
|
||||
const { output } = doc._source as { output: JobDocOutputExecuted };
|
||||
const { output } = doc._source;
|
||||
|
||||
if (status === 'completed') {
|
||||
return getCompleted(output, jobType, title);
|
||||
|
|
|
@ -15,11 +15,11 @@ export type HandlerFunction = (
|
|||
|
||||
export type HandlerErrorFunction = (exportType: string, err: Error) => any;
|
||||
|
||||
export interface QueuedJobPayload {
|
||||
export interface QueuedJobPayload<JobParamsType> {
|
||||
error?: boolean;
|
||||
source: {
|
||||
job: {
|
||||
payload: JobDocPayload;
|
||||
payload: JobDocPayload<JobParamsType>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
115
x-pack/legacy/plugins/reporting/types.d.ts
vendored
115
x-pack/legacy/plugins/reporting/types.d.ts
vendored
|
@ -25,13 +25,15 @@ export type Job = EventEmitter & {
|
|||
|
||||
export interface ReportingPlugin {
|
||||
queue: {
|
||||
addJob: (type: string, payload: object, options: object) => Job;
|
||||
addJob: <PayloadType>(type: string, payload: PayloadType, options: object) => Job;
|
||||
};
|
||||
// TODO: convert exportTypesRegistry to TS
|
||||
exportTypesRegistry: {
|
||||
getById: (id: string) => ExportTypeDefinition;
|
||||
getAll: () => ExportTypeDefinition[];
|
||||
get: (callback: (item: ExportTypeDefinition) => boolean) => ExportTypeDefinition;
|
||||
getById: <T, U, V, W>(id: string) => ExportTypeDefinition<T, U, V, W>;
|
||||
getAll: <T, U, V, W>() => Array<ExportTypeDefinition<T, U, V, W>>;
|
||||
get: <T, U, V, W>(
|
||||
callback: (item: ExportTypeDefinition<T, U, V, W>) => boolean
|
||||
) => ExportTypeDefinition<T, U, V, W>;
|
||||
};
|
||||
browserDriverFactory: HeadlessChromiumDriverFactory;
|
||||
}
|
||||
|
@ -182,7 +184,7 @@ export interface ConditionalHeadersConditions {
|
|||
}
|
||||
|
||||
export interface CryptoFactory {
|
||||
decrypt: (headers?: Record<string, string>) => string;
|
||||
decrypt: (headers?: string) => any;
|
||||
}
|
||||
|
||||
export interface TimeRangeParams {
|
||||
|
@ -196,17 +198,11 @@ export interface JobParamPostPayload {
|
|||
timerange: TimeRangeParams;
|
||||
}
|
||||
|
||||
export interface JobDocPayload {
|
||||
headers?: Record<string, string>;
|
||||
jobParams: any;
|
||||
export interface JobDocPayload<JobParamsType> {
|
||||
headers?: string; // serialized encrypted headers
|
||||
jobParams: JobParamsType;
|
||||
title: string;
|
||||
type: string | null;
|
||||
objects?: null | object[];
|
||||
}
|
||||
|
||||
export interface JobSource {
|
||||
_id: string;
|
||||
_source: JobDoc;
|
||||
}
|
||||
|
||||
export interface JobDocOutput {
|
||||
|
@ -214,18 +210,21 @@ export interface JobDocOutput {
|
|||
contentType: string;
|
||||
}
|
||||
|
||||
export interface JobDoc {
|
||||
export interface JobDocExecuted<JobParamsType> {
|
||||
jobtype: string;
|
||||
output: JobDocOutput;
|
||||
payload: JobDocPayload;
|
||||
output: JobDocOutputExecuted;
|
||||
payload: JobDocPayload<JobParamsType>;
|
||||
status: string; // completed, failed, etc
|
||||
}
|
||||
|
||||
export interface JobDocExecuted {
|
||||
jobtype: string;
|
||||
output: JobDocOutputExecuted;
|
||||
payload: JobDocPayload;
|
||||
status: string; // completed, failed, etc
|
||||
export interface JobSource<JobParamsType> {
|
||||
_id: string;
|
||||
_source: {
|
||||
jobtype: string;
|
||||
output: JobDocOutput;
|
||||
payload: JobDocPayload<JobParamsType>;
|
||||
status: string; // completed, failed, etc
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -251,41 +250,35 @@ export interface ESQueueWorker {
|
|||
on: (event: string, handler: any) => void;
|
||||
}
|
||||
|
||||
type JobParamsUrl = object;
|
||||
|
||||
interface JobParamsSavedObject {
|
||||
savedObjectType: string;
|
||||
savedObjectId: string;
|
||||
isImmediate: boolean;
|
||||
}
|
||||
|
||||
export type ESQueueCreateJobFn = (
|
||||
jobParams: JobParamsSavedObject | JobParamsUrl,
|
||||
export type ESQueueCreateJobFn<JobParamsType> = (
|
||||
jobParams: JobParamsType,
|
||||
headers: Record<string, string>,
|
||||
request: RequestFacade
|
||||
) => Promise<JobParamsSavedObject | JobParamsUrl>;
|
||||
) => Promise<JobParamsType>;
|
||||
|
||||
export type ImmediateCreateJobFn = (
|
||||
jobParams: any,
|
||||
export type ImmediateCreateJobFn<JobParamsType> = (
|
||||
jobParams: JobParamsType,
|
||||
headers: Record<string, string>,
|
||||
req: RequestFacade
|
||||
) => Promise<{
|
||||
type: string | null;
|
||||
title: string;
|
||||
jobParams: any;
|
||||
jobParams: JobParamsType;
|
||||
}>;
|
||||
|
||||
export type ESQueueWorkerExecuteFn = (
|
||||
export type ESQueueWorkerExecuteFn<JobDocPayloadType> = (
|
||||
jobId: string,
|
||||
job: JobDoc,
|
||||
job: JobDocPayloadType,
|
||||
cancellationToken?: CancellationToken
|
||||
) => void;
|
||||
|
||||
export type JobIDForImmediate = null;
|
||||
|
||||
export type ImmediateExecuteFn = (
|
||||
jobId: JobIDForImmediate,
|
||||
job: JobDocPayload,
|
||||
/*
|
||||
* ImmediateExecuteFn receives the job doc payload because the payload was
|
||||
* generated in the CreateFn
|
||||
*/
|
||||
export type ImmediateExecuteFn<JobParamsType> = (
|
||||
jobId: null,
|
||||
job: JobDocPayload<JobParamsType>,
|
||||
request: RequestFacade
|
||||
) => Promise<JobDocOutputExecuted>;
|
||||
|
||||
|
@ -296,30 +289,48 @@ export interface ESQueueWorkerOptions {
|
|||
intervalErrorMultiplier: number;
|
||||
}
|
||||
|
||||
export interface ESQueueInstance {
|
||||
// GenericWorkerFn is a generic for ImmediateExecuteFn<JobParamsType> | ESQueueWorkerExecuteFn<JobDocPayloadType>,
|
||||
type GenericWorkerFn<JobParamsType> = (
|
||||
jobSource: JobSource<JobParamsType>,
|
||||
...workerRestArgs: any[]
|
||||
) => void | Promise<JobDocOutputExecuted>;
|
||||
|
||||
export interface ESQueueInstance<JobParamsType, JobDocPayloadType> {
|
||||
registerWorker: (
|
||||
jobtype: string,
|
||||
workerFn: any,
|
||||
pluginId: string,
|
||||
workerFn: GenericWorkerFn<JobParamsType>,
|
||||
workerOptions: ESQueueWorkerOptions
|
||||
) => ESQueueWorker;
|
||||
}
|
||||
|
||||
export type CreateJobFactory = (server: ServerFacade) => ESQueueCreateJobFn | ImmediateCreateJobFn;
|
||||
export type ExecuteJobFactory = (server: ServerFacade) => ESQueueWorkerExecuteFn | ImmediateExecuteFn; // prettier-ignore
|
||||
export type CreateJobFactory<CreateJobFnType> = (server: ServerFacade) => CreateJobFnType;
|
||||
export type ExecuteJobFactory<ExecuteJobFnType> = (server: ServerFacade) => ExecuteJobFnType;
|
||||
|
||||
export interface ExportTypeDefinition {
|
||||
export interface ExportTypeDefinition<
|
||||
JobParamsType,
|
||||
CreateJobFnType,
|
||||
JobPayloadType,
|
||||
ExecuteJobFnType
|
||||
> {
|
||||
id: string;
|
||||
name: string;
|
||||
jobType: string;
|
||||
jobContentEncoding?: string;
|
||||
jobContentExtension: string;
|
||||
createJobFactory: CreateJobFactory;
|
||||
executeJobFactory: ExecuteJobFactory;
|
||||
createJobFactory: CreateJobFactory<CreateJobFnType>;
|
||||
executeJobFactory: ExecuteJobFactory<ExecuteJobFnType>;
|
||||
validLicenses: string[];
|
||||
}
|
||||
|
||||
export interface ExportTypesRegistry {
|
||||
register: (exportTypeDefinition: ExportTypeDefinition) => void;
|
||||
register: <JobParamsType, CreateJobFnType, JobPayloadType, ExecuteJobFnType>(
|
||||
exportTypeDefinition: ExportTypeDefinition<
|
||||
JobParamsType,
|
||||
CreateJobFnType,
|
||||
JobPayloadType,
|
||||
ExecuteJobFnType
|
||||
>
|
||||
) => void;
|
||||
}
|
||||
|
||||
export { CancellationToken } from './common/cancellation_token';
|
||||
|
|
|
@ -9610,8 +9610,6 @@
|
|||
"xpack.reporting.exportTypes.csv.executeJob.failedToDecryptReportJobDataErrorMessage": "レポートジョブデータの解読に失敗しました{encryptionKey} が設定されていることを確認してこのレポートを再生成してください。{err}",
|
||||
"xpack.reporting.exportTypes.csv.hitIterator.expectedHitsErrorMessage": "次の Elasticsearch からの応答で期待される {hits}: {response}",
|
||||
"xpack.reporting.exportTypes.csv.hitIterator.expectedScrollIdErrorMessage": "次の Elasticsearch からの応答で期待される {scrollId}: {response}",
|
||||
"xpack.reporting.exportTypes.png.compShim.failedToDecryptReportJobDataErrorMessage": "レポートジョブデータの解読に失敗しました{encryptionKey} が設定されていることを確認してこのレポートを再生成してください。{err}",
|
||||
"xpack.reporting.exportTypes.printablePdf.compShim.failedToDecryptReportJobDataErrorMessage": "レポートジョブデータの解読に失敗しました{encryptionKey} が設定されていることを確認してこのレポートを再生成してください。{err}",
|
||||
"xpack.reporting.exportTypes.printablePdf.documentStreamIsNotgeneratedErrorMessage": "ドキュメントストリームが生成されていません。",
|
||||
"xpack.reporting.exportTypes.printablePdf.logoDescription": "Elastic 提供",
|
||||
"xpack.reporting.exportTypes.printablePdf.pagingDescription": "{pageCount} ページ中 {currentPage} ページ目",
|
||||
|
|
|
@ -9699,8 +9699,6 @@
|
|||
"xpack.reporting.exportTypes.csv.executeJob.failedToDecryptReportJobDataErrorMessage": "无法解密报告作业数据。请确保已设置 {encryptionKey},然后重新生成此报告。{err}",
|
||||
"xpack.reporting.exportTypes.csv.hitIterator.expectedHitsErrorMessage": "在以下 Elasticsearch 响应中预期 {hits}:{response}",
|
||||
"xpack.reporting.exportTypes.csv.hitIterator.expectedScrollIdErrorMessage": "在以下 Elasticsearch 响应中预期 {scrollId}:{response}",
|
||||
"xpack.reporting.exportTypes.png.compShim.failedToDecryptReportJobDataErrorMessage": "无法解密报告作业数据。请确保已设置 {encryptionKey},然后重新生成此报告。{err}",
|
||||
"xpack.reporting.exportTypes.printablePdf.compShim.failedToDecryptReportJobDataErrorMessage": "无法解密报告作业数据。请确保已设置 {encryptionKey},然后重新生成此报告。{err}",
|
||||
"xpack.reporting.exportTypes.printablePdf.documentStreamIsNotgeneratedErrorMessage": "尚未生成文档流",
|
||||
"xpack.reporting.exportTypes.printablePdf.logoDescription": "由 Elastic 提供支持",
|
||||
"xpack.reporting.exportTypes.printablePdf.pagingDescription": "第 {currentPage} 页,共 {pageCount} 页",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue