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

[Reporting] Define shims of legacy dependencies #54082

Merged
merged 33 commits into from
Jan 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7315513
simplify serverfacade definition
tsullivan Jan 6, 2020
d954ef0
simplify requestfacade definition
tsullivan Jan 6, 2020
bb65245
makeRequestFacade
tsullivan Jan 6, 2020
79a9288
requestFacade
tsullivan Jan 6, 2020
c931081
use the shim
tsullivan Jan 6, 2020
62dc408
import sorting
tsullivan Jan 7, 2020
78a2ea1
originalServer
tsullivan Jan 7, 2020
3e71f57
reduce loc change
tsullivan Jan 7, 2020
956ae17
remove consolelog
tsullivan Jan 7, 2020
e4761ff
hacks to fix tests
tsullivan Jan 7, 2020
48ec88c
Merge branch 'master' into reporting/shim-plugin
elasticmachine Jan 7, 2020
44b714c
ServerFacade in index
tsullivan Jan 7, 2020
4192c34
Merge branch 'master' into reporting/shim-plugin
elasticmachine Jan 7, 2020
4e7e448
Cosmetic
tsullivan Jan 7, 2020
71bd4a5
remove field from serverfacade
tsullivan Jan 7, 2020
c64b4f7
Merge branch 'master' into reporting/shim-plugin
tsullivan Jan 8, 2020
ae8d134
add raw to the request
tsullivan Jan 8, 2020
81afb28
fix types
tsullivan Jan 8, 2020
a6e830a
add fieldFormatServiceFactory to legacy
tsullivan Jan 9, 2020
79524ef
Merge branch 'master' into reporting/shim-plugin
elasticmachine Jan 10, 2020
ec3f12b
Merge branch 'master' into reporting/shim-plugin
elasticmachine Jan 13, 2020
bdc87c9
Merge branch 'master' into reporting/shim-plugin
elasticmachine Jan 14, 2020
80e0fcc
Pass the complete request object to sec plugin
tsullivan Jan 14, 2020
17b3b10
Fix test
tsullivan Jan 14, 2020
f7609d1
fix test 2
tsullivan Jan 14, 2020
cb40fa3
getUser takes a legacy request
tsullivan Jan 14, 2020
15ab24b
add unit test for new lib
tsullivan Jan 14, 2020
37f6c3e
Merge branch 'master' into reporting/shim-plugin
elasticmachine Jan 15, 2020
ddf8fac
add getRawRequest to pass to saved objects method
tsullivan Jan 15, 2020
b4dcefb
update test snapshot
tsullivan Jan 16, 2020
9d446a1
Merge branch 'master' into reporting/shim-plugin
elasticmachine Jan 16, 2020
25e30da
leave a TODO comment for type import
tsullivan Jan 16, 2020
5378584
variable rename for legacy id
tsullivan Jan 16, 2020
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 @@ -86,7 +86,6 @@ export const executeJobFactory: ExecuteJobFactory<ESQueueWorkerExecuteFn<

const [formatsMap, uiSettings] = await Promise.all([
(async () => {
// @ts-ignore fieldFormatServiceFactory' does not exist on type 'ServerFacade TODO
Copy link
Member Author

Choose a reason for hiding this comment

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

moved the ts-ignore to reporting/index.ts

const fieldFormats = await server.fieldFormatServiceFactory(uiConfig);
return fieldFormatMapFactory(indexPatternSavedObject, fieldFormats);
})(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export async function generateCsvSearch(
jobParams: JobParamsDiscoverCsv
): Promise<CsvResultFromSearch> {
const { savedObjects, uiSettingsServiceFactory } = server;
const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(req);
const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(req.getRawRequest());
const { indexPatternSavedObjectId, timerange } = searchPanel;
const savedSearchObjectAttr = searchPanel.attributes as SavedSearchObjectAttributes;
const { indexPatternSavedObject } = await getDataSource(
Expand Down
63 changes: 49 additions & 14 deletions x-pack/legacy/plugins/reporting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

import { resolve } from 'path';
import { i18n } from '@kbn/i18n';
import { Legacy } from 'kibana';
import { IUiSettingsClient } from 'src/core/server';
import { XPackMainPlugin } from '../xpack_main/server/xpack_main';
import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants';
// @ts-ignore untyped module defintition
import { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status';
Expand All @@ -16,16 +19,35 @@ import {
getExportTypesRegistry,
runValidations,
} from './server/lib';
import { config as reportingConfig } from './config';
import { logConfiguration } from './log_configuration';
import { createBrowserDriverFactory } from './server/browsers';
import { registerReportingUsageCollector } from './server/usage';
import { ReportingConfigOptions, ReportingPluginSpecOptions, ServerFacade } from './types.d';
import { ReportingConfigOptions, ReportingPluginSpecOptions } from './types.d';
import { config as reportingConfig } from './config';
import { logConfiguration } from './log_configuration';

const kbToBase64Length = (kb: number) => {
return Math.floor((kb * 1024 * 8) / 6);
};

type LegacyPlugins = Legacy.Server['plugins'];

export interface ServerFacade {
config: Legacy.Server['config'];
info: Legacy.Server['info'];
log: Legacy.Server['log'];
plugins: {
elasticsearch: LegacyPlugins['elasticsearch'];
security: LegacyPlugins['security'];
xpack_main: XPackMainPlugin & {
status?: any;
};
};
route: Legacy.Server['route'];
savedObjects: Legacy.Server['savedObjects'];
uiSettingsServiceFactory: Legacy.Server['uiSettingsServiceFactory'];
fieldFormatServiceFactory: (uiConfig: IUiSettingsClient) => unknown;
}

export const reporting = (kibana: any) => {
return new kibana.Plugin({
id: PLUGIN_ID,
Expand All @@ -42,7 +64,7 @@ export const reporting = (kibana: any) => {
embeddableActions: ['plugins/reporting/panel_actions/get_csv_panel_action'],
home: ['plugins/reporting/register_feature'],
managementSections: ['plugins/reporting/views/management'],
injectDefaultVars(server: ServerFacade, options?: ReportingConfigOptions) {
injectDefaultVars(server: Legacy.Server, options?: ReportingConfigOptions) {
const config = server.config();
return {
reportingPollConfig: options ? options.poll : {},
Expand Down Expand Up @@ -70,28 +92,41 @@ export const reporting = (kibana: any) => {
},
},

// TODO: Decouple Hapi: Build a server facade object based on the server to
// pass through to the libs. Do not pass server directly
async init(server: ServerFacade) {
async init(server: Legacy.Server) {
const serverFacade: ServerFacade = {
config: server.config,
info: server.info,
route: server.route.bind(server),
plugins: {
elasticsearch: server.plugins.elasticsearch,
xpack_main: server.plugins.xpack_main,
security: server.plugins.security,
},
savedObjects: server.savedObjects,
uiSettingsServiceFactory: server.uiSettingsServiceFactory,
// @ts-ignore Property 'fieldFormatServiceFactory' does not exist on type 'Server'.
fieldFormatServiceFactory: server.fieldFormatServiceFactory,
log: server.log.bind(server),
};
const exportTypesRegistry = getExportTypesRegistry();

let isCollectorReady = false;
// Register a function with server to manage the collection of usage stats
const { usageCollection } = server.newPlatform.setup.plugins;
registerReportingUsageCollector(
usageCollection,
server,
serverFacade,
() => isCollectorReady,
exportTypesRegistry
);

const logger = LevelLogger.createForServer(server, [PLUGIN_ID]);
const browserDriverFactory = await createBrowserDriverFactory(server);
const logger = LevelLogger.createForServer(serverFacade, [PLUGIN_ID]);
const browserDriverFactory = await createBrowserDriverFactory(serverFacade);

logConfiguration(server, logger);
runValidations(server, logger, browserDriverFactory);
logConfiguration(serverFacade, logger);
runValidations(serverFacade, logger, browserDriverFactory);

const { xpack_main: xpackMainPlugin } = server.plugins;
const { xpack_main: xpackMainPlugin } = serverFacade.plugins;
mirrorPluginStatus(xpackMainPlugin, this);
const checkLicense = checkLicenseFactory(exportTypesRegistry);
(xpackMainPlugin as any).status.once('green', () => {
Expand All @@ -104,7 +139,7 @@ export const reporting = (kibana: any) => {
isCollectorReady = true;

// Reporting routes
registerRoutes(server, exportTypesRegistry, browserDriverFactory, logger);
registerRoutes(serverFacade, exportTypesRegistry, browserDriverFactory, logger);
},

deprecations({ unused }: any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@
* you may not use this file except in compliance with the Elastic License.
*/

export function getUserFactory(server) {
return async request => {
import { Legacy } from 'kibana';
import { ServerFacade } from '../../types';

export function getUserFactory(server: ServerFacade) {
/*
* Legacy.Request because this is called from routing middleware
*/
return async (request: Legacy.Request) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

This function takes a Legacy request because it is called from middleware. That means we get into this code before we get into any request handlers which is where we pick the properties for RequestFacade.

Copy link
Member Author

Choose a reason for hiding this comment

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

@pgayvallet @restrry this means that I don't actually need to call request.getRawRequest - doing so made all the code break since this isn't an actual RequestFacade :)

Copy link
Member Author

Choose a reason for hiding this comment

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

ddf8fac added that method back, but for a different usage

Copy link
Contributor

Choose a reason for hiding this comment

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

Just thinking loudly: why not call this function from a route handler? NP routing doesn't provide a pre-middleware mechanism at the moment.

Copy link
Member Author

Choose a reason for hiding this comment

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

NP routing doesn't provide a pre-middleware mechanism at the moment.

Hm, if that is the case it will be a major blocker for Reporting migration.

@rudolf can you confirm whether we will be able to register routing middleware in the new platform?

Copy link
Contributor

@pgayvallet pgayvallet Jan 21, 2020

Choose a reason for hiding this comment

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

@rudolf can you confirm whether we will be able to register routing middleware in the new platform?

ATM this is not something we are planing to provide.

Hm, if that is the case it will be a major blocker for Reporting migration.

Correct me if I'm wrong, but the whole pre-routing mechanism can easily be replaced by some wrapper around the actual route handlers. The usage would be really close to what you currently have and should not be that big of a change

Looking at your getRouteConfigFactoryReportingPre method and usages in x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts, the pre-routing can be moved to an higher level. This would be something similar as what we are going in src/core/server/http/router/error_wrapper.ts

  const getRouteConfig = () => {
    const getOriginalRouteConfig: GetRouteConfigFactoryFn = getRouteConfigFactoryReportingPre(
      server
    );
    const routeConfigFactory: RouteConfigFactory = getOriginalRouteConfig(
      ({ params: { exportType } }) => exportType
    );

    return {
      ...routeConfigFactory,
      validate: {
        params: Joi.object({
         [...]
      },
    };
  };

  // generate report
  server.route({
    path: `${BASE_GENERATE}/{exportType}`,
    method: 'POST',
    options: getRouteConfig(),
    handler: async (legacyRequest: Legacy.Request, h: ReportingResponseToolkit) => {
      [...]
      return response;
    },
  });

would become something like (also adapted to NP router format)

  const getRouteConfig = () => {
    const getOriginalRouteConfig: GetRouteConfigFactoryFn = getRouteConfigFactoryReportingPre(
      server
    );
    const routeConfigFactory: RouteConfigFactory = getOriginalRouteConfig(
      ({ params: { exportType } }) => exportType
    );

    return {
      ...routeConfigFactory,
      validate: {
        params: schema.object({
         [...]
      },
    };
  };


const routeConfig = getRouteConfig()
router.post(
      {
        path: `${BASE_GENERATE}/{exportType}`,
        options: routeConfig.options
        validate: routeConfig.validate,
      },
      routeConfig.performPreAuth(async (ctx, req, res, preAuthResult) => {
        const request = makeRequestFacade(legacyRequest, preAuthResults);
        ....
      })
    );

The performPreAuth generated from getRouteConfigFactoryReportingPre would need some adaption from the existing code to execute all pre-handlers and either returns a response error if one throws or execute the underlying handler with an additional parameter containing the resolved objects from the pre handlers (basically what HAPI was doing itself before), but that doesn't sound like big changes, the actual pre-hook handlers would remains unchanged.

Please ping me if you want me to go a little more in-details with that.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm ++ on any solution that will allow us to move further, and I don't see any problems with your proposals. I think there are a lot of ways to achieve the goal that the code is doing and pre-routing is just one of them.

if (!server.plugins.security) {
return null;
}

try {
return await server.plugins.security.getUser(request);
} catch (err) {
server.log(['reporting', 'getUser', 'debug'], err);
server.log(['reporting', 'getUser', 'error'], err);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice catch here.

return null;
}
};
Expand Down
8 changes: 4 additions & 4 deletions x-pack/legacy/plugins/reporting/server/lib/level_logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

type ServerLog = (tags: string[], msg: string) => void;
import { ServerFacade } from '../../types';

const trimStr = (toTrim: string) => {
return typeof toTrim === 'string' ? toTrim.trim() : toTrim;
Expand All @@ -16,12 +16,12 @@ export class LevelLogger {

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

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

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

Expand Down
4 changes: 0 additions & 4 deletions x-pack/legacy/plugins/reporting/server/lib/once_per_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ export function oncePerServer(fn: ServerFn) {
throw new TypeError('This function expects to be called with a single argument');
}

if (!server || typeof server.expose !== 'function') {
Copy link
Contributor

Choose a reason for hiding this comment

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

Yesssss

throw new TypeError('This function expects to be passed the server');
}

// @ts-ignore
return fn.call(this, server);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Legacy } from 'kibana';
import boom from 'boom';
import Joi from 'joi';
import rison from 'rison-node';
import { API_BASE_URL } from '../../common/constants';
import { ServerFacade, RequestFacade, ReportingResponseToolkit } from '../../types';
import { ServerFacade, ReportingResponseToolkit } from '../../types';
import {
getRouteConfigFactoryReportingPre,
GetRouteConfigFactoryFn,
RouteConfigFactory,
} from './lib/route_config_factories';
import { makeRequestFacade } from './lib/make_request_facade';
import { HandlerErrorFunction, HandlerFunction } from './types';

const BASE_GENERATE = `${API_BASE_URL}/generate`;
Expand Down Expand Up @@ -54,7 +56,8 @@ export function registerGenerateFromJobParams(
path: `${BASE_GENERATE}/{exportType}`,
method: 'POST',
options: getRouteConfig(),
handler: async (request: RequestFacade, h: ReportingResponseToolkit) => {
handler: async (legacyRequest: Legacy.Request, h: ReportingResponseToolkit) => {
const request = makeRequestFacade(legacyRequest);
let jobParamsRison: string | null;

if (request.payload) {
Expand All @@ -80,7 +83,7 @@ export function registerGenerateFromJobParams(
if (!jobParams) {
throw new Error('missing jobParams!');
}
response = await handler(exportType, jobParams, request, h);
response = await handler(exportType, jobParams, legacyRequest, h);
} catch (err) {
throw boom.badRequest(`invalid rison: ${jobParamsRison}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Legacy } from 'kibana';
import { get } from 'lodash';
import { API_BASE_GENERATE_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants';
import { ServerFacade, RequestFacade, ReportingResponseToolkit } from '../../types';
import { ServerFacade, ReportingResponseToolkit } from '../../types';
import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types';
import { getRouteOptionsCsv } from './lib/route_config_factories';
import { makeRequestFacade } from './lib/make_request_facade';
import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request';

/*
Expand All @@ -31,17 +33,18 @@ export function registerGenerateCsvFromSavedObject(
path: `${API_BASE_GENERATE_V1}/csv/saved-object/{savedObjectType}:{savedObjectId}`,
method: 'POST',
options: routeOptions,
handler: async (request: RequestFacade, h: ReportingResponseToolkit) => {
handler: async (legacyRequest: Legacy.Request, h: ReportingResponseToolkit) => {
const requestFacade = makeRequestFacade(legacyRequest);

/*
* 1. Build `jobParams` object: job data that execution will need to reference in various parts of the lifecycle
* 2. Pass the jobParams and other common params to `handleRoute`, a shared function to enqueue the job with the params
* 3. Ensure that details for a queued job were returned
*/

let result: QueuedJobPayload<any>;
try {
const jobParams = getJobParamsFromRequest(request, { isImmediate: false });
result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, request, h);
const jobParams = getJobParamsFromRequest(requestFacade, { isImmediate: false });
result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, legacyRequest, h); // pass the original request because the handler will make the request facade on its own
} catch (err) {
throw handleRouteError(CSV_FROM_SAVEDOBJECT_JOB_TYPE, err);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Legacy } from 'kibana';
import { API_BASE_GENERATE_V1 } from '../../common/constants';
import { createJobFactory, executeJobFactory } from '../../export_types/csv_from_savedobject';
import {
ServerFacade,
RequestFacade,
ResponseFacade,
HeadlessChromiumDriverFactory,
ReportingResponseToolkit,
Logger,
JobDocOutputExecuted,
} from '../../types';
import { JobDocPayloadPanelCsv } from '../../export_types/csv_from_savedobject/types';
import { getRouteOptionsCsv } from './lib/route_config_factories';
import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request';
import { getRouteOptionsCsv } from './lib/route_config_factories';
import { makeRequestFacade } from './lib/make_request_facade';

/*
* This function registers API Endpoints for immediate Reporting jobs. The API inputs are:
Expand All @@ -43,7 +44,8 @@ export function registerGenerateCsvFromSavedObjectImmediate(
path: `${API_BASE_GENERATE_V1}/immediate/csv/saved-object/{savedObjectType}:{savedObjectId}`,
method: 'POST',
options: routeOptions,
handler: async (request: RequestFacade, h: ReportingResponseToolkit) => {
handler: async (legacyRequest: Legacy.Request, h: ReportingResponseToolkit) => {
const request = makeRequestFacade(legacyRequest);
const logger = parentLogger.clone(['savedobject-csv']);
const jobParams = getJobParamsFromRequest(request, { isImmediate: true });

Expand Down
6 changes: 4 additions & 2 deletions x-pack/legacy/plugins/reporting/server/routes/generation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@
*/

import boom from 'boom';
import { Legacy } from 'kibana';
import { API_BASE_URL } from '../../common/constants';
import {
ServerFacade,
ExportTypesRegistry,
HeadlessChromiumDriverFactory,
RequestFacade,
ReportingResponseToolkit,
Logger,
} from '../../types';
import { registerGenerateFromJobParams } from './generate_from_jobparams';
import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject';
import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate';
import { createQueueFactory, enqueueJobFactory } from '../lib';
import { makeRequestFacade } from './lib/make_request_facade';

export function registerJobGenerationRoutes(
server: ServerFacade,
Expand All @@ -39,9 +40,10 @@ export function registerJobGenerationRoutes(
async function handler(
exportTypeId: string,
jobParams: object,
request: RequestFacade,
legacyRequest: Legacy.Request,
h: ReportingResponseToolkit
) {
const request = makeRequestFacade(legacyRequest);
const user = request.pre.user;
const headers = request.headers;

Expand Down
Loading