{
return false;
}
- const filters = await getDataActions().createFiltersFromEvent(item.values);
+ const filters = await getDataActions().createFiltersFromValueClickAction({ data: item.values });
return Boolean(filters.length);
};
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js
index ecf67ee3e017c..f33ce0395af1f 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js
@@ -83,10 +83,21 @@ export class Handler {
// memoize so that the same function is returned every time,
// allowing us to remove/re-add the same function
- this.getProxyHandler = _.memoize(function(event) {
+ this.getProxyHandler = _.memoize(function(eventType) {
const self = this;
- return function(e) {
- self.vis.emit(event, e);
+ return function(eventPayload) {
+ switch (eventType) {
+ case 'brush':
+ const xRaw = _.get(eventPayload.data, 'series[0].values[0].xRaw');
+ if (!xRaw) return; // not sure if this is possible?
+ return self.vis.emit(eventType, {
+ table: xRaw.table,
+ range: eventPayload.range,
+ column: xRaw.column,
+ });
+ case 'click':
+ return self.vis.emit(eventType, eventPayload);
+ }
};
});
diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss
index aaed52f8b120a..f10718ba58c2c 100644
--- a/src/legacy/ui/public/_index.scss
+++ b/src/legacy/ui/public/_index.scss
@@ -9,9 +9,7 @@
// kbnChart__legend-isLoading
@import './accessibility/index';
-@import './chrome/index';
@import './directives/index';
-@import './error_auto_create_index/index';
@import './error_url_overflow/index';
@import './exit_full_screen/index';
@import './field_editor/index';
diff --git a/src/legacy/ui/public/chrome/_index.scss b/src/legacy/ui/public/chrome/_index.scss
deleted file mode 100644
index 7e6c3ebaccc5c..0000000000000
--- a/src/legacy/ui/public/chrome/_index.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-@import './variables';
-
-@import './directives/index';
diff --git a/src/legacy/ui/public/chrome/_variables.scss b/src/legacy/ui/public/chrome/_variables.scss
deleted file mode 100644
index 5097fe4c9bfae..0000000000000
--- a/src/legacy/ui/public/chrome/_variables.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-$kbnGlobalNavClosedWidth: 53px;
-$kbnGlobalNavOpenWidth: 180px;
-$kbnGlobalNavLogoHeight: 70px;
-$kbnGlobalNavAppIconHeight: $euiSizeXXL + $euiSizeXS;
diff --git a/src/legacy/ui/public/chrome/directives/_index.scss b/src/legacy/ui/public/chrome/directives/_index.scss
deleted file mode 100644
index 4d00b02279116..0000000000000
--- a/src/legacy/ui/public/chrome/directives/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './kbn_chrome';
diff --git a/src/legacy/ui/public/error_auto_create_index/_error_auto_create_index.scss b/src/legacy/ui/public/error_auto_create_index/_error_auto_create_index.scss
deleted file mode 100644
index ad31aabfc66cd..0000000000000
--- a/src/legacy/ui/public/error_auto_create_index/_error_auto_create_index.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.kbnError--auto-create-index {
- padding: $euiSizeL;
-}
diff --git a/src/legacy/ui/public/error_auto_create_index/_index.scss b/src/legacy/ui/public/error_auto_create_index/_index.scss
deleted file mode 100644
index 42e672ab322dc..0000000000000
--- a/src/legacy/ui/public/error_auto_create_index/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './error_auto_create_index'
diff --git a/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.html b/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.html
deleted file mode 100644
index 2af31dda6c345..0000000000000
--- a/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.html
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.test.js b/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.test.js
deleted file mode 100644
index a8f6318090b1d..0000000000000
--- a/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.test.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-// @ts-ignore
-import './error_auto_create_index.test.mocks';
-import fetchMock from 'fetch-mock/es5/client';
-import { kfetch } from '../kfetch';
-
-import { isAutoCreateIndexError } from './error_auto_create_index';
-
-describe('isAutoCreateIndexError correctly handles KFetchError thrown by kfetch', () => {
- describe('404', () => {
- beforeEach(() => {
- fetchMock.post({
- matcher: '*',
- response: {
- status: 404,
- },
- });
- });
- afterEach(() => fetchMock.restore());
-
- test('should return false', async () => {
- expect.assertions(1);
- try {
- await kfetch({ method: 'POST', pathname: '/my/path' });
- } catch (kfetchError) {
- expect(isAutoCreateIndexError(kfetchError)).toBe(false);
- }
- });
- });
-
- describe('503 error that is not ES_AUTO_CREATE_INDEX_ERROR', () => {
- beforeEach(() => {
- fetchMock.post({
- matcher: '*',
- response: {
- status: 503,
- },
- });
- });
- afterEach(() => fetchMock.restore());
-
- test('should return false', async () => {
- expect.assertions(1);
- try {
- await kfetch({ method: 'POST', pathname: '/my/path' });
- } catch (kfetchError) {
- expect(isAutoCreateIndexError(kfetchError)).toBe(false);
- }
- });
- });
-
- describe('503 error that is ES_AUTO_CREATE_INDEX_ERROR', () => {
- beforeEach(() => {
- fetchMock.post({
- matcher: '*',
- response: {
- body: {
- attributes: {
- code: 'ES_AUTO_CREATE_INDEX_ERROR',
- },
- },
- status: 503,
- },
- });
- });
- afterEach(() => fetchMock.restore());
-
- test('should return true', async () => {
- expect.assertions(1);
- try {
- await kfetch({ method: 'POST', pathname: '/my/path' });
- } catch (kfetchError) {
- expect(isAutoCreateIndexError(kfetchError)).toBe(true);
- }
- });
- });
-});
diff --git a/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.ts b/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.ts
deleted file mode 100644
index 09c6bfd93148f..0000000000000
--- a/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { i18n } from '@kbn/i18n';
-import { get } from 'lodash';
-
-import uiRoutes from '../routes';
-
-import template from './error_auto_create_index.html';
-
-uiRoutes.when('/error/action.auto_create_index', {
- template,
- k7Breadcrumbs: () => [
- {
- text: i18n.translate('common.ui.errorAutoCreateIndex.breadcrumbs.errorText', {
- defaultMessage: 'Error',
- }),
- },
- ],
-});
-
-export function isAutoCreateIndexError(error: object) {
- return (
- get(error, 'res.status') === 503 &&
- get(error, 'body.attributes.code') === 'ES_AUTO_CREATE_INDEX_ERROR'
- );
-}
-
-export function showAutoCreateIndexErrorPage() {
- window.location.hash = '/error/action.auto_create_index';
-}
diff --git a/src/legacy/ui/public/i18n/index.tsx b/src/legacy/ui/public/i18n/index.tsx
index 4d0f5d3a5bd56..c918554563fcb 100644
--- a/src/legacy/ui/public/i18n/index.tsx
+++ b/src/legacy/ui/public/i18n/index.tsx
@@ -44,7 +44,7 @@ export function wrapInI18nContext(ComponentToWrap: React.ComponentType
) {
}
uiModules
- .get('i18n')
+ .get('i18n', ['ngSanitize'])
.provider('i18n', I18nProvider)
.filter('i18n', i18nFilter)
.directive('i18nId', i18nDirective);
diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
index f14f26613ef01..271586bb8c582 100644
--- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
+++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
@@ -377,7 +377,8 @@ export const npStart = {
},
data: {
actions: {
- createFiltersFromEvent: Promise.resolve(['yes']),
+ createFiltersFromValueClickAction: Promise.resolve(['yes']),
+ createFiltersFromRangeSelectAction: sinon.fake(),
},
autocomplete: {
getProvider: sinon.fake(),
diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts
index 5ae2e2348aaa1..a15c7cce5511d 100644
--- a/src/legacy/ui/public/new_platform/new_platform.ts
+++ b/src/legacy/ui/public/new_platform/new_platform.ts
@@ -59,7 +59,6 @@ import {
NavigationPublicPluginSetup,
NavigationPublicPluginStart,
} from '../../../../plugins/navigation/public';
-import { VisTypeVegaSetup } from '../../../../plugins/vis_type_vega/public';
import { DiscoverSetup, DiscoverStart } from '../../../../plugins/discover/public';
import {
SavedObjectsManagementPluginSetup,
@@ -88,7 +87,6 @@ export interface PluginsSetup {
usageCollection: UsageCollectionSetup;
advancedSettings: AdvancedSettingsSetup;
management: ManagementSetup;
- visTypeVega: VisTypeVegaSetup;
discover: DiscoverSetup;
visualizations: VisualizationsSetup;
telemetry?: TelemetryPluginSetup;
diff --git a/src/legacy/ui/ui_bundles/app_entry_template.js b/src/legacy/ui/ui_bundles/app_entry_template.js
index a1c3a153a196c..683fedd34316f 100644
--- a/src/legacy/ui/ui_bundles/app_entry_template.js
+++ b/src/legacy/ui/ui_bundles/app_entry_template.js
@@ -25,6 +25,9 @@ export const appEntryTemplate = bundle => `
*
* This is programmatically created and updated, do not modify
*
+ * Any changes to this file should be kept in sync with
+ * src/core/public/entry_point.ts
+ *
* context: ${bundle.getContext()}
*/
@@ -45,7 +48,9 @@ i18n.load(injectedMetadata.i18n.translationsUrl)
browserSupportsCsp: !window.__kbnCspNotEnforced__,
requireLegacyFiles: () => {
${bundle.getRequires().join('\n ')}
- }
+ },
+ requireLegacyBootstrapModule: () => require('ui/chrome'),
+ requireNewPlatformShimModule: () => require('ui/new_platform'),
});
coreSystem
diff --git a/src/legacy/ui/ui_bundles/ui_bundles_controller.js b/src/legacy/ui/ui_bundles/ui_bundles_controller.js
index 7afa283af83e0..79112fd687e84 100644
--- a/src/legacy/ui/ui_bundles/ui_bundles_controller.js
+++ b/src/legacy/ui/ui_bundles/ui_bundles_controller.js
@@ -99,13 +99,6 @@ export class UiBundlesController {
this._postLoaders = [];
this._bundles = [];
- // create a bundle for core-only with no modules
- this.add({
- id: 'core',
- modules: [],
- template: appEntryTemplate,
- });
-
// create a bundle for each uiApp
for (const uiApp of uiApps) {
this.add({
diff --git a/src/legacy/ui/ui_exports/ui_export_defaults.js b/src/legacy/ui/ui_exports/ui_export_defaults.js
index bb246d97bfe4e..35e1f8b7d2127 100644
--- a/src/legacy/ui/ui_exports/ui_export_defaults.js
+++ b/src/legacy/ui/ui_exports/ui_export_defaults.js
@@ -24,6 +24,7 @@ export const UI_EXPORT_DEFAULTS = {
webpackNoParseRules: [
/node_modules[\/\\](angular|elasticsearch-browser)[\/\\]/,
/node_modules[\/\\](mocha|moment)[\/\\]/,
+ /node_modules[\/\\]vega-lib[\/\\]build[\/\\]vega\.js$/,
],
webpackAliases: {
diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs
index 1093153edbbf7..8a71c6ccb1506 100644
--- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs
+++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs
@@ -1,6 +1,7 @@
var kbnCsp = JSON.parse(document.querySelector('kbn-csp').getAttribute('data'));
window.__kbnStrictCsp__ = kbnCsp.strictCsp;
window.__kbnDarkMode__ = {{darkMode}};
+window.__kbnPublicPath__ = {{publicPathMap}};
if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) {
var legacyBrowserError = document.getElementById('kbn_legacy_browser_error');
@@ -69,26 +70,16 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) {
}
load([
- {{#each sharedJsDepFilenames}}
- '{{../regularBundlePath}}/kbn-ui-shared-deps/{{this}}',
- {{/each}}
- '{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedJsFilename}}',
- '{{dllBundlePath}}/vendors_runtime.bundle.dll.js',
- {{#each dllJsChunks}}
+ {{#each jsDependencyPaths}}
'{{this}}',
{{/each}}
- '{{regularBundlePath}}/commons.bundle.js',
- {{!-- '{{regularBundlePath}}/plugin/data/data.plugin.js', --}}
- '{{regularBundlePath}}/plugin/kibanaUtils/kibanaUtils.plugin.js',
- '{{regularBundlePath}}/plugin/esUiShared/esUiShared.plugin.js',
- '{{regularBundlePath}}/plugin/kibanaReact/kibanaReact.plugin.js'
], function () {
load([
- '{{regularBundlePath}}/{{appId}}.bundle.js',
+ '{{entryBundlePath}}',
{{#each styleSheetPaths}}
'{{this}}',
{{/each}}
- ])
+ ]);
});
- };
+ }
}
diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js
index 0912d8683fc48..801eecf5b608b 100644
--- a/src/legacy/ui/ui_render/ui_render_mixin.js
+++ b/src/legacy/ui/ui_render/ui_render_mixin.js
@@ -103,41 +103,78 @@ export function uiRenderMixin(kbnServer, server, config) {
const dllJsChunks = DllCompiler.getRawDllConfig().chunks.map(
chunk => `${dllBundlePath}/vendors${chunk}.bundle.dll.js`
);
+
const styleSheetPaths = [
- ...dllStyleChunks,
+ ...(isCore ? [] : dllStyleChunks),
`${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`,
...(darkMode
? [
`${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}`,
`${basePath}/node_modules/@kbn/ui-framework/dist/kui_dark.css`,
+ `${regularBundlePath}/dark_theme.style.css`,
]
: [
`${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`,
`${basePath}/node_modules/@kbn/ui-framework/dist/kui_light.css`,
+ `${regularBundlePath}/light_theme.style.css`,
]),
- `${regularBundlePath}/${darkMode ? 'dark' : 'light'}_theme.style.css`,
`${regularBundlePath}/commons.style.css`,
- ...(!isCore ? [`${regularBundlePath}/${app.getId()}.style.css`] : []),
- ...kbnServer.uiExports.styleSheetPaths
- .filter(path => path.theme === '*' || path.theme === (darkMode ? 'dark' : 'light'))
- .map(path =>
- path.localPath.endsWith('.scss')
- ? `${basePath}/built_assets/css/${path.publicPath}`
- : `${basePath}/${path.publicPath}`
- )
- .reverse(),
+ ...(isCore
+ ? []
+ : [
+ `${regularBundlePath}/${app.getId()}.style.css`,
+ ...kbnServer.uiExports.styleSheetPaths
+ .filter(
+ path => path.theme === '*' || path.theme === (darkMode ? 'dark' : 'light')
+ )
+ .map(path =>
+ path.localPath.endsWith('.scss')
+ ? `${basePath}/built_assets/css/${path.publicPath}`
+ : `${basePath}/${path.publicPath}`
+ )
+ .reverse(),
+ ]),
];
+ const jsDependencyPaths = [
+ ...UiSharedDeps.jsDepFilenames.map(
+ filename => `${regularBundlePath}/kbn-ui-shared-deps/${filename}`
+ ),
+ `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.jsFilename}`,
+ ...(isCore
+ ? []
+ : [
+ `${dllBundlePath}/vendors_runtime.bundle.dll.js`,
+ ...dllJsChunks,
+ `${regularBundlePath}/commons.bundle.js`,
+ ]),
+ `${regularBundlePath}/plugin/kibanaUtils/kibanaUtils.plugin.js`,
+ `${regularBundlePath}/plugin/esUiShared/esUiShared.plugin.js`,
+ `${regularBundlePath}/plugin/kibanaReact/kibanaReact.plugin.js`,
+ ];
+
+ const uiPluginIds = [...kbnServer.newPlatform.__internals.uiPlugins.public.keys()];
+
+ // These paths should align with the bundle routes configured in
+ // src/optimize/bundles_route/bundles_route.js
+ const publicPathMap = JSON.stringify({
+ core: `${regularBundlePath}/core/`,
+ 'kbn-ui-shared-deps': `${regularBundlePath}/kbn-ui-shared-deps/`,
+ ...uiPluginIds.reduce(
+ (acc, pluginId) => ({ ...acc, [pluginId]: `${regularBundlePath}/plugin/${pluginId}/` }),
+ {}
+ ),
+ });
+
const bootstrap = new AppBootstrap({
templateData: {
- appId: isCore ? 'core' : app.getId(),
- regularBundlePath,
- dllBundlePath,
- dllJsChunks,
- styleSheetPaths,
- sharedJsFilename: UiSharedDeps.jsFilename,
- sharedJsDepFilenames: UiSharedDeps.jsDepFilenames,
darkMode,
+ jsDependencyPaths,
+ styleSheetPaths,
+ publicPathMap,
+ entryBundlePath: isCore
+ ? `${regularBundlePath}/core/core.entry.js`
+ : `${regularBundlePath}/${app.getId()}.bundle.js`,
},
});
diff --git a/src/optimize/bundles_route/bundles_route.js b/src/optimize/bundles_route/bundles_route.js
index 0c2e98b5acd63..4030988c8552c 100644
--- a/src/optimize/bundles_route/bundles_route.js
+++ b/src/optimize/bundles_route/bundles_route.js
@@ -17,11 +17,12 @@
* under the License.
*/
-import { isAbsolute, extname } from 'path';
+import { isAbsolute, extname, join } from 'path';
import LruCache from 'lru-cache';
import * as UiSharedDeps from '@kbn/ui-shared-deps';
import { createDynamicAssetResponse } from './dynamic_asset_response';
import { assertIsNpUiPluginPublicDirs } from '../np_ui_plugin_public_dirs';
+import { fromRoot } from '../../core/server/utils';
/**
* Creates the routes that serves files from `bundlesPath` or from
@@ -71,37 +72,57 @@ export function createBundlesRoute({
}
return [
- buildRouteForBundles(
- `${basePublicPath}/bundles/kbn-ui-shared-deps/`,
- '/bundles/kbn-ui-shared-deps/',
- UiSharedDeps.distDir,
- fileHashCache
- ),
+ buildRouteForBundles({
+ publicPath: `${basePublicPath}/bundles/kbn-ui-shared-deps/`,
+ routePath: '/bundles/kbn-ui-shared-deps/',
+ bundlesPath: UiSharedDeps.distDir,
+ fileHashCache,
+ replacePublicPath: false,
+ }),
...npUiPluginPublicDirs.map(({ id, path }) =>
- buildRouteForBundles(
- `${basePublicPath}/bundles/plugin/${id}/`,
- `/bundles/plugin/${id}/`,
- path,
- fileHashCache
- )
- ),
- buildRouteForBundles(
- `${basePublicPath}/bundles/`,
- '/bundles/',
- regularBundlesPath,
- fileHashCache
+ buildRouteForBundles({
+ publicPath: `${basePublicPath}/bundles/plugin/${id}/`,
+ routePath: `/bundles/plugin/${id}/`,
+ bundlesPath: path,
+ fileHashCache,
+ replacePublicPath: false,
+ })
),
- buildRouteForBundles(
- `${basePublicPath}/built_assets/dlls/`,
- '/built_assets/dlls/',
- dllBundlesPath,
- fileHashCache
- ),
- buildRouteForBundles(`${basePublicPath}/`, '/built_assets/css/', builtCssPath, fileHashCache),
+ buildRouteForBundles({
+ publicPath: `${basePublicPath}/bundles/core/`,
+ routePath: `/bundles/core/`,
+ bundlesPath: fromRoot(join('src', 'core', 'target', 'public')),
+ fileHashCache,
+ replacePublicPath: false,
+ }),
+ buildRouteForBundles({
+ publicPath: `${basePublicPath}/bundles/`,
+ routePath: '/bundles/',
+ bundlesPath: regularBundlesPath,
+ fileHashCache,
+ }),
+ buildRouteForBundles({
+ publicPath: `${basePublicPath}/built_assets/dlls/`,
+ routePath: '/built_assets/dlls/',
+ bundlesPath: dllBundlesPath,
+ fileHashCache,
+ }),
+ buildRouteForBundles({
+ publicPath: `${basePublicPath}/`,
+ routePath: '/built_assets/css/',
+ bundlesPath: builtCssPath,
+ fileHashCache,
+ }),
];
}
-function buildRouteForBundles(publicPath, routePath, bundlesPath, fileHashCache) {
+function buildRouteForBundles({
+ publicPath,
+ routePath,
+ bundlesPath,
+ fileHashCache,
+ replacePublicPath = true,
+}) {
return {
method: 'GET',
path: `${routePath}{path*}`,
@@ -122,6 +143,7 @@ function buildRouteForBundles(publicPath, routePath, bundlesPath, fileHashCache)
bundlesPath,
fileHashCache,
publicPath,
+ replacePublicPath,
});
},
},
diff --git a/src/optimize/bundles_route/dynamic_asset_response.js b/src/optimize/bundles_route/dynamic_asset_response.js
index 7af780a79e430..80c49a26270fd 100644
--- a/src/optimize/bundles_route/dynamic_asset_response.js
+++ b/src/optimize/bundles_route/dynamic_asset_response.js
@@ -52,7 +52,7 @@ import { replacePlaceholder } from '../public_path_placeholder';
* @property {LruCache} options.fileHashCache
*/
export async function createDynamicAssetResponse(options) {
- const { request, h, bundlesPath, publicPath, fileHashCache } = options;
+ const { request, h, bundlesPath, publicPath, fileHashCache, replacePublicPath } = options;
let fd;
try {
@@ -78,11 +78,14 @@ export async function createDynamicAssetResponse(options) {
});
fd = null; // read stream is now responsible for fd
+ const content = replacePublicPath ? replacePlaceholder(read, publicPath) : read;
+ const etag = replacePublicPath ? `${hash}-${publicPath}` : hash;
+
return h
- .response(replacePlaceholder(read, publicPath))
+ .response(content)
.takeover()
.code(200)
- .etag(`${hash}-${publicPath}`)
+ .etag(etag)
.header('cache-control', 'must-revalidate')
.type(request.server.mime.path(path).type);
} catch (error) {
diff --git a/src/plugins/advanced_settings/kibana.json b/src/plugins/advanced_settings/kibana.json
index cac9a6daa8df8..e6ca6e797ba45 100644
--- a/src/plugins/advanced_settings/kibana.json
+++ b/src/plugins/advanced_settings/kibana.json
@@ -1,7 +1,7 @@
{
"id": "advancedSettings",
"version": "kibana",
- "server": false,
+ "server": true,
"ui": true,
"requiredPlugins": ["management"]
}
diff --git a/src/plugins/advanced_settings/server/capabilities_provider.ts b/src/plugins/advanced_settings/server/capabilities_provider.ts
new file mode 100644
index 0000000000000..083d5f3ffced4
--- /dev/null
+++ b/src/plugins/advanced_settings/server/capabilities_provider.ts
@@ -0,0 +1,25 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export const capabilitiesProvider = () => ({
+ advancedSettings: {
+ show: true,
+ save: true,
+ },
+});
diff --git a/src/plugins/advanced_settings/server/index.ts b/src/plugins/advanced_settings/server/index.ts
new file mode 100644
index 0000000000000..ffcf7cd49a8c3
--- /dev/null
+++ b/src/plugins/advanced_settings/server/index.ts
@@ -0,0 +1,24 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext } from 'kibana/server';
+import { AdvancedSettingsServerPlugin } from './plugin';
+
+export const plugin = (initContext: PluginInitializerContext) =>
+ new AdvancedSettingsServerPlugin(initContext);
diff --git a/src/plugins/advanced_settings/server/plugin.ts b/src/plugins/advanced_settings/server/plugin.ts
new file mode 100644
index 0000000000000..4d7bd34259819
--- /dev/null
+++ b/src/plugins/advanced_settings/server/plugin.ts
@@ -0,0 +1,44 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from 'kibana/server';
+import { capabilitiesProvider } from './capabilities_provider';
+
+export class AdvancedSettingsServerPlugin implements Plugin {
+ private readonly logger: Logger;
+
+ constructor(initializerContext: PluginInitializerContext) {
+ this.logger = initializerContext.logger.get();
+ }
+
+ public setup(core: CoreSetup) {
+ this.logger.debug('advancedSettings: Setup');
+
+ core.capabilities.registerProvider(capabilitiesProvider);
+
+ return {};
+ }
+
+ public start(core: CoreStart) {
+ this.logger.debug('advancedSettings: Started');
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/src/plugins/dashboard/public/bwc/types.ts b/src/plugins/dashboard/common/bwc/types.ts
similarity index 93%
rename from src/plugins/dashboard/public/bwc/types.ts
rename to src/plugins/dashboard/common/bwc/types.ts
index d5655e525e9bd..2427799345463 100644
--- a/src/plugins/dashboard/public/bwc/types.ts
+++ b/src/plugins/dashboard/common/bwc/types.ts
@@ -18,33 +18,28 @@
*/
import { SavedObjectReference } from 'kibana/public';
-import { GridData } from '../application';
-export interface SavedObjectAttributes {
+import { GridData } from '../';
+
+interface SavedObjectAttributes {
kibanaSavedObjectMeta: {
searchSourceJSON: string;
};
}
-export interface Doc {
+interface Doc {
references: SavedObjectReference[];
attributes: Attributes;
id: string;
type: string;
}
-export interface DocPre700 {
+interface DocPre700 {
attributes: Attributes;
id: string;
type: string;
}
-export interface SavedObjectAttributes {
- kibanaSavedObjectMeta: {
- searchSourceJSON: string;
- };
-}
-
interface DashboardAttributes extends SavedObjectAttributes {
panelsJSON: string;
description: string;
@@ -55,8 +50,6 @@ interface DashboardAttributes extends SavedObjectAttributes {
optionsJSON?: string;
}
-export type DashboardAttributes730ToLatest = DashboardAttributes;
-
interface DashboardAttributesTo720 extends SavedObjectAttributes {
panelsJSON: string;
description: string;
diff --git a/src/plugins/dashboard/common/embeddable/types.ts b/src/plugins/dashboard/common/embeddable/types.ts
new file mode 100644
index 0000000000000..eb76d73af7a58
--- /dev/null
+++ b/src/plugins/dashboard/common/embeddable/types.ts
@@ -0,0 +1,26 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface GridData {
+ w: number;
+ h: number;
+ x: number;
+ y: number;
+ i: string;
+}
diff --git a/src/plugins/dashboard/common/index.ts b/src/plugins/dashboard/common/index.ts
new file mode 100644
index 0000000000000..e3f3f629ae5d0
--- /dev/null
+++ b/src/plugins/dashboard/common/index.ts
@@ -0,0 +1,36 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export { GridData } from './embeddable/types';
+export {
+ RawSavedDashboardPanel730ToLatest,
+ DashboardDoc730ToLatest,
+ DashboardDoc700To720,
+ DashboardDocPre700,
+} from './bwc/types';
+export {
+ SavedDashboardPanelTo60,
+ SavedDashboardPanel610,
+ SavedDashboardPanel620,
+ SavedDashboardPanel630,
+ SavedDashboardPanel640To720,
+ SavedDashboardPanel730ToLatest,
+} from './types';
+
+export { migratePanelsTo730 } from './migrate_to_730_panels';
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.test.ts b/src/plugins/dashboard/common/migrate_to_730_panels.test.ts
similarity index 97%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.test.ts
rename to src/plugins/dashboard/common/migrate_to_730_panels.test.ts
index 4dd71fd8ee5f4..0867909225ddb 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.test.ts
+++ b/src/plugins/dashboard/common/migrate_to_730_panels.test.ts
@@ -19,15 +19,12 @@
import { migratePanelsTo730 } from './migrate_to_730_panels';
import {
RawSavedDashboardPanelTo60,
- RawSavedDashboardPanel610,
- RawSavedDashboardPanel620,
RawSavedDashboardPanel630,
RawSavedDashboardPanel640To720,
- DEFAULT_PANEL_WIDTH,
- DEFAULT_PANEL_HEIGHT,
- SavedDashboardPanelTo60,
- SavedDashboardPanel730ToLatest,
-} from '../../../../../../plugins/dashboard/public';
+ RawSavedDashboardPanel610,
+ RawSavedDashboardPanel620,
+} from './bwc/types';
+import { SavedDashboardPanelTo60, SavedDashboardPanel730ToLatest } from './types';
test('6.0 migrates uiState, sort, scales, and gridData', async () => {
const uiState = {
@@ -96,8 +93,8 @@ test('6.0 migration gives default width and height when missing', () => {
},
];
const newPanels = migratePanelsTo730(panels, '8.0.0', true);
- expect(newPanels[0].gridData.w).toBe(DEFAULT_PANEL_WIDTH);
- expect(newPanels[0].gridData.h).toBe(DEFAULT_PANEL_HEIGHT);
+ expect(newPanels[0].gridData.w).toBe(24);
+ expect(newPanels[0].gridData.h).toBe(15);
expect(newPanels[0].version).toBe('8.0.0');
});
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.ts b/src/plugins/dashboard/common/migrate_to_730_panels.ts
similarity index 97%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.ts
rename to src/plugins/dashboard/common/migrate_to_730_panels.ts
index a19c861f092d5..b89345f0a872c 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.ts
+++ b/src/plugins/dashboard/common/migrate_to_730_panels.ts
@@ -21,17 +21,19 @@ import semver from 'semver';
import uuid from 'uuid';
import {
GridData,
+ SavedDashboardPanelTo60,
+ SavedDashboardPanel620,
+ SavedDashboardPanel630,
+ SavedDashboardPanel610,
+} from './';
+import {
RawSavedDashboardPanelTo60,
RawSavedDashboardPanel630,
RawSavedDashboardPanel640To720,
RawSavedDashboardPanel730ToLatest,
RawSavedDashboardPanel610,
RawSavedDashboardPanel620,
- SavedDashboardPanelTo60,
- SavedDashboardPanel620,
- SavedDashboardPanel630,
- SavedDashboardPanel610,
-} from '../../../../../../plugins/dashboard/public';
+} from './bwc/types';
const PANEL_HEIGHT_SCALE_FACTOR = 5;
const PANEL_HEIGHT_SCALE_FACTOR_WITH_MARGINS = 4;
@@ -92,7 +94,7 @@ function migratePre61PanelToLatest(
): RawSavedDashboardPanel730ToLatest {
if (panel.col === undefined || panel.row === undefined) {
throw new Error(
- i18n.translate('kbn.dashboard.panel.unableToMigratePanelDataForSixOneZeroErrorMessage', {
+ i18n.translate('dashboard.panel.unableToMigratePanelDataForSixOneZeroErrorMessage', {
defaultMessage:
'Unable to migrate panel data for "6.1.0" backwards compatibility, panel does not contain expected col and/or row fields',
})
@@ -151,7 +153,7 @@ function migrate610PanelToLatest(
(['w', 'x', 'h', 'y'] as Array).forEach(key => {
if (panel.gridData[key] === undefined) {
throw new Error(
- i18n.translate('kbn.dashboard.panel.unableToMigratePanelDataForSixThreeZeroErrorMessage', {
+ i18n.translate('dashboard.panel.unableToMigratePanelDataForSixThreeZeroErrorMessage', {
defaultMessage:
'Unable to migrate panel data for "6.3.0" backwards compatibility, panel does not contain expected field: {key}',
values: { key },
diff --git a/src/plugins/dashboard/common/types.ts b/src/plugins/dashboard/common/types.ts
new file mode 100644
index 0000000000000..7cc82a9173976
--- /dev/null
+++ b/src/plugins/dashboard/common/types.ts
@@ -0,0 +1,76 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {
+ RawSavedDashboardPanelTo60,
+ RawSavedDashboardPanel610,
+ RawSavedDashboardPanel620,
+ RawSavedDashboardPanel630,
+ RawSavedDashboardPanel640To720,
+ RawSavedDashboardPanel730ToLatest,
+} from './bwc/types';
+
+export type SavedDashboardPanel640To720 = Pick<
+ RawSavedDashboardPanel640To720,
+ Exclude
+> & {
+ readonly id: string;
+ readonly type: string;
+};
+
+export type SavedDashboardPanel630 = Pick<
+ RawSavedDashboardPanel630,
+ Exclude
+> & {
+ readonly id: string;
+ readonly type: string;
+};
+
+export type SavedDashboardPanel620 = Pick<
+ RawSavedDashboardPanel620,
+ Exclude
+> & {
+ readonly id: string;
+ readonly type: string;
+};
+
+export type SavedDashboardPanel610 = Pick<
+ RawSavedDashboardPanel610,
+ Exclude
+> & {
+ readonly id: string;
+ readonly type: string;
+};
+
+export type SavedDashboardPanelTo60 = Pick<
+ RawSavedDashboardPanelTo60,
+ Exclude
+> & {
+ readonly id: string;
+ readonly type: string;
+};
+
+// id becomes optional starting in 7.3.0
+export type SavedDashboardPanel730ToLatest = Pick<
+ RawSavedDashboardPanel730ToLatest,
+ Exclude
+> & {
+ readonly id?: string;
+ readonly type: string;
+};
diff --git a/src/plugins/dashboard/kibana.json b/src/plugins/dashboard/kibana.json
index 9bcd999c2dcc0..4cd8f3c7d981f 100644
--- a/src/plugins/dashboard/kibana.json
+++ b/src/plugins/dashboard/kibana.json
@@ -11,6 +11,6 @@
"savedObjects"
],
"optionalPlugins": ["home", "share", "usageCollection"],
- "server": false,
+ "server": true,
"ui": true
}
diff --git a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
index 7210879c5eacc..1bc85fa110ca0 100644
--- a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
+++ b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
@@ -304,13 +304,13 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = `
url="/plugins/kibana/home/assets/welcome_graphic_light_2x.png"
>
@@ -998,13 +998,13 @@ exports[`DashboardEmptyScreen renders correctly without visualize paragraph 1`]
url="/plugins/kibana/home/assets/welcome_graphic_light_2x.png"
>
diff --git a/src/plugins/dashboard/public/application/application.ts b/src/plugins/dashboard/public/application/application.ts
index 3134a5bfe2c67..a1696298117b0 100644
--- a/src/plugins/dashboard/public/application/application.ts
+++ b/src/plugins/dashboard/public/application/application.ts
@@ -38,12 +38,7 @@ import { EmbeddableStart } from '../../../embeddable/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../navigation/public';
import { DataPublicPluginStart } from '../../../data/public';
import { SharePluginStart } from '../../../share/public';
-import {
- KibanaLegacyStart,
- configureAppAngularModule,
- createTopNavDirective,
- createTopNavHelper,
-} from '../../../kibana_legacy/public';
+import { KibanaLegacyStart, configureAppAngularModule } from '../../../kibana_legacy/public';
import { SavedObjectLoader } from '../../../saved_objects/public';
export interface RenderDeps {
@@ -114,13 +109,11 @@ function mountDashboardApp(appBasePath: string, element: HTMLElement) {
function createLocalAngularModule(core: AppMountContext['core'], navigation: NavigationStart) {
createLocalI18nModule();
- createLocalTopNavModule(navigation);
createLocalIconModule();
const dashboardAngularModule = angular.module(moduleName, [
...thirdPartyAngularDependencies,
'app/dashboard/I18n',
- 'app/dashboard/TopNav',
'app/dashboard/icon',
]);
return dashboardAngularModule;
@@ -132,13 +125,6 @@ function createLocalIconModule() {
.directive('icon', reactDirective => reactDirective(EuiIcon));
}
-function createLocalTopNavModule(navigation: NavigationStart) {
- angular
- .module('app/dashboard/TopNav', ['react'])
- .directive('kbnTopNav', createTopNavDirective)
- .directive('kbnTopNavHelper', createTopNavHelper(navigation.ui));
-}
-
function createLocalI18nModule() {
angular
.module('app/dashboard/I18n', [])
diff --git a/src/plugins/dashboard/public/application/dashboard_app.html b/src/plugins/dashboard/public/application/dashboard_app.html
index 3cf8932958b6d..87a5728ac2059 100644
--- a/src/plugins/dashboard/public/application/dashboard_app.html
+++ b/src/plugins/dashboard/public/application/dashboard_app.html
@@ -2,52 +2,7 @@
class="app-container dshAppContainer"
ng-class="{'dshAppContainer--withMargins': model.useMargins}"
>
-
-
-
-
-
-
-
-
+
{{screenTitle}}
diff --git a/src/plugins/dashboard/public/application/dashboard_app.tsx b/src/plugins/dashboard/public/application/dashboard_app.tsx
index 150cd8f8fcbb5..f101935b9288d 100644
--- a/src/plugins/dashboard/public/application/dashboard_app.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_app.tsx
@@ -33,7 +33,6 @@ import { SavedObjectDashboard } from '../saved_dashboards';
export interface DashboardAppScope extends ng.IScope {
dash: SavedObjectDashboard;
appState: DashboardAppState;
- screenTitle: string;
model: {
query: Query;
filters: Filter[];
@@ -54,21 +53,7 @@ export interface DashboardAppScope extends ng.IScope {
getShouldShowEditHelp: () => boolean;
getShouldShowViewHelp: () => boolean;
updateQueryAndFetch: ({ query, dateRange }: { query: Query; dateRange?: TimeRange }) => void;
- onRefreshChange: ({
- isPaused,
- refreshInterval,
- }: {
- isPaused: boolean;
- refreshInterval: any;
- }) => void;
- onFiltersUpdated: (filters: Filter[]) => void;
- onCancelApplyFilters: () => void;
- onApplyFilters: (filters: Filter[]) => void;
- onQuerySaved: (savedQuery: SavedQuery) => void;
- onSavedQueryUpdated: (savedQuery: SavedQuery) => void;
- onClearSavedQuery: () => void;
topNavMenu: any;
- showFilterBar: () => boolean;
showAddPanel: any;
showSaveQuery: boolean;
kbnTopNav: any;
diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
index 283fe9f0a83a4..fa2f06bfcdcdd 100644
--- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx
@@ -21,12 +21,15 @@ import _, { uniq } from 'lodash';
import { i18n } from '@kbn/i18n';
import { EUI_MODAL_CANCEL_BUTTON } from '@elastic/eui';
import React from 'react';
+import ReactDOM from 'react-dom';
import angular from 'angular';
import { Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { History } from 'history';
import { SavedObjectSaveOpts } from 'src/plugins/saved_objects/public';
+import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public';
+import { TimeRange } from 'src/plugins/data/public';
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';
import {
@@ -87,6 +90,7 @@ export interface DashboardAppControllerDependencies extends RenderDeps {
dashboardConfig: KibanaLegacyStart['dashboardConfig'];
history: History;
kbnUrlStateStorage: IKbnUrlStateStorage;
+ navigation: NavigationStart;
}
export class DashboardAppController {
@@ -123,10 +127,13 @@ export class DashboardAppController {
history,
kbnUrlStateStorage,
usageCollection,
+ navigation,
}: DashboardAppControllerDependencies) {
const filterManager = queryService.filterManager;
const queryFilter = filterManager;
const timefilter = queryService.timefilter.timefilter;
+ let showSearchBar = true;
+ let showQueryBar = true;
let lastReloadRequestTime = 0;
const dash = ($scope.dash = $route.current.locals.dash);
@@ -243,6 +250,9 @@ export class DashboardAppController {
}
};
+ const showFilterBar = () =>
+ $scope.model.filters.length > 0 || !dashboardStateManager.getFullScreenMode();
+
const getEmptyScreenProps = (
shouldShowEditHelp: boolean,
isEmptyInReadOnlyMode: boolean
@@ -310,7 +320,6 @@ export class DashboardAppController {
refreshInterval: timefilter.getRefreshInterval(),
};
$scope.panels = dashboardStateManager.getPanels();
- $scope.screenTitle = dashboardStateManager.getTitle();
};
updateState();
@@ -515,49 +524,8 @@ export class DashboardAppController {
}
};
- $scope.onRefreshChange = function({ isPaused, refreshInterval }) {
- timefilter.setRefreshInterval({
- pause: isPaused,
- value: refreshInterval ? refreshInterval : $scope.model.refreshInterval.value,
- });
- };
-
- $scope.onFiltersUpdated = filters => {
- // The filters will automatically be set when the queryFilter emits an update event (see below)
- queryFilter.setFilters(filters);
- };
-
- $scope.onQuerySaved = savedQuery => {
- $scope.savedQuery = savedQuery;
- };
-
- $scope.onSavedQueryUpdated = savedQuery => {
- $scope.savedQuery = { ...savedQuery };
- };
-
- $scope.onClearSavedQuery = () => {
- delete $scope.savedQuery;
- dashboardStateManager.setSavedQueryId(undefined);
- dashboardStateManager.applyFilters(
- {
- query: '',
- language:
- localStorage.get('kibana.userQueryLanguage') || uiSettings.get('search:queryLanguage'),
- },
- queryFilter.getGlobalFilters()
- );
- // Making this method sync broke the updates.
- // Temporary fix, until we fix the complex state in this file.
- setTimeout(() => {
- queryFilter.setFilters(queryFilter.getGlobalFilters());
- }, 0);
- };
-
const updateStateFromSavedQuery = (savedQuery: SavedQuery) => {
- const savedQueryFilters = savedQuery.attributes.filters || [];
- const globalFilters = queryFilter.getGlobalFilters();
- const allFilters = [...globalFilters, ...savedQueryFilters];
-
+ const allFilters = filterManager.getFilters();
dashboardStateManager.applyFilters(savedQuery.attributes.query, allFilters);
if (savedQuery.attributes.timefilter) {
timefilter.setTime({
@@ -616,6 +584,48 @@ export class DashboardAppController {
}
);
+ const onSavedQueryIdChange = (savedQueryId?: string) => {
+ dashboardStateManager.setSavedQueryId(savedQueryId);
+ };
+
+ const getNavBarProps = () => {
+ const isFullScreenMode = dashboardStateManager.getFullScreenMode();
+ const screenTitle = dashboardStateManager.getTitle();
+ return {
+ appName: 'dashboard',
+ config: $scope.isVisible ? $scope.topNavMenu : undefined,
+ className: isFullScreenMode ? 'kbnTopNavMenu-isFullScreen' : undefined,
+ screenTitle,
+ showSearchBar,
+ showQueryBar,
+ showFilterBar: showFilterBar(),
+ indexPatterns: $scope.indexPatterns,
+ showSaveQuery: $scope.showSaveQuery,
+ query: $scope.model.query,
+ savedQuery: $scope.savedQuery,
+ onSavedQueryIdChange,
+ savedQueryId: dashboardStateManager.getSavedQueryId(),
+ useDefaultBehaviors: true,
+ onQuerySubmit: (payload: { dateRange: TimeRange; query?: Query }): void => {
+ if (!payload.query) {
+ $scope.updateQueryAndFetch({ query: $scope.model.query, dateRange: payload.dateRange });
+ } else {
+ $scope.updateQueryAndFetch({ query: payload.query, dateRange: payload.dateRange });
+ }
+ },
+ };
+ };
+ const dashboardNavBar = document.getElementById('dashboardChrome');
+ const updateNavBar = () => {
+ ReactDOM.render( , dashboardNavBar);
+ };
+
+ const unmountNavBar = () => {
+ if (dashboardNavBar) {
+ ReactDOM.unmountComponentAtNode(dashboardNavBar);
+ }
+ };
+
$scope.timefilterSubscriptions$ = new Subscription();
$scope.timefilterSubscriptions$.add(
@@ -707,6 +717,8 @@ export class DashboardAppController {
revertChangesAndExitEditMode();
}
});
+
+ updateNavBar();
};
/**
@@ -761,9 +773,6 @@ export class DashboardAppController {
});
}
- $scope.showFilterBar = () =>
- $scope.model.filters.length > 0 || !dashboardStateManager.getFullScreenMode();
-
$scope.showAddPanel = () => {
dashboardStateManager.setFullScreenMode(false);
/*
@@ -785,7 +794,11 @@ export class DashboardAppController {
const navActions: {
[key: string]: NavAction;
} = {};
- navActions[TopNavIds.FULL_SCREEN] = () => dashboardStateManager.setFullScreenMode(true);
+ navActions[TopNavIds.FULL_SCREEN] = () => {
+ dashboardStateManager.setFullScreenMode(true);
+ showQueryBar = false;
+ updateNavBar();
+ };
navActions[TopNavIds.EXIT_EDIT_MODE] = () => onChangeViewMode(ViewMode.VIEW);
navActions[TopNavIds.ENTER_EDIT_MODE] = () => onChangeViewMode(ViewMode.EDIT);
navActions[TopNavIds.SAVE] = () => {
@@ -858,6 +871,7 @@ export class DashboardAppController {
if ((response as { error: Error }).error) {
dashboardStateManager.setTitle(currentTitle);
}
+ updateNavBar();
return response;
});
};
@@ -939,6 +953,9 @@ export class DashboardAppController {
const visibleSubscription = chrome.getIsVisible$().subscribe(isVisible => {
$scope.$evalAsync(() => {
$scope.isVisible = isVisible;
+ showSearchBar = isVisible || showFilterBar();
+ showQueryBar = !dashboardStateManager.getFullScreenMode() && isVisible;
+ updateNavBar();
});
});
@@ -949,9 +966,17 @@ export class DashboardAppController {
navActions,
dashboardConfig.getHideWriteControls()
);
+ updateNavBar();
+ });
+
+ $scope.$watch('indexPatterns', () => {
+ updateNavBar();
});
$scope.$on('$destroy', () => {
+ // we have to unmount nav bar manually to make sure all internal subscriptions are unsubscribed
+ unmountNavBar();
+
updateSubscription.unsubscribe();
stopSyncingQueryServiceStateWithUrl();
stopSyncingAppFilters();
diff --git a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx
index b15a813aff903..f8632011002d0 100644
--- a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx
+++ b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx
@@ -29,9 +29,10 @@ import _ from 'lodash';
import React from 'react';
import { Subscription } from 'rxjs';
import ReactGridLayout, { Layout } from 'react-grid-layout';
+import { GridData } from '../../../../common';
import { ViewMode, EmbeddableChildPanel } from '../../../embeddable_plugin';
import { DASHBOARD_GRID_COLUMN_COUNT, DASHBOARD_GRID_HEIGHT } from '../dashboard_constants';
-import { DashboardPanelState, GridData } from '../types';
+import { DashboardPanelState } from '../types';
import { withKibana } from '../../../../../kibana_react/public';
import { DashboardContainerInput } from '../dashboard_container';
import { DashboardContainer, DashboardReactContextValue } from '../dashboard_container';
@@ -274,6 +275,7 @@ class DashboardGridUi extends React.Component {
getEmbeddableFactory={this.props.kibana.services.embeddable.getEmbeddableFactory}
getAllEmbeddableFactories={this.props.kibana.services.embeddable.getEmbeddableFactories}
overlays={this.props.kibana.services.overlays}
+ application={this.props.kibana.services.application}
notifications={this.props.kibana.services.notifications}
inspector={this.props.kibana.services.inspector}
SavedObjectFinder={this.props.kibana.services.SavedObjectFinder}
diff --git a/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts b/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts
index 70a6c83418587..b95b7f394a27d 100644
--- a/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts
+++ b/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts
@@ -18,7 +18,8 @@
*/
import { PanelNotFoundError } from '../../../embeddable_plugin';
-import { DashboardPanelState, GridData, DASHBOARD_GRID_COLUMN_COUNT } from '..';
+import { GridData } from '../../../../common';
+import { DashboardPanelState, DASHBOARD_GRID_COLUMN_COUNT } from '..';
export type PanelPlacementMethod = (
args: PlacementArgs
diff --git a/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts
index 30a93989649a7..b3ce2f1e57d5f 100644
--- a/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts
+++ b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts
@@ -33,6 +33,10 @@ export class PlaceholderEmbeddableFactory implements EmbeddableFactoryDefinition
return false;
}
+ public canCreateNew() {
+ return false;
+ }
+
public async create(initialInput: EmbeddableInput, parent?: IContainer) {
return new PlaceholderEmbeddable(initialInput, parent);
}
diff --git a/src/plugins/dashboard/public/application/embeddable/types.ts b/src/plugins/dashboard/public/application/embeddable/types.ts
index 6d0221cb10e8b..66cdd22ed6bd4 100644
--- a/src/plugins/dashboard/public/application/embeddable/types.ts
+++ b/src/plugins/dashboard/public/application/embeddable/types.ts
@@ -17,18 +17,11 @@
* under the License.
*/
import { SavedObjectEmbeddableInput } from 'src/plugins/embeddable/public';
+import { GridData } from '../../../common';
import { PanelState, EmbeddableInput } from '../../embeddable_plugin';
export type PanelId = string;
export type SavedObjectId = string;
-export interface GridData {
- w: number;
- h: number;
- x: number;
- y: number;
- i: string;
-}
-
export interface DashboardPanelState<
TEmbeddableInput extends EmbeddableInput | SavedObjectEmbeddableInput = SavedObjectEmbeddableInput
> extends PanelState {
diff --git a/src/plugins/dashboard/public/application/legacy_app.js b/src/plugins/dashboard/public/application/legacy_app.js
index 10243dbf2f979..31225530b10b9 100644
--- a/src/plugins/dashboard/public/application/legacy_app.js
+++ b/src/plugins/dashboard/public/application/legacy_app.js
@@ -28,7 +28,6 @@ import { initDashboardAppDirective } from './dashboard_app';
import { createDashboardEditUrl, DashboardConstants } from '../dashboard_constants';
import {
createKbnUrlStateStorage,
- ensureDefaultIndexPattern,
redirectWhenMissing,
InvalidJSONProperty,
SavedObjectNotFound,
@@ -138,7 +137,7 @@ export function initDashboardApp(app, deps) {
},
resolve: {
dash: function($route, history) {
- return ensureDefaultIndexPattern(deps.core, deps.data, history).then(() => {
+ return deps.data.indexPatterns.ensureDefaultIndexPattern(history).then(() => {
const savedObjectsClient = deps.savedObjectsClient;
const title = $route.current.params.title;
if (title) {
@@ -173,7 +172,8 @@ export function initDashboardApp(app, deps) {
requireUICapability: 'dashboard.createNew',
resolve: {
dash: history =>
- ensureDefaultIndexPattern(deps.core, deps.data, history)
+ deps.data.indexPatterns
+ .ensureDefaultIndexPattern(history)
.then(() => deps.savedDashboards.get())
.catch(
redirectWhenMissing({
@@ -194,7 +194,8 @@ export function initDashboardApp(app, deps) {
dash: function($route, history) {
const id = $route.current.params.id;
- return ensureDefaultIndexPattern(deps.core, deps.data, history)
+ return deps.data.indexPatterns
+ .ensureDefaultIndexPattern(history)
.then(() => deps.savedDashboards.get(id))
.then(savedDashboard => {
deps.chrome.recentlyAccessed.add(
diff --git a/src/plugins/dashboard/public/application/lib/migrate_app_state.ts b/src/plugins/dashboard/public/application/lib/migrate_app_state.ts
index 8f8de3663518a..f4d97578adebf 100644
--- a/src/plugins/dashboard/public/application/lib/migrate_app_state.ts
+++ b/src/plugins/dashboard/public/application/lib/migrate_app_state.ts
@@ -22,18 +22,16 @@ import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
+import { DashboardAppState, SavedDashboardPanel } from '../../types';
import {
- DashboardAppState,
+ migratePanelsTo730,
SavedDashboardPanelTo60,
SavedDashboardPanel730ToLatest,
SavedDashboardPanel610,
SavedDashboardPanel630,
SavedDashboardPanel640To720,
SavedDashboardPanel620,
- SavedDashboardPanel,
-} from '../../types';
-// should be moved in src/plugins/dashboard/common right after https://github.com/elastic/kibana/pull/61895 is merged
-import { migratePanelsTo730 } from '../../../../../legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels';
+} from '../../../common';
/**
* Attempts to migrate the state stored in the URL into the latest version of it.
diff --git a/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts b/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts
index 57c147ffe3588..ee59c68cce451 100644
--- a/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts
+++ b/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts
@@ -17,17 +17,19 @@
* under the License.
*/
-import { searchSourceMock } from '../../../../data/public/mocks';
+import { dataPluginMock } from '../../../../data/public/mocks';
import { SavedObjectDashboard } from '../../saved_dashboards';
export function getSavedDashboardMock(
config?: Partial
): SavedObjectDashboard {
+ const searchSource = dataPluginMock.createStartContract();
+
return {
id: '123',
title: 'my dashboard',
panelsJSON: '[]',
- searchSource: searchSourceMock,
+ searchSource: searchSource.search.searchSource.create(),
copyOnSave: false,
timeRestore: false,
timeTo: 'now',
diff --git a/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx b/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx
index 836cea298f035..5dab21ff671b4 100644
--- a/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx
+++ b/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx
@@ -84,6 +84,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => {
getAllEmbeddableFactories={(() => []) as any}
getEmbeddableFactory={(() => null) as any}
notifications={{} as any}
+ application={{} as any}
overlays={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts
index ca0ea0293b07c..44733499cdcba 100644
--- a/src/plugins/dashboard/public/index.ts
+++ b/src/plugins/dashboard/public/index.ts
@@ -20,29 +20,6 @@
import { PluginInitializerContext } from '../../../core/public';
import { DashboardPlugin } from './plugin';
-/**
- * These types can probably be internal once all of dashboard app is migrated into this plugin. Right
- * now, migrations are still in legacy land.
- */
-export {
- DashboardDoc730ToLatest,
- DashboardDoc700To720,
- RawSavedDashboardPanelTo60,
- RawSavedDashboardPanel610,
- RawSavedDashboardPanel620,
- RawSavedDashboardPanel630,
- RawSavedDashboardPanel640To720,
- RawSavedDashboardPanel730ToLatest,
- DashboardDocPre700,
-} from './bwc';
-export {
- SavedDashboardPanelTo60,
- SavedDashboardPanel610,
- SavedDashboardPanel620,
- SavedDashboardPanel630,
- SavedDashboardPanel730ToLatest,
-} from './types';
-
export {
DashboardContainer,
DashboardContainerInput,
@@ -51,7 +28,6 @@ export {
// Types below here can likely be made private when dashboard app moved into this NP plugin.
DEFAULT_PANEL_WIDTH,
DEFAULT_PANEL_HEIGHT,
- GridData,
} from './application';
export { DashboardConstants, createDashboardEditUrl } from './dashboard_constants';
diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx
index 203c784d9df4e..5f6b67ee6ad20 100644
--- a/src/plugins/dashboard/public/plugin.tsx
+++ b/src/plugins/dashboard/public/plugin.tsx
@@ -251,6 +251,8 @@ export class DashboardPlugin
localStorage: new Storage(localStorage),
usageCollection,
};
+ // make sure the index pattern list is up to date
+ await dataStart.indexPatterns.clearCache();
const { renderApp } = await import('./application/application');
const unmount = renderApp(params.element, params.appBasePath, deps);
return () => {
diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts
index d96d2cdf75626..21c6bbc1bfc51 100644
--- a/src/plugins/dashboard/public/types.ts
+++ b/src/plugins/dashboard/public/types.ts
@@ -19,14 +19,7 @@
import { Query, Filter } from 'src/plugins/data/public';
import { SavedObject as SavedObjectType, SavedObjectAttributes } from 'src/core/public';
-import {
- RawSavedDashboardPanelTo60,
- RawSavedDashboardPanel610,
- RawSavedDashboardPanel620,
- RawSavedDashboardPanel630,
- RawSavedDashboardPanel640To720,
- RawSavedDashboardPanel730ToLatest,
-} from './bwc';
+import { SavedDashboardPanel730ToLatest } from '../common';
import { ViewMode } from './embeddable_plugin';
export interface DashboardCapabilities {
@@ -83,55 +76,6 @@ export type NavAction = (anchorElement?: any) => void;
*/
export type SavedDashboardPanel = SavedDashboardPanel730ToLatest;
-// id becomes optional starting in 7.3.0
-export type SavedDashboardPanel730ToLatest = Pick<
- RawSavedDashboardPanel730ToLatest,
- Exclude
-> & {
- readonly id?: string;
- readonly type: string;
-};
-
-export type SavedDashboardPanel640To720 = Pick<
- RawSavedDashboardPanel640To720,
- Exclude
-> & {
- readonly id: string;
- readonly type: string;
-};
-
-export type SavedDashboardPanel630 = Pick<
- RawSavedDashboardPanel630,
- Exclude
-> & {
- readonly id: string;
- readonly type: string;
-};
-
-export type SavedDashboardPanel620 = Pick<
- RawSavedDashboardPanel620,
- Exclude
-> & {
- readonly id: string;
- readonly type: string;
-};
-
-export type SavedDashboardPanel610 = Pick<
- RawSavedDashboardPanel610,
- Exclude
-> & {
- readonly id: string;
- readonly type: string;
-};
-
-export type SavedDashboardPanelTo60 = Pick<
- RawSavedDashboardPanelTo60,
- Exclude
-> & {
- readonly id: string;
- readonly type: string;
-};
-
export interface DashboardAppState {
panels: SavedDashboardPanel[];
fullScreenMode: boolean;
diff --git a/src/plugins/dashboard/server/capabilities_provider.ts b/src/plugins/dashboard/server/capabilities_provider.ts
new file mode 100644
index 0000000000000..0bb53d60c38a5
--- /dev/null
+++ b/src/plugins/dashboard/server/capabilities_provider.ts
@@ -0,0 +1,27 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export const capabilitiesProvider = () => ({
+ dashboard: {
+ createNew: true,
+ show: true,
+ showWriteControls: true,
+ saveQuery: true,
+ },
+});
diff --git a/src/plugins/dashboard/server/index.ts b/src/plugins/dashboard/server/index.ts
new file mode 100644
index 0000000000000..9719586001c59
--- /dev/null
+++ b/src/plugins/dashboard/server/index.ts
@@ -0,0 +1,30 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext } from '../../../core/server';
+import { DashboardPlugin } from './plugin';
+
+// This exports static code and TypeScript types,
+// as well as, Kibana Platform `plugin()` initializer.
+
+export function plugin(initializerContext: PluginInitializerContext) {
+ return new DashboardPlugin(initializerContext);
+}
+
+export { DashboardPluginSetup, DashboardPluginStart } from './types';
diff --git a/src/plugins/dashboard/server/plugin.ts b/src/plugins/dashboard/server/plugin.ts
new file mode 100644
index 0000000000000..ba7bdeeda0133
--- /dev/null
+++ b/src/plugins/dashboard/server/plugin.ts
@@ -0,0 +1,55 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {
+ PluginInitializerContext,
+ CoreSetup,
+ CoreStart,
+ Plugin,
+ Logger,
+} from '../../../core/server';
+
+import { dashboardSavedObjectType } from './saved_objects';
+import { capabilitiesProvider } from './capabilities_provider';
+
+import { DashboardPluginSetup, DashboardPluginStart } from './types';
+
+export class DashboardPlugin implements Plugin {
+ private readonly logger: Logger;
+
+ constructor(initializerContext: PluginInitializerContext) {
+ this.logger = initializerContext.logger.get();
+ }
+
+ public setup(core: CoreSetup) {
+ this.logger.debug('dashboard: Setup');
+
+ core.savedObjects.registerType(dashboardSavedObjectType);
+ core.capabilities.registerProvider(capabilitiesProvider);
+
+ return {};
+ }
+
+ public start(core: CoreStart) {
+ this.logger.debug('dashboard: Started');
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/src/plugins/dashboard/server/saved_objects/dashboard.ts b/src/plugins/dashboard/server/saved_objects/dashboard.ts
new file mode 100644
index 0000000000000..65d5a4021f962
--- /dev/null
+++ b/src/plugins/dashboard/server/saved_objects/dashboard.ts
@@ -0,0 +1,67 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { SavedObjectsType } from 'kibana/server';
+import { dashboardSavedObjectTypeMigrations } from './dashboard_migrations';
+
+export const dashboardSavedObjectType: SavedObjectsType = {
+ name: 'dashboard',
+ hidden: false,
+ namespaceType: 'single',
+ management: {
+ icon: 'dashboardApp',
+ defaultSearchField: 'title',
+ importableAndExportable: true,
+ getTitle(obj) {
+ return obj.attributes.title;
+ },
+ getEditUrl(obj) {
+ return `/management/kibana/objects/savedDashboards/${encodeURIComponent(obj.id)}`;
+ },
+ getInAppUrl(obj) {
+ return {
+ path: `/app/kibana#/dashboard/${encodeURIComponent(obj.id)}`,
+ uiCapabilitiesPath: 'dashboard.show',
+ };
+ },
+ },
+ mappings: {
+ properties: {
+ description: { type: 'text' },
+ hits: { type: 'integer' },
+ kibanaSavedObjectMeta: { properties: { searchSourceJSON: { type: 'text' } } },
+ optionsJSON: { type: 'text' },
+ panelsJSON: { type: 'text' },
+ refreshInterval: {
+ properties: {
+ display: { type: 'keyword' },
+ pause: { type: 'boolean' },
+ section: { type: 'integer' },
+ value: { type: 'integer' },
+ },
+ },
+ timeFrom: { type: 'keyword' },
+ timeRestore: { type: 'boolean' },
+ timeTo: { type: 'keyword' },
+ title: { type: 'text' },
+ version: { type: 'integer' },
+ },
+ },
+ migrations: dashboardSavedObjectTypeMigrations,
+};
diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.test.js b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts
similarity index 95%
rename from src/legacy/core_plugins/kibana/migrations/migrations.test.js
rename to src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts
index b02081128c858..9829498118cc0 100644
--- a/src/legacy/core_plugins/kibana/migrations/migrations.test.js
+++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts
@@ -17,14 +17,15 @@
* under the License.
*/
-import { migrations } from './migrations';
+import { SavedObjectUnsanitizedDoc } from 'kibana/server';
+import { dashboardSavedObjectTypeMigrations as migrations } from './dashboard_migrations';
describe('dashboard', () => {
describe('7.0.0', () => {
- const migration = migrations.dashboard['7.0.0'];
+ const migration = migrations['7.0.0'];
test('skips error on empty object', () => {
- expect(migration({})).toMatchInlineSnapshot(`
+ expect(migration({} as SavedObjectUnsanitizedDoc)).toMatchInlineSnapshot(`
Object {
"references": Array [],
}
@@ -329,7 +330,7 @@ Object {
attributes: {
panelsJSON: 123,
},
- };
+ } as SavedObjectUnsanitizedDoc;
expect(migration(doc)).toMatchInlineSnapshot(`
Object {
"attributes": Object {
@@ -347,7 +348,7 @@ Object {
attributes: {
panelsJSON: '{123abc}',
},
- };
+ } as SavedObjectUnsanitizedDoc;
expect(migration(doc)).toMatchInlineSnapshot(`
Object {
"attributes": Object {
@@ -365,7 +366,7 @@ Object {
attributes: {
panelsJSON: '{}',
},
- };
+ } as SavedObjectUnsanitizedDoc;
expect(migration(doc)).toMatchInlineSnapshot(`
Object {
"attributes": Object {
@@ -383,7 +384,7 @@ Object {
attributes: {
panelsJSON: '[{"id":"123"}]',
},
- };
+ } as SavedObjectUnsanitizedDoc;
expect(migration(doc)).toMatchInlineSnapshot(`
Object {
"attributes": Object {
@@ -401,7 +402,7 @@ Object {
attributes: {
panelsJSON: '[{"type":"visualization"}]',
},
- };
+ } as SavedObjectUnsanitizedDoc;
expect(migration(doc)).toMatchInlineSnapshot(`
Object {
"attributes": Object {
@@ -420,7 +421,7 @@ Object {
panelsJSON:
'[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]',
},
- };
+ } as SavedObjectUnsanitizedDoc;
const migratedDoc = migration(doc);
expect(migratedDoc).toMatchInlineSnapshot(`
Object {
diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts
new file mode 100644
index 0000000000000..7c1d0568cd3d7
--- /dev/null
+++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts
@@ -0,0 +1,117 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { get, flow } from 'lodash';
+
+import { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'kibana/server';
+import { migrations730 } from './migrations_730';
+import { migrateMatchAllQuery } from './migrate_match_all_query';
+import { DashboardDoc700To720 } from '../../common';
+
+function migrateIndexPattern(doc: DashboardDoc700To720) {
+ const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON');
+ if (typeof searchSourceJSON !== 'string') {
+ return;
+ }
+ let searchSource;
+ try {
+ searchSource = JSON.parse(searchSourceJSON);
+ } catch (e) {
+ // Let it go, the data is invalid and we'll leave it as is
+ return;
+ }
+ if (searchSource.index) {
+ searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index';
+ doc.references.push({
+ name: searchSource.indexRefName,
+ type: 'index-pattern',
+ id: searchSource.index,
+ });
+ delete searchSource.index;
+ }
+ if (searchSource.filter) {
+ searchSource.filter.forEach((filterRow: any, i: number) => {
+ if (!filterRow.meta || !filterRow.meta.index) {
+ return;
+ }
+ filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`;
+ doc.references.push({
+ name: filterRow.meta.indexRefName,
+ type: 'index-pattern',
+ id: filterRow.meta.index,
+ });
+ delete filterRow.meta.index;
+ });
+ }
+ doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource);
+}
+
+const migrations700: SavedObjectMigrationFn = (doc): DashboardDoc700To720 => {
+ // Set new "references" attribute
+ doc.references = doc.references || [];
+
+ // Migrate index pattern
+ migrateIndexPattern(doc as DashboardDoc700To720);
+ // Migrate panels
+ const panelsJSON = get(doc, 'attributes.panelsJSON');
+ if (typeof panelsJSON !== 'string') {
+ return doc as DashboardDoc700To720;
+ }
+ let panels;
+ try {
+ panels = JSON.parse(panelsJSON);
+ } catch (e) {
+ // Let it go, the data is invalid and we'll leave it as is
+ return doc as DashboardDoc700To720;
+ }
+ if (!Array.isArray(panels)) {
+ return doc as DashboardDoc700To720;
+ }
+ panels.forEach((panel, i) => {
+ if (!panel.type || !panel.id) {
+ return;
+ }
+ panel.panelRefName = `panel_${i}`;
+ doc.references!.push({
+ name: `panel_${i}`,
+ type: panel.type,
+ id: panel.id,
+ });
+ delete panel.type;
+ delete panel.id;
+ });
+ doc.attributes.panelsJSON = JSON.stringify(panels);
+ return doc as DashboardDoc700To720;
+};
+
+export const dashboardSavedObjectTypeMigrations = {
+ /**
+ * We need to have this migration twice, once with a version prior to 7.0.0 once with a version
+ * after it. The reason for that is, that this migration has been introduced once 7.0.0 was already
+ * released. Thus a user who already had 7.0.0 installed already got the 7.0.0 migrations below running,
+ * so we need a version higher than that. But this fix was backported to the 6.7 release, meaning if we
+ * would only have the 7.0.1 migration in here a user on the 6.7 release will migrate their saved objects
+ * to the 7.0.1 state, and thus when updating their Kibana to 7.0, will never run the 7.0.0 migrations introduced
+ * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7
+ * only contained the 6.7.2 migration and not the 7.0.1 migration.
+ */
+ '6.7.2': flow(migrateMatchAllQuery),
+ '7.0.0': flow<(doc: SavedObjectUnsanitizedDoc) => DashboardDoc700To720>(migrations700),
+ '7.3.0': flow(migrations730),
+};
diff --git a/src/legacy/ui/public/error_auto_create_index/index.ts b/src/plugins/dashboard/server/saved_objects/index.ts
similarity index 89%
rename from src/legacy/ui/public/error_auto_create_index/index.ts
rename to src/plugins/dashboard/server/saved_objects/index.ts
index d290e0334b3d6..ca97b9d2a6b70 100644
--- a/src/legacy/ui/public/error_auto_create_index/index.ts
+++ b/src/plugins/dashboard/server/saved_objects/index.ts
@@ -17,4 +17,4 @@
* under the License.
*/
-export { isAutoCreateIndexError, showAutoCreateIndexErrorPage } from './error_auto_create_index';
+export { dashboardSavedObjectType } from './dashboard';
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/is_dashboard_doc.ts b/src/plugins/dashboard/server/saved_objects/is_dashboard_doc.ts
similarity index 70%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/is_dashboard_doc.ts
rename to src/plugins/dashboard/server/saved_objects/is_dashboard_doc.ts
index d8f8882a218dd..c9b35263a549f 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/is_dashboard_doc.ts
+++ b/src/plugins/dashboard/server/saved_objects/is_dashboard_doc.ts
@@ -17,8 +17,21 @@
* under the License.
*/
-import { DashboardDoc730ToLatest } from '../../../../../../plugins/dashboard/public';
-import { isDoc } from '../../../migrations/is_doc';
+import { SavedObjectUnsanitizedDoc } from 'kibana/server';
+import { DashboardDoc730ToLatest } from '../../common';
+
+function isDoc(
+ doc: { [key: string]: unknown } | SavedObjectUnsanitizedDoc
+): doc is SavedObjectUnsanitizedDoc {
+ return (
+ typeof doc.id === 'string' &&
+ typeof doc.type === 'string' &&
+ doc.attributes !== null &&
+ typeof doc.attributes === 'object' &&
+ doc.references !== null &&
+ typeof doc.references === 'object'
+ );
+}
export function isDashboardDoc(
doc: { [key: string]: unknown } | DashboardDoc730ToLatest
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.test.ts b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.test.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.test.ts
rename to src/plugins/dashboard/server/saved_objects/migrate_match_all_query.test.ts
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.ts b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts
similarity index 95%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.ts
rename to src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts
index 707aae9e5d4ac..5b8582bf821ef 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.ts
+++ b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts
@@ -19,7 +19,7 @@
import { SavedObjectMigrationFn } from 'kibana/server';
import { get } from 'lodash';
-import { DEFAULT_QUERY_LANGUAGE } from '../../../../../../plugins/data/common';
+import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common';
export const migrateMatchAllQuery: SavedObjectMigrationFn = doc => {
const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON');
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrations_730.test.ts b/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts
similarity index 91%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/migrations_730.test.ts
rename to src/plugins/dashboard/server/saved_objects/migrations_730.test.ts
index 5a4970897098d..aa744324428a4 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrations_730.test.ts
+++ b/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts
@@ -17,20 +17,18 @@
* under the License.
*/
-import { migrations } from '../../../migrations';
+import { dashboardSavedObjectTypeMigrations as migrations } from './dashboard_migrations';
import { migrations730 } from './migrations_730';
-import {
- DashboardDoc700To720,
- DashboardDoc730ToLatest,
- RawSavedDashboardPanel730ToLatest,
- DashboardDocPre700,
-} from '../../../../../../plugins/dashboard/public';
-
-const mockLogger = {
- warning: () => {},
- warn: () => {},
- debug: () => {},
- info: () => {},
+import { DashboardDoc700To720, DashboardDoc730ToLatest, DashboardDocPre700 } from '../../common';
+import { RawSavedDashboardPanel730ToLatest } from '../../common';
+
+const mockContext = {
+ log: {
+ warning: () => {},
+ warn: () => {},
+ debug: () => {},
+ info: () => {},
+ },
};
test('dashboard migration 7.3.0 migrates filters to query on search source', () => {
@@ -53,7 +51,7 @@ test('dashboard migration 7.3.0 migrates filters to query on search source', ()
'[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]',
},
};
- const newDoc = migrations730(doc, mockLogger);
+ const newDoc = migrations730(doc, mockContext);
expect(newDoc).toMatchInlineSnapshot(`
Object {
@@ -97,8 +95,8 @@ test('dashboard migration 7.3.0 migrates filters to query on search source when
},
};
- const doc700: DashboardDoc700To720 = migrations.dashboard['7.0.0'](doc, mockLogger);
- const newDoc = migrations.dashboard['7.3.0'](doc700, mockLogger);
+ const doc700: DashboardDoc700To720 = migrations['7.0.0'](doc);
+ const newDoc = migrations['7.3.0'](doc700, mockContext);
const parsedSearchSource = JSON.parse(newDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON);
expect(parsedSearchSource.filter.length).toBe(0);
@@ -129,8 +127,8 @@ test('dashboard migration works when panelsJSON is missing panelIndex', () => {
},
};
- const doc700: DashboardDoc700To720 = migrations.dashboard['7.0.0'](doc, mockLogger);
- const newDoc = migrations.dashboard['7.3.0'](doc700, mockLogger);
+ const doc700: DashboardDoc700To720 = migrations['7.0.0'](doc);
+ const newDoc = migrations['7.3.0'](doc700, mockContext);
const parsedSearchSource = JSON.parse(newDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON);
expect(parsedSearchSource.filter.length).toBe(0);
@@ -159,7 +157,7 @@ test('dashboard migration 7.3.0 migrates panels', () => {
},
};
- const newDoc = migrations730(doc, mockLogger) as DashboardDoc730ToLatest;
+ const newDoc = migrations730(doc, mockContext) as DashboardDoc730ToLatest;
const newPanels = JSON.parse(newDoc.attributes.panelsJSON) as RawSavedDashboardPanel730ToLatest[];
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrations_730.ts b/src/plugins/dashboard/server/saved_objects/migrations_730.ts
similarity index 79%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/migrations_730.ts
rename to src/plugins/dashboard/server/saved_objects/migrations_730.ts
index 56856f7b21303..e9d483f68a5da 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrations_730.ts
+++ b/src/plugins/dashboard/server/saved_objects/migrations_730.ts
@@ -16,26 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-// This file should be moved to dashboard/server/
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { SavedObjectsMigrationLogger } from 'src/core/server';
+
import { inspect } from 'util';
-import {
- DashboardDoc730ToLatest,
- DashboardDoc700To720,
-} from '../../../../../../plugins/dashboard/public';
+import { SavedObjectMigrationContext } from 'kibana/server';
+import { DashboardDoc730ToLatest } from '../../common';
import { isDashboardDoc } from './is_dashboard_doc';
import { moveFiltersToQuery } from './move_filters_to_query';
-import { migratePanelsTo730 } from './migrate_to_730_panels';
+import { migratePanelsTo730, DashboardDoc700To720 } from '../../common';
-export function migrations730(
- doc:
- | {
- [key: string]: unknown;
- }
- | DashboardDoc700To720,
- logger: SavedObjectsMigrationLogger
-): DashboardDoc730ToLatest | { [key: string]: unknown } {
+export const migrations730 = (doc: DashboardDoc700To720, { log }: SavedObjectMigrationContext) => {
if (!isDashboardDoc(doc)) {
// NOTE: we should probably throw an error here... but for now following suit and in the
// case of errors, just returning the same document.
@@ -48,7 +37,7 @@ export function migrations730(
moveFiltersToQuery(searchSource)
);
} catch (e) {
- logger.warning(
+ log.warning(
`Exception @ migrations730 while trying to migrate dashboard query filters!\n` +
`${e.stack}\n` +
`dashboard: ${inspect(doc, false, null)}`
@@ -75,7 +64,7 @@ export function migrations730(
delete doc.attributes.uiStateJSON;
} catch (e) {
- logger.warning(
+ log.warning(
`Exception @ migrations730 while trying to migrate dashboard panels!\n` +
`Error: ${e.stack}\n` +
`dashboard: ${inspect(doc, false, null)}`
@@ -84,4 +73,4 @@ export function migrations730(
}
return doc as DashboardDoc730ToLatest;
-}
+};
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.test.ts b/src/plugins/dashboard/server/saved_objects/move_filters_to_query.test.ts
similarity index 96%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.test.ts
rename to src/plugins/dashboard/server/saved_objects/move_filters_to_query.test.ts
index 621983b1ca8a5..a06f64e0f0c40 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.test.ts
+++ b/src/plugins/dashboard/server/saved_objects/move_filters_to_query.test.ts
@@ -17,8 +17,8 @@
* under the License.
*/
+import { esFilters, Filter } from 'src/plugins/data/public';
import { moveFiltersToQuery, Pre600FilterQuery } from './move_filters_to_query';
-import { esFilters, Filter } from '../../../../../../plugins/data/public';
const filter: Filter = {
meta: { disabled: false, negate: false, alias: '' },
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts b/src/plugins/dashboard/server/saved_objects/move_filters_to_query.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts
rename to src/plugins/dashboard/server/saved_objects/move_filters_to_query.ts
diff --git a/src/plugins/dashboard/server/types.ts b/src/plugins/dashboard/server/types.ts
new file mode 100644
index 0000000000000..1151b06dbdab7
--- /dev/null
+++ b/src/plugins/dashboard/server/types.ts
@@ -0,0 +1,23 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface DashboardPluginSetup {}
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface DashboardPluginStart {}
diff --git a/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js b/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js
index f745f01873bae..fc6b706f6e01e 100644
--- a/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js
+++ b/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js
@@ -222,9 +222,8 @@ module.exports = (function() {
if (sequence === 'true') return buildLiteralNode(true);
if (sequence === 'false') return buildLiteralNode(false);
if (chars.includes(wildcardSymbol)) return buildWildcardNode(sequence);
- const number = Number(sequence);
- const value = isNaN(number) ? sequence : number;
- return buildLiteralNode(value);
+ const isNumberPattern = /^(-?[1-9]+\d*([.]\d+)?)$|^(-?0[.]\d*[1-9]+)$|^0$|^0.0$|^[.]\d{1,}$/
+ return buildLiteralNode(isNumberPattern.test(sequence) ? Number(sequence) : sequence);
},
peg$c50 = { type: "any", description: "any character" },
peg$c51 = "*",
@@ -3164,4 +3163,4 @@ module.exports = (function() {
SyntaxError: peg$SyntaxError,
parse: peg$parse
};
-})();
\ No newline at end of file
+})();
diff --git a/src/plugins/data/common/es_query/kuery/ast/ast.test.ts b/src/plugins/data/common/es_query/kuery/ast/ast.test.ts
index e441420760475..6a69d52d72134 100644
--- a/src/plugins/data/common/es_query/kuery/ast/ast.test.ts
+++ b/src/plugins/data/common/es_query/kuery/ast/ast.test.ts
@@ -278,6 +278,33 @@ describe('kuery AST API', () => {
expect(fromLiteralExpression('true')).toEqual(booleanTrueLiteral);
expect(fromLiteralExpression('false')).toEqual(booleanFalseLiteral);
expect(fromLiteralExpression('42')).toEqual(numberLiteral);
+
+ expect(fromLiteralExpression('.3').value).toEqual(0.3);
+ expect(fromLiteralExpression('.36').value).toEqual(0.36);
+ expect(fromLiteralExpression('.00001').value).toEqual(0.00001);
+ expect(fromLiteralExpression('3').value).toEqual(3);
+ expect(fromLiteralExpression('-4').value).toEqual(-4);
+ expect(fromLiteralExpression('0').value).toEqual(0);
+ expect(fromLiteralExpression('0.0').value).toEqual(0);
+ expect(fromLiteralExpression('2.0').value).toEqual(2.0);
+ expect(fromLiteralExpression('0.8').value).toEqual(0.8);
+ expect(fromLiteralExpression('790.9').value).toEqual(790.9);
+ expect(fromLiteralExpression('0.0001').value).toEqual(0.0001);
+ expect(fromLiteralExpression('96565646732345').value).toEqual(96565646732345);
+
+ expect(fromLiteralExpression('..4').value).toEqual('..4');
+ expect(fromLiteralExpression('.3text').value).toEqual('.3text');
+ expect(fromLiteralExpression('text').value).toEqual('text');
+ expect(fromLiteralExpression('.').value).toEqual('.');
+ expect(fromLiteralExpression('-').value).toEqual('-');
+ expect(fromLiteralExpression('001').value).toEqual('001');
+ expect(fromLiteralExpression('00.2').value).toEqual('00.2');
+ expect(fromLiteralExpression('0.0.1').value).toEqual('0.0.1');
+ expect(fromLiteralExpression('3.').value).toEqual('3.');
+ expect(fromLiteralExpression('--4').value).toEqual('--4');
+ expect(fromLiteralExpression('-.4').value).toEqual('-.4');
+ expect(fromLiteralExpression('-0').value).toEqual('-0');
+ expect(fromLiteralExpression('00949').value).toEqual('00949');
});
test('should allow escaping of special characters with a backslash', () => {
diff --git a/src/plugins/data/common/es_query/kuery/ast/kuery.peg b/src/plugins/data/common/es_query/kuery/ast/kuery.peg
index 389b9a82d2c76..625c5069f936a 100644
--- a/src/plugins/data/common/es_query/kuery/ast/kuery.peg
+++ b/src/plugins/data/common/es_query/kuery/ast/kuery.peg
@@ -247,9 +247,8 @@ UnquotedLiteral
if (sequence === 'true') return buildLiteralNode(true);
if (sequence === 'false') return buildLiteralNode(false);
if (chars.includes(wildcardSymbol)) return buildWildcardNode(sequence);
- const number = Number(sequence);
- const value = isNaN(number) ? sequence : number;
- return buildLiteralNode(value);
+ const isNumberPattern = /^(-?[1-9]+\d*([.]\d+)?)$|^(-?0[.]\d*[1-9]+)$|^0$|^0.0$|^[.]\d{1,}$/
+ return buildLiteralNode(isNumberPattern.test(sequence) ? Number(sequence) : sequence);
}
UnquotedCharacter
diff --git a/src/plugins/data/common/field_formats/constants/base_formatters.ts b/src/plugins/data/common/field_formats/constants/base_formatters.ts
index 6befe8cea71f5..921c50571f727 100644
--- a/src/plugins/data/common/field_formats/constants/base_formatters.ts
+++ b/src/plugins/data/common/field_formats/constants/base_formatters.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { IFieldFormatType } from '../types';
+import { FieldFormatInstanceType } from '../types';
import {
BoolFormat,
@@ -36,7 +36,7 @@ import {
UrlFormat,
} from '../converters';
-export const baseFormatters: IFieldFormatType[] = [
+export const baseFormatters: FieldFormatInstanceType[] = [
BoolFormat,
BytesFormat,
ColorFormat,
diff --git a/src/plugins/data/common/field_formats/converters/custom.ts b/src/plugins/data/common/field_formats/converters/custom.ts
index a1ce0cf3e7b54..4dd011a7feff3 100644
--- a/src/plugins/data/common/field_formats/converters/custom.ts
+++ b/src/plugins/data/common/field_formats/converters/custom.ts
@@ -18,9 +18,9 @@
*/
import { FieldFormat } from '../field_format';
-import { TextContextTypeConvert, FIELD_FORMAT_IDS, IFieldFormatType } from '../types';
+import { TextContextTypeConvert, FIELD_FORMAT_IDS, FieldFormatInstanceType } from '../types';
-export const createCustomFieldFormat = (convert: TextContextTypeConvert): IFieldFormatType =>
+export const createCustomFieldFormat = (convert: TextContextTypeConvert): FieldFormatInstanceType =>
class CustomFieldFormat extends FieldFormat {
static id = FIELD_FORMAT_IDS.CUSTOM;
diff --git a/src/plugins/data/common/field_formats/field_format.ts b/src/plugins/data/common/field_formats/field_format.ts
index 49baa8c074da8..96d0024dff2a2 100644
--- a/src/plugins/data/common/field_formats/field_format.ts
+++ b/src/plugins/data/common/field_formats/field_format.ts
@@ -22,7 +22,7 @@ import { createCustomFieldFormat } from './converters/custom';
import {
FieldFormatsGetConfigFn,
FieldFormatsContentType,
- IFieldFormatType,
+ FieldFormatInstanceType,
FieldFormatConvert,
FieldFormatConvertFunction,
HtmlContextTypeOptions,
@@ -199,7 +199,7 @@ export abstract class FieldFormat {
};
}
- static from(convertFn: FieldFormatConvertFunction): IFieldFormatType {
+ static from(convertFn: FieldFormatConvertFunction): FieldFormatInstanceType {
return createCustomFieldFormat(convertFn);
}
diff --git a/src/plugins/data/common/field_formats/field_formats_registry.test.ts b/src/plugins/data/common/field_formats/field_formats_registry.test.ts
index 0b32a62744fb1..f04524505a711 100644
--- a/src/plugins/data/common/field_formats/field_formats_registry.test.ts
+++ b/src/plugins/data/common/field_formats/field_formats_registry.test.ts
@@ -18,7 +18,7 @@
*/
import { FieldFormatsRegistry } from './field_formats_registry';
import { BoolFormat, PercentFormat, StringFormat } from './converters';
-import { FieldFormatsGetConfigFn, IFieldFormatType } from './types';
+import { FieldFormatsGetConfigFn, FieldFormatInstanceType } from './types';
import { KBN_FIELD_TYPES } from '../../common';
const getValueOfPrivateField = (instance: any, field: string) => instance[field];
@@ -75,10 +75,10 @@ describe('FieldFormatsRegistry', () => {
test('should register field formats', () => {
fieldFormatsRegistry.register([StringFormat, BoolFormat]);
- const registeredFieldFormatters: Map = getValueOfPrivateField(
- fieldFormatsRegistry,
- 'fieldFormats'
- );
+ const registeredFieldFormatters: Map<
+ string,
+ FieldFormatInstanceType
+ > = getValueOfPrivateField(fieldFormatsRegistry, 'fieldFormats');
expect(registeredFieldFormatters.size).toBe(2);
diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/data/common/field_formats/field_formats_registry.ts
index 15b1687e22312..b0a57ad6912a7 100644
--- a/src/plugins/data/common/field_formats/field_formats_registry.ts
+++ b/src/plugins/data/common/field_formats/field_formats_registry.ts
@@ -24,7 +24,7 @@ import {
FieldFormatsGetConfigFn,
FieldFormatConfig,
FIELD_FORMAT_IDS,
- IFieldFormatType,
+ FieldFormatInstanceType,
FieldFormatId,
IFieldFormatMetaParams,
IFieldFormat,
@@ -35,7 +35,7 @@ import { SerializedFieldFormat } from '../../../expressions/common/types';
import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../types';
export class FieldFormatsRegistry {
- protected fieldFormats: Map = new Map();
+ protected fieldFormats: Map = new Map();
protected defaultMap: Record = {};
protected metaParamsOptions: Record = {};
protected getConfig?: FieldFormatsGetConfigFn;
@@ -47,7 +47,7 @@ export class FieldFormatsRegistry {
init(
getConfig: FieldFormatsGetConfigFn,
metaParamsOptions: Record = {},
- defaultFieldConverters: IFieldFormatType[] = baseFormatters
+ defaultFieldConverters: FieldFormatInstanceType[] = baseFormatters
) {
const defaultTypeMap = getConfig('format:defaultTypeMap');
this.register(defaultFieldConverters);
@@ -79,23 +79,23 @@ export class FieldFormatsRegistry {
* Get a derived FieldFormat class by its id.
*
* @param {FieldFormatId} formatId - the format id
- * @return {IFieldFormatType | undefined}
+ * @return {FieldFormatInstanceType | undefined}
*/
- getType = (formatId: FieldFormatId): IFieldFormatType | undefined => {
+ getType = (formatId: FieldFormatId): FieldFormatInstanceType | undefined => {
const fieldFormat = this.fieldFormats.get(formatId);
if (fieldFormat) {
const decoratedFieldFormat: any = this.fieldFormatMetaParamsDecorator(fieldFormat);
if (decoratedFieldFormat) {
- return decoratedFieldFormat as IFieldFormatType;
+ return decoratedFieldFormat as FieldFormatInstanceType;
}
}
return undefined;
};
- getTypeWithoutMetaParams = (formatId: FieldFormatId): IFieldFormatType | undefined => {
+ getTypeWithoutMetaParams = (formatId: FieldFormatId): FieldFormatInstanceType | undefined => {
return this.fieldFormats.get(formatId);
};
@@ -106,12 +106,12 @@ export class FieldFormatsRegistry {
*
* @param {KBN_FIELD_TYPES} fieldType
* @param {ES_FIELD_TYPES[]} esTypes - Array of ES data types
- * @return {IFieldFormatType | undefined}
+ * @return {FieldFormatInstanceType | undefined}
*/
getDefaultType = (
fieldType: KBN_FIELD_TYPES,
esTypes: ES_FIELD_TYPES[]
- ): IFieldFormatType | undefined => {
+ ): FieldFormatInstanceType | undefined => {
const config = this.getDefaultConfig(fieldType, esTypes);
return this.getType(config.id);
@@ -206,14 +206,16 @@ export class FieldFormatsRegistry {
* Get filtered list of field formats by format type
*
* @param {KBN_FIELD_TYPES} fieldType
- * @return {IFieldFormatType[]}
+ * @return {FieldFormatInstanceType[]}
*/
- getByFieldType(fieldType: KBN_FIELD_TYPES): IFieldFormatType[] {
+ getByFieldType(fieldType: KBN_FIELD_TYPES): FieldFormatInstanceType[] {
return [...this.fieldFormats.values()]
- .filter((format: IFieldFormatType) => format && format.fieldType.indexOf(fieldType) !== -1)
+ .filter(
+ (format: FieldFormatInstanceType) => format && format.fieldType.indexOf(fieldType) !== -1
+ )
.map(
- (format: IFieldFormatType) =>
- this.fieldFormatMetaParamsDecorator(format) as IFieldFormatType
+ (format: FieldFormatInstanceType) =>
+ this.fieldFormatMetaParamsDecorator(format) as FieldFormatInstanceType
);
}
@@ -238,7 +240,7 @@ export class FieldFormatsRegistry {
});
}
- register(fieldFormats: IFieldFormatType[]) {
+ register(fieldFormats: FieldFormatInstanceType[]) {
fieldFormats.forEach(fieldFormat => this.fieldFormats.set(fieldFormat.id, fieldFormat));
}
@@ -246,12 +248,12 @@ export class FieldFormatsRegistry {
* FieldFormat decorator - provide a one way to add meta-params for all field formatters
*
* @private
- * @param {IFieldFormatType} fieldFormat - field format type
- * @return {IFieldFormatType | undefined}
+ * @param {FieldFormatInstanceType} fieldFormat - field format type
+ * @return {FieldFormatInstanceType | undefined}
*/
private fieldFormatMetaParamsDecorator = (
- fieldFormat: IFieldFormatType
- ): IFieldFormatType | undefined => {
+ fieldFormat: FieldFormatInstanceType
+ ): FieldFormatInstanceType | undefined => {
const getMetaParams = (customParams: Record) => this.buildMetaParams(customParams);
if (fieldFormat) {
diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/data/common/field_formats/index.ts
index 13d3d9d73d43a..b64e115fd55ff 100644
--- a/src/plugins/data/common/field_formats/index.ts
+++ b/src/plugins/data/common/field_formats/index.ts
@@ -52,6 +52,6 @@ export {
FieldFormatConfig,
FieldFormatId,
// Used in data plugin only
- IFieldFormatType,
+ FieldFormatInstanceType,
IFieldFormat,
} from './types';
diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts
index 7c1d6a8522e52..5f11c7fe094bc 100644
--- a/src/plugins/data/common/field_formats/types.ts
+++ b/src/plugins/data/common/field_formats/types.ts
@@ -16,9 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-
import { FieldFormat } from './field_format';
-export { FieldFormat };
/** @public **/
export type FieldFormatsContentType = 'html' | 'text';
@@ -82,10 +80,12 @@ export type IFieldFormat = PublicMethodsOf;
*/
export type FieldFormatId = FIELD_FORMAT_IDS | string;
-export type IFieldFormatType = (new (
+/** @internal **/
+export type FieldFormatInstanceType = (new (
params?: any,
getConfig?: FieldFormatsGetConfigFn
) => FieldFormat) & {
+ // Static properties:
id: FieldFormatId;
title: string;
fieldType: string | string[];
diff --git a/src/plugins/data/public/actions/filters/brush_event.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts
similarity index 58%
rename from src/plugins/data/public/actions/filters/brush_event.test.ts
rename to src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts
index 60244354f06e4..5d21b395b994f 100644
--- a/src/plugins/data/public/actions/filters/brush_event.test.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts
@@ -19,30 +19,34 @@
import moment from 'moment';
-import { onBrushEvent, BrushEvent } from './brush_event';
+import { createFiltersFromRangeSelectAction } from './create_filters_from_range_select';
-import { IndexPatternsContract } from '../../../public';
+import { IndexPatternsContract, RangeFilter } from '../../../public';
import { dataPluginMock } from '../../../public/mocks';
import { setIndexPatterns } from '../../../public/services';
import { mockDataServices } from '../../../public/search/aggs/test_helpers';
+import { TriggerContextMapping } from '../../../../ui_actions/public';
describe('brushEvent', () => {
const DAY_IN_MS = 24 * 60 * 60 * 1000;
const JAN_01_2014 = 1388559600000;
- let baseEvent: BrushEvent;
+ let baseEvent: TriggerContextMapping['SELECT_RANGE_TRIGGER']['data'];
+
+ const indexPattern = {
+ id: 'indexPatternId',
+ timeFieldName: 'time',
+ fields: {
+ getByName: () => undefined,
+ filter: () => [],
+ },
+ };
const aggConfigs = [
{
params: {
field: {},
},
- getIndexPattern: () => ({
- timeFieldName: 'time',
- fields: {
- getByName: () => undefined,
- filter: () => [],
- },
- }),
+ getIndexPattern: () => indexPattern,
},
];
@@ -50,56 +54,37 @@ describe('brushEvent', () => {
mockDataServices();
setIndexPatterns(({
...dataPluginMock.createStartContract().indexPatterns,
- get: async () => ({
- id: 'indexPatternId',
- timeFieldName: 'time',
- fields: {
- getByName: () => undefined,
- filter: () => [],
- },
- }),
+ get: async () => indexPattern,
} as unknown) as IndexPatternsContract);
baseEvent = {
- data: {
- ordered: {
- date: false,
- },
- series: [
+ column: 0,
+ table: {
+ type: 'kibana_datatable',
+ columns: [
{
- values: [
- {
- xRaw: {
- column: 0,
- table: {
- columns: [
- {
- id: '1',
- meta: {
- type: 'histogram',
- indexPatternId: 'indexPatternId',
- aggConfigParams: aggConfigs[0].params,
- },
- },
- ],
- },
- },
- },
- ],
+ id: '1',
+ name: '1',
+ meta: {
+ type: 'histogram',
+ indexPatternId: 'indexPatternId',
+ aggConfigParams: aggConfigs[0].params,
+ },
},
],
+ rows: [],
},
range: [],
};
});
test('should be a function', () => {
- expect(typeof onBrushEvent).toBe('function');
+ expect(typeof createFiltersFromRangeSelectAction).toBe('function');
});
test('ignores event when data.xAxisField not provided', async () => {
- const filter = await onBrushEvent(baseEvent);
- expect(filter).toBeUndefined();
+ const filter = await createFiltersFromRangeSelectAction(baseEvent);
+ expect(filter).toEqual([]);
});
describe('handles an event when the x-axis field is a date field', () => {
@@ -109,29 +94,29 @@ describe('brushEvent', () => {
name: 'time',
type: 'date',
};
- baseEvent.data.ordered = { date: true };
});
afterAll(() => {
baseEvent.range = [];
- baseEvent.data.ordered = { date: false };
+ aggConfigs[0].params.field = {};
});
test('by ignoring the event when range spans zero time', async () => {
baseEvent.range = [JAN_01_2014, JAN_01_2014];
- const filter = await onBrushEvent(baseEvent);
- expect(filter).toBeUndefined();
+ const filter = await createFiltersFromRangeSelectAction(baseEvent);
+ expect(filter).toEqual([]);
});
test('by updating the timefilter', async () => {
baseEvent.range = [JAN_01_2014, JAN_01_2014 + DAY_IN_MS];
- const filter = await onBrushEvent(baseEvent);
+ const filter = await createFiltersFromRangeSelectAction(baseEvent);
expect(filter).toBeDefined();
- if (filter) {
- expect(filter.range.time.gte).toBe(new Date(JAN_01_2014).toISOString());
+ if (filter.length) {
+ const rangeFilter = filter[0] as RangeFilter;
+ expect(rangeFilter.range.time.gte).toBe(new Date(JAN_01_2014).toISOString());
// Set to a baseline timezone for comparison.
- expect(filter.range.time.lt).toBe(new Date(JAN_01_2014 + DAY_IN_MS).toISOString());
+ expect(rangeFilter.range.time.lt).toBe(new Date(JAN_01_2014 + DAY_IN_MS).toISOString());
}
});
});
@@ -142,26 +127,26 @@ describe('brushEvent', () => {
name: 'anotherTimeField',
type: 'date',
};
- baseEvent.data.ordered = { date: true };
});
afterAll(() => {
baseEvent.range = [];
- baseEvent.data.ordered = { date: false };
+ aggConfigs[0].params.field = {};
});
test('creates a new range filter', async () => {
const rangeBegin = JAN_01_2014;
const rangeEnd = rangeBegin + DAY_IN_MS;
baseEvent.range = [rangeBegin, rangeEnd];
- const filter = await onBrushEvent(baseEvent);
+ const filter = await createFiltersFromRangeSelectAction(baseEvent);
expect(filter).toBeDefined();
- if (filter) {
- expect(filter.range.anotherTimeField.gte).toBe(moment(rangeBegin).toISOString());
- expect(filter.range.anotherTimeField.lt).toBe(moment(rangeEnd).toISOString());
- expect(filter.range.anotherTimeField).toHaveProperty(
+ if (filter.length) {
+ const rangeFilter = filter[0] as RangeFilter;
+ expect(rangeFilter.range.anotherTimeField.gte).toBe(moment(rangeBegin).toISOString());
+ expect(rangeFilter.range.anotherTimeField.lt).toBe(moment(rangeEnd).toISOString());
+ expect(rangeFilter.range.anotherTimeField).toHaveProperty(
'format',
'strict_date_optional_time'
);
@@ -184,20 +169,21 @@ describe('brushEvent', () => {
test('by ignoring the event when range does not span at least 2 values', async () => {
baseEvent.range = [1];
- const filter = await onBrushEvent(baseEvent);
- expect(filter).toBeUndefined();
+ const filter = await createFiltersFromRangeSelectAction(baseEvent);
+ expect(filter).toEqual([]);
});
test('by creating a new filter', async () => {
baseEvent.range = [1, 2, 3, 4];
- const filter = await onBrushEvent(baseEvent);
+ const filter = await createFiltersFromRangeSelectAction(baseEvent);
expect(filter).toBeDefined();
- if (filter) {
- expect(filter.range.numberField.gte).toBe(1);
- expect(filter.range.numberField.lt).toBe(4);
- expect(filter.range.numberField).not.toHaveProperty('format');
+ if (filter.length) {
+ const rangeFilter = filter[0] as RangeFilter;
+ expect(rangeFilter.range.numberField.gte).toBe(1);
+ expect(rangeFilter.range.numberField.lt).toBe(4);
+ expect(rangeFilter.range.numberField).not.toHaveProperty('format');
}
});
});
diff --git a/src/plugins/data/public/actions/filters/brush_event.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
similarity index 74%
rename from src/plugins/data/public/actions/filters/brush_event.ts
rename to src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
index 714f005fbeb6d..409614ca9c380 100644
--- a/src/plugins/data/public/actions/filters/brush_event.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
@@ -17,34 +17,18 @@
* under the License.
*/
-import { get, last } from 'lodash';
+import { last } from 'lodash';
import moment from 'moment';
import { esFilters, IFieldType, RangeFilterParams } from '../../../public';
import { getIndexPatterns } from '../../../public/services';
import { deserializeAggConfig } from '../../search/expressions/utils';
+import { RangeSelectTriggerContext } from '../../../../embeddable/public';
-export interface BrushEvent {
- data: {
- ordered: {
- date: boolean;
- };
- series: Array>;
- };
- range: number[];
-}
-
-export async function onBrushEvent(event: BrushEvent) {
- const isDate = get(event.data, 'ordered.date');
- const xRaw: Record = get(event.data, 'series[0].values[0].xRaw');
-
- if (!xRaw) {
- return;
- }
-
- const column: Record = xRaw.table.columns[xRaw.column];
+export async function createFiltersFromRangeSelectAction(event: RangeSelectTriggerContext['data']) {
+ const column: Record = event.table.columns[event.column];
if (!column || !column.meta) {
- return;
+ return [];
}
const indexPattern = await getIndexPatterns().get(column.meta.indexPatternId);
@@ -55,16 +39,18 @@ export async function onBrushEvent(event: BrushEvent) {
const field: IFieldType = aggConfig.params.field;
if (!field || event.range.length <= 1) {
- return;
+ return [];
}
const min = event.range[0];
const max = last(event.range);
if (min === max) {
- return;
+ return [];
}
+ const isDate = field.type === 'date';
+
const range: RangeFilterParams = {
gte: isDate ? moment(min).toISOString() : min,
lt: isDate ? moment(max).toISOString() : max,
@@ -74,5 +60,5 @@ export async function onBrushEvent(event: BrushEvent) {
range.format = 'strict_date_optional_time';
}
- return esFilters.buildRangeFilter(field, range, indexPattern);
+ return esFilters.mapAndFlattenFilters([esFilters.buildRangeFilter(field, range, indexPattern)]);
}
diff --git a/src/plugins/data/public/actions/filters/create_filters_from_event.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts
similarity index 85%
rename from src/plugins/data/public/actions/filters/create_filters_from_event.test.ts
rename to src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts
index 1ed09002816d1..a0e285c20d776 100644
--- a/src/plugins/data/public/actions/filters/create_filters_from_event.test.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts
@@ -26,7 +26,8 @@ import {
import { dataPluginMock } from '../../../public/mocks';
import { setIndexPatterns } from '../../../public/services';
import { mockDataServices } from '../../../public/search/aggs/test_helpers';
-import { createFiltersFromEvent, EventData } from './create_filters_from_event';
+import { createFiltersFromValueClickAction } from './create_filters_from_value_click';
+import { ValueClickTriggerContext } from '../../../../embeddable/public';
const mockField = {
name: 'bytes',
@@ -37,8 +38,8 @@ const mockField = {
format: new fieldFormats.BytesFormat({}, (() => {}) as FieldFormatsGetConfigFn),
};
-describe('createFiltersFromEvent', () => {
- let dataPoints: EventData[];
+describe('createFiltersFromValueClick', () => {
+ let dataPoints: ValueClickTriggerContext['data']['data'];
beforeEach(() => {
dataPoints = [
@@ -86,7 +87,7 @@ describe('createFiltersFromEvent', () => {
test('ignores event when value for rows is not provided', async () => {
dataPoints[0].table.rows[0]['1-1'] = null;
- const filters = await createFiltersFromEvent(dataPoints);
+ const filters = await createFiltersFromValueClickAction({ data: dataPoints });
expect(filters.length).toEqual(0);
});
@@ -95,14 +96,14 @@ describe('createFiltersFromEvent', () => {
if (dataPoints[0].table.columns[0].meta) {
dataPoints[0].table.columns[0].meta.type = 'terms';
}
- const filters = await createFiltersFromEvent(dataPoints);
+ const filters = await createFiltersFromValueClickAction({ data: dataPoints });
expect(filters.length).toEqual(1);
expect(filters[0].query.match_phrase.bytes).toEqual('2048');
});
test('handles an event when aggregations type is not terms', async () => {
- const filters = await createFiltersFromEvent(dataPoints);
+ const filters = await createFiltersFromValueClickAction({ data: dataPoints });
expect(filters.length).toEqual(1);
diff --git a/src/plugins/data/public/actions/filters/create_filters_from_event.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
similarity index 90%
rename from src/plugins/data/public/actions/filters/create_filters_from_event.ts
rename to src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
index e62945a592072..2b426813a98a4 100644
--- a/src/plugins/data/public/actions/filters/create_filters_from_event.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
@@ -21,13 +21,7 @@ import { KibanaDatatable } from '../../../../../plugins/expressions/public';
import { deserializeAggConfig } from '../../search/expressions';
import { esFilters, Filter } from '../../../public';
import { getIndexPatterns } from '../../../public/services';
-
-export interface EventData {
- table: Pick;
- column: number;
- row: number;
- value: any;
-}
+import { ValueClickTriggerContext } from '../../../../embeddable/public';
/**
* For terms aggregations on `__other__` buckets, this assembles a list of applicable filter
@@ -39,7 +33,7 @@ export interface EventData {
* @return {array} - array of terms to filter against
*/
const getOtherBucketFilterTerms = (
- table: EventData['table'],
+ table: Pick,
columnIndex: number,
rowIndex: number
) => {
@@ -76,7 +70,11 @@ const getOtherBucketFilterTerms = (
* @param {string} cellValue - value of the current cell
* @return {Filter[]|undefined} - list of filters to provide to queryFilter.addFilters()
*/
-const createFilter = async (table: EventData['table'], columnIndex: number, rowIndex: number) => {
+const createFilter = async (
+ table: Pick,
+ columnIndex: number,
+ rowIndex: number
+) => {
if (!table || !table.columns || !table.columns[columnIndex]) {
return;
}
@@ -113,11 +111,14 @@ const createFilter = async (table: EventData['table'], columnIndex: number, rowI
};
/** @public */
-export const createFiltersFromEvent = async (dataPoints: EventData[], negate?: boolean) => {
+export const createFiltersFromValueClickAction = async ({
+ data,
+ negate,
+}: ValueClickTriggerContext['data']) => {
const filters: Filter[] = [];
await Promise.all(
- dataPoints
+ data
.filter(point => point)
.map(async val => {
const { table, column, row } = val;
@@ -133,5 +134,5 @@ export const createFiltersFromEvent = async (dataPoints: EventData[], negate?: b
})
);
- return filters;
+ return esFilters.mapAndFlattenFilters(filters);
};
diff --git a/src/plugins/data/public/actions/index.ts b/src/plugins/data/public/actions/index.ts
index cdb84ff13f25e..ef9014aafe82d 100644
--- a/src/plugins/data/public/actions/index.ts
+++ b/src/plugins/data/public/actions/index.ts
@@ -18,6 +18,7 @@
*/
export { ACTION_GLOBAL_APPLY_FILTER, createFilterAction } from './apply_filter_action';
-export { createFiltersFromEvent } from './filters/create_filters_from_event';
+export { createFiltersFromValueClickAction } from './filters/create_filters_from_value_click';
+export { createFiltersFromRangeSelectAction } from './filters/create_filters_from_range_select';
export { selectRangeAction } from './select_range_action';
export { valueClickAction } from './value_click_action';
diff --git a/src/plugins/data/public/actions/select_range_action.ts b/src/plugins/data/public/actions/select_range_action.ts
index 6e1f16a09e803..4882e8eafc0d3 100644
--- a/src/plugins/data/public/actions/select_range_action.ts
+++ b/src/plugins/data/public/actions/select_range_action.ts
@@ -23,19 +23,17 @@ import {
IncompatibleActionError,
ActionByType,
} from '../../../../plugins/ui_actions/public';
-import { onBrushEvent } from './filters/brush_event';
+import { createFiltersFromRangeSelectAction } from './filters/create_filters_from_range_select';
+import { RangeSelectTriggerContext } from '../../../embeddable/public';
import { FilterManager, TimefilterContract, esFilters } from '..';
export const ACTION_SELECT_RANGE = 'ACTION_SELECT_RANGE';
-export interface SelectRangeActionContext {
- data: any;
- timeFieldName: string;
-}
+export type SelectRangeActionContext = RangeSelectTriggerContext;
async function isCompatible(context: SelectRangeActionContext) {
try {
- return Boolean(await onBrushEvent(context.data));
+ return Boolean(await createFiltersFromRangeSelectAction(context.data));
} catch {
return false;
}
@@ -48,6 +46,7 @@ export function selectRangeAction(
return createAction({
type: ACTION_SELECT_RANGE,
id: ACTION_SELECT_RANGE,
+ getIconType: () => 'filter',
getDisplayName: () => {
return i18n.translate('data.filter.applyFilterActionTitle', {
defaultMessage: 'Apply filter to current view',
@@ -59,13 +58,7 @@ export function selectRangeAction(
throw new IncompatibleActionError();
}
- const filter = await onBrushEvent(data);
-
- if (!filter) {
- return;
- }
-
- const selectedFilters = esFilters.mapAndFlattenFilters([filter]);
+ const selectedFilters = await createFiltersFromRangeSelectAction(data);
if (timeFieldName) {
const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
diff --git a/src/plugins/data/public/actions/value_click_action.ts b/src/plugins/data/public/actions/value_click_action.ts
index 01c32e27da07d..210a58b3f75aa 100644
--- a/src/plugins/data/public/actions/value_click_action.ts
+++ b/src/plugins/data/public/actions/value_click_action.ts
@@ -26,21 +26,17 @@ import {
} from '../../../../plugins/ui_actions/public';
import { getOverlays, getIndexPatterns } from '../services';
import { applyFiltersPopover } from '../ui/apply_filters';
-import { createFiltersFromEvent } from './filters/create_filters_from_event';
+import { createFiltersFromValueClickAction } from './filters/create_filters_from_value_click';
+import { ValueClickTriggerContext } from '../../../embeddable/public';
import { Filter, FilterManager, TimefilterContract, esFilters } from '..';
export const ACTION_VALUE_CLICK = 'ACTION_VALUE_CLICK';
-export interface ValueClickActionContext {
- data: any;
- timeFieldName: string;
-}
+export type ValueClickActionContext = ValueClickTriggerContext;
async function isCompatible(context: ValueClickActionContext) {
try {
- const filters: Filter[] =
- (await createFiltersFromEvent(context.data.data || [context.data], context.data.negate)) ||
- [];
+ const filters: Filter[] = await createFiltersFromValueClickAction(context.data);
return filters.length > 0;
} catch {
return false;
@@ -54,23 +50,23 @@ export function valueClickAction(
return createAction({
type: ACTION_VALUE_CLICK,
id: ACTION_VALUE_CLICK,
+ getIconType: () => 'filter',
getDisplayName: () => {
return i18n.translate('data.filter.applyFilterActionTitle', {
defaultMessage: 'Apply filter to current view',
});
},
isCompatible,
- execute: async ({ timeFieldName, data }: ValueClickActionContext) => {
- if (!(await isCompatible({ timeFieldName, data }))) {
+ execute: async (context: ValueClickActionContext) => {
+ if (!(await isCompatible(context))) {
throw new IncompatibleActionError();
}
- const filters: Filter[] =
- (await createFiltersFromEvent(data.data || [data], data.negate)) || [];
+ const filters: Filter[] = await createFiltersFromValueClickAction(context.data);
- let selectedFilters: Filter[] = esFilters.mapAndFlattenFilters(filters);
+ let selectedFilters = filters;
- if (selectedFilters.length > 1) {
+ if (filters.length > 1) {
const indexPatterns = await Promise.all(
filters.map(filter => {
return getIndexPatterns().get(filter.meta.index!);
@@ -102,9 +98,9 @@ export function valueClickAction(
selectedFilters = await filterSelectionPromise;
}
- if (timeFieldName) {
+ if (context.timeFieldName) {
const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
- timeFieldName,
+ context.timeFieldName,
selectedFilters
);
filterManager.addFilters(restOfFilters);
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index 05a4141483587..e1e2576b2a0e7 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -365,8 +365,6 @@ export {
SearchResponse,
SearchError,
ISearchSource,
- SearchSource,
- createSearchSource,
SearchSourceFields,
EsQuerySortValue,
SortDirection,
diff --git a/src/plugins/data/public/index_patterns/index_patterns/ensure_default_index_pattern.tsx b/src/plugins/data/public/index_patterns/index_patterns/ensure_default_index_pattern.tsx
new file mode 100644
index 0000000000000..6b71739862f62
--- /dev/null
+++ b/src/plugins/data/public/index_patterns/index_patterns/ensure_default_index_pattern.tsx
@@ -0,0 +1,98 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { contains } from 'lodash';
+import React from 'react';
+import { History } from 'history';
+import { i18n } from '@kbn/i18n';
+import { EuiCallOut } from '@elastic/eui';
+import { CoreStart } from 'kibana/public';
+import { toMountPoint } from '../../../../kibana_react/public';
+import { IndexPatternsContract } from './index_patterns';
+
+export type EnsureDefaultIndexPattern = (history: History) => Promise | undefined;
+
+export const createEnsureDefaultIndexPattern = (core: CoreStart) => {
+ let bannerId: string;
+ let timeoutId: NodeJS.Timeout | undefined;
+
+ /**
+ * Checks whether a default index pattern is set and exists and defines
+ * one otherwise.
+ *
+ * If there are no index patterns, redirect to management page and show
+ * banner. In this case the promise returned from this function will never
+ * resolve to wait for the URL change to happen.
+ */
+ return async function ensureDefaultIndexPattern(this: IndexPatternsContract, history: History) {
+ const patterns = await this.getIds();
+ let defaultId = core.uiSettings.get('defaultIndex');
+ let defined = !!defaultId;
+ const exists = contains(patterns, defaultId);
+
+ if (defined && !exists) {
+ core.uiSettings.remove('defaultIndex');
+ defaultId = defined = false;
+ }
+
+ if (defined) {
+ return;
+ }
+
+ // If there is any index pattern created, set the first as default
+ if (patterns.length >= 1) {
+ defaultId = patterns[0];
+ core.uiSettings.set('defaultIndex', defaultId);
+ } else {
+ const canManageIndexPatterns = core.application.capabilities.management.kibana.index_patterns;
+ const redirectTarget = canManageIndexPatterns ? '/management/kibana/index_pattern' : '/home';
+
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+
+ // Avoid being hostile to new users who don't have an index pattern setup yet
+ // give them a friendly info message instead of a terse error message
+ bannerId = core.overlays.banners.replace(
+ bannerId,
+ toMountPoint(
+
+ )
+ );
+
+ // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around
+ timeoutId = setTimeout(() => {
+ core.overlays.banners.remove(bannerId);
+ timeoutId = undefined;
+ }, 15000);
+
+ history.push(redirectTarget);
+
+ // return never-resolving promise to stop resolving and wait for the url change
+ return new Promise(() => {});
+ }
+ };
+};
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts
index c429431b632bd..cf1f83d0e28cb 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts
+++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts
@@ -21,9 +21,9 @@
import { IndexPatternsService } from './index_patterns';
import {
SavedObjectsClientContract,
- IUiSettingsClient,
HttpSetup,
SavedObjectsFindResponsePublic,
+ CoreStart,
} from 'kibana/public';
jest.mock('./index_pattern', () => {
@@ -61,10 +61,10 @@ describe('IndexPatterns', () => {
}) as Promise>
);
- const uiSettings = {} as IUiSettingsClient;
+ const core = {} as CoreStart;
const http = {} as HttpSetup;
- indexPatterns = new IndexPatternsService(uiSettings, savedObjectsClient, http);
+ indexPatterns = new IndexPatternsService(core, savedObjectsClient, http);
});
test('does cache gets for the same id', async () => {
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts
index acce5ed57683c..b5d66a6aab60a 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts
+++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts
@@ -22,11 +22,16 @@ import {
SimpleSavedObject,
IUiSettingsClient,
HttpStart,
+ CoreStart,
} from 'src/core/public';
import { createIndexPatternCache } from './_pattern_cache';
import { IndexPattern } from './index_pattern';
import { IndexPatternsApiClient, GetFieldsOptions } from './index_patterns_api_client';
+import {
+ createEnsureDefaultIndexPattern,
+ EnsureDefaultIndexPattern,
+} from './ensure_default_index_pattern';
const indexPatternCache = createIndexPatternCache();
@@ -37,15 +42,13 @@ export class IndexPatternsService {
private savedObjectsClient: SavedObjectsClientContract;
private savedObjectsCache?: Array>> | null;
private apiClient: IndexPatternsApiClient;
+ ensureDefaultIndexPattern: EnsureDefaultIndexPattern;
- constructor(
- config: IUiSettingsClient,
- savedObjectsClient: SavedObjectsClientContract,
- http: HttpStart
- ) {
+ constructor(core: CoreStart, savedObjectsClient: SavedObjectsClientContract, http: HttpStart) {
this.apiClient = new IndexPatternsApiClient(http);
- this.config = config;
+ this.config = core.uiSettings;
this.savedObjectsClient = savedObjectsClient;
+ this.ensureDefaultIndexPattern = createEnsureDefaultIndexPattern(core);
}
private async refreshSavedObjectsCache() {
diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts
index 2d43cae79ac98..ba1df89c41358 100644
--- a/src/plugins/data/public/mocks.ts
+++ b/src/plugins/data/public/mocks.ts
@@ -45,7 +45,8 @@ const createStartContract = (): Start => {
const queryStartMock = queryServiceMock.createStartContract();
return {
actions: {
- createFiltersFromEvent: jest.fn().mockResolvedValue(['yes']),
+ createFiltersFromValueClickAction: jest.fn().mockResolvedValue(['yes']),
+ createFiltersFromRangeSelectAction: jest.fn(),
},
autocomplete: autocompleteMock,
search: searchStartMock,
@@ -56,6 +57,7 @@ const createStartContract = (): Start => {
SearchBar: jest.fn(),
},
indexPatterns: ({
+ ensureDefaultIndexPattern: jest.fn(),
make: () => ({
fieldsFetcher: {
fetchForWildcard: jest.fn(),
@@ -67,7 +69,7 @@ const createStartContract = (): Start => {
};
};
-export { searchSourceMock } from './search/mocks';
+export { createSearchSourceMock } from './search/mocks';
export { getCalculateAutoTimeExpression } from './search/aggs';
export const dataPluginMock = {
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index 1723545b32522..f3a88287313a0 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -24,13 +24,13 @@ import {
Plugin,
PackageInfo,
} from 'src/core/public';
-import { Storage, IStorageWrapper } from '../../kibana_utils/public';
+import { Storage, IStorageWrapper, createStartServicesGetter } from '../../kibana_utils/public';
import {
DataPublicPluginSetup,
DataPublicPluginStart,
DataSetupDependencies,
DataStartDependencies,
- GetInternalStartServicesFn,
+ InternalStartServices,
} from './types';
import { AutocompleteService } from './autocomplete';
import { SearchService } from './search/search_service';
@@ -48,8 +48,6 @@ import {
setQueryService,
setSearchService,
setUiSettings,
- getFieldFormats,
- getNotifications,
} from './services';
import { createSearchBar } from './ui/search_bar/create_search_bar';
import { esaggs } from './search/expressions';
@@ -58,7 +56,12 @@ import {
VALUE_CLICK_TRIGGER,
APPLY_FILTER_TRIGGER,
} from '../../ui_actions/public';
-import { ACTION_GLOBAL_APPLY_FILTER, createFilterAction, createFiltersFromEvent } from './actions';
+import {
+ ACTION_GLOBAL_APPLY_FILTER,
+ createFilterAction,
+ createFiltersFromValueClickAction,
+ createFiltersFromRangeSelectAction,
+} from './actions';
import { ApplyGlobalFilterActionContext } from './actions/apply_filter_action';
import {
selectRangeAction,
@@ -99,15 +102,21 @@ export class DataPublicPlugin implements Plugin {
+ const { core: coreStart, self }: any = startServices();
+ return {
+ fieldFormats: self.fieldFormats,
+ notifications: coreStart.notifications,
+ uiSettings: coreStart.uiSettings,
+ searchService: self.search,
+ injectedMetadata: coreStart.injectedMetadata,
+ };
+ };
expressions.registerFunction(esaggs);
- const getInternalStartServices: GetInternalStartServicesFn = () => ({
- fieldFormats: getFieldFormats(),
- notifications: getNotifications(),
- });
-
const queryService = this.queryService.setup({
uiSettings: core.uiSettings,
storage: this.storage,
@@ -130,6 +139,7 @@ export class DataPublicPlugin implements Plugin;
- // (undocumented)
- schema?: string;
- // (undocumented)
+export type AggConfigOptions = Assign;
// Warning: (ae-missing-release-tag) "AggGroupNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -112,7 +105,7 @@ export class AggParamType extends Ba
// (undocumented)
allowedAggs: string[];
// (undocumented)
- makeAgg: (agg: TAggConfig, state?: any) => TAggConfig;
+ makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
}
// Warning: (ae-missing-release-tag) "AggTypeFieldFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -145,7 +138,7 @@ export class AggTypeFilters {
// Warning: (ae-missing-release-tag) "baseFormattersPublic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const baseFormattersPublic: (import("../../common").IFieldFormatType | typeof DateFormat)[];
+export const baseFormattersPublic: (import("../../common").FieldFormatInstanceType | typeof DateFormat)[];
// Warning: (ae-missing-release-tag) "BUCKET_TYPES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -210,9 +203,6 @@ export const connectToQueryState: ({ timefilter: { timefil
// @public (undocumented)
export const createSavedQueryService: (savedObjectsClient: Pick) => SavedQueryService;
-// @public
-export const createSearchSource: (indexPatterns: Pick) => (searchSourceJson: string, references: SavedObjectReference[]) => Promise;
-
// Warning: (ae-missing-release-tag) "CustomFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -248,7 +238,8 @@ export interface DataPublicPluginSetup {
export interface DataPublicPluginStart {
// (undocumented)
actions: {
- createFiltersFromEvent: typeof createFiltersFromEvent;
+ createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction;
+ createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction;
};
// Warning: (ae-forgotten-export) The symbol "AutocompleteStart" needs to be exported by the entry point index.d.ts
//
@@ -484,7 +475,7 @@ export type FieldFormatId = FIELD_FORMAT_IDS | string;
export const fieldFormats: {
FieldFormat: typeof FieldFormat;
FieldFormatsRegistry: typeof FieldFormatsRegistry;
- serialize: (agg: import("./search").AggConfig) => import("../../expressions/common").SerializedFieldFormat;
+ serialize: (agg: import("./search").AggConfig) => import("../../expressions").SerializedFieldFormat;
DEFAULT_CONVERTER_COLOR: {
range: string;
regex: string;
@@ -636,21 +627,21 @@ export type IAggType = AggType;
// Warning: (ae-missing-release-tag) "IDataPluginServices" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export interface IDataPluginServices extends Partial {
+export interface IDataPluginServices extends Partial {
// (undocumented)
appName: string;
// (undocumented)
data: DataPublicPluginStart;
// (undocumented)
- http: CoreStart['http'];
+ http: CoreStart_2['http'];
// (undocumented)
- notifications: CoreStart['notifications'];
+ notifications: CoreStart_2['notifications'];
// (undocumented)
- savedObjects: CoreStart['savedObjects'];
+ savedObjects: CoreStart_2['savedObjects'];
// (undocumented)
storage: IStorageWrapper;
// (undocumented)
- uiSettings: CoreStart['uiSettings'];
+ uiSettings: CoreStart_2['uiSettings'];
}
// Warning: (ae-missing-release-tag) "IEsSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -1098,7 +1089,7 @@ export type ISearch =
// @public (undocumented)
export interface ISearchContext {
// (undocumented)
- core: CoreStart_2;
+ core: CoreStart;
// (undocumented)
getSearchStrategy: (name: T) => TSearchStrategyProvider;
}
@@ -1116,7 +1107,7 @@ export interface ISearchOptions {
signal?: AbortSignal;
}
-// Warning: (ae-missing-release-tag) "ISearchSource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+// Warning: (ae-forgotten-export) The symbol "SearchSource" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type ISearchSource = Pick;
@@ -1311,7 +1302,7 @@ export class Plugin implements Plugin_2;
- getField(field: K, recurse?: boolean): SearchSourceFields[K];
- // (undocumented)
- getFields(): {
- type?: string | undefined;
- query?: import("../..").Query | undefined;
- filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined;
- sort?: Record | Record[] | undefined;
- highlight?: any;
- highlightAll?: boolean | undefined;
- aggs?: any;
- from?: number | undefined;
- size?: number | undefined;
- source?: string | boolean | string[] | undefined;
- version?: boolean | undefined;
- fields?: string | boolean | string[] | undefined;
- index?: import("../..").IndexPattern | undefined;
- searchAfter?: import("./types").EsQuerySearchAfter | undefined;
- timeout?: string | undefined;
- terminate_after?: number | undefined;
- };
- // (undocumented)
- getId(): string;
- getOwnField(field: K): SearchSourceFields[K];
- getParent(): SearchSource | undefined;
- // (undocumented)
- getSearchRequestBody(): Promise;
- // (undocumented)
- history: SearchRequest[];
- onRequestStart(handler: (searchSource: ISearchSource, options?: FetchOptions) => Promise): void;
- serialize(): {
- searchSourceJSON: string;
- references: SavedObjectReference[];
- };
- // (undocumented)
- setField(field: K, value: SearchSourceFields[K]): this;
- // (undocumented)
- setFields(newFields: SearchSourceFields): this;
- // Warning: (ae-forgotten-export) The symbol "SearchSourceOptions" needs to be exported by the entry point index.d.ts
- setParent(parent?: ISearchSource, options?: SearchSourceOptions): this;
- setPreferredSearchStrategyId(searchStrategyId: string): void;
-}
-
// Warning: (ae-missing-release-tag) "SearchSourceFields" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -1875,25 +1811,26 @@ export type TSearchStrategyProvider = (context: ISearc
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:381:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:381:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:381:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:381:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:404:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromEvent" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/types.ts:60:5 - (ae-forgotten-export) The symbol "IndexPatternSelectProps" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/types.ts:53:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/types.ts:61:5 - (ae-forgotten-export) The symbol "IndexPatternSelectProps" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
diff --git a/src/plugins/data/public/search/aggs/agg_config.test.ts b/src/plugins/data/public/search/aggs/agg_config.test.ts
index 2813e3b9c5373..b5df90313230c 100644
--- a/src/plugins/data/public/search/aggs/agg_config.test.ts
+++ b/src/plugins/data/public/search/aggs/agg_config.test.ts
@@ -24,18 +24,21 @@ import { AggConfigs, CreateAggConfigParams } from './agg_configs';
import { AggType } from './agg_type';
import { AggTypesRegistryStart } from './agg_types_registry';
import { mockDataServices, mockAggTypesRegistry } from './test_helpers';
+import { MetricAggType } from './metrics/metric_agg_type';
import { Field as IndexPatternField, IndexPattern } from '../../index_patterns';
import { stubIndexPatternWithFields } from '../../../public/stubs';
+import { FieldFormatsStart } from '../../field_formats';
import { fieldFormatsServiceMock } from '../../field_formats/mocks';
describe('AggConfig', () => {
let indexPattern: IndexPattern;
let typesRegistry: AggTypesRegistryStart;
- const fieldFormats = fieldFormatsServiceMock.createStartContract();
+ let fieldFormats: FieldFormatsStart;
beforeEach(() => {
jest.restoreAllMocks();
mockDataServices();
+ fieldFormats = fieldFormatsServiceMock.createStartContract();
indexPattern = stubIndexPatternWithFields as IndexPattern;
typesRegistry = mockAggTypesRegistry();
});
@@ -325,7 +328,7 @@ describe('AggConfig', () => {
});
});
- describe('#toJSON', () => {
+ describe('#serialize', () => {
it('includes the aggs id, params, type and schema', () => {
const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
const configStates = {
@@ -342,7 +345,7 @@ describe('AggConfig', () => {
expect(aggConfig.type).toHaveProperty('name', 'date_histogram');
expect(typeof aggConfig.schema).toBe('string');
- const state = aggConfig.toJSON();
+ const state = aggConfig.serialize();
expect(state).toHaveProperty('id', '1');
expect(typeof state.params).toBe('object');
expect(state).toHaveProperty('type', 'date_histogram');
@@ -367,6 +370,201 @@ describe('AggConfig', () => {
});
});
+ describe('#toExpressionAst', () => {
+ beforeEach(() => {
+ fieldFormats.getDefaultInstance = (() => ({
+ getConverterFor: (t?: string) => t || identity,
+ })) as any;
+ indexPattern.fields.getByName = name =>
+ ({
+ format: {
+ getConverterFor: (t?: string) => t || identity,
+ },
+ } as IndexPatternField);
+ });
+
+ it('works with primitive param types', () => {
+ const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
+ const configStates = {
+ enabled: true,
+ type: 'terms',
+ schema: 'segment',
+ params: {
+ field: 'machine.os.keyword',
+ order: 'asc',
+ },
+ };
+ const aggConfig = ac.createAggConfig(configStates);
+ expect(aggConfig.toExpressionAst()).toMatchInlineSnapshot(`
+ Object {
+ "arguments": Object {
+ "enabled": Array [
+ true,
+ ],
+ "id": Array [
+ "1",
+ ],
+ "missingBucket": Array [
+ false,
+ ],
+ "missingBucketLabel": Array [
+ "Missing",
+ ],
+ "order": Array [
+ "asc",
+ ],
+ "otherBucket": Array [
+ false,
+ ],
+ "otherBucketLabel": Array [
+ "Other",
+ ],
+ "schema": Array [
+ "segment",
+ ],
+ "size": Array [
+ 5,
+ ],
+ },
+ "function": "aggTerms",
+ "type": "function",
+ }
+ `);
+ });
+
+ it('creates a subexpression for params of type "agg"', () => {
+ const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
+ const configStates = {
+ type: 'terms',
+ params: {
+ field: 'machine.os.keyword',
+ order: 'asc',
+ orderAgg: {
+ enabled: true,
+ type: 'terms',
+ params: {
+ field: 'bytes',
+ order: 'asc',
+ size: 5,
+ },
+ },
+ },
+ };
+ const aggConfig = ac.createAggConfig(configStates);
+ const aggArg = aggConfig.toExpressionAst()?.arguments.orderAgg;
+ expect(aggArg).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "chain": Array [
+ Object {
+ "arguments": Object {
+ "enabled": Array [
+ true,
+ ],
+ "id": Array [
+ "1-orderAgg",
+ ],
+ "missingBucket": Array [
+ false,
+ ],
+ "missingBucketLabel": Array [
+ "Missing",
+ ],
+ "order": Array [
+ "asc",
+ ],
+ "otherBucket": Array [
+ false,
+ ],
+ "otherBucketLabel": Array [
+ "Other",
+ ],
+ "schema": Array [
+ "orderAgg",
+ ],
+ "size": Array [
+ 5,
+ ],
+ },
+ "function": "aggTerms",
+ "type": "function",
+ },
+ ],
+ "type": "expression",
+ },
+ ]
+ `);
+ });
+
+ it('creates a subexpression for param types other than "agg" which have specified toExpressionAst', () => {
+ // Overwrite the `ranges` param in the `range` agg with a mock toExpressionAst function
+ const range: MetricAggType = typesRegistry.get('range');
+ range.expressionName = 'aggRange';
+ const rangesParam = range.params.find(p => p.name === 'ranges');
+ rangesParam!.toExpressionAst = (val: any) => ({
+ type: 'function',
+ function: 'aggRanges',
+ arguments: {
+ ranges: ['oh hi there!'],
+ },
+ });
+
+ const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
+ const configStates = {
+ type: 'range',
+ params: {
+ field: 'bytes',
+ },
+ };
+
+ const aggConfig = ac.createAggConfig(configStates);
+ const ranges = aggConfig.toExpressionAst()!.arguments.ranges;
+ expect(ranges).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "chain": Array [
+ Object {
+ "arguments": Object {
+ "ranges": Array [
+ "oh hi there!",
+ ],
+ },
+ "function": "aggRanges",
+ "type": "function",
+ },
+ ],
+ "type": "expression",
+ },
+ ]
+ `);
+ });
+
+ it('stringifies any other params which are an object', () => {
+ const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
+ const configStates = {
+ type: 'terms',
+ params: {
+ field: 'machine.os.keyword',
+ order: 'asc',
+ json: { foo: 'bar' },
+ },
+ };
+ const aggConfig = ac.createAggConfig(configStates);
+ const json = aggConfig.toExpressionAst()?.arguments.json;
+ expect(json).toEqual([JSON.stringify(configStates.params.json)]);
+ });
+
+ it(`returns undefined if an expressionName doesn't exist on the agg type`, () => {
+ const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
+ const configStates = {
+ type: 'unknown type',
+ params: {},
+ };
+ const aggConfig = ac.createAggConfig(configStates);
+ expect(aggConfig.toExpressionAst()).toBe(undefined);
+ });
+ });
+
describe('#makeLabel', () => {
let aggConfig: AggConfig;
@@ -422,6 +620,9 @@ describe('AggConfig', () => {
let aggConfig: AggConfig;
beforeEach(() => {
+ fieldFormats.getDefaultInstance = (() => ({
+ getConverterFor: (t?: string) => t || identity,
+ })) as any;
indexPattern.fields.getByName = name =>
({
format: {
@@ -434,11 +635,7 @@ describe('AggConfig', () => {
type: 'histogram',
schema: 'bucket',
params: {
- field: {
- format: {
- getConverterFor: (t?: string) => t || identity,
- },
- },
+ field: 'bytes',
},
};
const ac = new AggConfigs(indexPattern, [configStates], { typesRegistry, fieldFormats });
@@ -446,6 +643,11 @@ describe('AggConfig', () => {
});
it("returns the field's formatter", () => {
+ aggConfig.params.field = {
+ format: {
+ getConverterFor: (t?: string) => t || identity,
+ },
+ };
expect(aggConfig.fieldFormatter().toString()).toBe(
aggConfig
.getField()
diff --git a/src/plugins/data/public/search/aggs/agg_config.ts b/src/plugins/data/public/search/aggs/agg_config.ts
index 6188849e0e6d4..973c69e3d4f5f 100644
--- a/src/plugins/data/public/search/aggs/agg_config.ts
+++ b/src/plugins/data/public/search/aggs/agg_config.ts
@@ -19,6 +19,8 @@
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
+import { Assign } from '@kbn/utility-types';
+import { ExpressionAstFunction, ExpressionAstArgument } from 'src/plugins/expressions/public';
import { IAggType } from './agg_type';
import { writeParams } from './agg_params';
import { IAggConfigs } from './agg_configs';
@@ -27,11 +29,17 @@ import { ISearchSource } from '../search_source';
import { FieldFormatsContentType, KBN_FIELD_TYPES } from '../../../common';
import { FieldFormatsStart } from '../../field_formats';
-export interface AggConfigOptions {
- type: IAggType;
+type State = string | number | boolean | null | undefined | SerializableState;
+
+interface SerializableState {
+ [key: string]: State | State[];
+}
+
+export interface AggConfigSerialized {
+ type: string;
enabled?: boolean;
id?: string;
- params?: Record;
+ params?: SerializableState;
schema?: string;
}
@@ -39,6 +47,8 @@ export interface AggConfigDependencies {
fieldFormats: FieldFormatsStart;
}
+export type AggConfigOptions = Assign;
+
/**
* @name AggConfig
*
@@ -257,7 +267,10 @@ export class AggConfig {
return configDsl;
}
- toJSON() {
+ /**
+ * @returns Returns a serialized representation of an AggConfig.
+ */
+ serialize(): AggConfigSerialized {
const params = this.params;
const outParams = _.transform(
@@ -281,7 +294,64 @@ export class AggConfig {
enabled: this.enabled,
type: this.type && this.type.name,
schema: this.schema,
- params: outParams,
+ params: outParams as SerializableState,
+ };
+ }
+
+ /**
+ * @deprecated - Use serialize() instead.
+ */
+ toJSON(): AggConfigSerialized {
+ return this.serialize();
+ }
+
+ /**
+ * @returns Returns an ExpressionAst representing the function for this agg type.
+ */
+ toExpressionAst(): ExpressionAstFunction | undefined {
+ const functionName = this.type && this.type.expressionName;
+ const { type, ...rest } = this.serialize();
+ if (!functionName || !rest.params) {
+ // Return undefined - there is no matching expression function for this agg
+ return;
+ }
+
+ // Go through each of the params and convert to an array of expression args.
+ const params = Object.entries(rest.params).reduce((acc, [key, value]) => {
+ const deserializedParam = this.getAggParams().find(p => p.name === key);
+
+ if (deserializedParam && deserializedParam.toExpressionAst) {
+ // If the param provides `toExpressionAst`, we call it with the value
+ const paramExpressionAst = deserializedParam.toExpressionAst(this.getParam(key));
+ if (paramExpressionAst) {
+ acc[key] = [
+ {
+ type: 'expression',
+ chain: [paramExpressionAst],
+ },
+ ];
+ }
+ } else if (typeof value === 'object') {
+ // For object params which don't provide `toExpressionAst`, we stringify
+ acc[key] = [JSON.stringify(value)];
+ } else if (typeof value !== 'undefined') {
+ // Everything else just gets stored in an array if it is defined
+ acc[key] = [value];
+ }
+
+ return acc;
+ }, {} as Record);
+
+ return {
+ type: 'function',
+ function: functionName,
+ arguments: {
+ ...params,
+ // Expression args which are provided to all functions
+ id: [this.id],
+ enabled: [this.enabled],
+ ...(this.schema ? { schema: [this.schema] } : {}), // schema may be undefined
+ },
};
}
diff --git a/src/plugins/data/public/search/aggs/agg_configs.ts b/src/plugins/data/public/search/aggs/agg_configs.ts
index 5ad09f824d3e4..d2151a2c5ed4d 100644
--- a/src/plugins/data/public/search/aggs/agg_configs.ts
+++ b/src/plugins/data/public/search/aggs/agg_configs.ts
@@ -20,7 +20,7 @@
import _ from 'lodash';
import { Assign } from '@kbn/utility-types';
-import { AggConfig, AggConfigOptions, IAggConfig } from './agg_config';
+import { AggConfig, AggConfigSerialized, IAggConfig } from './agg_config';
import { IAggType } from './agg_type';
import { AggTypesRegistryStart } from './agg_types_registry';
import { AggGroupNames } from './agg_groups';
@@ -51,7 +51,7 @@ export interface AggConfigsOptions {
fieldFormats: FieldFormatsStart;
}
-export type CreateAggConfigParams = Assign;
+export type CreateAggConfigParams = Assign;
/**
* @name AggConfigs
diff --git a/src/plugins/data/public/search/aggs/agg_params.test.ts b/src/plugins/data/public/search/aggs/agg_params.test.ts
index 784be803e2644..e116bdca157ff 100644
--- a/src/plugins/data/public/search/aggs/agg_params.test.ts
+++ b/src/plugins/data/public/search/aggs/agg_params.test.ts
@@ -25,13 +25,15 @@ import { AggParamType } from '../aggs/param_types/agg';
import { fieldFormatsServiceMock } from '../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../src/core/public/mocks';
import { AggTypeDependencies } from './agg_type';
+import { InternalStartServices } from '../../types';
describe('AggParams class', () => {
const aggTypesDependencies: AggTypeDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
describe('constructor args', () => {
diff --git a/src/plugins/data/public/search/aggs/agg_type.test.ts b/src/plugins/data/public/search/aggs/agg_type.test.ts
index 0c9e110c34ae6..369ae0ce0b3a5 100644
--- a/src/plugins/data/public/search/aggs/agg_type.test.ts
+++ b/src/plugins/data/public/search/aggs/agg_type.test.ts
@@ -21,19 +21,21 @@ import { AggType, AggTypeConfig, AggTypeDependencies } from './agg_type';
import { IAggConfig } from './agg_config';
import { fieldFormatsServiceMock } from '../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../types';
describe('AggType Class', () => {
let dependencies: AggTypeDependencies;
beforeEach(() => {
dependencies = {
- getInternalStartServices: () => ({
- fieldFormats: {
- ...fieldFormatsServiceMock.createStartContract(),
- getDefaultInstance: jest.fn(() => 'default') as any,
- },
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: {
+ ...fieldFormatsServiceMock.createStartContract(),
+ getDefaultInstance: jest.fn(() => 'default') as any,
+ },
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
});
diff --git a/src/plugins/data/public/search/aggs/agg_type.ts b/src/plugins/data/public/search/aggs/agg_type.ts
index 70c116d560c6f..fb0cb609a08cf 100644
--- a/src/plugins/data/public/search/aggs/agg_type.ts
+++ b/src/plugins/data/public/search/aggs/agg_type.ts
@@ -39,6 +39,7 @@ export interface AggTypeConfig<
createFilter?: (aggConfig: TAggConfig, key: any, params?: any) => any;
type?: string;
dslName?: string;
+ expressionName?: string;
makeLabel?: ((aggConfig: TAggConfig) => string) | (() => string);
ordered?: any;
hasNoDsl?: boolean;
@@ -88,6 +89,14 @@ export class AggType<
* @type {string}
*/
dslName: string;
+ /**
+ * the name of the expression function that this aggType represents.
+ * TODO: this should probably be a required field.
+ *
+ * @property name
+ * @type {string}
+ */
+ expressionName?: string;
/**
* the user friendly name that will be shown in the ui for this aggType
*
@@ -219,6 +228,7 @@ export class AggType<
this.name = config.name;
this.type = config.type || 'metrics';
this.dslName = config.dslName || config.name;
+ this.expressionName = config.expressionName;
this.title = config.title;
this.makeLabel = config.makeLabel || constant(this.name);
this.ordered = config.ordered;
diff --git a/src/plugins/data/public/search/aggs/agg_types.ts b/src/plugins/data/public/search/aggs/agg_types.ts
index 4b154c338d48c..da07f581c9274 100644
--- a/src/plugins/data/public/search/aggs/agg_types.ts
+++ b/src/plugins/data/public/search/aggs/agg_types.ts
@@ -37,6 +37,7 @@ import { getDerivativeMetricAgg } from './metrics/derivative';
import { getCumulativeSumMetricAgg } from './metrics/cumulative_sum';
import { getMovingAvgMetricAgg } from './metrics/moving_avg';
import { getSerialDiffMetricAgg } from './metrics/serial_diff';
+
import { getDateHistogramBucketAgg } from './buckets/date_histogram';
import { getHistogramBucketAgg } from './buckets/histogram';
import { getRangeBucketAgg } from './buckets/range';
@@ -103,3 +104,7 @@ export const getAggTypes = ({
getGeoTitleBucketAgg({ getInternalStartServices }),
],
});
+
+import { aggTerms } from './buckets/terms_fn';
+
+export const getAggTypesFunctions = () => [aggTerms];
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts
index 7778fcb36bcd6..bb73c8a39df19 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts
@@ -32,6 +32,7 @@ import { RangeFilter } from '../../../../../common';
import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks';
import { queryServiceMock } from '../../../../query/mocks';
import { fieldFormatsServiceMock } from '../../../../field_formats/mocks';
+import { InternalStartServices } from '../../../../types';
describe('AggConfig Filters', () => {
describe('date_histogram', () => {
@@ -47,10 +48,11 @@ describe('AggConfig Filters', () => {
aggTypesDependencies = {
uiSettings,
query: queryServiceMock.createSetupContract(),
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
mockDataServices();
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts
index 4207fa92736f8..0d66d9cfcdca2 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts
@@ -28,6 +28,7 @@ import { BUCKET_TYPES } from '../bucket_agg_types';
import { IBucketAggConfig } from '../bucket_agg_type';
import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks';
import { fieldFormatsServiceMock } from '../../../../field_formats/mocks';
+import { InternalStartServices } from '../../../../types';
describe('AggConfig Filters', () => {
describe('Date range', () => {
@@ -38,10 +39,11 @@ describe('AggConfig Filters', () => {
aggTypesDependencies = {
uiSettings,
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
});
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts
index bf05f7463db6c..0fdb07cc4198a 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts
@@ -24,6 +24,7 @@ import { mockAggTypesRegistry } from '../../test_helpers';
import { IBucketAggConfig } from '../bucket_agg_type';
import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks';
import { fieldFormatsServiceMock } from '../../../../field_formats/mocks';
+import { InternalStartServices } from '../../../../types';
describe('AggConfig Filters', () => {
describe('filters', () => {
@@ -34,10 +35,11 @@ describe('AggConfig Filters', () => {
aggTypesDependencies = {
uiSettings,
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
});
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts
index 1999b759a23d0..72d2029a12b0d 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts
+++ b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts
@@ -28,6 +28,6 @@ export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) =>
const indexPattern = aggConfig.getIndexPattern();
if (filter && indexPattern && indexPattern.id) {
- return buildQueryFilter(filter.query, indexPattern.id, key);
+ return buildQueryFilter(filter, indexPattern.id, key);
}
};
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts
index d85576a0ccb14..990adde5f8a0b 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts
@@ -26,16 +26,18 @@ import { BUCKET_TYPES } from '../bucket_agg_types';
import { IBucketAggConfig } from '../bucket_agg_type';
import { fieldFormatsServiceMock } from '../../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../core/public/mocks';
+import { InternalStartServices } from '../../../../types';
describe('AggConfig Filters', () => {
describe('IP range', () => {
const fieldFormats = fieldFormatsServiceMock.createStartContract();
const typesRegistry = mockAggTypesRegistry([
getIpRangeBucketAgg({
- getInternalStartServices: () => ({
- fieldFormats,
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats,
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
}),
]);
const getAggConfigs = (aggs: CreateAggConfigParams[]) => {
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts
index cadd8e9fe13ed..564e7b4763c8d 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts
@@ -26,6 +26,7 @@ import { BUCKET_TYPES } from '../bucket_agg_types';
import { IBucketAggConfig } from '../bucket_agg_type';
import { fieldFormatsServiceMock } from '../../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../core/public/mocks';
+import { InternalStartServices } from '../../../../types';
describe('AggConfig Filters', () => {
describe('range', () => {
@@ -33,10 +34,11 @@ describe('AggConfig Filters', () => {
beforeEach(() => {
aggTypesDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
mockDataServices();
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts
index d9ff63613b640..36e4bef025ef9 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts
@@ -27,6 +27,7 @@ import { Filter, ExistsFilter } from '../../../../../common';
import { RangeBucketAggDependencies } from '../range';
import { fieldFormatsServiceMock } from '../../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../core/public/mocks';
+import { InternalStartServices } from '../../../../types';
describe('AggConfig Filters', () => {
describe('terms', () => {
@@ -34,10 +35,11 @@ describe('AggConfig Filters', () => {
beforeEach(() => {
aggTypesDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
});
diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts b/src/plugins/data/public/search/aggs/buckets/date_range.test.ts
index f78f0cce732e7..e1881c3bbc7f4 100644
--- a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/date_range.test.ts
@@ -23,6 +23,7 @@ import { AggConfigs } from '../agg_configs';
import { mockAggTypesRegistry } from '../test_helpers';
import { BUCKET_TYPES } from './bucket_agg_types';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
+import { InternalStartServices } from '../../../types';
describe('date_range params', () => {
let aggTypesDependencies: DateRangeBucketAggDependencies;
@@ -32,10 +33,11 @@ describe('date_range params', () => {
aggTypesDependencies = {
uiSettings,
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
});
diff --git a/src/plugins/data/public/search/aggs/buckets/filters.ts b/src/plugins/data/public/search/aggs/buckets/filters.ts
index a42cb70a62b7d..fe013928bba65 100644
--- a/src/plugins/data/public/search/aggs/buckets/filters.ts
+++ b/src/plugins/data/public/search/aggs/buckets/filters.ts
@@ -107,7 +107,7 @@ export const getFiltersBucketAgg = ({
(typeof filter.input.query === 'string'
? filter.input.query
: toAngularJSON(filter.input.query));
- filters[label] = { query };
+ filters[label] = query;
},
{}
);
diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts b/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts
index 226faefe43482..877a817984dc6 100644
--- a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts
@@ -24,6 +24,7 @@ import { BUCKET_TYPES } from './bucket_agg_types';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { BucketAggType, IBucketAggConfig } from './bucket_agg_type';
+import { InternalStartServices } from '../../../types';
describe('Geohash Agg', () => {
let aggTypesDependencies: GeoHashBucketAggDependencies;
@@ -31,10 +32,11 @@ describe('Geohash Agg', () => {
beforeEach(() => {
aggTypesDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
geoHashBucketAgg = getGeoHashBucketAgg(aggTypesDependencies);
diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/histogram.test.ts
index a55c32951232a..4756669f5b4b3 100644
--- a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/histogram.test.ts
@@ -29,6 +29,7 @@ import {
} from './histogram';
import { BucketAggType } from './bucket_agg_type';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
+import { InternalStartServices } from '../../../types';
describe('Histogram Agg', () => {
let aggTypesDependencies: HistogramBucketAggDependencies;
@@ -38,10 +39,11 @@ describe('Histogram Agg', () => {
aggTypesDependencies = {
uiSettings,
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
});
diff --git a/src/plugins/data/public/search/aggs/buckets/range.test.ts b/src/plugins/data/public/search/aggs/buckets/range.test.ts
index 144d2b779e950..4c2d3af1ab734 100644
--- a/src/plugins/data/public/search/aggs/buckets/range.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/range.test.ts
@@ -24,6 +24,7 @@ import { BUCKET_TYPES } from './bucket_agg_types';
import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../common';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../../types';
const buckets = [
{
@@ -50,10 +51,11 @@ describe('Range Agg', () => {
beforeEach(() => {
aggTypesDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
mockDataServices();
diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts b/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts
index d0ace5a50c28d..156f7f8108482 100644
--- a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts
@@ -26,6 +26,7 @@ import {
} from './significant_terms';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../../types';
describe('Significant Terms Agg', () => {
describe('order agg editor UI', () => {
@@ -34,10 +35,11 @@ describe('Significant Terms Agg', () => {
beforeEach(() => {
aggTypesDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
});
diff --git a/src/plugins/data/public/search/aggs/buckets/terms.ts b/src/plugins/data/public/search/aggs/buckets/terms.ts
index 698e0dfb1d340..a12a1d7ac2d3d 100644
--- a/src/plugins/data/public/search/aggs/buckets/terms.ts
+++ b/src/plugins/data/public/search/aggs/buckets/terms.ts
@@ -26,7 +26,7 @@ import {
isStringOrNumberType,
migrateIncludeExcludeFormat,
} from './migrate_include_exclude_format';
-import { IAggConfigs } from '../agg_configs';
+import { AggConfigSerialized, IAggConfigs } from '../types';
import { Adapters } from '../../../../../inspector/public';
import { ISearchSource } from '../../search_source';
@@ -63,10 +63,27 @@ export interface TermsBucketAggDependencies {
getInternalStartServices: GetInternalStartServicesFn;
}
+export interface AggParamsTerms {
+ field: string;
+ order: 'asc' | 'desc';
+ orderBy: string;
+ orderAgg?: AggConfigSerialized;
+ size?: number;
+ missingBucket?: boolean;
+ missingBucketLabel?: string;
+ otherBucket?: boolean;
+ otherBucketLabel?: string;
+ // advanced
+ exclude?: string;
+ include?: string;
+ json?: string;
+}
+
export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDependencies) =>
new BucketAggType(
{
name: BUCKET_TYPES.TERMS,
+ expressionName: 'aggTerms',
title: termsTitle,
makeLabel(agg) {
const params = agg.params;
@@ -154,8 +171,7 @@ export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDe
type: 'agg',
allowedAggs: termsAggFilter,
default: null,
- makeAgg(termsAgg, state) {
- state = state || {};
+ makeAgg(termsAgg, state = { type: 'count' }) {
state.schema = 'orderAgg';
const orderAgg = termsAgg.aggConfigs.createAggConfig(state, {
addToAggConfigs: false,
diff --git a/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts
new file mode 100644
index 0000000000000..f55f1de796013
--- /dev/null
+++ b/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts
@@ -0,0 +1,164 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { functionWrapper } from '../test_helpers';
+import { aggTerms } from './terms_fn';
+
+describe('agg_expression_functions', () => {
+ describe('aggTerms', () => {
+ const fn = functionWrapper(aggTerms());
+
+ test('fills in defaults when only required args are provided', () => {
+ const actual = fn({
+ field: 'machine.os.keyword',
+ order: 'asc',
+ orderBy: '1',
+ });
+ expect(actual).toMatchInlineSnapshot(`
+ Object {
+ "type": "agg_type",
+ "value": Object {
+ "enabled": true,
+ "id": undefined,
+ "params": Object {
+ "exclude": undefined,
+ "field": "machine.os.keyword",
+ "include": undefined,
+ "json": undefined,
+ "missingBucket": false,
+ "missingBucketLabel": "Missing",
+ "order": "asc",
+ "orderAgg": undefined,
+ "orderBy": "1",
+ "otherBucket": false,
+ "otherBucketLabel": "Other",
+ "size": 5,
+ },
+ "schema": undefined,
+ "type": "terms",
+ },
+ }
+ `);
+ });
+
+ test('includes optional params when they are provided', () => {
+ const actual = fn({
+ id: '1',
+ enabled: false,
+ schema: 'whatever',
+ field: 'machine.os.keyword',
+ order: 'desc',
+ orderBy: '2',
+ size: 6,
+ missingBucket: true,
+ missingBucketLabel: 'missing',
+ otherBucket: true,
+ otherBucketLabel: 'other',
+ exclude: 'ios',
+ });
+
+ expect(actual.value).toMatchInlineSnapshot(`
+ Object {
+ "enabled": false,
+ "id": "1",
+ "params": Object {
+ "exclude": "ios",
+ "field": "machine.os.keyword",
+ "include": undefined,
+ "json": undefined,
+ "missingBucket": true,
+ "missingBucketLabel": "missing",
+ "order": "desc",
+ "orderAgg": undefined,
+ "orderBy": "2",
+ "otherBucket": true,
+ "otherBucketLabel": "other",
+ "size": 6,
+ },
+ "schema": "whatever",
+ "type": "terms",
+ }
+ `);
+ });
+
+ test('handles orderAgg as a subexpression', () => {
+ const actual = fn({
+ field: 'machine.os.keyword',
+ order: 'asc',
+ orderBy: '1',
+ orderAgg: fn({ field: 'name', order: 'asc', orderBy: '1' }),
+ });
+
+ expect(actual.value.params).toMatchInlineSnapshot(`
+ Object {
+ "exclude": undefined,
+ "field": "machine.os.keyword",
+ "include": undefined,
+ "json": undefined,
+ "missingBucket": false,
+ "missingBucketLabel": "Missing",
+ "order": "asc",
+ "orderAgg": Object {
+ "enabled": true,
+ "id": undefined,
+ "params": Object {
+ "exclude": undefined,
+ "field": "name",
+ "include": undefined,
+ "json": undefined,
+ "missingBucket": false,
+ "missingBucketLabel": "Missing",
+ "order": "asc",
+ "orderAgg": undefined,
+ "orderBy": "1",
+ "otherBucket": false,
+ "otherBucketLabel": "Other",
+ "size": 5,
+ },
+ "schema": undefined,
+ "type": "terms",
+ },
+ "orderBy": "1",
+ "otherBucket": false,
+ "otherBucketLabel": "Other",
+ "size": 5,
+ }
+ `);
+ });
+
+ test('correctly parses json string argument', () => {
+ const actual = fn({
+ field: 'machine.os.keyword',
+ order: 'asc',
+ orderBy: '1',
+ json: '{ "foo": true }',
+ });
+
+ expect(actual.value.params.json).toEqual({ foo: true });
+ expect(() => {
+ fn({
+ field: 'machine.os.keyword',
+ order: 'asc',
+ orderBy: '1',
+ json: '/// intentionally malformed json ///',
+ });
+ }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`);
+ });
+ });
+});
diff --git a/src/plugins/data/public/search/aggs/buckets/terms_fn.ts b/src/plugins/data/public/search/aggs/buckets/terms_fn.ts
new file mode 100644
index 0000000000000..7980bfabe79fb
--- /dev/null
+++ b/src/plugins/data/public/search/aggs/buckets/terms_fn.ts
@@ -0,0 +1,181 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { Assign } from '@kbn/utility-types';
+import { ExpressionFunctionDefinition } from '../../../../../expressions/public';
+import { AggExpressionType, AggExpressionFunctionArgs } from '../';
+
+const aggName = 'terms';
+const fnName = 'aggTerms';
+
+type Input = any;
+type AggArgs = AggExpressionFunctionArgs;
+// Since the orderAgg param is an agg nested in a subexpression, we need to
+// overwrite the param type to expect a value of type AggExpressionType.
+type Arguments = AggArgs &
+ Assign<
+ AggArgs,
+ { orderAgg?: AggArgs['orderAgg'] extends undefined ? undefined : AggExpressionType }
+ >;
+type Output = AggExpressionType;
+type FunctionDefinition = ExpressionFunctionDefinition;
+
+export const aggTerms = (): FunctionDefinition => ({
+ name: fnName,
+ help: i18n.translate('data.search.aggs.function.buckets.terms.help', {
+ defaultMessage: 'Generates a serialized agg config for a terms agg',
+ }),
+ type: 'agg_type',
+ args: {
+ id: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.buckets.terms.id.help', {
+ defaultMessage: 'ID for this aggregation',
+ }),
+ },
+ enabled: {
+ types: ['boolean'],
+ default: true,
+ help: i18n.translate('data.search.aggs.buckets.terms.enabled.help', {
+ defaultMessage: 'Specifies whether this aggregation should be enabled',
+ }),
+ },
+ schema: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.buckets.terms.schema.help', {
+ defaultMessage: 'Schema to use for this aggregation',
+ }),
+ },
+ field: {
+ types: ['string'],
+ required: true,
+ help: i18n.translate('data.search.aggs.buckets.terms.field.help', {
+ defaultMessage: 'Field to use for this aggregation',
+ }),
+ },
+ order: {
+ types: ['string'],
+ required: true,
+ help: i18n.translate('data.search.aggs.buckets.terms.order.help', {
+ defaultMessage: 'Order in which to return the results: asc or desc',
+ }),
+ },
+ orderBy: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.buckets.terms.orderBy.help', {
+ defaultMessage: 'Field to order results by',
+ }),
+ },
+ orderAgg: {
+ types: ['agg_type'],
+ help: i18n.translate('data.search.aggs.buckets.terms.orderAgg.help', {
+ defaultMessage: 'Agg config to use for ordering results',
+ }),
+ },
+ size: {
+ types: ['number'],
+ default: 5,
+ help: i18n.translate('data.search.aggs.buckets.terms.size.help', {
+ defaultMessage: 'Max number of buckets to retrieve',
+ }),
+ },
+ missingBucket: {
+ types: ['boolean'],
+ default: false,
+ help: i18n.translate('data.search.aggs.buckets.terms.missingBucket.help', {
+ defaultMessage: 'When set to true, groups together any buckets with missing fields',
+ }),
+ },
+ missingBucketLabel: {
+ types: ['string'],
+ default: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel', {
+ defaultMessage: 'Missing',
+ description: `Default label used in charts when documents are missing a field.
+ Visible when you create a chart with a terms aggregation and enable "Show missing values"`,
+ }),
+ help: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel.help', {
+ defaultMessage: 'Default label used in charts when documents are missing a field.',
+ }),
+ },
+ otherBucket: {
+ types: ['boolean'],
+ default: false,
+ help: i18n.translate('data.search.aggs.buckets.terms.otherBucket.help', {
+ defaultMessage: 'When set to true, groups together any buckets beyond the allowed size',
+ }),
+ },
+ otherBucketLabel: {
+ types: ['string'],
+ default: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel', {
+ defaultMessage: 'Other',
+ }),
+ help: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel.help', {
+ defaultMessage: 'Default label used in charts for documents in the Other bucket',
+ }),
+ },
+ exclude: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.buckets.terms.exclude.help', {
+ defaultMessage: 'Specific bucket values to exclude from results',
+ }),
+ },
+ include: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.buckets.terms.include.help', {
+ defaultMessage: 'Specific bucket values to include in results',
+ }),
+ },
+ json: {
+ types: ['string'],
+ help: i18n.translate('data.search.aggs.buckets.terms.json.help', {
+ defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
+ }),
+ },
+ },
+ fn: (input, args) => {
+ const { id, enabled, schema, ...rest } = args;
+
+ let json;
+ try {
+ json = args.json ? JSON.parse(args.json) : undefined;
+ } catch (e) {
+ throw new Error('Unable to parse json argument string');
+ }
+
+ // Need to spread this object to work around TS bug:
+ // https://github.com/microsoft/TypeScript/issues/15300#issuecomment-436793742
+ const orderAgg = args.orderAgg?.value ? { ...args.orderAgg.value } : undefined;
+
+ return {
+ type: 'agg_type',
+ value: {
+ id,
+ enabled,
+ schema,
+ type: aggName,
+ params: {
+ ...rest,
+ orderAgg,
+ json,
+ },
+ },
+ };
+ },
+});
diff --git a/src/plugins/data/public/search/aggs/index.test.ts b/src/plugins/data/public/search/aggs/index.test.ts
index 419c3fdab1caf..382c5a10c2da5 100644
--- a/src/plugins/data/public/search/aggs/index.test.ts
+++ b/src/plugins/data/public/search/aggs/index.test.ts
@@ -24,6 +24,7 @@ import { isBucketAggType } from './buckets/bucket_agg_type';
import { isMetricAggType } from './metrics/metric_agg_type';
import { QueryStart } from '../../query';
import { FieldFormatsStart } from '../../field_formats';
+import { InternalStartServices } from '../../types';
describe('AggTypesComponent', () => {
const coreSetup = coreMock.createSetup();
@@ -32,10 +33,11 @@ describe('AggTypesComponent', () => {
const aggTypes = getAggTypes({
uiSettings: coreSetup.uiSettings,
query: {} as QueryStart,
- getInternalStartServices: () => ({
- notifications: coreStart.notifications,
- fieldFormats: {} as FieldFormatsStart,
- }),
+ getInternalStartServices: () =>
+ (({
+ notifications: coreStart.notifications,
+ fieldFormats: {} as FieldFormatsStart,
+ } as unknown) as InternalStartServices),
});
const { buckets, metrics } = aggTypes;
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts b/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
index 3868d8f1bcd16..947394c97bdcd 100644
--- a/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
+++ b/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
@@ -36,14 +36,14 @@ const metricAggFilter = [
'!geo_centroid',
];
-const parentPipelineType = i18n.translate(
+export const parentPipelineType = i18n.translate(
'data.search.aggs.metrics.parentPipelineAggregationsSubtypeTitle',
{
defaultMessage: 'Parent Pipeline Aggregations',
}
);
-const parentPipelineAggHelper = {
+export const parentPipelineAggHelper = {
subtype: parentPipelineType,
params() {
return [
@@ -56,13 +56,9 @@ const parentPipelineAggHelper = {
name: 'customMetric',
type: 'agg',
allowedAggs: metricAggFilter,
- makeAgg(termsAgg, state: any) {
- state = state || { type: 'count' };
-
+ makeAgg(termsAgg, state = { type: 'count' }) {
const metricAgg = termsAgg.aggConfigs.createAggConfig(state, { addToAggConfigs: false });
-
metricAgg.id = termsAgg.id + '-metric';
-
return metricAgg;
},
modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart(
@@ -89,5 +85,3 @@ const parentPipelineAggHelper = {
return subAgg ? subAgg.type.getFormat(subAgg) : new (FieldFormat.from(identity))();
},
};
-
-export { parentPipelineAggHelper, parentPipelineType };
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts b/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
index c1d05a39285b7..cee7841a8c3b9 100644
--- a/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
+++ b/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
@@ -43,14 +43,14 @@ const metricAggFilter: string[] = [
];
const bucketAggFilter: string[] = [];
-const siblingPipelineType = i18n.translate(
+export const siblingPipelineType = i18n.translate(
'data.search.aggs.metrics.siblingPipelineAggregationsSubtypeTitle',
{
defaultMessage: 'Sibling pipeline aggregations',
}
);
-const siblingPipelineAggHelper = {
+export const siblingPipelineAggHelper = {
subtype: siblingPipelineType,
params() {
return [
@@ -59,11 +59,9 @@ const siblingPipelineAggHelper = {
type: 'agg',
allowedAggs: bucketAggFilter,
default: null,
- makeAgg(agg: IMetricAggConfig, state: any) {
- state = state || { type: 'date_histogram' };
+ makeAgg(agg: IMetricAggConfig, state = { type: 'date_histogram' }) {
const orderAgg = agg.aggConfigs.createAggConfig(state, { addToAggConfigs: false });
orderAgg.id = agg.id + '-bucket';
-
return orderAgg;
},
modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart(
@@ -76,11 +74,9 @@ const siblingPipelineAggHelper = {
type: 'agg',
allowedAggs: metricAggFilter,
default: null,
- makeAgg(agg: IMetricAggConfig, state: any) {
- state = state || { type: 'count' };
+ makeAgg(agg: IMetricAggConfig, state = { type: 'count' }) {
const orderAgg = agg.aggConfigs.createAggConfig(state, { addToAggConfigs: false });
orderAgg.id = agg.id + '-metric';
-
return orderAgg;
},
modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart(
@@ -98,5 +94,3 @@ const siblingPipelineAggHelper = {
: new (FieldFormat.from(identity))();
},
};
-
-export { siblingPipelineAggHelper, siblingPipelineType };
diff --git a/src/plugins/data/public/search/aggs/metrics/median.test.ts b/src/plugins/data/public/search/aggs/metrics/median.test.ts
index de3ca646ead9e..71c48f04a3ca8 100644
--- a/src/plugins/data/public/search/aggs/metrics/median.test.ts
+++ b/src/plugins/data/public/search/aggs/metrics/median.test.ts
@@ -23,14 +23,16 @@ import { mockAggTypesRegistry } from '../test_helpers';
import { METRIC_TYPES } from './metric_agg_types';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../../types';
describe('AggTypeMetricMedianProvider class', () => {
let aggConfigs: IAggConfigs;
const aggTypesDependencies: MedianMetricAggDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
beforeEach(() => {
diff --git a/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts b/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts
index 3beb92a2fa000..f386034ea8a7b 100644
--- a/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts
+++ b/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts
@@ -25,14 +25,15 @@ import { AggConfigs } from '../agg_configs';
import { mockAggTypesRegistry } from '../test_helpers';
import { IMetricAggConfig, MetricAggType } from './metric_agg_type';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
-import { GetInternalStartServicesFn } from '../../../types';
+import { GetInternalStartServicesFn, InternalStartServices } from '../../../types';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
describe('parent pipeline aggs', function() {
- const getInternalStartServices: GetInternalStartServicesFn = () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- });
+ const getInternalStartServices: GetInternalStartServicesFn = () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices);
const typesRegistry = mockAggTypesRegistry();
diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts
index 1b94ecd602075..7491f15aa3002 100644
--- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts
+++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts
@@ -25,19 +25,28 @@ import {
import { AggConfigs, IAggConfigs } from '../agg_configs';
import { mockAggTypesRegistry } from '../test_helpers';
import { METRIC_TYPES } from './metric_agg_types';
+import { FieldFormatsStart } from '../../../field_formats';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../../types';
describe('AggTypesMetricsPercentileRanksProvider class', function() {
let aggConfigs: IAggConfigs;
- const aggTypesDependencies: PercentileRanksMetricAggDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
- };
+ let fieldFormats: FieldFormatsStart;
+ let aggTypesDependencies: PercentileRanksMetricAggDependencies;
beforeEach(() => {
+ fieldFormats = fieldFormatsServiceMock.createStartContract();
+ fieldFormats.getDefaultInstance = (() => ({
+ convert: (t?: string) => t,
+ })) as any;
+ aggTypesDependencies = {
+ getInternalStartServices: () =>
+ (({
+ fieldFormats,
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
+ };
const typesRegistry = mockAggTypesRegistry([getPercentileRanksMetricAgg(aggTypesDependencies)]);
const field = {
name: 'bytes',
@@ -59,12 +68,7 @@ describe('AggTypesMetricsPercentileRanksProvider class', function() {
type: METRIC_TYPES.PERCENTILE_RANKS,
schema: 'metric',
params: {
- field: {
- displayName: 'bytes',
- format: {
- convert: jest.fn(x => x),
- },
- },
+ field: 'bytes',
customLabel: 'my custom field label',
values: [5000, 10000],
},
diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts b/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts
index 76da2fe3eb62c..76382c01bcc10 100644
--- a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts
+++ b/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts
@@ -27,14 +27,16 @@ import { mockAggTypesRegistry } from '../test_helpers';
import { METRIC_TYPES } from './metric_agg_types';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../../types';
describe('AggTypesMetricsPercentilesProvider class', () => {
let aggConfigs: IAggConfigs;
const aggTypesDependencies: PercentilesMetricAggDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
beforeEach(() => {
@@ -59,12 +61,7 @@ describe('AggTypesMetricsPercentilesProvider class', () => {
type: METRIC_TYPES.PERCENTILES,
schema: 'metric',
params: {
- field: {
- displayName: 'bytes',
- format: {
- convert: jest.fn(x => `${x}th`),
- },
- },
+ field: 'bytes',
customLabel: 'prince',
percents: [95],
},
diff --git a/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts b/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts
index a47aa2c677ade..5e1834d3b4935 100644
--- a/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts
+++ b/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts
@@ -26,14 +26,15 @@ import { AggConfigs } from '../agg_configs';
import { IMetricAggConfig, MetricAggType } from './metric_agg_type';
import { mockAggTypesRegistry } from '../test_helpers';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
-import { GetInternalStartServicesFn } from '../../../types';
+import { GetInternalStartServicesFn, InternalStartServices } from '../../../types';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
describe('sibling pipeline aggs', () => {
- const getInternalStartServices: GetInternalStartServicesFn = () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- });
+ const getInternalStartServices: GetInternalStartServicesFn = () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices);
const typesRegistry = mockAggTypesRegistry();
diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts
index d2370e1fed02c..536764b2bcf0b 100644
--- a/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts
+++ b/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts
@@ -27,13 +27,15 @@ import { mockAggTypesRegistry } from '../test_helpers';
import { METRIC_TYPES } from './metric_agg_types';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../../types';
describe('AggTypeMetricStandardDeviationProvider class', () => {
const aggTypesDependencies: StdDeviationMetricAggDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
const typesRegistry = mockAggTypesRegistry([getStdDeviationMetricAgg(aggTypesDependencies)]);
const getAggConfigs = (customLabel?: string) => {
diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts b/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts
index 142b8e4c83301..617e458cf6243 100644
--- a/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts
+++ b/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts
@@ -25,15 +25,17 @@ import { IMetricAggConfig } from './metric_agg_type';
import { KBN_FIELD_TYPES } from '../../../../common';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../../types';
describe('Top hit metric', () => {
let aggDsl: Record;
let aggConfig: IMetricAggConfig;
const aggTypesDependencies: TopHitMetricAggDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
const init = ({
diff --git a/src/plugins/data/public/search/aggs/param_types/agg.ts b/src/plugins/data/public/search/aggs/param_types/agg.ts
index e5b53020c3159..e3f8c7c922170 100644
--- a/src/plugins/data/public/search/aggs/param_types/agg.ts
+++ b/src/plugins/data/public/search/aggs/param_types/agg.ts
@@ -17,13 +17,13 @@
* under the License.
*/
-import { AggConfig, IAggConfig } from '../agg_config';
+import { AggConfig, IAggConfig, AggConfigSerialized } from '../agg_config';
import { BaseParamType } from './base';
export class AggParamType extends BaseParamType<
TAggConfig
> {
- makeAgg: (agg: TAggConfig, state?: any) => TAggConfig;
+ makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
allowedAggs: string[] = [];
constructor(config: Record) {
@@ -42,17 +42,25 @@ export class AggParamType extends Ba
}
if (!config.serialize) {
this.serialize = (agg: TAggConfig) => {
- return agg.toJSON();
+ return agg.serialize();
};
}
if (!config.deserialize) {
- this.deserialize = (state: unknown, agg?: TAggConfig): TAggConfig => {
+ this.deserialize = (state: AggConfigSerialized, agg?: TAggConfig): TAggConfig => {
if (!agg) {
throw new Error('aggConfig was not provided to AggParamType deserialize function');
}
return this.makeAgg(agg, state);
};
}
+ if (!config.toExpressionAst) {
+ this.toExpressionAst = (agg: TAggConfig) => {
+ if (!agg || !agg.toExpressionAst) {
+ throw new Error('aggConfig was not provided to AggParamType toExpressionAst function');
+ }
+ return agg.toExpressionAst();
+ };
+ }
this.makeAgg = config.makeAgg;
this.valueType = AggConfig;
diff --git a/src/plugins/data/public/search/aggs/param_types/base.ts b/src/plugins/data/public/search/aggs/param_types/base.ts
index 2cbc5866e284d..a6f7e5adea043 100644
--- a/src/plugins/data/public/search/aggs/param_types/base.ts
+++ b/src/plugins/data/public/search/aggs/param_types/base.ts
@@ -17,6 +17,7 @@
* under the License.
*/
+import { ExpressionAstFunction } from 'src/plugins/expressions/public';
import { IAggConfigs } from '../agg_configs';
import { IAggConfig } from '../agg_config';
import { FetchOptions } from '../../fetch';
@@ -37,6 +38,7 @@ export class BaseParamType {
) => void;
serialize: (value: any, aggConfig?: TAggConfig) => any;
deserialize: (value: any, aggConfig?: TAggConfig) => any;
+ toExpressionAst?: (value: any) => ExpressionAstFunction | undefined;
options: any[];
valueType?: any;
@@ -77,6 +79,7 @@ export class BaseParamType {
this.write = config.write || defaultWrite;
this.serialize = config.serialize;
this.deserialize = config.deserialize;
+ this.toExpressionAst = config.toExpressionAst;
this.options = config.options;
this.modifyAggConfigOnSearchRequestStart =
config.modifyAggConfigOnSearchRequestStart || function() {};
diff --git a/src/plugins/data/public/search/aggs/param_types/field.test.ts b/src/plugins/data/public/search/aggs/param_types/field.test.ts
index ea7931130b84a..2c51d9709f906 100644
--- a/src/plugins/data/public/search/aggs/param_types/field.test.ts
+++ b/src/plugins/data/public/search/aggs/param_types/field.test.ts
@@ -23,13 +23,15 @@ import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../../../common';
import { IAggConfig } from '../agg_config';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
+import { InternalStartServices } from '../../../types';
describe('Field', () => {
const fieldParamTypeDependencies: FieldParamTypeDependencies = {
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ } as unknown) as InternalStartServices),
};
const indexPattern = {
diff --git a/src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts b/src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts
new file mode 100644
index 0000000000000..cb0e37c0296d7
--- /dev/null
+++ b/src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts
@@ -0,0 +1,49 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { mapValues } from 'lodash';
+import {
+ AnyExpressionFunctionDefinition,
+ ExpressionFunctionDefinition,
+ ExecutionContext,
+} from '../../../../../../plugins/expressions/public';
+
+/**
+ * Takes a function spec and passes in default args,
+ * overriding with any provided args.
+ *
+ * Similar to the test helper used in Expressions & Canvas,
+ * however in this case we are ignoring the input & execution
+ * context, as they are not applicable to the agg type
+ * expression functions.
+ */
+export const functionWrapper = (spec: T) => {
+ const defaultArgs = mapValues(spec.args, argSpec => argSpec.default);
+ return (
+ args: T extends ExpressionFunctionDefinition<
+ infer Name,
+ infer Input,
+ infer Arguments,
+ infer Output,
+ infer Context
+ >
+ ? Arguments
+ : never
+ ) => spec.fn(null, { ...defaultArgs, ...args }, {} as ExecutionContext);
+};
diff --git a/src/plugins/data/public/search/aggs/test_helpers/index.ts b/src/plugins/data/public/search/aggs/test_helpers/index.ts
index 131f921586144..63f8ae0ce5f58 100644
--- a/src/plugins/data/public/search/aggs/test_helpers/index.ts
+++ b/src/plugins/data/public/search/aggs/test_helpers/index.ts
@@ -17,5 +17,6 @@
* under the License.
*/
+export { functionWrapper } from './function_wrapper';
export { mockAggTypesRegistry } from './mock_agg_types_registry';
export { mockDataServices } from './mock_data_services';
diff --git a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts b/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts
index 2383affa2a8c5..3ff2fbf35ad7e 100644
--- a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts
+++ b/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts
@@ -17,7 +17,6 @@
* under the License.
*/
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { coreMock, notificationServiceMock } from '../../../../../../../src/core/public/mocks';
import { AggTypesRegistry, AggTypesRegistryStart } from '../agg_types_registry';
import { getAggTypes } from '../agg_types';
@@ -25,6 +24,7 @@ import { BucketAggType } from '../buckets/bucket_agg_type';
import { MetricAggType } from '../metrics/metric_agg_type';
import { queryServiceMock } from '../../../query/mocks';
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
+import { InternalStartServices } from '../../../types';
/**
* Testing utility which creates a new instance of AggTypesRegistry,
@@ -53,14 +53,19 @@ export function mockAggTypesRegistry | MetricAggTyp
}
});
} else {
- const core = coreMock.createSetup();
+ const coreSetup = coreMock.createSetup();
+ const coreStart = coreMock.createStart();
+
const aggTypes = getAggTypes({
- uiSettings: core.uiSettings,
+ uiSettings: coreSetup.uiSettings,
query: queryServiceMock.createSetupContract(),
- getInternalStartServices: () => ({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- notifications: notificationServiceMock.createStartContract(),
- }),
+ getInternalStartServices: () =>
+ (({
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ notifications: notificationServiceMock.createStartContract(),
+ uiSettings: coreStart.uiSettings,
+ injectedMetadata: coreStart.injectedMetadata,
+ } as unknown) as InternalStartServices),
});
aggTypes.buckets.forEach(type => registrySetup.registerBucket(type));
diff --git a/src/plugins/data/public/search/aggs/types.ts b/src/plugins/data/public/search/aggs/types.ts
index 4b2b1620ad1d3..95a7a45013567 100644
--- a/src/plugins/data/public/search/aggs/types.ts
+++ b/src/plugins/data/public/search/aggs/types.ts
@@ -19,21 +19,23 @@
import { IndexPattern } from '../../index_patterns';
import {
+ AggConfig,
+ AggConfigSerialized,
+ AggConfigs,
+ AggParamsTerms,
AggType,
+ aggTypeFieldFilters,
AggTypesRegistrySetup,
AggTypesRegistryStart,
- AggConfig,
- AggConfigs,
CreateAggConfigParams,
FieldParamType,
getCalculateAutoTimeExpression,
MetricAggType,
- aggTypeFieldFilters,
parentPipelineAggHelper,
siblingPipelineAggHelper,
} from './';
-export { IAggConfig } from './agg_config';
+export { IAggConfig, AggConfigSerialized } from './agg_config';
export { CreateAggConfigParams, IAggConfigs } from './agg_configs';
export { IAggType } from './agg_type';
export { AggParam, AggParamOption } from './agg_params';
@@ -70,3 +72,25 @@ export interface SearchAggsStart {
) => InstanceType;
types: AggTypesRegistryStart;
}
+
+/** @internal */
+export interface AggExpressionType {
+ type: 'agg_type';
+ value: AggConfigSerialized;
+}
+
+/** @internal */
+export type AggExpressionFunctionArgs<
+ Name extends keyof AggParamsMapping
+> = AggParamsMapping[Name] & Pick;
+
+/**
+ * A global list of the param interfaces for each agg type.
+ * For now this is internal, but eventually we will probably
+ * want to make it public.
+ *
+ * @internal
+ */
+export interface AggParamsMapping {
+ terms: AggParamsTerms;
+}
diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts
index 2341f4fe447db..087b83127079f 100644
--- a/src/plugins/data/public/search/expressions/esaggs.ts
+++ b/src/plugins/data/public/search/expressions/esaggs.ts
@@ -30,7 +30,7 @@ import { PersistedState } from '../../../../../plugins/visualizations/public';
import { Adapters } from '../../../../../plugins/inspector/public';
import { IAggConfigs } from '../aggs';
-import { ISearchSource, SearchSource } from '../search_source';
+import { ISearchSource } from '../search_source';
import { tabifyAggResponse } from '../tabify';
import { Filter, Query, serializeFieldFormat, TimeRange } from '../../../common';
import { FilterManager, getTime } from '../../query';
@@ -253,7 +253,8 @@ export const esaggs = (): ExpressionFunctionDefinition {
test('Passes the additional arguments it is given to the search strategy', () => {
const searchRequests = [{ _searchStrategyId: 0 }];
- const args = { searchService: {}, config: {}, esShardTimeout: 0 } as FetchHandlers;
+ const args = { legacySearchService: {}, config: {}, esShardTimeout: 0 } as FetchHandlers;
callClient(searchRequests, [], args);
diff --git a/src/plugins/data/public/search/legacy/default_search_strategy.test.ts b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts
index 835b02b3cd5c7..9e3d65a69bf02 100644
--- a/src/plugins/data/public/search/legacy/default_search_strategy.test.ts
+++ b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts
@@ -62,10 +62,10 @@ describe('defaultSearchStrategy', function() {
},
],
esShardTimeout: 0,
- searchService,
+ legacySearchService: searchService.__LEGACY,
};
- es = searchArgs.searchService.__LEGACY.esClient;
+ es = searchArgs.legacySearchService.esClient;
});
test('does not send max_concurrent_shard_requests by default', async () => {
diff --git a/src/plugins/data/public/search/legacy/default_search_strategy.ts b/src/plugins/data/public/search/legacy/default_search_strategy.ts
index 1552410f9090c..3216803dcbfa2 100644
--- a/src/plugins/data/public/search/legacy/default_search_strategy.ts
+++ b/src/plugins/data/public/search/legacy/default_search_strategy.ts
@@ -32,11 +32,11 @@ export const defaultSearchStrategy: SearchStrategyProvider = {
function msearch({
searchRequests,
- searchService,
+ legacySearchService,
config,
esShardTimeout,
}: SearchStrategySearchParams) {
- const es = searchService.__LEGACY.esClient;
+ const es = legacySearchService.esClient;
const inlineRequests = searchRequests.map(({ index, body, search_type: searchType }) => {
const inlineHeader = {
index: index.title || index,
diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts
index cb1c625a72959..dd196074c8173 100644
--- a/src/plugins/data/public/search/mocks.ts
+++ b/src/plugins/data/public/search/mocks.ts
@@ -20,20 +20,19 @@
import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks';
import { AggTypeFieldFilters } from './aggs/param_types/filter';
import { ISearchStart } from './types';
+import { searchSourceMock, createSearchSourceMock } from './search_source/mocks';
-export * from './search_source/mocks';
-
-export const searchSetupMock = {
+const searchSetupMock = {
aggs: searchAggsSetupMock(),
registerSearchStrategyContext: jest.fn(),
registerSearchStrategyProvider: jest.fn(),
};
-export const searchStartMock: jest.Mocked = {
+const searchStartMock: jest.Mocked = {
aggs: searchAggsStartMock(),
setInterceptor: jest.fn(),
search: jest.fn(),
- createSearchSource: jest.fn(),
+ searchSource: searchSourceMock,
__LEGACY: {
AggConfig: jest.fn() as any,
AggType: jest.fn(),
@@ -48,3 +47,5 @@ export const searchStartMock: jest.Mocked = {
},
},
};
+
+export { searchSetupMock, searchStartMock, createSearchSourceMock };
diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts
index 19308dd387d3a..b1f7925bec4bb 100644
--- a/src/plugins/data/public/search/search_service.test.ts
+++ b/src/plugins/data/public/search/search_service.test.ts
@@ -18,9 +18,10 @@
*/
import { coreMock } from '../../../../core/public/mocks';
+import { CoreSetup } from '../../../../core/public';
+import { expressionsPluginMock } from '../../../../plugins/expressions/public/mocks';
import { SearchService } from './search_service';
-import { CoreSetup } from '../../../../core/public';
describe('Search service', () => {
let searchService: SearchService;
@@ -35,6 +36,7 @@ describe('Search service', () => {
it('exposes proper contract', async () => {
const setup = searchService.setup(mockCoreSetup, {
packageInfo: { version: '8' },
+ expressions: expressionsPluginMock.createSetupContract(),
} as any);
expect(setup).toHaveProperty('registerSearchStrategyProvider');
});
diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts
index 916278a96659b..b59524baa9fa7 100644
--- a/src/plugins/data/public/search/search_service.ts
+++ b/src/plugins/data/public/search/search_service.ts
@@ -18,20 +18,27 @@
*/
import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public';
+import { ExpressionsSetup } from '../../../../plugins/expressions/public';
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
+import {
+ createSearchSourceFromJSON,
+ SearchSource,
+ SearchSourceDependencies,
+ SearchSourceFields,
+} from './search_source';
import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types';
import { TStrategyTypes } from './strategy_types';
import { getEsClient, LegacyApiCaller } from './legacy';
import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search';
import { esSearchStrategyProvider } from './es_search';
import { IndexPatternsContract } from '../index_patterns/index_patterns';
-import { createSearchSource } from './search_source';
import { QuerySetup } from '../query';
import { GetInternalStartServicesFn } from '../types';
import { SearchInterceptor } from './search_interceptor';
import {
getAggTypes,
+ getAggTypesFunctions,
AggType,
AggTypesRegistry,
AggConfig,
@@ -43,18 +50,19 @@ import {
parentPipelineAggHelper,
siblingPipelineAggHelper,
} from './aggs';
-
import { FieldFormatsStart } from '../field_formats';
+import { ISearchGeneric } from './i_search';
interface SearchServiceSetupDependencies {
+ expressions: ExpressionsSetup;
+ getInternalStartServices: GetInternalStartServicesFn;
packageInfo: PackageInfo;
query: QuerySetup;
- getInternalStartServices: GetInternalStartServicesFn;
}
-interface SearchStartDependencies {
- fieldFormats: FieldFormatsStart;
+interface SearchServiceStartDependencies {
indexPatterns: IndexPatternsContract;
+ fieldFormats: FieldFormatsStart;
}
/**
@@ -92,22 +100,27 @@ export class SearchService implements Plugin {
public setup(
core: CoreSetup,
- { packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies
+ { expressions, packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies
): ISearchSetup {
this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo);
this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider);
this.registerSearchStrategyProvider(ES_SEARCH_STRATEGY, esSearchStrategyProvider);
const aggTypesSetup = this.aggTypesRegistry.setup();
+
+ // register each agg type
const aggTypes = getAggTypes({
query,
uiSettings: core.uiSettings,
getInternalStartServices,
});
-
aggTypes.buckets.forEach(b => aggTypesSetup.registerBucket(b));
aggTypes.metrics.forEach(m => aggTypesSetup.registerMetric(m));
+ // register expression functions for each agg type
+ const aggFunctions = getAggTypesFunctions();
+ aggFunctions.forEach(fn => expressions.registerFunction(fn));
+
return {
aggs: {
calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings),
@@ -117,10 +130,7 @@ export class SearchService implements Plugin {
};
}
- public start(
- core: CoreStart,
- { fieldFormats, indexPatterns }: SearchStartDependencies
- ): ISearchStart {
+ public start(core: CoreStart, dependencies: SearchServiceStartDependencies): ISearchStart {
/**
* A global object that intercepts all searches and provides convenience methods for cancelling
* all pending search requests, as well as getting the number of pending search requests.
@@ -135,40 +145,54 @@ export class SearchService implements Plugin {
const aggTypesStart = this.aggTypesRegistry.start();
+ const search: ISearchGeneric = (request, options, strategyName) => {
+ const strategyProvider = this.getSearchStrategy(strategyName || DEFAULT_SEARCH_STRATEGY);
+ const searchStrategy = strategyProvider({
+ core,
+ getSearchStrategy: this.getSearchStrategy,
+ });
+ return this.searchInterceptor.search(searchStrategy.search as any, request, options);
+ };
+
+ const legacySearch = {
+ esClient: this.esClient!,
+ AggConfig,
+ AggType,
+ aggTypeFieldFilters,
+ FieldParamType,
+ MetricAggType,
+ parentPipelineAggHelper,
+ siblingPipelineAggHelper,
+ };
+
+ const searchSourceDependencies: SearchSourceDependencies = {
+ uiSettings: core.uiSettings,
+ injectedMetadata: core.injectedMetadata,
+ search,
+ legacySearch,
+ };
+
return {
aggs: {
calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings),
createAggConfigs: (indexPattern, configStates = [], schemas) => {
return new AggConfigs(indexPattern, configStates, {
+ fieldFormats: dependencies.fieldFormats,
typesRegistry: aggTypesStart,
- fieldFormats,
});
},
types: aggTypesStart,
},
- search: (request, options, strategyName) => {
- const strategyProvider = this.getSearchStrategy(strategyName || DEFAULT_SEARCH_STRATEGY);
- const { search } = strategyProvider({
- core,
- getSearchStrategy: this.getSearchStrategy,
- });
- return this.searchInterceptor.search(search as any, request, options);
+ search,
+ searchSource: {
+ create: (fields?: SearchSourceFields) => new SearchSource(fields, searchSourceDependencies),
+ fromJSON: createSearchSourceFromJSON(dependencies.indexPatterns, searchSourceDependencies),
},
setInterceptor: (searchInterceptor: SearchInterceptor) => {
// TODO: should an intercepror have a destroy method?
this.searchInterceptor = searchInterceptor;
},
- createSearchSource: createSearchSource(indexPatterns),
- __LEGACY: {
- esClient: this.esClient!,
- AggConfig,
- AggType,
- aggTypeFieldFilters,
- FieldParamType,
- MetricAggType,
- parentPipelineAggHelper,
- siblingPipelineAggHelper,
- },
+ __LEGACY: legacySearch,
};
}
diff --git a/src/plugins/data/public/search/search_source/create_search_source.test.ts b/src/plugins/data/public/search/search_source/create_search_source.test.ts
index d49ce5a0d11f8..efa63b0722e28 100644
--- a/src/plugins/data/public/search/search_source/create_search_source.test.ts
+++ b/src/plugins/data/public/search/search_source/create_search_source.test.ts
@@ -16,30 +16,43 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { createSearchSource as createSearchSourceFactory } from './create_search_source';
+import { createSearchSourceFromJSON } from './create_search_source';
import { IIndexPattern } from '../../../common/index_patterns';
import { IndexPatternsContract } from '../../index_patterns/index_patterns';
import { Filter } from '../../../common/es_query/filters';
+import { coreMock } from '../../../../../core/public/mocks';
+import { dataPluginMock } from '../../mocks';
-describe('createSearchSource', function() {
- let createSearchSource: ReturnType;
+describe('createSearchSource', () => {
const indexPatternMock: IIndexPattern = {} as IIndexPattern;
let indexPatternContractMock: jest.Mocked;
+ let dependencies: any;
+ let createSearchSource: ReturnType;
beforeEach(() => {
+ const core = coreMock.createStart();
+ const data = dataPluginMock.createStartContract();
+
+ dependencies = {
+ searchService: data.search,
+ uiSettings: core.uiSettings,
+ injectedMetadata: core.injectedMetadata,
+ };
+
indexPatternContractMock = ({
get: jest.fn().mockReturnValue(Promise.resolve(indexPatternMock)),
} as unknown) as jest.Mocked;
- createSearchSource = createSearchSourceFactory(indexPatternContractMock);
+
+ createSearchSource = createSearchSourceFromJSON(indexPatternContractMock, dependencies);
});
- it('should fail if JSON is invalid', () => {
+ test('should fail if JSON is invalid', () => {
expect(createSearchSource('{', [])).rejects.toThrow();
expect(createSearchSource('0', [])).rejects.toThrow();
expect(createSearchSource('"abcdefg"', [])).rejects.toThrow();
});
- it('should set fields', async () => {
+ test('should set fields', async () => {
const searchSource = await createSearchSource(
JSON.stringify({
highlightAll: true,
@@ -50,6 +63,7 @@ describe('createSearchSource', function() {
}),
[]
);
+
expect(searchSource.getOwnField('highlightAll')).toBe(true);
expect(searchSource.getOwnField('query')).toEqual({
query: '',
@@ -57,7 +71,7 @@ describe('createSearchSource', function() {
});
});
- it('should resolve referenced index pattern', async () => {
+ test('should resolve referenced index pattern', async () => {
const searchSource = await createSearchSource(
JSON.stringify({
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
@@ -70,11 +84,12 @@ describe('createSearchSource', function() {
},
]
);
+
expect(indexPatternContractMock.get).toHaveBeenCalledWith('123-456');
expect(searchSource.getOwnField('index')).toBe(indexPatternMock);
});
- it('should set filters and resolve referenced index patterns', async () => {
+ test('should set filters and resolve referenced index patterns', async () => {
const searchSource = await createSearchSource(
JSON.stringify({
filter: [
@@ -110,6 +125,7 @@ describe('createSearchSource', function() {
]
);
const filters = searchSource.getOwnField('filter') as Filter[];
+
expect(filters[0]).toMatchInlineSnapshot(`
Object {
"$state": Object {
@@ -135,7 +151,7 @@ describe('createSearchSource', function() {
`);
});
- it('should migrate legacy queries on the fly', async () => {
+ test('should migrate legacy queries on the fly', async () => {
const searchSource = await createSearchSource(
JSON.stringify({
highlightAll: true,
@@ -143,6 +159,7 @@ describe('createSearchSource', function() {
}),
[]
);
+
expect(searchSource.getOwnField('query')).toEqual({
query: 'a:b',
language: 'lucene',
diff --git a/src/plugins/data/public/search/search_source/create_search_source.ts b/src/plugins/data/public/search/search_source/create_search_source.ts
index 35b7ac4eb9762..cc98f433b3a03 100644
--- a/src/plugins/data/public/search/search_source/create_search_source.ts
+++ b/src/plugins/data/public/search/search_source/create_search_source.ts
@@ -16,11 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-import _ from 'lodash';
+import { transform, defaults, isFunction } from 'lodash';
import { SavedObjectReference } from 'kibana/public';
import { migrateLegacyQuery } from '../../../../kibana_legacy/public';
import { InvalidJSONProperty } from '../../../../kibana_utils/public';
-import { SearchSource } from './search_source';
+import { SearchSourceDependencies, SearchSource, ISearchSource } from './search_source';
import { IndexPatternsContract } from '../../index_patterns/index_patterns';
import { SearchSourceFields } from './types';
@@ -38,12 +38,16 @@ import { SearchSourceFields } from './types';
* returned by `serializeSearchSource` and `references`, a list of references including the ones
* returned by `serializeSearchSource`.
*
+ *
* @public */
-export const createSearchSource = (indexPatterns: IndexPatternsContract) => async (
+export const createSearchSourceFromJSON = (
+ indexPatterns: IndexPatternsContract,
+ searchSourceDependencies: SearchSourceDependencies
+) => async (
searchSourceJson: string,
references: SavedObjectReference[]
-) => {
- const searchSource = new SearchSource();
+): Promise => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
// if we have a searchSource, set its values based on the searchSourceJson field
let searchSourceValues: Record;
@@ -90,17 +94,17 @@ export const createSearchSource = (indexPatterns: IndexPatternsContract) => asyn
}
const searchSourceFields = searchSource.getFields();
- const fnProps = _.transform(
+ const fnProps = transform(
searchSourceFields,
function(dynamic, val, name) {
- if (_.isFunction(val) && name) dynamic[name] = val;
+ if (isFunction(val) && name) dynamic[name] = val;
},
{}
);
// This assignment might hide problems because the type of values passed from the parsed JSON
// might not fit the SearchSourceFields interface.
- const newFields: SearchSourceFields = _.defaults(searchSourceValues, fnProps);
+ const newFields: SearchSourceFields = defaults(searchSourceValues, fnProps);
searchSource.setFields(newFields);
const query = searchSource.getOwnField('query');
diff --git a/src/plugins/data/public/search/search_source/index.ts b/src/plugins/data/public/search/search_source/index.ts
index 0e9f530d0968a..9c4106b2dc616 100644
--- a/src/plugins/data/public/search/search_source/index.ts
+++ b/src/plugins/data/public/search/search_source/index.ts
@@ -17,6 +17,6 @@
* under the License.
*/
-export * from './search_source';
-export { createSearchSource } from './create_search_source';
+export { SearchSource, ISearchSource, SearchSourceDependencies } from './search_source';
+export { createSearchSourceFromJSON } from './create_search_source';
export { SortDirection, EsQuerySortValue, SearchSourceFields } from './types';
diff --git a/src/plugins/data/public/search/search_source/mocks.ts b/src/plugins/data/public/search/search_source/mocks.ts
index 1ef7c1187a9e0..157331ea87bb0 100644
--- a/src/plugins/data/public/search/search_source/mocks.ts
+++ b/src/plugins/data/public/search/search_source/mocks.ts
@@ -17,9 +17,15 @@
* under the License.
*/
-import { ISearchSource } from './search_source';
+import {
+ injectedMetadataServiceMock,
+ uiSettingsServiceMock,
+} from '../../../../../core/public/mocks';
-export const searchSourceMock: MockedKeys = {
+import { ISearchSource, SearchSource } from './search_source';
+import { SearchSourceFields } from './types';
+
+export const searchSourceInstanceMock: MockedKeys = {
setPreferredSearchStrategyId: jest.fn(),
setFields: jest.fn().mockReturnThis(),
setField: jest.fn().mockReturnThis(),
@@ -39,3 +45,21 @@ export const searchSourceMock: MockedKeys = {
history: [],
serialize: jest.fn(),
};
+
+export const searchSourceMock = {
+ create: jest.fn().mockReturnValue(searchSourceInstanceMock),
+ fromJSON: jest.fn().mockReturnValue(searchSourceInstanceMock),
+};
+
+export const createSearchSourceMock = (fields?: SearchSourceFields) =>
+ new SearchSource(fields, {
+ search: jest.fn(),
+ legacySearch: {
+ esClient: {
+ search: jest.fn(),
+ msearch: jest.fn(),
+ },
+ },
+ uiSettings: uiSettingsServiceMock.createStartContract(),
+ injectedMetadata: injectedMetadataServiceMock.createStartContract(),
+ });
diff --git a/src/plugins/data/public/search/search_source/search_source.test.ts b/src/plugins/data/public/search/search_source/search_source.test.ts
index 6e878844664ad..7783e65889a12 100644
--- a/src/plugins/data/public/search/search_source/search_source.test.ts
+++ b/src/plugins/data/public/search/search_source/search_source.test.ts
@@ -16,28 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
-
+import { Observable } from 'rxjs';
import { SearchSource } from './search_source';
import { IndexPattern, SortDirection } from '../..';
-import { mockDataServices } from '../aggs/test_helpers';
-import { setSearchService } from '../../services';
-import { searchStartMock } from '../mocks';
import { fetchSoon } from '../legacy';
-import { CoreStart } from 'kibana/public';
-import { Observable } from 'rxjs';
-
-// Setup search service mock
-searchStartMock.search = jest.fn(() => {
- return new Observable(subscriber => {
- setTimeout(() => {
- subscriber.next({
- rawResponse: '',
- });
- subscriber.complete();
- }, 100);
- });
-}) as any;
-setSearchService(searchStartMock);
+import { IUiSettingsClient } from '../../../../../core/public';
+import { dataPluginMock } from '../../../../data/public/mocks';
+import { coreMock } from '../../../../../core/public/mocks';
jest.mock('../legacy', () => ({
fetchSoon: jest.fn().mockResolvedValue({}),
@@ -48,48 +33,70 @@ const getComputedFields = () => ({
scriptFields: [],
docvalueFields: [],
});
+
const mockSource = { excludes: ['foo-*'] };
const mockSource2 = { excludes: ['bar-*'] };
+
const indexPattern = ({
title: 'foo',
getComputedFields,
getSourceFiltering: () => mockSource,
} as unknown) as IndexPattern;
+
const indexPattern2 = ({
title: 'foo',
getComputedFields,
getSourceFiltering: () => mockSource2,
} as unknown) as IndexPattern;
-describe('SearchSource', function() {
- let uiSettingsMock: jest.Mocked;
+describe('SearchSource', () => {
+ let mockSearchMethod: any;
+ let searchSourceDependencies: any;
+
beforeEach(() => {
- const { core } = mockDataServices();
- uiSettingsMock = core.uiSettings;
- jest.clearAllMocks();
+ const core = coreMock.createStart();
+ const data = dataPluginMock.createStartContract();
+
+ mockSearchMethod = jest.fn(() => {
+ return new Observable(subscriber => {
+ setTimeout(() => {
+ subscriber.next({
+ rawResponse: '',
+ });
+ subscriber.complete();
+ }, 100);
+ });
+ });
+
+ searchSourceDependencies = {
+ search: mockSearchMethod,
+ legacySearch: data.search.__LEGACY,
+ injectedMetadata: core.injectedMetadata,
+ uiSettings: core.uiSettings,
+ };
});
- describe('#setField()', function() {
- it('sets the value for the property', function() {
- const searchSource = new SearchSource();
+ describe('#setField()', () => {
+ test('sets the value for the property', () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
searchSource.setField('aggs', 5);
expect(searchSource.getField('aggs')).toBe(5);
});
});
- describe('#getField()', function() {
- it('gets the value for the property', function() {
- const searchSource = new SearchSource();
+ describe('#getField()', () => {
+ test('gets the value for the property', () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
searchSource.setField('aggs', 5);
expect(searchSource.getField('aggs')).toBe(5);
});
});
- describe(`#setField('index')`, function() {
- describe('auto-sourceFiltering', function() {
- describe('new index pattern assigned', function() {
- it('generates a searchSource filter', async function() {
- const searchSource = new SearchSource();
+ describe(`#setField('index')`, () => {
+ describe('auto-sourceFiltering', () => {
+ describe('new index pattern assigned', () => {
+ test('generates a searchSource filter', async () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
expect(searchSource.getField('index')).toBe(undefined);
expect(searchSource.getField('source')).toBe(undefined);
searchSource.setField('index', indexPattern);
@@ -98,8 +105,8 @@ describe('SearchSource', function() {
expect(request._source).toBe(mockSource);
});
- it('removes created searchSource filter on removal', async function() {
- const searchSource = new SearchSource();
+ test('removes created searchSource filter on removal', async () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
searchSource.setField('index', indexPattern);
searchSource.setField('index', undefined);
const request = await searchSource.getSearchRequestBody();
@@ -107,9 +114,9 @@ describe('SearchSource', function() {
});
});
- describe('new index pattern assigned over another', function() {
- it('replaces searchSource filter with new', async function() {
- const searchSource = new SearchSource();
+ describe('new index pattern assigned over another', () => {
+ test('replaces searchSource filter with new', async () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
searchSource.setField('index', indexPattern);
searchSource.setField('index', indexPattern2);
expect(searchSource.getField('index')).toBe(indexPattern2);
@@ -117,8 +124,8 @@ describe('SearchSource', function() {
expect(request._source).toBe(mockSource2);
});
- it('removes created searchSource filter on removal', async function() {
- const searchSource = new SearchSource();
+ test('removes created searchSource filter on removal', async () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
searchSource.setField('index', indexPattern);
searchSource.setField('index', indexPattern2);
searchSource.setField('index', undefined);
@@ -130,8 +137,8 @@ describe('SearchSource', function() {
});
describe('#onRequestStart()', () => {
- it('should be called when starting a request', async () => {
- const searchSource = new SearchSource({ index: indexPattern });
+ test('should be called when starting a request', async () => {
+ const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
const fn = jest.fn();
searchSource.onRequestStart(fn);
const options = {};
@@ -139,9 +146,9 @@ describe('SearchSource', function() {
expect(fn).toBeCalledWith(searchSource, options);
});
- it('should not be called on parent searchSource', async () => {
- const parent = new SearchSource();
- const searchSource = new SearchSource({ index: indexPattern });
+ test('should not be called on parent searchSource', async () => {
+ const parent = new SearchSource({}, searchSourceDependencies);
+ const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
const fn = jest.fn();
searchSource.onRequestStart(fn);
@@ -154,9 +161,12 @@ describe('SearchSource', function() {
expect(parentFn).not.toBeCalled();
});
- it('should be called on parent searchSource if callParentStartHandlers is true', async () => {
- const parent = new SearchSource();
- const searchSource = new SearchSource({ index: indexPattern }).setParent(parent, {
+ test('should be called on parent searchSource if callParentStartHandlers is true', async () => {
+ const parent = new SearchSource({}, searchSourceDependencies);
+ const searchSource = new SearchSource(
+ { index: indexPattern },
+ searchSourceDependencies
+ ).setParent(parent, {
callParentStartHandlers: true,
});
@@ -174,19 +184,21 @@ describe('SearchSource', function() {
describe('#legacy fetch()', () => {
beforeEach(() => {
- uiSettingsMock.get.mockImplementation(() => {
- return true; // batchSearches = true
- });
- });
+ const core = coreMock.createStart();
- afterEach(() => {
- uiSettingsMock.get.mockImplementation(() => {
- return false; // batchSearches = false
- });
+ searchSourceDependencies = {
+ ...searchSourceDependencies,
+ uiSettings: {
+ ...core.uiSettings,
+ get: jest.fn(() => {
+ return true; // batchSearches = true
+ }),
+ } as IUiSettingsClient,
+ };
});
- it('should call msearch', async () => {
- const searchSource = new SearchSource({ index: indexPattern });
+ test('should call msearch', async () => {
+ const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
const options = {};
await searchSource.fetch(options);
expect(fetchSoon).toBeCalledTimes(1);
@@ -194,18 +206,19 @@ describe('SearchSource', function() {
});
describe('#search service fetch()', () => {
- it('should call msearch', async () => {
- const searchSource = new SearchSource({ index: indexPattern });
+ test('should call msearch', async () => {
+ const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
const options = {};
+
await searchSource.fetch(options);
- expect(searchStartMock.search).toBeCalledTimes(1);
+ expect(mockSearchMethod).toBeCalledTimes(1);
});
});
- describe('#serialize', function() {
- it('should reference index patterns', () => {
+ describe('#serialize', () => {
+ test('should reference index patterns', () => {
const indexPattern123 = { id: '123' } as IndexPattern;
- const searchSource = new SearchSource();
+ const searchSource = new SearchSource({}, searchSourceDependencies);
searchSource.setField('index', indexPattern123);
const { searchSourceJSON, references } = searchSource.serialize();
expect(references[0].id).toEqual('123');
@@ -213,8 +226,8 @@ describe('SearchSource', function() {
expect(JSON.parse(searchSourceJSON).indexRefName).toEqual(references[0].name);
});
- it('should add other fields', () => {
- const searchSource = new SearchSource();
+ test('should add other fields', () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
searchSource.setField('highlightAll', true);
searchSource.setField('from', 123456);
const { searchSourceJSON } = searchSource.serialize();
@@ -222,8 +235,8 @@ describe('SearchSource', function() {
expect(JSON.parse(searchSourceJSON).from).toEqual(123456);
});
- it('should omit sort and size', () => {
- const searchSource = new SearchSource();
+ test('should omit sort and size', () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
searchSource.setField('highlightAll', true);
searchSource.setField('from', 123456);
searchSource.setField('sort', { field: SortDirection.asc });
@@ -232,8 +245,8 @@ describe('SearchSource', function() {
expect(Object.keys(JSON.parse(searchSourceJSON))).toEqual(['highlightAll', 'from']);
});
- it('should serialize filters', () => {
- const searchSource = new SearchSource();
+ test('should serialize filters', () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
const filter = [
{
query: 'query',
@@ -249,8 +262,8 @@ describe('SearchSource', function() {
expect(JSON.parse(searchSourceJSON).filter).toEqual(filter);
});
- it('should reference index patterns in filters separately from index field', () => {
- const searchSource = new SearchSource();
+ test('should reference index patterns in filters separately from index field', () => {
+ const searchSource = new SearchSource({}, searchSourceDependencies);
const indexPattern123 = { id: '123' } as IndexPattern;
searchSource.setField('index', indexPattern123);
const filter = [
diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts
index 9d2bb889953cf..091a27a6f418d 100644
--- a/src/plugins/data/public/search/search_source/search_source.ts
+++ b/src/plugins/data/public/search/search_source/search_source.ts
@@ -69,34 +69,45 @@
* `appSearchSource`.
*/
-import _ from 'lodash';
+import { uniqueId, uniq, extend, pick, difference, set, omit, keys, isFunction } from 'lodash';
import { map } from 'rxjs/operators';
-import { SavedObjectReference } from 'kibana/public';
+import { CoreStart, SavedObjectReference } from 'kibana/public';
import { normalizeSortRequest } from './normalize_sort_request';
import { filterDocvalueFields } from './filter_docvalue_fields';
import { fieldWildcardFilter } from '../../../../kibana_utils/public';
-import { IIndexPattern, SearchRequest } from '../..';
+import { IIndexPattern, ISearchGeneric, SearchRequest } from '../..';
import { SearchSourceOptions, SearchSourceFields } from './types';
import { FetchOptions, RequestFailure, getSearchParams, handleResponse } from '../fetch';
-import { getSearchService, getUiSettings, getInjectedMetadata } from '../../services';
import { getEsQueryConfig, buildEsQuery, Filter } from '../../../common';
import { getHighlightRequest } from '../../../common/field_formats';
import { fetchSoon } from '../legacy';
+import { ISearchStartLegacy } from '../types';
-export type ISearchSource = Pick;
+export interface SearchSourceDependencies {
+ uiSettings: CoreStart['uiSettings'];
+ search: ISearchGeneric;
+ legacySearch: ISearchStartLegacy;
+ injectedMetadata: CoreStart['injectedMetadata'];
+}
+/** @public **/
export class SearchSource {
- private id: string = _.uniqueId('data_source');
+ private id: string = uniqueId('data_source');
private searchStrategyId?: string;
private parent?: SearchSource;
private requestStartHandlers: Array<
- (searchSource: ISearchSource, options?: FetchOptions) => Promise
+ (searchSource: SearchSource, options?: FetchOptions) => Promise
> = [];
private inheritOptions: SearchSourceOptions = {};
public history: SearchRequest[] = [];
+ private fields: SearchSourceFields;
+ private readonly dependencies: SearchSourceDependencies;
- constructor(private fields: SearchSourceFields = {}) {}
+ constructor(fields: SearchSourceFields = {}, dependencies: SearchSourceDependencies) {
+ this.fields = fields;
+ this.dependencies = dependencies;
+ }
/** ***
* PUBLIC API
@@ -147,11 +158,11 @@ export class SearchSource {
}
create() {
- return new SearchSource();
+ return new SearchSource({}, this.dependencies);
}
createCopy() {
- const newSearchSource = new SearchSource();
+ const newSearchSource = new SearchSource({}, this.dependencies);
newSearchSource.setFields({ ...this.fields });
// when serializing the internal fields we lose the internal classes used in the index
// pattern, so we have to set it again to workaround this behavior
@@ -161,7 +172,7 @@ export class SearchSource {
}
createChild(options = {}) {
- const childSearchSource = new SearchSource();
+ const childSearchSource = new SearchSource({}, this.dependencies);
childSearchSource.setParent(this, options);
return childSearchSource;
}
@@ -191,16 +202,17 @@ export class SearchSource {
* @return {Observable>}
*/
private fetch$(searchRequest: SearchRequest, signal?: AbortSignal) {
- const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number;
- const searchParams = getSearchParams(getUiSettings(), esShardTimeout);
+ const { search, injectedMetadata, uiSettings } = this.dependencies;
+ const esShardTimeout = injectedMetadata.getInjectedVar('esShardTimeout') as number;
+ const searchParams = getSearchParams(uiSettings, esShardTimeout);
const params = {
index: searchRequest.index.title || searchRequest.index,
body: searchRequest.body,
...searchParams,
};
- return getSearchService()
- .search({ params, indexType: searchRequest.indexType }, { signal })
- .pipe(map(({ rawResponse }) => handleResponse(searchRequest, rawResponse)));
+ return search({ params, indexType: searchRequest.indexType }, { signal }).pipe(
+ map(({ rawResponse }) => handleResponse(searchRequest, rawResponse))
+ );
}
/**
@@ -208,7 +220,9 @@ export class SearchSource {
* @return {Promise>}
*/
private async legacyFetch(searchRequest: SearchRequest, options: FetchOptions) {
- const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number;
+ const { injectedMetadata, legacySearch, uiSettings } = this.dependencies;
+ const esShardTimeout = injectedMetadata.getInjectedVar('esShardTimeout') as number;
+
return await fetchSoon(
searchRequest,
{
@@ -216,8 +230,8 @@ export class SearchSource {
...options,
},
{
- searchService: getSearchService(),
- config: getUiSettings(),
+ legacySearchService: legacySearch,
+ config: uiSettings,
esShardTimeout,
}
);
@@ -228,13 +242,14 @@ export class SearchSource {
* @async
*/
async fetch(options: FetchOptions = {}) {
+ const { uiSettings } = this.dependencies;
await this.requestIsStarting(options);
const searchRequest = await this.flatten();
this.history = [searchRequest];
let response;
- if (getUiSettings().get('courier:batchSearches')) {
+ if (uiSettings.get('courier:batchSearches')) {
response = await this.legacyFetch(searchRequest, options);
} else {
response = this.fetch$(searchRequest, options.abortSignal).toPromise();
@@ -253,7 +268,7 @@ export class SearchSource {
* @return {undefined}
*/
onRequestStart(
- handler: (searchSource: ISearchSource, options?: FetchOptions) => Promise
+ handler: (searchSource: SearchSource, options?: FetchOptions) => Promise
) {
this.requestStartHandlers.push(handler);
}
@@ -326,13 +341,15 @@ export class SearchSource {
}
};
+ const { uiSettings } = this.dependencies;
+
switch (key) {
case 'filter':
return addToRoot('filters', (data.filters || []).concat(val));
case 'query':
return addToRoot(key, (data[key] || []).concat(val));
case 'fields':
- const fields = _.uniq((data[key] || []).concat(val));
+ const fields = uniq((data[key] || []).concat(val));
return addToRoot(key, fields);
case 'index':
case 'type':
@@ -346,7 +363,7 @@ export class SearchSource {
const sort = normalizeSortRequest(
val,
this.getField('index'),
- getUiSettings().get('sort:options')
+ uiSettings.get('sort:options')
);
return addToBody(key, sort);
default:
@@ -389,7 +406,7 @@ export class SearchSource {
body.stored_fields = computedFields.storedFields;
body.script_fields = body.script_fields || {};
- _.extend(body.script_fields, computedFields.scriptFields);
+ extend(body.script_fields, computedFields.scriptFields);
const defaultDocValueFields = computedFields.docvalueFields
? computedFields.docvalueFields
@@ -400,9 +417,11 @@ export class SearchSource {
body._source = index.getSourceFiltering();
}
+ const { uiSettings } = this.dependencies;
+
if (body._source) {
// exclude source fields for this index pattern specified by the user
- const filter = fieldWildcardFilter(body._source.excludes, getUiSettings().get('metaFields'));
+ const filter = fieldWildcardFilter(body._source.excludes, uiSettings.get('metaFields'));
body.docvalue_fields = body.docvalue_fields.filter((docvalueField: any) =>
filter(docvalueField.field)
);
@@ -412,42 +431,22 @@ export class SearchSource {
if (fields) {
// filter out the docvalue_fields, and script_fields to only include those that we are concerned with
body.docvalue_fields = filterDocvalueFields(body.docvalue_fields, fields);
- body.script_fields = _.pick(body.script_fields, fields);
+ body.script_fields = pick(body.script_fields, fields);
// request the remaining fields from both stored_fields and _source
- const remainingFields = _.difference(fields, _.keys(body.script_fields));
+ const remainingFields = difference(fields, keys(body.script_fields));
body.stored_fields = remainingFields;
- _.set(body, '_source.includes', remainingFields);
+ set(body, '_source.includes', remainingFields);
}
- const esQueryConfigs = getEsQueryConfig(getUiSettings());
+ const esQueryConfigs = getEsQueryConfig(uiSettings);
body.query = buildEsQuery(index, query, filters, esQueryConfigs);
if (highlightAll && body.query) {
- body.highlight = getHighlightRequest(body.query, getUiSettings().get('doc_table:highlight'));
+ body.highlight = getHighlightRequest(body.query, uiSettings.get('doc_table:highlight'));
delete searchRequest.highlightAll;
}
- const translateToQuery = (filter: Filter) => filter && (filter.query || filter);
-
- // re-write filters within filter aggregations
- (function recurse(aggBranch) {
- if (!aggBranch) return;
- Object.keys(aggBranch).forEach(function(id) {
- const agg = aggBranch[id];
-
- if (agg.filters) {
- // translate filters aggregations
- const { filters: aggFilters } = agg.filters;
- Object.keys(aggFilters).forEach(filterId => {
- aggFilters[filterId] = translateToQuery(aggFilters[filterId]);
- });
- }
-
- recurse(agg.aggs || agg.aggregations);
- });
- })(body.aggs || body.aggregations);
-
return searchRequest;
}
@@ -467,7 +466,7 @@ export class SearchSource {
const {
filter: originalFilters,
...searchSourceFields
- }: Omit = _.omit(this.getFields(), ['sort', 'size']);
+ }: Omit = omit(this.getFields(), ['sort', 'size']);
let serializedSearchSourceFields: Omit & {
indexRefName?: string;
filter?: Array & { meta: Filter['meta'] & { indexRefName?: string } }>;
@@ -524,10 +523,13 @@ export class SearchSource {
return filterField;
}
- if (_.isFunction(filterField)) {
+ if (isFunction(filterField)) {
return this.getFilters(filterField());
}
return [filterField];
}
}
+
+/** @public **/
+export type ISearchSource = Pick;
diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts
index 2122e4e82ec1d..99d111ce1574e 100644
--- a/src/plugins/data/public/search/types.ts
+++ b/src/plugins/data/public/search/types.ts
@@ -17,13 +17,13 @@
* under the License.
*/
-import { CoreStart } from 'kibana/public';
-import { createSearchSource } from './search_source';
+import { CoreStart, SavedObjectReference } from 'kibana/public';
import { SearchAggsSetup, SearchAggsStart, SearchAggsStartLegacy } from './aggs';
import { ISearch, ISearchGeneric } from './i_search';
import { TStrategyTypes } from './strategy_types';
import { LegacyApiCaller } from './legacy/es_client';
import { SearchInterceptor } from './search_interceptor';
+import { ISearchSource, SearchSourceFields } from './search_source';
export interface ISearchContext {
core: CoreStart;
@@ -60,7 +60,7 @@ export type TRegisterSearchStrategyProvider = (
searchStrategyProvider: TSearchStrategyProvider
) => void;
-interface ISearchStartLegacy {
+export interface ISearchStartLegacy {
esClient: LegacyApiCaller;
}
@@ -81,6 +81,12 @@ export interface ISearchStart {
aggs: SearchAggsStart;
setInterceptor: (searchInterceptor: SearchInterceptor) => void;
search: ISearchGeneric;
- createSearchSource: ReturnType;
+ searchSource: {
+ create: (fields?: SearchSourceFields) => ISearchSource;
+ fromJSON: (
+ searchSourceJson: string,
+ references: SavedObjectReference[]
+ ) => Promise;
+ };
__LEGACY: ISearchStartLegacy & SearchAggsStartLegacy;
}
diff --git a/src/plugins/data/public/services.ts b/src/plugins/data/public/services.ts
index 199ba17b3b81b..ba0b2de393bde 100644
--- a/src/plugins/data/public/services.ts
+++ b/src/plugins/data/public/services.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { NotificationsStart, CoreSetup, CoreStart } from 'src/core/public';
+import { NotificationsStart, CoreStart } from 'src/core/public';
import { FieldFormatsStart } from './field_formats';
import { createGetterSetter } from '../../kibana_utils/public';
import { IndexPatternsContract } from './index_patterns';
@@ -48,7 +48,7 @@ export const [getQueryService, setQueryService] = createGetterSetter<
>('Query');
export const [getInjectedMetadata, setInjectedMetadata] = createGetterSetter<
- CoreSetup['injectedMetadata']
+ CoreStart['injectedMetadata']
>('InjectedMetadata');
export const [getSearchService, setSearchService] = createGetterSetter<
diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts
index e24e01d241278..aaef403979de6 100644
--- a/src/plugins/data/public/types.ts
+++ b/src/plugins/data/public/types.ts
@@ -24,7 +24,7 @@ import { ExpressionsSetup } from 'src/plugins/expressions/public';
import { UiActionsSetup, UiActionsStart } from 'src/plugins/ui_actions/public';
import { AutocompleteSetup, AutocompleteStart } from './autocomplete';
import { FieldFormatsSetup, FieldFormatsStart } from './field_formats';
-import { createFiltersFromEvent } from './actions';
+import { createFiltersFromRangeSelectAction, createFiltersFromValueClickAction } from './actions';
import { ISearchSetup, ISearchStart } from './search';
import { QuerySetup, QueryStart } from './query';
import { IndexPatternSelectProps } from './ui/index_pattern_select';
@@ -49,7 +49,8 @@ export interface DataPublicPluginSetup {
export interface DataPublicPluginStart {
actions: {
- createFiltersFromEvent: typeof createFiltersFromEvent;
+ createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction;
+ createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction;
};
autocomplete: AutocompleteStart;
indexPatterns: IndexPatternsContract;
@@ -74,8 +75,11 @@ export interface IDataPluginServices extends Partial {
/** @internal **/
export interface InternalStartServices {
- fieldFormats: FieldFormatsStart;
- notifications: CoreStart['notifications'];
+ readonly fieldFormats: FieldFormatsStart;
+ readonly notifications: CoreStart['notifications'];
+ readonly uiSettings: CoreStart['uiSettings'];
+ readonly searchService: DataPublicPluginStart['search'];
+ readonly injectedMetadata: CoreStart['injectedMetadata'];
}
/** @internal **/
diff --git a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
index c56060bb9c288..6eb4f82a940b1 100644
--- a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
+++ b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
@@ -27,6 +27,7 @@ import { SavedObjectsClientContract, SimpleSavedObject } from '../../../../../co
import { getTitle } from '../../index_patterns/lib';
export type IndexPatternSelectProps = Required<
+ // Omit, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions' | 'append' | 'prepend' | 'sortMatchesBy'>,
Omit, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>,
'onChange' | 'placeholder'
> & {
diff --git a/src/plugins/data/server/field_formats/field_formats_service.ts b/src/plugins/data/server/field_formats/field_formats_service.ts
index 0dac64fb5dc1d..3404fe8cee9fd 100644
--- a/src/plugins/data/server/field_formats/field_formats_service.ts
+++ b/src/plugins/data/server/field_formats/field_formats_service.ts
@@ -17,16 +17,20 @@
* under the License.
*/
import { has } from 'lodash';
-import { FieldFormatsRegistry, IFieldFormatType, baseFormatters } from '../../common/field_formats';
+import {
+ FieldFormatsRegistry,
+ FieldFormatInstanceType,
+ baseFormatters,
+} from '../../common/field_formats';
import { IUiSettingsClient } from '../../../../core/server';
import { DateFormat } from './converters';
export class FieldFormatsService {
- private readonly fieldFormatClasses: IFieldFormatType[] = [DateFormat, ...baseFormatters];
+ private readonly fieldFormatClasses: FieldFormatInstanceType[] = [DateFormat, ...baseFormatters];
public setup() {
return {
- register: (customFieldFormat: IFieldFormatType) =>
+ register: (customFieldFormat: FieldFormatInstanceType) =>
this.fieldFormatClasses.push(customFieldFormat),
};
}
diff --git a/src/plugins/data/server/index_patterns/capabilities_provider.ts b/src/plugins/data/server/index_patterns/capabilities_provider.ts
new file mode 100644
index 0000000000000..d603830e44a53
--- /dev/null
+++ b/src/plugins/data/server/index_patterns/capabilities_provider.ts
@@ -0,0 +1,24 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export const capabilitiesProvider = () => ({
+ indexPatterns: {
+ save: true,
+ },
+});
diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.test.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.test.ts
new file mode 100644
index 0000000000000..784b0b4d4f3d7
--- /dev/null
+++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.test.ts
@@ -0,0 +1,42 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values';
+
+describe('shouldReadFieldFromDocValues', () => {
+ test('should read field from doc values for aggregatable "number" field', async () => {
+ expect(shouldReadFieldFromDocValues(true, 'number')).toBe(true);
+ });
+
+ test('should not read field from doc values for non-aggregatable "number "field', async () => {
+ expect(shouldReadFieldFromDocValues(false, 'number')).toBe(false);
+ });
+
+ test('should not read field from doc values for "text" field', async () => {
+ expect(shouldReadFieldFromDocValues(true, 'text')).toBe(false);
+ });
+
+ test('should not read field from doc values for "geo_shape" field', async () => {
+ expect(shouldReadFieldFromDocValues(true, 'geo_shape')).toBe(false);
+ });
+
+ test('should not read field from doc values for underscore field', async () => {
+ expect(shouldReadFieldFromDocValues(true, '_source')).toBe(false);
+ });
+});
diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts
index 6d58f7a02c134..56a1cf3ccd161 100644
--- a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts
+++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts
@@ -18,5 +18,5 @@
*/
export function shouldReadFieldFromDocValues(aggregatable: boolean, esType: string) {
- return aggregatable && esType !== 'text' && !esType.startsWith('_');
+ return aggregatable && !['text', 'geo_shape'].includes(esType) && !esType.startsWith('_');
}
diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts
index 58e8fbae9f9e2..3e31f8e8a566d 100644
--- a/src/plugins/data/server/index_patterns/index_patterns_service.ts
+++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts
@@ -20,10 +20,12 @@
import { CoreSetup, Plugin } from 'kibana/server';
import { registerRoutes } from './routes';
import { indexPatternSavedObjectType } from '../saved_objects';
+import { capabilitiesProvider } from './capabilities_provider';
export class IndexPatternsService implements Plugin {
public setup(core: CoreSetup) {
core.savedObjects.registerType(indexPatternSavedObjectType);
+ core.capabilities.registerProvider(capabilitiesProvider);
registerRoutes(core.http);
}
diff --git a/src/plugins/data/server/kql_telemetry/kql_telemetry_service.ts b/src/plugins/data/server/kql_telemetry/kql_telemetry_service.ts
index e45ad796bd9d4..3dfaa9c6d0a98 100644
--- a/src/plugins/data/server/kql_telemetry/kql_telemetry_service.ts
+++ b/src/plugins/data/server/kql_telemetry/kql_telemetry_service.ts
@@ -22,14 +22,16 @@ import { CoreSetup, Plugin, PluginInitializerContext } from 'kibana/server';
import { registerKqlTelemetryRoute } from './route';
import { UsageCollectionSetup } from '../../../usage_collection/server';
import { makeKQLUsageCollector } from './usage_collector';
+import { kqlTelemetry } from '../saved_objects';
export class KqlTelemetryService implements Plugin {
constructor(private initializerContext: PluginInitializerContext) {}
public setup(
- { http, getStartServices }: CoreSetup,
+ { http, getStartServices, savedObjects }: CoreSetup,
{ usageCollection }: { usageCollection?: UsageCollectionSetup }
) {
+ savedObjects.registerType(kqlTelemetry);
registerKqlTelemetryRoute(
http.createRouter(),
getStartServices,
diff --git a/src/plugins/data/server/saved_objects/index.ts b/src/plugins/data/server/saved_objects/index.ts
index 5d980974474de..4326200141179 100644
--- a/src/plugins/data/server/saved_objects/index.ts
+++ b/src/plugins/data/server/saved_objects/index.ts
@@ -20,3 +20,4 @@
export { searchSavedObjectType } from './search';
export { querySavedObjectType } from './query';
export { indexPatternSavedObjectType } from './index_patterns';
+export { kqlTelemetry } from './kql_telementry';
diff --git a/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts b/src/plugins/data/server/saved_objects/kql_telementry.ts
similarity index 71%
rename from src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts
rename to src/plugins/data/server/saved_objects/kql_telementry.ts
index 8c58ac2386da4..6539d5eacfde2 100644
--- a/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts
+++ b/src/plugins/data/server/saved_objects/kql_telementry.ts
@@ -16,11 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { SavedObjectsType } from 'kibana/server';
-import { Class } from '@kbn/utility-types';
-import { SearchSource as SearchSourceClass, ISearchSource } from '../../../../plugins/data/public';
-
-export { SearchSourceFields } from '../../../../plugins/data/public';
-
-export type SearchSource = Class;
-export const SearchSource = SearchSourceClass;
+export const kqlTelemetry: SavedObjectsType = {
+ name: 'kql-telemetry',
+ namespaceType: 'agnostic',
+ hidden: false,
+ mappings: {
+ properties: {
+ optInCount: {
+ type: 'long',
+ },
+ optOutCount: {
+ type: 'long',
+ },
+ },
+ },
+};
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index c41023eab6d20..9b673de60ca65 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -283,7 +283,7 @@ export interface FieldFormatConfig {
export const fieldFormats: {
FieldFormatsRegistry: typeof FieldFormatsRegistry;
FieldFormat: typeof FieldFormat;
- serializeFieldFormat: (agg: import("../public/search").AggConfig) => import("../../expressions/common").SerializedFieldFormat;
+ serializeFieldFormat: (agg: import("../public/search").AggConfig) => import("../../expressions").SerializedFieldFormat;
BoolFormat: typeof BoolFormat;
BytesFormat: typeof BytesFormat;
ColorFormat: typeof ColorFormat;
@@ -609,7 +609,7 @@ export class Plugin implements Plugin_2 {
// (undocumented)
setup(core: CoreSetup, { usageCollection }: DataPluginSetupDependencies): {
fieldFormats: {
- register: (customFieldFormat: import("../common").IFieldFormatType) => number;
+ register: (customFieldFormat: import("../common").FieldFormatInstanceType) => number;
};
search: ISearchSetup;
};
diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json
index 91d6358d44c18..2d41293f26369 100644
--- a/src/plugins/discover/kibana.json
+++ b/src/plugins/discover/kibana.json
@@ -1,6 +1,6 @@
{
"id": "discover",
"version": "kibana",
- "server": false,
+ "server": true,
"ui": true
}
diff --git a/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap
index 9f48e6e57e0ff..afb541253d994 100644
--- a/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap
+++ b/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap
@@ -8,12 +8,12 @@ exports[`FieldName renders a geo field, useShortDots is set to true 1`] = `
class="euiFlexItem euiFlexItem--flexGrowZero"
>
@@ -39,12 +39,12 @@ exports[`FieldName renders a number field by providing a field record, useShortD
class="euiFlexItem euiFlexItem--flexGrowZero"
>
@@ -70,12 +70,12 @@ exports[`FieldName renders a string field by providing fieldType and fieldName 1
class="euiFlexItem euiFlexItem--flexGrowZero"
>
diff --git a/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.test.mocks.js b/src/plugins/discover/server/capabilities_provider.ts
similarity index 84%
rename from src/legacy/ui/public/error_auto_create_index/error_auto_create_index.test.mocks.js
rename to src/plugins/discover/server/capabilities_provider.ts
index 1ac30b85c5a85..2e03631894aee 100644
--- a/src/legacy/ui/public/error_auto_create_index/error_auto_create_index.test.mocks.js
+++ b/src/plugins/discover/server/capabilities_provider.ts
@@ -17,6 +17,11 @@
* under the License.
*/
-import { setup } from '../../../../test_utils/public/http_test_setup';
-
-jest.doMock('ui/new_platform', () => ({ npSetup: { core: setup() } }));
+export const capabilitiesProvider = () => ({
+ discover: {
+ show: true,
+ createShortUrl: true,
+ save: true,
+ saveQuery: true,
+ },
+});
diff --git a/src/plugins/discover/server/index.ts b/src/plugins/discover/server/index.ts
new file mode 100644
index 0000000000000..15a948c56148e
--- /dev/null
+++ b/src/plugins/discover/server/index.ts
@@ -0,0 +1,24 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext } from 'kibana/server';
+import { DiscoverServerPlugin } from './plugin';
+
+export const plugin = (initContext: PluginInitializerContext) =>
+ new DiscoverServerPlugin(initContext);
diff --git a/src/plugins/discover/server/plugin.ts b/src/plugins/discover/server/plugin.ts
new file mode 100644
index 0000000000000..04502f5fc14e6
--- /dev/null
+++ b/src/plugins/discover/server/plugin.ts
@@ -0,0 +1,44 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from 'kibana/server';
+import { capabilitiesProvider } from './capabilities_provider';
+
+export class DiscoverServerPlugin implements Plugin {
+ private readonly logger: Logger;
+
+ constructor(initializerContext: PluginInitializerContext) {
+ this.logger = initializerContext.logger.get();
+ }
+
+ public setup(core: CoreSetup) {
+ this.logger.debug('discover: Setup');
+
+ core.capabilities.registerProvider(capabilitiesProvider);
+
+ return {};
+ }
+
+ public start(core: CoreStart) {
+ this.logger.debug('discover: Started');
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts
index bdb7bfbddc308..5ee66f9d19ac0 100644
--- a/src/plugins/embeddable/public/index.ts
+++ b/src/plugins/embeddable/public/index.ts
@@ -47,7 +47,8 @@ export {
EmbeddableOutput,
EmbeddablePanel,
EmbeddableRoot,
- EmbeddableVisTriggerContext,
+ ValueClickTriggerContext,
+ RangeSelectTriggerContext,
ErrorEmbeddable,
IContainer,
IEmbeddable,
diff --git a/src/plugins/embeddable/public/lib/actions/apply_filter_action.test.ts b/src/plugins/embeddable/public/lib/actions/apply_filter_action.test.ts
index 5297cf6cd365c..636ce3e623c5b 100644
--- a/src/plugins/embeddable/public/lib/actions/apply_filter_action.test.ts
+++ b/src/plugins/embeddable/public/lib/actions/apply_filter_action.test.ts
@@ -31,6 +31,14 @@ test('has expected display name', () => {
expect(action.getDisplayName({} as any)).toMatchInlineSnapshot(`"Apply filter to current view"`);
});
+describe('getIconType()', () => {
+ test('returns "filter" icon', async () => {
+ const action = createFilterAction();
+ const result = action.getIconType({} as any);
+ expect(result).toBe('filter');
+ });
+});
+
describe('isCompatible()', () => {
test('when embeddable filters and filters exist, returns true', async () => {
const action = createFilterAction();
diff --git a/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts b/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts
index 4680512fb81c8..1cdb5af00e748 100644
--- a/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts
+++ b/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts
@@ -42,6 +42,7 @@ export function createFilterAction(): ActionByType {
return createAction({
type: ACTION_APPLY_FILTER,
id: ACTION_APPLY_FILTER,
+ getIconType: () => 'filter',
getDisplayName: () => {
return i18n.translate('embeddableApi.actions.applyFilterActionTitle', {
defaultMessage: 'Apply filter to current view',
diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx
index d07bf915845e9..fc5438b8c8dcb 100644
--- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx
+++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx
@@ -41,7 +41,7 @@ class EditableEmbeddable extends Embeddable {
}
test('is compatible when edit url is available, in edit mode and editable', async () => {
- const action = new EditPanelAction(getFactory);
+ const action = new EditPanelAction(getFactory, {} as any);
expect(
await action.isCompatible({
embeddable: new EditableEmbeddable({ id: '123', viewMode: ViewMode.EDIT }, true),
@@ -50,7 +50,7 @@ test('is compatible when edit url is available, in edit mode and editable', asyn
});
test('getHref returns the edit urls', async () => {
- const action = new EditPanelAction(getFactory);
+ const action = new EditPanelAction(getFactory, {} as any);
expect(action.getHref).toBeDefined();
if (action.getHref) {
@@ -64,7 +64,7 @@ test('getHref returns the edit urls', async () => {
});
test('is not compatible when edit url is not available', async () => {
- const action = new EditPanelAction(getFactory);
+ const action = new EditPanelAction(getFactory, {} as any);
const embeddable = new ContactCardEmbeddable(
{
id: '123',
@@ -83,7 +83,7 @@ test('is not compatible when edit url is not available', async () => {
});
test('is not visible when edit url is available but in view mode', async () => {
- const action = new EditPanelAction(getFactory);
+ const action = new EditPanelAction(getFactory, {} as any);
expect(
await action.isCompatible({
embeddable: new EditableEmbeddable(
@@ -98,7 +98,7 @@ test('is not visible when edit url is available but in view mode', async () => {
});
test('is not compatible when edit url is available, in edit mode, but not editable', async () => {
- const action = new EditPanelAction(getFactory);
+ const action = new EditPanelAction(getFactory, {} as any);
expect(
await action.isCompatible({
embeddable: new EditableEmbeddable(
diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts
index 044e7b5d35ad8..0abbc25ff49a6 100644
--- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts
+++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts
@@ -18,6 +18,7 @@
*/
import { i18n } from '@kbn/i18n';
+import { ApplicationStart } from 'kibana/public';
import { Action } from 'src/plugins/ui_actions/public';
import { ViewMode } from '../types';
import { EmbeddableFactoryNotFoundError } from '../errors';
@@ -35,7 +36,10 @@ export class EditPanelAction implements Action {
public readonly id = ACTION_EDIT_PANEL;
public order = 15;
- constructor(private readonly getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']) {}
+ constructor(
+ private readonly getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'],
+ private readonly application: ApplicationStart
+ ) {}
public getDisplayName({ embeddable }: ActionContext) {
const factory = this.getEmbeddableFactory(embeddable.type);
@@ -56,18 +60,35 @@ export class EditPanelAction implements Action {
public async isCompatible({ embeddable }: ActionContext) {
const canEditEmbeddable = Boolean(
- embeddable && embeddable.getOutput().editable && embeddable.getOutput().editUrl
+ embeddable &&
+ embeddable.getOutput().editable &&
+ (embeddable.getOutput().editUrl ||
+ (embeddable.getOutput().editApp && embeddable.getOutput().editPath))
);
const inDashboardEditMode = embeddable.getInput().viewMode === ViewMode.EDIT;
return Boolean(canEditEmbeddable && inDashboardEditMode);
}
public async execute(context: ActionContext) {
+ const appTarget = this.getAppTarget(context);
+
+ if (appTarget) {
+ await this.application.navigateToApp(appTarget.app, { path: appTarget.path });
+ return;
+ }
+
const href = await this.getHref(context);
if (href) {
- // TODO: when apps start using browser router instead of hash router this has to be fixed
- // https://github.com/elastic/kibana/issues/58217
window.location.href = href;
+ return;
+ }
+ }
+
+ public getAppTarget({ embeddable }: ActionContext): { app: string; path: string } | undefined {
+ const app = embeddable ? embeddable.getOutput().editApp : undefined;
+ const path = embeddable ? embeddable.getOutput().editPath : undefined;
+ if (app && path) {
+ return { app, path };
}
}
diff --git a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx
index 2a0ffd723850b..b046376a304ae 100644
--- a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx
+++ b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx
@@ -66,6 +66,7 @@ test('EmbeddableChildPanel renders an embeddable when it is done loading', async
getAllEmbeddableFactories={start.getEmbeddableFactories}
getEmbeddableFactory={getEmbeddableFactory}
notifications={{} as any}
+ application={{} as any}
overlays={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
@@ -105,6 +106,7 @@ test(`EmbeddableChildPanel renders an error message if the factory doesn't exist
getEmbeddableFactory={(() => undefined) as any}
notifications={{} as any}
overlays={{} as any}
+ application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
/>
diff --git a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx
index 4c08a80a356bf..70628665e6e8c 100644
--- a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx
+++ b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx
@@ -40,6 +40,7 @@ export interface EmbeddableChildPanelProps {
getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: CoreStart['overlays'];
notifications: CoreStart['notifications'];
+ application: CoreStart['application'];
inspector: InspectorStartContract;
SavedObjectFinder: React.ComponentType;
}
@@ -101,6 +102,7 @@ export class EmbeddableChildPanel extends React.Component {
getAllEmbeddableFactories={start.getEmbeddableFactories}
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
+ application={{} as any}
overlays={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
@@ -198,6 +199,7 @@ const renderInEditModeAndOpenContextMenu = async (
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
+ application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
/>
@@ -296,6 +298,7 @@ test('HelloWorldContainer in edit mode shows edit mode actions', async () => {
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
+ application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
/>
@@ -358,6 +361,7 @@ test('Updates when hidePanelTitles is toggled', async () => {
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
+ application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
/>
@@ -410,6 +414,7 @@ test('Check when hide header option is false', async () => {
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
+ application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
hideHeader={false}
@@ -447,6 +452,7 @@ test('Check when hide header option is true', async () => {
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
+ application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
hideHeader={true}
diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
index b95060a73252f..c43359382a33d 100644
--- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
+++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
@@ -45,6 +45,7 @@ interface Props {
getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: CoreStart['overlays'];
notifications: CoreStart['notifications'];
+ application: CoreStart['application'];
inspector: InspectorStartContract;
SavedObjectFinder: React.ComponentType;
hideHeader?: boolean;
@@ -243,7 +244,7 @@ export class EmbeddablePanel extends React.Component {
),
new InspectPanelAction(this.props.inspector),
new RemovePanelAction(),
- new EditPanelAction(this.props.getEmbeddableFactory),
+ new EditPanelAction(this.props.getEmbeddableFactory, this.props.application),
];
const sorted = actions
diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx
index a88c3ba086325..31e14a0af59d7 100644
--- a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx
+++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx
@@ -49,6 +49,7 @@ interface HelloWorldContainerOptions {
getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'];
getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: CoreStart['overlays'];
+ application: CoreStart['application'];
notifications: CoreStart['notifications'];
inspector: InspectorStartContract;
SavedObjectFinder: React.ComponentType;
@@ -81,6 +82,7 @@ export class HelloWorldContainer extends Container;
@@ -112,6 +113,7 @@ export class HelloWorldContainerComponent extends Component {
getAllEmbeddableFactories={this.props.getAllEmbeddableFactories}
overlays={this.props.overlays}
notifications={this.props.notifications}
+ application={this.props.application}
inspector={this.props.inspector}
SavedObjectFinder={this.props.SavedObjectFinder}
/>
diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts
index e29302fd6cc13..da7be1eea199a 100644
--- a/src/plugins/embeddable/public/lib/triggers/triggers.ts
+++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts
@@ -18,18 +18,34 @@
*/
import { Trigger } from '../../../../ui_actions/public';
+import { KibanaDatatable } from '../../../../expressions';
import { IEmbeddable } from '..';
export interface EmbeddableContext {
embeddable: IEmbeddable;
}
-export interface EmbeddableVisTriggerContext {
+export interface ValueClickTriggerContext {
embeddable?: IEmbeddable;
timeFieldName?: string;
data: {
- e?: MouseEvent;
- data: unknown;
+ data: Array<{
+ table: Pick;
+ column: number;
+ row: number;
+ value: any;
+ }>;
+ negate?: boolean;
+ };
+}
+
+export interface RangeSelectTriggerContext {
+ embeddable?: IEmbeddable;
+ timeFieldName?: string;
+ data: {
+ table: KibanaDatatable;
+ column: number;
+ range: number[];
};
}
diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx
index 01fbf52c80182..36f49f2508e80 100644
--- a/src/plugins/embeddable/public/plugin.tsx
+++ b/src/plugins/embeddable/public/plugin.tsx
@@ -118,6 +118,7 @@ export class EmbeddablePublicPlugin implements Plugin
diff --git a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts
index 3bd414ecf0d4a..ebb76c743393b 100644
--- a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts
+++ b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts
@@ -110,6 +110,7 @@ test('ApplyFilterAction is incompatible if the root container does not accept a
getAllEmbeddableFactories: api.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector,
SavedObjectFinder: () => null,
}
@@ -145,6 +146,7 @@ test('trying to execute on incompatible context throws an error ', async () => {
getAllEmbeddableFactories: api.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector,
SavedObjectFinder: () => null,
}
diff --git a/src/plugins/embeddable/public/tests/container.test.ts b/src/plugins/embeddable/public/tests/container.test.ts
index 1aae43550ec6f..d2769e208ba42 100644
--- a/src/plugins/embeddable/public/tests/container.test.ts
+++ b/src/plugins/embeddable/public/tests/container.test.ts
@@ -74,6 +74,7 @@ async function creatHelloWorldContainerAndEmbeddable(
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
});
@@ -147,6 +148,7 @@ test('Container.removeEmbeddable removes and cleans up', async done => {
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@@ -327,6 +329,7 @@ test(`Container updates its state when a child's input is updated`, async done =
getEmbeddableFactory: start.getEmbeddableFactory,
notifications: coreStart.notifications,
overlays: coreStart.overlays,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
});
@@ -584,6 +587,7 @@ test('Container changes made directly after adding a new embeddable are propagat
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@@ -708,6 +712,7 @@ test('untilEmbeddableLoaded() throws an error if there is no such child panel in
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@@ -742,6 +747,7 @@ test('untilEmbeddableLoaded() resolves if child is loaded in the container', asy
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@@ -781,6 +787,7 @@ test('untilEmbeddableLoaded resolves with undefined if child is subsequently rem
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@@ -821,6 +828,7 @@ test('adding a panel then subsequently removing it before its loaded removes the
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
diff --git a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx
index 19e461b8bde7e..a9cb83504d958 100644
--- a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx
+++ b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx
@@ -63,6 +63,7 @@ beforeEach(async () => {
getAllEmbeddableFactories: api.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
diff --git a/src/plugins/embeddable/public/tests/explicit_input.test.ts b/src/plugins/embeddable/public/tests/explicit_input.test.ts
index 0e03db3ec8358..ef3c4b6f17e7f 100644
--- a/src/plugins/embeddable/public/tests/explicit_input.test.ts
+++ b/src/plugins/embeddable/public/tests/explicit_input.test.ts
@@ -88,6 +88,7 @@ test('Explicit embeddable input mapped to undefined with no inherited value will
getEmbeddableFactory: start.getEmbeddableFactory,
notifications: coreStart.notifications,
overlays: coreStart.overlays,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@@ -136,6 +137,7 @@ test('Explicit input tests in async situations', (done: () => void) => {
getEmbeddableFactory: start.getEmbeddableFactory,
notifications: coreStart.notifications,
overlays: coreStart.overlays,
+ application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
diff --git a/src/plugins/home/kibana.json b/src/plugins/home/kibana.json
index d5b047924f599..1c4b44a946e62 100644
--- a/src/plugins/home/kibana.json
+++ b/src/plugins/home/kibana.json
@@ -4,5 +4,5 @@
"server": true,
"ui": true,
"requiredPlugins": ["data", "kibanaLegacy"],
- "optionalPlugins": ["usage_collection", "telemetry"]
+ "optionalPlugins": ["usageCollection", "telemetry"]
}
diff --git a/src/plugins/home/server/capabilities_provider.ts b/src/plugins/home/server/capabilities_provider.ts
new file mode 100644
index 0000000000000..1c662e2301e16
--- /dev/null
+++ b/src/plugins/home/server/capabilities_provider.ts
@@ -0,0 +1,29 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export const capabilitiesProvider = () => ({
+ catalogue: {
+ discover: true,
+ dashboard: true,
+ visualize: true,
+ console: true,
+ advanced_settings: true,
+ index_patterns: true,
+ },
+});
diff --git a/src/plugins/home/server/plugin.ts b/src/plugins/home/server/plugin.ts
index d2f2d7041024e..1050c19362ae1 100644
--- a/src/plugins/home/server/plugin.ts
+++ b/src/plugins/home/server/plugin.ts
@@ -26,9 +26,11 @@ import {
SampleDataRegistryStart,
} from './services';
import { UsageCollectionSetup } from '../../usage_collection/server';
+import { capabilitiesProvider } from './capabilities_provider';
+import { sampleDataTelemetry } from './saved_objects';
interface HomeServerPluginSetupDependencies {
- usage_collection?: UsageCollectionSetup;
+ usageCollection?: UsageCollectionSetup;
}
export class HomeServerPlugin implements Plugin {
@@ -37,9 +39,11 @@ export class HomeServerPlugin implements Plugin {
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.tsx b/src/plugins/input_control_vis/public/components/editor/options_tab.tsx
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.tsx
rename to src/plugins/input_control_vis/public/components/editor/options_tab.tsx
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx b/src/plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx
rename to src/plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.tsx b/src/plugins/input_control_vis/public/components/editor/range_control_editor.tsx
similarity index 97%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.tsx
rename to src/plugins/input_control_vis/public/components/editor/range_control_editor.tsx
index 97850879a2d38..b6b852bcfa707 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.tsx
+++ b/src/plugins/input_control_vis/public/components/editor/range_control_editor.tsx
@@ -24,11 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { IndexPatternSelectFormRow } from './index_pattern_select_form_row';
import { FieldSelect } from './field_select';
import { ControlParams, ControlParamsOptions } from '../../editor_utils';
-import {
- IIndexPattern,
- IFieldType,
- IndexPatternSelect,
-} from '../../../../../../plugins/data/public';
+import { IIndexPattern, IFieldType, IndexPatternSelect } from '../../../../data/public';
import { InputControlVisDependencies } from '../../plugin';
interface RangeControlEditorProps {
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.tsx.snap b/src/plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.tsx.snap
rename to src/plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.tsx.snap
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.tsx.snap b/src/plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.tsx.snap
rename to src/plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.tsx.snap
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap b/src/plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap
similarity index 97%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap
rename to src/plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap
index 59ae99260cecd..43e2af6d099e8 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap
+++ b/src/plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap
@@ -46,6 +46,7 @@ exports[`renders ListControl 1`] = `
placeholder="Select..."
selectedOptions={Array []}
singleSelection={false}
+ sortMatchesBy="none"
/>
`;
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/range_control.test.tsx.snap b/src/plugins/input_control_vis/public/components/vis/__snapshots__/range_control.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/range_control.test.tsx.snap
rename to src/plugins/input_control_vis/public/components/vis/__snapshots__/range_control.test.tsx.snap
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/_index.scss b/src/plugins/input_control_vis/public/components/vis/_index.scss
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/_index.scss
rename to src/plugins/input_control_vis/public/components/vis/_index.scss
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/_vis.scss b/src/plugins/input_control_vis/public/components/vis/_vis.scss
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/_vis.scss
rename to src/plugins/input_control_vis/public/components/vis/_vis.scss
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.test.tsx b/src/plugins/input_control_vis/public/components/vis/form_row.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.test.tsx
rename to src/plugins/input_control_vis/public/components/vis/form_row.test.tsx
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.tsx b/src/plugins/input_control_vis/public/components/vis/form_row.tsx
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.tsx
rename to src/plugins/input_control_vis/public/components/vis/form_row.tsx
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx b/src/plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx
similarity index 99%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx
rename to src/plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx
index 1712f024f5b7b..b0b674ad7b6ee 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx
+++ b/src/plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx
@@ -28,8 +28,6 @@ import { InputControlVis } from './input_control_vis';
import { ListControl } from '../../control/list_control_factory';
import { RangeControl } from '../../control/range_control_factory';
-jest.mock('ui/new_platform');
-
const mockListControl: ListControl = {
id: 'mock-list-control',
isEnabled: () => {
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.tsx b/src/plugins/input_control_vis/public/components/vis/input_control_vis.tsx
similarity index 97%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.tsx
rename to src/plugins/input_control_vis/public/components/vis/input_control_vis.tsx
index e2497287f35d0..c0ef99664fdf8 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.tsx
+++ b/src/plugins/input_control_vis/public/components/vis/input_control_vis.tsx
@@ -23,8 +23,8 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { CONTROL_TYPES } from '../../editor_utils';
import { ListControl } from '../../control/list_control_factory';
import { RangeControl } from '../../control/range_control_factory';
-import { ListControl as ListControlComponent } from '../vis/list_control';
-import { RangeControl as RangeControlComponent } from '../vis/range_control';
+import { ListControl as ListControlComponent } from './list_control';
+import { RangeControl as RangeControlComponent } from './range_control';
function isListControl(control: RangeControl | ListControl): control is ListControl {
return control.type === CONTROL_TYPES.LIST;
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.test.tsx b/src/plugins/input_control_vis/public/components/vis/list_control.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.test.tsx
rename to src/plugins/input_control_vis/public/components/vis/list_control.test.tsx
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.tsx b/src/plugins/input_control_vis/public/components/vis/list_control.tsx
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.tsx
rename to src/plugins/input_control_vis/public/components/vis/list_control.tsx
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.tsx b/src/plugins/input_control_vis/public/components/vis/range_control.test.tsx
similarity index 98%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.tsx
rename to src/plugins/input_control_vis/public/components/vis/range_control.test.tsx
index 639616151a395..ff5d572fa21c4 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.tsx
+++ b/src/plugins/input_control_vis/public/components/vis/range_control.test.tsx
@@ -23,8 +23,6 @@ import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { RangeControl, ceilWithPrecision, floorWithPrecision } from './range_control';
import { RangeControl as RangeControlClass } from '../../control/range_control_factory';
-jest.mock('ui/new_platform');
-
const control: RangeControlClass = {
id: 'mock-range-control',
isEnabled: () => {
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx b/src/plugins/input_control_vis/public/components/vis/range_control.tsx
similarity index 97%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx
rename to src/plugins/input_control_vis/public/components/vis/range_control.tsx
index 0cd2a2b331980..f028feaf5f84f 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx
+++ b/src/plugins/input_control_vis/public/components/vis/range_control.tsx
@@ -19,7 +19,7 @@
import _ from 'lodash';
import React, { PureComponent } from 'react';
-import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_react/public';
+import { ValidatedDualRange } from '../../../../kibana_react/public';
import { FormRow } from './form_row';
import { RangeControl as RangeControlClass } from '../../control/range_control_factory';
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/control.test.ts b/src/plugins/input_control_vis/public/control/control.test.ts
similarity index 94%
rename from src/legacy/core_plugins/input_control_vis/public/control/control.test.ts
rename to src/plugins/input_control_vis/public/control/control.test.ts
index e76b199a0262c..a2d220c14a3f7 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/control.test.ts
+++ b/src/plugins/input_control_vis/public/control/control.test.ts
@@ -21,7 +21,6 @@ import expect from '@kbn/expect';
import { Control } from './control';
import { ControlParams } from '../editor_utils';
import { FilterManager as BaseFilterManager } from './filter_manager/filter_manager';
-import { SearchSource } from '../legacy_imports';
function createControlParams(id: string, label: string): ControlParams {
return {
@@ -51,18 +50,12 @@ class ControlMock extends Control {
destroy() {}
}
-const mockKbnApi: SearchSource = {} as SearchSource;
describe('hasChanged', () => {
let control: ControlMock;
beforeEach(() => {
- control = new ControlMock(
- createControlParams('3', 'control'),
- mockFilterManager,
- false,
- mockKbnApi
- );
+ control = new ControlMock(createControlParams('3', 'control'), mockFilterManager, false);
});
afterEach(() => {
@@ -93,20 +86,17 @@ describe('ancestors', () => {
grandParentControl = new ControlMock(
createControlParams('1', 'grandparent control'),
mockFilterManager,
- false,
- mockKbnApi
+ false
);
parentControl = new ControlMock(
createControlParams('2', 'parent control'),
mockFilterManager,
- false,
- mockKbnApi
+ false
);
childControl = new ControlMock(
createControlParams('3', 'child control'),
mockFilterManager,
- false,
- mockKbnApi
+ false
);
});
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/control.ts b/src/plugins/input_control_vis/public/control/control.ts
similarity index 95%
rename from src/legacy/core_plugins/input_control_vis/public/control/control.ts
rename to src/plugins/input_control_vis/public/control/control.ts
index 6fddef777f73e..c57b09a19ebc8 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/control.ts
+++ b/src/plugins/input_control_vis/public/control/control.ts
@@ -22,8 +22,7 @@
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
-import { Filter } from '../../../../../plugins/data/public';
-import { SearchSource as SearchSourceClass } from '../legacy_imports';
+import { Filter } from 'src/plugins/data/public';
import { ControlParams, ControlParamsOptions, CONTROL_TYPES } from '../editor_utils';
import { RangeFilterManager } from './filter_manager/range_filter_manager';
import { PhraseFilterManager } from './filter_manager/phrase_filter_manager';
@@ -61,8 +60,7 @@ export abstract class Control {
constructor(
public controlParams: ControlParams,
public filterManager: FilterManager,
- public useTimeFilter: boolean,
- public SearchSource: SearchSourceClass
+ public useTimeFilter: boolean
) {
this.id = controlParams.id;
this.controlParams = controlParams;
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/control_factory.ts b/src/plugins/input_control_vis/public/control/control_factory.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/control/control_factory.ts
rename to src/plugins/input_control_vis/public/control/control_factory.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts b/src/plugins/input_control_vis/public/control/create_search_source.ts
similarity index 83%
rename from src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts
rename to src/plugins/input_control_vis/public/control/create_search_source.ts
index f238a2287ecdb..d6772a7cba5b8 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts
+++ b/src/plugins/input_control_vis/public/control/create_search_source.ts
@@ -17,11 +17,16 @@
* under the License.
*/
-import { PhraseFilter, IndexPattern, TimefilterContract } from '../../../../../plugins/data/public';
-import { SearchSource as SearchSourceClass, SearchSourceFields } from '../legacy_imports';
+import {
+ SearchSourceFields,
+ PhraseFilter,
+ IndexPattern,
+ TimefilterContract,
+ DataPublicPluginStart,
+} from 'src/plugins/data/public';
export function createSearchSource(
- SearchSource: SearchSourceClass,
+ { create }: DataPublicPluginStart['search']['searchSource'],
initialState: SearchSourceFields | null,
indexPattern: IndexPattern,
aggs: any,
@@ -29,7 +34,8 @@ export function createSearchSource(
filters: PhraseFilter[] = [],
timefilter: TimefilterContract
) {
- const searchSource = initialState ? new SearchSource(initialState) : new SearchSource();
+ const searchSource = create(initialState || {});
+
// Do not not inherit from rootSearchSource to avoid picking up time and globals
searchSource.setParent(undefined);
searchSource.setField('filter', () => {
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts b/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts
similarity index 93%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts
rename to src/plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts
index 39c9d843e6bce..a9b7550be44ae 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts
+++ b/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts
@@ -20,12 +20,8 @@
import expect from '@kbn/expect';
import { FilterManager } from './filter_manager';
-import { coreMock } from '../../../../../../core/public/mocks';
-import {
- Filter,
- IndexPattern,
- FilterManager as QueryFilterManager,
-} from '../../../../../../plugins/data/public';
+import { coreMock } from '../../../../../core/public/mocks';
+import { Filter, IndexPattern, FilterManager as QueryFilterManager } from '../../../../data/public';
const setupMock = coreMock.createSetup();
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.ts b/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.ts
similarity index 93%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.ts
rename to src/plugins/input_control_vis/public/control/filter_manager/filter_manager.ts
index 90b88a56950e2..bb806b336c3e0 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.ts
+++ b/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.ts
@@ -19,11 +19,7 @@
import _ from 'lodash';
-import {
- FilterManager as QueryFilterManager,
- IndexPattern,
- Filter,
-} from '../../../../../../plugins/data/public';
+import { FilterManager as QueryFilterManager, IndexPattern, Filter } from '../../../../data/public';
export abstract class FilterManager {
constructor(
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts b/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts
similarity index 98%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts
rename to src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts
index 5be5d0157541e..6398c10b63a8c 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts
+++ b/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts
@@ -19,11 +19,7 @@
import expect from '@kbn/expect';
-import {
- Filter,
- IndexPattern,
- FilterManager as QueryFilterManager,
-} from '../../../../../../plugins/data/public';
+import { Filter, IndexPattern, FilterManager as QueryFilterManager } from '../../../../data/public';
import { PhraseFilterManager } from './phrase_filter_manager';
describe('PhraseFilterManager', function() {
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts b/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts
similarity index 98%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts
rename to src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts
index 6f4a95b491907..bf167afa69bcf 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts
+++ b/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts
@@ -25,7 +25,7 @@ import {
esFilters,
IndexPattern,
FilterManager as QueryFilterManager,
-} from '../../../../../../plugins/data/public';
+} from '../../../../data/public';
export class PhraseFilterManager extends FilterManager {
constructor(
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts b/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts
similarity index 98%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts
rename to src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts
index c776042ea4ba6..6e66b6942e5d3 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts
+++ b/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts
@@ -25,7 +25,7 @@ import {
RangeFilterMeta,
IndexPattern,
FilterManager as QueryFilterManager,
-} from '../../../../../../plugins/data/public';
+} from '../../../../data/public';
describe('RangeFilterManager', function() {
const controlId = 'control1';
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts b/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts
similarity index 95%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts
rename to src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts
index 7a6719e85961b..1a884cf267c41 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts
+++ b/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts
@@ -20,12 +20,7 @@
import _ from 'lodash';
import { FilterManager } from './filter_manager';
-import {
- esFilters,
- RangeFilter,
- RangeFilterParams,
- IFieldType,
-} from '../../../../../../plugins/data/public';
+import { esFilters, RangeFilter, RangeFilterParams, IFieldType } from '../../../../data/public';
interface SliderValue {
min?: string | number;
diff --git a/src/plugins/input_control_vis/public/control/list_control_factory.test.ts b/src/plugins/input_control_vis/public/control/list_control_factory.test.ts
new file mode 100644
index 0000000000000..72070175a233c
--- /dev/null
+++ b/src/plugins/input_control_vis/public/control/list_control_factory.test.ts
@@ -0,0 +1,142 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { listControlFactory, ListControl } from './list_control_factory';
+import { ControlParams, CONTROL_TYPES } from '../editor_utils';
+import { getDepsMock, getSearchSourceMock } from '../test_utils';
+
+describe('listControlFactory', () => {
+ const searchSourceMock = getSearchSourceMock();
+ const deps = getDepsMock({
+ searchSource: {
+ create: searchSourceMock,
+ },
+ });
+
+ describe('hasValue', () => {
+ const controlParams: ControlParams = {
+ id: '1',
+ fieldName: 'myField',
+ options: {} as any,
+ type: CONTROL_TYPES.LIST,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: 'parent',
+ };
+ const useTimeFilter = false;
+
+ let listControl: ListControl;
+ beforeEach(async () => {
+ listControl = await listControlFactory(controlParams, useTimeFilter, deps);
+ });
+
+ test('should be false when control has no value', () => {
+ expect(listControl.hasValue()).toBe(false);
+ });
+
+ test('should be true when control has value', () => {
+ listControl.set([{ value: 'selected option', label: 'selection option' }]);
+ expect(listControl.hasValue()).toBe(true);
+ });
+
+ test('should be true when control has value that is the string "false"', () => {
+ listControl.set([{ value: 'false', label: 'selection option' }]);
+ expect(listControl.hasValue()).toBe(true);
+ });
+ });
+
+ describe('fetch', () => {
+ const controlParams: ControlParams = {
+ id: '1',
+ fieldName: 'myField',
+ options: {} as any,
+ type: CONTROL_TYPES.LIST,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: 'parent',
+ };
+ const useTimeFilter = false;
+
+ let listControl: ListControl;
+ beforeEach(async () => {
+ listControl = await listControlFactory(controlParams, useTimeFilter, deps);
+ });
+
+ test('should pass in timeout parameters from injected vars', async () => {
+ await listControl.fetch();
+ expect(searchSourceMock).toHaveBeenCalledWith({
+ timeout: `1000ms`,
+ terminate_after: 100000,
+ });
+ });
+
+ test('should set selectOptions to results of terms aggregation', async () => {
+ await listControl.fetch();
+ expect(listControl.selectOptions).toEqual([
+ 'Zurich Airport',
+ 'Xi an Xianyang International Airport',
+ ]);
+ });
+ });
+
+ describe('fetch with ancestors', () => {
+ const controlParams: ControlParams = {
+ id: '1',
+ fieldName: 'myField',
+ options: {} as any,
+ type: CONTROL_TYPES.LIST,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: 'parent',
+ };
+ const useTimeFilter = false;
+
+ let listControl: ListControl;
+ let parentControl;
+ beforeEach(async () => {
+ listControl = await listControlFactory(controlParams, useTimeFilter, deps);
+
+ const parentControlParams: ControlParams = {
+ id: 'parent',
+ fieldName: 'myField',
+ options: {} as any,
+ type: CONTROL_TYPES.LIST,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: 'parent',
+ };
+ parentControl = await listControlFactory(parentControlParams, useTimeFilter, deps);
+ parentControl.clear();
+ listControl.setAncestors([parentControl]);
+ });
+
+ describe('ancestor does not have value', () => {
+ test('should disable control', async () => {
+ await listControl.fetch();
+ expect(listControl.isEnabled()).toBe(false);
+ });
+
+ test('should reset lastAncestorValues', async () => {
+ listControl.lastAncestorValues = 'last ancestor value';
+ await listControl.fetch();
+ expect(listControl.lastAncestorValues).toBeUndefined();
+ });
+ });
+ });
+});
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts b/src/plugins/input_control_vis/public/control/list_control_factory.ts
similarity index 94%
rename from src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts
rename to src/plugins/input_control_vis/public/control/list_control_factory.ts
index 8364c82efecdb..123ef83277e0b 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts
+++ b/src/plugins/input_control_vis/public/control/list_control_factory.ts
@@ -19,14 +19,17 @@
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
-
-import { SearchSource as SearchSourceClass, SearchSourceFields } from '../legacy_imports';
+import {
+ IFieldType,
+ TimefilterContract,
+ SearchSourceFields,
+ DataPublicPluginStart,
+} from 'src/plugins/data/public';
import { Control, noValuesDisableMsg, noIndexPatternMsg } from './control';
import { PhraseFilterManager } from './filter_manager/phrase_filter_manager';
import { createSearchSource } from './create_search_source';
import { ControlParams } from '../editor_utils';
import { InputControlVisDependencies } from '../plugin';
-import { IFieldType, TimefilterContract } from '../../../../../plugins/data/public';
function getEscapedQuery(query = '') {
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#_standard_operators
@@ -75,6 +78,7 @@ const termsAgg = ({ field, size, direction, query }: TermsAggArgs) => {
export class ListControl extends Control {
private getInjectedVar: InputControlVisDependencies['core']['injectedMetadata']['getInjectedVar'];
private timefilter: TimefilterContract;
+ private searchSource: DataPublicPluginStart['search']['searchSource'];
abortController?: AbortController;
lastAncestorValues: any;
@@ -86,12 +90,13 @@ export class ListControl extends Control {
controlParams: ControlParams,
filterManager: PhraseFilterManager,
useTimeFilter: boolean,
- SearchSource: SearchSourceClass,
+ searchSource: DataPublicPluginStart['search']['searchSource'],
deps: InputControlVisDependencies
) {
- super(controlParams, filterManager, useTimeFilter, SearchSource);
+ super(controlParams, filterManager, useTimeFilter);
this.getInjectedVar = deps.core.injectedMetadata.getInjectedVar;
this.timefilter = deps.data.query.timefilter.timefilter;
+ this.searchSource = searchSource;
}
fetch = async (query?: string) => {
@@ -143,7 +148,7 @@ export class ListControl extends Control {
query,
});
const searchSource = createSearchSource(
- this.SearchSource,
+ this.searchSource,
initialSearchSourceState,
indexPattern,
aggs,
@@ -202,7 +207,6 @@ export class ListControl extends Control {
export async function listControlFactory(
controlParams: ControlParams,
useTimeFilter: boolean,
- SearchSource: SearchSourceClass,
deps: InputControlVisDependencies
) {
const [, { data: dataPluginStart }] = await deps.core.getStartServices();
@@ -225,7 +229,7 @@ export async function listControlFactory(
deps.data.query.filterManager
),
useTimeFilter,
- SearchSource,
+ dataPluginStart.search.searchSource,
deps
);
return listControl;
diff --git a/src/plugins/input_control_vis/public/control/range_control_factory.test.ts b/src/plugins/input_control_vis/public/control/range_control_factory.test.ts
new file mode 100644
index 0000000000000..084c02e138a2d
--- /dev/null
+++ b/src/plugins/input_control_vis/public/control/range_control_factory.test.ts
@@ -0,0 +1,97 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { rangeControlFactory } from './range_control_factory';
+import { ControlParams, CONTROL_TYPES } from '../editor_utils';
+import { getDepsMock, getSearchSourceMock } from '../test_utils';
+
+describe('rangeControlFactory', () => {
+ describe('fetch', () => {
+ const controlParams: ControlParams = {
+ id: '1',
+ fieldName: 'myNumberField',
+ options: {},
+ type: CONTROL_TYPES.RANGE,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: {} as any,
+ };
+ const useTimeFilter = false;
+
+ test('should set min and max from aggregation results', async () => {
+ const esSearchResponse = {
+ aggregations: {
+ maxAgg: { value: 100 },
+ minAgg: { value: 10 },
+ },
+ };
+ const searchSourceMock = getSearchSourceMock(esSearchResponse);
+ const deps = getDepsMock({
+ searchSource: {
+ create: searchSourceMock,
+ },
+ });
+
+ const rangeControl = await rangeControlFactory(controlParams, useTimeFilter, deps);
+ await rangeControl.fetch();
+
+ expect(rangeControl.isEnabled()).toBe(true);
+ expect(rangeControl.min).toBe(10);
+ expect(rangeControl.max).toBe(100);
+ });
+
+ test('should disable control when there are 0 hits', async () => {
+ // ES response when the query does not match any documents
+ const esSearchResponse = {
+ aggregations: {
+ maxAgg: { value: null },
+ minAgg: { value: null },
+ },
+ };
+ const searchSourceMock = getSearchSourceMock(esSearchResponse);
+ const deps = getDepsMock({
+ searchSource: {
+ create: searchSourceMock,
+ },
+ });
+
+ const rangeControl = await rangeControlFactory(controlParams, useTimeFilter, deps);
+ await rangeControl.fetch();
+
+ expect(rangeControl.isEnabled()).toBe(false);
+ });
+
+ test('should disable control when response is empty', async () => {
+ // ES response for dashboardonly user who does not have read permissions on index is 200 (which is weird)
+ // and there is not aggregations key
+ const esSearchResponse = {};
+ const searchSourceMock = getSearchSourceMock(esSearchResponse);
+ const deps = getDepsMock({
+ searchSource: {
+ create: searchSourceMock,
+ },
+ });
+
+ const rangeControl = await rangeControlFactory(controlParams, useTimeFilter, deps);
+ await rangeControl.fetch();
+
+ expect(rangeControl.isEnabled()).toBe(false);
+ });
+ });
+});
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts b/src/plugins/input_control_vis/public/control/range_control_factory.ts
similarity index 91%
rename from src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts
rename to src/plugins/input_control_vis/public/control/range_control_factory.ts
index d9b43c9dff201..326756ad5ffc6 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts
+++ b/src/plugins/input_control_vis/public/control/range_control_factory.ts
@@ -20,13 +20,12 @@
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
-import { SearchSource as SearchSourceClass } from '../legacy_imports';
+import { IFieldType, TimefilterContract, DataPublicPluginStart } from 'src/plugins/data/public';
import { Control, noValuesDisableMsg, noIndexPatternMsg } from './control';
import { RangeFilterManager } from './filter_manager/range_filter_manager';
import { createSearchSource } from './create_search_source';
import { ControlParams } from '../editor_utils';
import { InputControlVisDependencies } from '../plugin';
-import { IFieldType, TimefilterContract } from '../.../../../../../../plugins/data/public';
const minMaxAgg = (field?: IFieldType) => {
const aggBody: any = {};
@@ -52,6 +51,8 @@ const minMaxAgg = (field?: IFieldType) => {
};
export class RangeControl extends Control {
+ private searchSource: DataPublicPluginStart['search']['searchSource'];
+
timefilter: TimefilterContract;
abortController: any;
min: any;
@@ -61,11 +62,12 @@ export class RangeControl extends Control {
controlParams: ControlParams,
filterManager: RangeFilterManager,
useTimeFilter: boolean,
- SearchSource: SearchSourceClass,
+ searchSource: DataPublicPluginStart['search']['searchSource'],
deps: InputControlVisDependencies
) {
- super(controlParams, filterManager, useTimeFilter, SearchSource);
+ super(controlParams, filterManager, useTimeFilter);
this.timefilter = deps.data.query.timefilter.timefilter;
+ this.searchSource = searchSource;
}
async fetch() {
@@ -83,7 +85,7 @@ export class RangeControl extends Control {
const fieldName = this.filterManager.fieldName;
const aggs = minMaxAgg(indexPattern.fields.getByName(fieldName));
const searchSource = createSearchSource(
- this.SearchSource,
+ this.searchSource,
null,
indexPattern,
aggs,
@@ -129,7 +131,6 @@ export class RangeControl extends Control {
export async function rangeControlFactory(
controlParams: ControlParams,
useTimeFilter: boolean,
- SearchSource: SearchSourceClass,
deps: InputControlVisDependencies
): Promise {
const [, { data: dataPluginStart }] = await deps.core.getStartServices();
@@ -144,7 +145,7 @@ export async function rangeControlFactory(
deps.data.query.filterManager
),
useTimeFilter,
- SearchSource,
+ dataPluginStart.search.searchSource,
deps
);
}
diff --git a/src/legacy/core_plugins/input_control_vis/public/editor_utils.ts b/src/plugins/input_control_vis/public/editor_utils.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/editor_utils.ts
rename to src/plugins/input_control_vis/public/editor_utils.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/index.scss b/src/plugins/input_control_vis/public/index.scss
similarity index 80%
rename from src/legacy/core_plugins/input_control_vis/public/index.scss
rename to src/plugins/input_control_vis/public/index.scss
index ac4692494b923..42fded23d7761 100644
--- a/src/legacy/core_plugins/input_control_vis/public/index.scss
+++ b/src/plugins/input_control_vis/public/index.scss
@@ -1,5 +1,3 @@
-@import 'src/legacy/ui/public/styles/styling_constants';
-
// Prefix all styles with "icv" to avoid conflicts.
// Examples
// icvChart
diff --git a/src/legacy/core_plugins/input_control_vis/public/index.ts b/src/plugins/input_control_vis/public/index.ts
similarity index 91%
rename from src/legacy/core_plugins/input_control_vis/public/index.ts
rename to src/plugins/input_control_vis/public/index.ts
index e14c2cc4b69b6..8edd3fd9996c3 100644
--- a/src/legacy/core_plugins/input_control_vis/public/index.ts
+++ b/src/plugins/input_control_vis/public/index.ts
@@ -17,7 +17,9 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../../core/public';
+import './index.scss';
+
+import { PluginInitializerContext } from '../../../core/public';
import { InputControlVisPlugin as Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
diff --git a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts b/src/plugins/input_control_vis/public/input_control_fn.test.ts
similarity index 86%
rename from src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts
rename to src/plugins/input_control_vis/public/input_control_fn.test.ts
index acc214ed31180..f3ea2d2d6f0ba 100644
--- a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts
+++ b/src/plugins/input_control_vis/public/input_control_fn.test.ts
@@ -18,11 +18,7 @@
*/
import { createInputControlVisFn } from './input_control_fn';
-
-// eslint-disable-next-line
-import { functionWrapper } from '../../../../plugins/expressions/common/expression_functions/specs/tests/utils';
-
-jest.mock('./legacy_imports.ts');
+import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils';
describe('interpreter/functions#input_control_vis', () => {
const fn = functionWrapper(createInputControlVisFn());
@@ -48,8 +44,9 @@ describe('interpreter/functions#input_control_vis', () => {
pinFilters: false,
};
- it('returns an object with the correct structure', async () => {
+ test('returns an object with the correct structure', async () => {
const actual = await fn(null, { visConfig: JSON.stringify(visConfig) });
+
expect(actual).toMatchSnapshot();
});
});
diff --git a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.ts b/src/plugins/input_control_vis/public/input_control_fn.ts
similarity index 93%
rename from src/legacy/core_plugins/input_control_vis/public/input_control_fn.ts
rename to src/plugins/input_control_vis/public/input_control_fn.ts
index e779c6d344ab5..59c0e03505bb7 100644
--- a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.ts
+++ b/src/plugins/input_control_vis/public/input_control_fn.ts
@@ -19,11 +19,7 @@
import { i18n } from '@kbn/i18n';
-import {
- ExpressionFunctionDefinition,
- KibanaDatatable,
- Render,
-} from '../../../../plugins/expressions/public';
+import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public';
interface Arguments {
visConfig: string;
diff --git a/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts b/src/plugins/input_control_vis/public/input_control_vis_type.ts
similarity index 96%
rename from src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts
rename to src/plugins/input_control_vis/public/input_control_vis_type.ts
index badea68eec19f..8114dbf110f8b 100644
--- a/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts
+++ b/src/plugins/input_control_vis/public/input_control_vis_type.ts
@@ -23,7 +23,7 @@ import { createInputControlVisController } from './vis_controller';
import { getControlsTab } from './components/editor/controls_tab';
import { OptionsTab } from './components/editor/options_tab';
import { InputControlVisDependencies } from './plugin';
-import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/public';
+import { defaultFeedbackMessage } from '../../kibana_utils/public';
export function createInputControlVisTypeDefinition(deps: InputControlVisDependencies) {
const InputControlVisController = createInputControlVisController(deps);
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/index.ts b/src/plugins/input_control_vis/public/lineage/index.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/index.ts
rename to src/plugins/input_control_vis/public/lineage/index.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.test.ts b/src/plugins/input_control_vis/public/lineage/lineage_map.test.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.test.ts
rename to src/plugins/input_control_vis/public/lineage/lineage_map.test.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.ts b/src/plugins/input_control_vis/public/lineage/lineage_map.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.ts
rename to src/plugins/input_control_vis/public/lineage/lineage_map.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.test.ts b/src/plugins/input_control_vis/public/lineage/parent_candidates.test.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.test.ts
rename to src/plugins/input_control_vis/public/lineage/parent_candidates.test.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.ts b/src/plugins/input_control_vis/public/lineage/parent_candidates.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.ts
rename to src/plugins/input_control_vis/public/lineage/parent_candidates.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/plugin.ts b/src/plugins/input_control_vis/public/plugin.ts
similarity index 92%
rename from src/legacy/core_plugins/input_control_vis/public/plugin.ts
rename to src/plugins/input_control_vis/public/plugin.ts
index b743468065430..9fc7df24c2dcc 100644
--- a/src/legacy/core_plugins/input_control_vis/public/plugin.ts
+++ b/src/plugins/input_control_vis/public/plugin.ts
@@ -19,11 +19,8 @@
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public';
import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public';
-import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public';
-import {
- VisualizationsSetup,
- VisualizationsStart,
-} from '../../../../plugins/visualizations/public';
+import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public';
+import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public';
import { createInputControlVisFn } from './input_control_fn';
import { createInputControlVisTypeDefinition } from './input_control_vis_type';
diff --git a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_deps_mock.tsx b/src/plugins/input_control_vis/public/test_utils/get_deps_mock.tsx
similarity index 85%
rename from src/legacy/core_plugins/input_control_vis/public/test_utils/get_deps_mock.tsx
rename to src/plugins/input_control_vis/public/test_utils/get_deps_mock.tsx
index 78a4ef3a5597a..efd7d4c020854 100644
--- a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_deps_mock.tsx
+++ b/src/plugins/input_control_vis/public/test_utils/get_deps_mock.tsx
@@ -19,6 +19,7 @@
import React from 'react';
import { InputControlVisDependencies } from '../plugin';
+import { getSearchSourceMock } from './get_search_service_mock';
const fields = [] as any;
fields.push({ name: 'myField' } as any);
@@ -26,13 +27,20 @@ fields.getByName = (name: any) => {
return fields.find(({ name: n }: { name: string }) => n === name);
};
-export const getDepsMock = (): InputControlVisDependencies =>
+export const getDepsMock = ({
+ searchSource = {
+ create: getSearchSourceMock(),
+ },
+} = {}): InputControlVisDependencies =>
({
core: {
getStartServices: jest.fn().mockReturnValue([
null,
{
data: {
+ search: {
+ searchSource,
+ },
ui: {
IndexPatternSelect: () => (
) as any,
},
@@ -58,6 +66,11 @@ export const getDepsMock = (): InputControlVisDependencies =>
},
},
data: {
+ search: {
+ searchSource: {
+ create: getSearchSourceMock(),
+ },
+ },
query: {
filterManager: {
fieldName: 'myField',
diff --git a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_index_pattern_mock.ts b/src/plugins/input_control_vis/public/test_utils/get_index_pattern_mock.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/test_utils/get_index_pattern_mock.ts
rename to src/plugins/input_control_vis/public/test_utils/get_index_pattern_mock.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_index_patterns_mock.ts b/src/plugins/input_control_vis/public/test_utils/get_index_patterns_mock.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/test_utils/get_index_patterns_mock.ts
rename to src/plugins/input_control_vis/public/test_utils/get_index_patterns_mock.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_search_service_mock.ts b/src/plugins/input_control_vis/public/test_utils/get_search_service_mock.ts
similarity index 91%
rename from src/legacy/core_plugins/input_control_vis/public/test_utils/get_search_service_mock.ts
rename to src/plugins/input_control_vis/public/test_utils/get_search_service_mock.ts
index 94a460086e9da..24b7d7bcbb5c1 100644
--- a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_search_service_mock.ts
+++ b/src/plugins/input_control_vis/public/test_utils/get_search_service_mock.ts
@@ -17,9 +17,7 @@
* under the License.
*/
-import { SearchSource } from '../legacy_imports';
-
-export const getSearchSourceMock = (esSearchResponse?: any): SearchSource =>
+export const getSearchSourceMock = (esSearchResponse?: any) =>
jest.fn().mockImplementation(() => ({
setParent: jest.fn(),
setField: jest.fn(),
diff --git a/src/legacy/core_plugins/input_control_vis/public/test_utils/index.ts b/src/plugins/input_control_vis/public/test_utils/index.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/test_utils/index.ts
rename to src/plugins/input_control_vis/public/test_utils/index.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/test_utils/update_component.ts b/src/plugins/input_control_vis/public/test_utils/update_component.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/test_utils/update_component.ts
rename to src/plugins/input_control_vis/public/test_utils/update_component.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx b/src/plugins/input_control_vis/public/vis_controller.tsx
similarity index 97%
rename from src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx
rename to src/plugins/input_control_vis/public/vis_controller.tsx
index c4a7d286850e3..97506556d7e0a 100644
--- a/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx
+++ b/src/plugins/input_control_vis/public/vis_controller.tsx
@@ -21,8 +21,6 @@ import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nStart } from 'kibana/public';
-import { SearchSource } from './legacy_imports';
-
import { InputControlVis } from './components/vis/input_control_vis';
import { getControlFactory } from './control/control_factory';
import { getLineageMap } from './lineage';
@@ -30,8 +28,8 @@ import { ControlParams } from './editor_utils';
import { RangeControl } from './control/range_control_factory';
import { ListControl } from './control/list_control_factory';
import { InputControlVisDependencies } from './plugin';
-import { FilterManager, Filter } from '../../../../plugins/data/public';
-import { VisParams, Vis } from '../../../../plugins/visualizations/public';
+import { FilterManager, Filter } from '../../data/public';
+import { VisParams, Vis } from '../../visualizations/public';
export const createInputControlVisController = (deps: InputControlVisDependencies) => {
return class InputControlVisController {
@@ -102,7 +100,8 @@ export const createInputControlVisController = (deps: InputControlVisDependencie
const controlFactoryPromises = controlParamsList.map(controlParams => {
const factory = getControlFactory(controlParams);
- return factory(controlParams, this.visParams?.useTimeFilter, SearchSource, deps);
+
+ return factory(controlParams, this.visParams?.useTimeFilter, deps);
});
const controls = await Promise.all(controlFactoryPromises);
diff --git a/src/legacy/core_plugins/vis_type_vega/public/index.ts b/src/plugins/input_control_vis/server/index.ts
similarity index 73%
rename from src/legacy/core_plugins/vis_type_vega/public/index.ts
rename to src/plugins/input_control_vis/server/index.ts
index 34ca0e72190e4..043657ba98a3c 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/index.ts
+++ b/src/plugins/input_control_vis/server/index.ts
@@ -17,9 +17,14 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../../core/public';
-import { VegaPlugin as Plugin } from './plugin';
+import { PluginConfigDescriptor } from 'kibana/server';
+import { schema } from '@kbn/config-schema';
-export function plugin(initializerContext: PluginInitializerContext) {
- return new Plugin(initializerContext);
-}
+export const config: PluginConfigDescriptor = {
+ schema: schema.object({ enabled: schema.boolean({ defaultValue: true }) }),
+};
+
+export const plugin = () => ({
+ setup() {},
+ start() {},
+});
diff --git a/src/plugins/kibana_utils/public/history/ensure_default_index_pattern.tsx b/src/plugins/kibana_utils/public/history/ensure_default_index_pattern.tsx
deleted file mode 100644
index 7992f650cb372..0000000000000
--- a/src/plugins/kibana_utils/public/history/ensure_default_index_pattern.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { contains } from 'lodash';
-import React from 'react';
-import { History } from 'history';
-import { i18n } from '@kbn/i18n';
-import { EuiCallOut } from '@elastic/eui';
-import { CoreStart } from 'kibana/public';
-import { DataPublicPluginStart } from 'src/plugins/data/public';
-import { toMountPoint } from '../../../kibana_react/public';
-
-let bannerId: string;
-let timeoutId: NodeJS.Timeout | undefined;
-
-/**
- * Checks whether a default index pattern is set and exists and defines
- * one otherwise.
- *
- * If there are no index patterns, redirect to management page and show
- * banner. In this case the promise returned from this function will never
- * resolve to wait for the URL change to happen.
- */
-export async function ensureDefaultIndexPattern(
- core: CoreStart,
- data: DataPublicPluginStart,
- history: History
-) {
- const patterns = await data.indexPatterns.getIds();
- let defaultId = core.uiSettings.get('defaultIndex');
- let defined = !!defaultId;
- const exists = contains(patterns, defaultId);
-
- if (defined && !exists) {
- core.uiSettings.remove('defaultIndex');
- defaultId = defined = false;
- }
-
- if (defined) {
- return;
- }
-
- // If there is any index pattern created, set the first as default
- if (patterns.length >= 1) {
- defaultId = patterns[0];
- core.uiSettings.set('defaultIndex', defaultId);
- } else {
- const canManageIndexPatterns = core.application.capabilities.management.kibana.index_patterns;
- const redirectTarget = canManageIndexPatterns ? '/management/kibana/index_pattern' : '/home';
-
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
-
- // Avoid being hostile to new users who don't have an index pattern setup yet
- // give them a friendly info message instead of a terse error message
- bannerId = core.overlays.banners.replace(
- bannerId,
- toMountPoint(
-
- )
- );
-
- // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around
- timeoutId = setTimeout(() => {
- core.overlays.banners.remove(bannerId);
- timeoutId = undefined;
- }, 15000);
-
- history.push(redirectTarget);
-
- // return never-resolving promise to stop resolving and wait for the url change
- return new Promise(() => {});
- }
-}
diff --git a/src/plugins/kibana_utils/public/history/index.ts b/src/plugins/kibana_utils/public/history/index.ts
index 1a73bbb6b04a1..bb13ea09f928a 100644
--- a/src/plugins/kibana_utils/public/history/index.ts
+++ b/src/plugins/kibana_utils/public/history/index.ts
@@ -19,4 +19,3 @@
export { removeQueryParam } from './remove_query_param';
export { redirectWhenMissing } from './redirect_when_missing';
-export { ensureDefaultIndexPattern } from './ensure_default_index_pattern';
diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts
index 2f139050e994a..c634322b23d0b 100644
--- a/src/plugins/kibana_utils/public/index.ts
+++ b/src/plugins/kibana_utils/public/index.ts
@@ -74,7 +74,7 @@ export {
StartSyncStateFnType,
StopSyncStateFnType,
} from './state_sync';
-export { removeQueryParam, redirectWhenMissing, ensureDefaultIndexPattern } from './history';
+export { removeQueryParam, redirectWhenMissing } from './history';
export { applyDiff } from './state_management/utils/diff_object';
/** dummy plugin, we just want kibanaUtils to have its own bundle */
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
index 6e4c505c62ebc..513c70e60048a 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
@@ -31,6 +31,7 @@ import {
setStateToKbnUrl,
getStateFromKbnUrl,
} from './kbn_url_storage';
+import { ScopedHistory } from '../../../../../core/public';
describe('kbn_url_storage', () => {
describe('getStateFromUrl & setStateToUrl', () => {
@@ -187,23 +188,54 @@ describe('kbn_url_storage', () => {
urlControls.update('/', true);
});
- const getCurrentUrl = () => window.location.href;
+ const getCurrentUrl = () => history.createHref(history.location);
it('should flush async url updates', async () => {
const pr1 = urlControls.updateAsync(() => '/1', false);
const pr2 = urlControls.updateAsync(() => '/2', false);
const pr3 = urlControls.updateAsync(() => '/3', false);
- expect(getCurrentUrl()).toBe('http://localhost/');
- expect(urlControls.flush()).toBe('http://localhost/3');
- expect(getCurrentUrl()).toBe('http://localhost/3');
+ expect(getCurrentUrl()).toBe('/');
+ expect(urlControls.flush()).toBe('/3');
+ expect(getCurrentUrl()).toBe('/3');
+ await Promise.all([pr1, pr2, pr3]);
+ expect(getCurrentUrl()).toBe('/3');
+ });
+
+ it('flush() should return undefined, if no url updates happened', () => {
+ expect(urlControls.flush()).toBeUndefined();
+ urlControls.updateAsync(() => '/1', false);
+ urlControls.updateAsync(() => '/', false);
+ expect(urlControls.flush()).toBeUndefined();
+ });
+ });
+
+ describe('urlControls - scoped history integration', () => {
+ let history: History;
+ let urlControls: IKbnUrlControls;
+ beforeEach(() => {
+ const parentHistory = createBrowserHistory();
+ parentHistory.replace('/app/kibana/');
+ history = new ScopedHistory(parentHistory, '/app/kibana/');
+ urlControls = createKbnUrlControls(history);
+ });
+
+ const getCurrentUrl = () => history.createHref(history.location);
+
+ it('should flush async url updates', async () => {
+ const pr1 = urlControls.updateAsync(() => '/app/kibana/1', false);
+ const pr2 = urlControls.updateAsync(() => '/app/kibana/2', false);
+ const pr3 = urlControls.updateAsync(() => '/app/kibana/3', false);
+ expect(getCurrentUrl()).toBe('/app/kibana/');
+ expect(urlControls.flush()).toBe('/app/kibana/3');
+ expect(getCurrentUrl()).toBe('/app/kibana/3');
await Promise.all([pr1, pr2, pr3]);
- expect(getCurrentUrl()).toBe('http://localhost/3');
+ expect(getCurrentUrl()).toBe('/app/kibana/3');
});
it('flush() should return undefined, if no url updates happened', () => {
expect(urlControls.flush()).toBeUndefined();
- urlControls.updateAsync(() => 'http://localhost/1', false);
- urlControls.updateAsync(() => 'http://localhost/', false);
+ urlControls.updateAsync(() => '/app/kibana/1', false);
+ urlControls.updateAsync(() => '/app/kibana/', false);
expect(urlControls.flush()).toBeUndefined();
});
});
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
index 40a411d425a54..337d122e2854b 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
@@ -154,7 +154,7 @@ export const createKbnUrlControls = (
let shouldReplace = true;
function updateUrl(newUrl: string, replace = false): string | undefined {
- const currentUrl = getCurrentUrl();
+ const currentUrl = getCurrentUrl(history);
if (newUrl === currentUrl) return undefined; // skip update
const historyPath = getRelativeToHistoryPath(newUrl, history);
@@ -165,7 +165,7 @@ export const createKbnUrlControls = (
history.push(historyPath);
}
- return getCurrentUrl();
+ return getCurrentUrl(history);
}
// queue clean up
@@ -187,7 +187,10 @@ export const createKbnUrlControls = (
function getPendingUrl() {
if (updateQueue.length === 0) return undefined;
- const resultUrl = updateQueue.reduce((url, nextUpdate) => nextUpdate(url), getCurrentUrl());
+ const resultUrl = updateQueue.reduce(
+ (url, nextUpdate) => nextUpdate(url),
+ getCurrentUrl(history)
+ );
return resultUrl;
}
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
index af8811b1969e6..8adbbfb06e1ed 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
@@ -57,6 +57,7 @@ export function createKbnUrlTracker({
navLinkUpdater$,
toastNotifications,
history,
+ getHistory,
storage,
shouldTrackUrlUpdate = pathname => {
const currentAppName = defaultSubUrl.slice(2); // cut hash and slash symbols
@@ -103,6 +104,12 @@ export function createKbnUrlTracker({
* History object to use to track url changes. If this isn't provided, a local history instance will be created.
*/
history?: History;
+
+ /**
+ * Lazily retrieve history instance
+ */
+ getHistory?: () => History;
+
/**
* Storage object to use to persist currently active url. If this isn't provided, the browser wide session storage instance will be used.
*/
@@ -158,7 +165,7 @@ export function createKbnUrlTracker({
function onMountApp() {
unsubscribe();
- const historyInstance = history || createHashHistory();
+ const historyInstance = history || (getHistory && getHistory()) || createHashHistory();
// track current hash when within app
unsubscribeURLHistory = historyInstance.listen(location => {
if (shouldTrackUrlUpdate(location.pathname)) {
diff --git a/src/plugins/kibana_utils/public/state_management/url/parse.ts b/src/plugins/kibana_utils/public/state_management/url/parse.ts
index 95041d0662f56..6339002ea5c68 100644
--- a/src/plugins/kibana_utils/public/state_management/url/parse.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/parse.ts
@@ -18,12 +18,11 @@
*/
import { parse as _parseUrl } from 'url';
+import { History } from 'history';
export const parseUrl = (url: string) => _parseUrl(url, true);
export const parseUrlHash = (url: string) => {
const hash = parseUrl(url).hash;
return hash ? parseUrl(hash.slice(1)) : null;
};
-export const getCurrentUrl = () => window.location.href;
-export const parseCurrentUrl = () => parseUrl(getCurrentUrl());
-export const parseCurrentUrlHash = () => parseUrlHash(getCurrentUrl());
+export const getCurrentUrl = (history: History) => history.createHref(history.location);
diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts
index cc3f1df7c1e00..8a9a4ea71ee9a 100644
--- a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts
+++ b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts
@@ -21,6 +21,7 @@ import { createKbnUrlStateStorage, IKbnUrlStateStorage } from './create_kbn_url_
import { History, createBrowserHistory } from 'history';
import { takeUntil, toArray } from 'rxjs/operators';
import { Subject } from 'rxjs';
+import { ScopedHistory } from '../../../../../core/public';
describe('KbnUrlStateStorage', () => {
describe('useHash: false', () => {
@@ -132,4 +133,78 @@ describe('KbnUrlStateStorage', () => {
expect(await result).toEqual([{ test: 'test', ok: 1 }, { test: 'test', ok: 2 }, null]);
});
});
+
+ describe('ScopedHistory integration', () => {
+ let urlStateStorage: IKbnUrlStateStorage;
+ let history: ScopedHistory;
+ const getCurrentUrl = () => history.createHref(history.location);
+ beforeEach(() => {
+ const parentHistory = createBrowserHistory();
+ parentHistory.push('/kibana/app/');
+ history = new ScopedHistory(parentHistory, '/kibana/app/');
+ urlStateStorage = createKbnUrlStateStorage({ useHash: false, history });
+ });
+
+ it('should persist state to url', async () => {
+ const state = { test: 'test', ok: 1 };
+ const key = '_s';
+ await urlStateStorage.set(key, state);
+ expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/#?_s=(ok:1,test:test)"`);
+ expect(urlStateStorage.get(key)).toEqual(state);
+ });
+
+ it('should flush state to url', () => {
+ const state = { test: 'test', ok: 1 };
+ const key = '_s';
+ urlStateStorage.set(key, state);
+ expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/"`);
+ expect(urlStateStorage.flush()).toBe(true);
+ expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/#?_s=(ok:1,test:test)"`);
+ expect(urlStateStorage.get(key)).toEqual(state);
+
+ expect(urlStateStorage.flush()).toBe(false); // nothing to flush, not update
+ });
+
+ it('should cancel url updates', async () => {
+ const state = { test: 'test', ok: 1 };
+ const key = '_s';
+ const pr = urlStateStorage.set(key, state);
+ expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/"`);
+ urlStateStorage.cancel();
+ await pr;
+ expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/"`);
+ expect(urlStateStorage.get(key)).toEqual(null);
+ });
+
+ it('should cancel url updates if synchronously returned to the same state', async () => {
+ const state1 = { test: 'test', ok: 1 };
+ const state2 = { test: 'test', ok: 2 };
+ const key = '_s';
+ const pr1 = urlStateStorage.set(key, state1);
+ await pr1;
+ const historyLength = history.length;
+ const pr2 = urlStateStorage.set(key, state2);
+ const pr3 = urlStateStorage.set(key, state1);
+ await Promise.all([pr2, pr3]);
+ expect(history.length).toBe(historyLength);
+ });
+
+ it('should notify about url changes', async () => {
+ expect(urlStateStorage.change$).toBeDefined();
+ const key = '_s';
+ const destroy$ = new Subject();
+ const result = urlStateStorage.change$!(key)
+ .pipe(takeUntil(destroy$), toArray())
+ .toPromise();
+
+ history.push(`/#?${key}=(ok:1,test:test)`);
+ history.push(`/?query=test#?${key}=(ok:2,test:test)&some=test`);
+ history.push(`/?query=test#?some=test`);
+
+ destroy$.next();
+ destroy$.complete();
+
+ expect(await result).toEqual([{ test: 'test', ok: 1 }, { test: 'test', ok: 2 }, null]);
+ });
+ });
});
diff --git a/src/plugins/management/kibana.json b/src/plugins/management/kibana.json
index 1789b7cd5ddba..cc411a8c6a25c 100644
--- a/src/plugins/management/kibana.json
+++ b/src/plugins/management/kibana.json
@@ -1,7 +1,7 @@
{
"id": "management",
"version": "kibana",
- "server": false,
+ "server": true,
"ui": true,
"requiredPlugins": ["kibanaLegacy", "home"]
}
diff --git a/src/plugins/management/public/management_service.test.ts b/src/plugins/management/public/management_service.test.ts
index ceb91837921eb..18569ef285ff3 100644
--- a/src/plugins/management/public/management_service.test.ts
+++ b/src/plugins/management/public/management_service.test.ts
@@ -29,9 +29,8 @@ test('Provides default sections', () => {
() => {},
coreMock.createSetup().getStartServices
);
- expect(service.getAllSections().length).toEqual(3);
+ expect(service.getAllSections().length).toEqual(2);
expect(service.getSection('kibana')).not.toBeUndefined();
- expect(service.getSection('logstash')).not.toBeUndefined();
expect(service.getSection('elasticsearch')).not.toBeUndefined();
});
diff --git a/src/plugins/management/public/management_service.ts b/src/plugins/management/public/management_service.ts
index ed31a22992da8..8fc207e32e6ce 100644
--- a/src/plugins/management/public/management_service.ts
+++ b/src/plugins/management/public/management_service.ts
@@ -80,7 +80,6 @@ export class ManagementService {
);
register({ id: 'kibana', title: 'Kibana', order: 30, euiIconType: 'logoKibana' });
- register({ id: 'logstash', title: 'Logstash', order: 30, euiIconType: 'logoLogstash' });
register({
id: 'elasticsearch',
title: 'Elasticsearch',
diff --git a/src/plugins/management/server/capabilities_provider.ts b/src/plugins/management/server/capabilities_provider.ts
new file mode 100644
index 0000000000000..9a69749c8233b
--- /dev/null
+++ b/src/plugins/management/server/capabilities_provider.ts
@@ -0,0 +1,32 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export const capabilitiesProvider = () => ({
+ management: {
+ /*
+ * Management settings correspond to management section/link ids, and should not be changed
+ * without also updating those definitions.
+ */
+ kibana: {
+ settings: true,
+ index_patterns: true,
+ objects: true,
+ },
+ },
+});
diff --git a/src/plugins/management/server/index.ts b/src/plugins/management/server/index.ts
new file mode 100644
index 0000000000000..afc7adf8832e1
--- /dev/null
+++ b/src/plugins/management/server/index.ts
@@ -0,0 +1,24 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext } from 'kibana/server';
+import { ManagementServerPlugin } from './plugin';
+
+export const plugin = (initContext: PluginInitializerContext) =>
+ new ManagementServerPlugin(initContext);
diff --git a/src/plugins/management/server/plugin.ts b/src/plugins/management/server/plugin.ts
new file mode 100644
index 0000000000000..f8fda7da9b95a
--- /dev/null
+++ b/src/plugins/management/server/plugin.ts
@@ -0,0 +1,44 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from 'kibana/server';
+import { capabilitiesProvider } from './capabilities_provider';
+
+export class ManagementServerPlugin implements Plugin {
+ private readonly logger: Logger;
+
+ constructor(initializerContext: PluginInitializerContext) {
+ this.logger = initializerContext.logger.get();
+ }
+
+ public setup(core: CoreSetup) {
+ this.logger.debug('management: Setup');
+
+ core.capabilities.registerProvider(capabilitiesProvider);
+
+ return {};
+ }
+
+ public start(core: CoreStart) {
+ this.logger.debug('management: Started');
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/src/plugins/navigation/public/top_nav_menu/_index.scss b/src/plugins/navigation/public/top_nav_menu/_index.scss
index 5befe4789dd6c..a6ddf7a8b4264 100644
--- a/src/plugins/navigation/public/top_nav_menu/_index.scss
+++ b/src/plugins/navigation/public/top_nav_menu/_index.scss
@@ -8,4 +8,8 @@
padding: 0 $euiSizeS;
}
}
+
+ .kbnTopNavMenu-isFullScreen {
+ padding: 0;
+ }
}
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx
index 8e0e8b3031132..74cfd125c2e3a 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx
@@ -75,4 +75,17 @@ describe('TopNavMenu', () => {
expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0);
expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(1);
});
+
+ it('Should render with a class name', () => {
+ const component = shallowWithIntl(
+
+ );
+ expect(component.find('.kbnTopNavMenu').length).toBe(1);
+ expect(component.find('.myCoolClass').length).toBeTruthy();
+ });
});
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
index 14ad40f13e388..d492c7feb61a7 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
@@ -21,6 +21,7 @@ import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import classNames from 'classnames';
import { TopNavMenuData } from './top_nav_menu_data';
import { TopNavMenuItem } from './top_nav_menu_item';
import { StatefulSearchBarProps, DataPublicPluginStart } from '../../../data/public';
@@ -29,6 +30,7 @@ export type TopNavMenuProps = StatefulSearchBarProps & {
config?: TopNavMenuData[];
showSearchBar?: boolean;
data?: DataPublicPluginStart;
+ className?: string;
};
/*
@@ -65,6 +67,7 @@ export function TopNavMenu(props: TopNavMenuProps) {
}
function renderLayout() {
+ const className = classNames('kbnTopNavMenu', props.className);
return (
{renderItems()}
diff --git a/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
index 9776887b6d741..df687a051fc7d 100644
--- a/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
@@ -17,9 +17,9 @@
* under the License.
*/
import _ from 'lodash';
-import { EsResponse, SavedObject, SavedObjectConfig } from '../../types';
+import { EsResponse, SavedObject, SavedObjectConfig, SavedObjectKibanaServices } from '../../types';
import { expandShorthand, SavedObjectNotFound } from '../../../../kibana_utils/public';
-import { DataPublicPluginStart, IndexPattern } from '../../../../data/public';
+import { IndexPattern } from '../../../../data/public';
/**
* A given response of and ElasticSearch containing a plain saved object is applied to the given
@@ -29,7 +29,7 @@ export async function applyESResp(
resp: EsResponse,
savedObject: SavedObject,
config: SavedObjectConfig,
- createSearchSource: DataPublicPluginStart['search']['createSearchSource']
+ dependencies: SavedObjectKibanaServices
) {
const mapping = expandShorthand(config.mapping);
const esType = config.type || '';
@@ -65,7 +65,10 @@ export async function applyESResp(
if (config.searchSource) {
try {
- savedObject.searchSource = await createSearchSource(meta.searchSourceJSON, resp.references);
+ savedObject.searchSource = await dependencies.search.searchSource.fromJSON(
+ meta.searchSourceJSON,
+ resp.references
+ );
} catch (error) {
if (
error.constructor.name === 'SavedObjectNotFound' &&
diff --git a/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts
index e8faef4e9e040..e5b0e18e7b433 100644
--- a/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts
@@ -16,8 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import _ from 'lodash';
-import { SearchSource } from '../../../../data/public';
+import { once } from 'lodash';
import { hydrateIndexPattern } from './hydrate_index_pattern';
import { intializeSavedObject } from './initialize_saved_object';
import { serializeSavedObject } from './serialize_saved_object';
@@ -55,7 +54,9 @@ export function buildSavedObject(
savedObject.isSaving = false;
savedObject.defaults = config.defaults || {};
// optional search source which this object configures
- savedObject.searchSource = config.searchSource ? new SearchSource() : undefined;
+ savedObject.searchSource = config.searchSource
+ ? services.search.searchSource.create()
+ : undefined;
// the id of the document
savedObject.id = config.id || void 0;
// the migration version of the document, should only be set on imports
@@ -79,10 +80,9 @@ export function buildSavedObject(
* @return {Promise}
* @resolved {SavedObject}
*/
- savedObject.init = _.once(() => intializeSavedObject(savedObject, savedObjectsClient, config));
+ savedObject.init = once(() => intializeSavedObject(savedObject, savedObjectsClient, config));
- savedObject.applyESResp = (resp: EsResponse) =>
- applyESResp(resp, savedObject, config, services.search.createSearchSource);
+ savedObject.applyESResp = (resp: EsResponse) => applyESResp(resp, savedObject, config, services);
/**
* Serialize this object
diff --git a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts
index 60c66f84080b2..f7e67dbe3ee1d 100644
--- a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts
+++ b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts
@@ -28,9 +28,8 @@ import {
// @ts-ignore
import StubIndexPattern from 'test_utils/stub_index_pattern';
-import { InvalidJSONProperty } from '../../../kibana_utils/public';
import { coreMock } from '../../../../core/public/mocks';
-import { dataPluginMock } from '../../../../plugins/data/public/mocks';
+import { dataPluginMock, createSearchSourceMock } from '../../../../plugins/data/public/mocks';
import { SavedObjectAttributes, SimpleSavedObject } from 'kibana/public';
import { IIndexPattern } from '../../../data/common/index_patterns';
@@ -40,9 +39,9 @@ describe('Saved Object', () => {
const startMock = coreMock.createStart();
const dataStartMock = dataPluginMock.createStartContract();
const saveOptionsMock = {} as SavedObjectSaveOpts;
+ const savedObjectsClientStub = startMock.savedObjects.client;
let SavedObjectClass: new (config: SavedObjectConfig) => SavedObject;
- const savedObjectsClientStub = startMock.savedObjects.client;
/**
* Returns a fake doc response with the given index and id, of type dashboard
@@ -99,16 +98,22 @@ describe('Saved Object', () => {
function createInitializedSavedObject(config: SavedObjectConfig = {}) {
const savedObject = new SavedObjectClass(config);
savedObject.title = 'my saved object';
+
return savedObject.init!();
}
beforeEach(() => {
- (dataStartMock.search.createSearchSource as jest.Mock).mockReset();
- SavedObjectClass = createSavedObjectClass({
+ SavedObjectClass = createSavedObjectClass(({
savedObjectsClient: savedObjectsClientStub,
indexPatterns: dataStartMock.indexPatterns,
- search: dataStartMock.search,
- } as SavedObjectKibanaServices);
+ search: {
+ ...dataStartMock.search,
+ searchSource: {
+ ...dataStartMock.search.searchSource,
+ create: createSearchSourceMock,
+ },
+ },
+ } as unknown) as SavedObjectKibanaServices);
});
describe('save', () => {
@@ -411,27 +416,6 @@ describe('Saved Object', () => {
});
});
- it('forwards thrown exceptions from createSearchSource', async () => {
- (dataStartMock.search.createSearchSource as jest.Mock).mockImplementation(() => {
- throw new InvalidJSONProperty('');
- });
- const savedObject = await createInitializedSavedObject({
- type: 'dashboard',
- searchSource: true,
- });
- const response = {
- found: true,
- _source: {},
- };
-
- try {
- await savedObject.applyESResp(response);
- throw new Error('applyESResp should have failed, but did not.');
- } catch (err) {
- expect(err instanceof InvalidJSONProperty).toBe(true);
- }
- });
-
it('preserves original defaults if not overridden', () => {
const id = 'anid';
const preserveMeValue = 'here to stay!';
@@ -589,42 +573,45 @@ describe('Saved Object', () => {
it('passes references to search source parsing function', async () => {
const savedObject = new SavedObjectClass({ type: 'dashboard', searchSource: true });
- return savedObject.init!().then(() => {
- const searchSourceJSON = JSON.stringify({
- indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
- filter: [
- {
- meta: {
- indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
- },
- },
- ],
- });
- const response = {
- found: true,
- _source: {
- kibanaSavedObjectMeta: {
- searchSourceJSON,
+ await savedObject.init!();
+
+ const searchSourceJSON = JSON.stringify({
+ indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
+ filter: [
+ {
+ meta: {
+ indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
},
},
- references: [
- {
- name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
- type: 'index-pattern',
- id: 'my-index-1',
- },
- {
- name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
- type: 'index-pattern',
- id: 'my-index-2',
- },
- ],
- };
- savedObject.applyESResp(response);
- expect(dataStartMock.search.createSearchSource).toBeCalledWith(
- searchSourceJSON,
- response.references
- );
+ ],
+ });
+ const response = {
+ found: true,
+ _source: {
+ kibanaSavedObjectMeta: {
+ searchSourceJSON,
+ },
+ },
+ references: [
+ {
+ name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
+ type: 'index-pattern',
+ id: 'my-index-1',
+ },
+ {
+ name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
+ type: 'index-pattern',
+ id: 'my-index-2',
+ },
+ ],
+ };
+ const result = await savedObject.applyESResp(response);
+
+ expect(result._source).toEqual({
+ kibanaSavedObjectMeta: {
+ searchSourceJSON:
+ '{"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index","filter":[{"meta":{"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index"}}]}',
+ },
});
});
});
diff --git a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts
index 23c2b75169555..f98b1762511f4 100644
--- a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts
+++ b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts
@@ -25,6 +25,7 @@ import {
} from './resolve_saved_objects';
import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public';
import { IndexPatternsContract } from '../../../data/public';
+import { dataPluginMock } from '../../../data/public/mocks';
class SavedObjectNotFound extends Error {
constructor(options: Record) {
@@ -233,6 +234,19 @@ describe('resolveSavedObjects', () => {
});
describe('resolveIndexPatternConflicts', () => {
+ let dependencies: Parameters[3];
+
+ beforeEach(() => {
+ const search = dataPluginMock.createStartContract().search;
+
+ dependencies = {
+ indexPatterns: ({
+ get: (id: string) => Promise.resolve({ id }),
+ } as unknown) as IndexPatternsContract,
+ search,
+ };
+ });
+
it('should resave resolutions', async () => {
const save = jest.fn();
@@ -284,11 +298,13 @@ describe('resolveSavedObjects', () => {
const overwriteAll = false;
- await resolveIndexPatternConflicts(resolutions, conflictedIndexPatterns, overwriteAll, ({
- get: (id: string) => Promise.resolve({ id }),
- } as unknown) as IndexPatternsContract);
- expect(conflictedIndexPatterns[0].obj.searchSource!.getField('index')!.id).toEqual('2');
- expect(conflictedIndexPatterns[1].obj.searchSource!.getField('index')!.id).toEqual('4');
+ await resolveIndexPatternConflicts(
+ resolutions,
+ conflictedIndexPatterns,
+ overwriteAll,
+ dependencies
+ );
+
expect(save.mock.calls.length).toBe(2);
expect(save).toHaveBeenCalledWith({ confirmOverwrite: !overwriteAll });
});
@@ -345,13 +361,13 @@ describe('resolveSavedObjects', () => {
const overwriteAll = false;
- await resolveIndexPatternConflicts(resolutions, conflictedIndexPatterns, overwriteAll, ({
- get: (id: string) => Promise.resolve({ id }),
- } as unknown) as IndexPatternsContract);
+ await resolveIndexPatternConflicts(
+ resolutions,
+ conflictedIndexPatterns,
+ overwriteAll,
+ dependencies
+ );
- expect(conflictedIndexPatterns[0].obj.searchSource!.getField('filter')).toEqual([
- { meta: { index: 'newFilterIndex' } },
- ]);
expect(save.mock.calls.length).toBe(2);
});
});
diff --git a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts
index 15e03ed39d88c..d4764b8949a60 100644
--- a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts
+++ b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts
@@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n';
import { cloneDeep } from 'lodash';
import { OverlayStart, SavedObjectReference } from 'src/core/public';
import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public';
-import { IndexPatternsContract, IIndexPattern, createSearchSource } from '../../../data/public';
+import { IndexPatternsContract, IIndexPattern, DataPublicPluginStart } from '../../../data/public';
type SavedObjectsRawDoc = Record