diff --git a/x-pack/legacy/plugins/rollup/common/index.ts b/x-pack/legacy/plugins/rollup/common/index.ts index 800da79552a57..4229803462203 100644 --- a/x-pack/legacy/plugins/rollup/common/index.ts +++ b/x-pack/legacy/plugins/rollup/common/index.ts @@ -4,12 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ +import { LICENSE_TYPE_BASIC, LicenseType } from '../../../common/constants'; + export const PLUGIN = { ID: 'rollup', + MINIMUM_LICENSE_REQUIRED: LICENSE_TYPE_BASIC as LicenseType, + getI18nName: (i18n: any): string => { + return i18n.translate('xpack.rollupJobs.appName', { + defaultMessage: 'Rollup jobs', + }); + }, }; export const CONFIG_ROLLUPS = 'rollups:enableIndexPatterns'; +export const API_BASE_PATH = '/api/rollup'; + export { UIM_APP_NAME, UIM_APP_LOAD, diff --git a/x-pack/legacy/plugins/rollup/index.js b/x-pack/legacy/plugins/rollup/index.ts similarity index 57% rename from x-pack/legacy/plugins/rollup/index.js rename to x-pack/legacy/plugins/rollup/index.ts index cace3bba1592b..7548af23b3aae 100644 --- a/x-pack/legacy/plugins/rollup/index.js +++ b/x-pack/legacy/plugins/rollup/index.ts @@ -5,20 +5,13 @@ */ import { resolve } from 'path'; -import { PLUGIN, CONFIG_ROLLUPS } from './common'; -import { registerLicenseChecker } from './server/lib/register_license_checker'; -import { rollupDataEnricher } from './rollup_data_enricher'; -import { registerRollupSearchStrategy } from './server/lib/search_strategies'; -import { - registerIndicesRoute, - registerFieldsForWildcardRoute, - registerSearchRoute, - registerJobsRoute, -} from './server/routes/api'; -import { registerRollupUsageCollector } from './server/usage'; import { i18n } from '@kbn/i18n'; +import { PluginInitializerContext } from 'src/core/server'; +import { RollupSetup } from '../../../plugins/rollup/server'; +import { PLUGIN, CONFIG_ROLLUPS } from './common'; +import { plugin } from './server'; -export function rollup(kibana) { +export function rollup(kibana: any) { return new kibana.Plugin({ id: PLUGIN.ID, configPrefix: 'xpack.rollup', @@ -45,22 +38,30 @@ export function rollup(kibana) { visualize: ['plugins/rollup/legacy'], search: ['plugins/rollup/legacy'], }, - init: function(server) { - const { usageCollection } = server.newPlatform.setup.plugins; - registerLicenseChecker(server); - registerIndicesRoute(server); - registerFieldsForWildcardRoute(server); - registerSearchRoute(server); - registerJobsRoute(server); - registerRollupUsageCollector(usageCollection, server); - if ( - server.plugins.index_management && - server.plugins.index_management.addIndexManagementDataEnricher - ) { - server.plugins.index_management.addIndexManagementDataEnricher(rollupDataEnricher); - } + init(server: any) { + const { core: coreSetup, plugins } = server.newPlatform.setup; + const { usageCollection, metrics } = plugins; + + const rollupSetup = (plugins.rollup as unknown) as RollupSetup; - registerRollupSearchStrategy(this.kbnServer); + const initContext = ({ + config: rollupSetup.__legacy.config, + logger: rollupSetup.__legacy.logger, + } as unknown) as PluginInitializerContext; + + const rollupPluginInstance = plugin(initContext); + + rollupPluginInstance.setup(coreSetup, { + usageCollection, + metrics, + __LEGACY: { + plugins: { + xpack_main: server.plugins.xpack_main, + rollup: server.plugins[PLUGIN.ID], + index_management: server.plugins.index_management, + }, + }, + }); }, }); } diff --git a/x-pack/legacy/plugins/rollup/kibana.json b/x-pack/legacy/plugins/rollup/kibana.json new file mode 100644 index 0000000000000..3781d59d8c0f3 --- /dev/null +++ b/x-pack/legacy/plugins/rollup/kibana.json @@ -0,0 +1,14 @@ +{ + "id": "rollup", + "version": "kibana", + "requiredPlugins": [ + "home", + "index_management", + "metrics" + ], + "optionalPlugins": [ + "usageCollection" + ], + "server": true, + "ui": false +} diff --git a/x-pack/legacy/plugins/rollup/server/client/elasticsearch_rollup.js b/x-pack/legacy/plugins/rollup/server/client/elasticsearch_rollup.ts similarity index 96% rename from x-pack/legacy/plugins/rollup/server/client/elasticsearch_rollup.js rename to x-pack/legacy/plugins/rollup/server/client/elasticsearch_rollup.ts index 3b073cd2139c1..840f66a056d2d 100644 --- a/x-pack/legacy/plugins/rollup/server/client/elasticsearch_rollup.js +++ b/x-pack/legacy/plugins/rollup/server/client/elasticsearch_rollup.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const elasticsearchJsPlugin = (Client, config, components) => { +export const elasticsearchJsPlugin = (Client: any, config: any, components: any) => { const ca = components.clientAction.factory; Client.prototype.rollup = components.clientAction.namespaceFactory(); diff --git a/x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/index.js b/x-pack/legacy/plugins/rollup/server/collectors/index.ts similarity index 80% rename from x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/index.js rename to x-pack/legacy/plugins/rollup/server/collectors/index.ts index 441648a8701e0..47c1bcb6c7248 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/index.js +++ b/x-pack/legacy/plugins/rollup/server/collectors/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { isEsErrorFactory } from './is_es_error_factory'; +export { registerRollupUsageCollector } from './register'; diff --git a/x-pack/legacy/plugins/rollup/server/usage/collector.js b/x-pack/legacy/plugins/rollup/server/collectors/register.ts similarity index 83% rename from x-pack/legacy/plugins/rollup/server/usage/collector.js rename to x-pack/legacy/plugins/rollup/server/collectors/register.ts index 21c4de62c8fdc..02ad5dc92fd13 100644 --- a/x-pack/legacy/plugins/rollup/server/usage/collector.js +++ b/x-pack/legacy/plugins/rollup/server/collectors/register.ts @@ -5,25 +5,31 @@ */ import { get } from 'lodash'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; + +interface IdToFlagMap { + [key: string]: boolean; +} const ROLLUP_USAGE_TYPE = 'rollups'; // elasticsearch index.max_result_window default value const ES_MAX_RESULT_WINDOW_DEFAULT_VALUE = 1000; -function getIdFromSavedObjectId(savedObjectId) { +function getIdFromSavedObjectId(savedObjectId: string) { // The saved object ID is formatted `{TYPE}:{ID}`. return savedObjectId.split(':')[1]; } -function createIdToFlagMap(ids) { +function createIdToFlagMap(ids: string[]) { return ids.reduce((map, id) => { map[id] = true; return map; - }, {}); + }, {} as any); } -async function fetchRollupIndexPatterns(kibanaIndex, callCluster) { +async function fetchRollupIndexPatterns(kibanaIndex: string, callCluster: CallCluster) { const searchParams = { size: ES_MAX_RESULT_WINDOW_DEFAULT_VALUE, index: kibanaIndex, @@ -50,7 +56,11 @@ async function fetchRollupIndexPatterns(kibanaIndex, callCluster) { }); } -async function fetchRollupSavedSearches(kibanaIndex, callCluster, rollupIndexPatternToFlagMap) { +async function fetchRollupSavedSearches( + kibanaIndex: string, + callCluster: CallCluster, + rollupIndexPatternToFlagMap: IdToFlagMap +) { const searchParams = { size: ES_MAX_RESULT_WINDOW_DEFAULT_VALUE, index: kibanaIndex, @@ -86,19 +96,19 @@ async function fetchRollupSavedSearches(kibanaIndex, callCluster, rollupIndexPat const searchSource = JSON.parse(searchSourceJSON); if (rollupIndexPatternToFlagMap[searchSource.index]) { - const id = getIdFromSavedObjectId(savedObjectId); + const id = getIdFromSavedObjectId(savedObjectId) as string; rollupSavedSearches.push(id); } return rollupSavedSearches; - }, []); + }, [] as string[]); } async function fetchRollupVisualizations( - kibanaIndex, - callCluster, - rollupIndexPatternToFlagMap, - rollupSavedSearchesToFlagMap + kibanaIndex: string, + callCluster: CallCluster, + rollupIndexPatternToFlagMap: IdToFlagMap, + rollupSavedSearchesToFlagMap: IdToFlagMap ) { const searchParams = { size: ES_MAX_RESULT_WINDOW_DEFAULT_VALUE, @@ -135,7 +145,7 @@ async function fetchRollupVisualizations( savedSearchRefName, kibanaSavedObjectMeta: { searchSourceJSON }, }, - references = [], + references = [] as any[], }, } = visualization; @@ -164,13 +174,14 @@ async function fetchRollupVisualizations( }; } -export function registerRollupUsageCollector(usageCollection, server) { - const kibanaIndex = server.config().get('kibana.index'); - +export function registerRollupUsageCollector( + usageCollection: UsageCollectionSetup, + kibanaIndex: string +): void { const collector = usageCollection.makeUsageCollector({ type: ROLLUP_USAGE_TYPE, isReady: () => true, - fetch: async callCluster => { + fetch: async (callCluster: CallCluster) => { const rollupIndexPatterns = await fetchRollupIndexPatterns(kibanaIndex, callCluster); const rollupIndexPatternToFlagMap = createIdToFlagMap(rollupIndexPatterns); diff --git a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/index.js b/x-pack/legacy/plugins/rollup/server/index.ts similarity index 55% rename from x-pack/legacy/plugins/rollup/server/lib/error_wrappers/index.js rename to x-pack/legacy/plugins/rollup/server/index.ts index f275f15637091..6bbd00ac6576e 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/index.js +++ b/x-pack/legacy/plugins/rollup/server/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { PluginInitializerContext } from 'src/core/server'; +import { RollupsServerPlugin } from './plugin'; -export { wrapCustomError } from './wrap_custom_error'; -export { wrapEsError } from './wrap_es_error'; -export { wrapUnknownError } from './wrap_unknown_error'; +export const plugin = (ctx: PluginInitializerContext) => new RollupsServerPlugin(ctx); diff --git a/x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/call_with_request_factory.js b/x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/call_with_request_factory.js deleted file mode 100644 index 284151d404a47..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/call_with_request_factory.js +++ /dev/null @@ -1,21 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { once } from 'lodash'; -import { elasticsearchJsPlugin } from '../../client/elasticsearch_rollup'; - -const callWithRequest = once(server => { - const client = server.newPlatform.setup.core.elasticsearch.createClient('rollup', { - plugins: [elasticsearchJsPlugin], - }); - return (request, ...args) => client.asScoped(request).callAsCurrentUser(...args); -}); - -export const callWithRequestFactory = (server, request) => { - return (...args) => { - return callWithRequest(server)(request, ...args); - }; -}; diff --git a/x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/call_with_request_factory.ts b/x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/call_with_request_factory.ts new file mode 100644 index 0000000000000..883b3552a7c02 --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/call_with_request_factory.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ElasticsearchServiceSetup } from 'kibana/server'; +import { once } from 'lodash'; +import { elasticsearchJsPlugin } from '../../client/elasticsearch_rollup'; + +const callWithRequest = once((elasticsearchService: ElasticsearchServiceSetup) => { + const config = { plugins: [elasticsearchJsPlugin] }; + return elasticsearchService.createClient('rollup', config); +}); + +export const callWithRequestFactory = ( + elasticsearchService: ElasticsearchServiceSetup, + request: any +) => { + return (...args: any[]) => { + return ( + callWithRequest(elasticsearchService) + .asScoped(request) + // @ts-ignore + .callAsCurrentUser(...args) + ); + }; +}; diff --git a/x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/index.js b/x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/index.ts similarity index 100% rename from x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/index.js rename to x-pack/legacy/plugins/rollup/server/lib/call_with_request_factory/index.ts diff --git a/x-pack/legacy/plugins/rollup/server/lib/check_license/__tests__/check_license.js b/x-pack/legacy/plugins/rollup/server/lib/check_license/__tests__/check_license.js deleted file mode 100644 index 933fda01c055d..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/check_license/__tests__/check_license.js +++ /dev/null @@ -1,145 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { set } from 'lodash'; -import { checkLicense } from '../check_license'; - -describe('check_license', function() { - let mockLicenseInfo; - beforeEach(() => (mockLicenseInfo = {})); - - describe('license information is undefined', () => { - beforeEach(() => (mockLicenseInfo = undefined)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is not available', () => { - beforeEach(() => (mockLicenseInfo.isAvailable = () => false)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is available', () => { - beforeEach(() => { - mockLicenseInfo.isAvailable = () => true; - set(mockLicenseInfo, 'license.getType', () => 'basic'); - }); - - describe('& license is > basic', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true)); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set isAvailable to true', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); - }); - - it('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it('should set enableLinks to true', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); - }); - - it('should not set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); - - describe('& license is basic', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true)); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set isAvailable to true', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); - }); - - it('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it('should set enableLinks to true', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); - }); - - it('should not set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/rollup/server/lib/check_license/check_license.js b/x-pack/legacy/plugins/rollup/server/lib/check_license/check_license.js deleted file mode 100644 index 3885a20a1f358..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/check_license/check_license.js +++ /dev/null @@ -1,66 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export function checkLicense(xpackLicenseInfo) { - const pluginName = 'Rollups'; - - // If, for some reason, we cannot get the license information - // from Elasticsearch, assume worst case and disable - if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { - return { - isAvailable: false, - showLinks: true, - enableLinks: false, - message: i18n.translate('xpack.rollupJobs.checkLicense.errorUnavailableMessage', { - defaultMessage: - 'You cannot use {pluginName} because license information is not available at this time.', - values: { pluginName }, - }), - }; - } - - const VALID_LICENSE_MODES = ['trial', 'basic', 'standard', 'gold', 'platinum', 'enterprise']; - - const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES); - const isLicenseActive = xpackLicenseInfo.license.isActive(); - const licenseType = xpackLicenseInfo.license.getType(); - - // License is not valid - if (!isLicenseModeValid) { - return { - isAvailable: false, - showLinks: false, - message: i18n.translate('xpack.rollupJobs.checkLicense.errorUnsupportedMessage', { - defaultMessage: - 'Your {licenseType} license does not support {pluginName}. Please upgrade your license.', - values: { licenseType, pluginName }, - }), - }; - } - - // License is valid but not active - if (!isLicenseActive) { - return { - isAvailable: false, - showLinks: true, - enableLinks: false, - message: i18n.translate('xpack.rollupJobs.checkLicense.errorExpiredMessage', { - defaultMessage: - 'You cannot use {pluginName} because your {licenseType} license has expired', - values: { licenseType, pluginName }, - }), - }; - } - - // License is valid and active - return { - isAvailable: true, - showLinks: true, - enableLinks: true, - }; -} diff --git a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_custom_error.js b/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_custom_error.js deleted file mode 100644 index f9c102be7a1ff..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_custom_error.js +++ /dev/null @@ -1,21 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { wrapCustomError } from '../wrap_custom_error'; - -describe('wrap_custom_error', () => { - describe('#wrapCustomError', () => { - it('should return a Boom object', () => { - const originalError = new Error('I am an error'); - const statusCode = 404; - const wrappedError = wrapCustomError(originalError, statusCode); - - expect(wrappedError.isBoom).to.be(true); - expect(wrappedError.output.statusCode).to.equal(statusCode); - }); - }); -}); diff --git a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_es_error.js b/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_es_error.js deleted file mode 100644 index 8241dc4329137..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_es_error.js +++ /dev/null @@ -1,39 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { wrapEsError } from '../wrap_es_error'; - -describe('wrap_es_error', () => { - describe('#wrapEsError', () => { - let originalError; - beforeEach(() => { - originalError = new Error('I am an error'); - originalError.statusCode = 404; - originalError.response = '{}'; - }); - - it('should return a Boom object', () => { - const wrappedError = wrapEsError(originalError); - - expect(wrappedError.isBoom).to.be(true); - }); - - it('should return the correct Boom object', () => { - const wrappedError = wrapEsError(originalError); - - expect(wrappedError.output.statusCode).to.be(originalError.statusCode); - expect(wrappedError.output.payload.message).to.be(originalError.message); - }); - - it('should return the correct Boom object with custom message', () => { - const wrappedError = wrapEsError(originalError, { 404: 'No encontrado!' }); - - expect(wrappedError.output.statusCode).to.be(originalError.statusCode); - expect(wrappedError.output.payload.message).to.be('No encontrado!'); - }); - }); -}); diff --git a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_unknown_error.js b/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_unknown_error.js deleted file mode 100644 index 85e0b2b3033ad..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/__tests__/wrap_unknown_error.js +++ /dev/null @@ -1,19 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { wrapUnknownError } from '../wrap_unknown_error'; - -describe('wrap_unknown_error', () => { - describe('#wrapUnknownError', () => { - it('should return a Boom object', () => { - const originalError = new Error('I am an error'); - const wrappedError = wrapUnknownError(originalError); - - expect(wrappedError.isBoom).to.be(true); - }); - }); -}); diff --git a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_custom_error.js b/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_custom_error.js deleted file mode 100644 index 3295113d38ee5..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_custom_error.js +++ /dev/null @@ -1,18 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -/** - * Wraps a custom error into a Boom error response and returns it - * - * @param err Object error - * @param statusCode Error status code - * @return Object Boom error response - */ -export function wrapCustomError(err, statusCode) { - return Boom.boomify(err, { statusCode }); -} diff --git a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_es_error.js b/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_es_error.js deleted file mode 100644 index 5f4884a3f2d26..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_es_error.js +++ /dev/null @@ -1,59 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -function extractCausedByChain(causedBy = {}, accumulator = []) { - const { reason, caused_by } = causedBy; // eslint-disable-line camelcase - - if (reason) { - accumulator.push(reason); - } - - // eslint-disable-next-line camelcase - if (caused_by) { - return extractCausedByChain(caused_by, accumulator); - } - - return accumulator; -} - -/** - * Wraps an error thrown by the ES JS client into a Boom error response and returns it - * - * @param err Object Error thrown by ES JS client - * @param statusCodeToMessageMap Object Optional map of HTTP status codes => error messages - * @return Object Boom error response - */ -export function wrapEsError(err, statusCodeToMessageMap = {}) { - const { statusCode, response } = err; - - const { - error: { - root_cause = [], // eslint-disable-line camelcase - caused_by, // eslint-disable-line camelcase - } = {}, - } = JSON.parse(response); - - // If no custom message if specified for the error's status code, just - // wrap the error as a Boom error response and return it - if (!statusCodeToMessageMap[statusCode]) { - const boomError = Boom.boomify(err, { statusCode }); - - // The caused_by chain has the most information so use that if it's available. If not then - // settle for the root_cause. - const causedByChain = extractCausedByChain(caused_by); - const defaultCause = root_cause.length ? extractCausedByChain(root_cause[0]) : undefined; - - boomError.output.payload.cause = causedByChain.length ? causedByChain : defaultCause; - return boomError; - } - - // Otherwise, use the custom message to create a Boom error response and - // return it - const message = statusCodeToMessageMap[statusCode]; - return new Boom(message, { statusCode }); -} diff --git a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_unknown_error.js b/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_unknown_error.js deleted file mode 100644 index ffd915c513362..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/error_wrappers/wrap_unknown_error.js +++ /dev/null @@ -1,17 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -/** - * Wraps an unknown error into a Boom error response and returns it - * - * @param err Object Unknown error - * @return Object Boom error response - */ -export function wrapUnknownError(err) { - return Boom.boomify(err); -} diff --git a/x-pack/legacy/plugins/rollup/server/lib/check_license/index.js b/x-pack/legacy/plugins/rollup/server/lib/is_es_error/index.ts similarity index 83% rename from x-pack/legacy/plugins/rollup/server/lib/check_license/index.js rename to x-pack/legacy/plugins/rollup/server/lib/is_es_error/index.ts index f2c070fd44b6e..a9a3c61472d8c 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/check_license/index.js +++ b/x-pack/legacy/plugins/rollup/server/lib/is_es_error/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { checkLicense } from './check_license'; +export { isEsError } from './is_es_error'; diff --git a/x-pack/legacy/plugins/rollup/server/lib/is_es_error/is_es_error.ts b/x-pack/legacy/plugins/rollup/server/lib/is_es_error/is_es_error.ts new file mode 100644 index 0000000000000..4137293cf39c0 --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/lib/is_es_error/is_es_error.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as legacyElasticsearch from 'elasticsearch'; + +const esErrorsParent = legacyElasticsearch.errors._Abstract; + +export function isEsError(err: Error) { + return err instanceof esErrorsParent; +} diff --git a/x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js b/x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js deleted file mode 100644 index 5f2141cce9395..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js +++ /dev/null @@ -1,44 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { isEsErrorFactory } from '../is_es_error_factory'; -import { set } from 'lodash'; - -class MockAbstractEsError {} - -describe('is_es_error_factory', () => { - let mockServer; - let isEsError; - - beforeEach(() => { - const mockEsErrors = { - _Abstract: MockAbstractEsError, - }; - mockServer = {}; - set(mockServer, 'plugins.elasticsearch.getCluster', () => ({ errors: mockEsErrors })); - - isEsError = isEsErrorFactory(mockServer); - }); - - describe('#isEsErrorFactory', () => { - it('should return a function', () => { - expect(isEsError).to.be.a(Function); - }); - - describe('returned function', () => { - it('should return true if passed-in err is a known esError', () => { - const knownEsError = new MockAbstractEsError(); - expect(isEsError(knownEsError)).to.be(true); - }); - - it('should return false if passed-in err is not a known esError', () => { - const unknownEsError = {}; - expect(isEsError(unknownEsError)).to.be(false); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/is_es_error_factory.js b/x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/is_es_error_factory.js deleted file mode 100644 index 6c17554385ef8..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/is_es_error_factory/is_es_error_factory.js +++ /dev/null @@ -1,18 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { memoize } from 'lodash'; - -const esErrorsFactory = memoize(server => { - return server.plugins.elasticsearch.getCluster('admin').errors; -}); - -export function isEsErrorFactory(server) { - const esErrors = esErrorsFactory(server); - return function isEsError(err) { - return err instanceof esErrors._Abstract; - }; -} diff --git a/x-pack/legacy/plugins/rollup/server/lib/jobs_compatibility.js b/x-pack/legacy/plugins/rollup/server/lib/jobs_compatibility.ts similarity index 94% rename from x-pack/legacy/plugins/rollup/server/lib/jobs_compatibility.js rename to x-pack/legacy/plugins/rollup/server/lib/jobs_compatibility.ts index 9423e7befb557..f93641e5962b7 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/jobs_compatibility.js +++ b/x-pack/legacy/plugins/rollup/server/lib/jobs_compatibility.ts @@ -37,10 +37,10 @@ export function mergeJobConfigurations(jobs = []) { throw new Error('No capabilities available'); } - const allAggs = {}; + const allAggs: { [key: string]: any } = {}; // For each job, look through all of its fields - jobs.forEach(job => { + jobs.forEach((job: { fields: { [key: string]: any } }) => { const fields = job.fields; const fieldNames = Object.keys(fields); @@ -49,7 +49,7 @@ export function mergeJobConfigurations(jobs = []) { const fieldAggs = fields[fieldName]; // Look through each field's capabilities (aggregations) - fieldAggs.forEach(agg => { + fieldAggs.forEach((agg: { agg: string; interval: string }) => { const aggName = agg.agg; const aggDoesntExist = !allAggs[aggName]; const fieldDoesntExist = allAggs[aggName] && !allAggs[aggName][fieldName]; diff --git a/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js deleted file mode 100644 index a73aa96209c26..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js +++ /dev/null @@ -1,66 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { licensePreRoutingFactory } from '../license_pre_routing_factory'; - -describe('license_pre_routing_factory', () => { - describe('#reportingFeaturePreRoutingFactory', () => { - let mockServer; - let mockLicenseCheckResults; - - beforeEach(() => { - mockServer = { - plugins: { - xpack_main: { - info: { - feature: () => ({ - getLicenseCheckResults: () => mockLicenseCheckResults, - }), - }, - }, - }, - }; - }); - - it('only instantiates one instance per server', () => { - const firstInstance = licensePreRoutingFactory(mockServer); - const secondInstance = licensePreRoutingFactory(mockServer); - - expect(firstInstance).to.be(secondInstance); - }); - - describe('isAvailable is false', () => { - beforeEach(() => { - mockLicenseCheckResults = { - isAvailable: false, - }; - }); - - it('replies with 403', () => { - const licensePreRouting = licensePreRoutingFactory(mockServer); - const response = licensePreRouting(); - expect(response).to.be.an(Error); - expect(response.isBoom).to.be(true); - expect(response.output.statusCode).to.be(403); - }); - }); - - describe('isAvailable is true', () => { - beforeEach(() => { - mockLicenseCheckResults = { - isAvailable: true, - }; - }); - - it('replies with nothing', () => { - const licensePreRouting = licensePreRoutingFactory(mockServer); - const response = licensePreRouting(); - expect(response).to.be(null); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/index.js b/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/index.ts similarity index 100% rename from x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/index.js rename to x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/index.ts diff --git a/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.js deleted file mode 100644 index 1c2c9f2b2276b..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.js +++ /dev/null @@ -1,28 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { once } from 'lodash'; -import { wrapCustomError } from '../error_wrappers'; -import { PLUGIN } from '../../../common'; - -export const licensePreRoutingFactory = once(server => { - const xpackMainPlugin = server.plugins.xpack_main; - - // License checking and enable/disable logic - function licensePreRouting() { - const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); - if (!licenseCheckResults.isAvailable) { - const error = new Error(licenseCheckResults.message); - const statusCode = 403; - const wrappedError = wrapCustomError(error, statusCode); - return wrappedError; - } else { - return null; - } - } - - return licensePreRouting; -}); diff --git a/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js b/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js new file mode 100644 index 0000000000000..b6cea09e0ea3c --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { licensePreRoutingFactory } from '.'; +import { + LICENSE_STATUS_VALID, + LICENSE_STATUS_INVALID, +} from '../../../../../common/constants/license_status'; +import { kibanaResponseFactory } from '../../../../../../../src/core/server'; + +describe('licensePreRoutingFactory()', () => { + let mockServer; + let mockLicenseCheckResults; + + beforeEach(() => { + mockServer = { + plugins: { + xpack_main: { + info: { + feature: () => ({ + getLicenseCheckResults: () => mockLicenseCheckResults, + }), + }, + }, + }, + }; + }); + + describe('status is invalid', () => { + beforeEach(() => { + mockLicenseCheckResults = { + status: LICENSE_STATUS_INVALID, + }; + }); + + it('replies with 403', () => { + const routeWithLicenseCheck = licensePreRoutingFactory(mockServer, () => {}); + const stubRequest = {}; + const response = routeWithLicenseCheck({}, stubRequest, kibanaResponseFactory); + expect(response.status).to.be(403); + }); + }); + + describe('status is valid', () => { + beforeEach(() => { + mockLicenseCheckResults = { + status: LICENSE_STATUS_VALID, + }; + }); + + it('replies with nothing', () => { + const routeWithLicenseCheck = licensePreRoutingFactory(mockServer, () => null); + const stubRequest = {}; + const response = routeWithLicenseCheck({}, stubRequest, kibanaResponseFactory); + expect(response).to.be(null); + }); + }); +}); diff --git a/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts b/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts new file mode 100644 index 0000000000000..353510d96a00d --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + KibanaRequest, + KibanaResponseFactory, + RequestHandler, + RequestHandlerContext, +} from 'src/core/server'; +import { PLUGIN } from '../../../common'; +import { LICENSE_STATUS_VALID } from '../../../../../common/constants/license_status'; +import { ServerShim } from '../../types'; + +export const licensePreRoutingFactory = ( + server: ServerShim, + handler: RequestHandler +): RequestHandler => { + const xpackMainPlugin = server.plugins.xpack_main; + + // License checking and enable/disable logic + return function licensePreRouting( + ctx: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ) { + const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); + const { status } = licenseCheckResults; + + if (status !== LICENSE_STATUS_VALID) { + return response.customError({ + body: { + message: licenseCheckResults.messsage, + }, + statusCode: 403, + }); + } + + return handler(ctx, request, response); + }; +}; diff --git a/x-pack/legacy/plugins/rollup/server/lib/map_capabilities.js b/x-pack/legacy/plugins/rollup/server/lib/map_capabilities.ts similarity index 81% rename from x-pack/legacy/plugins/rollup/server/lib/map_capabilities.js rename to x-pack/legacy/plugins/rollup/server/lib/map_capabilities.ts index a365ca4c75616..e0f8af865beb4 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/map_capabilities.js +++ b/x-pack/legacy/plugins/rollup/server/lib/map_capabilities.ts @@ -6,9 +6,9 @@ import { mergeJobConfigurations } from './jobs_compatibility'; -export function getCapabilitiesForRollupIndices(indices) { +export function getCapabilitiesForRollupIndices(indices: { [key: string]: any }) { const indexNames = Object.keys(indices); - const capabilities = {}; + const capabilities = {} as { [key: string]: any }; indexNames.forEach(index => { try { diff --git a/x-pack/legacy/plugins/rollup/server/lib/merge_capabilities_with_fields.js b/x-pack/legacy/plugins/rollup/server/lib/merge_capabilities_with_fields.ts similarity index 90% rename from x-pack/legacy/plugins/rollup/server/lib/merge_capabilities_with_fields.js rename to x-pack/legacy/plugins/rollup/server/lib/merge_capabilities_with_fields.ts index 76592bf12b2e3..24abe9045aae8 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/merge_capabilities_with_fields.js +++ b/x-pack/legacy/plugins/rollup/server/lib/merge_capabilities_with_fields.ts @@ -6,13 +6,18 @@ // Merge rollup capabilities information with field information +export interface Field { + name?: string; + [key: string]: any; +} + export const mergeCapabilitiesWithFields = ( - rollupIndexCapabilities, - fieldsFromFieldCapsApi, - previousFields = [] + rollupIndexCapabilities: { [key: string]: any }, + fieldsFromFieldCapsApi: { [key: string]: any }, + previousFields: Field[] = [] ) => { const rollupFields = [...previousFields]; - const rollupFieldNames = []; + const rollupFieldNames: string[] = []; Object.keys(rollupIndexCapabilities).forEach(agg => { // Field names of the aggregation diff --git a/x-pack/legacy/plugins/rollup/server/lib/register_license_checker/register_license_checker.js b/x-pack/legacy/plugins/rollup/server/lib/register_license_checker/register_license_checker.js deleted file mode 100644 index 5f1772800a012..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/register_license_checker/register_license_checker.js +++ /dev/null @@ -1,24 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; -import { checkLicense } from '../check_license'; -import { PLUGIN } from '../../../common'; - -export function registerLicenseChecker(server) { - const xpackMainPlugin = server.plugins.xpack_main; - const rollupPlugin = server.plugins[PLUGIN.ID]; - - mirrorPluginStatus(xpackMainPlugin, rollupPlugin); - xpackMainPlugin.status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); - }); -} diff --git a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/index.js b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/index.ts similarity index 100% rename from x-pack/legacy/plugins/rollup/server/lib/search_strategies/index.js rename to x-pack/legacy/plugins/rollup/server/lib/search_strategies/index.ts diff --git a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/lib/interval_helper.js b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/lib/interval_helper.ts similarity index 52% rename from x-pack/legacy/plugins/rollup/server/lib/search_strategies/lib/interval_helper.js rename to x-pack/legacy/plugins/rollup/server/lib/search_strategies/lib/interval_helper.ts index 8fc17252f9943..91d73cecdf401 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/lib/interval_helper.js +++ b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/lib/interval_helper.ts @@ -4,9 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { unitsMap } from '@elastic/datemath'; +import dateMath from '@elastic/datemath'; + +export type Unit = 'ms' | 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'y'; export const leastCommonInterval = (num = 0, base = 0) => Math.max(Math.ceil(num / base) * base, base); -export const isCalendarInterval = ({ unit, value }) => - value === 1 && ['calendar', 'mixed'].includes(unitsMap[unit].type); + +export const isCalendarInterval = ({ unit, value }: { unit: Unit; value: number }) => { + const { unitsMap } = dateMath; + return value === 1 && ['calendar', 'mixed'].includes(unitsMap[unit].type); +}; diff --git a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.js b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.js deleted file mode 100644 index fe65a7f1f30e9..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.js +++ /dev/null @@ -1,32 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { getRollupSearchStrategy } from './rollup_search_strategy'; -import { getRollupSearchRequest } from './rollup_search_request'; -import { getRollupSearchCapabilities } from './rollup_search_capabilities'; -import { - AbstractSearchRequest, - DefaultSearchCapabilities, - AbstractSearchStrategy, -} from '../../../../../../../src/plugins/vis_type_timeseries/server'; - -export const registerRollupSearchStrategy = kbnServer => - kbnServer.afterPluginsInit(() => { - if (!kbnServer.newPlatform.setup.plugins.metrics) { - return; - } - - const { addSearchStrategy } = kbnServer.newPlatform.setup.plugins.metrics; - - const RollupSearchRequest = getRollupSearchRequest(AbstractSearchRequest); - const RollupSearchCapabilities = getRollupSearchCapabilities(DefaultSearchCapabilities); - const RollupSearchStrategy = getRollupSearchStrategy( - AbstractSearchStrategy, - RollupSearchRequest, - RollupSearchCapabilities - ); - - addSearchStrategy(new RollupSearchStrategy(kbnServer)); - }); diff --git a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.test.js b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.test.js index acd016d75f97e..d466ebd69737e 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.test.js +++ b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.test.js @@ -6,45 +6,22 @@ import { registerRollupSearchStrategy } from './register_rollup_search_strategy'; describe('Register Rollup Search Strategy', () => { - let kbnServer; - let metrics; + let routeDependencies; + let addSearchStrategy; beforeEach(() => { - const afterPluginsInit = jest.fn(callback => callback()); - - kbnServer = { - afterPluginsInit, - newPlatform: { - setup: { plugins: {} }, - }, - }; - - metrics = { - addSearchStrategy: jest.fn().mockName('addSearchStrategy'), - AbstractSearchRequest: jest.fn().mockName('AbstractSearchRequest'), - AbstractSearchStrategy: jest.fn().mockName('AbstractSearchStrategy'), - DefaultSearchCapabilities: jest.fn().mockName('DefaultSearchCapabilities'), + routeDependencies = { + router: jest.fn().mockName('router'), + elasticsearchService: jest.fn().mockName('elasticsearchService'), + elasticsearch: jest.fn().mockName('elasticsearch'), }; - }); - - test('should run initialization on "afterPluginsInit" hook', () => { - registerRollupSearchStrategy(kbnServer); - - expect(kbnServer.afterPluginsInit).toHaveBeenCalled(); - }); - - test('should run initialization if metrics plugin available', () => { - registerRollupSearchStrategy({ - ...kbnServer, - newPlatform: { setup: { plugins: { metrics } } }, - }); - expect(metrics.addSearchStrategy).toHaveBeenCalled(); + addSearchStrategy = jest.fn().mockName('addSearchStrategy'); }); - test('should not run initialization if metrics plugin unavailable', () => { - registerRollupSearchStrategy(kbnServer); + test('should run initialization', () => { + registerRollupSearchStrategy(routeDependencies, addSearchStrategy); - expect(metrics.addSearchStrategy).not.toHaveBeenCalled(); + expect(addSearchStrategy).toHaveBeenCalled(); }); }); diff --git a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.ts b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.ts new file mode 100644 index 0000000000000..93c4c1b52140b --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/register_rollup_search_strategy.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { getRollupSearchStrategy } from './rollup_search_strategy'; +import { getRollupSearchRequest } from './rollup_search_request'; +import { getRollupSearchCapabilities } from './rollup_search_capabilities'; +import { + AbstractSearchRequest, + DefaultSearchCapabilities, + AbstractSearchStrategy, +} from '../../../../../../../src/plugins/vis_type_timeseries/server'; +import { RouteDependencies } from '../../types'; + +export const registerRollupSearchStrategy = ( + { elasticsearchService }: RouteDependencies, + addSearchStrategy: (searchStrategy: any) => void +) => { + const RollupSearchRequest = getRollupSearchRequest(AbstractSearchRequest); + const RollupSearchCapabilities = getRollupSearchCapabilities(DefaultSearchCapabilities); + const RollupSearchStrategy = getRollupSearchStrategy( + AbstractSearchStrategy, + RollupSearchRequest, + RollupSearchCapabilities + ); + + addSearchStrategy(new RollupSearchStrategy(elasticsearchService)); +}; diff --git a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.js b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.ts similarity index 82% rename from x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.js rename to x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.ts index b84664c765dc6..5a57129aa6039 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.js +++ b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.ts @@ -4,24 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ import { get, has } from 'lodash'; +import { KibanaRequest } from 'kibana/server'; import { leastCommonInterval, isCalendarInterval } from './lib/interval_helper'; -export const getRollupSearchCapabilities = DefaultSearchCapabilities => +export const getRollupSearchCapabilities = (DefaultSearchCapabilities: any) => class RollupSearchCapabilities extends DefaultSearchCapabilities { - constructor(req, fieldsCapabilities, rollupIndex) { + constructor( + req: KibanaRequest, + fieldsCapabilities: { [key: string]: any }, + rollupIndex: string + ) { super(req, fieldsCapabilities); this.rollupIndex = rollupIndex; this.availableMetrics = get(fieldsCapabilities, `${rollupIndex}.aggs`, {}); } - get dateHistogram() { + public get dateHistogram() { const [dateHistogram] = Object.values(this.availableMetrics.date_histogram); return dateHistogram; } - get defaultTimeInterval() { + public get defaultTimeInterval() { return ( this.dateHistogram.fixed_interval || this.dateHistogram.calendar_interval || @@ -34,16 +39,16 @@ export const getRollupSearchCapabilities = DefaultSearchCapabilities => ); } - get searchTimezone() { + public get searchTimezone() { return get(this.dateHistogram, 'time_zone', null); } - get whiteListedMetrics() { + public get whiteListedMetrics() { const baseRestrictions = this.createUiRestriction({ count: this.createUiRestriction(), }); - const getFields = fields => + const getFields = (fields: { [key: string]: any }) => Object.keys(fields).reduce( (acc, item) => ({ ...acc, @@ -61,20 +66,20 @@ export const getRollupSearchCapabilities = DefaultSearchCapabilities => ); } - get whiteListedGroupByFields() { + public get whiteListedGroupByFields() { return this.createUiRestriction({ everything: true, terms: has(this.availableMetrics, 'terms'), }); } - get whiteListedTimerangeModes() { + public get whiteListedTimerangeModes() { return this.createUiRestriction({ last_value: true, }); } - getValidTimeInterval(userIntervalString) { + getValidTimeInterval(userIntervalString: string) { const parsedRollupJobInterval = this.parseInterval(this.defaultTimeInterval); const inRollupJobUnit = this.convertIntervalToUnit( userIntervalString, diff --git a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_request.js b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_request.ts similarity index 75% rename from x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_request.js rename to x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_request.ts index ee8e5553c8963..7e12d5286f34c 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_request.js +++ b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_request.ts @@ -5,9 +5,16 @@ */ const SEARCH_METHOD = 'rollup.search'; -export const getRollupSearchRequest = AbstractSearchRequest => +interface Search { + index: string; + body: { + [key: string]: any; + }; +} + +export const getRollupSearchRequest = (AbstractSearchRequest: any) => class RollupSearchRequest extends AbstractSearchRequest { - async search(searches) { + async search(searches: Search[]) { const requests = searches.map(({ body, index }) => this.callWithRequest(SEARCH_METHOD, { body, diff --git a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_strategy.js b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_strategy.ts similarity index 68% rename from x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_strategy.js rename to x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_strategy.ts index 5cf7a3c8fd941..9d5aad2c2d3bc 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_strategy.js +++ b/x-pack/legacy/plugins/rollup/server/lib/search_strategies/rollup_search_strategy.ts @@ -4,31 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ import { indexBy, isString } from 'lodash'; +import { ElasticsearchServiceSetup, KibanaRequest } from 'kibana/server'; import { callWithRequestFactory } from '../call_with_request_factory'; import { mergeCapabilitiesWithFields } from '../merge_capabilities_with_fields'; import { getCapabilitiesForRollupIndices } from '../map_capabilities'; const ROLLUP_INDEX_CAPABILITIES_METHOD = 'rollup.rollupIndexCapabilities'; -const getRollupIndices = rollupData => Object.keys(rollupData); +const getRollupIndices = (rollupData: { [key: string]: any[] }) => Object.keys(rollupData); -const isIndexPatternContainsWildcard = indexPattern => indexPattern.includes('*'); -const isIndexPatternValid = indexPattern => +const isIndexPatternContainsWildcard = (indexPattern: string) => indexPattern.includes('*'); +const isIndexPatternValid = (indexPattern: string) => indexPattern && isString(indexPattern) && !isIndexPatternContainsWildcard(indexPattern); export const getRollupSearchStrategy = ( - AbstractSearchStrategy, - RollupSearchRequest, - RollupSearchCapabilities + AbstractSearchStrategy: any, + RollupSearchRequest: any, + RollupSearchCapabilities: any ) => class RollupSearchStrategy extends AbstractSearchStrategy { name = 'rollup'; - constructor(server) { - super(server, callWithRequestFactory, RollupSearchRequest); + constructor(elasticsearchService: ElasticsearchServiceSetup) { + super(elasticsearchService, callWithRequestFactory, RollupSearchRequest); } - getRollupData(req, indexPattern) { + getRollupData(req: KibanaRequest, indexPattern: string) { const callWithRequest = this.getCallWithRequestInstance(req); return callWithRequest(ROLLUP_INDEX_CAPABILITIES_METHOD, { @@ -36,7 +37,7 @@ export const getRollupSearchStrategy = ( }).catch(() => Promise.resolve({})); } - async checkForViability(req, indexPattern) { + async checkForViability(req: KibanaRequest, indexPattern: string) { let isViable = false; let capabilities = null; @@ -60,7 +61,14 @@ export const getRollupSearchStrategy = ( }; } - async getFieldsForWildcard(req, indexPattern, { fieldsCapabilities, rollupIndex }) { + async getFieldsForWildcard( + req: KibanaRequest, + indexPattern: string, + { + fieldsCapabilities, + rollupIndex, + }: { fieldsCapabilities: { [key: string]: any }; rollupIndex: string } + ) { const fields = await super.getFieldsForWildcard(req, indexPattern); const fieldsFromFieldCapsApi = indexBy(fields, 'name'); const rollupIndexCapabilities = fieldsCapabilities[rollupIndex].aggs; diff --git a/x-pack/legacy/plugins/rollup/server/plugin.ts b/x-pack/legacy/plugins/rollup/server/plugin.ts new file mode 100644 index 0000000000000..52b1e31af4eb2 --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/plugin.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { CoreSetup, Plugin, PluginInitializerContext, Logger } from 'src/core/server'; +import { first } from 'rxjs/operators'; +import { i18n } from '@kbn/i18n'; + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server'; +import { registerLicenseChecker } from '../../../server/lib/register_license_checker'; +import { PLUGIN } from '../common'; +import { ServerShim, RouteDependencies } from './types'; + +import { + registerIndicesRoute, + registerFieldsForWildcardRoute, + registerSearchRoute, + registerJobsRoute, +} from './routes/api'; + +import { registerRollupUsageCollector } from './collectors'; + +import { rollupDataEnricher } from './rollup_data_enricher'; +import { registerRollupSearchStrategy } from './lib/search_strategies'; + +export class RollupsServerPlugin implements Plugin { + log: Logger; + + constructor(private readonly initializerContext: PluginInitializerContext) { + this.log = initializerContext.logger.get(); + } + + async setup( + { http, elasticsearch: elasticsearchService }: CoreSetup, + { + __LEGACY: serverShim, + usageCollection, + metrics, + }: { + __LEGACY: ServerShim; + usageCollection?: UsageCollectionSetup; + metrics?: VisTypeTimeseriesSetup; + } + ) { + const elasticsearch = await elasticsearchService.adminClient; + const router = http.createRouter(); + const routeDependencies: RouteDependencies = { + elasticsearch, + elasticsearchService, + router, + }; + + registerLicenseChecker( + serverShim as any, + PLUGIN.ID, + PLUGIN.getI18nName(i18n), + PLUGIN.MINIMUM_LICENSE_REQUIRED + ); + + registerIndicesRoute(routeDependencies, serverShim); + registerFieldsForWildcardRoute(routeDependencies, serverShim); + registerSearchRoute(routeDependencies, serverShim); + registerJobsRoute(routeDependencies, serverShim); + + if (usageCollection) { + this.initializerContext.config.legacy.globalConfig$ + .pipe(first()) + .toPromise() + .then(config => { + registerRollupUsageCollector(usageCollection, config.kibana.index); + }) + .catch(e => { + this.log.warn(`Registering Rollup collector failed: ${e}`); + }); + } + + if ( + serverShim.plugins.index_management && + serverShim.plugins.index_management.addIndexManagementDataEnricher + ) { + serverShim.plugins.index_management.addIndexManagementDataEnricher(rollupDataEnricher); + } + + if (metrics) { + const { addSearchStrategy } = metrics; + registerRollupSearchStrategy(routeDependencies, addSearchStrategy); + } + } + + start() {} + + stop() {} +} diff --git a/x-pack/legacy/plugins/rollup/rollup_data_enricher.js b/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts similarity index 77% rename from x-pack/legacy/plugins/rollup/rollup_data_enricher.js rename to x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts index e92cd3b0b4fbc..7c5e160c54a31 100644 --- a/x-pack/legacy/plugins/rollup/rollup_data_enricher.js +++ b/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts @@ -4,14 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -export const rollupDataEnricher = async (indicesList, callWithRequest) => { +interface Index { + name: string; + [key: string]: unknown; +} + +export const rollupDataEnricher = async (indicesList: Index[], callWithRequest: any) => { if (!indicesList || !indicesList.length) { return indicesList; } + const params = { path: '/_all/_rollup/data', method: 'GET', }; + try { const rollupJobData = await callWithRequest('transport.request', params); return indicesList.map(index => { @@ -22,7 +29,7 @@ export const rollupDataEnricher = async (indicesList, callWithRequest) => { }; }); } catch (e) { - //swallow exceptions and return original list + // swallow exceptions and return original list return indicesList; } }; diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/index.js b/x-pack/legacy/plugins/rollup/server/routes/api/index.ts similarity index 100% rename from x-pack/legacy/plugins/rollup/server/routes/api/index.js rename to x-pack/legacy/plugins/rollup/server/routes/api/index.ts diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js deleted file mode 100644 index dfc486c030812..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js +++ /dev/null @@ -1,93 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import Joi from 'joi'; -import { callWithRequestFactory } from '../../lib/call_with_request_factory'; -import { isEsErrorFactory } from '../../lib/is_es_error_factory'; -import { wrapEsError, wrapUnknownError } from '../../lib/error_wrappers'; -import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import indexBy from 'lodash/collection/indexBy'; -import { getCapabilitiesForRollupIndices } from '../../lib/map_capabilities'; -import { mergeCapabilitiesWithFields } from '../../lib/merge_capabilities_with_fields'; -import querystring from 'querystring'; - -/** - * Get list of fields for rollup index pattern, in the format of regular index pattern fields - */ -export function registerFieldsForWildcardRoute(server) { - const isEsError = isEsErrorFactory(server); - const licensePreRouting = licensePreRoutingFactory(server); - - server.route({ - path: '/api/index_patterns/rollup/_fields_for_wildcard', - method: 'GET', - config: { - pre: [licensePreRouting], - validate: { - query: Joi.object() - .keys({ - pattern: Joi.string().required(), - meta_fields: Joi.array() - .items(Joi.string()) - .default([]), - params: Joi.object() - .keys({ - rollup_index: Joi.string().required(), - }) - .required(), - }) - .default(), - }, - }, - handler: async request => { - const { pattern, meta_fields: metaFields, params } = request.query; - - // Format call to standard index pattern `fields for wildcard` - const standardRequestQuery = querystring.stringify({ pattern, meta_fields: metaFields }); - const standardRequest = { - url: `${request.getBasePath()}/api/index_patterns/_fields_for_wildcard?${standardRequestQuery}`, - method: 'GET', - headers: request.headers, - }; - - try { - // Make call and use field information from response - const standardResponse = await server.inject(standardRequest); - const fields = standardResponse.result && standardResponse.result.fields; - - const rollupIndex = params.rollup_index; - const callWithRequest = callWithRequestFactory(server, request); - - const rollupFields = []; - const fieldsFromFieldCapsApi = indexBy(fields, 'name'); - const rollupIndexCapabilities = getCapabilitiesForRollupIndices( - await callWithRequest('rollup.rollupIndexCapabilities', { - indexPattern: rollupIndex, - }) - )[rollupIndex].aggs; - - // Keep meta fields - metaFields.forEach( - field => fieldsFromFieldCapsApi[field] && rollupFields.push(fieldsFromFieldCapsApi[field]) - ); - - const mergedRollupFields = mergeCapabilitiesWithFields( - rollupIndexCapabilities, - fieldsFromFieldCapsApi, - rollupFields - ); - - return { - fields: mergedRollupFields, - }; - } catch (err) { - if (isEsError(err)) { - return wrapEsError(err); - } - return wrapUnknownError(err); - } - }, - }); -} diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.ts b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.ts new file mode 100644 index 0000000000000..2516840bd9537 --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.ts @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; +import { RequestHandler } from 'src/core/server'; + +import { indexBy } from 'lodash'; +import { IndexPatternsFetcher } from '../../shared_imports'; +import { RouteDependencies, ServerShim } from '../../types'; +import { callWithRequestFactory } from '../../lib/call_with_request_factory'; +import { isEsError } from '../../lib/is_es_error'; +import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; +import { getCapabilitiesForRollupIndices } from '../../lib/map_capabilities'; +import { mergeCapabilitiesWithFields, Field } from '../../lib/merge_capabilities_with_fields'; + +const parseMetaFields = (metaFields: string | string[]) => { + let parsedFields: string[] = []; + if (typeof metaFields === 'string') { + parsedFields = JSON.parse(metaFields); + } else { + parsedFields = metaFields; + } + return parsedFields; +}; + +const getFieldsForWildcardRequest = async (context: any, request: any, response: any) => { + const { callAsCurrentUser } = context.core.elasticsearch.dataClient; + const indexPatterns = new IndexPatternsFetcher(callAsCurrentUser); + const { pattern, meta_fields: metaFields } = request.query; + + let parsedFields: string[] = []; + try { + parsedFields = parseMetaFields(metaFields); + } catch (error) { + return response.badRequest({ + body: error, + }); + } + + try { + const fields = await indexPatterns.getFieldsForWildcard({ + pattern, + metaFields: parsedFields, + }); + + return response.ok({ + body: { fields }, + headers: { + 'content-type': 'application/json', + }, + }); + } catch (error) { + return response.notFound(); + } +}; + +/** + * Get list of fields for rollup index pattern, in the format of regular index pattern fields + */ +export function registerFieldsForWildcardRoute(deps: RouteDependencies, legacy: ServerShim) { + const handler: RequestHandler = async (ctx, request, response) => { + const { params, meta_fields: metaFields } = request.query; + + try { + // Make call and use field information from response + const { payload } = await getFieldsForWildcardRequest(ctx, request, response); + const fields = payload.fields; + const parsedParams = JSON.parse(params); + const rollupIndex = parsedParams.rollup_index; + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + const rollupFields: Field[] = []; + const fieldsFromFieldCapsApi: { [key: string]: any } = indexBy(fields, 'name'); + const rollupIndexCapabilities = getCapabilitiesForRollupIndices( + await callWithRequest('rollup.rollupIndexCapabilities', { + indexPattern: rollupIndex, + }) + )[rollupIndex].aggs; + // Keep meta fields + metaFields.forEach( + (field: string) => + fieldsFromFieldCapsApi[field] && rollupFields.push(fieldsFromFieldCapsApi[field]) + ); + const mergedRollupFields = mergeCapabilitiesWithFields( + rollupIndexCapabilities, + fieldsFromFieldCapsApi, + rollupFields + ); + return response.ok({ body: { fields: mergedRollupFields } }); + } catch (err) { + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + return response.internalError({ body: err }); + } + }; + + deps.router.get( + { + path: '/api/index_patterns/rollup/_fields_for_wildcard', + validate: { + query: schema.object({ + pattern: schema.string(), + meta_fields: schema.arrayOf(schema.string(), { + defaultValue: [], + }), + params: schema.string({ + validate(value) { + try { + const params = JSON.parse(value); + const keys = Object.keys(params); + const { rollup_index: rollupIndex } = params; + + if (!rollupIndex) { + return '[request query.params]: "rollup_index" is required'; + } else if (keys.length > 1) { + const invalidParams = keys.filter(key => key !== 'rollup_index'); + return `[request query.params]: ${invalidParams.join(', ')} is not allowed`; + } + } catch (err) { + return '[request query.params]: expected JSON string'; + } + }, + }), + }), + }, + }, + licensePreRoutingFactory(legacy, handler) + ); +} diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/indices.js b/x-pack/legacy/plugins/rollup/server/routes/api/indices.js deleted file mode 100644 index 3d1c6932575bc..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/routes/api/indices.js +++ /dev/null @@ -1,128 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { callWithRequestFactory } from '../../lib/call_with_request_factory'; -import { isEsErrorFactory } from '../../lib/is_es_error_factory'; -import { wrapEsError, wrapUnknownError } from '../../lib/error_wrappers'; -import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import { getCapabilitiesForRollupIndices } from '../../lib/map_capabilities'; - -function isNumericField(fieldCapability) { - const numericTypes = [ - 'long', - 'integer', - 'short', - 'byte', - 'double', - 'float', - 'half_float', - 'scaled_float', - ]; - return numericTypes.some(numericType => fieldCapability[numericType] != null); -} - -export function registerIndicesRoute(server) { - const isEsError = isEsErrorFactory(server); - const licensePreRouting = licensePreRoutingFactory(server); - - /** - * Returns a list of all rollup index names - */ - server.route({ - path: '/api/rollup/indices', - method: 'GET', - config: { - pre: [licensePreRouting], - }, - handler: async request => { - const callWithRequest = callWithRequestFactory(server, request); - try { - const data = await callWithRequest('rollup.rollupIndexCapabilities', { - indexPattern: '_all', - }); - return getCapabilitiesForRollupIndices(data); - } catch (err) { - if (isEsError(err)) { - return wrapEsError(err); - } - return wrapUnknownError(err); - } - }, - }); - - /** - * Returns information on validity of an index pattern for creating a rollup job: - * - Does the index pattern match any indices? - * - Does the index pattern match rollup indices? - * - Which date fields, numeric fields, and keyword fields are available in the matching indices? - */ - server.route({ - path: '/api/rollup/index_pattern_validity/{indexPattern}', - method: 'GET', - config: { - pre: [licensePreRouting], - }, - handler: async request => { - const callWithRequest = callWithRequestFactory(server, request); - - try { - const { indexPattern } = request.params; - const [fieldCapabilities, rollupIndexCapabilities] = await Promise.all([ - callWithRequest('rollup.fieldCapabilities', { indexPattern }), - callWithRequest('rollup.rollupIndexCapabilities', { indexPattern }), - ]); - - const doesMatchIndices = Object.entries(fieldCapabilities.fields).length !== 0; - const doesMatchRollupIndices = Object.entries(rollupIndexCapabilities).length !== 0; - - const dateFields = []; - const numericFields = []; - const keywordFields = []; - - const fieldCapabilitiesEntries = Object.entries(fieldCapabilities.fields); - fieldCapabilitiesEntries.forEach(([fieldName, fieldCapability]) => { - if (fieldCapability.date) { - dateFields.push(fieldName); - return; - } - - if (isNumericField(fieldCapability)) { - numericFields.push(fieldName); - return; - } - - if (fieldCapability.keyword) { - keywordFields.push(fieldName); - } - }); - - return { - doesMatchIndices, - doesMatchRollupIndices, - dateFields, - numericFields, - keywordFields, - }; - } catch (err) { - // 404s are still valid results. - if (err.statusCode === 404) { - return { - doesMatchIndices: false, - doesMatchRollupIndices: false, - dateFields: [], - numericFields: [], - keywordFields: [], - }; - } - - if (isEsError(err)) { - return wrapEsError(err); - } - - return wrapUnknownError(err); - } - }, - }); -} diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/indices.ts b/x-pack/legacy/plugins/rollup/server/routes/api/indices.ts new file mode 100644 index 0000000000000..e78f09a71876b --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/routes/api/indices.ts @@ -0,0 +1,175 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; +import { RequestHandler } from 'src/core/server'; +import { callWithRequestFactory } from '../../lib/call_with_request_factory'; +import { isEsError } from '../../lib/is_es_error'; +import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; +import { getCapabilitiesForRollupIndices } from '../../lib/map_capabilities'; +import { API_BASE_PATH } from '../../../common'; +import { RouteDependencies, ServerShim } from '../../types'; + +type NumericField = + | 'long' + | 'integer' + | 'short' + | 'byte' + | 'scaled_float' + | 'double' + | 'float' + | 'half_float'; + +interface FieldCapability { + date?: any; + keyword?: any; + long?: any; + integer?: any; + short?: any; + byte?: any; + double?: any; + float?: any; + half_float?: any; + scaled_float?: any; +} + +interface FieldCapabilities { + fields: FieldCapability[]; +} + +function isNumericField(fieldCapability: FieldCapability) { + const numericTypes = [ + 'long', + 'integer', + 'short', + 'byte', + 'double', + 'float', + 'half_float', + 'scaled_float', + ]; + return numericTypes.some(numericType => fieldCapability[numericType as NumericField] != null); +} + +export function registerIndicesRoute(deps: RouteDependencies, legacy: ServerShim) { + const getIndicesHandler: RequestHandler = async (ctx, request, response) => { + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + + try { + const data = await callWithRequest('rollup.rollupIndexCapabilities', { + indexPattern: '_all', + }); + return response.ok({ body: getCapabilitiesForRollupIndices(data) }); + } catch (err) { + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + return response.internalError({ body: err }); + } + }; + + const validateIndexPatternHandler: RequestHandler = async ( + ctx, + request, + response + ) => { + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + + try { + const { indexPattern } = request.params; + const [fieldCapabilities, rollupIndexCapabilities]: [ + FieldCapabilities, + { [key: string]: any } + ] = await Promise.all([ + callWithRequest('rollup.fieldCapabilities', { indexPattern }), + callWithRequest('rollup.rollupIndexCapabilities', { indexPattern }), + ]); + + const doesMatchIndices = Object.entries(fieldCapabilities.fields).length !== 0; + const doesMatchRollupIndices = Object.entries(rollupIndexCapabilities).length !== 0; + + const dateFields: string[] = []; + const numericFields: string[] = []; + const keywordFields: string[] = []; + + const fieldCapabilitiesEntries = Object.entries(fieldCapabilities.fields); + + fieldCapabilitiesEntries.forEach( + ([fieldName, fieldCapability]: [string, FieldCapability]) => { + if (fieldCapability.date) { + dateFields.push(fieldName); + return; + } + + if (isNumericField(fieldCapability)) { + numericFields.push(fieldName); + return; + } + + if (fieldCapability.keyword) { + keywordFields.push(fieldName); + } + } + ); + + const body = { + doesMatchIndices, + doesMatchRollupIndices, + dateFields, + numericFields, + keywordFields, + }; + + return response.ok({ body }); + } catch (err) { + // 404s are still valid results. + if (err.statusCode === 404) { + const notFoundBody = { + doesMatchIndices: false, + doesMatchRollupIndices: false, + dateFields: [], + numericFields: [], + keywordFields: [], + }; + return response.ok({ body: notFoundBody }); + } + + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + + return response.internalError({ body: err }); + } + }; + + /** + * Returns a list of all rollup index names + */ + deps.router.get( + { + path: `${API_BASE_PATH}/indices`, + validate: false, + }, + licensePreRoutingFactory(legacy, getIndicesHandler) + ); + + /** + * Returns information on validity of an index pattern for creating a rollup job: + * - Does the index pattern match any indices? + * - Does the index pattern match rollup indices? + * - Which date fields, numeric fields, and keyword fields are available in the matching indices? + */ + deps.router.get( + { + path: `${API_BASE_PATH}/index_pattern_validity/{indexPattern}`, + validate: { + params: schema.object({ + indexPattern: schema.string(), + }), + }, + }, + licensePreRoutingFactory(legacy, validateIndexPatternHandler) + ); +} diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/jobs.js b/x-pack/legacy/plugins/rollup/server/routes/api/jobs.js deleted file mode 100644 index 1a9a402ad6518..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/routes/api/jobs.js +++ /dev/null @@ -1,153 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { callWithRequestFactory } from '../../lib/call_with_request_factory'; -import { isEsErrorFactory } from '../../lib/is_es_error_factory'; -import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import { wrapEsError, wrapUnknownError } from '../../lib/error_wrappers'; - -export function registerJobsRoute(server) { - const isEsError = isEsErrorFactory(server); - const licensePreRouting = licensePreRoutingFactory(server); - - server.route({ - path: '/api/rollup/jobs', - method: 'GET', - config: { - pre: [licensePreRouting], - }, - handler: async request => { - try { - const callWithRequest = callWithRequestFactory(server, request); - return await callWithRequest('rollup.jobs'); - } catch (err) { - if (isEsError(err)) { - return wrapEsError(err); - } - - return wrapUnknownError(err); - } - }, - }); - - server.route({ - path: '/api/rollup/create', - method: 'PUT', - config: { - pre: [licensePreRouting], - }, - handler: async request => { - try { - const { id, ...rest } = request.payload.job; - - const callWithRequest = callWithRequestFactory(server, request); - - // Create job. - await callWithRequest('rollup.createJob', { - id, - body: rest, - }); - - // Then request the newly created job. - const results = await callWithRequest('rollup.job', { id }); - return results.jobs[0]; - } catch (err) { - if (isEsError(err)) { - return wrapEsError(err); - } - - return wrapUnknownError(err); - } - }, - }); - - server.route({ - path: '/api/rollup/start', - method: 'POST', - config: { - pre: [licensePreRouting], - }, - handler: async request => { - try { - const { jobIds } = request.payload; - - const callWithRequest = callWithRequestFactory(server, request); - return await Promise.all( - jobIds.map(id => callWithRequest('rollup.startJob', { id })) - ).then(() => ({ success: true })); - } catch (err) { - if (isEsError(err)) { - return wrapEsError(err); - } - - return wrapUnknownError(err); - } - }, - }); - - server.route({ - path: '/api/rollup/stop', - method: 'POST', - config: { - pre: [licensePreRouting], - }, - handler: async request => { - try { - const { jobIds } = request.payload; - // For our API integration tests we need to wait for the jobs to be stopped - // in order to be able to delete them sequencially. - const { waitForCompletion } = request.query; - const callWithRequest = callWithRequestFactory(server, request); - - const stopRollupJob = id => - callWithRequest('rollup.stopJob', { - id, - waitForCompletion: waitForCompletion === 'true', - }); - - return await Promise.all(jobIds.map(stopRollupJob)).then(() => ({ success: true })); - } catch (err) { - if (isEsError(err)) { - return wrapEsError(err); - } - - return wrapUnknownError(err); - } - }, - }); - - server.route({ - path: '/api/rollup/delete', - method: 'POST', - config: { - pre: [licensePreRouting], - }, - handler: async request => { - try { - const { jobIds } = request.payload; - - const callWithRequest = callWithRequestFactory(server, request); - return await Promise.all( - jobIds.map(id => callWithRequest('rollup.deleteJob', { id })) - ).then(() => ({ success: true })); - } catch (err) { - // There is an issue opened on ES to handle the following error correctly - // https://github.com/elastic/elasticsearch/issues/42908 - // Until then we'll modify the response here. - if (err.response && err.response.includes('Job must be [STOPPED] before deletion')) { - err.status = 400; - err.statusCode = 400; - err.displayName = 'Bad request'; - err.message = JSON.parse(err.response).task_failures[0].reason.reason; - } - if (isEsError(err)) { - throw wrapEsError(err); - } - - throw wrapUnknownError(err); - } - }, - }); -} diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts b/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts new file mode 100644 index 0000000000000..e58bc95b9a375 --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts @@ -0,0 +1,178 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; +import { RequestHandler } from 'src/core/server'; +import { callWithRequestFactory } from '../../lib/call_with_request_factory'; +import { isEsError } from '../../lib/is_es_error'; +import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; +import { API_BASE_PATH } from '../../../common'; +import { RouteDependencies, ServerShim } from '../../types'; + +export function registerJobsRoute(deps: RouteDependencies, legacy: ServerShim) { + const getJobsHandler: RequestHandler = async (ctx, request, response) => { + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + + try { + const data = await callWithRequest('rollup.jobs'); + return response.ok({ body: data }); + } catch (err) { + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + return response.internalError({ body: err }); + } + }; + + const createJobsHandler: RequestHandler = async (ctx, request, response) => { + try { + const { id, ...rest } = request.body.job; + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + // Create job. + await callWithRequest('rollup.createJob', { + id, + body: rest, + }); + // Then request the newly created job. + const results = await callWithRequest('rollup.job', { id }); + return response.ok({ body: results.jobs[0] }); + } catch (err) { + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + return response.internalError({ body: err }); + } + }; + + const startJobsHandler: RequestHandler = async (ctx, request, response) => { + try { + const { jobIds } = request.body; + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + + const data = await Promise.all( + jobIds.map((id: string) => callWithRequest('rollup.startJob', { id })) + ).then(() => ({ success: true })); + return response.ok({ body: data }); + } catch (err) { + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + return response.internalError({ body: err }); + } + }; + + const stopJobsHandler: RequestHandler = async (ctx, request, response) => { + try { + const { jobIds } = request.body; + // For our API integration tests we need to wait for the jobs to be stopped + // in order to be able to delete them sequencially. + const { waitForCompletion } = request.query; + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + const stopRollupJob = (id: string) => + callWithRequest('rollup.stopJob', { + id, + waitForCompletion: waitForCompletion === 'true', + }); + const data = await Promise.all(jobIds.map(stopRollupJob)).then(() => ({ success: true })); + return response.ok({ body: data }); + } catch (err) { + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + return response.internalError({ body: err }); + } + }; + + const deleteJobsHandler: RequestHandler = async (ctx, request, response) => { + try { + const { jobIds } = request.body; + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + const data = await Promise.all( + jobIds.map((id: string) => callWithRequest('rollup.deleteJob', { id })) + ).then(() => ({ success: true })); + return response.ok({ body: data }); + } catch (err) { + // There is an issue opened on ES to handle the following error correctly + // https://github.com/elastic/elasticsearch/issues/42908 + // Until then we'll modify the response here. + if (err.response && err.response.includes('Job must be [STOPPED] before deletion')) { + err.status = 400; + err.statusCode = 400; + err.displayName = 'Bad request'; + err.message = JSON.parse(err.response).task_failures[0].reason.reason; + } + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + return response.internalError({ body: err }); + } + }; + + deps.router.get( + { + path: `${API_BASE_PATH}/jobs`, + validate: false, + }, + licensePreRoutingFactory(legacy, getJobsHandler) + ); + + deps.router.put( + { + path: `${API_BASE_PATH}/create`, + validate: { + body: schema.object({ + job: schema.object( + { + id: schema.string(), + }, + { allowUnknowns: true } + ), + }), + }, + }, + licensePreRoutingFactory(legacy, createJobsHandler) + ); + + deps.router.post( + { + path: `${API_BASE_PATH}/start`, + validate: { + body: schema.object({ + jobIds: schema.arrayOf(schema.string()), + }), + query: schema.maybe( + schema.object({ + waitForCompletion: schema.maybe(schema.string()), + }) + ), + }, + }, + licensePreRoutingFactory(legacy, startJobsHandler) + ); + + deps.router.post( + { + path: `${API_BASE_PATH}/stop`, + validate: { + body: schema.object({ + jobIds: schema.arrayOf(schema.string()), + }), + }, + }, + licensePreRoutingFactory(legacy, stopJobsHandler) + ); + + deps.router.post( + { + path: `${API_BASE_PATH}/delete`, + validate: { + body: schema.object({ + jobIds: schema.arrayOf(schema.string()), + }), + }, + }, + licensePreRoutingFactory(legacy, deleteJobsHandler) + ); +} diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/search.js b/x-pack/legacy/plugins/rollup/server/routes/api/search.js deleted file mode 100644 index 58098421f0a8f..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/routes/api/search.js +++ /dev/null @@ -1,44 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { callWithRequestFactory } from '../../lib/call_with_request_factory'; -import { isEsErrorFactory } from '../../lib/is_es_error_factory'; -import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import { wrapEsError, wrapUnknownError } from '../../lib/error_wrappers'; - -export function registerSearchRoute(server) { - const isEsError = isEsErrorFactory(server); - const licensePreRouting = licensePreRoutingFactory(server); - - server.route({ - path: '/api/rollup/search', - method: 'POST', - config: { - pre: [licensePreRouting], - }, - handler: async request => { - const callWithRequest = callWithRequestFactory(server, request); - - try { - const requests = request.payload.map(({ index, query }) => - callWithRequest('rollup.search', { - index, - rest_total_hits_as_int: true, - body: query, - }) - ); - - return await Promise.all(requests); - } catch (err) { - if (isEsError(err)) { - return wrapEsError(err); - } - - return wrapUnknownError(err); - } - }, - }); -} diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/search.ts b/x-pack/legacy/plugins/rollup/server/routes/api/search.ts new file mode 100644 index 0000000000000..97999a4b5ce8d --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/routes/api/search.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { RequestHandler } from 'src/core/server'; +import { callWithRequestFactory } from '../../lib/call_with_request_factory'; +import { isEsError } from '../../lib/is_es_error'; +import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; +import { API_BASE_PATH } from '../../../common'; +import { RouteDependencies, ServerShim } from '../../types'; + +export function registerSearchRoute(deps: RouteDependencies, legacy: ServerShim) { + const handler: RequestHandler = async (ctx, request, response) => { + const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request); + try { + const requests = request.body.map(({ index, query }: { index: string; query: any }) => + callWithRequest('rollup.search', { + index, + rest_total_hits_as_int: true, + body: query, + }) + ); + const data = await Promise.all(requests); + return response.ok({ body: data }); + } catch (err) { + if (isEsError(err)) { + return response.customError({ statusCode: err.statusCode, body: err }); + } + return response.internalError({ body: err }); + } + }; + + deps.router.post( + { + path: `${API_BASE_PATH}/search`, + validate: { + body: schema.arrayOf( + schema.object({ + index: schema.string(), + query: schema.any(), + }) + ), + }, + }, + licensePreRoutingFactory(legacy, handler) + ); +} diff --git a/x-pack/legacy/plugins/rollup/server/lib/register_license_checker/index.js b/x-pack/legacy/plugins/rollup/server/shared_imports.ts similarity index 75% rename from x-pack/legacy/plugins/rollup/server/lib/register_license_checker/index.js rename to x-pack/legacy/plugins/rollup/server/shared_imports.ts index 7b0f97c38d129..941610b97707f 100644 --- a/x-pack/legacy/plugins/rollup/server/lib/register_license_checker/index.js +++ b/x-pack/legacy/plugins/rollup/server/shared_imports.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { registerLicenseChecker } from './register_license_checker'; +export { IndexPatternsFetcher } from '../../../../../src/plugins/data/server'; diff --git a/x-pack/legacy/plugins/rollup/server/types.ts b/x-pack/legacy/plugins/rollup/server/types.ts new file mode 100644 index 0000000000000..62a4841133cff --- /dev/null +++ b/x-pack/legacy/plugins/rollup/server/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter, ElasticsearchServiceSetup, IClusterClient } from 'src/core/server'; +import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; + +export interface ServerShim { + plugins: { + xpack_main: XPackMainPlugin; + rollup: any; + index_management: any; + }; +} + +export interface RouteDependencies { + router: IRouter; + elasticsearchService: ElasticsearchServiceSetup; + elasticsearch: IClusterClient; +} diff --git a/x-pack/legacy/plugins/rollup/server/usage/index.js b/x-pack/legacy/plugins/rollup/server/usage/index.js deleted file mode 100644 index 9304b35aeb6c7..0000000000000 --- a/x-pack/legacy/plugins/rollup/server/usage/index.js +++ /dev/null @@ -1,7 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -export { registerRollupUsageCollector } from './collector'; diff --git a/x-pack/plugins/rollup/kibana.json b/x-pack/plugins/rollup/kibana.json new file mode 100644 index 0000000000000..6ab2fc8907c0d --- /dev/null +++ b/x-pack/plugins/rollup/kibana.json @@ -0,0 +1,6 @@ +{ + "id": "rollup", + "version": "8.0.0", + "kibanaVersion": "kibana", + "server": true +} diff --git a/x-pack/plugins/rollup/server/index.ts b/x-pack/plugins/rollup/server/index.ts new file mode 100644 index 0000000000000..4056842453776 --- /dev/null +++ b/x-pack/plugins/rollup/server/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext } from 'src/core/server'; +import { RollupPlugin } from './plugin'; + +export const plugin = (initContext: PluginInitializerContext) => new RollupPlugin(initContext); + +export { RollupSetup } from './plugin'; diff --git a/x-pack/plugins/rollup/server/plugin.ts b/x-pack/plugins/rollup/server/plugin.ts new file mode 100644 index 0000000000000..fa05b8d1307d6 --- /dev/null +++ b/x-pack/plugins/rollup/server/plugin.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin, PluginInitializerContext } from 'src/core/server'; + +export class RollupPlugin implements Plugin { + private readonly initContext: PluginInitializerContext; + + constructor(initContext: PluginInitializerContext) { + this.initContext = initContext; + } + + public setup() { + return { + __legacy: { + config: this.initContext.config, + logger: this.initContext.logger, + }, + }; + } + + public start() {} + public stop() {} +} + +export interface RollupSetup { + /** @deprecated */ + __legacy: { + config: PluginInitializerContext['config']; + logger: PluginInitializerContext['logger']; + }; +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3944b3e240abf..e26c8dc0ce69f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10253,9 +10253,6 @@ "xpack.reporting.shareContextMenu.csvReportsButtonLabel": "CSV レポート", "xpack.reporting.shareContextMenu.pdfReportsButtonLabel": "PDF レポート", "xpack.rollupJobs.appTitle": "ロールアップジョブ", - "xpack.rollupJobs.checkLicense.errorExpiredMessage": "{licenseType} ライセンスが期限切れのため {pluginName} を使用できません", - "xpack.rollupJobs.checkLicense.errorUnavailableMessage": "現在ライセンス情報が利用できないため {pluginName} を使用できません。", - "xpack.rollupJobs.checkLicense.errorUnsupportedMessage": "ご使用の {licenseType} ライセンスは {pluginName} をサポートしていません。ライセンスをアップグレードしてください。", "xpack.rollupJobs.create.backButton.label": "戻る", "xpack.rollupJobs.create.dateTypeField": "日付", "xpack.rollupJobs.create.errors.dateHistogramFieldMissing": "日付フィールドが必要です。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4315722a888eb..80cc08caa266c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10252,9 +10252,6 @@ "xpack.reporting.shareContextMenu.csvReportsButtonLabel": "CSV 报告", "xpack.reporting.shareContextMenu.pdfReportsButtonLabel": "PDF 报告", "xpack.rollupJobs.appTitle": "汇总/打包作业", - "xpack.rollupJobs.checkLicense.errorExpiredMessage": "您不能使用 {pluginName},因为您的 {licenseType} 许可证已过期", - "xpack.rollupJobs.checkLicense.errorUnavailableMessage": "您不能使用 {pluginName},因为许可证信息当前不可用。", - "xpack.rollupJobs.checkLicense.errorUnsupportedMessage": "您的 {licenseType} 许可证不支持 {pluginName}。请升级您的许可。", "xpack.rollupJobs.create.backButton.label": "上一步", "xpack.rollupJobs.create.dateTypeField": "日期", "xpack.rollupJobs.create.errors.dateHistogramFieldMissing": "“日期”字段必填。", diff --git a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js index 8eb084f24c52f..be2af7cb76fd5 100644 --- a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js +++ b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js @@ -32,28 +32,42 @@ export default function({ getService }) { it('"pattern" is required', async () => { uri = `${BASE_URI}`; ({ body } = await supertest.get(uri).expect(400)); - expect(body.message).to.contain('"pattern" is required'); + expect(body.message).to.contain( + '[request query.pattern]: expected value of type [string]' + ); }); it('"params" is required', async () => { params = { pattern: 'foo' }; uri = `${BASE_URI}?${querystring.stringify(params)}`; ({ body } = await supertest.get(uri).expect(400)); - expect(body.message).to.contain('"params" is required'); + expect(body.message).to.contain( + '[request query.params]: expected value of type [string]' + ); }); - it('"params" must be an object', async () => { - params = { pattern: 'foo', params: 'bar' }; + it('"params" must be a valid JSON string', async () => { + params = { pattern: 'foo', params: 'foobarbaz' }; uri = `${BASE_URI}?${querystring.stringify(params)}`; ({ body } = await supertest.get(uri).expect(400)); - expect(body.message).to.contain('"params" must be an object'); + expect(body.message).to.contain('[request query.params]: expected JSON string'); }); - it('"params" must be an object that only accepts a "rollup_index" property', async () => { - params = { pattern: 'foo', params: JSON.stringify({ someProp: 'bar' }) }; + it('"params" requires a "rollup_index" property', async () => { + params = { pattern: 'foo', params: JSON.stringify({}) }; uri = `${BASE_URI}?${querystring.stringify(params)}`; ({ body } = await supertest.get(uri).expect(400)); - expect(body.message).to.contain('"someProp" is not allowed'); + expect(body.message).to.contain('[request query.params]: "rollup_index" is required'); + }); + + it('"params" only accepts a "rollup_index" property', async () => { + params = { + pattern: 'foo', + params: JSON.stringify({ rollup_index: 'my_index', someProp: 'bar' }), + }; + uri = `${BASE_URI}?${querystring.stringify(params)}`; + ({ body } = await supertest.get(uri).expect(400)); + expect(body.message).to.contain('[request query.params]: someProp is not allowed'); }); it('"meta_fields" must be an Array', async () => { @@ -64,7 +78,9 @@ export default function({ getService }) { }; uri = `${BASE_URI}?${querystring.stringify(params)}`; ({ body } = await supertest.get(uri).expect(400)); - expect(body.message).to.contain('"meta_fields" must be an array'); + expect(body.message).to.contain( + '[request query.meta_fields]: expected value of type [array]' + ); }); it('should return 404 the rollup index to query does not exist', async () => { @@ -73,7 +89,7 @@ export default function({ getService }) { params: JSON.stringify({ rollup_index: 'bar' }), })}`; ({ body } = await supertest.get(uri).expect(404)); - expect(body.message).to.contain('no such index [bar]'); + expect(body.message).to.contain('[index_not_found_exception] no such index [bar]'); }); });