Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent creating a job in the reporting index when the type is immediate #35011

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* 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 url from 'url';
import { getAbsoluteUrlFactory } from '../../../common/get_absolute_url';
import { ConditionalHeaders, JobDocPayload, KbnServer } from '../../../types';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export * from './index.d';

/*
* These functions are exported to share with the API route handler that
* generates csv from saved object immediately on request.
*/
export { executeJobFactory } from './server/execute_job';
export { createJobFactory } from './server/create_job';
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import { notFound, notImplemented } from 'boom';
import { Request } from 'hapi';
import { get } from 'lodash';
// @ts-ignore
import { createTaggedLogger, cryptoFactory, oncePerServer } from '../../../../server/lib';
import { JobDocPayload, JobParams, KbnServer, Logger } from '../../../../types';

import { cryptoFactory, LevelLogger, oncePerServer } from '../../../../server/lib';
import { JobDocPayload, JobParams, KbnServer } from '../../../../types';
import {
SavedObject,
SavedObjectServiceError,
Expand All @@ -18,7 +18,6 @@ import {
TimeRangeParams,
VisObjectAttributesJSON,
} from '../../';
import { createGenerateCsv } from '../lib/generate_csv';
import { createJobSearch } from './create_job_search';

interface VisData {
Expand All @@ -27,21 +26,18 @@ interface VisData {
panel: SearchPanel;
}

function createJobFn(server: KbnServer) {
type CreateJobFn = (jobParams: JobParams, headers: any, req: Request) => Promise<JobDocPayload>;

function createJobFn(server: KbnServer): CreateJobFn {
const crypto = cryptoFactory(server);
const logger: Logger = {
debug: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'debug']),
warning: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'warning']),
error: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'error']),
};
const generateCsv = createGenerateCsv(logger);
const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']);

return async function createJob(
jobParams: JobParams,
headers: any,
req: Request
): Promise<JobDocPayload> {
const { isImmediate, savedObjectType, savedObjectId } = jobParams;
const { savedObjectType, savedObjectId } = jobParams;
const serializedEncryptedHeaders = await crypto.encrypt(headers);
const client = req.getSavedObjectsClient();

Expand Down Expand Up @@ -86,28 +82,13 @@ function createJobFn(server: KbnServer) {
throw new Error(`Unable to create a job from saved object data! Error: ${err}`);
});

let type: string = '';
let result: any = null;

if (isImmediate) {
try {
({ type, result } = await generateCsv(req, server, visType, panel));
} catch (err) {
if (err.stack) {
logger.error(err.stack);
}
logger.error(`Generate CSV Error! ${err}`);
throw err;
}
}

return {
basePath: req.getBasePath(),
headers: serializedEncryptedHeaders,
jobParams: { ...jobParams, panel, visType },
type: null, // resolved in executeJob
objects: null, // resolved in executeJob
title,
type,
objects: result ? result.content : result,
headers: serializedEncryptedHeaders,
basePath: req.getBasePath(),
};
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,46 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Request } from 'hapi';
import { i18n } from '@kbn/i18n';

import { cryptoFactory, LevelLogger, oncePerServer } from '../../../server/lib';
import { JobDocOutputExecuted, JobDocPayload, KbnServer } from '../../../types';
import { CONTENT_TYPE_CSV } from '../../../common/constants';
// @ts-ignore
import { createTaggedLogger, cryptoFactory, oncePerServer } from '../../../server/lib';
import { JobDocPayload, KbnServer, Logger } from '../../../types';
import { createGenerateCsv } from './lib/generate_csv';

interface JobDocOutputPseudo {
content_type: 'text/csv';
content: string | null | undefined;
}

interface FakeRequest {
headers: any;
getBasePath: (opts: any) => string;
server: KbnServer;
}

/*
* @return {Object}: pseudo-JobDocOutput. See interface JobDocOutput
*/
function executeJobFn(server: KbnServer) {
type ExecuteJobFn = (job: JobDocPayload, realRequest?: Request) => Promise<JobDocOutputExecuted>;

function executeJobFn(server: KbnServer): ExecuteJobFn {
const crypto = cryptoFactory(server);
const config = server.config();
const serverBasePath = config.get('server.basePath');
const logger: Logger = {
tsullivan marked this conversation as resolved.
Show resolved Hide resolved
debug: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'debug']),
warning: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'warning']),
error: createTaggedLogger(server, ['reporting', 'savedobject-csv', 'error']),
};

const logger = LevelLogger.createForServer(server, ['reporting', 'savedobject-csv']);
const generateCsv = createGenerateCsv(logger);
return async function executeJob(job: JobDocPayload): Promise<JobDocOutputPseudo> {
const { basePath, objects, headers: serializedEncryptedHeaders, jobParams } = job; // FIXME how to remove payload.objects for cleanup?

return async function executeJob(
job: JobDocPayload,
realRequest?: Request
): Promise<JobDocOutputExecuted> {
const { basePath, jobParams } = job;
const { isImmediate, panel, visType } = jobParams;
if (!isImmediate) {
logger.debug(`Execute job generating [${visType}] csv`);

logger.debug(`Execute job generating [${visType}] csv`);

let requestObject: Request | FakeRequest;
if (isImmediate && realRequest) {
logger.debug(`executing job from immediate API`);
requestObject = realRequest;
} else {
logger.debug(`executing job async using encrypted headers`);
let decryptedHeaders;
const serializedEncryptedHeaders = job.headers;
try {
decryptedHeaders = await crypto.decrypt(serializedEncryptedHeaders);
} catch (err) {
Expand All @@ -58,25 +59,33 @@ function executeJobFn(server: KbnServer) {
);
}

const fakeRequest: FakeRequest = {
requestObject = {
headers: decryptedHeaders,
getBasePath: () => basePath || serverBasePath,
server,
};

const content = await generateCsv(fakeRequest, server, visType as string, panel);
return {
content_type: CONTENT_TYPE_CSV,
content: content.result ? content.result.content : null,
};
}

logger.debug(`Execute job using previously-generated [${visType}] csv`);
let content: string;
let maxSizeReached = false;
let size = 0;
try {
({
result: { content, maxSizeReached, size },
} = await generateCsv(requestObject, server, visType as string, panel));
} catch (err) {
if (err.stack) {
logger.error(err.stack);
}
logger.error(`Generate CSV Error! ${err}`);
throw err;
}

// if job was created with "immediate", just return the data in the job doc
return {
content_type: CONTENT_TYPE_CSV,
content: objects,
content,
max_size_reached: maxSizeReached,
size,
};
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

import { badRequest } from 'boom';
import { Request } from 'hapi';
// @ts-ignore
import { createTaggedLogger } from '../../../../server/lib';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup, this was invalid but it had ts-ignore

import { KbnServer, Logger } from '../../../../types';
import { SearchPanel, VisPanel } from '../../';
import { generateCsvSearch } from './generate_csv_search';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface SavedSearchGeneratorResult {

export interface CsvResultFromSearch {
type: string;
result: SavedSearchGeneratorResult | null;
result: SavedSearchGeneratorResult;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It made things complicated to try to support this being null, since the value gets passed in h.response in the route handler, and h.response does not accept null per TS types.

That is why, further down, the code will check if this is null and convert to undefined

}

type EndpointCaller = (method: string, params: any) => Promise<any>;
Expand Down
8 changes: 5 additions & 3 deletions x-pack/plugins/reporting/server/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/

// @ts-ignore
// @ts-ignore untyped module
export { createTaggedLogger } from './create_tagged_logger';
// @ts-ignore
// @ts-ignore untyped module
export { cryptoFactory } from './crypto';
// @ts-ignore
// @ts-ignore untyped module
export { oncePerServer } from './once_per_server';

export { LevelLogger } from './level_logger';
42 changes: 0 additions & 42 deletions x-pack/plugins/reporting/server/lib/level_logger.js

This file was deleted.

52 changes: 52 additions & 0 deletions x-pack/plugins/reporting/server/lib/level_logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this change for CI to pass. These changes come from #34972

* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { Logger } from '../../types';

type ServerLog = (tags: string[], msg: string) => void;

export class LevelLogger implements Logger {
private _logger: any;
private _tags: string[];

public warn: (msg: string, tags?: string[]) => void;

static createForServer(server: any, tags: string[]) {
const serverLog: ServerLog = (tgs: string[], msg: string) => server.log(tgs, msg);
return new LevelLogger(serverLog, tags);
}

constructor(logger: ServerLog, tags: string[]) {
this._logger = logger;
this._tags = tags;

/*
* This shortcut provides maintenance convenience: Reporting code has been
* using both .warn and .warning
*/
this.warn = this.warning.bind(this);
}

public error(msg: string, tags: string[] = []) {
this._logger([...this._tags, ...tags, 'error'], msg);
}

public warning(msg: string, tags: string[] = []) {
this._logger([...this._tags, ...tags, 'warning'], msg);
}

public debug(msg: string, tags: string[] = []) {
this._logger([...this._tags, ...tags, 'debug'], msg);
}

public info(msg: string, tags: string[] = []) {
this._logger([...this._tags, ...tags, 'info'], msg);
}

public clone(tags: string[]) {
return new LevelLogger(this._logger, [...this._tags, ...tags]);
}
}
Loading