Skip to content

Commit

Permalink
Move csv_v2 and csv_searchsource to export type class (#160041)
Browse files Browse the repository at this point in the history
## Summary

This is a part of #158092

This PR encompasses both csv_v2 and csv_searchsource as the
CsvExportType and CsvSearchsourceExportType. Based on the reporting
plugin readme, CsvSearchsourceExportType is currently used but
CsvV2ExportType is refactored for when it can reach feature parity to
csv searchsource.

### Checklist

- [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

---------

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Timothy Sullivan <[email protected]>
  • Loading branch information
3 people committed Jul 13, 2023
1 parent f3e2ed5 commit 65e94c6
Show file tree
Hide file tree
Showing 30 changed files with 436 additions and 362 deletions.
1 change: 0 additions & 1 deletion x-pack/plugins/reporting/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ module.exports = {
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/reporting',
coverageReporters: ['text', 'html'],
testPathIgnorePatterns: [
'<rootDir>/x-pack/plugins/reporting/server/export_types/csv_searchsource/*',
'<rootDir>/x-pack/plugins/reporting/server/export_types/common/get_full_urls.test.ts',
'<rootDir>/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts',
'<rootDir>/x-pack/plugins/reporting/server/export_types/png/execute_job/index.test.ts',
Expand Down
27 changes: 23 additions & 4 deletions x-pack/plugins/reporting/server/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ import type {
} from '@kbn/task-manager-plugin/server';
import type { UsageCounter } from '@kbn/usage-collection-plugin/server';
import * as Rx from 'rxjs';
import { filter, first, map, switchMap, take } from 'rxjs/operators';
import { filter, first, map, take } from 'rxjs/operators';
import type { ReportingSetup } from '.';
import { REPORTING_REDIRECT_LOCATOR_STORE_KEY } from '../common/constants';
import { createConfig, ReportingConfigType } from './config';
import { CsvSearchSourceExportType } from './export_types/csv_searchsource';
import { CsvV2ExportType } from './export_types/csv_v2';
import { PdfExportType } from './export_types/printable_pdf_v2';
import { checkLicense, ExportTypesRegistry } from './lib';
import { reportingEventLoggerFactory } from './lib/event_logger/logger';
Expand Down Expand Up @@ -104,6 +106,8 @@ export class ReportingCore {
private monitorTask: MonitorReportsTask;
private config: ReportingConfigType;
private executing: Set<string>;
private csvSearchSourceExport: CsvSearchSourceExportType;
private csvV2ExportType: CsvV2ExportType;
private pdfExport: PdfExportType;
private exportTypesRegistry = new ExportTypesRegistry();

Expand All @@ -120,6 +124,17 @@ export class ReportingCore {
const config = createConfig(core, context.config.get<ReportingConfigType>(), logger);
this.config = config;

this.csvSearchSourceExport = new CsvSearchSourceExportType(
this.core,
this.config,
this.logger,
this.context
);
this.exportTypesRegistry.register(this.csvSearchSourceExport);

this.csvV2ExportType = new CsvV2ExportType(this.core, this.config, this.logger, this.context);
this.exportTypesRegistry.register(this.csvV2ExportType);

this.pdfExport = new PdfExportType(this.core, this.config, this.logger, this.context);
this.exportTypesRegistry.register(this.pdfExport);

Expand Down Expand Up @@ -148,6 +163,8 @@ export class ReportingCore {
this.pluginSetup$.next(true); // trigger the observer
this.pluginSetupDeps = setupDeps; // cache

this.csvSearchSourceExport.setup(setupDeps);
this.csvV2ExportType.setup(setupDeps);
this.pdfExport.setup(setupDeps);

const { executeTask, monitorTask } = this;
Expand All @@ -163,7 +180,10 @@ export class ReportingCore {
public async pluginStart(startDeps: ReportingInternalStart) {
this.pluginStart$.next(startDeps); // trigger the observer
this.pluginStartDeps = startDeps; // cache
this.pdfExport.start({ ...startDeps, reporting: this.getContract() });
const reportingStart = this.getContract();
this.csvSearchSourceExport.start({ ...startDeps, reporting: reportingStart });
this.csvV2ExportType.start({ ...startDeps, reporting: reportingStart });
this.pdfExport.start({ ...startDeps, reporting: reportingStart });

await this.assertKibanaIsAvailable();

Expand Down Expand Up @@ -191,7 +211,6 @@ export class ReportingCore {
)
.toPromise();
}

/*
* Blocks the caller until setup is done
*/
Expand Down Expand Up @@ -382,7 +401,7 @@ export class ReportingCore {
options: PngScreenshotOptions | PdfScreenshotOptions
): Rx.Observable<PngScreenshotResult | PdfScreenshotResult> {
return Rx.defer(() => this.getPluginStartDeps()).pipe(
switchMap(({ screenshotting }) => {
Rx.switchMap(({ screenshotting }) => {
return screenshotting.getScreenshots({
...options,
urls: options.urls.map((url) =>
Expand Down
18 changes: 11 additions & 7 deletions x-pack/plugins/reporting/server/export_types/common/export_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
SavedObjectsClientContract,
SavedObjectsServiceStart,
UiSettingsServiceStart,
IClusterClient,
} from '@kbn/core/server';
import { LicenseType } from '@kbn/licensing-plugin/common/types';
import { ScreenshottingStart } from '@kbn/screenshotting-plugin/server';
Expand All @@ -30,21 +31,24 @@ import { CreateJobFn, ReportingStart, RunTaskFn } from '../../types';
/**
* @TODO move to be within @kbn-reporting-export-types
*/
export interface ExportTypeSetupDeps {
export interface BaseExportTypeSetupDeps {
basePath: Pick<IBasePath, 'set'>;
spaces?: SpacesPluginSetup;
}

export interface ExportTypeStartDeps {
export interface BaseExportTypeStartDeps {
savedObjects: SavedObjectsServiceStart;
uiSettings: UiSettingsServiceStart;
esClient: IClusterClient;
screenshotting: ScreenshottingStart;
reporting: ReportingStart;
}

export abstract class ExportType<
JobParamsType extends object = any,
TaskPayloadType extends object = any
TaskPayloadType extends object = any,
SetupDepsType extends BaseExportTypeSetupDeps = BaseExportTypeSetupDeps,
StartDepsType extends BaseExportTypeStartDeps = BaseExportTypeStartDeps
> {
abstract id: string; // ID for exportTypesRegistry.get()
abstract name: string; // user-facing string
Expand All @@ -58,8 +62,8 @@ export abstract class ExportType<

abstract validLicenses: LicenseType[];

public setupDeps!: ExportTypeSetupDeps;
public startDeps!: ExportTypeStartDeps;
public setupDeps!: SetupDepsType;
public startDeps!: StartDepsType;
public http!: HttpServiceSetup;

constructor(
Expand All @@ -71,10 +75,10 @@ export abstract class ExportType<
this.http = core.http;
}

setup(setupDeps: ExportTypeSetupDeps) {
setup(setupDeps: SetupDepsType) {
this.setupDeps = setupDeps;
}
start(startDeps: ExportTypeStartDeps) {
start(startDeps: StartDepsType) {
this.startDeps = startDeps;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export { validateUrls } from './validate_urls';
export { generatePngObservable } from './generate_png';
export { getCustomLogo } from './get_custom_logo';
export { ExportType } from './export_type';
export type { BaseExportTypeSetupDeps, BaseExportTypeStartDeps } from './export_type';

export interface TimeRangeParams {
min?: Date | string | number | null;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

jest.mock('@kbn/generate-csv', () => ({
CsvGenerator: class CsvGeneratorMock {
generateData() {
return {
size: 123,
content_type: 'text/csv',
};
}
},
}));

import nodeCrypto from '@elastic/node-crypto';
import { coreMock, elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks';
import { Writable } from 'stream';
import { ReportingCore } from '../..';
import { CancellationToken } from '@kbn/reporting-common';
import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers';
import { CsvSearchSourceExportType } from './csv_searchsource';
import { discoverPluginMock } from '@kbn/discover-plugin/server/mocks';
import { dataPluginMock } from '@kbn/data-plugin/server/mocks';
import { createMockScreenshottingStart } from '@kbn/screenshotting-plugin/server/mock';

const mockLogger = loggingSystemMock.createLogger();
const encryptionKey = 'tetkey';
const headers = { sid: 'cooltestheaders' };
let encryptedHeaders: string;
let mockReportingCore: ReportingCore;
let stream: jest.Mocked<Writable>;
let mockCsvSearchSourceExportType: CsvSearchSourceExportType;

beforeAll(async () => {
const crypto = nodeCrypto({ encryptionKey });

encryptedHeaders = await crypto.encrypt(headers);
const configType = createMockConfigSchema({
encryptionKey,
csv: {
checkForFormulas: true,
escapeFormulaValues: true,
maxSizeBytes: 180000,
scroll: { size: 500, duration: '30s' },
},
});
const mockCoreSetup = coreMock.createSetup();
const mockCoreStart = coreMock.createStart();
const context = coreMock.createPluginInitializerContext(configType);

mockReportingCore = await createMockReportingCore(configType);

mockCsvSearchSourceExportType = new CsvSearchSourceExportType(
mockCoreSetup,
configType,
mockLogger,
context
);

mockCsvSearchSourceExportType.setup({
basePath: { set: jest.fn() },
});

mockCsvSearchSourceExportType.start({
esClient: elasticsearchServiceMock.createClusterClient(),
savedObjects: mockCoreStart.savedObjects,
uiSettings: mockCoreStart.uiSettings,
discover: discoverPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
screenshotting: createMockScreenshottingStart(),
reporting: mockReportingCore.getContract(),
});
});

beforeEach(() => {
stream = {} as typeof stream;
});

test('gets the csv content from job parameters', async () => {
const payload = await mockCsvSearchSourceExportType.runTask(
'cool-job-id',
{
headers: encryptedHeaders,
browserTimezone: 'US/Alaska',
searchSource: {},
objectType: 'search',
title: 'Test Search',
version: '7.13.0',
},
new CancellationToken(),
stream
);

expect(payload).toMatchInlineSnapshot(`
Object {
"content_type": "text/csv",
"size": 123,
}
`);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { DataPluginStart } from '@kbn/data-plugin/server/plugin';
import { DiscoverServerPluginStart } from '@kbn/discover-plugin/server';
import { CsvGenerator } from '@kbn/generate-csv';
import { CancellationToken } from '@kbn/reporting-common';
import { Writable } from 'stream';
import {
CSV_JOB_TYPE,
LICENSE_TYPE_BASIC,
LICENSE_TYPE_CLOUD_STANDARD,
LICENSE_TYPE_ENTERPRISE,
LICENSE_TYPE_GOLD,
LICENSE_TYPE_PLATINUM,
LICENSE_TYPE_TRIAL,
} from '../../../common/constants';
import { getFieldFormats } from '../../services';
import { ExportType, BaseExportTypeSetupDeps, BaseExportTypeStartDeps } from '../common';
import { decryptJobHeaders } from '../common/decrypt_job_headers';
import { JobParamsCSV, TaskPayloadCSV } from './types';

/*
* @TODO move to be within @kbn/reporitng-export-types
*/

type CsvSearchSourceExportTypeSetupDeps = BaseExportTypeSetupDeps;
interface CsvSearchSourceExportTypeStartDeps extends BaseExportTypeStartDeps {
discover: DiscoverServerPluginStart;
data: DataPluginStart;
}

export class CsvSearchSourceExportType extends ExportType<
JobParamsCSV,
TaskPayloadCSV,
CsvSearchSourceExportTypeSetupDeps,
CsvSearchSourceExportTypeStartDeps
> {
id = 'csv_searchsource';
name = CSV_JOB_TYPE;
jobType = CSV_JOB_TYPE;
jobContentEncoding = 'base64' as const;
jobContentExtension = 'csv' as const;
validLicenses = [
LICENSE_TYPE_TRIAL,
LICENSE_TYPE_BASIC,
LICENSE_TYPE_CLOUD_STANDARD,
LICENSE_TYPE_GOLD,
LICENSE_TYPE_PLATINUM,
LICENSE_TYPE_ENTERPRISE,
];
constructor(...args: ConstructorParameters<typeof ExportType>) {
super(...args);
const logger = args[2];
this.logger = logger.get('csv-searchsource-export');
}

public createJob = async (jobParams: JobParamsCSV) => {
return { ...jobParams, isDeprecated: false };
};

public runTask = async (
jobId: string,
job: TaskPayloadCSV,
cancellationToken: CancellationToken,
stream: Writable
) => {
const { encryptionKey, csv: csvConfig } = this.config;
const logger = this.logger.get(`execute-job:${jobId}`);
const headers = await decryptJobHeaders(encryptionKey, job.headers, logger);
const fakeRequest = this.getFakeRequest(headers, job.spaceId, logger);
const uiSettings = await this.getUiSettingsClient(fakeRequest, logger);
const dataPluginStart = this.startDeps.data;
const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory(uiSettings);

const es = this.startDeps.esClient.asScoped(fakeRequest);
const searchSourceStart = await dataPluginStart.search.searchSource.asScoped(fakeRequest);

const clients = {
uiSettings,
data: dataPluginStart.search.asScoped(fakeRequest),
es,
};
const dependencies = {
searchSourceStart,
fieldFormatsRegistry,
};

const csv = new CsvGenerator(
job,
csvConfig,
clients,
dependencies,
cancellationToken,
logger,
stream
);
return await csv.generateData();
};
}
Loading

0 comments on commit 65e94c6

Please sign in to comment.