From ee5fa28f2a5951cdc9ad3c3a3c56a248777b64ca Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Thu, 23 Jan 2020 13:40:05 -0700 Subject: [PATCH] [Reporting/New Platform] Use the logger service from core (#55442) * More Typescript Fixes * [Reporting/New Platform] Use the logger service from core * Add log tag * fix jest tests * ts fixes * fix mocha test * convert to jest --- .../execute_job.js => execute_job.test.js} | 267 +++++++++--------- .../export_types/csv/server/execute_job.ts | 13 +- .../server/create_job/create_job.ts | 13 +- .../server/execute_job.ts | 25 +- .../png/server/execute_job/index.test.js | 8 +- .../png/server/execute_job/index.ts | 7 +- .../server/execute_job/index.test.js | 8 +- .../printable_pdf/server/execute_job/index.ts | 7 +- x-pack/legacy/plugins/reporting/index.ts | 4 +- .../browsers/create_browser_driver_factory.ts | 9 +- .../reporting/server/lib/create_queue.ts | 14 +- .../server/lib/create_tagged_logger.ts | 32 +-- .../server/lib/create_worker.test.ts | 7 +- .../reporting/server/lib/create_worker.ts | 6 +- .../reporting/server/lib/enqueue_job.ts | 6 +- .../plugins/reporting/server/lib/get_user.ts | 6 +- .../reporting/server/lib/level_logger.ts | 32 +-- .../legacy/plugins/reporting/server/plugin.ts | 22 +- .../server/routes/generate_from_jobparams.ts | 8 +- .../routes/generate_from_savedobject.ts | 7 +- .../generate_from_savedobject_immediate.ts | 10 +- .../reporting/server/routes/generation.ts | 12 +- .../plugins/reporting/server/routes/jobs.ts | 48 ++-- .../plugins/reporting/server/routes/legacy.ts | 9 +- .../authorized_user_pre_routing.test.js | 34 +-- .../routes/lib/authorized_user_pre_routing.ts | 14 +- .../server/routes/lib/get_document_payload.ts | 26 +- ...nse_handler.js => job_response_handler.ts} | 33 ++- .../lib/reporting_feature_pre_routing.ts | 7 +- .../routes/lib/route_config_factories.ts | 31 +- x-pack/legacy/plugins/reporting/types.d.ts | 41 +-- 31 files changed, 404 insertions(+), 362 deletions(-) rename x-pack/legacy/plugins/reporting/export_types/csv/server/{__tests__/execute_job.js => execute_job.test.js} (78%) rename x-pack/legacy/plugins/reporting/server/routes/lib/{__tests__ => }/authorized_user_pre_routing.test.js (87%) rename x-pack/legacy/plugins/reporting/server/routes/lib/{job_response_handler.js => job_response_handler.ts} (65%) diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js similarity index 78% rename from x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js rename to x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js index 00eaec2418f529..e35d478214ca39 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js @@ -4,34 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import Puid from 'puid'; import sinon from 'sinon'; import nodeCrypto from '@elastic/node-crypto'; - -import { CancellationToken } from '../../../../common/cancellation_token'; -// Reporting uses an unconventional directory structure so the linter marks this as a violation -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FieldFormatsService } from '../../../../../../../../src/legacy/ui/field_formats/mixin/field_formats_service'; +import { CancellationToken } from '../../../common/cancellation_token'; +import { FieldFormatsService } from '../../../../../../../src/legacy/ui/field_formats/mixin/field_formats_service'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { StringFormat } from '../../../../../../../../src/plugins/data/server'; - -import { executeJobFactory } from '../execute_job'; +import { StringFormat } from '../../../../../../../src/plugins/data/server'; +import { LevelLogger } from '../../../server/lib/level_logger'; +import { executeJobFactory } from './execute_job'; const delay = ms => new Promise(resolve => setTimeout(() => resolve(), ms)); -const expectRejectedPromise = async promise => { - let error = null; - try { - await promise; - } catch (err) { - error = err; - } finally { - expect(error).to.not.be(null); - expect(error).to.be.an(Error); - } -}; - const puid = new Puid(); const getRandomScrollId = () => { return puid.generate(); @@ -42,6 +26,13 @@ describe('CSV Execute Job', function() { const headers = { sid: 'test', }; + const mockLogger = new LevelLogger({ + get: () => ({ + debug: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }), + }); let defaultElasticsearchResponse; let encryptedHeaders; @@ -51,7 +42,7 @@ describe('CSV Execute Job', function() { let callWithRequestStub; let uiSettingsGetStub; - before(async function() { + beforeAll(async function() { const crypto = nodeCrypto({ encryptionKey }); encryptedHeaders = await crypto.encrypt(headers); }); @@ -124,16 +115,16 @@ describe('CSV Execute Job', function() { describe('calls getScopedSavedObjectsClient with request', function() { it('containing decrypted headers', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, cancellationToken ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).to.be(true); - expect( - mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].headers - ).to.be.eql(headers); + 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() { @@ -142,16 +133,16 @@ describe('CSV Execute Job', function() { .config() .get.withArgs('server.basePath') .returns(serverBasePath); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, cancellationToken ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).to.be(true); + expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).toBe(true); expect( mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].getBasePath() - ).to.be.eql(serverBasePath); + ).toEqual(serverBasePath); }); it(`containing getBasePath() returning job's basePath if the job has one`, async function() { @@ -160,7 +151,7 @@ describe('CSV Execute Job', function() { .config() .get.withArgs('server.basePath') .returns(serverBasePath); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobBasePath = 'foo-job/basePath/'; await executeJob( 'job789', @@ -172,10 +163,10 @@ describe('CSV Execute Job', function() { }, cancellationToken ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).to.be(true); + expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).toBe(true); expect( mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].getBasePath() - ).to.be.eql(jobBasePath); + ).toEqual(jobBasePath); }); }); @@ -183,14 +174,14 @@ describe('CSV Execute Job', function() { it('passed scoped SavedObjectsClient to uiSettingsServiceFactory', async function() { const returnValue = Symbol(); mockServer.savedObjects.getScopedSavedObjectsClient.returns(returnValue); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, cancellationToken ); - expect(mockServer.uiSettingsServiceFactory.calledOnce).to.be(true); - expect(mockServer.uiSettingsServiceFactory.firstCall.args[0].savedObjectsClient).to.be( + expect(mockServer.uiSettingsServiceFactory.calledOnce).toBe(true); + expect(mockServer.uiSettingsServiceFactory.firstCall.args[0].savedObjectsClient).toBe( returnValue ); }); @@ -198,14 +189,14 @@ describe('CSV Execute Job', function() { describe('basic Elasticsearch call behavior', function() { it('should decrypt encrypted headers and pass to callWithRequest', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, cancellationToken ); - expect(callWithRequestStub.called).to.be(true); - expect(callWithRequestStub.firstCall.args[0].headers).to.be.eql(headers); + expect(callWithRequestStub.called).toBe(true); + expect(callWithRequestStub.firstCall.args[0].headers).toEqual(headers); }); it('should pass the index and body to execute the initial search', async function() { @@ -214,7 +205,7 @@ describe('CSV Execute Job', function() { testBody: true, }; - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const job = { headers: encryptedHeaders, fields: [], @@ -227,9 +218,9 @@ describe('CSV Execute Job', function() { await executeJob('job777', job, cancellationToken); const searchCall = callWithRequestStub.firstCall; - expect(searchCall.args[1]).to.be('search'); - expect(searchCall.args[2].index).to.be(index); - expect(searchCall.args[2].body).to.be(body); + expect(searchCall.args[1]).toBe('search'); + expect(searchCall.args[2].index).toBe(index); + expect(searchCall.args[2].body).toBe(body); }); it('should pass the scrollId from the initial search to the subsequent scroll', async function() { @@ -241,7 +232,7 @@ describe('CSV Execute Job', function() { _scroll_id: scrollId, }); callWithRequestStub.onSecondCall().resolves(defaultElasticsearchResponse); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -250,25 +241,25 @@ describe('CSV Execute Job', function() { const scrollCall = callWithRequestStub.secondCall; - expect(scrollCall.args[1]).to.be('scroll'); - expect(scrollCall.args[2].scrollId).to.be(scrollId); + expect(scrollCall.args[1]).toBe('scroll'); + expect(scrollCall.args[2].scrollId).toBe(scrollId); }); it('should not execute scroll if there are no hits from the search', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, cancellationToken ); - expect(callWithRequestStub.callCount).to.be(2); + expect(callWithRequestStub.callCount).toBe(2); const searchCall = callWithRequestStub.firstCall; - expect(searchCall.args[1]).to.be('search'); + expect(searchCall.args[1]).toBe('search'); const clearScrollCall = callWithRequestStub.secondCall; - expect(clearScrollCall.args[1]).to.be('clearScroll'); + expect(clearScrollCall.args[1]).toBe('clearScroll'); }); it('should stop executing scroll if there are no hits', async function() { @@ -285,23 +276,23 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, cancellationToken ); - expect(callWithRequestStub.callCount).to.be(3); + expect(callWithRequestStub.callCount).toBe(3); const searchCall = callWithRequestStub.firstCall; - expect(searchCall.args[1]).to.be('search'); + expect(searchCall.args[1]).toBe('search'); const scrollCall = callWithRequestStub.secondCall; - expect(scrollCall.args[1]).to.be('scroll'); + expect(scrollCall.args[1]).toBe('scroll'); const clearScroll = callWithRequestStub.thirdCall; - expect(clearScroll.args[1]).to.be('clearScroll'); + expect(clearScroll.args[1]).toBe('clearScroll'); }); it('should call clearScroll with scrollId when there are no more hits', async function() { @@ -320,7 +311,7 @@ describe('CSV Execute Job', function() { _scroll_id: lastScrollId, }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -328,8 +319,8 @@ describe('CSV Execute Job', function() { ); const lastCall = callWithRequestStub.getCall(callWithRequestStub.callCount - 1); - expect(lastCall.args[1]).to.be('clearScroll'); - expect(lastCall.args[2].scrollId).to.eql([lastScrollId]); + expect(lastCall.args[1]).toBe('clearScroll'); + expect(lastCall.args[2].scrollId).toEqual([lastScrollId]); }); it('calls clearScroll when there is an error iterating the hits', async function() { @@ -348,18 +339,20 @@ describe('CSV Execute Job', function() { _scroll_id: lastScrollId, }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: undefined, searchRequest: { index: null, body: null }, }; - await expectRejectedPromise(executeJob('job123', jobParams, cancellationToken)); + await expect( + executeJob('job123', jobParams, cancellationToken) + ).rejects.toMatchInlineSnapshot(`[TypeError: Cannot read property 'indexOf' of undefined]`); const lastCall = callWithRequestStub.getCall(callWithRequestStub.callCount - 1); - expect(lastCall.args[1]).to.be('clearScroll'); - expect(lastCall.args[2].scrollId).to.eql([lastScrollId]); + expect(lastCall.args[1]).toBe('clearScroll'); + expect(lastCall.args[2].scrollId).toEqual([lastScrollId]); }); }); @@ -376,7 +369,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -389,7 +382,7 @@ describe('CSV Execute Job', function() { cancellationToken ); - expect(csvContainsFormulas).to.equal(true); + expect(csvContainsFormulas).toEqual(true); }); it('returns warnings when headings contain formulas', async function() { @@ -404,7 +397,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['=SUM(A1:A2)', 'two'], @@ -417,7 +410,7 @@ describe('CSV Execute Job', function() { cancellationToken ); - expect(csvContainsFormulas).to.equal(true); + expect(csvContainsFormulas).toEqual(true); }); it('returns no warnings when cells have no formulas', async function() { @@ -432,7 +425,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -445,7 +438,7 @@ describe('CSV Execute Job', function() { cancellationToken ); - expect(csvContainsFormulas).to.equal(false); + expect(csvContainsFormulas).toEqual(false); }); it('returns no warnings when configured not to', async () => { @@ -460,7 +453,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -473,20 +466,22 @@ describe('CSV Execute Job', function() { cancellationToken ); - expect(csvContainsFormulas).to.equal(false); + expect(csvContainsFormulas).toEqual(false); }); }); describe('Elasticsearch call errors', function() { it('should reject Promise if search call errors out', async function() { callWithRequestStub.rejects(new Error()); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }; - await expectRejectedPromise(executeJob('job123', jobParams, cancellationToken)); + await expect( + executeJob('job123', jobParams, cancellationToken) + ).rejects.toMatchInlineSnapshot(`[Error]`); }); it('should reject Promise if scroll call errors out', async function() { @@ -497,13 +492,15 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); callWithRequestStub.onSecondCall().rejects(new Error()); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }; - await expectRejectedPromise(executeJob('job123', jobParams, cancellationToken)); + await expect( + executeJob('job123', jobParams, cancellationToken) + ).rejects.toMatchInlineSnapshot(`[Error]`); }); }); @@ -516,13 +513,17 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }; - await expectRejectedPromise(executeJob('job123', jobParams, cancellationToken)); + await expect( + executeJob('job123', jobParams, cancellationToken) + ).rejects.toMatchInlineSnapshot( + `[Error: Expected _scroll_id in the following Elasticsearch response: {"hits":{"hits":[{}]}}]` + ); }); it('should reject Promise if search returns no hits and no _scroll_id', async function() { @@ -533,13 +534,17 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }; - await expectRejectedPromise(executeJob('job123', jobParams, cancellationToken)); + await expect( + executeJob('job123', jobParams, cancellationToken) + ).rejects.toMatchInlineSnapshot( + `[Error: Expected _scroll_id in the following Elasticsearch response: {"hits":{"hits":[]}}]` + ); }); it('should reject Promise if scroll returns hits but no _scroll_id', async function() { @@ -557,13 +562,17 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }; - await expectRejectedPromise(executeJob('job123', jobParams, cancellationToken)); + await expect( + executeJob('job123', jobParams, cancellationToken) + ).rejects.toMatchInlineSnapshot( + `[Error: Expected _scroll_id in the following Elasticsearch response: {"hits":{"hits":[{}]}}]` + ); }); it('should reject Promise if scroll returns no hits and no _scroll_id', async function() { @@ -581,13 +590,17 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }; - await expectRejectedPromise(executeJob('job123', jobParams, cancellationToken)); + await expect( + executeJob('job123', jobParams, cancellationToken) + ).rejects.toMatchInlineSnapshot( + `[Error: Expected _scroll_id in the following Elasticsearch response: {"hits":{"hits":[]}}]` + ); }); }); @@ -612,7 +625,7 @@ describe('CSV Execute Job', function() { }); it('should stop calling Elasticsearch when cancellationToken.cancel is called', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -622,12 +635,12 @@ describe('CSV Execute Job', function() { await delay(100); const callCount = callWithRequestStub.callCount; cancellationToken.cancel(); - await delay(100); - expect(callWithRequestStub.callCount).to.be(callCount + 1); // last call is to clear the scroll + await delay(250); + expect(callWithRequestStub.callCount).toBe(callCount + 1); // last call is to clear the scroll }); it(`shouldn't call clearScroll if it never got a scrollId`, async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -641,7 +654,7 @@ describe('CSV Execute Job', function() { }); it('should call clearScroll if it got a scrollId', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -652,61 +665,61 @@ describe('CSV Execute Job', function() { await delay(100); const lastCall = callWithRequestStub.getCall(callWithRequestStub.callCount - 1); - expect(lastCall.args[1]).to.be('clearScroll'); - expect(lastCall.args[2].scrollId).to.eql([scrollId]); + expect(lastCall.args[1]).toBe('clearScroll'); + expect(lastCall.args[2].scrollId).toEqual([scrollId]); }); }); describe('csv content', function() { it('should write column headers to output, even if there are no results', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], searchRequest: { index: null, body: null }, }; const { content } = await executeJob('job123', jobParams, cancellationToken); - expect(content).to.be(`one,two\n`); + expect(content).toBe(`one,two\n`); }); it('should use custom uiSettings csv:separator for header', async function() { uiSettingsGetStub.withArgs('csv:separator').returns(';'); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], searchRequest: { index: null, body: null }, }; const { content } = await executeJob('job123', jobParams, cancellationToken); - expect(content).to.be(`one;two\n`); + expect(content).toBe(`one;two\n`); }); it('should escape column headers if uiSettings csv:quoteValues is true', async function() { uiSettingsGetStub.withArgs('csv:quoteValues').returns(true); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one and a half', 'two', 'three-and-four', 'five & six'], searchRequest: { index: null, body: null }, }; const { content } = await executeJob('job123', jobParams, cancellationToken); - expect(content).to.be(`"one and a half",two,"three-and-four","five & six"\n`); + expect(content).toBe(`"one and a half",two,"three-and-four","five & six"\n`); }); it(`shouldn't escape column headers if uiSettings csv:quoteValues is false`, async function() { uiSettingsGetStub.withArgs('csv:quoteValues').returns(false); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one and a half', 'two', 'three-and-four', 'five & six'], searchRequest: { index: null, body: null }, }; const { content } = await executeJob('job123', jobParams, cancellationToken); - expect(content).to.be(`one and a half,two,three-and-four,five & six\n`); + expect(content).toBe(`one and a half,two,three-and-four,five & six\n`); }); it('should write column headers to output, when there are results', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); callWithRequestStub.onFirstCall().resolves({ hits: { hits: [{ one: '1', two: '2' }], @@ -722,11 +735,11 @@ describe('CSV Execute Job', function() { const { content } = await executeJob('job123', jobParams, cancellationToken); const lines = content.split('\n'); const headerLine = lines[0]; - expect(headerLine).to.be('one,two'); + expect(headerLine).toBe('one,two'); }); it('should use comma separated values of non-nested fields from _source', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); callWithRequestStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -743,11 +756,11 @@ describe('CSV Execute Job', function() { const { content } = await executeJob('job123', jobParams, cancellationToken); const lines = content.split('\n'); const valuesLine = lines[1]; - expect(valuesLine).to.be('foo,bar'); + expect(valuesLine).toBe('foo,bar'); }); it('should concatenate the hits from multiple responses', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); callWithRequestStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -770,12 +783,12 @@ describe('CSV Execute Job', function() { const { content } = await executeJob('job123', jobParams, cancellationToken); const lines = content.split('\n'); - expect(lines[1]).to.be('foo,bar'); - expect(lines[2]).to.be('baz,qux'); + expect(lines[1]).toBe('foo,bar'); + expect(lines[2]).toBe('baz,qux'); }); it('should use field formatters to format fields', async function() { - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); callWithRequestStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -801,7 +814,7 @@ describe('CSV Execute Job', function() { const { content } = await executeJob('job123', jobParams, cancellationToken); const lines = content.split('\n'); - expect(lines[1]).to.be('FOO,bar'); + expect(lines[1]).toBe('FOO,bar'); }); }); @@ -820,7 +833,7 @@ describe('CSV Execute Job', function() { .get.withArgs('xpack.reporting.csv.maxSizeBytes') .returns(1); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -835,11 +848,11 @@ describe('CSV Execute Job', function() { }); it('should return max_size_reached', function() { - expect(maxSizeReached).to.be(true); + expect(maxSizeReached).toBe(true); }); it('should return empty content', function() { - expect(content).to.be(''); + expect(content).toBe(''); }); }); @@ -853,7 +866,7 @@ describe('CSV Execute Job', function() { .get.withArgs('xpack.reporting.csv.maxSizeBytes') .returns(9); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -868,11 +881,11 @@ describe('CSV Execute Job', function() { }); it(`shouldn't return max_size_reached`, function() { - expect(maxSizeReached).to.be(false); + expect(maxSizeReached).toBe(false); }); it(`should return content`, function() { - expect(content).to.be('one,two\n'); + expect(content).toBe('one,two\n'); }); }); @@ -893,7 +906,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -909,11 +922,11 @@ describe('CSV Execute Job', function() { }); it(`should return max_size_reached`, function() { - expect(maxSizeReached).to.be(true); + expect(maxSizeReached).toBe(true); }); it(`should return the headers in the content`, function() { - expect(content).to.be('one,two\n'); + expect(content).toBe('one,two\n'); }); }); @@ -934,7 +947,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -950,11 +963,11 @@ describe('CSV Execute Job', function() { }); it(`shouldn't return max_size_reached`, async function() { - expect(maxSizeReached).to.be(false); + expect(maxSizeReached).toBe(false); }); it('should return headers and data in content', function() { - expect(content).to.be('one,two\nfoo,bar\n'); + expect(content).toBe('one,two\nfoo,bar\n'); }); }); }); @@ -974,7 +987,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -985,8 +998,8 @@ describe('CSV Execute Job', function() { await executeJob('job123', jobParams, cancellationToken); const searchCall = callWithRequestStub.firstCall; - expect(searchCall.args[1]).to.be('search'); - expect(searchCall.args[2].scroll).to.be(scrollDuration); + expect(searchCall.args[1]).toBe('search'); + expect(searchCall.args[2].scroll).toBe(scrollDuration); }); it('passes scroll size to initial search call', async function() { @@ -1003,7 +1016,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -1014,8 +1027,8 @@ describe('CSV Execute Job', function() { await executeJob('job123', jobParams, cancellationToken); const searchCall = callWithRequestStub.firstCall; - expect(searchCall.args[1]).to.be('search'); - expect(searchCall.args[2].size).to.be(scrollSize); + expect(searchCall.args[1]).toBe('search'); + expect(searchCall.args[2].size).toBe(scrollSize); }); it('passes scroll duration to subsequent scroll call', async function() { @@ -1032,7 +1045,7 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer); + const executeJob = executeJobFactory(mockServer, mockLogger); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -1043,8 +1056,8 @@ describe('CSV Execute Job', function() { await executeJob('job123', jobParams, cancellationToken); const scrollCall = callWithRequestStub.secondCall; - expect(scrollCall.args[1]).to.be('scroll'); - expect(scrollCall.args[2].scroll).to.be(scrollDuration); + expect(scrollCall.args[1]).toBe('scroll'); + expect(scrollCall.args[2].scroll).toBe(scrollDuration); }); }); }); 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 83bcc90ba92ed3..d8b4cdd9718eaa 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 @@ -7,25 +7,26 @@ import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { KibanaRequest } from '../../../../../../../src/core/server'; +import { CSV_JOB_TYPE } from '../../../common/constants'; +import { cryptoFactory } from '../../../server/lib'; import { - ExecuteJobFactory, ESQueueWorkerExecuteFn, + ExecuteJobFactory, FieldFormats, + Logger, ServerFacade, } from '../../../types'; -import { CSV_JOB_TYPE, PLUGIN_ID } from '../../../common/constants'; -import { cryptoFactory, LevelLogger } from '../../../server/lib'; import { JobDocPayloadDiscoverCsv } from '../types'; -import { createGenerateCsv } from './lib/generate_csv'; import { fieldFormatMapFactory } from './lib/field_format_map'; +import { createGenerateCsv } from './lib/generate_csv'; export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn(server: ServerFacade) { +>> = function executeJobFactoryFn(server: ServerFacade, parentLogger: Logger) { const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); const crypto = cryptoFactory(server); const config = server.config(); - const logger = LevelLogger.createForServer(server, [PLUGIN_ID, CSV_JOB_TYPE, 'execute-job']); + const logger = parentLogger.clone([CSV_JOB_TYPE, 'execute-job']); const serverBasePath = config.get('server.basePath'); return async function executeJob( 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 8443be2b25f4f9..a270e3e0329fe8 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 @@ -6,13 +6,14 @@ import { notFound, notImplemented } from 'boom'; import { get } from 'lodash'; -import { PLUGIN_ID, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../../common/constants'; -import { cryptoFactory, LevelLogger } from '../../../../server/lib'; +import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../../common/constants'; +import { cryptoFactory } from '../../../../server/lib'; import { CreateJobFactory, ImmediateCreateJobFn, ServerFacade, RequestFacade, + Logger, } from '../../../../types'; import { SavedObject, @@ -34,13 +35,9 @@ interface VisData { export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(server: ServerFacade) { +>> = function createJobFactoryFn(server: ServerFacade, parentLogger: Logger) { const crypto = cryptoFactory(server); - const logger = LevelLogger.createForServer(server, [ - PLUGIN_ID, - CSV_FROM_SAVEDOBJECT_JOB_TYPE, - 'create-job', - ]); + const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'create-job']); return async function createJob( jobParams: JobParamsPanelCsv, 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 69d9a173d40b38..03f491deaa43d6 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 @@ -5,38 +5,31 @@ */ import { i18n } from '@kbn/i18n'; -import { cryptoFactory, LevelLogger } from '../../../server/lib'; +import { CONTENT_TYPE_CSV, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; +import { cryptoFactory } from '../../../server/lib'; import { ExecuteJobFactory, ImmediateExecuteFn, - JobDocOutputExecuted, - ServerFacade, + JobDocOutput, + Logger, RequestFacade, + ServerFacade, } from '../../../types'; -import { - CONTENT_TYPE_CSV, - CSV_FROM_SAVEDOBJECT_JOB_TYPE, - PLUGIN_ID, -} from '../../../common/constants'; import { CsvResultFromSearch } from '../../csv/types'; -import { JobParamsPanelCsv, SearchPanel, JobDocPayloadPanelCsv, FakeRequest } from '../types'; +import { FakeRequest, JobDocPayloadPanelCsv, JobParamsPanelCsv, SearchPanel } from '../types'; import { createGenerateCsv } from './lib'; export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn(server: ServerFacade) { +>> = function executeJobFactoryFn(server: ServerFacade, parentLogger: Logger) { const crypto = cryptoFactory(server); - const logger = LevelLogger.createForServer(server, [ - PLUGIN_ID, - CSV_FROM_SAVEDOBJECT_JOB_TYPE, - 'execute-job', - ]); + const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'execute-job']); return async function executeJob( jobId: string | null, job: JobDocPayloadPanelCsv, realRequest?: RequestFacade - ): Promise { + ): Promise { // There will not be a jobID for "immediate" generation. // jobID is only for "queued" jobs // Use the jobID as a logging tag or "immediate" 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 1be65722fa668b..4f02ab5d4c077e 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 @@ -57,6 +57,8 @@ beforeEach(() => { afterEach(() => generatePngObservableFactory.mockReset()); +const getMockLogger = () => new LevelLogger(); + const encryptHeaders = async headers => { const crypto = cryptoFactory(mockServer); return await crypto.encrypt(headers); @@ -68,7 +70,7 @@ test(`passes browserTimezone to generatePng`, async () => { const generatePngObservable = generatePngObservableFactory(); generatePngObservable.mockReturnValue(Rx.of(Buffer.from(''))); - const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); + const executeJob = executeJobFactory(mockServer, getMockLogger(), { browserDriverFactory: {} }); const browserTimezone = 'UTC'; await executeJob( 'pngJobId', @@ -86,7 +88,7 @@ test(`passes browserTimezone to generatePng`, async () => { }); test(`returns content_type of application/png`, async () => { - const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); + const executeJob = executeJobFactory(mockServer, getMockLogger(), { browserDriverFactory: {} }); const encryptedHeaders = await encryptHeaders({}); const generatePngObservable = generatePngObservableFactory(); @@ -106,7 +108,7 @@ test(`returns content of generatePng getBuffer base64 encoded`, async () => { const generatePngObservable = generatePngObservableFactory(); generatePngObservable.mockReturnValue(Rx.of(Buffer.from(testContent))); - const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); + const executeJob = executeJobFactory(mockServer, 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 c2fda05fbe3e93..7d5c69655c362f 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 @@ -6,14 +6,14 @@ import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; -import { PLUGIN_ID, PNG_JOB_TYPE } from '../../../../common/constants'; +import { PNG_JOB_TYPE } from '../../../../common/constants'; import { ServerFacade, ExecuteJobFactory, ESQueueWorkerExecuteFn, HeadlessChromiumDriverFactory, + Logger, } from '../../../../types'; -import { LevelLogger } from '../../../../server/lib'; import { decryptJobHeaders, omitBlacklistedHeaders, @@ -27,10 +27,11 @@ type QueuedPngExecutorFactory = ExecuteJobFactory { afterEach(() => generatePdfObservableFactory.mockReset()); +const getMockLogger = () => new LevelLogger(); + const encryptHeaders = async headers => { const crypto = cryptoFactory(mockServer); return await crypto.encrypt(headers); @@ -67,7 +69,7 @@ test(`passes browserTimezone to generatePdf`, async () => { const generatePdfObservable = generatePdfObservableFactory(); generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(''))); - const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); + const executeJob = executeJobFactory(mockServer, getMockLogger(), { browserDriverFactory: {} }); const browserTimezone = 'UTC'; await executeJob( 'pdfJobId', @@ -88,7 +90,7 @@ test(`passes browserTimezone to generatePdf`, async () => { }); test(`returns content_type of application/pdf`, async () => { - const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); + const executeJob = executeJobFactory(mockServer, getMockLogger(), { browserDriverFactory: {} }); const encryptedHeaders = await encryptHeaders({}); const generatePdfObservable = generatePdfObservableFactory(); @@ -108,7 +110,7 @@ test(`returns content of generatePdf getBuffer base64 encoded`, async () => { const generatePdfObservable = generatePdfObservableFactory(); generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(testContent))); - const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); + const executeJob = executeJobFactory(mockServer, getMockLogger(), { browserDriverFactory: {} }); const encryptedHeaders = await encryptHeaders({}); const { content } = await executeJob( 'pdfJobId', 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 d85207e6712125..dee53697c6681d 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 @@ -11,10 +11,10 @@ import { ExecuteJobFactory, ESQueueWorkerExecuteFn, HeadlessChromiumDriverFactory, + Logger, } from '../../../../types'; import { JobDocPayloadPDF } from '../../types'; -import { PLUGIN_ID, PDF_JOB_TYPE } from '../../../../common/constants'; -import { LevelLogger } from '../../../../server/lib'; +import { PDF_JOB_TYPE } from '../../../../common/constants'; import { generatePdfObservableFactory } from '../lib/generate_pdf'; import { decryptJobHeaders, @@ -28,10 +28,11 @@ type QueuedPdfExecutorFactory = ExecuteJobFactory { uiSettingsServiceFactory: server.uiSettingsServiceFactory, // @ts-ignore Property 'fieldFormatServiceFactory' does not exist on type 'Server'. fieldFormatServiceFactory: server.fieldFormatServiceFactory, - log: server.log.bind(server), }; - const plugin: ReportingPlugin = reportingPluginFactory(__LEGACY, this); + const initializerContext = server.newPlatform.coreContext; + const plugin: ReportingPlugin = reportingPluginFactory(initializerContext, __LEGACY, this); await plugin.setup(coreSetup, pluginsSetup); }, diff --git a/x-pack/legacy/plugins/reporting/server/browsers/create_browser_driver_factory.ts b/x-pack/legacy/plugins/reporting/server/browsers/create_browser_driver_factory.ts index a253988b01952e..128df4d318c764 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/create_browser_driver_factory.ts +++ b/x-pack/legacy/plugins/reporting/server/browsers/create_browser_driver_factory.ts @@ -6,17 +6,16 @@ import { ensureBrowserDownloaded } from './download'; import { installBrowser } from './install'; -import { LevelLogger } from '../lib/level_logger'; -import { ServerFacade, CaptureConfig } from '../../types'; -import { PLUGIN_ID, BROWSER_TYPE } from '../../common/constants'; +import { ServerFacade, CaptureConfig, Logger } from '../../types'; +import { BROWSER_TYPE } from '../../common/constants'; import { chromium } from './index'; import { HeadlessChromiumDriverFactory } from './chromium/driver_factory'; export async function createBrowserDriverFactory( - server: ServerFacade + server: ServerFacade, + logger: Logger ): Promise { const config = server.config(); - const logger = LevelLogger.createForServer(server, [PLUGIN_ID, 'browser-driver']); const dataDir: string = config.get('path.data'); const captureConfig: CaptureConfig = config.get('xpack.reporting.capture'); 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 4a9b0c7cd2ebb3..05b760c0c3bd6c 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts @@ -4,17 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PLUGIN_ID } from '../../common/constants'; import { ServerFacade, ExportTypesRegistry, HeadlessChromiumDriverFactory, QueueConfig, + Logger, } from '../../types'; // @ts-ignore import { Esqueue } from './esqueue'; import { createWorkerFactory } from './create_worker'; -import { LevelLogger } from './level_logger'; import { createTaggedLogger } from './create_tagged_logger'; // TODO remove createTaggedLogger once esqueue is removed interface CreateQueueFactoryOpts { @@ -24,6 +23,7 @@ interface CreateQueueFactoryOpts { export function createQueueFactory( server: ServerFacade, + logger: Logger, { exportTypesRegistry, browserDriverFactory }: CreateQueueFactoryOpts ): Esqueue { const queueConfig: QueueConfig = server.config().get('xpack.reporting.queue'); @@ -34,23 +34,25 @@ export function createQueueFactory( timeout: queueConfig.timeout, dateSeparator: '.', client: server.plugins.elasticsearch.getCluster('admin'), - logger: createTaggedLogger(server, [PLUGIN_ID, 'esqueue', 'queue-worker']), + logger: createTaggedLogger(logger, ['esqueue', 'queue-worker']), }; const queue: Esqueue = 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, { exportTypesRegistry, browserDriverFactory }); + const createWorker = createWorkerFactory(server, logger, { + exportTypesRegistry, + browserDriverFactory, + }); createWorker(queue); } else { - const logger = LevelLogger.createForServer(server, [PLUGIN_ID, 'create_queue']); logger.info( 'xpack.reporting.queue.pollEnabled is set to false. This Kibana instance ' + 'will not poll for idle jobs to claim and execute. Make sure another ' + 'Kibana instance with polling enabled is running in this cluster so ' + 'reporting jobs can complete.', - ['info'] + ['create_queue'] ); } diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_tagged_logger.ts b/x-pack/legacy/plugins/reporting/server/lib/create_tagged_logger.ts index 40a1cd8203d2fe..97b34dfe40830c 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_tagged_logger.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_tagged_logger.ts @@ -4,23 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ServerFacade } from '../../types'; +import { Logger } from '../../types'; -/** - * @function taggedLogger - * @param {string} message - * @param {string[]} [additionalTags] - */ - -/** - * Creates a taggedLogger function with tags, allows the consumer to optionally provide additional tags - * - * @param {Server} server - * @param {string[]} tags - tags to always be passed into the `logger` function - * @returns taggedLogger - */ -export function createTaggedLogger(server: ServerFacade, tags: string[]) { +export function createTaggedLogger(logger: Logger, tags: string[]) { return (msg: string, additionalTags = []) => { - server.log([...tags, ...additionalTags], msg); + const allTags = [...tags, ...additionalTags]; + + if (allTags.includes('info')) { + const newTags = allTags.filter(t => t !== 'info'); // Ensure 'info' is not included twice + logger.info(msg, newTags); + } else if (allTags.includes('debug')) { + const newTags = allTags.filter(t => t !== 'debug'); + logger.debug(msg, newTags); + } else if (allTags.includes('warn') || allTags.includes('warning')) { + const newTags = allTags.filter(t => t !== 'warn' && t !== 'warning'); + logger.warn(msg, newTags); + } else { + logger.error(msg, allTags); + } }; } 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 8f843752491ecb..6a5c93db32376a 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 @@ -25,10 +25,11 @@ const executeJobFactoryStub = sinon.stub(); const getMockServer = (): ServerFacade => { return ({ - log: sinon.stub(), config: () => ({ get: configGetStub }), } as unknown) as ServerFacade; }; +const getMockLogger = jest.fn(); + const getMockExportTypesRegistry = ( exportTypes: any[] = [{ executeJobFactory: executeJobFactoryStub }] ) => ({ @@ -47,7 +48,7 @@ describe('Create Worker', () => { test('Creates a single Esqueue worker for Reporting', async () => { const exportTypesRegistry = getMockExportTypesRegistry(); - const createWorker = createWorkerFactory(getMockServer(), { + const createWorker = createWorkerFactory(getMockServer(), getMockLogger(), { exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry, browserDriverFactory: {} as HeadlessChromiumDriverFactory, }); @@ -81,7 +82,7 @@ Object { { executeJobFactory: executeJobFactoryStub }, { executeJobFactory: executeJobFactoryStub }, ]); - const createWorker = createWorkerFactory(getMockServer(), { + const createWorker = createWorkerFactory(getMockServer(), getMockLogger(), { exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry, browserDriverFactory: {} as HeadlessChromiumDriverFactory, }); 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 1326e411b6c5c7..67869016a250be 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts @@ -17,10 +17,10 @@ import { JobSource, RequestFacade, ServerFacade, + Logger, } from '../../types'; // @ts-ignore untyped dependency import { events as esqueueEvents } from './esqueue'; -import { LevelLogger } from './level_logger'; interface CreateWorkerFactoryOpts { exportTypesRegistry: ExportTypesRegistry; @@ -29,11 +29,11 @@ interface CreateWorkerFactoryOpts { export function createWorkerFactory( server: ServerFacade, + logger: Logger, { exportTypesRegistry, browserDriverFactory }: CreateWorkerFactoryOpts ) { type JobDocPayloadType = JobDocPayload; const config = server.config(); - const logger = LevelLogger.createForServer(server, [PLUGIN_ID, 'queue-worker']); const queueConfig: QueueConfig = config.get('xpack.reporting.queue'); const kibanaName: string = config.get('server.name'); const kibanaId: string = config.get('server.uuid'); @@ -50,7 +50,7 @@ export function createWorkerFactory( ExportTypeDefinition >) { // TODO: the executeJobFn should be unwrapped in the register method of the export types registry - const jobExecutor = exportType.executeJobFactory(server, { browserDriverFactory }); + const jobExecutor = exportType.executeJobFactory(server, logger, { browserDriverFactory }); 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 2d044ab31a160e..14c57fa35dcf4e 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts @@ -35,8 +35,10 @@ interface EnqueueJobFactoryOpts { export function enqueueJobFactory( server: ServerFacade, + parentLogger: Logger, { exportTypesRegistry, esqueue }: EnqueueJobFactoryOpts ): EnqueueJobFn { + const logger = parentLogger.clone(['queue-job']); const config = server.config(); const captureConfig: CaptureConfig = config.get('xpack.reporting.capture'); const browserType = captureConfig.browser.type; @@ -44,7 +46,6 @@ export function enqueueJobFactory( const queueConfig: QueueConfig = config.get('xpack.reporting.queue'); return async function enqueueJob( - parentLogger: Logger, exportTypeId: string, jobParams: JobParamsType, user: string, @@ -53,7 +54,6 @@ export function enqueueJobFactory( ): Promise { type CreateJobFn = ESQueueCreateJobFn | ImmediateCreateJobFn; - const logger = parentLogger.clone(['queue-job']); const exportType = exportTypesRegistry.getById(exportTypeId); if (exportType == null) { @@ -61,7 +61,7 @@ export function enqueueJobFactory( } // TODO: the createJobFn should be unwrapped in the register method of the export types registry - const createJob = exportType.createJobFactory(server) as CreateJobFn; + const createJob = exportType.createJobFactory(server, 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 e2921de795012b..9ee8d9a835c89e 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/get_user.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/get_user.ts @@ -5,9 +5,9 @@ */ import { Legacy } from 'kibana'; -import { ServerFacade } from '../../types'; +import { Logger, ServerFacade } from '../../types'; -export function getUserFactory(server: ServerFacade) { +export function getUserFactory(server: ServerFacade, logger: Logger) { /* * Legacy.Request because this is called from routing middleware */ @@ -19,7 +19,7 @@ export function getUserFactory(server: ServerFacade) { try { return await server.plugins.security.getUser(request); } catch (err) { - server.log(['reporting', 'getUser', 'error'], err); + logger.error(err, ['getUser']); return null; } }; diff --git a/x-pack/legacy/plugins/reporting/server/lib/level_logger.ts b/x-pack/legacy/plugins/reporting/server/lib/level_logger.ts index 839fa16a716b7d..d015d500363c12 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/level_logger.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/level_logger.ts @@ -4,48 +4,46 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ServerFacade } from '../../types'; +import { LoggerFactory } from 'src/core/server'; const trimStr = (toTrim: string) => { return typeof toTrim === 'string' ? toTrim.trim() : toTrim; }; export class LevelLogger { - private _logger: any; + private _logger: LoggerFactory; private _tags: string[]; + public warning: (msg: string, tags?: string[]) => void; - public warn: (msg: string, tags?: string[]) => void; - - static createForServer(server: ServerFacade, tags: string[]) { - const serverLog: ServerFacade['log'] = (tgs: string[], msg: string) => server.log(tgs, msg); - return new LevelLogger(serverLog, tags); - } - - constructor(logger: ServerFacade['log'], tags: string[]) { + constructor(logger: LoggerFactory, tags?: string[]) { this._logger = logger; - this._tags = tags; + this._tags = tags || []; /* * This shortcut provides maintenance convenience: Reporting code has been * using both .warn and .warning */ - this.warn = this.warning.bind(this); + this.warning = this.warn.bind(this); + } + + private getLogger(tags: string[]) { + return this._logger.get(...this._tags, ...tags); } public error(err: string | Error, tags: string[] = []) { - this._logger([...this._tags, ...tags, 'error'], err); + this.getLogger(tags).error(err); } - public warning(msg: string, tags: string[] = []) { - this._logger([...this._tags, ...tags, 'warning'], trimStr(msg)); + public warn(msg: string, tags: string[] = []) { + this.getLogger(tags).warn(msg); } public debug(msg: string, tags: string[] = []) { - this._logger([...this._tags, ...tags, 'debug'], trimStr(msg)); + this.getLogger(tags).debug(msg); } public info(msg: string, tags: string[] = []) { - this._logger([...this._tags, ...tags, 'info'], trimStr(msg)); + this.getLogger(tags).info(trimStr(msg)); } public clone(tags: string[]) { diff --git a/x-pack/legacy/plugins/reporting/server/plugin.ts b/x-pack/legacy/plugins/reporting/server/plugin.ts index 934a3487209c42..b0dc56dd8d8d0e 100644 --- a/x-pack/legacy/plugins/reporting/server/plugin.ts +++ b/x-pack/legacy/plugins/reporting/server/plugin.ts @@ -5,7 +5,7 @@ */ import { Legacy } from 'kibana'; -import { CoreSetup, CoreStart, Plugin } from 'src/core/server'; +import { CoreSetup, CoreStart, Plugin, LoggerFactory } from 'src/core/server'; import { IUiSettingsClient } from 'src/core/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; @@ -14,11 +14,15 @@ import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; import { PLUGIN_ID } from '../common/constants'; import { ReportingPluginSpecOptions } from '../types.d'; import { registerRoutes } from './routes'; -import { LevelLogger, checkLicenseFactory, getExportTypesRegistry, runValidations } from './lib'; +import { checkLicenseFactory, getExportTypesRegistry, runValidations, LevelLogger } from './lib'; import { createBrowserDriverFactory } from './browsers'; import { registerReportingUsageCollector } from './usage'; import { logConfiguration } from '../log_configuration'; +export interface ReportingInitializerContext { + logger: LoggerFactory; +} + // For now there is no exposed functionality to other plugins export type ReportingSetup = object; export type ReportingStart = object; @@ -33,7 +37,6 @@ type LegacyPlugins = Legacy.Server['plugins']; export interface LegacySetup { config: Legacy.Server['config']; info: Legacy.Server['info']; - log: Legacy.Server['log']; plugins: { elasticsearch: LegacyPlugins['elasticsearch']; security: LegacyPlugins['security']; @@ -59,10 +62,17 @@ export type ReportingPlugin = Plugin< * into `setup`. The factory parameters take the legacy dependencies, and the * `setup` method gets it from enclosure */ export function reportingPluginFactory( + initializerContext: ReportingInitializerContext, __LEGACY: LegacySetup, legacyPlugin: ReportingPluginSpecOptions ) { return new (class ReportingPlugin implements ReportingPlugin { + private initializerContext: ReportingInitializerContext; + + constructor(context: ReportingInitializerContext) { + this.initializerContext = context; + } + public async setup(core: CoreSetup, plugins: ReportingSetupDeps): Promise { const exportTypesRegistry = getExportTypesRegistry(); @@ -76,8 +86,8 @@ export function reportingPluginFactory( exportTypesRegistry ); - const logger = LevelLogger.createForServer(__LEGACY, [PLUGIN_ID]); - const browserDriverFactory = await createBrowserDriverFactory(__LEGACY); + const logger = new LevelLogger(this.initializerContext.logger.get('reporting')); + const browserDriverFactory = await createBrowserDriverFactory(__LEGACY, logger); logConfiguration(__LEGACY, logger); runValidations(__LEGACY, logger, browserDriverFactory); @@ -103,5 +113,5 @@ export function reportingPluginFactory( public start(core: CoreStart, plugins: ReportingStartDeps): ReportingStart { return {}; } - })(); + })(initializerContext); } 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 c9225dfee69788..d920015c4290c0 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 @@ -9,7 +9,7 @@ import boom from 'boom'; import Joi from 'joi'; import rison from 'rison-node'; import { API_BASE_URL } from '../../common/constants'; -import { ServerFacade, ReportingResponseToolkit } from '../../types'; +import { ServerFacade, ReportingResponseToolkit, Logger } from '../../types'; import { getRouteConfigFactoryReportingPre, GetRouteConfigFactoryFn, @@ -23,11 +23,13 @@ const BASE_GENERATE = `${API_BASE_URL}/generate`; export function registerGenerateFromJobParams( server: ServerFacade, handler: HandlerFunction, - handleError: HandlerErrorFunction + handleError: HandlerErrorFunction, + logger: Logger ) { const getRouteConfig = () => { const getOriginalRouteConfig: GetRouteConfigFactoryFn = getRouteConfigFactoryReportingPre( - server + server, + logger ); const routeConfigFactory: RouteConfigFactory = getOriginalRouteConfig( ({ params: { exportType } }) => exportType 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 2c509136b1b441..0da8e40ea29c0f 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 @@ -7,7 +7,7 @@ import { Legacy } from 'kibana'; import { get } from 'lodash'; import { API_BASE_GENERATE_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants'; -import { ServerFacade, ReportingResponseToolkit } from '../../types'; +import { ServerFacade, ReportingResponseToolkit, Logger } from '../../types'; import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types'; import { getRouteOptionsCsv } from './lib/route_config_factories'; import { makeRequestFacade } from './lib/make_request_facade'; @@ -25,9 +25,10 @@ import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject export function registerGenerateCsvFromSavedObject( server: ServerFacade, handleRoute: HandlerFunction, - handleRouteError: HandlerErrorFunction + handleRouteError: HandlerErrorFunction, + logger: Logger ) { - const routeOptions = getRouteOptionsCsv(server); + const routeOptions = getRouteOptionsCsv(server, logger); server.route({ path: `${API_BASE_GENERATE_V1}/csv/saved-object/{savedObjectType}:{savedObjectId}`, 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 8d1c84664cbe9d..60799b20ce4205 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 @@ -13,7 +13,7 @@ import { HeadlessChromiumDriverFactory, ReportingResponseToolkit, Logger, - JobDocOutputExecuted, + JobDocOutput, } from '../../types'; import { JobDocPayloadPanelCsv } from '../../export_types/csv_from_savedobject/types'; import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request'; @@ -33,7 +33,7 @@ export function registerGenerateCsvFromSavedObjectImmediate( server: ServerFacade, parentLogger: Logger ) { - const routeOptions = getRouteOptionsCsv(server); + const routeOptions = getRouteOptionsCsv(server, parentLogger); /* * CSV export with the `immediate` option does not queue a job with Reporting's ESQueue to run the job async. Instead, this does: @@ -55,8 +55,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); - const executeJobFn = executeJobFactory(server, { + const createJobFn = createJobFactory(server, logger); + const executeJobFn = executeJobFactory(server, logger, { browserDriverFactory: {} as HeadlessChromiumDriverFactory, }); const jobDocPayload: JobDocPayloadPanelCsv = await createJobFn( @@ -68,7 +68,7 @@ export function registerGenerateCsvFromSavedObjectImmediate( content_type: jobOutputContentType, content: jobOutputContent, size: jobOutputSize, - }: JobDocOutputExecuted = await executeJobFn(null, jobDocPayload, request); + }: JobDocOutput = await executeJobFn(null, jobDocPayload, request); logger.info(`Job output size: ${jobOutputSize} bytes`); diff --git a/x-pack/legacy/plugins/reporting/server/routes/generation.ts b/x-pack/legacy/plugins/reporting/server/routes/generation.ts index 8f5f5397d044dd..81dd5650df4635 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generation.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generation.ts @@ -32,8 +32,8 @@ export function registerJobGenerationRoutes( // @ts-ignore TODO const { errors: esErrors } = server.plugins.elasticsearch.getCluster('admin'); - const esqueue = createQueueFactory(server, { exportTypesRegistry, browserDriverFactory }); - const enqueueJob = enqueueJobFactory(server, { exportTypesRegistry, esqueue }); + const esqueue = createQueueFactory(server, logger, { exportTypesRegistry, browserDriverFactory }); + const enqueueJob = enqueueJobFactory(server, logger, { exportTypesRegistry, esqueue }); /* * Generates enqueued job details to use in responses @@ -48,7 +48,7 @@ export function registerJobGenerationRoutes( const user = request.pre.user; const headers = request.headers; - const job = await enqueueJob(logger, exportTypeId, jobParams, user, headers, request); + const job = await enqueueJob(exportTypeId, jobParams, user, headers, request); // return the queue's job information const jobJson = job.toJSON(); @@ -74,12 +74,12 @@ export function registerJobGenerationRoutes( return err; } - registerGenerateFromJobParams(server, handler, handleError); - registerLegacy(server, handler, handleError); + registerGenerateFromJobParams(server, handler, handleError, logger); + registerLegacy(server, handler, handleError, logger); // 7.x only // Register beta panel-action download-related API's if (config.get('xpack.reporting.csv.enablePanelActionDownload')) { - registerGenerateCsvFromSavedObject(server, handler, handleError); + registerGenerateCsvFromSavedObject(server, handler, handleError, logger); registerGenerateCsvFromSavedObjectImmediate(server, logger); } } diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts index 6084ca613d10ed..049ee0ce20ceb7 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import Boom from 'boom'; import { Legacy } from 'kibana'; -import boom from 'boom'; +import { ResponseObject } from 'hapi'; import { API_BASE_URL } from '../../common/constants'; import { ServerFacade, @@ -16,9 +17,7 @@ import { JobSource, ListQuery, } from '../../types'; -// @ts-ignore import { jobsQueryFactory } from '../lib/jobs_query'; -// @ts-ignore import { jobResponseHandlerFactory } from './lib/job_response_handler'; import { getRouteConfigFactoryDownloadPre, @@ -28,14 +27,18 @@ import { makeRequestFacade } from './lib/make_request_facade'; const MAIN_ENTRY = `${API_BASE_URL}/jobs`; +function isResponse(response: Boom | ResponseObject): response is ResponseObject { + return !(response as Boom).isBoom; +} + export function registerJobInfoRoutes( server: ServerFacade, exportTypesRegistry: ExportTypesRegistry, logger: Logger ) { const jobsQuery = jobsQueryFactory(server); - const getRouteConfig = getRouteConfigFactoryManagementPre(server); - const getRouteConfigDownload = getRouteConfigFactoryDownloadPre(server); + const getRouteConfig = getRouteConfigFactoryManagementPre(server, logger); + const getRouteConfigDownload = getRouteConfigFactoryDownloadPre(server, logger); // list jobs in the queue, paginated server.route({ @@ -84,14 +87,14 @@ export function registerJobInfoRoutes( return jobsQuery.get(request.pre.user, docId, { includeContent: true }).then( (result): JobDocOutput => { if (!result) { - throw boom.notFound(); + throw Boom.notFound(); } const { _source: { jobtype: jobType, output: jobOutput }, } = result; if (!request.pre.management.jobTypes.includes(jobType)) { - throw boom.unauthorized(`Sorry, you are not authorized to download ${jobType} reports`); + throw Boom.unauthorized(`Sorry, you are not authorized to download ${jobType} reports`); } return jobOutput; @@ -111,13 +114,13 @@ export function registerJobInfoRoutes( return jobsQuery.get(request.pre.user, docId).then((result): JobSource['_source'] => { if (!result) { - throw boom.notFound(); + throw Boom.notFound(); } const { _source: job } = result; const { jobtype: jobType, payload: jobPayload } = job; if (!request.pre.management.jobTypes.includes(jobType)) { - throw boom.unauthorized(`Sorry, you are not authorized to view ${jobType} info`); + throw Boom.unauthorized(`Sorry, you are not authorized to view ${jobType} info`); } return { @@ -147,21 +150,22 @@ export function registerJobInfoRoutes( h, { docId } ); - const { statusCode } = response; - - if (statusCode !== 200) { - if (statusCode === 500) { - logger.error(`Report ${docId} has failed: ${JSON.stringify(response.source)}`); - } else { - logger.debug( - `Report ${docId} has non-OK status: [${statusCode}] Reason: [${JSON.stringify( - response.source - )}]` - ); + + if (isResponse(response)) { + const { statusCode } = response; + + if (statusCode !== 200) { + if (statusCode === 500) { + logger.error(`Report ${docId} has failed: ${JSON.stringify(response.source)}`); + } else { + logger.debug( + `Report ${docId} has non-OK status: [${statusCode}] Reason: [${JSON.stringify( + response.source + )}]` + ); + } } - } - if (!response.isBoom) { response = response.header('accept-ranges', 'none'); } diff --git a/x-pack/legacy/plugins/reporting/server/routes/legacy.ts b/x-pack/legacy/plugins/reporting/server/routes/legacy.ts index 038d5dc12d6a42..db403672cb2766 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/legacy.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/legacy.ts @@ -7,7 +7,7 @@ import { Legacy } from 'kibana'; import querystring from 'querystring'; import { API_BASE_URL } from '../../common/constants'; -import { ServerFacade, ReportingResponseToolkit } from '../../types'; +import { ServerFacade, ReportingResponseToolkit, Logger } from '../../types'; import { getRouteConfigFactoryReportingPre, GetRouteConfigFactoryFn, @@ -22,9 +22,10 @@ const BASE_GENERATE = `${API_BASE_URL}/generate`; export function registerLegacy( server: ServerFacade, handler: HandlerFunction, - handleError: HandlerErrorFunction + handleError: HandlerErrorFunction, + logger: Logger ) { - const getRouteConfig = getRouteConfigFactoryReportingPre(server); + const getRouteConfig = getRouteConfigFactoryReportingPre(server, logger); function createLegacyPdfRoute({ path, objectType }: { path: string; objectType: string }) { const exportTypeId = 'printablePdf'; @@ -34,7 +35,7 @@ export function registerLegacy( options: getStaticFeatureConfig(getRouteConfig, exportTypeId), handler: async (request: Legacy.Request, h: ReportingResponseToolkit) => { const message = `The following URL is deprecated and will stop working in the next major version: ${request.url.path}`; - server.log(['warning', 'reporting', 'deprecation'], message); + logger.warn(message, ['deprecation']); try { const savedObjectId = request.params.savedId; diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/__tests__/authorized_user_pre_routing.test.js b/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.js similarity index 87% rename from x-pack/legacy/plugins/reporting/server/routes/lib/__tests__/authorized_user_pre_routing.test.js rename to x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.js index 0b2aff53793cdb..841f753f0c09b4 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/__tests__/authorized_user_pre_routing.test.js +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.js @@ -5,8 +5,7 @@ */ import expect from '@kbn/expect'; -import sinon from 'sinon'; -import { authorizedUserPreRoutingFactory } from '../authorized_user_pre_routing'; +import { authorizedUserPreRoutingFactory } from './authorized_user_pre_routing'; describe('authorized_user_pre_routing', function() { // the getClientShield is using `once` which forces us to use a constant mock @@ -14,14 +13,14 @@ describe('authorized_user_pre_routing', function() { // so createMockServer reuses the same 'instance' of the server and overwrites // the properties to contain different values const createMockServer = (function() { - const getUserStub = sinon.stub(); + const getUserStub = jest.fn(); let mockConfig; const mockServer = { - expose: function() {}, - config: function() { + expose() {}, + config() { return { - get: function(key) { + get(key) { return mockConfig[key]; }, }; @@ -45,7 +44,7 @@ describe('authorized_user_pre_routing', function() { mockServer.plugins.xpack_main = { info: !xpackInfoUndefined && { isAvailable: () => xpackInfoAvailable, - feature: function(featureName) { + feature(featureName) { if (featureName === 'security') { return { isEnabled: () => securityEnabled, @@ -56,17 +55,18 @@ describe('authorized_user_pre_routing', function() { }, }; - getUserStub.resetHistory(); - getUserStub.resolves(user); + getUserStub.mockReset(); + getUserStub.mockResolvedValue(user); return mockServer; }; })(); + const getMockLogger = () => ({ warn: jest.fn() }); it('should return with boom notFound when xpackInfo is undefined', async function() { const mockServer = createMockServer({ xpackInfoUndefined: true }); - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer); - const response = await authorizedUserPreRouting(); + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer, getMockLogger()); + const response = await authorizedUserPreRouting({}); expect(response.isBoom).to.be(true); expect(response.output.statusCode).to.be(404); }); @@ -74,7 +74,7 @@ describe('authorized_user_pre_routing', function() { it(`should return with boom notFound when xpackInfo isn't available`, async function() { const mockServer = createMockServer({ xpackInfoAvailable: false }); - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer); + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer, getMockLogger()); const response = await authorizedUserPreRouting(); expect(response.isBoom).to.be(true); expect(response.output.statusCode).to.be(404); @@ -83,7 +83,7 @@ describe('authorized_user_pre_routing', function() { it('should return with null user when security is disabled in Elasticsearch', async function() { const mockServer = createMockServer({ securityEnabled: false }); - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer); + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer, getMockLogger()); const response = await authorizedUserPreRouting(); expect(response).to.be(null); }); @@ -91,7 +91,7 @@ describe('authorized_user_pre_routing', function() { it('should return with boom unauthenticated when security is enabled but no authenticated user', async function() { const mockServer = createMockServer({ user: null }); - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer); + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer, getMockLogger()); const response = await authorizedUserPreRouting(); expect(response.isBoom).to.be(true); expect(response.output.statusCode).to.be(401); @@ -103,7 +103,7 @@ describe('authorized_user_pre_routing', function() { config: { 'xpack.reporting.roles.allow': ['.reporting_user'] }, }); - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer); + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer, getMockLogger()); const response = await authorizedUserPreRouting(); expect(response.isBoom).to.be(true); expect(response.output.statusCode).to.be(403); @@ -116,7 +116,7 @@ describe('authorized_user_pre_routing', function() { config: { 'xpack.reporting.roles.allow': ['.reporting_user'] }, }); - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer); + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer, getMockLogger()); const response = await authorizedUserPreRouting(); expect(response).to.be(user); }); @@ -128,7 +128,7 @@ describe('authorized_user_pre_routing', function() { config: { 'xpack.reporting.roles.allow': [] }, }); - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer); + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockServer, getMockLogger()); const response = await authorizedUserPreRouting(); expect(response).to.be(user); }); 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 eb473e0bc76d45..906f266290a421 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 @@ -8,7 +8,7 @@ import Boom from 'boom'; import { Legacy } from 'kibana'; import { AuthenticatedUser } from '../../../../../../plugins/security/server'; import { getUserFactory } from '../../lib/get_user'; -import { ServerFacade } from '../../../types'; +import { ServerFacade, Logger } from '../../../types'; const superuserRole = 'superuser'; @@ -17,19 +17,19 @@ export type PreRoutingFunction = ( ) => Promise | AuthenticatedUser | null>; export const authorizedUserPreRoutingFactory = function authorizedUserPreRoutingFn( - server: ServerFacade + server: ServerFacade, + logger: Logger ) { - const getUser = getUserFactory(server); + const getUser = getUserFactory(server, logger); const config = server.config(); return async function authorizedUserPreRouting(request: Legacy.Request) { const xpackInfo = server.plugins.xpack_main.info; if (!xpackInfo || !xpackInfo.isAvailable()) { - server.log( - ['reporting', 'authorizedUserPreRouting', 'debug'], - 'Unable to authorize user before xpack info is available.' - ); + logger.warn('Unable to authorize user before xpack info is available.', [ + 'authorizedUserPreRouting', + ]); return Boom.notFound(); } diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/get_document_payload.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/get_document_payload.ts index c3a30f9dda4543..1c0566100e1977 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/get_document_payload.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/get_document_payload.ts @@ -11,8 +11,8 @@ import { ServerFacade, ExportTypesRegistry, ExportTypeDefinition, - JobDocExecuted, - JobDocOutputExecuted, + JobDocOutput, + JobSource, } from '../../../types'; import { CSV_JOB_TYPE } from '../../../common/constants'; @@ -20,14 +20,21 @@ interface ICustomHeaders { [x: string]: any; } -const DEFAULT_TITLE = 'report'; +type ExportTypeType = ExportTypeDefinition; + +interface Payload { + statusCode: number; + content: any; + contentType: string; + headers: Record; +} -type ExportTypeType = ExportTypeDefinition; +const DEFAULT_TITLE = 'report'; const getTitle = (exportType: ExportTypeType, title?: string): string => `${title || DEFAULT_TITLE}.${exportType.jobContentExtension}`; -const getReportingHeaders = (output: JobDocOutputExecuted, exportType: ExportTypeType) => { +const getReportingHeaders = (output: JobDocOutput, exportType: ExportTypeType) => { const metaDataHeaders: ICustomHeaders = {}; if (exportType.jobType === CSV_JOB_TYPE) { @@ -54,7 +61,7 @@ export function getDocumentPayloadFactory( } } - function getCompleted(output: JobDocOutputExecuted, jobType: string, title: string) { + function getCompleted(output: JobDocOutput, jobType: string, title: string) { const exportType = exportTypesRegistry.get((item: ExportTypeType) => item.jobType === jobType); const filename = getTitle(exportType, title); const headers = getReportingHeaders(output, exportType); @@ -70,7 +77,7 @@ export function getDocumentPayloadFactory( }; } - function getFailure(output: JobDocOutputExecuted) { + function getFailure(output: JobDocOutput) { return { statusCode: 500, content: { @@ -78,6 +85,7 @@ export function getDocumentPayloadFactory( reason: output.content, }, contentType: 'application/json', + headers: {}, }; } @@ -90,9 +98,7 @@ export function getDocumentPayloadFactory( }; } - return function getDocumentPayload(doc: { - _source: JobDocExecuted<{ output: JobDocOutputExecuted }>; - }) { + return function getDocumentPayload(doc: JobSource): Payload { const { status, jobtype: jobType, payload: { title } = { title: '' } } = doc._source; const { output } = doc._source; diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.js b/x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.ts similarity index 65% rename from x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.js rename to x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.ts index e2da3235461136..3ba7aa30eedcb7 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.js +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/job_response_handler.ts @@ -4,29 +4,48 @@ * you may not use this file except in compliance with the Elastic License. */ -import boom from 'boom'; +import Boom from 'boom'; +import { ResponseToolkit } from 'hapi'; +import { ServerFacade, ExportTypesRegistry } from '../../../types'; import { jobsQueryFactory } from '../../lib/jobs_query'; import { WHITELISTED_JOB_CONTENT_TYPES } from '../../../common/constants'; import { getDocumentPayloadFactory } from './get_document_payload'; -export function jobResponseHandlerFactory(server, exportTypesRegistry) { +interface JobResponseHandlerParams { + docId: string; +} + +interface JobResponseHandlerOpts { + excludeContent?: boolean; +} + +export function jobResponseHandlerFactory( + server: ServerFacade, + exportTypesRegistry: ExportTypesRegistry +) { const jobsQuery = jobsQueryFactory(server); const getDocumentPayload = getDocumentPayloadFactory(server, exportTypesRegistry); - return function jobResponseHandler(validJobTypes, user, h, params, opts = {}) { + return function jobResponseHandler( + validJobTypes: string[], + user: any, + h: ResponseToolkit, + params: JobResponseHandlerParams, + opts: JobResponseHandlerOpts = {} + ) { const { docId } = params; return jobsQuery.get(user, docId, { includeContent: !opts.excludeContent }).then(doc => { - if (!doc) return boom.notFound(); + if (!doc) return Boom.notFound(); const { jobtype: jobType } = doc._source; if (!validJobTypes.includes(jobType)) { - return boom.unauthorized(`Sorry, you are not authorized to download ${jobType} reports`); + return Boom.unauthorized(`Sorry, you are not authorized to download ${jobType} reports`); } const output = getDocumentPayload(doc); if (!WHITELISTED_JOB_CONTENT_TYPES.includes(output.contentType)) { - return boom.badImplementation( + return Boom.badImplementation( `Unsupported content-type of ${output.contentType} specified by job output` ); } @@ -42,7 +61,7 @@ export function jobResponseHandlerFactory(server, exportTypesRegistry) { }); } - return response; + return response; // Hapi }); }; } 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 6efac818981efa..88c5e4edc12f87 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 @@ -6,12 +6,13 @@ import Boom from 'boom'; import { Legacy } from 'kibana'; -import { ServerFacade } from '../../../types'; +import { Logger, ServerFacade } from '../../../types'; export type GetReportingFeatureIdFn = (request: Legacy.Request) => string; export const reportingFeaturePreRoutingFactory = function reportingFeaturePreRoutingFn( - server: ServerFacade + server: ServerFacade, + logger: Logger ) { const xpackMainPlugin = server.plugins.xpack_main; const pluginId = 'reporting'; @@ -20,7 +21,7 @@ export const reportingFeaturePreRoutingFactory = function reportingFeaturePreRou return function reportingFeaturePreRouting(getReportingFeatureId: GetReportingFeatureIdFn) { return function licensePreRouting(request: Legacy.Request) { const licenseCheckResults = xpackMainPlugin.info.feature(pluginId).getLicenseCheckResults(); - const reportingFeatureId = getReportingFeatureId(request); + const reportingFeatureId = getReportingFeatureId(request) as string; const reportingFeature = licenseCheckResults[reportingFeatureId]; if (!reportingFeature.showLinks || !reportingFeature.enableLinks) { throw Boom.forbidden(reportingFeature.message); 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 caf24bf64f6023..25c08261490d56 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 @@ -6,7 +6,7 @@ import Joi from 'joi'; import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; -import { ServerFacade } from '../../../types'; +import { ServerFacade, Logger } from '../../../types'; import { authorizedUserPreRoutingFactory } from './authorized_user_pre_routing'; import { reportingFeaturePreRoutingFactory } from './reporting_feature_pre_routing'; import { GetReportingFeatureIdFn } from './reporting_feature_pre_routing'; @@ -25,9 +25,12 @@ export type GetRouteConfigFactoryFn = ( getFeatureId?: GetReportingFeatureIdFn ) => RouteConfigFactory; -export function getRouteConfigFactoryReportingPre(server: ServerFacade): GetRouteConfigFactoryFn { - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(server); - const reportingFeaturePreRouting = reportingFeaturePreRoutingFactory(server); +export function getRouteConfigFactoryReportingPre( + server: ServerFacade, + logger: Logger +): GetRouteConfigFactoryFn { + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(server, logger); + const reportingFeaturePreRouting = reportingFeaturePreRoutingFactory(server, logger); return (getFeatureId?: GetReportingFeatureIdFn): RouteConfigFactory => { const preRouting: any[] = [{ method: authorizedUserPreRouting, assign: 'user' }]; @@ -42,8 +45,8 @@ export function getRouteConfigFactoryReportingPre(server: ServerFacade): GetRout }; } -export function getRouteOptionsCsv(server: ServerFacade) { - const getRouteConfig = getRouteConfigFactoryReportingPre(server); +export function getRouteOptionsCsv(server: ServerFacade, logger: Logger) { + const getRouteConfig = getRouteConfigFactoryReportingPre(server, logger); return { ...getRouteConfig(() => CSV_FROM_SAVEDOBJECT_JOB_TYPE), validate: { @@ -63,9 +66,12 @@ export function getRouteOptionsCsv(server: ServerFacade) { }; } -export function getRouteConfigFactoryManagementPre(server: ServerFacade): GetRouteConfigFactoryFn { - const authorizedUserPreRouting = authorizedUserPreRoutingFactory(server); - const reportingFeaturePreRouting = reportingFeaturePreRoutingFactory(server); +export function getRouteConfigFactoryManagementPre( + server: ServerFacade, + logger: Logger +): GetRouteConfigFactoryFn { + const authorizedUserPreRouting = authorizedUserPreRoutingFactory(server, logger); + const reportingFeaturePreRouting = reportingFeaturePreRoutingFactory(server, logger); const managementPreRouting = reportingFeaturePreRouting(() => 'management'); return (): RouteConfigFactory => { @@ -83,8 +89,11 @@ export function getRouteConfigFactoryManagementPre(server: ServerFacade): GetRou // TOC at the end of the PDF, but it's sending multiple cookies and causing our auth to fail with a 401. // Additionally, the range-request doesn't alleviate any performance issues on the server as the entire // download is loaded into memory. -export function getRouteConfigFactoryDownloadPre(server: ServerFacade): GetRouteConfigFactoryFn { - const getManagementRouteConfig = getRouteConfigFactoryManagementPre(server); +export function getRouteConfigFactoryDownloadPre( + server: ServerFacade, + logger: Logger +): GetRouteConfigFactoryFn { + const getManagementRouteConfig = getRouteConfigFactoryManagementPre(server, logger); return (): RouteConfigFactory => ({ ...getManagementRouteConfig(), tags: [API_TAG], diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index 406d9571e8d718..a921b34a065041 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -74,7 +74,6 @@ export type ServerFacade = LegacySetup; export type ReportingPluginSpecOptions = Legacy.PluginSpecOptions; export type EnqueueJobFn = ( - parentLogger: LevelLogger, exportTypeId: string, jobParams: JobParamsType, user: string, @@ -200,18 +199,6 @@ export interface JobDocPayload { type: string | null; } -export interface JobDocOutput { - content: string; // encoded content - contentType: string; -} - -export interface JobDocExecuted { - jobtype: string; - output: JobDocOutputExecuted; - payload: JobDocPayload; - status: string; // completed, failed, etc -} - export interface JobSource { _id: string; _source: { @@ -222,21 +209,9 @@ export interface JobSource { }; } -/* - * A snake_cased field is the only significant difference in structure of - * JobDocOutputExecuted vs JobDocOutput. - * - * JobDocOutput is the structure of the object returned by getDocumentPayload - * - * data in the _source fields of the - * Reporting index. - * - * The ESQueueWorker internals have executed job objects returned with this - * structure. See `_formatOutput` in reporting/server/lib/esqueue/worker.js - */ -export interface JobDocOutputExecuted { - content_type: string; // vs `contentType` above - content: string | null; // defaultOutput is null +export interface JobDocOutput { + content_type: string; + content: string | null; max_size_reached: boolean; size: number; } @@ -279,7 +254,7 @@ export type ImmediateExecuteFn = ( jobId: null, job: JobDocPayload, request: RequestFacade -) => Promise; +) => Promise; export interface ESQueueWorkerOptions { kibanaName: string; @@ -292,7 +267,7 @@ export interface ESQueueWorkerOptions { type GenericWorkerFn = ( jobSource: JobSource, ...workerRestArgs: any[] -) => void | Promise; +) => void | Promise; export interface ESQueueInstance { registerWorker: ( @@ -302,9 +277,13 @@ export interface ESQueueInstance { ) => ESQueueWorker; } -export type CreateJobFactory = (server: ServerFacade) => CreateJobFnType; +export type CreateJobFactory = ( + server: ServerFacade, + logger: LevelLogger +) => CreateJobFnType; export type ExecuteJobFactory = ( server: ServerFacade, + logger: LevelLogger, opts: { browserDriverFactory: HeadlessChromiumDriverFactory; }