diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts index 1b7ba3c90bab1..468caf93ec5dd 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts @@ -5,7 +5,7 @@ */ import { cryptoFactory } from '../../../server/lib/crypto'; -import { createMockServer } from '../../../test_helpers/create_mock_server'; +import { createMockServer } from '../../../test_helpers'; import { Logger } from '../../../types'; import { decryptJobHeaders } from './decrypt_job_headers'; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts index 070bdb4314af9..eedb742ad7597 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts @@ -4,13 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../../../test_helpers/create_mock_server'; -import { getConditionalHeaders, getCustomLogo } from './index'; +import { createMockReportingCore, createMockServer } from '../../../test_helpers'; +import { ReportingCore } from '../../../server'; import { JobDocPayload } from '../../../types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; +import { getConditionalHeaders, getCustomLogo } from './index'; +let mockReportingPlugin: ReportingCore; let mockServer: any; -beforeEach(() => { +beforeEach(async () => { + mockReportingPlugin = await createMockReportingCore(); mockServer = createMockServer(''); }); @@ -148,56 +151,76 @@ describe('conditions', () => { }); test('uses basePath from job when creating saved object service', async () => { + const mockGetSavedObjectsClient = jest.fn(); + mockReportingPlugin.getSavedObjectsClient = mockGetSavedObjectsClient; + const permittedHeaders = { foo: 'bar', baz: 'quix', }; - const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, }); - - const logo = 'custom-logo'; - mockServer.uiSettingsServiceFactory().get.mockReturnValue(logo); - const jobBasePath = '/sbp/s/marketing'; await getCustomLogo({ + reporting: mockReportingPlugin, job: { basePath: jobBasePath } as JobDocPayloadPDF, conditionalHeaders, server: mockServer, }); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.mock.calls[0][0].getBasePath()).toBe( - jobBasePath - ); + const getBasePath = mockGetSavedObjectsClient.mock.calls[0][0].getBasePath; + expect(getBasePath()).toBe(jobBasePath); }); test(`uses basePath from server if job doesn't have a basePath when creating saved object service`, async () => { + const mockGetSavedObjectsClient = jest.fn(); + mockReportingPlugin.getSavedObjectsClient = mockGetSavedObjectsClient; + const permittedHeaders = { foo: 'bar', baz: 'quix', }; - const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, }); - const logo = 'custom-logo'; - mockServer.uiSettingsServiceFactory().get.mockReturnValue(logo); - await getCustomLogo({ + reporting: mockReportingPlugin, job: {} as JobDocPayloadPDF, conditionalHeaders, server: mockServer, }); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.mock.calls[0][0].getBasePath()).toBe( - '/sbp' - ); + const getBasePath = mockGetSavedObjectsClient.mock.calls[0][0].getBasePath; + expect(getBasePath()).toBe(`/sbp`); + expect(mockGetSavedObjectsClient.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "getBasePath": [Function], + "headers": Object { + "baz": "quix", + "foo": "bar", + }, + "path": "/", + "raw": Object { + "req": Object { + "url": "/", + }, + }, + "route": Object { + "settings": Object {}, + }, + "url": Object { + "href": "/", + }, + }, + ] + `); }); describe('config formatting', () => { diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts index ff2c44026315d..fa53f474dfba7 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../../../test_helpers/create_mock_server'; -import { getConditionalHeaders, getCustomLogo } from './index'; +import { ReportingCore } from '../../../server'; +import { createMockReportingCore, createMockServer } from '../../../test_helpers'; +import { ServerFacade } from '../../../types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; +import { getConditionalHeaders, getCustomLogo } from './index'; -let mockServer: any; -beforeEach(() => { +let mockReportingPlugin: ReportingCore; +let mockServer: ServerFacade; +beforeEach(async () => { + mockReportingPlugin = await createMockReportingCore(); mockServer = createMockServer(''); }); @@ -19,6 +23,17 @@ test(`gets logo from uiSettings`, async () => { baz: 'quix', }; + const mockGet = jest.fn(); + mockGet.mockImplementationOnce((...args: any[]) => { + if (args[0] === 'xpackReporting:customPdfLogo') { + return 'purple pony'; + } + throw new Error('wrong caller args!'); + }); + mockReportingPlugin.getUiSettingsServiceFactory = jest.fn().mockResolvedValue({ + get: mockGet, + }); + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayloadPDF, filteredHeaders: permittedHeaders, @@ -26,12 +41,12 @@ test(`gets logo from uiSettings`, async () => { }); const { logo } = await getCustomLogo({ + reporting: mockReportingPlugin, job: {} as JobDocPayloadPDF, conditionalHeaders, server: mockServer, }); - mockServer.uiSettingsServiceFactory().get.mockReturnValue(logo); - - expect(mockServer.uiSettingsServiceFactory().get).toBeCalledWith('xpackReporting:customPdfLogo'); + expect(mockGet).toBeCalledWith('xpackReporting:customPdfLogo'); + expect(logo).toBe('purple pony'); }); diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts index 0059276f6df71..7af5edab41ab7 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts @@ -5,14 +5,17 @@ */ import { UI_SETTINGS_CUSTOM_PDF_LOGO } from '../../../common/constants'; +import { ReportingCore } from '../../../server'; import { ConditionalHeaders, ServerFacade } from '../../../types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; // Logo is PDF only export const getCustomLogo = async ({ + reporting, server, job, conditionalHeaders, }: { + reporting: ReportingCore; server: ServerFacade; job: JobDocPayloadPDF; conditionalHeaders: ConditionalHeaders; @@ -27,19 +30,12 @@ export const getCustomLogo = async ({ getBasePath: () => job.basePath || serverBasePath, path: '/', route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, + url: { href: '/' }, + raw: { req: { url: '/' } }, }; - const savedObjects = server.savedObjects; - const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(fakeRequest); - const uiSettings = server.uiSettingsServiceFactory({ savedObjectsClient }); + const savedObjectsClient = await reporting.getSavedObjectsClient(fakeRequest); + const uiSettings = await reporting.getUiSettingsServiceFactory(savedObjectsClient); const logo: string = await uiSettings.get(UI_SETTINGS_CUSTOM_PDF_LOGO); return { conditionalHeaders, logo }; }; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts index e25b94e98d020..db9ca7bdcbea0 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../../../test_helpers/create_mock_server'; +import { createMockServer } from '../../../test_helpers'; import { ServerFacade } from '../../../types'; import { JobDocPayloadPNG } from '../../png/types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts index 9fd3ee391ddbb..62b5e29e88ecf 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts @@ -5,19 +5,19 @@ */ import * as Rx from 'rxjs'; -import { first, concatMap, take, toArray, mergeMap } from 'rxjs/operators'; -import { ServerFacade, CaptureConfig, HeadlessChromiumDriverFactory } from '../../../../types'; -import { ScreenshotResults, ScreenshotObservableOpts } from './types'; -import { injectCustomCss } from './inject_css'; -import { openUrl } from './open_url'; -import { waitForRenderComplete } from './wait_for_render'; -import { getNumberOfItems } from './get_number_of_items'; -import { waitForElementsToBeInDOM } from './wait_for_dom_elements'; -import { getTimeRange } from './get_time_range'; +import { concatMap, first, mergeMap, take, toArray } from 'rxjs/operators'; +import { CaptureConfig, HeadlessChromiumDriverFactory, ServerFacade } from '../../../../types'; import { getElementPositionAndAttributes } from './get_element_position_data'; +import { getNumberOfItems } from './get_number_of_items'; import { getScreenshots } from './get_screenshots'; +import { getTimeRange } from './get_time_range'; +import { injectCustomCss } from './inject_css'; +import { openUrl } from './open_url'; import { scanPage } from './scan_page'; import { skipTelemetry } from './skip_telemetry'; +import { ScreenshotObservableOpts, ScreenshotResults } from './types'; +import { waitForElementsToBeInDOM } from './wait_for_dom_elements'; +import { waitForRenderComplete } from './wait_for_render'; export function screenshotsObservableFactory( server: ServerFacade, diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/create_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/create_job.ts index 063ac7f77704c..7ea67277015ab 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/create_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/create_job.ts @@ -4,19 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ReportingCore } from '../../../server'; import { cryptoFactory } from '../../../server/lib/crypto'; import { - CreateJobFactory, ConditionalHeaders, - ServerFacade, - RequestFacade, + CreateJobFactory, ESQueueCreateJobFn, + RequestFacade, + ServerFacade, } from '../../../types'; import { JobParamsDiscoverCsv } from '../types'; export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(server: ServerFacade) { +>> = function createJobFactoryFn(reporting: ReportingCore, server: ServerFacade) { const crypto = cryptoFactory(server); return async function createJob( diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js index 537b366acb7ac..dd0d44be6f9f5 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js @@ -9,6 +9,7 @@ import sinon from 'sinon'; import nodeCrypto from '@elastic/node-crypto'; import { CancellationToken } from '../../../common/cancellation_token'; import { fieldFormats } from '../../../../../../../src/plugins/data/server'; +import { createMockReportingCore } from '../../../test_helpers'; import { LevelLogger } from '../../../server/lib/level_logger'; import { executeJobFactory } from './execute_job'; import { setFieldFormats } from '../../../server/services'; @@ -36,16 +37,19 @@ describe('CSV Execute Job', function() { let encryptedHeaders; let cancellationToken; + let mockReportingPlugin; let mockServer; let clusterStub; let callAsCurrentUserStub; - let uiSettingsGetStub; const mockElasticsearch = { dataClient: { asScoped: () => clusterStub, }, }; + const mockUiSettingsClient = { + get: sinon.stub(), + }; beforeAll(async function() { const crypto = nodeCrypto({ encryptionKey }); @@ -53,6 +57,8 @@ describe('CSV Execute Job', function() { }); beforeEach(async function() { + mockReportingPlugin = await createMockReportingCore(); + mockReportingPlugin.getUiSettingsServiceFactory = () => mockUiSettingsClient; cancellationToken = new CancellationToken(); defaultElasticsearchResponse = { @@ -70,9 +76,8 @@ describe('CSV Execute Job', function() { .resolves(defaultElasticsearchResponse); const configGetStub = sinon.stub(); - uiSettingsGetStub = sinon.stub(); - uiSettingsGetStub.withArgs('csv:separator').returns(','); - uiSettingsGetStub.withArgs('csv:quoteValues').returns(true); + mockUiSettingsClient.get.withArgs('csv:separator').returns(','); + mockUiSettingsClient.get.withArgs('csv:quoteValues').returns(true); setFieldFormats({ fieldFormatServiceFactory: function() { @@ -90,26 +95,11 @@ describe('CSV Execute Job', function() { }); mockServer = { - expose: function() {}, - plugins: { - elasticsearch: { - getCluster: function() { - return clusterStub; - }, - }, - }, config: function() { return { get: configGetStub, }; }, - savedObjects: { - getScopedSavedObjectsClient: sinon.stub(), - }, - uiSettingsServiceFactory: sinon.stub().returns({ - get: uiSettingsGetStub, - }), - log: function() {}, }; mockServer .config() @@ -125,83 +115,14 @@ describe('CSV Execute Job', function() { .returns({}); }); - describe('calls getScopedSavedObjectsClient with request', function() { - it('containing decrypted headers', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); - await executeJob( - 'job456', - { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, - cancellationToken - ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).toBe(true); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].headers).toEqual( - headers - ); - }); - - it(`containing getBasePath() returning server's basePath if the job doesn't have one`, async function() { - const serverBasePath = '/foo-server/basePath/'; - mockServer - .config() - .get.withArgs('server.basePath') - .returns(serverBasePath); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); - await executeJob( - 'job456', - { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, - cancellationToken - ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).toBe(true); - expect( - mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].getBasePath() - ).toEqual(serverBasePath); - }); - - it(`containing getBasePath() returning job's basePath if the job has one`, async function() { - const serverBasePath = '/foo-server/basePath/'; - mockServer - .config() - .get.withArgs('server.basePath') - .returns(serverBasePath); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); - const jobBasePath = 'foo-job/basePath/'; - await executeJob( - 'job789', - { - headers: encryptedHeaders, - fields: [], - searchRequest: { index: null, body: null }, - basePath: jobBasePath, - }, - cancellationToken - ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).toBe(true); - expect( - mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].getBasePath() - ).toEqual(jobBasePath); - }); - }); - - describe('uiSettings', function() { - it('passed scoped SavedObjectsClient to uiSettingsServiceFactory', async function() { - const returnValue = Symbol(); - mockServer.savedObjects.getScopedSavedObjectsClient.returns(returnValue); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); - await executeJob( - 'job456', - { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, - cancellationToken - ); - expect(mockServer.uiSettingsServiceFactory.calledOnce).toBe(true); - expect(mockServer.uiSettingsServiceFactory.firstCall.args[0].savedObjectsClient).toBe( - returnValue - ); - }); - }); - describe('basic Elasticsearch call behavior', function() { it('should decrypt encrypted headers and pass to callAsCurrentUser', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -217,7 +138,12 @@ describe('CSV Execute Job', function() { testBody: true, }; - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const job = { headers: encryptedHeaders, fields: [], @@ -244,7 +170,12 @@ describe('CSV Execute Job', function() { _scroll_id: scrollId, }); callAsCurrentUserStub.onSecondCall().resolves(defaultElasticsearchResponse); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -258,7 +189,12 @@ describe('CSV Execute Job', function() { }); it('should not execute scroll if there are no hits from the search', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -288,7 +224,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -323,7 +264,12 @@ describe('CSV Execute Job', function() { _scroll_id: lastScrollId, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -351,7 +297,12 @@ describe('CSV Execute Job', function() { _scroll_id: lastScrollId, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -381,7 +332,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -409,7 +365,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['=SUM(A1:A2)', 'two'], @@ -437,7 +398,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -465,7 +431,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -485,7 +456,12 @@ describe('CSV Execute Job', function() { describe('Elasticsearch call errors', function() { it('should reject Promise if search call errors out', async function() { callAsCurrentUserStub.rejects(new Error()); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -504,7 +480,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); callAsCurrentUserStub.onSecondCall().rejects(new Error()); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -525,7 +506,12 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -546,7 +532,12 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -574,7 +565,12 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -602,7 +598,12 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -639,7 +640,12 @@ describe('CSV Execute Job', function() { }); it('should stop calling Elasticsearch when cancellationToken.cancel is called', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -654,7 +660,12 @@ describe('CSV Execute Job', function() { }); it(`shouldn't call clearScroll if it never got a scrollId`, async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -668,7 +679,12 @@ describe('CSV Execute Job', function() { }); it('should call clearScroll if it got a scrollId', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -686,7 +702,12 @@ describe('CSV Execute Job', function() { describe('csv content', function() { it('should write column headers to output, even if there are no results', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -697,8 +718,13 @@ describe('CSV Execute Job', function() { }); it('should use custom uiSettings csv:separator for header', async function() { - uiSettingsGetStub.withArgs('csv:separator').returns(';'); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + mockUiSettingsClient.get.withArgs('csv:separator').returns(';'); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -709,8 +735,13 @@ describe('CSV Execute Job', function() { }); it('should escape column headers if uiSettings csv:quoteValues is true', async function() { - uiSettingsGetStub.withArgs('csv:quoteValues').returns(true); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + mockUiSettingsClient.get.withArgs('csv:quoteValues').returns(true); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one and a half', 'two', 'three-and-four', 'five & six'], @@ -721,8 +752,13 @@ describe('CSV Execute Job', function() { }); it(`shouldn't escape column headers if uiSettings csv:quoteValues is false`, async function() { - uiSettingsGetStub.withArgs('csv:quoteValues').returns(false); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + mockUiSettingsClient.get.withArgs('csv:quoteValues').returns(false); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one and a half', 'two', 'three-and-four', 'five & six'], @@ -733,7 +769,12 @@ describe('CSV Execute Job', function() { }); it('should write column headers to output, when there are results', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ one: '1', two: '2' }], @@ -753,7 +794,12 @@ describe('CSV Execute Job', function() { }); it('should use comma separated values of non-nested fields from _source', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -774,7 +820,12 @@ describe('CSV Execute Job', function() { }); it('should concatenate the hits from multiple responses', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -802,7 +853,12 @@ describe('CSV Execute Job', function() { }); it('should use field formatters to format fields', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -847,7 +903,12 @@ describe('CSV Execute Job', function() { .get.withArgs('xpack.reporting.csv.maxSizeBytes') .returns(1); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -880,7 +941,12 @@ describe('CSV Execute Job', function() { .get.withArgs('xpack.reporting.csv.maxSizeBytes') .returns(9); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -920,7 +986,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -949,6 +1020,7 @@ describe('CSV Execute Job', function() { let maxSizeReached; beforeEach(async function() { + mockReportingPlugin.getUiSettingsServiceFactory = () => mockUiSettingsClient; mockServer .config() .get.withArgs('xpack.reporting.csv.maxSizeBytes') @@ -961,7 +1033,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -1001,7 +1078,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -1030,7 +1112,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -1059,7 +1146,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts index 9f94a755cf655..1579985891053 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts @@ -4,20 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import Hapi from 'hapi'; import { i18n } from '@kbn/i18n'; -import { ElasticsearchServiceSetup, KibanaRequest } from '../../../../../../../src/core/server'; +import Hapi from 'hapi'; +import { + ElasticsearchServiceSetup, + IUiSettingsClient, + KibanaRequest, +} from '../../../../../../../src/core/server'; import { CSV_JOB_TYPE } from '../../../common/constants'; +import { ReportingCore } from '../../../server'; import { cryptoFactory } from '../../../server/lib'; +import { getFieldFormats } from '../../../server/services'; import { ESQueueWorkerExecuteFn, ExecuteJobFactory, Logger, ServerFacade } from '../../../types'; import { JobDocPayloadDiscoverCsv } from '../types'; import { fieldFormatMapFactory } from './lib/field_format_map'; import { createGenerateCsv } from './lib/generate_csv'; -import { getFieldFormats } from '../../../server/services'; export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn( +>> = async function executeJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, parentLogger: Logger @@ -40,83 +46,78 @@ export const executeJobFactory: ExecuteJobFactory { + let decryptedHeaders; + try { + decryptedHeaders = await crypto.decrypt(headers); + } catch (err) { + logger.error(err); + throw new Error( + i18n.translate( + 'xpack.reporting.exportTypes.csv.executeJob.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() }, + } + ) + ); // prettier-ignore + } + return decryptedHeaders; + }; - const fakeRequest = { - headers: decryptedHeaders, + const fakeRequest = KibanaRequest.from({ + headers: await decryptHeaders(), // This is used by the spaces SavedObjectClientWrapper to determine the existing space. // We use the basePath from the saved job, which we'll have post spaces being implemented; // or we use the server base path, which uses the default space getBasePath: () => basePath || serverBasePath, path: '/', route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }; + url: { href: '/' }, + raw: { req: { url: '/' } }, + } as Hapi.Request); + + const { callAsCurrentUser } = elasticsearch.dataClient.asScoped(fakeRequest); + const callEndpoint = (endpoint: string, clientParams = {}, options = {}) => + callAsCurrentUser(endpoint, clientParams, options); - const { callAsCurrentUser } = elasticsearch.dataClient.asScoped( - KibanaRequest.from(fakeRequest as Hapi.Request) - ); - const callEndpoint = (endpoint: string, clientParams = {}, options = {}) => { - return callAsCurrentUser(endpoint, clientParams, options); + const savedObjectsClient = await reporting.getSavedObjectsClient(fakeRequest); + const uiSettingsClient = await reporting.getUiSettingsServiceFactory(savedObjectsClient); + + const getFormatsMap = async (client: IUiSettingsClient) => { + const fieldFormats = await getFieldFormats().fieldFormatServiceFactory(client); + return fieldFormatMapFactory(indexPatternSavedObject, fieldFormats); }; - const savedObjects = server.savedObjects; - const savedObjectsClient = savedObjects.getScopedSavedObjectsClient( - (fakeRequest as unknown) as KibanaRequest - ); - const uiConfig = server.uiSettingsServiceFactory({ - savedObjectsClient, - }); + const getUiSettings = async (client: IUiSettingsClient) => { + const [separator, quoteValues, timezone] = await Promise.all([ + client.get('csv:separator'), + client.get('csv:quoteValues'), + client.get('dateFormat:tz'), + ]); - const [formatsMap, uiSettings] = await Promise.all([ - (async () => { - const fieldFormats = await getFieldFormats().fieldFormatServiceFactory(uiConfig); - return fieldFormatMapFactory(indexPatternSavedObject, fieldFormats); - })(), - (async () => { - const [separator, quoteValues, timezone] = await Promise.all([ - uiConfig.get('csv:separator'), - uiConfig.get('csv:quoteValues'), - uiConfig.get('dateFormat:tz'), - ]); + if (timezone === 'Browser') { + logger.warn( + i18n.translate('xpack.reporting.exportTypes.csv.executeJob.dateFormateSetting', { + defaultMessage: 'Kibana Advanced Setting "{dateFormatTimezone}" is set to "Browser". Dates will be formatted as UTC to avoid ambiguity.', + values: { dateFormatTimezone: 'dateFormat:tz' } + }) + ); // prettier-ignore + } - if (timezone === 'Browser') { - jobLogger.warn( - `Kibana Advanced Setting "dateFormat:tz" is set to "Browser". Dates will be formatted as UTC to avoid ambiguity.` - ); - } + return { + separator, + quoteValues, + timezone, + }; + }; - return { - separator, - quoteValues, - timezone, - }; - })(), + const [formatsMap, uiSettings] = await Promise.all([ + getFormatsMap(uiSettingsClient), + getUiSettings(uiSettingsClient), ]); const generateCsv = createGenerateCsv(jobLogger); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts index e1459e195d9f6..dac963635c469 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts @@ -9,15 +9,7 @@ import { FieldFormatConfig, IFieldFormatsRegistry, } from '../../../../../../../../src/plugins/data/server'; - -interface IndexPatternSavedObject { - attributes: { - fieldFormatMap: string; - }; - id: string; - type: string; - version: string; -} +import { IndexPatternSavedObject } from '../../../../types'; /** * Create a map of FieldFormat instances for index pattern fields diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts index ddef2aa0a6268..17072d311b35f 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts @@ -5,9 +5,10 @@ */ import { notFound, notImplemented } from 'boom'; -import { get } from 'lodash'; import { ElasticsearchServiceSetup } from 'kibana/server'; +import { get } from 'lodash'; import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../../common/constants'; +import { ReportingCore } from '../../../../server'; import { cryptoFactory } from '../../../../server/lib'; import { CreateJobFactory, @@ -37,6 +38,7 @@ interface VisData { export const createJobFactory: CreateJobFactory> = function createJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, parentLogger: Logger diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts index b1b7b7d818200..6bb3e73fcfe84 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { ElasticsearchServiceSetup } from 'kibana/server'; import { CONTENT_TYPE_CSV, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; +import { ReportingCore } from '../../../server'; import { cryptoFactory } from '../../../server/lib'; import { ExecuteJobFactory, @@ -22,13 +23,15 @@ import { createGenerateCsv } from './lib'; export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn( +>> = async function executeJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, parentLogger: Logger ) { const crypto = cryptoFactory(server); const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'execute-job']); + const generateCsv = createGenerateCsv(reporting, server, elasticsearch, parentLogger); return async function executeJob( jobId: string | null, @@ -86,11 +89,8 @@ export const executeJobFactory: ExecuteJobFactory { +const getEsQueryConfig = async (config: IUiSettingsClient) => { const configs = await Promise.all([ config.get('query:allowLeadingWildcards'), config.get('query:queryString:options'), @@ -49,7 +53,7 @@ const getEsQueryConfig = async (config: any) => { } as EsQueryConfig; }; -const getUiSettings = async (config: any) => { +const getUiSettings = async (config: IUiSettingsClient) => { const configs = await Promise.all([config.get('csv:separator'), config.get('csv:quoteValues')]); const [separator, quoteValues] = configs; return { separator, quoteValues }; @@ -57,14 +61,14 @@ const getUiSettings = async (config: any) => { export async function generateCsvSearch( req: RequestFacade, + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, logger: Logger, searchPanel: SearchPanel, jobParams: JobParamsDiscoverCsv ): Promise { - const { savedObjects, uiSettingsServiceFactory } = server; - const savedObjectsClient = savedObjects.getScopedSavedObjectsClient( + const savedObjectsClient = await reporting.getSavedObjectsClient( KibanaRequest.from(req.getRawRequest()) ); const { indexPatternSavedObjectId, timerange } = searchPanel; @@ -73,7 +77,8 @@ export async function generateCsvSearch( savedObjectsClient, indexPatternSavedObjectId ); - const uiConfig = uiSettingsServiceFactory({ savedObjectsClient }); + + const uiConfig = await reporting.getUiSettingsServiceFactory(savedObjectsClient); const esQueryConfig = await getEsQueryConfig(uiConfig); const { diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts index 3f03246106d3e..a6911e1f14704 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts @@ -4,20 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validateUrls } from '../../../../common/validate_urls'; +import { ReportingCore } from '../../../../server'; +import { cryptoFactory } from '../../../../server/lib/crypto'; import { + ConditionalHeaders, CreateJobFactory, - ServerFacade, - RequestFacade, ESQueueCreateJobFn, - ConditionalHeaders, + RequestFacade, + ServerFacade, } from '../../../../types'; -import { validateUrls } from '../../../../common/validate_urls'; -import { cryptoFactory } from '../../../../server/lib/crypto'; import { JobParamsPNG } from '../../types'; export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(server: ServerFacade) { +>> = function createJobFactoryFn(reporting: ReportingCore, server: ServerFacade) { const crypto = cryptoFactory(server); return async function createJob( diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js index bb33ef9c19a1d..c0c21119e1d53 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js @@ -6,6 +6,7 @@ import * as Rx from 'rxjs'; import { memoize } from 'lodash'; +import { createMockReportingCore } from '../../../../test_helpers'; import { cryptoFactory } from '../../../../server/lib/crypto'; import { executeJobFactory } from './index'; import { generatePngObservableFactory } from '../lib/generate_png'; @@ -19,7 +20,11 @@ const cancellationToken = { let config; let mockServer; -beforeEach(() => { +let mockReporting; + +beforeEach(async () => { + mockReporting = await createMockReportingCore(); + config = { 'xpack.reporting.encryptionKey': 'testencryptionkey', 'server.basePath': '/sbp', @@ -27,18 +32,11 @@ beforeEach(() => { 'server.port': 5601, }; mockServer = { - expose: () => {}, // NOTE: this is for oncePerServer config: memoize(() => ({ get: jest.fn() })), info: { protocol: 'http', }, - savedObjects: { - getScopedSavedObjectsClient: jest.fn(), - }, - uiSettingsServiceFactory: jest.fn().mockReturnValue({ get: jest.fn() }), - log: jest.fn(), }; - mockServer.config().get.mockImplementation(key => { return config[key]; }); @@ -67,9 +65,12 @@ test(`passes browserTimezone to generatePng`, async () => { const generatePngObservable = generatePngObservableFactory(); generatePngObservable.mockReturnValue(Rx.of(Buffer.from(''))); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger() + ); const browserTimezone = 'UTC'; await executeJob( 'pngJobId', @@ -87,9 +88,15 @@ test(`passes browserTimezone to generatePng`, async () => { }); test(`returns content_type of application/png`, async () => { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger(), + { + browserDriverFactory: {}, + } + ); const encryptedHeaders = await encryptHeaders({}); const generatePngObservable = generatePngObservableFactory(); @@ -109,9 +116,15 @@ test(`returns content of generatePng getBuffer base64 encoded`, async () => { const generatePngObservable = generatePngObservableFactory(); generatePngObservable.mockReturnValue(Rx.of(Buffer.from(testContent))); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger(), + { + browserDriverFactory: {}, + } + ); const encryptedHeaders = await encryptHeaders({}); const { content } = await executeJob( 'pngJobId', diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts index c9f370197da66..5cde245080914 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts @@ -7,14 +7,9 @@ import * as Rx from 'rxjs'; import { ElasticsearchServiceSetup } from 'kibana/server'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; +import { ReportingCore } from '../../../../server'; import { PNG_JOB_TYPE } from '../../../../common/constants'; -import { - ServerFacade, - ExecuteJobFactory, - ESQueueWorkerExecuteFn, - HeadlessChromiumDriverFactory, - Logger, -} from '../../../../types'; +import { ServerFacade, ExecuteJobFactory, ESQueueWorkerExecuteFn, Logger } from '../../../../types'; import { decryptJobHeaders, omitBlacklistedHeaders, @@ -26,12 +21,13 @@ import { generatePngObservableFactory } from '../lib/generate_png'; type QueuedPngExecutorFactory = ExecuteJobFactory>; -export const executeJobFactory: QueuedPngExecutorFactory = function executeJobFactoryFn( +export const executeJobFactory: QueuedPngExecutorFactory = async function executeJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - parentLogger: Logger, - { browserDriverFactory }: { browserDriverFactory: HeadlessChromiumDriverFactory } + parentLogger: Logger ) { + const browserDriverFactory = await reporting.getBrowserDriverFactory(); const generatePngObservable = generatePngObservableFactory(server, browserDriverFactory); const logger = parentLogger.clone([PNG_JOB_TYPE, 'execute']); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts index 22c28f14effd9..a5314882e285d 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts @@ -4,16 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validateUrls } from '../../../../common/validate_urls'; +import { ReportingCore } from '../../../../server'; +import { cryptoFactory } from '../../../../server/lib/crypto'; import { + ConditionalHeaders, CreateJobFactory, ESQueueCreateJobFn, - ServerFacade, - RequestFacade, - ConditionalHeaders, Logger, + RequestFacade, + ServerFacade, } from '../../../../types'; -import { validateUrls } from '../../../../common/validate_urls'; -import { cryptoFactory } from '../../../../server/lib/crypto'; import { JobParamsPDF } from '../../types'; // @ts-ignore untyped module import { compatibilityShimFactory } from './compatibility_shim'; @@ -28,7 +29,7 @@ interface CreateJobFnOpts { export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(server: ServerFacade, elasticsearch: unknown, logger: Logger) { +>> = function createJobFactoryFn(reporting: ReportingCore, server: ServerFacade, elasticsearch: unknown, logger: Logger) { const compatibilityShim = compatibilityShimFactory(server, logger); const crypto = cryptoFactory(server); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js index e917b5ce991a6..ebd3e8a403b53 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js @@ -6,6 +6,7 @@ import * as Rx from 'rxjs'; import { memoize } from 'lodash'; +import { createMockReportingCore } from '../../../../test_helpers'; import { cryptoFactory } from '../../../../server/lib/crypto'; import { executeJobFactory } from './index'; import { generatePdfObservableFactory } from '../lib/generate_pdf'; @@ -19,7 +20,11 @@ const cancellationToken = { let config; let mockServer; -beforeEach(() => { +let mockReporting; + +beforeEach(async () => { + mockReporting = await createMockReportingCore(); + config = { 'xpack.reporting.encryptionKey': 'testencryptionkey', 'server.basePath': '/sbp', @@ -27,17 +32,11 @@ beforeEach(() => { 'server.port': 5601, }; mockServer = { - expose: () => {}, config: memoize(() => ({ get: jest.fn() })), info: { protocol: 'http', }, - savedObjects: { - getScopedSavedObjectsClient: jest.fn(), - }, - uiSettingsServiceFactory: jest.fn().mockReturnValue({ get: jest.fn() }), }; - mockServer.config().get.mockImplementation(key => { return config[key]; }); @@ -59,38 +58,13 @@ const encryptHeaders = async headers => { return await crypto.encrypt(headers); }; -test(`passes browserTimezone to generatePdf`, async () => { - const encryptedHeaders = await encryptHeaders({}); - - const generatePdfObservable = generatePdfObservableFactory(); - generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(''))); - - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); - const browserTimezone = 'UTC'; - await executeJob( - 'pdfJobId', - { objects: [], browserTimezone, headers: encryptedHeaders }, - cancellationToken - ); - - expect(mockServer.uiSettingsServiceFactory().get).toBeCalledWith('xpackReporting:customPdfLogo'); - expect(generatePdfObservable).toBeCalledWith( - expect.any(LevelLogger), - undefined, - [], - browserTimezone, - expect.anything(), - undefined, - undefined - ); -}); - test(`returns content_type of application/pdf`, async () => { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger() + ); const encryptedHeaders = await encryptHeaders({}); const generatePdfObservable = generatePdfObservableFactory(); @@ -98,7 +72,7 @@ test(`returns content_type of application/pdf`, async () => { const { content_type: contentType } = await executeJob( 'pdfJobId', - { objects: [], timeRange: {}, headers: encryptedHeaders }, + { objects: [], timeRange: {}, headers: encryptedHeaders }, // 7.x and below only cancellationToken ); expect(contentType).toBe('application/pdf'); @@ -110,13 +84,16 @@ test(`returns content of generatePdf getBuffer base64 encoded`, async () => { const generatePdfObservable = generatePdfObservableFactory(); generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(testContent))); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger() + ); const encryptedHeaders = await encryptHeaders({}); const { content } = await executeJob( 'pdfJobId', - { objects: [], timeRange: {}, headers: encryptedHeaders }, + { objects: [], timeRange: {}, headers: encryptedHeaders }, // 7.x and below only cancellationToken ); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts index 162376e31216e..e8461862bee82 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts @@ -7,13 +7,8 @@ import * as Rx from 'rxjs'; import { ElasticsearchServiceSetup } from 'kibana/server'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; -import { - ServerFacade, - ExecuteJobFactory, - ESQueueWorkerExecuteFn, - HeadlessChromiumDriverFactory, - Logger, -} from '../../../../types'; +import { ReportingCore } from '../../../../server'; +import { ServerFacade, ExecuteJobFactory, ESQueueWorkerExecuteFn, Logger } from '../../../../types'; import { JobDocPayloadPDF } from '../../types'; import { PDF_JOB_TYPE } from '../../../../common/constants'; import { generatePdfObservableFactory } from '../lib/generate_pdf'; @@ -27,12 +22,13 @@ import { type QueuedPdfExecutorFactory = ExecuteJobFactory>; -export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFactoryFn( +export const executeJobFactory: QueuedPdfExecutorFactory = async function executeJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - parentLogger: Logger, - { browserDriverFactory }: { browserDriverFactory: HeadlessChromiumDriverFactory } + parentLogger: Logger ) { + const browserDriverFactory = await reporting.getBrowserDriverFactory(); const generatePdfObservable = generatePdfObservableFactory(server, browserDriverFactory); const logger = parentLogger.clone([PDF_JOB_TYPE, 'execute']); @@ -43,7 +39,7 @@ export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFa mergeMap(() => decryptJobHeaders({ server, job, logger })), map(decryptedHeaders => omitBlacklistedHeaders({ job, decryptedHeaders })), map(filteredHeaders => getConditionalHeaders({ server, job, filteredHeaders })), - mergeMap(conditionalHeaders => getCustomLogo({ server, job, conditionalHeaders })), + mergeMap(conditionalHeaders => getCustomLogo({ reporting, server, job, conditionalHeaders })), mergeMap(({ logo, conditionalHeaders }) => { const urls = getFullUrls({ server, job }); diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index cbafc4b1ecc4b..9ce4e807f8ef8 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -10,7 +10,7 @@ import { resolve } from 'path'; import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; import { config as reportingConfig } from './config'; import { legacyInit } from './server/legacy'; -import { ReportingConfigOptions, ReportingPluginSpecOptions } from './types.d'; +import { ReportingConfigOptions, ReportingPluginSpecOptions } from './types'; const kbToBase64Length = (kb: number) => { return Math.floor((kb * 1024 * 8) / 6); diff --git a/x-pack/legacy/plugins/reporting/reporting.d.ts b/x-pack/legacy/plugins/reporting/reporting.d.ts index d4a7943f6d067..ec65c15f53864 100644 --- a/x-pack/legacy/plugins/reporting/reporting.d.ts +++ b/x-pack/legacy/plugins/reporting/reporting.d.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ReportingPlugin } from './types'; +export { ReportingPlugin } from './server/plugin'; diff --git a/x-pack/legacy/plugins/reporting/server/core.ts b/x-pack/legacy/plugins/reporting/server/core.ts new file mode 100644 index 0000000000000..4506d41e4f5c3 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/core.ts @@ -0,0 +1,122 @@ +/* + * 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. + */ + +import * as Rx from 'rxjs'; +import { first, mapTo } from 'rxjs/operators'; +import { + IUiSettingsClient, + KibanaRequest, + SavedObjectsClient, + SavedObjectsServiceStart, + UiSettingsServiceStart, +} from 'src/core/server'; +// @ts-ignore no module definition +import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; +import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; +import { PLUGIN_ID } from '../common/constants'; +import { EnqueueJobFn, ESQueueInstance, ReportingPluginSpecOptions, ServerFacade } from '../types'; +import { HeadlessChromiumDriverFactory } from './browsers/chromium/driver_factory'; +import { checkLicenseFactory, getExportTypesRegistry, LevelLogger } from './lib'; +import { registerRoutes } from './routes'; +import { ReportingSetupDeps } from './types'; + +interface ReportingInternalSetup { + browserDriverFactory: HeadlessChromiumDriverFactory; +} +interface ReportingInternalStart { + savedObjects: SavedObjectsServiceStart; + uiSettings: UiSettingsServiceStart; + esqueue: ESQueueInstance; + enqueueJob: EnqueueJobFn; +} + +export class ReportingCore { + private pluginSetupDeps?: ReportingInternalSetup; + private pluginStartDeps?: ReportingInternalStart; + private readonly pluginSetup$ = new Rx.ReplaySubject(); + private readonly pluginStart$ = new Rx.ReplaySubject(); + private exportTypesRegistry = getExportTypesRegistry(); + + constructor(private logger: LevelLogger) {} + + legacySetup( + xpackMainPlugin: XPackMainPlugin, + reporting: ReportingPluginSpecOptions, + __LEGACY: ServerFacade, + plugins: ReportingSetupDeps + ) { + mirrorPluginStatus(xpackMainPlugin, reporting); + const checkLicense = checkLicenseFactory(this.exportTypesRegistry); + (xpackMainPlugin as any).status.once('green', () => { + // Register a function that is called whenever the xpack info changes, + // to re-compute the license check results for this plugin + xpackMainPlugin.info.feature(PLUGIN_ID).registerLicenseCheckResultsGenerator(checkLicense); + }); + // Reporting routes + registerRoutes(this, __LEGACY, plugins, this.logger); + } + + public pluginSetup(reportingSetupDeps: ReportingInternalSetup) { + this.pluginSetup$.next(reportingSetupDeps); + } + + public pluginStart(reportingStartDeps: ReportingInternalStart) { + this.pluginStart$.next(reportingStartDeps); + } + + public pluginHasStarted(): Promise { + return this.pluginStart$.pipe(first(), mapTo(true)).toPromise(); + } + + /* + * Internal module dependencies + */ + public getExportTypesRegistry() { + return this.exportTypesRegistry; + } + + public async getEsqueue(): Promise { + return (await this.getPluginStartDeps()).esqueue; + } + + public async getEnqueueJob(): Promise { + return (await this.getPluginStartDeps()).enqueueJob; + } + + public async getBrowserDriverFactory(): Promise { + return (await this.getPluginSetupDeps()).browserDriverFactory; + } + + /* + * Kibana core module dependencies + */ + private async getPluginSetupDeps() { + if (this.pluginSetupDeps) { + return this.pluginSetupDeps; + } + return await this.pluginSetup$.pipe(first()).toPromise(); + } + + private async getPluginStartDeps() { + if (this.pluginStartDeps) { + return this.pluginStartDeps; + } + return await this.pluginStart$.pipe(first()).toPromise(); + } + + public async getSavedObjectsClient(fakeRequest: KibanaRequest): Promise { + const { savedObjects } = await this.getPluginStartDeps(); + return savedObjects.getScopedClient(fakeRequest) as SavedObjectsClient; + } + + public async getUiSettingsServiceFactory( + savedObjectsClient: SavedObjectsClient + ): Promise { + const { uiSettings: uiSettingsService } = await this.getPluginStartDeps(); + const scopedUiSettingsService = uiSettingsService.asScopedToClient(savedObjectsClient); + return scopedUiSettingsService; + } +} diff --git a/x-pack/legacy/plugins/reporting/server/index.ts b/x-pack/legacy/plugins/reporting/server/index.ts index 438a3fd595a10..24e2a954415d9 100644 --- a/x-pack/legacy/plugins/reporting/server/index.ts +++ b/x-pack/legacy/plugins/reporting/server/index.ts @@ -10,3 +10,6 @@ import { ReportingPlugin as Plugin } from './plugin'; export const plugin = (context: PluginInitializerContext) => { return new Plugin(context); }; + +export { ReportingCore } from './core'; +export { ReportingPlugin } from './plugin'; diff --git a/x-pack/legacy/plugins/reporting/server/legacy.ts b/x-pack/legacy/plugins/reporting/server/legacy.ts index c80aef06cf270..336ff5f4d2ee7 100644 --- a/x-pack/legacy/plugins/reporting/server/legacy.ts +++ b/x-pack/legacy/plugins/reporting/server/legacy.ts @@ -8,7 +8,7 @@ import { PluginInitializerContext } from 'src/core/server'; import { SecurityPluginSetup } from '../../../../plugins/security/server'; import { ReportingPluginSpecOptions } from '../types'; import { plugin } from './index'; -import { LegacySetup, ReportingStartDeps } from './plugin'; +import { LegacySetup, ReportingStartDeps } from './types'; const buildLegacyDependencies = ( server: Legacy.Server, @@ -22,8 +22,6 @@ const buildLegacyDependencies = ( xpack_main: server.plugins.xpack_main, reporting: reportingPlugin, }, - savedObjects: server.savedObjects, - uiSettingsServiceFactory: server.uiSettingsServiceFactory, }); export const legacyInit = async ( @@ -33,17 +31,20 @@ export const legacyInit = async ( const coreSetup = server.newPlatform.setup.core; const pluginInstance = plugin(server.newPlatform.coreContext as PluginInitializerContext); + const __LEGACY = buildLegacyDependencies(server, reportingPlugin); await pluginInstance.setup(coreSetup, { elasticsearch: coreSetup.elasticsearch, security: server.newPlatform.setup.plugins.security as SecurityPluginSetup, usageCollection: server.newPlatform.setup.plugins.usageCollection, - __LEGACY: buildLegacyDependencies(server, reportingPlugin), + __LEGACY, }); // Schedule to call the "start" hook only after start dependencies are ready coreSetup.getStartServices().then(([core, plugins]) => pluginInstance.start(core, { + elasticsearch: coreSetup.elasticsearch, data: (plugins as ReportingStartDeps).data, + __LEGACY, }) ); }; diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts b/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts index c4e32b3ebcd99..d593e4625cdf4 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts @@ -5,29 +5,19 @@ */ import { ElasticsearchServiceSetup } from 'kibana/server'; -import { - ServerFacade, - ExportTypesRegistry, - HeadlessChromiumDriverFactory, - QueueConfig, - Logger, -} from '../../types'; +import { ESQueueInstance, ServerFacade, QueueConfig, Logger } from '../../types'; +import { ReportingCore } from '../core'; // @ts-ignore import { Esqueue } from './esqueue'; import { createWorkerFactory } from './create_worker'; import { createTaggedLogger } from './create_tagged_logger'; // TODO remove createTaggedLogger once esqueue is removed -interface CreateQueueFactoryOpts { - exportTypesRegistry: ExportTypesRegistry; - browserDriverFactory: HeadlessChromiumDriverFactory; -} - -export function createQueueFactory( +export async function createQueueFactory( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - logger: Logger, - { exportTypesRegistry, browserDriverFactory }: CreateQueueFactoryOpts -): Esqueue { + logger: Logger +): Promise { const queueConfig: QueueConfig = server.config().get('xpack.reporting.queue'); const index = server.config().get('xpack.reporting.index'); @@ -39,15 +29,12 @@ export function createQueueFactory( logger: createTaggedLogger(logger, ['esqueue', 'queue-worker']), }; - const queue: Esqueue = new Esqueue(index, queueOptions); + const queue: ESQueueInstance = new Esqueue(index, queueOptions); if (queueConfig.pollEnabled) { // create workers to poll the index for idle jobs waiting to be claimed and executed - const createWorker = createWorkerFactory(server, elasticsearch, logger, { - exportTypesRegistry, - browserDriverFactory, - }); - createWorker(queue); + const createWorker = createWorkerFactory(reporting, server, elasticsearch, logger); + await createWorker(queue); } else { logger.info( 'xpack.reporting.queue.pollEnabled is set to false. This Kibana instance ' + diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts index f5c42e5505cd1..d4d913243e18d 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts @@ -4,9 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as sinon from 'sinon'; import { ElasticsearchServiceSetup } from 'kibana/server'; -import { HeadlessChromiumDriverFactory, ServerFacade } from '../../types'; +import * as sinon from 'sinon'; +import { ReportingCore } from '../../server'; +import { createMockReportingCore } from '../../test_helpers'; +import { ServerFacade } from '../../types'; import { createWorkerFactory } from './create_worker'; // @ts-ignore import { Esqueue } from './esqueue'; @@ -33,34 +35,34 @@ const getMockLogger = jest.fn(); const getMockExportTypesRegistry = ( exportTypes: any[] = [{ executeJobFactory: executeJobFactoryStub }] -) => ({ - getAll: () => exportTypes, -}); +) => + ({ + getAll: () => exportTypes, + } as ExportTypesRegistry); describe('Create Worker', () => { let queue: Esqueue; let client: ClientMock; + let mockReporting: ReportingCore; - beforeEach(() => { + beforeEach(async () => { + mockReporting = await createMockReportingCore(); client = new ClientMock(); queue = new Esqueue('reporting-queue', { client }); executeJobFactoryStub.reset(); }); test('Creates a single Esqueue worker for Reporting', async () => { - const exportTypesRegistry = getMockExportTypesRegistry(); + mockReporting.getExportTypesRegistry = () => getMockExportTypesRegistry(); const createWorker = createWorkerFactory( + mockReporting, getMockServer(), {} as ElasticsearchServiceSetup, - getMockLogger(), - { - exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry, - browserDriverFactory: {} as HeadlessChromiumDriverFactory, - } + getMockLogger() ); const registerWorkerSpy = sinon.spy(queue, 'registerWorker'); - createWorker(queue); + await createWorker(queue); sinon.assert.callCount(executeJobFactoryStub, 1); sinon.assert.callCount(registerWorkerSpy, 1); @@ -88,18 +90,16 @@ Object { { executeJobFactory: executeJobFactoryStub }, { executeJobFactory: executeJobFactoryStub }, ]); + mockReporting.getExportTypesRegistry = () => exportTypesRegistry; const createWorker = createWorkerFactory( + mockReporting, getMockServer(), {} as ElasticsearchServiceSetup, - getMockLogger(), - { - exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry, - browserDriverFactory: {} as HeadlessChromiumDriverFactory, - } + getMockLogger() ); const registerWorkerSpy = sinon.spy(queue, 'registerWorker'); - createWorker(queue); + await createWorker(queue); sinon.assert.callCount(executeJobFactoryStub, 5); sinon.assert.callCount(registerWorkerSpy, 1); diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts index 2ca638f641291..3567712367608 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts @@ -5,34 +5,29 @@ */ import { ElasticsearchServiceSetup } from 'kibana/server'; -import { PLUGIN_ID } from '../../common/constants'; -import { ExportTypesRegistry, HeadlessChromiumDriverFactory } from '../../types'; import { CancellationToken } from '../../common/cancellation_token'; +import { PLUGIN_ID } from '../../common/constants'; import { ESQueueInstance, - QueueConfig, - ExportTypeDefinition, ESQueueWorkerExecuteFn, - JobDocPayload, + ExportTypeDefinition, ImmediateExecuteFn, + JobDocPayload, JobSource, + Logger, + QueueConfig, RequestFacade, ServerFacade, - Logger, } from '../../types'; +import { ReportingCore } from '../core'; // @ts-ignore untyped dependency import { events as esqueueEvents } from './esqueue'; -interface CreateWorkerFactoryOpts { - exportTypesRegistry: ExportTypesRegistry; - browserDriverFactory: HeadlessChromiumDriverFactory; -} - export function createWorkerFactory( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - logger: Logger, - { exportTypesRegistry, browserDriverFactory }: CreateWorkerFactoryOpts + logger: Logger ) { type JobDocPayloadType = JobDocPayload; const config = server.config(); @@ -41,20 +36,23 @@ export function createWorkerFactory( const kibanaId: string = config.get('server.uuid'); // Once more document types are added, this will need to be passed in - return function createWorker(queue: ESQueueInstance) { + return async function createWorker(queue: ESQueueInstance) { // export type / execute job map const jobExecutors: Map< string, ImmediateExecuteFn | ESQueueWorkerExecuteFn > = new Map(); - for (const exportType of exportTypesRegistry.getAll() as Array< - ExportTypeDefinition + for (const exportType of reporting.getExportTypesRegistry().getAll() as Array< + ExportTypeDefinition >) { // TODO: the executeJobFn should be unwrapped in the register method of the export types registry - const jobExecutor = exportType.executeJobFactory(server, elasticsearch, logger, { - browserDriverFactory, - }); + const jobExecutor = await exportType.executeJobFactory( + reporting, + server, + elasticsearch, + logger + ); jobExecutors.set(exportType.jobType, jobExecutor); } diff --git a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts index 1da8a3795aacc..c215bdc398904 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts @@ -16,11 +16,11 @@ import { ServerFacade, RequestFacade, Logger, - ExportTypesRegistry, CaptureConfig, QueueConfig, ConditionalHeaders, } from '../../types'; +import { ReportingCore } from '../core'; interface ConfirmedJob { id: string; @@ -29,16 +29,11 @@ interface ConfirmedJob { _primary_term: number; } -interface EnqueueJobFactoryOpts { - exportTypesRegistry: ExportTypesRegistry; - esqueue: any; -} - export function enqueueJobFactory( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - parentLogger: Logger, - { exportTypesRegistry, esqueue }: EnqueueJobFactoryOpts + parentLogger: Logger ): EnqueueJobFn { const logger = parentLogger.clone(['queue-job']); const config = server.config(); @@ -56,14 +51,20 @@ export function enqueueJobFactory( ): Promise { type CreateJobFn = ESQueueCreateJobFn | ImmediateCreateJobFn; - const exportType = exportTypesRegistry.getById(exportTypeId); + const esqueue = await reporting.getEsqueue(); + const exportType = reporting.getExportTypesRegistry().getById(exportTypeId); if (exportType == null) { throw new Error(`Export type ${exportTypeId} does not exist in the registry!`); } // TODO: the createJobFn should be unwrapped in the register method of the export types registry - const createJob = exportType.createJobFactory(server, elasticsearch, logger) as CreateJobFn; + const createJob = exportType.createJobFactory( + reporting, + server, + elasticsearch, + logger + ) as CreateJobFn; const payload = await createJob(jobParams, headers, request); const options = { diff --git a/x-pack/legacy/plugins/reporting/server/lib/get_user.ts b/x-pack/legacy/plugins/reporting/server/lib/get_user.ts index ab02dfe0743f0..49d5c568c3981 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/get_user.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/get_user.ts @@ -7,7 +7,7 @@ import { Legacy } from 'kibana'; import { KibanaRequest } from '../../../../../../src/core/server'; import { ServerFacade } from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps } from '../types'; export function getUserFactory(server: ServerFacade, security: ReportingSetupDeps['security']) { /* diff --git a/x-pack/legacy/plugins/reporting/server/lib/validate/index.ts b/x-pack/legacy/plugins/reporting/server/lib/validate/index.ts index 028d8fa143487..0fdbd858b8e3c 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/validate/index.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/validate/index.ts @@ -16,8 +16,8 @@ import { validateServerHost } from './validate_server_host'; export async function runValidations( server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - logger: Logger, - browserFactory: HeadlessChromiumDriverFactory + browserFactory: HeadlessChromiumDriverFactory, + logger: Logger ) { try { await Promise.all([ @@ -32,6 +32,7 @@ export async function runValidations( }) ); } catch (err) { + logger.error(err); logger.warning( i18n.translate('xpack.reporting.selfCheck.warning', { defaultMessage: `Reporting plugin self-check generated a warning: {err}`, diff --git a/x-pack/legacy/plugins/reporting/server/plugin.ts b/x-pack/legacy/plugins/reporting/server/plugin.ts index ef7b01f8e9c15..4f24cc16b2277 100644 --- a/x-pack/legacy/plugins/reporting/server/plugin.ts +++ b/x-pack/legacy/plugins/reporting/server/plugin.ts @@ -4,97 +4,66 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Legacy } from 'kibana'; -import { - CoreSetup, - CoreStart, - ElasticsearchServiceSetup, - Plugin, - PluginInitializerContext, -} from 'src/core/server'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/server'; -import { SecurityPluginSetup } from '../../../../plugins/security/server'; -// @ts-ignore -import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; -import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; -import { PLUGIN_ID } from '../common/constants'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/server'; import { logConfiguration } from '../log_configuration'; -import { ReportingPluginSpecOptions } from '../types.d'; import { createBrowserDriverFactory } from './browsers'; -import { checkLicenseFactory, getExportTypesRegistry, LevelLogger, runValidations } from './lib'; -import { registerRoutes } from './routes'; +import { ReportingCore } from './core'; +import { createQueueFactory, enqueueJobFactory, LevelLogger, runValidations } from './lib'; import { setFieldFormats } from './services'; +import { ReportingSetup, ReportingSetupDeps, ReportingStart, ReportingStartDeps } from './types'; import { registerReportingUsageCollector } from './usage'; +// @ts-ignore no module definition +import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; -export interface ReportingSetupDeps { - elasticsearch: ElasticsearchServiceSetup; - usageCollection: UsageCollectionSetup; - security: SecurityPluginSetup; - __LEGACY: LegacySetup; -} - -export interface ReportingStartDeps { - data: DataPluginStart; -} - -export interface LegacySetup { - config: Legacy.Server['config']; - info: Legacy.Server['info']; - plugins: { - elasticsearch: Legacy.Server['plugins']['elasticsearch']; - xpack_main: XPackMainPlugin & { - status?: any; - }; - reporting: ReportingPluginSpecOptions; - }; - route: Legacy.Server['route']; - savedObjects: Legacy.Server['savedObjects']; - uiSettingsServiceFactory: Legacy.Server['uiSettingsServiceFactory']; -} +export class ReportingPlugin + implements Plugin { + private logger: LevelLogger; + private reportingCore: ReportingCore; -export class ReportingPlugin implements Plugin { - constructor(private context: PluginInitializerContext) {} + constructor(context: PluginInitializerContext) { + this.logger = new LevelLogger(context.logger.get('reporting')); + this.reportingCore = new ReportingCore(this.logger); + } public async setup(core: CoreSetup, plugins: ReportingSetupDeps) { const { elasticsearch, usageCollection, __LEGACY } = plugins; - const exportTypesRegistry = getExportTypesRegistry(); - let isCollectorReady = false; + const browserDriverFactory = await createBrowserDriverFactory(__LEGACY, this.logger); // required for validations :( + runValidations(__LEGACY, elasticsearch, browserDriverFactory, this.logger); // this must run early, as it sets up config defaults + + const { xpack_main: xpackMainLegacy, reporting: reportingLegacy } = __LEGACY.plugins; + this.reportingCore.legacySetup(xpackMainLegacy, reportingLegacy, __LEGACY, plugins); // Register a function with server to manage the collection of usage stats - registerReportingUsageCollector( - usageCollection, - __LEGACY, - () => isCollectorReady, - exportTypesRegistry - ); + registerReportingUsageCollector(this.reportingCore, __LEGACY, usageCollection); - const logger = new LevelLogger(this.context.logger.get('reporting')); - const browserDriverFactory = await createBrowserDriverFactory(__LEGACY, logger); + // regsister setup internals + this.reportingCore.pluginSetup({ browserDriverFactory }); - logConfiguration(__LEGACY, logger); - runValidations(__LEGACY, elasticsearch, logger, browserDriverFactory); + return {}; + } - const { xpack_main: xpackMainPlugin, reporting } = __LEGACY.plugins; - mirrorPluginStatus(xpackMainPlugin, reporting); + public async start(core: CoreStart, plugins: ReportingStartDeps) { + const { reportingCore, logger } = this; + const { elasticsearch, __LEGACY } = plugins; - const checkLicense = checkLicenseFactory(exportTypesRegistry); + const esqueue = await createQueueFactory(reportingCore, __LEGACY, elasticsearch, logger); + const enqueueJob = enqueueJobFactory(reportingCore, __LEGACY, elasticsearch, logger); - (xpackMainPlugin as any).status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(PLUGIN_ID).registerLicenseCheckResultsGenerator(checkLicense); + this.reportingCore.pluginStart({ + savedObjects: core.savedObjects, + uiSettings: core.uiSettings, + esqueue, + enqueueJob, }); - // Post initialization of the above code, the collector is now ready to fetch its data - isCollectorReady = true; + setFieldFormats(plugins.data.fieldFormats); + logConfiguration(__LEGACY, this.logger); - // Reporting routes - registerRoutes(__LEGACY, plugins, exportTypesRegistry, browserDriverFactory, logger); + return {}; } - public start(core: CoreStart, plugins: ReportingStartDeps) { - setFieldFormats(plugins.data.fieldFormats); + public getReportingCore() { + return this.reportingCore; } } diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts index ed761b1e684ae..49868bb7ad5d5 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts @@ -10,7 +10,7 @@ import { Legacy } from 'kibana'; import rison from 'rison-node'; import { API_BASE_URL } from '../../common/constants'; import { Logger, ReportingResponseToolkit, ServerFacade } from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps } from '../types'; import { makeRequestFacade } from './lib/make_request_facade'; import { GetRouteConfigFactoryFn, diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts index 8696f36a45c62..415b6b7d64366 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -9,7 +9,7 @@ import { get } from 'lodash'; import { API_BASE_GENERATE_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants'; import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request'; import { Logger, ReportingResponseToolkit, ServerFacade } from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps } from '../types'; import { makeRequestFacade } from './lib/make_request_facade'; import { getRouteOptionsCsv } from './lib/route_config_factories'; import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types'; diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts index fd1d85fef0f21..5d17fa2e82b8c 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts @@ -10,14 +10,13 @@ import { createJobFactory, executeJobFactory } from '../../export_types/csv_from import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request'; import { JobDocPayloadPanelCsv } from '../../export_types/csv_from_savedobject/types'; import { - HeadlessChromiumDriverFactory, JobDocOutput, Logger, ReportingResponseToolkit, ResponseFacade, ServerFacade, } from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps, ReportingCore } from '../types'; import { makeRequestFacade } from './lib/make_request_facade'; import { getRouteOptionsCsv } from './lib/route_config_factories'; @@ -31,6 +30,7 @@ import { getRouteOptionsCsv } from './lib/route_config_factories'; * - local (transient) changes the user made to the saved object */ export function registerGenerateCsvFromSavedObjectImmediate( + reporting: ReportingCore, server: ServerFacade, plugins: ReportingSetupDeps, parentLogger: Logger @@ -58,10 +58,8 @@ export function registerGenerateCsvFromSavedObjectImmediate( * * Calling an execute job factory requires passing a browserDriverFactory option, so we should not call the factory from here */ - const createJobFn = createJobFactory(server, elasticsearch, logger); - const executeJobFn = executeJobFactory(server, elasticsearch, logger, { - browserDriverFactory: {} as HeadlessChromiumDriverFactory, - }); + const createJobFn = createJobFactory(reporting, server, elasticsearch, logger); + const executeJobFn = await executeJobFactory(reporting, server, elasticsearch, logger); const jobDocPayload: JobDocPayloadPanelCsv = await createJobFn( jobParams, request.headers, diff --git a/x-pack/legacy/plugins/reporting/server/routes/generation.ts b/x-pack/legacy/plugins/reporting/server/routes/generation.ts index 6e9a8e24db467..f3a8f12119b7d 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generation.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generation.ts @@ -8,15 +8,8 @@ import boom from 'boom'; import { errors as elasticsearchErrors } from 'elasticsearch'; import { Legacy } from 'kibana'; import { API_BASE_URL } from '../../common/constants'; -import { - ExportTypesRegistry, - HeadlessChromiumDriverFactory, - Logger, - ReportingResponseToolkit, - ServerFacade, -} from '../../types'; -import { createQueueFactory, enqueueJobFactory } from '../lib'; -import { ReportingSetupDeps } from '../plugin'; +import { Logger, ReportingResponseToolkit, ServerFacade } from '../../types'; +import { ReportingSetupDeps, ReportingCore } from '../types'; import { registerGenerateFromJobParams } from './generate_from_jobparams'; import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject'; import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; @@ -26,23 +19,13 @@ import { makeRequestFacade } from './lib/make_request_facade'; const esErrors = elasticsearchErrors as Record; export function registerJobGenerationRoutes( + reporting: ReportingCore, server: ServerFacade, plugins: ReportingSetupDeps, - exportTypesRegistry: ExportTypesRegistry, - browserDriverFactory: HeadlessChromiumDriverFactory, logger: Logger ) { const config = server.config(); const DOWNLOAD_BASE_URL = config.get('server.basePath') + `${API_BASE_URL}/jobs/download`; - const { elasticsearch } = plugins; - const esqueue = createQueueFactory(server, elasticsearch, logger, { - exportTypesRegistry, - browserDriverFactory, - }); - const enqueueJob = enqueueJobFactory(server, elasticsearch, logger, { - exportTypesRegistry, - esqueue, - }); /* * Generates enqueued job details to use in responses @@ -57,6 +40,7 @@ export function registerJobGenerationRoutes( const user = request.pre.user; const headers = request.headers; + const enqueueJob = await reporting.getEnqueueJob(); const job = await enqueueJob(exportTypeId, jobParams, user, headers, request); // return the queue's job information @@ -89,6 +73,6 @@ export function registerJobGenerationRoutes( // Register beta panel-action download-related API's if (config.get('xpack.reporting.csv.enablePanelActionDownload')) { registerGenerateCsvFromSavedObject(server, plugins, handler, handleError, logger); - registerGenerateCsvFromSavedObjectImmediate(server, plugins, logger); + registerGenerateCsvFromSavedObjectImmediate(reporting, server, plugins, logger); } } diff --git a/x-pack/legacy/plugins/reporting/server/routes/index.ts b/x-pack/legacy/plugins/reporting/server/routes/index.ts index 4cfa9dd465eab..610ab4907d369 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/index.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/index.ts @@ -4,23 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - ExportTypesRegistry, - HeadlessChromiumDriverFactory, - Logger, - ServerFacade, -} from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { Logger, ServerFacade } from '../../types'; +import { ReportingCore, ReportingSetupDeps } from '../types'; import { registerJobGenerationRoutes } from './generation'; import { registerJobInfoRoutes } from './jobs'; export function registerRoutes( + reporting: ReportingCore, server: ServerFacade, plugins: ReportingSetupDeps, - exportTypesRegistry: ExportTypesRegistry, - browserDriverFactory: HeadlessChromiumDriverFactory, logger: Logger ) { - registerJobGenerationRoutes(server, plugins, exportTypesRegistry, browserDriverFactory, logger); - registerJobInfoRoutes(server, plugins, exportTypesRegistry, logger); + registerJobGenerationRoutes(reporting, server, plugins, logger); + registerJobInfoRoutes(reporting, server, plugins, logger); } diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js index 811c81c502b81..071b401d2321b 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js @@ -5,30 +5,30 @@ */ import Hapi from 'hapi'; -import { difference, memoize } from 'lodash'; -import { registerJobInfoRoutes } from './jobs'; +import { memoize } from 'lodash'; +import { createMockReportingCore } from '../../test_helpers'; import { ExportTypesRegistry } from '../lib/export_types_registry'; -jest.mock('./lib/authorized_user_pre_routing', () => { - return { - authorizedUserPreRoutingFactory: () => () => ({}), - }; -}); -jest.mock('./lib/reporting_feature_pre_routing', () => { - return { - reportingFeaturePreRoutingFactory: () => () => () => ({ - jobTypes: ['unencodedJobType', 'base64EncodedJobType'], - }), - }; -}); + +jest.mock('./lib/authorized_user_pre_routing', () => ({ + authorizedUserPreRoutingFactory: () => () => ({}), +})); +jest.mock('./lib/reporting_feature_pre_routing', () => ({ + reportingFeaturePreRoutingFactory: () => () => () => ({ + jobTypes: ['unencodedJobType', 'base64EncodedJobType'], + }), +})); + +import { registerJobInfoRoutes } from './jobs'; let mockServer; let exportTypesRegistry; +let mockReportingPlugin; const mockLogger = { error: jest.fn(), debug: jest.fn(), }; -beforeEach(() => { +beforeEach(async () => { mockServer = new Hapi.Server({ debug: false, port: 8080, routes: { log: { collect: true } } }); mockServer.config = memoize(() => ({ get: jest.fn() })); exportTypesRegistry = new ExportTypesRegistry(); @@ -43,6 +43,8 @@ beforeEach(() => { jobContentEncoding: 'base64', jobContentExtension: 'pdf', }); + mockReportingPlugin = await createMockReportingCore(); + mockReportingPlugin.getExportTypesRegistry = () => exportTypesRegistry; }); const mockPlugins = { @@ -60,12 +62,15 @@ const getHits = (...sources) => { }; }; +const getErrorsFromRequest = request => + request.logs.filter(log => log.tags.includes('error')).map(log => log.error); + test(`returns 404 if job not found`, async () => { mockPlugins.elasticsearch.adminClient = { callAsInternalUser: jest.fn().mockReturnValue(Promise.resolve(getHits())), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -84,7 +89,7 @@ test(`returns 401 if not valid job type`, async () => { .mockReturnValue(Promise.resolve(getHits({ jobtype: 'invalidJobType' }))), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -105,7 +110,7 @@ describe(`when job is incomplete`, () => { ), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -147,7 +152,7 @@ describe(`when job is failed`, () => { callAsInternalUser: jest.fn().mockReturnValue(Promise.resolve(hits)), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -192,7 +197,7 @@ describe(`when job is completed`, () => { callAsInternalUser: jest.fn().mockReturnValue(Promise.resolve(hits)), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -203,72 +208,115 @@ describe(`when job is completed`, () => { }; test(`sets statusCode to 200`, async () => { - const { statusCode } = await getCompletedResponse(); + const { statusCode, request } = await getCompletedResponse(); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(statusCode).toBe(200); }); test(`doesn't encode output content for not-specified jobTypes`, async () => { - const { payload } = await getCompletedResponse({ + const { payload, request } = await getCompletedResponse({ jobType: 'unencodedJobType', outputContent: 'test', }); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); + expect(payload).toBe('test'); }); test(`base64 encodes output content for configured jobTypes`, async () => { - const { payload } = await getCompletedResponse({ + const { payload, request } = await getCompletedResponse({ jobType: 'base64EncodedJobType', outputContent: 'test', }); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); + expect(payload).toBe(Buffer.from('test', 'base64').toString()); }); test(`specifies text/csv; charset=utf-8 contentType header from the job output`, async () => { - const { headers } = await getCompletedResponse({ outputContentType: 'text/csv' }); + const { headers, request } = await getCompletedResponse({ outputContentType: 'text/csv' }); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); + expect(headers['content-type']).toBe('text/csv; charset=utf-8'); }); test(`specifies default filename in content-disposition header if no title`, async () => { - const { headers } = await getCompletedResponse({}); + const { headers, request } = await getCompletedResponse({}); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(headers['content-disposition']).toBe('inline; filename="report.csv"'); }); test(`specifies payload title in content-disposition header`, async () => { - const { headers } = await getCompletedResponse({ title: 'something' }); + const { headers, request } = await getCompletedResponse({ title: 'something' }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(headers['content-disposition']).toBe('inline; filename="something.csv"'); }); test(`specifies jobContentExtension in content-disposition header`, async () => { - const { headers } = await getCompletedResponse({ jobType: 'base64EncodedJobType' }); + const { headers, request } = await getCompletedResponse({ jobType: 'base64EncodedJobType' }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(headers['content-disposition']).toBe('inline; filename="report.pdf"'); }); test(`specifies application/pdf contentType header from the job output`, async () => { - const { headers } = await getCompletedResponse({ outputContentType: 'application/pdf' }); + const { headers, request } = await getCompletedResponse({ + outputContentType: 'application/pdf', + }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(headers['content-type']).toBe('application/pdf'); }); describe(`when non-whitelisted contentType specified in job output`, () => { test(`sets statusCode to 500`, async () => { - const { statusCode } = await getCompletedResponse({ outputContentType: 'application/html' }); + const { statusCode, request } = await getCompletedResponse({ + outputContentType: 'application/html', + }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: Unsupported content-type of application/html specified by job output], + [Error: Unsupported content-type of application/html specified by job output], + ] + `); expect(statusCode).toBe(500); }); test(`doesn't include job output content in payload`, async () => { - const { payload } = await getCompletedResponse({ outputContentType: 'application/html' }); - expect(payload).not.toMatch(/job output content/); + const { payload, request } = await getCompletedResponse({ + outputContentType: 'application/html', + }); + expect(payload).toMatchInlineSnapshot( + `"{\\"statusCode\\":500,\\"error\\":\\"Internal Server Error\\",\\"message\\":\\"An internal server error occurred\\"}"` + ); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: Unsupported content-type of application/html specified by job output], + [Error: Unsupported content-type of application/html specified by job output], + ] + `); }); test(`logs error message about invalid content type`, async () => { - const { - request: { logs }, - } = await getCompletedResponse({ outputContentType: 'application/html' }); - const errorLogs = logs.filter( - log => difference(['internal', 'implementation', 'error'], log.tags).length === 0 - ); - expect(errorLogs).toHaveLength(1); - expect(errorLogs[0].error).toBeInstanceOf(Error); - expect(errorLogs[0].error.message).toMatch(/Unsupported content-type of application\/html/); + const { request } = await getCompletedResponse({ outputContentType: 'application/html' }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: Unsupported content-type of application/html specified by job output], + [Error: Unsupported content-type of application/html specified by job output], + ] + `); }); }); }); diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts index daabc2cf22f4e..2de420e6577c3 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts @@ -9,7 +9,6 @@ import { ResponseObject } from 'hapi'; import { Legacy } from 'kibana'; import { API_BASE_URL } from '../../common/constants'; import { - ExportTypesRegistry, JobDocOutput, JobSource, ListQuery, @@ -18,7 +17,7 @@ import { ServerFacade, } from '../../types'; import { jobsQueryFactory } from '../lib/jobs_query'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps, ReportingCore } from '../types'; import { jobResponseHandlerFactory } from './lib/job_response_handler'; import { makeRequestFacade } from './lib/make_request_facade'; import { @@ -33,9 +32,9 @@ function isResponse(response: Boom | ResponseObject): response is Response } export function registerJobInfoRoutes( + reporting: ReportingCore, server: ServerFacade, plugins: ReportingSetupDeps, - exportTypesRegistry: ExportTypesRegistry, logger: Logger ) { const { elasticsearch } = plugins; @@ -138,6 +137,7 @@ export function registerJobInfoRoutes( }); // trigger a download of the output from a job + const exportTypesRegistry = reporting.getExportTypesRegistry(); const jobResponseHandler = jobResponseHandlerFactory(server, elasticsearch, exportTypesRegistry); server.route({ path: `${MAIN_ENTRY}/download/{docId}`, diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts index 57c3fcee222da..c5f8c78016f61 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts @@ -9,7 +9,7 @@ import { Legacy } from 'kibana'; import { AuthenticatedUser } from '../../../../../../plugins/security/server'; import { Logger, ServerFacade } from '../../../types'; import { getUserFactory } from '../../lib/get_user'; -import { ReportingSetupDeps } from '../../plugin'; +import { ReportingSetupDeps } from '../../types'; const superuserRole = 'superuser'; diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/reporting_feature_pre_routing.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/reporting_feature_pre_routing.ts index 7367fceb50857..9e618ff1fe40a 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/reporting_feature_pre_routing.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/reporting_feature_pre_routing.ts @@ -7,7 +7,7 @@ import Boom from 'boom'; import { Legacy } from 'kibana'; import { Logger, ServerFacade } from '../../../types'; -import { ReportingSetupDeps } from '../../plugin'; +import { ReportingSetupDeps } from '../../types'; export type GetReportingFeatureIdFn = (request: Legacy.Request) => string; diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/route_config_factories.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/route_config_factories.ts index 931f642397bf8..82ba9ba22c706 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/route_config_factories.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/route_config_factories.ts @@ -7,7 +7,7 @@ import Joi from 'joi'; import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; import { Logger, ServerFacade } from '../../../types'; -import { ReportingSetupDeps } from '../../plugin'; +import { ReportingSetupDeps } from '../../types'; import { authorizedUserPreRoutingFactory } from './authorized_user_pre_routing'; import { GetReportingFeatureIdFn, diff --git a/x-pack/legacy/plugins/reporting/server/types.d.ts b/x-pack/legacy/plugins/reporting/server/types.d.ts new file mode 100644 index 0000000000000..20673423aa448 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/types.d.ts @@ -0,0 +1,50 @@ +/* + * 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. + */ + +import { Legacy } from 'kibana'; +import { + ElasticsearchServiceSetup, + SavedObjectsServiceStart, + UiSettingsServiceStart, +} from 'src/core/server'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/server'; +import { SecurityPluginSetup } from '../../../../plugins/security/server'; +import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; +import { EnqueueJobFn, ESQueueInstance, ReportingPluginSpecOptions } from '../types'; +import { HeadlessChromiumDriverFactory } from './browsers/chromium/driver_factory'; + +export interface ReportingSetupDeps { + elasticsearch: ElasticsearchServiceSetup; + security: SecurityPluginSetup; + usageCollection: UsageCollectionSetup; + __LEGACY: LegacySetup; +} + +export interface ReportingStartDeps { + elasticsearch: ElasticsearchServiceSetup; + data: DataPluginStart; + __LEGACY: LegacySetup; +} + +export type ReportingSetup = object; + +export type ReportingStart = object; + +export interface LegacySetup { + config: Legacy.Server['config']; + info: Legacy.Server['info']; + plugins: { + elasticsearch: Legacy.Server['plugins']['elasticsearch']; + xpack_main: XPackMainPlugin & { + status?: any; + }; + reporting: ReportingPluginSpecOptions; + }; + route: Legacy.Server['route']; +} + +export { ReportingCore } from './core'; diff --git a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts index 0118dea38d985..359bcc45230c3 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts @@ -6,7 +6,7 @@ import { uniq } from 'lodash'; import { CSV_JOB_TYPE, PDF_JOB_TYPE, PNG_JOB_TYPE } from '../../common/constants'; -import { AvailableTotal, FeatureAvailabilityMap, RangeStats, ExportType } from './types.d'; +import { AvailableTotal, FeatureAvailabilityMap, RangeStats, ExportType } from './types'; function getForFeature( range: Partial, diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js index c6ff7d25a3089..b6ebbff18fc27 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js @@ -3,9 +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 sinon from 'sinon'; +import { createMockReportingCore } from '../../test_helpers'; import { getExportTypesRegistry } from '../lib/export_types_registry'; -import { getReportingUsageCollector } from './reporting_usage_collector'; +import { + registerReportingUsageCollector, + getReportingUsageCollector, +} from './reporting_usage_collector'; const exportTypesRegistry = getExportTypesRegistry(); @@ -71,9 +76,8 @@ describe('license checks', () => { const callClusterMock = jest.fn(() => Promise.resolve(getResponseMock())); const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithBasicLicenseMock, - () => {}, + usageCollection, exportTypesRegistry ); usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); @@ -102,9 +106,8 @@ describe('license checks', () => { const callClusterMock = jest.fn(() => Promise.resolve(getResponseMock())); const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithNoLicenseMock, - () => {}, + usageCollection, exportTypesRegistry ); usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); @@ -133,9 +136,8 @@ describe('license checks', () => { const callClusterMock = jest.fn(() => Promise.resolve(getResponseMock())); const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithPlatinumLicenseMock, - () => {}, + usageCollection, exportTypesRegistry ); usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); @@ -164,9 +166,8 @@ describe('license checks', () => { const callClusterMock = jest.fn(() => Promise.resolve({})); const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithBasicLicenseMock, - () => {}, + usageCollection, exportTypesRegistry ); usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); @@ -191,9 +192,8 @@ describe('data modeling', () => { .stub() .returns('platinum'); ({ fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithPlatinumLicenseMock, - () => {}, + usageCollection, exportTypesRegistry )); }); @@ -323,94 +323,124 @@ describe('data modeling', () => { const usageStats = await getReportingUsage(callClusterMock); expect(usageStats).toMatchInlineSnapshot(` -Object { - "PNG": Object { - "available": true, - "total": 4, - }, - "_all": 54, - "available": true, - "browser_type": undefined, - "csv": Object { - "available": true, - "total": 27, - }, - "enabled": true, - "last7Days": Object { - "PNG": Object { - "available": true, - "total": 4, - }, - "_all": 27, - "csv": Object { - "available": true, - "total": 10, - }, - "printable_pdf": Object { - "app": Object { - "dashboard": 13, - "visualization": 0, - }, - "available": true, - "layout": Object { - "preserve_layout": 3, - "print": 10, - }, - "total": 13, - }, - "status": Object { - "completed": 0, - "failed": 0, - "pending": 27, - }, - }, - "lastDay": Object { - "PNG": Object { - "available": true, - "total": 4, - }, - "_all": 11, - "csv": Object { - "available": true, - "total": 5, - }, - "printable_pdf": Object { - "app": Object { - "dashboard": 2, - "visualization": 0, - }, - "available": true, - "layout": Object { - "preserve_layout": 0, - "print": 2, - }, - "total": 2, - }, - "status": Object { - "completed": 0, - "failed": 0, - "pending": 11, - }, - }, - "printable_pdf": Object { - "app": Object { - "dashboard": 23, - "visualization": 0, - }, - "available": true, - "layout": Object { - "preserve_layout": 13, - "print": 10, - }, - "total": 23, - }, - "status": Object { - "completed": 20, - "failed": 0, - "pending": 33, - "processing": 1, - }, -} -`); + Object { + "PNG": Object { + "available": true, + "total": 4, + }, + "_all": 54, + "available": true, + "browser_type": undefined, + "csv": Object { + "available": true, + "total": 27, + }, + "enabled": true, + "last7Days": Object { + "PNG": Object { + "available": true, + "total": 4, + }, + "_all": 27, + "csv": Object { + "available": true, + "total": 10, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 13, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 3, + "print": 10, + }, + "total": 13, + }, + "status": Object { + "completed": 0, + "failed": 0, + "pending": 27, + }, + }, + "lastDay": Object { + "PNG": Object { + "available": true, + "total": 4, + }, + "_all": 11, + "csv": Object { + "available": true, + "total": 5, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 2, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 0, + "print": 2, + }, + "total": 2, + }, + "status": Object { + "completed": 0, + "failed": 0, + "pending": 11, + }, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 23, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 13, + "print": 10, + }, + "total": 23, + }, + "status": Object { + "completed": 20, + "failed": 0, + "pending": 33, + "processing": 1, + }, + } + `); + }); +}); + +describe('Ready for collection observable', () => { + let mockReporting; + + beforeEach(async () => { + mockReporting = await createMockReportingCore(); + }); + + test('converts observable to promise', async () => { + const serverWithBasicLicenseMock = getServerMock(); + const makeCollectorSpy = sinon.spy(); + const usageCollection = { + makeUsageCollector: makeCollectorSpy, + registerCollector: sinon.stub(), + }; + registerReportingUsageCollector(mockReporting, serverWithBasicLicenseMock, usageCollection); + + const [args] = makeCollectorSpy.firstCall.args; + expect(args).toMatchInlineSnapshot(` + Object { + "fetch": [Function], + "formatForBulkUpload": [Function], + "isReady": [Function], + "type": "reporting", + } + `); + + await expect(args.isReady()).resolves.toBe(true); }); }); diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts index 567838391d2e7..14202530fb6c7 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts @@ -5,8 +5,9 @@ */ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { ServerFacade, ExportTypesRegistry, ESCallCluster } from '../../types'; import { KIBANA_REPORTING_TYPE } from '../../common/constants'; +import { ReportingCore } from '../../server'; +import { ESCallCluster, ExportTypesRegistry, ServerFacade } from '../../types'; import { getReportingUsage } from './get_reporting_usage'; import { RangeStats } from './types'; @@ -18,16 +19,16 @@ const METATYPE = 'kibana_stats'; * @return {Object} kibana usage stats type collection object */ export function getReportingUsageCollector( - usageCollection: UsageCollectionSetup, server: ServerFacade, - isReady: () => boolean, - exportTypesRegistry: ExportTypesRegistry + usageCollection: UsageCollectionSetup, + exportTypesRegistry: ExportTypesRegistry, + isReady: () => Promise ) { return usageCollection.makeUsageCollector({ type: KIBANA_REPORTING_TYPE, - isReady, fetch: (callCluster: ESCallCluster) => getReportingUsage(server, callCluster, exportTypesRegistry), + isReady, /* * Format the response data into a model for internal upload @@ -50,16 +51,18 @@ export function getReportingUsageCollector( } export function registerReportingUsageCollector( - usageCollection: UsageCollectionSetup, + reporting: ReportingCore, server: ServerFacade, - isReady: () => boolean, - exportTypesRegistry: ExportTypesRegistry + usageCollection: UsageCollectionSetup ) { + const exportTypesRegistry = reporting.getExportTypesRegistry(); + const collectionIsReady = reporting.pluginHasStarted.bind(reporting); + const collector = getReportingUsageCollector( - usageCollection, server, - isReady, - exportTypesRegistry + usageCollection, + exportTypesRegistry, + collectionIsReady ); usageCollection.registerCollector(collector); } diff --git a/x-pack/legacy/plugins/reporting/test_helpers/create_mock_reportingplugin.ts b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_reportingplugin.ts new file mode 100644 index 0000000000000..2cd129d47b3f9 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_reportingplugin.ts @@ -0,0 +1,53 @@ +/* + * 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. + */ + +jest.mock('../server/routes'); +jest.mock('../server/usage'); +jest.mock('../server/browsers'); +jest.mock('../server/browsers'); +jest.mock('../server/lib/create_queue'); +jest.mock('../server/lib/enqueue_job'); +jest.mock('../server/lib/validate'); +jest.mock('../log_configuration'); + +import { EventEmitter } from 'events'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { coreMock } from 'src/core/server/mocks'; +import { ReportingPlugin, ReportingCore } from '../server'; +import { ReportingSetupDeps, ReportingStartDeps } from '../server/types'; + +export const createMockSetupDeps = (setupMock?: any): ReportingSetupDeps => ({ + elasticsearch: setupMock.elasticsearch, + security: setupMock.security, + usageCollection: {} as any, + __LEGACY: { plugins: { xpack_main: { status: new EventEmitter() } } } as any, +}); + +export const createMockStartDeps = (startMock?: any): ReportingStartDeps => ({ + data: startMock.data, + elasticsearch: startMock.elasticsearch, + __LEGACY: {} as any, +}); + +const createMockReportingPlugin = async (config = {}): Promise => { + const plugin = new ReportingPlugin(coreMock.createPluginInitializerContext(config)); + const setupMock = coreMock.createSetup(); + const coreStartMock = coreMock.createStart(); + const startMock = { + ...coreStartMock, + data: { fieldFormats: {} }, + }; + + await plugin.setup(setupMock, createMockSetupDeps(setupMock)); + await plugin.start(startMock, createMockStartDeps(startMock)); + + return plugin; +}; + +export const createMockReportingCore = async (config = {}): Promise => { + const plugin = await createMockReportingPlugin(config); + return plugin.getReportingCore(); +}; diff --git a/x-pack/legacy/plugins/reporting/test_helpers/create_mock_server.ts b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_server.ts index 226355f5edc61..bb7851ba036a9 100644 --- a/x-pack/legacy/plugins/reporting/test_helpers/create_mock_server.ts +++ b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_server.ts @@ -8,9 +8,6 @@ import { ServerFacade } from '../types'; export const createMockServer = ({ settings = {} }: any): ServerFacade => { const mockServer = { - expose: () => { - ' '; - }, config: memoize(() => ({ get: jest.fn() })), info: { protocol: 'http', @@ -24,10 +21,6 @@ export const createMockServer = ({ settings = {} }: any): ServerFacade => { }), }, }, - savedObjects: { - getScopedSavedObjectsClient: jest.fn(), - }, - uiSettingsServiceFactory: jest.fn().mockReturnValue({ get: jest.fn() }), }; const defaultSettings: any = { diff --git a/x-pack/legacy/plugins/reporting/test_helpers/index.ts b/x-pack/legacy/plugins/reporting/test_helpers/index.ts new file mode 100644 index 0000000000000..7fbc5661d5211 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/test_helpers/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { createMockServer } from './create_mock_server'; +export { createMockReportingCore } from './create_mock_reportingplugin'; diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index 76d4fd9099666..1dc99129a356b 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -6,16 +6,15 @@ import { EventEmitter } from 'events'; import { ResponseObject } from 'hapi'; -import { ElasticsearchServiceSetup } from 'kibana/server'; import { Legacy } from 'kibana'; +import { ElasticsearchServiceSetup } from 'kibana/server'; import { CallCluster } from '../../../../src/legacy/core_plugins/elasticsearch'; import { CancellationToken } from './common/cancellation_token'; import { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver_factory'; import { BrowserType } from './server/browsers/types'; import { LevelLogger } from './server/lib/level_logger'; -import { LegacySetup, ReportingSetupDeps } from './server/plugin'; - -export type ReportingPlugin = object; // For Plugin contract +import { ReportingCore } from './server/core'; +import { LegacySetup, ReportingStartDeps, ReportingSetup, ReportingStart } from './server/types'; export type Job = EventEmitter & { id: string; @@ -65,6 +64,7 @@ interface GenerateExportTypePayload { /* * Legacy System + * TODO: move to server/types */ export type ServerFacade = LegacySetup; @@ -179,6 +179,15 @@ export interface CryptoFactory { decrypt: (headers?: string) => any; } +export interface IndexPatternSavedObject { + attributes: { + fieldFormatMap: string; + }; + id: string; + type: string; + version: string; +} + export interface TimeRangeParams { timezone: string; min: Date | string | number; @@ -214,10 +223,6 @@ export interface JobDocOutput { size: number; } -export interface ESQueue { - addJob: (type: string, payload: object, options: object) => Job; -} - export interface ESQueueWorker { on: (event: string, handler: any) => void; } @@ -267,8 +272,9 @@ type GenericWorkerFn = ( ...workerRestArgs: any[] ) => void | Promise; -export interface ESQueueInstance { - registerWorker: ( +export interface ESQueueInstance { + addJob: (type: string, payload: unknown, options: object) => Job; + registerWorker: ( pluginId: string, workerFn: GenericWorkerFn, workerOptions: ESQueueWorkerOptions @@ -276,18 +282,17 @@ export interface ESQueueInstance { } export type CreateJobFactory = ( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, logger: LevelLogger ) => CreateJobFnType; export type ExecuteJobFactory = ( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - logger: LevelLogger, - opts: { - browserDriverFactory: HeadlessChromiumDriverFactory; - } -) => ExecuteJobFnType; + logger: LevelLogger +) => Promise; export interface ExportTypeDefinition< JobParamsType,