From 51b049aad11ead28ae49f81d2b1e2b6f467f2944 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 11:16:55 -0700 Subject: [PATCH 01/47] prep to use internal prefix for APIs --- .../server/routes/deprecations/deprecations.ts | 11 +++++++---- .../reporting/server/routes/diagnostic/browser.ts | 2 +- .../reporting/server/routes/diagnostic/index.ts | 2 +- .../server/routes/generate/generate_from_jobparams.ts | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts index ac9cf8be2074b..6aca67a62f003 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts @@ -17,10 +17,9 @@ import { IlmPolicyManager } from '../../lib'; import { deprecations } from '../../lib/deprecations'; import { getCounters } from '../lib'; -export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Logger) => { - const { router } = reporting.getPluginSetupDeps(); - - const authzWrapper = (handler: RequestHandler): RequestHandler => { +const getAuthzWrapper = + (reporting: ReportingCore, logger: Logger) => + (handler: RequestHandler): RequestHandler => { return async (ctx, req, res) => { const { security } = reporting.getPluginSetupDeps(); if (!security?.license.isEnabled()) { @@ -56,6 +55,10 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log }; }; +export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Logger) => { + const { router } = reporting.getPluginSetupDeps(); + const authzWrapper = getAuthzWrapper(reporting, logger); + router.get( { path: API_GET_ILM_POLICY_STATUS, validate: false }, authzWrapper(async ({ core }, req, res) => { diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts index 93a5ab72aff28..dadebcc1c2388 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts @@ -42,7 +42,7 @@ export const registerDiagnoseBrowser = (reporting: ReportingCore, logger: Logger const { router } = reporting.getPluginSetupDeps(); router.post( - { path: `${path}`, validate: {} }, + { path, validate: {} }, authorizedUserPreRouting(reporting, async (_user, _context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/index.ts b/x-pack/plugins/reporting/server/routes/diagnostic/index.ts index 952163a260806..48a48efab4806 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/index.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/index.ts @@ -12,7 +12,7 @@ import { registerDiagnoseScreenshot } from './screenshot'; export const registerDiagnosticRoutes = (reporting: ReportingCore, logger: Logger) => { registerDiagnoseBrowser(reporting, logger); - registerDiagnoseScreenshot(reporting, logger); + registerDiagnoseScreenshot(reporting, logger); // TODO remove }; export interface DiagnosticResponse { diff --git a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts index 373e72efb8bd0..68625a0ef25a6 100644 --- a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts @@ -28,6 +28,7 @@ export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Lo const registerPostGenerationEndpoint = () => { const path = `${BASE_GENERATE}/{exportType}`; + router.post( { path, From 8a859d86f6cd82da76d82a1e22b6fa42d5963895 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 15:24:44 -0700 Subject: [PATCH 02/47] internal and public routes objects --- .../reporting/common/constants/index.ts | 19 ++----- .../reporting/common/constants/routes.ts | 34 +++++++++++++ .../public/lib/reporting_api_client/hooks.ts | 9 ++-- .../reporting_api_client.ts | 50 ++++++++++--------- .../routes/deprecations/deprecations.ts | 24 +++------ .../integration_tests/deprecations.test.ts | 8 +-- .../server/routes/diagnostic/browser.ts | 4 +- .../server/routes/diagnostic/screenshot.ts | 4 +- .../generate/csv_searchsource_immediate.ts | 7 +-- .../generate/generate_from_jobparams.ts | 8 ++- .../server/routes/lib/request_handler.ts | 4 +- .../server/routes/management/jobs.ts | 22 +++----- .../services/scenarios.ts | 15 ++---- 13 files changed, 99 insertions(+), 109 deletions(-) create mode 100644 x-pack/plugins/reporting/common/constants/routes.ts diff --git a/x-pack/plugins/reporting/common/constants/index.ts b/x-pack/plugins/reporting/common/constants/index.ts index 1a3cbde98f838..f27e91dc960b4 100644 --- a/x-pack/plugins/reporting/common/constants/index.ts +++ b/x-pack/plugins/reporting/common/constants/index.ts @@ -6,8 +6,8 @@ */ import { CONTENT_TYPE_CSV } from '@kbn/generate-csv/src/constants'; -import * as reportTypes from './report_types'; import * as jobTypes from './job_types'; +import * as reportTypes from './report_types'; const { PDF_JOB_TYPE, PDF_JOB_TYPE_V2, PNG_JOB_TYPE, PNG_JOB_TYPE_V2 } = jobTypes; @@ -29,8 +29,9 @@ export const ALLOWED_JOB_CONTENT_TYPES = [ ]; // Re-export type definitions here for convenience. -export * from './report_types'; export * from './job_types'; +export * from './report_types'; +export * from './routes'; type ReportTypeDeclaration = typeof reportTypes; export type ReportTypes = ReportTypeDeclaration[keyof ReportTypeDeclaration]; @@ -62,16 +63,6 @@ export const LICENSE_TYPE_GOLD = 'gold'; export const LICENSE_TYPE_PLATINUM = 'platinum'; export const LICENSE_TYPE_ENTERPRISE = 'enterprise'; -// Routes -export const API_BASE_URL = '/api/reporting'; // "Generation URL" from share menu -export const API_BASE_GENERATE = `${API_BASE_URL}/generate`; -export const API_LIST_URL = `${API_BASE_URL}/jobs`; -export const API_DIAGNOSE_URL = `${API_BASE_URL}/diagnose`; - -export const API_GET_ILM_POLICY_STATUS = `${API_BASE_URL}/ilm_policy_status`; -export const API_MIGRATE_ILM_POLICY_URL = `${API_BASE_URL}/deprecations/migrate_ilm_policy`; -export const API_BASE_URL_V1 = '/api/reporting/v1'; // - export const ILM_POLICY_NAME = 'kibana-reporting'; // Usage counter types @@ -110,7 +101,3 @@ export const REPORT_TABLE_ROW_ID = 'reportJobRow'; // automation that have no version value in the job params, we assume the // intended version is 7.14.0 export const UNVERSIONED_VERSION = '7.14.0'; - -// hacky endpoint: download CSV without queueing a report -// FIXME: find a way to make these endpoints "generic" instead of hardcoded, as are the queued report export types -export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv_searchsource`; diff --git a/x-pack/plugins/reporting/common/constants/routes.ts b/x-pack/plugins/reporting/common/constants/routes.ts new file mode 100644 index 0000000000000..374ce418a4c3e --- /dev/null +++ b/x-pack/plugins/reporting/common/constants/routes.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +const INTERNAL_API_PREFIX = '/_internal/reporting'; +export const INTERNAL_ROUTES = { + MIGRATE: { + MIGRATE_ILM_POLICY: INTERNAL_API_PREFIX + '/ilm_policy', + GET_ILM_POLICY_STATUS: INTERNAL_API_PREFIX + '/ilm_policy_status', + }, + DIAGNOSE: { + BROWSER: INTERNAL_API_PREFIX + '/diagnose/browser', + SCREENSHOT: INTERNAL_API_PREFIX + '/diagnose/screenshot', + }, + JOBS: { + COUNT: INTERNAL_API_PREFIX + '/jobs/count', + LIST: INTERNAL_API_PREFIX + '/jobs/list', + INFO_PREFIX: INTERNAL_API_PREFIX + '/jobs/info', // docId is added to the final path + DELETE_PREFIX: INTERNAL_API_PREFIX + '/jobs/delete', // docId is added to the final path + DOWNLOAD_PREFIX: INTERNAL_API_PREFIX + '/jobs/download', // docId is added to the final path + }, + GENERATE: { + CSV_IMMEDIATE: INTERNAL_API_PREFIX + '/generate/immediate/csv_searchsource', + EXPORT_TYPE_PREFIX: INTERNAL_API_PREFIX + '/generate', // exportTypeId is added to the final path + }, +}; + +export const PUBLIC_ROUTES = { + GENERATE_PREFIX: `/api/reporting/generate`, // public endpoint for POST URL strings, exportTypeId is added to the final path + DOWNLOAD_PREFIX: `/api/reporting/download`, // public endpoint used by Watcher, jobId is added to the final path +}; diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/hooks.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/hooks.ts index 0b697b333dddd..8410ec8f82019 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/hooks.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/hooks.ts @@ -5,16 +5,13 @@ * 2.0. */ -import { useRequest, UseRequestResponse } from '../../shared_imports'; +import { INTERNAL_ROUTES } from '../../../common/constants'; import { IlmPolicyStatusResponse } from '../../../common/types'; - -import { API_GET_ILM_POLICY_STATUS } from '../../../common/constants'; - -import { useKibana } from '../../shared_imports'; +import { useKibana, useRequest, UseRequestResponse } from '../../shared_imports'; export const useCheckIlmPolicyStatus = (): UseRequestResponse => { const { services: { http }, } = useKibana(); - return useRequest(http, { path: API_GET_ILM_POLICY_STATUS, method: 'get' }); + return useRequest(http, { path: INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS, method: 'get' }); }; diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index 91abac38f4d66..d316b100e3bb1 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -4,20 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { HttpFetchQuery } from '@kbn/core/public'; +import { HttpSetup, IUiSettingsClient } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; +import rison from '@kbn/rison'; import moment from 'moment'; import { stringify } from 'query-string'; -import rison from '@kbn/rison'; -import type { HttpFetchQuery } from '@kbn/core/public'; -import { HttpSetup, IUiSettingsClient } from '@kbn/core/public'; import { buildKibanaPath } from '../../../common/build_kibana_path'; import { - API_BASE_GENERATE, - API_BASE_URL, - API_GENERATE_IMMEDIATE, - API_LIST_URL, - API_MIGRATE_ILM_POLICY_URL, getRedirectAppPath, + INTERNAL_ROUTES, REPORTING_MANAGEMENT_HOME, } from '../../../common/constants'; import { @@ -97,8 +93,9 @@ export class ReportingAPIClient implements IReportingAPI { } public getReportURL(jobId: string) { - const apiBaseUrl = this.http.basePath.prepend(API_LIST_URL); - const downloadLink = `${apiBaseUrl}/download/${jobId}`; + const downloadLink = this.http.basePath.prepend( + `${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/${jobId}` + ); return downloadLink; } @@ -110,7 +107,7 @@ export class ReportingAPIClient implements IReportingAPI { } public async deleteReport(jobId: string) { - return await this.http.delete(`${API_LIST_URL}/delete/${jobId}`, { + return await this.http.delete(`${INTERNAL_ROUTES.JOBS.DELETE_PREFIX}/${jobId}`, { asSystemRequest: true, }); } @@ -122,7 +119,7 @@ export class ReportingAPIClient implements IReportingAPI { query.ids = jobIds.slice(0, 10).join(','); } - const jobQueueEntries: ReportApiJSON[] = await this.http.get(`${API_LIST_URL}/list`, { + const jobQueueEntries: ReportApiJSON[] = await this.http.get(INTERNAL_ROUTES.JOBS.LIST, { query, asSystemRequest: true, }); @@ -131,7 +128,7 @@ export class ReportingAPIClient implements IReportingAPI { } public async total() { - return await this.http.get(`${API_LIST_URL}/count`, { + return await this.http.get(INTERNAL_ROUTES.JOBS.COUNT, { asSystemRequest: true, }); } @@ -151,14 +148,17 @@ export class ReportingAPIClient implements IReportingAPI { } public async getInfo(jobId: string) { - const report: ReportApiJSON = await this.http.get(`${API_LIST_URL}/info/${jobId}`, { - asSystemRequest: true, - }); + const report: ReportApiJSON = await this.http.get( + `${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/info/${jobId}`, + { + asSystemRequest: true, + } + ); return new Job(report); } public async findForJobIds(jobIds: JobId[]) { - const reports: ReportApiJSON[] = await this.http.fetch(`${API_LIST_URL}/list`, { + const reports: ReportApiJSON[] = await this.http.fetch(INTERNAL_ROUTES.JOBS.LIST, { query: { page: 0, ids: jobIds.join(',') }, method: 'GET', }); @@ -169,13 +169,15 @@ export class ReportingAPIClient implements IReportingAPI { const params = stringify({ jobParams: rison.encode(jobParams), }); - return `${this.http.basePath.prepend(API_BASE_GENERATE)}/${exportType}?${params}`; + return `${this.http.basePath.prepend( + INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX + )}/${exportType}?${params}`; } public async createReportingJob(exportType: string, jobParams: BaseParams) { const jobParamsRison = rison.encode(jobParams); const resp: { job: ReportApiJSON } = await this.http.post( - `${API_BASE_GENERATE}/${exportType}`, + `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/${exportType}`, { method: 'POST', body: JSON.stringify({ @@ -191,7 +193,7 @@ export class ReportingAPIClient implements IReportingAPI { public async createImmediateReport(baseParams: BaseParams) { const { objectType: _objectType, ...params } = baseParams; // objectType is not needed for immediate download api - return this.http.post(`${API_GENERATE_IMMEDIATE}`, { + return this.http.post(INTERNAL_ROUTES.GENERATE.CSV_IMMEDIATE, { asResponse: true, body: JSON.stringify(params), }); @@ -217,23 +219,23 @@ export class ReportingAPIClient implements IReportingAPI { this.http.basePath.prepend(REPORTING_MANAGEMENT_HOME); public getDownloadLink: DownloadReportFn = (jobId: JobId) => - this.http.basePath.prepend(`${API_LIST_URL}/download/${jobId}`); + this.http.basePath.prepend(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/${jobId}`); public getServerBasePath = () => this.http.basePath.serverBasePath; public verifyBrowser() { - return this.http.post(`${API_BASE_URL}/diagnose/browser`, { + return this.http.post(INTERNAL_ROUTES.DIAGNOSE.BROWSER, { asSystemRequest: true, }); } public verifyScreenCapture() { - return this.http.post(`${API_BASE_URL}/diagnose/screenshot`, { + return this.http.post(INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT, { asSystemRequest: true, }); } public migrateReportingIndicesIlmPolicy() { - return this.http.put(`${API_MIGRATE_ILM_POLICY_URL}`); + return this.http.put(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS); // FIXME This is wrong } } diff --git a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts index 6aca67a62f003..6786520ece0d6 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts @@ -6,11 +6,7 @@ */ import { errors } from '@elastic/elasticsearch'; import type { Logger, RequestHandler } from '@kbn/core/server'; -import { - API_GET_ILM_POLICY_STATUS, - API_MIGRATE_ILM_POLICY_URL, - ILM_POLICY_NAME, -} from '../../../common/constants'; +import { INTERNAL_ROUTES, ILM_POLICY_NAME } from '../../../common/constants'; import type { IlmPolicyStatusResponse } from '../../../common/types'; import type { ReportingCore } from '../../core'; import { IlmPolicyManager } from '../../lib'; @@ -59,14 +55,11 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log const { router } = reporting.getPluginSetupDeps(); const authzWrapper = getAuthzWrapper(reporting, logger); + const getStatusPath = INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS; router.get( - { path: API_GET_ILM_POLICY_STATUS, validate: false }, + { path: getStatusPath, validate: false }, authzWrapper(async ({ core }, req, res) => { - const counters = getCounters( - req.route.method, - API_GET_ILM_POLICY_STATUS, - reporting.getUsageCounter() - ); + const counters = getCounters(req.route.method, getStatusPath, reporting.getUsageCounter()); const { elasticsearch: { client: scopedClient }, @@ -99,14 +92,11 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log }) ); + const migrateApiPath = INTERNAL_ROUTES.MIGRATE.MIGRATE_ILM_POLICY; router.put( - { path: API_MIGRATE_ILM_POLICY_URL, validate: false }, + { path: migrateApiPath, validate: false }, authzWrapper(async ({ core }, req, res) => { - const counters = getCounters( - req.route.method, - API_GET_ILM_POLICY_STATUS, - reporting.getUsageCounter() - ); + const counters = getCounters(req.route.method, migrateApiPath, reporting.getUsageCounter()); const store = await reporting.getStore(); const { diff --git a/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts index 02aa8de4959d7..27882a26149bf 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts @@ -10,7 +10,7 @@ import { setupServer } from '@kbn/core-test-helpers-test-utils'; import supertest from 'supertest'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { securityMock } from '@kbn/security-plugin/server/mocks'; -import { API_GET_ILM_POLICY_STATUS } from '../../../../common/constants'; +import { INTERNAL_ROUTES } from '../../../../common/constants'; import { createMockConfigSchema, createMockPluginSetup, @@ -21,7 +21,7 @@ import { registerDeprecationsRoutes } from '../deprecations'; type SetupServerReturn = Awaited>; -describe(`GET ${API_GET_ILM_POLICY_STATUS}`, () => { +describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { jest.setTimeout(6000); const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; @@ -57,7 +57,7 @@ describe(`GET ${API_GET_ILM_POLICY_STATUS}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .get(API_GET_ILM_POLICY_STATUS) + .get(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS) .expect(200) .then(/* Ignore result */); }); @@ -71,7 +71,7 @@ describe(`GET ${API_GET_ILM_POLICY_STATUS}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .get(API_GET_ILM_POLICY_STATUS) + .get(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS) .expect(200) .then(/* Ignore result */); }); diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts index dadebcc1c2388..0a9b8e3a6a6c8 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { lastValueFrom } from 'rxjs'; import type { DiagnosticResponse } from '.'; import type { ReportingCore } from '../..'; -import { API_DIAGNOSE_URL } from '../../../common/constants'; +import { INTERNAL_ROUTES } from '../../../common/constants'; import { authorizedUserPreRouting, getCounters } from '../lib'; const logsToHelpMapFactory = (docLinks: DocLinksServiceSetup) => ({ @@ -36,7 +36,7 @@ const logsToHelpMapFactory = (docLinks: DocLinksServiceSetup) => ({ }), }); -const path = `${API_DIAGNOSE_URL}/browser`; +const path = INTERNAL_ROUTES.DIAGNOSE.BROWSER; export const registerDiagnoseBrowser = (reporting: ReportingCore, logger: Logger) => { const { router } = reporting.getPluginSetupDeps(); diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts index 1afb438981b97..33f2f420c4673 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts @@ -10,12 +10,12 @@ import { APP_WRAPPER_CLASS } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { lastValueFrom } from 'rxjs'; import type { ReportingCore } from '../..'; -import { API_DIAGNOSE_URL } from '../../../common/constants'; +import { INTERNAL_ROUTES } from '../../../common/constants'; import { generatePngObservable } from '../../export_types/common'; import { getAbsoluteUrlFactory } from '../../export_types/common/get_absolute_url'; import { authorizedUserPreRouting, getCounters } from '../lib'; -const path = `${API_DIAGNOSE_URL}/screenshot`; +const path = INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT; export const registerDiagnoseScreenshot = (reporting: ReportingCore, logger: Logger) => { const setupDeps = reporting.getPluginSetupDeps(); diff --git a/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts b/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts index 7c4886be207b3..f5ceac67563c1 100644 --- a/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts @@ -10,19 +10,16 @@ import { schema } from '@kbn/config-schema'; import type { KibanaRequest, Logger } from '@kbn/core/server'; import moment from 'moment'; import type { ReportingCore } from '../..'; -import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE } from '../../../common/constants'; +import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE, INTERNAL_ROUTES } from '../../../common/constants'; import { runTaskFnFactory } from '../../export_types/csv_searchsource_immediate/execute_job'; import type { JobParamsDownloadCSV } from '../../export_types/csv_searchsource_immediate/types'; import { PassThroughStream } from '../../lib'; import { authorizedUserPreRouting, getCounters } from '../lib'; -const API_BASE_URL_V1 = '/api/reporting/v1'; -const API_BASE_GENERATE_V1 = `${API_BASE_URL_V1}/generate`; +const path = INTERNAL_ROUTES.GENERATE.CSV_IMMEDIATE; export type CsvFromSavedObjectRequest = KibanaRequest; -const path = `${API_BASE_GENERATE_V1}/immediate/csv_searchsource`; - /* * This function registers API Endpoints for immediate Reporting jobs. The API inputs are: * - saved object type and ID diff --git a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts index 68625a0ef25a6..4f5481e4be00b 100644 --- a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts @@ -9,12 +9,10 @@ import { schema } from '@kbn/config-schema'; import type { Logger } from '@kbn/core/server'; import rison from '@kbn/rison'; import type { ReportingCore } from '../..'; -import { API_BASE_URL } from '../../../common/constants'; +import { INTERNAL_ROUTES } from '../../../common/constants'; import type { BaseParams } from '../../types'; import { authorizedUserPreRouting, getCounters, RequestHandler } from '../lib'; -const BASE_GENERATE = `${API_BASE_URL}/generate`; - export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; @@ -27,7 +25,7 @@ export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Lo const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; const registerPostGenerationEndpoint = () => { - const path = `${BASE_GENERATE}/{exportType}`; + const path = `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{exportType}`; router.post( { @@ -98,7 +96,7 @@ export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Lo // Get route to generation endpoint: show error about GET method to user router.get( { - path: `${BASE_GENERATE}/{p*}`, + path: `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{p*}`, validate: false, }, (_context, _req, res) => { diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts b/x-pack/plugins/reporting/server/routes/lib/request_handler.ts index 234048e5b2221..a1c7ec866f0d5 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts +++ b/x-pack/plugins/reporting/server/routes/lib/request_handler.ts @@ -10,7 +10,7 @@ import Boom from '@hapi/boom'; import { i18n } from '@kbn/i18n'; import type { KibanaRequest, KibanaResponseFactory, Logger } from '@kbn/core/server'; import type { ReportingCore } from '../..'; -import { API_BASE_URL } from '../../../common/constants'; +import { INTERNAL_ROUTES } from '../../../common/constants'; import { checkParamsVersion, cryptoFactory } from '../../lib'; import { Report } from '../../lib/store'; import type { BaseParams, ReportingRequestHandlerContext, ReportingUser } from '../../types'; @@ -22,7 +22,7 @@ export const handleUnavailable = (res: KibanaResponseFactory) => { const getDownloadBaseUrl = (reporting: ReportingCore) => { const { basePath } = reporting.getServerInfo(); - return basePath + `${API_BASE_URL}/jobs/download`; + return basePath + INTERNAL_ROUTES.JOBS.DOWNLOAD; }; /** diff --git a/x-pack/plugins/reporting/server/routes/management/jobs.ts b/x-pack/plugins/reporting/server/routes/management/jobs.ts index 9b2a63deca2ba..71f2af5aa46ef 100644 --- a/x-pack/plugins/reporting/server/routes/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/management/jobs.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; import { promisify } from 'util'; import { ReportingCore } from '../..'; -import { ALLOWED_JOB_CONTENT_TYPES, API_BASE_URL } from '../../../common/constants'; +import { ALLOWED_JOB_CONTENT_TYPES, INTERNAL_ROUTES } from '../../../common/constants'; import { getContentStream } from '../../lib'; import { authorizedUserPreRouting, @@ -19,8 +19,6 @@ import { jobsQueryFactory, } from '../lib'; -const MAIN_ENTRY = `${API_BASE_URL}/jobs`; - export function registerJobInfoRoutes(reporting: ReportingCore) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; @@ -28,7 +26,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerGetList = () => { // list jobs in the queue, paginated - const path = `${MAIN_ENTRY}/list`; + const path = INTERNAL_ROUTES.JOBS.LIST; router.get( { @@ -72,13 +70,10 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerGetCount = () => { // return the count of all jobs in the queue - const path = `${MAIN_ENTRY}/count`; + const path = INTERNAL_ROUTES.JOBS.COUNT; router.get( - { - path, - validate: false, - }, + { path, validate: false }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); @@ -107,8 +102,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerGetInfo = () => { // return some info about the job - const path = `${MAIN_ENTRY}/info/{docId}`; - + const path = INTERNAL_ROUTES.JOBS.INFO; router.get( { path, @@ -141,8 +135,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerDownloadReport = () => { // trigger a download of the output from a job - const path = `${MAIN_ENTRY}/download/{docId}`; - + const path = INTERNAL_ROUTES.JOBS.DOWNLOAD; router.get( { path, @@ -192,8 +185,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerDeleteReport = () => { // allow a report to be deleted - const path = `${MAIN_ENTRY}/delete/{docId}`; - + const path = INTERNAL_ROUTES.JOBS.DELETE; router.delete( { path, diff --git a/x-pack/test/reporting_api_integration/services/scenarios.ts b/x-pack/test/reporting_api_integration/services/scenarios.ts index 181af370e42c6..54e03b1facb59 100644 --- a/x-pack/test/reporting_api_integration/services/scenarios.ts +++ b/x-pack/test/reporting_api_integration/services/scenarios.ts @@ -5,15 +5,12 @@ * 2.0. */ -import rison from '@kbn/rison'; -import { - API_GET_ILM_POLICY_STATUS, - API_MIGRATE_ILM_POLICY_URL, -} from '@kbn/reporting-plugin/common/constants'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; import { JobParamsCSV } from '@kbn/reporting-plugin/server/export_types/csv_searchsource/types'; import { JobParamsDownloadCSV } from '@kbn/reporting-plugin/server/export_types/csv_searchsource_immediate/types'; import { JobParamsPNGDeprecated } from '@kbn/reporting-plugin/server/export_types/png/types'; import { JobParamsPDFDeprecated } from '@kbn/reporting-plugin/server/export_types/printable_pdf/types'; +import rison from '@kbn/rison'; import { FtrProviderContext } from '../ftr_provider_context'; function removeWhitespace(str: string) { @@ -223,7 +220,7 @@ export function createScenarios({ getService }: Pick { log.debug('ReportingAPI.checkIlmMigrationStatus'); const { body } = await supertestWithoutAuth - .get(API_GET_ILM_POLICY_STATUS) + .get(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS) .auth(username, password) .set('kbn-xsrf', 'xxx') .expect(200); @@ -234,7 +231,7 @@ export function createScenarios({ getService }: Pick Date: Wed, 19 Jul 2023 15:39:48 -0700 Subject: [PATCH 03/47] UI Api Client mistakenly set everything asSystemRequest --- .../reporting_api_client.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index d316b100e3bb1..7df818035da39 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -107,9 +107,7 @@ export class ReportingAPIClient implements IReportingAPI { } public async deleteReport(jobId: string) { - return await this.http.delete(`${INTERNAL_ROUTES.JOBS.DELETE_PREFIX}/${jobId}`, { - asSystemRequest: true, - }); + return await this.http.delete(`${INTERNAL_ROUTES.JOBS.DELETE_PREFIX}/${jobId}`); } public async list(page = 0, jobIds: string[] = []) { @@ -149,10 +147,7 @@ export class ReportingAPIClient implements IReportingAPI { public async getInfo(jobId: string) { const report: ReportApiJSON = await this.http.get( - `${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/info/${jobId}`, - { - asSystemRequest: true, - } + `${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/info/${jobId}` ); return new Job(report); } @@ -224,15 +219,11 @@ export class ReportingAPIClient implements IReportingAPI { public getServerBasePath = () => this.http.basePath.serverBasePath; public verifyBrowser() { - return this.http.post(INTERNAL_ROUTES.DIAGNOSE.BROWSER, { - asSystemRequest: true, - }); + return this.http.post(INTERNAL_ROUTES.DIAGNOSE.BROWSER); } public verifyScreenCapture() { - return this.http.post(INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT, { - asSystemRequest: true, - }); + return this.http.post(INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT); } public migrateReportingIndicesIlmPolicy() { From f09c9b7763a6bf83311237362bcb71b88d6ef6f4 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 15:49:11 -0700 Subject: [PATCH 04/47] Use the right route paths --- .../reporting/common/constants/routes.ts | 37 ++++++++++++------- .../reporting_api_client.ts | 2 +- .../server/routes/management/jobs.ts | 6 +-- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/reporting/common/constants/routes.ts b/x-pack/plugins/reporting/common/constants/routes.ts index 374ce418a4c3e..1eeb520dfec92 100644 --- a/x-pack/plugins/reporting/common/constants/routes.ts +++ b/x-pack/plugins/reporting/common/constants/routes.ts @@ -5,30 +5,39 @@ * 2.0. */ -const INTERNAL_API_PREFIX = '/_internal/reporting'; +const prefixInternalPath = '/_internal/reporting'; export const INTERNAL_ROUTES = { MIGRATE: { - MIGRATE_ILM_POLICY: INTERNAL_API_PREFIX + '/ilm_policy', - GET_ILM_POLICY_STATUS: INTERNAL_API_PREFIX + '/ilm_policy_status', + MIGRATE_ILM_POLICY: prefixInternalPath + '/deprecations/migrate_ilm_policy', + GET_ILM_POLICY_STATUS: prefixInternalPath + '/ilm_policy_status', }, DIAGNOSE: { - BROWSER: INTERNAL_API_PREFIX + '/diagnose/browser', - SCREENSHOT: INTERNAL_API_PREFIX + '/diagnose/screenshot', + BROWSER: prefixInternalPath + '/diagnose/browser', + SCREENSHOT: prefixInternalPath + '/diagnose/screenshot', }, JOBS: { - COUNT: INTERNAL_API_PREFIX + '/jobs/count', - LIST: INTERNAL_API_PREFIX + '/jobs/list', - INFO_PREFIX: INTERNAL_API_PREFIX + '/jobs/info', // docId is added to the final path - DELETE_PREFIX: INTERNAL_API_PREFIX + '/jobs/delete', // docId is added to the final path - DOWNLOAD_PREFIX: INTERNAL_API_PREFIX + '/jobs/download', // docId is added to the final path + COUNT: prefixInternalPath + '/jobs/count', + LIST: prefixInternalPath + '/jobs/list', + INFO_PREFIX: prefixInternalPath + '/jobs/info', // docId is added to the final path + DELETE_PREFIX: prefixInternalPath + '/jobs/delete', // docId is added to the final path + DOWNLOAD_PREFIX: prefixInternalPath + '/jobs/download', // docId is added to the final path }, GENERATE: { - CSV_IMMEDIATE: INTERNAL_API_PREFIX + '/generate/immediate/csv_searchsource', - EXPORT_TYPE_PREFIX: INTERNAL_API_PREFIX + '/generate', // exportTypeId is added to the final path + EXPORT_TYPE_PREFIX: prefixInternalPath + '/generate', // exportTypeId is added to the final path + CSV_IMMEDIATE: prefixInternalPath + '/generate/immediate/csv_searchsource', }, }; +const prefixPublicPath = '/api/reporting'; export const PUBLIC_ROUTES = { - GENERATE_PREFIX: `/api/reporting/generate`, // public endpoint for POST URL strings, exportTypeId is added to the final path - DOWNLOAD_PREFIX: `/api/reporting/download`, // public endpoint used by Watcher, jobId is added to the final path + /** + * Public endpoint for POST URL strings and automated report generation + * exportTypeId is added to the final path + */ + GENERATE_PREFIX: prefixPublicPath + `/generate`, + /** + * Public endpoint used by Watcher and automated report downloads + * jobId is added to the final path + */ + DOWNLOAD_PREFIX: prefixPublicPath + `/download`, }; diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index 7df818035da39..1e2e1a15d04dc 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -147,7 +147,7 @@ export class ReportingAPIClient implements IReportingAPI { public async getInfo(jobId: string) { const report: ReportApiJSON = await this.http.get( - `${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/info/${jobId}` + `${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/${jobId}` ); return new Job(report); } diff --git a/x-pack/plugins/reporting/server/routes/management/jobs.ts b/x-pack/plugins/reporting/server/routes/management/jobs.ts index 71f2af5aa46ef..57ba5e01489ef 100644 --- a/x-pack/plugins/reporting/server/routes/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/management/jobs.ts @@ -102,7 +102,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerGetInfo = () => { // return some info about the job - const path = INTERNAL_ROUTES.JOBS.INFO; + const path = INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/{docId}'; router.get( { path, @@ -135,7 +135,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerDownloadReport = () => { // trigger a download of the output from a job - const path = INTERNAL_ROUTES.JOBS.DOWNLOAD; + const path = INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; router.get( { path, @@ -185,7 +185,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerDeleteReport = () => { // allow a report to be deleted - const path = INTERNAL_ROUTES.JOBS.DELETE; + const path = INTERNAL_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; router.delete( { path, From d59f0d8c4815cf2d7147c62a53125c7f956c65e9 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 16:03:34 -0700 Subject: [PATCH 05/47] use the public API endpoint in the POST URL --- .../reporting_api_client.test.ts | 2 +- .../reporting_api_client.ts | 21 ++++++++++--------- .../reporting_panel_content.tsx | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.test.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.test.ts index 3a0086e642f56..d5017104ed09f 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.test.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.test.ts @@ -174,7 +174,7 @@ describe('ReportingAPIClient', () => { describe('getReportingJobPath', () => { it('should generate a job path', () => { expect( - apiClient.getReportingJobPath('pdf', { + apiClient.getReportingPublicJobPath('pdf', { browserTimezone: 'UTC', objectType: 'something', title: 'some title', diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index 1e2e1a15d04dc..52f9d167976e9 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -14,6 +14,7 @@ import { buildKibanaPath } from '../../../common/build_kibana_path'; import { getRedirectAppPath, INTERNAL_ROUTES, + PUBLIC_ROUTES, REPORTING_MANAGEMENT_HOME, } from '../../../common/constants'; import { @@ -42,7 +43,7 @@ export interface DiagnoseResponse { interface IReportingAPI { // Helpers getReportURL(jobId: string): string; - getReportingJobPath(exportType: string, jobParams: BaseParams & T): string; // Return a URL to queue a job, with the job params encoded in the query string of the URL. Used for copying POST URL + getReportingPublicJobPath(exportType: string, jobParams: BaseParams & T): string; // Return a URL to queue a job, with the job params encoded in the query string of the URL. Used for copying POST URL createReportingJob(exportType: string, jobParams: BaseParams & T): Promise; // Sends a request to queue a job, with the job params in the POST body getServerBasePath(): string; // Provides the raw server basePath to allow it to be stripped out from relativeUrls in job params @@ -160,29 +161,29 @@ export class ReportingAPIClient implements IReportingAPI { return reports.map((report) => new Job(report)); } - public getReportingJobPath(exportType: string, jobParams: BaseParams) { + /** + * Returns a string for the public API endpoint used to automate the generation of reports + */ + public getReportingPublicJobPath(exportType: string, jobParams: BaseParams) { const params = stringify({ jobParams: rison.encode(jobParams), }); - return `${this.http.basePath.prepend( - INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX - )}/${exportType}?${params}`; + return `${this.http.basePath.prepend(PUBLIC_ROUTES.GENERATE_PREFIX)}/${exportType}?${params}`; } + /** + * Calls the internal API to generate a report job on-demand + */ public async createReportingJob(exportType: string, jobParams: BaseParams) { const jobParamsRison = rison.encode(jobParams); const resp: { job: ReportApiJSON } = await this.http.post( `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/${exportType}`, { method: 'POST', - body: JSON.stringify({ - jobParams: jobParamsRison, - }), + body: JSON.stringify({ jobParams: jobParamsRison }), } ); - add(resp.job.id); - return new Job(resp.job); } diff --git a/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx index 6ac0e374b3919..2252694fcaa97 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx @@ -84,11 +84,11 @@ class ReportingPanelContentUi extends Component { } private getAbsoluteReportGenerationUrl = (props: Props) => { - const relativePath = this.props.apiClient.getReportingJobPath( + const relativePath = this.props.apiClient.getReportingPublicJobPath( props.reportType, this.props.apiClient.getDecoratedJobParams(this.props.getJobParams(true)) ); - return url.resolve(window.location.href, relativePath); // FIXME: '(from: string, to: string): string' is deprecated + return url.resolve(window.location.href, relativePath); }; public componentDidUpdate(_prevProps: Props, prevState: State) { From d1710b5a44ba6bdb797b8086d9c5b8e457dd35c4 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 16:10:43 -0700 Subject: [PATCH 06/47] fix public download path for post response --- .../reporting/common/constants/routes.ts | 2 +- .../migrate_existing_indices_ilm_policy.ts | 4 ++-- .../server/routes/lib/request_handler.ts | 17 ++++++----------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/reporting/common/constants/routes.ts b/x-pack/plugins/reporting/common/constants/routes.ts index 1eeb520dfec92..5b92b384c415a 100644 --- a/x-pack/plugins/reporting/common/constants/routes.ts +++ b/x-pack/plugins/reporting/common/constants/routes.ts @@ -39,5 +39,5 @@ export const PUBLIC_ROUTES = { * Public endpoint used by Watcher and automated report downloads * jobId is added to the final path */ - DOWNLOAD_PREFIX: prefixPublicPath + `/download`, + DOWNLOAD_PREFIX: prefixPublicPath + `/jobs/download`, }; diff --git a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts index 7ce888b57727f..6f0ddba9ce296 100644 --- a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts +++ b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { DeprecationsDetails, GetDeprecationsContext } from '@kbn/core/server'; -import { API_MIGRATE_ILM_POLICY_URL, ILM_POLICY_NAME } from '../../common/constants'; +import { INTERNAL_ROUTES, ILM_POLICY_NAME } from '../../common/constants'; import { ReportingCore } from '../core'; import { deprecations } from '../lib/deprecations'; @@ -54,7 +54,7 @@ export const getDeprecationsInfo = async ( ], api: { method: 'PUT', - path: API_MIGRATE_ILM_POLICY_URL, + path: INTERNAL_ROUTES.MIGRATE.MIGRATE_ILM_POLICY, }, }, }, diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts b/x-pack/plugins/reporting/server/routes/lib/request_handler.ts index a1c7ec866f0d5..08eec9a71180b 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts +++ b/x-pack/plugins/reporting/server/routes/lib/request_handler.ts @@ -5,12 +5,12 @@ * 2.0. */ -import moment from 'moment'; import Boom from '@hapi/boom'; -import { i18n } from '@kbn/i18n'; import type { KibanaRequest, KibanaResponseFactory, Logger } from '@kbn/core/server'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; import type { ReportingCore } from '../..'; -import { INTERNAL_ROUTES } from '../../../common/constants'; +import { PUBLIC_ROUTES } from '../../../common/constants'; import { checkParamsVersion, cryptoFactory } from '../../lib'; import { Report } from '../../lib/store'; import type { BaseParams, ReportingRequestHandlerContext, ReportingUser } from '../../types'; @@ -20,11 +20,6 @@ export const handleUnavailable = (res: KibanaResponseFactory) => { return res.custom({ statusCode: 503, body: 'Not Available' }); }; -const getDownloadBaseUrl = (reporting: ReportingCore) => { - const { basePath } = reporting.getServerInfo(); - return basePath + INTERNAL_ROUTES.JOBS.DOWNLOAD; -}; - /** * Handles the common parts of requests to generate a report */ @@ -140,16 +135,16 @@ export class RequestHandler { let report: Report | undefined; try { report = await this.enqueueJob(exportTypeId, jobParams); + const { basePath } = this.reporting.getServerInfo(); + const publicDownloadPath = basePath + PUBLIC_ROUTES.DOWNLOAD_PREFIX; // return task manager's task information and the download URL - const downloadBaseUrl = getDownloadBaseUrl(this.reporting); - counters.usageCounter(); return this.res.ok({ headers: { 'content-type': 'application/json' }, body: { - path: `${downloadBaseUrl}/${report._id}`, + path: `${publicDownloadPath}/${report._id}`, job: report.toApiJSON(), }, }); From 01c6b5d83b7d9d24c31f9dda322159997cd2cce6 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 16:23:43 -0700 Subject: [PATCH 07/47] fix tests import --- .../reporting_and_security/ilm_migration_apis.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/ilm_migration_apis.ts b/x-pack/test/reporting_api_integration/reporting_and_security/ilm_migration_apis.ts index db131130f40ea..5a39c2fc86979 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/ilm_migration_apis.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/ilm_migration_apis.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { ILM_POLICY_NAME } from '@kbn/reporting-plugin/common/constants'; +import { ILM_POLICY_NAME, INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -194,13 +194,13 @@ export default function ({ getService }: FtrProviderContext) { try { await supertestWithoutAuth - .put(reportingAPI.routes.API_MIGRATE_ILM_POLICY_URL) + .put(INTERNAL_ROUTES.MIGRATE.MIGRATE_ILM_POLICY) .auth(UNAUTHZD_TEST_USERNAME, UNAUTHZD_TEST_USER_PASSWORD) .set('kbn-xsrf', 'xxx') .expect(404); await supertestWithoutAuth - .get(reportingAPI.routes.API_GET_ILM_POLICY_STATUS) + .get(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS) .auth(UNAUTHZD_TEST_USERNAME, UNAUTHZD_TEST_USER_PASSWORD) .set('kbn-xsrf', 'xxx') .expect(404); From 8ef4e042101ca83c8c48166087a31b8b75853506 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 16:27:48 -0700 Subject: [PATCH 08/47] update comment --- .../public/lib/reporting_api_client/reporting_api_client.ts | 2 +- x-pack/plugins/reporting/server/routes/diagnostic/browser.ts | 1 - .../reporting/server/routes/generate/generate_from_jobparams.ts | 1 - x-pack/plugins/reporting/server/routes/management/jobs.ts | 2 -- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index 52f9d167976e9..15656ac710788 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -228,6 +228,6 @@ export class ReportingAPIClient implements IReportingAPI { } public migrateReportingIndicesIlmPolicy() { - return this.http.put(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS); // FIXME This is wrong + return this.http.put(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS); } } diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts index 0a9b8e3a6a6c8..1be5f54106ba3 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts @@ -37,7 +37,6 @@ const logsToHelpMapFactory = (docLinks: DocLinksServiceSetup) => ({ }); const path = INTERNAL_ROUTES.DIAGNOSE.BROWSER; - export const registerDiagnoseBrowser = (reporting: ReportingCore, logger: Logger) => { const { router } = reporting.getPluginSetupDeps(); diff --git a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts index 4f5481e4be00b..94231cedac582 100644 --- a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts @@ -26,7 +26,6 @@ export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Lo const registerPostGenerationEndpoint = () => { const path = `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{exportType}`; - router.post( { path, diff --git a/x-pack/plugins/reporting/server/routes/management/jobs.ts b/x-pack/plugins/reporting/server/routes/management/jobs.ts index 57ba5e01489ef..7a44c1cea52ba 100644 --- a/x-pack/plugins/reporting/server/routes/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/management/jobs.ts @@ -27,7 +27,6 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerGetList = () => { // list jobs in the queue, paginated const path = INTERNAL_ROUTES.JOBS.LIST; - router.get( { path, @@ -71,7 +70,6 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerGetCount = () => { // return the count of all jobs in the queue const path = INTERNAL_ROUTES.JOBS.COUNT; - router.get( { path, validate: false }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { From a6950af48759f6e855a3b6c445dc347a571c0b18 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 16:49:50 -0700 Subject: [PATCH 09/47] Use more correct endpoint paths in tests --- .../plugins/reporting/common/constants/routes.ts | 2 +- .../diagnostic/integration_tests/browser.test.ts | 7 ++++--- .../integration_tests/screenshot.test.ts | 14 +++++++++----- .../generation_from_jobparams.test.ts | 7 ++++--- .../download_csv_dashboard.ts | 2 +- .../services/scenarios.ts | 2 +- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/reporting/common/constants/routes.ts b/x-pack/plugins/reporting/common/constants/routes.ts index 5b92b384c415a..2a9d210837a96 100644 --- a/x-pack/plugins/reporting/common/constants/routes.ts +++ b/x-pack/plugins/reporting/common/constants/routes.ts @@ -5,7 +5,7 @@ * 2.0. */ -const prefixInternalPath = '/_internal/reporting'; +const prefixInternalPath = '/internal/reporting'; export const INTERNAL_ROUTES = { MIGRATE: { MIGRATE_ILM_POLICY: prefixInternalPath + '/deprecations/migrate_ilm_policy', diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts index 262353071f677..d6cd67a159ddd 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts @@ -18,6 +18,7 @@ import { } from '../../../test_helpers'; import type { ReportingRequestHandlerContext } from '../../../types'; import { registerDiagnoseBrowser } from '../browser'; +import { INTERNAL_ROUTES } from '../../../../common/constants'; type SetupServerReturn = Awaited>; @@ -81,7 +82,7 @@ describe('POST /diagnose/browser', () => { screenshotting.diagnose.mockReturnValue(Rx.of(devtoolMessage)); return supertest(httpSetup.server.listener) - .post('/api/reporting/diagnose/browser') + .post(INTERNAL_ROUTES.DIAGNOSE.BROWSER) .expect(200) .then(({ body }) => { expect(body.success).toEqual(true); @@ -97,7 +98,7 @@ describe('POST /diagnose/browser', () => { screenshotting.diagnose.mockReturnValue(Rx.of(logs)); return supertest(httpSetup.server.listener) - .post('/api/reporting/diagnose/browser') + .post(INTERNAL_ROUTES.DIAGNOSE.BROWSER) .expect(200) .then(({ body }) => { expect(body).toMatchInlineSnapshot(` @@ -119,7 +120,7 @@ describe('POST /diagnose/browser', () => { screenshotting.diagnose.mockReturnValue(Rx.of(`${devtoolMessage}\n${fontNotFoundMessage}`)); return supertest(httpSetup.server.listener) - .post('/api/reporting/diagnose/browser') + .post(INTERNAL_ROUTES.DIAGNOSE.BROWSER) .expect(200) .then(({ body }) => { expect(body).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts index b4a6e638a4dee..d3e0261b5e40e 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts @@ -5,10 +5,12 @@ * 2.0. */ -import { loggingSystemMock } from '@kbn/core/server/mocks'; import { setupServer } from '@kbn/core-test-helpers-test-utils'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { defer } from 'rxjs'; import supertest from 'supertest'; import { ReportingCore } from '../../..'; +import { INTERNAL_ROUTES } from '../../../../common/constants'; import { generatePngObservable } from '../../../export_types/common'; import { createMockConfigSchema, @@ -17,12 +19,14 @@ import { } from '../../../test_helpers'; import type { ReportingRequestHandlerContext } from '../../../types'; import { registerDiagnoseScreenshot } from '../screenshot'; -import { defer } from 'rxjs'; jest.mock('../../../export_types/common/generate_png'); type SetupServerReturn = Awaited>; +/** + * Tests internal diagnostic API endpoints + */ describe('POST /diagnose/screenshot', () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; @@ -67,7 +71,7 @@ describe('POST /diagnose/screenshot', () => { await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/diagnose/screenshot') + .post(INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT) .expect(200) .then(({ body }) => { expect(body).toMatchInlineSnapshot(` @@ -86,7 +90,7 @@ describe('POST /diagnose/screenshot', () => { await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/diagnose/screenshot') + .post(INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT) .expect(200) .then(({ body }) => { expect(body).toMatchInlineSnapshot(` @@ -107,7 +111,7 @@ describe('POST /diagnose/screenshot', () => { await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/diagnose/screenshot') + .post(INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT) .expect(200) .then(({ body }) => { expect(body.help).toContain(`We couldn't screenshot your Kibana install.`); diff --git a/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts index 627e2210dac37..bbfed34d15efc 100644 --- a/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts @@ -26,6 +26,9 @@ import { registerJobGenerationRoutes } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; +/** + * Tests the public API endpoints of Reporting + */ describe('POST /api/reporting/generate', () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; @@ -49,9 +52,7 @@ describe('POST /api/reporting/generate', () => { ); const mockSetupDeps = createMockPluginSetup({ - security: { - license: { isEnabled: () => true }, - }, + security: { license: { isEnabled: () => true } }, router: httpSetup.createRouter(''), }); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/download_csv_dashboard.ts b/x-pack/test/reporting_api_integration/reporting_and_security/download_csv_dashboard.ts index 3941037733c70..f71efb61aeb2c 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/download_csv_dashboard.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/download_csv_dashboard.ts @@ -27,7 +27,7 @@ export default function ({ getService }: FtrProviderContext) { const generateAPI = { getCSVFromSearchSource: async (job: JobParamsDownloadCSV) => { return await supertestSvc - .post(`/api/reporting/v1/generate/immediate/csv_searchsource`) + .post(`/internal/reporting/generate/immediate/csv_searchsource`) .set('kbn-xsrf', 'xxx') .send(job); }, diff --git a/x-pack/test/reporting_api_integration/services/scenarios.ts b/x-pack/test/reporting_api_integration/services/scenarios.ts index 54e03b1facb59..ee5d0ab2b72c3 100644 --- a/x-pack/test/reporting_api_integration/services/scenarios.ts +++ b/x-pack/test/reporting_api_integration/services/scenarios.ts @@ -134,7 +134,7 @@ export function createScenarios({ getService }: Pick { return await supertestWithoutAuth - .post(`/api/reporting/v1/generate/immediate/csv_searchsource`) + .post(`/internal/reporting/generate/immediate/csv_searchsource`) .auth(username, password) .set('kbn-xsrf', 'xxx') .send(job); From 9379f44c2d4e6c4d93bf2616461b1f3553c838e8 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 16:54:28 -0700 Subject: [PATCH 10/47] Delete endpoint should be public --- x-pack/plugins/reporting/common/constants/routes.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugins/reporting/common/constants/routes.ts b/x-pack/plugins/reporting/common/constants/routes.ts index 2a9d210837a96..5ed9cb50d2cf9 100644 --- a/x-pack/plugins/reporting/common/constants/routes.ts +++ b/x-pack/plugins/reporting/common/constants/routes.ts @@ -40,4 +40,9 @@ export const PUBLIC_ROUTES = { * jobId is added to the final path */ DOWNLOAD_PREFIX: prefixPublicPath + `/jobs/download`, + /** + * Public endpoint potentially used to delete a report after download in automation + * jobId is added to the final path + */ + DELETE_PREFIX: prefixPublicPath + `/jobs/delete`, }; From 059484587e1a50e3f186e1adec54cd1b2e5f4655 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 17:06:37 -0700 Subject: [PATCH 11/47] reorganize part i --- .../reporting/server/routes/generate/index.ts | 9 - .../plugins/reporting/server/routes/index.ts | 22 +- .../deprecations/deprecations.ts | 14 +- .../integration_tests/deprecations.test.ts | 0 .../{ => internal}/diagnostic/browser.ts | 0 .../routes/{ => internal}/diagnostic/index.ts | 6 +- .../integration_tests/browser.test.ts | 0 .../integration_tests/screenshot.test.ts | 0 .../{ => internal}/diagnostic/screenshot.ts | 0 .../generate/csv_searchsource_immediate.ts | 12 +- .../generate/generate_from_jobparams.ts | 109 +++++++++ .../generation_from_jobparams.test.ts | 0 .../routes/{ => internal}/management/index.ts | 0 .../management/integration_tests/jobs.test.ts | 0 .../server/routes/internal/management/jobs.ts | 227 ++++++++++++++++++ .../generate_from_jobparams.ts | 0 .../routes/{management => public}/jobs.ts | 0 17 files changed, 362 insertions(+), 37 deletions(-) delete mode 100644 x-pack/plugins/reporting/server/routes/generate/index.ts rename x-pack/plugins/reporting/server/routes/{ => internal}/deprecations/deprecations.ts (91%) rename x-pack/plugins/reporting/server/routes/{ => internal}/deprecations/integration_tests/deprecations.test.ts (100%) rename x-pack/plugins/reporting/server/routes/{ => internal}/diagnostic/browser.ts (100%) rename x-pack/plugins/reporting/server/routes/{ => internal}/diagnostic/index.ts (73%) rename x-pack/plugins/reporting/server/routes/{ => internal}/diagnostic/integration_tests/browser.test.ts (100%) rename x-pack/plugins/reporting/server/routes/{ => internal}/diagnostic/integration_tests/screenshot.test.ts (100%) rename x-pack/plugins/reporting/server/routes/{ => internal}/diagnostic/screenshot.ts (100%) rename x-pack/plugins/reporting/server/routes/{ => internal}/generate/csv_searchsource_immediate.ts (91%) create mode 100644 x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts rename x-pack/plugins/reporting/server/routes/{ => internal}/generate/integration_tests/generation_from_jobparams.test.ts (100%) rename x-pack/plugins/reporting/server/routes/{ => internal}/management/index.ts (100%) rename x-pack/plugins/reporting/server/routes/{ => internal}/management/integration_tests/jobs.test.ts (100%) create mode 100644 x-pack/plugins/reporting/server/routes/internal/management/jobs.ts rename x-pack/plugins/reporting/server/routes/{generate => public}/generate_from_jobparams.ts (100%) rename x-pack/plugins/reporting/server/routes/{management => public}/jobs.ts (100%) diff --git a/x-pack/plugins/reporting/server/routes/generate/index.ts b/x-pack/plugins/reporting/server/routes/generate/index.ts deleted file mode 100644 index 210abee3e6606..0000000000000 --- a/x-pack/plugins/reporting/server/routes/generate/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { registerGenerateCsvFromSavedObjectImmediate } from './csv_searchsource_immediate'; // FIXME: should not need to register each immediate export type separately -export { registerJobGenerationRoutes } from './generate_from_jobparams'; diff --git a/x-pack/plugins/reporting/server/routes/index.ts b/x-pack/plugins/reporting/server/routes/index.ts index 570d74f3e02ab..864e9024e5b5b 100644 --- a/x-pack/plugins/reporting/server/routes/index.ts +++ b/x-pack/plugins/reporting/server/routes/index.ts @@ -7,18 +7,16 @@ import type { Logger } from '@kbn/core/server'; import { ReportingCore } from '..'; -import { registerDeprecationsRoutes } from './deprecations/deprecations'; -import { registerDiagnosticRoutes } from './diagnostic'; -import { - registerGenerateCsvFromSavedObjectImmediate, - registerJobGenerationRoutes, -} from './generate'; -import { registerJobInfoRoutes } from './management'; +import { registerDeprecations as internalDeprecations } from './internal/deprecations/deprecations'; +import { registerDiagnostics as internalDiagnostics } from './internal/diagnostic'; +import { registerGenerateCsvFromSavedObjectImmediate as internalCsvFromSavedObjectImmediate } from './internal/generate/csv_searchsource_immediate'; +import { registerJobGeneration as internalJobGenerationRoutes } from './internal/generate/generate_from_jobparams'; +import { registerJobInfo as internalJobInfoRoutes } from './internal/management'; export function registerRoutes(reporting: ReportingCore, logger: Logger) { - registerDeprecationsRoutes(reporting, logger); - registerDiagnosticRoutes(reporting, logger); - registerGenerateCsvFromSavedObjectImmediate(reporting, logger); - registerJobGenerationRoutes(reporting, logger); - registerJobInfoRoutes(reporting); + internalDeprecations(reporting, logger); + internalDiagnostics(reporting, logger); + internalCsvFromSavedObjectImmediate(reporting, logger); + internalJobGenerationRoutes(reporting, logger); + internalJobInfoRoutes(reporting); } diff --git a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts similarity index 91% rename from x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts rename to x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts index 6786520ece0d6..6c3a9c604e719 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts @@ -6,12 +6,12 @@ */ import { errors } from '@elastic/elasticsearch'; import type { Logger, RequestHandler } from '@kbn/core/server'; -import { INTERNAL_ROUTES, ILM_POLICY_NAME } from '../../../common/constants'; -import type { IlmPolicyStatusResponse } from '../../../common/types'; -import type { ReportingCore } from '../../core'; -import { IlmPolicyManager } from '../../lib'; -import { deprecations } from '../../lib/deprecations'; -import { getCounters } from '../lib'; +import { INTERNAL_ROUTES, ILM_POLICY_NAME } from '../../../../common/constants'; +import type { IlmPolicyStatusResponse } from '../../../../common/types'; +import type { ReportingCore } from '../../../core'; +import { IlmPolicyManager } from '../../../lib'; +import { deprecations } from '../../../lib/deprecations'; +import { getCounters } from '../../lib'; const getAuthzWrapper = (reporting: ReportingCore, logger: Logger) => @@ -51,7 +51,7 @@ const getAuthzWrapper = }; }; -export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Logger) => { +export const registerDeprecations = (reporting: ReportingCore, logger: Logger) => { const { router } = reporting.getPluginSetupDeps(); const authzWrapper = getAuthzWrapper(reporting, logger); diff --git a/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts rename to x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/diagnostic/browser.ts rename to x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/index.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/index.ts similarity index 73% rename from x-pack/plugins/reporting/server/routes/diagnostic/index.ts rename to x-pack/plugins/reporting/server/routes/internal/diagnostic/index.ts index 48a48efab4806..4d385121f9b56 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/index.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/index.ts @@ -6,13 +6,13 @@ */ import type { Logger } from '@kbn/core/server'; -import type { ReportingCore } from '../../core'; +import type { ReportingCore } from '../../../core'; import { registerDiagnoseBrowser } from './browser'; import { registerDiagnoseScreenshot } from './screenshot'; -export const registerDiagnosticRoutes = (reporting: ReportingCore, logger: Logger) => { +export const registerDiagnostics = (reporting: ReportingCore, logger: Logger) => { registerDiagnoseBrowser(reporting, logger); - registerDiagnoseScreenshot(reporting, logger); // TODO remove + registerDiagnoseScreenshot(reporting, logger); }; export interface DiagnosticResponse { diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts rename to x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/screenshot.test.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts rename to x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/screenshot.test.ts diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts rename to x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts diff --git a/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts similarity index 91% rename from x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts rename to x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts index f5ceac67563c1..53ee9c932452b 100644 --- a/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts @@ -9,12 +9,12 @@ import Boom from '@hapi/boom'; import { schema } from '@kbn/config-schema'; import type { KibanaRequest, Logger } from '@kbn/core/server'; import moment from 'moment'; -import type { ReportingCore } from '../..'; -import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE, INTERNAL_ROUTES } from '../../../common/constants'; -import { runTaskFnFactory } from '../../export_types/csv_searchsource_immediate/execute_job'; -import type { JobParamsDownloadCSV } from '../../export_types/csv_searchsource_immediate/types'; -import { PassThroughStream } from '../../lib'; -import { authorizedUserPreRouting, getCounters } from '../lib'; +import type { ReportingCore } from '../../..'; +import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE, INTERNAL_ROUTES } from '../../../../common/constants'; +import { runTaskFnFactory } from '../../../export_types/csv_searchsource_immediate/execute_job'; +import type { JobParamsDownloadCSV } from '../../../export_types/csv_searchsource_immediate/types'; +import { PassThroughStream } from '../../../lib'; +import { authorizedUserPreRouting, getCounters } from '../../lib'; const path = INTERNAL_ROUTES.GENERATE.CSV_IMMEDIATE; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts new file mode 100644 index 0000000000000..0ef505d82a8b7 --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import type { Logger } from '@kbn/core/server'; +import rison from '@kbn/rison'; +import type { ReportingCore } from '../../..'; +import { INTERNAL_ROUTES } from '../../../../common/constants'; +import type { BaseParams } from '../../../types'; +import { authorizedUserPreRouting, getCounters, RequestHandler } from '../../lib'; + +export function registerJobGeneration(reporting: ReportingCore, logger: Logger) { + const setupDeps = reporting.getPluginSetupDeps(); + const { router } = setupDeps; + + // TODO: find a way to abstract this using ExportTypeRegistry: it needs a new + // public method to return this array + // const registry = reporting.getExportTypesRegistry(); + // const kibanaAccessControlTags = registry.getAllAccessControlTags(); + const useKibanaAccessControl = reporting.getDeprecatedAllowedRoles() === false; // true if Reporting's deprecated access control feature is disabled + const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; + + const registerPostGenerationEndpoint = () => { + const path = `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{exportType}`; + router.post( + { + path, + validate: { + params: schema.object({ exportType: schema.string({ minLength: 2 }) }), + body: schema.nullable(schema.object({ jobParams: schema.maybe(schema.string()) })), + query: schema.nullable(schema.object({ jobParams: schema.string({ defaultValue: '' }) })), + }, + options: { tags: kibanaAccessControlTags }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const counters = getCounters( + req.route.method, + path.replace(/{exportType}/, req.params.exportType), + reporting.getUsageCounter() + ); + + let jobParamsRison: null | string = null; + + if (req.body) { + const { jobParams: jobParamsPayload } = req.body; + jobParamsRison = jobParamsPayload ? jobParamsPayload : null; + } else if (req.query?.jobParams) { + const { jobParams: queryJobParams } = req.query; + if (queryJobParams) { + jobParamsRison = queryJobParams; + } else { + jobParamsRison = null; + } + } + + if (!jobParamsRison) { + return res.customError({ + statusCode: 400, + body: 'A jobParams RISON string is required in the querystring or POST body', + }); + } + + let jobParams; + + try { + jobParams = rison.decode(jobParamsRison) as BaseParams | null; + if (!jobParams) { + return res.customError({ + statusCode: 400, + body: 'Missing jobParams!', + }); + } + } catch (err) { + return res.customError({ + statusCode: 400, + body: `invalid rison: ${jobParamsRison}`, + }); + } + + const requestHandler = new RequestHandler(reporting, user, context, req, res, logger); + return await requestHandler.handleGenerateRequest( + req.params.exportType, + jobParams, + counters + ); + }) + ); + }; + + const registerGetGenerationEndpoint = () => { + // Get route to generation endpoint: show error about GET method to user + router.get( + { + path: `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{p*}`, + validate: false, + }, + (_context, _req, res) => { + return res.customError({ statusCode: 405, body: 'GET is not allowed' }); + } + ); + }; + + registerPostGenerationEndpoint(); + registerGetGenerationEndpoint(); +} diff --git a/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts rename to x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts diff --git a/x-pack/plugins/reporting/server/routes/management/index.ts b/x-pack/plugins/reporting/server/routes/internal/management/index.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/management/index.ts rename to x-pack/plugins/reporting/server/routes/internal/management/index.ts diff --git a/x-pack/plugins/reporting/server/routes/management/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/management/integration_tests/jobs.test.ts rename to x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts diff --git a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts new file mode 100644 index 0000000000000..8f02f3e369eff --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts @@ -0,0 +1,227 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; +import { promisify } from 'util'; +import { ReportingCore } from '../../..'; +import { ALLOWED_JOB_CONTENT_TYPES, INTERNAL_ROUTES } from '../../../../common/constants'; +import { getContentStream } from '../../../lib'; +import { + authorizedUserPreRouting, + getCounters, + handleUnavailable, + jobManagementPreRouting, + jobsQueryFactory, +} from '../../lib'; + +export function registerJobInfoRoutes(reporting: ReportingCore) { + const setupDeps = reporting.getPluginSetupDeps(); + const { router } = setupDeps; + const jobsQuery = jobsQueryFactory(reporting); + + const registerGetList = () => { + // list jobs in the queue, paginated + const path = INTERNAL_ROUTES.JOBS.LIST; + router.get( + { + path, + validate: { + query: schema.object({ + page: schema.string({ defaultValue: '0' }), + size: schema.string({ defaultValue: '10' }), + ids: schema.maybe(schema.string()), + }), + }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return handleUnavailable(res); + } + + const { + management: { jobTypes = [] }, + } = await reporting.getLicenseInfo(); + const { page: queryPage = '0', size: querySize = '10', ids: queryIds = null } = req.query; + const page = parseInt(queryPage, 10) || 0; + const size = Math.min(100, parseInt(querySize, 10) || 10); + const jobIds = queryIds ? queryIds.split(',') : null; + const results = await jobsQuery.list(jobTypes, user, page, size, jobIds); + + counters.usageCounter(); + + return res.ok({ + body: results, + headers: { + 'content-type': 'application/json', + }, + }); + }) + ); + }; + + const registerGetCount = () => { + // return the count of all jobs in the queue + const path = INTERNAL_ROUTES.JOBS.COUNT; + router.get( + { path, validate: false }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return handleUnavailable(res); + } + + const { + management: { jobTypes = [] }, + } = await reporting.getLicenseInfo(); + + const count = await jobsQuery.count(jobTypes, user); + + counters.usageCounter(); + + return res.ok({ + body: count.toString(), + headers: { + 'content-type': 'text/plain', + }, + }); + }) + ); + }; + + const registerGetInfo = () => { + // return some info about the job + const path = INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/{docId}'; + router.get( + { + path, + validate: { + params: schema.object({ + docId: schema.string({ minLength: 2 }), + }), + }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return res.custom({ statusCode: 503 }); + } + + const { docId } = req.params; + return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => + res.ok({ + body: doc, + headers: { + 'content-type': 'application/json', + }, + }) + ); + }) + ); + }; + + const registerDownloadReport = () => { + // trigger a download of the output from a job + const path = INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; + router.get( + { + path, + validate: { + params: schema.object({ + docId: schema.string({ minLength: 3 }), + }), + }, + options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return handleUnavailable(res); + } + + const { docId } = req.params; + + return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { + const payload = await jobsQuery.getDocumentPayload(doc); + const { contentType, content, filename, statusCode } = payload; + + if (!contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(contentType)) { + return res.badRequest({ + body: `Unsupported content-type of ${contentType} specified by job output`, + }); + } + + const body = typeof content === 'string' ? Buffer.from(content) : content; + + const headers = { + ...payload.headers, + 'content-type': contentType, + }; + + if (filename) { + return res.file({ body, headers, filename }); + } + + return res.custom({ body, headers, statusCode }); + }); + }) + ); + }; + + const registerDeleteReport = () => { + // allow a report to be deleted + const path = INTERNAL_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; + router.delete( + { + path, + validate: { + params: schema.object({ + docId: schema.string({ minLength: 3 }), + }), + }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return handleUnavailable(res); + } + + const { docId } = req.params; + + return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { + const docIndex = doc.index; + const stream = await getContentStream(reporting, { id: docId, index: docIndex }); + + /** @note Overwriting existing content with an empty buffer to remove all the chunks. */ + await promisify(stream.end.bind(stream, '', 'utf8'))(); + await jobsQuery.delete(docIndex, docId); + + return res.ok({ + body: { deleted: true }, + }); + }); + }) + ); + }; + + registerGetList(); + registerGetCount(); + registerGetInfo(); + registerDownloadReport(); + registerDeleteReport(); +} diff --git a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts rename to x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts diff --git a/x-pack/plugins/reporting/server/routes/management/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/management/jobs.ts rename to x-pack/plugins/reporting/server/routes/public/jobs.ts From 264b6443255c3185026290c2abb0e7ea1dfe2859 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 17:17:23 -0700 Subject: [PATCH 12/47] register public routes --- .../plugins/reporting/server/routes/index.ts | 16 ++- .../internal/deprecations/deprecations.ts | 2 +- .../integration_tests/deprecations.test.ts | 6 +- .../routes/internal/diagnostic/index.ts | 2 +- .../generate/generate_from_jobparams.ts | 14 --- .../generation_from_jobparams.test.ts | 16 +-- .../routes/internal/management/index.ts | 2 +- .../server/routes/internal/management/jobs.ts | 2 +- .../routes/public/generate_from_jobparams.ts | 6 +- .../reporting/server/routes/public/jobs.ts | 118 +----------------- 10 files changed, 32 insertions(+), 152 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/index.ts b/x-pack/plugins/reporting/server/routes/index.ts index 864e9024e5b5b..033a13d8a1caa 100644 --- a/x-pack/plugins/reporting/server/routes/index.ts +++ b/x-pack/plugins/reporting/server/routes/index.ts @@ -7,16 +7,20 @@ import type { Logger } from '@kbn/core/server'; import { ReportingCore } from '..'; -import { registerDeprecations as internalDeprecations } from './internal/deprecations/deprecations'; -import { registerDiagnostics as internalDiagnostics } from './internal/diagnostic'; +import { registerDeprecationRoutes as internalDeprecationRoutes } from './internal/deprecations/deprecations'; +import { registerDiagnosticRoutes as internalDiagnosticsRoutes } from './internal/diagnostic'; import { registerGenerateCsvFromSavedObjectImmediate as internalCsvFromSavedObjectImmediate } from './internal/generate/csv_searchsource_immediate'; import { registerJobGeneration as internalJobGenerationRoutes } from './internal/generate/generate_from_jobparams'; -import { registerJobInfo as internalJobInfoRoutes } from './internal/management'; +import { registerJobRoutes as internalJobRoutes } from './internal/management'; +import { registerJobGenerationRoutes as publicJobGenerationRoutes } from './public/generate_from_jobparams'; +import { registerJobRoutes as publicJobRoutes } from './public/jobs'; export function registerRoutes(reporting: ReportingCore, logger: Logger) { - internalDeprecations(reporting, logger); - internalDiagnostics(reporting, logger); + internalDeprecationRoutes(reporting, logger); + internalDiagnosticsRoutes(reporting, logger); internalCsvFromSavedObjectImmediate(reporting, logger); internalJobGenerationRoutes(reporting, logger); - internalJobInfoRoutes(reporting); + internalJobRoutes(reporting); + publicJobGenerationRoutes(reporting, logger); + publicJobRoutes(reporting); } diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts index 6c3a9c604e719..b3b4e66dcb560 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts @@ -51,7 +51,7 @@ const getAuthzWrapper = }; }; -export const registerDeprecations = (reporting: ReportingCore, logger: Logger) => { +export const registerDeprecationRoutes = (reporting: ReportingCore, logger: Logger) => { const { router } = reporting.getPluginSetupDeps(); const authzWrapper = getAuthzWrapper(reporting, logger); diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts index 27882a26149bf..11ceb1f3e4aa3 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts @@ -17,7 +17,7 @@ import { createMockPluginStart, createMockReportingCore, } from '../../../test_helpers'; -import { registerDeprecationsRoutes } from '../deprecations'; +import { registerDeprecations } from '../deprecations'; type SetupServerReturn = Awaited>; @@ -53,7 +53,7 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { it('correctly handles authz when security is unavailable', async () => { const core = await createReportingCore({}); - registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); + registerDeprecations(core, loggingSystemMock.createLogger()); await server.start(); await supertest(httpSetup.server.listener) @@ -67,7 +67,7 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { security.license.isEnabled.mockReturnValue(false); const core = await createReportingCore({ security }); - registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); + registerDeprecations(core, loggingSystemMock.createLogger()); await server.start(); await supertest(httpSetup.server.listener) diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/index.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/index.ts index 4d385121f9b56..d6bf4781d8fea 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/index.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/index.ts @@ -10,7 +10,7 @@ import type { ReportingCore } from '../../../core'; import { registerDiagnoseBrowser } from './browser'; import { registerDiagnoseScreenshot } from './screenshot'; -export const registerDiagnostics = (reporting: ReportingCore, logger: Logger) => { +export const registerDiagnosticRoutes = (reporting: ReportingCore, logger: Logger) => { registerDiagnoseBrowser(reporting, logger); registerDiagnoseScreenshot(reporting, logger); }; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index 0ef505d82a8b7..6f5d6abc5b1af 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -91,19 +91,5 @@ export function registerJobGeneration(reporting: ReportingCore, logger: Logger) ); }; - const registerGetGenerationEndpoint = () => { - // Get route to generation endpoint: show error about GET method to user - router.get( - { - path: `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{p*}`, - validate: false, - }, - (_context, _req, res) => { - return res.customError({ statusCode: 405, body: 'GET is not allowed' }); - } - ); - }; - registerPostGenerationEndpoint(); - registerGetGenerationEndpoint(); } diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts index bbfed34d15efc..70102866b7235 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts @@ -22,7 +22,7 @@ import { createMockReportingCore, } from '../../../test_helpers'; import type { ReportingRequestHandlerContext } from '../../../types'; -import { registerJobGenerationRoutes } from '../generate_from_jobparams'; +import { registerJobGeneration } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; @@ -105,7 +105,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if there are no job params', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerJobGeneration(mockReportingCore, mockLogger); await server.start(); @@ -120,7 +120,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params query is invalid', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerJobGeneration(mockReportingCore, mockLogger); await server.start(); @@ -131,7 +131,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params body is invalid', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerJobGeneration(mockReportingCore, mockLogger); await server.start(); @@ -143,7 +143,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 export type is invalid', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerJobGeneration(mockReportingCore, mockLogger); await server.start(); @@ -157,7 +157,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 on invalid browser timezone', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerJobGeneration(mockReportingCore, mockLogger); await server.start(); @@ -173,7 +173,7 @@ describe('POST /api/reporting/generate', () => { it('returns 500 if job handler throws an error', async () => { store.addReport = jest.fn().mockRejectedValue('silly'); - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerJobGeneration(mockReportingCore, mockLogger); await server.start(); @@ -184,7 +184,7 @@ describe('POST /api/reporting/generate', () => { }); it(`returns 200 if job handler doesn't error`, async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerJobGeneration(mockReportingCore, mockLogger); await server.start(); diff --git a/x-pack/plugins/reporting/server/routes/internal/management/index.ts b/x-pack/plugins/reporting/server/routes/internal/management/index.ts index 0c31b2b0d6a0c..f3f8ef5f3be02 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/index.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { registerJobInfoRoutes } from './jobs'; +export { registerJobRoutes } from './jobs'; diff --git a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts index 8f02f3e369eff..435ef5b3637cf 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts @@ -19,7 +19,7 @@ import { jobsQueryFactory, } from '../../lib'; -export function registerJobInfoRoutes(reporting: ReportingCore) { +export function registerJobRoutes(reporting: ReportingCore) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; const jobsQuery = jobsQueryFactory(reporting); diff --git a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts index 94231cedac582..7bbd66dbd6769 100644 --- a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import type { Logger } from '@kbn/core/server'; import rison from '@kbn/rison'; import type { ReportingCore } from '../..'; -import { INTERNAL_ROUTES } from '../../../common/constants'; +import { PUBLIC_ROUTES } from '../../../common/constants'; import type { BaseParams } from '../../types'; import { authorizedUserPreRouting, getCounters, RequestHandler } from '../lib'; @@ -25,7 +25,7 @@ export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Lo const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; const registerPostGenerationEndpoint = () => { - const path = `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{exportType}`; + const path = `${PUBLIC_ROUTES.GENERATE_PREFIX}/{exportType}`; router.post( { path, @@ -95,7 +95,7 @@ export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Lo // Get route to generation endpoint: show error about GET method to user router.get( { - path: `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{p*}`, + path: `${PUBLIC_ROUTES.GENERATE_PREFIX}/{p*}`, validate: false, }, (_context, _req, res) => { diff --git a/x-pack/plugins/reporting/server/routes/public/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts index 7a44c1cea52ba..23f0674531346 100644 --- a/x-pack/plugins/reporting/server/routes/public/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/public/jobs.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; import { promisify } from 'util'; import { ReportingCore } from '../..'; -import { ALLOWED_JOB_CONTENT_TYPES, INTERNAL_ROUTES } from '../../../common/constants'; +import { ALLOWED_JOB_CONTENT_TYPES, PUBLIC_ROUTES } from '../../../common/constants'; import { getContentStream } from '../../lib'; import { authorizedUserPreRouting, @@ -19,121 +19,14 @@ import { jobsQueryFactory, } from '../lib'; -export function registerJobInfoRoutes(reporting: ReportingCore) { +export function registerJobRoutes(reporting: ReportingCore) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; const jobsQuery = jobsQueryFactory(reporting); - const registerGetList = () => { - // list jobs in the queue, paginated - const path = INTERNAL_ROUTES.JOBS.LIST; - router.get( - { - path, - validate: { - query: schema.object({ - page: schema.string({ defaultValue: '0' }), - size: schema.string({ defaultValue: '10' }), - ids: schema.maybe(schema.string()), - }), - }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return handleUnavailable(res); - } - - const { - management: { jobTypes = [] }, - } = await reporting.getLicenseInfo(); - const { page: queryPage = '0', size: querySize = '10', ids: queryIds = null } = req.query; - const page = parseInt(queryPage, 10) || 0; - const size = Math.min(100, parseInt(querySize, 10) || 10); - const jobIds = queryIds ? queryIds.split(',') : null; - const results = await jobsQuery.list(jobTypes, user, page, size, jobIds); - - counters.usageCounter(); - - return res.ok({ - body: results, - headers: { - 'content-type': 'application/json', - }, - }); - }) - ); - }; - - const registerGetCount = () => { - // return the count of all jobs in the queue - const path = INTERNAL_ROUTES.JOBS.COUNT; - router.get( - { path, validate: false }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return handleUnavailable(res); - } - - const { - management: { jobTypes = [] }, - } = await reporting.getLicenseInfo(); - - const count = await jobsQuery.count(jobTypes, user); - - counters.usageCounter(); - - return res.ok({ - body: count.toString(), - headers: { - 'content-type': 'text/plain', - }, - }); - }) - ); - }; - - const registerGetInfo = () => { - // return some info about the job - const path = INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/{docId}'; - router.get( - { - path, - validate: { - params: schema.object({ - docId: schema.string({ minLength: 2 }), - }), - }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return res.custom({ statusCode: 503 }); - } - - const { docId } = req.params; - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => - res.ok({ - body: doc, - headers: { - 'content-type': 'application/json', - }, - }) - ); - }) - ); - }; - const registerDownloadReport = () => { // trigger a download of the output from a job - const path = INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; + const path = PUBLIC_ROUTES.DOWNLOAD_PREFIX + '/{docId}'; router.get( { path, @@ -183,7 +76,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { const registerDeleteReport = () => { // allow a report to be deleted - const path = INTERNAL_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; + const path = PUBLIC_ROUTES.DELETE_PREFIX + '/{docId}'; router.delete( { path, @@ -219,9 +112,6 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { ); }; - registerGetList(); - registerGetCount(); - registerGetInfo(); registerDownloadReport(); registerDeleteReport(); } From 99ba556236bf511ca46cf1778d7cd1442b086609 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 17:20:57 -0700 Subject: [PATCH 13/47] fix tests --- .../plugins/reporting/server/routes/index.ts | 8 ++-- .../integration_tests/deprecations.test.ts | 10 ++--- .../routes/internal/diagnostic/browser.ts | 6 +-- .../integration_tests/browser.test.ts | 14 +++---- .../integration_tests/screenshot.test.ts | 12 +++--- .../routes/internal/diagnostic/screenshot.ts | 10 ++--- .../generate/generate_from_jobparams.ts | 2 +- .../generation_from_jobparams.test.ts | 28 ++++++------- .../management/integration_tests/jobs.test.ts | 42 +++++++++---------- .../routes/public/generate_from_jobparams.ts | 2 +- 10 files changed, 67 insertions(+), 67 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/index.ts b/x-pack/plugins/reporting/server/routes/index.ts index 033a13d8a1caa..12d10f7c8e9e5 100644 --- a/x-pack/plugins/reporting/server/routes/index.ts +++ b/x-pack/plugins/reporting/server/routes/index.ts @@ -10,17 +10,17 @@ import { ReportingCore } from '..'; import { registerDeprecationRoutes as internalDeprecationRoutes } from './internal/deprecations/deprecations'; import { registerDiagnosticRoutes as internalDiagnosticsRoutes } from './internal/diagnostic'; import { registerGenerateCsvFromSavedObjectImmediate as internalCsvFromSavedObjectImmediate } from './internal/generate/csv_searchsource_immediate'; -import { registerJobGeneration as internalJobGenerationRoutes } from './internal/generate/generate_from_jobparams'; +import { registerGeneration as internalGenerationRoutes } from './internal/generate/generate_from_jobparams'; import { registerJobRoutes as internalJobRoutes } from './internal/management'; -import { registerJobGenerationRoutes as publicJobGenerationRoutes } from './public/generate_from_jobparams'; +import { registerGenerationRoutes as publicGenerationRoutes } from './public/generate_from_jobparams'; import { registerJobRoutes as publicJobRoutes } from './public/jobs'; export function registerRoutes(reporting: ReportingCore, logger: Logger) { internalDeprecationRoutes(reporting, logger); internalDiagnosticsRoutes(reporting, logger); internalCsvFromSavedObjectImmediate(reporting, logger); - internalJobGenerationRoutes(reporting, logger); + internalGenerationRoutes(reporting, logger); internalJobRoutes(reporting); - publicJobGenerationRoutes(reporting, logger); + publicGenerationRoutes(reporting, logger); publicJobRoutes(reporting); } diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts index 11ceb1f3e4aa3..f40dd6d5ce6ae 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts @@ -10,14 +10,14 @@ import { setupServer } from '@kbn/core-test-helpers-test-utils'; import supertest from 'supertest'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { securityMock } from '@kbn/security-plugin/server/mocks'; -import { INTERNAL_ROUTES } from '../../../../common/constants'; +import { INTERNAL_ROUTES } from '../../../../../common/constants'; import { createMockConfigSchema, createMockPluginSetup, createMockPluginStart, createMockReportingCore, -} from '../../../test_helpers'; -import { registerDeprecations } from '../deprecations'; +} from '../../../../test_helpers'; +import { registerDeprecationRoutes } from '../deprecations'; type SetupServerReturn = Awaited>; @@ -53,7 +53,7 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { it('correctly handles authz when security is unavailable', async () => { const core = await createReportingCore({}); - registerDeprecations(core, loggingSystemMock.createLogger()); + registerDeprecationRoutes(core, loggingSystemMock.createLogger()); await server.start(); await supertest(httpSetup.server.listener) @@ -67,7 +67,7 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { security.license.isEnabled.mockReturnValue(false); const core = await createReportingCore({ security }); - registerDeprecations(core, loggingSystemMock.createLogger()); + registerDeprecationRoutes(core, loggingSystemMock.createLogger()); await server.start(); await supertest(httpSetup.server.listener) diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts index 1be5f54106ba3..cdb7f405dd993 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts @@ -9,9 +9,9 @@ import type { DocLinksServiceSetup, Logger } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { lastValueFrom } from 'rxjs'; import type { DiagnosticResponse } from '.'; -import type { ReportingCore } from '../..'; -import { INTERNAL_ROUTES } from '../../../common/constants'; -import { authorizedUserPreRouting, getCounters } from '../lib'; +import type { ReportingCore } from '../../..'; +import { INTERNAL_ROUTES } from '../../../../common/constants'; +import { authorizedUserPreRouting, getCounters } from '../../lib'; const logsToHelpMapFactory = (docLinks: DocLinksServiceSetup) => ({ 'error while loading shared libraries': i18n.translate( diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts index d6cd67a159ddd..3c053c1d47921 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts @@ -5,20 +5,20 @@ * 2.0. */ -import * as Rx from 'rxjs'; -import { docLinksServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { setupServer } from '@kbn/core-test-helpers-test-utils'; -import supertest from 'supertest'; -import { ReportingCore } from '../../..'; +import { docLinksServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; import type { ScreenshottingStart } from '@kbn/screenshotting-plugin/server'; +import * as Rx from 'rxjs'; +import supertest from 'supertest'; +import { ReportingCore } from '../../../..'; +import { INTERNAL_ROUTES } from '../../../../../common/constants'; import { createMockConfigSchema, createMockPluginSetup, createMockReportingCore, -} from '../../../test_helpers'; -import type { ReportingRequestHandlerContext } from '../../../types'; +} from '../../../../test_helpers'; +import type { ReportingRequestHandlerContext } from '../../../../types'; import { registerDiagnoseBrowser } from '../browser'; -import { INTERNAL_ROUTES } from '../../../../common/constants'; type SetupServerReturn = Awaited>; diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/screenshot.test.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/screenshot.test.ts index d3e0261b5e40e..8a943aa303537 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/screenshot.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/screenshot.test.ts @@ -9,18 +9,18 @@ import { setupServer } from '@kbn/core-test-helpers-test-utils'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { defer } from 'rxjs'; import supertest from 'supertest'; -import { ReportingCore } from '../../..'; -import { INTERNAL_ROUTES } from '../../../../common/constants'; -import { generatePngObservable } from '../../../export_types/common'; +import { ReportingCore } from '../../../..'; +import { INTERNAL_ROUTES } from '../../../../../common/constants'; +import { generatePngObservable } from '../../../../export_types/common'; import { createMockConfigSchema, createMockPluginSetup, createMockReportingCore, -} from '../../../test_helpers'; -import type { ReportingRequestHandlerContext } from '../../../types'; +} from '../../../../test_helpers'; +import type { ReportingRequestHandlerContext } from '../../../../types'; import { registerDiagnoseScreenshot } from '../screenshot'; -jest.mock('../../../export_types/common/generate_png'); +jest.mock('../../../../export_types/common/generate_png'); type SetupServerReturn = Awaited>; diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts index 33f2f420c4673..654bdaa1ae9d7 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts @@ -9,11 +9,11 @@ import type { Logger } from '@kbn/core/server'; import { APP_WRAPPER_CLASS } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { lastValueFrom } from 'rxjs'; -import type { ReportingCore } from '../..'; -import { INTERNAL_ROUTES } from '../../../common/constants'; -import { generatePngObservable } from '../../export_types/common'; -import { getAbsoluteUrlFactory } from '../../export_types/common/get_absolute_url'; -import { authorizedUserPreRouting, getCounters } from '../lib'; +import type { ReportingCore } from '../../..'; +import { INTERNAL_ROUTES } from '../../../../common/constants'; +import { generatePngObservable } from '../../../export_types/common'; +import { getAbsoluteUrlFactory } from '../../../export_types/common/get_absolute_url'; +import { authorizedUserPreRouting, getCounters } from '../../lib'; const path = INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index 6f5d6abc5b1af..be87dbc7846a6 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -13,7 +13,7 @@ import { INTERNAL_ROUTES } from '../../../../common/constants'; import type { BaseParams } from '../../../types'; import { authorizedUserPreRouting, getCounters, RequestHandler } from '../../lib'; -export function registerJobGeneration(reporting: ReportingCore, logger: Logger) { +export function registerGeneration(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts index 70102866b7235..e854212e84723 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts @@ -10,19 +10,19 @@ import { BehaviorSubject } from 'rxjs'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { setupServer } from '@kbn/core-test-helpers-test-utils'; import supertest from 'supertest'; -import { ReportingCore } from '../../..'; +import { ReportingCore } from '../../../..'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; -import { ReportingStore } from '../../../lib'; -import { ExportTypesRegistry } from '../../../lib/export_types_registry'; -import { Report } from '../../../lib/store'; +import { ReportingStore } from '../../../../lib'; +import { ExportTypesRegistry } from '../../../../lib/export_types_registry'; +import { Report } from '../../../../lib/store'; import { createMockConfigSchema, createMockPluginSetup, createMockPluginStart, createMockReportingCore, -} from '../../../test_helpers'; -import type { ReportingRequestHandlerContext } from '../../../types'; -import { registerJobGeneration } from '../generate_from_jobparams'; +} from '../../../../test_helpers'; +import type { ReportingRequestHandlerContext } from '../../../../types'; +import { registerGeneration } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; @@ -105,7 +105,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if there are no job params', async () => { - registerJobGeneration(mockReportingCore, mockLogger); + registerGeneration(mockReportingCore, mockLogger); await server.start(); @@ -120,7 +120,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params query is invalid', async () => { - registerJobGeneration(mockReportingCore, mockLogger); + registerGeneration(mockReportingCore, mockLogger); await server.start(); @@ -131,7 +131,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params body is invalid', async () => { - registerJobGeneration(mockReportingCore, mockLogger); + registerGeneration(mockReportingCore, mockLogger); await server.start(); @@ -143,7 +143,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 export type is invalid', async () => { - registerJobGeneration(mockReportingCore, mockLogger); + registerGeneration(mockReportingCore, mockLogger); await server.start(); @@ -157,7 +157,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 on invalid browser timezone', async () => { - registerJobGeneration(mockReportingCore, mockLogger); + registerGeneration(mockReportingCore, mockLogger); await server.start(); @@ -173,7 +173,7 @@ describe('POST /api/reporting/generate', () => { it('returns 500 if job handler throws an error', async () => { store.addReport = jest.fn().mockRejectedValue('silly'); - registerJobGeneration(mockReportingCore, mockLogger); + registerGeneration(mockReportingCore, mockLogger); await server.start(); @@ -184,7 +184,7 @@ describe('POST /api/reporting/generate', () => { }); it(`returns 200 if job handler doesn't error`, async () => { - registerJobGeneration(mockReportingCore, mockLogger); + registerGeneration(mockReportingCore, mockLogger); await server.start(); diff --git a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts index 7a31f44ba4f14..0640d01496c6d 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -jest.mock('../../../lib/content_stream', () => ({ +jest.mock('../../../../lib/content_stream', () => ({ getContentStream: jest.fn(), })); import { estypes } from '@elastic/elasticsearch'; @@ -15,17 +15,17 @@ import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { BehaviorSubject } from 'rxjs'; import { Readable } from 'stream'; import supertest from 'supertest'; -import { ReportingCore } from '../../..'; -import { ReportingInternalSetup, ReportingInternalStart } from '../../../core'; -import { ContentStream, ExportTypesRegistry, getContentStream } from '../../../lib'; +import { ReportingCore } from '../../../..'; +import { ReportingInternalSetup, ReportingInternalStart } from '../../../../core'; +import { ContentStream, ExportTypesRegistry, getContentStream } from '../../../../lib'; import { createMockConfigSchema, createMockPluginSetup, createMockPluginStart, createMockReportingCore, -} from '../../../test_helpers'; -import { ExportTypeDefinition, ReportingRequestHandlerContext } from '../../../types'; -import { registerJobInfoRoutes } from '../jobs'; +} from '../../../../test_helpers'; +import { ExportTypeDefinition, ReportingRequestHandlerContext } from '../../../../types'; +import { registerJobRoutes } from '../jobs'; type SetupServerReturn = Awaited>; @@ -115,7 +115,7 @@ describe('GET /api/reporting/jobs/download', () => { it('fails on malformed download IDs', async () => { mockEsClient.search.mockResponseOnce(getHits()); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); @@ -141,7 +141,7 @@ describe('GET /api/reporting/jobs/download', () => { mockConfigSchema ); core = await createMockReportingCore(mockConfigSchema, mockSetupDeps, mockStartDeps); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); @@ -155,7 +155,7 @@ describe('GET /api/reporting/jobs/download', () => { it('returns 404 if job not found', async () => { mockEsClient.search.mockResponseOnce(getHits()); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); @@ -169,7 +169,7 @@ describe('GET /api/reporting/jobs/download', () => { payload: { title: 'invalid!' }, }) ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); @@ -184,7 +184,7 @@ describe('GET /api/reporting/jobs/download', () => { }) ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); @@ -199,7 +199,7 @@ describe('GET /api/reporting/jobs/download', () => { }) ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); @@ -214,7 +214,7 @@ describe('GET /api/reporting/jobs/download', () => { payload: { title: 'incomplete!' }, }) ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -234,7 +234,7 @@ describe('GET /api/reporting/jobs/download', () => { payload: { title: 'failing job!' }, }) ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -262,7 +262,7 @@ describe('GET /api/reporting/jobs/download', () => { it('when a known job-type is complete', async () => { mockEsClient.search.mockResponseOnce(getCompleteHits()); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -278,7 +278,7 @@ describe('GET /api/reporting/jobs/download', () => { // @ts-ignore core.pluginSetupDeps.security = null; - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); @@ -295,7 +295,7 @@ describe('GET /api/reporting/jobs/download', () => { jobType: 'unencodedJobType', }) ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -312,7 +312,7 @@ describe('GET /api/reporting/jobs/download', () => { outputContentType: 'application/html', }) ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -334,7 +334,7 @@ describe('GET /api/reporting/jobs/download', () => { title: '日本語ダッシュボード', }) ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -371,7 +371,7 @@ describe('GET /api/reporting/jobs/download', () => { mockStartDeps ); - registerJobInfoRoutes(core); + registerJobRoutes(core); await server.start(); diff --git a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts index 7bbd66dbd6769..3eef92bcc22bf 100644 --- a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts @@ -13,7 +13,7 @@ import { PUBLIC_ROUTES } from '../../../common/constants'; import type { BaseParams } from '../../types'; import { authorizedUserPreRouting, getCounters, RequestHandler } from '../lib'; -export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Logger) { +export function registerGenerationRoutes(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; From 6d4617472d2d572d469f02c827ab32f0d3e41119 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 17:47:26 -0700 Subject: [PATCH 14/47] update snapshot --- .../deprecations/migrate_existing_indices_ilm_policy.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.test.ts b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.test.ts index 551bf76465642..5dbbb9e9570c1 100644 --- a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.test.ts +++ b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.test.ts @@ -52,7 +52,7 @@ describe("Migrate existing indices' ILM policy deprecations", () => { "correctiveActions": Object { "api": Object { "method": "PUT", - "path": "/api/reporting/deprecations/migrate_ilm_policy", + "path": "/internal/reporting/deprecations/migrate_ilm_policy", }, "manualSteps": Array [ "Update all reporting indices to use the \\"kibana-reporting\\" policy using the index settings API.", From fc30deefbdadde436205f435e2884a63f854f17c Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 17:54:07 -0700 Subject: [PATCH 15/47] fix tests --- .../reporting_api_client.test.ts | 24 +++++-------------- .../reporting_api_client.ts | 6 ++++- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.test.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.test.ts index d5017104ed09f..38e95e9d75323 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.test.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.test.ts @@ -25,9 +25,9 @@ describe('ReportingAPIClient', () => { }); describe('getReportURL', () => { - it('should generate report URL', () => { + it('should generate the internal report download URL', () => { expect(apiClient.getReportURL('123')).toMatchInlineSnapshot( - `"/base/path/api/reporting/jobs/download/123"` + `"/base/path/internal/reporting/jobs/download/123"` ); }); }); @@ -52,10 +52,7 @@ describe('ReportingAPIClient', () => { it('should send a delete request', async () => { await apiClient.deleteReport('123'); - expect(httpClient.delete).toHaveBeenCalledWith( - expect.stringContaining('/delete/123'), - expect.any(Object) - ); + expect(httpClient.delete).toHaveBeenCalledWith(expect.stringContaining('/delete/123')); }); }); @@ -112,10 +109,7 @@ describe('ReportingAPIClient', () => { it('should send a get request', async () => { await apiClient.getInfo('123'); - expect(httpClient.get).toHaveBeenCalledWith( - expect.stringContaining('/info/123'), - expect.any(Object) - ); + expect(httpClient.get).toHaveBeenCalledWith(expect.stringContaining('/info/123')); }); it('should return a job instance', async () => { @@ -293,10 +287,7 @@ describe('ReportingAPIClient', () => { it('should send a post request', async () => { await apiClient.verifyBrowser(); - expect(httpClient.post).toHaveBeenCalledWith( - expect.stringContaining('/diagnose/browser'), - expect.any(Object) - ); + expect(httpClient.post).toHaveBeenCalledWith(expect.stringContaining('/diagnose/browser')); }); }); @@ -304,10 +295,7 @@ describe('ReportingAPIClient', () => { it('should send a post request', async () => { await apiClient.verifyScreenCapture(); - expect(httpClient.post).toHaveBeenCalledWith( - expect.stringContaining('/diagnose/screenshot'), - expect.any(Object) - ); + expect(httpClient.post).toHaveBeenCalledWith(expect.stringContaining('/diagnose/screenshot')); }); }); diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index 15656ac710788..e07f3101b0f78 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -93,6 +93,9 @@ export class ReportingAPIClient implements IReportingAPI { return href; } + /** + * Get the internal URL + */ public getReportURL(jobId: string) { const downloadLink = this.http.basePath.prepend( `${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/${jobId}` @@ -163,6 +166,7 @@ export class ReportingAPIClient implements IReportingAPI { /** * Returns a string for the public API endpoint used to automate the generation of reports + * This string must be shown when the user selects the option to view/copy the POST URL */ public getReportingPublicJobPath(exportType: string, jobParams: BaseParams) { const params = stringify({ @@ -228,6 +232,6 @@ export class ReportingAPIClient implements IReportingAPI { } public migrateReportingIndicesIlmPolicy() { - return this.http.put(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS); + return this.http.put(INTERNAL_ROUTES.MIGRATE.MIGRATE_ILM_POLICY); } } From 664aaa4105f497d48f1f101e91b229b933f19376 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 19 Jul 2023 19:02:50 -0700 Subject: [PATCH 16/47] Tests --- .../management/integration_tests/jobs.test.ts | 30 +-- .../generation_from_jobparams.test.ts | 34 +-- .../public/integration_tests/jobs.test.ts | 245 ++++++++++++++++++ 3 files changed, 277 insertions(+), 32 deletions(-) rename x-pack/plugins/reporting/server/routes/{internal/generate => public}/integration_tests/generation_from_jobparams.test.ts (88%) create mode 100644 x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts diff --git a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts index 0640d01496c6d..f1e9c7133ed58 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts @@ -29,7 +29,7 @@ import { registerJobRoutes } from '../jobs'; type SetupServerReturn = Awaited>; -describe('GET /api/reporting/jobs/download', () => { +describe('GET /internal/reporting/jobs/download', () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; let httpSetup: SetupServerReturn['httpSetup']; @@ -120,7 +120,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/1') + .get('/internal/reporting/jobs/download/1') .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot( @@ -146,7 +146,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dope') + .get('/internal/reporting/jobs/download/dope') .expect(401) .then(({ body }) => expect(body.message).toMatchInlineSnapshot(`"Sorry, you aren't authenticated"`) @@ -159,7 +159,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); - await supertest(httpSetup.server.listener).get('/api/reporting/jobs/download/poo').expect(404); + await supertest(httpSetup.server.listener).get('/internal/reporting/jobs/download/poo').expect(404); }); it('returns a 403 if not a valid job type', async () => { @@ -173,7 +173,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); - await supertest(httpSetup.server.listener).get('/api/reporting/jobs/download/poo').expect(403); + await supertest(httpSetup.server.listener).get('/internal/reporting/jobs/download/poo').expect(403); }); it(`returns job's info`, async () => { @@ -188,7 +188,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); - await supertest(httpSetup.server.listener).get('/api/reporting/jobs/info/test').expect(200); + await supertest(httpSetup.server.listener).get('/internal/reporting/jobs/info/test').expect(200); }); it(`returns 403 if a user cannot view a job's info`, async () => { @@ -203,7 +203,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); - await supertest(httpSetup.server.listener).get('/api/reporting/jobs/info/test').expect(403); + await supertest(httpSetup.server.listener).get('/internal/reporting/jobs/info/test').expect(403); }); it('when a job is incomplete', async () => { @@ -218,7 +218,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dank') + .get('/internal/reporting/jobs/download/dank') .expect(503) .expect('Content-Type', 'text/plain; charset=utf-8') .expect('Retry-After', '30') @@ -238,7 +238,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dank') + .get('/internal/reporting/jobs/download/dank') .expect(500) .expect('Content-Type', 'application/json; charset=utf-8') .then(({ body }) => @@ -266,7 +266,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dank') + .get('/internal/reporting/jobs/download/dank') .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); @@ -283,7 +283,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dope') + .get('/internal/reporting/jobs/download/dope') .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); @@ -299,7 +299,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dank') + .get('/internal/reporting/jobs/download/dank') .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .then(({ text }) => expect(text).toEqual('test')); @@ -316,7 +316,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dank') + .get('/internal/reporting/jobs/download/dank') .expect(400) .then(({ body }) => { expect(body).toEqual({ @@ -338,7 +338,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/japanese-dashboard') + .get('/internal/reporting/jobs/download/japanese-dashboard') .expect(200) .expect('Content-Type', 'application/pdf') .expect( @@ -376,7 +376,7 @@ describe('GET /api/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dope') + .get('/internal/reporting/jobs/download/dope') .expect(403) .then(({ body }) => expect(body.message).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts similarity index 88% rename from x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts rename to x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts index e854212e84723..8831a515fb1a9 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts @@ -5,24 +5,24 @@ * 2.0. */ +import { setupServer } from '@kbn/core-test-helpers-test-utils'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import rison from '@kbn/rison'; import { BehaviorSubject } from 'rxjs'; -import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { setupServer } from '@kbn/core-test-helpers-test-utils'; import supertest from 'supertest'; -import { ReportingCore } from '../../../..'; -import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; -import { ReportingStore } from '../../../../lib'; -import { ExportTypesRegistry } from '../../../../lib/export_types_registry'; -import { Report } from '../../../../lib/store'; +import { ReportingCore } from '../../..'; +import { ReportingStore } from '../../../lib'; +import { ExportTypesRegistry } from '../../../lib/export_types_registry'; +import { Report } from '../../../lib/store'; import { createMockConfigSchema, createMockPluginSetup, createMockPluginStart, createMockReportingCore, -} from '../../../../test_helpers'; -import type { ReportingRequestHandlerContext } from '../../../../types'; -import { registerGeneration } from '../generate_from_jobparams'; +} from '../../../test_helpers'; +import type { ReportingRequestHandlerContext } from '../../../types'; +import { registerGenerationRoutes } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; @@ -105,7 +105,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if there are no job params', async () => { - registerGeneration(mockReportingCore, mockLogger); + registerGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -120,7 +120,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params query is invalid', async () => { - registerGeneration(mockReportingCore, mockLogger); + registerGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -131,7 +131,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params body is invalid', async () => { - registerGeneration(mockReportingCore, mockLogger); + registerGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -143,7 +143,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 export type is invalid', async () => { - registerGeneration(mockReportingCore, mockLogger); + registerGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -157,7 +157,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 on invalid browser timezone', async () => { - registerGeneration(mockReportingCore, mockLogger); + registerGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -173,7 +173,7 @@ describe('POST /api/reporting/generate', () => { it('returns 500 if job handler throws an error', async () => { store.addReport = jest.fn().mockRejectedValue('silly'); - registerGeneration(mockReportingCore, mockLogger); + registerGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -184,7 +184,7 @@ describe('POST /api/reporting/generate', () => { }); it(`returns 200 if job handler doesn't error`, async () => { - registerGeneration(mockReportingCore, mockLogger); + registerGenerationRoutes(mockReportingCore, mockLogger); await server.start(); diff --git a/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts new file mode 100644 index 0000000000000..1d1c6fb3e01f6 --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts @@ -0,0 +1,245 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +jest.mock('../../../lib/content_stream', () => ({ + getContentStream: jest.fn(), +})); +import { estypes } from '@elastic/elasticsearch'; +import { setupServer } from '@kbn/core-test-helpers-test-utils'; +import type { ElasticsearchClientMock } from '@kbn/core/server/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; +import { BehaviorSubject } from 'rxjs'; +import { Readable } from 'stream'; +import supertest from 'supertest'; +import { ReportingCore } from '../../..'; +import { ReportingInternalSetup, ReportingInternalStart } from '../../../core'; +import { ContentStream, ExportTypesRegistry, getContentStream } from '../../../lib'; +import { + createMockConfigSchema, + createMockPluginSetup, + createMockPluginStart, + createMockReportingCore, +} from '../../../test_helpers'; +import { ExportTypeDefinition, ReportingRequestHandlerContext } from '../../../types'; +import { registerJobRoutes } from '../jobs'; + +type SetupServerReturn = Awaited>; + +describe('GET /api/reporting/jobs/download', () => { + const reportingSymbol = Symbol('reporting'); + let server: SetupServerReturn['server']; + let httpSetup: SetupServerReturn['httpSetup']; + let exportTypesRegistry: ExportTypesRegistry; + let core: ReportingCore; + let mockSetupDeps: ReportingInternalSetup; + let mockStartDeps: ReportingInternalStart; + let mockEsClient: ElasticsearchClientMock; + let stream: jest.Mocked; + + const getHits = (...sources: any) => { + return { + hits: { + hits: sources.map((source: object) => ({ _source: source })), + }, + } as estypes.SearchResponseBody; + }; + + const mockConfigSchema = createMockConfigSchema({ roles: { enabled: false } }); + + beforeEach(async () => { + ({ server, httpSetup } = await setupServer(reportingSymbol)); + httpSetup.registerRouteHandlerContext( + reportingSymbol, + 'reporting', + () => ({ usesUiCapabilities: jest.fn(), registerExportTypes: jest.fn() }) + ); + + mockSetupDeps = createMockPluginSetup({ + security: { + license: { isEnabled: () => true }, + }, + router: httpSetup.createRouter(''), + }); + + mockStartDeps = await createMockPluginStart( + { + licensing: { + ...licensingMock.createStart(), + license$: new BehaviorSubject({ isActive: true, isAvailable: true, type: 'gold' }), + }, + security: { + authc: { + getCurrentUser: () => ({ id: '123', roles: ['superuser'], username: 'Tom Riddle' }), + }, + }, + }, + mockConfigSchema + ); + + core = await createMockReportingCore(mockConfigSchema, mockSetupDeps, mockStartDeps); + + exportTypesRegistry = new ExportTypesRegistry(); + exportTypesRegistry.register({ + id: 'unencoded', + jobType: 'unencodedJobType', + jobContentExtension: 'csv', + validLicenses: ['basic', 'gold'], + } as ExportTypeDefinition); + exportTypesRegistry.register({ + id: 'base64Encoded', + jobType: 'base64EncodedJobType', + jobContentEncoding: 'base64', + jobContentExtension: 'pdf', + validLicenses: ['basic', 'gold'], + } as ExportTypeDefinition); + core.getExportTypesRegistry = () => exportTypesRegistry; + + mockEsClient = (await core.getEsClient()).asInternalUser as typeof mockEsClient; + stream = new Readable({ + read() { + this.push('test'); + this.push(null); + }, + }) as typeof stream; + + (getContentStream as jest.MockedFunction).mockResolvedValue(stream); + }); + + afterEach(async () => { + await server.stop(); + }); + + it('fails on malformed download IDs', async () => { + mockEsClient.search.mockResponseOnce(getHits()); + registerJobRoutes(core); + + await server.start(); + + await supertest(httpSetup.server.listener) + .get('/api/reporting/jobs/download/1') + .expect(400) + .then(({ body }) => + expect(body.message).toMatchInlineSnapshot( + '"[request params.docId]: value has length [1] but it must have a minimum length of [3]."' + ) + ); + }); + + it('fails on unauthenticated users', async () => { + mockStartDeps = await createMockPluginStart( + { + licensing: { + ...licensingMock.createStart(), + license$: new BehaviorSubject({ isActive: true, isAvailable: true, type: 'gold' }), + }, + security: { authc: { getCurrentUser: () => undefined } }, + }, + mockConfigSchema + ); + core = await createMockReportingCore(mockConfigSchema, mockSetupDeps, mockStartDeps); + registerJobRoutes(core); + + await server.start(); + + await supertest(httpSetup.server.listener) + .get('/api/reporting/jobs/download/dope') + .expect(401) + .then(({ body }) => + expect(body.message).toMatchInlineSnapshot(`"Sorry, you aren't authenticated"`) + ); + }); + + it('returns 404 if job not found', async () => { + mockEsClient.search.mockResponseOnce(getHits()); + registerJobRoutes(core); + + await server.start(); + + await supertest(httpSetup.server.listener).get('/api/reporting/jobs/download/poo').expect(404); + }); + + it('returns a 403 if not a valid job type', async () => { + mockEsClient.search.mockResponseOnce( + getHits({ + jobtype: 'invalidJobType', + payload: { title: 'invalid!' }, + }) + ); + registerJobRoutes(core); + + await server.start(); + + await supertest(httpSetup.server.listener).get('/api/reporting/jobs/download/poo').expect(403); + }); + + it('when a job is incomplete', async () => { + mockEsClient.search.mockResponseOnce( + getHits({ + jobtype: 'unencodedJobType', + status: 'pending', + payload: { title: 'incomplete!' }, + }) + ); + registerJobRoutes(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .get('/api/reporting/jobs/download/dank') + .expect(503) + .expect('Content-Type', 'text/plain; charset=utf-8') + .expect('Retry-After', '30') + .then(({ text }) => expect(text).toEqual('pending')); + }); + + it('when a job fails', async () => { + mockEsClient.search.mockResponse( + getHits({ + jobtype: 'unencodedJobType', + status: 'failed', + output: { content: 'job failure message' }, + payload: { title: 'failing job!' }, + }) + ); + registerJobRoutes(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .get('/api/reporting/jobs/download/dank') + .expect(500) + .expect('Content-Type', 'application/json; charset=utf-8') + .then(({ body }) => + expect(body.message).toEqual('Reporting generation failed: job failure message') + ); + }); + + describe('successful downloads', () => { + const getCompleteHits = ({ + jobType = 'unencodedJobType', + outputContentType = 'text/plain', + title = '', + } = {}) => { + return getHits({ + jobtype: jobType, + status: 'completed', + output: { content_type: outputContentType }, + payload: { title }, + }) as estypes.SearchResponseBody; + }; + + it('when a known job-type is complete', async () => { + mockEsClient.search.mockResponseOnce(getCompleteHits()); + registerJobRoutes(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .get('/api/reporting/jobs/download/dank') + .expect(200) + .expect('Content-Type', 'text/csv; charset=utf-8') + .expect('content-disposition', 'attachment; filename=report.csv'); + }); + }); +}); From 167bc55df72b4d8482fa914a035fbb8a2179d1c7 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 20 Jul 2023 02:16:58 +0000 Subject: [PATCH 17/47] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../management/integration_tests/jobs.test.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts index f1e9c7133ed58..8a419cab18fb8 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts @@ -159,7 +159,9 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); - await supertest(httpSetup.server.listener).get('/internal/reporting/jobs/download/poo').expect(404); + await supertest(httpSetup.server.listener) + .get('/internal/reporting/jobs/download/poo') + .expect(404); }); it('returns a 403 if not a valid job type', async () => { @@ -173,7 +175,9 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); - await supertest(httpSetup.server.listener).get('/internal/reporting/jobs/download/poo').expect(403); + await supertest(httpSetup.server.listener) + .get('/internal/reporting/jobs/download/poo') + .expect(403); }); it(`returns job's info`, async () => { @@ -188,7 +192,9 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); - await supertest(httpSetup.server.listener).get('/internal/reporting/jobs/info/test').expect(200); + await supertest(httpSetup.server.listener) + .get('/internal/reporting/jobs/info/test') + .expect(200); }); it(`returns 403 if a user cannot view a job's info`, async () => { @@ -203,7 +209,9 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); - await supertest(httpSetup.server.listener).get('/internal/reporting/jobs/info/test').expect(403); + await supertest(httpSetup.server.listener) + .get('/internal/reporting/jobs/info/test') + .expect(403); }); it('when a job is incomplete', async () => { From 61c611ea50693300adba4d40274bf656be468ef4 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 20 Jul 2023 15:54:25 -0700 Subject: [PATCH 18/47] Fix tests --- .../reporting_and_security/error_codes.ts | 3 ++- .../usage/api_counters.ts | 25 ++++++++++--------- .../job_apis_csv.ts | 11 ++++---- .../services/scenarios.ts | 2 +- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/error_codes.ts b/x-pack/test/reporting_api_integration/reporting_and_security/error_codes.ts index 9f0d6ebb15c3e..d60531110e5da 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/error_codes.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/error_codes.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; import { ReportApiJSON } from '@kbn/reporting-plugin/common/types'; import { FtrProviderContext } from '../ftr_provider_context'; @@ -48,7 +49,7 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.load('x-pack/test/functional/es_archives/reporting/archived_reports'); const jobInfo = await supertest - .get('/api/reporting/jobs/info/kraz4j94154g0763b583rc37') + .get(INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/kraz4j94154g0763b583rc37') .auth('test_user', 'changeme'); expect(jobInfo.body.output.warnings).to.eql([ diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts index a79799a4ee850..f35aea31017a7 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; import { createPdfV2Params, createPngV2Params } from '..'; import { FtrProviderContext } from '../../ftr_provider_context'; import { UsageStatsPayloadTestFriendly } from '../../../api_integration/services/usage_api'; @@ -45,14 +46,14 @@ export default function ({ getService }: FtrProviderContext) { }); describe('API counters: management', () => { - enum paths { - LIST = '/api/reporting/jobs/list', - COUNT = '/api/reporting/jobs/count', - INFO = '/api/reporting/jobs/info/kraz0qle154g0763b569zz83', - ILM = '/api/reporting/ilm_policy_status', - DIAG_BROWSER = '/api/reporting/diagnose/browser', - DIAG_SCREENSHOT = '/api/reporting/diagnose/screenshot', - } + const paths = { + LIST: INTERNAL_ROUTES.JOBS.LIST, + COUNT: INTERNAL_ROUTES.JOBS.COUNT, + INFO: INTERNAL_ROUTES.JOBS.COUNT + '/kraz0qle154g0763b569zz83', + ILM: INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS, + DIAG_BROWSER: INTERNAL_ROUTES.DIAGNOSE.BROWSER, + DIAG_SCREENSHOT: INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT, + }; let initialStats: UsageStatsPayloadTestFriendly; let stats: UsageStatsPayloadTestFriendly; @@ -91,11 +92,11 @@ export default function ({ getService }: FtrProviderContext) { it('job info', async () => { const initialCount = getUsageCount( initialStats, - `get /api/reporting/jobs/info/{docId}:printable_pdf` - ); - expect(getUsageCount(stats, `get /api/reporting/jobs/info/{docId}:printable_pdf`)).to.be( - CALL_COUNT + initialCount + `get ${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/{docId}:printable_pdf` ); + expect( + getUsageCount(stats, `get ${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/{docId}:printable_pdf`) + ).to.be(CALL_COUNT + initialCount); }); }); diff --git a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts index 22be1d776e792..b4d9c39f40cc6 100644 --- a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts +++ b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts @@ -6,8 +6,9 @@ */ import expect from '@kbn/expect'; -import { pick } from 'lodash'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; import { ReportApiJSON } from '@kbn/reporting-plugin/common/types'; +import { pick } from 'lodash'; import { FtrProviderContext } from '../ftr_provider_context'; const apiResponseFields = [ @@ -81,7 +82,7 @@ export default function ({ getService }: FtrProviderContext) { // call the job count api const { text: countText } = await supertestNoAuth - .get(`/api/reporting/jobs/count`) + .get(INTERNAL_ROUTES.JOBS.COUNT) .set('kbn-xsrf', 'xxx'); expect(countText).to.be('1'); @@ -109,7 +110,7 @@ export default function ({ getService }: FtrProviderContext) { // call the listing api const { text: listText } = await supertestNoAuth - .get(`/api/reporting/jobs/list?page=0&ids=${job.id}`) + .get(`${INTERNAL_ROUTES.JOBS.LIST}?page=0&ids=${job.id}`) .set('kbn-xsrf', 'xxx'); // verify the top item in the list @@ -156,7 +157,7 @@ export default function ({ getService }: FtrProviderContext) { // call the listing api const { text: listText, status } = await supertestNoAuth - .get(`/api/reporting/jobs/list?page=0`) + .get(`${INTERNAL_ROUTES.JOBS.LIST}?page=0`) .set('kbn-xsrf', 'xxx'); expect(status).to.be(200); @@ -203,7 +204,7 @@ export default function ({ getService }: FtrProviderContext) { `); const { text: infoText, status } = await supertestNoAuth - .get(`/api/reporting/jobs/info/${job.id}`) + .get(`${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/${job.id}`) .set('kbn-xsrf', 'xxx'); expect(status).to.be(200); diff --git a/x-pack/test/reporting_api_integration/services/scenarios.ts b/x-pack/test/reporting_api_integration/services/scenarios.ts index ee5d0ab2b72c3..7d3ef8748c87f 100644 --- a/x-pack/test/reporting_api_integration/services/scenarios.ts +++ b/x-pack/test/reporting_api_integration/services/scenarios.ts @@ -197,7 +197,7 @@ export function createScenarios({ getService }: Pick Date: Thu, 20 Jul 2023 16:03:09 -0700 Subject: [PATCH 19/47] fix more tests --- .../test/reporting_api_integration/services/usage.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/x-pack/test/reporting_api_integration/services/usage.ts b/x-pack/test/reporting_api_integration/services/usage.ts index dd65ef5e38727..e3da1286dc5d9 100644 --- a/x-pack/test/reporting_api_integration/services/usage.ts +++ b/x-pack/test/reporting_api_integration/services/usage.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { Response } from 'supertest'; import expect from '@kbn/expect'; +import { INTERNAL_ROUTES, PUBLIC_ROUTES } from '@kbn/reporting-plugin/common/constants'; import { indexTimestamp } from '@kbn/reporting-plugin/server/lib/store/index_timestamp'; import { AvailableTotal, @@ -14,6 +14,7 @@ import { LayoutCounts, ReportingUsageType, } from '@kbn/reporting-plugin/server/usage/types'; +import { Response } from 'supertest'; import { FtrProviderContext } from '../ftr_provider_context'; export function createUsageServices({ getService }: FtrProviderContext) { @@ -44,7 +45,12 @@ export function createUsageServices({ getService }: FtrProviderContext) { }, 1500); }); if (!ignoreFailure) { - const jobInfo = await supertest.get(downloadReportPath.replace(/download/, 'info')); + const jobInfo = await supertest.get( + downloadReportPath.replace( + PUBLIC_ROUTES.DOWNLOAD_PREFIX, + INTERNAL_ROUTES.JOBS.INFO_PREFIX + ) + ); expect(jobInfo.body.output.warnings).to.be(undefined); // expect no failure message to be present in job info expect(statusCode).to.be(200); } From 7d22b64a81acda5166a47056c876abdc2f6fb427 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 20 Jul 2023 16:13:28 -0700 Subject: [PATCH 20/47] replace a string with a variable --- x-pack/test/reporting_api_integration/services/scenarios.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/reporting_api_integration/services/scenarios.ts b/x-pack/test/reporting_api_integration/services/scenarios.ts index 7d3ef8748c87f..480701ff12ee1 100644 --- a/x-pack/test/reporting_api_integration/services/scenarios.ts +++ b/x-pack/test/reporting_api_integration/services/scenarios.ts @@ -134,7 +134,7 @@ export function createScenarios({ getService }: Pick { return await supertestWithoutAuth - .post(`/internal/reporting/generate/immediate/csv_searchsource`) + .post(INTERNAL_ROUTES.GENERATE.CSV_IMMEDIATE) .auth(username, password) .set('kbn-xsrf', 'xxx') .send(job); From 7539cef77a4d9916467b920bae5999b8b0fa9c83 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 24 Jul 2023 12:52:32 -0700 Subject: [PATCH 21/47] move common report generation code into RequestHandler.handleGenerateRequest --- .../reporting/server/routes/common/index.ts | 9 ++ .../generate/generate_from_jobparams.ts | 85 ++----------- .../server/routes/lib/request_handler.ts | 80 ++++++++++-- .../routes/public/generate_from_jobparams.ts | 114 ++++-------------- 4 files changed, 119 insertions(+), 169 deletions(-) create mode 100644 x-pack/plugins/reporting/server/routes/common/index.ts diff --git a/x-pack/plugins/reporting/server/routes/common/index.ts b/x-pack/plugins/reporting/server/routes/common/index.ts new file mode 100644 index 0000000000000..455c98d636d50 --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/common/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { generate } from './generate'; +export { jobs } from './jobs'; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index be87dbc7846a6..47edf10e00268 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -5,13 +5,10 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; import type { Logger } from '@kbn/core/server'; -import rison from '@kbn/rison'; import type { ReportingCore } from '../../..'; import { INTERNAL_ROUTES } from '../../../../common/constants'; -import type { BaseParams } from '../../../types'; -import { authorizedUserPreRouting, getCounters, RequestHandler } from '../../lib'; +import { authorizedUserPreRouting, RequestHandler } from '../../lib'; export function registerGeneration(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); @@ -24,72 +21,16 @@ export function registerGeneration(reporting: ReportingCore, logger: Logger) { const useKibanaAccessControl = reporting.getDeprecatedAllowedRoles() === false; // true if Reporting's deprecated access control feature is disabled const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; - const registerPostGenerationEndpoint = () => { - const path = `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{exportType}`; - router.post( - { - path, - validate: { - params: schema.object({ exportType: schema.string({ minLength: 2 }) }), - body: schema.nullable(schema.object({ jobParams: schema.maybe(schema.string()) })), - query: schema.nullable(schema.object({ jobParams: schema.string({ defaultValue: '' }) })), - }, - options: { tags: kibanaAccessControlTags }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters( - req.route.method, - path.replace(/{exportType}/, req.params.exportType), - reporting.getUsageCounter() - ); - - let jobParamsRison: null | string = null; - - if (req.body) { - const { jobParams: jobParamsPayload } = req.body; - jobParamsRison = jobParamsPayload ? jobParamsPayload : null; - } else if (req.query?.jobParams) { - const { jobParams: queryJobParams } = req.query; - if (queryJobParams) { - jobParamsRison = queryJobParams; - } else { - jobParamsRison = null; - } - } - - if (!jobParamsRison) { - return res.customError({ - statusCode: 400, - body: 'A jobParams RISON string is required in the querystring or POST body', - }); - } - - let jobParams; - - try { - jobParams = rison.decode(jobParamsRison) as BaseParams | null; - if (!jobParams) { - return res.customError({ - statusCode: 400, - body: 'Missing jobParams!', - }); - } - } catch (err) { - return res.customError({ - statusCode: 400, - body: `invalid rison: ${jobParamsRison}`, - }); - } - - const requestHandler = new RequestHandler(reporting, user, context, req, res, logger); - return await requestHandler.handleGenerateRequest( - req.params.exportType, - jobParams, - counters - ); - }) - ); - }; - - registerPostGenerationEndpoint(); + const path = `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{exportType}`; + router.post( + { + path, + validate: RequestHandler.getValidation(), + options: { tags: kibanaAccessControlTags }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const requestHandler = new RequestHandler(reporting, user, context, req, res, logger); + return await requestHandler.handleGenerateRequest(path, req.params.exportType); + }) + ); } diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts b/x-pack/plugins/reporting/server/routes/lib/request_handler.ts index 08eec9a71180b..2778e25a08d52 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts +++ b/x-pack/plugins/reporting/server/routes/lib/request_handler.ts @@ -6,20 +6,28 @@ */ import Boom from '@hapi/boom'; +import { schema, TypeOf } from '@kbn/config-schema'; import type { KibanaRequest, KibanaResponseFactory, Logger } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; +import rison from '@kbn/rison'; import moment from 'moment'; import type { ReportingCore } from '../..'; import { PUBLIC_ROUTES } from '../../../common/constants'; import { checkParamsVersion, cryptoFactory } from '../../lib'; import { Report } from '../../lib/store'; import type { BaseParams, ReportingRequestHandlerContext, ReportingUser } from '../../types'; -import { Counters } from './get_counter'; +import { Counters, getCounters } from './get_counter'; export const handleUnavailable = (res: KibanaResponseFactory) => { return res.custom({ statusCode: 503, body: 'Not Available' }); }; +const validation = { + params: schema.object({ exportType: schema.string({ minLength: 2 }) }), + body: schema.nullable(schema.object({ jobParams: schema.maybe(schema.string()) })), + query: schema.nullable(schema.object({ jobParams: schema.string({ defaultValue: '' }) })), +}; + /** * Handles the common parts of requests to generate a report */ @@ -28,7 +36,11 @@ export class RequestHandler { private reporting: ReportingCore, private user: ReportingUser, private context: ReportingRequestHandlerContext, - private req: KibanaRequest, + private req: KibanaRequest< + TypeOf, + TypeOf, + TypeOf + >, private res: KibanaResponseFactory, private logger: Logger ) {} @@ -105,11 +117,64 @@ export class RequestHandler { return report; } - public async handleGenerateRequest( - exportTypeId: string, - jobParams: BaseParams, - counters: Counters - ) { + private getJobParams(): BaseParams { + let jobParamsRison: null | string = null; + const req = this.req; + const res = this.res; + + if (req.body) { + const { jobParams: jobParamsPayload } = req.body; + jobParamsRison = jobParamsPayload ? jobParamsPayload : null; + } else if (req.query?.jobParams) { + const { jobParams: queryJobParams } = req.query; + if (queryJobParams) { + jobParamsRison = queryJobParams; + } else { + jobParamsRison = null; + } + } + + if (!jobParamsRison) { + throw res.customError({ + statusCode: 400, + body: 'A jobParams RISON string is required in the querystring or POST body', + }); + } + + let jobParams; + + try { + jobParams = rison.decode(jobParamsRison) as BaseParams | null; + if (!jobParams) { + throw res.customError({ + statusCode: 400, + body: 'Missing jobParams!', + }); + } + } catch (err) { + throw res.customError({ + statusCode: 400, + body: `invalid rison: ${jobParamsRison}`, + }); + } + + return jobParams; + } + + public static getValidation() { + return validation; + } + + public async handleGenerateRequest(path: string, exportTypeId: string) { + const req = this.req; + const reporting = this.reporting; + + const counters = getCounters( + req.route.method, + path.replace(/{exportType}/, req.params.exportType), + reporting.getUsageCounter() + ); + // ensure the async dependencies are loaded if (!this.context.reporting) { return handleUnavailable(this.res); @@ -126,6 +191,7 @@ export class RequestHandler { return this.res.forbidden({ body: licenseResults.message }); } + const jobParams = this.getJobParams(); if (jobParams.browserTimezone && !moment.tz.zone(jobParams.browserTimezone)) { return this.res.badRequest({ body: `Invalid timezone "${jobParams.browserTimezone ?? ''}".`, diff --git a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts index 3eef92bcc22bf..df5509f458e4e 100644 --- a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts @@ -5,105 +5,39 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; import type { Logger } from '@kbn/core/server'; -import rison from '@kbn/rison'; import type { ReportingCore } from '../..'; import { PUBLIC_ROUTES } from '../../../common/constants'; -import type { BaseParams } from '../../types'; -import { authorizedUserPreRouting, getCounters, RequestHandler } from '../lib'; +import { authorizedUserPreRouting, RequestHandler } from '../lib'; export function registerGenerationRoutes(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; - // TODO: find a way to abstract this using ExportTypeRegistry: it needs a new - // public method to return this array - // const registry = reporting.getExportTypesRegistry(); - // const kibanaAccessControlTags = registry.getAllAccessControlTags(); const useKibanaAccessControl = reporting.getDeprecatedAllowedRoles() === false; // true if Reporting's deprecated access control feature is disabled const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; - const registerPostGenerationEndpoint = () => { - const path = `${PUBLIC_ROUTES.GENERATE_PREFIX}/{exportType}`; - router.post( - { - path, - validate: { - params: schema.object({ exportType: schema.string({ minLength: 2 }) }), - body: schema.nullable(schema.object({ jobParams: schema.maybe(schema.string()) })), - query: schema.nullable(schema.object({ jobParams: schema.string({ defaultValue: '' }) })), - }, - options: { tags: kibanaAccessControlTags }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters( - req.route.method, - path.replace(/{exportType}/, req.params.exportType), - reporting.getUsageCounter() - ); - - let jobParamsRison: null | string = null; - - if (req.body) { - const { jobParams: jobParamsPayload } = req.body; - jobParamsRison = jobParamsPayload ? jobParamsPayload : null; - } else if (req.query?.jobParams) { - const { jobParams: queryJobParams } = req.query; - if (queryJobParams) { - jobParamsRison = queryJobParams; - } else { - jobParamsRison = null; - } - } - - if (!jobParamsRison) { - return res.customError({ - statusCode: 400, - body: 'A jobParams RISON string is required in the querystring or POST body', - }); - } - - let jobParams; - - try { - jobParams = rison.decode(jobParamsRison) as BaseParams | null; - if (!jobParams) { - return res.customError({ - statusCode: 400, - body: 'Missing jobParams!', - }); - } - } catch (err) { - return res.customError({ - statusCode: 400, - body: `invalid rison: ${jobParamsRison}`, - }); - } - - const requestHandler = new RequestHandler(reporting, user, context, req, res, logger); - return await requestHandler.handleGenerateRequest( - req.params.exportType, - jobParams, - counters - ); - }) - ); - }; - - const registerGetGenerationEndpoint = () => { - // Get route to generation endpoint: show error about GET method to user - router.get( - { - path: `${PUBLIC_ROUTES.GENERATE_PREFIX}/{p*}`, - validate: false, - }, - (_context, _req, res) => { - return res.customError({ statusCode: 405, body: 'GET is not allowed' }); - } - ); - }; - - registerPostGenerationEndpoint(); - registerGetGenerationEndpoint(); + const path = `${PUBLIC_ROUTES.GENERATE_PREFIX}/{exportType}`; + router.post( + { + path, + validate: RequestHandler.getValidation(), + options: { tags: kibanaAccessControlTags }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const requestHandler = new RequestHandler(reporting, user, context, req, res, logger); + return await requestHandler.handleGenerateRequest(path, req.params.exportType); + }) + ); + + // Get route to generation endpoint: show error about GET method to user + router.get( + { + path: `${PUBLIC_ROUTES.GENERATE_PREFIX}/{p*}`, + validate: false, + }, + (_context, _req, res) => { + return res.customError({ statusCode: 405, body: 'GET is not allowed' }); + } + ); } From 24ffa4d66186a8fd7d4a36141a673d76de3d2809 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 24 Jul 2023 13:11:37 -0700 Subject: [PATCH 22/47] --wip-- [skip ci] --- .../reporting/server/routes/common/index.ts | 9 ----- .../server/routes/lib/request_handler.test.ts | 37 +++++++------------ 2 files changed, 14 insertions(+), 32 deletions(-) delete mode 100644 x-pack/plugins/reporting/server/routes/common/index.ts diff --git a/x-pack/plugins/reporting/server/routes/common/index.ts b/x-pack/plugins/reporting/server/routes/common/index.ts deleted file mode 100644 index 455c98d636d50..0000000000000 --- a/x-pack/plugins/reporting/server/routes/common/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { generate } from './generate'; -export { jobs } from './jobs'; diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts b/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts index 69fb16831d557..75cbbd5aa27b4 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts +++ b/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts @@ -26,11 +26,15 @@ const getMockContext = () => core: coreMock.createRequestHandlerContext(), } as unknown as ReportingRequestHandlerContext); -const getMockRequest = () => - ({ - url: { port: '5601', search: '', pathname: '/foo' }, - route: { path: '/foo', options: {} }, - } as KibanaRequest); +const getMockRequest = (): KibanaRequest< + Readonly<{} & { exportType: string }>, + Readonly<{} & { jobParams: string }> | null, + Readonly<{ jobParams?: string | undefined } & {}> | null, + any +> => ({ + url: { port: '5601', search: '', pathname: '/foo' }, + route: { path: '/foo', options: {} }, +}); const getMockResponseFactory = () => ({ @@ -49,11 +53,6 @@ const mockJobParams: JobParamsPDFDeprecated = { relativeUrls: [], }; -const mockCounters = { - usageCounter: jest.fn(), - errorCounter: jest.fn(), -}; - describe('Handle request to generate', () => { let reportingCore: ReportingCore; let mockContext: ReturnType; @@ -153,7 +152,7 @@ describe('Handle request to generate', () => { }); test('disallows invalid export type', async () => { - expect(await requestHandler.handleGenerateRequest('neanderthals', mockJobParams, mockCounters)) + expect(await requestHandler.handleGenerateRequest('/api/reporting/generate', 'neanderthals')) .toMatchInlineSnapshot(` Object { "body": "Invalid export-type of neanderthals", @@ -170,7 +169,7 @@ describe('Handle request to generate', () => { })); expect( - await requestHandler.handleGenerateRequest('csv_searchsource', mockJobParams, mockCounters) + await requestHandler.handleGenerateRequest('/api/reporting/generate', 'csv_searchsource') ).toMatchInlineSnapshot(` Object { "body": "seeing this means the license isn't supported", @@ -187,14 +186,7 @@ describe('Handle request to generate', () => { })); expect( - await requestHandler.handleGenerateRequest( - 'csv_searchsource', - { - ...mockJobParams, - browserTimezone: 'America/Amsterdam', - }, - mockCounters - ) + await requestHandler.handleGenerateRequest('/api/reporting/generate', 'csv_searchsource') ).toMatchInlineSnapshot(` Object { "body": "seeing this means the license isn't supported", @@ -204,9 +196,8 @@ describe('Handle request to generate', () => { test('generates the download path', async () => { const response = (await requestHandler.handleGenerateRequest( - 'csv_searchsource', - mockJobParams, - mockCounters + '/api/reporting/generate', + 'csv_searchsource' )) as unknown as { body: { job: ReportApiJSON } }; const { id, created_at: _created_at, ...snapObj } = response.body.job; expect(snapObj).toMatchInlineSnapshot(` From 8c9dda064c6d4e56487d6bcc563d39c768a083bd Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 25 Jul 2023 17:39:12 -0700 Subject: [PATCH 23/47] reorganize routes libs --- .../reporting/common/constants/index.ts | 9 ++-- .../authorized_user_pre_routing.test.ts | 0 .../authorized_user_pre_routing.ts | 0 .../management => common/generate}/index.ts | 2 +- .../generate}/request_handler.test.ts | 51 ++++++++++--------- .../generate}/request_handler.ts | 20 ++++---- .../routes/{lib => common}/get_counter.ts | 0 .../server/routes/{lib => common}/get_user.ts | 0 .../reporting/server/routes/common/index.ts | 9 ++++ .../jobs}/get_document_payload.test.ts | 12 ++--- .../jobs}/get_document_payload.ts | 10 ++-- .../routes/{lib => common/jobs}/index.ts | 8 +-- .../jobs}/job_management_pre_routing.test.ts | 8 +-- .../jobs}/job_management_pre_routing.ts | 10 ++-- .../{lib => common/jobs}/jobs_query.test.ts | 7 ++- .../routes/{lib => common/jobs}/jobs_query.ts | 14 ++--- .../plugins/reporting/server/routes/index.ts | 28 +++++----- .../internal/deprecations/deprecations.ts | 6 +-- .../integration_tests/deprecations.test.ts | 6 +-- .../routes/internal/diagnostic/browser.ts | 2 +- .../integration_tests/browser.test.ts | 2 +- .../routes/internal/diagnostic/screenshot.ts | 2 +- .../generate/csv_searchsource_immediate.ts | 2 +- .../generate/generate_from_jobparams.ts | 12 +++-- .../management/integration_tests/jobs.test.ts | 30 +++++------ .../server/routes/internal/management/jobs.ts | 14 ++--- .../routes/public/generate_from_jobparams.ts | 12 +++-- .../generation_from_jobparams.test.ts | 19 +++---- .../public/integration_tests/jobs.test.ts | 10 ++-- .../reporting/server/routes/public/jobs.ts | 14 ++--- .../reporting_and_security/error_codes.ts | 2 +- .../ilm_migration_apis.ts | 3 +- .../usage/api_counters.ts | 2 +- .../job_apis_csv.ts | 2 +- .../services/scenarios.ts | 2 +- .../services/usage.ts | 2 +- 36 files changed, 168 insertions(+), 164 deletions(-) rename x-pack/plugins/reporting/server/routes/{lib => common}/authorized_user_pre_routing.test.ts (100%) rename x-pack/plugins/reporting/server/routes/{lib => common}/authorized_user_pre_routing.ts (100%) rename x-pack/plugins/reporting/server/routes/{internal/management => common/generate}/index.ts (78%) rename x-pack/plugins/reporting/server/routes/{lib => common/generate}/request_handler.test.ts (86%) rename x-pack/plugins/reporting/server/routes/{lib => common/generate}/request_handler.ts (93%) rename x-pack/plugins/reporting/server/routes/{lib => common}/get_counter.ts (100%) rename x-pack/plugins/reporting/server/routes/{lib => common}/get_user.ts (100%) create mode 100644 x-pack/plugins/reporting/server/routes/common/index.ts rename x-pack/plugins/reporting/server/routes/{lib => common/jobs}/get_document_payload.test.ts (96%) rename x-pack/plugins/reporting/server/routes/{lib => common/jobs}/get_document_payload.ts (93%) rename x-pack/plugins/reporting/server/routes/{lib => common/jobs}/index.ts (61%) rename x-pack/plugins/reporting/server/routes/{lib => common/jobs}/job_management_pre_routing.test.ts (97%) rename x-pack/plugins/reporting/server/routes/{lib => common/jobs}/job_management_pre_routing.ts (90%) rename x-pack/plugins/reporting/server/routes/{lib => common/jobs}/jobs_query.test.ts (99%) rename x-pack/plugins/reporting/server/routes/{lib => common/jobs}/jobs_query.ts (93%) diff --git a/x-pack/plugins/reporting/common/constants/index.ts b/x-pack/plugins/reporting/common/constants/index.ts index b6479074ecd07..f02ea766da66b 100644 --- a/x-pack/plugins/reporting/common/constants/index.ts +++ b/x-pack/plugins/reporting/common/constants/index.ts @@ -28,11 +28,6 @@ export const ALLOWED_JOB_CONTENT_TYPES = [ 'text/plain', ]; -// Re-export type definitions here for convenience. -export * from './job_types'; -export * from './report_types'; -export * from './routes'; - type ReportTypeDeclaration = typeof reportTypes; export type ReportTypes = ReportTypeDeclaration[keyof ReportTypeDeclaration]; @@ -101,3 +96,7 @@ export const REPORT_TABLE_ROW_ID = 'reportJobRow'; // automation that have no version value in the job params, we assume the // intended version is 7.14.0 export const UNVERSIONED_VERSION = '7.14.0'; + +export * from './job_types'; +export * from './report_types'; +export * from './routes'; diff --git a/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts b/x-pack/plugins/reporting/server/routes/common/authorized_user_pre_routing.test.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts rename to x-pack/plugins/reporting/server/routes/common/authorized_user_pre_routing.test.ts diff --git a/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts b/x-pack/plugins/reporting/server/routes/common/authorized_user_pre_routing.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts rename to x-pack/plugins/reporting/server/routes/common/authorized_user_pre_routing.ts diff --git a/x-pack/plugins/reporting/server/routes/internal/management/index.ts b/x-pack/plugins/reporting/server/routes/common/generate/index.ts similarity index 78% rename from x-pack/plugins/reporting/server/routes/internal/management/index.ts rename to x-pack/plugins/reporting/server/routes/common/generate/index.ts index f3f8ef5f3be02..a16ddf1204b8f 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/index.ts +++ b/x-pack/plugins/reporting/server/routes/common/generate/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { registerJobRoutes } from './jobs'; +export { handleUnavailable, RequestHandler } from './request_handler'; diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts b/x-pack/plugins/reporting/server/routes/common/generate/request_handler.test.ts similarity index 86% rename from x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts rename to x-pack/plugins/reporting/server/routes/common/generate/request_handler.test.ts index 35ed85af862d4..fe1f6b38cc79d 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts +++ b/x-pack/plugins/reporting/server/routes/common/generate/request_handler.test.ts @@ -7,16 +7,16 @@ import { KibanaRequest, KibanaResponseFactory } from '@kbn/core/server'; import { coreMock, httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; -import { TaskPayloadPDFV2 } from '../../../common/types/export_types/printable_pdf_v2'; -import { ReportingCore } from '../..'; -import { JobParamsPDFDeprecated } from '../../export_types/printable_pdf/types'; -import { Report, ReportingStore } from '../../lib/store'; -import { ReportApiJSON } from '../../lib/store/report'; -import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; -import { ReportingRequestHandlerContext, ReportingSetup } from '../../types'; +import { ReportingCore } from '../../..'; +import { TaskPayloadPDFV2 } from '../../../../common/types/export_types/printable_pdf_v2'; +import { JobParamsPDFDeprecated } from '../../../export_types/printable_pdf/types'; +import { Report, ReportingStore } from '../../../lib/store'; +import { ReportApiJSON } from '../../../lib/store/report'; +import { createMockConfigSchema, createMockReportingCore } from '../../../test_helpers'; +import { ReportingRequestHandlerContext, ReportingSetup } from '../../../types'; import { RequestHandler } from './request_handler'; -jest.mock('../../lib/crypto', () => ({ +jest.mock('../../../lib/crypto', () => ({ cryptoFactory: () => ({ encrypt: () => `hello mock cypher text`, }), @@ -27,15 +27,15 @@ const getMockContext = () => core: coreMock.createRequestHandlerContext(), } as unknown as ReportingRequestHandlerContext); -const getMockRequest = (): KibanaRequest< - Readonly<{} & { exportType: string }>, - Readonly<{} & { jobParams: string }> | null, - Readonly<{ jobParams?: string | undefined } & {}> | null, - any -> => ({ - url: { port: '5601', search: '', pathname: '/foo' }, - route: { path: '/foo', options: {} }, -}); +const getMockRequest = () => + ({ + url: { port: '5601', search: '', pathname: '/foo' }, + route: { path: '/foo', options: {} }, + } as KibanaRequest< + { exportType: string }, + { jobParams: string } | null, + { jobParams: string } | null + >); const getMockResponseFactory = () => ({ @@ -86,6 +86,7 @@ describe('Handle request to generate', () => { reportingCore, { username: 'testymcgee' }, mockContext, + '/api/reporting/test/generate/pdf', mockRequest, mockResponseFactory, mockLogger @@ -155,7 +156,7 @@ describe('Handle request to generate', () => { }); test('disallows invalid export type', async () => { - expect(await requestHandler.handleGenerateRequest('/api/reporting/generate', 'neanderthals')) + expect(await requestHandler.handleGenerateRequest('neanderthals', mockJobParams)) .toMatchInlineSnapshot(` Object { "body": "Invalid export-type of neanderthals", @@ -171,9 +172,8 @@ describe('Handle request to generate', () => { }, })); - expect( - await requestHandler.handleGenerateRequest('/api/reporting/generate', 'csv_searchsource') - ).toMatchInlineSnapshot(` + expect(await requestHandler.handleGenerateRequest('csv_searchsource', mockJobParams)) + .toMatchInlineSnapshot(` Object { "body": "seeing this means the license isn't supported", } @@ -189,7 +189,10 @@ describe('Handle request to generate', () => { })); expect( - await requestHandler.handleGenerateRequest('/api/reporting/generate', 'csv_searchsource') + await requestHandler.handleGenerateRequest('csv_searchsource', { + ...mockJobParams, + browserTimezone: 'America/Amsterdam', + }) ).toMatchInlineSnapshot(` Object { "body": "seeing this means the license isn't supported", @@ -199,8 +202,8 @@ describe('Handle request to generate', () => { test('generates the download path', async () => { const response = (await requestHandler.handleGenerateRequest( - '/api/reporting/generate', - 'csv_searchsource' + 'csv_searchsource', + mockJobParams )) as unknown as { body: { job: ReportApiJSON } }; const { id, created_at: _created_at, ...snapObj } = response.body.job; expect(snapObj).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts b/x-pack/plugins/reporting/server/routes/common/generate/request_handler.ts similarity index 93% rename from x-pack/plugins/reporting/server/routes/lib/request_handler.ts rename to x-pack/plugins/reporting/server/routes/common/generate/request_handler.ts index 40c4be90b02ff..5143c8abd4201 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts +++ b/x-pack/plugins/reporting/server/routes/common/generate/request_handler.ts @@ -11,12 +11,12 @@ import type { KibanaRequest, KibanaResponseFactory, Logger } from '@kbn/core/ser import { i18n } from '@kbn/i18n'; import rison from '@kbn/rison'; import moment from 'moment'; -import type { ReportingCore } from '../..'; -import { PUBLIC_ROUTES } from '../../../common/constants'; -import { checkParamsVersion, cryptoFactory } from '../../lib'; -import { Report } from '../../lib/store'; -import type { BaseParams, ReportingRequestHandlerContext, ReportingUser } from '../../types'; -import { Counters, getCounters } from './get_counter'; +import { Counters, getCounters } from '..'; +import type { ReportingCore } from '../../..'; +import { PUBLIC_ROUTES } from '../../../../common/constants'; +import { checkParamsVersion, cryptoFactory } from '../../../lib'; +import { Report } from '../../../lib/store'; +import type { BaseParams, ReportingRequestHandlerContext, ReportingUser } from '../../../types'; export const handleUnavailable = (res: KibanaResponseFactory) => { return res.custom({ statusCode: 503, body: 'Not Available' }); @@ -36,6 +36,7 @@ export class RequestHandler { private reporting: ReportingCore, private user: ReportingUser, private context: ReportingRequestHandlerContext, + private path: string, private req: KibanaRequest< TypeOf, TypeOf, @@ -113,7 +114,7 @@ export class RequestHandler { return report; } - private getJobParams(): BaseParams { + public getJobParams(): BaseParams { let jobParamsRison: null | string = null; const req = this.req; const res = this.res; @@ -161,13 +162,13 @@ export class RequestHandler { return validation; } - public async handleGenerateRequest(path: string, exportTypeId: string) { + public async handleGenerateRequest(exportTypeId: string, jobParams: BaseParams) { const req = this.req; const reporting = this.reporting; const counters = getCounters( req.route.method, - path.replace(/{exportType}/, req.params.exportType), + this.path.replace(/{exportType}/, exportTypeId), reporting.getUsageCounter() ); @@ -187,7 +188,6 @@ export class RequestHandler { return this.res.forbidden({ body: licenseResults.message }); } - const jobParams = this.getJobParams(); if (jobParams.browserTimezone && !moment.tz.zone(jobParams.browserTimezone)) { return this.res.badRequest({ body: `Invalid timezone "${jobParams.browserTimezone ?? ''}".`, diff --git a/x-pack/plugins/reporting/server/routes/lib/get_counter.ts b/x-pack/plugins/reporting/server/routes/common/get_counter.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/lib/get_counter.ts rename to x-pack/plugins/reporting/server/routes/common/get_counter.ts diff --git a/x-pack/plugins/reporting/server/routes/lib/get_user.ts b/x-pack/plugins/reporting/server/routes/common/get_user.ts similarity index 100% rename from x-pack/plugins/reporting/server/routes/lib/get_user.ts rename to x-pack/plugins/reporting/server/routes/common/get_user.ts diff --git a/x-pack/plugins/reporting/server/routes/common/index.ts b/x-pack/plugins/reporting/server/routes/common/index.ts new file mode 100644 index 0000000000000..cd5341a3ae143 --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/common/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { authorizedUserPreRouting } from './authorized_user_pre_routing'; +export { type Counters, getCounters } from './get_counter'; diff --git a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.test.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.test.ts similarity index 96% rename from x-pack/plugins/reporting/server/routes/lib/get_document_payload.test.ts rename to x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.test.ts index b8091e09027a2..8c308de4544a1 100644 --- a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.test.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.test.ts @@ -6,14 +6,14 @@ */ import { Readable } from 'stream'; -import { CSV_JOB_TYPE, PDF_JOB_TYPE, PDF_JOB_TYPE_V2 } from '../../../common/constants'; -import { ReportApiJSON } from '../../../common/types'; -import { ContentStream, getContentStream, statuses } from '../../lib'; -import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; -import { jobsQueryFactory } from './jobs_query'; +import { CSV_JOB_TYPE, PDF_JOB_TYPE, PDF_JOB_TYPE_V2 } from '../../../../common/constants'; +import { ReportApiJSON } from '../../../../common/types'; +import { ContentStream, getContentStream, statuses } from '../../../lib'; +import { createMockConfigSchema, createMockReportingCore } from '../../../test_helpers'; import { getDocumentPayloadFactory } from './get_document_payload'; +import { jobsQueryFactory } from './jobs_query'; -jest.mock('../../lib/content_stream'); +jest.mock('../../../lib/content_stream'); jest.mock('./jobs_query'); describe('getDocumentPayload', () => { diff --git a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.ts similarity index 93% rename from x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts rename to x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.ts index 158d42b6e94e3..92fac37bb26f2 100644 --- a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.ts @@ -7,11 +7,11 @@ import { ResponseHeaders } from '@kbn/core-http-server'; import { Stream } from 'stream'; -import { ReportingCore } from '../..'; -import { CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED } from '../../../common/constants'; -import { ReportApiJSON } from '../../../common/types'; -import { ExportType } from '../../export_types/common'; -import { getContentStream, statuses } from '../../lib'; +import { ReportingCore } from '../../..'; +import { CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED } from '../../../../common/constants'; +import { ReportApiJSON } from '../../../../common/types'; +import { ExportType } from '../../../export_types/common'; +import { getContentStream, statuses } from '../../../lib'; import { jobsQueryFactory } from './jobs_query'; export interface ErrorFromPayload { diff --git a/x-pack/plugins/reporting/server/routes/lib/index.ts b/x-pack/plugins/reporting/server/routes/common/jobs/index.ts similarity index 61% rename from x-pack/plugins/reporting/server/routes/lib/index.ts rename to x-pack/plugins/reporting/server/routes/common/jobs/index.ts index 50f5653f894ff..36e661f3686e0 100644 --- a/x-pack/plugins/reporting/server/routes/lib/index.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/index.ts @@ -5,11 +5,5 @@ * 2.0. */ -export { authorizedUserPreRouting } from './authorized_user_pre_routing'; -export { jobManagementPreRouting } from './job_management_pre_routing'; - -export { getCounters } from './get_counter'; -export type { Counters } from './get_counter'; - -export { handleUnavailable, RequestHandler } from './request_handler'; export { jobsQueryFactory } from './jobs_query'; +export { jobManagementPreRouting } from './job_management_pre_routing'; diff --git a/x-pack/plugins/reporting/server/routes/lib/job_management_pre_routing.test.ts b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.test.ts similarity index 97% rename from x-pack/plugins/reporting/server/routes/lib/job_management_pre_routing.test.ts rename to x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.test.ts index 3e3bebbc614b1..3d4a182d0d743 100644 --- a/x-pack/plugins/reporting/server/routes/lib/job_management_pre_routing.test.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.test.ts @@ -6,18 +6,18 @@ */ import { httpServerMock } from '@kbn/core/server/mocks'; -import { ReportingCore } from '../..'; -import { ReportingInternalSetup, ReportingInternalStart } from '../../core'; +import { ReportingCore } from '../../..'; +import { ReportingInternalSetup, ReportingInternalStart } from '../../../core'; import { createMockConfigSchema, createMockPluginSetup, createMockPluginStart, createMockReportingCore, -} from '../../test_helpers'; +} from '../../../test_helpers'; import { jobsQueryFactory } from './jobs_query'; import { jobManagementPreRouting } from './job_management_pre_routing'; -jest.mock('../../lib/content_stream'); +jest.mock('../../../lib/content_stream'); jest.mock('./jobs_query'); const mockReportingConfig = createMockConfigSchema({ roles: { enabled: false } }); diff --git a/x-pack/plugins/reporting/server/routes/lib/job_management_pre_routing.ts b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts similarity index 90% rename from x-pack/plugins/reporting/server/routes/lib/job_management_pre_routing.ts rename to x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts index a443b6fc05bb3..a7cea25884ef1 100644 --- a/x-pack/plugins/reporting/server/routes/lib/job_management_pre_routing.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts @@ -8,11 +8,11 @@ import Boom from '@hapi/boom'; import { IKibanaResponse, kibanaResponseFactory } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; -import { jobsQueryFactory } from '.'; -import { ReportingCore } from '../..'; -import { ReportApiJSON } from '../../lib/store/report'; -import { ReportingUser } from '../../types'; -import type { Counters } from './get_counter'; +import { Counters } from '..'; +import { ReportingCore } from '../../..'; +import { ReportApiJSON } from '../../../lib/store/report'; +import { ReportingUser } from '../../../types'; +import { jobsQueryFactory } from './jobs_query'; /** * The body of a route handler to call via callback diff --git a/x-pack/plugins/reporting/server/routes/lib/jobs_query.test.ts b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.test.ts similarity index 99% rename from x-pack/plugins/reporting/server/routes/lib/jobs_query.test.ts rename to x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.test.ts index 202fa55049b3d..f0c656da2050a 100644 --- a/x-pack/plugins/reporting/server/routes/lib/jobs_query.test.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.test.ts @@ -5,12 +5,11 @@ * 2.0. */ -import { set } from '@kbn/safer-lodash-set'; import { ElasticsearchClient } from '@kbn/core/server'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; -import { statuses } from '../../lib'; -import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; - +import { set } from '@kbn/safer-lodash-set'; +import { statuses } from '../../../lib'; +import { createMockConfigSchema, createMockReportingCore } from '../../../test_helpers'; import { jobsQueryFactory } from './jobs_query'; describe('jobsQuery', () => { diff --git a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts similarity index 93% rename from x-pack/plugins/reporting/server/routes/lib/jobs_query.ts rename to x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts index ff01c0e635e9e..87bf38648bd2f 100644 --- a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts @@ -8,13 +8,13 @@ import { estypes, errors, TransportResult } from '@elastic/elasticsearch'; import type { ElasticsearchClient } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; -import type { ReportingCore } from '../..'; -import { REPORTING_SYSTEM_INDEX } from '../../../common/constants'; -import type { ReportApiJSON, ReportSource } from '../../../common/types'; -import { statuses } from '../../lib/statuses'; -import { Report } from '../../lib/store'; -import { runtimeFieldKeys, runtimeFields } from '../../lib/store/runtime_fields'; -import type { ReportingUser } from '../../types'; +import type { ReportingCore } from '../../..'; +import { REPORTING_SYSTEM_INDEX } from '../../../../common/constants'; +import type { ReportApiJSON, ReportSource } from '../../../../common/types'; +import { statuses } from '../../../lib/statuses'; +import { Report } from '../../../lib/store'; +import { runtimeFieldKeys, runtimeFields } from '../../../lib/store/runtime_fields'; +import type { ReportingUser } from '../../../types'; import type { Payload } from './get_document_payload'; import { getDocumentPayloadFactory } from './get_document_payload'; diff --git a/x-pack/plugins/reporting/server/routes/index.ts b/x-pack/plugins/reporting/server/routes/index.ts index 12d10f7c8e9e5..f0ce70e00d6bd 100644 --- a/x-pack/plugins/reporting/server/routes/index.ts +++ b/x-pack/plugins/reporting/server/routes/index.ts @@ -7,20 +7,20 @@ import type { Logger } from '@kbn/core/server'; import { ReportingCore } from '..'; -import { registerDeprecationRoutes as internalDeprecationRoutes } from './internal/deprecations/deprecations'; -import { registerDiagnosticRoutes as internalDiagnosticsRoutes } from './internal/diagnostic'; -import { registerGenerateCsvFromSavedObjectImmediate as internalCsvFromSavedObjectImmediate } from './internal/generate/csv_searchsource_immediate'; -import { registerGeneration as internalGenerationRoutes } from './internal/generate/generate_from_jobparams'; -import { registerJobRoutes as internalJobRoutes } from './internal/management'; -import { registerGenerationRoutes as publicGenerationRoutes } from './public/generate_from_jobparams'; -import { registerJobRoutes as publicJobRoutes } from './public/jobs'; +import { registerDeprecationsRoutes } from './internal/deprecations/deprecations'; +import { registerDiagnosticRoutes } from './internal/diagnostic'; +import { registerGenerateCsvFromSavedObjectImmediate } from './internal/generate/csv_searchsource_immediate'; +import { registerGenerationRoutesInternal } from './internal/generate/generate_from_jobparams'; +import { registerJobInfoRoutesInternal } from './internal/management/jobs'; +import { registerGenerationRoutesPublic } from './public/generate_from_jobparams'; +import { registerJobInfoRoutesPublic } from './public/jobs'; export function registerRoutes(reporting: ReportingCore, logger: Logger) { - internalDeprecationRoutes(reporting, logger); - internalDiagnosticsRoutes(reporting, logger); - internalCsvFromSavedObjectImmediate(reporting, logger); - internalGenerationRoutes(reporting, logger); - internalJobRoutes(reporting); - publicGenerationRoutes(reporting, logger); - publicJobRoutes(reporting); + registerDeprecationsRoutes(reporting, logger); + registerDiagnosticRoutes(reporting, logger); + registerGenerateCsvFromSavedObjectImmediate(reporting, logger); + registerGenerationRoutesInternal(reporting, logger); + registerJobInfoRoutesInternal(reporting); + registerGenerationRoutesPublic(reporting, logger); + registerJobInfoRoutesPublic(reporting); } diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts index b3b4e66dcb560..a4b217166d1a5 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts @@ -6,12 +6,12 @@ */ import { errors } from '@elastic/elasticsearch'; import type { Logger, RequestHandler } from '@kbn/core/server'; -import { INTERNAL_ROUTES, ILM_POLICY_NAME } from '../../../../common/constants'; +import { ILM_POLICY_NAME, INTERNAL_ROUTES } from '../../../../common/constants'; import type { IlmPolicyStatusResponse } from '../../../../common/types'; import type { ReportingCore } from '../../../core'; import { IlmPolicyManager } from '../../../lib'; import { deprecations } from '../../../lib/deprecations'; -import { getCounters } from '../../lib'; +import { getCounters } from '../../common'; const getAuthzWrapper = (reporting: ReportingCore, logger: Logger) => @@ -51,7 +51,7 @@ const getAuthzWrapper = }; }; -export const registerDeprecationRoutes = (reporting: ReportingCore, logger: Logger) => { +export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Logger) => { const { router } = reporting.getPluginSetupDeps(); const authzWrapper = getAuthzWrapper(reporting, logger); diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts index f40dd6d5ce6ae..1e655d80adfc6 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts @@ -17,7 +17,7 @@ import { createMockPluginStart, createMockReportingCore, } from '../../../../test_helpers'; -import { registerDeprecationRoutes } from '../deprecations'; +import { registerDeprecationsRoutes } from '../deprecations'; type SetupServerReturn = Awaited>; @@ -53,7 +53,7 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { it('correctly handles authz when security is unavailable', async () => { const core = await createReportingCore({}); - registerDeprecationRoutes(core, loggingSystemMock.createLogger()); + registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); await server.start(); await supertest(httpSetup.server.listener) @@ -67,7 +67,7 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { security.license.isEnabled.mockReturnValue(false); const core = await createReportingCore({ security }); - registerDeprecationRoutes(core, loggingSystemMock.createLogger()); + registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); await server.start(); await supertest(httpSetup.server.listener) diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts index cdb7f405dd993..edd3302237cd7 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts @@ -11,7 +11,7 @@ import { lastValueFrom } from 'rxjs'; import type { DiagnosticResponse } from '.'; import type { ReportingCore } from '../../..'; import { INTERNAL_ROUTES } from '../../../../common/constants'; -import { authorizedUserPreRouting, getCounters } from '../../lib'; +import { authorizedUserPreRouting, getCounters } from '../../common'; const logsToHelpMapFactory = (docLinks: DocLinksServiceSetup) => ({ 'error while loading shared libraries': i18n.translate( diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts index 24e9f50db907d..5ef2bed77cc61 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts @@ -12,6 +12,7 @@ import * as Rx from 'rxjs'; import supertest from 'supertest'; import { ReportingCore } from '../../../..'; import { INTERNAL_ROUTES } from '../../../../../common/constants'; +import { reportingMock } from '../../../../mocks'; import { createMockConfigSchema, createMockPluginSetup, @@ -19,7 +20,6 @@ import { } from '../../../../test_helpers'; import type { ReportingRequestHandlerContext } from '../../../../types'; import { registerDiagnoseBrowser } from '../browser'; -import { reportingMock } from '../../../mocks'; type SetupServerReturn = Awaited>; diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts index 654bdaa1ae9d7..c1be4e9dda720 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts @@ -13,7 +13,7 @@ import type { ReportingCore } from '../../..'; import { INTERNAL_ROUTES } from '../../../../common/constants'; import { generatePngObservable } from '../../../export_types/common'; import { getAbsoluteUrlFactory } from '../../../export_types/common/get_absolute_url'; -import { authorizedUserPreRouting, getCounters } from '../../lib'; +import { authorizedUserPreRouting, getCounters } from '../../common'; const path = INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts index 78f4fd234fa30..f437dc7e6ef99 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts @@ -13,7 +13,7 @@ import type { ReportingCore } from '../../..'; import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE, INTERNAL_ROUTES } from '../../../../common/constants'; import type { JobParamsDownloadCSV } from '../../../export_types/csv_searchsource_immediate/types'; import { PassThroughStream } from '../../../lib'; -import { authorizedUserPreRouting, getCounters } from '../../lib'; +import { authorizedUserPreRouting, getCounters } from '../../common'; const path = INTERNAL_ROUTES.GENERATE.CSV_IMMEDIATE; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index 47edf10e00268..901236ed11e0e 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -8,9 +8,10 @@ import type { Logger } from '@kbn/core/server'; import type { ReportingCore } from '../../..'; import { INTERNAL_ROUTES } from '../../../../common/constants'; -import { authorizedUserPreRouting, RequestHandler } from '../../lib'; +import { authorizedUserPreRouting } from '../../common'; +import { RequestHandler } from '../../common/generate'; -export function registerGeneration(reporting: ReportingCore, logger: Logger) { +export function registerGenerationRoutesInternal(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; @@ -29,8 +30,11 @@ export function registerGeneration(reporting: ReportingCore, logger: Logger) { options: { tags: kibanaAccessControlTags }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const requestHandler = new RequestHandler(reporting, user, context, req, res, logger); - return await requestHandler.handleGenerateRequest(path, req.params.exportType); + const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); + return await requestHandler.handleGenerateRequest( + req.params.exportType, + requestHandler.getJobParams() + ); }) ); } diff --git a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts index d04f37d44248b..bb9ba088755ca 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts @@ -27,7 +27,7 @@ import { createMockReportingCore, } from '../../../../test_helpers'; import { ReportingRequestHandlerContext } from '../../../../types'; -import { registerJobRoutes } from '../jobs'; +import { registerJobInfoRoutesInternal as registerJobInfoRoutes } from '../jobs'; type SetupServerReturn = Awaited>; @@ -117,7 +117,7 @@ describe('GET /internal/reporting/jobs/download', () => { it('fails on malformed download IDs', async () => { mockEsClient.search.mockResponseOnce(getHits()); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); @@ -143,7 +143,7 @@ describe('GET /internal/reporting/jobs/download', () => { mockConfigSchema ); core = await createMockReportingCore(mockConfigSchema, mockSetupDeps, mockStartDeps); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); @@ -157,7 +157,7 @@ describe('GET /internal/reporting/jobs/download', () => { it('returns 404 if job not found', async () => { mockEsClient.search.mockResponseOnce(getHits()); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); @@ -173,7 +173,7 @@ describe('GET /internal/reporting/jobs/download', () => { payload: { title: 'invalid!' }, }) ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); @@ -190,7 +190,7 @@ describe('GET /internal/reporting/jobs/download', () => { }) ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); @@ -207,7 +207,7 @@ describe('GET /internal/reporting/jobs/download', () => { }) ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); @@ -224,7 +224,7 @@ describe('GET /internal/reporting/jobs/download', () => { payload: { title: 'incomplete!' }, }) ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -244,7 +244,7 @@ describe('GET /internal/reporting/jobs/download', () => { payload: { title: 'failing job!' }, }) ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -272,7 +272,7 @@ describe('GET /internal/reporting/jobs/download', () => { it('when a known job-type is complete', async () => { mockEsClient.search.mockResponseOnce(getCompleteHits()); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -288,7 +288,7 @@ describe('GET /internal/reporting/jobs/download', () => { // @ts-ignore core.pluginSetupDeps.security = null; - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); @@ -305,7 +305,7 @@ describe('GET /internal/reporting/jobs/download', () => { jobType: 'unencodedJobType', }) ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -322,7 +322,7 @@ describe('GET /internal/reporting/jobs/download', () => { outputContentType: 'application/html', }) ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -344,7 +344,7 @@ describe('GET /internal/reporting/jobs/download', () => { title: '日本語ダッシュボード', }) ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); await supertest(httpSetup.server.listener) @@ -381,7 +381,7 @@ describe('GET /internal/reporting/jobs/download', () => { mockStartDeps ); - registerJobRoutes(core); + registerJobInfoRoutes(core); await server.start(); diff --git a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts index 435ef5b3637cf..3e78f269387b1 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts @@ -11,15 +11,11 @@ import { promisify } from 'util'; import { ReportingCore } from '../../..'; import { ALLOWED_JOB_CONTENT_TYPES, INTERNAL_ROUTES } from '../../../../common/constants'; import { getContentStream } from '../../../lib'; -import { - authorizedUserPreRouting, - getCounters, - handleUnavailable, - jobManagementPreRouting, - jobsQueryFactory, -} from '../../lib'; - -export function registerJobRoutes(reporting: ReportingCore) { +import { authorizedUserPreRouting, getCounters } from '../../common'; +import { handleUnavailable } from '../../common/generate'; +import { jobManagementPreRouting, jobsQueryFactory } from '../../common/jobs'; + +export function registerJobInfoRoutesInternal(reporting: ReportingCore) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; const jobsQuery = jobsQueryFactory(reporting); diff --git a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts index df5509f458e4e..5ef435d2a8ec3 100644 --- a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts @@ -8,9 +8,10 @@ import type { Logger } from '@kbn/core/server'; import type { ReportingCore } from '../..'; import { PUBLIC_ROUTES } from '../../../common/constants'; -import { authorizedUserPreRouting, RequestHandler } from '../lib'; +import { authorizedUserPreRouting } from '../common'; +import { RequestHandler } from '../common/generate'; -export function registerGenerationRoutes(reporting: ReportingCore, logger: Logger) { +export function registerGenerationRoutesPublic(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; @@ -25,8 +26,11 @@ export function registerGenerationRoutes(reporting: ReportingCore, logger: Logge options: { tags: kibanaAccessControlTags }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const requestHandler = new RequestHandler(reporting, user, context, req, res, logger); - return await requestHandler.handleGenerateRequest(path, req.params.exportType); + const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); + return await requestHandler.handleGenerateRequest( + req.params.exportType, + requestHandler.getJobParams() + ); }) ); diff --git a/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts index e8fcb2416da7b..5949d248e1247 100644 --- a/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts @@ -24,13 +24,10 @@ import { createMockReportingCore, } from '../../../test_helpers'; import type { ReportingRequestHandlerContext } from '../../../types'; -import { registerGenerationRoutes } from '../generate_from_jobparams'; +import { registerGenerationRoutesPublic as registerJobGenerationRoutes } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; -/** - * Tests the public API endpoints of Reporting - */ describe('POST /api/reporting/generate', () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; @@ -105,7 +102,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if there are no job params', async () => { - registerGenerationRoutes(mockReportingCore, mockLogger); + registerJobGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -120,7 +117,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params query is invalid', async () => { - registerGenerationRoutes(mockReportingCore, mockLogger); + registerJobGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -131,7 +128,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params body is invalid', async () => { - registerGenerationRoutes(mockReportingCore, mockLogger); + registerJobGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -143,7 +140,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 export type is invalid', async () => { - registerGenerationRoutes(mockReportingCore, mockLogger); + registerJobGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -157,7 +154,7 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 on invalid browser timezone', async () => { - registerGenerationRoutes(mockReportingCore, mockLogger); + registerJobGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -173,7 +170,7 @@ describe('POST /api/reporting/generate', () => { it('returns 500 if job handler throws an error', async () => { store.addReport = jest.fn().mockRejectedValue('silly'); - registerGenerationRoutes(mockReportingCore, mockLogger); + registerJobGenerationRoutes(mockReportingCore, mockLogger); await server.start(); @@ -184,7 +181,7 @@ describe('POST /api/reporting/generate', () => { }); it(`returns 200 if job handler doesn't error`, async () => { - registerGenerationRoutes(mockReportingCore, mockLogger); + registerJobGenerationRoutes(mockReportingCore, mockLogger); await server.start(); diff --git a/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts index 1d1c6fb3e01f6..61b7de8c49388 100644 --- a/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts @@ -17,14 +17,16 @@ import { Readable } from 'stream'; import supertest from 'supertest'; import { ReportingCore } from '../../..'; import { ReportingInternalSetup, ReportingInternalStart } from '../../../core'; +import { ExportType } from '../../../export_types/common'; import { ContentStream, ExportTypesRegistry, getContentStream } from '../../../lib'; +import { reportingMock } from '../../../mocks'; import { createMockConfigSchema, createMockPluginSetup, createMockPluginStart, createMockReportingCore, } from '../../../test_helpers'; -import { ExportTypeDefinition, ReportingRequestHandlerContext } from '../../../types'; +import { ReportingRequestHandlerContext } from '../../../types'; import { registerJobRoutes } from '../jobs'; type SetupServerReturn = Awaited>; @@ -55,7 +57,7 @@ describe('GET /api/reporting/jobs/download', () => { httpSetup.registerRouteHandlerContext( reportingSymbol, 'reporting', - () => ({ usesUiCapabilities: jest.fn(), registerExportTypes: jest.fn() }) + () => reportingMock.createStart() ); mockSetupDeps = createMockPluginSetup({ @@ -88,14 +90,14 @@ describe('GET /api/reporting/jobs/download', () => { jobType: 'unencodedJobType', jobContentExtension: 'csv', validLicenses: ['basic', 'gold'], - } as ExportTypeDefinition); + } as ExportType); exportTypesRegistry.register({ id: 'base64Encoded', jobType: 'base64EncodedJobType', jobContentEncoding: 'base64', jobContentExtension: 'pdf', validLicenses: ['basic', 'gold'], - } as ExportTypeDefinition); + } as ExportType); core.getExportTypesRegistry = () => exportTypesRegistry; mockEsClient = (await core.getEsClient()).asInternalUser as typeof mockEsClient; diff --git a/x-pack/plugins/reporting/server/routes/public/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts index 23f0674531346..817a33ad432aa 100644 --- a/x-pack/plugins/reporting/server/routes/public/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/public/jobs.ts @@ -11,15 +11,11 @@ import { promisify } from 'util'; import { ReportingCore } from '../..'; import { ALLOWED_JOB_CONTENT_TYPES, PUBLIC_ROUTES } from '../../../common/constants'; import { getContentStream } from '../../lib'; -import { - authorizedUserPreRouting, - getCounters, - handleUnavailable, - jobManagementPreRouting, - jobsQueryFactory, -} from '../lib'; - -export function registerJobRoutes(reporting: ReportingCore) { +import { authorizedUserPreRouting, getCounters } from '../common'; +import { handleUnavailable } from '../common/generate'; +import { jobManagementPreRouting, jobsQueryFactory } from '../common/jobs'; + +export function registerJobInfoRoutesPublic(reporting: ReportingCore) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; const jobsQuery = jobsQueryFactory(reporting); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/error_codes.ts b/x-pack/test/reporting_api_integration/reporting_and_security/error_codes.ts index d60531110e5da..0d83da710ba23 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/error_codes.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/error_codes.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; import { ReportApiJSON } from '@kbn/reporting-plugin/common/types'; import { FtrProviderContext } from '../ftr_provider_context'; diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/ilm_migration_apis.ts b/x-pack/test/reporting_api_integration/reporting_and_security/ilm_migration_apis.ts index 5a39c2fc86979..6f71974364942 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/ilm_migration_apis.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/ilm_migration_apis.ts @@ -6,7 +6,8 @@ */ import expect from '@kbn/expect'; -import { ILM_POLICY_NAME, INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; +import { ILM_POLICY_NAME } from '@kbn/reporting-plugin/common/constants'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts index f35aea31017a7..3e6d6c982833e 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; import { createPdfV2Params, createPngV2Params } from '..'; import { FtrProviderContext } from '../../ftr_provider_context'; import { UsageStatsPayloadTestFriendly } from '../../../api_integration/services/usage_api'; diff --git a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts index b4d9c39f40cc6..30f58524b46f8 100644 --- a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts +++ b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; import { ReportApiJSON } from '@kbn/reporting-plugin/common/types'; import { pick } from 'lodash'; import { FtrProviderContext } from '../ftr_provider_context'; diff --git a/x-pack/test/reporting_api_integration/services/scenarios.ts b/x-pack/test/reporting_api_integration/services/scenarios.ts index 480701ff12ee1..ff6a64c5cf024 100644 --- a/x-pack/test/reporting_api_integration/services/scenarios.ts +++ b/x-pack/test/reporting_api_integration/services/scenarios.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants'; +import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; import { JobParamsCSV } from '@kbn/reporting-plugin/server/export_types/csv_searchsource/types'; import { JobParamsDownloadCSV } from '@kbn/reporting-plugin/server/export_types/csv_searchsource_immediate/types'; import { JobParamsPNGDeprecated } from '@kbn/reporting-plugin/server/export_types/png/types'; diff --git a/x-pack/test/reporting_api_integration/services/usage.ts b/x-pack/test/reporting_api_integration/services/usage.ts index e3da1286dc5d9..ff600735f62ea 100644 --- a/x-pack/test/reporting_api_integration/services/usage.ts +++ b/x-pack/test/reporting_api_integration/services/usage.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { INTERNAL_ROUTES, PUBLIC_ROUTES } from '@kbn/reporting-plugin/common/constants'; +import { INTERNAL_ROUTES, PUBLIC_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; import { indexTimestamp } from '@kbn/reporting-plugin/server/lib/store/index_timestamp'; import { AvailableTotal, From 543af197f0cac6a6aabacaa7a04ae0f61e73dbce Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 11:42:49 -0700 Subject: [PATCH 24/47] unskip API usage counter tests --- .../reporting_and_security/usage/api_counters.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts index 3e6d6c982833e..fa4d8ac5a3e82 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts @@ -19,9 +19,7 @@ export default function ({ getService }: FtrProviderContext) { const usageAPI = getService('usageAPI'); const reportingAPI = getService('reportingAPI'); - // Failing: See https://github.com/elastic/kibana/issues/134517 - // Failing: See https://github.com/elastic/kibana/issues/149942 - describe.skip(`Usage Counters`, () => { + describe(`Usage Counters`, () => { before(async () => { await esArchiver.emptyKibanaIndex(); await reportingAPI.initEcommerce(); @@ -49,7 +47,7 @@ export default function ({ getService }: FtrProviderContext) { const paths = { LIST: INTERNAL_ROUTES.JOBS.LIST, COUNT: INTERNAL_ROUTES.JOBS.COUNT, - INFO: INTERNAL_ROUTES.JOBS.COUNT + '/kraz0qle154g0763b569zz83', + INFO: INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/kraz0qle154g0763b569zz83', ILM: INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS, DIAG_BROWSER: INTERNAL_ROUTES.DIAGNOSE.BROWSER, DIAG_SCREENSHOT: INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT, From 8ce821d5cab8d0bd662bbaf7761946edba6d6f06 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 11:55:25 -0700 Subject: [PATCH 25/47] unskip usage counter tests --- .../usage/api_counters.ts | 210 +++--------------- 1 file changed, 36 insertions(+), 174 deletions(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts index fa4d8ac5a3e82..d308abd132da7 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts @@ -6,14 +6,27 @@ */ import expect from '@kbn/expect'; -import { INTERNAL_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; -import { createPdfV2Params, createPngV2Params } from '..'; +import { INTERNAL_ROUTES, PUBLIC_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; import { FtrProviderContext } from '../../ftr_provider_context'; import { UsageStatsPayloadTestFriendly } from '../../../api_integration/services/usage_api'; +// helpers +const waitOnAggregation = async () => { + await new Promise((resolve) => { + setTimeout(resolve, 8000); + }); +}; + +const getUsageCount = (checkUsage: UsageStatsPayloadTestFriendly, counterName: string): number => { + return ( + checkUsage.stack_stats.kibana.plugins.usage_counters.dailyEvents.find( + (item: any) => item.counterName === counterName + )?.total || 0 + ); +}; + // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { - const log = getService('log'); const supertest = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); const usageAPI = getService('usageAPI'); @@ -32,41 +45,26 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.unload('x-pack/test/functional/es_archives/reporting/archived_reports'); }); - describe('server', function () { - this.tags('skipCloud'); - it('configuration settings of the tests_server', async () => { - const [{ stats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); - const usage = stats.stack_stats.kibana.plugins; - expect(usage.kibana_config_usage['xpack.reporting.capture.maxAttempts']).to.be(1); - expect(usage.kibana_config_usage['xpack.reporting.csv.maxSizeBytes']).to.be(6000); - expect(usage.kibana_config_usage['xpack.reporting.roles.enabled']).to.be(false); - }); - }); - describe('API counters: management', () => { const paths = { LIST: INTERNAL_ROUTES.JOBS.LIST, COUNT: INTERNAL_ROUTES.JOBS.COUNT, - INFO: INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/kraz0qle154g0763b569zz83', - ILM: INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS, - DIAG_BROWSER: INTERNAL_ROUTES.DIAGNOSE.BROWSER, - DIAG_SCREENSHOT: INTERNAL_ROUTES.DIAGNOSE.SCREENSHOT, + INFO: INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/kraz0qle154g0763b569zz83', // required report stored in archived_reports }; let initialStats: UsageStatsPayloadTestFriendly; let stats: UsageStatsPayloadTestFriendly; - const CALL_COUNT = 3; - before('call APIs', async () => { + before(async () => { [{ stats: initialStats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); + // call APIs to increment counters await Promise.all( Object.keys(paths).map(async (key) => { - await Promise.all( - [...Array(CALL_COUNT)].map(() => - supertest.get(paths[key as keyof typeof paths]).auth('test_user', 'changeme') - ) - ); + await supertest + .get(paths[key as keyof typeof paths]) + .auth('test_user', 'changeme') + .expect(200); }) ); @@ -79,12 +77,12 @@ export default function ({ getService }: FtrProviderContext) { it('job listing', async () => { const initialCount = getUsageCount(initialStats, `get ${paths.LIST}`); - expect(getUsageCount(stats, `get ${paths.LIST}`)).to.be(CALL_COUNT + initialCount); + expect(getUsageCount(stats, `get ${paths.LIST}`)).to.be(initialCount + 1); }); it('job count', async () => { const initialCount = getUsageCount(initialStats, `get ${paths.COUNT}`); - expect(getUsageCount(stats, `get ${paths.COUNT}`)).to.be(CALL_COUNT + initialCount); + expect(getUsageCount(stats, `get ${paths.COUNT}`)).to.be(initialCount + 1); }); it('job info', async () => { @@ -94,74 +92,7 @@ export default function ({ getService }: FtrProviderContext) { ); expect( getUsageCount(stats, `get ${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/{docId}:printable_pdf`) - ).to.be(CALL_COUNT + initialCount); - }); - }); - - describe('downloading and deleting', () => { - let initialStats: UsageStatsPayloadTestFriendly; - before('gather initial stats', async () => { - [{ stats: initialStats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); - }); - - it('downloading', async () => { - try { - await Promise.all([ - supertest - .get('/api/reporting/jobs/download/kraz0qle154g0763b569zz83') - .auth('test_user', 'changeme'), - supertest - .get('/api/reporting/jobs/download/kraz0vj4154g0763b5curq51') - .auth('test_user', 'changeme'), - supertest - .get('/api/reporting/jobs/download/k9a9rq1i0gpe1457b17s7yc6') - .auth('test_user', 'changeme'), - ]); - } catch (error) { - log.error(error); - } - - log.info(`waiting on internal stats aggregation...`); - await waitOnAggregation(); - log.info(`waiting on aggregation completed.`); - - log.info(`calling getUsageStats...`); - const [{ stats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); - const initialCount = getUsageCount( - initialStats, - `get /api/reporting/jobs/download/{docId}:printable_pdf` - ); - expect( - getUsageCount(stats, `get /api/reporting/jobs/download/{docId}:printable_pdf`) - ).to.be(3 + initialCount); - }); - - it('deleting', async () => { - log.info(`sending 1 delete request...`); - - try { - await supertest - .delete('/api/reporting/jobs/delete/krazcyw4156m0763b503j7f9') - .auth('test_user', 'changeme') - .set('kbn-xsrf', 'xxx'); - } catch (error) { - log.error(error); - } - log.info(`delete request completed.`); - - log.info(`waiting on internal stats aggregation...`); - await waitOnAggregation(); - log.info(`waiting on aggregation completed.`); - - log.info(`calling getUsageStats...`); - const [{ stats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); - const initialCount = getUsageCount( - initialStats, - `delete /api/reporting/jobs/delete/{docId}:csv_searchsource` - ); - expect( - getUsageCount(stats, `delete /api/reporting/jobs/delete/{docId}:csv_searchsource`) - ).to.be(1 + initialCount); + ).to.be(initialCount + 1); }); }); @@ -170,93 +101,24 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { // call generation APIs - await Promise.all([ - postCsv(), - postCsv(), - postPng(), - postPng(), - postPdf(), - postPdf(), - postPdf(), - downloadCsv(), - ]); - + await reportingAPI.postJobJSON(`${PUBLIC_ROUTES.GENERATE_PREFIX}/csv_searchsource`, { + jobParams: + `(browserTimezone:UTC,` + + `columns:!(order_date,category,customer_full_name,taxful_total_price,currency),objectType:search,searchSource:(fields:!((field:'*',include_unmapped:true))` + + `,filter:!((meta:(field:order_date,index:aac3e500-f2c7-11ea-8250-fb138aa491e7,params:()),query:(range:(order_date:(format:strict_date_optional_time,gte:'2019-06-02T12:28:40.866Z'` + + `,lte:'2019-07-18T20:59:57.136Z'))))),index:aac3e500-f2c7-11ea-8250-fb138aa491e7,parent:(filter:!(),highlightAll:!t,index:aac3e500-f2c7-11ea-8250-fb138aa491e7` + + `,query:(language:kuery,query:''),version:!t),sort:!((order_date:desc)),trackTotalHits:!t)` + + `)`, + }); await waitOnAggregation(); - [{ stats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); }); - it('PNG', async () => { - expect(getUsageCount(stats, 'post /api/reporting/generate/pngV2')).to.be(2); - }); - - it('PDF', async () => { - expect(getUsageCount(stats, 'post /api/reporting/generate/printablePdfV2')).to.be(3); - }); - it('CSV', async () => { - expect(getUsageCount(stats, 'post /api/reporting/generate/csv_searchsource')).to.be(2); - }); - - it('Download CSV', async () => { expect( - getUsageCount(stats, 'post /api/reporting/v1/generate/immediate/csv_searchsource') + getUsageCount(stats, `post ${PUBLIC_ROUTES.GENERATE_PREFIX}/csv_searchsource`) ).to.be(1); }); }); - - // helpers - const waitOnAggregation = async () => { - await new Promise((resolve) => { - setTimeout(resolve, 8000); - }); - }; - - const getUsageCount = ( - checkUsage: UsageStatsPayloadTestFriendly, - counterName: string - ): number => { - return ( - checkUsage.stack_stats.kibana.plugins.usage_counters.dailyEvents.find( - (item: any) => item.counterName === counterName - )?.total || 0 - ); - }; - - const postCsv = () => - reportingAPI.postJobJSON(`/api/reporting/generate/csv_searchsource`, { - jobParams: - `(browserTimezone:UTC,` + - `columns:!(order_date,category,customer_full_name,taxful_total_price,currency),objectType:search,searchSource:(fields:!((field:'*',include_unmapped:true))` + - `,filter:!((meta:(field:order_date,index:aac3e500-f2c7-11ea-8250-fb138aa491e7,params:()),query:(range:(order_date:(format:strict_date_optional_time,gte:'2019-06-02T12:28:40.866Z'` + - `,lte:'2019-07-18T20:59:57.136Z'))))),index:aac3e500-f2c7-11ea-8250-fb138aa491e7,parent:(filter:!(),highlightAll:!t,index:aac3e500-f2c7-11ea-8250-fb138aa491e7` + - `,query:(language:kuery,query:''),version:!t),sort:!((order_date:desc)),trackTotalHits:!t)` + - `)`, - }); - - const postPng = () => - reportingAPI.postJobJSON(`/api/reporting/generate/pngV2`, { - jobParams: createPngV2Params(1600), - }); - - const postPdf = () => - reportingAPI.postJobJSON(`/api/reporting/generate/printablePdfV2`, { - jobParams: createPdfV2Params(1600), - }); - - const downloadCsv = () => - reportingAPI.downloadCsv( - reportingAPI.REPORTING_USER_USERNAME, - reportingAPI.REPORTING_USER_PASSWORD, - { - searchSource: { - query: { query: '', language: 'kuery' }, - index: '5193f870-d861-11e9-a311-0fa548c5f953', - filter: [], - }, - browserTimezone: 'UTC', - title: 'testfooyu78yt90-', - } - ); }); } From 1b146db909a5d6c54ac5605261d563b35db21eac Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 14:16:14 -0700 Subject: [PATCH 26/47] improve jobs routes integration tests --- .../common/jobs/job_management_pre_routing.ts | 1 - .../management/integration_tests/jobs.test.ts | 139 +++++++++++++----- .../public/integration_tests/jobs.test.ts | 117 ++++++++++----- 3 files changed, 184 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts index a7cea25884ef1..79221c0c49d34 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts @@ -52,7 +52,6 @@ export const jobManagementPreRouting = async ( }); } - // Count usage once allowing the request counters.usageCounter(jobtype); try { diff --git a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts index bb9ba088755ca..11dfd6a3f90c4 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts @@ -12,10 +12,12 @@ import { estypes } from '@elastic/elasticsearch'; import { setupServer } from '@kbn/core-test-helpers-test-utils'; import type { ElasticsearchClientMock } from '@kbn/core/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; +import { IUsageCounter } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counter'; import { BehaviorSubject } from 'rxjs'; import { Readable } from 'stream'; import supertest from 'supertest'; import { ReportingCore } from '../../../..'; +import { INTERNAL_ROUTES } from '../../../../../common/constants'; import { ReportingInternalSetup, ReportingInternalStart } from '../../../../core'; import { ExportType } from '../../../../export_types/common'; import { ContentStream, ExportTypesRegistry, getContentStream } from '../../../../lib'; @@ -31,9 +33,10 @@ import { registerJobInfoRoutesInternal as registerJobInfoRoutes } from '../jobs' type SetupServerReturn = Awaited>; -describe('GET /internal/reporting/jobs/download', () => { +describe('GET ${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}', () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; + let usageCounter: IUsageCounter; let httpSetup: SetupServerReturn['httpSetup']; let exportTypesRegistry: ExportTypesRegistry; let core: ReportingCore; @@ -50,6 +53,21 @@ describe('GET /internal/reporting/jobs/download', () => { } as estypes.SearchResponseBody; }; + const mockJobTypeUnencoded = 'unencodedJobType'; + const mockJobTypeBase64Encoded = 'base64EncodedJobType'; + const getCompleteHits = ({ + jobType = mockJobTypeUnencoded, + outputContentType = 'text/plain', + title = '', + } = {}) => { + return getHits({ + jobtype: jobType, + status: 'completed', + output: { content_type: outputContentType }, + payload: { title }, + }) as estypes.SearchResponseBody; + }; + const mockConfigSchema = createMockConfigSchema({ roles: { enabled: false } }); beforeEach(async () => { @@ -84,16 +102,21 @@ describe('GET /internal/reporting/jobs/download', () => { core = await createMockReportingCore(mockConfigSchema, mockSetupDeps, mockStartDeps); + usageCounter = { + incrementCounter: jest.fn(), + }; + core.getUsageCounter = jest.fn().mockReturnValue(usageCounter); + exportTypesRegistry = new ExportTypesRegistry(); exportTypesRegistry.register({ id: 'unencoded', - jobType: 'unencodedJobType', + jobType: mockJobTypeUnencoded, jobContentExtension: 'csv', validLicenses: ['basic', 'gold'], } as ExportType); exportTypesRegistry.register({ id: 'base64Encoded', - jobType: 'base64EncodedJobType', + jobType: mockJobTypeBase64Encoded, jobContentEncoding: 'base64', jobContentExtension: 'pdf', validLicenses: ['basic', 'gold'], @@ -107,6 +130,9 @@ describe('GET /internal/reporting/jobs/download', () => { this.push(null); }, }) as typeof stream; + stream.end = jest.fn().mockImplementation((_name, _encoding, callback) => { + callback(); + }); (getContentStream as jest.MockedFunction).mockResolvedValue(stream); }); @@ -122,7 +148,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/1') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/1`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot( @@ -148,7 +174,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/dope') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dope`) .expect(401) .then(({ body }) => expect(body.message).toMatchInlineSnapshot(`"Sorry, you aren't authenticated"`) @@ -162,7 +188,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/poo') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/poo`) .expect(404); }); @@ -178,14 +204,14 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/poo') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/poo`) .expect(403); }); it(`returns job's info`, async () => { mockEsClient.search.mockResponseOnce( getHits({ - jobtype: 'base64EncodedJobType', + jobtype: mockJobTypeBase64Encoded, payload: {}, // payload is irrelevant }) ); @@ -195,7 +221,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/info/test') + .get(`${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/test`) .expect(200); }); @@ -212,14 +238,14 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/info/test') + .get(`${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/test`) .expect(403); }); it('when a job is incomplete', async () => { mockEsClient.search.mockResponseOnce( getHits({ - jobtype: 'unencodedJobType', + jobtype: mockJobTypeUnencoded, status: 'pending', payload: { title: 'incomplete!' }, }) @@ -228,7 +254,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/dank') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(503) .expect('Content-Type', 'text/plain; charset=utf-8') .expect('Retry-After', '30') @@ -238,7 +264,7 @@ describe('GET /internal/reporting/jobs/download', () => { it('when a job fails', async () => { mockEsClient.search.mockResponse( getHits({ - jobtype: 'unencodedJobType', + jobtype: mockJobTypeUnencoded, status: 'failed', output: { content: 'job failure message' }, payload: { title: 'failing job!' }, @@ -248,7 +274,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/dank') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(500) .expect('Content-Type', 'application/json; charset=utf-8') .then(({ body }) => @@ -257,26 +283,13 @@ describe('GET /internal/reporting/jobs/download', () => { }); describe('successful downloads', () => { - const getCompleteHits = ({ - jobType = 'unencodedJobType', - outputContentType = 'text/plain', - title = '', - } = {}) => { - return getHits({ - jobtype: jobType, - status: 'completed', - output: { content_type: outputContentType }, - payload: { title }, - }) as estypes.SearchResponseBody; - }; - it('when a known job-type is complete', async () => { mockEsClient.search.mockResponseOnce(getCompleteHits()); registerJobInfoRoutes(core); await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/dank') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); @@ -293,7 +306,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/dope') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dope`) .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); @@ -302,14 +315,14 @@ describe('GET /internal/reporting/jobs/download', () => { it('forwards job content stream', async () => { mockEsClient.search.mockResponseOnce( getCompleteHits({ - jobType: 'unencodedJobType', + jobType: mockJobTypeUnencoded, }) ); registerJobInfoRoutes(core); await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/dank') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .then(({ text }) => expect(text).toEqual('test')); @@ -318,7 +331,7 @@ describe('GET /internal/reporting/jobs/download', () => { it('refuses to return unknown content-types', async () => { mockEsClient.search.mockResponseOnce( getCompleteHits({ - jobType: 'unencodedJobType', + jobType: mockJobTypeUnencoded, outputContentType: 'application/html', }) ); @@ -326,7 +339,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/dank') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(400) .then(({ body }) => { expect(body).toEqual({ @@ -340,7 +353,7 @@ describe('GET /internal/reporting/jobs/download', () => { it('allows multi-byte characters in file names', async () => { mockEsClient.search.mockResponseOnce( getCompleteHits({ - jobType: 'base64EncodedJobType', + jobType: mockJobTypeBase64Encoded, title: '日本語ダッシュボード', }) ); @@ -348,7 +361,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/japanese-dashboard') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/japanese-dashboard`) .expect(200) .expect('Content-Type', 'application/pdf') .expect( @@ -386,7 +399,7 @@ describe('GET /internal/reporting/jobs/download', () => { await server.start(); await supertest(httpSetup.server.listener) - .get('/internal/reporting/jobs/download/dope') + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dope`) .expect(403) .then(({ body }) => expect(body.message).toMatchInlineSnapshot(` @@ -396,4 +409,58 @@ describe('GET /internal/reporting/jobs/download', () => { ); }); }); + + describe('usage counters', () => { + it('increments the info api counter', async () => { + mockEsClient.search.mockResponseOnce(getCompleteHits()); + registerJobInfoRoutes(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .get(`${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/dank`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8'); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `get ${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/{docId}:${mockJobTypeUnencoded}`, + counterType: 'reportingApi', + }); + }); + + it('increments the download api counter', async () => { + mockEsClient.search.mockResponseOnce(getCompleteHits()); + registerJobInfoRoutes(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) + .expect(200) + .expect('Content-Type', 'text/csv; charset=utf-8') + .expect('content-disposition', 'attachment; filename=report.csv'); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `get ${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/{docId}:${mockJobTypeUnencoded}`, + counterType: 'reportingApi', + }); + }); + + it('increments the delete api counter', async () => { + mockEsClient.search.mockResponseOnce(getCompleteHits()); + registerJobInfoRoutes(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .delete(`${INTERNAL_ROUTES.JOBS.DELETE_PREFIX}/dank`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8'); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `delete ${INTERNAL_ROUTES.JOBS.DELETE_PREFIX}/{docId}:${mockJobTypeUnencoded}`, + counterType: 'reportingApi', + }); + }); + }); }); diff --git a/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts index 61b7de8c49388..4521fa5be1778 100644 --- a/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts @@ -12,10 +12,12 @@ import { estypes } from '@elastic/elasticsearch'; import { setupServer } from '@kbn/core-test-helpers-test-utils'; import type { ElasticsearchClientMock } from '@kbn/core/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; +import { IUsageCounter } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counter'; import { BehaviorSubject } from 'rxjs'; import { Readable } from 'stream'; import supertest from 'supertest'; import { ReportingCore } from '../../..'; +import { PUBLIC_ROUTES } from '../../../../common/constants'; import { ReportingInternalSetup, ReportingInternalStart } from '../../../core'; import { ExportType } from '../../../export_types/common'; import { ContentStream, ExportTypesRegistry, getContentStream } from '../../../lib'; @@ -27,13 +29,14 @@ import { createMockReportingCore, } from '../../../test_helpers'; import { ReportingRequestHandlerContext } from '../../../types'; -import { registerJobRoutes } from '../jobs'; +import { registerJobInfoRoutesPublic } from '../jobs'; type SetupServerReturn = Awaited>; -describe('GET /api/reporting/jobs/download', () => { +describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; + let usageCounter: IUsageCounter; let httpSetup: SetupServerReturn['httpSetup']; let exportTypesRegistry: ExportTypesRegistry; let core: ReportingCore; @@ -50,6 +53,19 @@ describe('GET /api/reporting/jobs/download', () => { } as estypes.SearchResponseBody; }; + const getCompleteHits = ({ + jobType = 'unencodedJobType', + outputContentType = 'text/plain', + title = '', + } = {}) => { + return getHits({ + jobtype: jobType, + status: 'completed', + output: { content_type: outputContentType }, + payload: { title }, + }) as estypes.SearchResponseBody; + }; + const mockConfigSchema = createMockConfigSchema({ roles: { enabled: false } }); beforeEach(async () => { @@ -84,6 +100,11 @@ describe('GET /api/reporting/jobs/download', () => { core = await createMockReportingCore(mockConfigSchema, mockSetupDeps, mockStartDeps); + usageCounter = { + incrementCounter: jest.fn(), + }; + core.getUsageCounter = jest.fn().mockReturnValue(usageCounter); + exportTypesRegistry = new ExportTypesRegistry(); exportTypesRegistry.register({ id: 'unencoded', @@ -91,13 +112,6 @@ describe('GET /api/reporting/jobs/download', () => { jobContentExtension: 'csv', validLicenses: ['basic', 'gold'], } as ExportType); - exportTypesRegistry.register({ - id: 'base64Encoded', - jobType: 'base64EncodedJobType', - jobContentEncoding: 'base64', - jobContentExtension: 'pdf', - validLicenses: ['basic', 'gold'], - } as ExportType); core.getExportTypesRegistry = () => exportTypesRegistry; mockEsClient = (await core.getEsClient()).asInternalUser as typeof mockEsClient; @@ -107,6 +121,9 @@ describe('GET /api/reporting/jobs/download', () => { this.push(null); }, }) as typeof stream; + stream.end = jest.fn().mockImplementation((_name, _encoding, callback) => { + callback(); + }); (getContentStream as jest.MockedFunction).mockResolvedValue(stream); }); @@ -117,12 +134,12 @@ describe('GET /api/reporting/jobs/download', () => { it('fails on malformed download IDs', async () => { mockEsClient.search.mockResponseOnce(getHits()); - registerJobRoutes(core); + registerJobInfoRoutesPublic(core); await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/1') + .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/1`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot( @@ -143,12 +160,12 @@ describe('GET /api/reporting/jobs/download', () => { mockConfigSchema ); core = await createMockReportingCore(mockConfigSchema, mockSetupDeps, mockStartDeps); - registerJobRoutes(core); + registerJobInfoRoutesPublic(core); await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dope') + .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dope`) .expect(401) .then(({ body }) => expect(body.message).toMatchInlineSnapshot(`"Sorry, you aren't authenticated"`) @@ -157,11 +174,13 @@ describe('GET /api/reporting/jobs/download', () => { it('returns 404 if job not found', async () => { mockEsClient.search.mockResponseOnce(getHits()); - registerJobRoutes(core); + registerJobInfoRoutesPublic(core); await server.start(); - await supertest(httpSetup.server.listener).get('/api/reporting/jobs/download/poo').expect(404); + await supertest(httpSetup.server.listener) + .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/poo`) + .expect(404); }); it('returns a 403 if not a valid job type', async () => { @@ -171,11 +190,13 @@ describe('GET /api/reporting/jobs/download', () => { payload: { title: 'invalid!' }, }) ); - registerJobRoutes(core); + registerJobInfoRoutesPublic(core); await server.start(); - await supertest(httpSetup.server.listener).get('/api/reporting/jobs/download/poo').expect(403); + await supertest(httpSetup.server.listener) + .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/poo`) + .expect(403); }); it('when a job is incomplete', async () => { @@ -186,11 +207,11 @@ describe('GET /api/reporting/jobs/download', () => { payload: { title: 'incomplete!' }, }) ); - registerJobRoutes(core); + registerJobInfoRoutesPublic(core); await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dank') + .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dank`) .expect(503) .expect('Content-Type', 'text/plain; charset=utf-8') .expect('Retry-After', '30') @@ -206,11 +227,11 @@ describe('GET /api/reporting/jobs/download', () => { payload: { title: 'failing job!' }, }) ); - registerJobRoutes(core); + registerJobInfoRoutesPublic(core); await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dank') + .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dank`) .expect(500) .expect('Content-Type', 'application/json; charset=utf-8') .then(({ body }) => @@ -219,29 +240,53 @@ describe('GET /api/reporting/jobs/download', () => { }); describe('successful downloads', () => { - const getCompleteHits = ({ - jobType = 'unencodedJobType', - outputContentType = 'text/plain', - title = '', - } = {}) => { - return getHits({ - jobtype: jobType, - status: 'completed', - output: { content_type: outputContentType }, - payload: { title }, - }) as estypes.SearchResponseBody; - }; - it('when a known job-type is complete', async () => { mockEsClient.search.mockResponseOnce(getCompleteHits()); - registerJobRoutes(core); + registerJobInfoRoutesPublic(core); await server.start(); await supertest(httpSetup.server.listener) - .get('/api/reporting/jobs/download/dank') + .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dank`) .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); }); }); + + describe('usage counters', () => { + it('increments the download api counter', async () => { + mockEsClient.search.mockResponseOnce(getCompleteHits()); + registerJobInfoRoutesPublic(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dank`) + .expect(200) + .expect('Content-Type', 'text/csv; charset=utf-8') + .expect('content-disposition', 'attachment; filename=report.csv'); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `get ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/{docId}:unencodedJobType`, + counterType: 'reportingApi', + }); + }); + + it('increments the delete api counter', async () => { + mockEsClient.search.mockResponseOnce(getCompleteHits()); + registerJobInfoRoutesPublic(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .delete(`/api/reporting/jobs/delete/dank`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8'); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: 'delete /api/reporting/jobs/delete/{docId}:unencodedJobType', + counterType: 'reportingApi', + }); + }); + }); }); From 5ccabec29ba62120ab10afcec315679c0e510386 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 15:26:08 -0700 Subject: [PATCH 27/47] fix generation integration tests --- .../generate/generate_from_jobparams.ts | 18 +- .../generation_from_jobparams.test.ts | 229 ++++++++++++++++++ .../routes/public/generate_from_jobparams.ts | 18 +- .../generation_from_jobparams.test.ts | 31 +-- 4 files changed, 271 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index 901236ed11e0e..47271fa3c33e9 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { KibanaResponse } from '@kbn/core-http-router-server-internal'; import type { Logger } from '@kbn/core/server'; import type { ReportingCore } from '../../..'; import { INTERNAL_ROUTES } from '../../../../common/constants'; @@ -30,11 +31,18 @@ export function registerGenerationRoutesInternal(reporting: ReportingCore, logge options: { tags: kibanaAccessControlTags }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); - return await requestHandler.handleGenerateRequest( - req.params.exportType, - requestHandler.getJobParams() - ); + try { + const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); + const jobParams = requestHandler.getJobParams(); + console.log({ jobParams }); + return await requestHandler.handleGenerateRequest(req.params.exportType, jobParams); + } catch (err) { + console.log(err); + if (err instanceof KibanaResponse) { + return err; + } + throw err; + } }) ); } diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts new file mode 100644 index 0000000000000..71e6e2ec8fd6a --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setupServer } from '@kbn/core-test-helpers-test-utils'; +import { coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; +import rison from '@kbn/rison'; +import { BehaviorSubject } from 'rxjs'; +import supertest from 'supertest'; +import { ReportingCore } from '../../../..'; +import { INTERNAL_ROUTES } from '../../../../../common/constants'; +import { PdfExportType } from '../../../../export_types/printable_pdf_v2'; +import { ReportingStore } from '../../../../lib'; +import { ExportTypesRegistry } from '../../../../lib/export_types_registry'; +import { Report } from '../../../../lib/store'; +import { reportingMock } from '../../../../mocks'; +import { + createMockConfigSchema, + createMockPluginSetup, + createMockPluginStart, + createMockReportingCore, +} from '../../../../test_helpers'; +import type { ReportingRequestHandlerContext } from '../../../../types'; +import { registerGenerationRoutesInternal } from '../generate_from_jobparams'; + +type SetupServerReturn = Awaited>; + +describe('POST /internal/reporting/generate', () => { + const reportingSymbol = Symbol('reporting'); + let server: SetupServerReturn['server']; + let httpSetup: SetupServerReturn['httpSetup']; + let mockExportTypesRegistry: ExportTypesRegistry; + let mockReportingCore: ReportingCore; + let store: ReportingStore; + + const mockConfigSchema = createMockConfigSchema({ + queue: { indexInterval: 'year', timeout: 10000, pollEnabled: true }, + }); + + const mockLogger = loggingSystemMock.createLogger(); + const mockCoreSetup = coreMock.createSetup(); + + const mockPdfExportType = new PdfExportType( + mockCoreSetup, + mockConfigSchema, + mockLogger, + coreMock.createPluginInitializerContext(mockConfigSchema) + ); + + beforeEach(async () => { + ({ server, httpSetup } = await setupServer(reportingSymbol)); + httpSetup.registerRouteHandlerContext( + reportingSymbol, + 'reporting', + () => reportingMock.createStart() + ); + + const mockSetupDeps = createMockPluginSetup({ + security: { license: { isEnabled: () => true } }, + router: httpSetup.createRouter(''), + }); + + const mockStartDeps = await createMockPluginStart( + { + licensing: { + ...licensingMock.createStart(), + license$: new BehaviorSubject({ isActive: true, isAvailable: true, type: 'gold' }), + }, + security: { + authc: { + getCurrentUser: () => ({ id: '123', roles: ['superuser'], username: 'Tom Riddle' }), + }, + }, + }, + mockConfigSchema + ); + + mockReportingCore = await createMockReportingCore( + mockConfigSchema, + mockSetupDeps, + mockStartDeps + ); + + mockExportTypesRegistry = new ExportTypesRegistry(); + mockExportTypesRegistry.register(mockPdfExportType); + + store = await mockReportingCore.getStore(); + store.addReport = jest.fn().mockImplementation(async (opts) => { + return new Report({ + ...opts, + _id: 'foo', + _index: 'foo-index', + }); + }); + }); + + afterEach(async () => { + await server.stop(); + }); + + it('returns 400 if there are no job params', async () => { + registerGenerationRoutesInternal(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .expect(400) + .then(({ body }) => + expect(body.message).toMatchInlineSnapshot( + '"A jobParams RISON string is required in the querystring or POST body"' + ) + ); + }); + + it('returns 400 if job params query is invalid', async () => { + registerGenerationRoutesInternal(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf?jobParams=foo:`) + .expect(400) + .then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"')); + }); + + it('returns 400 if job params body is invalid', async () => { + registerGenerationRoutesInternal(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .send({ jobParams: `foo:` }) + .expect(400) + .then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"')); + }); + + it('returns 400 export type is invalid', async () => { + registerGenerationRoutesInternal(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${INTERNAL_ROUTES.GENERATE}/TonyHawksProSkater2`) + .send({ jobParams: rison.encode({ title: `abc` }) }) + .expect(400) + .then(({ body }) => + expect(body.message).toMatchInlineSnapshot('"Invalid export-type of TonyHawksProSkater2"') + ); + }); + + it('returns 400 on invalid browser timezone', async () => { + registerGenerationRoutesInternal(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .send({ jobParams: rison.encode({ browserTimezone: 'America/Amsterdam', title: `abc` }) }) + .expect(400) + .then(({ body }) => + expect(body.message).toMatchInlineSnapshot(`"Invalid timezone \\"America/Amsterdam\\"."`) + ); + }); + + it('returns 500 if job handler throws an error', async () => { + store.addReport = jest.fn().mockRejectedValue('silly'); + + registerGenerationRoutesInternal(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .send({ jobParams: rison.encode({ title: `abc` }) }) + .expect(500); + }); + + it(`returns 200 if job handler doesn't error`, async () => { + registerGenerationRoutesInternal(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .send({ + jobParams: rison.encode({ + title: `abc`, + relativeUrls: ['test'], + layout: { id: 'test' }, + objectType: 'canvas workpad', + }), + }) + .expect(200) + .then(({ body }) => { + expect(body).toMatchObject({ + job: { + attempts: 0, + created_by: 'Tom Riddle', + id: 'foo', + index: 'foo-index', + jobtype: 'printable_pdf', + payload: { + forceNow: expect.any(String), + isDeprecated: true, + layout: { + id: 'test', + }, + objectType: 'canvas workpad', + objects: [ + { + relativeUrl: 'test', + }, + ], + title: 'abc', + version: '7.14.0', + }, + status: 'pending', + }, + path: '/mock-server-basepath/api/reporting/jobs/download/foo', + }); + }); + }); +}); diff --git a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts index 5ef435d2a8ec3..b5868e8d93cc2 100644 --- a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { KibanaResponse } from '@kbn/core-http-router-server-internal'; import type { Logger } from '@kbn/core/server'; import type { ReportingCore } from '../..'; import { PUBLIC_ROUTES } from '../../../common/constants'; @@ -26,11 +27,18 @@ export function registerGenerationRoutesPublic(reporting: ReportingCore, logger: options: { tags: kibanaAccessControlTags }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); - return await requestHandler.handleGenerateRequest( - req.params.exportType, - requestHandler.getJobParams() - ); + try { + const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); + return await requestHandler.handleGenerateRequest( + req.params.exportType, + requestHandler.getJobParams() + ); + } catch (err) { + if (err instanceof KibanaResponse) { + return err; + } + throw err; + } }) ); diff --git a/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts index 5949d248e1247..70c048c1ac8c1 100644 --- a/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts @@ -12,6 +12,7 @@ import rison from '@kbn/rison'; import { BehaviorSubject } from 'rxjs'; import supertest from 'supertest'; import { ReportingCore } from '../../..'; +import { PUBLIC_ROUTES } from '../../../../common/constants'; import { PdfExportType } from '../../../export_types/printable_pdf_v2'; import { ReportingStore } from '../../../lib'; import { ExportTypesRegistry } from '../../../lib/export_types_registry'; @@ -24,7 +25,7 @@ import { createMockReportingCore, } from '../../../test_helpers'; import type { ReportingRequestHandlerContext } from '../../../types'; -import { registerGenerationRoutesPublic as registerJobGenerationRoutes } from '../generate_from_jobparams'; +import { registerGenerationRoutesPublic } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; @@ -102,12 +103,12 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if there are no job params', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerGenerationRoutesPublic(mockReportingCore, mockLogger); await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/generate/printablePdf') + .post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot( @@ -117,35 +118,35 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 if job params query is invalid', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerGenerationRoutesPublic(mockReportingCore, mockLogger); await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/generate/printablePdf?jobParams=foo:') + .post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf?jobParams=foo:`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"')); }); it('returns 400 if job params body is invalid', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerGenerationRoutesPublic(mockReportingCore, mockLogger); await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/generate/printablePdf') + .post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: `foo:` }) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"')); }); it('returns 400 export type is invalid', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerGenerationRoutesPublic(mockReportingCore, mockLogger); await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/generate/TonyHawksProSkater2') + .post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/TonyHawksProSkater2`) .send({ jobParams: rison.encode({ title: `abc` }) }) .expect(400) .then(({ body }) => @@ -154,12 +155,12 @@ describe('POST /api/reporting/generate', () => { }); it('returns 400 on invalid browser timezone', async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerGenerationRoutesPublic(mockReportingCore, mockLogger); await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/generate/printablePdf') + .post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ browserTimezone: 'America/Amsterdam', title: `abc` }) }) .expect(400) .then(({ body }) => @@ -170,23 +171,23 @@ describe('POST /api/reporting/generate', () => { it('returns 500 if job handler throws an error', async () => { store.addReport = jest.fn().mockRejectedValue('silly'); - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerGenerationRoutesPublic(mockReportingCore, mockLogger); await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/generate/printablePdf') + .post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ title: `abc` }) }) .expect(500); }); it(`returns 200 if job handler doesn't error`, async () => { - registerJobGenerationRoutes(mockReportingCore, mockLogger); + registerGenerationRoutesPublic(mockReportingCore, mockLogger); await server.start(); await supertest(httpSetup.server.listener) - .post('/api/reporting/generate/printablePdf') + .post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ title: `abc`, From a4dd40b33d6b220b788019765bee4cb7cc493751 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 15:59:03 -0700 Subject: [PATCH 28/47] fix console.log --- .../server/routes/internal/generate/generate_from_jobparams.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index 47271fa3c33e9..0db142dec179a 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -34,10 +34,8 @@ export function registerGenerationRoutesInternal(reporting: ReportingCore, logge try { const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); const jobParams = requestHandler.getJobParams(); - console.log({ jobParams }); return await requestHandler.handleGenerateRequest(req.params.exportType, jobParams); } catch (err) { - console.log(err); if (err instanceof KibanaResponse) { return err; } From 0eb8a757ed1109e1e1effe863e76325c5769f6e3 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 26 Jul 2023 23:05:30 +0000 Subject: [PATCH 29/47] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/reporting/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/reporting/tsconfig.json b/x-pack/plugins/reporting/tsconfig.json index e6b3e0b81e9d4..05e62e6251e19 100644 --- a/x-pack/plugins/reporting/tsconfig.json +++ b/x-pack/plugins/reporting/tsconfig.json @@ -39,6 +39,7 @@ "@kbn/generate-csv", "@kbn/reporting-common", "@kbn/saved-search-plugin", + "@kbn/core-http-router-server-internal", ], "exclude": [ "target/**/*", From 1438f06927f5160fefcfe19f95b4ec4a16a987f9 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 16:14:39 -0700 Subject: [PATCH 30/47] fix internal generation integration test --- .../generation_from_jobparams.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts index 71e6e2ec8fd6a..60cb5cda4f8ed 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts @@ -108,7 +108,7 @@ describe('POST /internal/reporting/generate', () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot( @@ -123,7 +123,7 @@ describe('POST /internal/reporting/generate', () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf?jobParams=foo:`) + .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf?jobParams=foo:`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"')); }); @@ -134,7 +134,7 @@ describe('POST /internal/reporting/generate', () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) .send({ jobParams: `foo:` }) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"')); @@ -146,7 +146,7 @@ describe('POST /internal/reporting/generate', () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE}/TonyHawksProSkater2`) + .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/TonyHawksProSkater2`) .send({ jobParams: rison.encode({ title: `abc` }) }) .expect(400) .then(({ body }) => @@ -160,7 +160,7 @@ describe('POST /internal/reporting/generate', () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ browserTimezone: 'America/Amsterdam', title: `abc` }) }) .expect(400) .then(({ body }) => @@ -176,7 +176,7 @@ describe('POST /internal/reporting/generate', () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ title: `abc` }) }) .expect(500); }); @@ -187,7 +187,7 @@ describe('POST /internal/reporting/generate', () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ title: `abc`, From 73500d4f8969ddd73308bab7b814f7f71e4a5ab2 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 16:24:29 -0700 Subject: [PATCH 31/47] add usage counter jest integration tests for generation routes --- .../generation_from_jobparams.test.ts | 33 +++++++++++++++++++ .../generation_from_jobparams.test.ts | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts index 60cb5cda4f8ed..3d76fcba21985 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts @@ -9,6 +9,7 @@ import { setupServer } from '@kbn/core-test-helpers-test-utils'; import { coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import rison from '@kbn/rison'; +import { IUsageCounter } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counter'; import { BehaviorSubject } from 'rxjs'; import supertest from 'supertest'; import { ReportingCore } from '../../../..'; @@ -32,6 +33,7 @@ type SetupServerReturn = Awaited>; describe('POST /internal/reporting/generate', () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; + let usageCounter: IUsageCounter; let httpSetup: SetupServerReturn['httpSetup']; let mockExportTypesRegistry: ExportTypesRegistry; let mockReportingCore: ReportingCore; @@ -85,6 +87,11 @@ describe('POST /internal/reporting/generate', () => { mockStartDeps ); + usageCounter = { + incrementCounter: jest.fn(), + }; + mockReportingCore.getUsageCounter = jest.fn().mockReturnValue(usageCounter); + mockExportTypesRegistry = new ExportTypesRegistry(); mockExportTypesRegistry.register(mockPdfExportType); @@ -226,4 +233,30 @@ describe('POST /internal/reporting/generate', () => { }); }); }); + + describe('usage counters', () => { + it('increments generation api counter', async () => { + registerGenerationRoutesInternal(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) + .send({ + jobParams: rison.encode({ + title: `abc`, + relativeUrls: ['test'], + layout: { id: 'test' }, + objectType: 'canvas workpad', + }), + }) + .expect(200); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `post /internal/reporting/generate/printablePdf`, + counterType: 'reportingApi', + }); + }); + }); }); diff --git a/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts index 70c048c1ac8c1..b2fd53961223b 100644 --- a/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts @@ -9,6 +9,7 @@ import { setupServer } from '@kbn/core-test-helpers-test-utils'; import { coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import rison from '@kbn/rison'; +import { IUsageCounter } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counter'; import { BehaviorSubject } from 'rxjs'; import supertest from 'supertest'; import { ReportingCore } from '../../..'; @@ -32,6 +33,7 @@ type SetupServerReturn = Awaited>; describe('POST /api/reporting/generate', () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; + let usageCounter: IUsageCounter; let httpSetup: SetupServerReturn['httpSetup']; let mockExportTypesRegistry: ExportTypesRegistry; let mockReportingCore: ReportingCore; @@ -85,6 +87,11 @@ describe('POST /api/reporting/generate', () => { mockStartDeps ); + usageCounter = { + incrementCounter: jest.fn(), + }; + mockReportingCore.getUsageCounter = jest.fn().mockReturnValue(usageCounter); + mockExportTypesRegistry = new ExportTypesRegistry(); mockExportTypesRegistry.register(mockPdfExportType); @@ -226,4 +233,30 @@ describe('POST /api/reporting/generate', () => { }); }); }); + + describe('usage counters', () => { + it('increments generation api counter', async () => { + registerGenerationRoutesPublic(mockReportingCore, mockLogger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .post(`${PUBLIC_ROUTES.GENERATE_PREFIX}/printablePdf`) + .send({ + jobParams: rison.encode({ + title: `abc`, + relativeUrls: ['test'], + layout: { id: 'test' }, + objectType: 'canvas workpad', + }), + }) + .expect(200); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `post /api/reporting/generate/printablePdf`, + counterType: 'reportingApi', + }); + }); + }); }); From 4688106d0545ad8c0bb8715f7cca05a887db8b70 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 16:41:18 -0700 Subject: [PATCH 32/47] remove api_counter functional test --- .../usage/api_counters.ts | 124 ------------------ .../reporting_and_security/usage/index.ts | 1 - 2 files changed, 125 deletions(-) delete mode 100644 x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts deleted file mode 100644 index d308abd132da7..0000000000000 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage/api_counters.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { INTERNAL_ROUTES, PUBLIC_ROUTES } from '@kbn/reporting-plugin/common/constants/routes'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { UsageStatsPayloadTestFriendly } from '../../../api_integration/services/usage_api'; - -// helpers -const waitOnAggregation = async () => { - await new Promise((resolve) => { - setTimeout(resolve, 8000); - }); -}; - -const getUsageCount = (checkUsage: UsageStatsPayloadTestFriendly, counterName: string): number => { - return ( - checkUsage.stack_stats.kibana.plugins.usage_counters.dailyEvents.find( - (item: any) => item.counterName === counterName - )?.total || 0 - ); -}; - -// eslint-disable-next-line import/no-default-export -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); - const usageAPI = getService('usageAPI'); - const reportingAPI = getService('reportingAPI'); - - describe(`Usage Counters`, () => { - before(async () => { - await esArchiver.emptyKibanaIndex(); - await reportingAPI.initEcommerce(); - await esArchiver.load('x-pack/test/functional/es_archives/reporting/archived_reports'); - }); - - after(async () => { - await reportingAPI.deleteAllReports(); - await reportingAPI.teardownEcommerce(); - await esArchiver.unload('x-pack/test/functional/es_archives/reporting/archived_reports'); - }); - - describe('API counters: management', () => { - const paths = { - LIST: INTERNAL_ROUTES.JOBS.LIST, - COUNT: INTERNAL_ROUTES.JOBS.COUNT, - INFO: INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/kraz0qle154g0763b569zz83', // required report stored in archived_reports - }; - - let initialStats: UsageStatsPayloadTestFriendly; - let stats: UsageStatsPayloadTestFriendly; - - before(async () => { - [{ stats: initialStats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); - - // call APIs to increment counters - await Promise.all( - Object.keys(paths).map(async (key) => { - await supertest - .get(paths[key as keyof typeof paths]) - .auth('test_user', 'changeme') - .expect(200); - }) - ); - - // wait for events to aggregate into the usage stats - await waitOnAggregation(); - - // determine the result usage count - [{ stats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); - }); - - it('job listing', async () => { - const initialCount = getUsageCount(initialStats, `get ${paths.LIST}`); - expect(getUsageCount(stats, `get ${paths.LIST}`)).to.be(initialCount + 1); - }); - - it('job count', async () => { - const initialCount = getUsageCount(initialStats, `get ${paths.COUNT}`); - expect(getUsageCount(stats, `get ${paths.COUNT}`)).to.be(initialCount + 1); - }); - - it('job info', async () => { - const initialCount = getUsageCount( - initialStats, - `get ${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/{docId}:printable_pdf` - ); - expect( - getUsageCount(stats, `get ${INTERNAL_ROUTES.JOBS.INFO_PREFIX}/{docId}:printable_pdf`) - ).to.be(initialCount + 1); - }); - }); - - describe('API counters: job generation', () => { - let stats: UsageStatsPayloadTestFriendly; - - before(async () => { - // call generation APIs - await reportingAPI.postJobJSON(`${PUBLIC_ROUTES.GENERATE_PREFIX}/csv_searchsource`, { - jobParams: - `(browserTimezone:UTC,` + - `columns:!(order_date,category,customer_full_name,taxful_total_price,currency),objectType:search,searchSource:(fields:!((field:'*',include_unmapped:true))` + - `,filter:!((meta:(field:order_date,index:aac3e500-f2c7-11ea-8250-fb138aa491e7,params:()),query:(range:(order_date:(format:strict_date_optional_time,gte:'2019-06-02T12:28:40.866Z'` + - `,lte:'2019-07-18T20:59:57.136Z'))))),index:aac3e500-f2c7-11ea-8250-fb138aa491e7,parent:(filter:!(),highlightAll:!t,index:aac3e500-f2c7-11ea-8250-fb138aa491e7` + - `,query:(language:kuery,query:''),version:!t),sort:!((order_date:desc)),trackTotalHits:!t)` + - `)`, - }); - await waitOnAggregation(); - [{ stats }] = await usageAPI.getTelemetryStats({ unencrypted: true }); - }); - - it('CSV', async () => { - expect( - getUsageCount(stats, `post ${PUBLIC_ROUTES.GENERATE_PREFIX}/csv_searchsource`) - ).to.be(1); - }); - }); - }); -} diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage/index.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage/index.ts index f43ca39136dcb..5bb4b15eefe7e 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage/index.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage/index.ts @@ -15,6 +15,5 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./metrics')); loadTestFile(require.resolve('./new_jobs')); loadTestFile(require.resolve('./error_codes')); - loadTestFile(require.resolve('./api_counters')); }); } From 7e6a4d52018ac6b8b2577c7e30feed5f2ededff4 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 16:52:14 -0700 Subject: [PATCH 33/47] update route integration tests with api counter testing --- .../integration_tests/browser.test.ts | 2 +- .../generation_from_jobparams.test.ts | 2 +- .../management/integration_tests/jobs.test.ts | 19 ++++++++++++++++++- .../generation_from_jobparams.test.ts | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts index 5ef2bed77cc61..a058d4705e822 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts @@ -26,7 +26,7 @@ type SetupServerReturn = Awaited>; const devtoolMessage = 'DevTools listening on (ws://localhost:4000)'; const fontNotFoundMessage = 'Could not find the default font'; -describe('POST /diagnose/browser', () => { +describe(`POST ${INTERNAL_ROUTES.DIAGNOSE.BROWSER}`, () => { jest.setTimeout(6000); const reportingSymbol = Symbol('reporting'); const mockLogger = loggingSystemMock.createLogger(); diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts index 3d76fcba21985..474466d1a42fb 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts @@ -30,7 +30,7 @@ import { registerGenerationRoutesInternal } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; -describe('POST /internal/reporting/generate', () => { +describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; let usageCounter: IUsageCounter; diff --git a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts index 11dfd6a3f90c4..d77a3840cfac3 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts @@ -33,7 +33,7 @@ import { registerJobInfoRoutesInternal as registerJobInfoRoutes } from '../jobs' type SetupServerReturn = Awaited>; -describe('GET ${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}', () => { +describe(`GET ${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}`, () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; let usageCounter: IUsageCounter; @@ -462,5 +462,22 @@ describe('GET ${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}', () => { counterType: 'reportingApi', }); }); + + it('increments the count api counter', async () => { + mockEsClient.search.mockResponseOnce(getCompleteHits()); + registerJobInfoRoutes(core); + + await server.start(); + await supertest(httpSetup.server.listener) + .get(INTERNAL_ROUTES.JOBS.COUNT) + .expect(200) + .expect('Content-Type', 'text/plain; charset=utf-8'); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `get ${INTERNAL_ROUTES.JOBS.COUNT}`, + counterType: 'reportingApi', + }); + }); }); }); diff --git a/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts index b2fd53961223b..18883564030d1 100644 --- a/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/generation_from_jobparams.test.ts @@ -30,7 +30,7 @@ import { registerGenerationRoutesPublic } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; -describe('POST /api/reporting/generate', () => { +describe(`POST ${PUBLIC_ROUTES.GENERATE_PREFIX}`, () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; let usageCounter: IUsageCounter; From a98eb76232d7cea032bef73f8be869c139c56df7 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 17:18:42 -0700 Subject: [PATCH 34/47] change route objects structures --- .../reporting/common/constants/routes.ts | 28 +++++++++---------- .../reporting_api_client.ts | 4 +-- .../generate/csv_searchsource_immediate.ts | 2 +- .../generate/generate_from_jobparams.ts | 2 +- .../generation_from_jobparams.test.ts | 18 ++++++------ .../public/integration_tests/jobs.test.ts | 20 ++++++------- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/x-pack/plugins/reporting/common/constants/routes.ts b/x-pack/plugins/reporting/common/constants/routes.ts index 5ed9cb50d2cf9..864b9c0653e13 100644 --- a/x-pack/plugins/reporting/common/constants/routes.ts +++ b/x-pack/plugins/reporting/common/constants/routes.ts @@ -22,10 +22,8 @@ export const INTERNAL_ROUTES = { DELETE_PREFIX: prefixInternalPath + '/jobs/delete', // docId is added to the final path DOWNLOAD_PREFIX: prefixInternalPath + '/jobs/download', // docId is added to the final path }, - GENERATE: { - EXPORT_TYPE_PREFIX: prefixInternalPath + '/generate', // exportTypeId is added to the final path - CSV_IMMEDIATE: prefixInternalPath + '/generate/immediate/csv_searchsource', - }, + DOWNLOAD_CSV: prefixInternalPath + '/generate/immediate/csv_searchsource', + GENERATE_PREFIX: prefixInternalPath + '/generate', // exportTypeId is added to the final path }; const prefixPublicPath = '/api/reporting'; @@ -35,14 +33,16 @@ export const PUBLIC_ROUTES = { * exportTypeId is added to the final path */ GENERATE_PREFIX: prefixPublicPath + `/generate`, - /** - * Public endpoint used by Watcher and automated report downloads - * jobId is added to the final path - */ - DOWNLOAD_PREFIX: prefixPublicPath + `/jobs/download`, - /** - * Public endpoint potentially used to delete a report after download in automation - * jobId is added to the final path - */ - DELETE_PREFIX: prefixPublicPath + `/jobs/delete`, + JOBS: { + /** + * Public endpoint used by Watcher and automated report downloads + * jobId is added to the final path + */ + DOWNLOAD_PREFIX: prefixPublicPath + `/jobs/download`, + /** + * Public endpoint potentially used to delete a report after download in automation + * jobId is added to the final path + */ + DELETE_PREFIX: prefixPublicPath + `/jobs/delete`, + }, }; diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index e07f3101b0f78..2681789745751 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -181,7 +181,7 @@ export class ReportingAPIClient implements IReportingAPI { public async createReportingJob(exportType: string, jobParams: BaseParams) { const jobParamsRison = rison.encode(jobParams); const resp: { job: ReportApiJSON } = await this.http.post( - `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/${exportType}`, + `${INTERNAL_ROUTES.GENERATE_PREFIX}/${exportType}`, { method: 'POST', body: JSON.stringify({ jobParams: jobParamsRison }), @@ -193,7 +193,7 @@ export class ReportingAPIClient implements IReportingAPI { public async createImmediateReport(baseParams: BaseParams) { const { objectType: _objectType, ...params } = baseParams; // objectType is not needed for immediate download api - return this.http.post(INTERNAL_ROUTES.GENERATE.CSV_IMMEDIATE, { + return this.http.post(INTERNAL_ROUTES.DOWNLOAD_CSV, { asResponse: true, body: JSON.stringify(params), }); diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts index f437dc7e6ef99..f213dc8eb8127 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts @@ -15,7 +15,7 @@ import type { JobParamsDownloadCSV } from '../../../export_types/csv_searchsourc import { PassThroughStream } from '../../../lib'; import { authorizedUserPreRouting, getCounters } from '../../common'; -const path = INTERNAL_ROUTES.GENERATE.CSV_IMMEDIATE; +const path = INTERNAL_ROUTES.DOWNLOAD_CSV; export type CsvFromSavedObjectRequest = KibanaRequest; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index 0db142dec179a..a1f7eeefc2750 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -23,7 +23,7 @@ export function registerGenerationRoutesInternal(reporting: ReportingCore, logge const useKibanaAccessControl = reporting.getDeprecatedAllowedRoles() === false; // true if Reporting's deprecated access control feature is disabled const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; - const path = `${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/{exportType}`; + const path = `${INTERNAL_ROUTES.GENERATE_PREFIX}/{exportType}`; router.post( { path, diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts index 474466d1a42fb..c36385ab5174f 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/integration_tests/generation_from_jobparams.test.ts @@ -30,7 +30,7 @@ import { registerGenerationRoutesInternal } from '../generate_from_jobparams'; type SetupServerReturn = Awaited>; -describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { +describe(`POST ${INTERNAL_ROUTES.GENERATE_PREFIX}`, () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; let usageCounter: IUsageCounter; @@ -115,7 +115,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot( @@ -130,7 +130,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf?jobParams=foo:`) + .post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf?jobParams=foo:`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"')); }); @@ -141,7 +141,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: `foo:` }) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot('"invalid rison: foo:"')); @@ -153,7 +153,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/TonyHawksProSkater2`) + .post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/TonyHawksProSkater2`) .send({ jobParams: rison.encode({ title: `abc` }) }) .expect(400) .then(({ body }) => @@ -167,7 +167,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ browserTimezone: 'America/Amsterdam', title: `abc` }) }) .expect(400) .then(({ body }) => @@ -183,7 +183,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ title: `abc` }) }) .expect(500); }); @@ -194,7 +194,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ title: `abc`, @@ -241,7 +241,7 @@ describe(`POST ${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}`, () => { await server.start(); await supertest(httpSetup.server.listener) - .post(`${INTERNAL_ROUTES.GENERATE.EXPORT_TYPE_PREFIX}/printablePdf`) + .post(`${INTERNAL_ROUTES.GENERATE_PREFIX}/printablePdf`) .send({ jobParams: rison.encode({ title: `abc`, diff --git a/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts index 4521fa5be1778..b268d8c089d57 100644 --- a/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/public/integration_tests/jobs.test.ts @@ -33,7 +33,7 @@ import { registerJobInfoRoutesPublic } from '../jobs'; type SetupServerReturn = Awaited>; -describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { +describe(`GET ${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}`, () => { const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; let usageCounter: IUsageCounter; @@ -139,7 +139,7 @@ describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { await server.start(); await supertest(httpSetup.server.listener) - .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/1`) + .get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/1`) .expect(400) .then(({ body }) => expect(body.message).toMatchInlineSnapshot( @@ -165,7 +165,7 @@ describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { await server.start(); await supertest(httpSetup.server.listener) - .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dope`) + .get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/dope`) .expect(401) .then(({ body }) => expect(body.message).toMatchInlineSnapshot(`"Sorry, you aren't authenticated"`) @@ -179,7 +179,7 @@ describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { await server.start(); await supertest(httpSetup.server.listener) - .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/poo`) + .get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/poo`) .expect(404); }); @@ -195,7 +195,7 @@ describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { await server.start(); await supertest(httpSetup.server.listener) - .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/poo`) + .get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/poo`) .expect(403); }); @@ -211,7 +211,7 @@ describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { await server.start(); await supertest(httpSetup.server.listener) - .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dank`) + .get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(503) .expect('Content-Type', 'text/plain; charset=utf-8') .expect('Retry-After', '30') @@ -231,7 +231,7 @@ describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { await server.start(); await supertest(httpSetup.server.listener) - .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dank`) + .get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(500) .expect('Content-Type', 'application/json; charset=utf-8') .then(({ body }) => @@ -246,7 +246,7 @@ describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { await server.start(); await supertest(httpSetup.server.listener) - .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dank`) + .get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); @@ -260,14 +260,14 @@ describe('GET ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}', () => { await server.start(); await supertest(httpSetup.server.listener) - .get(`${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/dank`) + .get(`${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) .expect(200) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ - counterName: `get ${PUBLIC_ROUTES.DOWNLOAD_PREFIX}/{docId}:unencodedJobType`, + counterName: `get ${PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX}/{docId}:unencodedJobType`, counterType: 'reportingApi', }); }); From 3f16fd7f02c72a72f2e67c2d1e11573b035c2f71 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 17:19:03 -0700 Subject: [PATCH 35/47] Add usage counter test for get ILM status --- .../integration_tests/deprecations.test.ts | 68 +++++++++++++++---- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts index 1e655d80adfc6..db00be6aef963 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts @@ -5,11 +5,13 @@ * 2.0. */ -import { loggingSystemMock } from '@kbn/core/server/mocks'; import { setupServer } from '@kbn/core-test-helpers-test-utils'; -import supertest from 'supertest'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { securityMock } from '@kbn/security-plugin/server/mocks'; +import { IUsageCounter } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counter'; +import supertest from 'supertest'; +import { ReportingCore } from '../../../..'; import { INTERNAL_ROUTES } from '../../../../../common/constants'; import { createMockConfigSchema, @@ -25,21 +27,13 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { jest.setTimeout(6000); const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; + let usageCounter: IUsageCounter; + let core: ReportingCore; let httpSetup: SetupServerReturn['httpSetup']; const mockConfig = createMockConfigSchema({ queue: { indexInterval: 'year', timeout: 10000, pollEnabled: true }, }); - const createReportingCore = async ({ - security, - }: { - security?: ReturnType; - }) => - createMockReportingCore( - mockConfig, - createMockPluginSetup({ security, router: httpSetup.createRouter('') }), - await createMockPluginStart({ licensing: licensingMock.createStart() }, mockConfig) - ); beforeEach(async () => { jest.clearAllMocks(); @@ -51,8 +45,14 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { }); it('correctly handles authz when security is unavailable', async () => { - const core = await createReportingCore({}); - + core = await createMockReportingCore( + mockConfig, + createMockPluginSetup({ + security: undefined, + router: httpSetup.createRouter(''), + }), + await createMockPluginStart({ licensing: licensingMock.createStart() }, mockConfig) + ); registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); await server.start(); @@ -65,7 +65,11 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { it('correctly handles authz when security is disabled', async () => { const security = securityMock.createSetup(); security.license.isEnabled.mockReturnValue(false); - const core = await createReportingCore({ security }); + core = await createMockReportingCore( + mockConfig, + createMockPluginSetup({ security, router: httpSetup.createRouter('') }), + await createMockPluginStart({ licensing: licensingMock.createStart() }, mockConfig) + ); registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); await server.start(); @@ -75,4 +79,38 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { .expect(200) .then(/* Ignore result */); }); + + describe('usage counter', () => { + it('increments the download api counter', async () => { + const security = securityMock.createSetup(); + security.license.isEnabled.mockReturnValue(false); + core = await createMockReportingCore( + mockConfig, + createMockPluginSetup({ + security, + router: httpSetup.createRouter(''), + }), + await createMockPluginStart({ licensing: licensingMock.createStart() }, mockConfig) + ); + + usageCounter = { + incrementCounter: jest.fn(), + }; + core.getUsageCounter = jest.fn().mockReturnValue(usageCounter); + + registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); + await server.start(); + + await supertest(httpSetup.server.listener) + .get(INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS) + .expect(200) + .then(/* Ignore result */); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `get ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, + counterType: 'reportingApi', + }); + }); + }); }); From 427330929600aa7d26b904628fa76ae847bc776c Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 17:23:02 -0700 Subject: [PATCH 36/47] add usage counter test for diagnose api --- .../integration_tests/browser.test.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts index a058d4705e822..be23298a0f0e1 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts @@ -8,6 +8,7 @@ import { setupServer } from '@kbn/core-test-helpers-test-utils'; import { docLinksServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; import type { ScreenshottingStart } from '@kbn/screenshotting-plugin/server'; +import { IUsageCounter } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counter'; import * as Rx from 'rxjs'; import supertest from 'supertest'; import { ReportingCore } from '../../../..'; @@ -32,6 +33,7 @@ describe(`POST ${INTERNAL_ROUTES.DIAGNOSE.BROWSER}`, () => { const mockLogger = loggingSystemMock.createLogger(); let server: SetupServerReturn['server']; + let usageCounter: IUsageCounter; let httpSetup: SetupServerReturn['httpSetup']; let core: ReportingCore; let screenshotting: jest.Mocked; @@ -68,6 +70,11 @@ describe(`POST ${INTERNAL_ROUTES.DIAGNOSE.BROWSER}`, () => { }) ); + usageCounter = { + incrementCounter: jest.fn(), + }; + core.getUsageCounter = jest.fn().mockReturnValue(usageCounter); + screenshotting = (await core.getPluginStartDeps()).screenshotting as typeof screenshotting; }); @@ -136,4 +143,22 @@ describe(`POST ${INTERNAL_ROUTES.DIAGNOSE.BROWSER}`, () => { `); }); }); + + describe('usage counter', () => { + it('increments the counter', async () => { + registerDiagnoseBrowser(core, mockLogger); + + await server.start(); + + screenshotting.diagnose.mockReturnValue(Rx.of(devtoolMessage)); + + await supertest(httpSetup.server.listener).post(INTERNAL_ROUTES.DIAGNOSE.BROWSER).expect(200); + + expect(usageCounter.incrementCounter).toHaveBeenCalledTimes(1); + expect(usageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `post ${INTERNAL_ROUTES.DIAGNOSE.BROWSER}:success`, + counterType: 'reportingApi', + }); + }); + }); }); From 55c1751254ee94e1212b78c229a8fb3a4a7b41e5 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 26 Jul 2023 17:29:10 -0700 Subject: [PATCH 37/47] cleanup diff --- .../integration_tests/deprecations.test.ts | 42 +++++++------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts index db00be6aef963..aa6230994cfb6 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts @@ -27,13 +27,21 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { jest.setTimeout(6000); const reportingSymbol = Symbol('reporting'); let server: SetupServerReturn['server']; - let usageCounter: IUsageCounter; - let core: ReportingCore; let httpSetup: SetupServerReturn['httpSetup']; const mockConfig = createMockConfigSchema({ queue: { indexInterval: 'year', timeout: 10000, pollEnabled: true }, }); + const createReportingCore = async ({ + security, + }: { + security?: ReturnType; + }) => + createMockReportingCore( + mockConfig, + createMockPluginSetup({ security, router: httpSetup.createRouter('') }), + await createMockPluginStart({ licensing: licensingMock.createStart() }, mockConfig) + ); beforeEach(async () => { jest.clearAllMocks(); @@ -45,14 +53,8 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { }); it('correctly handles authz when security is unavailable', async () => { - core = await createMockReportingCore( - mockConfig, - createMockPluginSetup({ - security: undefined, - router: httpSetup.createRouter(''), - }), - await createMockPluginStart({ licensing: licensingMock.createStart() }, mockConfig) - ); + const core = await createReportingCore({}); + registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); await server.start(); @@ -65,11 +67,7 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { it('correctly handles authz when security is disabled', async () => { const security = securityMock.createSetup(); security.license.isEnabled.mockReturnValue(false); - core = await createMockReportingCore( - mockConfig, - createMockPluginSetup({ security, router: httpSetup.createRouter('') }), - await createMockPluginStart({ licensing: licensingMock.createStart() }, mockConfig) - ); + const core = await createReportingCore({ security }); registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); await server.start(); @@ -82,18 +80,8 @@ describe(`GET ${INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS}`, () => { describe('usage counter', () => { it('increments the download api counter', async () => { - const security = securityMock.createSetup(); - security.license.isEnabled.mockReturnValue(false); - core = await createMockReportingCore( - mockConfig, - createMockPluginSetup({ - security, - router: httpSetup.createRouter(''), - }), - await createMockPluginStart({ licensing: licensingMock.createStart() }, mockConfig) - ); - - usageCounter = { + const core = await createReportingCore({}); + const usageCounter = { incrementCounter: jest.fn(), }; core.getUsageCounter = jest.fn().mockReturnValue(usageCounter); From 80ae40a5e03b1b6224d0982e1603d4ff0657253a Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 27 Jul 2023 00:35:09 +0000 Subject: [PATCH 38/47] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../deprecations/integration_tests/deprecations.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts index aa6230994cfb6..8b82e2bef151f 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/integration_tests/deprecations.test.ts @@ -9,9 +9,7 @@ import { setupServer } from '@kbn/core-test-helpers-test-utils'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { securityMock } from '@kbn/security-plugin/server/mocks'; -import { IUsageCounter } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counter'; import supertest from 'supertest'; -import { ReportingCore } from '../../../..'; import { INTERNAL_ROUTES } from '../../../../../common/constants'; import { createMockConfigSchema, From 8c0371b84b3fc0ae88d24d49c8da4a7973e45306 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 27 Jul 2023 07:51:12 -0700 Subject: [PATCH 39/47] fix references --- .../server/routes/common/generate/request_handler.ts | 2 +- x-pack/plugins/reporting/server/routes/public/jobs.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/common/generate/request_handler.ts b/x-pack/plugins/reporting/server/routes/common/generate/request_handler.ts index 5143c8abd4201..25b7feff925d7 100644 --- a/x-pack/plugins/reporting/server/routes/common/generate/request_handler.ts +++ b/x-pack/plugins/reporting/server/routes/common/generate/request_handler.ts @@ -198,7 +198,7 @@ export class RequestHandler { try { report = await this.enqueueJob(exportTypeId, jobParams); const { basePath } = this.reporting.getServerInfo(); - const publicDownloadPath = basePath + PUBLIC_ROUTES.DOWNLOAD_PREFIX; + const publicDownloadPath = basePath + PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX; // return task manager's task information and the download URL counters.usageCounter(); diff --git a/x-pack/plugins/reporting/server/routes/public/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts index 817a33ad432aa..8b6c3ea199462 100644 --- a/x-pack/plugins/reporting/server/routes/public/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/public/jobs.ts @@ -22,7 +22,7 @@ export function registerJobInfoRoutesPublic(reporting: ReportingCore) { const registerDownloadReport = () => { // trigger a download of the output from a job - const path = PUBLIC_ROUTES.DOWNLOAD_PREFIX + '/{docId}'; + const path = PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; router.get( { path, @@ -72,7 +72,7 @@ export function registerJobInfoRoutesPublic(reporting: ReportingCore) { const registerDeleteReport = () => { // allow a report to be deleted - const path = PUBLIC_ROUTES.DELETE_PREFIX + '/{docId}'; + const path = PUBLIC_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; router.delete( { path, From 5cfda74557806aebbb0283e2a2a300b20f8f6641 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 27 Jul 2023 08:07:33 -0700 Subject: [PATCH 40/47] fix references in tests --- x-pack/test/reporting_api_integration/services/scenarios.ts | 2 +- x-pack/test/reporting_api_integration/services/usage.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/reporting_api_integration/services/scenarios.ts b/x-pack/test/reporting_api_integration/services/scenarios.ts index ff6a64c5cf024..c7dd12cc5adab 100644 --- a/x-pack/test/reporting_api_integration/services/scenarios.ts +++ b/x-pack/test/reporting_api_integration/services/scenarios.ts @@ -134,7 +134,7 @@ export function createScenarios({ getService }: Pick { return await supertestWithoutAuth - .post(INTERNAL_ROUTES.GENERATE.CSV_IMMEDIATE) + .post(INTERNAL_ROUTES.DOWNLOAD_CSV) .auth(username, password) .set('kbn-xsrf', 'xxx') .send(job); diff --git a/x-pack/test/reporting_api_integration/services/usage.ts b/x-pack/test/reporting_api_integration/services/usage.ts index ff600735f62ea..6ede1dba18d2d 100644 --- a/x-pack/test/reporting_api_integration/services/usage.ts +++ b/x-pack/test/reporting_api_integration/services/usage.ts @@ -47,7 +47,7 @@ export function createUsageServices({ getService }: FtrProviderContext) { if (!ignoreFailure) { const jobInfo = await supertest.get( downloadReportPath.replace( - PUBLIC_ROUTES.DOWNLOAD_PREFIX, + PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX, INTERNAL_ROUTES.JOBS.INFO_PREFIX ) ); From 53ac996f7ed9e61426bbb4d4d1839f0c1ecf7f33 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 27 Jul 2023 11:59:28 -0700 Subject: [PATCH 41/47] consolidate duplicated code for job management routes --- .../routes/common/jobs/get_job_routes.ts | 114 ++++++++++++++++ .../server/routes/common/jobs/index.ts | 3 +- .../server/routes/internal/management/jobs.ts | 125 +++--------------- .../reporting/server/routes/public/jobs.ts | 107 +-------------- 4 files changed, 140 insertions(+), 209 deletions(-) create mode 100644 x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts new file mode 100644 index 0000000000000..e9a2f5ba7f569 --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; +import { promisify } from 'util'; +import { authorizedUserPreRouting, getCounters } from '..'; +import { ReportingCore } from '../../..'; +import { ALLOWED_JOB_CONTENT_TYPES } from '../../../../common/constants'; +import { getContentStream } from '../../../lib'; +import { handleUnavailable } from '../generate'; +import { jobsQueryFactory } from './jobs_query'; +import { jobManagementPreRouting } from './job_management_pre_routing'; + +export const getCommonJobManagementRoutes = (reporting: ReportingCore) => { + const setupDeps = reporting.getPluginSetupDeps(); + const { router } = setupDeps; + const jobsQuery = jobsQueryFactory(reporting); + + // TODO: add route options param + const registerDownloadReport = (path: string) => { + router.get( + { + path, + validate: { + params: schema.object({ + docId: schema.string({ minLength: 3 }), + }), + }, + options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return handleUnavailable(res); + } + + const { docId } = req.params; + + return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { + const payload = await jobsQuery.getDocumentPayload(doc); + const { contentType, content, filename, statusCode } = payload; + + if (!contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(contentType)) { + return res.badRequest({ + body: `Unsupported content-type of ${contentType} specified by job output`, + }); + } + + const body = typeof content === 'string' ? Buffer.from(content) : content; + + const headers = { + ...payload.headers, + 'content-type': contentType, + }; + + if (filename) { + return res.file({ body, headers, filename }); + } + + return res.custom({ body, headers, statusCode }); + }); + }) + ); + }; + + // TODO: add route options param + const registerDeleteReport = (path: string) => { + router.delete( + { + path, + validate: { + params: schema.object({ + docId: schema.string({ minLength: 3 }), + }), + }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return handleUnavailable(res); + } + + const { docId } = req.params; + + return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { + const docIndex = doc.index; + const stream = await getContentStream(reporting, { id: docId, index: docIndex }); + + /** @note Overwriting existing content with an empty buffer to remove all the chunks. */ + await promisify(stream.end.bind(stream, '', 'utf8'))(); + await jobsQuery.delete(docIndex, docId); + + return res.ok({ + body: { deleted: true }, + }); + }); + }) + ); + }; + + return { + registerDownloadReport, + registerDeleteReport, + }; +}; diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/index.ts b/x-pack/plugins/reporting/server/routes/common/jobs/index.ts index 36e661f3686e0..3db47295f402c 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/index.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/index.ts @@ -5,5 +5,6 @@ * 2.0. */ -export { jobsQueryFactory } from './jobs_query'; +export { getCommonJobManagementRoutes } from './get_job_routes'; export { jobManagementPreRouting } from './job_management_pre_routing'; +export { jobsQueryFactory } from './jobs_query'; diff --git a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts index 3e78f269387b1..6b02055573240 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts @@ -6,23 +6,27 @@ */ import { schema } from '@kbn/config-schema'; -import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; -import { promisify } from 'util'; import { ReportingCore } from '../../..'; -import { ALLOWED_JOB_CONTENT_TYPES, INTERNAL_ROUTES } from '../../../../common/constants'; -import { getContentStream } from '../../../lib'; +import { INTERNAL_ROUTES } from '../../../../common/constants'; import { authorizedUserPreRouting, getCounters } from '../../common'; import { handleUnavailable } from '../../common/generate'; -import { jobManagementPreRouting, jobsQueryFactory } from '../../common/jobs'; +import { + getCommonJobManagementRoutes, + jobManagementPreRouting, + jobsQueryFactory, +} from '../../common/jobs'; export function registerJobInfoRoutesInternal(reporting: ReportingCore) { + const commonRoutes = getCommonJobManagementRoutes(reporting); + commonRoutes.registerDownloadReport(INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'); + commonRoutes.registerDeleteReport(INTERNAL_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'); + const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; const jobsQuery = jobsQueryFactory(reporting); - const registerGetList = () => { - // list jobs in the queue, paginated - const path = INTERNAL_ROUTES.JOBS.LIST; + // list jobs in the queue, paginated + const registerGetList = (path: string) => { router.get( { path, @@ -63,9 +67,8 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - const registerGetCount = () => { - // return the count of all jobs in the queue - const path = INTERNAL_ROUTES.JOBS.COUNT; + // return the count of all jobs in the queue + const registerGetCount = (path: string) => { router.get( { path, validate: false }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { @@ -94,9 +97,9 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - const registerGetInfo = () => { + const registerGetInfo = (path: string) => { // return some info about the job - const path = INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/{docId}'; + router.get( { path, @@ -127,97 +130,7 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - const registerDownloadReport = () => { - // trigger a download of the output from a job - const path = INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; - router.get( - { - path, - validate: { - params: schema.object({ - docId: schema.string({ minLength: 3 }), - }), - }, - options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return handleUnavailable(res); - } - - const { docId } = req.params; - - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { - const payload = await jobsQuery.getDocumentPayload(doc); - const { contentType, content, filename, statusCode } = payload; - - if (!contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(contentType)) { - return res.badRequest({ - body: `Unsupported content-type of ${contentType} specified by job output`, - }); - } - - const body = typeof content === 'string' ? Buffer.from(content) : content; - - const headers = { - ...payload.headers, - 'content-type': contentType, - }; - - if (filename) { - return res.file({ body, headers, filename }); - } - - return res.custom({ body, headers, statusCode }); - }); - }) - ); - }; - - const registerDeleteReport = () => { - // allow a report to be deleted - const path = INTERNAL_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; - router.delete( - { - path, - validate: { - params: schema.object({ - docId: schema.string({ minLength: 3 }), - }), - }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return handleUnavailable(res); - } - - const { docId } = req.params; - - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { - const docIndex = doc.index; - const stream = await getContentStream(reporting, { id: docId, index: docIndex }); - - /** @note Overwriting existing content with an empty buffer to remove all the chunks. */ - await promisify(stream.end.bind(stream, '', 'utf8'))(); - await jobsQuery.delete(docIndex, docId); - - return res.ok({ - body: { deleted: true }, - }); - }); - }) - ); - }; - - registerGetList(); - registerGetCount(); - registerGetInfo(); - registerDownloadReport(); - registerDeleteReport(); + registerGetList(INTERNAL_ROUTES.JOBS.LIST); + registerGetCount(INTERNAL_ROUTES.JOBS.COUNT); + registerGetInfo(INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/{docId}'); } diff --git a/x-pack/plugins/reporting/server/routes/public/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts index 8b6c3ea199462..e01b53eb13b79 100644 --- a/x-pack/plugins/reporting/server/routes/public/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/public/jobs.ts @@ -5,109 +5,12 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; -import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; -import { promisify } from 'util'; import { ReportingCore } from '../..'; -import { ALLOWED_JOB_CONTENT_TYPES, PUBLIC_ROUTES } from '../../../common/constants'; -import { getContentStream } from '../../lib'; -import { authorizedUserPreRouting, getCounters } from '../common'; -import { handleUnavailable } from '../common/generate'; -import { jobManagementPreRouting, jobsQueryFactory } from '../common/jobs'; +import { PUBLIC_ROUTES } from '../../../common/constants'; +import { getCommonJobManagementRoutes } from '../common/jobs'; export function registerJobInfoRoutesPublic(reporting: ReportingCore) { - const setupDeps = reporting.getPluginSetupDeps(); - const { router } = setupDeps; - const jobsQuery = jobsQueryFactory(reporting); - - const registerDownloadReport = () => { - // trigger a download of the output from a job - const path = PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; - router.get( - { - path, - validate: { - params: schema.object({ - docId: schema.string({ minLength: 3 }), - }), - }, - options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return handleUnavailable(res); - } - - const { docId } = req.params; - - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { - const payload = await jobsQuery.getDocumentPayload(doc); - const { contentType, content, filename, statusCode } = payload; - - if (!contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(contentType)) { - return res.badRequest({ - body: `Unsupported content-type of ${contentType} specified by job output`, - }); - } - - const body = typeof content === 'string' ? Buffer.from(content) : content; - - const headers = { - ...payload.headers, - 'content-type': contentType, - }; - - if (filename) { - return res.file({ body, headers, filename }); - } - - return res.custom({ body, headers, statusCode }); - }); - }) - ); - }; - - const registerDeleteReport = () => { - // allow a report to be deleted - const path = PUBLIC_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; - router.delete( - { - path, - validate: { - params: schema.object({ - docId: schema.string({ minLength: 3 }), - }), - }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return handleUnavailable(res); - } - - const { docId } = req.params; - - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { - const docIndex = doc.index; - const stream = await getContentStream(reporting, { id: docId, index: docIndex }); - - /** @note Overwriting existing content with an empty buffer to remove all the chunks. */ - await promisify(stream.end.bind(stream, '', 'utf8'))(); - await jobsQuery.delete(docIndex, docId); - - return res.ok({ - body: { deleted: true }, - }); - }); - }) - ); - }; - - registerDownloadReport(); - registerDeleteReport(); + const commonRoutes = getCommonJobManagementRoutes(reporting); + commonRoutes.registerDownloadReport(PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'); + commonRoutes.registerDeleteReport(PUBLIC_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'); } From 4031a4d3f762f33b43db25696a165923f5a0620b Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 27 Jul 2023 16:36:08 -0700 Subject: [PATCH 42/47] Use common job management route handlers --- .../routes/common/jobs/get_job_routes.ts | 164 ++++++++---------- .../server/routes/common/jobs/index.ts | 2 +- .../server/routes/internal/management/jobs.ts | 66 +++++-- .../reporting/server/routes/public/jobs.ts | 44 ++++- 4 files changed, 165 insertions(+), 111 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts index e9a2f5ba7f569..e11868da9eb4b 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts @@ -5,110 +5,98 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; -import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; +import { schema, TypeOf } from '@kbn/config-schema'; +import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server'; import { promisify } from 'util'; -import { authorizedUserPreRouting, getCounters } from '..'; +import { getCounters } from '..'; import { ReportingCore } from '../../..'; import { ALLOWED_JOB_CONTENT_TYPES } from '../../../../common/constants'; import { getContentStream } from '../../../lib'; +import { ReportingRequestHandlerContext, ReportingUser } from '../../../types'; import { handleUnavailable } from '../generate'; import { jobsQueryFactory } from './jobs_query'; import { jobManagementPreRouting } from './job_management_pre_routing'; -export const getCommonJobManagementRoutes = (reporting: ReportingCore) => { - const setupDeps = reporting.getPluginSetupDeps(); - const { router } = setupDeps; +const validate = { + params: schema.object({ + docId: schema.string({ minLength: 3 }), + }), +}; + +interface HandlerOpts { + path: string; + user: ReportingUser; + context: ReportingRequestHandlerContext; + req: KibanaRequest>; + res: KibanaResponseFactory; +} + +export const commonJobsRouteHandlerFactory = (reporting: ReportingCore) => { const jobsQuery = jobsQueryFactory(reporting); - // TODO: add route options param - const registerDownloadReport = (path: string) => { - router.get( - { - path, - validate: { - params: schema.object({ - docId: schema.string({ minLength: 3 }), - }), - }, - options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return handleUnavailable(res); - } - - const { docId } = req.params; - - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { - const payload = await jobsQuery.getDocumentPayload(doc); - const { contentType, content, filename, statusCode } = payload; - - if (!contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(contentType)) { - return res.badRequest({ - body: `Unsupported content-type of ${contentType} specified by job output`, - }); - } - - const body = typeof content === 'string' ? Buffer.from(content) : content; - - const headers = { - ...payload.headers, - 'content-type': contentType, - }; - - if (filename) { - return res.file({ body, headers, filename }); - } - - return res.custom({ body, headers, statusCode }); + const handleDownloadReport = ({ path, user, context, req, res }: HandlerOpts) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return handleUnavailable(res); + } + + const { docId } = req.params; + + return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { + const payload = await jobsQuery.getDocumentPayload(doc); + const { contentType, content, filename, statusCode } = payload; + + if (!contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(contentType)) { + return res.badRequest({ + body: `Unsupported content-type of ${contentType} specified by job output`, }); - }) - ); + } + + const body = typeof content === 'string' ? Buffer.from(content) : content; + + const headers = { + ...payload.headers, + 'content-type': contentType, + }; + + if (filename) { + return res.file({ body, headers, filename }); + } + + return res.custom({ body, headers, statusCode }); + }); }; // TODO: add route options param - const registerDeleteReport = (path: string) => { - router.delete( - { - path, - validate: { - params: schema.object({ - docId: schema.string({ minLength: 3 }), - }), - }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); - - // ensure the async dependencies are loaded - if (!context.reporting) { - return handleUnavailable(res); - } - - const { docId } = req.params; - - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { - const docIndex = doc.index; - const stream = await getContentStream(reporting, { id: docId, index: docIndex }); - - /** @note Overwriting existing content with an empty buffer to remove all the chunks. */ - await promisify(stream.end.bind(stream, '', 'utf8'))(); - await jobsQuery.delete(docIndex, docId); - - return res.ok({ - body: { deleted: true }, - }); - }); - }) - ); + const handleDeleteReport = ({ path, user, context, req, res }: HandlerOpts) => { + const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); + + // ensure the async dependencies are loaded + if (!context.reporting) { + return handleUnavailable(res); + } + + const { docId } = req.params; + + return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { + const docIndex = doc.index; + const stream = await getContentStream(reporting, { id: docId, index: docIndex }); + + /** @note Overwriting existing content with an empty buffer to remove all the chunks. */ + await promisify(stream.end.bind(stream, '', 'utf8'))(); + await jobsQuery.delete(docIndex, docId); + + return res.ok({ + body: { deleted: true }, + }); + }); }; return { - registerDownloadReport, - registerDeleteReport, + validate, + handleDownloadReport, + handleDeleteReport, }; }; diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/index.ts b/x-pack/plugins/reporting/server/routes/common/jobs/index.ts index 3db47295f402c..9cc52f8fb35b6 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/index.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/index.ts @@ -5,6 +5,6 @@ * 2.0. */ -export { getCommonJobManagementRoutes } from './get_job_routes'; +export { commonJobsRouteHandlerFactory } from './get_job_routes'; export { jobManagementPreRouting } from './job_management_pre_routing'; export { jobsQueryFactory } from './jobs_query'; diff --git a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts index 6b02055573240..dc06d458700a2 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts @@ -6,27 +6,25 @@ */ import { schema } from '@kbn/config-schema'; +import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; import { ReportingCore } from '../../..'; import { INTERNAL_ROUTES } from '../../../../common/constants'; import { authorizedUserPreRouting, getCounters } from '../../common'; import { handleUnavailable } from '../../common/generate'; import { - getCommonJobManagementRoutes, + commonJobsRouteHandlerFactory, jobManagementPreRouting, jobsQueryFactory, } from '../../common/jobs'; export function registerJobInfoRoutesInternal(reporting: ReportingCore) { - const commonRoutes = getCommonJobManagementRoutes(reporting); - commonRoutes.registerDownloadReport(INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'); - commonRoutes.registerDeleteReport(INTERNAL_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'); - const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; const jobsQuery = jobsQueryFactory(reporting); - // list jobs in the queue, paginated - const registerGetList = (path: string) => { + const registerGetList = () => { + // list jobs in the queue, paginated + const path = INTERNAL_ROUTES.JOBS.LIST; router.get( { path, @@ -68,7 +66,9 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { }; // return the count of all jobs in the queue - const registerGetCount = (path: string) => { + const registerGetCount = () => { + // return the count of all jobs in the queue + const path = INTERNAL_ROUTES.JOBS.COUNT; router.get( { path, validate: false }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { @@ -97,17 +97,16 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - const registerGetInfo = (path: string) => { - // return some info about the job + // use common route handlers that are shared for public and internal routes + const jobHandlers = commonJobsRouteHandlerFactory(reporting); + const registerGetInfo = () => { + // return some info about the job + const path = INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/{docId}'; router.get( { path, - validate: { - params: schema.object({ - docId: schema.string({ minLength: 2 }), - }), - }, + validate: jobHandlers.validate, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); @@ -130,7 +129,38 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - registerGetList(INTERNAL_ROUTES.JOBS.LIST); - registerGetCount(INTERNAL_ROUTES.JOBS.COUNT); - registerGetInfo(INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/{docId}'); + const registerDownloadReport = () => { + // trigger a download of the output from a job + const path = INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; + router.get( + { + path, + validate: jobHandlers.validate, + options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + return jobHandlers.handleDownloadReport({ path, user, context, req, res }); + }) + ); + }; + + const registerDeleteReport = () => { + // allow a report to be deleted + const path = INTERNAL_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; + router.delete( + { + path, + validate: jobHandlers.validate, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + return jobHandlers.handleDeleteReport({ path, user, context, req, res }); + }) + ); + }; + + registerGetList(); + registerGetCount(); + registerGetInfo(); + registerDownloadReport(); + registerDeleteReport(); } diff --git a/x-pack/plugins/reporting/server/routes/public/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts index e01b53eb13b79..22cf46db08892 100644 --- a/x-pack/plugins/reporting/server/routes/public/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/public/jobs.ts @@ -5,12 +5,48 @@ * 2.0. */ +import { ROUTE_TAG_CAN_REDIRECT } from '@kbn/security-plugin/server'; import { ReportingCore } from '../..'; import { PUBLIC_ROUTES } from '../../../common/constants'; -import { getCommonJobManagementRoutes } from '../common/jobs'; +import { authorizedUserPreRouting } from '../common'; +import { commonJobsRouteHandlerFactory } from '../common/jobs'; export function registerJobInfoRoutesPublic(reporting: ReportingCore) { - const commonRoutes = getCommonJobManagementRoutes(reporting); - commonRoutes.registerDownloadReport(PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'); - commonRoutes.registerDeleteReport(PUBLIC_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'); + const setupDeps = reporting.getPluginSetupDeps(); + const { router } = setupDeps; + + // use common route handlers that are shared for public and internal routes + const jobHandlers = commonJobsRouteHandlerFactory(reporting); + + const registerDownloadReport = () => { + // trigger a download of the output from a job + const path = PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; + router.get( + { + path, + validate: jobHandlers.validate, + options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + return jobHandlers.handleDownloadReport({ path, user, context, req, res }); + }) + ); + }; + + const registerDeleteReport = () => { + // allow a report to be deleted + const path = PUBLIC_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; + router.delete( + { + path, + validate: jobHandlers.validate, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + return jobHandlers.handleDeleteReport({ path, user, context, req, res }); + }) + ); + }; + + registerDownloadReport(); + registerDeleteReport(); } From 5122ed637b0a0041215cf85dbd6c8867eb14ab9f Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 27 Jul 2023 16:42:49 -0700 Subject: [PATCH 43/47] add access:public to public endpoints --- .../reporting/server/routes/public/generate_from_jobparams.ts | 3 ++- x-pack/plugins/reporting/server/routes/public/jobs.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts index b5868e8d93cc2..c3a8e886302f5 100644 --- a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts @@ -24,7 +24,7 @@ export function registerGenerationRoutesPublic(reporting: ReportingCore, logger: { path, validate: RequestHandler.getValidation(), - options: { tags: kibanaAccessControlTags }, + options: { tags: kibanaAccessControlTags, access: 'public' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { try { @@ -47,6 +47,7 @@ export function registerGenerationRoutesPublic(reporting: ReportingCore, logger: { path: `${PUBLIC_ROUTES.GENERATE_PREFIX}/{p*}`, validate: false, + options: { access: 'public' }, }, (_context, _req, res) => { return res.customError({ statusCode: 405, body: 'GET is not allowed' }); diff --git a/x-pack/plugins/reporting/server/routes/public/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts index 22cf46db08892..5f90e77c9520f 100644 --- a/x-pack/plugins/reporting/server/routes/public/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/public/jobs.ts @@ -25,7 +25,7 @@ export function registerJobInfoRoutesPublic(reporting: ReportingCore) { { path, validate: jobHandlers.validate, - options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, + options: { tags: [ROUTE_TAG_CAN_REDIRECT], access: 'public' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { return jobHandlers.handleDownloadReport({ path, user, context, req, res }); @@ -40,6 +40,7 @@ export function registerJobInfoRoutesPublic(reporting: ReportingCore) { { path, validate: jobHandlers.validate, + options: { access: 'public' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { return jobHandlers.handleDeleteReport({ path, user, context, req, res }); From 760bceb3d361e9981b383c149437348cb786eb85 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 27 Jul 2023 17:06:43 -0700 Subject: [PATCH 44/47] polish diff --- .../generate/generate_from_jobparams.ts | 52 ++++++++----- .../server/routes/internal/management/jobs.ts | 42 ++++++---- .../routes/public/generate_from_jobparams.ts | 77 +++++++++++-------- .../reporting/server/routes/public/jobs.ts | 8 +- 4 files changed, 108 insertions(+), 71 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index a1f7eeefc2750..03f437925dd92 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -12,6 +12,8 @@ import { INTERNAL_ROUTES } from '../../../../common/constants'; import { authorizedUserPreRouting } from '../../common'; import { RequestHandler } from '../../common/generate'; +const { GENERATE_PREFIX } = INTERNAL_ROUTES; + export function registerGenerationRoutesInternal(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; @@ -23,24 +25,36 @@ export function registerGenerationRoutesInternal(reporting: ReportingCore, logge const useKibanaAccessControl = reporting.getDeprecatedAllowedRoles() === false; // true if Reporting's deprecated access control feature is disabled const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; - const path = `${INTERNAL_ROUTES.GENERATE_PREFIX}/{exportType}`; - router.post( - { - path, - validate: RequestHandler.getValidation(), - options: { tags: kibanaAccessControlTags }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - try { - const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); - const jobParams = requestHandler.getJobParams(); - return await requestHandler.handleGenerateRequest(req.params.exportType, jobParams); - } catch (err) { - if (err instanceof KibanaResponse) { - return err; + const registerInternalPostGenerationEndpoint = () => { + const path = `${GENERATE_PREFIX}/{exportType}`; + router.post( + { + path, + validate: RequestHandler.getValidation(), + options: { tags: kibanaAccessControlTags }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + try { + const requestHandler = new RequestHandler( + reporting, + user, + context, + path, + req, + res, + logger + ); + const jobParams = requestHandler.getJobParams(); + return await requestHandler.handleGenerateRequest(req.params.exportType, jobParams); + } catch (err) { + if (err instanceof KibanaResponse) { + return err; + } + throw err; } - throw err; - } - }) - ); + }) + ); + }; + + registerInternalPostGenerationEndpoint(); } diff --git a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts index dc06d458700a2..7be5a65d8eafc 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts @@ -17,14 +17,16 @@ import { jobsQueryFactory, } from '../../common/jobs'; +const { JOBS } = INTERNAL_ROUTES; + export function registerJobInfoRoutesInternal(reporting: ReportingCore) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; const jobsQuery = jobsQueryFactory(reporting); - const registerGetList = () => { + const registerInternalGetList = () => { // list jobs in the queue, paginated - const path = INTERNAL_ROUTES.JOBS.LIST; + const path = JOBS.LIST; router.get( { path, @@ -65,12 +67,15 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - // return the count of all jobs in the queue - const registerGetCount = () => { + const registerInternalGetCount = () => { // return the count of all jobs in the queue - const path = INTERNAL_ROUTES.JOBS.COUNT; + const path = JOBS.COUNT; + router.get( - { path, validate: false }, + { + path, + validate: false, + }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); @@ -100,9 +105,10 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { // use common route handlers that are shared for public and internal routes const jobHandlers = commonJobsRouteHandlerFactory(reporting); - const registerGetInfo = () => { + const registerInternalGetInfo = () => { // return some info about the job - const path = INTERNAL_ROUTES.JOBS.INFO_PREFIX + '/{docId}'; + const path = `${JOBS.INFO_PREFIX}/{docId}`; + router.get( { path, @@ -129,9 +135,10 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - const registerDownloadReport = () => { + const registerInternalDownloadReport = () => { // trigger a download of the output from a job - const path = INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; + const path = `${JOBS.DOWNLOAD_PREFIX}/{docId}`; + router.get( { path, @@ -144,9 +151,10 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - const registerDeleteReport = () => { + const registerInternalDeleteReport = () => { // allow a report to be deleted - const path = INTERNAL_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; + const path = `${JOBS.DELETE_PREFIX}/{docId}`; + router.delete( { path, @@ -158,9 +166,9 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ); }; - registerGetList(); - registerGetCount(); - registerGetInfo(); - registerDownloadReport(); - registerDeleteReport(); + registerInternalGetList(); + registerInternalGetCount(); + registerInternalGetInfo(); + registerInternalDownloadReport(); + registerInternalDeleteReport(); } diff --git a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts index c3a8e886302f5..512b39935de5e 100644 --- a/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/public/generate_from_jobparams.ts @@ -19,38 +19,53 @@ export function registerGenerationRoutesPublic(reporting: ReportingCore, logger: const useKibanaAccessControl = reporting.getDeprecatedAllowedRoles() === false; // true if Reporting's deprecated access control feature is disabled const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; - const path = `${PUBLIC_ROUTES.GENERATE_PREFIX}/{exportType}`; - router.post( - { - path, - validate: RequestHandler.getValidation(), - options: { tags: kibanaAccessControlTags, access: 'public' }, - }, - authorizedUserPreRouting(reporting, async (user, context, req, res) => { - try { - const requestHandler = new RequestHandler(reporting, user, context, path, req, res, logger); - return await requestHandler.handleGenerateRequest( - req.params.exportType, - requestHandler.getJobParams() - ); - } catch (err) { - if (err instanceof KibanaResponse) { - return err; + const registerPublicPostGenerationEndpoint = () => { + const path = `${PUBLIC_ROUTES.GENERATE_PREFIX}/{exportType}`; + router.post( + { + path, + validate: RequestHandler.getValidation(), + options: { tags: kibanaAccessControlTags, access: 'public' }, + }, + authorizedUserPreRouting(reporting, async (user, context, req, res) => { + try { + const requestHandler = new RequestHandler( + reporting, + user, + context, + path, + req, + res, + logger + ); + return await requestHandler.handleGenerateRequest( + req.params.exportType, + requestHandler.getJobParams() + ); + } catch (err) { + if (err instanceof KibanaResponse) { + return err; + } + throw err; } - throw err; + }) + ); + }; + + const registerPublicGetGenerationEndpoint = () => { + // Get route to generation endpoint: show error about GET method to user + router.get( + { + path: `${PUBLIC_ROUTES.GENERATE_PREFIX}/{p*}`, + validate: false, + options: { access: 'public' }, + }, + (_context, _req, res) => { + return res.customError({ statusCode: 405, body: 'GET is not allowed' }); } - }) - ); + ); + }; - // Get route to generation endpoint: show error about GET method to user - router.get( - { - path: `${PUBLIC_ROUTES.GENERATE_PREFIX}/{p*}`, - validate: false, - options: { access: 'public' }, - }, - (_context, _req, res) => { - return res.customError({ statusCode: 405, body: 'GET is not allowed' }); - } - ); + registerPublicPostGenerationEndpoint(); + registerPublicGetGenerationEndpoint(); } diff --git a/x-pack/plugins/reporting/server/routes/public/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts index 5f90e77c9520f..7b56e4393a31a 100644 --- a/x-pack/plugins/reporting/server/routes/public/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/public/jobs.ts @@ -18,7 +18,7 @@ export function registerJobInfoRoutesPublic(reporting: ReportingCore) { // use common route handlers that are shared for public and internal routes const jobHandlers = commonJobsRouteHandlerFactory(reporting); - const registerDownloadReport = () => { + const registerPublicDownloadReport = () => { // trigger a download of the output from a job const path = PUBLIC_ROUTES.JOBS.DOWNLOAD_PREFIX + '/{docId}'; router.get( @@ -33,7 +33,7 @@ export function registerJobInfoRoutesPublic(reporting: ReportingCore) { ); }; - const registerDeleteReport = () => { + const registerPublicDeleteReport = () => { // allow a report to be deleted const path = PUBLIC_ROUTES.JOBS.DELETE_PREFIX + '/{docId}'; router.delete( @@ -48,6 +48,6 @@ export function registerJobInfoRoutesPublic(reporting: ReportingCore) { ); }; - registerDownloadReport(); - registerDeleteReport(); + registerPublicDownloadReport(); + registerPublicDeleteReport(); } From 5afefd4e309bfa7db94474a04622d6f2a2f8546d Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 28 Jul 2023 10:10:40 -0700 Subject: [PATCH 45/47] remove unimportant TODOs --- .../reporting/server/routes/common/jobs/get_job_routes.ts | 1 - .../routes/internal/generate/csv_searchsource_immediate.ts | 4 ---- .../routes/internal/generate/generate_from_jobparams.ts | 4 ---- 3 files changed, 9 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts index e11868da9eb4b..c0d29849e94f3 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts @@ -69,7 +69,6 @@ export const commonJobsRouteHandlerFactory = (reporting: ReportingCore) => { }); }; - // TODO: add route options param const handleDeleteReport = ({ path, user, context, req, res }: HandlerOpts) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts index f213dc8eb8127..e859033b60a4e 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts @@ -35,10 +35,6 @@ export function registerGenerateCsvFromSavedObjectImmediate( const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; - // TODO: find a way to abstract this using ExportTypeRegistry: it needs a new - // public method to return this array - // const registry = reporting.getExportTypesRegistry(); - // const kibanaAccessControlTags = registry.getAllAccessControlTags(); const useKibanaAccessControl = reporting.getDeprecatedAllowedRoles() === false; // true if deprecated config is turned off const kibanaAccessControlTags = useKibanaAccessControl ? ['access:downloadCsv'] : []; diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index 03f437925dd92..0bb571e32a2fa 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -18,10 +18,6 @@ export function registerGenerationRoutesInternal(reporting: ReportingCore, logge const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; - // TODO: find a way to abstract this using ExportTypeRegistry: it needs a new - // public method to return this array - // const registry = reporting.getExportTypesRegistry(); - // const kibanaAccessControlTags = registry.getAllAccessControlTags(); const useKibanaAccessControl = reporting.getDeprecatedAllowedRoles() === false; // true if Reporting's deprecated access control feature is disabled const kibanaAccessControlTags = useKibanaAccessControl ? ['access:generateReport'] : []; From 0400bc7911bff0192a61a4b3bbc1bdfd972aa4d2 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 28 Jul 2023 10:33:18 -0700 Subject: [PATCH 46/47] add jest testing for requestHandler.getJobParams --- .../common/generate/request_handler.test.ts | 118 ++++++++++++------ 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/common/generate/request_handler.test.ts b/x-pack/plugins/reporting/server/routes/common/generate/request_handler.test.ts index fe1f6b38cc79d..5d6ba3f20a004 100644 --- a/x-pack/plugins/reporting/server/routes/common/generate/request_handler.test.ts +++ b/x-pack/plugins/reporting/server/routes/common/generate/request_handler.test.ts @@ -6,6 +6,7 @@ */ import { KibanaRequest, KibanaResponseFactory } from '@kbn/core/server'; +import rison from '@kbn/rison'; import { coreMock, httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { ReportingCore } from '../../..'; import { TaskPayloadPDFV2 } from '../../../../common/types/export_types/printable_pdf_v2'; @@ -42,6 +43,7 @@ const getMockResponseFactory = () => ...httpServerMock.createResponseFactory(), forbidden: (obj: unknown) => obj, unauthorized: (obj: unknown) => obj, + customError: (err: unknown) => err, } as unknown as KibanaResponseFactory); const mockLogger = loggingSystemMock.createLogger(); @@ -155,58 +157,101 @@ describe('Handle request to generate', () => { }); }); - test('disallows invalid export type', async () => { - expect(await requestHandler.handleGenerateRequest('neanderthals', mockJobParams)) - .toMatchInlineSnapshot(` + describe('getJobParams', () => { + test('parse jobParams from query string', () => { + // @ts-ignore query is a read-only property + mockRequest.query = { jobParams: rison.encode(mockJobParams) }; + expect(requestHandler.getJobParams()).toEqual(mockJobParams); + }); + + test('parse jobParams from body', () => { + // @ts-ignore body is a read-only property + mockRequest.body = { jobParams: rison.encode(mockJobParams) }; + expect(requestHandler.getJobParams()).toEqual(mockJobParams); + }); + + test('handles missing job params', () => { + try { + requestHandler.getJobParams(); + } catch (err) { + expect(err.statusCode).toBe(400); + } + }); + + test('handles null job params', () => { + try { + // @ts-ignore body is a read-only property + mockRequest.body = { jobParams: rison.encode(null) }; + requestHandler.getJobParams(); + } catch (err) { + expect(err.statusCode).toBe(400); + } + }); + + test('handles invalid rison', () => { + // @ts-ignore body is a read-only property + mockRequest.body = { jobParams: mockJobParams }; + try { + requestHandler.getJobParams(); + } catch (err) { + expect(err.statusCode).toBe(400); + } + }); + }); + + describe('handleGenerateRequest', () => { + test('disallows invalid export type', async () => { + expect(await requestHandler.handleGenerateRequest('neanderthals', mockJobParams)) + .toMatchInlineSnapshot(` Object { "body": "Invalid export-type of neanderthals", } `); - }); + }); - test('disallows unsupporting license', async () => { - (reportingCore.getLicenseInfo as jest.Mock) = jest.fn(() => ({ - csv_searchsource: { - enableLinks: false, - message: `seeing this means the license isn't supported`, - }, - })); + test('disallows unsupporting license', async () => { + (reportingCore.getLicenseInfo as jest.Mock) = jest.fn(() => ({ + csv_searchsource: { + enableLinks: false, + message: `seeing this means the license isn't supported`, + }, + })); - expect(await requestHandler.handleGenerateRequest('csv_searchsource', mockJobParams)) - .toMatchInlineSnapshot(` + expect(await requestHandler.handleGenerateRequest('csv_searchsource', mockJobParams)) + .toMatchInlineSnapshot(` Object { "body": "seeing this means the license isn't supported", } `); - }); + }); - test('disallows invalid browser timezone', async () => { - (reportingCore.getLicenseInfo as jest.Mock) = jest.fn(() => ({ - csv_searchsource: { - enableLinks: false, - message: `seeing this means the license isn't supported`, - }, - })); - - expect( - await requestHandler.handleGenerateRequest('csv_searchsource', { - ...mockJobParams, - browserTimezone: 'America/Amsterdam', - }) - ).toMatchInlineSnapshot(` + test('disallows invalid browser timezone', async () => { + (reportingCore.getLicenseInfo as jest.Mock) = jest.fn(() => ({ + csv_searchsource: { + enableLinks: false, + message: `seeing this means the license isn't supported`, + }, + })); + + expect( + await requestHandler.handleGenerateRequest('csv_searchsource', { + ...mockJobParams, + browserTimezone: 'America/Amsterdam', + }) + ).toMatchInlineSnapshot(` Object { "body": "seeing this means the license isn't supported", } `); - }); + }); - test('generates the download path', async () => { - const response = (await requestHandler.handleGenerateRequest( - 'csv_searchsource', - mockJobParams - )) as unknown as { body: { job: ReportApiJSON } }; - const { id, created_at: _created_at, ...snapObj } = response.body.job; - expect(snapObj).toMatchInlineSnapshot(` + test('generates the download path', async () => { + const response = (await requestHandler.handleGenerateRequest( + 'csv_searchsource', + mockJobParams + )) as unknown as { body: { job: ReportApiJSON } }; + const { id, created_at: _created_at, ...snapObj } = response.body.job; + expect(snapObj).toMatchInlineSnapshot(` Object { "attempts": 0, "completed_at": undefined, @@ -242,5 +287,6 @@ describe('Handle request to generate', () => { "timeout": undefined, } `); + }); }); }); From 9bbc650b5aaad8011b7843a17468e797a2ff288d Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 28 Jul 2023 11:12:30 -0700 Subject: [PATCH 47/47] add access:internal option to internal route registrations --- .../routes/internal/deprecations/deprecations.ts | 12 ++++++++++-- .../server/routes/internal/diagnostic/browser.ts | 6 +++++- .../server/routes/internal/diagnostic/screenshot.ts | 6 +++++- .../internal/generate/csv_searchsource_immediate.ts | 4 +--- .../internal/generate/generate_from_jobparams.ts | 2 +- .../server/routes/internal/management/jobs.ts | 6 +++++- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts index a4b217166d1a5..04ff2474674d1 100644 --- a/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/internal/deprecations/deprecations.ts @@ -57,7 +57,11 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log const getStatusPath = INTERNAL_ROUTES.MIGRATE.GET_ILM_POLICY_STATUS; router.get( - { path: getStatusPath, validate: false }, + { + path: getStatusPath, + validate: false, + options: { access: 'internal' }, + }, authzWrapper(async ({ core }, req, res) => { const counters = getCounters(req.route.method, getStatusPath, reporting.getUsageCounter()); @@ -94,7 +98,11 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log const migrateApiPath = INTERNAL_ROUTES.MIGRATE.MIGRATE_ILM_POLICY; router.put( - { path: migrateApiPath, validate: false }, + { + path: migrateApiPath, + validate: false, + options: { access: 'internal' }, + }, authzWrapper(async ({ core }, req, res) => { const counters = getCounters(req.route.method, migrateApiPath, reporting.getUsageCounter()); diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts index edd3302237cd7..34c7583f9ef06 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/browser.ts @@ -41,7 +41,11 @@ export const registerDiagnoseBrowser = (reporting: ReportingCore, logger: Logger const { router } = reporting.getPluginSetupDeps(); router.post( - { path, validate: {} }, + { + path, + validate: {}, + options: { access: 'internal' }, + }, authorizedUserPreRouting(reporting, async (_user, _context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts index c1be4e9dda720..10bccd6dd5941 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/screenshot.ts @@ -22,7 +22,11 @@ export const registerDiagnoseScreenshot = (reporting: ReportingCore, logger: Log const { router } = setupDeps; router.post( - { path, validate: {} }, + { + path, + validate: {}, + options: { access: 'internal' }, + }, authorizedUserPreRouting(reporting, async (_user, _context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts index e859033b60a4e..90fc2d01dba66 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/csv_searchsource_immediate.ts @@ -55,9 +55,7 @@ export function registerGenerateCsvFromSavedObjectImmediate( version: schema.maybe(schema.string()), }), }, - options: { - tags: kibanaAccessControlTags, - }, + options: { tags: kibanaAccessControlTags, access: 'internal' }, }, authorizedUserPreRouting( reporting, diff --git a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts index 0bb571e32a2fa..4dbd187069e58 100644 --- a/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/internal/generate/generate_from_jobparams.ts @@ -27,7 +27,7 @@ export function registerGenerationRoutesInternal(reporting: ReportingCore, logge { path, validate: RequestHandler.getValidation(), - options: { tags: kibanaAccessControlTags }, + options: { tags: kibanaAccessControlTags, access: 'internal' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { try { diff --git a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts index 7be5a65d8eafc..717ac8db9b104 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts @@ -37,6 +37,7 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { ids: schema.maybe(schema.string()), }), }, + options: { access: 'internal' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); @@ -75,6 +76,7 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { { path, validate: false, + options: { access: 'internal' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); @@ -113,6 +115,7 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { { path, validate: jobHandlers.validate, + options: { access: 'internal' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); @@ -143,7 +146,7 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { { path, validate: jobHandlers.validate, - options: { tags: [ROUTE_TAG_CAN_REDIRECT] }, + options: { tags: [ROUTE_TAG_CAN_REDIRECT], access: 'internal' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { return jobHandlers.handleDownloadReport({ path, user, context, req, res }); @@ -159,6 +162,7 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { { path, validate: jobHandlers.validate, + options: { access: 'internal' }, }, authorizedUserPreRouting(reporting, async (user, context, req, res) => { return jobHandlers.handleDeleteReport({ path, user, context, req, res });