mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
* Initial move of searchprofiler into new platform directory, lots of things need testing * Whitespace, clean up types and remove unused files * First iteration of end-to-end plugin working - Updated license check to only check for presence of basic license (not search profiler as a feature - Updated the payload: removed types from validation - Also added README in public regarding the location of styles * Added extractProfilerErrorMessage function to interface with new error reporting from profiler endpoint * Fix paths to test_utils * Update I18n for search profiler * Fix react hooks ordering bug with license status updates and fix test (wait for first license object before rendering) * Added index.ts file to common in searchprofiler route Marked types and values as internal Removed unnecessary "async" from function Update import to not use "src" alias Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
0c6ed2aa14
commit
582faa0eb1
104 changed files with 527 additions and 509 deletions
156
.github/CODEOWNERS
vendored
Normal file
156
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
# GitHub CODEOWNERS definition
|
||||
# Identify which groups will be pinged by changes to different parts of the codebase.
|
||||
# For more info, see https://help.github.com/articles/about-codeowners/
|
||||
|
||||
# App
|
||||
/x-pack/legacy/plugins/lens/ @elastic/kibana-app
|
||||
/x-pack/legacy/plugins/graph/ @elastic/kibana-app
|
||||
/src/plugins/share/ @elastic/kibana-app
|
||||
/src/legacy/server/url_shortening/ @elastic/kibana-app
|
||||
/src/legacy/server/sample_data/ @elastic/kibana-app
|
||||
/src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-app
|
||||
/src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app
|
||||
/src/legacy/core_plugins/kibana/public/visualize/ @elastic/kibana-app
|
||||
/src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app
|
||||
/src/legacy/core_plugins/kibana/public/home/ @elastic/kibana-app
|
||||
/src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app
|
||||
/src/legacy/core_plugins/metrics/ @elastic/kibana-app
|
||||
/src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app
|
||||
/src/plugins/home/ @elastic/kibana-app
|
||||
/src/plugins/kibana_legacy/ @elastic/kibana-app
|
||||
/src/plugins/timelion/ @elastic/kibana-app
|
||||
/src/plugins/dev_tools/ @elastic/kibana-app
|
||||
|
||||
# App Architecture
|
||||
/src/plugins/data/ @elastic/kibana-app-arch
|
||||
/src/plugins/embeddable/ @elastic/kibana-app-arch
|
||||
/src/plugins/expressions/ @elastic/kibana-app-arch
|
||||
/src/plugins/kibana_react/ @elastic/kibana-app-arch
|
||||
/src/plugins/kibana_utils/ @elastic/kibana-app-arch
|
||||
/src/plugins/navigation/ @elastic/kibana-app-arch
|
||||
/src/plugins/ui_actions/ @elastic/kibana-app-arch
|
||||
/src/plugins/visualizations/ @elastic/kibana-app-arch
|
||||
/x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/data/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/elasticsearch/lib/create_proxy.js @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/kibana/public/management/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/kibana/server/field_formats/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/kibana/server/routes/api/management/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/kibana/server/routes/api/suggestions/ @elastic/kibana-app-arch
|
||||
/src/legacy/core_plugins/visualizations/ @elastic/kibana-app-arch
|
||||
/src/legacy/server/index_patterns/ @elastic/kibana-app-arch
|
||||
|
||||
# APM
|
||||
/x-pack/legacy/plugins/apm/ @elastic/apm-ui
|
||||
/x-pack/test/functional/apps/apm/ @elastic/apm-ui
|
||||
/src/legacy/core_plugins/apm_oss/ @elastic/apm-ui
|
||||
|
||||
# Beats
|
||||
/x-pack/legacy/plugins/beats_management/ @elastic/beats
|
||||
|
||||
# Canvas
|
||||
/x-pack/legacy/plugins/canvas/ @elastic/kibana-canvas
|
||||
|
||||
# Logs & Metrics UI
|
||||
/x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui
|
||||
/x-pack/legacy/plugins/integrations_manager/ @elastic/epm
|
||||
|
||||
# Machine Learning
|
||||
/x-pack/legacy/plugins/ml/ @elastic/ml-ui
|
||||
/x-pack/test/functional/apps/machine_learning/ @elastic/ml-ui
|
||||
/x-pack/test/functional/services/machine_learning/ @elastic/ml-ui
|
||||
/x-pack/test/functional/services/ml.ts @elastic/ml-ui
|
||||
# ML team owns the transform plugin, ES team added here for visibility
|
||||
# because the plugin lives in Kibana's Elasticsearch management section.
|
||||
/x-pack/legacy/plugins/transform/ @elastic/ml-ui @elastic/es-ui
|
||||
/x-pack/test/functional/apps/transform/ @elastic/ml-ui
|
||||
/x-pack/test/functional/services/transform_ui/ @elastic/ml-ui
|
||||
/x-pack/test/functional/services/transform.ts @elastic/ml-ui
|
||||
|
||||
# Maps
|
||||
/x-pack/legacy/plugins/maps/ @elastic/kibana-gis
|
||||
/x-pack/test/api_integration/apis/maps/ @elastic/kibana-gis
|
||||
/x-pack/test/functional/apps/maps/ @elastic/kibana-gis
|
||||
/x-pack/test/functional/es_archives/maps/ @elastic/kibana-gis
|
||||
/x-pack/test/visual_regression/tests/maps/index.js @elastic/kibana-gis
|
||||
|
||||
# Operations
|
||||
/src/dev/ @elastic/kibana-operations
|
||||
/src/setup_node_env/ @elastic/kibana-operations
|
||||
/src/optimize/ @elastic/kibana-operations
|
||||
/packages/*eslint*/ @elastic/kibana-operations
|
||||
/packages/*babel*/ @elastic/kibana-operations
|
||||
/packages/kbn-dev-utils*/ @elastic/kibana-operations
|
||||
/packages/kbn-es/ @elastic/kibana-operations
|
||||
/packages/kbn-pm/ @elastic/kibana-operations
|
||||
/packages/kbn-test/ @elastic/kibana-operations
|
||||
/packages/kbn-ui-shared-deps/ @elastic/kibana-operations
|
||||
/src/legacy/server/keystore/ @elastic/kibana-operations
|
||||
/src/legacy/server/pid/ @elastic/kibana-operations
|
||||
/src/legacy/server/sass/ @elastic/kibana-operations
|
||||
/src/legacy/server/utils/ @elastic/kibana-operations
|
||||
/src/legacy/server/warnings/ @elastic/kibana-operations
|
||||
|
||||
# Platform
|
||||
/src/core/ @elastic/kibana-platform
|
||||
/config/kibana.yml @elastic/kibana-platform
|
||||
/x-pack/plugins/features/ @elastic/kibana-platform
|
||||
/x-pack/plugins/licensing/ @elastic/kibana-platform
|
||||
/packages/kbn-config-schema/ @elastic/kibana-platform
|
||||
/src/legacy/server/config/ @elastic/kibana-platform
|
||||
/src/legacy/server/http/ @elastic/kibana-platform
|
||||
/src/legacy/server/i18n/ @elastic/kibana-platform
|
||||
/src/legacy/server/logging/ @elastic/kibana-platform
|
||||
/src/legacy/server/saved_objects/ @elastic/kibana-platform
|
||||
/src/legacy/server/status/ @elastic/kibana-platform
|
||||
|
||||
# Security
|
||||
/src/core/server/csp/ @elastic/kibana-security @elastic/kibana-platform
|
||||
/x-pack/legacy/plugins/security/ @elastic/kibana-security
|
||||
/x-pack/legacy/plugins/spaces/ @elastic/kibana-security
|
||||
/x-pack/plugins/spaces/ @elastic/kibana-security
|
||||
/x-pack/legacy/plugins/encrypted_saved_objects/ @elastic/kibana-security
|
||||
/x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security
|
||||
/x-pack/plugins/security/ @elastic/kibana-security
|
||||
/x-pack/test/api_integration/apis/security/ @elastic/kibana-security
|
||||
|
||||
# Kibana Localization
|
||||
/src/dev/i18n/ @elastic/kibana-localization
|
||||
|
||||
# Pulse
|
||||
/packages/kbn-analytics/ @elastic/pulse
|
||||
/src/legacy/core_plugins/ui_metric/ @elastic/pulse
|
||||
/src/plugins/usage_collection/ @elastic/pulse
|
||||
/x-pack/legacy/plugins/telemetry/ @elastic/pulse
|
||||
|
||||
# Kibana Alerting Services
|
||||
/x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services
|
||||
/x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services
|
||||
/x-pack/plugins/event_log/ @elastic/kibana-alerting-services
|
||||
/x-pack/plugins/task_manager/ @elastic/kibana-alerting-services
|
||||
/x-pack/test/alerting_api_integration/ @elastic/kibana-alerting-services
|
||||
/x-pack/test/plugin_api_integration/plugins/task_manager/ @elastic/kibana-alerting-services
|
||||
/x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/kibana-alerting-services
|
||||
/x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services
|
||||
/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/kibana-alerting-services
|
||||
/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services
|
||||
|
||||
# Design
|
||||
**/*.scss @elastic/kibana-design
|
||||
|
||||
# Elasticsearch UI
|
||||
/src/legacy/core_plugins/console/ @elastic/es-ui
|
||||
/src/plugins/es_ui_shared/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/console_extensions/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/cross_cluster_replication/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/index_lifecycle_management/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/index_management/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/license_management/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/remote_clusters/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/rollup/ @elastic/es-ui
|
||||
/x-pack/plugins/searchprofiler/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/watcher/ @elastic/es-ui
|
|
@ -27,9 +27,9 @@
|
|||
"xpack.main": "legacy/plugins/xpack_main",
|
||||
"xpack.monitoring": "legacy/plugins/monitoring",
|
||||
"xpack.remoteClusters": "legacy/plugins/remote_clusters",
|
||||
"xpack.reporting": [ "plugins/reporting", "legacy/plugins/reporting" ],
|
||||
"xpack.reporting": ["plugins/reporting", "legacy/plugins/reporting"],
|
||||
"xpack.rollupJobs": "legacy/plugins/rollup",
|
||||
"xpack.searchProfiler": "legacy/plugins/searchprofiler",
|
||||
"xpack.searchProfiler": "plugins/searchprofiler",
|
||||
"xpack.siem": "legacy/plugins/siem",
|
||||
"xpack.security": ["legacy/plugins/security", "plugins/security"],
|
||||
"xpack.server": "legacy/server",
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
# Profiler
|
||||
|
||||
A UI for the query and aggregation profiler in Elasticsearch
|
||||
|
||||
## Development
|
||||
|
||||
Assuming you've checked out x-plugins next to kibana...
|
||||
|
||||
- Run `yarn kbn bootstrap`
|
||||
- Run `yarn start` to watch for and sync files on change
|
||||
- Open a new terminal to run Kibana - use `yarn start` to launch it in dev mode
|
||||
- Kibana will automatically restart as files are synced
|
||||
- If you need debugging output, run `DEBUG=reporting yarn start` instead
|
||||
|
||||
If you have installed this somewhere other than via x-plugins, and next to the kibana repo, you'll need to change the `pathToKibana` setting in `gulpfile.js`
|
||||
|
||||
## Testing
|
||||
|
||||
To run the server tests, change into `x-plugins/kibana` and run:
|
||||
|
||||
```bash
|
||||
mocha --debug --compilers js:@babel/register plugins/profiler/**/__tests__/**/*.js
|
||||
```
|
||||
|
||||
|
||||
--kbnServer.tests_bundle.pluginId
|
|
@ -5,12 +5,10 @@
|
|||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import Boom from 'boom';
|
||||
|
||||
import { CoreSetup } from 'src/core/server';
|
||||
import { Server } from 'src/legacy/server/kbn_server';
|
||||
import { LegacySetup } from './server/np_ready/types';
|
||||
import { plugin } from './server/np_ready';
|
||||
// TODO:
|
||||
// Until we can process SCSS in new platform, this part of Searchprofiler
|
||||
// legacy must remain here.
|
||||
|
||||
export const searchprofiler = (kibana: any) => {
|
||||
const publicSrc = resolve(__dirname, 'public');
|
||||
|
@ -22,43 +20,8 @@ export const searchprofiler = (kibana: any) => {
|
|||
publicDir: publicSrc,
|
||||
|
||||
uiExports: {
|
||||
// NP Ready
|
||||
devTools: [`${publicSrc}/legacy`],
|
||||
styleSheetPaths: `${publicSrc}/np_ready/application/index.scss`,
|
||||
// Legacy
|
||||
home: ['plugins/searchprofiler/register_feature'],
|
||||
},
|
||||
init(server: Server) {
|
||||
const serverPlugin = plugin();
|
||||
const thisPlugin = this;
|
||||
|
||||
const commonRouteConfig = {
|
||||
pre: [
|
||||
function forbidApiAccess() {
|
||||
const licenseCheckResults = server.plugins.xpack_main.info
|
||||
.feature(thisPlugin.id)
|
||||
.getLicenseCheckResults();
|
||||
if (licenseCheckResults.showAppLink && licenseCheckResults.enableAppLink) {
|
||||
return null;
|
||||
} else {
|
||||
throw Boom.forbidden(licenseCheckResults.message);
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const legacySetup: LegacySetup = {
|
||||
route: (args: Parameters<typeof server.route>[0]) => server.route(args),
|
||||
plugins: {
|
||||
__LEGACY: {
|
||||
thisPlugin,
|
||||
xpackMain: server.plugins.xpack_main,
|
||||
elasticsearch: server.plugins.elasticsearch,
|
||||
commonRouteConfig,
|
||||
},
|
||||
},
|
||||
};
|
||||
serverPlugin.setup({} as CoreSetup, legacySetup);
|
||||
styleSheetPaths: `${publicSrc}/index.scss`,
|
||||
},
|
||||
init() {},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
||||
/* eslint-disable @kbn/eslint/no-restricted-paths */
|
||||
import { npSetup } from 'ui/new_platform';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
// @ts-ignore
|
||||
import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
|
||||
// @ts-ignore
|
||||
import { formatAngularHttpError } from 'ui/notify/lib';
|
||||
import 'ui/autoload/all';
|
||||
|
||||
import { plugin } from './np_ready';
|
||||
|
||||
const pluginInstance = plugin({} as any);
|
||||
|
||||
pluginInstance.setup(npSetup.core, {
|
||||
...npSetup.plugins,
|
||||
__LEGACY: {
|
||||
I18nContext,
|
||||
licenseEnabled: xpackInfo.get('features.searchprofiler.enableAppLink'),
|
||||
notifications: npSetup.core.notifications.toasts,
|
||||
formatAngularHttpError,
|
||||
},
|
||||
});
|
|
@ -1,62 +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';
|
||||
import {
|
||||
Plugin,
|
||||
CoreStart,
|
||||
CoreSetup,
|
||||
PluginInitializerContext,
|
||||
ToastsStart,
|
||||
} from 'src/core/public';
|
||||
|
||||
import { DevToolsSetup } from '../../../../../../src/plugins/dev_tools/public';
|
||||
|
||||
export class SearchProfilerUIPlugin implements Plugin {
|
||||
constructor(ctx: PluginInitializerContext) {}
|
||||
|
||||
async setup(
|
||||
core: CoreSetup,
|
||||
plugins: {
|
||||
__LEGACY: {
|
||||
I18nContext: any;
|
||||
licenseEnabled: boolean;
|
||||
notifications: ToastsStart;
|
||||
formatAngularHttpError: any;
|
||||
};
|
||||
dev_tools: DevToolsSetup;
|
||||
}
|
||||
) {
|
||||
const { http } = core;
|
||||
const {
|
||||
__LEGACY: { I18nContext, licenseEnabled, notifications, formatAngularHttpError },
|
||||
dev_tools,
|
||||
} = plugins;
|
||||
dev_tools.register({
|
||||
id: 'searchprofiler',
|
||||
title: i18n.translate('xpack.searchProfiler.pageDisplayName', {
|
||||
defaultMessage: 'Search Profiler',
|
||||
}),
|
||||
order: 5,
|
||||
enableRouting: false,
|
||||
async mount(ctx, params) {
|
||||
const { boot } = await import('./application/boot');
|
||||
return boot({
|
||||
http,
|
||||
licenseEnabled,
|
||||
el: params.element,
|
||||
I18nContext,
|
||||
notifications,
|
||||
formatAngularHttpError,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async start(core: CoreStart, plugins: any) {}
|
||||
|
||||
async stop() {}
|
||||
}
|
|
@ -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 {
|
||||
FeatureCatalogueRegistryProvider,
|
||||
FeatureCatalogueCategory,
|
||||
} from 'ui/registry/feature_catalogue';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
FeatureCatalogueRegistryProvider.register(() => {
|
||||
return {
|
||||
id: 'searchprofiler',
|
||||
title: i18n.translate('xpack.searchProfiler.registryProviderTitle', {
|
||||
defaultMessage: 'Search Profiler',
|
||||
}),
|
||||
description: i18n.translate('xpack.searchProfiler.registryProviderDescription', {
|
||||
defaultMessage: 'Quickly check the performance of any Elasticsearch query.',
|
||||
}),
|
||||
icon: 'searchProfilerApp',
|
||||
path: '/app/kibana#/dev_tools/searchprofiler',
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
};
|
||||
});
|
|
@ -1,81 +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', () => {
|
||||
let mockLicenseInfo: any;
|
||||
beforeEach(() => (mockLicenseInfo = {}));
|
||||
|
||||
describe('license information is not available', () => {
|
||||
beforeEach(() => (mockLicenseInfo.isAvailable = () => false));
|
||||
|
||||
it('should set showLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showAppLink).to.be(true);
|
||||
});
|
||||
|
||||
it('should set enableLinks to false', () => {
|
||||
expect(checkLicense(mockLicenseInfo).enableAppLink).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
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 showLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showAppLink).to.be(true);
|
||||
});
|
||||
|
||||
it('should set enableLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).enableAppLink).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('& license is expired', () => {
|
||||
beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false));
|
||||
|
||||
it('should set showLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showAppLink).to.be(true);
|
||||
});
|
||||
|
||||
it('should set enableLinks to false', () => {
|
||||
expect(checkLicense(mockLicenseInfo).enableAppLink).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('& license is basic', () => {
|
||||
beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => false));
|
||||
|
||||
describe('& license is active', () => {
|
||||
beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true));
|
||||
|
||||
it('should set showLinks to false', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showAppLink).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('& license is expired', () => {
|
||||
beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false));
|
||||
|
||||
it('should set showLinks to false', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showAppLink).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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 { i18n } from '@kbn/i18n';
|
||||
import { XPackInfo } from '../../../../xpack_main/server/lib/xpack_info';
|
||||
|
||||
export function checkLicense(
|
||||
xpackLicenseInfo: XPackInfo
|
||||
): { showAppLink: boolean; enableAppLink: boolean; message: string | undefined } {
|
||||
if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) {
|
||||
return {
|
||||
showAppLink: true,
|
||||
enableAppLink: false,
|
||||
message: i18n.translate('xpack.searchProfiler.unavailableLicenseInformationMessage', {
|
||||
defaultMessage:
|
||||
'Search Profiler is unavailable - license information is not available at this time.',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
const isLicenseActive = xpackLicenseInfo.license.isActive();
|
||||
let message: string | undefined;
|
||||
if (!isLicenseActive) {
|
||||
message = i18n.translate('xpack.searchProfiler.licenseHasExpiredMessage', {
|
||||
defaultMessage: 'Search Profiler is unavailable - license has expired.',
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
xpackLicenseInfo.license.isOneOf([
|
||||
'trial',
|
||||
'basic',
|
||||
'standard',
|
||||
'gold',
|
||||
'platinum',
|
||||
'enterprise',
|
||||
])
|
||||
) {
|
||||
return {
|
||||
showAppLink: true,
|
||||
enableAppLink: isLicenseActive,
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
||||
message = i18n.translate('xpack.searchProfiler.upgradeLicenseMessage', {
|
||||
defaultMessage:
|
||||
'Search Profiler is unavailable for the current {licenseInfo} license. Please upgrade your license.',
|
||||
values: { licenseInfo: xpackLicenseInfo.license.getType() },
|
||||
});
|
||||
return {
|
||||
showAppLink: false,
|
||||
enableAppLink: false,
|
||||
message,
|
||||
};
|
||||
}
|
|
@ -1,38 +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 { CoreSetup, Plugin } from 'src/core/server';
|
||||
import { LegacySetup } from './types';
|
||||
import { checkLicense } from './lib';
|
||||
// @ts-ignore
|
||||
import { mirrorPluginStatus } from '../../../../server/lib/mirror_plugin_status';
|
||||
|
||||
import * as profileRoute from './routes/profile';
|
||||
|
||||
export class SearchProfilerServerPlugin implements Plugin {
|
||||
async setup(
|
||||
core: CoreSetup,
|
||||
{
|
||||
route,
|
||||
plugins: {
|
||||
__LEGACY: { thisPlugin, elasticsearch, xpackMain, commonRouteConfig },
|
||||
},
|
||||
}: LegacySetup
|
||||
) {
|
||||
mirrorPluginStatus(xpackMain, thisPlugin);
|
||||
(xpackMain as any).status.once('green', () => {
|
||||
// Register a function that is called whenever the xpack info changes,
|
||||
// to re-compute the license check results for this plugin
|
||||
xpackMain.info.feature(thisPlugin.id).registerLicenseCheckResultsGenerator(checkLicense);
|
||||
});
|
||||
|
||||
profileRoute.register({ elasticsearch }, route, commonRouteConfig);
|
||||
}
|
||||
|
||||
async start() {}
|
||||
|
||||
stop(): void {}
|
||||
}
|
|
@ -1,53 +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 { RequestShim, ServerShim, RegisterRoute } from '../types';
|
||||
|
||||
export const handler = async (server: ServerShim, request: RequestShim) => {
|
||||
const { callWithRequest } = server.elasticsearch.getCluster('data');
|
||||
let parsed = request.payload.query;
|
||||
parsed.profile = true;
|
||||
parsed = JSON.stringify(parsed, null, 2);
|
||||
|
||||
const body = {
|
||||
index: request.payload.index,
|
||||
body: parsed,
|
||||
};
|
||||
try {
|
||||
const resp = await callWithRequest(request, 'search', body);
|
||||
return {
|
||||
ok: true,
|
||||
resp,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
ok: false,
|
||||
err,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const register = (server: ServerShim, route: RegisterRoute, commonConfig: any) => {
|
||||
route({
|
||||
path: '/api/searchprofiler/profile',
|
||||
method: 'POST',
|
||||
config: {
|
||||
...commonConfig,
|
||||
validate: {
|
||||
payload: Joi.object()
|
||||
.keys({
|
||||
query: Joi.object().required(),
|
||||
index: Joi.string().required(),
|
||||
type: Joi.string().optional(),
|
||||
})
|
||||
.required(),
|
||||
},
|
||||
},
|
||||
handler: req => {
|
||||
return handler(server, { headers: req.headers, payload: req.payload as any });
|
||||
},
|
||||
});
|
||||
};
|
|
@ -1,33 +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 { ServerRoute } from 'hapi';
|
||||
import { ElasticsearchPlugin, Request } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { XPackMainPlugin } from '../../../xpack_main/server/xpack_main';
|
||||
|
||||
export type RegisterRoute = (args: ServerRoute & { config: any }) => void;
|
||||
|
||||
export interface LegacyPlugins {
|
||||
__LEGACY: {
|
||||
thisPlugin: any;
|
||||
elasticsearch: ElasticsearchPlugin;
|
||||
xpackMain: XPackMainPlugin;
|
||||
commonRouteConfig: any;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LegacySetup {
|
||||
route: RegisterRoute;
|
||||
plugins: LegacyPlugins;
|
||||
}
|
||||
|
||||
export interface ServerShim {
|
||||
elasticsearch: ElasticsearchPlugin;
|
||||
}
|
||||
|
||||
export interface RequestShim extends Request {
|
||||
payload: any;
|
||||
}
|
14
x-pack/plugins/searchprofiler/common/constants.ts
Normal file
14
x-pack/plugins/searchprofiler/common/constants.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 { LicenseType } from '../../licensing/common/types';
|
||||
|
||||
const basicLicense: LicenseType = 'basic';
|
||||
|
||||
/** @internal */
|
||||
export const PLUGIN = Object.freeze({
|
||||
id: 'searchprofiler',
|
||||
minimumLicenseType: basicLicense,
|
||||
});
|
|
@ -4,4 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { checkLicense } from './check_license';
|
||||
export { PLUGIN } from './constants';
|
||||
|
||||
export { LicenseStatus } from './types';
|
|
@ -3,8 +3,9 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { SearchProfilerServerPlugin } from './plugin';
|
||||
|
||||
export const plugin = () => {
|
||||
return new SearchProfilerServerPlugin();
|
||||
};
|
||||
/** @internal */
|
||||
export interface LicenseStatus {
|
||||
valid: boolean;
|
||||
message?: string;
|
||||
}
|
8
x-pack/plugins/searchprofiler/kibana.json
Normal file
8
x-pack/plugins/searchprofiler/kibana.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"id": "searchprofiler",
|
||||
"version": "8.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"requiredPlugins": ["dev_tools", "home", "licensing"],
|
||||
"server": true,
|
||||
"ui": true
|
||||
}
|
3
x-pack/plugins/searchprofiler/public/README.md
Normal file
3
x-pack/plugins/searchprofiler/public/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## Please note
|
||||
|
||||
See x-pack/legacy/plugins/searchprofiler/public for styles.
|
|
@ -4,17 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { HttpStart as Http, ToastsSetup } from 'kibana/public';
|
||||
import React from 'react';
|
||||
import { HttpStart as Http, ToastsSetup } from 'src/core/public';
|
||||
|
||||
import { LicenseStatus } from '../../common';
|
||||
import { App } from '.';
|
||||
|
||||
export interface Dependencies {
|
||||
el: HTMLElement;
|
||||
http: Http;
|
||||
licenseEnabled: boolean;
|
||||
I18nContext: any;
|
||||
notifications: ToastsSetup;
|
||||
formatAngularHttpError: any;
|
||||
initialLicenseStatus: LicenseStatus;
|
||||
}
|
||||
|
||||
export type AppDependencies = Omit<Dependencies, 'el'>;
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { registerTestBed } from '../../../../../../../../test_utils';
|
||||
import { registerTestBed } from '../../../../../../test_utils';
|
||||
import { HighlightDetailsFlyout, Props } from '.';
|
||||
|
||||
describe('Highlight Details Flyout', () => {
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { registerTestBed } from '../../../../../../../test_utils';
|
||||
import { registerTestBed } from '../../../../../test_utils';
|
||||
|
||||
import { LicenseWarningNotice } from './license_warning_notice';
|
||||
|
|
@ -36,7 +36,7 @@ export const LicenseWarningNotice = () => {
|
|||
title={i18n.translate('xpack.searchProfiler.licenseErrorMessageTitle', {
|
||||
defaultMessage: 'License error',
|
||||
})}
|
||||
color="warning"
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
style={{ padding: '16px' }}
|
||||
>
|
|
@ -1,3 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const breakdown = {
|
||||
advance: 0,
|
||||
advance_count: 0,
|
|
@ -1,3 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const inputTimes = [
|
||||
{
|
||||
type: 'BooleanQuery',
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { registerTestBed } from '../../../../../../../../../test_utils';
|
||||
import { registerTestBed } from '../../../../../../../test_utils';
|
||||
import { searchResponse } from './fixtures/search_response';
|
||||
import { ProfileTree, Props } from '../profile_tree';
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { registerTestBed } from '../../../../../../../test_utils';
|
||||
import { registerTestBed } from '../../../../../test_utils';
|
||||
|
||||
import { SearchProfilerTabs, Props } from './searchprofiler_tabs';
|
||||
|
|
@ -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 { registerTestBed } from '../../../../../../../../../test_utils';
|
||||
import { registerTestBed } from '../../../../../../../test_utils';
|
||||
import { EmptyTreePlaceHolder } from '.';
|
||||
|
||||
describe('EmptyTreePlaceholder', () => {
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { registerTestBed } from '../../../../../../../../../test_utils';
|
||||
import { registerTestBed } from '../../../../../../../test_utils';
|
||||
import { ProfileLoadingPlaceholder } from '.';
|
||||
|
||||
describe('Profile Loading Placeholder', () => {
|
|
@ -42,7 +42,7 @@ function hasAggregations(profileResponse: ShardSerialized[]) {
|
|||
}
|
||||
|
||||
export const Main = () => {
|
||||
const { licenseEnabled } = useAppContext();
|
||||
const { getLicenseStatus } = useAppContext();
|
||||
|
||||
const {
|
||||
activeTab,
|
||||
|
@ -63,7 +63,7 @@ export const Main = () => {
|
|||
]);
|
||||
|
||||
const renderLicenseWarning = () => {
|
||||
return !licenseEnabled ? (
|
||||
return !getLicenseStatus().valid ? (
|
||||
<>
|
||||
<LicenseWarningNotice />
|
||||
<EuiSpacer size="s" />
|
||||
|
@ -84,7 +84,7 @@ export const Main = () => {
|
|||
);
|
||||
}
|
||||
|
||||
if (licenseEnabled && pristine) {
|
||||
if (getLicenseStatus().valid && pristine) {
|
||||
return <EmptyTreePlaceHolder />;
|
||||
}
|
||||
|
|
@ -3,6 +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 React, { useRef, memo, useCallback } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
|
@ -39,7 +40,7 @@ export const ProfileQueryEditor = memo(() => {
|
|||
|
||||
const dispatch = useProfilerActionContext();
|
||||
|
||||
const { licenseEnabled, notifications } = useAppContext();
|
||||
const { getLicenseStatus, notifications } = useAppContext();
|
||||
const requestProfile = useRequestProfile();
|
||||
|
||||
const handleProfileClick = async () => {
|
||||
|
@ -65,6 +66,7 @@ export const ProfileQueryEditor = memo(() => {
|
|||
};
|
||||
|
||||
const onEditorReady = useCallback(editorInstance => (editorRef.current = editorInstance), []);
|
||||
const licenseEnabled = getLicenseStatus().valid;
|
||||
|
||||
return (
|
||||
<EuiFlexGroup
|
|
@ -4,26 +4,45 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useContext, createContext } from 'react';
|
||||
import React, { useContext, createContext, useCallback } from 'react';
|
||||
|
||||
import { HttpSetup, ToastsSetup } from 'kibana/public';
|
||||
import { LicenseStatus } from '../../../common';
|
||||
|
||||
export interface ContextArgs {
|
||||
http: HttpSetup;
|
||||
notifications: ToastsSetup;
|
||||
initialLicenseStatus: LicenseStatus;
|
||||
}
|
||||
|
||||
export interface ContextValue {
|
||||
http: HttpSetup;
|
||||
notifications: ToastsSetup;
|
||||
licenseEnabled: boolean;
|
||||
formatAngularHttpError: (error: any) => string;
|
||||
getLicenseStatus: () => LicenseStatus;
|
||||
}
|
||||
|
||||
const AppContext = createContext<ContextValue>(null as any);
|
||||
|
||||
export const AppContextProvider = ({
|
||||
children,
|
||||
value,
|
||||
args: { http, notifications, initialLicenseStatus },
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
value: ContextValue;
|
||||
args: ContextArgs;
|
||||
}) => {
|
||||
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
|
||||
const getLicenseStatus = useCallback(() => initialLicenseStatus, [initialLicenseStatus]);
|
||||
|
||||
return (
|
||||
<AppContext.Provider
|
||||
value={{
|
||||
http,
|
||||
notifications,
|
||||
getLicenseStatus,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AppContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAppContext = () => {
|
|
@ -10,7 +10,7 @@ jest.mock('./worker', () => {
|
|||
return { workerModule: { id: 'ace/mode/json_worker', src: '' } };
|
||||
});
|
||||
|
||||
import { registerTestBed } from '../../../../../../../test_utils';
|
||||
import { registerTestBed } from '../../../../../test_utils';
|
||||
import { Editor, Props } from '.';
|
||||
|
||||
describe('Editor Component', () => {
|
|
@ -10,10 +10,7 @@ import { Editor as AceEditor } from 'brace';
|
|||
import { initializeEditor } from './init_editor';
|
||||
import { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode';
|
||||
|
||||
interface EditorShim {
|
||||
getValue(): string;
|
||||
focus(): void;
|
||||
}
|
||||
type EditorShim = ReturnType<typeof createEditorShim>;
|
||||
|
||||
export type EditorInstance = EditorShim;
|
||||
|
||||
|
@ -23,7 +20,7 @@ export interface Props {
|
|||
onEditorReady: (editor: EditorShim) => void;
|
||||
}
|
||||
|
||||
const createEditorShim = (aceEditor: AceEditor): EditorShim => {
|
||||
const createEditorShim = (aceEditor: AceEditor) => {
|
||||
return {
|
||||
getValue() {
|
||||
return aceEditor.getValue();
|
||||
|
@ -40,15 +37,13 @@ export const Editor = memo(({ licenseEnabled, initialValue, onEditorReady }: Pro
|
|||
|
||||
const [textArea, setTextArea] = useState<HTMLTextAreaElement | null>(null);
|
||||
|
||||
if (licenseEnabled) {
|
||||
useUIAceKeyboardMode(textArea);
|
||||
}
|
||||
useUIAceKeyboardMode(textArea);
|
||||
|
||||
useEffect(() => {
|
||||
const divEl = containerRef.current;
|
||||
editorInstanceRef.current = initializeEditor({ el: divEl, licenseEnabled });
|
||||
editorInstanceRef.current.setValue(initialValue, 1);
|
||||
setTextArea(containerRef.current!.querySelector('textarea'));
|
||||
setTextArea(licenseEnabled ? containerRef.current!.querySelector('textarea') : null);
|
||||
|
||||
onEditorReady(createEditorShim(editorInstanceRef.current));
|
||||
}, [initialValue, onEditorReady, licenseEnabled]);
|
|
@ -19,8 +19,22 @@ interface ReturnValue {
|
|||
error?: string;
|
||||
}
|
||||
|
||||
const extractProfilerErrorMessage = (e: any): string | undefined => {
|
||||
if (e.body?.attributes?.error?.reason) {
|
||||
const { reason, line, col } = e.body.attributes.error;
|
||||
return `${reason} at line: ${line - 1} col: ${col}`;
|
||||
}
|
||||
|
||||
if (e.body?.message) {
|
||||
return e.body.message;
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export const useRequestProfile = () => {
|
||||
const { http, notifications, formatAngularHttpError, licenseEnabled } = useAppContext();
|
||||
const { http, notifications, getLicenseStatus } = useAppContext();
|
||||
const licenseEnabled = getLicenseStatus().valid;
|
||||
return async ({ query, index }: Args): Promise<ReturnValue> => {
|
||||
if (!licenseEnabled) {
|
||||
return { data: null };
|
||||
|
@ -39,7 +53,7 @@ export const useRequestProfile = () => {
|
|||
return { data: parsed.profile.shards };
|
||||
}
|
||||
|
||||
const payload: Record<string, any> = { query };
|
||||
const payload: Record<string, any> = { query: parsed };
|
||||
|
||||
if (index == null || index === '') {
|
||||
payload.index = '_all';
|
||||
|
@ -59,11 +73,13 @@ export const useRequestProfile = () => {
|
|||
|
||||
return { data: resp.resp.profile.shards };
|
||||
} catch (e) {
|
||||
try {
|
||||
// Is this a known error type?
|
||||
const errorString = formatAngularHttpError(e);
|
||||
notifications.addError(e, { title: errorString });
|
||||
} catch (_) {
|
||||
const profilerErrorMessage = extractProfilerErrorMessage(e);
|
||||
if (profilerErrorMessage) {
|
||||
notifications.addError(e, {
|
||||
title: e.message,
|
||||
toastMessage: profilerErrorMessage,
|
||||
});
|
||||
} else {
|
||||
// Otherwise just report the original error
|
||||
notifications.addError(e, {
|
||||
title: i18n.translate('xpack.searchProfiler.errorSomethingWentWrongTitle', {
|
|
@ -10,16 +10,10 @@ import { ProfileContextProvider } from './contexts/profiler_context';
|
|||
|
||||
import { AppDependencies } from './boot';
|
||||
|
||||
export function App({
|
||||
I18nContext,
|
||||
licenseEnabled,
|
||||
notifications,
|
||||
http,
|
||||
formatAngularHttpError,
|
||||
}: AppDependencies) {
|
||||
export function App({ I18nContext, initialLicenseStatus, notifications, http }: AppDependencies) {
|
||||
return (
|
||||
<I18nContext>
|
||||
<AppContextProvider value={{ licenseEnabled, notifications, http, formatAngularHttpError }}>
|
||||
<AppContextProvider args={{ initialLicenseStatus, notifications, http }}>
|
||||
<ProfileContextProvider>
|
||||
<Main />
|
||||
</ProfileContextProvider>
|
69
x-pack/plugins/searchprofiler/public/plugin.ts
Normal file
69
x-pack/plugins/searchprofiler/public/plugin.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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';
|
||||
import { Plugin, CoreStart, CoreSetup, PluginInitializerContext } from 'kibana/public';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public';
|
||||
import { LICENSE_CHECK_STATE } from '../../licensing/public';
|
||||
|
||||
import { PLUGIN } from '../common';
|
||||
import { AppPublicPluginDependencies } from './types';
|
||||
|
||||
export class SearchProfilerUIPlugin implements Plugin<void, void, AppPublicPluginDependencies> {
|
||||
constructor(ctx: PluginInitializerContext) {}
|
||||
|
||||
async setup(
|
||||
{ http, getStartServices }: CoreSetup,
|
||||
{ dev_tools, home, licensing }: AppPublicPluginDependencies
|
||||
) {
|
||||
home.featureCatalogue.register({
|
||||
id: PLUGIN.id,
|
||||
title: i18n.translate('xpack.searchProfiler.registryProviderTitle', {
|
||||
defaultMessage: 'Search Profiler',
|
||||
}),
|
||||
description: i18n.translate('xpack.searchProfiler.registryProviderDescription', {
|
||||
defaultMessage: 'Quickly check the performance of any Elasticsearch query.',
|
||||
}),
|
||||
icon: 'searchProfilerApp',
|
||||
path: '/app/kibana#/dev_tools/searchprofiler',
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
|
||||
dev_tools.register({
|
||||
id: 'searchprofiler',
|
||||
title: i18n.translate('xpack.searchProfiler.pageDisplayName', {
|
||||
defaultMessage: 'Search Profiler',
|
||||
}),
|
||||
order: 5,
|
||||
enableRouting: false,
|
||||
mount: async (ctx, params) => {
|
||||
const [coreStart] = await getStartServices();
|
||||
const { notifications, i18n: i18nDep } = coreStart;
|
||||
const { boot } = await import('./application/boot');
|
||||
|
||||
const license = await licensing.license$.pipe(first()).toPromise();
|
||||
const { state, message } = license.check(PLUGIN.id, PLUGIN.minimumLicenseType);
|
||||
const initialLicenseStatus =
|
||||
state === LICENSE_CHECK_STATE.Valid ? { valid: true } : { valid: false, message };
|
||||
|
||||
return boot({
|
||||
http,
|
||||
initialLicenseStatus,
|
||||
el: params.element,
|
||||
I18nContext: i18nDep.Context,
|
||||
notifications: notifications.toasts,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async start(core: CoreStart, plugins: any) {}
|
||||
|
||||
async stop() {}
|
||||
}
|
15
x-pack/plugins/searchprofiler/public/types.ts
Normal file
15
x-pack/plugins/searchprofiler/public/types.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
|
||||
import { DevToolsSetup } from '../../../../src/plugins/dev_tools/public';
|
||||
import { LicensingPluginSetup } from '../../licensing/public';
|
||||
|
||||
export interface AppPublicPluginDependencies {
|
||||
licensing: LicensingPluginSetup;
|
||||
home: HomePublicPluginSetup;
|
||||
dev_tools: DevToolsSetup;
|
||||
}
|
11
x-pack/plugins/searchprofiler/server/index.ts
Normal file
11
x-pack/plugins/searchprofiler/server/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 'kibana/server';
|
||||
import { SearchProfilerServerPlugin } from './plugin';
|
||||
|
||||
export const plugin = (ctx: PluginInitializerContext) => {
|
||||
return new SearchProfilerServerPlugin(ctx);
|
||||
};
|
59
x-pack/plugins/searchprofiler/server/plugin.ts
Normal file
59
x-pack/plugins/searchprofiler/server/plugin.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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';
|
||||
import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
|
||||
|
||||
import { LICENSE_CHECK_STATE } from '../../licensing/common/types';
|
||||
|
||||
import { LicenseStatus, PLUGIN } from '../common';
|
||||
import { AppServerPluginDependencies } from './types';
|
||||
import * as profileRoute from './routes/profile';
|
||||
|
||||
export class SearchProfilerServerPlugin implements Plugin {
|
||||
licenseStatus: LicenseStatus;
|
||||
log: Logger;
|
||||
|
||||
constructor({ logger }: PluginInitializerContext) {
|
||||
this.log = logger.get();
|
||||
this.licenseStatus = { valid: false };
|
||||
}
|
||||
|
||||
async setup({ http }: CoreSetup, { licensing, elasticsearch }: AppServerPluginDependencies) {
|
||||
const router = http.createRouter();
|
||||
profileRoute.register({
|
||||
elasticsearch,
|
||||
router,
|
||||
getLicenseStatus: () => this.licenseStatus,
|
||||
log: this.log,
|
||||
});
|
||||
|
||||
licensing.license$.subscribe(license => {
|
||||
const { state, message } = license.check(PLUGIN.id, PLUGIN.minimumLicenseType);
|
||||
const hasRequiredLicense = state === LICENSE_CHECK_STATE.Valid;
|
||||
if (hasRequiredLicense) {
|
||||
this.licenseStatus = { valid: true };
|
||||
} else {
|
||||
this.licenseStatus = {
|
||||
valid: false,
|
||||
message:
|
||||
message ||
|
||||
// Ensure that there is a message when license check fails
|
||||
i18n.translate('xpack.searchProfiler.licenseCheckErrorMessage', {
|
||||
defaultMessage: 'License check failed',
|
||||
}),
|
||||
};
|
||||
if (message) {
|
||||
this.log.info(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
start() {}
|
||||
|
||||
stop() {}
|
||||
}
|
70
x-pack/plugins/searchprofiler/server/routes/profile.ts
Normal file
70
x-pack/plugins/searchprofiler/server/routes/profile.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 { RouteDependencies } from '../types';
|
||||
|
||||
export const register = ({ router, getLicenseStatus, log }: RouteDependencies) => {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/searchprofiler/profile',
|
||||
validate: {
|
||||
body: schema.object({
|
||||
query: schema.object({}, { allowUnknowns: true }),
|
||||
index: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (ctx, request, response) => {
|
||||
const currentLicenseStatus = getLicenseStatus();
|
||||
if (!currentLicenseStatus.valid) {
|
||||
return response.forbidden({
|
||||
body: {
|
||||
message: currentLicenseStatus.message!,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
core: { elasticsearch },
|
||||
} = ctx;
|
||||
|
||||
const {
|
||||
body: { query, index },
|
||||
} = request;
|
||||
|
||||
const parsed = {
|
||||
// Activate profiler mode for this query.
|
||||
profile: true,
|
||||
...query,
|
||||
};
|
||||
|
||||
const body = {
|
||||
index,
|
||||
body: JSON.stringify(parsed, null, 2),
|
||||
};
|
||||
try {
|
||||
const resp = await elasticsearch.dataClient.callAsCurrentUser('search', body);
|
||||
return response.ok({
|
||||
body: {
|
||||
ok: true,
|
||||
resp,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
log.error(err);
|
||||
return response.customError({
|
||||
statusCode: err.status || 500,
|
||||
body: err.body
|
||||
? {
|
||||
message: err.message,
|
||||
attributes: err.body,
|
||||
}
|
||||
: err,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue